diff --git a/UBigInt.pas b/UBigInt.pas index 56e48f9..e90b5c6 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -35,6 +35,8 @@ type FDigits: TDigits; FIsNegative: Boolean; + function GetSign: Integer; + // Copies consecutive digits from this BigInt to create a new one. The result will be positive. Leading zeros are // removed from the result, but AIndex + ACount must not exceed the number of digits of this BigInt. // AIndex is the first (least significant) digit to be taken. The digit with this index will become the 0th digit of @@ -66,11 +68,19 @@ type // Sign * (Abs(a) * Abs(b)) // where Sign is 1 for ReturnNegative = False and -1 otherwise. class function MultiplyAbsoluteValues(constref AA, AB: TBigInt; const AReturnNegative: Boolean): TBigInt; static; + + class function FromHexOrBinString(const AValue: string; const AFromBase: Integer): TBigInt; static; + class function ConvertDigitBlock(const AValue: string; var AStartIndex: Integer; const ACharBlockSize, AFromBase: + Integer): Cardinal; public property IsNegative: Boolean read FIsNegative; + property Sign: Integer read GetSign; class property Zero: TBigInt read GetZero; function CompareTo(constref AOther: TBigInt): Integer; + function TryToInt64(out AOutput: Int64): Boolean; class function FromInt64(const AValue: Int64): TBigInt; static; + class function FromHexadecimalString(const AValue: string): TBigInt; static; + class function FromBinaryString(const AValue: string): TBigInt; static; end; { Operators } @@ -83,6 +93,10 @@ type operator shl (const A: TBigInt; const B: Integer): TBigInt; operator = (const A, B: TBigInt): Boolean; operator <> (const A, B: TBigInt): Boolean; + operator < (const A, B: TBigInt): Boolean; + operator <= (const A, B: TBigInt): Boolean; + operator > (const A, B: TBigInt): Boolean; + operator >= (const A, B: TBigInt): Boolean; implementation @@ -98,6 +112,16 @@ const { TBigInt } +function TBigInt.GetSign: Integer; +begin + if FIsNegative then + Result := -1 + else if (Length(FDigits) > 1) or (FDigits[0] <> 0) then + Result := 1 + else + Result := 0; +end; + function TBigInt.GetSegment(const AIndex, ACount: Integer): TBigInt; var trimmedCount: Integer; @@ -105,8 +129,7 @@ begin trimmedCount := ACount; while (trimmedCount > 1) and (FDigits[AIndex + trimmedCount - 1] = 0) do Dec(trimmedCount); - SetLength(Result.FDigits, trimmedCount); - Move(FDigits[AIndex], Result.FDigits[0], CDigitSize * trimmedCount); + Result.FDigits := Copy(FDigits, AIndex, trimmedCount); Result.FIsNegative := False; end; @@ -362,6 +385,65 @@ begin end; end; +class function TBigInt.FromHexOrBinString(const AValue: string; const AFromBase: Integer): TBigInt; +var + charBlockSize, offset, i, j, k, remainder: Integer; + d: Cardinal; +begin + charBlockSize := 64 div AFromBase; + 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, charBlockSize, i, remainder); + k := offset; + d := 0; + // Checks the first block of chars that is not a full block. + if remainder > 0 then + d := ConvertDigitBlock(AValue, k, remainder, AFromBase); + // Checks full blocks of chars for first digit. + while (d = 0) and (i > 0) do + begin + Dec(i); + d := ConvertDigitBlock(AValue, k, charBlockSize, AFromBase); + end; + + // Checks for zero. + if (d = 0) and (i = 0) then + Result := Zero + else begin + // Initializes the array of digits. + SetLength(Result.FDigits, i + 1); + Result.FDigits[i] := d; + + // Calculates the other digits. + for j := i - 1 downto 0 do + Result.FDigits[j] := ConvertDigitBlock(AValue, k, charBlockSize, AFromBase); + end; +end; + +class function TBigInt.ConvertDigitBlock(const AValue: string; var AStartIndex: Integer; const ACharBlockSize, + AFromBase: Integer): Cardinal; +var + part: string; +begin + part := Copy(AValue, AStartIndex, ACharBlockSize); + Inc(AStartIndex, ACharBlockSize); + case AFromBase of + 2: part := '%' + part; + 8: part := '&' + part; + 16: part := '$' + part; + end; + Result := StrToDWord(part); +end; + function TBigInt.CompareTo(constref AOther: TBigInt): Integer; begin if FIsNegative = AOther.FIsNegative then @@ -372,6 +454,35 @@ begin Result := -Result; end; +function TBigInt.TryToInt64(out AOutput: Int64): Boolean; +begin + AOutput := 0; + Result := False; + case Length(FDigits) of + 0: Result := True; + 1: begin + AOutput := FDigits[0]; + if FIsNegative then + AOutput := -AOutput; + Result := True; + end; + 2: begin + if FDigits[1] <= Integer.MaxValue then + begin + AOutput := FDigits[1] * CBase + FDigits[0]; + if FIsNegative then + AOutput := -AOutput; + Result := True; + end + else if (FDigits[1] = Integer.MaxValue + 1) and (FDigits[0] = 0) and FIsNegative then + begin + AOutput := Int64.MinValue; + Result := True; + end; + end; + end; +end; + class function TBigInt.FromInt64(const AValue: Int64): TBigInt; var absVal: Int64; @@ -391,6 +502,16 @@ begin end; end; +class function TBigInt.FromHexadecimalString(const AValue: string): TBigInt; +begin + Result := FromHexOrBinString(AValue, 16); +end; + +class function TBigInt.FromBinaryString(const AValue: string): TBigInt; +begin + Result := FromHexOrBinString(AValue, 2); +end; + { Operators } operator := (const A: Int64): TBigInt; @@ -403,9 +524,13 @@ var len: Integer; begin len := Length(A.FDigits); - SetLength(Result.FDigits, len); - Move(A.FDigits[0], Result.FDigits[0], len); - Result.FIsNegative := not A.FIsNegative; + if (len > 1) or (A.FDigits[0] > 0) then + begin + Result.FDigits := Copy(A.FDigits, 0, len); + Result.FIsNegative := not A.FIsNegative; + end + else + Result := TBigInt.Zero; end; operator + (const A, B: TBigInt): TBigInt; @@ -492,5 +617,25 @@ begin Result := A.CompareTo(B) <> 0; end; +operator < (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) < 0; +end; + +operator <= (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) <= 0; +end; + +operator > (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) > 0; +end; + +operator >= (const A, B: TBigInt): Boolean; +begin + Result := A.CompareTo(B) >= 0; +end; + end.