From 52cee7312394324503c85d8c095e69a2926f1c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Mon, 20 May 2024 15:03:54 +0200 Subject: [PATCH] Added TBigInt.GetMostSignificantBitIndex and tests --- UBigInt.pas | 33 +++++++++++++++++++++++++++++ tests/UBigIntTestCases.pas | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/UBigInt.pas b/UBigInt.pas index 5a31b72..699145f 100644 --- a/UBigInt.pas +++ b/UBigInt.pas @@ -76,6 +76,10 @@ type property IsNegative: Boolean read FIsNegative; property Sign: Integer read GetSign; class property Zero: TBigInt read GetZero; + + // 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. @@ -461,6 +465,21 @@ begin 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 @@ -549,6 +568,7 @@ end; operator := (const A: Int64): TBigInt; begin Result := TBigInt.FromInt64(A); + //WriteLn(':=a op: ', Result.ToString); end; operator - (const A: TBigInt): TBigInt; @@ -563,6 +583,8 @@ begin end else Result := TBigInt.Zero; + //WriteLn(' a: ', A.ToString); + //WriteLn('-a op: ', Result.ToString); end; operator + (const A, B: TBigInt): TBigInt; @@ -571,6 +593,9 @@ begin Result := TBigInt.AddAbsoluteValues(A, B, A.IsNegative) else Result := TBigInt.SubtractAbsoluteValues(A, B, A.IsNegative); + //WriteLn(' a: ', A.ToString); + //WriteLn(' b: ', B.ToString); + //WriteLn('a+b op: ', Result.ToString); end; operator - (const A, B: TBigInt): TBigInt; @@ -579,6 +604,9 @@ begin Result := TBigInt.SubtractAbsoluteValues(A, B, A.IsNegative) else Result := TBigInt.AddAbsoluteValues(A, B, A.IsNegative); + //WriteLn(' a: ', A.ToString); + //WriteLn(' b: ', B.ToString); + //WriteLn('a-b op: ', Result.ToString); end; operator * (const A, B: TBigInt): TBigInt; @@ -587,6 +615,9 @@ begin Result := TBigInt.Zero else Result := TBigInt.MultiplyAbsoluteValues(A, B, A.IsNegative <> B.IsNegative); + //WriteLn(' a: ', A.ToString); + //WriteLn(' b: ', B.ToString); + //WriteLn('a*b op: ', Result.ToString); end; operator shl(const A: TBigInt; const B: Integer): TBigInt; @@ -639,6 +670,8 @@ begin Result.FIsNegative := A.IsNegative; end; + //WriteLn(' a: ', A.ToString); + //WriteLn('a<< op: ', Result.ToString); end; operator = (const A, B: TBigInt): Boolean; diff --git a/tests/UBigIntTestCases.pas b/tests/UBigIntTestCases.pas index 4cba54d..2457462 100644 --- a/tests/UBigIntTestCases.pas +++ b/tests/UBigIntTestCases.pas @@ -70,6 +70,17 @@ type procedure TestLongNegative; end; + { TBigIntMostSignificantBitIndexTestCase } + + TBigIntMostSignificantBitIndexTestCase = class(TTestCase) + private + procedure Test(const ABinValue: string; const AExpectedIndex: Int64); + published + procedure TestZero; + procedure TestShort; + procedure TestLong; + end; + { TBigIntFromInt64TestCase } TBigIntFromInt64TestCase = class(TTestCase) @@ -271,6 +282,37 @@ begin Test('-192648F1305DD04757A24C873F29F60B6184B5', -1); end; +{ TBigIntMostSignificantBitIndexTestCase } + +procedure TBigIntMostSignificantBitIndexTestCase.Test(const ABinValue: string; const AExpectedIndex: Int64); +var + a: TBigInt; +begin + a := TBigInt.FromBinaryString(ABinValue); + AssertEquals('BigInt from binary string ''' + ABinValue + ''' did not have its most significant bit at index ''' + + IntToStr(AExpectedIndex) + '''.', + AExpectedIndex, a.GetMostSignificantBitIndex); +end; + +procedure TBigIntMostSignificantBitIndexTestCase.TestZero; +begin + Test('0', -1); +end; + +procedure TBigIntMostSignificantBitIndexTestCase.TestShort; +begin + Test('1', 0); + Test('11111101111001', 13); + Test('10010111101100001110110101111000', 31); +end; + +procedure TBigIntMostSignificantBitIndexTestCase.TestLong; +begin + Test('111100010101010100011101010100011', 32); + Test('11101001101010111101000101010001010101010101111111001010101010010100101000101011111001000111001001100011', 103); + Test('111101100011110110111011010111100000000001010111101110101101101100101010110111101011010101001100', 95); +end; + { TBigIntFromInt64TestCase } procedure TBigIntFromInt64TestCase.Test(const AValue: Int64); @@ -903,6 +945,7 @@ end; initialization RegisterTest(TBigIntSignTestCase); + RegisterTest(TBigIntMostSignificantBitIndexTestCase); RegisterTest(TBigIntFromInt64TestCase); RegisterTest(TBigIntFromHexTestCase); RegisterTest(TBigIntFromBinTestCase);