{Start of file MultiID.Pas *************************************************}
unit MultiID; {Multiple-precision integer decimal algorithms}
{ Version = 'Version 2.00, last revised: 92/02/09, 1000 hours';
Author = 'Copyright (c) 1981-1992 by author: Harry J. Smith,';
Address = '19628 Via Monte Dr., Saratoga, CA 95070. All rights reserved.';
}
{***************************************************************************}
{$I-} {Do our own i/o error checks}
{$N+} {Uses numeric coprocessor}
{$R-} {No index Range checking}
{$V-} {Parameter string length need not match}
{Developed in TURBO Pascal 5.5, maintained in TURBO Pascal 6.0}
{ Revisions made - Listed after end. statement }
{In Turbo Pascal 4.0 etc. the max odd integer to store in a Single is}
{2**24-1 = 167,77215; for a Double is 2**53-1 = 9,00719,92547,40991}
interface
uses Dos, Crt, Printer; {Turbo Pascal interface}
const
MuDMax = 7; {Max number of decimal digits in a super digit}
MuNMax = 16379; {Max number of super digits in a multi-precision number}
{Each super digit is from 0 to 9999999, i.e. Base = 10**7}
type
Mu1Digit = Single; {Can hold + or - integer <= Base}
Mu2Digit = Double; {Can hold + or - integer <= Base squared + Base}
VType = Array[0..MuNMax] of Mu1Digit; {Value, LSD at index 0}
VPtr = ^VType;
MultiIDO = object {Multiple-precision integer decimal algorithms}
constructor Init( MuMbM : Integer);
destructor Done;
end;
MultiUI = object {A multiple-precision unsigned integer}
N : Integer; {Current length in super digits, 1..MuNMax}
M : Integer; {Max number of super digits ever in this number}
V : VPtr;
constructor Init( NMaxI : Integer);
destructor Done; virtual;
procedure Clear; virtual; {Set value = 0}
procedure SetTo( var X : MultiUI); {Self:= X}
procedure Norm; {Normalize}
procedure RAdd( var X : MultiUI); {Self:= Self + X}
procedure RSub( var X : MultiUI); {Self:= Self - X}
procedure Add( var X, Y : MultiUI); {Self:= X + Y}
procedure Sub( var X, Y : MultiUI); {Self:= X - Y}
function AbComp( var X : MultiUI): Integer; {AbComp = sign(|Self| - |X|)}
function ZTest : Boolean; {True if Self is Zero}
function EQ1( D1 : Mu1Digit) : Boolean; {True if |Self| = D1}
function GT1( D1 : Mu1Digit) : Boolean; {True if |Self| > D1}
end;
MultiSI = object( MultiUI) {A multiple-precision signed integer}
S : Boolean; {Sign, True = negative}
constructor Init( NMaxI : Integer);
destructor Done; virtual;
procedure Clear; virtual; {Set value = 0}
procedure ChS; {Change sign, Self = 0 - Self}
procedure SetTo( var X : MultiSI); {Self:= X}
procedure Norm; {Normalize}
procedure RAdd( var X : MultiSI); {Self:= Self + X}
procedure RSub( var X : MultiSI); {Self:= Self - X}
procedure Add( var X, Y : MultiSI); {Self:= X + Y}
procedure Sub( var X, Y : MultiSI); {Self:= X - Y}
procedure Value( St : String; var I : Integer);
{Convert String to Self, returns I = # of character used}
procedure SetTo1( D1 : Mu1Digit); {Self = D1 Mod Base}
procedure SetToEx( Ex : Extended); {Self = Ex Mod Base**4}
procedure SetToD( Db : Double); {Self = Db Mod Base**4}
procedure Get1( var D1 : Mu1Digit); {D1 = Self Mod Base}
procedure GetEx( var Ex : Extended); {Ex = Self Mod Base**4}
procedure GetD( var Db : Double); {Db = Self Mod Base**4}
procedure RAdd1( D1 : Mu1Digit); {Self = Self + D1}
procedure Add1( var X : MultiSI; D1 : Mu1Digit); {Self = X + D1}
function Comp( var X : MultiSI) : Integer; {Comp = sign( Self - X)}
procedure Writ( var Out : TEXT); {Output Self as a line of text}
procedure WritLn( var Out : TEXT); {Output Self and a new line}
procedure ShortWr( var Out : TEXT; Short : LongInt);
{Output Self in short form ... if more than Short digits}
procedure ShortWrLn( var Out : TEXT; Short : LongInt);
{Output Self short and a new line}
procedure RMul1( D1 : Mu1Digit); {Self = Self * D1}
procedure Mul1( var X : MultiSI; D1 : Mu1Digit); {Self = X * D1}
procedure Mul( var X, Y : MultiSI); {Self = X * Y}
procedure RMul( var X : MultiSI); {Self = Self * X}
procedure Sq( var X : MultiSI); {Self = X * X}
procedure RSq; {Self = Self * Self}
procedure PowMB( var B, P : MultiSI); {Self = (B ** P) Mod MuMB}
procedure RPowMB( var P : MultiSI); {Self = (Self ** P) Mod MuMB}
procedure ModMB; {Self = Self Mod MuMB}
procedure RDiv1( D1 : Mu1Digit); {Self = Self / D1}
procedure Div1( var U : MultiSI; D1 : Mu1Digit); {Self = U / D1}
procedure Mod1( D1: Mu1Digit; var R1: Mu1Digit); {R1 = Rem( Self / D1)}
procedure RDiv1QR( D1: Mu1Digit; var R1: Mu1Digit);
{Self = Self / D1, R1 = Rem}
procedure Div1QR( var U: MultiSI; D1: Mu1Digit; var R1: Mu1Digit);
{Self = U / D1, R1 = Rem}
procedure Divi( var U, D : MultiSI); {Self = U / D}
procedure RDiv( var D : MultiSI); {Self = Self / D}
procedure Modu( var U, D : MultiSI); {Self = Rem(U / D)}
procedure RMod( var D : MultiSI); {Self = Rem( Self / D)}
procedure DivQR( var U, D, R : MultiSI); {Self = U / D, R = Rem}
procedure RDivQR( var D, R : MultiSI); {Self = Self / D, R = Rem}
procedure GCD( var A, B : MultiSI);{Self = Greatest common divisor A, B}
procedure RGCD( var A : MultiSI); {Self=Greatest common divisor A, Self}
procedure FacMB( var X : MultiSI); {Self = (X !) Mod MuMB = 1*2*3*...*X}
procedure RFacMB; {Self = (Self !) Mod MuMB = 1*2*3*...*X}
procedure SqRtRem( var X, R : MultiSI); {Self = SqRt(X), R = Rem}
procedure RSqRtRem( var R : MultiSI); {Self = SqRt( Self), R = Rem}
procedure SqRoot( var X : MultiSI); {Self = SqRt(X)}
procedure RSqRt; {Self = SqRt( Self)}
end;
var
MultiIDV : MultiIDO; {Multiple-precision integer decimal algorithms}
{The following are set when the unit MultiID is loaded}
Base : Mu2Digit; {Radix of multi-precision numbers, 10**MuDMax}
MaxDigit : Mu1Digit; {Base - 1}
BaseSq : Mu2Digit; {Base squared}
BSMB : Mu2Digit; {(BaseSq - Base) for MuDiv and MuSqRt}
OutP : Array[0..1] of ^Text; {Pointers to Output and Lst}
{The following can be tested by user}
MuErr : Boolean; {True if multi-precision error occurred}
MuAbort : Boolean; {True if Mu procedure interrupted by operator}
KeyHit : Boolean; {True if KeyPressed has been cleared}
KeyCh : Char; {Key input when KeyPressed was cleared}
{The following can be changed by user}
Echo : Integer; {1 if in Echo output to printer mode, 0 otherwise}
MuDpG : Integer; {Digits per group in number output display >= 2}
MuLenD : Integer; {Min length in digits to display length in digits}
MuErrRep : Boolean; {True if multi-precision error reports are desired}
MMax : Integer; {Max number of super digits <= MuNMax}
TracN : LongInt; {Number if iterations of an innermost loop}
Trace : LongInt; {Number of "Digits" left to compute}
DiagOn : Boolean; {Diagnostics turned on}
TV0 : Double; {Dos time value at time zero for diag}
TV1 : Double; {Dos time value for delta time, TV2 - TV1}
TV2 : Double; {Dos time value for total time, TV2 - TV0}
DosClockP: Double; {Previous value of the Dos clock for day cycle test}
DosDays : Integer; {Number of days to add to Dos clock}
MuMaxW : Integer; {Max number of characters to write on a line}
MuTot : Integer; {Total number of characters on the line so far}
{The following are set when the object MultiIDV is initialized}
MuMB : ^MultiSI; {Multi-precision modulo base, 0 => normal aritm.}
procedure ShiftUp( var St : String); {Shift string to upper case}
procedure MuWriteErr( St : String); {Write error statement and set error flag}
procedure ReadLInt( Mess : String; Min, Max, Nom : LongInt;
var II : LongInt); {Read in a long integer from keyboard}
procedure ReadInt( Mess : String; Min, Max, Nom : Integer;
var II : Integer); {Read in an integer from keyboard}
procedure MuSetMMax( NRegs : Integer); {Set MMax using MemAvail}
procedure MuInterrupt; {Test for ESC key pressed to abort operation}
function DosClock : Double; {Get the value of the Dos clock in total seconds}
{Only good to 0.01 seconds, must call earlier once each day}
procedure Diag( Mess : String); {Output a diagnostic message with delta times}
procedure WriteMax( var Out : TEXT; St : String);
{Output String and line feed every MuMaxW, MuTot = characters on line so far}
procedure WriteMaxLn( var Out : TEXT; St : String);
implementation
{--------------------------------------}
{ Common hidden routines }
{--------------------------------------}
{--------------------------------------}
procedure IpAdd( var X, Y : MultiUI; var K : Mu1Digit); {|Y| = |Y| + |X|}
{Multi-precision integer add absolute values in place; Y is changed}
{Y.N Must be >= X.N, X.S and Y.S not used or changed; X not changed}
{Overflow super digit returned not zero if and only if Y overflows}
{MuErr flag not set. Address of X and Y can be the same, then X is changed}
var
I : Integer;
T : Mu2Digit;
begin
I:= 0; K:= 0;
while I < X.N do begin
T:= X.V^[I] + Y.V^[I] + K; {Add next super digit with carry}
if T >= Base then begin
K:= 1; Y.V^[I]:= T - Base;
end
else begin
K:= 0; Y.V^[I]:= T;
end;
Inc(I);
end;
if K = 0 then Exit;
while (I < Y.N) do begin {X = 0 from here on}
if Y.V^[I] = MaxDigit then begin {K = 1, carry to next super digit of Y}
Y.V^[I]:= 0;
end
else begin
Y.V^[I]:= Y.V^[I] + 1; K:= 0; Exit; {Return K = 0}
end;
Inc(I);
end;
Y.N:= Y.N + 1;
if Y.N > Y.M then begin {If overflow return K = 1}
Y.N:= Y.M;
Y.Norm;
end
else begin
Y.V^[ Y.N-1]:= 1; K:= 0; {Return K = 0}
end;
end; {IpAdd}
{--------------------------------------}
procedure IpSub( var X, Y : MultiUI); {|Y| = |Y| - |X|}
{Multi-precision integer subtract absolute values in place; Y is changed}
{|Y| Must be >= |X|, X.S and Y.S not used or changed; X not changed}
{MuErr flag not set. Address of X and Y can be the same, then X is changed}
var
I : Integer;
K : Mu1Digit;
T : Mu2Digit;
begin
I:= 0; K:= 0;
while (I < X.N) do begin
T:= Y.V^[I] - X.V^[I] + K; {Subtract next super digit with borrow}
if T < 0 then begin
K:= -1; Y.V^[I]:= T + Base;
end
else begin
K:= 0; Y.V^[I]:= T;
end;
Inc(I);
end;
if (K = 0) or (X.N = Y.N) then begin Y.Norm; Exit; end;
while (I < Y.N) do begin {X = 0 from here on}
if Y.V^[I] = 0 then begin {K = -1, borrow from next super digit of Y}
Y.V^[I]:= MaxDigit;
end
else begin
Y.V^[I]:= Y.V^[I] - 1; Y.Norm; Exit;
end;
Inc(I);
end;
end; {IpSub}
{--------------------------------------}
procedure IpMul1(D : Mu1Digit; var X : MultiUI; var K : Mu1Digit);{X = |D| * X}
{Multi-precision one super digit multiply in place; X is changed}
{K = Overflow super digit returned > zero if and only if X overflows}
{Signs not used}
var
I : Integer;
T : Mu2Digit;
begin
D:= Abs(D);
K:= 0;
for I:= 0 to X.N - 1 do begin
T:= D * X.V^[I] + K; {Multiply next super digit and add carry}
K:= Int(T / Base);
X.V^[I]:= T - K * Base;
end;
if K = 0 then Exit;
X.N:= X.N + 1;
if X.N > X.M then begin {If overflow return K > 0}
X.N:= X.M;
end
else begin
X.V^[ X.N-1]:= K; K:= 0;
end;
end; {IpMul1}
{--------------------------------------}
procedure IpDiv1(D : Mu1Digit; var U : MultiSI; var R : Mu1Digit); {U = U / D}
{Multi-precision one super digit divide in place, U is changed} {R = Remainder}
{Allows literal D, MuErr set True if D = 0}
var
T : Mu2Digit;
I : Integer;
AD : Mu1Digit;
begin
R:= 0;
if D = 0 then begin
MuWriteErr('Cannot divide by zero, continuing...');
Exit;
end;
MuInterrupt;
if MuAbort then begin
U.Clear; Exit;
end;
AD:= Abs(D);
I:= U.N - 1;
while I >= 0 do begin
T:= R * Base + U.V^[I]; {Divide next super digit and save remainder}
U.V^[I]:= Int(T / AD);
R:= T - U.V^[I] * AD;
Dec(I);
end;
if U.S and (R <> 0) then R:= -R;
U.S:= U.S xor (D < 0);
U.Norm;
end; {IpDiv1}
{--------------------------------------}
procedure IpDiv( var D, U, Q, R : MultiSI); {Q = U / D, R = Rem}
{U and D are not changed, U is moved to R and then R is divided}
{in place to give Q and a remainder in R, it is OK for U and R}
{to have the same location in memory, but then U will be changed}
{Sets MuErr True if D = 0}
{See Knuth Vol. 2 of "The Art..." Algorithm D, pg. 237.}
var
V1, V2, D1, K, QH : Mu1Digit;
T : Mu2Digit;
I, J, IJ : Integer;
begin
if D.N = 1 then begin {Use IpDiv1 for a one super digit divide}
Q.SetTo(U); D1:= D.V^[0];
if D.S and (D1 <> 0) then D1:= -D1;
IpDiv1( D1, Q, K);
R.S:= (K < 0);
R.V^[0]:= Abs(K);
R.N:= 1;
end
else begin
R.SetTo(U); {SetTo is No-Op if same address}
if R.N < D.N then begin
Q.Clear; Exit;
end;
Q.N:= R.N - D.N + 1;
J:= R.N;
R.V^[ R.N]:= 0;
D.V^[ D.N]:= 0;
V1:= D.V^[ D.N-1];
V2:= D.V^[ D.N-2];
D1:= Int( Base / (V1 + 1)); {D1. Normalize}
if D1 > 1 then begin
IpMul1( D1, D, K); V1:= D.V^[ D.N-1]; V2:= D.V^[ D.N-2];
IpMul1( D1, R, K);
if K <> 0 then R.V^[J]:= K;
end; {D2. Init J done above}
KeyHit:= False;
while J >= D.N do begin {D3. Calculate Q hat}
T:= R.V^[J] * Base + R.V^[ J-1];
if V1 = R.V^[J]
then QH:= MaxDigit
else QH:= Int(T / V1);
while (V2 * QH > (T - QH * V1) * Base + R.V^[ J-2]) do QH:= QH - 1;
{QH:=QH+1; .Diag for add back test}
K:= Base;
for I:= 0 to D.N do begin {D4. Multiply and subtract}
IJ:= I + J - D.N;
T:= R.V^[ IJ] - QH * D.V^[I] + K + BSMB; {+ BaseSq - Base}
K:= Int(T / Base);
R.V^[ IJ]:= T - K * Base;
end;
Q.V^[J - D.N]:= QH; {D5. Test remainder}
if K = MaxDigit then begin
{WriteLn('MuDiv: Doing an add back'); .Diag for add back test}
Q.V^[J - D.N]:= QH - 1; {D6. Add back}
K:= 0; IJ:= J - D.N;
for I:= 0 to D.N do begin
T:= D.V^[I] + R.V^[ IJ] + K;
K:= Int(T / Base);
R.V^[ IJ]:= T - K * Base;
Inc( IJ);
end;
end;
Dec(J); {D7. Loop on J}
MuInterrupt;
if MuAbort then begin
Q.Clear; R.Clear; Exit;
end;
if KeyHit then begin
for I:= 0 to Echo do
WriteLn( OutP[I]^, 'Integer Divide: ',
Trunc( (100.0 * (R.N - J)) / (R.N - D.N + 1)), ' Percent done');
KeyHit:= False;
end;
end;
Q.S:= U.S xor D.S;
R.S:= U.S;
Q.Norm;
R.Norm;
if D1 > 1 then begin {D8. Unnormalize}
IpDiv1( D1, R, K);
IpDiv1( D1, D, K);
end;
end;
end; {IpDiv}
{---End of common hidden routines------}
{--------------------------------------}
{ MultiIDO's method implementation }
{--------------------------------------}
{--------------------------------------}
constructor MultiIDO.Init( MuMbM: Integer);{Initialize Multi-precision package}
{Input: MuMbM, M for MuMb}
{Initialize: MuMB = 0}
begin
if MuMbM > MuNMax then MuMbM:= MuNMax;
New( MuMB, Init( MuMbM));
end; {MultiIDO.Init}
{--------------------------------------}
destructor MultiIDO.Done; {Destruct Multi-precision package}
begin
Dispose( MuMB, Done);
end; {MultiIDO.Done}
{------End of MultiIDO's methods-------}
{--------------------------------------}
{ MultiUI's method implementation }
{--------------------------------------}
constructor MultiUI.Init( NMaxI : Integer);
var
Size : Word;
I : Integer;
begin
M:= NMaxI;
if M > MuNMax then M:= MuNMax;
Size:= SizeOf( Mu1Digit);
Size:= (M+1) * Size;
if Size > MaxAvail then begin
if MemAvail = MaxAvail then
for I:= 0 to Echo do Write( OutP[I]^, 'MemAvail = MaxAvail = ')
else
for I:= 0 to Echo do
Write( OutP[I]^, 'MemAvail = ', MemAvail, ', MaxAvail = ');
for I:= 0 to Echo do
WriteLn( OutP[I]^, MaxAvail, ', need ', Size);
M:= 1; Size:= SizeOf( Mu1Digit); Size:= (M+1) * Size; MuAbort:= True;
end;
GetMem(V, Size);
Clear;
end; {MultiUI.Init}
{--------------------------------------}
destructor MultiUI.Done;
var
Size : Word;
begin
Size:= SizeOf( Mu1Digit);
Size:= (M+1) * Size;
FreeMem(V, Size);
M:= -1;
end; {MultiUI.Done}
{--------------------------------------}
procedure MultiUI.Clear; {Set value = 0}
begin
V^[0]:= 0; N:= 1;
end; {MultiUI.Clear}
{--------------------------------------}
procedure MultiUI.SetTo( var X : MultiUI); {Self = X, copy used "digits" only}
var
I : Integer;
begin
if Self.V = X.V then Exit;
N:= X.N;
if N > M then begin
N:= M;
MuWriteErr('SetTo overflow, continuing...');
end;
for I:= 0 to N - 1 do V^[I]:= X.V^[I];
end; {MultiUI.SetTo}
{--------------------------------------}
procedure MultiUI.Norm; {Normalize}
var
I : Integer;
begin
if N < 1 then Clear;
I:= N - 1; {Delete leading zeros}
while (V^[I] = 0) and (I > 0) do begin
Dec(I);
end;
N:= I + 1;
end; {MultiUI.Norm}
{--------------------------------------}
function MultiUI.AbComp( var X: MultiUI): Integer; {AbComp= sign(|Self| - |X|)}
{Si = -1 if |Self| < |X|; Si = 0 if |Self| = |X|; Si = +1 if |Self| > |X|}
{Self.S and X.S not used or changed; Self, X not changed}
var
I : Integer;
begin
if N < X.N then begin AbComp:= -1; Exit; end;
if N > X.N then begin AbComp:= +1; Exit; end;
I:= N - 1;
while (V^[I] = X.V^[I]) and (I > 0) do begin
Dec(I);
end;
if V^[I] < X.V^[I] then begin AbComp:= -1; Exit; end;
if V^[I] > X.V^[I] then begin AbComp:= +1; Exit; end;
AbComp:= 0;
end; {MultiUI.AbComp}
{--------------------------------------}
procedure MultiUI.RAdd( var X : MultiUI); {Self:= Self + X}
{Sets MuErr True if overflow}
var
K : Mu1Digit;
I : Integer;
begin
if N < X.N then begin {If Digits in Self < Digits in X}
if M >= X.N then begin
for I:= N to X.N-1 do V^[I]:= 0;
N:= X.N;
end
else begin
MuWriteErr('X too big to add, continuing...');
end;
end;
IpAdd(X, Self, K);
if (K > 0) then MuWriteErr('Addition overflow, continuing...');
end; {MultiUI.RAdd}
{--------------------------------------}
procedure MultiUI.RSub( var X : MultiUI); {Self:= Self - X}
{Sets MuErr True if N > Self}
var
I : Integer;
begin
I:= AbComp(X);
if I = 0 then Clear
else if I < 0 then {If Self < X, Error}
MuWriteErr('Unsigned subtraction error, continuing...')
else
IpSub(X, Self);
end; {MultiUI.RSub}
{--------------------------------------}
procedure MultiUI.Add( var X, Y : MultiUI); {Self:= X + Y}
{Sets MuErr True if overflow}
begin
if Self.V = X.V then
RAdd(Y)
else if Self.V = Y.V then
RAdd(X)
else if X.N >= Y.N then begin
SetTo(X); RAdd(Y);
end
else begin
SetTo(Y); RAdd(X);
end;
end; {MultiUI.Add}
{--------------------------------------}
procedure MultiUI.Sub( var X, Y : MultiUI); {Self:= X - Y}
{Sets MuErr True if overflow}
var
YL : ^MultiUI;
begin
if @X = @Y then Clear
else if Self.V = X.V then
RSub(Y)
else if Self.V = Y.V then begin
New( YL, Init( Y.N)); YL^.SetTo(Y);
SetTo(X);
RSub( YL^);
Dispose( YL, Done);
end
else begin
SetTo(X);
RSub(Y);
end;
end; {MultiUI.Sub}
{--------------------------------------}
function MultiUI.ZTest : Boolean; {True if Self is Zero}
begin
ZTest:= (N = 1) and (V^[0] = 0);
end; {MuLTI.ZTest}
{--------------------------------------}
function MultiUI.EQ1( D1 : Mu1Digit) : Boolean; {True if |Self| = D1}
{Self, D1 are not changed, allows literal D1}
begin
EQ1:= (N = 1) and (V^[0] = D1);
end; {MultiUI.EQ1}
{--------------------------------------}
function MultiUI.GT1( D1 : Mu1Digit) : Boolean; {True if |Self| > D1}
{Self, D1 are not changed, allows literal D1}
begin
GT1:= (N > 1) or (V^[0] > D1);
end; {MultiUI.GT1}
{------End of MultiUI's methods--------}
{--------------------------------------}
{ MultiSI's method implementation }
{--------------------------------------}
{--------------------------------------}
constructor MultiSI.Init( NMaxI : Integer);
begin
MultiUI.Init( NMaxI);
S:= False;
end; {MultiSI.Init}
{--------------------------------------}
destructor MultiSI.Done;
begin
MultiUI.Done;
end; {MultiSI.Done}
{--------------------------------------}
procedure MultiSI.Clear; {Set value = 0}
begin
MultiUI.Clear;
S:= False;
end; {MultiSI.Clear}
{--------------------------------------}
procedure MultiSI.ChS; {Change sign, Self = - Self}
begin
if not ZTest then S:= not S;
end; {MultiSI.ChS}
{--------------------------------------}
procedure MultiSI.SetTo( var X : MultiSI); {Self:= X}
begin
MultiUI.SetTo(X);
S:= X.S;
end; {MultiSI.SetTo}
{--------------------------------------}
procedure MultiSI.Norm; {Normalize}
begin
MultiUI.Norm;
if ZTest then S:= False; {Prevent -0}
end; {MultiSI.Norm}
{--------------------------------------}
procedure MultiSI.RAdd( var X : MultiSI); {Self:= Self + X}
{Sets MuErr True if overflow}
var
Temp : ^MultiUI;
K : Mu1Digit;
I : Integer;
begin
if S = X.S then {Same signs}
MultiUI.RAdd(X)
else begin {Opposite signs}
I:= AbComp(X);
if I < 0 then begin {If |Self| < |X|, |Self| = |X| - |Self|}
New( Temp, Init(N));
Temp^.SetTo( Self); {Temp:= Small Self}
Self.SetTo(X); {Self:= Large X}
IpSub( Temp^, Self);
Dispose( Temp, Done);
end
else if I > 0 then {If |Self| > |X|, |Self| = |Self| - |X|}
IpSub(X, Self)
else
Clear; {If |Self| = |X|, Self = 0}
end;
end; {MultiSI.RAdd}
{--------------------------------------}
procedure MultiSI.RSub( var X : MultiSI); {Self:= Self - X}
{Sets MuErr True if overflow}
begin
if Self.V = X.V then
Clear
else begin
X.S:= not X.S;
RAdd(X);
X.S:= not X.S;
end;
end; {MultiSI.RSub}
{--------------------------------------}
procedure MultiSI.Add( var X, Y : MultiSI); {Self:= X + Y}
{Sets MuErr True if overflow}
var
I : Integer;
begin
if Self.V = X.V then
RAdd(Y)
else if Self.V = Y.V then
RAdd(X)
else if X.S = Y.S then begin
MultiUI.Add(X, Y); S:= X.S;
end
else begin
I:= X.AbComp(Y); {Signs are different}
if I = 0 then Clear
else if I > 0 then begin
SetTo(X); IpSub(Y, Self); {|X| > |Y|, so |X| - |Y|}
end
else begin
SetTo(Y); IpSub(X, Self);
end;
end;
end; {MultiSI.Add}
{--------------------------------------}
procedure MultiSI.Sub( var X, Y : MultiSI); {Self:= X - Y}
{Sets MuErr True if overflow}
begin
if @X = @Y then Clear
else if Self.V = X.V then
RSub(Y)
else if Self.V = Y.V then begin
RSub(X); ChS;
end
else begin
Y.S:= not Y.S;
Add(X, Y);
Y.S:= not Y.S;
end;
end; {MultiSI.Sub}
{--------------------------------------}
procedure MultiSI.Value( St : String; var I : Integer);
{Convert String to Self, returns I = # of character used}
{Allows St to be a literal, MuErr set True if overflow}
var
K : Mu1Digit;
Len : Integer;
J, D : Integer;
Fini : Boolean;
Overflow : Boolean;
begin
I:= 1;
While St[I] = ' ' do Inc(I);
if (St[I] = '-') or (St[I] = '+') then Inc(I);
while (I <= Length( St)) and
(((St[I] >= '0') and (St[I] <= '9')) or (St[I] = ',')) do
Inc(I);
Dec(I);
Clear; Fini:= False; Overflow:= False;
Len:= I;
if Len = 0 then Exit;
J:= 1;
While St[J] = ' ' do Inc(J); { HJS 91/04/24, 91/09/24 }
if St[J] = '-' then begin Inc(J); S:= True; end
else if St[J] = '+' then Inc(J);
while Not Fini and (J <= Len) do begin
if (St[J] >= '0') and (St[J] <= '9') then begin
if (N = M) and (V^[ N-1] >= Base / 10) then begin
Overflow:= True;
Fini:= True;
end
else begin
D:= Ord( St[J]) - Ord('0');
IpMul1( 10, Self, K);
if K > 0 then Overflow:= True;
V^[0]:= V^[0] + D;
end;
end
else
if St[J] <> ',' then Fini:= True;
Inc(J);
end;
if Overflow then MuWriteErr('Input number overflow, continuing...');
Norm;
end; {MultiSI.Value}
{--------------------------------------}
procedure MultiSI.SetTo1( D1 : Mu1Digit); {Self = D1 Mod Base}
{Allows a literal D1, D1 an integer, |D1| < Base, D1 not changed}
var
AbsD : Mu1Digit;
begin
AbsD:= Abs( D1);
N:= 1; S:= (D1 < 0);
if AbsD < Base then V^[0]:= AbsD
else
V^[0]:= AbsD - Int( AbsD / Base) * Base;
end; {MultiSI.SetTo1}
{--------------------------------------}
procedure MultiSI.SetToEx( Ex : Extended); {Self = Ex Mod Base**4}
{Allows a literal Ex, Ex not changed, MuErr set True if overflow}
{ Added procedure SetToEx HJS 91/05/02 }
var
St1, St2 : String[ 255];
AE, E1, E2 : Extended;
L, ML : Integer;
begin
AE:= Abs( Ex); E1:= Int( AE / Base); E2:= AE - E1 * Base + Base;
Str( E1:0:0, St1); Str( E2:0:0, St2);
Delete( St2, 1, 1);
St1:= St1 + St2;
L:= Length( St1); ML:= 4 * MuDMax;
if L > ML then St1:= Copy( St1, L - ML + 1, ML);
Value( St1, L);
if Ex < 0.0 then ChS; {Prevents -0}
end; {MultiSI.SetToEx}
{--------------------------------------}
procedure MultiSI.SetToD( Db : Double); {Self = Db Mod Base**4}
{Allows a literal Db, Db not changed, MuErr set True if overflow}
begin
SetToEx( Db);
end; {MultiSI.SetToD}
{--------------------------------------}
procedure MultiSI.Get1( var D1 : Mu1Digit); {D1 = Self Mod Base}
{Self is not changed}
begin
D1:= V^[0];
if S and (D1 <> 0) then D1:= -D1;
end; {MultiSI.Get1}
{--------------------------------------}
procedure MultiSI.GetEx( var Ex : Extended); {Ex = Self Mod Base**4}
{Self is not changed} { Added procedure GetEx HJS 91/05/02 }
var
I : Integer;
T : Extended;
begin
I:= 1; Ex:= V^[0]; T:= Base;
while (I < N) and (I < 4) do begin
Ex:= Ex + V^[I] * T; {Add next super digit}
T:= T * Base;
Inc(I);
end;
if S and (Ex <> 0.0) then Ex:= -Ex;
end; {MultiSI.GetEx}
{--------------------------------------}
procedure MultiSI.GetD( var Db : Double); {Db = Self Mod Base**4}
{Self is not changed}
var
T : Extended;
begin
GetEx( T); Db:= T;
end; {MultiSI.GetD}
{--------------------------------------}
procedure MultiSI.RAdd1( D1 : Mu1Digit); {Self = Self + D1}
{Allows literal D1, D1 is not changed, sets MuErr True if overflow}
var
DL : ^MultiSI;
begin
New( DL, Init(1));
DL^.SetTo1( D1);
RAdd( DL^);
Dispose( DL, Done);
end; {MultiSI.RAdd1}
{--------------------------------------}
procedure MultiSI.Add1( var X : MultiSI; D1 : Mu1Digit); {Self = X + D1}
{Allows literal D1, D1 is not changed, sets MuErr True if overflow}
begin
SetTo(X);
RAdd1( D1);
end; {MultiSI.Add1}