Skip to content

Commit 5634c2d

Browse files
committed
Split Luhn class
- Clear separation of concerns: The project is well-structured, with distinct classes for calculation (`LuhnCalculator`), validation (`LuhnValidator`), and core algorithm logic (`LuhnAlgorithm`). This promotes maintainability and testability. - Extension methods: The use of extension methods for string manipulation and validation provides a fluent and convenient API. Resolves: No entry
1 parent 250348a commit 5634c2d

15 files changed

+1349
-886
lines changed

CHANGELOG.md

+14-10
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88
### Added
9-
- Add `InvalidCharacterException` to throw an exception if the input string contains invalid characters.
10-
- Add `RemoveSeparators` method to remove all separators from a string. Use it, for example, with credit card numbers.
9+
- Added `InvalidCharacterException` to throw an exception if the input string contains invalid characters.
10+
- Added `RemoveSeparators` methods to remove all separators from a string or ReadOnlySpan. Use it, for example, with credit card numbers.
1111

1212
### Changed
13-
- Renamed `Luhn.ConvertAlphaNumericToNumeric` to `Luhn.AlphaNumericToNumeric`
13+
- Renamed `ConvertAlphaNumericToNumeric` to `AlphaNumericToNumeric`
14+
- Moved `AlphaNumericToNumeric` method to the `StringExtensions` and `ReadOnlySpanExtensions` classes.
15+
- Moved `ComputeCheckDigit` and `ComputeLuhnNumber` methods to the `LuhnCalculator` class.
16+
- Moved `IsValidNumber` and `IsValidCheckDigit` methods to the `LuhnValidator` class.
17+
- Renamed `Luhn` class to `LuhnAlgorithm`.
1418

1519
### Removed
1620
- Removed `Luhn.IsValid` methods
1721

1822
## [1.3.0] - 2024-12-27
1923
### Added
20-
- Add project icon
21-
- Add project social preview image
22-
- Add `Luhn.IsValidNumber`
23-
- Add `Luhn.IsValidCheckDigit`
24+
- Added project icon
25+
- Added project social preview image
26+
- Added `Luhn.IsValidNumber`
27+
- Added `Luhn.IsValidCheckDigit`
2428

2529
### Deprecated
2630
- `Luhn.IsValid` methods are deprecated. Use `Luhn.IsValidNumber` or `Luhn.IsValidCheckDigit` instead.
2731

