{
Delphi 4 內建 Int64 是最新功能,可是更大的位數呢?前一陣子因為研究密碼
學的緣故必須使用到任意位數的整數運算,因此就花了點時間研究了一下。
說起來這個大數 Library 有點半弔子,因為那時寫得實在很興奮,以為可以自己
可以寫一套非常完整的 Public-Key Security System ,包括 Encryption/
Decryption/Sign/Verify signature 等等的功能,後來見識到 PGP 的威力之後
就發現其實這不過雕蟲小技。可是對一般人來說,這個 Library 算是相當足夠了。
提供了:加、減、乘、除的基本運算、十進位字串及大數間的轉換、十六進位字串
及大數間的轉換、算出a^b mod n、比較大小、shift left/right,等等功能。
注意事項:
1.大數使用 Dynamic Array ,因此變數拷貝的時候僅僅拷貝參考而已,不是本體,
特別注意(請參照 Delphi 4使用手冊)
2.此大數 Library 不提供負數、小數,請自己再外包一層 :Q,因為密碼學似乎不
需要用到負數以及小數。
}
unit BigNum;
interface
uses
SysUtils, Classes, Windows;
type
{ TDigits: a bignum is no more than a dynamic array containing variable
number of cardinals(unsigned 32-bit integers) }
TDigits = array of Cardinal;
EBigError = class(Exception)
end;
var
BigZero: TDigits;
BigOne: TDigits;
BigTwo: TDigits;
procedure _BigNormalize(var a: TDigits);
function BigAdd(a, b: TDigits): TDigits;
function _BigSubAt(var a: TDigits; const b: TDigits; ka, kb: Integer): Integer;
function BigSub(a, b: TDigits): TDigits;
function BigMul(a, b: TDigits): TDigits; overload;
function BigMul(a: TDigits; b: Cardinal): TDigits; overload;
function TrimLeftChar(const S: string; C: Char): string;
function BigToStrHex(a: TDigits): string;
function BigConvertHex(S: string): TDigits; overload;
function BigConvert(S: string): TDigits; overload;
function BigCopy(a: TDigits; paddings: Integer = 0): TDigits;
function BigCompareAt(a, b: TDigits; ka, kb: Integer): Integer;
function BigCompare(a, b: TDigits): Integer; overload;
procedure _BigDivBy2(var a: TDigits);
function BigDivBy2(a: TDigits; Count: Integer = 1): TDigits; overload;
function BigDiv(u, b: TDigits; var Remainder: TDigits): TDigits; overload;
function BigIsOdd(a: TDigits): Boolean;
function BigDiv(a, b: TDigits): TDigits; overload;
function BigMod(a, b: TDigits): TDigits; overload;
function BigExpMod(x, y, n: TDigits): TDigits;
function BigGCD(x, y: TDigits): TDigits;
function BigLCM(x, y: TDigits): TDigits;
function BigMax(a: array of TDigits): TDigits;
function BigMin(a: array of TDigits): TDigits;
procedure BigSwap(var a, b: TDigits);
function BigPower(x: TDigits; y: Integer): TDigits;
function BigToStr(a: TDigits): string;
implementation
procedure BigError(Msg: string);
begin
raise EBigError.Create(Msg);
end;
function Max(a, b: Integer): Integer;
begin
if a > b then
Result := a
else
Result := b;
end;
function NormalLength(a: TDigits): Integer;
var
I: Integer;
begin
I := High(a);
while (I >= 0) and (a[I] = 0) do
Dec(I);
Result := I + 1;
end;
{ _BigNormalize: Cuts off the trailing zero digits. }
procedure _BigNormalize(var a: TDigits);
var
I: Integer;
begin
I := High(a);
while (I >= 0) and (a[I] = 0) do
Dec(I);
SetLength(a, I+1);
end;
function BigNormalize(a: TDigits): TDigits;
begin
Result := BigCopy(a);
_BigNormalize(a);
end;
{ BigAdd: Performs addition between 'a' and 'b' and returns the result bignum }
function BigAdd(a, b: TDigits): TDigits;
var
I: Integer;
Sum: Int64;
Carry: Integer;
begin
SetLength(Result, Max(Length(a), Length(b)));
Carry := 0;
for I:=0 to High(Result) do
begin
Sum := Carry;
if I < Length(a) then
Inc(Sum, Int64(a[I]));
if I < Length(b) then
Inc(Sum, Int64(b[I]));
Result[I] := Int64Rec(Sum).Lo;
Carry := Int64Rec(Sum).Hi;
end;
_BigNormalize(Result);
end;
{ _BigSubAt: Performs subtraction in 'a' and 'b' with 'a' and 'b' firstly
shifted by 'ka' and 'kb'.
Returns:
0 if a = b
1 if a > b
-1 if a < b.
If 'b' is less than
'a', then the result should be represented in 2^32's complement
}
function _BigSubAt(var a: TDigits; const b: TDigits; ka, kb: Integer): Integer;
var
I: Integer;
Sum: Int64;
Carry: Integer;
EqualFlag: Boolean;
begin
EqualFlag := True;
Carry := 0;
for I:=0 to High(a) do
begin
Sum := - Carry;
if (I-ka <= High(a)) and (I-ka >= 0) then
Inc(Sum, Int64(a[I-ka]));
if (I-kb <= High(b)) and (I-kb >= 0) then
Dec(Sum, Int64(b[I-kb]));
if Sum <> 0 then
EqualFlag := False;
if Sum < 0 then
begin
a[I] := Sum + (Int64(1) shl 32);
Carry := 1;
end
else
begin
Carry := 0;
a[I] := Sum;
end;
end;
Result := 1;
if Carry <> 0 then
Result := -1;
if EqualFlag then
Result := 0;
end;
{ BigSub: Subtracts 'b' from 'a' and returns the result. If 'b' is less than
'a', then the result should be represented in 2^32's complement }
function BigSub(a, b: TDigits): TDigits;
begin
Result := BigCopy(a);
_BigSubAt(Result, b, 0, 0);
_BigNormalize(Result);
end;
{ BigMul: Performs multiplication between bignums 'a' and 'b' and returns the
result bignum}
function BigMul(a, b: TDigits): TDigits; overload;
var
I, J: Integer;
Temp: array of Int64;
Sum: Int64;
Carry: Cardinal;
begin
SetLength(Temp, Length(a) + Length(b) + 1);
for I:=0 to High(Temp) do
Temp[I] := 0;
for I:=0 to High(a) do
for J:=0 to High(b) do
Inc(Temp[I+J], Int64(a[I]) * Int64(b[J]));
SetLength(Result, Length(Temp));
Carry := 0;
for I:=0 to High(Temp) do
begin
Sum := Temp[I] + Carry;
Result[I] := Int64Rec(Sum).Lo;
Carry := Int64Rec(Sum).Hi;
end;
_BigNormalize(Result);
end;
function BigMul(a: TDigits; b: Cardinal): TDigits; overload;
var
k: TDigits;
begin
SetLength(k, 1);
k[0] := b;
Result := BigMul(a, k);
end;
const
HexStr = '0123456789ABCDEF';
function TrimLeftChar(const S: string; C: Char): string;
var
I, L: Integer;
begin
L := Length(S);
I := 1;
while (I <= L) and (S[I] <= C) do Inc(I);
Result := Copy(S, I, Maxint);
end;
{ BigToStrHex: Converts a bignum into a hexidecimal string }
function BigToStrHex(a: TDigits): string;
var
I: Integer;
function NibbleToStr(Nib: Cardinal): string;
var
I: Integer;
K: Cardinal;
begin
SetLength(Result, 8);
for I:=8 downto 1 do
begin
K := Nib and $F;
Result[I] := HexStr[K+1];
Nib := Nib shr 4;
end;
end;
begin
if Length(a) = 0 then
begin
Result := '0';
Exit;
end;
Result := TrimLeftChar(NibbleToStr(a[High(a)]), '0');
for I:=High(a)-1 downto 0 do
Result := Result + NibbleToStr(a[I]);
if Result = '' then
Result := '0';
end;
{ BigToStrHex: Converts a bignum into a hexidecimal string }
function BigDebug(a: TDigits): string;
var
I: Integer;
function NibbleToStr(Nib: Cardinal): string;
var
I: Integer;
K: Cardinal;
begin
SetLength(Result, 8);
for I:=8 downto 1 do
begin
K := Nib and $F;
Result[I] := HexStr[K+1];
Nib := Nib shr 4;
end;
end;
begin
Result := '';
for I:=High(a) downto 0 do
Result := Result + NibbleToStr(a[I]) + ' ';
end;
{ BigConvertHex: Converts a hexidecimal string into a bignum }
function BigConvertHex(S: string): TDigits; overload;
var
I, J: Integer;
function StrToNibble(P: string): Cardinal;
var
I: Integer;
K: Cardinal;
Base: Cardinal;
begin
Base := 1;
Result := 0;
for I:=8 downto 1 do
begin
K := Pos(P[I], HexStr);
if K = 0 then
BigError('BigConvertHex: Invalid character: ' + P[I]);
Result := Result + (K-1) * Base;
Base := Base shl 4;
end;
end;
begin
SetLength(Result, (Length(S) + 7) div 8);
S := UpperCase(Trim(S));
I := Length(S);
J := 0;
while True do
begin
if I >= 8 then
Result[J] := StrToNibble(Copy(S, I-8+1, 8))
else
begin
Result[J] := StrToNibble(StringOfChar('0', 8-I) + Copy(S, 1, I));
Exit;
end;
Dec(I, 8);
Inc(J);
end;
end;
{ BigConvert: Converts a decimal string into a bignum }
function BigConvert(S: string): TDigits; overload;
var
bcd: array of Byte;
I: Integer;
L: Integer;
Base: Cardinal;
K: Integer;
begin
S := Trim(S);
L := Length(S);
SetLength(bcd, L+1);
SetLength(Result, 0);
SetLength(Result, L div 4 + 1);
for I:=1 to L do
bcd[L-I] := Ord(S[I]) - Ord('0');
Base := 1;
K := 0;
while True do
begin
if bcd[0] and 1 <> 0 then
Result[K] := Result[K] or Base;
for I:=0 to L-1 do
begin
bcd[I] := bcd[I] shr 1;
if bcd[I+1] and 1 <> 0 then
Inc(bcd[I], 5);
end;
Base := Base shl 1;
if Base = 0 then
begin
Base := 1;
Inc(K);
if K > High(Result) then
Break;
end;
end;
_BigNormalize(Result);
end;
function BigCopy(a: TDigits; paddings: Integer = 0): TDigits;
var
I: Integer;
begin
SetLength(Result, Length(a) + paddings);
for I:=0 to High(a) do
Result[I] := a[I];
end;
{ BigCompare: Compares the values in 'a' and 'b' with their digits shifted
by 'ka' and 'kb' first. Assuming 'A' equals to 'a' shifting 'ka' and 'B'
equals to 'b' shifting 'kb' then this function
returns 1: when A > B
-1: when A < B
0: when A = B }
function BigCompareAt(a, b: TDigits; ka, kb: Integer): Integer;
var
I, L: Integer;
da, db: Cardinal;
begin
L := Max(High(a)+ka, High(b)+kb);
for I:=L downto 0 do
begin
if (I-ka <= High(a)) and (I-ka >= 0) then
da := a[I-ka]
else
da := 0;
if (I-kb <= High(b)) and (I-kb >= 0) then
db := b[I-kb]
else
db := 0;
if da > db then
begin
Result := 1;
Exit;
end else if da < db then
begin
Result := -1;
Exit;
end;
end;
Result := 0;
end;
{ BigCompare: Compares a bignum 'a' with another bignum 'b'.
Returns 1: when a > b
-1: when a < b
0: when a = b }
function BigCompare(a, b: TDigits): Integer; overload;
begin
Result := BigCompareAt(a, b, 0, 0);
end;
{ BigCompare: Compares a bignum 'a' with a cardinal 'b'.
Returns 1: when a > b
-1: when a < b
0: when a = b }
function BigCompare(a: TDigits; b: Cardinal): Integer; overload;
var
I: Integer;
begin
if Length(a) = 0 then
begin
if b = 0 then
Result := 0
else
Result := -1;
Exit;
end;
Result := 1;
for I:=1 to High(a) do
if a[I] <> 0 then
Exit;
if a[0] > b then
Result := 1
else if a[0] < b then
Result := -1
else
Result := 0;
end;
{ _BigDivBy2: Divides a bignum by 2. Using 'shr' and is fast. }
procedure _BigDivBy2(var a: TDigits);
var
I: Integer;
begin
for I:=0 to High(a)-1 do
a[I] := (a[I] shr 1) or ((a[I+1] and 1) shl 31);
I := High(a);
a[I] := a[I] shr 1;
end;
function BigDivBy2(a: TDigits; Count: Integer = 1): TDigits; overload;
var
I: Integer;
begin
Result := BigCopy(a);
for I:=1 to Count do
_BigDivBy2(Result);
_BigNormalize(Result);
end;
function BigDiv1(k, b: TDigits; var m: TDigits): TDigits; overload;
var
D: Integer;
QLower, QUpper, Q: Int64;
P: TDigits;
a: TDigits;
Sum: Int64;
Leader: Cardinal;
begin
if BigCompare(b, 0) = 0 then
BigError('BigDiv: Division by zero');
{ Before performing operation on 'k', I make a copy of it and store it in 'a',
this is because dynamic arrays are shared among variables refer to it while
we don't want the caller to expect his variables is modified unsolicited }
a := BigCopy(k);
_BigNormalize(a);
_BigNormalize(b);
{ 'D' is the alignment position on 'a' in respect of 'b'. }
D := High(a) - High(b) + 1;
if D < 0 then
D := 0;
{ Set the length of the 'Result' bignum. The maximum length of the 'Result'
bignum should not exceed High(a) - High(b) + 1. }
SetLength(Result, D);
{ 'Sum' is the the sum of the leading two nibbles in 'a' that is validated
each time when 'a' is modified. }
Sum := 0;
{ 'Leader' is the leading nibble in 'b' and is used to determine the upper
bound of the quotient. }
Leader := b[High(b)];
{ Now begins the main loop processing each digit in 'a' }
while True do
begin
Dec(D);
{ If D < 0, the division must have been over and the remaining digits in
'a' should be the modulo }
if D < 0 then
begin
m := a;
_BigNormalize(m);
_BigNormalize(Result);
Exit;
end;
{ Shifts the digits in 'Sum' }
Int64Rec(Sum).Hi := Int64Rec(Sum).Lo;
Int64Rec(Sum).Lo := a[D+High(b)];
{ 'QUpper' and 'QLower' determines the upperbound and lowerbound of the
real quotient 'Q'. QUpper assumes the remaining bignum as 'Sum'
"appended" by a series zeros. QLower assumes the remaining bignum as
'Sum+1' "appended" by a series zeros. Note that QLower does not include
'Sum div (Leader + 1)'. Because the real quotient is discrete, we simply
assign the lowerbound of the quotient to 'Sum div (Leader + 1) + 1'.
This also helps when we're performing binary search in the following
lines. }
QUpper := Sum div Leader;
QLower := Sum div (Leader + 1) + 1;
{ By obtaining 'QUpper' and 'QLower', we are then able to use binary
search to determine the exact position of 'Q'. }
while True do
begin
Q := (QUpper + QLower) shr 1;
{ If QUpper and QLower differ only by one, we should stop immediately.
This is because the exact value of Q may not be obtained due to the
remaining digits in 'a'. Since Q is always in the valid range of the
real quotient, there should be no reason that Q is other than the real
quotient when the error gap is only 1. }
if QUpper - QLower <= 1 then
begin
Q := QUpper;
repeat
P := BigMul(b, Q);
if BigCompareAt(a, P, 0, D) >= 0 then
Break;
Dec(Q);
until False;
{ Store the calculated real quotient into Result }
Result[D] := Q;
{ Since the real quotient is obtained in Q, we now could subtract
P (P = b * Q) from a and proceed with next round. }
_BigSubAt(a, P, 0, D);
Break;
end;
P := BigMul(b, Q);
{ Binary search scheme }
case BigCompareAt(a, P, 0, D) of
0:
begin
QUpper := Q;
QLower := Q;
end;
1: QLower := Q;
-1: QUpper := Q;
end;
end;
{ Assigns the new value of 'Sum'. }
Sum := a[D+High(b)];
end;
end;
function GetNibble(a: TDigits; I: Integer): Cardinal;
begin
if (I >= 0) and (I < Length(a)) then
Result := a[I]
else
Result := 0;
end;
procedure _BigShr(var a: TDigits; Count: Integer); forward;
procedure _BigShl(var a: TDigits; Count: Integer); forward;
procedure _BigShr(var a: TDigits; Count: Integer);
var
I: Integer;
Seg: Integer;
M, N: Integer;
begin
if Count < 0 then
begin
_BigShl(a, -Count);
Exit;
end;
Seg := Count div 32;
N := Count mod 32;
M := 32 - N;
if N = 0 then
for I:=0 to High(a) do
a[I] := GetNibble(a, I+Seg)
else
for I:=0 to High(a) do
a[I] := (GetNibble(a, I+Seg) shr N) or (GetNibble(a, I+Seg+1) shl M);
_BigNormalize(a);
end;
procedure _BigShl(var a: TDigits; Count: Integer);
var
I: Integer;
Seg: Integer;
M, N: Integer;
begin
if Count < 0 then
begin
_BigShr(a, -Count);
Exit;
end;
Seg := Count div 32;
N := Count mod 32;
M := 32 - N;
SetLength(a, Length(a) + Seg + 1);
if N = 0 then
for I:=High(a) downto 0 do
a[I] := GetNibble(a, I-Seg) shl N
else
for I:=High(a) downto 0 do
a[I] := (GetNibble(a, I-Seg) shl N) or (GetNibble(a, I-Seg-1) shr M);
_BigNormalize(a);
end;
procedure _BigAlign(var a: TDigits; var Count: Integer);
var
I: Integer;
N, M: Integer;
Leader, Mask: Cardinal;
begin
_BigNormalize(a);
N := 0;
Leader := a[High(a)];
Mask := Cardinal(1) shl 31;
while Leader and Mask = 0 do
begin
Inc(N);
Mask := Mask shr 1;
end;
M := 32 - N;
for I:=High(a) downto 1 do
a[I] := (a[I] shl N) or (a[I-1] shr M);
a[0] := a[0] shl N;
Count := N;
end;
function BigDiv(u, b: TDigits; var Remainder: TDigits): TDigits; overload;
var
I: Integer;
K: Integer;
Mask: Cardinal;
Count: Integer;
D: Integer;
a: TDigits;
begin
if BigCompare(b, 0) = 0 then
BigError('BigDiv: Division by zero');
a := BigCopy(u, Length(b));
Mask := Cardinal(1) shl 31;
SetLength(Result, 0);
SetLength(Result, High(a) - High(b));
K := High(Result);
Count := 32*Length(u);
D := Length(u);
for I:=0 to Count-1 do
begin
_BigShl(a, 1);
if BigCompareAt(a, b, 0, D) >= 0 then
begin
_BigSubAt(a, b, 0, D);
Result[K] := Result[K] or Mask;
end;
Mask := Mask shr 1;
if Mask = 0 then
begin
Mask := Cardinal(1) shl 31;
Dec(K);
end;
end;
_BigShr(a, Count);
Remainder := a;
_BigNormalize(Result);
end;
function BigIsDivisible(a: TDigits; b: Cardinal): Boolean;
var
Sum: Int64;
I: Integer;
begin
Sum := 0;
for I:=High(a) downto 0 do
begin
Int64Rec(Sum).Hi := Int64Rec(Sum).Lo;
Int64Rec(Sum).Lo := a[I];
Sum := Sum mod b;
end;
Result := Sum = 0;
end;
function BigDiv(a: TDigits; b: Cardinal; var Remainder: Cardinal): TDigits; overload
var
Sum: Int64;
I: Integer;
begin
Sum := 0;
SetLength(Result, Length(a));
for I:=High(a) downto 0 do
begin
Int64Rec(Sum).Hi := Int64Rec(Sum).Lo;
Int64Rec(Sum).Lo := a[I];
Result[I] := Sum div b;
Sum := Sum mod b;
end;
Remainder := Sum;
end;
function BigMod(a: TDigits; b: Cardinal): Cardinal; overload;
begin
BigDiv(a, b, Result);
end;
procedure BigInc(var a: TDigits; b: Cardinal);
var
I: Integer;
begin
I := 0;
while True do
begin
if High(a) < I then
begin
SetLength(a, I+1);
a[I] := 0;
end;
if a[I] = $FFFFFFFF then
a[I] := 0
else
begin
Inc(a[I]);
Exit;
end;
end;
end;
procedure BigDec(var a: TDigits);
var
I: Integer;
begin
I := 0;
while True do
begin
if High(a) < I then
BigError('BigDec: Decrement underflow');
if a[I] = 0 then
a[I] := $FFFFFFFF
else
begin
Dec(a[I]);
_BigNormalize(a);
Exit;
end;
end;
end;
function BigIsOdd(a: TDigits): Boolean;
begin
if Length(a) = 0 then
Result := False
else
Result := a[0] and 1 <> 0;
end;
function BigIsEven(a: TDigits): Boolean;
begin
if Length(a) = 0 then
Result := True
else
Result := a[0] and 1 = 0;
end;
function BigDiv(a, b: TDigits): TDigits; overload;
var
m: TDigits;
begin
Result := BigDiv(a, b, m);
end;
function BigMod(a, b: TDigits): TDigits; overload
begin
BigDiv(a, b, Result);
end;
function BigExpMod(x, y, n: TDigits): TDigits;
var
s, t, u: TDigits;
begin
s := BigOne;
t := x;
u := BigCopy(y);
while BigCompare(u, 0) > 0 do
begin
if BigIsOdd(u) then
s := BigMod(BigMul(s, t), n);
_BigDivBy2(u);
t := BigMod(BigMul(t, t), n);
end;
Result := s;
end;
function BigGCD(x, y: TDigits): TDigits; overload;
begin
if (BigCompare(x, 0) = 0) and (BigCompare(y, 0) = 0) then
BigError('BigGCD: Cannot compute the greatest common divisor between two zero bignum.');
while True do
begin
if BigCompare(x, 0) = 0 then
begin
Result := y;
Exit;
end;
if BigCompare(y, 0) = 0 then
begin
Result := x;
Exit;
end;
if BigCompare(x, y) > 0 then
x := BigMod(x, y)
else
y := BigMod(y, x);
end;
end;
function BigGCD(x: array of TDigits): TDigits; overload;
var
g: TDigits;
I: Integer;
begin
if Length(x) = 0 then
BigError('BigGCD: Cannot compute the greatest common divisor when no arguments are given.');
g := x[0];
for I:=1 to High(x) do
begin
g := BigGCD(g, x[I]);
if BigCompare(g, 1) = 0 then
Break;
end;
Result := g;
end;
function BigLCM(x, y: TDigits): TDigits;
begin
Result := BigDiv(BigMul(x, y), BigGCD(x, y));
end;
{ BigMax: Computes the maximum value of a given list. Note that this function
DOES NOT returns copied value of the maximum. }
function BigMax(a: array of TDigits): TDigits;
var
I: Integer;
begin
if Length(a) = 0 then
BigError('BigMax: Cannot compute maximum value given no argument at all.');
Result := a[0];
for I:=1 to High(a) do
if BigCompare(Result, a[I]) > 0 then
Result := a[I];
end;
{ BigMin: Computes the minimum value of a given list. Note that this function
DOES NOT returns copied value of the minimum. }
function BigMin(a: array of TDigits): TDigits;
var
I: Integer;
begin
if Length(a) = 0 then
BigError('BigMin: Cannot compute minimum value given no argument at all.');
Result := a[0];
for I:=1 to High(a) do
if BigCompare(Result, a[I]) < 0 then
Result := a[I];
end;
{ BigSwap: Exchanges values between 'a' and 'b'. }
procedure BigSwap(var a, b: TDigits);
var
t: TDigits;
begin
t := a;
a := b;
b := t;
end;
function BigPower(x: TDigits; y: Integer): TDigits;
var
I: Integer;
begin
Result := BigOne;
for I:=1 to y do
Result := BigMul(Result, x);
end;
{ BigToStr: Converts a bignum into a decimal string }
function BigToStr(a: TDigits): string;
var
bcd: array of Byte;
K: Integer;
Base: Cardinal;
I: Integer;
Carry: Cardinal;
L: Integer;
begin
if Length(a) = 0 then
begin
Result := '0';
Exit;
end;
SetLength(bcd, Length(a) * 10);
Base := Cardinal(1) shl 31;
K := High(a);
while True do
begin
if a[K] and Base = 0 then
Carry := 0
else
Carry := 1;
for I:=0 to High(bcd) do
begin
bcd[I] := (bcd[I] shl 1) or Carry;
if bcd[I] < 10 then
Carry := 0
else
begin
Carry := 1;
Dec(bcd[I], 10);
end;
end;
Base := Base shr 1;
if Base = 0 then
begin
Base := Cardinal(1) shl 31;
Dec(K);
if K < 0 then
Break;
end;
end;
I := High(bcd);
while (I >= 0) and (bcd[I] = 0) do
Dec(I);
L := I+1;
SetLength(Result, L);
for I:=0 to L-1 do
Result[L-I] := Char(bcd[I] + Ord('0'));
end;
{ ExtBinEuclid: Performs extended Euclidean algorithm (from page 247) }
procedure _ExtBinEuclid(var u, v, u1, u2, u3: TDigits);
var
k: Integer;
t1, t2, t3: TDigits;
begin
if BigCompare(u, v) < 0 then
BigSwap(u, v);
k := 0;
while BigIsEven(u) and BigIsEven(v) do
begin
_BigShr(u, 1);
_BigShr(v, 1);
Inc(K);
end;
u1 := BigCopy(BigOne);
u2 := BigCopy(BigZero);
u3 := BigCopy(u);
t1 := BigCopy(v);
t2 := BigSub(u, BigOne);
t3 := BigCopy(v);
repeat
repeat
if BigIsEven(u3) then
begin
if BigIsOdd(u1) or BigIsOdd(u2) then
begin
u1 := BigAdd(u1, v);
u2 := BigAdd(u2, u);
end;
_BigShr(u1, 1);
_BigShr(u2, 1);
_BigShr(u3, 1);
end;
if BigIsEven(t3) or (BigCompare(u3, t3) < 0) then
begin
BigSwap(u1, t1);
BigSwap(u2, t2);
BigSwap(u3, t3);
end;
until BigIsEven(u3);
while (BigCompare(u1, t1) < 0) or (BigCompare(u2, t2) < 0) do
begin
u1 := BigAdd(u1, v);
u2 := BigAdd(u2, u);
end;
_BigSubAt(u1, t1, 0, 0);
_BigSubAt(u2, t2, 0, 0);
_BigSubAt(u3, t3, 0, 0);
until BigCompare(t3, 0) > 0;
_BigShl(u1, k);
_BigShl(u2, k);
_BigShl(u3, k);
end;
function BigExtEuclid(u1, v1: TDigits): TDigits;
var
a, b, gcd, u, v: TDigits;
begin
u := BigCopy(u1);
v := BigCopy(v1);
_ExtBinEuclid(u, v, a, b, gcd);
if BigCompare(gcd, 1) = 0 then
Result := BigSub(u, b)
else
Result := BigZero;
end;
function BigRandom(Nibbles: Integer): TDigits; overload;
var
I: Integer;
begin
SetLength(Result, Nibbles);
for I:=0 to Nibbles-1 do
Result[I] := Cardinal(Random(-1)+1);
while Result[High(Result)] = 0 do
Result[High(Result)] := Random(0);
end;
function BigRandom(p: TDigits): TDigits; overload;
var
L: Integer;
begin
L := NormalLength(p);
if L = 0 then
begin
SetLength(Result, 0);
Exit;
end;
Result := BigRandom(L);
Result[L-1] := Cardinal(Random(p[L-1]));
end;
function RabbinMillerPrimeTest(p: TDigits): Boolean;
var
p_1: TDigits;
a, z, m: TDigits;
b, j: Cardinal;
label
Step4;
begin
Result := True;
b := 0;
p_1 := BigSub(p, BigOne);
m := BigCopy(p_1);
while BigIsEven(m) do
begin
Inc(b);
_BigShr(m, 1);
end;
Write('@');
a := BigRandom(1);
j := 0;
z := BigExpMod(a, m, p);
Write('!');
if (BigCompare(z, 1) = 0) or (BigCompare(z, p_1) = 0) then
begin
Result := True;
Exit;
end;
Step4:
Write('#');
if (j > 0) and (BigCompare(z, 1) = 0) then
begin
Result := False;
Exit;
end;
Inc(j);
if (j < b) and (BigCompare(z, p_1) <> 0) then
begin
z := BigExpMod(z, BigTwo, p);
goto Step4;
end;
if BigCompare(z, p_1) = 0 then
begin
Result := True;
Exit;
end;
if (j = b) and (BigCompare(z, p_1) <> 0) then
Result := False;
end;
var
Primes : array[1..1026] of Integer =
(3, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127,
131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389,
397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009,
1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123,
1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279,
1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429,
1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553,
1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693,
1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847,
1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997,
1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131,
2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417,
2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593,
2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719,
2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861,
2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037,
3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209,
3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359,
3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527,
3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659,
3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821,
3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967,
3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129,
4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273,
4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637,
4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789,
4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957,
4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101,
5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281,
5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623,
5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779,
5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903,
5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101,
6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269,
6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397,
6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599,
6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779,
6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947,
6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283,
7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487,
7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607,
7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789,
7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951,
7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161,
8167, 8171, 8179, 8191);
function BigPrimeTest(p: TDigits; nTest: Integer = 5): Boolean;
var
I: Integer;
begin
Result := False;
for I:=Low(Primes) to High(Primes) do
if BigIsDivisible(p, Primes[I]) then
Exit;
Write('-');
for I:=1 to nTest do
if not RabbinMillerPrimeTest(p) then
Exit;
Result := True;
end;
function BigGeneratePrime(Nibbles: Integer): TDigits;
var
Round: Integer;
begin
Round := 0;
while True do
begin
Result := BigRandom(Nibbles);
Result[Nibbles-1] := Result[Nibbles-1] or Cardinal(1 shl 31);
Result[0] := Result[0] or 1;
Write('*');
if BigPrimeTest(Result, 5) then
begin
WriteLn('(r=', round, ')');
Exit;
end;
Write('.');
Inc(Round);
end;
end;
procedure TestGCD(a, b: string);
begin
WriteLn('gcd(',a , ',', b,')=',BigToStr(BigGCD(BigConvert(a), BigConvert(b))));
end;
procedure TestDiv(sa, sb: string); overload;
var
a, b, r, m: TDigits;
begin
a := BigConvert(sa);
b := BigConvert(sb);
r := BigDiv(a, b, m);
WriteLn('BigDiv(',sa , ',', sb,')=', BigToStr(r), ' ... ', BigToStr(m));
end;
procedure TestDiv(sa: string; b: Cardinal); overload;
var
a, r: TDigits;
m: Cardinal;
begin
a := BigConvert(sa);
r := BigDiv(a, b, m);
WriteLn('BigDiv(',sa , ',', b,')=', BigToStr(r), ' ... ', m);
end;
procedure TestRandom;
var
s: string;
begin
while True do
begin
WriteLn('BigRandom=' + BigToStr(BigRandom(16)));
// ReadLn(s);
if s <> '' then
Exit;
end;
end;
procedure TestPrime;
var
I: Integer;
begin
for I:=1 to 5 do
begin
WriteLn('Prime=' + BigToStr(BigGeneratePrime(4)));
end;
end;
procedure TestShr;
var
a: TDigits;
begin
a := BigConvertHex('1');
while BigCompare(a, 0) > 0 do
begin
_BigShl(a, 1);
WriteLn(BigToStr(a));
ReadLn;
end;
end;
procedure TestExpMod(x, y, n: string);
begin
WriteLn(x, '^', y, ' mod ', n, ' = ', BigToStr(BigExpMod(BigConvert(x), BigConvert(y), BigConvert(n))));
end;
procedure Main;
begin
TestDiv('1', '1');
TestDiv('0', '1');
TestDiv('99999', '1');
TestDiv('50000000000000000000000003', 5);
TestDiv('10000000000000000', '9');
TestDiv('10000000000000000', '4294967296');
TestDiv('4312092353817979174', '444877445811470254');
TestDiv('4312092353817979174', '444877445811470254');
TestDiv('10000000000000000000000000000000000000', '7');
TestExpMod('7', '100', '1234567890');
TestExpMod('9', '100', '1234567890');
TestGCD('44455555874312443214','4432174432919929448122');
TestGCD('1234', '5678');
TestGCD('195265', '266198');
WriteLn('gcd(12,6) = ', BigToStr(BigGCD(BigConvert('12'),BigConvert('6'))));
// TestRandom;
// Randomize;
TestPrime;
end;
procedure Test;
var
a, b, c: Int64;
procedure Unavoid(var p);
begin
end;
begin
a := 10;
Unavoid(a);
b := 5;
Unavoid(b);
c := a div b;
Unavoid(c);
end;
initialization
Test;
BigDebug(BigZero);
BigOne := BigConvert('1');
BigTwo := BigConvert('2');
Main;
end.
--
Chaos is the best description of the constant state of human society.
Therefore, dynamic balance is required for us to to survive when vital fault
occurrs. So in the society, we don't chase for peace and order, but existence
and survival, instead.
--
※ 發信站: 批踢踢實業坊(ptt.twbbs.org)
◆ From: h82.s80.ts30.hi