diff --git a/UBigInt.pas b/UBigInt.pas index 56e48f9..a49ea16 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -35,6 +35,8 @@ type FDigits: TDigits; FIsNegative: Boolean; + function GetSign: Integer; + // Copies consecutive digits from this BigInt to create a new one. The result will be positive. Leading zeros are // removed from the result, but AIndex + ACount must not exceed the number of digits of this BigInt. // AIndex is the first (least significant) digit to be taken. The digit with this index will become the 0th digit of @@ -66,11 +68,19 @@ type // Sign * (Abs(a) * Abs(b)) // where Sign is 1 for ReturnNegative = False and -1 otherwise. class function MultiplyAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; static; + + class function FromHexOrBinString(const AValue: string; const AFromBase: Integer): TBigInt; static; + class function ConvertDigitBlock(const AValue: string; var AStartIndex: Integer; const ACharBlockSize, AFromBase: + Integer): Cardinal; public property IsNegative: Boolean read FIsNegative; + property Sign: Integer read GetSign; class property Zero: TBigInt read GetZero; function CompareTo(constref AOther: TBigInt): Integer; + function TryToInt64(out AOutput: Int64): Boolean; class function FromInt64(const AValue: Int64): TBigInt; static; + class function FromHexadecimalString(const AValue: string): TBigInt; static; + class function FromBinaryString(const AValue: string): TBigInt; static; end; { Operators } @@ -83,6 +93,10 @@ type operator shl (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; + operator <= (const A, B: TBigInt): Boolean; + operator > (const A, B: TBigInt): Boolean; + operator >= (const A, B: TBigInt): Boolean; implementation @@ -98,6 +112,16 @@ const { TBigInt } +function TBigInt.GetSign: Integer; +begin + if FIsNegative then + Result := -1 + else if (Length(FDigits) > 1) or (FDigits[0] <> 0) then + Result := 1 + else + Result := 0; +end; + function TBigInt.GetSegment(const AIndex, ACount: Integer): TBigInt; var trimmedCount: Integer; @@ -105,8 +129,7 @@ begin trimmedCount := ACount; while (trimmedCount > 1) and (FDigits[AIndex + trimmedCount - 1] = 0) do Dec(trimmedCount); - SetLength(Result.FDigits, trimmedCount); - Move(FDigits[AIndex], Result.FDigits[0], CDigitSize * trimmedCount); + Result.FDigits := Copy(FDigits, AIndex, trimmedCount); Result.FIsNegative := False; end; @@ -138,7 +161,7 @@ end; class function TBigInt.AddAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; var - i, lenA, lenB, len, shorter: Integer; + i, j, lenA, lenB, len, shorter: Integer; carry: Cardinal; begin lenA := Length(AA.FDigits); @@ -185,9 +208,11 @@ begin // carry-overs. This avoids additional tests for finding the shorter digit array. if (i < lenA) or (i < lenB) then if lenA >= lenB then - Move(AA.FDigits[i], Result.FDigits[i], CDigitSize * (len - i)) + for j := i to len - 1 do + Result.FDigits[j] := AA.FDigits[j] else - Move(AB.FDigits[i], Result.FDigits[i], CDigitSize * (len - i)); + for j := i to len - 1 do + Result.FDigits[j] := AB.FDigits[j]; // Applies the remaining carry-overs until the end of the prepared result digit array. while (carry > 0) and (i < len) do @@ -212,7 +237,7 @@ class function TBigInt.SubtractAbsoluteValues(constref AA, AB: TBigInt; const AR var a, b: TBigInt; carry: Cardinal; - i, lastNonZeroDigitIndex, len: Integer; + i, j, lastNonZeroDigitIndex, len: Integer; begin // Establishes the operand order, such that Abs(a) is not less than Abs(b). if (AA.CompareToAbsoluteValues(AB) >= 0) then @@ -277,7 +302,8 @@ begin // Copies the missing unchanged digits from the longer operand to the result, if any. If there are none, then no trim // needs to occur because the most significant digit is not zero. if i < len then - Move(a.FDigits[i], Result.FDigits[i], CDigitSize * (len - i)) + for j := i to len - 1 do + Result.FDigits[j] := a.FDigits[j] else if (lastNonZeroDigitIndex + 1 < len) then // Trims leading zeros from the digits array. Delete(Result.FDigits, lastNonZeroDigitIndex + 1, len - lastNonZeroDigitIndex - 1); @@ -362,6 +388,70 @@ begin end; end; +class function TBigInt.FromHexOrBinString(const AValue: string; const AFromBase: Integer): TBigInt; +var + charBlockSize, offset, i, j, k, remainder: Integer; + d: Cardinal; +begin + // 2 ^ (32 / charBlockSize) = AFromBase + case AFromBase of + 2: charBlockSize := 32; + 16: charBlockSize := 8; + end; + + if AValue[1] = '-' then + begin + offset := 2; + Result.FIsNegative := True; + end + else begin + offset := 1; + Result.FIsNegative := False; + end; + + // Calculates the first (most significant) digit d of the result. + DivMod(AValue.Length - offset + 1, charBlockSize, i, remainder); + k := offset; + d := 0; + // Checks the first block of chars that is not a full block. + if remainder > 0 then + d := ConvertDigitBlock(AValue, k, remainder, AFromBase); + // Checks full blocks of chars for first digit. + while (d = 0) and (i > 0) do + begin + Dec(i); + d := ConvertDigitBlock(AValue, k, charBlockSize, AFromBase); + end; + + // Checks for zero. + if (d = 0) and (i = 0) then + Result := Zero + else begin + // Initializes the array of digits. + SetLength(Result.FDigits, i + 1); + Result.FDigits[i] := d; + + // Calculates the other digits. + for j := i - 1 downto 0 do + Result.FDigits[j] := ConvertDigitBlock(AValue, k, charBlockSize, AFromBase); + end; +end; + +class function TBigInt.ConvertDigitBlock(const AValue: string; var AStartIndex: Integer; const ACharBlockSize, + AFromBase: Integer): Cardinal; +var + part: string; +begin + part := Copy(AValue, AStartIndex, ACharBlockSize); + Inc(AStartIndex, ACharBlockSize); + case AFromBase of + 2: part := '%' + part; + 8: part := '&' + part; + 16: part := '$' + part; + end; + Result := StrToDWord(part); +end; + function TBigInt.CompareTo(constref AOther: TBigInt): Integer; begin if FIsNegative = AOther.FIsNegative then @@ -372,6 +462,35 @@ begin Result := -Result; end; +function TBigInt.TryToInt64(out AOutput: Int64): Boolean; +begin + AOutput := 0; + Result := False; + case Length(FDigits) of + 0: Result := True; + 1: begin + AOutput := FDigits[0]; + if FIsNegative then + AOutput := -AOutput; + Result := True; + end; + 2: begin + if FDigits[1] <= Integer.MaxValue then + begin + AOutput := FDigits[1] * CBase + FDigits[0]; + if FIsNegative then + AOutput := -AOutput; + Result := True; + end + else if (FDigits[1] = Integer.MaxValue + 1) and (FDigits[0] = 0) and FIsNegative then + begin + AOutput := Int64.MinValue; + Result := True; + end; + end; + end; +end; + class function TBigInt.FromInt64(const AValue: Int64): TBigInt; var absVal: Int64; @@ -391,6 +510,16 @@ begin end; end; +class function TBigInt.FromHexadecimalString(const AValue: string): TBigInt; +begin + Result := FromHexOrBinString(AValue, 16); +end; + +class function TBigInt.FromBinaryString(const AValue: string): TBigInt; +begin + Result := FromHexOrBinString(AValue, 2); +end; + { Operators } operator := (const A: Int64): TBigInt; @@ -403,9 +532,13 @@ var len: Integer; begin len := Length(A.FDigits); - SetLength(Result.FDigits, len); - Move(A.FDigits[0], Result.FDigits[0], len); - Result.FIsNegative := not A.FIsNegative; + if (len > 1) or (A.FDigits[0] > 0) then + begin + Result.FDigits := Copy(A.FDigits, 0, len); + Result.FIsNegative := not A.FIsNegative; + end + else + Result := TBigInt.Zero; end; operator + (const A, B: TBigInt): TBigInt; @@ -475,7 +608,10 @@ begin // Performs full digit shifts by copy if there are no bit shifts. len := Length(A.FDigits); SetLength(Result.FDigits, len + digitShifts); - Move(A.FDigits[0], Result.FDigits[digitShifts], CDigitSize * len); + 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; @@ -492,5 +628,25 @@ begin Result := A.CompareTo(B) <> 0; end; +operator < (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) < 0; +end; + +operator <= (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) <= 0; +end; + +operator > (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) > 0; +end; + +operator >= (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) >= 0; +end; + end. diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi index 6d98efa..1d0b455 100644 --- a/tests/AdventOfCodeFPCUnit.lpi +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -144,10 +144,18 @@ + + + + + + + + diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr index eddfbea..27320a1 100644 --- a/tests/AdventOfCodeFPCUnit.lpr +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -9,7 +9,7 @@ uses UHotSpringsTestCases, UPointOfIncidenceTestCases, UParabolicReflectorDishTestCases, ULensLibraryTestCases, UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases, UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases, - UNeverTellMeTheOddsTestCases, UPolynomialTestCases, UPolynomialRootsTestCases; + UNeverTellMeTheOddsTestCases, UBigIntTestCases, UPolynomialTestCases, UPolynomialRootsTestCases; {$R *.res} diff --git a/tests/UBigIntTestCases.pas b/tests/UBigIntTestCases.pas new file mode 100644 index 0000000..4cba54d --- /dev/null +++ b/tests/UBigIntTestCases.pas @@ -0,0 +1,917 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2022-2024 Stefan Müller + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . +} + +unit UBigIntTestCases; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, UBigInt; + +type + // TODO: TBigIntAbsTestCase + // TODO: TBigIntFromDecTestCase + // TestCase('80091110002223289436355210101231765016693209176113648', 10, '80091110002223289436355210101231765016693209176113648'); + // TestCase('-3201281944789146858325672846129872035678639439423746384198412894', 10, '-3201281944789146858325672846129872035678639439423746384198412894'); + // TestCase('000000000000000000000000000000000000000080091', 10, '80091'); + // TestCase('0000000000000000000000000000000000000000000000', 10, '0'); + // TestCase('-000000000000000000000000000000004687616873215464763146843215486643215', 10, '-4687616873215464763146843215486643215'); + // TestTryFromDec + // TODO: TBigIntFromOctTestCase + // OctValue, 8, DecValue: + // TestCase('3132521', 8, '832849'); + // TestCase('772350021110002226514', 8, '9123449598017875276'); + // TestCase('-247515726712721', 8, '-11520970560977'); + // TestCase('000000000000000000000000000000000000000072', 8, '58'); + // TestCase('000000000000000000000000000000000000000000000000000000000', 8, '0'); + // OctValue, HexValue: + // TestCase('7440737076307076026772', '3C83BE3E638F82DFA'); + // TestCase('1336625076203401614325664', '16F6547C8380E31ABB4'); + // TestCase('3254410316166274457257121050201413373225', '6AC84338ECBC97ABCA22840C2DF695'); + // TestCase('1441330072562106272767270117307274151276215', '642D81D5C88CBAFBAE09EC75E1A57C8D'); + // TestCase('3533665174054677131163436413756431236774257271133', '3ADED4F82CDF964E71E85FBA329EFE2BD725B'); + // TestCase('57346415317074055631627141161054073014006076155710653', '5EE686B3C782DCCE5CC271160EC18061F1B791AB'); + // TestCase('000000000000000000000000000000245745251354033134', 8, '5838773085550172'); + // TestTryFromOct + // TODO: TBigIntUnaryPlusTestCase + // TODO: TBigIntBitwiseLogicalTestCase + // TODO: TBigIntComplementTestCase + // TODO: TBigIntConversionTestCase + // TODO: TBigIntIncrementDecrementTestCase + // TODO: TBigIntQuotientTestCase + // TODO: TBigIntShiftRightTestCase + + { TBigIntSignTestCase } + + TBigIntSignTestCase = class(TTestCase) + private + procedure Test(const AHexValue: string; const AExpectedSign: Integer); + published + procedure TestZero; + procedure TestShortPositive; + procedure TestShortNegative; + procedure TestLongPositive; + procedure TestLongNegative; + end; + + { TBigIntFromInt64TestCase } + + TBigIntFromInt64TestCase = class(TTestCase) + private + procedure Test(const AValue: Int64); + published + procedure TestShortPositive; + procedure TestShortNegative; + procedure TestLongPositive; + procedure TestLongNegative; + procedure TestZero; + end; + + { TBigIntFromHexTestCase } + + TBigIntFromHexTestCase = class(TTestCase) + private + procedure TestShort(const AHexValue: string; const ADecValue: Int64); + published + procedure TestPositive; + procedure TestNegative; + procedure TestZero; + procedure TestLeadingZeros; + // TODO: TestTryFromHex + end; + + { TBigIntFromBinTestCase } + + TBigIntFromBinTestCase = class(TTestCase) + private + procedure TestShort(const ABinValue: string; const ADecValue: Int64); + published + procedure TestPositive; + procedure TestNegative; + procedure TestLeadingZeros; + // TODO: TestTryFromBin + end; + + { TBigIntUnaryMinusTestCase } + + TBigIntUnaryMinusTestCase = class(TTestCase) + private + procedure Test(const AValue: Int64); + procedure TestTwice(const AValue: Int64); + published + procedure TestZero; + procedure TestPositive; + procedure TestNegative; + procedure TestPositiveTwice; + procedure TestNegativeTwice; + end; + + { TBigIntSumTestCase } + + TBigIntSumTestCase = class(TTestCase) + private + procedure Test(const AHexValueLeft, AHexValueRight, AHexValueSum: string); + published + procedure TestShort; + procedure TestPositivePlusPositive; + procedure TestNegativePlusNegative; + procedure TestLargePositivePlusSmallNegative; + procedure TestSmallPositivePlusLargeNegative; + procedure TestLargeNegativePlusSmallPositive; + procedure TestSmallNegativePlusLargePositive; + procedure TestZeroPlusPositive; + procedure TestPositivePlusZero; + procedure TestZero; + procedure TestSumShorterLeft; + procedure TestSumShorterRight; + procedure TestSumEndsInCarry; + procedure TestSumEndsInCarryNewDigit; + procedure TestSumMultiCarry; + end; + + { TBigIntDifferenceTestCase } + + TBigIntDifferenceTestCase = class(TTestCase) + private + procedure Test(const AHexValueMinuend, AHexValueSubtrahend, AHexValueDifference: string); + published + procedure TestShort; + procedure TestLargePositiveMinusSmallPositive; + procedure TestSmallPositiveMinusLargePositive; + procedure TestLargeNegativeMinusSmallNegative; + procedure TestSmallNegativeMinusLargeNegative; + procedure TestNegativeMinusPositive; + procedure TestPositiveMinusNegative; + procedure TestLargeOperands; + procedure TestPositiveMinusZero; + procedure TestZeroMinusPositive; + procedure TestZero; + procedure TestDifferenceShorterLeft; + procedure TestDifferenceShorterRight; + procedure TestDifferenceEndsInCarry; + procedure TestDifferenceEndsInCarryLosingDigit; + procedure TestDifferenceMultiCarry; + end; + + { TBigIntProductTestCase } + + TBigIntProductTestCase = class(TTestCase) + private + procedure Test(const AHexValueLeft, AHexValueRight, AHexValueProduct: string); + published + procedure TestShort; + procedure TestLongPositiveTimesPositive; + procedure TestLongPositiveTimesNegative; + procedure TestLongNegativeTimesPositive; + procedure TestLongNegativeTimesNegative; + procedure TestZeroTimesPositive; + procedure TestZeroTimesNegative; + procedure TestZero; + end; + + { TBigIntShiftLeftTestCase } + + TBigIntShiftLeftTestCase = 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 TestZero; + end; + + { TBigIntEqualityTestCase } + + TBigIntEqualityTestCase = class(TTestCase) + private + procedure TestEqual(const AValue: Int64); + procedure TestEqualHex(const AHexValue: string); + procedure TestNotEqualHex(const AHexValueLeft, AHexValueRight: string); + published + procedure TestShortEqual; + procedure TestLongEqual; + procedure TestZeroEqual; + procedure TestShortNotEqual; + procedure TestLongNotEqualSign; + procedure TestZeroNotEqual; + end; + + { TBigIntComparisonTestCase } + + TBigIntComparisonTestCase = class(TTestCase) + private + procedure TestLessThan(const AHexValueLeft, AHexValueRight: string); + procedure TestGreaterThan(const AHexValueLeft, AHexValueRight: string); + procedure TestEqual(const AHexValue: string); + published + procedure TestLessSameLength; + procedure TestLessShorterLeft; + procedure TestLessNegativeShorterRight; + procedure TestGreaterSameLength; + procedure TestGreaterShorterRight; + procedure TestGreaterNegativeShorterLeft; + procedure TestEqualPositive; + procedure TestEqualNegative; + procedure TestEqualZero; + end; + +implementation + +{ TBigIntSignTestCase } + +procedure TBigIntSignTestCase.Test(const AHexValue: string; const AExpectedSign: Integer); +var + a: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValue); + AssertEquals(AExpectedSign, a.Sign); +end; + +procedure TBigIntSignTestCase.TestZero; +begin + Test('0', 0); +end; + +procedure TBigIntSignTestCase.TestShortPositive; +begin + Test('36A', 1); +end; + +procedure TBigIntSignTestCase.TestShortNegative; +begin + Test('-29B7', -1); +end; + +procedure TBigIntSignTestCase.TestLongPositive; +begin + Test('1240168AB09ABDF8283B77124', 1); +end; + +procedure TBigIntSignTestCase.TestLongNegative; +begin + Test('-192648F1305DD04757A24C873F29F60B6184B5', -1); +end; + +{ TBigIntFromInt64TestCase } + +procedure TBigIntFromInt64TestCase.Test(const AValue: Int64); +var + a: TBigInt; + act: Int64; +begin + a := TBigInt.FromInt64(AValue); + AssertTrue('BigInt from ''' + IntToStr(AValue) + ''' could not be converted back to an Int64 value.', + a.TryToInt64(act)); + AssertEquals('BigInt from ''' + IntToStr(AValue) + ''' converted back to Int64 was not equal to initial value.', + AValue, act); +end; + +procedure TBigIntFromInt64TestCase.TestShortPositive; +begin + Test(17); + Test(4864321); + Test(Cardinal.MaxValue); +end; + +procedure TBigIntFromInt64TestCase.TestShortNegative; +begin + Test(-54876); + Test(Integer.MinValue); +end; + +procedure TBigIntFromInt64TestCase.TestLongPositive; +begin + Test(5800754643214654); + Test(Int64.MaxValue); +end; + +procedure TBigIntFromInt64TestCase.TestLongNegative; +begin + Test(-94136445555883); + Test(Int64.MinValue); +end; + +procedure TBigIntFromInt64TestCase.TestZero; +begin + Test(0); +end; + +{ TBigIntFromHexTestCase } + +procedure TBigIntFromHexTestCase.TestShort(const AHexValue: string; const ADecValue: Int64); +var + a, b: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValue); + b := TBigInt.FromInt64(ADecValue); + AssertTrue('BigInt from hexadecimal string ''' + AHexValue + ''' was not equal to ''' + IntToStr(ADecValue) + '''.', + a = b); +end; + +procedure TBigIntFromHexTestCase.TestPositive; +begin + TestShort('91', 145); + TestShort('800E111000222', 2252766466540066); + TestShort('DE0B227802AB233', 999995000000000563); + TestShort('19C0048155000', 453000000000000); + TestShort('3B9ACA00', 1000000000); +end; + +procedure TBigIntFromHexTestCase.TestNegative; +begin + TestShort('-47', -71); + TestShort('-800E111000222', -2252766466540066); + TestShort('-4512FF3', -72429555); +end; + +procedure TBigIntFromHexTestCase.TestZero; +begin + TestShort('0', 0); + TestShort('-0', 0); +end; + +procedure TBigIntFromHexTestCase.TestLeadingZeros; +begin + TestShort('000000000000000000000000000000004B', 75); + TestShort('-00000000000000000000000000000000A2', -162); + TestShort('00000000000000000000000000000000FF452849DB01', 280672493755137); + TestShort('000000000000000000000000000000000000', 0); + TestShort('-000000000000000000000000000000000000', 0); +end; + +{ TBigIntFromBinTestCase } + +procedure TBigIntFromBinTestCase.TestShort(const ABinValue: string; const ADecValue: Int64); +var + a, b: TBigInt; +begin + a := TBigInt.FromBinaryString(ABinValue); + b := TBigInt.FromInt64(ADecValue); + AssertTrue('BigInt from binary string ''' + ABinValue + ''' was not equal to ''' + IntToStr(ADecValue) + '''.', + a = b); +end; + +procedure TBigIntFromBinTestCase.TestPositive; +begin + TestShort('110101010101101101010010110000101010100101001001010100110', 120109162101379750); +end; + +procedure TBigIntFromBinTestCase.TestNegative; +begin + TestShort('-11100100111010100111000111100011110000100110110111100101010010', -4123780452057839954); +end; + +procedure TBigIntFromBinTestCase.TestLeadingZeros; +begin + TestShort('0000000000000000000000000000000000000000000000000000000000000000000000111', 7); + TestShort('0000000000000000000000000000000000000000000000000000000000000000000000000000000', 0); +end; + +{ TBigIntUnaryMinusTestCase } + +procedure TBigIntUnaryMinusTestCase.Test(const AValue: Int64); +var + a, b: TBigInt; +begin + a := TBigInt.FromInt64(AValue); + b := TBigInt.FromInt64(-AValue); + AssertTrue('Negative BigInt from ''' + IntToStr(AValue) + ''' was not equal to the BigInt from the negative value.', + b = -a); +end; + +procedure TBigIntUnaryMinusTestCase.TestTwice(const AValue: Int64); +var + a: TBigInt; +begin + a := TBigInt.FromInt64(AValue); + AssertTrue('BigInt from ''' + IntToStr(AValue) + '''was not equal to the double negative of itself.', + a = -(-a)); +end; + +procedure TBigIntUnaryMinusTestCase.TestZero; +begin + Test(0); +end; + +procedure TBigIntUnaryMinusTestCase.TestPositive; +begin + Test(234972358233); +end; + +procedure TBigIntUnaryMinusTestCase.TestNegative; +begin + Test(-999214927400); +end; + +procedure TBigIntUnaryMinusTestCase.TestPositiveTwice; +begin + TestTwice(8647613456601); +end; + +procedure TBigIntUnaryMinusTestCase.TestNegativeTwice; +begin + TestTwice(-38600421308534); +end; + +{ TBigIntSumTestCase } + +procedure TBigIntSumTestCase.Test(const AHexValueLeft, AHexValueRight, AHexValueSum: string); +var + a, b, s: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueLeft); + b := TBigInt.FromHexadecimalString(AHexValueRight); + s := TBigInt.FromHexadecimalString(AHexValueSum); + AssertTrue('Sum of BigInt from ''' + AHexValueLeft + ''' and from ''' + AHexValueRight + + ''' was not equal to the BigInt from ''' + AHexValueSum + '''.', + s = a + b); +end; + +procedure TBigIntSumTestCase.TestShort; +begin + Test('5', '7', 'C'); +end; + +procedure TBigIntSumTestCase.TestPositivePlusPositive; +begin + Test('7000822000111', '9000911000333', '10001133000444'); +end; + +procedure TBigIntSumTestCase.TestNegativePlusNegative; +begin + Test('-129B92A84643D608141', '-4574DBA206951ECFE', '-12E10783E84A6B26E3F'); +end; + +procedure TBigIntSumTestCase.TestLargePositivePlusSmallNegative; +begin + Test('87BAD26984', '-8DCB20461', '7EDE206523'); +end; + +procedure TBigIntSumTestCase.TestSmallPositivePlusLargeNegative; +begin + Test('A58301E4006', '-9851DA0FD433', '-8DF9A9F1942D'); +end; + +procedure TBigIntSumTestCase.TestLargeNegativePlusSmallPositive; +begin + Test('-1FDB60CB5698870', '99CB1E00DE', '-1FDB572EA4B8792'); +end; + +procedure TBigIntSumTestCase.TestSmallNegativePlusLargePositive; +begin + Test('-1ED598BBFEC2', '59CD4F02ECB56', '57DFF5772CC94'); +end; + +procedure TBigIntSumTestCase.TestZeroPlusPositive; +begin + Test('0', '9BB000911FF5A000333', '9BB000911FF5A000333'); +end; + +procedure TBigIntSumTestCase.TestPositivePlusZero; +begin + Test('23009605A16BCBB294A1FD', '0', '23009605A16BCBB294A1FD'); +end; + +procedure TBigIntSumTestCase.TestZero; +begin + Test('0', '0', '0'); +end; + +procedure TBigIntSumTestCase.TestSumShorterLeft; +begin + Test('3FFFF', '9000911000222', '9000911040221'); +end; + +procedure TBigIntSumTestCase.TestSumShorterRight; +begin + Test('9000911000555', '3000EEEE', '900094100F443'); +end; + +procedure TBigIntSumTestCase.TestSumEndsInCarry; +begin + Test('4000444000220', 'FFFFFFFF', '400054400021F'); +end; + +procedure TBigIntSumTestCase.TestSumEndsInCarryNewDigit; +begin + Test('99990000', '99990055', '133320055'); +end; + +procedure TBigIntSumTestCase.TestSumMultiCarry; +begin + Test('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'FFFFFFFF', '1000000000000000000000000FFFFFFFE'); +end; + +{ TBigIntDifferenceTestCase } + +procedure TBigIntDifferenceTestCase.Test(const AHexValueMinuend, AHexValueSubtrahend, AHexValueDifference: string); +var + a, b, s: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueMinuend); + b := TBigInt.FromHexadecimalString(AHexValueSubtrahend); + s := TBigInt.FromHexadecimalString(AHexValueDifference); + AssertTrue('Difference of BigInt from ''' + AHexValueMinuend + ''' and from ''' + AHexValueSubtrahend + + ''' was not equal to the BigInt from ''' + AHexValueDifference + '''.', + s = a - b); +end; + +procedure TBigIntDifferenceTestCase.TestShort; +begin + Test('230', '6D', '1C3'); +end; + +procedure TBigIntDifferenceTestCase.TestLargePositiveMinusSmallPositive; +begin + Test('910AAD86E5002455', '72DE020F932A1', '91037FA6C406F1B4'); +end; + +procedure TBigIntDifferenceTestCase.TestSmallPositiveMinusLargePositive; +begin + Test('3127541F4C0AA', '3818CD9FBE652B', '-3506585DC9A481'); +end; + +procedure TBigIntDifferenceTestCase.TestLargeNegativeMinusSmallNegative; +begin + Test('-B12BE1E20098', '-148F3137CED396', '13DE0555ECD2FE'); +end; + +procedure TBigIntDifferenceTestCase.TestSmallNegativeMinusLargeNegative; +begin + Test('-AF3FF1EC626908C', '-18295', '-AF3FF1EC6250DF7'); +end; + +procedure TBigIntDifferenceTestCase.TestNegativeMinusPositive; +begin + Test('-E493506B19', '20508ED255', '-104E3DF3D6E'); +end; + +procedure TBigIntDifferenceTestCase.TestPositiveMinusNegative; +begin + Test('114EEC66851AFD98', '-100AA4308C5249FBBFADEB89CD6A7D9CC', '100AA4308C5249FBC0C2DA5035BC2D764'); +end; + +procedure TBigIntDifferenceTestCase.TestLargeOperands; +begin + Test('1069FC8EA3D99C39E1C07AA078B77B5CC679CB448563345A878C603D32FB0F8FEFE02AD9574A5EB6', + '1069FC8EA3D99C39E1C07AA078B77B5CC679CB448563345A878C603D32FB0F8FEFE023C522B87F8C', + '7143491DF2A'); +end; + +procedure TBigIntDifferenceTestCase.TestPositiveMinusZero; +begin + Test('8ABB000911FF5A000333', '0', '8ABB000911FF5A000333'); +end; + +procedure TBigIntDifferenceTestCase.TestZeroMinusPositive; +begin + Test('0', '243961982DDD64F81B2', '-243961982DDD64F81B2'); +end; + +procedure TBigIntDifferenceTestCase.TestZero; +begin + Test('0', '0', '0'); +end; + +procedure TBigIntDifferenceTestCase.TestDifferenceShorterLeft; +begin + Test('3FFFF', '9000911040221', '-9000911000222'); +end; + +procedure TBigIntDifferenceTestCase.TestDifferenceShorterRight; +begin + Test('900094100F443', '3000EEEE', '9000911000555'); +end; + +procedure TBigIntDifferenceTestCase.TestDifferenceEndsInCarry; +begin + Test('400054400021F', 'FFFFFFFF', '4000444000220'); +end; + +procedure TBigIntDifferenceTestCase.TestDifferenceEndsInCarryLosingDigit; +begin + Test('133320055', '99990000', '99990055'); +end; + +procedure TBigIntDifferenceTestCase.TestDifferenceMultiCarry; +begin + Test('1000000000000000000000000FFFFFFFE', 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'FFFFFFFF'); +end; + +{ TBigIntProductTestCase } + +procedure TBigIntProductTestCase.Test(const AHexValueLeft, AHexValueRight, AHexValueProduct: string); +var + a, b, s: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueLeft); + b := TBigInt.FromHexadecimalString(AHexValueRight); + s := TBigInt.FromHexadecimalString(AHexValueProduct); + AssertTrue('Product of BigInt from ''' + AHexValueLeft + ''' and from ''' + AHexValueRight + + ''' was not equal to the BigInt from ''' + AHexValueProduct + '''.', + s = a * b); +end; + +procedure TBigIntProductTestCase.TestShort; +begin + Test('9', 'B', '63'); + Test('29E531A', 'DF5F299', '248E3915103E8A'); +end; + +procedure TBigIntProductTestCase.TestLongPositiveTimesPositive; +begin + Test('FFFFFFFF', 'FFFFFFFF', 'FFFFFFFE00000001'); + Test('4873B23699D07741F1544862221B1966AA84512', '4DE0013', '160A322C656DEB1DD6D721D36E35F2E29B4D2A18192056'); + Test('74FD3E6988116762', '22DB271AFC4941', 'FEDC8CD51DEE46BE83C283B5E31E2'); +end; + +procedure TBigIntProductTestCase.TestLongPositiveTimesNegative; +begin + Test('23401834190D12FF3210F0B0129123AA', '-A4C0530234', '-16AF8B019CA1436BBFD1F1FB08494FFC9EF7E09288'); +end; + +procedure TBigIntProductTestCase.TestLongNegativeTimesPositive; +begin + Test('-3ACB78882923810', 'F490B8022A4BCBFF34E01', '-382B2B9851BC93CB0308B502C3B036D71810'); +end; + +procedure TBigIntProductTestCase.TestLongNegativeTimesNegative; +begin + Test('-554923FB201', '-9834FDC032', '32B514C1BA1E774EE8432'); +end; + +procedure TBigIntProductTestCase.TestZeroTimesPositive; +begin + Test('0', '1AF5D0039B888AC00F299', '0'); +end; + +procedure TBigIntProductTestCase.TestZeroTimesNegative; +begin + Test('0', '-1AF5D0039B888AC00F299', '0'); +end; + +procedure TBigIntProductTestCase.TestZero; +begin + Test('0', '0', '0'); +end; + +{ TBigIntShiftLeftTestCase } + +procedure TBigIntShiftLeftTestCase.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 left by ''' + IntToStr(AShift) + + ''' was not equal to BigInt from hexadecimal string ''' + AHexValueResult + '''.', + s = (a << AShift)); +end; + +procedure TBigIntShiftLeftTestCase.TestShort; +begin + // BIN 101 + // BIN 101000 + Test('5', 3, '28'); +end; + +procedure TBigIntShiftLeftTestCase.TestShortWithCarry; +begin + // BIN 1110 1101 0111 0001 1010 1010 + // BIN 1 1101 1010 1110 0011 0101 0100 0000 0000 0000 + Test('ED71AA', 13, '1DAE354000'); +end; + +procedure TBigIntShiftLeftTestCase.TestLongWithCarry; +begin + // BIN 1 0011 0000 1011 0010 0011 1110 0100 1100 1111 1001 0101 0100 0100 0101 + // BIN 10 0110 0001 0110 0100 0111 1100 1001 1001 1111 0010 1010 1000 1000 1010 0000 0000 + Test('130B23E4CF95445', 9, '261647C99F2A88A00'); +end; + +procedure TBigIntShiftLeftTestCase.TestLongWithMultiDigitCarry; +begin + Test('C99F12A735A3B83901BF92011', 140, 'C99F12A735A3B83901BF9201100000000000000000000000000000000000'); +end; + +procedure TBigIntShiftLeftTestCase.TestWithAlignedDigits; +begin + // Shifts the left operand by a multiple of the length of full TBigInt digits, so the digits will be shifted, but not + // changed. + Test('10F0F39C5E', 32 * 4, '10F0F39C5E00000000000000000000000000000000'); +end; + +procedure TBigIntShiftLeftTestCase.TestZero; +begin + Test('0', 119, '0'); +end; + +{ TBigIntEqualityTestCase } + +procedure TBigIntEqualityTestCase.TestEqual(const AValue: Int64); +var + a, b: TBigInt; +begin + a := TBigInt.FromInt64(AValue); + b := TBigInt.FromInt64(AValue); + AssertTrue('Two BigInt from ''' + IntToStr(AValue) + ''' were not equal.', a = b); + AssertFalse('Two BigInt from ''' + IntToStr(AValue) + ''' were un-equal.', a <> b); +end; + +procedure TBigIntEqualityTestCase.TestEqualHex(const AHexValue: string); +var + a, b: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValue); + b := TBigInt.FromHexadecimalString(AHexValue); + AssertTrue('Two BigInt from ''' + AHexValue + ''' were not equal.', a = b); + AssertFalse('Two BigInt from ''' + AHexValue + ''' were un-equal.', a <> b); +end; + +procedure TBigIntEqualityTestCase.TestNotEqualHex(const AHexValueLeft, AHexValueRight: string); +var + a, b: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueLeft); + b := TBigInt.FromHexadecimalString(AHexValueRight); + AssertTrue('BigInt from ''' + AHexValueLeft + ''' and from ''' + AHexValueRight + ''' were not un-equal.', + a <> b); + AssertFalse('BigInt from ''' + AHexValueLeft + ''' and from ''' + AHexValueRight + ''' were equal.', + a = b); +end; + +procedure TBigIntEqualityTestCase.TestShortEqual; +begin + TestEqual(14); + TestEqualHex('8000111000111'); +end; + +procedure TBigIntEqualityTestCase.TestLongEqual; +begin + TestEqualHex('5AB60FF292A014BF1DD0A'); +end; + +procedure TBigIntEqualityTestCase.TestZeroEqual; +begin + TestEqual(0); +end; + +procedure TBigIntEqualityTestCase.TestShortNotEqual; +begin + TestNotEqualHex('9FF99920', '100'); + TestNotEqualHex('20001110002111', '70001110007111'); +end; + +procedure TBigIntEqualityTestCase.TestLongNotEqualSign; +begin + TestNotEqualHex('48843AB320FF0041123A', '-48843AB320FF0041123A'); + TestNotEqualHex('-B13F79D842A30957DD09523667', 'B13F79D842A30957DD09523667'); +end; + +procedure TBigIntEqualityTestCase.TestZeroNotEqual; +begin + TestNotEqualHex('0', 'F'); + TestNotEqualHex('568F7', '0'); +end; + +{ TBigIntComparisonTestCase } + +procedure TBigIntComparisonTestCase.TestLessThan(const AHexValueLeft, AHexValueRight: string); +var + a, b: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueLeft); + b := TBigInt.FromHexadecimalString(AHexValueRight); + AssertTrue('BigInt from ''' + AHexValueLeft + ''' was greater than BigInt from ''' + AHexValueRight + '''.', + a < b); + AssertTrue('BigInt from ''' + AHexValueLeft + ''' was greater or equal than BigInt from ''' + AHexValueRight + '''.', + a <= b); + AssertFalse('BigInt from ''' + AHexValueLeft + ''' was greater than BigInt from ''' + AHexValueRight + '''.', + a > b); + AssertFalse('BigInt from ''' + AHexValueLeft + ''' was greater or equal than BigInt from ''' + AHexValueRight + '''.', + a >= b); +end; + +procedure TBigIntComparisonTestCase.TestGreaterThan(const AHexValueLeft, AHexValueRight: string); +var + a, b: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValueLeft); + b := TBigInt.FromHexadecimalString(AHexValueRight); + AssertFalse('BigInt from ''' + AHexValueLeft + ''' was less than BigInt from ''' + AHexValueRight + '''.', + a < b); + AssertFalse('BigInt from ''' + AHexValueLeft + ''' was less or equal than BigInt from ''' + AHexValueRight + '''.', + a <= b); + AssertTrue('BigInt from ''' + AHexValueLeft + ''' was less than BigInt from ''' + AHexValueRight + '''.', + a > b); + AssertTrue('BigInt from ''' + AHexValueLeft + ''' was less or equal than BigInt from ''' + AHexValueRight + '''.', + a >= b); +end; + +procedure TBigIntComparisonTestCase.TestEqual(const AHexValue: string); +var + a, b: TBigInt; +begin + a := TBigInt.FromHexadecimalString(AHexValue); + b := TBigInt.FromHexadecimalString(AHexValue); + AssertFalse('First BigInt from ''' + AHexValue + ''' was less than the second.', + a < b); + AssertTrue('First BigInt from ''' + AHexValue + ''' was greater than the second.', + a <= b); + AssertFalse('First BigInt from ''' + AHexValue + ''' was greater than the second.', + a > b); + AssertTrue('First BigInt from ''' + AHexValue + ''' was less than the second.', + a >= b); +end; + +procedure TBigIntComparisonTestCase.TestLessSameLength; +begin + TestLessThan('104234FF2B29100C012', '234867AB261F1003429103C'); + TestLessThan('-9812FB2964AC7632865238BBD3', '294625DF51B2A842582244C18163490'); + TestLessThan('-12834653A2856DF8', '-A946C2BF89401B8'); + TestLessThan('-2F846', '0'); + TestLessThan('0', 'FF67B'); +end; + +procedure TBigIntComparisonTestCase.TestLessShorterLeft; +begin + TestLessThan('34FF2B29100C012', '234867AB261F1003429103C'); + TestLessThan('0', 'BFF008112BA00012'); +end; + +procedure TBigIntComparisonTestCase.TestLessNegativeShorterRight; +begin + TestLessThan('-9B72844AC', '1F3486B'); + TestLessThan('-BB884F022111190', '0'); +end; + +procedure TBigIntComparisonTestCase.TestGreaterSameLength; +begin + TestGreaterThan('B042104234FF2B29100C012', '23867AB261F1003429103C'); + TestGreaterThan('1294B77', '-611F3B93'); + TestGreaterThan('-782163498326593562D548AAF715B4DC9143E42C68F39A29BB2', '-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'); + TestGreaterThan('783BDA0', '0'); + TestGreaterThan('0', '-99B6D'); +end; + +procedure TBigIntComparisonTestCase.TestGreaterShorterRight; +begin + TestGreaterThan('102343234423FF67278B11ADD345F6BB21232C9129100C012', '234867AB261F1003429103C'); + TestGreaterThan('249D00F63BBAA8668B23', '0'); +end; + +procedure TBigIntComparisonTestCase.TestGreaterNegativeShorterLeft; +begin + TestGreaterThan('81F2A7B78B812', '-23490D7F19F247F91A365B1893BB701'); + TestGreaterThan('0', '-80F88242B34127'); +end; + +procedure TBigIntComparisonTestCase.TestEqualPositive; +begin + TestEqual('A44B80191059CA123318921A219BB'); +end; + +procedure TBigIntComparisonTestCase.TestEqualNegative; +begin + TestEqual('-28912798DD1246DAC9FB4269908012D496896812FF3A8D071B32'); +end; + +procedure TBigIntComparisonTestCase.TestEqualZero; +begin + TestEqual('0'); +end; + +initialization + + RegisterTest(TBigIntSignTestCase); + RegisterTest(TBigIntFromInt64TestCase); + RegisterTest(TBigIntFromHexTestCase); + RegisterTest(TBigIntFromBinTestCase); + RegisterTest(TBigIntUnaryMinusTestCase); + RegisterTest(TBigIntSumTestCase); + RegisterTest(TBigIntDifferenceTestCase); + RegisterTest(TBigIntProductTestCase); + RegisterTest(TBigIntShiftLeftTestCase); + RegisterTest(TBigIntEqualityTestCase); + RegisterTest(TBigIntComparisonTestCase); +end. +