Fixed TBigInt heap memory allocation (fixed memory leaks)

This commit is contained in:
Stefan Müller 2024-02-14 11:56:11 +01:00 committed by Stefan Müller
parent bfb33673ee
commit 5a3c320942
1 changed files with 89 additions and 81 deletions

View File

@ -29,20 +29,36 @@ type
{ TBigInt }
// This is an abbreviated reimplementation of a C# class created in 2022.
// This is an abbreviated reimplementation in Freepascal of a C# class created in 2022.
TBigInt = object
private
FDigits: TDigits;
FIsNegative: Boolean;
// 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;
// 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/
@ -50,21 +66,11 @@ type
// Sign * (Abs(a) * Abs(b))
// where Sign is 1 for ReturnNegative = False and -1 otherwise.
class function MultiplyAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; static;
// 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;
public
property IsNegative: Boolean read FIsNegative;
constructor InitZero;
constructor Init(const AValue: Int64);
destructor Done;
class property Zero: TBigInt read GetZero;
function CompareTo(constref AOther: TBigInt): Integer;
class function FromInt64(const AValue: Int64): TBigInt; static;
end;
operator := (const A: Int64): TBigInt;
@ -84,8 +90,48 @@ const
CHalfBits = CBitsPerDigit >> 1;
CHalfDigitMax = (1 << CHalfBits) - 1;
CZero: TBigInt = (FDigits: (0); FIsNegative: False);
{ TBigInt }
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);
SetLength(Result.FDigits, trimmedCount);
Move(FDigits[AIndex], Result.FDigits[0], CDigitSize * 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.AddAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt;
var
i, lenA, lenB, len, shorter: Integer;
@ -245,7 +291,7 @@ begin
begin
if (AA.FDigits[0] <= CHalfDigitMax) and (AB.FDigits[0] <= CHalfDigitMax) then
if (AA.FDigits[0] = 0) or (AB.FDigits[0] = 0) then
Result.InitZero
Result := Zero
else begin
Result.FDigits := TDigits.Create(AA.FDigits[0] * AB.FDigits[0]);
Result.FIsNegative := AReturnNegative;
@ -267,8 +313,8 @@ begin
Result.FIsNegative := AReturnNegative;
// The result of (a1 + a0) * (b1 + b0) might not fit in one digit, so one last recursion step is necessary.
am.Init(a1 + a0);
bm.Init(b1 + b0);
am := FromInt64(a1 + a0);
bm := FromInt64(b1 + b0);
middle := (MultiplyAbsoluteValues(am, bm, False) - a1b1 - a0b0) << CHalfBits;
if AReturnNegative then
Result := Result - middle
@ -312,86 +358,48 @@ begin
end;
end;
function TBigInt.GetSegment(const AIndex, ACount: Integer): TBigInt;
var
trimmedCount: Integer;
function TBigInt.CompareTo(constref AOther: TBigInt): Integer;
begin
trimmedCount := ACount;
while (trimmedCount > 1) and (FDigits[AIndex + trimmedCount - 1] = 0) do
Dec(trimmedCount);
SetLength(Result.FDigits, trimmedCount);
Move(FDigits[AIndex], Result.FDigits[0], CDigitSize * trimmedCount);
Result.FIsNegative := False;
end;
function TBigInt.CompareToAbsoluteValues(constref AOther: TBigInt): Integer;
var
i: Integer;
begin
if Length(FDigits) < Length(AOther.FDigits) then
Result := -1
else if Length(FDigits) > Length(AOther.FDigits) then
Result := 1
else begin
Result := 0;
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
if FIsNegative = AOther.FIsNegative then
Result := CompareToAbsoluteValues(AOther)
else
Result := 1;
Break;
end;
end;
if FIsNegative then
Result := -Result;
end;
constructor TBigInt.InitZero;
begin
FIsNegative := False;
FDigits := TDigits.Create(0);
end;
constructor TBigInt.Init(const AValue: Int64);
class function TBigInt.FromInt64(const AValue: Int64): TBigInt;
var
absVal: Int64;
begin
FIsNegative := AValue < 0;
if AValue <> Int64.MinValue then
begin
absVal := Abs(AValue);
if absVal >= CBase then
FDigits := TDigits.Create(absVal mod CBase, absVal div CBase)
Result.FDigits := TDigits.Create(absVal mod CBase, absVal div CBase)
else
FDigits := TDigits.Create(absVal);
Result.FDigits := TDigits.Create(absVal);
Result.FIsNegative := AValue < 0;
end
else begin
FIsNegative := True;
FDigits := TDigits.Create(0, 1 << 31);
Result.FDigits := TDigits.Create(0, 1 << 31);
Result.FIsNegative := True;
end;
end;
destructor TBigInt.Done;
begin
SetLength(FDigits, 0);
end;
function TBigInt.CompareTo(constref AOther: TBigInt): Integer;
begin
if IsNegative = AOther.IsNegative then
Result := CompareToAbsoluteValues(AOther)
else
Result := 1;
if IsNegative then
Result := -Result;
end;
operator := (const A: Int64): TBigInt;
begin
Result.Done;
Result.Init(A);
Result := TBigInt.FromInt64(A);
end;
operator - (const A: TBigInt): TBigInt;
var
len: Integer;
begin
len := Length(A.FDigits);
SetLength(Result.FDigits, len);
Move(A.FDigits[0], Result.FDigits[0], len);
Result.FIsNegative := not A.FIsNegative;
end;
operator + (const A, B: TBigInt): TBigInt;
@ -412,8 +420,8 @@ end;
operator * (const A: TBigInt; const B: Int64): TBigInt;
begin
if (a = 0) or (b = 0) then
Result.InitZero
if (A = 0) or (B = 0) then
Result := TBigInt.Zero
else
Result := TBigInt.MultiplyAbsoluteValues(A, B, A.IsNegative = (B > 0));
end;
@ -425,7 +433,7 @@ var
begin
// Handles shift of zero.
if A = 0 then
Result.InitZero
Result := TBigInt.Zero
else begin
// Determines full digit shifts and bit shifts.
DivMod(B, CBitsPerDigit, digitShifts, bitShifts);