Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

hi!

I am making an application here but I am stuck with a checksum my customer requires for encoding cards they use for door access.

They uses the Luhn 10 checksum, I found two functions for this, but both returns the wrong values, maybe one of you experts know how to change this so I get the correct value...

function LuhnCheckDigit(const AInputString: string): Byte;

var

i: Integer;

LSum: Integer;

LDigit: Integer;

begin

i := 0;

LSum := 0;

while i < Length(AInputString) do

begin

LDigit := StrToInt(AInputString[Length(AInputString) - i]);

LDigit := LDigit * (1 + (i mod 2));

LSum := LSum + (LDigit div 10) + (LDigit mod 10);

inc(i);

end;

Result := LSum mod 10;

end;

function Mod10(const Value: string): string;

var

i, intOdd, intEven: Integer;

MyResult : integer;

begin

{add all odd seq numbers}

intOdd := 0;

i := 1;

while (i <= Length(Value)) do

begin

Inc(intOdd, StrToIntDef(Value[i], 0));

Inc(i, 2);

end;

{add all even seq numbers}

intEven := 0;

i := 2;

while (i <= Length(Value)) do

begin

Inc(intEven, StrToIntDef(Value[i], 0));

Inc(i, 2);

end;

MyResult := 3*intOdd + intEven;

{modulus by 10 to get}

MyResult := MyResult mod 10;

if MyResult <> 0 then

MyResult := 10 - MyResult;

Result := IntToStr(MyResult)

end;

Example values:

Result should be 1 : 91843862918438629

Result should be 7 : 270093000000000

Result should be 6 : 310023000000000

Result should be 7 : 000078000000000

I am making an application here but I am stuck with a checksum my customer requires for encoding cards they use for door access.

They uses the Luhn 10 checksum, I found two functions for this, but both returns the wrong values, maybe one of you experts know how to change this so I get the correct value...

function LuhnCheckDigit(const AInputString: string): Byte;

var

i: Integer;

LSum: Integer;

LDigit: Integer;

begin

i := 0;

LSum := 0;

while i < Length(AInputString) do

begin

LDigit := StrToInt(AInputString[Leng

LDigit := LDigit * (1 + (i mod 2));

LSum := LSum + (LDigit div 10) + (LDigit mod 10);

inc(i);

end;

Result := LSum mod 10;

end;

function Mod10(const Value: string): string;

var

i, intOdd, intEven: Integer;

MyResult : integer;

begin

{add all odd seq numbers}

intOdd := 0;

i := 1;

while (i <= Length(Value)) do

begin

Inc(intOdd, StrToIntDef(Value[i], 0));

Inc(i, 2);

end;

{add all even seq numbers}

intEven := 0;

i := 2;

while (i <= Length(Value)) do

begin

Inc(intEven, StrToIntDef(Value[i], 0));

Inc(i, 2);

end;

MyResult := 3*intOdd + intEven;

{modulus by 10 to get}

MyResult := MyResult mod 10;

if MyResult <> 0 then

MyResult := 10 - MyResult;

Result := IntToStr(MyResult)

end;

Example values:

Result should be 1 : 91843862918438629

Result should be 7 : 270093000000000

Result should be 6 : 310023000000000

Result should be 7 : 000078000000000

918438629184386291

2700930000000007

from this, 2 options

1) I state it just for fun, try all digits from '0'..'9' added to your key and return the one that return true with this function... Ok, I'm not proud of this idea :o)))

2) reverse engineer this function to correct your own algo

I'll give it more time to think about option 2

```
function CheckLUHN(S:String):Boolean;
Var
i,F,V,Sum:integer;
begin
Result:=False;
F:=1;
Sum:=0;
for i:=Length(S) downto 1 do
begin
if Not (S[i] In ['0'..'9']) Then Exit;
V:=F*(Ord(S[i])-Ord('0'));
if V>9 Then V:=(V Mod 10)+1;
Sum:=Sum+V;
F:=3-F;
end;
Result:=(Sum Mod 10)=0;
end;
```

LDigit := LDigit * (1 + (i mod 2)); to

LDigit := LDigit * (i mod 2);

And then add the following line before end

if Result <> 0 Then Result:= 10 - Result;

(which is the same as epasquier did with his algorithm :) )

nope, I think this is correct (appart other error as stated below) :

LDigit := LDigit * (1 + (i mod 2));

i mod 2 can return either 0 or 1, but the algo need to multiply once by 1, once by 2

I haven't checked his algo before, and not entirely even now, but I think his problem was coming from the fact that the LUHN algo started the multiplier (2 or 1) from the END of the string, not from the begining. so :

i := 0;

LSum := 0;

while i < Length(AInputString) do

begin

...

inc(i);

end;

is incorrect, it should be :

