Merge branch 'day24-analytical'
This commit is contained in:
commit
859a5db921
|
@ -137,6 +137,18 @@
|
|||
<Filename Value="solvers\UNeverTellMeTheOdds.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UBigInt.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomial.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomialRoots.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
|
|
@ -0,0 +1,763 @@
|
|||
{
|
||||
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 UBigInt;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Math;
|
||||
|
||||
type
|
||||
TDigits = array of Cardinal;
|
||||
|
||||
{ TBigInt }
|
||||
|
||||
// This is an abbreviated reimplementation in Freepascal of a C# class created in 2022.
|
||||
TBigInt = object
|
||||
private
|
||||
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
|
||||
// the new BigInt.
|
||||
// ACount is the number of consecutive digits to be taken, and the number of digits of the result.
|
||||
function GetSegment(const AIndex, ACount: Integer): TBigInt;
|
||||
|
||||
// Compares the absolute value of this TBigInt object to the absolute value of another one. Returns -1 if this
|
||||
// object is less than AOther, 1 if this object is greater than AOther, and 0 if they are equal.
|
||||
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)),
|
||||
// where Sign is 1 for ReturnNegative = False and -1 otherwise.
|
||||
class function AddAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; static;
|
||||
|
||||
// Subtracts B from A, ignoring their signs. However, the result might be negative, and the sign can be reversed by
|
||||
// setting ReturnNegative to True. The result is
|
||||
// Sign * (Abs(A) - Abs(B)),
|
||||
// where Sign is 1 for ReturnNegative = False and -1 otherwise.
|
||||
class function SubtractAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; static;
|
||||
|
||||
// Multiplies A and B, ignoring their signs and using ReturnNegative instead. This multiplication uses a recursive
|
||||
// implementation of the Karatsuba algorithm. See
|
||||
// https://www.geeksforgeeks.org/karatsuba-algorithm-for-fast-multiplication-using-divide-and-conquer-algorithm/
|
||||
// The result is
|
||||
// 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;
|
||||
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.
|
||||
function GetMostSignificantBitIndex: Int64;
|
||||
function CompareTo(constref AOther: TBigInt): Integer;
|
||||
function TryToInt64(out AOutput: Int64): Boolean;
|
||||
// TODO: ToString is currently for debug output only.
|
||||
function ToString: string;
|
||||
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;
|
||||
|
||||
TBigIntArray = array of TBigInt;
|
||||
|
||||
{ Operators }
|
||||
|
||||
operator := (const A: Int64): TBigInt;
|
||||
operator - (const A: TBigInt): TBigInt;
|
||||
operator + (const A, B: TBigInt): TBigInt;
|
||||
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;
|
||||
operator <= (const A, B: TBigInt): Boolean;
|
||||
operator > (const A, B: TBigInt): Boolean;
|
||||
operator >= (const A, B: TBigInt): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
const
|
||||
CBase = Cardinal.MaxValue + 1;
|
||||
CMaxDigit = Cardinal.MaxValue;
|
||||
CDigitSize = SizeOf(Cardinal);
|
||||
CBitsPerDigit = CDigitSize * 8;
|
||||
CHalfBits = CBitsPerDigit >> 1;
|
||||
CHalfDigitMax = (1 << CHalfBits) - 1;
|
||||
|
||||
CZero: TBigInt = (FDigits: (0); FIsNegative: False);
|
||||
COne: TBigInt = (FDigits: (1); FIsNegative: False);
|
||||
|
||||
{ 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;
|
||||
begin
|
||||
trimmedCount := ACount;
|
||||
while (trimmedCount > 1) and (FDigits[AIndex + trimmedCount - 1] = 0) do
|
||||
Dec(trimmedCount);
|
||||
Result.FDigits := Copy(FDigits, AIndex, trimmedCount);
|
||||
Result.FIsNegative := False;
|
||||
end;
|
||||
|
||||
function TBigInt.CompareToAbsoluteValues(constref AOther: TBigInt): Integer;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := Length(FDigits) - Length(AOther.FDigits);
|
||||
if Result = 0 then
|
||||
begin
|
||||
for i := High(FDigits) downto 0 do
|
||||
if FDigits[i] < AOther.FDigits[i] then
|
||||
begin
|
||||
Result := -1;
|
||||
Break;
|
||||
end
|
||||
else if FDigits[i] > AOther.FDigits[i] then
|
||||
begin
|
||||
Result := 1;
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TBigInt.GetZero: TBigInt;
|
||||
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;
|
||||
carry: Cardinal;
|
||||
begin
|
||||
lenA := Length(AA.FDigits);
|
||||
lenB := Length(AB.FDigits);
|
||||
|
||||
// Initializes the digits array of the result, with a simple test to try to predict a carry-over into a new digit. The
|
||||
// result could still carry into new digit depending on lower digits (carry over multiple digits), which would be
|
||||
// handled at the end.
|
||||
if lenA = lenB then
|
||||
if CMaxDigit - AA.FDigits[lenA - 1] < AB.FDigits[lenB - 1] then
|
||||
// Result will carry into new digit.
|
||||
SetLength(Result.FDigits, lenA + 1)
|
||||
else
|
||||
SetLength(Result.FDigits, lenA)
|
||||
else
|
||||
SetLength(Result.FDigits, Max(lenA, lenB));
|
||||
len := Length(Result.FDigits);
|
||||
|
||||
// Calculates the new digits from less to more significant until the end of the shorter operand is reached.
|
||||
shorter := Min(Length(AA.FDigits), Length(AB.FDigits));
|
||||
i := 0;
|
||||
carry := 0;
|
||||
while i < shorter do
|
||||
begin
|
||||
if (AB.FDigits[i] = CMaxDigit) and (carry > 0) then
|
||||
begin
|
||||
Result.FDigits[i] := AA.FDigits[i];
|
||||
carry := 1;
|
||||
end
|
||||
else
|
||||
if CMaxDigit - AA.FDigits[i] < AB.FDigits[i] + carry then
|
||||
begin
|
||||
Result.FDigits[i] := AB.FDigits[i] + carry - 1 - (CMaxDigit - AA.FDigits[i]);
|
||||
carry := 1;
|
||||
end
|
||||
else begin
|
||||
Result.FDigits[i] := AA.FDigits[i] + AB.FDigits[i] + carry;
|
||||
carry := 0;
|
||||
end;
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
// Copies the missing unchanged digits from the longer operand to the result, if any, before applying remaining
|
||||
// carry-overs. This avoids additional tests for finding the shorter digit array.
|
||||
if (i < lenA) or (i < lenB) then
|
||||
if lenA >= lenB then
|
||||
for j := i to len - 1 do
|
||||
Result.FDigits[j] := AA.FDigits[j]
|
||||
else
|
||||
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
|
||||
begin
|
||||
if Result.FDigits[i] = CMaxDigit then
|
||||
Result.FDigits[i] := 0
|
||||
else begin
|
||||
Inc(Result.FDigits[i]);
|
||||
carry := 0;
|
||||
end;
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
// Applies the carry-over into a new digit that was not anticipated in the initialization at the top (carry over
|
||||
// multiple digits).
|
||||
if carry > 0 then
|
||||
Insert(1, Result.FDigits, len);
|
||||
Result.FIsNegative := AReturnNegative;
|
||||
end;
|
||||
|
||||
class function TBigInt.SubtractAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt;
|
||||
var
|
||||
a, b: TBigInt;
|
||||
carry: Cardinal;
|
||||
compare, i, j, lastNonZeroDigitIndex, len: Integer;
|
||||
begin
|
||||
// Establishes the operand order, such that Abs(a) is not less than Abs(b).
|
||||
compare := AA.CompareToAbsoluteValues(AB);
|
||||
if compare = 0 then
|
||||
begin
|
||||
Result := Zero;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if compare > 0 then
|
||||
begin
|
||||
a := AA;
|
||||
b := AB;
|
||||
Result.FIsNegative := AReturnNegative;
|
||||
end
|
||||
else begin
|
||||
a := AB;
|
||||
b := AA;
|
||||
Result.FIsNegative := not AReturnNegative;
|
||||
end;
|
||||
|
||||
// Calculates the new digits from less to more significant until the end of the shorter operand is reached and all
|
||||
// carry-overs have been applied.
|
||||
len := Length(a.FDigits);
|
||||
SetLength(Result.FDigits, len);
|
||||
carry := 0;
|
||||
// Tracks leading zeros for the trim below.
|
||||
lastNonZeroDigitIndex := 0;
|
||||
i := 0;
|
||||
while i < Length(b.FDigits) do
|
||||
begin
|
||||
if (a.FDigits[i] = b.FDigits[i]) and (carry > 0) then
|
||||
begin
|
||||
Result.FDigits[i] := CMaxDigit;
|
||||
carry := 1;
|
||||
lastNonZeroDigitIndex := i;
|
||||
end
|
||||
else begin
|
||||
if a.FDigits[i] < b.FDigits[i] then
|
||||
begin
|
||||
Result.FDigits[i] := CMaxDigit - (b.FDigits[i] - a.FDigits[i]) + 1 - carry;
|
||||
carry := 1;
|
||||
end
|
||||
else begin
|
||||
Result.FDigits[i] := a.FDigits[i] - b.FDigits[i] - carry;
|
||||
carry := 0;
|
||||
end;
|
||||
if Result.FDigits[i] > 0 then
|
||||
lastNonZeroDigitIndex := i;
|
||||
end;
|
||||
Inc(i);
|
||||
end;
|
||||
while carry > 0 do
|
||||
begin
|
||||
if a.FDigits[i] = 0 then
|
||||
begin
|
||||
Result.FDigits[i] := CMaxDigit;
|
||||
lastNonZeroDigitIndex := i;
|
||||
end
|
||||
else begin
|
||||
Result.FDigits[i] := a.FDigits[i] - carry;
|
||||
carry := 0;
|
||||
if Result.FDigits[i] > 0 then
|
||||
lastNonZeroDigitIndex := i;
|
||||
end;
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
// 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
|
||||
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);
|
||||
end;
|
||||
|
||||
class function TBigInt.MultiplyAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt;
|
||||
var
|
||||
lenA, lenB, lenMax, floorHalfLength, ceilHalfLength: Integer;
|
||||
a1, a0, b1, b0, a1b1, a0b0: Cardinal;
|
||||
am, bm, middle, biga1, biga0, bigb1, bigb0, biga1b1, biga0b0: TBigInt;
|
||||
begin
|
||||
lenA := Length(AA.FDigits);
|
||||
lenB := Length(AB.FDigits);
|
||||
if (lenA <= 1) and (lenB <= 1) then
|
||||
begin
|
||||
if (AA.FDigits[0] <= CHalfDigitMax) and (AB.FDigits[0] <= CHalfDigitMax) then
|
||||
if (AA.FDigits[0] = 0) or (AB.FDigits[0] = 0) then
|
||||
Result := Zero
|
||||
else begin
|
||||
Result.FDigits := TDigits.Create(AA.FDigits[0] * AB.FDigits[0]);
|
||||
Result.FIsNegative := AReturnNegative;
|
||||
end
|
||||
else begin
|
||||
// a1, a0, b1, b0 use only the lower (less significant) half of the bits of a digit, so the product of any two of
|
||||
// these fits in one digit.
|
||||
a1 := AA.FDigits[0] >> CHalfBits;
|
||||
a0 := AA.FDigits[0] and CHalfDigitMax;
|
||||
b1 := AB.FDigits[0] >> CHalfBits;
|
||||
b0 := AB.FDigits[0] and CHalfDigitMax;
|
||||
a1b1 := a1 * b1;
|
||||
a0b0 := a0 * b0;
|
||||
|
||||
if a1b1 > 0 then
|
||||
Result.FDigits := TDigits.Create(a0b0, a1b1)
|
||||
else
|
||||
Result.FDigits := TDigits.Create(a0b0);
|
||||
Result.FIsNegative := AReturnNegative;
|
||||
|
||||
// The result of (a1 + a0) * (b1 + b0) might not fit in one digit, so one last recursion step is necessary.
|
||||
am := FromInt64(a1 + a0);
|
||||
bm := FromInt64(b1 + b0);
|
||||
middle := (MultiplyAbsoluteValues(am, bm, False) - a1b1 - a0b0) << CHalfBits;
|
||||
if AReturnNegative then
|
||||
Result := Result - middle
|
||||
else
|
||||
Result := Result + middle;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
// Calculates where to split the two numbers.
|
||||
lenMax := Max(lenA, lenB);
|
||||
floorHalfLength := lenMax >> 1;
|
||||
ceilHalfLength := lenMax - floorHalfLength;
|
||||
|
||||
// Performs one recursion step.
|
||||
if ceilHalfLength < lenA then
|
||||
begin
|
||||
biga1 := AA.GetSegment(ceilHalfLength, lenA - ceilHalfLength);
|
||||
biga0 := AA.GetSegment(0, ceilHalfLength);
|
||||
|
||||
if ceilHalfLength < lenB then
|
||||
begin
|
||||
bigb1 := AB.GetSegment(ceilHalfLength, lenB - ceilHalfLength);
|
||||
bigb0 := AB.GetSegment(0, ceilHalfLength);
|
||||
biga1b1 := MultiplyAbsoluteValues(biga1, bigb1, AReturnNegative);
|
||||
biga0b0 := MultiplyAbsoluteValues(biga0, bigb0, AReturnNegative);
|
||||
Result := (biga1b1 << (2 * ceilHalfLength * CBitsPerDigit))
|
||||
+ ((MultiplyAbsoluteValues(biga1 + biga0, bigb1 + bigb0, AReturnNegative) - biga1b1 - biga0b0) << (ceilHalfLength * CBitsPerDigit))
|
||||
+ biga0b0;
|
||||
end
|
||||
else begin
|
||||
biga0b0 := MultiplyAbsoluteValues(biga0, AB, AReturnNegative);
|
||||
Result := ((MultiplyAbsoluteValues(biga1 + biga0, AB, AReturnNegative) - biga0b0) << (ceilHalfLength * CBitsPerDigit)) + biga0b0;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
bigb1 := AB.GetSegment(ceilHalfLength, lenB - ceilHalfLength);
|
||||
bigb0 := AB.GetSegment(0, ceilHalfLength);
|
||||
biga0b0 := MultiplyAbsoluteValues(AA, bigb0, AReturnNegative);
|
||||
Result := ((MultiplyAbsoluteValues(AA, bigb1 + bigb0, AReturnNegative) - biga0b0) << (ceilHalfLength * CBitsPerDigit)) + biga0b0;
|
||||
end;
|
||||
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.GetMostSignificantBitIndex: Int64;
|
||||
var
|
||||
high, i: Integer;
|
||||
begin
|
||||
high := Length(FDigits) - 1;
|
||||
if (high = 0) and (FDigits[0] = 0) then
|
||||
Result := -1
|
||||
else begin
|
||||
i := CBitsPerDigit - 1;
|
||||
while ((1 << i) and FDigits[high]) = 0 do
|
||||
Dec(i);
|
||||
Result := high * CBitsPerDigit + i;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigInt.CompareTo(constref AOther: TBigInt): Integer;
|
||||
begin
|
||||
if FIsNegative = AOther.FIsNegative then
|
||||
Result := CompareToAbsoluteValues(AOther)
|
||||
else
|
||||
Result := 1;
|
||||
if FIsNegative then
|
||||
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;
|
||||
|
||||
function TBigInt.ToString: string;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if FIsNegative then
|
||||
Result := '(-'
|
||||
else
|
||||
Result := '';
|
||||
for i := 0 to Length(FDigits) - 2 do
|
||||
Result := Result + '(' + IntToStr(FDigits[i]) + ' + 2^32 * ';
|
||||
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;
|
||||
var
|
||||
absVal: Int64;
|
||||
begin
|
||||
if AValue <> Int64.MinValue then
|
||||
begin
|
||||
absVal := Abs(AValue);
|
||||
if absVal >= CBase then
|
||||
Result.FDigits := TDigits.Create(absVal mod CBase, absVal div CBase)
|
||||
else
|
||||
Result.FDigits := TDigits.Create(absVal);
|
||||
Result.FIsNegative := AValue < 0;
|
||||
end
|
||||
else begin
|
||||
Result.FDigits := TDigits.Create(0, 1 << 31);
|
||||
Result.FIsNegative := True;
|
||||
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;
|
||||
begin
|
||||
Result := TBigInt.FromInt64(A);
|
||||
end;
|
||||
|
||||
operator - (const A: TBigInt): TBigInt;
|
||||
var
|
||||
len: Integer;
|
||||
begin
|
||||
len := Length(A.FDigits);
|
||||
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;
|
||||
begin
|
||||
if A.IsNegative = B.IsNegative then
|
||||
Result := TBigInt.AddAbsoluteValues(A, B, A.IsNegative)
|
||||
else
|
||||
Result := TBigInt.SubtractAbsoluteValues(A, B, A.IsNegative);
|
||||
end;
|
||||
|
||||
operator - (const A, B: TBigInt): TBigInt;
|
||||
begin
|
||||
if A.IsNegative = B.IsNegative then
|
||||
Result := TBigInt.SubtractAbsoluteValues(A, B, A.IsNegative)
|
||||
else
|
||||
Result := TBigInt.AddAbsoluteValues(A, B, A.IsNegative);
|
||||
end;
|
||||
|
||||
operator * (const A, B: TBigInt): TBigInt;
|
||||
begin
|
||||
if (A = TBigInt.Zero) or (B = TBigInt.Zero) then
|
||||
Result := TBigInt.Zero
|
||||
else
|
||||
Result := TBigInt.MultiplyAbsoluteValues(A, B, A.IsNegative <> B.IsNegative);
|
||||
end;
|
||||
|
||||
operator shl(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);
|
||||
|
||||
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
|
||||
// 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 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;
|
||||
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;
|
||||
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;
|
||||
|
||||
operator >= (const A, B: TBigInt): Boolean;
|
||||
begin
|
||||
Result := A.CompareTo(B) >= 0;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -22,7 +22,7 @@ unit UNumberTheory;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
Classes, SysUtils, Generics.Collections, Math;
|
||||
|
||||
type
|
||||
|
||||
|
@ -34,6 +34,52 @@ type
|
|||
class function LeastCommonMultiple(AValue1, AValue2: Int64): Int64;
|
||||
end;
|
||||
|
||||
TInt64Array = array of Int64;
|
||||
|
||||
{ TIntegerFactor }
|
||||
|
||||
TIntegerFactor = record
|
||||
Factor: Int64;
|
||||
Exponent: Byte;
|
||||
end;
|
||||
|
||||
TIntegerFactors = specialize TList<TIntegerFactor>;
|
||||
|
||||
{ TIntegerFactorization }
|
||||
|
||||
TIntegerFactorization = class
|
||||
public
|
||||
class function PollardsRhoAlgorithm(const AValue: Int64): TInt64Array;
|
||||
class function GetNormalized(constref AIntegerFactorArray: TInt64Array): TIntegerFactors;
|
||||
end;
|
||||
|
||||
{ TDividersEnumerator }
|
||||
|
||||
TDividersEnumerator = class
|
||||
private
|
||||
FFactors: TIntegerFactors;
|
||||
FCurrentExponents: array of Byte;
|
||||
function GetCount: Integer;
|
||||
public
|
||||
constructor Create(constref AIntegerFactorArray: TInt64Array);
|
||||
destructor Destroy; override;
|
||||
function GetCurrent: Int64;
|
||||
function MoveNext: Boolean;
|
||||
procedure Reset;
|
||||
property Current: Int64 read GetCurrent;
|
||||
property Count: Integer read GetCount;
|
||||
end;
|
||||
|
||||
{ TDividers }
|
||||
|
||||
TDividers = class
|
||||
private
|
||||
FFactorArray: TInt64Array;
|
||||
public
|
||||
constructor Create(constref AIntegerFactorArray: TInt64Array);
|
||||
function GetEnumerator: TDividersEnumerator;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TNumberTheory }
|
||||
|
@ -58,5 +104,177 @@ begin
|
|||
Result := (Abs(AValue1) div GreatestCommonDivisor(AValue1, AValue2)) * Abs(AValue2);
|
||||
end;
|
||||
|
||||
{ TIntegerFactorization }
|
||||
|
||||
// https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
|
||||
class function TIntegerFactorization.PollardsRhoAlgorithm(const AValue: Int64): TInt64Array;
|
||||
var
|
||||
primes: specialize TList<Int64>;
|
||||
composites: specialize TStack<Int64>;
|
||||
factor, n: Int64;
|
||||
i: Integer;
|
||||
|
||||
function G(const AX, AC: Int64): Int64;
|
||||
begin
|
||||
Result := (AX * AX + AC) mod n;
|
||||
end;
|
||||
|
||||
function FindFactor(const AStartValue, AC: Int64): Int64;
|
||||
var
|
||||
x, y, d: Int64;
|
||||
begin
|
||||
x := AStartValue;
|
||||
y := x;
|
||||
d := 1;
|
||||
while d = 1 do
|
||||
begin
|
||||
x := G(x, AC);
|
||||
y := G(G(y, AC), AC);
|
||||
d := TNumberTheory.GreatestCommonDivisor(Abs(x - y), n);
|
||||
end;
|
||||
Result := d;
|
||||
end;
|
||||
|
||||
begin
|
||||
primes := specialize TList<Int64>.Create;
|
||||
composites := specialize TStack<Int64>.Create;
|
||||
|
||||
n := Abs(AValue);
|
||||
while (n and 1) = 0 do
|
||||
begin
|
||||
primes.Add(2);
|
||||
n := n shr 1;
|
||||
end;
|
||||
|
||||
composites.Push(n);
|
||||
while composites.Count > 0 do
|
||||
begin
|
||||
n := composites.Pop;
|
||||
i := 0;
|
||||
repeat
|
||||
factor := FindFactor(2 + (i + 1) div 2, 1 - i div 2);
|
||||
if factor < n then
|
||||
begin
|
||||
composites.Push(factor);
|
||||
composites.Push(n div factor);
|
||||
end;
|
||||
Inc(i);
|
||||
until (factor < n) or (i > 3);
|
||||
if factor = n then
|
||||
primes.Add(factor);
|
||||
end;
|
||||
|
||||
Result := primes.ToArray;
|
||||
|
||||
primes.Free;
|
||||
composites.Free;
|
||||
end;
|
||||
|
||||
class function TIntegerFactorization.GetNormalized(constref AIntegerFactorArray: TInt64Array): TIntegerFactors;
|
||||
var
|
||||
i: Integer;
|
||||
factor: Int64;
|
||||
normal: TIntegerFactor;
|
||||
found: Boolean;
|
||||
begin
|
||||
Result := TIntegerFactors.Create;
|
||||
for factor in AIntegerFactorArray do
|
||||
begin
|
||||
found := False;
|
||||
for i := 0 to Result.Count - 1 do
|
||||
if Result[i].Factor = factor then
|
||||
begin
|
||||
found := True;
|
||||
normal := Result[i];
|
||||
Inc(normal.Exponent);
|
||||
Result[i] := normal;
|
||||
Break;
|
||||
end;
|
||||
if not found then
|
||||
begin
|
||||
normal.Factor := factor;
|
||||
normal.Exponent := 1;
|
||||
Result.Add(normal);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TDividersEnumerator }
|
||||
|
||||
function TDividersEnumerator.GetCount: Integer;
|
||||
var
|
||||
factor: TIntegerFactor;
|
||||
begin
|
||||
if FFactors.Count > 0 then
|
||||
begin
|
||||
Result := 1;
|
||||
for factor in FFactors do
|
||||
Result := Result * factor.Exponent;
|
||||
Dec(Result);
|
||||
end
|
||||
else
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
constructor TDividersEnumerator.Create(constref AIntegerFactorArray: TInt64Array);
|
||||
begin
|
||||
FFactors := TIntegerFactorization.GetNormalized(AIntegerFactorArray);
|
||||
SetLength(FCurrentExponents, FFactors.Count);
|
||||
end;
|
||||
|
||||
destructor TDividersEnumerator.Destroy;
|
||||
begin
|
||||
FFactors.Free;
|
||||
end;
|
||||
|
||||
function TDividersEnumerator.GetCurrent: Int64;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := 1;
|
||||
for i := Low(FCurrentExponents) to High(FCurrentExponents) do
|
||||
if FCurrentExponents[i] > 0 then
|
||||
Result := Result * Round(Power(FFactors[i].Factor, FCurrentExponents[i]));
|
||||
end;
|
||||
|
||||
function TDividersEnumerator.MoveNext: Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
i := 0;
|
||||
while (i <= High(FCurrentExponents)) and (FCurrentExponents[i] >= FFactors[i].Exponent) do
|
||||
begin
|
||||
FCurrentExponents[i] := 0;
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
if i <= High(FCurrentExponents) then
|
||||
begin
|
||||
Inc(FCurrentExponents[i]);
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDividersEnumerator.Reset;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := Low(FCurrentExponents) to High(FCurrentExponents) do
|
||||
FCurrentExponents[i] := 0;
|
||||
end;
|
||||
|
||||
{ TDividers }
|
||||
|
||||
constructor TDividers.Create(constref AIntegerFactorArray: TInt64Array);
|
||||
begin
|
||||
FFactorArray := AIntegerFactorArray;
|
||||
end;
|
||||
|
||||
function TDividers.GetEnumerator: TDividersEnumerator;
|
||||
begin
|
||||
Result := TDividersEnumerator.Create(FFactorArray);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 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 UPolynomial;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, UBigInt;
|
||||
|
||||
type
|
||||
TInt64Array = array of Int64;
|
||||
|
||||
{ TBigIntPolynomial }
|
||||
|
||||
TBigIntPolynomial = object
|
||||
private
|
||||
FCoefficients: array of TBigInt;
|
||||
function GetDegree: Integer;
|
||||
function GetCoefficient(const AIndex: Integer): TBigInt;
|
||||
public
|
||||
property Degree: Integer read GetDegree;
|
||||
property Coefficient[const AIndex: Integer]: TBigInt read GetCoefficient;
|
||||
function CalcValueAt(const AX: Int64): TBigInt;
|
||||
function CalcSignVariations: Integer;
|
||||
|
||||
// Returns 2^n * f(x), given a polynomial f(x) and exponent n.
|
||||
function ScaleByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
|
||||
// Returns f(s * x), given a polynomial f(x) and scale factor s.
|
||||
function ScaleVariable(const AScaleFactor: TBigInt): TBigIntPolynomial;
|
||||
|
||||
// Returns f(2^n * x), given a polynomial f(x) and an exponent n.
|
||||
function ScaleVariableByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
|
||||
// Returns f(x / 2), given a polynomial f(x).
|
||||
function ScaleVariableByHalf: TBigIntPolynomial;
|
||||
|
||||
// Returns f(x + 1), given a polynomial f(x).
|
||||
function TranslateVariableByOne: TBigIntPolynomial;
|
||||
|
||||
// Returns a polynomial with the reverse order of coefficients, i.e. the polynomial
|
||||
// a_0 * x^n + a_1 * x^(n - 1) + ... + a_(n - 1) * x + a_n,
|
||||
// given a polynomial
|
||||
// a_n * x^n + a_(n - 1) * x^(n - 1) + ... + a_1 * x + a_0.
|
||||
function RevertOrderOfCoefficients: TBigIntPolynomial;
|
||||
|
||||
// Returns a polynomial with all coefficents shifted down one position, and the constant term removed. This should
|
||||
// only be used when the constant term is zero and is then equivalent to a division of polynomial f(x) by x.
|
||||
function DivideByVariable: TBigIntPolynomial;
|
||||
function IsEqualTo(const AOther: TBigIntPolynomial): Boolean;
|
||||
function ToString: string;
|
||||
class function Create(const ACoefficients: array of TBigInt): TBigIntPolynomial; static;
|
||||
end;
|
||||
|
||||
{ Operators }
|
||||
|
||||
operator = (const A, B: TBigIntPolynomial): Boolean;
|
||||
operator <> (const A, B: TBigIntPolynomial): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
{ TBigIntPolynomial }
|
||||
|
||||
function TBigIntPolynomial.GetDegree: Integer;
|
||||
begin
|
||||
Result := Length(FCoefficients) - 1;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.GetCoefficient(const AIndex: Integer): TBigInt;
|
||||
begin
|
||||
Result := FCoefficients[AIndex];
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.CalcValueAt(const AX: Int64): TBigInt;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := TBigInt.Zero;
|
||||
for i := High(FCoefficients) downto 0 do
|
||||
Result := Result * AX + FCoefficients[i];
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.CalcSignVariations: Integer;
|
||||
var
|
||||
current, last, i: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
last := 0;
|
||||
for i := 0 to Length(FCoefficients) - 1 do
|
||||
begin
|
||||
current := FCoefficients[i].Sign;
|
||||
if (current <> 0) and (last <> current) then
|
||||
begin
|
||||
if last <> 0 then
|
||||
Inc(Result);
|
||||
last := current
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
for i := 0 to len - 1 do
|
||||
Result.FCoefficients[i] := FCoefficients[i] << AExponent;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleVariable(const AScaleFactor: TBigInt): TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
factor: TBigInt;
|
||||
begin
|
||||
if AScaleFactor <> TBigInt.Zero then
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
Result.FCoefficients[0] := FCoefficients[0];
|
||||
factor := AScaleFactor;
|
||||
for i := 1 to len - 1 do begin
|
||||
Result.FCoefficients[i] := FCoefficients[i] * factor;
|
||||
factor := factor * AScaleFactor;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
SetLength(Result.FCoefficients, 1);
|
||||
Result.FCoefficients[0] := TBigInt.Zero;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleVariableByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
shift: Cardinal;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
Result.FCoefficients[0] := FCoefficients[0];
|
||||
shift := AExponent;
|
||||
for i := 1 to len - 1 do begin
|
||||
Result.FCoefficients[i] := FCoefficients[i] << shift;
|
||||
Inc(shift, AExponent);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleVariableByHalf: TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
Result.FCoefficients[0] := FCoefficients[0];
|
||||
for i := 1 to len - 1 do
|
||||
Result.FCoefficients[i] := FCoefficients[i] >> i;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.TranslateVariableByOne: TBigIntPolynomial;
|
||||
var
|
||||
len, i, j: Integer;
|
||||
factors: array of Cardinal;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
SetLength(factors, len);
|
||||
for i := 0 to len - 1 do
|
||||
begin
|
||||
Result.FCoefficients[i] := TBigInt.Zero;
|
||||
factors[i] := 1;
|
||||
end;
|
||||
|
||||
// Calculates new coefficients.
|
||||
for i := 0 to len - 1 do
|
||||
begin
|
||||
for j := 0 to len - i - 1 do
|
||||
begin
|
||||
if (i <> 0) and (j <> 0) then
|
||||
factors[j] := factors[j] + factors[j - 1];
|
||||
Result.FCoefficients[i] := Result.FCoefficients[i] + factors[j] * FCoefficients[j + i];
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.RevertOrderOfCoefficients: TBigIntPolynomial;
|
||||
var
|
||||
len, skip, i: Integer;
|
||||
begin
|
||||
// Counts the trailing zeros to skip.
|
||||
len := Length(FCoefficients);
|
||||
skip := 0;
|
||||
while (skip < len) and (FCoefficients[skip] = 0) do
|
||||
Inc(skip);
|
||||
|
||||
// Copies the other coefficients in reverse order.
|
||||
SetLength(Result.FCoefficients, len - skip);
|
||||
for i := skip to len - 1 do
|
||||
Result.FCoefficients[len - i - 1] := FCoefficients[i];
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.DivideByVariable: TBigIntPolynomial;
|
||||
var
|
||||
len: Integer;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
if len > 1 then
|
||||
Result.FCoefficients := Copy(FCoefficients, 1, len - 1)
|
||||
else begin
|
||||
SetLength(Result.FCoefficients, 1);
|
||||
Result.FCoefficients[0] := TBigInt.Zero;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.IsEqualTo(const AOther: TBigIntPolynomial): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if Length(FCoefficients) = Length(AOther.FCoefficients) then
|
||||
begin
|
||||
Result := True;
|
||||
for i := 0 to Length(FCoefficients) - 1 do
|
||||
if FCoefficients[i] <> AOther.FCoefficients[i] then
|
||||
begin
|
||||
Result := False;
|
||||
Break;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ToString: string;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := FCoefficients[0].ToString;
|
||||
for i := 1 to Length(FCoefficients) - 1 do
|
||||
if i > 1 then
|
||||
Result := Result + ' + ' + FCoefficients[i].ToString + ' * x^' + IntToStr(i)
|
||||
else
|
||||
Result := Result + ' + ' + FCoefficients[i].ToString + ' * x';
|
||||
end;
|
||||
|
||||
class function TBigIntPolynomial.Create(const ACoefficients: array of TBigInt): TBigIntPolynomial;
|
||||
var
|
||||
high, i: integer;
|
||||
begin
|
||||
high := -1;
|
||||
for i := Length(ACoefficients) - 1 downto 0 do
|
||||
if ACoefficients[i] <> 0 then
|
||||
begin
|
||||
high := i;
|
||||
Break;
|
||||
end;
|
||||
if high >= 0 then
|
||||
begin
|
||||
SetLength(Result.FCoefficients, high + 1);
|
||||
for i := 0 to high do
|
||||
Result.FCoefficients[i] := ACoefficients[i];
|
||||
end
|
||||
else begin
|
||||
SetLength(Result.FCoefficients, 1);
|
||||
Result.FCoefficients[0] := TBigInt.Zero;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Operators }
|
||||
|
||||
operator = (const A, B: TBigIntPolynomial): Boolean;
|
||||
begin
|
||||
Result := A.IsEqualTo(B);
|
||||
end;
|
||||
|
||||
operator <> (const A, B: TBigIntPolynomial): Boolean;
|
||||
begin
|
||||
Result := not A.IsEqualTo(B);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 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 UPolynomialRoots;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, UPolynomial, UBigInt;
|
||||
|
||||
type
|
||||
|
||||
{ TIsolatingInterval }
|
||||
|
||||
// Represents an isolating interval of the form [C / 2^K, (C + H) / 2^K] in respect to [0, 1] or [A, B] in respect to
|
||||
// [0, 2^boundexp], with A = C * 2^boundexp / 2^K and B = (C + H) * 2^boundexp / 2^K.
|
||||
TIsolatingInterval = record
|
||||
C: TBigInt;
|
||||
K, H, BoundExp: Cardinal;
|
||||
A, B: TBigInt;
|
||||
end;
|
||||
|
||||
TIsolatingIntervals = specialize TList<TIsolatingInterval>;
|
||||
|
||||
TIsolatingIntervalArray = array of TIsolatingInterval;
|
||||
|
||||
{ TPolynomialRoots }
|
||||
|
||||
TPolynomialRoots = class
|
||||
private
|
||||
// Returns the exponent (base two) of an upper bound for the roots of the given polynomial, i.e. all real roots of
|
||||
// the given polynomial are less or equal than 2^b, where b is the returned positive integer.
|
||||
class function CalcUpperRootBound(constref APolynomial: TBigIntPolynomial): Cardinal;
|
||||
class function CreateIsolatingInterval(constref AC: TBigInt; const AK, AH: Cardinal; constref ABoundExp: Cardinal):
|
||||
TIsolatingInterval;
|
||||
public
|
||||
// Returns root-isolating intervals for non-negative, non-multiple roots.
|
||||
class function BisectIsolation(constref APolynomial: TBigIntPolynomial): TIsolatingIntervalArray;
|
||||
// Returns root-isolating intervals for non-multiple roots in the interval [0, 2^boundexp].
|
||||
class function BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal;
|
||||
const AFindIntegers: Boolean = False): TIsolatingIntervalArray;
|
||||
// Returns non-negative, non-multiple, integer roots in the interval [0, 2^boundexp].
|
||||
class function BisectInteger(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal):
|
||||
TBigIntArray;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPolynomialRoots }
|
||||
|
||||
class function TPolynomialRoots.CalcUpperRootBound(constref APolynomial: TBigIntPolynomial): Cardinal;
|
||||
var
|
||||
i, sign: Integer;
|
||||
an, ai, max: TBigInt;
|
||||
numeratorBit, denominatorBit: Int64;
|
||||
begin
|
||||
// We need a_n > 0 here, so we use -sign(a_n) instead of actually flipping the polynomial.
|
||||
// Sign is not 0 because a_n is not 0.
|
||||
an := APolynomial.Coefficient[APolynomial.Degree];
|
||||
sign := -an.Sign;
|
||||
|
||||
// This is a simplification of Cauchy's bound to avoid division and make it a power of two.
|
||||
// https://en.wikipedia.org/wiki/Geometrical_properties_of_polynomial_roots#Bounds_of_positive_real_roots
|
||||
max := TBigInt.Zero;
|
||||
for i := 0 to APolynomial.Degree - 1 do begin
|
||||
ai := sign * APolynomial.Coefficient[i];
|
||||
if max < ai then
|
||||
max := ai;
|
||||
end;
|
||||
numeratorBit := max.GetMostSignificantBitIndex + 1;
|
||||
denominatorBit := an.GetMostSignificantBitIndex;
|
||||
Result := numeratorBit - denominatorBit;
|
||||
end;
|
||||
|
||||
class function TPolynomialRoots.CreateIsolatingInterval(constref AC: TBigInt; const AK, AH: Cardinal;
|
||||
constref ABoundExp: Cardinal): TIsolatingInterval;
|
||||
begin
|
||||
Result.C := AC;
|
||||
Result.K := AK;
|
||||
Result.H := AH;
|
||||
Result.BoundExp := ABoundExp;
|
||||
if ABoundExp >= AK then
|
||||
begin
|
||||
Result.A := AC << (ABoundExp - AK);
|
||||
Result.B := (AC + AH) << (ABoundExp - AK);
|
||||
end
|
||||
else begin
|
||||
Result.A := AC << (ABoundExp - AK);
|
||||
Result.B := (AC + AH) << (ABoundExp - AK);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TPolynomialRoots.BisectIsolation(constref APolynomial: TBigIntPolynomial): TIsolatingIntervalArray;
|
||||
var
|
||||
boundExp: Cardinal;
|
||||
begin
|
||||
boundExp := CalcUpperRootBound(APolynomial);
|
||||
Result := BisectIsolation(APolynomial, boundExp);
|
||||
end;
|
||||
|
||||
// This is adapted from https://en.wikipedia.org/wiki/Real-root_isolation#Bisection_method
|
||||
class function TPolynomialRoots.BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal;
|
||||
const AFindIntegers: Boolean): TIsolatingIntervalArray;
|
||||
type
|
||||
TWorkItem = record
|
||||
C: TBigInt;
|
||||
K: Cardinal;
|
||||
P: TBigIntPolynomial;
|
||||
end;
|
||||
TWorkStack = specialize TStack<TWorkItem>;
|
||||
var
|
||||
item: TWorkItem;
|
||||
stack: TWorkStack;
|
||||
n, v: Integer;
|
||||
varq: TBigIntPolynomial;
|
||||
iso: TIsolatingIntervals;
|
||||
begin
|
||||
iso := TIsolatingIntervals.Create;
|
||||
stack := TWorkStack.Create;
|
||||
|
||||
item.C := 0;
|
||||
item.K := 0;
|
||||
item.P := APolynomial.ScaleVariableByPowerOfTwo(ABoundExp);
|
||||
stack.Push(item);
|
||||
n := item.P.Degree;
|
||||
|
||||
while stack.Count > 0 do
|
||||
begin
|
||||
item := stack.Pop;
|
||||
if item.P.Coefficient[0] = TBigInt.Zero then
|
||||
begin
|
||||
// Found an integer root at 0.
|
||||
item.P := item.P.DivideByVariable;
|
||||
Dec(n);
|
||||
iso.Add(CreateIsolatingInterval(item.C, item.K, 0, ABoundExp));
|
||||
end;
|
||||
|
||||
varq := item.P.RevertOrderOfCoefficients.TranslateVariableByOne;
|
||||
v := varq.CalcSignVariations;
|
||||
if (v > 1)
|
||||
or ((v = 1) and AFindIntegers and (item.K < ABoundExp)) then
|
||||
begin
|
||||
// Bisects, first new work item is (2c, k + 1, 2^n * q(x/2)).
|
||||
item.C := item.C << 1;
|
||||
Inc(item.K);
|
||||
item.P := item.P.ScaleVariableByHalf.ScaleByPowerOfTwo(n);
|
||||
stack.Push(item);
|
||||
// ... second new work item is (2c + 1, k + 1, 2^n * q((x+1)/2)).
|
||||
item.C := item.C + 1;
|
||||
item.P := item.P.TranslateVariableByOne;
|
||||
stack.Push(item);
|
||||
end
|
||||
else if v = 1 then
|
||||
begin
|
||||
// Found isolating interval.
|
||||
iso.Add(CreateIsolatingInterval(item.C, item.K, 1, ABoundExp));
|
||||
end;
|
||||
end;
|
||||
Result := iso.ToArray;
|
||||
iso.Free;
|
||||
stack.Free;
|
||||
end;
|
||||
|
||||
class function TPolynomialRoots.BisectInteger(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal):
|
||||
TBigIntArray;
|
||||
var
|
||||
intervals: TIsolatingIntervalArray;
|
||||
i: TIsolatingInterval;
|
||||
r: specialize TList<TBigInt>;
|
||||
value: Int64;
|
||||
begin
|
||||
// Calculates isolating intervals.
|
||||
intervals := BisectIsolation(APolynomial, ABoundExp, True);
|
||||
r := specialize TList<TBigInt>.Create;
|
||||
|
||||
for i in intervals do
|
||||
if i.H = 0 then
|
||||
r.Add(i.A)
|
||||
else if i.A.TryToInt64(value) and (APolynomial.CalcValueAt(value) = 0) then
|
||||
r.Add(value)
|
||||
else if i.B.TryToInt64(value) and (APolynomial.CalcValueAt(value) = 0) then
|
||||
r.Add(value);
|
||||
|
||||
Result := r.ToArray;
|
||||
r.Free;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-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
|
||||
|
@ -22,26 +22,37 @@ unit UNeverTellMeTheOdds;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver;
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver, UNumberTheory, UBigInt, UPolynomial, UPolynomialRoots;
|
||||
|
||||
type
|
||||
|
||||
{ THailstone }
|
||||
|
||||
THailstone = record
|
||||
X, Y, Z: Int64;
|
||||
VX, VY, VZ: Integer;
|
||||
THailstone = class
|
||||
public
|
||||
P0, P1, P2: Int64;
|
||||
V0, V1, V2: Integer;
|
||||
constructor Create(const ALine: string);
|
||||
constructor Create;
|
||||
end;
|
||||
|
||||
THailstones = specialize TList<THailstone>;
|
||||
THailstones = specialize TObjectList<THailstone>;
|
||||
|
||||
TInt64Array = array of Int64;
|
||||
|
||||
{ TNeverTellMeTheOdds }
|
||||
|
||||
TNeverTellMeTheOdds = class(TSolver)
|
||||
private
|
||||
FMin, FMax: Int64;
|
||||
FHailStones: THailstones;
|
||||
FHailstones: THailstones;
|
||||
function AreIntersecting(constref AHailstone1, AHailstone2: THailstone): Boolean;
|
||||
function FindRockThrow(const AIndex0, AIndex1, AIndex2: Integer): Int64;
|
||||
procedure CalcCollisionPolynomials(constref AHailstone0, AHailstone1, AHailstone2: THailstone; out OPolynomial0,
|
||||
OPolynomial1: TBigIntPolynomial);
|
||||
function CalcRockThrowCollisionOptions(constref AHailstone0, AHailstone1, AHailstone2: THailstone): TInt64Array;
|
||||
function ValidateRockThrow(constref AHailstone0, AHailstone1, AHailstone2: THailstone; const AT0, AT1: Int64):
|
||||
Int64;
|
||||
public
|
||||
constructor Create(const AMin: Int64 = 200000000000000; const AMax: Int64 = 400000000000000);
|
||||
destructor Destroy; override;
|
||||
|
@ -53,6 +64,26 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ THailstone }
|
||||
|
||||
constructor THailstone.Create(const ALine: string);
|
||||
var
|
||||
split: TStringArray;
|
||||
begin
|
||||
split := ALine.Split([',', '@']);
|
||||
P0 := StrToInt64(Trim(split[0]));
|
||||
P1 := StrToInt64(Trim(split[1]));
|
||||
P2 := StrToInt64(Trim(split[2]));
|
||||
V0 := StrToInt(Trim(split[3]));
|
||||
V1 := StrToInt(Trim(split[4]));
|
||||
V2 := StrToInt(Trim(split[5]));
|
||||
end;
|
||||
|
||||
constructor THailstone.Create;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOdds }
|
||||
|
||||
function TNeverTellMeTheOdds.AreIntersecting(constref AHailstone1, AHailstone2: THailstone): Boolean;
|
||||
|
@ -60,58 +91,432 @@ var
|
|||
m1, m2, x, y: Double;
|
||||
begin
|
||||
Result := False;
|
||||
m1 := AHailstone1.VY / AHailstone1.VX;
|
||||
m2 := AHailstone2.VY / AHailstone2.VX;
|
||||
m1 := AHailstone1.V1 / AHailstone1.V0;
|
||||
m2 := AHailstone2.V1 / AHailstone2.V0;
|
||||
if m1 <> m2 then
|
||||
begin
|
||||
x := (AHailstone2.Y - m2 * AHailstone2.X - AHailstone1.Y + m1 * AHailstone1.X) / (m1 - m2);
|
||||
x := (AHailstone2.P1 - m2 * AHailstone2.P0
|
||||
- AHailstone1.P1 + m1 * AHailstone1.P0)
|
||||
/ (m1 - m2);
|
||||
if (FMin <= x) and (x <= FMax)
|
||||
and (x * Sign(AHailstone1.VX) >= AHailstone1.X * Sign(AHailstone1.VX))
|
||||
and (x * Sign(AHailstone2.VX) >= AHailstone2.X * Sign(AHailstone2.VX)) then
|
||||
and (x * Sign(AHailstone1.V0) >= AHailstone1.P0 * Sign(AHailstone1.V0))
|
||||
and (x * Sign(AHailstone2.V0) >= AHailstone2.P0 * Sign(AHailstone2.V0))
|
||||
then
|
||||
begin
|
||||
y := m1 * (x - AHailstone1.X) + AHailstone1.Y;
|
||||
y := m1 * (x - AHailstone1.P0) + AHailstone1.P1;
|
||||
if (FMin <= y) and (y <= FMax) then
|
||||
Result := True
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.FindRockThrow(const AIndex0, AIndex1, AIndex2: Integer): Int64;
|
||||
var
|
||||
t0, t1: TInt64Array;
|
||||
i, j: Int64;
|
||||
begin
|
||||
t0 := CalcRockThrowCollisionOptions(FHailstones[AIndex0], FHailstones[AIndex1], FHailstones[AIndex2]);
|
||||
t1 := CalcRockThrowCollisionOptions(FHailstones[AIndex1], FHailstones[AIndex0], FHailstones[AIndex2]);
|
||||
|
||||
Result := 0;
|
||||
for i in t0 do
|
||||
begin
|
||||
for j in t1 do
|
||||
begin
|
||||
Result := ValidateRockThrow(FHailstones[AIndex0], FHailstones[AIndex1], FHailstones[AIndex2], i, j);
|
||||
if Result > 0 then
|
||||
Break;
|
||||
end;
|
||||
if Result > 0 then
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOdds.CalcCollisionPolynomials(constref AHailstone0, AHailstone1, AHailstone2: THailstone; out
|
||||
OPolynomial0, OPolynomial1: TBigIntPolynomial);
|
||||
var
|
||||
k: array[0..74] of TBigInt;
|
||||
begin
|
||||
// Solving this non-linear equation system, with velocities V_i and start positions P_i:
|
||||
// V_0 * t_0 + P_0 = V_x * t_0 + P_x
|
||||
// V_1 * t_1 + P_1 = V_x * t_1 + P_x
|
||||
// V_2 * t_2 + P_2 = V_x * t_2 + P_x
|
||||
// Which gives:
|
||||
// P_x = (V_0 - V_x) * t_0 + P_0
|
||||
// V_x = (V_0 * t_0 - V_1 * t_1 + P_0 - P_1) / (t_0 - t_1)
|
||||
// And with vertex components:
|
||||
// 1: 0 = (t_1 - t_0) * (V_00 * t_0 - V_20 * t_2 + P_00 - P_20)
|
||||
// - (t_2 - t_0) * (V_00 * t_0 - V_10 * t_1 + P_00 - P_10)
|
||||
// 2: t_1 = (((V_01 - V_21) * t_2 + P_11 - P_21) * t_0 + (P_01 - P_11) * t_2)
|
||||
// / ((V_01 - V_11) * t_0 + (V_11 - V_21) * t_2 + P_01 - P_21)
|
||||
// 3: t_2 = (((V_02 - V_12) * t_1 + P_22 - P_12) * t_0 + (P_02 - P_22) * t_1)
|
||||
// / ((V_02 - V_22) * t_0 + (V_22 - V_12) * t_1 + P_02 - P_12)
|
||||
// for t_0, t_1, t_2 not pairwise equal.
|
||||
// With some substitutions depending only on t_0 this gives
|
||||
// 1: 0 = (t_1 - t_0) * (a_1 - V_20 * t_2) - (t_2 - t_0) * (a_2 - V_10 * t_1)
|
||||
// 2: t_1 = (b_0 + b_1 * t_2) / (c_0 + c_1 * t_2)
|
||||
// 3: t_2 = (d_0 + d_1 * t_1) / (e_0 + e_1 * t_1)
|
||||
// And 3 in 2 gives:
|
||||
// 4: f_2 * t_1^2 + f_1 * t_1 - f_0 = 0
|
||||
// Then, with 4 and 3 in 1 and many substitutions (see constants k below, now independent of t_0), the equation
|
||||
// 5: 0 = p_0(t_0) + p_1(t_0) * sqrt(p_2(t_0))
|
||||
// can be constructed, where p_0, p_1, and p_2 are polynomials in t_0. Since we are searching for an integer solution,
|
||||
// we assume that there is an integer t_0 that is a root of both p_0 and p_1, which would solve the equation.
|
||||
|
||||
// Subsitutions depending on t_0:
|
||||
// a_1 = V_00 * t_0 + P_00 - P_20
|
||||
// a_2 = V_00 * t_0 + P_00 - P_10
|
||||
// b_0 = (P_11 - P_21) * t_0
|
||||
// b_1 = (V_01 - V_21) * t_0 + P_01 - P_11
|
||||
// c_0 = (V_01 - V_11) * t_0 + P_01 - P_21
|
||||
// c_1 = V_11 - V_21
|
||||
// d_0 = (P_22 - P_12) * t_0
|
||||
// d_1 = (V_02 - V_12) * t_0 + P_02 - P_22
|
||||
// e_0 = (V_02 - V_22) * t_0 + P_02 - P_12
|
||||
// e_1 = V_22 - V_12
|
||||
// f_0 = b_1 * d_0 + b_0 * e_0
|
||||
// f_1 = c_0 * e_0 + c_1 * d_0 - b_0 * e_1 - b_1 * d_1
|
||||
// f_2 = c_0 * e_1 + c_1 * d_1
|
||||
|
||||
// Calculations for equation 5 (4 and 3 in 1).
|
||||
// 1: 0 = (t_1 - t_0) * (a_1 - V_20 * t_2) - (t_2 - t_0) * (a_2 - V_10 * t_1)
|
||||
// 3: (e_0 + e_1 * t_1) * t_2 = (d_0 + d_1 * t_1)
|
||||
// 0 = (t_1 - t_0) * (a_1 - V_20 * t_2) - (t_2 - t_0) * (a_2 - V_10 * t_1)
|
||||
// = (t_1 - t_0) * (a_1 * (e_0 + e_1 * t_1) - V_20 * (e_0 + e_1 * t_1) * t_2) - ((e_0 + e_1 * t_1) * t_2 - (e_0 + e_1 * t_1) * t_0) * (a_2 - V_10 * t_1)
|
||||
// = (t_1 - t_0) * (a_1 * (e_0 + e_1 * t_1) - V_20 * (d_0 + d_1 * t_1)) - ((d_0 + d_1 * t_1) - (e_0 + e_1 * t_1) * t_0) * (a_2 - V_10 * t_1)
|
||||
// = (t_1 - t_0) * (a_1 * e_0 + a_1 * e_1 * t_1 - V_20 * d_0 - V_20 * d_1 * t_1) - (d_0 + d_1 * t_1 - e_0 * t_0 - e_1 * t_1 * t_0) * (a_2 - V_10 * t_1)
|
||||
// = (a_1 * e_1 - V_20 * d_1) * t_1^2 + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1)) * t_1 - t_0 * (a_1 * e_0 - V_20 * d_0)
|
||||
// - ( - V_10 * (d_1 - e_1 * t_0) * t_1^2 + ((d_1 - e_1 * t_0) * a_2 - V_10 * (d_0 - e_0 * t_0)) * t_1 + (d_0 - e_0 * t_0) * a_2)
|
||||
// = (a_1 * e_1 - V_20 * d_1 + V_10 * (d_1 - e_1 * t_0)) * t_1^2
|
||||
// + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1) - (d_1 - e_1 * t_0) * a_2 + V_10 * (d_0 - e_0 * t_0)) * t_1
|
||||
// + t_0 * (V_20 * d_0 - a_1 * e_0) + (e_0 * t_0 - d_0) * a_2
|
||||
// Inserting 4, solved for t_0: t_1 = - f_1 / (2 * f_2) + sqrt((f_1 / (2 * f_2))^2 + f_0 / f_2)
|
||||
// = (a_1 * e_1 - V_20 * d_1 + V_10 * (d_1 - e_1 * t_0)) * (f_1^2 + 2 * f_0 * f_2 - f_1 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1) - (d_1 - e_1 * t_0) * a_2 + V_10 * (d_0 - e_0 * t_0)) * (- f_1 * f_2 + f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + t_0 * (V_20 * d_0 - a_1 * e_0) * 2 * f_2^2 + (e_0 * t_0 - d_0) * a_2 * 2 * f_2^2
|
||||
|
||||
// a_1 = V_00 * t_0 + k_0
|
||||
// a_2 = V_00 * t_0 + k_1
|
||||
// b_0 = k_2 * t_0
|
||||
// b_1 = k_10 * t_0 + k_3
|
||||
// c_0 = k_11 * t_0 + k_4
|
||||
// d_0 = k_5 * t_0
|
||||
// d_1 = k_12 * t_0 + k_6
|
||||
// e_0 = k_13 * t_0 + k_7
|
||||
// f_2 = (k_11 * t_0 + k_4) * k_9 + k_8 * (k_12 * t_0 + k_6)
|
||||
// = (k_11 * k_9 + k_8 * k_12) * t_0 + k_4 * k_9 + k_8 * k_6
|
||||
// = k_14 * t_0 + k_15
|
||||
// f_1 = (k_11 * t_0 + k_4) * (k_13 * t_0 + k_7) + k_8 * k_5 * t_0 - k_2 * t_0 * k_9 - (k_10 * t_0 + k_3) * (k_12 * t_0 + k_6)
|
||||
// = (k_11 * k_13 - k_10 * k_12) * t_0^2 + (k_11 * k_7 + k_4 * k_12 + k_8 * k_5 - k_2 * k_9 - k_10 * k_6 - k_3 * k_12) * t_0 + k_4 * k_7 - k_3 * k_6
|
||||
// = k_16 * t_0^2 + k_17 * t_0 + k_18
|
||||
// f_0 = (k_10 * t_0 + k_3) * k_5 * t_0 + k_2 * t_0 * (k_13 * t_0 + k_7)
|
||||
// = (k_10 * k_5 + k_2 * k_13) * t_0^2 + (k_3 * k_5 + k_2 * k_7) * t_0
|
||||
// = k_19 * t_0^2 + k_20 * t_0
|
||||
|
||||
k[0] := AHailstone0.P0 - AHailstone2.P0;
|
||||
k[1] := AHailstone0.P0 - AHailstone1.P0;
|
||||
k[2] := AHailstone1.P1 - AHailstone2.P1;
|
||||
k[3] := AHailstone0.P1 - AHailstone1.P1;
|
||||
k[4] := AHailstone0.P1 - AHailstone2.P1;
|
||||
k[5] := AHailstone2.P2 - AHailstone1.P2;
|
||||
k[6] := AHailstone0.P2 - AHailstone2.P2;
|
||||
k[7] := AHailstone0.P2 - AHailstone1.P2;
|
||||
k[8] := AHailstone1.V1 - AHailstone2.V1;
|
||||
k[9] := AHailstone2.V2 - AHailstone1.V2;
|
||||
k[10] := AHailstone0.V1 - AHailstone2.V1;
|
||||
k[11] := AHailstone0.V1 - AHailstone1.V1;
|
||||
k[12] := AHailstone0.V2 - AHailstone1.V2;
|
||||
k[13] := AHailstone0.V2 - AHailstone2.V2;
|
||||
|
||||
k[14] := k[11] * k[9] + k[8] * k[12];
|
||||
k[15] := k[4] * k[9] + k[8] * k[6];
|
||||
k[16] := k[11] * k[13] - k[10] * k[12];
|
||||
k[17] := k[11] * k[7] + k[4] * k[13] + k[8] * k[5] - k[2] * k[9] - k[10] * k[6] - k[3] * k[12];
|
||||
k[18] := k[4] * k[7] - k[3] * k[6];
|
||||
k[19] := k[10] * k[5] + k[2] * k[13];
|
||||
k[20] := k[3] * k[5] + k[2] * k[7];
|
||||
|
||||
// Additional substitutions.
|
||||
// a_1 * k_9 - V_20 * d_1
|
||||
// = (V_00 * t_0 + k_0) * k_9 - V_20 * (k_12 * t_0 + k_6)
|
||||
// = (V_00 * k_9 - V_20 * k_12) * t_0 + k_0 * k_9 - V_20 * k_6
|
||||
// = k_21 * t_0 + k_22
|
||||
// d_1 - k_9 * t_0
|
||||
// = k_12 * t_0 + k_6 - k_9 * t_0
|
||||
// = (k_12 - k_9) * t_0 + k_6
|
||||
// a_1 * e_0 - V_20 * d_0
|
||||
// = (V_00 * t_0 + k_0) * (k_13 * t_0 + k_7) - V_20 * k_5 * t_0
|
||||
// = V_00 * k_13 * t_0^2 + (V_00 * k_7 + k_0 * k_13 - V_20 * k_5) * t_0 + k_0 * k_7
|
||||
// = k_23 * t_0^2 + k_24 * t_0 + k_25
|
||||
// d_0 - e_0 * t_0
|
||||
// = k_5 * t_0 - k_13 * t_0^2 - k_7 * t_0
|
||||
// = - k_13 * t_0^2 + k_26 * t_0
|
||||
// f_1^2
|
||||
// = (k_16 * t_0^2 + k_17 * t_0 + k_18)^2
|
||||
// = k_16^2 * t_0^4 + k_17^2 * t_0^2 + k_18^2 + 2 * k_16 * t_0^2 * k_17 * t_0 + 2 * k_16 * t_0^2 * k_18 + 2 * k_17 * t_0 * k_18
|
||||
// = k_16^2 * t_0^4 + 2 * k_16 * k_17 * t_0^3 + (k_17^2 + 2 * k_16 * k_18) * t_0^2 + 2 * k_17 * k_18 * t_0 + k_18^2
|
||||
// = k_16^2 * t_0^4 + k_27 * t_0^3 + k_29 * t_0^2 + k_30 * t_0 + k_18^2
|
||||
// f_2^2
|
||||
// = (k_14 * t_0 + k_15)^2
|
||||
// = k_14^2 * t_0^2 + 2 * k_14 * k_15 * t_0 + k_15^2
|
||||
// = k_14^2 * t_0^2 + k_31 * t_0 + k_15^2
|
||||
// f_0 * f_2
|
||||
// = (k_19 * t_0^2 + k_20 * t_0) * (k_14 * t_0 + k_15)
|
||||
// = k_19 * k_14 * t_0^3 + (k_19 * k_15 + k_20 * k_14) * t_0^2 + k_20 * k_15 * t_0
|
||||
// = k_33 * t_0^3 + k_34 * t_0^2 + k_35 * t_0
|
||||
// f_1^2 + 4 * f_0 * f_2
|
||||
// = k_16^2 * t_0^4 + k_27 * t_0^3 + k_29 * t_0^2 + k_30 * t_0 + k_18^2 + 4 * (k_33 * t_0^3 + k_34 * t_0^2 + k_35 * t_0)
|
||||
// = k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59
|
||||
// f_1^2 + 2 * f_0 * f_2
|
||||
// = k_16^2 * t_0^4 + k_27 * t_0^3 + k_29 * t_0^2 + k_30 * t_0 + k_18^2 + 2 * (k_33 * t_0^3 + k_34 * t_0^2 + k_35 * t_0)
|
||||
// = k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59
|
||||
|
||||
k[21] := AHailstone0.V0 * k[9] - AHailstone2.V0 * k[12];
|
||||
k[22] := k[0] * k[9] - AHailstone2.V0 * k[6];
|
||||
k[23] := AHailstone0.V0 * k[13];
|
||||
k[24] := AHailstone0.V0 * k[7] + k[0] * k[13] - AHailstone2.V0 * k[5];
|
||||
k[25] := k[0] * k[7];
|
||||
k[26] := k[5] - k[7];
|
||||
k[27] := 2 * k[16] * k[17];
|
||||
k[28] := k[17] * k[17];
|
||||
k[29] := k[28] + 2 * k[16] * k[18];
|
||||
k[30] := 2 * k[17] * k[18];
|
||||
k[31] := 2 * k[14] * k[15];
|
||||
k[32] := k[14] * k[14];
|
||||
k[33] := k[19] * k[14];
|
||||
k[34] := k[19] * k[15] + k[20] * k[14];
|
||||
k[35] := k[20] * k[15];
|
||||
k[36] := k[15] * k[15];
|
||||
k[37] := k[16] * k[16];
|
||||
k[38] := k[27] + 2 * k[33];
|
||||
k[39] := k[29] + 2 * k[34];
|
||||
k[40] := k[30] + 2 * k[35];
|
||||
k[41] := k[21] + AHailstone1.V0 * (k[12] - k[9]);
|
||||
k[42] := k[22] + AHailstone1.V0 * k[6];
|
||||
k[43] := k[23] - k[21] - AHailstone1.V0 * k[13] - (k[12] - k[9]) * AHailstone0.V0;
|
||||
k[44] := k[24] - k[22] + AHailstone1.V0 * k[26] - (k[12] - k[9]) * k[1] - k[6] * AHailstone0.V0;
|
||||
k[45] := k[25] - k[6] * k[1];
|
||||
k[46] := k[43] * k[14] - k[41] * k[16];
|
||||
k[47] := k[43] * k[15] + k[44] * k[14] - k[42] * k[16] - k[41] * k[17];
|
||||
k[48] := k[44] * k[15] + k[45] * k[14] - k[42] * k[17] - k[41] * k[18];
|
||||
k[49] := k[45] * k[15] - k[42] * k[18];
|
||||
k[50] := k[43] * k[16];
|
||||
k[51] := k[41] * k[37] - k[50] * k[14];
|
||||
k[52] := k[43] * k[17] + k[44] * k[16];
|
||||
k[53] := k[41] * k[38] + k[42] * k[37] - k[52] * k[14] - k[50] * k[15];
|
||||
k[54] := k[43] * k[18] + k[44] * k[17] + k[45] * k[16];
|
||||
k[55] := k[41] * k[39] + k[42] * k[38] - k[54] * k[14] - k[52] * k[15];
|
||||
k[56] := k[44] * k[18] + k[45] * k[17];
|
||||
k[57] := k[41] * k[40] + k[42] * k[39] - k[56] * k[14] - k[54] * k[15];
|
||||
k[58] := k[45] * k[18];
|
||||
k[59] := k[18] * k[18];
|
||||
k[60] := k[41] * k[59] + k[42] * k[40] - k[58] * k[14] - k[56] * k[15];
|
||||
k[61] := k[42] * k[59] - k[58] * k[15];
|
||||
k[62] := k[13] * AHailstone0.V0 - k[23];
|
||||
k[63] := 2 * k[32] * k[62];
|
||||
k[64] := k[13] * k[1] - k[26] * AHailstone0.V0 - k[24];
|
||||
k[65] := 2 * (k[31] * k[62] + k[32] * k[64]);
|
||||
k[66] := - k[26] * k[1] - k[25];
|
||||
k[67] := 2 * (k[36] * k[62] + k[31] * k[64] + k[32] * k[66]);
|
||||
k[68] := 2 * (k[36] * k[64] + k[31] * k[66]);
|
||||
k[69] := 2 * k[36] * k[66];
|
||||
k[70] := k[51] + k[63];
|
||||
k[71] := k[53] + k[65];
|
||||
k[72] := k[55] + k[67];
|
||||
k[73] := k[57] + k[68];
|
||||
k[74] := k[60] + k[69];
|
||||
// Unused, they are part of the polynomial inside the square root.
|
||||
//k[75] := k[27] + 4 * k[33];
|
||||
//k[76] := k[29] + 4 * k[34];
|
||||
//k[77] := k[30] + 4 * k[35];
|
||||
|
||||
// Continuing calculations for equation 5.
|
||||
// 0 = (k_21 * t_0 + k_22 + V_10 * ((k_12 - k_9) * t_0 + k_6)) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59 -+ f_1 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + (k_23 * t_0^2 + k_24 * t_0 + k_25 - t_0 * (k_21 * t_0 + k_22) - ((k_12 - k_9) * t_0 + k_6) * a_2 - V_10 * (k_13 * t_0^2 - k_26 * t_0)) * (- f_1 * f_2 +- f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59 -+ f_1 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + ((k_23 - k_21 - V_10 * k_13 - (k_12 - k_9) * V_00) * t_0^2 + (k_24 - k_22 + V_10 * k_26 - (k_12 - k_9) * k_1 - k_6 * V_00) * t_0 + k_25 - k_6 * k_1) * (- f_1 * f_2 +- f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// -+ (k_41 * t_0 + k_42) * f_1 * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_43 * t_0^2 + k_44 * t_0 + k_45) * (- f_1 * f_2 +- f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// -+ (k_41 * t_0 + k_42) * f_1 * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// - (k_43 * t_0^2 + k_44 * t_0 + k_45) * f_1 * f_2
|
||||
// +- (k_43 * t_0^2 + k_44 * t_0 + k_45) * f_2 * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = +- ((k_43 * t_0^2 + k_44 * t_0 + k_45) * f_2 - (k_41 * t_0 + k_42) * f_1) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// - (k_43 * t_0^2 + k_44 * t_0 + k_45) * f_1 * f_2
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = +- ((k_43 * t_0^2 + k_44 * t_0 + k_45) * (k_14 * t_0 + k_15) - (k_41 * t_0 + k_42) * (k_16 * t_0^2 + k_17 * t_0 + k_18)) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// - (k_43 * t_0^2 + k_44 * t_0 + k_45) * (k_16 * t_0^2 + k_17 * t_0 + k_18) * (k_14 * t_0 + k_15)
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * (V_00 * t_0 + k_1) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = +- (
|
||||
// (k_43 * k_14 - k_41 * k_16) * t_0^3
|
||||
// + (k_43 * k_15 + k_44 * k_14 - k_42 * k_16 - k_41 * k_17) * t_0^2
|
||||
// + (k_44 * k_15 + k_45 * k_14 - k_42 * k_17 - k_41 * k_18) * t_0
|
||||
// + k_45 * k_15 - k_42 * k_18
|
||||
// ) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_41 * k_37 - k_43 * k_16 * k_14) * t_0^5
|
||||
// + (k_41 * k_38 + k_42 * k_37 - k_43 * k_17 * k_14 - k_44 * k_16 * k_14 - k_43 * k_16 * k_15) * t_0^4
|
||||
// + (k_41 * k_39 + k_42 * k_38 - k_43 * k_18 * k_14 - k_44 * k_17 * k_14 - k_45 * k_16 * k_14 - k_43 * k_17 * k_15 - k_44 * k_16 * k_15) * t_0^3
|
||||
// + (k_41 * k_40 + k_42 * k_39 - k_44 * k_18 * k_14 - k_45 * k_17 * k_14 - k_43 * k_18 * k_15 - k_44 * k_17 * k_15 - k_45 * k_16 * k_15) * t_0^2
|
||||
// + (k_41 * k_59 + k_42 * k_40 - k_45 * k_18 * k_14 - k_44 * k_18 * k_15 - k_45 * k_17 * k_15) * t_0
|
||||
// + k_42 * k_59 - k_45 * k_18 * k_15
|
||||
// + 2 * (k_13 * V_00 * k_14^2 - k_23 * k_14^2) * t_0^5
|
||||
// + 2 * (k_13 * V_00 * k_31 + k_13 * k_1 * k_14^2 - k_26 * V_00 * k_14^2 - k_23 * k_31 - k_24 * k_14^2) * t_0^4
|
||||
// + 2 * (k_13 * V_00 * k_15^2 + k_13 * k_1 * k_31 - k_26 * V_00 * k_31 - k_26 * k_1 * k_14^2 - k_23 * k_15^2 - k_24 * k_31 - k_25 * k_14^2) * t_0^3
|
||||
// + 2 * (k_13 * k_1 * k_15^2 - k_26 * V_00 * k_15^2 - k_26 * k_1 * k_31 - k_24 * k_15^2 - k_25 * k_31) * t_0^2
|
||||
// + 2 * (- k_26 * k_1 * k_15^2 - k_25 * k_15^2) * t_0
|
||||
// 0 = +- (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_51 + k_63) * t_0^5 + (k_53 + k_65) * t_0^4 + (k_55 + k_67) * t_0^3 + (k_57 + k_68) * t_0^2 + (k_60 + k_69) * t_0 + k_61
|
||||
// 0 = +- (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49) * sqrt(k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59)
|
||||
// + k_70 * t_0^5 + k_71 * t_0^4 + k_72 * t_0^3 + k_73 * t_0^2 + k_74 * t_0 + k_61
|
||||
|
||||
OPolynomial0 := TBigIntPolynomial.Create([k[61], k[74], k[73], k[72], k[71], k[70]]);
|
||||
OPolynomial1 := TBigIntPolynomial.Create([k[49], k[48], k[47], k[46]]);
|
||||
|
||||
// Squaring that formula eliminates the square root, but may lead to a polynomial with all coefficients zero in some
|
||||
// cases. Therefore this part is merely included for the interested reader.
|
||||
// -+ (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49) * sqrt(k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59) =
|
||||
// k_70 * t_0^5 + k_71 * t_0^4 + k_72 * t_0^3 + k_73 * t_0^2 + k_74 * t_0 + k_61
|
||||
// (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49)^2 * (k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59) =
|
||||
// (k_70 * t_0^5 + k_71 * t_0^4 + k_72 * t_0^3 + k_73 * t_0^2 + k_74 * t_0 + k_61)^2
|
||||
// 0 =
|
||||
// (k_46^2 * t_0^6
|
||||
// + 2 * k_46 * k_47 * t_0^5
|
||||
// + k_47^2 * t_0^4 + 2 * k_46 * k_48 * t_0^4
|
||||
// + 2 * k_46 * k_49 * t_0^3 + 2 * k_47 * k_48 * t_0^3
|
||||
// + k_48^2 * t_0^2 + 2 * k_47 * k_49 * t_0^2
|
||||
// + 2 * k_48 * k_49 * t_0
|
||||
// + k_49^2
|
||||
// ) * (k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59)
|
||||
// - k_70^2 * t_0^10
|
||||
// - 2 * k_70 * k_71 * t_0^9
|
||||
// - (k_71^2 + 2 * k_70 * k_72) * t_0^8
|
||||
// - 2 * (k_70 * k_73 + k_71 * k_72) * t_0^7
|
||||
// - (k_72^2 + 2 * k_70 * k_74 + 2 * k_71 * k_73) * t_0^6
|
||||
// - 2 * (k_70 * k_61 + k_71 * k_74 + k_72 * k_73) * t_0^5
|
||||
// - (k_73^2 + 2 * k_71 * k_61 + 2 * k_72 * k_74) * t_0^4
|
||||
// - 2 * (k_72 * k_61 + k_73 * k_74) * t_0^3
|
||||
// - (k_74^2 + 2 * k_73 * k_61) * t_0^2
|
||||
// - 2 * k_74 * k_61 * t_0
|
||||
// - k_61^2
|
||||
// 0 = ak_10 * t_0^10 + ak_9 * t_0^9 + ak_8 * t_0^8 + ak_7 * t_0^7 + ak_6 * t_0^6 + ak_5 * t_0^5 + ak_4 * t_0^4 + ak_3 * t_0^3 + ak_2 * t_0^2 + ak_1 * t_0 + ak_0
|
||||
|
||||
//k[78] := k[46] * k[46];
|
||||
//k[79] := 2 * k[46] * k[47];
|
||||
//k[80] := k[47] * k[47] + 2 * k[46] * k[48];
|
||||
//k[81] := 2 * (k[46] * k[49] + k[47] * k[48]);
|
||||
//k[82] := k[48] * k[48] + 2 * k[47] * k[49];
|
||||
//k[83] := 2 * k[48] * k[49];
|
||||
//k[84] := k[49] * k[49];
|
||||
//ak[0] := k[59] * k[84] - k[61] * k[61];
|
||||
//ak[1] := k[77] * k[84] + k[59] * k[83] - 2 * k[74] * k[61];
|
||||
//ak[2] := k[76] * k[84] + k[77] * k[83] + k[59] * k[82] - k[74] * k[74] - 2 * k[73] * k[61];
|
||||
//ak[3] := k[76] * k[83] + k[77] * k[82] + k[59] * k[81] + k[75] * k[84]
|
||||
// - 2 * (k[72] * k[61] + k[73] * k[74]);
|
||||
//ak[4] := k[37] * k[84] + k[76] * k[82] + k[77] * k[81] + k[59] * k[80] + k[75] * k[83] - k[73] * k[73]
|
||||
// - 2 * (k[71] * k[61] + k[72] * k[74]);
|
||||
//ak[5] := k[37] * k[83] + k[76] * k[81] + k[77] * k[80] + k[59] * k[79] + k[75] * k[82]
|
||||
// - 2 * (k[70] * k[61] + k[71] * k[74] + k[72] * k[73]);
|
||||
//ak[6] := k[37] * k[82] + k[76] * k[80] + k[77] * k[79] + k[59] * k[78] + k[75] * k[81] - k[72] * k[72]
|
||||
// - 2 * (k[70] * k[74] + k[71] * k[73]);
|
||||
//ak[7] := k[37] * k[81] + k[76] * k[79] + k[77] * k[78] + k[75] * k[80] - 2 * (k[70] * k[73] + k[71] * k[72]);
|
||||
//ak[8] := k[37] * k[80] + k[75] * k[79] + k[76] * k[78] - k[71] * k[71] - 2 * k[70] * k[72];
|
||||
//ak[9] := k[37] * k[79] + k[75] * k[78] - 2 * k[70] * k[71];
|
||||
//ak[10] := k[37] * k[78] - k[70] * k[70];
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.CalcRockThrowCollisionOptions(constref AHailstone0, AHailstone1, AHailstone2: THailstone):
|
||||
TInt64Array;
|
||||
var
|
||||
a0, a1: TBigIntPolynomial;
|
||||
a0Roots, a1Roots: TBigIntArray;
|
||||
options: specialize TList<Int64>;
|
||||
i, j: TBigInt;
|
||||
val: Int64;
|
||||
begin
|
||||
CalcCollisionPolynomials(AHailstone0, AHailstone1, AHailstone2, a0, a1);
|
||||
a0Roots := TPolynomialRoots.BisectInteger(a0, 64);
|
||||
a1Roots := TPolynomialRoots.BisectInteger(a1, 64);
|
||||
|
||||
options := specialize TList<Int64>.Create;
|
||||
for i in a0Roots do
|
||||
for j in a1Roots do
|
||||
if (i = j) and i.TryToInt64(val) then
|
||||
options.Add(val);
|
||||
Result := options.ToArray;
|
||||
options.Free;
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.ValidateRockThrow(constref AHailstone0, AHailstone1, AHailstone2: THailstone; const AT0,
|
||||
AT1: Int64): Int64;
|
||||
var
|
||||
divisor, t: Int64;
|
||||
rock: THailstone;
|
||||
begin
|
||||
// V_x = (V_0 * t_0 - V_1 * t_1 + P_0 - P_1) / (t_0 - t_1)
|
||||
divisor := AT0 - AT1;
|
||||
rock := THailstone.Create;
|
||||
rock.V0 := (AHailstone0.V0 * AT0 - AHailstone1.V0 * AT1 + AHailstone0.P0 - AHailstone1.P0) div divisor;
|
||||
rock.V1 := (AHailstone0.V1 * AT0 - AHailstone1.V1 * AT1 + AHailstone0.P1 - AHailstone1.P1) div divisor;
|
||||
rock.V2 := (AHailstone0.V2 * AT0 - AHailstone1.V2 * AT1 + AHailstone0.P2 - AHailstone1.P2) div divisor;
|
||||
|
||||
// P_x = (V_0 - V_x) * t_0 + P_0
|
||||
rock.P0 := (AHailstone0.V0 - rock.V0) * AT0 + AHailstone0.P0;
|
||||
rock.P1 := (AHailstone0.V1 - rock.V1) * AT0 + AHailstone0.P1;
|
||||
rock.P2 := (AHailstone0.V2 - rock.V2) * AT0 + AHailstone0.P2;
|
||||
|
||||
Result := rock.P0 + rock.P1 + rock.P2;
|
||||
|
||||
// Checks collision with the third hailstone.
|
||||
if ((AHailstone2.V0 = rock.V0) and (AHailstone2.P0 <> rock.P0))
|
||||
or ((AHailstone2.V1 = rock.V1) and (AHailstone2.P1 <> rock.P1))
|
||||
or ((AHailstone2.V2 = rock.V2) and (AHailstone2.P2 <> rock.P2)) then
|
||||
Result := 0
|
||||
else begin
|
||||
t := (AHailstone2.P0 - rock.P0) div (rock.V0 - AHailstone2.V0);
|
||||
if (t <> (AHailstone2.P1 - rock.P1) div (rock.V1 - AHailstone2.V1))
|
||||
or (t <> (AHailstone2.P2 - rock.P2) div (rock.V2 - AHailstone2.V2)) then
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
rock.Free;
|
||||
end;
|
||||
|
||||
constructor TNeverTellMeTheOdds.Create(const AMin: Int64; const AMax: Int64);
|
||||
begin
|
||||
FMin := AMin;
|
||||
FMax := AMax;
|
||||
FHailStones := THailstones.Create;
|
||||
FHailstones := THailstones.Create;
|
||||
end;
|
||||
|
||||
destructor TNeverTellMeTheOdds.Destroy;
|
||||
begin
|
||||
FHailStones.Free;
|
||||
FHailstones.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOdds.ProcessDataLine(const ALine: string);
|
||||
var
|
||||
split: TStringArray;
|
||||
hailstone: THailstone;
|
||||
begin
|
||||
split := ALine.Split([',', '@']);
|
||||
hailstone.X := StrToInt64(Trim(split[0]));
|
||||
hailstone.Y := StrToInt64(Trim(split[1]));
|
||||
hailstone.Z := StrToInt64(Trim(split[2]));
|
||||
hailstone.VX := StrToInt(Trim(split[3]));
|
||||
hailstone.VY := StrToInt(Trim(split[4]));
|
||||
hailstone.VZ := StrToInt(Trim(split[5]));
|
||||
FHailStones.Add(hailstone);
|
||||
FHailstones.Add(THailstone.Create(ALine));
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOdds.Finish;
|
||||
var
|
||||
i, j: Integer;
|
||||
begin
|
||||
for i := 0 to FHailStones.Count - 2 do
|
||||
for j := i + 1 to FHailStones.Count - 1 do
|
||||
if AreIntersecting(FHailStones[i], FHailStones[j]) then
|
||||
for i := 0 to FHailstones.Count - 2 do
|
||||
for j := i + 1 to FHailstones.Count - 1 do
|
||||
if AreIntersecting(FHailstones[i], FHailstones[j]) then
|
||||
Inc(FPart1);
|
||||
|
||||
if FHailstones.Count >= 3 then
|
||||
FPart2 := FindRockThrow(0, 1, 2);
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.GetDataFileName: string;
|
||||
|
|
|
@ -40,10 +40,6 @@
|
|||
<Filename Value="UGearRatiosTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="..\USolver.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UBaseTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
|
@ -140,6 +136,18 @@
|
|||
<Filename Value="UNeverTellMeTheOddsTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomialTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomialRootsTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UBigIntTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
|
|
@ -9,7 +9,7 @@ uses
|
|||
UHotSpringsTestCases, UPointOfIncidenceTestCases, UParabolicReflectorDishTestCases, ULensLibraryTestCases,
|
||||
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
|
||||
UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases,
|
||||
UNeverTellMeTheOddsTestCases;
|
||||
UNeverTellMeTheOddsTestCases, UBigIntTestCases, UPolynomialTestCases, UPolynomialRootsTestCases;
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,6 +33,7 @@ type
|
|||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOddsExampleTestCase }
|
||||
|
@ -42,6 +43,7 @@ type
|
|||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOddsTestCase }
|
||||
|
@ -77,6 +79,11 @@ begin
|
|||
AssertEquals(15107, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOddsFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(856642398547748, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOddsExampleTestCase }
|
||||
|
||||
function TNeverTellMeTheOddsExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -89,6 +96,11 @@ begin
|
|||
AssertEquals(2, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOddsExampleTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(47, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOddsTestCase }
|
||||
|
||||
function TNeverTellMeTheOddsTestCase.CreateSolver: ISolver;
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 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 UPolynomialRootsTestCases;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testregistry, UPolynomial, UPolynomialRoots, UBigInt;
|
||||
|
||||
type
|
||||
|
||||
{ TPolynomialRootsTestCase }
|
||||
|
||||
TPolynomialRootsTestCase = class(TTestCase)
|
||||
private
|
||||
procedure AssertBisectIntervals(AIsolatingIntervals: TIsolatingIntervalArray; constref AExpectedRoots:
|
||||
array of Cardinal);
|
||||
procedure AssertBisectIntegers(ARoots: TBigIntArray; constref AExpectedRoots: array of Cardinal);
|
||||
published
|
||||
procedure TestBisectNoBound;
|
||||
procedure TestBisectWithBound;
|
||||
procedure TestBisectInteger;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPolynomialRootsTestCase }
|
||||
|
||||
procedure TPolynomialRootsTestCase.AssertBisectIntervals(AIsolatingIntervals: TIsolatingIntervalArray;
|
||||
constref AExpectedRoots: array of Cardinal);
|
||||
var
|
||||
exp: Cardinal;
|
||||
found: Boolean;
|
||||
i, foundIndex: Integer;
|
||||
begin
|
||||
AssertEquals('Unexpected number of isolating intervals.', Length(AExpectedRoots), Length(AIsolatingIntervals));
|
||||
for exp in AExpectedRoots do
|
||||
begin
|
||||
found := False;
|
||||
for i := 0 to Length(AIsolatingIntervals) - 1 do
|
||||
if (AIsolatingIntervals[i].A <= exp) and (exp <= AIsolatingIntervals[i].B) then
|
||||
begin
|
||||
found := True;
|
||||
foundIndex := i;
|
||||
Break;
|
||||
end;
|
||||
AssertTrue('No isolating interval for expected root ' + IntToStr(exp) + ' found.', found);
|
||||
Delete(AIsolatingIntervals, foundIndex, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.AssertBisectIntegers(ARoots: TBigIntArray; constref AExpectedRoots:
|
||||
array of Cardinal);
|
||||
var
|
||||
exp: Cardinal;
|
||||
found: Boolean;
|
||||
i, foundIndex: Integer;
|
||||
begin
|
||||
AssertEquals('Unexpected number of integer roots.', Length(AExpectedRoots), Length(ARoots));
|
||||
for exp in AExpectedRoots do
|
||||
begin
|
||||
found := False;
|
||||
for i := 0 to Length(ARoots) - 1 do
|
||||
if ARoots[i] = exp then
|
||||
begin
|
||||
found := True;
|
||||
foundIndex := i;
|
||||
Break;
|
||||
end;
|
||||
AssertTrue('Expected root ' + IntToStr(exp) + ' not found.', found);
|
||||
Delete(ARoots, foundIndex, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.TestBisectNoBound;
|
||||
const
|
||||
expRoots: array of Cardinal = (34000, 23017, 5);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
r: TIsolatingIntervalArray;
|
||||
begin
|
||||
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
|
||||
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
|
||||
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
|
||||
r := TPolynomialRoots.BisectIsolation(a);
|
||||
AssertBisectIntervals(r, expRoots);
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.TestBisectWithBound;
|
||||
const
|
||||
expRoots: array of Cardinal = (23017, 5);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
r: TIsolatingIntervalArray;
|
||||
begin
|
||||
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
|
||||
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
|
||||
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
|
||||
r := TPolynomialRoots.BisectIsolation(a, 15);
|
||||
AssertBisectIntervals(r, expRoots);
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.TestBisectInteger;
|
||||
const
|
||||
expRoots: array of Cardinal = (23017, 5);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
r: TBigIntArray;
|
||||
begin
|
||||
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
|
||||
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
|
||||
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
|
||||
r := TPolynomialRoots.BisectInteger(a, 15);
|
||||
AssertBisectIntegers(r, expRoots);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest(TPolynomialRootsTestCase);
|
||||
end.
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 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 UPolynomialTestCases;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testregistry, UPolynomial, UBigInt;
|
||||
|
||||
type
|
||||
|
||||
{ TBigIntPolynomialTestCase }
|
||||
|
||||
TBigIntPolynomialTestCase = class(TTestCase)
|
||||
private
|
||||
procedure TestCreateWithDegree(const ACoefficients: array of TBigInt; const ADegree: Integer);
|
||||
published
|
||||
procedure TestCreate;
|
||||
procedure TestCreateDegreeZero;
|
||||
procedure TestEqual;
|
||||
procedure TestUnequalSameLength;
|
||||
procedure TestUnequalDifferentLength;
|
||||
procedure TestTrimLeadingZeros;
|
||||
procedure TestCalcValueAt;
|
||||
procedure TestSignVariations;
|
||||
procedure TestScaleByPowerOfTwo;
|
||||
procedure TestScaleVariable;
|
||||
procedure TestScaleVariableByPowerOfTwo;
|
||||
procedure TestTranslateVariableByOne;
|
||||
procedure TestRevertOrderOfCoefficients;
|
||||
procedure TestDivideByVariable;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TBigIntPolynomialTestCase }
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCreateWithDegree(const ACoefficients: array of TBigInt; const ADegree: Integer);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create(ACoefficients);
|
||||
AssertEquals('Degree of created polynomial incorrect.', ADegree, a.Degree);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCreate;
|
||||
begin
|
||||
TestCreateWithDegree([992123, 7, 20, 4550022], 3);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCreateDegreeZero;
|
||||
begin
|
||||
TestCreateWithDegree([4007], 0);
|
||||
TestCreateWithDegree([], 0);
|
||||
TestCreateWithDegree([0], 0);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestEqual;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestUnequalSameLength;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([103, 7, 5, 10]);
|
||||
b := TBigIntPolynomial.Create([1034, 7, 5, 10]);
|
||||
AssertTrue('Polynomials are equal.', a <> b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestUnequalDifferentLength;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([40000, 10, 7, 5, 1034]);
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are equal.', a <> b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestTrimLeadingZeros;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034, 0, 0]);
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCalcValueAt;
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
exp: TBigInt;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([80, 892477222, 0, 921556, 7303]);
|
||||
exp:= TBigInt.FromInt64(16867124285);
|
||||
AssertTrue('Polynomial evaluation unexpected.', a.CalcValueAt(15) = exp);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestSignVariations;
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([-10, 15, 0, 10, -20, -15, 0, 0, 5, 10, -10]);
|
||||
AssertEquals(4, a.CalcSignVariations);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestScaleByPowerOfTwo;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).ScaleByPowerOfTwo(7);
|
||||
b := TBigIntPolynomial.Create([128 * 10, 128 * 7, 128 * 5, 128 * 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestScaleVariable;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).ScaleVariable(TBigInt.FromInt64(10));
|
||||
b := TBigIntPolynomial.Create([10, 70, 500, 1034000]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestScaleVariableByPowerOfTwo;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).ScaleVariableByPowerOfTwo(5);
|
||||
b := TBigIntPolynomial.Create([10, 7 * 32, 5 * 32 * 32, 1034 * 32 * 32 * 32]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestTranslateVariableByOne;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).TranslateVariableByOne;
|
||||
b := TBigIntPolynomial.Create([1056, 3119, 3107, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestRevertOrderOfCoefficients;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([0, 10, 7, 5, 1034]).RevertOrderOfCoefficients;
|
||||
b := TBigIntPolynomial.Create([1034, 5, 7, 10]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestDivideByVariable;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([0, 10, 7, 5, 1034]).DivideByVariable;
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest(TBigIntPolynomialTestCase);
|
||||
end.
|
||||
|
Loading…
Reference in New Issue