Skip to content

Instantly share code, notes, and snippets.

@VinceAvery
Last active September 15, 2023 04:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VinceAvery/695607f870f267b4a114b19fa03ee283 to your computer and use it in GitHub Desktop.
Save VinceAvery/695607f870f267b4a114b19fa03ee283 to your computer and use it in GitHub Desktop.
A validator to check a financial identifier called an ISIN, an International Securities Identification Number. It has the ability to both calculate the checksum and validate the complete ISIN, and also it is simple to understand, with as few methods as possible.
namespace ConsoleApplication.Validators {
public interface IIsinValidator {
bool IsChecksumCorrect(string dataWithChecksum, bool checkLastDigit);
}
public class IsinValidator : IIsinValidator {
public bool IsChecksumCorrect(string dataWithChecksum, bool checkLastDigit) {
try {
if (!Regex.IsMatch(dataWithChecksum, @"^([a-zA-Z]{2}[\w]{9}[0-9]{1})$")) {
return false;
}
if (checkLastDigit) {
var checksum = dataWithChecksum.Last() - '0';
return checksum == CalculateChecksum(dataWithChecksum.Take(dataWithChecksum.Length - 1));
}
return IsChecksumCorrect(dataWithChecksum.ToCharArray());
} catch {
return false;
}
}
private int CalculateChecksum(IEnumerable<char> codeWithoutChecksum) {
var sum = CalculateSum(codeWithoutChecksum, false);
// Final step is important in the instance where the modulus of the sum is 0, as the resulting check digit would be 10.
return (10 - (sum % 10)) % 10;
}
private bool IsChecksumCorrect(IEnumerable<char> code) {
var sum = CalculateSum(code, true);
return sum % 10 == 0;
}
private int CalculateSum(IEnumerable<char> chars, bool hasChecksum) {
return chars.
Select(c => { // Convert to an array of digits where chars are ordinal starting A = 10
return char.IsDigit(c) ?
new[] { c - '0' } :
new[] { (char.ToUpper(c) - ('A' - 10)) / 10, (char.ToUpper(c) - ('A' - 10)) % 10 };
}).
SelectMany(d => d).
Reverse().
Select((c, index) => // Double entries at odd location (0 based) and sum digits
(index % 2 == 0 ^ hasChecksum) ?
(c * 2 > 9) ? (c * 2 - 9) : (c * 2) :
c).
Sum();
}
}
}
namespace ConsoleApplication.Test {
[TestClass]
public class ISINValidatorTests {
private static IIsinValidator[] m_validators;
[ClassInitialize()]
public static void InitializeTestClass(TestContext testContext) {
m_validators = new IIsinValidator[] {
new IsinValidator(),
};
}
[TestMethod]
public void GivenValidISIN_Validate_NoErrorReturned() {
var data = new[] {
"US0378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040"};
foreach (var item in data) {
AssertTrue(item);
}
}
[TestMethod]
public void GivenInvalidISIN_Validate_ErrorReturned() {
var data = new[] {
"US037383100", // Too short
"5U0378331005", // Char 1 not letter
"U50378331005", // Char 2 not letter
"US03378331005" // Too long
};
foreach (var item in data) {
AssertFalse(item);
}
}
private void AssertFalse(string isin) {
foreach (var validator in m_validators) {
Assert.IsFalse(validator.IsChecksumCorrect(isin, true));
Assert.IsFalse(validator.IsChecksumCorrect(isin, false));
}
}
private static void AssertTrue(string isin) {
foreach (var validator in m_validators) {
Assert.IsTrue(validator.IsChecksumCorrect(isin, true));
Assert.IsTrue(validator.IsChecksumCorrect(isin, false));
}
}
}
}
@equdosBD
Copy link

rockin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment