Contributor: JES R. KLINKE
{
You may use the following unit I have made for an encryption program
of mine. It implements real binary arithmetic, no BCD. But be careful,
there is currently no range checking at all, and overflows may result
in endless loops. If you need 2048 bit integers you have to set
BigNumLength to at least 128, a little more would be safer. Also
notice that the routines cannot handle negative numbers.
I hope you find this one useful.
 Jes R Klinke
}
PROGRAM BigNum;
USES
 Crt, Dos;
CONST
 BigNumLength = 20; {Number of words in value}
TYPE
 PBigNum = ^TBigNum;
 TBigNum = object
 Value : ARRAY [0..BigNumLength - 1] OF WORD;
 PROCEDURE ASSIGN (VAR AValue : TBigNum);
 PROCEDURE AssignLong (AValue : LONGINT);
 PROCEDURE ADD (VAR AValue : TBigNum);
 PROCEDURE Subtract (VAR AValue : TBigNum);
 PROCEDURE Multiply (VAR AMultiplicator : TBigNum);
 FUNCTION Divide (VAR ADivisor : TBigNum) : BOOLEAN;
 FUNCTION Modulus (VAR ADivisor : TBigNum) : BOOLEAN;
 PROCEDURE SquareRoot;
 PROCEDURE Increment (By : WORD);
 PROCEDURE Decrement (By : WORD);
 PROCEDURE BitwiseOr (VAR AMaske : TBigNum);
 FUNCTION Compare (VAR AValue : TBigNum) : INTEGER;
 PROCEDURE Mult10;
 PROCEDURE Div10;
 PROCEDURE Mult2;
 PROCEDURE Div2;
 FUNCTION STR : STRING;
 FUNCTION Str16 : STRING;
 PROCEDURE VAL (CONST S : STRING);
 FUNCTION AsLong : LONGINT;
 END;
PROCEDURE TBigNum.ASSIGN (VAR AValue : TBigNum);
BEGIN
 MOVE (AValue.Value, Value, SIZEOF (Value) );;
END;
PROCEDURE TBigNum.AssignLong (AValue : LONGINT);
BEGIN
 MOVE (AValue, Value [0], SIZEOF (LONGINT) );;
 FILLCHAR (Value [SIZEOF (LONGINT) SHR 1], BigNumLength SHL 1 -
SIZEOF (LONGINT), 0);
END;
PROCEDURE TBigNum.ADD (VAR AValue : TBigNum); assembler;
asm
 PUSH DS
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 LDS SI, AValue
 ADD SI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 CLD
 CLC
@@0 : LODSW
 ADC [ES : DI], AX
 INC DI
 INC DI
 LOOP @@0
 POP DS
END;
PROCEDURE TBigNum.Subtract (VAR AValue : TBigNum); assembler;
asm
 PUSH DS
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 LDS SI, AValue
 ADD SI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 CLD
 CLC
@@0 : LODSW
 SBB [ES : DI], AX
 INC DI
 INC DI
 LOOP @@0
 POP DS
END;
PROCEDURE TBigNum.Multiply (VAR AMultiplicator : TBigNum); assembler;
VAR
 Res : ARRAY [0..BigNumLength] OF WORD;
asm
 PUSH DS
 PUSH BP
 STD
 LES DI, AMultiplicator
 ADD DI, OFFSET TBigNum.Value
 LDS SI, Self
 ADD SI, OFFSET TBigNum.Value
 PUSH SI
 LEA BP, Res
 XOR SI, SI
 MOV CX, BigNumLength
 XOR AX, AX
@@8 : MOV SS : [BP + SI], AX
 ADD SI, 2
 LOOP @@8
 POP SI
 XOR BX, BX
@@0 : MOV CX, BX
 MOV DX, CX
 SHL DX, 1
 ADD SI, DX
 INC CX
@@1 : LODSW
 MOV DX, ES : [DI]
 ADD DI, 2
 MUL DX
 ADD SS : [BP], AX
 ADC SS : [BP + 2], DX
 JC @@3
@@2 : LOOP @@1
 MOV DX, BX
 INC DX
 SHL DX, 1
 SUB DI, DX
 ADD SI, 2
 ADD BP, 2
 INC BX
 CMP BX, BigNumLength
 JNE @@0
 CLD
 POP BP
 LEA SI, Res
 PUSH SS
 POP DS
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 REP MOVSW
 POP DS
 JMP @@9
@@3 : PUSH SI
 MOV DX, 1
 MOV SI, 4
@@4 : ADD [BP + SI], DX
 INC SI
 INC SI
 JC @@4
 POP SI
 JMP @@2
@@9 :
END;
FUNCTION TBigNum.Divide (VAR ADivisor : TBigNum) : BOOLEAN;
VAR
 Bit, Res, Divisor : TBigNum;
 WholeResult : BOOLEAN;
BEGIN
 Divisor.ASSIGN (ADivisor);
 WholeResult := FALSE;
 Bit.AssignLong (1);
 Res.AssignLong (0);
 WHILE Compare (Divisor)>= 0 DO
 BEGIN
 Bit.Mult2;
 Divisor.Mult2;
 END;
 WHILE (Bit.Value [0] AND 1 = 0) AND NOT WholeResult DO
 BEGIN
 Bit.Div2;
 Divisor.Div2;
 CASE Compare (Divisor) OF
 1 :
 BEGIN
 Res.BitwiseOr (Bit);
 Subtract (Divisor);
 END;
 0 :
 BEGIN
 WholeResult := TRUE;
 Res.BitwiseOr (Bit);
 Subtract (Divisor);
 END;
 END;
 END;
 ASSIGN (Res);
 Divide := WholeResult;
END;
FUNCTION TBigNum.Modulus (VAR ADivisor : TBigNum) : BOOLEAN;
VAR
 Bit, Res, Divisor : TBigNum;
 WholeResult : BOOLEAN;
BEGIN
 Divisor.ASSIGN (ADivisor);
 WholeResult := FALSE;
 Bit.AssignLong (1);
 Res.AssignLong (0);
 WHILE Compare (Divisor)>= 0 DO
 BEGIN
 Bit.Mult2;
 Divisor.Mult2;
 END;
 WHILE (Bit.Value [0] AND 1 = 0) AND NOT WholeResult DO
 BEGIN
 Bit.Div2;
 Divisor.Div2;
 CASE Compare (Divisor) OF
 1 :
 BEGIN
 Res.BitwiseOr (Bit);
 Subtract (Divisor);
 END;
 0 :
 BEGIN
 WholeResult := TRUE;
 Res.BitwiseOr (Bit);
 Subtract (Divisor);
 END;
 END;
 END;
 Modulus := WholeResult;
END;
PROCEDURE TBigNum.SquareRoot;
VAR
 Guess, NewGuess : TBigNum;
BEGIN
 NewGuess.ASSIGN (Self);
 NewGuess.Div2;
 REPEAT
 Guess.ASSIGN (NewGuess);
 NewGuess.ASSIGN (Self);
 NewGuess.Divide (Guess);
 NewGuess.ADD (Guess);
 NewGuess.Div2;
 UNTIL NewGuess.Compare (Guess) = 0;
 ASSIGN (NewGuess);
END;
PROCEDURE TBigNum.Increment (By : WORD); assembler;
asm
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 CLD
 MOV AX, ES : [DI]
 ADD AX, By
 STOSW
 MOV CX, BigNumLength - 1
@@0 : MOV AX, ES : [DI]
 ADC AX, 0
 STOSW
 LOOP @@0
END;
PROCEDURE TBigNum.Decrement (By : WORD); assembler;
asm
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 CLD
 MOV AX, ES : [DI]
 SUB AX, By
 STOSW
 MOV CX, BigNumLength - 1
@@0 : MOV AX, ES : [DI]
 SBB AX, 0
 STOSW
 LOOP @@0
END;
PROCEDURE TBigNum.BitwiseOr (VAR AMaske : TBigNum); assembler;
asm
 PUSH DS
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 LDS SI, AMaske
 ADD SI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 CLD
@@0 : LODSW
 OR AX, ES : [DI]
 STOSW
 LOOP @@0
 POP DS
END;
FUNCTION TBigNum.Compare (VAR AValue : TBigNum) : INTEGER; assembler;
asm
 PUSH DS
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 LDS SI, AValue
 ADD SI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 MOV DX, CX
 DEC DX
 SHL DX, 1
 ADD DI, DX
 ADD SI, DX
 STD
 REPZ CMPSW
 MOV AX, 0FFFFh
 JA @@1
 MOV AX, 0000h
 JE @@1
 MOV AX, 0001h
@@1 : POP DS
END;
PROCEDURE TBigNum.Mult10; assembler;
asm
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 XOR BX, BX
 MOV CX, BigNumLength
@@0 : MOV AX, [ES : DI]
 MOV DX, 10
 MUL DX
 ADD AX, BX
 ADC DX, 0
 MOV [ES : DI], AX
 INC DI
 INC DI
 MOV BX, DX
 LOOP @@0
