From fad6e496c01b388c87cd063d05d98a64a460a122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 23 May 2024 21:06:33 +0200 Subject: [PATCH 1/5] Removed unintentional WriteLn comments --- UBigInt.pas | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/UBigInt.pas b/UBigInt.pas index 699145f..fd34585 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -568,7 +568,6 @@ end; operator := (const A: Int64): TBigInt; begin Result := TBigInt.FromInt64(A); - //WriteLn(':=a op: ', Result.ToString); end; operator - (const A: TBigInt): TBigInt; @@ -583,8 +582,6 @@ begin end else Result := TBigInt.Zero; - //WriteLn(' a: ', A.ToString); - //WriteLn('-a op: ', Result.ToString); end; operator + (const A, B: TBigInt): TBigInt; @@ -593,9 +590,6 @@ begin Result := TBigInt.AddAbsoluteValues(A, B, A.IsNegative) else Result := TBigInt.SubtractAbsoluteValues(A, B, A.IsNegative); - //WriteLn(' a: ', A.ToString); - //WriteLn(' b: ', B.ToString); - //WriteLn('a+b op: ', Result.ToString); end; operator - (const A, B: TBigInt): TBigInt; @@ -604,9 +598,6 @@ begin Result := TBigInt.SubtractAbsoluteValues(A, B, A.IsNegative) else Result := TBigInt.AddAbsoluteValues(A, B, A.IsNegative); - //WriteLn(' a: ', A.ToString); - //WriteLn(' b: ', B.ToString); - //WriteLn('a-b op: ', Result.ToString); end; operator * (const A, B: TBigInt): TBigInt; @@ -615,9 +606,6 @@ begin Result := TBigInt.Zero else Result := TBigInt.MultiplyAbsoluteValues(A, B, A.IsNegative <> B.IsNegative); - //WriteLn(' a: ', A.ToString); - //WriteLn(' b: ', B.ToString); - //WriteLn('a*b op: ', Result.ToString); end; operator shl(const A: TBigInt; const B: Integer): TBigInt; @@ -670,8 +658,6 @@ begin Result.FIsNegative := A.IsNegative; end; - //WriteLn(' a: ', A.ToString); - //WriteLn('a<< op: ', Result.ToString); end; operator = (const A, B: TBigInt): Boolean; From cfb74da86b00d78d54acf13e931ab86f8731119f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 23 May 2024 21:07:11 +0200 Subject: [PATCH 2/5] Added TBigInt.One --- UBigInt.pas | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UBigInt.pas b/UBigInt.pas index fd34585..cd2fb7c 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -49,6 +49,7 @@ type function CompareToAbsoluteValues(constref AOther: TBigInt): Integer; class function GetZero: TBigInt; static; + class function GetOne: TBigInt; static; // Adds A and B, ignoring their signs and using ReturnNegative instead. The result is // Sign * (Abs(A) + Abs(B)), @@ -76,6 +77,7 @@ type property IsNegative: Boolean read FIsNegative; property Sign: Integer read GetSign; class property Zero: TBigInt read GetZero; + class property One: TBigInt read GetOne; // Returns the index of the most significant bit, i.e. returns integer k, where 2^k is the largest power of 2 that // is less than or equal to the absolute value of the number itself. Returns -1 if the given number is 0. @@ -115,6 +117,7 @@ const CHalfDigitMax = (1 << CHalfBits) - 1; CZero: TBigInt = (FDigits: (0); FIsNegative: False); + COne: TBigInt = (FDigits: (1); FIsNegative: False); { TBigInt } @@ -165,6 +168,11 @@ begin Result := CZero; end; +class function TBigInt.GetOne: TBigInt; +begin + Result := COne; +end; + class function TBigInt.AddAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; var i, j, lenA, lenB, len, shorter: Integer; From 18f432bdfe27abeb46abc7e72c618fd6ccc6c7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 23 May 2024 21:08:01 +0200 Subject: [PATCH 3/5] Added parenthesis in TBigInt.ToString for negative values --- UBigInt.pas | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UBigInt.pas b/UBigInt.pas index cd2fb7c..a672b9a 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -532,7 +532,7 @@ var i: Integer; begin if FIsNegative then - Result := '-' + Result := '(-' else Result := ''; for i := 0 to Length(FDigits) - 2 do @@ -540,6 +540,8 @@ begin Result := Result + IntToStr(FDigits[Length(FDigits) - 1]); for i := 0 to Length(FDigits) - 2 do Result := Result + ')'; + if FIsNegative then + Result := Result + ')' end; class function TBigInt.FromInt64(const AValue: Int64): TBigInt; From 4329041353378e4b88cbad00523ef0ee7104ed08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 23 May 2024 22:00:45 +0200 Subject: [PATCH 4/5] Added TBigInt shift right operator and tests --- UBigInt.pas | 55 ++++++++++++++++++++++++++++ tests/UBigIntTestCases.pas | 74 +++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/UBigInt.pas b/UBigInt.pas index a672b9a..907ec6b 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -99,6 +99,7 @@ type operator - (const A, B: TBigInt): TBigInt; operator * (const A, B: TBigInt): TBigInt; operator shl (const A: TBigInt; const B: Integer): TBigInt; + operator shr (const A: TBigInt; const B: Integer): TBigInt; operator = (const A, B: TBigInt): Boolean; operator <> (const A, B: TBigInt): Boolean; operator < (const A, B: TBigInt): Boolean; @@ -670,6 +671,60 @@ begin end; end; +operator shr(const A: TBigInt; const B: Integer): TBigInt; +var + i, j, digitShifts, bitShifts, reverseShift, len, newLength: Integer; + lastDigit: Cardinal; +begin + // Handles shift of zero. + if A = 0 then + begin + Result := TBigInt.Zero; + Exit; + end; + + // Determines full digit shifts and bit shifts. + DivMod(B, CBitsPerDigit, digitShifts, bitShifts); + + // Handles shift to zero. + if digitShifts >= Length(A.FDigits) then + begin + Result := TBigInt.Zero; + Exit; + end; + + if bitShifts > 0 then + begin + reverseShift := CBitsPerDigit - bitShifts; + len := Length(A.FDigits); + lastDigit := A.FDigits[len - 1] >> bitShifts; + newLength := len - digitShifts; + + if lastDigit = 0 then + SetLength(Result.FDigits, newLength - 1) + else + SetLength(Result.FDigits, newLength); + + // Performs full digit shifts by shifting the access index j for A.FDigits. + j := digitShifts; + for i := 0 to newLength - 2 do + begin + // Performs bit shifts. + Result.FDigits[i] := A.FDigits[j] >> bitShifts; + Inc(j); + Result.FDigits[i] := Result.FDigits[i] or (A.FDigits[j] << reverseShift); + end; + + if lastDigit > 0 then + Result.FDigits[newLength - 1] := lastDigit; + end + else + // Performs full digit shifts by copy if there are no bit shifts. + Result.FDigits := Copy(A.FDigits, digitShifts, Length(A.FDigits) - digitShifts); + + Result.FIsNegative := A.IsNegative; +end; + operator = (const A, B: TBigInt): Boolean; begin Result := A.CompareTo(B) = 0; diff --git a/tests/UBigIntTestCases.pas b/tests/UBigIntTestCases.pas index 2457462..06019b1 100644 --- a/tests/UBigIntTestCases.pas +++ b/tests/UBigIntTestCases.pas @@ -55,7 +55,6 @@ type // TODO: TBigIntConversionTestCase // TODO: TBigIntIncrementDecrementTestCase // TODO: TBigIntQuotientTestCase - // TODO: TBigIntShiftRightTestCase { TBigIntSignTestCase } @@ -210,6 +209,21 @@ type procedure TestZero; end; + { TBigIntShiftRightTestCase } + + TBigIntShiftRightTestCase = class(TTestCase) + private + procedure Test(const AHexValueOperand: string; const AShift: Integer; const AHexValueResult: string); + published + procedure TestShort; + procedure TestShortWithCarry; + procedure TestLongWithCarry; + procedure TestLongWithMultiDigitCarry; + procedure TestWithAlignedDigits; + procedure TestShiftToZero; + procedure TestZero; + end; + { TBigIntEqualityTestCase } TBigIntEqualityTestCase = class(TTestCase) @@ -767,6 +781,63 @@ begin Test('0', 119, '0'); end; +{ TBigIntShiftRightTestCase } + +procedure TBigIntShiftRightTestCase.Test(const AHexValueOperand: string; const AShift: Integer; const AHexValueResult: + string); +var + a, s: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueOperand); + s := TBigInt.FromHexadecimalString(AHexValueResult); + AssertTrue('BigInt from hexadecimal string ''' + AHexValueOperand + ''' shifted right by ''' + IntToStr(AShift) + + ''' was not equal to BigInt from hexadecimal string ''' + AHexValueResult + '''.', + s = (a >> AShift)); +end; + +procedure TBigIntShiftRightTestCase.TestShort; +begin + // BIN 100110101 + // BIN 10011 + Test('135', 4, '13'); +end; + +procedure TBigIntShiftRightTestCase.TestShortWithCarry; +begin + // BIN 1 1101 1010 1110 1001 1000 0111 0000 0000 0000 1111 + // BIN 11 1011 0101 1101 0011 0000 1110 + Test('1DAE987000F', 15, '3B5D30E'); +end; + +procedure TBigIntShiftRightTestCase.TestLongWithCarry; +begin + // BIN 10 0110 0001 0110 0100 0111 1100 1001 1001 1111 0010 1010 1000 1000 1010 0010 0010 1101 1101 + // BIN 100 1100 0010 1100 1000 1111 1001 0011 0011 1110 0101 0101 0001 0001 0100 0100 + Test('261647C99F2A88A22DD', 11, '4C2C8F933E551144'); +end; + +procedure TBigIntShiftRightTestCase.TestLongWithMultiDigitCarry; +begin + Test('647C99F12A088A22FF6DD02187345A3B839401BFB9272', 104, '647C99F12A088A22FF6'); +end; + +// Shifts the left operand by a multiple of the length of full TBigInt digits, so the digits will be shifted, but not +// changed. +procedure TBigIntShiftRightTestCase.TestWithAlignedDigits; +begin + Test('C5E10F0F39000AA2000C020000010000000000000F00000007', 32 * 5, 'C5E10F0F39'); +end; + +procedure TBigIntShiftRightTestCase.TestShiftToZero; +begin + Test('B5D10F0F39882F', 150, '0'); +end; + +procedure TBigIntShiftRightTestCase.TestZero; +begin + Test('0', 3, '0'); +end; + { TBigIntEqualityTestCase } procedure TBigIntEqualityTestCase.TestEqual(const AValue: Int64); @@ -954,6 +1025,7 @@ initialization RegisterTest(TBigIntDifferenceTestCase); RegisterTest(TBigIntProductTestCase); RegisterTest(TBigIntShiftLeftTestCase); + RegisterTest(TBigIntShiftRightTestCase); RegisterTest(TBigIntEqualityTestCase); RegisterTest(TBigIntComparisonTestCase); end. From 2df8266d42dcb2364268ed479d36127e2665383f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 23 May 2024 22:01:25 +0200 Subject: [PATCH 5/5] Added some improvements for TBigInt shift left operator --- UBigInt.pas | 78 +++++++++++++++++++------------------- tests/UBigIntTestCases.pas | 3 +- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/UBigInt.pas b/UBigInt.pas index 907ec6b..421368d 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -626,49 +626,51 @@ var begin // Handles shift of zero. if A = 0 then - Result := TBigInt.Zero - else begin - // Determines full digit shifts and bit shifts. - DivMod(B, CBitsPerDigit, digitShifts, bitShifts); + begin + Result := TBigInt.Zero; + Exit; + end; - if bitShifts > 0 then + // Determines full digit shifts and bit shifts. + DivMod(B, CBitsPerDigit, digitShifts, bitShifts); + + if bitShifts > 0 then + begin + reverseShift := CBitsPerDigit - bitShifts; + len := Length(A.FDigits); + lastDigit := A.FDigits[len - 1] >> reverseShift; + newLength := len + digitShifts; + + if lastDigit = 0 then + SetLength(Result.FDigits, newLength) + else + SetLength(Result.FDigits, newLength + 1); + + // Performs full digit shifts by shifting the access index j for A.FDigits. + Result.FDigits[digitShifts] := A.FDigits[0] << bitShifts; + j := 0; + for i := digitShifts + 1 to newLength - 1 do begin - reverseShift := CBitsPerDigit - bitShifts; - len := Length(A.FDigits); - lastDigit := A.FDigits[len - 1] >> reverseShift; - newLength := len + digitShifts; - - if lastDigit = 0 then - SetLength(Result.FDigits, newLength) - else - SetLength(Result.FDigits, newLength + 1); - - // Performs full digit shifts by shifting the access index j for A.FDigits. - Result.FDigits[digitShifts] := A.FDigits[0] << bitShifts; - j := 0; - for i := digitShifts + 1 to newLength - 1 do - begin - // Performs bit shifts. - Result.FDigits[i] := A.FDigits[j] >> reverseShift; - Inc(j); - Result.FDigits[i] := Result.FDigits[i] or (A.FDigits[j] << bitShifts); - end; - - if Length(Result.FDigits) > newLength then - Result.FDigits[newLength] := lastDigit; - end - else begin - // Performs full digit shifts by copy if there are no bit shifts. - len := Length(A.FDigits); - SetLength(Result.FDigits, len + digitShifts); - for i := 0 to digitShifts - 1 do - Result.FDigits[i] := 0; - for i := 0 to len - 1 do - Result.FDigits[i + digitShifts] := A.FDigits[i]; + // Performs bit shifts. + Result.FDigits[i] := A.FDigits[j] >> reverseShift; + Inc(j); + Result.FDigits[i] := Result.FDigits[i] or (A.FDigits[j] << bitShifts); end; - Result.FIsNegative := A.IsNegative; + if lastDigit > 0 then + Result.FDigits[newLength] := lastDigit; + end + else begin + // Performs full digit shifts by copy if there are no bit shifts. + len := Length(A.FDigits); + SetLength(Result.FDigits, len + digitShifts); + for i := 0 to digitShifts - 1 do + Result.FDigits[i] := 0; + for i := 0 to len - 1 do + Result.FDigits[i + digitShifts] := A.FDigits[i]; end; + + Result.FIsNegative := A.IsNegative; end; operator shr(const A: TBigInt; const B: Integer): TBigInt; diff --git a/tests/UBigIntTestCases.pas b/tests/UBigIntTestCases.pas index 06019b1..9639ac6 100644 --- a/tests/UBigIntTestCases.pas +++ b/tests/UBigIntTestCases.pas @@ -732,7 +732,8 @@ end; { TBigIntShiftLeftTestCase } -procedure TBigIntShiftLeftTestCase.Test(const AHexValueOperand: string; const AShift: Integer; const AHexValueResult: string); +procedure TBigIntShiftLeftTestCase.Test(const AHexValueOperand: string; const AShift: Integer; const AHexValueResult: + string); var a, s: TBigInt; begin