diff --git a/UBigInt.pas b/UBigInt.pas index a6ce5af..9fbbf3f 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -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; + if FIsNegative = AOther.FIsNegative then + Result := CompareToAbsoluteValues(AOther) + else + Result := 1; + if FIsNegative then + Result := -Result; 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 - Result := 1; - Break; - end; - end; -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);