Skip to content

Instantly share code, notes, and snippets.

@angelyordanov
Created February 9, 2024 10:27
Show Gist options
  • Save angelyordanov/5d133c29035d802e7d49b30c99eaff03 to your computer and use it in GitHub Desktop.
Save angelyordanov/5d133c29035d802e7d49b30c99eaff03 to your computer and use it in GitHub Desktop.
Bulstat EIK validation implemented in C# in T-SQL
using System.Text.RegularExpressions;
namespace Erpy.Domain;
// based on https://tsvetanv.wordpress.com/2011/04/01/eik/
public partial class BulstatEikValidation
{
private static readonly int[] FIRST_SUM_9DIGIT_WEIGHTS = [1, 2, 3, 4, 5, 6, 7, 8];
private static readonly int[] SECOND_SUM_9DIGIT_WEIGHTS = [3, 4, 5, 6, 7, 8, 9, 10];
private static readonly int[] FIRST_SUM_13DIGIT_WEIGHTS = [2, 7, 3, 5];
private static readonly int[] SECOND_SUM_13DIGIT_WEIGHTS = [4, 9, 5, 7];
[GeneratedRegex(@"^\d+$", RegexOptions.Singleline)]
private static partial Regex EikRegex();
public static bool IsValid(string eik)
{
ArgumentNullException.ThrowIfNull(eik);
if (eik.Length != 9 && eik.Length != 13)
{
return false;
}
if (!EikRegex().IsMatch(eik))
{
return false;
}
int[] digits = eik.Select(c => int.Parse(c.ToString())).ToArray();
return
digits.Length switch
{
9 => CalculateChecksum(digits, FIRST_SUM_9DIGIT_WEIGHTS, SECOND_SUM_9DIGIT_WEIGHTS) == digits[8],
13 => CalculateChecksum(digits[0..9], FIRST_SUM_9DIGIT_WEIGHTS, SECOND_SUM_9DIGIT_WEIGHTS) == digits[8] &&
CalculateChecksum(digits[8..13], FIRST_SUM_13DIGIT_WEIGHTS, SECOND_SUM_13DIGIT_WEIGHTS) == digits[12],
_ => throw new Exception("Should not happen.")
};
}
private static int CalculateChecksum(int[] digits, int[] firstSumWeights, int[] secondSumWeights)
{
if (digits.Length - 1 != firstSumWeights.Length || digits.Length - 1 != secondSumWeights.Length)
{
throw new ArgumentException("The length of the digits minus 1 should be equal to the length of the weights.");
}
int sum = 0;
for (int i = 0; i < digits.Length - 1; i++)
{
sum += digits[i] * firstSumWeights[i];
}
int remainder = sum % 11;
if (remainder != 10)
{
return remainder;
}
int secondSum = 0;
for (int i = 0; i < digits.Length - 1; i++)
{
secondSum += digits[i] * secondSumWeights[i];
}
int secondRem = secondSum % 11;
if (secondRem != 10)
{
return secondRem;
}
return 0;
}
}
-- based on https://tsvetanv.wordpress.com/2011/04/01/eik/
CREATE TYPE IntArray
AS TABLE (
-- indeces are 1 based
[Index] INT,
[Value] INT
)
GO
CREATE OR ALTER FUNCTION fn_eik_is_valid(@eik NVARCHAR(MAX))
RETURNS BIT
AS
BEGIN
IF @eik IS NULL
RETURN 0;
IF LEN(@eik) != 9 AND LEN(@eik) != 13
RETURN 0;
IF PATINDEX('%[^0-9]%', @eik) > 0
RETURN 0;
DECLARE @i INT = 1;
DECLARE @len INT = LEN(@eik);
DECLARE @digits IntArray;
WHILE @i <= @len
BEGIN
INSERT INTO @digits ([Index], [Value])
VALUES (@i, CAST(SUBSTRING(@eik, @i, 1) AS INT));
SET @i = @i + 1;
END;
DECLARE @result BIT;
IF @len = 9
BEGIN
IF dbo.fn_eik_calc_checksum(@digits, 1, 9) = (SELECT [Value] FROM @digits WHERE [Index] = 9)
SET @result = 1;
ELSE
SET @result = 0;
END
ELSE IF @len = 13
BEGIN
IF dbo.fn_eik_calc_checksum(@digits, 1, 9) = (SELECT [Value] FROM @digits WHERE [Index] = 9)
AND dbo.fn_eik_calc_checksum(@digits, 9, 13) = (SELECT [Value] FROM @digits WHERE [Index] = 13)
SET @result = 1;
ELSE
SET @result = 0;
END;
RETURN @result;
END;
GO
CREATE OR ALTER FUNCTION dbo.fn_eik_calc_checksum(@digits IntArray READONLY, @start INT, @end INT)
RETURNS INT
AS
BEGIN
DECLARE @sum INT = 0;
DECLARE @i INT = @start;
DECLARE @currentValue INT;
WHILE @i < @end
BEGIN
SET @currentValue = (SELECT [Value] FROM @digits WHERE [Index] = @i)
SET @sum = @sum + @currentValue * dbo.fn_eik_get_weight(@i);
SET @i = @i + 1;
END;
DECLARE @remainder INT = @sum % 11;
IF @remainder != 10
RETURN @remainder;
DECLARE @secondSum INT = 0;
SET @i = @start;
WHILE @i < @end
BEGIN
SET @currentValue = (SELECT [Value] FROM @digits WHERE [Index] = @i)
SET @secondSum = @secondSum + @currentValue * dbo.fn_eik_get_second_weight(@i);
SET @i = @i + 1;
END;
RETURN @secondSum % 11;
END;
GO
CREATE OR ALTER FUNCTION dbo.fn_eik_get_weight(@position INT)
RETURNS INT
AS
BEGIN
RETURN CASE
WHEN @position = 1 THEN 1
WHEN @position = 2 THEN 2
WHEN @position = 3 THEN 3
WHEN @position = 4 THEN 4
WHEN @position = 5 THEN 5
WHEN @position = 6 THEN 6
WHEN @position = 7 THEN 7
WHEN @position = 8 THEN 8
WHEN @position = 9 THEN 2
WHEN @position = 10 THEN 7
WHEN @position = 11 THEN 3
WHEN @position = 12 THEN 5
ELSE 0
END;
END;
GO
CREATE OR ALTER FUNCTION dbo.fn_eik_get_second_weight(@position INT)
RETURNS INT
AS
BEGIN
RETURN CASE
WHEN @position = 1 THEN 3
WHEN @position = 2 THEN 4
WHEN @position = 3 THEN 5
WHEN @position = 4 THEN 6
WHEN @position = 5 THEN 7
WHEN @position = 6 THEN 8
WHEN @position = 7 THEN 9
WHEN @position = 8 THEN 10
WHEN @position = 9 THEN 4
WHEN @position = 10 THEN 9
WHEN @position = 11 THEN 5
WHEN @position = 12 THEN 7
ELSE 0
END;
END;
GO
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment