
	12.2. ARITMETICA NUMERELOR FOARTE MARI

	12.2.1. Prezentare generala

n istoria calculatoarelor, aritmetica cu numere mari a fost mereu n actualitate. n lucrarea [3] se face un istoric al evolutiei acestei probleme. In aceasta sectiune vom prezenta un pachet de programe capabil sa efectueze cele patru operatii algebrice cu numere ntregi foarte mari. n acest scop vom defini un tip obiect numit Tnumar care descrie un numar ntreg foarte mare. Vom defini de asemenea o unitate numita B2E16 care defineste functii ce opereaza cu aceste numere mari.

Performantele acestei aritmetici de numere ntregi sunt urmatoarele:
	- posibilitatea de a introduce si de a extrage numere ntregi avnd pna la 65532 cifre n baza 10;
	- posibilitatea de a efectua calcule algebrice cu numere ntregi cu valori de pna la 2^(2^15), numar care are n reprezentarea n baza 10 aproximativ 159000 cifre n baza 10;
	- depasirea acestor limite este semnalata, dar din cauza diferentei enorme dintre posibilitatile de introducere / extragere primara si domeniul n care sunt efectuate calculele, probabilitatea de aparitie a depasirii este foarte mica;

	- reprezentarea numerelor facndu-se ntr-un mod foarte economic, singura limitare privitoare la numarul acestor numere este capacitatea memoriei interne a calculatorului.

Pachetul de programe pe care-l prezentam opereaza cu doua baze. Baza 10 (o vom nota prescurtat cu B10) este folosita numai pentru operatiile de introducere a numerelor ce constituie datele de intrare si pentru extragerea rezultatelor finale. Baza 2^16 (o vom nota prescurtat B2E16) este folosita pentru reprezentarea interna a numerelor. Motivul acestei alegeri este simplu: cifrele acestei baze sunt cuvinte ale masinii. De aici apare usurinta n operarea cifra cu cifra, instructiunile microprocesorului 8086 opernd la nivel de cuvnt.

	12.2.2. Obiectul TNUMAR si unitatea B2E16

Textul sursa prin care sunt definite obiectul TNumar si unitatea B2E16 este urmatorul:

{$I-,R-,D-,B-,S-,V-}
unit B2E16;
               interface
type
  PNumar = ^TNumar;
  String7 = string[7];
  TNumar = object
    InitDone: string7;     { Pentru controlul Init si Done }
    Sign: integer;    {  Indica semnul numarului si totodata
                      baza in care este reprezentat numarul:
       0 indica numarul 0;
      -1 numar negativ reprezentat numai in B10;
      -2 numar negativ reprezentat numai in B2E16;
      -3 numar negativ reprezentat in ambele baze;
       1 numar pozitiv reprezentat numai in B10;
       2 numar pozitiv reprezentat numai in B2E16;
       3 Numar pozitiv reprezentat in ambele baze }
    CifreB10,                    { Numarul de cifre in B10 }
    CifreB2E16: word;          { Numarul de cifre in B2E16 }
    PB10  ,            { Pointer spre reprezentarea in B10 }
    PB2E16 : pointer;{ Pointer spre reprezentarea in B2E16 }

    Destructor  Done;          { Elibereaza spatiul ocupat }
    Function    GetSign: integer;            { -1, 0 sau 1 }
    Procedure   ChangeSign;     { Schimba semnul numarului }
    Function    NCifB2E16: word; { Numar de cifre in B2E16 }
    Function    NCifB10  : word; { Numar de cifre in B10   }
    Function    GetB2E16: pointer; { Spre cifrele in B2E16 }
    Function    GetB10(rang, grup: word): string;
                           { Furnizeaza maximum "grup" cifre
                                in B10, incepind cu "rang" }
    Constructor InitB2E16(p: pointer; cifre: integer);
                    { Initializeaza cu reprezentarea B2E16 }
    Constructor InitB10(s: string);
                      { Initializeaza cu reprezentarea B10 }
    Procedure   AppendB10(s: string);
                       { Adauga cifre la reprezentarea B10 }
    Procedure   B10toB2E16;       { Trece din B10 in B2E16 }
    Procedure   B2E16toB10;       { Trece din B2E16 in B10 }
end;

const DEPASIRE: word = 0; { Indica desfasurarea operatiilor:
                     0 desfasurare normala;
                     1 depasire la InitB10;
                     2 depasire la InitB2E16;
                     3 utilizarea unui obiect neinitializat;
                     4 InitB10 dublu;
                     5 InitB2E16 dublu;
                     6 spatiu de memorie insuficient;
                     7 impartire la zero;
                     8 Done dublu }

function ComparB2E16(a, b: TNumar): integer;
function PlusB2E16  (a, b: TNumar): PNumar;
function MinusB2E16 (a, b: TNumar): PNumar;
function TimesB2E16 (a, b: TNumar): PNumar;
function DivideB2E16(a, b: TNumar; var r: PNumar): PNumar;
procedure PDone(p: PNumar);    { Sterge obiect din memorie }
             implementation
uses  crt;
const MAXCIFREB10:   word = 65532;
      MAXCIFREB2E16: word = 32764;
      CONSTINIT:  string7 = 'IniT Nr';
      CONSTDONE:  string7 = 'dOnE nR';
             { Proceduri si functii auxiliare }
{$I cifreefe }
{$I signum }
{$I debug }
{$I noinit }
{$I depB10 }
{$I depB2E16 }
{$I allocmem }
{$I dastring }
             { Inchidere numar }
{$I Done }
             { Furnizari de informatii despre numar }
{$I GetSign }
{$I NCifB2e1 }
{$I NCifB10 }
{$I GetB2E16 }
{$I Getb10 }
             { Modificari ale numarului }
{$I ChangeSi }
{$I InitB2E1 }
{$I InitB10 }
{$I appendb1 }
             { Conversii intre baze }
{$I B10toB2E }
{$I B2E16toB }
             { Functii ce lucreaza cu numere }
{$I ComparB2 }
{$I PlusB2E1 }
{$I MinusB2E }
{$I TimesB2E }
{$I DivideB2 }
{$I PDone }
end.

	12.2.3. Interfata TNUMAR si B2E16

	12.2.3.1. Cmpurile obiectului TNUMAR

Obiectul TNumar are sase cmpuri. 

Cmpul InitDone este un string prin care se controleaza crearea si distrugerea corecta a fiecarei instantieri a acestui obiect. Detalii asupra acestei probleme tipice programarii obiectuale n [4]. 

Cmpul Sign retine semnul numarului precum si bazele n care este acesta reprezentat. Comentariile din textul sursa aduc precizarile cuvenite.

Cmpurile CifreB10 si CifreB2E16 retin numarul de cifre semnificative ale numarului n reprezentarea n B10, respectiv pe cel al reprezentarii n baza B2E16.

Cmpurile PB10 si PB2E16 sunt doi pointeri (adrese FAR) prin care sunt reperate reprezentarile numarului n cele doua baze.

	12.2.3.2. Reprezentarea unui numar

Asa cum am mai spus, un numar mare l reprezentam n cele duua baze, B10 si B2E16. Este posibi ca la unele numere sa fie prezenta la un moment dat doar una dintre reprezentari. Daca una lipseste, atunci pointerul corespunzator ei are valoarea nil. 

Numarul zero se reprezinta prin valoarea 0 la Sign si valoarea nil la cei dou pointeri.

Un numar ntreg nenul se reprezinta n B10 ntr-o maniera specifica codurilor BCD ([6]): fiecare cifra zecimala se scrie pe patru biti, deci ncap patru cifre zecimale pe un cuvnt, iar cuvintele n care se reprezinta numarul au adrese consecutive. 

Pentru a permite adaugarea de cifre zecimale la un numar deja introdus, pointerul PB10 puncteaza spre cuvntul care contine cifrele zecimale de rang maxim. Cuvintele care urmeaza contin n continuare cifre zecimale de ranguri din ce n ce mai mici. n cadrul unui cuvnt, cifra zecimala din bitii 12-15 este cea mai semnificativa, iar cifra din bitii 0-3 este cea mai putin semnificativa dintre cele patru.

Din ratiuni de aliniere programul adauga eventual maximum 3 zerouri nesemnificative numarului reprezentat n B10, astfel nct numarul va avea CifreB10 nu numar multiplu de patru. Deci pentru reprezentarea n baza 10 sunt necesare CifreB10 div 4 cuvinte, respectiv CifreB10 div 2 octeti. Ultima expresie va fi des ntlnita n textele sursa care urmeaza.

Un numar ntreg nenul se reprezinta n B2E16 printr-un sir de CifreB2E16 cuvinte consecutive. Pentru a pastra filozofia de reprezentare a numerelor ntregi la 8086, pointerul PB2E16 puncteaza spre cifra (n B2E16) cea mai putin semnificativa. Cuvintele care urmeaza contin cifre de ordin din ce n ce mai mare, iar cel de-al CifreB2E16-lea cuvnt contine cifra de rang maxim. In aceasta reprezentare numerele nu au zerouri nesemnificative, deci cea de-a CifreB2E16-a cifra este nenula.

	12.2.3.3. Interfata obiectului TNUMAR

n aceasta sectiune vom prezenta pe scurt modul de utilizare a metodelor obiectului. Modul n care sunt implementate va fi prezentat n sectiunile urmatoare.

Constructorul InitB10 initializeaza obiectul cu un numar n B10. Acest numar este dat prin stringul s, deci poate avea maximum 255 cifre zecimale. Daca numarul este negativ, naintea cifrelor trebuie sa apara semnul minus (-). Metoda AppendB10 prin stringul s poate adauga oricte cifre numarului initializat prin InitB10, numai daca aceasta initializare s-a facut cu un numar nenul. Fiecare apel al lui AppendB10 poate adauga maximum 255 de cifre. 

Constructorul InitB2E16 initializeaza un numar cu reprezentarea lui n B2E16. Pointerul p indica nceputul cifrelor n baza B2E16, iar cifre indica prin abs(cifre) numarul de cifre n B2E16, iar semnul lui cifre este semnul numarului.

Destructorul Done elibereaza spatiile ocupate de reprezentarile numarului.
Metodele B10toB2E16 si B2E16toB10 realizeaza conversiile ntre cele doua baze de reprezentare a numarului.

Metoda ChangeSign schimba semnul numarului, iar metoda GetSign furnizeaza semnul numarului.

Metodele NCifB10 si NCifB2E16 furnizeaza numarul de cifre folosit n fiecare din cele doua reprezentari.

Metoda GetB2E16 ntoarce un pointer (PB2E16) spre reprezentarea numarului n B2E16.

Metoda GetB10 ntoarce reprezentarea numarului n baza 10, cod ASCII. Pentru comoditatea manipularii am ales pentru rezultat tipul de date string din Pascal. Deoarece numarul extras este, n general, un numar foarte mare, aceasta metoda pretinde precizarea unei portiuni care sa fie extrasa din reprezentarea zecimala a numarului. Cu alte cuvinte, GetB10 nu ntoarce reprezentarea zecimala a numarului, ci doar o parte a acestei reprezentari. Pentru specificarea partii de extras, utilizatorul precizeaza prin rang ncepnd de la ce putere a lui 10 sa i se furnizeze cifrele. Prin grup precizeaza cte cifre sa-i furnizeze. Metoda va forta ca grup sa fie un multiplu de 2 si sa fie maximum 200, iar rang sa fie un multiplu de grup. Stringul rezultat va fi completat cu un sufix n care sunt prezentate rangurile cifrelor furnizate.

	12.2.3.4. Interfata unitatii B2E16

Interfata unitatii B2E16 contine, pe lnga interfata obiectului TNumar, patru functii, o procedura si o variabila.

Functia ComparB2E16 primeste ca parametri doua numere mari a si b, iar ca rezultat ntoarce semnul diferentei a - b.

Functiile PlusB2E16, MinusB2E16 si TimesB2E16 implementeaza respectiv operatiile de adunare, scadere si nmultire algebrica a doua numere mari a si b. Rezultatul este dat printr-un pointer spre un obiect de tip numar foarte mare. Fiecare functie se ngrijeste sa aloce spatiu pentru rezultat. Ramne nsa pe seama utilizatorului sa elibereze acest spatiu atunci cnd nu mai are nevoie de el. 

Functia DivideB2E16 realizeaza operatia de mpartire. Ea furnizeaza ca rezultat un pointer spre un numar foarte mare care reprezinta ctul operatiei a / b, iar pointerul r dat ca parametru, va puncta spre restul aceleiasi mpartiri.

Procedura PDone Elibereaza ntreg spatiul ocupat de un obiect numar atunci cnd acesta (obiectul) este alocat dinamic. Pointerul p va indica la intrarea n procedura obiectul care va trebiu eliminat din memorie.

Variabila DEPASIRE va semnara pentru programul utilizator modul de desfasurare al fiecarei operatii. Comentariile din text sunt lamuritoare n acest sens. Pe lnga acest mod de semnalare a evenimentelor neobisnuite, unitatea mai afiseaza pe ecran un mesaj lamuritor nsotit de un semnal sonor.

	12.2.4. Implementarea TNUMAR si B2E16

n aceasta sectiune vom prezenta textele sursa ale procedurilor si functiilor care descriu metodele obiectului TNumar si ale unitatii B2E16. n multe dintre cazuri vom prezenta numai textul sursa, deoarece comentariile din text sunt suficient de lamuritoare. Deoarece n sectiunea de interfata am prezentat ce face fiecare proccedura sau functie, aici nu le vom mai reaminti. Ne vom margini la a arata cum se realizeaza practic scopul propus.

Constantele MAXCIFREB10 si MAXCIFREB2E16 stabilesc limitele superioare de reprezentare ale numarului n cele doua baze. Constantele CONSTINIT si CONSTDONE sunt doua constante string. Prima este atribuita cmpului InitDone n momentul initializarii obiectului, iar cea de-a doua n momentul anularii lui. Acest "joc" de constante permite depistarea erorilor de initializare dubla, anulare dubla, folosirea unui obiect neinitializat.

	12.2.4.1 Proceduri si functii auxiliare

Functia CifreEfective ntoarce numarul de cifre semnificative ale unui numar reprezentat n B2E16 pe n cifre, ncepnd de la adresa p. Din cele n cifre sunt excluse zerourile nesemnificative. 

function CifreEfective(p: pointer; n: word): word;
begin
  if p = nil then begin CifreEfective := 0;  exit  end;
  asm               { Sare peste zerourile nesemnificative }
    xor  ax,ax;     mov  cx,n;     les  di,p;     add  di,n
    add  di,n;      sub  di,2;     std
    repe scasw         { Se opreste la primul cuvint nenul }
    je          @1
    inc         cx
  @1:
    mov         n,cx
  end;
  CifreEfective := n     { Atitea cifre semnificative sunt }
end;

function signum(x: integer): integer;
begin
  if x = 0 then      signum :=  0
  else if x > 0 then signum :=  1
  else               signum := -1
end;

procedure Debug(n: word);               { Mesaje de eroare }
  begin
    DEPASIRE := n;    write('! DEPASIRE = ', n, ' ');
    case n of
      1: write('Prea multe cifre la InitB10');
      2: write('Prea multe cifre la InitB2E16');
      3: write('Utilizare obiect neinitializat);
      4: write('InitB10 dublat);
      5: write('InitB2E16 dublat);
      6: write('Spatiu de memorie insuficient);
      7: write('Impartire la zero');
      8: write('Done dublat);
    end;
    writeln(' !!!');    sound(440);        delay(2000);
    nosound;            repeat until Keypressed
  end;

function NoInit(s: string7): boolean;
begin                      { Obiectul este neinitializat ? }
  NoInit := false;
  if s <> CONSTINIT then begin NoInit := true; Debug(3) end
end;

function DepB10(n: longint): word;
begin                          { Prea multe cifre in B10 ? }
  if n > MAXCIFREB10 then begin
              DepB10 := MAXCIFREB10;     Debug(1)
  end else    DepB10 := n
end;

function DepB2E16(n: longint): word;
begin                        { Prea multe cifre in B2E16 ? }
  if n > MAXCIFREB2E16 then begin
           DepB2E16 := MAXCIFREB2E16;    Debug(2)
  end else DepB2E16 := n
end;

procedure AllocMem(var p: pointer; necesar: word);
begin          { Da memoria necesara daca mai este de unde }
  if MemAvail < necesar then begin
           p := nil;        Debug(6)
  end else GetMem(p, necesar)
end;


Function DaString4(s: string; var Semn: integer;
                   var u4, cit, ls: word): pointer;
  {  Primeste la intrare:
   - s sirul de cifre;
   - Semn = 0 la primul grup de cifre, <> 0 la urmatoarele;
   - u4 ultimele patru cifre comprimate din vechiul sir.
     Da la iesire:
   - un pointer spre sirul comprimat. In acest sir au fost
       eventual preluate "cit" cifre din "u4", astfel ca
       sirul sa reprezinte ultimele "ls" cifre ale noului
       numar. Numarul cifrelor acestuia va fi multiplu de 4;
   - u4 cifrele ramase din vechiul numar dupa aliniere;
   - cit cite cifre s-au luat din vechiul numar;
   - ls numarul de cifre preluate din sirul "s" completat cu
     "cit" cifre din "u4"
   Aceasta functie este folosita de catre "InitB10" in cazul
   particular al primului grup de cifre, si de "AppendB10"
   in cazul general, de completare cu cifre si realiniere  }

var  p, q: pointer;
     i: word;
     s3: string;
begin
  DaString4 := nil;  ls := 0;
  while (s <> '') and (s[1] = ' ') do delete(s, 1, 1);
  if s = '' then exit;         { S-a transmis numarul zero }
  if Semn = 0 then begin
    u4 := 0;
    if s[1] in ['+','-','0'..'9'] then begin
      if s[1] = '-'        then Semn := - 1 else Semn := 1;
      if s[1] in ['+','-'] then delete(s, 1, 1);
      while (s <> '') and (s[1] = ' ') do delete(s, 1, 1);
      if s = '' then exit;     { S-a transmis numarul zero }
      while (s <> '') and (s[1] = '0') do delete(s, 1, 1);
      if s = '' then exit      { S-a transmis numarul zero }
    end
  end;
  i := 1;
  while (i <> length(s)) and (s[i] in ['0'..'9']) do inc(i);
  if not(s[i] in ['0'..'9']) then delete(s,i,length(s)-i+1);
  if s = '' then exit;         { S-a transmis numarul zero }
  ls := length(s);  cit := (4 - ls mod 4) mod 4;
  s3 := '   ';      i := ord('0');
  s3[1] := chr(i + ((u4 shr 8) and $000F));
  s3[2] := chr(i + ((u4 shr 4) and $000F));
  s3[3] := chr(i + ( u4        and $000F));
  case cit of
    0: s3 := '';
    1: begin delete(s3, 1, 2); u4 := u4 shl 4 end;
    2: begin delete(s3, 1, 1); u4 := u4 shl 8 end;
    3: u4 := u4 shl 12
  end;
  ls := length(s) + length(s3);
  if ls < 256 then begin
                  s := s3 + s;         q := @s[1]
  end else begin  s := copy(s3,2,length(s3)-1)+s;
                  s[0] := s3[1];       q := @s[0]  end;
  i := ls div 2;  AllocMem(p, i);
  asm
    push  ds
    lds   si,q;    les   di,p;    mov   cx,i;    cld
  @1: lodsw                  { Incarca doua cifre in ASCII }
    sub   ax,('0' shl 8) + '0'             { ramin cifrele }
    push  cx
    mov   cl,4;    shl   al,cl;   shr   ax,cl
    pop   cx
    stosb               { Memoreaza-le comprimate pe octet }
    loop  @1
    pop   ds
  end;
  DaString4 := p   { Intoarce pointer spre sirul comprimat }
end;

	12.2.4.2. Distrugeri ale numarului: DONE si PDONE

Destructor TNumar.Done;      { Elibereaza spatiile ocupate }
begin                    { si anuleaza valorile cimpurilor }
  if NoInit(InitDone) then exit;
  if InitDone = CONSTDONE then begin Debug(8); exit end;
  if PB2E16 <> nil then FreeMem(PB2E16, 2 * CifreB2E16);
  if PB10   <> nil then FreeMem(PB10,   CifreB10 div 2);
  PB10 := nil;    PB2E16 := nil;    Sign := 0;
  CifreB10 := 0;  CifreB2E16 := 0;  InitDone := CONSTDONE
end;

procedure PDone(p: PNumar); { Sterge din memorie un obiect }
begin    { de tip TNumar daca acesta a fost alocat dinamic }
  if p = nil then exit;
  p^.Done; FreeMem(p, sizeof(TNumar))
end;

	12.2.4.3. Furnizarea de informatii despre numar

Function TNumar.GetSign: integer;            { -1, 0 sau 1 }
begin
  if NoInit(InitDone) then exit;
  GetSign := Signum(Sign)
end;

Function TNumar.NCifB2e16: word;
begin
  if NoInit(InitDone) then exit;
  if abs(Sign) = 1 then B10toB2E16;   { Furnizeaza numarul }
  NCifB2E16 := CifreB2E16             { de cifre in B2E16  }
end;

Function TNumar.NCifB10:word;
begin
  if NoInit(InitDone) then exit;
  if abs(Sign) = 2 then B2E16toB10;   { Furnizeaza numarul }
  NcifB10 := CifreB10                 { de cifre in B10    }
end;

Function TNumar.GetB2E16: pointer;
begin
  if NoInit(InitDone) then exit;
  if abs(Sign) = 1 then B10toB2E16;  { Intoarce un pointer }
  GetB2E16 := PB2E16         { spre reprezentarea in B2E16 }
end;

Function TNumar.GetB10(rang, grup: word): string;
   { Intoarce maximum "grup" cifre ale reprezentarii in  B10
     incepind cu "rang". "Grup" este fortat la multiplu de 2
     si la maximum 200,  iar "rang"  este fortat multiplu de
     "grup". Dupa cifre se indica si rangurile acestora.   }
var  s,inf, sup: string;
     r: integer;
     p, q: pointer;
begin
  if NoInit(InitDone) then exit;
  if abs(Sign) = 2 then B2E16toB10;
  GetB10 := '';
  if (rang >= CifreB10) and (Sign <> 0) then  exit;
  GetB10 := '0';
  if Sign = 0 then exit;
  if grup > 200 then grup := 200;
  grup := grup and $FFFE;     rang := rang div grup * grup;
  s[0] := chr(grup);          q := @s[1];
  str((rang+grup-1):5, sup);  str(rang:5, inf);
  r := CifreB10 - rang;       r := r - grup;
  if r <=0 then begin
    grup := r + grup;       rang := 0;       sup := ' <-- ';
    s[0] := chr(grup+1);    s[1] := ' ';     q := @s[2];
    if Sign < 0 then        s[1] := '-'
  end else rang := r;
  rang := rang div 2;       p := PB10;
  asm
    push  ds           { Muta portiunea de cifre in string }
    mov   cx,grup;    shr   cx,1;       lds   si,p
    add   si,rang;    add   si,cx;      sub   si,1
    les   di,q;       add   di,grup;    sub   di,2
    std                      { Terminat pregatirea mutarii }
  @1: lodsb               { Incarca 2 cifre dintr-un octet }
    push  cx
    mov   cx,4;        shl   ax,cl;      shr   al,cl
    and   ax,$0F0F;    add   ax,('0' shl 8) + '0'
    pop   cx
    stosw             { Memoreaza cele doua cifre in ASCII }
    loop  @1
    pop   ds
  end;
  if sup = ' <-- ' then
    while (length(s) > 1) and (s[2] = '0') do delete(s,2,1);
  if s[1] = ' ' then                          delete(s,1,1);
  GetB10 := s + ' (' + sup + ' : ' + inf + ' )'
end;

	12.2.4.4 Modificari ale numarului

Procedure TNumar.ChangeSign;    { Schimba semnul numarului }
begin
  if NoInit(InitDone) then exit;
  Sign := - Sign
end;


Constructor TNumar.InitB2E16(p: pointer; cifre: integer);
                    { Initializeaza cu reprezentarea B2E16 }
begin
  if InitDone = CONSTINIT then begin Debug(5); exit end;
  InitDone := CONSTINIT;    PB10 := nil;    CifreB10 := 0;
  CifreB2E16 := DepB2E16(CifreEfective(p, abs(cifre)));
  Sign := 2 * Signum(cifre);
  if (CifreB2E16 = 0) or (p = nil) then begin
    CifreB2E16 := 0;        Sign := 0;      PB2E16 := nil
  end else begin
    AllocMem(PB2E16,  2 * CifreB2E16);
    move(p^, PB2E16^, 2 * CifreB2E16)
  end
end;


Constructor TNumar.InitB10(s: string);
                      { Initializeaza cu reprezentarea B10 }
var  w1, w2: word;
begin
  if InitDone = CONSTINIT then begin Debug(4); exit end;
  InitDone := CONSTINIT;         PB2E16 := nil;
  CifreB2E16 := 0;               Sign := 0;
  PB10 := DaString4(s, Sign, w1, w2, CifreB10);
  if PB10 = nil then Sign := 0
end;

Procedure TNumar.AppendB10(s: string);
  { Adauga cifrele din "s" la reprezentarea B10 a vechiului
    numar. In colaborare cu "Dastring4" face in asa fel ca
    noul numar rezultat sa aiba un numar de cifre multiplu
    de patru, adaugind eventual in fata maximum trei zerouri
    nesemnificative si eventual deplasind potrivit vechiul
    numar. Numarul se reprezinta comprimat, patru cifre
    zecimale pe un cuvint.                                 }
var  l: longint;
     ls, lsd, lv, lvs, zn, cit, u4, masca: word;
     ps, pv, pvs: pointer;
begin
  if NoInit(InitDone) then  exit;
  if Sign = 0 then exit; { Initializat cu 0, nu se adauga! }
  pv := PB10;     lv := CifreB10;   u4 := word(pv^);
  if      u4 and $0FFF = 0 then     zn := 3
  else if u4 and $00FF = 0 then     zn := 2
  else if u4 and $000F = 0 then     zn := 1
  else                              zn := 0;
  asm                  { Exista zn zerouri nesemnificative }
    mov  ax,lv;    sub  ax,4;    shr  ax,1
    les  di,pv;    mov  ax,es:[di]
    mov  u4,ax              { Memorat ultimele patru cifre }
  end;
  ps := Dastring4(s, Sign, u4, cit, ls);
  if ps = nil then exit;    { Nu s-a transmis nici o cifra }
  l := lv - zn;        l := (l + ls + 3) and $FFFFFFFC;
  lvs := DepB10(l);    lsd := l - lvs;   { Cit s-a depasit }
  zn := lvs - (lv - zn + ls);  { Noul numar de 0 nesemnif. }
  cit := cit shl 2;
  case cit of
    0:  masca := $FFFF;
    4:  masca := $F000;
    8:  masca := $FF00;
    12: masca := $FFF0
  end;
  if (cit <> 0) and (lv = 4) then asm   { Se pune numai u4 }
    les  di,pv;      mov  ax,u4;      mov  es:[di],ax
  end;
  if (cit <> 0) and (lv > 4) then begin
   { Este necesara deplasarea sirului vechi cu "cit" cifre }
    asm
      push  ds                   { Pregatirea deplasarii }
      lds   si,pv;      les   di,pv;      mov   cx,lv
      shr   cx,1;       add   si,cx;      sub   si,4
      mov   di,si;      add   di,2;       shr   cx,1
      sub   cx,1;       std;              mov   bx,u4
    @1: lodsw                       { Ciclul de deplasare }
      push  cx
      mov   dx,ax;      mov   cx,cit;      shl   ax,cl
      or    ax,bx;      rol   dx,cl;       mov   bx,dx
      and   bx,masca;
      pop   cx;              stosw;        loop  @1
      mov   es:[si],bx;      cmp  bx,0
      jnz   @2
      mov   word ptr pv,di           { Avansul lui pv cu 4 }
      sub   lv,4
    @2:
      pop   ds
    end
  end;
  AllocMem(pvs, lvs div 2);
  asm                   { Se concateneaza cele doua siruri }
    push ds
    cld
    les  di,pvs                   { Pregatirea destinatiei }
    lds  si,pv                  { Pregatirea sirului vechi }
    mov  cx,lv;     shr  cx,1;     shr  cx,1
    rep  movsw                     { Mutarea sirului vechi }
    lds  si,ps                    { Pregatirea sirului nou }
    mov  cx,ls;     sub  cx,lsd          { Minus depasirea }
    shr  cx,1;      shr  cx,1
    rep  movsw                       { Mutarea sirului nou }
    lds  si,pvs         { Se pun zerourile nesemnificative }
    mov  ax,ds:[si];     and  ax,masca;      mov  ds:[si],ax
    pop  ds
  end;
  FreeMem(PB10, CifreB10 div 2);  FreeMem(ps, ls div 2);
  PB10 := pvs;                    CifreB10 := lvs
end;

	12.2.4.5. Conversii ntre baze

Algoritmii de conversie ntre baze sunt cei clasici, toate calculele facndu-se n baza B2E16. 

Metoda B10toB2E16 ia reprezentarea numarului n B10 si aplicnd schema lui Horner determina reprezentarea numarului n baza B2E16. Fie k numarul de cifre n B10 al numarului si c0, c1, ... , ck-1, ck cifrele acestui numar n baza 10. Expresia care da valoarea acestui numar poate fi scrisa sub forma:

   ( ...((ck*10 + ck-1)*10 + ... + c2)*10 + c1)*10 + c0

Evalund aceasta expresie, de la parantezele interioare spre cele exterioare, se obtine valoarea numarului. Toate calculele se fac n B2E16, cifrele n B10, ca si numarul 10, reprezentndu-se n B2E16 printr-o cifra. Se pleaca de la numarul 0 si se executa repetat cte o nmultire cu 10 si adunarea cte unei cifre, de k+1 ori. 

Metoda B2E16toB10 determina cifrele reprezentarii numarului n B10 prin mpartiri succesive la 10, calculele efectundu-se tot n B2E16. Cifra c0 este restul mpartirii numarului la 10, cifra c1 este restul mpartirii la 10 a ctului precedent s.a.m.d., tot de k+1 ori.

Pentru cresterea eficientei, se determina n prealabil (vezi cele doua linii marcate n cele doua texte sursa), numarul maxim de cifre obtinut n urma conversiei se delimiteaza superior n prealabil. Cum s-a ajuns la aceaste delimitari ale numarului maxim de cifre? Vom schita doar calculele care ne-au condus la aceste delimitari, fiind vorba de calcule algebrice simple cu logaritmi, inegalitati si cu functia parte ntreaga.

Sa notam z = 10 si b = 2^16 cele doua baze, iar x un numar natural. Daca k este numarul de cifre n baza z al lui x si l numarul de cifre n baza b a lui x, atunci au loc relatiile:

 zk-1 ( x < zk  si  bl-1 ( x < bl

Evident, dorim sa obtinem delimitari pentru l cunoscnd pe k, respectiv sa obtinem delimitari pentru k cunoscnd pe l. Aceste delimitari se obtin usor daca s-ar cunoaste valoarea lui log2x! Aceasta cerinta este practic imposibil de satisfacut, fiind vorba de numere foarte mari. Vom evita deci calculul lui log2x. n acest scop vom folosi constanta:

   t  =  log2z / log2b  =  (1 + log25) / 16

Logaritmnd n baza doi cele doua delimitari ale lui x, apoi nmultind si mpartind potrivit cu log2z si cu log2b se ajunge la inegalitatile:

 k-1 ( log2x / (t * log2b) < k  si

 l-1 ( t * log2x / log2z < l

Pe de alta parte, log25 l putem delimita destul de bine. De exemplu, prin calcul direct (cu rabdare), sau cu un mic program Pascal, se constata ca au loc inegalitatile:

 230 < 513 < 231 de unde ==>  30/13 < log25 < 31/13

(Este nevoie de o delimitare att de strnsa pentru a realiza pentru k si l delimitari ct mai mici.) Avnd deduse inegalitatile de mai sus, le combinam si aplicam tranzitivitatea n asa fel nct se ajunge la delimitari care nu mai depind de log2x. Acestea sunt:

 43 * (k-1) / 208 <  l  < 1 + k * 11 / 52 si

 52 * (l-1) / 11  <  k  < 1 + l * 208 / 43.

Aceste relatii permit determinarea unei majorante a numarului de cifre a rezultatului. 

Desi se face o rezervare prealabila mai mare, la ncheierea conversiei se retine ocupat doar spatiul cu cifre semnificative, eliminndu-se zerourile nesemnificative.

Textele sursa ale algoritmilor de conversie sunt date mai jos.

Procedure TNumar.B10toB2E16;    { Trecere din B10 in B2E16 }
var  p2e, p10: pointer;
     n, i, maxim, n10, of10, zece, cifra, cuvint: word;
     l: longint;
begin
  if NoInit(InitDone) then exit;
  if abs(Sign) <> 1 then exit;{ Conversia este deja facuta }
  n10 := CifreB10;       p10 := PB10;
  if n10 = 0 then begin
    CifreB2E16 := 0;     PB2E16 := nil;     exit
  end;
  l := n10;              l := (l * 11 + 51) div 52 + 2;
  maxim := DepB2E16(l) * 2;
  AllocMem(p2e, maxim);  fillchar(p2e^, maxim, 0);
  zece := 10;            of10 := 0;
  for i := 1 to n10 do begin
    n := CifreEfective(p2e, maxim div 2);
    if n <> 0 then asm
      push  ds          { Inmultirea vechiului numar cu 10 }
      les   di,p2e;      lds   si,p2e;      cld
      mov   cx,n;         xor   bx,bx
    @1:  lodsw
      mul   zece;    add  ax,bx;    adc  dx,0;    mov  bx,dx
      stosw;        loop  @1
      mov   es:[di],dx
      pop   ds
    end;
    case i mod 4 of
      1: begin
           asm
             les  si,p10;        add  si,of10
             mov  ax,es:[si];    mov  cuvint,ax
           end;
           of10 := of10 + 2;     cifra := cuvint and $000F
         end;
      2: cifra := (cuvint shr 4) and $000F;
      3: cifra := (cuvint shr 8) and $000F;
      0: cifra := cuvint shr 12
    end;
    if cifra <> 0 then asm
      push  ds              { Adunarea unei cifre zecimale }
      les   di,p2e;      lds   si,p2e;      cld
      lodsw;             add   ax,cifra
    @1:  adc  ax,0;      stosw;      lodsw;      jc  @1
      pop   ds
    end
  end;
  CifreB2E16 := CifreEfective(p2e, maxim div 2);
  Sign := 3 * Signum(Sign);  
  AllocMem(PB2E16, CifreB2E16 * 2);
  move(p2e^, PB2E16^, CifreB2E16 * 2);
  FreeMem(p2e, maxim)
end;

Procedure   TNumar.B2E16toB10;    { Trece din B2E16 in B10 }
var  p2e, p10: pointer;
     n, i, maxim, of10, n2e, n10, zece, cifra, cuvint: word;
     l: longint;
begin
  if NoInit(InitDone) then exit;
  if abs(Sign) = 1 then exit; { Conversia este deja facuta }
  n2e := CifreEfective(PB2E16, CifreB2E16);
  if n2e = 0 then begin
    CifreB10 := 0;    PB10 := nil;    exit
  end;
  l := n2e;  l := (l * 208 + 42) div 43 + 5;
  maxim := DepB10(l) div 2;   of10 := maxim;
  AllocMem(p10, maxim);
  AllocMem(p2e, 2 * n2e);     move(PB2E16^, p2e^, 2 * n2e);
  zece := 10;                 n10 := 0;
  for i := 1 to 2 * maxim do begin
    n := CifreEfective(p2e, n2e);
    if n <> 0 then asm
      push ds                 { Conversie din B2E16 in B10 }
      les  di,p2e;      add  di,n;      add  di,n
      sub  di,2;        lds  si,p2e;    mov  si,di
      std;              mov  cx,n;      xor  dx,dx
    @1:  lodsw;         div  zece;      stosw;      loop  @1
      mov  cifra,dx;    inc  n10     { Inca o cifra in B10 }
      pop  ds
    end else
      cifra := 0;
    case i mod 4 of
      1: cuvint := cifra shl 12;
      2: cuvint := cuvint or (cifra shl 8);
      3: cuvint := cuvint or (cifra shl 4);
      0: begin
           cuvint := cuvint or cifra;
           of10 := of10 - 2;
           asm
             les  si,p10;         add  si,of10
             mov  ax,cuvint;      mov  es:[si],ax
           end
         end
    end
  end;
  FreeMem(p2e, 2 * n2e);          p2e := p10;
  n10 := (n10 + 3) and $FFFC;     of10 := maxim - n10 div 2;
  asm
    mov  ax,word ptr p2e;         add  ax,of10
    mov  word ptr p2e,ax
  end;
  CifreB10 := n10;                Sign := 3 * Signum(Sign);
  AllocMem(PB10, n10 div 2);
  move(p2e^, PB10^, n10 div 2);
  FreeMem(p10, maxim)
end;

	12.2.4.6. Functii ce lucreaza cu numere foarte mari

function ComparB2E16(a, b: TNumar): integer;
                    { Intoarce semnul diferentei " - "b" }
var  signa, signb: integer;
     na, nb: word;
     pa, pb: pointer;
begin
  ComparB2E16 := 0;
  if NoInit(a.InitDone) then         exit;
  if NoInit(b.InitDone) then         exit;
  if abs(a.Sign) = 1 then a.B10toB2E16;
  if abs(b.Sign) = 1 then b.B10toB2E16;
  signa := Signum(a.Sign);      signb := Signum(b.Sign);
  pa := a.PB2E16;               pb := b.PB2E16;
  na := CifreEfective(pa, a.CifreB2E16);
  nb := CifreEfective(pb, b.CifreB2E16);
  if na + nb = 0 then                 exit;
  if signa < signb then begin
    ComparB2E16 := - 1;               exit   end;
  if signa > signb then begin
    ComparB2E16 :=   1;               exit   end;
  if na > nb then begin
    ComparB2E16 := Signum(a.Sign);    exit   end;
  if na < nb then begin
    ComparB2E16 := - Signum(b.Sign);  exit   end;

     { Acelasi numar de cifre, acelasi semn, numere nenule }
  signb := 0;              { retine rezultatul comparatiei }
  asm
    push ds
    lds  si,pa;    les  di,pb;    add  si,na
    add  si,na;    sub  si,2     { Adresa dreapta a sursei }
    add  di,na;    add  di,na
    sub  di,2               { Adresa dreapta a destinatiei }
    mov  cx,na;    std
    repe   cmpsw                       { Compara cuvintele }
    je   @gata
    ja   @maimare
    mov  cx,-1                                   { Mai mic }
    jmp  @gata
  @maimare:        mov  cx,1                    { Mai mare }
  @gata:           mov  signb,cx    { La egal, CX ramine 0 }
    pop  ds
  end;
  ComparB2E16 := signa * signb;
end;


function PlusB2E16(a, b: TNumar): PNumar;
           { Aduna " cu "b" si da un pointer la rezultat }
var  maxim, minim, na, nb: word;
     signa, signb: integer;
     p, pa, pb: pointer;
     pc: PNumar;
begin
  PlusB2E16 := nil;
  if NoInit(a.InitDone) then                      exit;
  if NoInit(b.InitDone) then                      exit;
  if abs(a.Sign) = 1 then a.B10toB2E16;
  if abs(b.Sign) = 1 then b.B10toB2E16;
  pa := a.PB2E16;             pb := b.PB2E16;
  signa := Signum(a.Sign);    signb := Signum(b.Sign);
  na := CifreEfective(pa, a.CifreB2E16);
  nb := CifreEfective(pb, b.CifreB2e16);
  AllocMem(p, sizeof(TNumar));
  pc := p;                    PlusB2E16 := pc;
  if na > nb then begin   maxim := na;    minim := nb
  end else        begin   maxim := nb;    minim := na  end;
  if maxim = 0 then begin pc^.InitB2E16(nil, 0);  exit end;
  if nb = 0 then begin
    pc^.InitB2E16(pa, signa * na);                exit end;
  if na = 0 then begin
    pc^.InitB2E16(pb, signb * nb);                exit end;
  if signa <> signb then begin
    a.Sign:= abs(a.Sign);    b.Sign:= abs(b.Sign);
    case ComparB2E16(a, b) of
      1: begin
           FreeMem(p, sizeof(TNumar));
           pc := MinusB2E16(a, b);
           pc^.Sign := 2 * signa
         end;
      0: pc^.InitB2E16(nil, 0);
     -1: begin
           FreeMem(p, sizeof(TNumar));
           pc := MinusB2E16(b, a);
           pc^.Sign := 2 * signb
         end;
    end;
    PlusB2E16 := pc;                              exit
  end;
  AllocMem(p, 2 * maxim + 2);
  asm
    push  ds;       les   di,p;      mov   cx,minim
    xor   bx,bx;    cld;             clc
  @perechi:                     { Ciclu de adunare perechi }
    lds  si,pa;    mov  ax,ds:[bx+si]
    lds  si,pb;    adc  ax,ds:[bx+si]
    inc  bx;       inc  bx
    stosw;         loop      @perechi
    pushf                           { Salveaza transportul }
    mov  cx,maxim;    sub  cx,nb;    je   @eventualb
                                    { Mai multe cifre in a }
    lds  si,pa;    add  si,bx
    popf                              { Reface transportul }
  @onlya:
    lodsw;         adc  ax,0;    stosw;    loop  @onlya
    pushf                           { Salveaza transportul }
    jmp   @transportfinal
  @eventualb:              { Mai multe cifre eventual in b }
    mov  cx,maxim;               sub  cx,na
    je    @transportfinal
                                    { Mai multe cifre in b }
    lds  si,pb;    add  si,bx
    popf                              { Reface transportul }
  @onlyb:
    lodsw;         adc  ax,0;    stosw;    loop  @onlyb
    pushf                           { Salveaza transportul }
  @transportfinal:                   { Adauga ultima cifra }
    xor  ax,ax
    popf                              { Reface transportul }
    adc  ax,0
    stosw
    pop  ds
  end;
  pc^.InitB2E16(p, signa * CifreEfective(p, maxim + 1));
  FreeMem(p, 2 * maxim + 2)
end;

function MinusB2E16(a, b: TNumar): PNumar;
       { Scade pe "b" din " si da un pointer la rezultat }
var  maxim, minim, na, nb, t: word;
     signa, signb: integer;
     p, pa, pb: pointer;
     pc: PNumar;
begin
  MinusB2E16 := nil;
  if NoInit(a.InitDone) then                       exit;
  if NoInit(b.InitDone) then                       exit;
  if abs(a.Sign) = 1 then a.B10toB2E16;
  if abs(b.Sign) = 1 then b.B10toB2E16;
  pa := a.PB2E16;             pb := b.PB2E16;
  signa := Signum(a.Sign);    signb := Signum(b.Sign);
  na := CifreEfective(pa, a.CifreB2E16);
  nb := CifreEfective(pb, b.CifreB2e16);
  AllocMem(p, sizeof(TNumar));
  pc := p;                    MinusB2E16 := pc;
  if na > nb then begin    maxim := na;    minim := nb
  end else        begin    maxim := nb;    minim := na  end;
  if maxim = 0 then begin pc^.InitB2E16(nil, 0);   exit end;
  if nb = 0 then begin
    pc^.InitB2E16(pa, signa * na);                 exit end;
  if na = 0 then begin
    pc^.InitB2E16(pb, - signb * nb);               exit end;
  if signa <> signb then begin
    FreeMem(p, sizeof(TNumar));
    a.Sign := abs(a.Sign);    b.Sign := abs(b.Sign);
    pc := PlusB2E16(a, b);    pc^.Sign := 2 * signa;
    MinusB2E16 := pc;                              exit
  end;
  a.CifreB2E16 := na;         b.CifreB2E16 := nb;
  a.Sign := abs(a.Sign);      b.Sign := abs(b.Sign);
  if ComparB2E16(a, b) < 0 then begin
    t := na;    na := nb;    nb := t;
    p := pa;    pa := pb;    pb := p;
    signa := - signa
  end;
  AllocMem(p, 2 * maxim);
  asm
    push ds
    les  di,p;     mov  cx,minim;
    xor  bx,bx;    cld;         clc
  @perechi:                     { Ciclu de scadere perechi }
    lds   si,pa;    mov   ax,ds:[bx+si]
    lds   si,pb;    sbb   ax,ds:[bx+si]
    inc   bx;       inc   bx
    stosw;          loop  @perechi
    pushf                           { Salveaza transportul }
    mov   cx,maxim;       sub   cx,nb
    je    @eventualb
                                    { Mai multe cifre in a }
    lds   si,pa;    add   si,bx
    popf                              { Reface transportul }
  @onlya:
    lodsw;    sbb   ax,0;    stosw;    loop  @onlya
    pushf
    jmp   @final
  @eventualb:              { Mai multe cifre eventual in b }
    mov   cx,maxim;       sub   cx,na
    je    @final
                                    { Mai multe cifre in b }
    lds   si,pb;    sbb   si,bx
    popf                              { Reface transportul }
  @onlyb:
    lodsw;    sbb   ax,0;    stosw;    loop  @onlyb
    pushf
  @final:      
    popf
    pop   ds
  end;
  pc^.InitB2E16(p, signa * CifreEfective(p, maxim));
  FreeMem(p, 2 * maxim)
end;

Function TimesB2E16(a, b: TNumar): PNumar;
      { Inmulteste " cu "b" si da un pointer la rezultat }
label  CIFRAZERO;
var    na, nb, i, j, ta, ti: word;
       signa, signb: integer;
       l: longint;
       p, pa, pb: pointer;
       pc: PNumar;
begin
  TimesB2E16 := nil;
  if NoInit(a.InitDone) then                       exit;
  if NoInit(b.InitDone) then                       exit;
  if abs(a.Sign) = 1 then a.B10toB2E16;
  if abs(b.Sign) = 1 then b.B10toB2E16;
  pa := a.PB2E16;             pb := b.PB2E16;
  signa := Signum(a.Sign);    signb := Signum(b.Sign);
  na := CifreEfective(pa, a.CifreB2E16);
  nb := CifreEfective(pb, b.CifreB2e16);
  AllocMem(p, sizeof(TNumar));
  pc := p;                    TimesB2E16 := pc;
  if na * nb = 0 then begin pc^.InitB2E16(nil, 0); exit end;
  signa := signa * signb;     l := na;
  signb := DepB2E16(l + nb);
  AllocMem(p, 2 * signb);     fillchar(p^, 2 * signb, 0);
  for i := 0 to nb - 1 do begin
    asm
      les  si,pb;           add  si,i;      add  si,i
      mov  ax,es:[si];      cmp  ax,0;      je   CIFRAZERO
    end;
    ta := 0;            ti := 0;
    for j := 0 to na - 1 do asm
      les  si,pa;      add  si,j;      add  si,j
      mov  ax,es:[si]           { Incarca cifra inmultitor }
      les  si,pb;      add  si,i;      add  si,i
      mul  word ptr es:[si]  { Inmult. cu cifra deinmultit }
      add  ax,ti;      adc  dx,0;
      mov  ti,dx           { Retine transportul inmultirii }
      xor  dx,dx;      add  ax,ta
      adc  dx,0               { Aduna transportul adunarii }
      les  si,p
      add  si,i;      add  si,i
      add  si,j;      add  si,j
      add  ax,es:[si]
      adc  dx,0           { Aduna cifra veche a produsului }
      mov  ta,dx             { Retine transportul adunarii }
      mov  es:[si],ax               { Memoreaza noua cifra }
    end;
    asm
      les  si,p;      add  si,i;      add  si,i
      add  si,na;     add  si,na;     mov  ax,ti
      add  ax,ta;     mov  es:[si],ax  { Ultimul transport }
    end;
CIFRAZERO:
  end;
  pc^.InitB2E16(p, signa * CifreEfective(p, signb));
  FreeMem(p, 2 * signb)
end;

Pentru functia DivideB2E16 mai dorim sa precizam faptul ca algoritmul de mpartire (ca si algoritmii de adunare, scadere si nmultire) este cel cunoscut. Aici nsa am adus o mbunatatire indicata n [3] si anume. Pentru a "ghici" usor o cifra a ctului, nainte de mpartire se amplifica att dempartitul, ct si mpartitorul cu un factor care sa faca ca prima cifra a mpartitorului sa fie cel putin jumatate din baza. n textul sursa, acest factor este notat cu d, iar prima cifra a mpartitorului cu yn. Amplificnd astfel termenii mpartirii, se demonstreaza ca orice cifra a ctului se obtine mpatind doua cifre ale dempartitului la yn. Am notat cu q aceasta cifra. Daca cifra q nu este buna, atunci cu siguranta sau q-1, sau q-2 este cifra a ctului buna. 

Function DivideB2E16(a, b: TNumar; var r: PNumar): PNumar;
                  { Imparte " la "b" si da pointer la cit;
                    De asemenea, da un pointer la rest "r" }
var  signa, signb: integer;
     na, nb, i, d, q, yn, t: word;
     p, pa, pb, pbq: pointer;
     pc, pr: PNumar;
begin
  DivideB2E16 := nil;       r := nil;
  if NoInit(a.InitDone) then                       exit;
  if NoInit(b.InitDone) then                       exit;
  if abs(a.Sign) = 1 then a.B10toB2E16;
  if abs(b.Sign) = 1 then b.B10toB2E16;
  signa := Signum(a.Sign);     signb := Signum(b.Sign);
  na := CifreEfective(a.PB2E16, a.CifreB2E16);
  nb := CifreEfective(b.PB2E16, b.CifreB2E16);
  if nb = 0 then begin Debug(7);                   exit end;
  AllocMem(p, sizeof(TNumar));
  pc := p;                     DivideB2E16 := pc;
  AllocMem(p, sizeof(TNumar));
  pr := p;                     r := pr;
  if na < nb then begin pc^.InitB2E16(nil, 0);
              pr^.InitB2E16(b.PB2E16, signb * nb); exit end;
  na := na + 1;                  { Poate creste dupa a * q }
  AllocMem(pa, 2*na+2);          AllocMem(pb, 2*nb);
  AllocMem(pbq, 2 * nb + 2);     fillchar(pa^, 2*na+2, 0);
  move(a.PB2E16^, pa^, 2*na-2);  move(b.PB2E16^, pb^, 2*nb);
  d := 1;       { Ramine 1 daca prima cifra a impartitorului
                  este cel putin $8000                     }
  asm
    push  ds
    les   di,pb;      add   di,nb
    add   di,nb;      sub   di,2
    mov   bx,es:[di]        { Prima cifra a impartitorului }
    cmp   bx,$8000;   jae   @ESTE1    { Cifra factor d = 1 }
    add   bx,1;       mov   dx,1;
    mov   ax,0;       div   bx
    mov   d,ax                   { depunerea cifrei factor }
    xor   ax,ax;    mov   t,ax   { Transport  de inmultire }
    lds   si,pb                         { Calculeaza b * d }
    les   di,pb;    cld;     mov  cx,nb
  @DABD:
    lodsw
    mul   d;    add   ax,t;    adc   dx,0;    mov   t,dx
    stosw;      loop  @DABD
    lds   si,pa                         { Calculeaza a * d }
    les   di,pa;    cld;     mov   cx,na;    add   cx,1
    xor   ax,ax;    mov   t,ax    { Transport de inmultire }
  @DAAD:
    lodsw
    mul   d;    add   ax,t;    adc   dx,0;    mov   t,dx
    stosw;     loop   @DAAD
  @ESTE1:
    les   di,pb;    add   di,nb;       add   di,nb
    sub   di,2;     mov   ax,es:[di]
    mov   yn,ax             { Prima cifra a impartitorului }
    pop   ds
  end;
  for i := na - 1 downto nb - 1 do asm
    push  ds
    les   di,pa;         add   di,i;       add   di,i
    mov   ax,es:[di];    add   di,2
    mov   dx,es:[di]               { DX:AX = a[i + 1]:a[i] }
    div   yn;            mov   q,ax    { Cifra curenta cit }
  @CAUTAQ:
    cmp   ax,0;     je    @CIFRAZERO
    xor   ax,ax;    mov   t,ax   { Transport  de inmultire }
    les   di,pbq                        { Calculeaza b * q }
    lds   si,pb;    cld;       mov   cx,nb
  @DABQ:
    lodsw
    mul   q;    add   ax,t;    adc   dx,0;    mov   t,dx
    stosw;      loop  @DABQ
    mov   es:[di],dx         { Terminat calculul lui b * q }
    lds   si,pa              { compara in vederea scaderii }
    add   si,i;     add   si,i;      add   si,2
    mov   cx,nb;    add   cx,1;      std
    repe  cmpsw;                   jae   @SCADE
    dec   q;        mov   ax,q;    jmp   @CAUTAQ
  @SCADE:
    les  di,pa;    add  di,i;    add  di,i
    sub  di,nb;    sub  di,nb;   add  di,2;   cld
    lds  si,pbq;   mov  cx,nb;   add  cx,1;   clc
  @perechi:                     { Ciclu de scadere perechi }
    mov  ax,es:[di];    sbb  ax,ds:[si]
    inc  si;            inc  si
    stosw;              loop @perechi
    mov  ax,q;          mov  es:[di],ax
@CIFRAZERO:
    pop  ds
  end;
  if d <> 1 then asm        { Imparte restul la factorul d }
    push ds
    les  di,pa;    add  di,nb;    add  di,nb
    sub  di,2;     lds  si,pa;    mov  si,di
    std;           mov  cx,nb;    xor  dx,dx
  @1:
    lodsw;    div  d;    stosw;    loop @1
    pop  ds
  end;
  pr^.InitB2E16(pa, signa * nb);  FreeMem(pb, 2 * nb);
  FreeMem(pbq, 2 * nb + 2);       pb := pa;
  asm
    mov  ax,word ptr pa
    add  ax,nb;    add  ax,nb;    add  ax,2
    mov  word ptr pa,ax  { pa puncteaza la cifrele citului }
  end;
  pc^.InitB2E16(pa, signa * signb * (na - nb));
  FreeMem(pb, 2 * na + 2)
end;

	12.2.5. Aplicatii ale aritmeticii mari

	12.2.5.1. Calculul CMMDC a doua numere foarte mari

Sa notam cu u si v doua numere naturale. Se stie ca daca u3 este cel mai mare divizor comun a lui u si v, atunci exista numerele ntregi u1 si u2 astfel nct sa aiba loc relatia:

	u * u1 + v * u2  = u3

Textul sursa al programului care determina u1, u2 si u3 daca se dau la intrare u si v este:

{$I-,R-,D-,B-,S-,V-}
program cmmdc;
uses B2E16;
var  u1, u2, u3, v1, v2, v3, t1, t2, t3, u, v: TNumar;
     p1, p2, pq, pr: PNumar;
     w: word;
     s, t: string;
begin
  writeln('Primul numar ?');     readln(s);
  writeln('Al doilea numar ?');  readln(t);
  u.InitB10(s);                  u.B10toB2E16;
  v.InitB10(t);                  v.B10toB2E16;
  w := 1;                        u1.InitB2E16(@w, 1);
  w := 0;                        u2.InitB2E16(@w, 1);
  u3.InitB2E16(u.GetB2E16, u.NcifB2E16);
  w := 0;                        v1.InitB2E16(@w, 1);
  w := 1;                        v2.InitB2E16(@w, 1);
  v3.InitB2E16(v.GetB2E16, v.NcifB2E16);
  while v3.NcifB2E16 > 0 do begin
    pq := DivideB2E16(u3, v3, pr);
    p1 := TimesB2E16(v1, pq^);   p2 := MinusB2E16(u1, p1^);
    t1.InitB2E16(p2^.GetB2E16, p2^.GetSign * p2^.NcifB2E16);
    PDone(p1);     PDone(p2);
    p1 := TimesB2E16(v2, pq^);   p2 := MinusB2E16(u2, p1^);
    t2.InitB2E16(p2^.GetB2E16, p2^.GetSign * p2^.NcifB2E16);
    t3.InitB2E16(pr^.GetB2E16, pr^.NcifB2E16);
    PDone(p1);     PDone(p2);      PDone(pq);    PDone(pr);
    u1.Done;
    u1.InitB2E16(v1.GetB2E16, v1.GetSign * v1.NcifB2E16);
    u2.Done;
    u2.InitB2E16(v2.GetB2E16, v2.GetSign * v2.NcifB2E16);
    u3.Done;
    u3.InitB2E16(v3.GetB2E16, v3.GetSign * v3.NcifB2E16);
    v1.Done;
    v1.InitB2E16(t1.GetB2E16, t1.GetSign * t1.NcifB2E16);
    v2.Done;
    v2.InitB2E16(t2.GetB2E16, t2.GetSign * t2.NcifB2E16);
    v3.Done;
    v3.InitB2E16(t3.GetB2E16, t3.GetSign * t3.NcifB2E16);
    t1.Done;      t2.Done;        t3.Done;
  end;
  v1.Done;        v2.Done;        v3.Done;
  u1.B2E16toB10;  u2.B2E16toB10;  u3.B2E16toB10;
  writeln;  writeln('C.m.m.d.c (');
  writeln(s, ' ,');             writeln(t, '  ) =');
  s := u3.GetB10(0,200); writeln(Copy(s,1,Pos(' ',s)),' =');
  s :=  u.GetB10(0,200); writeln(Copy(s,1,Pos(' ',s)),' *');
  s := u1.GetB10(0,200); writeln(Copy(s,1,Pos(' ',s)),' +');
  s :=  v.GetB10(0,200); writeln(Copy(s,1,Pos(' ',s)),' *');
  s := u2.GetB10(0,200); writeln(Copy(s,1,Pos(' ',s)));
  pq := TimesB2E16(u, u1);       pr := TimesB2E16(v, u2);
  p1 := PlusB2E16(pq^, pr^);
  if ComparB2E16(p1^, u3) = 0
    then   writeln('OK')    else    writeln('EROARE ! ! !');
  PDone(pq);   PDone(pr);   PDone(p1);
  u1.Done;     u2.Done;     u3.Done;   u.Done;   v.Done;
  readln
end.

Algoritmul este preluat din [3] si mai foloseste variabilele auxiliare v1, v2, v3, t1, t2 si t3, explicate n aceeasi lucrare. 
Primele patru linii de instructiuni citesc cele doua numere u si v. Pentru comoditate am citit numerele ca stringuri, deci numerele u si v pot avea maximum 255 cifre n B10. Pentru a citi numere cu mai multe cifre, de exemplu pentru u se poate proceda astfel:

  writeln('Primul numar ?');     readln(s);
  u.InitB10(s);
  readln(s);
  while s <> '' do begin
    u.AppendB10(s); readln(s)
  end;
  u.B10toB2E16

In felul acesta se citesc siruri succesive de cifre pna cnd se da sirul vid (se apasa <ENTER> la nceput de linie. Aceste siruri de cifre sunt adaugate la numarul u. n mod analog se poate proceda si cu v.

n acelasi spirit, ncepnd cu linia marcata n text dupa ciclul while, linie care marcheaza nceputul tiparirii rezultatelor, utilizatorul poate sa-si adapteze modul de tiparire dupa dorinta.

	12.2.5.2. Calculul lui n! pentru n foarte mare

Textul sursa al programului este dat mai jos. 

{$I-,R-,D-,B-,S-,V-}
program factor;
uses B2E16;
const FMAX = 20000;
var   n, f, unu: TNumar;
      t: PNumar;
      w, i: word;
begin
  w := 1;                  unu.InitB2E16(@w, 1);
  n.InitB2E16(@w, 1);      f.InitB2E16(@w, 1);
  for w := 1 to FMAX do begin
    t := TimesB2E16(f, n);                     f.Done;
    f.InitB2E16(t^.GetB2E16, t^.NCifB2E16);    PDone(t);
    t := PlusB2E16(n, unu);                    n.Done;
    n.InitB2E16(t^.GetB2E16, t^.NCifB2E16);    PDone(t);
    f.B2E16toB10;
    writeln('n=', w:6, ' Numar cifre= ', f.NCifB10:6);
{    for i := f.NCifB10 div 50 downto 0 do
      writeln(f.GetB10(i * 50, 50)); }
  end;
  n.done;  f.done;  unu.done;  readln
end.

n acest program sunt calculate valorile lui n! pna la n = FMAX. Pentru a creste (putin) viteza de calcul, instructiunile care tiparesc efectiv valorile lui n! le-am izolat, punndu-le ca si comentarii. De asemenea, valoarea pentru FMAX este "putin" cam mare. 