i:= Length(AInputString)-1

while i>=0 do

begin

...

// and not use (i mod 2) to jump from 1 or 2 as multiplier, but do something similar of what I did

// which is fix the first value, and switch at each digit using the fact that 3-(1 or 2) will give you (2 or 1)

dec(i);

end;

```
function ValidLUHN(const EntryToValidate : String):Char;
var
TempNdx : Integer;
TempFactor : Integer;
TempCalc : Integer
TempSum : Integer;
TempResult : Boolean;
begin
TempFactor := 2;
TempSum := 0;
TempResult := False;
try
for TempNdx := Length(EntryToValidate) downto 1 do
begin
case EntryToValidate[TempNdx] do
'0'..'9': begin
TempCalc := TempFactor *
(Ord(EntryToValidate[TempNdx]) - Ord('0'));
if (TempCalc > 9)
then begin
TempCalc := (TempCalc Mod 10)+1;
end; {if}
TempSum := TempSum + TempCalc;
TempFactor := 3 - TempFactor;
end
else begin
raise Exception.Create( 'ERROR: Invalid entry; ' + #13#10 +
'Nonnumeric characters are not allowed.');
end;
end; {case}
end;
TempResult := ((TempSum Mod 10) = 0);
finally
Result := TempResult;
end; {try}
end;
```

oops! you are correct, my mod is wrong.

His algorithm (which is on Wikipedia, by the way, under Luhn) does go backwards. The problem is that the alternating doubling starts with the second number from the end ASSUMING the last number is the checksum. So, if you are trying to generate the checksum, you need to start alternating with the last number.

So, the modified mod line, i believe, should be

LDigit := LDigit * (1 + ((i+1) mod 2));

```
function ValidLUHN(const EntryToValidate: String): Boolean;
var
TempStr : string;
TempNdx : Integer;
TempFactor : Integer;
TempCalc : Integer;
TempSum : Integer;
TempResult : Boolean;
begin
TempFactor := 1;
TempNdx := Length(EntryToValidate);
TempSum := 0;
TempStr := ReverseString(EntryToValidate);
TempResult := False;
try
for TempNdx := 1 to Length(EntryToValidate) do
begin
case EntryToValidate[TempNdx] of
'0'..'9': begin
TempCalc := TempFactor *
(Ord(TempStr[TempNdx]) - Ord('0'));
if (TempCalc > 9)
then begin
TempCalc := (TempCalc Mod 10)+1;
end; {if}
TempSum := TempSum + TempCalc;
TempFactor := 3 - TempFactor;
end
else begin
raise Exception.Create( 'ERROR: Invalid character [' +
TempStr[TempNdx] + '] in entry; ' + #13#10 +
'Nonnumeric characters are not allowed.');
end;
end; {case}
end;
TempResult := ((TempSum Mod 10) = 0);
finally
Result := TempResult;
end; {try}
end;
```

Assuming ones know that a

and a minor error : you check EntryToValidate[TempNdx] in your case and use TempStr[TempNdx] in your calc, which is reversed

apart from that, your algo is exactly the same as my first CheckLUHN function (only diff : you raise an error if you encounter an invalid character where I return false), and it's not the question if you read correctly, the problem is CALCULATING the key

Now,

Yes, I

You are correct on the "minor error". That was introduced during a slight modification to allow comparisons between what came in and what was being checked.

Sorry , didn't slept too much these past few days, I'm a bit itchy.

I intended to tell everyone who listen that it exists, not just you. As you said, most developers don't know a CPU can run in reverse mode without sweating more. That's all the more evident with CLI/STI ASM operands that can produce VERY effective results combined with STO/LOD operators in REP/LOOP mode on some algorithm (does it sound nerd ?).

About the maintenance aspect, I prefer give the occasion of some newcomer to learn something rather to stick with basic methods. But I understand and agree that this point of view have its limits.

I'm sure you didn't take it personally ;o)

If I took something like that personally, I would have had to abandon my career several decades ago. ;-)

However, if you use goog coding practices in the process, then the newbies will tend to unconsciously learn those as well as the finer points being illustrated. Also, IMHO, by using more human-consumable naming, it is easier for the newbie to follow.

if by that you refer to the i (standard for index), F (Factor) ,V (Value) variables, I agree that could have been better even if not too difficult to follow seeing the compact code. Thing is, I had this code already for a project, I didn't wrote it specifically for this question. Otherwise I might have done a little effort. On the other end, you might have overkill that necessity with adding Temp in every of your local variable names.

Anyway, it has been a real pleasure to talk about these little difference in coding habits with you.

I appreciate very much the fact that you challenge the point of view, that's very refreshing and valuable in the long run for every one who reads it (even if off-topic)

All Courses

From novice to tech pro — start learning today.

Open in new window