{
  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 <http://www.gnu.org/licenses/>.
}

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

  { 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;

  { TBigIntMostSignificantBitIndexTestCase }

  TBigIntMostSignificantBitIndexTestCase = class(TTestCase)
  private
    procedure Test(const ABinValue: string; const AExpectedIndex: Int64);
  published
    procedure TestZero;
    procedure TestShort;
    procedure TestLong;
  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;

  { 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)
  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;

{ TBigIntMostSignificantBitIndexTestCase }

procedure TBigIntMostSignificantBitIndexTestCase.Test(const ABinValue: string; const AExpectedIndex: Int64);
var
  a: TBigInt;
begin
  a := TBigInt.FromBinaryString(ABinValue);
  AssertEquals('BigInt from binary string ''' + ABinValue + ''' did not have its most significant bit at index '''
    + IntToStr(AExpectedIndex) + '''.',
    AExpectedIndex, a.GetMostSignificantBitIndex);
end;

procedure TBigIntMostSignificantBitIndexTestCase.TestZero;
begin
  Test('0', -1);
end;

procedure TBigIntMostSignificantBitIndexTestCase.TestShort;
begin
  Test('1', 0);
  Test('11111101111001', 13);
  Test('10010111101100001110110101111000', 31);
end;

procedure TBigIntMostSignificantBitIndexTestCase.TestLong;
begin
  Test('111100010101010100011101010100011', 32);
  Test('11101001101010111101000101010001010101010101111111001010101010010100101000101011111001000111001001100011', 103);
  Test('111101100011110110111011010111100000000001010111101110101101101100101010110111101011010101001100', 95);
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;

{ 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);
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('Helper.TBigInt', TBigIntSignTestCase);
  RegisterTest('Helper.TBigInt', TBigIntMostSignificantBitIndexTestCase);
  RegisterTest('Helper.TBigInt', TBigIntFromInt64TestCase);
  RegisterTest('Helper.TBigInt', TBigIntFromHexTestCase);
  RegisterTest('Helper.TBigInt', TBigIntFromBinTestCase);
  RegisterTest('Helper.TBigInt', TBigIntUnaryMinusTestCase);
  RegisterTest('Helper.TBigInt', TBigIntSumTestCase);
  RegisterTest('Helper.TBigInt', TBigIntDifferenceTestCase);
  RegisterTest('Helper.TBigInt', TBigIntProductTestCase);
  RegisterTest('Helper.TBigInt', TBigIntShiftLeftTestCase);
  RegisterTest('Helper.TBigInt', TBigIntShiftRightTestCase);
  RegisterTest('Helper.TBigInt', TBigIntEqualityTestCase);
  RegisterTest('Helper.TBigInt', TBigIntComparisonTestCase);
end.