2832
## [1.2.0] - 2024-12-26
2933
### Added
30-
- Add [API documentation](https://sebastian-walther.de/LuhnDotNet/api/LuhnDotNet.html)
31-
- Add .NET 9 support
34+
- Added [API documentation](https://sebastian-walther.de/LuhnDotNet/api/LuhnDotNet.html)
35+
- Added .NET 9 support
3236

3337
### Removed
3438
- Removed .NET 7 support
@@ -37,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3741

3842
## [1.1.0] - 2024-03-30
3943
### Added
40-
- Add `ConvertAlphaNumericToNumeric` method to convert a string containing alphanumeric characters to a string containing only numeric characters (use case: converting an ISIN to a numeric string for Luhn validation).
44+
- Added `ConvertAlphaNumericToNumeric` method to convert a string containing alphanumeric characters to a string containing only numeric characters (use case: converting an ISIN to a numeric string for Luhn validation).
4145

4246
## [1.0.1] - 2024-03-19
4347
### Fixed

benchmark/LuhnDotNetBenchmark.cs

+92-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ namespace LuhnDotNetBenchmark;
55
using LuhnDotNet;
66
using System;
77
using System.Collections.Generic;
8+
using System.Diagnostics.CodeAnalysis;
89

10+
[ExcludeFromCodeCoverage]
911
[MemoryDiagnoser]
1012
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
1113
[CategoriesColumn]
@@ -15,66 +17,146 @@ public class LuhnDotNetBenchmark
1517
private const string ComputeLuhnCheckDigitCategory = "Compute Luhn check digit";
1618
private const string IsValidLuhnNumberCategory = "Validate Luhn number";
1719
private const string IsValidLuhnCheckDigitCategory = "Validate Luhn check digit";
20+
private const string AlphaNumericToNumericCategory = "AlphaNumeric to Numeric";
21+
private const string RemoveSeparatorCategory = "Remove separators";
22+
private const string ValidationChainCategory = "Validation chain";
23+
private const string ComputeChainCategory = "Compute chain";
1824

19-
public static IEnumerable<string> NumbersAsString()
25+
public static IEnumerable<string> NumericNumbersAsString()
2026
{
2127
yield return "79927398713";
2228
yield return "79927398718234234134645645645624563445654634343462545462546546245624356245625624565265432";
2329
}
2430

31+
public static IEnumerable<string> AlphaNumericNumbersAsString()
32+
{
33+
yield return "US79927398713";
34+
yield return "79927398713DE";
35+
yield return "ABCDEF";
36+
yield return "DE79927398718234234134645645645624563445654634343462545462546546245624356245625624565265432";
37+
}
38+
39+
public static IEnumerable<string> SeparatedNumbersAsString()
40+
{
41+
yield return "7992-7398-7134";
42+
yield return "79927398/71823423413464564564562456344565463434346254546254654/6245624356245625624565265432";
43+
}
44+
45+
public static IEnumerable<string> SeparatedAlphaNumericNumbersAsString()
46+
{
47+
yield return "US-7992-7398-7134";
48+
yield return "A7992-B7398-C7134";
49+
}
50+
2551
[Benchmark(Baseline = true), BenchmarkCategory(ComputeLuhnNumberCategory)]
26-
[ArgumentsSource(nameof(NumbersAsString))]
52+
[ArgumentsSource(nameof(NumericNumbersAsString))]
2753
public void ComputeLuhnNumberBasedOnString(string number)
2854
{
2955
number.ComputeLuhnNumber();
3056
}
3157

3258
[Benchmark(Baseline = true), BenchmarkCategory(ComputeLuhnCheckDigitCategory)]
33-
[ArgumentsSource(nameof(NumbersAsString))]
59+
[ArgumentsSource(nameof(NumericNumbersAsString))]
3460
public void ComputeLuhnCheckDigitBasedOnString(string number)
3561
{
3662
number.ComputeLuhnCheckDigit();
3763
}
3864

3965
[Benchmark(Baseline = true), BenchmarkCategory(IsValidLuhnNumberCategory)]
40-
[ArgumentsSource(nameof(NumbersAsString))]
66+
[ArgumentsSource(nameof(NumericNumbersAsString))]
4167
public void IsValidLuhnNumberBasedOnString(string number)
4268
{
4369
number.IsValidLuhnNumber();
4470
}
4571

4672
[Benchmark(Baseline = true), BenchmarkCategory(IsValidLuhnCheckDigitCategory)]
47-
[ArgumentsSource(nameof(NumbersAsString))]
73+
[ArgumentsSource(nameof(NumericNumbersAsString))]
4874
public void IsValidLuhnCheckDigitBasedOnString(string number)
4975
{
5076
byte.MinValue.IsValidLuhnCheckDigit(number);
5177
}
5278

5379
[Benchmark, BenchmarkCategory(ComputeLuhnNumberCategory)]
54-
[ArgumentsSource(nameof(NumbersAsString))]
80+
[ArgumentsSource(nameof(NumericNumbersAsString))]
5581
public void ComputeLuhnNumberBasedOnSpan(string number)
5682
{
5783
number.AsSpan().ComputeLuhnNumber();
5884
}
5985

6086
[Benchmark, BenchmarkCategory(ComputeLuhnCheckDigitCategory)]
61-
[ArgumentsSource(nameof(NumbersAsString))]
87+
[ArgumentsSource(nameof(NumericNumbersAsString))]
6288
public void ComputeLuhnCheckDigitBasedOnSpan(string number)
6389
{
6490
number.AsSpan().ComputeLuhnCheckDigit();
6591
}
6692

6793
[Benchmark, BenchmarkCategory(IsValidLuhnNumberCategory)]
68-
[ArgumentsSource(nameof(NumbersAsString))]
94+
[ArgumentsSource(nameof(NumericNumbersAsString))]
6995
public void IsValidLuhnNumberBasedOnSpan(string number)
7096
{
7197
number.AsSpan().IsValidLuhnNumber();
7298
}
7399

74100
[Benchmark, BenchmarkCategory(IsValidLuhnCheckDigitCategory)]
75-
[ArgumentsSource(nameof(NumbersAsString))]
101+
[ArgumentsSource(nameof(NumericNumbersAsString))]
76102
public void IsValidLuhnCheckDigitBasedOnSpan(string number)
77103
{
78104
byte.MinValue.IsValidLuhnCheckDigit(number.AsSpan());
79105
}
80-
}
106+
107+
[Benchmark(Baseline = true), BenchmarkCategory(AlphaNumericToNumericCategory)]
108+
[ArgumentsSource(nameof(AlphaNumericNumbersAsString))]
109+
public void AlphaNumericToNumericBasedOnString(string number)
110+
{
111+
number.AlphaNumericToNumeric();
112+
}
113+
114+
[Benchmark, BenchmarkCategory(AlphaNumericToNumericCategory)]
115+
[ArgumentsSource(nameof(AlphaNumericNumbersAsString))]
116+
public void AlphaNumericToNumericBasedOnSpan(string number)
117+
{
118+
number.AsSpan().AlphaNumericToNumeric();
119+
}
120+
121+
[Benchmark(Baseline = true), BenchmarkCategory(RemoveSeparatorCategory)]
122+
[ArgumentsSource(nameof(SeparatedNumbersAsString))]
123+
public void RemoveSeparatorBasedOnString(string number)
124+
{
125+
number.RemoveSeparators();
126+
}
127+
128+
[Benchmark, BenchmarkCategory(RemoveSeparatorCategory)]
129+
[ArgumentsSource(nameof(SeparatedNumbersAsString))]
130+
public void RemoveSeparatorBasedOnSpan(string number)
131+
{
132+
number.AsSpan().RemoveSeparators();
133+
}
134+
135+
[Benchmark(Baseline = true), BenchmarkCategory(ComputeChainCategory)]
136+
[ArgumentsSource(nameof(SeparatedAlphaNumericNumbersAsString))]
137+
public void ComputeChainBasedOnString(string number)
138+
{
139+
number.RemoveSeparators().AlphaNumericToNumeric().ComputeLuhnNumber();
140+
}
141+
142+
[Benchmark, BenchmarkCategory(ComputeChainCategory)]
143+
[ArgumentsSource(nameof(SeparatedAlphaNumericNumbersAsString))]
144+
public void ComputeChainBasedOnSpan(string number)
145+
{
146+
number.AsSpan().RemoveSeparators().AlphaNumericToNumeric().ComputeLuhnNumber();
147+
}
148+
149+
[Benchmark(Baseline = true), BenchmarkCategory(ValidationChainCategory)]
150+
[ArgumentsSource(nameof(SeparatedAlphaNumericNumbersAsString))]
151+
public void ValidationChainBasedOnString(string number)
152+
{
153+
number.RemoveSeparators().AlphaNumericToNumeric().IsValidLuhnNumber();
154+
}
155+
156+
[Benchmark, BenchmarkCategory(ValidationChainCategory)]
157+
[ArgumentsSource(nameof(SeparatedAlphaNumericNumbersAsString))]
158+
public void ValidationChainBasedOnSpan(string number)
159+
{
160+
number.AsSpan().RemoveSeparators().AlphaNumericToNumeric().IsValidLuhnNumber();
161+
}
162+
}

benchmark/Program.cs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1-
using BenchmarkDotNet.Running;
1+
namespace LuhnDotNetBenchmark;
22

3-
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
3+
using BenchmarkDotNet.Running;
4+
using System.Diagnostics.CodeAnalysis;
5+
6+
[ExcludeFromCodeCoverage]
7+
public static class Program
8+
{
9+
public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
10+
}

0 commit comments

Comments
 (0)