diff --git a/UBigInt.pas b/UBigInt.pas index 699145f..421368d 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. @@ -97,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; @@ -115,6 +118,7 @@ const CHalfDigitMax = (1 << CHalfBits) - 1; CZero: TBigInt = (FDigits: (0); FIsNegative: False); + COne: TBigInt = (FDigits: (1); FIsNegative: False); { TBigInt } @@ -165,6 +169,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; @@ -524,7 +533,7 @@ var i: Integer; begin if FIsNegative then - Result := '-' + Result := '(-' else Result := ''; for i := 0 to Length(FDigits) - 2 do @@ -532,6 +541,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; @@ -568,7 +579,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 +593,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 +601,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 +609,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 +617,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; @@ -627,51 +626,105 @@ 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; - //WriteLn(' a: ', A.ToString); - //WriteLn('a<< op: ', Result.ToString); + + Result.FIsNegative := A.IsNegative; +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; diff --git a/tests/UBigIntTestCases.pas b/tests/UBigIntTestCases.pas index 2457462..9639ac6 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) @@ -718,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 @@ -767,6 +782,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 +1026,7 @@ initialization RegisterTest(TBigIntDifferenceTestCase); RegisterTest(TBigIntProductTestCase); RegisterTest(TBigIntShiftLeftTestCase); + RegisterTest(TBigIntShiftRightTestCase); RegisterTest(TBigIntEqualityTestCase); RegisterTest(TBigIntComparisonTestCase); end.