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] 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.