END;
PROCEDURE TBigNum.Div10; assembler;
asm
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 MOV DX, CX
 DEC DX
 SHL DX, 1
 ADD DI, DX
 XOR DX, DX
@@0 : MOV AX, [ES : DI]
 MOV BX, 10
 DIV BX
 MOV [ES : DI], AX
 DEC DI
 DEC DI
 LOOP @@0
END;
PROCEDURE TBigNum.Mult2; assembler;
asm
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 XOR BX, BX
 MOV CX, BigNumLength
 CLC
 CLD
@@0 : MOV AX, [ES : DI]
 RCL AX, 1
 STOSW
 LOOP @@0
END;
PROCEDURE TBigNum.Div2; assembler;
asm
 LES DI, Self
 ADD DI, OFFSET TBigNum.Value
 MOV CX, BigNumLength
 MOV DX, CX
 DEC DX
 SHL DX, 1
 ADD DI, DX
 XOR DX, DX
 CLC
 STD
@@0 : MOV AX, [ES : DI]
 RCR AX, 1
 STOSW
 LOOP @@0
END;
FUNCTION TBigNum.STR : STRING;
VAR
 M, T : TBigNum;
 Res : STRING;
 I, Ciffer : INTEGER;
BEGIN
 M.ASSIGN (Self);
 T.AssignLong (1);
 I := 0;
 WHILE M.Compare (T)>= 0 DO
 BEGIN
 T.Mult10;
 INC (I);
 END;
 IF I <= 1 THEN BEGIN STR := CHAR (BYTE ('0') + M.Value [0]); END ELSE BEGIN Res := ''; T.Div10; WHILE I> 0 DO
 BEGIN
 Ciffer := 0;
 WHILE (M.Compare (T)>= 0) DO
 BEGIN
 M.Subtract (T);
 INC (Ciffer);
 END;
 Res := Res + CHAR (BYTE ('0') + Ciffer);
 DEC (I);
 T.Div10;
 END;
 STR := Res;
 END;
END;
FUNCTION TBigNum.Str16 : STRING;
CONST
 HexCif : ARRAY [0..15] OF CHAR = '0123456789ABCDEF';
VAR
 Res : STRING;
 I : INTEGER;
 ErMed : BOOLEAN;
BEGIN
 ErMed := FALSE;
 Res := '';
 FOR I := BigNumLength - 1 DOWNTO 0 DO
 BEGIN
 IF ErMed OR (Value [I]  0) THEN
 BEGIN
 IF ErMed OR (Value [I] SHR 12 AND $F  0) THEN
 BEGIN
 Res := Res + HexCif [Value [I] SHR 12 AND $F];
 ErMed := TRUE;
 END;
 IF ErMed OR (Value [I] SHR 8 AND $F  0) THEN
 BEGIN
 Res := Res + HexCif [Value [I] SHR 8 AND $F];
 ErMed := TRUE;
 END;
 IF ErMed OR (Value [I] SHR 4 AND $F  0) THEN
 BEGIN
 Res := Res + HexCif [Value [I] SHR 4 AND $F];
 ErMed := TRUE;
 END;
 Res := Res + HexCif [Value [I] AND $F];
 ErMed := TRUE;
 END;
 END;
 Str16 := Res;
END;
PROCEDURE TBigNum.VAL (CONST S : STRING);
VAR
 I : INTEGER;
BEGIN
 AssignLong (0);
 I := 1;
 WHILE I <= LENGTH (S) DO
 BEGIN
 Mult10;
 Increment (BYTE (S [I]) - BYTE ('0') );
 INC (I);
 END;
END;
FUNCTION TBigNum.AsLong : LONGINT;
VAR
 Res : ^LONGINT;
BEGIN
 Res := @Value [0];
 AsLong := Res^;
END;
VAR
 ABigNum : TBigNum;
 I : INTEGER;
BEGIN
 ABigNum.AssignLong (1);
 FOR I := 1 TO 260 DO
 BEGIN
 WRITELN (ABigNum.STR : 79);
 ABigNum.Mult2;
 END;
 FOR I := 1 TO 260 DO
 BEGIN
 WRITELN (ABigNum.STR : 79);
 ABigNum.Div2;
 END;
 WRITELN (ABigNum.STR : 79);
 WRITE ('Press enter to exit.');
 READLN;
END.


AltStyle によって変換されたページ (->オリジナル) /