{--------------------------------------------------------------------------- (c) Copyright Aleksandrov O.E., 2010 Molecular Physics department, USTU, Ekaterinsburg, K-2, 620002, RUSSIA phone 375-41-46 E-mail: aleks@dpt.ustu.ru ----------------------------------------------------------------------------} { Арифметические операции (+,-,*,/) над сверхдлинными беззнаковыми целыми числами Сверхдлинное беззнаковое целое = массив СЛОВ (16 битных беззнаковых целых), содержащий ЧЕТНОЕ число слов !!!МЛАДШИЕ - ПЕРВЫЕ Каждое слово массива можно рассматривать как ЦИФРУ в записи числа по основанию 2^16. Процедуры ниже реализуют элементарные арифметические операции сложения/вычитания/умножения/деления СТОЛБИКОМ, как в школе учили. } unit VeryLongArithmetic; {$Q- Отключить проверку OVERFLOW, ибо используются операции с переполнением. } {$R- Отключить проверку RANGECHECK. } interface USES SysUtils; const { Максимальная длина ДЛИННОГО числа. Это задано константой чтобы не париться с динамическим размещением чисел. При необходимости другого размера tVeryLongUInt - замените константу и перекомпилируйте модуль.} cMaxDigitsCount_=128; {!!! ДОЛЖНА БЫТЬ ЧЕТНОЙ } cMaxDigitsCount=((cMaxDigitsCount_ shr 1) shl 1); { принудительное деланье четным } { битовая рязрядность числа 16*cMaxDigitsCount бита, например 64*16=1024 бита. МЛАДШИЕ БИТЫ - ПЕРВЫЕ.} type {номер цифры (слова, разряда) в длинном целом числе } tDigitIndex=0..cMaxDigitsCount-1; {число цифр (слов, разрядов) в длинном целом числе } tDigitCount=0..cMaxDigitsCount; {номер пары цифр в длинном целом числе } tDoubleDigitIndex=0..(cMaxDigitsCount div 2)-1; {цифра-разряд в длинном целом числе} tDigit=word; {две цифры (два разряда) в длинном целом числе } tDDigit=LongWord; pDDigit=^tDDigit; {четыре цифры (четыре разряда) в длинном целом числе } tQDigit=int64; {к сожалению uint64 нема, ну да тут нужно реально только ТРИ word-а} {две цифры- два разряда в длинном целом числе } tDoubleDigit=packed record case byte of 0: (DDigit:tDDigit); 1: (LoDigit, HiDigit:tDigit); 2: (Digits: array[0..1] of tDigit); end; pDoubleDigit=^tDoubleDigit; {четыре цифры - четыре разряда в длинном целом числе } tQuadDigit=packed record case byte of 0: (QDigit:tQDigit); 1: (LoDDigit, HiDDigit:tDDigit); 2: (Digits: array[0..3] of tDigit); end; pQuadDigit=^tQuadDigit; /////////////////////////////////////////////////////////////////////////////// { НУ ОЧЕНЬ ДЛИиииииииииННОЕ БЕЗЗНАКОВОЕ ЦЕЛОЕ } tVeryLongUInt=packed record case byte of 0:(Digits: packed array[tDigitIndex] of tDigit); 1:(DDigits: packed array[tDoubleDigitIndex] of tDDigit); 2:(Digit0:tDigit); {прямой доступ к младшему разряду } 3:(DDigit0:tDDigit); {прямой доступ к двум младшим разрядам } 4:(QDigit0:tQDigit); {прямой доступ к четырем младшим разрядам } end; pVeryLongUInt=^tVeryLongUInt; { НУ ОЧЕНЬ ДЛИиииииииииННОЕ БЕЗЗНАКОВОЕ ЦЕЛОЕ !!! c памятью старшей цифры. Это несколько ускоряет процессы, но не радикально. } tVeryLongUIntEx=packed record MostSignificantDigitIndex:tDigitIndex; Uint:tVeryLongUInt; end; pVeryLongUIntEx=^tVeryLongUIntEx; /////////////////////////////////////////////////////////////////////////////// const cDigitMaxValue=High(tDigit); cDigitBitSize=SizeOf(tDigit)*8; cVeryLongUIntZero:tVeryLongUInt=(Digit0:0); cVeryLongUIntMaxValue:tVeryLongUInt=(Digit0:0); { инициируется в исполняемой секции модуля } cVeryLongUIntOneEx:tVeryLongUIntEx=(MostSignificantDigitIndex:0; Uint:(Digit0:1)); { Прибавляет к X значение Y. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе >0.} function vlaAdd(var X:tVeryLongUInt; const Y:tVeryLongUInt):tDigit; overload; function vlaAdd(var X:tVeryLongUIntEx; const Y:tVeryLongUIntEx):tDigit; overload; { Прибавляет к X число из одной цифры D. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе >0.} function vlaAdd(var X:tVeryLongUInt; D:tDigit):tDigit; overload; { Прибавляет к X число из двух цифр D. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе >0.} function vlaAdd(var X:tVeryLongUInt; D:tDoubleDigit):tDigit; overload; function vlaAdd(var X:tVeryLongUIntEx; D:tDoubleDigit):tDigit; overload; { Прибавляет к X единицу. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе 1.} function vlaInc(var X:tVeryLongUInt):tDigit; { Вычитает из X значение Y. Возвращает в X результат вычитания. Возвращает 0, если нет переполнения, иначе 1.} function vlaSub(var X:tVeryLongUInt; const Y:tVeryLongUInt):tDigit; overload; function vlaSub(var X:tVeryLongUInt; const Y:tVeryLongUInt; N:tDigitIndex):tDigit; overload; function vlaSub(var X:tVeryLongUIntEx; const Y:tVeryLongUIntEx):tDigit; overload; function vlaSub(var X:tVeryLongUIntEx; const Y:tVeryLongUIntEx; nY:tDigitIndex):tDigit; overload; function vlaSubTop(var X:tVeryLongUIntEx; const Y:tDoubleDigit; nX:tDigitIndex):tDigit; overload; function vlaDec(var X:tVeryLongUInt):tDigit; overload; function vlaDec(var X:tVeryLongUIntEx):tDigit; overload; { Сравнивает X и Y. Возвращает 0 - равно, <0 - X меньше Y, >0 - X больше Y.} function vlaCmp(const X, Y:tVeryLongUInt):integer; overload; function vlaCmp(const X, Y:tVeryLongUIntEx):integer; overload; function vlaCmp(const X:tVeryLongUInt; Y:tDoubleDigit):integer; overload; function vlaCmp(const X:tVeryLongUIntEx; Y:tDoubleDigit):integer; overload; function vlaCmp(const X, Y:tVeryLongUInt; nX,nY:tDigitIndex):integer; overload; { Сдвигает X на Y разрядов влево } procedure vlaShiftDigitsLeft(var X:tVeryLongUInt; Y:tDigitCount); overload; procedure vlaShiftDigitsLeft(var X:tVeryLongUIntEx; Y:tDigitCount); overload; { Сдвигает A:B на N разрядов влево. А - старшая часть. } procedure vlaShiftDigitsLeft(var A,B:tVeryLongUInt; N:tDigitCount); overload; { Сдвигает A:B на N разрядов влево. А - старшая часть. } procedure vlaShiftDigitsRight(var A,B:tVeryLongUInt; N:tDigitCount); overload; procedure vlaShiftDigitsRight(var A:tVeryLongUInt; N:tDigitCount); overload; procedure vlaShiftDigitsRight(var A:tVeryLongUIntEx; N:tDigitCount); overload; { Сдвигает X на N бит вправо } procedure vlaShiftBitsRight(var X:tVeryLongUInt; N:word); overload; procedure vlaShiftBitsRight(var X:tVeryLongUIntEx; N:word); overload; { Сдвигает X на Y бит влево } procedure vlaShiftBitsLeft(var X:tVeryLongUIntEx; N:word); overload; { Умножает X на Y, Возвращает в B:A результат умножения. B - старшие разряды, A - младшие разряды } procedure vlaMul(const X, Y:tVeryLongUInt; var B,A:tVeryLongUInt); overload; { Умножает X на Y, помещая результат в A - младшая часть произведения. !!!!ПЕРЕПОЛНЕНИЕ ИГНОРИРУЕТСЯ } procedure vlaMul(const X, Y:tVeryLongUInt; out A:tVeryLongUInt); overload; procedure vlaMul(const X, Y:tVeryLongUIntEx; out A:tVeryLongUIntEx); overload; { Умножает X на Y из двух цифр, помещая результат в A - младшая часть произведения. !!!!ПЕРЕПОЛНЕНИЕ ИГНОРИРУЕТСЯ } procedure vlaMul(const X:tVeryLongUInt; Y:tDoubleDigit; out A:tVeryLongUInt); overload; procedure vlaMul(const X:tVeryLongUIntEx; Y:tDoubleDigit; out A:tVeryLongUIntEx);overload; { Умножает число X на число из ОДНОЙ цифры D, X = результат умножения. Возвращает: 0 - нет переполнения, >0 - переполнение, возвращает цифру следующего разряда } function vlaMul(var X:tVeryLongUInt; D:tDigit):tDigit; overload; { Умножает число X на число из ДВУХ цифр D, X = результат умножения. !!!!ПЕРЕПОЛНЕНИЕ ИГНОРИРУЕТСЯ } procedure vlaMul(var X:tVeryLongUIntEx; Y:tDoubleDigit); overload; function vlaMulFast(var X:tVeryLongUIntEx; D:tDoubleDigit):tDDigit; overload; function vlaMulFast(var X:tVeryLongUIntEx; D:tDoubleDigit; out Y:tVeryLongUIntEx):tDDigit; overload; function vlaPowerOfDDigit(D:tDoubleDigit; P:word; out X:tVeryLongUIntEx):tDDigit; { Делит N на цифру D. Возвращает в Q=[N/D] - частное, R = N-[N/D]*D - остаток деления.} procedure vlaDiv(const N:tVeryLongUInt; D:tDigit; var Q:tVeryLongUInt; var R:tDigit); overload; { Делит N на D. Возвращает в Q=[N/D] - частное, R = N-[N/D]*D - остаток деления.} procedure vlaDiv(const N, D:tVeryLongUInt; out Q,R:tVeryLongUInt); overload; { Делит N на D. Возвращает в Q=[N/D] - частное, R = N-[N/D]*D - остаток деления.} procedure vlaDiv(const N:tVeryLongUInt; D:tDoubleDigit; out Q,R:tVeryLongUInt); overload; { Делит N на D. Возвращает в Q=[N/D] - частное.} procedure vlaDiv(const N:tVeryLongUInt; D:tDoubleDigit; out Q:tVeryLongUInt); overload; procedure vlaDiv(const N:tVeryLongUIntEx; D:tDoubleDigit; out Q:tVeryLongUIntEx); overload; { БЫСТРО Делит N на D, используя процессорное 64бит/32бит деление. Возвращает в Q=[N/D] - частное.} procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDigit); overload; procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; function vlaDivFast(var N:tVeryLongUIntEx; D:tDDigit):tDDigit; overload; { возвращает остаток деления } procedure vlaDivFast(const N:tVeryLongUIntEx; D:tDoubleDigit; out Q:tVeryLongUIntEx); overload; { БЫСТРО Делит N на D, используя только 32битное деление. Возвращает в Q=[N/D] - частное.} procedure vlaDivFast32(var NN:tVeryLongUIntEx; D:tDoubleDigit); { Делит N на D. Возвращает в Q=[N/D] - частное. Метод деления отрезка пополам. Тормоз тоже механизм...} procedure vlaDivEx(const N:tVeryLongUInt; D:tDoubleDigit; var Q:tVeryLongUInt); { Возвращает номер (нумерация с 0) старшего ненулевого разряда X. Для X=0 возвращает 0.} function vlaMostSignificantDigitIndex(const X:tVeryLongUInt):tDigitIndex; overload; function vlaMostSignificantDigitIndex(const X:tVeryLongUInt; L:tDigitIndex):tDigitIndex; overload; procedure vlaMostSignificantDigitIndex(var X:tVeryLongUIntEx); overload; procedure vlaDecrementMostSignificantDigitIndex(var X:tVeryLongUIntEx); { Возвращает номер (нумерация с 0) младшего ненулевого разряда X. Для X=0 возвращает 0.} function vlaLeastSignificantDigitIndex(const X:tVeryLongUInt):tDigitIndex; register; overload; { Выводит X на консоль.} procedure vlaWriteln(const X:tVeryLongUInt); overload; procedure vlaWriteln(const X:tVeryLongUIntEx); overload; { Присваивает Dest первые L цифр из Src и обнуляет остальные цифры Dest.} procedure vlaAssign(const Src:tVeryLongUInt; L:tDigitIndex; var Dest:tVeryLongUInt); overload; procedure vlaAssign(const Src:tVeryLongUInt; L:tDigitIndex; var Dest:tVeryLongUIntEx); overload; procedure vlaAssign(const Src:tDoubleDigit; var Dest:tVeryLongUInt); overload; procedure vlaAssign(const Src:tVeryLongUIntEx; var Dest:tVeryLongUIntEx); overload; procedure vlaAssign(const Src:tDoubleDigit; var Dest:tVeryLongUIntEx); overload; procedure vlaAssign(const Src:tVeryLongUInt; var Dest:tVeryLongUIntEx); overload; procedure vlaAssign(const Src:tVeryLongUIntEx; var Dest:tVeryLongUInt); overload; { Присваивает Dest все значащие цифры из Src.} procedure vlaAssignTillMSD(const Src:tVeryLongUIntEx; var Dest:tVeryLongUInt); overload; procedure vlaAssignTillMSD(const Src:tVeryLongUIntEx; var Dest:tVeryLongUIntEx); overload; ////////////////////////////////////////////// { Деление на константу. Это когда надо много делить на одно и то же число B.} // a/b == (int)((((2^16)^N)+b-1)/b) * (a) / ((2^16)^N) ////////////////////////////////////////////// { Вычисление инверсной константы} procedure vlaConstDivPrepare( const B:tDoubleDigit; N:tDigitIndex; var InverseB:tVeryLongUInt ); { Деление на константу B, посредством умножения на инверсную константу } procedure vlaConstDiv( const A:tVeryLongUInt; const InverseB:tVeryLongUInt; N:tDigitIndex; var Q:tVeryLongUInt ); { Обнуление X } procedure vlaZero(var X:tVeryLongUInt); register; overload; procedure vlaZero(var X:tVeryLongUIntEx); register; overload; implementation {ПРИМЕЧАНИЕ! Процедуры с именем Name_, содержащие подчеркивание на конце имени, ТОЛЬКО ДЛЯ ВНУТРЕННЕГО УПОТРЕБЛЕНИЯ!!!} { Умножение двух цифр. НЕ ИСПОЛЬЗУЕТСЯ.} function Mul_(X,Y:tDigit):tDDigit; register; asm {EAX:=X} {EDX:=Y} mul edx end; { заполнение нулями tVeryLongUInt} procedure vlaZero(var X:tVeryLongUInt); register; overload; asm mov edx, edi mov edi, eax xor eax, eax mov ecx,cMaxDigitsCount rep stosw mov edi, edx end; { заполнение нулями tVeryLongUIntEx} procedure vlaZero(var X:tVeryLongUIntEx); register; overload; asm mov edx, edi mov tVeryLongUIntEx([eax]).MostSignificantDigitIndex,0 lea edi, tVeryLongUIntEx([eax]).Uint xor eax, eax mov ecx,cMaxDigitsCount rep stosw mov edi, edx end; { заполнение нулями tVeryLongUIntEx до MostSignificantDigitIndex} procedure vlaZeroTillMSD(var X:tVeryLongUIntEx); register; asm mov edx, edi movzx ecx,tVeryLongUIntEx([X]).MostSignificantDigitIndex inc ecx mov tVeryLongUIntEx([X]).MostSignificantDigitIndex,0 lea edi, tVeryLongUIntEx([X]).Uint xor eax, eax rep stosw mov edi, edx end; { Прибавляет к X значение Y. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе 1.} function vlaAdd(var X:tVeryLongUInt; const Y:tVeryLongUInt):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под суммирование цифр с переполнением } dd.DDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to cMaxDigitsCount-1 do begin { складываем соотв. цифры X и Y (x.Digits[i]+y.Digits[i]), и добавляем результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Digits[i]+Y.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Digits[i]:=dd.LoDigit; end; { возвращаем результат переноса с последнего сложения (dd.HiDigit) } Result:=dd.HiDigit; end; function Add_(const Y:tVeryLongUInt; var X:tVeryLongUInt; L:tDigitIndex):tDigit assembler; register; asm push esi movzx ecx,L; inc ecx mov esi,eax clc @loop: lodsw adc tDigit([edx]), ax inc edx; inc edx loop @loop mov ax,0 adc ax,0 pop esi end; function vlaAdd(var X:tVeryLongUIntEx; const Y:tVeryLongUIntEx):tDigit; overload; var i,l:tDigitIndex; dd:tDoubleDigit; begin l:=Y.MostSignificantDigitIndex; dd.HiDigit:=Add_(Y.Uint, X.Uint,l); {X:=X+Y} (* { переменная в ДВЕ цифры под суммирование цифр с переполнением } dd.HiDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to l do begin { складываем соотв. цифры X и Y (x.Digits[i]+y.Digits[i]), и добавляем результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Uint.Digits[i]+Y.Uint.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; end;*) if (l0) then begin for i:=l+1 to cMaxDigitsCount-1 do begin { складываем соотв. цифру X и перенос с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Uint.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; if dd.HiDigit=0 then begin l:=i; break; end; end; end; if l>X.MostSignificantDigitIndex then X.MostSignificantDigitIndex:=l; { возвращаем результат переноса с последнего сложения (dd.HiDigit) } Result:=dd.HiDigit; end; { Прибавляет к X число из одной цифры D. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе >0.} function vlaAdd(var X:tVeryLongUInt; D:tDigit):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin Result:=D; for i:=0 to cMaxDigitsCount-1 do begin if Result=0 then Exit; dd.DDigit:=x.Digits[i]+Result; x.Digits[i]:=dd.LoDigit; Result:=dd.HiDigit; end; end; { Прибавляет к X число из двух цифр D. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе >0.} function vlaAdd(var X:tVeryLongUInt; D:tDoubleDigit):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под суммирование цифр с переполнением } dd.DDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to 1 do begin { складываем соотв. цифры X и Y (x.Digits[i]+y.Digits[i]), и добавляем результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Digits[i]+D.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Digits[i]:=dd.LoDigit; end; if (dd.HiDigit>0) then begin for i:=2 to cMaxDigitsCount-1 do begin { складываем соотв. цифру X и результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Digits[i]:=dd.LoDigit; if dd.HiDigit=0 then break; end; end; { возвращаем результат переноса с последнего сложения (dd.HiDigit) } Result:=dd.HiDigit; end; function vlaAdd(var X:tVeryLongUIntEx; D:tDoubleDigit):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под суммирование цифр с переполнением } dd.DDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to 1 do begin { складываем соотв. цифры X и Y (x.Digits[i]+y.Digits[i]), и добавляем результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.UInt.Digits[i]+D.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; end; if (dd.HiDigit>0) then begin for i:=2 to cMaxDigitsCount-1 do begin { складываем соотв. цифру X и результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Uint.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; if dd.HiDigit=0 then begin if X.MostSignificantDigitIndex0) and (X.MostSignificantDigitIndex=0) then X.MostSignificantDigitIndex:=1; end; { возвращаем результат переноса с последнего сложения (dd.HiDigit) } Result:=dd.HiDigit; end; { Прибавляет к X значение Y длиной L. Возвращает в X результат сложения. Возвращает 0, если нет переполнения, иначе >0.} function vlaAdd_(var X:tVeryLongUInt; const Y:tVeryLongUInt; nY:tDigitIndex):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под суммирование цифр с переполнением } dd.DDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to nY do begin { складываем соотв. цифры X и Y (x.Digits[i]+y.Digits[i]), и добавляем результат переноса с предыд. сложения (dd.HiDigit)} dd.DDigit:=X.Digits[i]+Y.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Digits[i]:=dd.LoDigit; end; if (dd.HiDigit>0) and (nYY.Uint.Digits[i]) then begin { вычитаем соотв. цифры X и Y (x.Digits[i]-y.Digits[i]), и добавляем результат переноса с предыд. вычитания (dd.HiDigit)} Inc(dd.DDigit,X.Uint.Digits[i]-Y.Uint.Digits[i]); { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; { записываем перенос } dd.LoDigit:=dd.HiDigit; end; if (dd.HiDigit>0) and (jj) then begin if X.Uint.Digits[i]=0 then Dec(X.MostSignificantDigitIndex); end else begin vlaMostSignificantDigitIndex(X); end; { возвращаем результат переноса с последнего вычитания (dd.HiDigit) } Result:=dd.HiDigit; end; function vlaSub(var X:tVeryLongUIntEx; const Y:tVeryLongUIntEx; nY:tDigitIndex):tDigit; overload; var i,j:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под вычитание с заемом из старшего разряда } dd.DDigit:=0; j:=Y.MostSignificantDigitIndex; { для каждой цифры, начиная с младших разрядов } for i:=nY to j do if (dd.HiDigit<>Y.Uint.Digits[i]) then begin { вычитаем соотв. цифры X и Y (x.Digits[i]-y.Digits[i]), и добавляем результат переноса с предыд. вычитания (dd.HiDigit)} Inc(dd.DDigit,X.Uint.Digits[i]-Y.Uint.Digits[i]); { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; { записываем перенос } dd.LoDigit:=dd.HiDigit; end; if (dd.HiDigit>0) and (jj) then begin if X.Uint.Digits[i]=0 then Dec(X.MostSignificantDigitIndex); end else begin vlaMostSignificantDigitIndex(X); end; { возвращаем результат переноса с последнего вычитания (dd.HiDigit) } Result:=dd.HiDigit; end; function vlaSubTop(var X:tVeryLongUIntEx; const Y:tDoubleDigit; nX:tDigitIndex):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под вычитание с заемом из старшего разряда } dd.DDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to 1 do begin { вычитаем соотв. цифры X и Y (x.Digits[i]-y.Digits[i]), и добавляем результат переноса с предыд. вычитания (dd.HiDigit)} Inc(dd.DDigit,X.Uint.Digits[nX+i]-Y.Digits[i]); { записываем полученную цифру в результат } X.Uint.Digits[nX+i]:=dd.LoDigit; { записываем перенос } dd.LoDigit:=dd.HiDigit; end; if (dd.HiDigit>0) then begin for i:=nX+2 to cMaxDigitsCount-1 do begin { вычитаем соотв. из цифры X (x.Digits[i]) результат переноса с предыд. вычитания (dd.HiDigit)} Inc(dd.DDigit,X.Uint.Digits[i]); { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; if dd.HiDigit=0 then break; dd.LoDigit:=dd.HiDigit; end; end; i:=X.MostSignificantDigitIndex; if Y.HiDigit>0 then Inc(nX); if (i>nX) then begin if X.Uint.Digits[i]=0 then Dec(X.MostSignificantDigitIndex); end else begin vlaMostSignificantDigitIndex(X); end; { возвращаем результат переноса с последнего вычитания (dd.HiDigit) } Result:=dd.HiDigit; end; function vlaSub(var X:tVeryLongUInt; const Y:tVeryLongUInt; N:tDigitIndex):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под вычитание с заемом из старшего разряда } dd.DDigit:=0; { для каждой цифры, начиная с младших разрядов } for i:=0 to N do begin { вычитаем соотв. цифры X и Y (x.Digits[i]-y.Digits[i]), и добавляем результат переноса с предыд. вычитания (dd.HiDigit)} dd.LoDigit:=X.Digits[i]; dd.DDigit:=dd.DDigit-Y.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Digits[i]:=dd.LoDigit; end; { возвращаем результат переноса с последнего вычитания (dd.HiDigit) } Result:=dd.HiDigit; end; function vlaDec(var X:tVeryLongUInt):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под вычитание с заемом из старшего разряда } dd.DDigit:=X.Digit0; dd.DDigit:=dd.DDigit-1; X.Digit0:=dd.LoDigit; if dd.HiDigit>0 then begin { для каждой цифры, начиная с младших разрядов } for i:=0 to cMaxDigitsCount-1 do begin dd.LoDigit:=X.Digits[i]; dd.DDigit:=dd.DDigit+dd.HiDigit; { записываем полученную цифру в результат } X.Digits[i]:=dd.LoDigit; if dd.HiDigit=0 then Break; end; end; { возвращаем результат переноса с последнего вычитания (dd.HiDigit) } Result:=dd.HiDigit; end; function vlaDec(var X:tVeryLongUIntEx):tDigit; overload; begin Result:=vlaSub(X, cVeryLongUIntOneEx); end; { Вычитает из старших НЕНУЛЕВЫХ разрядов X длиной nX+1 разрядов значение Y длиной nY+1 разрядов. !!!Должно быть nY<=nX. Это НЕ проверяется.} function vlaSubTop_(var X:tVeryLongUInt; const Y:tVeryLongUInt; nX,nY:tDigitIndex):tDigit; overload; var i:tDigitIndex; j:tDigitCount; dd:tDoubleDigit; begin dd.DDigit:=0; j:=nX-nY; for i:=0 to nY do begin dd.LoDigit:=X.Digits[j]; dd.DDigit:=dd.DDigit-Y.Digits[i]+dd.HiDigit; X.Digits[j]:=dd.LoDigit; j:=j+1; end; Result:=dd.HiDigit; end; function vlaSubTop_(var X:tVeryLongUInt; Y:tDoubleDigit; nX,nY:tDigitIndex):tDigit; overload; var i:tDigitIndex; j:tDigitCount; dd:tDoubleDigit; begin dd.DDigit:=0; j:=nX-nY; for i:=0 to nY do begin dd.LoDigit:=X.Digits[j]; dd.DDigit:=dd.DDigit-Y.Digits[i]+dd.HiDigit; X.Digits[j]:=dd.LoDigit; j:=j+1; end; Result:=dd.HiDigit; end; { Сравнивает X и Y. Возвращает 0 - равно, <0 - X меньше Y, >0 - X больше Y.} function vlaCmp(const X, Y:tVeryLongUInt):integer; var i:tDigitIndex; begin for i:=cMaxDigitsCount-1 downto 0 do begin Result:=X.Digits[i]-Y.Digits[i]; if Result<>0 then Exit; end; end; function vlaCmp(const X, Y:tVeryLongUIntEx):integer; overload; var i:tDigitIndex; begin if Y.MostSignificantDigitIndex=X.MostSignificantDigitIndex then begin for i:=X.MostSignificantDigitIndex downto 0 do begin Result:=X.Uint.Digits[i]-Y.Uint.Digits[i]; if Result<>0 then Exit; end; end else Result:=X.MostSignificantDigitIndex-Y.MostSignificantDigitIndex; end; function vlaCmp(const X:tVeryLongUInt; Y:tDoubleDigit):integer; begin if vlaMostSignificantDigitIndex(X)<=1 then begin Result:=X.DDigit0-Y.DDigit; end else Result:=1; end; function vlaCmp(const X:tVeryLongUIntEx; Y:tDoubleDigit):integer; overload; begin if X.MostSignificantDigitIndex<=1 then begin Result:=(X.Uint.DDigit0-Y.DDigit); end else Result:=1; end; { Сравнивает X и Y. Возвращает 0 - равно, <0 - X меньше Y, >0 - X больше Y.} function vlaCmp(const X, Y:tVeryLongUInt; nX,nY:tDigitIndex):integer; overload; begin if nY=nX then begin for nX:=nX downto 0 do begin Result:=X.Digits[nX]-Y.Digits[nX]; if Result<>0 then Exit; end; end else Result:=nX-nY; end; function vlaCmp_(const X:tVeryLongUInt; Y:tDoubleDigit; nX,nY:tDigitIndex):integer; overload; begin if nY=nX then begin for nX:=nX downto 0 do begin Result:=X.Digits[nX]-Y.Digits[nX]; if Result<>0 then Exit; end; Result:=0; end else Result:=nX-nY; end; function vlaCmp_(const X:tVeryLongUIntEx; Y:tDoubleDigit; nY:tDigitIndex):integer; overload; var i:tDigitIndex; begin if nY=X.MostSignificantDigitIndex then begin for i:=nY downto 0 do begin Result:=X.Uint.Digits[i]-Y.Digits[i]; if Result<>0 then Exit; end; Result:=0; end else Result:=X.MostSignificantDigitIndex-nY; end; { Сравнивает СТАРШИЕ РАЗРЯДЫ X и Y. Длина числа Y меньше или равна X. Возвращает 0 - равно, <0 - X меньше Y, >0 - X больше Y.} function vlaCmpTop_(const X, Y:tVeryLongUInt; nX,nY:tDigitIndex):integer; overload; begin for nY:=nY downto 0 do begin Result:=X.Digits[nX]-Y.Digits[nY]; if Result<>0 then Exit; if nX>0 then nX:=nX-1; end; Result:=0; end; function vlaCmpTop_(const X:tVeryLongUInt; Y:tDoubleDigit; nX,nY:tDigitIndex):integer; overload; begin for nY:=nY downto 0 do begin Result:=integer(X.Digits[nX])-Y.Digits[nY]; if Result<>0 then Exit; nX:=nX-1; end; end; { Сдвигает X на Y разрядов влево } procedure vlaShiftDigitsLeft(var X:tVeryLongUInt; Y:tDigitCount); var i, j:tDigitIndex; begin if Y=0 then begin end else if Y=cMaxDigitsCount then begin X:=cVeryLongUIntZero; end else begin j:=cMaxDigitsCount-1; for i:=j-Y downto 0 do begin X.Digits[j]:=X.Digits[i]; j:=j-1; end; for i:=(Y-1) downto 0 do begin X.Digits[i]:=0; end; end; end; procedure vlaShiftDigitsLeft(var X:tVeryLongUIntEx; Y:tDigitCount); overload; var i, j, msdi:tDigitIndex; begin if Y=0 then begin end else if Y=cMaxDigitsCount then begin vlaZeroTillMSD(X); end else begin j:=cMaxDigitsCount-1; msdi:=0; for i:=j-Y downto 0 do begin X.Uint.Digits[j]:=X.Uint.Digits[i]; if (msdi=0) and (X.Uint.Digits[j]>0) then msdi:=j; j:=j-1; end; for i:=(Y-1) downto 0 do begin X.Uint.Digits[i]:=0; end; X.MostSignificantDigitIndex:=msdi; end; end; { Сдвигает A:B на N разрядов влево. А - старшая часть. } procedure vlaShiftDigitsLeft(var A,B:tVeryLongUInt; N:tDigitCount); var i, j:tDigitIndex; begin if N=0 then begin end else if N=cMaxDigitsCount then begin A:=B; B:=cVeryLongUIntZero; end else begin j:=cMaxDigitsCount-1; for i:=j-N downto 0 do begin A.Digits[j]:=A.Digits[i]; j:=j-1; end; j:=cMaxDigitsCount-1; for i:=(N-1) downto 0 do begin A.Digits[i]:=B.Digits[j]; j:=j-1; end; j:=cMaxDigitsCount-1; for i:=j-N downto 0 do begin B.Digits[j]:=B.Digits[i]; j:=j-1; end; for i:=(N-1) downto 0 do begin B.Digits[i]:=0; end; end; end; { Сдвигает A:B на N разрядов влево. А - старшая часть. } procedure vlaShiftDigitsRight(var A,B:tVeryLongUInt; N:tDigitCount); var i, j:tDigitIndex; begin if N=0 then begin end else if N=cMaxDigitsCount then begin B:=A; A:=cVeryLongUIntZero; end else begin //B>>N j:=0; for i:=N to cMaxDigitsCount-1 do begin B.Digits[j]:=B.Digits[i]; j:=j+1; end; //A:B>>N j:=cMaxDigitsCount-1; for i:=(N-1) downto 0 do begin B.Digits[j]:=A.Digits[i]; j:=j-1; end; //A>>N j:=0; for i:=N to cMaxDigitsCount-1 do begin A.Digits[j]:=A.Digits[i]; j:=j+1; end; for i:=cMaxDigitsCount-N to cMaxDigitsCount-1 do begin A.Digits[i]:=0; end; end; end; procedure vlaShiftDigitsRight(var A:tVeryLongUInt; N:tDigitCount); var i, j:tDigitIndex; begin if N=0 then begin end else if N=cMaxDigitsCount then begin A:=cVeryLongUIntZero; end else begin //A>>N j:=0; for i:=N to cMaxDigitsCount-1 do begin A.Digits[j]:=A.Digits[i]; j:=j+1; end; for i:=cMaxDigitsCount-N to cMaxDigitsCount-1 do begin A.Digits[i]:=0; end; end; end; procedure vlaShiftDigitsRight(var A:tVeryLongUIntEx; N:tDigitCount); overload; var i, j:tDigitIndex; begin if N=0 then begin end else if N>=A.MostSignificantDigitIndex+1 then begin vlaZeroTillMSD(A); end else begin //A>>N j:=0; for i:=N to A.MostSignificantDigitIndex do begin A.Uint.Digits[j]:=A.Uint.Digits[i]; j:=j+1; end; for i:=A.MostSignificantDigitIndex-N+1 to A.MostSignificantDigitIndex do begin A.Uint.Digits[i]:=0; end; A.MostSignificantDigitIndex:=A.MostSignificantDigitIndex-N; end; end; procedure ZeroMemW(var X; N:word); register; asm movzx ecx,N mov edx,edi mov edi,eax xor eax,eax rep stosw mov edi, edx end; procedure vlaShiftDigitsRight_(var A:tVeryLongUInt; N:tDigitCount; nA:tDigitIndex); var i, j:tDigitIndex; begin if N=0 then begin end else if N>=(nA+1) then begin vlaZero(A); end else begin //A>>N i:=vlaLeastSignificantDigitIndex(A); if i=cMaxDigitsCount*16 then begin vlaZero(X); end else begin i:=N div 16; j:=vlaMostSignificantDigitIndex(X); if i>0 then vlaShiftDigitsRight_(X, i, j); if i<=j then begin j:=j-i; i:=N-16*i; if i>0 then begin X.Digit0:=X.Digit0 shr i; i:=16-i; for k:=1 to j do begin dd.DDigit:=(tDDigit(X.Digits[k]) shl i); X.Digits[k-1]:=X.Digits[k-1] or dd.LoDigit; X.Digits[k]:=dd.HiDigit; end; end; end; end; end; procedure vlaShiftBitsRight(var X:tVeryLongUIntEx; N:word); // var carry:tDoubleDigit; var i:tDigitIndex; { function localShr(lo32:tDDigit):tDDigit; register; asm //eax=lo32 mov edx, carry mov carry, 0 mov cl,i shrd carry,eax, cl shr eax,cl or eax, edx end;} var j, k:tDigitIndex; begin if N=0 then begin end else if N>=(X.MostSignificantDigitIndex+1)*16 then begin vlaZeroTillMSD(X); end else begin i:=N shr 4; j:=X.MostSignificantDigitIndex; if i>0 then begin vlaShiftDigitsRight_(X.Uint, i, j); j:=j-i; end; i:=N-(i shl 4); if i>0 then begin // carry.DDigit:=0; if j=cMaxDigitsCount-1 then begin for k:=0 to (cMaxDigitsCount-1)-1 do begin X.Uint.Digits[k]:=(pDDigit(@X.Uint.Digits[k])^ shr i); end; X.Uint.Digits[(cMaxDigitsCount-1)]:=(X.Uint.Digits[(cMaxDigitsCount-1)] shr i); end else begin { for k:=((j+1) div 2) downto 0 do begin X.Uint.DDigits[k]:=localShr(X.Uint.DDigits[k]); end;} for k:=0 to j do begin X.Uint.Digits[k]:=(pDDigit(@X.Uint.Digits[k])^ shr i); end end; end; if (X.Uint.Digits[j]=0) and (j>0) then j:=j-1; X.MostSignificantDigitIndex:=j; end; end; { Сдвигает X на Y бит влево } procedure vlaShiftBitsLeft(var X:tVeryLongUIntEx; N:word); var carry:tDoubleDigit; var i:tDigitIndex; function localShl(lo32:tDDigit):tDDigit; register; asm //eax=lo32 mov edx, carry mov carry, 0 mov cl,i shld carry,eax, cl shl eax,cl or eax, edx end; var j,k:tDigitIndex; begin if N=0 then begin end else if N>=cMaxDigitsCount*16 then begin vlaZeroTillMSD(X); end else begin i:=N shr 4; if i>0 then vlaShiftDigitsLeft(X, i); i:=N-(i shl 4); if i>0 then begin j:=X.MostSignificantDigitIndex; carry.DDigit:=0; if j=(cMaxDigitsCount-1) then begin for k:=0 to ((cMaxDigitsCount-1) div 2) do begin X.Uint.DDigits[k]:=localShl(X.Uint.DDigits[k]); end; if X.Uint.Digits[(cMaxDigitsCount-1)]=0 then vlaMostSignificantDigitIndex(X); end else begin for k:=0 to ((j+1) div 2) do begin X.Uint.DDigits[k]:=localShl(X.Uint.DDigits[k]); end; Inc(j); if X.Uint.Digits[j]>0 then X.MostSignificantDigitIndex:=j; end; end; end; end; { Возвращает номер старшего ненулевого разряда X. Для X=0 возвращает 0.} function vlaMostSignificantDigitIndex(const X:tVeryLongUInt):tDigitIndex; overload; begin for Result:=cMaxDigitsCount-1 downto 0 do begin if x.Digits[Result]>0 then Exit; end; Result:=0; end; function vlaMostSignificantDigitIndex(const X:tVeryLongUInt; L:tDigitIndex):tDigitIndex; overload; begin for Result:=L downto 0 do begin if x.Digits[Result]>0 then Exit; end; Result:=0; end; procedure vlaMostSignificantDigitIndex(var X:tVeryLongUIntEx); overload; var i:tDigitIndex; begin for i:=cMaxDigitsCount-1 downto 0 do begin if x.Uint.Digits[i]>0 then begin X.MostSignificantDigitIndex:=i; Exit; end; end; X.MostSignificantDigitIndex:=0; end; procedure vlaDecrementMostSignificantDigitIndex(var X:tVeryLongUIntEx); var i:tDigitIndex; begin i:=X.MostSignificantDigitIndex; if (i=0) OR (x.Uint.Digits[i]>0) then Exit; for i:=i-1 downto 0 do begin if x.Uint.Digits[i]>0 then begin X.MostSignificantDigitIndex:=i; Exit; end; end; X.MostSignificantDigitIndex:=0; end; function vlaLeastSignificantDigitIndex(const X:tVeryLongUInt):tDigitIndex; register; overload; asm mov edx, edi // lea edi,tVeryLongUInt([eax]).Digit0 mov edi,eax xor eax,eax mov ecx,cMaxDigitsCount repe scasw je @end mov eax,cMaxDigitsCount-1 sub eax,ecx @end: mov edi,edx end; (*function vlaLeastSignificantDigitIndex(const X:tVeryLongUInt):tDigitIndex; overload; begin for Result:=0 to cMaxDigitsCount-1 do begin if x.Digits[Result]>0 then Exit; end; Result:=0; end;*) (*procedure vlaDecrementLeastSignificantDigitIndex(var X:tVeryLongUIntEx); var i:tDigitIndex; begin for i:=X.MostSignificantDigitIndex downto 0 do begin if x.Uint.Digits[i]>0 then begin X.MostSignificantDigitIndex:=i; Exit; end; end; X.MostSignificantDigitIndex:=0; end; *) { Умножает число X со старшим ненулевым разрядом L на цифру D, X = результат умножения. Возвращает: 0 - нет переполнения, >0 - переполнение, возвращает цифру следующего разряда } function vlaMulOnDigit_(var X:tVeryLongUInt; L:tDigitIndex; D:tDigit):tDigit; overload; var i:tDigitIndex; dd:tDoubleDigit; begin Result:=0; if D>0 then begin if L0 then begin if L0 then begin dd.DDigit:=tDDigit(X.Digits[i])*D+Result; R.Digits[i]:=dd.LoDigit; Result:=dd.HiDigit; end else begin R.Digits[i]:=Result; Result:=0; end; end; if L0 then begin dd.DDigit:=0; L:=X.MostSignificantDigitIndex; if L0 then begin dd.DDigit:=tDDigit(X.Uint.Digits[i])*D+dd.HiDigit; R.Uint.Digits[i]:=dd.LoDigit; end else begin R.Uint.Digits[i]:=dd.HiDigit; dd.HiDigit:=0; end; end; Result:=dd.HiDigit; if R.Uint.Digits[L]>0 then begin R.MostSignificantDigitIndex:=L; end else begin R.MostSignificantDigitIndex:=L-1; end; if L0 then begin dd.DDigit:=0; L:=X.MostSignificantDigitIndex; if L0 then begin dd.DDigit:=tDDigit(X.Uint.Digits[i])*D+dd.HiDigit; X.Uint.Digits[i]:=dd.LoDigit; end else begin X.Uint.Digits[i]:=dd.HiDigit; dd.HiDigit:=0; end; end; Result:=dd.HiDigit; if X.Uint.Digits[L]>0 then begin X.MostSignificantDigitIndex:=L; end; if L0 then begin i:=Shift; while i>0 do begin Dec(i); R.Digits[i]:=0; end; if L0 then begin dd.DDigit:=tDDigit(X.Digits[i])*D+Result; R.Digits[i+Shift]:=dd.LoDigit; Result:=dd.HiDigit; end else begin R.Digits[i+Shift]:=Result; Result:=0; end; end; if L0 then begin i:=Shift; while i>0 do begin Dec(i); R.Uint.Digits[i]:=0; end; L:=X.MostSignificantDigitIndex; if L0 then begin dd.DDigit:=tDDigit(X.Uint.Digits[i])*D+dd.HiDigit; R.Uint.Digits[i+Shift]:=dd.LoDigit; end else begin R.Uint.Digits[i+Shift]:=dd.HiDigit; dd.HiDigit:=0; end; end; if L0 then R.MostSignificantDigitIndex:=L+Shift else R.MostSignificantDigitIndex:=L+Shift-1; Result:=dd.HiDigit; end else begin vlaZero(R); Result:=0; end; end; procedure vlaMulOnDigit_(X:tDoubleDigit; D:tDigit; var R:tVeryLongUInt); overload; begin R.QDigit0:=int64(X.DDigit)*D; end; { Умножает число X на число из ОДНОЙ цифры D, X = результат умножения. Возвращает: 0 - нет переполнения, >0 - переполнение, возвращает цифру следующего разряда } function vlaMul(var X:tVeryLongUInt; D:tDigit):tDigit; begin Result:=vlaMulOnDigit_(X, vlaMostSignificantDigitIndex(X), D); end; { Умножает X на Y, помещая результат в B - старшая часть произведения и A - младшая часть произведения} procedure vlaMul(const X, Y:tVeryLongUInt; var B,A:tVeryLongUInt); var i,l:tDigitIndex; Z,Z1:tVeryLongUInt; begin { инициализируем результат нулями} B:=cVeryLongUIntZero; A:=cVeryLongUIntZero; { номер старшего ненулевого разряда X } l:=vlaMostSignificantDigitIndex(X); { для каждой ЦИФРЫ Y } for i:=0 to vlaMostSignificantDigitIndex(Y) do if Y.Digits[i]>0 then begin { умножаем X } // Z:=X; { на эту цифру, Z1 используем для переноса } // Z1.Digit0:=vlaMulOnDigit_(Z, l, Y.Digits[i]); Z1.Digit0:=vlaMulOnDigit_(X, l, Y.Digits[i], Z); { сдвигаем результат на i разрядов влево } vlaShiftDigitsLeft(Z1,Z,i); { суммируем результат умножения в B:A } if vlaAdd(A,Z)>0 then vlaInc(B); vlaAdd_(B,Z1,i) end; end; { Умножает X на Y, помещая результат в A - младшая часть произведения. !!!!ПЕРЕПОЛНЕНИЕ ИГНОРИРУЕТСЯ } procedure vlaMul(const X, Y:tVeryLongUInt; out A:tVeryLongUInt); var i,l:tDigitIndex; Z:tVeryLongUInt; begin { инициализируем результат нулями} A:=cVeryLongUIntZero; { номер старшего ненулевого разряда X } l:=vlaMostSignificantDigitIndex(X); { для каждой ЦИФРЫ Y } for i:=0 to vlaMostSignificantDigitIndex(Y) do if Y.Digits[i]>0 then begin { умножаем X } { на эту цифру } vlaMulOnDigit_(X, l, Y.Digits[i], Z, i); { сдвигаем результат на i разрядов влево } { суммируем результат умножения в A } vlaAdd(A,Z) end; end; procedure vlaMul(const X, Y:tVeryLongUIntEx; out A:tVeryLongUIntEx); overload; var i:tDigitIndex; Z:tVeryLongUIntEx; begin { инициализируем результат нулями} vlaZero(A); { для каждой ЦИФРЫ Y } for i:=0 to Y.MostSignificantDigitIndex do if Y.Uint.Digits[i]>0 then begin { умножаем X на эту цифру и сдвигаем результат на i разрядов влево } vlaMulOnDigit_(X, Y.Uint.Digits[i], Z, i); { суммируем результат умножения в A } vlaAdd(A,Z); end; end; { Умножает X на Y из двух цифр, помещая результат в A - младшая часть произведения. !!!!ПЕРЕПОЛНЕНИЕ ИГНОРИРУЕТСЯ } procedure vlaMul(const X:tVeryLongUInt; Y:tDoubleDigit; out A:tVeryLongUInt); var l:tDigitIndex; Z:tVeryLongUInt; begin { номер старшего ненулевого разряда X } l:=vlaMostSignificantDigitIndex(X); { умножаем X на 1-ю цифру } vlaMulOnDigit_(X, l, Y.LoDigit, A); if Y.HiDigit>0 then begin { умножаем X на 2-ю цифру и сдвигаем результат на 1 разряд влево} vlaMulOnDigit_(X, l, Y.HiDigit, Z, 1); { суммируем результат умножения в A } vlaAdd(A,Z); end; end; procedure vlaMul(const X:tVeryLongUIntEx; Y:tDoubleDigit; out A:tVeryLongUIntEx); overload; var Z:tVeryLongUIntEx; begin { умножаем X на 1-ю цифру } vlaMulOnDigit_(X, Y.LoDigit, A); if Y.HiDigit>0 then begin { умножаем X на 2-ю цифру и сдвигаем результат на 1 разряд влево} vlaMulOnDigit_(X, Y.HiDigit, Z, 1); { суммируем результат умножения в A } vlaAdd(A,Z); end; end; procedure vlaMul(var X:tVeryLongUIntEx; Y:tDoubleDigit); overload; var Z:tVeryLongUIntEx; begin if Y.HiDigit>0 then begin { умножаем X на 2-ю цифру и сдвигаем результат на 1 разряд влево} vlaMulOnDigit_(X, Y.HiDigit, Z, 1); { умножаем X на 1-ю цифру } vlaMulOnDigit_(X, Y.LoDigit); { суммируем результат умножения в X } vlaAdd(X,Z); end else begin { умножаем X на 1-ю цифру } vlaMulOnDigit_(X, Y.LoDigit); end; end; function vlaMulFast(var X:tVeryLongUIntEx; D:tDoubleDigit):tDDigit; var carry:tDoubleDigit; function localMul(lo32:tDDigit):tDDigit; register; asm //eax=lo32 {$ifndef FastMul} mul D add eax, carry adc edx, 0 mov carry, edx {$else ifndef FastMul} mul D add eax, ecx adc edx, 0 mov ecx, edx {$endif ndef FastMul} end; var i,j:tDigitIndex; begin if D.DDigit>0 then begin {$ifndef FastMul } carry.DDigit:=0; i:=0; j:=(X.MostSignificantDigitIndex shr 1); if j>0 then while (X.Uint.DDigits[i]=0) do Inc(i); { пропускаем нули } for i:=i to j do begin X.Uint.DDigits[i]:=localMul(X.Uint.DDigits[i]); end; {$else ifndef FastMul !!! Здесь используется НЕБЕЗОПАСНЫЙ код. Оптимизатор НЕ трогает в ДАННОМ КОНКРЕТНОМ МЕСТЕ значения EСX , что позволяет не записывать его в переменную для каждого вызова localMul. !!! НО НА ДРУГИХ ВЕРСИЯХ Delphi ЭТО МОЖЕТ НЕ РАБОТАТЬ.} i:=0; j:=(X.MostSignificantDigitIndex shr 1); if j>0 then while (X.Uint.DDigits[i]=0) do Inc(i); { пропускаем нули } asm xor ecx,ecx end; for i:=i to j do begin X.Uint.DDigits[i]:=localMul(X.Uint.DDigits[i]); end; asm mov carry,ecx end; {$endif ndef FastMul} i:=X.MostSignificantDigitIndex; if i0 then i:=i+1; if carry.DDigit>0 then begin if i0) then i:=i+2 else if (X.Uint.Digits[i+1]>0) then i:=i+1; Result:=0; end else begin if carry.LoDigit>0 then begin i:=i+1; X.Uint.DDigits[i]:=carry.LoDigit; end; Result:=carry.HiDigit; end; end; X.MostSignificantDigitIndex:=i; end else begin Result:=carry.DDigit; end; end else begin vlaZeroTillMSD(X); Result:=0; end; end; function vlaMulFast(var X:tVeryLongUIntEx; D:tDoubleDigit; out Y:tVeryLongUIntEx):tDDigit; overload; begin Y:=X; Result:=vlaMulFast(Y, D); end; ///////////////////////////////////////// (* procedure vlaKMul(const X,Y:tVeryLongUInt; var Z:tVeryLongUInt; N:tDigitIndex; var NN:tDigitIndex); var i,l,j, nAC:tDigitIndex; DC, AB, AC, DCAB, BD:tVeryLongUInt; begin if N=0 then begin Z.DDigit0:=X.Digit0*Y.Digit0; if Z.DoubleDigits.HiDigit>0 then NN:=1 else NN:=0; end else begin N:=N div 2; vlaKMul(X,Y, AC, N, nAC); vlaAssign(pVeryLongUInt(@Y.Digits[N])^, N, DC); vlaSub(DC,pVeryLongUInt(@Y.Digit0)^,N); vlaAssign(pVeryLongUInt(@X.Digit0)^, N, AB); vlaKMul(DC,AB, DCAB, N); vlaSub(AB,pVeryLongUInt(@Y.Digits[N])^,N); vlaKMul(pVeryLongUInt(@Y.Digits[N])^,pVeryLongUInt(@Y.Digits[N])^, BD, N); end; end;*) //////////////////////////////////////// { Делит N на цифру D. Возвращает в Q=[N/D] - частное, R = N-[N/D]*D - остаток деления.} procedure vlaDiv(const N:tVeryLongUInt; D:tDigit; var Q:tVeryLongUInt; var R:tDigit); var i,l:tDigitIndex; dd:tDoubleDigit; x:tDigit; RR:tVeryLongUInt; begin { инициализируем } Q:=cVeryLongUIntZero; { частное } RR:=N; { остаток } { определяем старший ненулевой разряд N} i:=vlaMostSignificantDigitIndex(N); { проверяем тривиальные случаи:} if (D=0) then begin { Деление на нуль } i:=1 div D; { искуственно вызываем Exception } end; if (i=0) and (N.Digit00 then { Число из ДВУХ старших цифр R } dd.DDigit:=pDoubleDigit(@RR.Digits[i-1])^.DDigit else { Число из ОДНОЙ старшей цифры R } dd.DDigit:=RR.Digits[i]; { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D). } dd.DDigit:=dd.DDigit div D; { если результат угадывания ДВЕ цифры - берем старшую } if dd.HiDigit>0 then dd.DDigit:=dd.HiDigit; { Эта величина (dd.LoDigit) заведомо МЕНЬШЕ (или равна) правильной, вроде бы не намного... } { умножаем делитель D на угаданную цифру частного dd.LoDigit} x:=dd.LoDigit; dd.DDigit:=dd.LoDigit*D; { вроде переполнения быть не должно??? } { выясняем длину числа DD (старший ненулевой разряд) } if dd.HiDigit>0 then begin { вычитаем A из старших разрядов R } RR.Digits[i]:=RR.Digits[i]-dd.HiDigit; { вычитаем A из старших разрядов R } RR.Digits[i-1]:=RR.Digits[i-1]-dd.LoDigit; l:=2 { длина увеличилась } end else begin { вычитаем A из старших разрядов R } RR.Digits[i]:=RR.Digits[i]-dd.LoDigit; l:=1; end; {определяем номер разряда частного для очередной цифры} { запоминаем очередную цифру частного в Q в соотв. разряд} Q.Digits[i-l]:=x; { уточняем длину остатка } i:=vlaMostSignificantDigitIndex(RR, i); until (i>0) OR (RR.Digit0>D); end; R:=RR.Digit0; end; ///////////////////////////////////////////////////////////////////////////////// { Делит N на D. Возвращает в Q=[N/D] - частное, R = N-[N/D]*D - остаток деления.} procedure vlaDiv(const N, D:tVeryLongUInt; out Q,R:tVeryLongUInt); overload; var i,j,l:tDigitIndex; dd:tDoubleDigit; A:tVeryLongUInt; Dj, MvDj1:tDigit; begin { инициализируем } Q:=cVeryLongUIntZero; { частное } R:=N; { остаток } { определяем старший ненулевой разряд N} i:=vlaMostSignificantDigitIndex(N); { определяем старший ненулевой разряд D} j:=vlaMostSignificantDigitIndex(D); { проверяем тривиальные случаи:} if (D.Digits[j]=0) then begin { Деление на нуль } i:=1 div j; { искуственно вызываем Exception } end; if (i0 then { Запоминаем и разницу максимального значения цифры и второй старшей цифры D для ускорения доступа при делении } MvDj1:=(cDigitMaxValue-D.Digits[j-1]) else MvDj1:=0; { Начинаем делить...} repeat { Угадываем очередную цифру частного. } if i>j then { Число из ДВУХ старших цифр R } dd.DDigit:=pDoubleDigit(@R.Digits[i-1])^.DDigit else { Число из ОДНОЙ старшей цифры R } dd.DDigit:=R.Digits[i]; if j>0 then begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D + 1). } dd.DDigit:=dd.DDigit div (Dj+1); { Интерполяционное уточнение угадывания по второй цифре D ПРИМЕЧАНИЕ: Для эффективного деления нужно угадать очередную цифру частного максимально точно и гарантированно МЕНЬШЕ_ИЛИ_РАВНО точного значения, т.е. меньше на 1, 2, 3, а не на 10, 20...1000. Деление двух старших цифр R на (одну_старшую_цифру_D + 1) гарантирует результат МЕНЬШЕ, но он может быть СЛИШКОМ меньше. Деление двух старших цифр R на (одну_старшую_цифру_D) НЕ гарантирует результат МЕНЬШЕ, ибо неизвестно сколько там после старшей_цифры_D. Приходится устраивать интерполяцию. } dd.DDigit:=dd.DDigit+(((dd.DDigit*MvDj1) div Dj) shr cDigitBitSize); end else begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D). } dd.DDigit:=dd.DDigit div Dj; end; { если результат угадывания ДВЕ цифры - берем старшую } if dd.HiDigit>0 then dd.DDigit:=dd.HiDigit; { Эта величина (dd.LoDigit) заведомо МЕНЬШЕ (или равна) правильной, вроде бы не намного... } { умножаем делитель D на угаданную цифру частного dd.LoDigit} // A:=D; // vlaMulOnDigit_(A,j,dd.LoDigit); { вроде переполнения быть не должно??? } vlaMulOnDigit_(D,j,dd.LoDigit,A); { вроде переполнения быть не должно??? } { выясняем длину числа A (старший ненулевой разряд) } if (j<(cMaxDigitsCount-1)) and (A.Digits[j+1]>0) then l:=j+1 { длина увеличилась } else l:=j; { уточняем длину числа A } if R.Digits[i] D } while vlaCmpTop_(R, D, i, l)>=0 do begin {????????????? j И l - тута непонятно!!!} { вычитаем D из старших разрядов R. Теоретически это цикл выполняется раза 2-3 не больше } vlaSubTop_(R, D, i, l); { и уточняем цифру частного } dd.LoDigit:=dd.LoDigit+1; end; {определяем номер разряда частного для очередной цифры i-l} { запоминаем очередную цифру частного в Q в соотв. разряд} Q.Digits[i-l]:=dd.LoDigit; { уточняем длину остатка } i:=vlaMostSignificantDigitIndex(R, i); until vlaCmp(R, D, i, j)<0; end; end; { Делит N на D. Возвращает в Q=[N/D] - частное, R = N-[N/D]*D - остаток деления.} procedure vlaDiv(const N:tVeryLongUInt; D:tDoubleDigit; out Q,R:tVeryLongUInt); overload; var i,j,l:tDigitIndex; dd:tDoubleDigit; A:tVeryLongUInt; Dj, MvDj1:tDigit; begin { инициализируем } Q:=cVeryLongUIntZero; { частное } R:=N; { остаток } { определяем старший ненулевой разряд N} i:=vlaMostSignificantDigitIndex(N); { определяем старший ненулевой разряд D} if D.HiDigit>0 then j:=1 else j:=0; { проверяем тривиальные случаи:} if (D.Digits[j]=0) then begin { Деление на нуль } i:=1 div j; { искуственно вызываем Exception } end; if (i0 then { Запоминаем и разницу максимального значения цифры и второй старшей цифры D для ускорения доступа при делении } MvDj1:=(cDigitMaxValue-D.Digits[j-1]) else MvDj1:=0; { Начинаем делить...} repeat { Угадываем очередную цифру частного. } if i>j then begin { Число из ДВУХ старших цифр R } dd.DDigit:=pDoubleDigit(@R.Digits[i-1])^.DDigit; end else { Число из ОДНОЙ старшей цифры R } dd.DDigit:=R.Digits[i]; if j>0 then begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D + 1). } dd.DDigit:=dd.DDigit div (Dj+1); { Интерполяционное уточнение угадывания по второй цифре D ПРИМЕЧАНИЕ: Для эффективного деления нужно угадать очередную цифру частного максимально точно и гарантированно МЕНЬШЕ_ИЛИ_РАВНО точного значения, т.е. меньше на 1, 2, 3, а не на 10, 20...1000. Деление двух старших цифр R на (одну_старшую_цифру_D + 1) гарантирует результат МЕНЬШЕ, но он может быть СЛИШКОМ меньше. Деление двух старших цифр R на (одну_старшую_цифру_D) НЕ гарантирует результат МЕНЬШЕ, ибо неизвестно сколько там после старшей_цифры_D. Приходится устраивать интерполяцию. } dd.DDigit:=dd.DDigit+(((dd.DDigit*MvDj1) div Dj) shr cDigitBitSize); end else begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D). } dd.DDigit:=dd.DDigit div Dj; end; { если результат угадывания ДВЕ цифры - берем старшую } if dd.HiDigit>0 then dd.DDigit:=dd.HiDigit; { Эта величина (dd.LoDigit) заведомо МЕНЬШЕ (или равна) правильной, вроде бы не намного... } { умножаем делитель D на угаданную цифру частного dd.LoDigit} A.QDigit0:=int64(D.DDigit)*dd.LoDigit; // vlaMulOnDigit_(D,dd.LoDigit,A); { вроде переполнения быть не должно??? } { выясняем длину числа A (старший ненулевой разряд) } if (j<(cMaxDigitsCount-1)) and (A.Digits[j+1]>0) then l:=j+1 { длина увеличилась } else l:=j; { уточняем длину числа A } if R.Digits[i] D } while vlaCmpTop_(R, D, i, l)>=0 do begin {????????????? j И l - тута непонятно!!!} { вычитаем D из старших разрядов R. Теоретически это цикл выполняется раза 2-3 не больше } vlaSubTop_(R, D, i, l); { и уточняем цифру частного } dd.LoDigit:=dd.LoDigit+1; end; {определяем номер разряда частного для очередной цифры i-l} { запоминаем очередную цифру частного в Q в соотв. разряд} Q.Digits[i-l]:=dd.LoDigit; { уточняем длину остатка } i:=vlaMostSignificantDigitIndex(R, i); until vlaCmp_(R, D, i, j)<0; end; end; { Делит N на D. Возвращает в Q=[N/D] - частное.} procedure vlaDiv(const N:tVeryLongUInt; D:tDoubleDigit; out Q:tVeryLongUInt); overload; var i,j,l:tDigitIndex; dd:tDoubleDigit; A,R:tVeryLongUInt; Dj, MvDj1:tDigit; begin { инициализируем } vlaZero(Q); // почти ничего не выигрываем { определяем старший ненулевой разряд N} i:=vlaMostSignificantDigitIndex(N); { определяем старший ненулевой разряд D} if D.HiDigit>0 then j:=1 else j:=0; { проверяем тривиальные случаи } if (D.Digits[j]=0) then begin { Деление на нуль } i:=1 div j; { искуственно вызываем Exception } end; if (i0 then { Запоминаем и разницу максимального значения цифры и второй старшей цифры D для ускорения доступа при делении } MvDj1:=(cDigitMaxValue-D.LoDigit) else MvDj1:=0; { Начинаем делить...} repeat { Угадываем очередную цифру частного. } if i>j then begin { Число из ДВУХ старших цифр R } dd.DDigit:=pDoubleDigit(@R.Digits[i-1])^.DDigit; end else { Число из ОДНОЙ старшей цифры R } dd.DDigit:=R.Digits[i]; if j>0 then begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D + 1). } dd.DDigit:=dd.DDigit div (Dj+1); { Интерполяционное уточнение угадывания по второй цифре D ПРИМЕЧАНИЕ: Для эффективного деления нужно угадать очередную цифру частного максимально точно и гарантированно МЕНЬШЕ_ИЛИ_РАВНО точного значения, т.е. меньше на 1, 2, 3, а не на 10, 20...1000. Деление двух старших цифр R на (одну_старшую_цифру_D + 1) гарантирует результат МЕНЬШЕ, но он может быть СЛИШКОМ меньше. Деление двух старших цифр R на (одну_старшую_цифру_D) НЕ гарантирует результат МЕНЬШЕ, ибо неизвестно сколько там после старшей_цифры_D. Приходится устраивать интерполяцию. } dd.DDigit:=dd.DDigit+(((dd.DDigit*MvDj1) div Dj) shr cDigitBitSize); end else begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D). } dd.DDigit:=dd.DDigit div Dj; end; { если результат угадывания ДВЕ цифры - берем старшую } if dd.HiDigit>0 then dd.DDigit:=dd.HiDigit; { Эта величина (dd.LoDigit) заведомо МЕНЬШЕ (или равна) правильной, вроде бы не намного... } { умножаем делитель D на угаданную цифру частного dd.LoDigit} A.QDigit0:=int64(D.DDigit)*dd.LoDigit; // vlaMulOnDigit_(D,dd.LoDigit,A); { вроде переполнения быть не должно??? } { уточняем длину числа A (старший ненулевой разряд) } if (A.Digits[j+1]>0) then l:=j+1 { длина увеличилась } else l:=j; { уточняем длину числа A } if R.Digits[i] D } while vlaCmpTop_(R, D, i, l)>=0 do begin { вычитаем D из старших разрядов R. Теоретически это цикл выполняется раза 2-3 не больше } vlaSubTop_(R, D, i, l); { и уточняем цифру частного } dd.LoDigit:=dd.LoDigit+1; end; {определяем номер разряда частного для очередной цифры i-l} { запоминаем очередную цифру частного в Q в соотв. разряд} Q.Digits[i-l]:=dd.LoDigit; { уточняем длину остатка } while (i>0) and (R.Digits[i]=0) do begin i:=i-1; end; until vlaCmp_(R, D, i, j)<0; end; end; procedure vlaDiv(const N:tVeryLongUIntEx; D:tDoubleDigit; out Q:tVeryLongUIntEx); overload; var i,j,l, msdi, il:tDigitIndex; dd:tDoubleDigit; A,R:tVeryLongUInt; Dj, MvDj1:tDigit; begin { инициализируем } vlaZero(Q); // почти ничего не выигрываем { определяем старший ненулевой разряд N} i:=N.MostSignificantDigitIndex; { определяем старший ненулевой разряд D} if D.HiDigit>0 then j:=1 else j:=0; { проверяем тривиальные случаи } if (D.Digits[j]=0) then begin { Деление на нуль } i:=1 div j; { искуственно вызываем Exception } end; if (i0 then { Запоминаем и разницу максимального значения цифры и второй старшей цифры D для ускорения доступа при делении } MvDj1:=(cDigitMaxValue-D.LoDigit) else MvDj1:=0; { Начинаем делить...} repeat { Угадываем очередную цифру частного. } if i>j then begin { Число из ДВУХ старших цифр R } dd.DDigit:=pDoubleDigit(@R.Digits[i-1])^.DDigit; end else { Число из ОДНОЙ старшей цифры R } dd.DDigit:=R.Digits[i]; if j>0 then begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D + 1). } dd.DDigit:=dd.DDigit div (Dj+1); { Интерполяционное уточнение угадывания по второй цифре D ПРИМЕЧАНИЕ: Для эффективного деления нужно угадать очередную цифру частного максимально точно и гарантированно МЕНЬШЕ_ИЛИ_РАВНО точного значения, т.е. меньше на 1, 2, 3, а не на 10, 20...1000. Деление двух старших цифр R на (одну_старшую_цифру_D + 1) гарантирует результат МЕНЬШЕ, но он может быть СЛИШКОМ меньше. Деление двух старших цифр R на (одну_старшую_цифру_D) НЕ гарантирует результат МЕНЬШЕ, ибо неизвестно сколько там после старшей_цифры_D. Приходится устраивать интерполяцию. } dd.DDigit:=dd.DDigit+(((dd.DDigit*MvDj1) div Dj) shr cDigitBitSize); end else begin { Делим число из ДВУХ (ОДНОЙ) старших цифр R на (одну_старшую_цифру_D). } dd.DDigit:=dd.DDigit div Dj; end; { если результат угадывания ДВЕ цифры - берем старшую } if dd.HiDigit>0 then dd.DDigit:=dd.HiDigit; { Эта величина (dd.LoDigit) заведомо МЕНЬШЕ (или равна) правильной, вроде бы не намного... } { умножаем делитель D на угаданную цифру частного dd.LoDigit} A.QDigit0:=int64(D.DDigit)*dd.LoDigit; { уточняем длину числа A (старший ненулевой разряд) } if (A.Digits[j+1]>0) then l:=j+1 { длина увеличилась } else l:=j; { уточняем длину числа A } if R.Digits[i] D } while vlaCmpTop_(R, D, i, l)>=0 do begin { вычитаем D из старших разрядов R. Теоретически это цикл выполняется раза 2-3 не больше } vlaSubTop_(R, D, i, l); { и уточняем цифру частного } dd.LoDigit:=dd.LoDigit+1; end; {определяем номер разряда частного для очередной цифры i-l} { запоминаем очередную цифру частного в Q в соотв. разряд} il:=i-l; Q.Uint.Digits[il]:=dd.LoDigit; if msdi=0 then msdi:=il; { уточняем длину остатка } while (i>0) and (R.Digits[i]=0) do begin i:=i-1; end; until vlaCmp_(R, D, i, j)<0; Q.MostSignificantDigitIndex:=msdi; end; end; { Быстрое деление N на D } procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDigit); overload; var i:tDigitIndex; dd:tDoubleDigit; begin { делим } i:=N.MostSignificantDigitIndex; dd.LoDigit:=N.Uint.Digits[i] div D; dd.HiDigit:=N.Uint.Digits[i] - (dd.LoDigit*D); N.Uint.Digits[i]:=dd.LoDigit; if i>0 then begin if dd.LoDigit=0 then Dec(N.MostSignificantDigitIndex); for i:=i-1 downto 0 do begin dd.LoDigit:=N.Uint.Digits[i]; // if dd.DDigit>0 then begin N.Uint.Digits[i]:=dd.DDigit div D; dd.HiDigit:=dd.DDigit - (N.Uint.Digits[i]*D); // end else begin // N.Uint.Digits[i]:=0; // end; end; end; end; //////////////////////////////////////////////////////////////////////// // Тестовый алгоритм чистого 32 битного деления { Решение (как я придумал): Итак: I=(2^32)*X+Y, где 0<=X0) AND (NN.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (NN.Uint.Digits[i]=0) then Dec(i); end; NN.MostSignificantDigitIndex:=i; end; function vlaDiv64by32(lo32, hi32, denom:longword):int64; register; asm div denom end; function vlaDivQWbyDW(var Q:tQuadDigit; denom:tDoubleDigit):tDDigit; register; asm push Q mov ecx, denom mov edx,tQuadDigit([Q]).HiDDigit mov eax,tQuadDigit([Q]).LoDDigit div ECX pop ecx mov tQuadDigit([ecx]).HiDDigit, edx // остаток деления end; { БЫСТРО Делит N на D, используя процессорное 64бит/32бит деление. Возвращает в N=[N/D] - частное.} procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; {$ifndef FastDiv} var remainder:tDDigit; {$endif ndef FastDiv} { Делит 64битное число remainder:LO32 на 32битное число D Возврашает частное и запоминает остаток деления в remainder.} function localDiv(lo32:tDDigit):tDDigit; register; asm //eax=lo32 {$ifndef FastDiv} mov edx, remainder div D mov remainder, edx // остаток деления {$else ifndef FastDiv} // здесь используется НЕДОКУМЕНТИРОВАННЫЙ случай неизменности edx и ecx в цикле div ecx {$endif ndef FastDiv} end; var i:tDigitIndex; begin {$ifndef FastDiv } remainder:=0; { делим } for i:=(N.MostSignificantDigitIndex div 2) downto 0 do begin N.Uint.DDigits[i]:=localDiv(N.Uint.DDigits[i]); end; {$else ifndef FastDiv !!! Здесь используется НЕБЕЗОПАСНЫЙ код. Оптимизатор НЕ трогает в ДАННОМ КОНКРЕТНОМ МЕСТЕ значения EDX и ECX, что позволяет не записывать их в переменную для каждого вызова localDiv. !!! НО НА ДРУГИХ ВЕРСИЯХ Delphi ЭТО МОЖЕТ НЕ РАБОТАТЬ.} asm xor edx,edx mov ecx,D end; { делим } for i:=(N.MostSignificantDigitIndex div 2) downto 0 do begin N.Uint.DDigits[i]:=localDiv(N.Uint.DDigits[i]); end; {$endif ndef FastDiv} (* k:=(N.MostSignificantDigitIndex shr 1); p:=@N.Uint.DDigits[k]; asm push edi; push ebx std mov edi,p movzx ecx,k inc ecx mov ebx,D xor edx,edx @loop1: mov eax,[edi] div ebx stosd loop @loop1 cld pop ebx; pop edi end;*) { поделили, уточняем номер старшей ненулевой цифры } i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin repeat Dec(i); until (i=0) OR (N.Uint.Digits[i]<>0); N.MostSignificantDigitIndex:=i; end; end; function vlaDivFast(var N:tVeryLongUIntEx; D:tDDigit):tDDigit; overload; { возвращает остаток деления } { Делит 64битное число DD:LO32 на 32битное число D Возврашает частное и запоминает остаток деления в DD.} function localDiv(lo32:tDDigit):tDDigit; register; asm mov edx, Result //eax=lo32 div D mov Result, edx // остаток деления end; var i:tDigitIndex; begin Result:=0; { делим } for i:=(N.MostSignificantDigitIndex div 2) downto 0 do begin N.Uint.DDigits[i]:=localDiv(N.Uint.DDigits[i]); end; { поделили, уточняем номер старшей ненулевой цифры } i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (N.Uint.Digits[i]=0) then Dec(i); N.MostSignificantDigitIndex:=i; end; end; procedure vlaDivFast(const N:tVeryLongUIntEx; D:tDoubleDigit; out Q:tVeryLongUIntEx); overload; begin Q:=N; vlaDivFast(Q, D); end; { Делит N на D. Возвращает в Q=[N/D] - частное. Метод деления отрезка пополам. ПРИМЕЧАНИЕ!!! очень медленный алгоритм - проигрывает vlaDiv} procedure vlaDivEx(const N:tVeryLongUInt; D:tDoubleDigit; var Q:tVeryLongUInt); overload; var A,L,R:tVeryLongUInt; begin if vlaCmp(N,D)<0 then begin Q:=N; end else begin R:=N; L:=N; vlaShiftDigitsRight(L, 2); repeat Q:=L; vlaAdd(Q,R); vlaShiftBitsRight(Q,1); vlaMul(Q, D, A); case vlaCmp(A,N) of 0: Exit; LOW(integer)..-1: begin L:=Q; vlaInc(L); end; else begin R:=Q; vlaDec(R); end; end; until vlaCmp(L,R)>=0; vlaDec(L); Q:=L; end; end; function vlaPowerOfDDigit(D:tDoubleDigit; P:word; out X:tVeryLongUIntEx):tDDigit; var i:word; begin Result:=0; vlaZero(X); if p=0 then begin X.Uint.Digit0:=1; end else begin X.Uint.DDigit0:=D.DDigit; if D.HiDigit>0 then X.MostSignificantDigitIndex:=1; i:=1; while i

0 then for i:=i downto 1 do begin write(X.Digits[i], ':'); end; writeln(X.Digit0); end; procedure vlaWriteln(const X:tVeryLongUIntEx); overload; var i:tDigitIndex; begin i:=X.MostSignificantDigitIndex; if i>0 then for i:=i downto 1 do begin write(X.Uint.Digits[i]:5, ':'); end; writeln(X.Uint.Digit0:5); end; procedure vlaAssign(const Src:tVeryLongUInt; L:tDigitIndex; var Dest:tVeryLongUInt); overload; var i:tDigitIndex; begin for i:=Low(i) to L do begin Dest.Digits[i]:=Src.Digits[i]; end; for i:=L+1 to cMaxDigitsCount-1 do begin Dest.Digits[i]:=0; end; end; procedure vlaAssign(const Src:tVeryLongUInt; L:tDigitIndex; var Dest:tVeryLongUIntEx); overload; var i:tDigitIndex; begin for i:=Low(i) to L do begin Dest.Uint.Digits[i]:=Src.Digits[i]; end; if L<(cMaxDigitsCount-1) then for i:=L+1 to cMaxDigitsCount-1 do begin Dest.Uint.Digits[i]:=0; end; Dest.MostSignificantDigitIndex:=vlaMostSignificantDigitIndex(Dest.Uint); end; procedure vlaAssign(const Src:tVeryLongUIntEx; var Dest:tVeryLongUIntEx); overload; var i:tDigitIndex; begin for i:=0 to Src.MostSignificantDigitIndex do begin Dest.Uint.Digits[i]:=Src.Uint.Digits[i]; end; Dest.MostSignificantDigitIndex:=Src.MostSignificantDigitIndex; if Src.MostSignificantDigitIndex<(cMaxDigitsCount-1) then for i:=Src.MostSignificantDigitIndex+1 to cMaxDigitsCount-1 do begin Dest.Uint.Digits[i]:=0; end; end; procedure vlaAssign(const Src:tVeryLongUInt; var Dest:tVeryLongUIntEx); overload; var i:tDigitIndex; begin Dest.MostSignificantDigitIndex:=vlaMostSignificantDigitIndex(Src); for i:=Low(i) to Dest.MostSignificantDigitIndex do begin Dest.Uint.Digits[i]:=Src.Digits[i]; end; for i:=Dest.MostSignificantDigitIndex+1 to cMaxDigitsCount-1 do begin Dest.Uint.Digits[i]:=0; end; end; procedure vlaAssign(const Src:tVeryLongUIntEx; var Dest:tVeryLongUInt); overload; var i:tDigitIndex; begin for i:=Low(i) to Src.MostSignificantDigitIndex do begin Dest.Digits[i]:=Src.Uint.Digits[i]; end; for i:=Src.MostSignificantDigitIndex+1 to cMaxDigitsCount-1 do begin Dest.Digits[i]:=0; end; end; procedure vlaAssign(const Src:tDoubleDigit; var Dest:tVeryLongUInt); overload; var i:tDigitIndex; begin Dest.DDigit0:=Src.DDigit; for i:=2 to cMaxDigitsCount-1 do begin Dest.Digits[i]:=0; end; end; procedure vlaAssign(const Src:tDoubleDigit; var Dest:tVeryLongUIntEx); overload; var i:tDigitIndex; begin Dest.Uint.DDigit0:=Src.DDigit; if Src.Digits[1]>0 then Dest.MostSignificantDigitIndex:=1 else Dest.MostSignificantDigitIndex:=0; for i:=2 to cMaxDigitsCount-1 do begin Dest.Uint.Digits[i]:=0; end; end; procedure vlaAssignTillMSD(const Src:tVeryLongUIntEx; var Dest:tVeryLongUInt); overload; var i:tDigitIndex; begin for i:=Low(i) to Src.MostSignificantDigitIndex do begin Dest.Digits[i]:=Src.Uint.Digits[i]; end; end; procedure vlaAssignTillMSD(const Src:tVeryLongUIntEx; var Dest:tVeryLongUIntEx); overload; var i:tDigitIndex; begin for i:=Low(i) to Src.MostSignificantDigitIndex do begin Dest.Uint.Digits[i]:=Src.Uint.Digits[i]; end; Dest.MostSignificantDigitIndex:=Src.MostSignificantDigitIndex; end; ////////////////////////////////////////////// { Деление на константу. Это когда надо много делить на одно и то же число B.} // a/b == (int)((((2^16)^N)+b-1)/b) * (a) / ((2^16)^N) ////////////////////////////////////////////// // на практике себя не оправдало { Вычисление инверсной константы} procedure vlaConstDivPrepare( const B:tDoubleDigit; N:tDigitIndex; var InverseB:tVeryLongUInt ); var X:tVeryLongUInt; begin //((((2^16)^N)+b-1)/b) //((((2^16)^N))/b) X:=cVeryLongUIntZero; X.Digits[N]:=1; {!!!! должна быть СТЕПЕНЬЮ основания (2^16)^N } // vlaAdd(X, tDoubleDigit(B.DDigit-1)); vlaDiv(X, B, InverseB); end; { Деление на константу B, посредством умножения на инверсную константу } procedure vlaConstDiv( const A:tVeryLongUInt; const InverseB:tVeryLongUInt; N:tDigitIndex; var Q:tVeryLongUInt ); var X:tVeryLongUInt; begin // a/b == (int)((((2^16)^N)+b-1)/b) * (a) / ((2^16)^N) // a/b == (int)((((2^16)^N))/b) * (a) / ((2^16)^N) vlaMul(InverseB, A, X, Q); vlaShiftDigitsRight(X, Q, N); end; //////////////////////////////////////////////////////////////////////////////// var i:tDigitIndex; begin if ((cMaxDigitsCount shr 1) shl 1)<>cMaxDigitsCount then begin RunError(201); end; { инициирование константы - МАКСИМАЛЬНОЕ ЗНАЧЕНИЕ tLongUIntValue} for i:=0 to cMaxDigitsCount-1 do cVeryLongUIntMaxValue.Digits[i]:=High(tDigit); end. //////////////////////////////////////////////////////////////////////////////// (* procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; var i:tDigitIndex; qq:tQuadDigit; dd:tDoubleDigit; x,nn,rr,y:tDDigit; begin if D.HiDigit<>0 then begin vlaDivFast(N, D.LoDigit); EXIT; end; i:=N.MostSignificantDigitIndex div 2; qq.HiDDigit:=0; nn:=$FFFFFFFF div D.DDigit; rr:=($FFFFFFFF-(nn*D.DDigit)+1); { делим } for i:=i downto 0 do begin qq.LoDDigit:=N.Uint.DDigits[i]; dd.DDigit:=(qq.HiDDigit div D.DDigit) shl 16; //??? x:=qq.HiDDigit-(D.DDigit*dd.HiDigit); y:=(qq.LoDDigit div D.DDigit); dd.DDigit:=dd.DDigit+nn*x+y+( (rr*x+(qq.LoDDigit-y*D.DDigit)) div D.DDigit ); qq.HiDDigit:=qq.QDigit - (tQDigit(dd.DDigit)*D.DDigit); N.Uint.DDigits[i]:=dd.DDigit; end; i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (N.Uint.Digits[i]=0) then Dec(i); end; N.MostSignificantDigitIndex:=i; end; procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; var i:tDigitIndex; qq:tQuadDigit; dd, x:tDoubleDigit; k, d1, y, y1:tDDigit; begin // if D.HiDigit<>0 then begin // vlaDivFast(N, D.LoDigit); // EXIT; // end; i:=N.MostSignificantDigitIndex div 2; { делим старшую цифру } k:=N.Uint.DDigits[i]; d1:=k div D.DDigit; N.Uint.DDigits[i]:=d1; if i>0 then begin { делим } qq.HiDDigit:=k-(d1*D.DDigit); k:=$FFFFFFFF div D.DDigit; { k = (2^32 - 1) div D.DDigit } d1:=($FFFFFFFF-(k*D.DDigit))+1; { d1 = ((2^32 - 1) mod D.DDigit) + 1} if d1>=D.DDigit then begin Inc(k); d1:=d1-D.DDigit; end; for i:=i-1 downto 0 do begin qq.LoDDigit:=N.Uint.DDigits[i]; y:=(qq.LoDDigit div D.DDigit); y1:=(qq.HiDDigit*d1) div D.DDigit; dd.DDigit:=qq.HiDDigit*k+y+y1+(((qq.HiDDigit*d1)-y1*D.DDigit)+(qq.LoDDigit-y*D.DDigit)) div D.DDigit; // dd.DDigit:=qq.HiDDigit*k+(qq.HiDDigit*d1+qq.LoDDigit) div D.DDigit; x.DDigit:=qq.QDigit div D.DDigit; qq.HiDDigit:=qq.QDigit - (tQDigit(dd.DDigit)*D.DDigit); if x.DDigit<>dd.DDigit then writeln; N.Uint.DDigits[i]:=dd.DDigit; end; end; i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (N.Uint.Digits[i]=0) then Dec(i); end; N.MostSignificantDigitIndex:=i; end; procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; var i:tDigitIndex; qq:tQuadDigit; dd:tDoubleDigit; x,nn,rr,y:tDDigit; begin if D.HiDigit<>0 then begin vlaDivFast(N, D.LoDigit); EXIT; end; qq.HiDDigit:=0; nn:=$FFFFFFFF div D.DDigit; rr:=($FFFFFFFF-(nn*D.DDigit)+1); { делим } i:=N.MostSignificantDigitIndex div 2; for i:=i downto 0 do begin qq.LoDDigit:=N.Uint.DDigits[i]; dd.DDigit:=(qq.HiDDigit div D.DDigit) shl 16; //??? x:=qq.HiDDigit-(D.DDigit*dd.HiDigit); y:=(qq.LoDDigit div D.DDigit); dd.DDigit:=dd.DDigit+nn*x+y+( (rr*x+(qq.LoDDigit-y*D.DDigit)) div D.DDigit ); qq.HiDDigit:=qq.QDigit - (tQDigit(dd.DDigit)*D.DDigit); N.Uint.DDigits[i]:=dd.DDigit; end; i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (N.Uint.Digits[i]=0) then Dec(i); end; N.MostSignificantDigitIndex:=i; end; procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; var i:tDigitIndex; qq:tQuadDigit; function vlaDivQQ:tDDigit; register; asm mov edx,qq.HiDDigit mov eax,qq.LoDDigit div D.DDigit mov qq.HiDDigit, edx // остаток деления end; begin // i:=N.MostSignificantDigitIndex div 2; { делим старшую цифру } qq.HiDDigit:=0; { делим дальше } for i:=(N.MostSignificantDigitIndex div 2) downto 0 do begin qq.LoDDigit:=N.Uint.DDigits[i]; N.Uint.DDigits[i]:=vlaDivQQ; end; // qq.QDigit:=N.Uint.DDigits[i]; // N.Uint.DDigits[i]:=vlaDivQQ; // if i>0 then begin // { делим дальше } // for i:=i-1 downto 0 do begin // qq.LoDDigit:=N.Uint.DDigits[i]; // N.Uint.DDigits[i]:=vlaDivQQ; // end; // end; i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (N.Uint.Digits[i]=0) then Dec(i); N.MostSignificantDigitIndex:=i; end; end; procedure vlaDivFast(var N:tVeryLongUIntEx; D:tDoubleDigit); overload; var dd:tDDigit; //qq:tQuadDigit; // function localDivQQ:tDDigit; register; // asm // mov edx,qq.HiDDigit // mov eax,qq.LoDDigit // div D.DDigit // mov qq.HiDDigit, edx // остаток деления // end; function localDivQQ1(lo:tDDigit):tDDigit; register; asm mov edx,dd div D.DDigit mov dd, edx // остаток деления end; var i:tDigitIndex; begin // qq.HiDDigit:=0; dd:=0; { делим } for i:=(N.MostSignificantDigitIndex div 2) downto 0 do begin // qq.LoDDigit:=N.Uint.DDigits[i]; N.Uint.DDigits[i]:=localDivQQ1(N.Uint.DDigits[i]); end; { поделили } i:=N.MostSignificantDigitIndex; if (i>0) AND (N.Uint.Digits[i]=0) then begin Dec(i); if (i>0) AND (N.Uint.Digits[i]=0) then Dec(i); N.MostSignificantDigitIndex:=i; end; end; (* asm push edi mov edx,N movzx ecx,tVeryLongUIntEx([edx]).MostSignificantDigitIndex shr ecx,1 shl ecx,2 lea edi,tVeryLongUIntEx([edx][ecx]).Uint.Digits shr ecx,2 inc ecx std xor edx,edx @loop1: mov eax,[edi] div D stosd // loopd @loop1 dec ecx jnz @loop1 cld pop edi end; //////////////////////////////////////// asm push edi push ebx mov edx,N movzx ecx,tVeryLongUIntEx([edx]).MostSignificantDigitIndex shr ecx,1 shl ecx,2 lea edi,tVeryLongUIntEx([edx][ecx]).Uint.Digits shr ecx,2 inc ecx mov ebx,D std xor edx,edx @loop1: mov eax,[edi] div ebx stosd // loopd @loop1 dec ecx jnz @loop1 cld pop ebx pop edi end; function vlaMulFast(var X:tVeryLongUIntEx; D:tDoubleDigit):tDDigit; var dd:tDDigit; function localMul(lo32:tDDigit):tDDigit; register; asm mul D add eax, dd adc edx, 0 mov dd,edx end; var i:tDigitIndex; //qq:tQuadDigit; begin if D.DDigit>0 then begin // qq.HiDDigit:=0; dd:=0; for i:=0 to (X.MostSignificantDigitIndex div 2) do begin if X.Uint.DDigits[i]>0 then begin // qq.QDigit:=tQDigit(X.Uint.DDigits[i])*D.DDigit+qq.HiDDigit; // X.Uint.DDigits[i]:=qq.LoDDigit; X.Uint.DDigits[i]:=localMul(X.Uint.DDigits[i]); end else begin // X.Uint.DDigits[i]:=qq.HiDDigit; // qq.HiDDigit:=0; X.Uint.DDigits[i]:=dd; dd:=0; end; end; i:=X.MostSignificantDigitIndex; if i0) then i:=i+2 else if (X.Uint.Digits[i+1]>0) then i:=i+1; end else if X.Uint.Digits[i+1]>0 then begin i:=i+1; end; X.MostSignificantDigitIndex:=i; Result:=0; end else begin // Result:=qq.HiDDigit; Result:=dd; end; end else begin vlaZeroTillMSD(X); Result:=0; end; end; procedure vlaShiftBitsLeft(var X:tVeryLongUIntEx; N:word); var i, j, k:tDigitIndex; dd:tDoubleDigit; begin if N=0 then begin end else if N>=cMaxDigitsCount*16 then begin vlaZeroTillMSD(X); end else begin i:=N div 16; if i>0 then vlaShiftDigitsLeft(X, i); i:=N-16*i; if i>0 then begin j:=X.MostSignificantDigitIndex; if j=(cMaxDigitsCount-1) then X.Uint.Digits[j]:=X.Uint.Digits[j] shl i else begin dd.DDigit:=(tDDigit(X.Uint.Digits[j]) shl i); X.Uint.Digits[j+1]:=dd.HiDigit; X.Uint.Digits[j]:=dd.LoDigit; if dd.HiDigit>0 then X.MostSignificantDigitIndex:=j+1; end; if j>0 then for k:=j-1 downto 0 do begin dd.DDigit:=(tDDigit(X.Uint.Digits[k]) shl i); X.Uint.Digits[k+1]:=X.Uint.Digits[k+1] or dd.HiDigit; X.Uint.Digits[k]:=dd.LoDigit; end; end; end; end; procedure vlaShiftBitsLeft(var X:tVeryLongUIntEx; N:word); var i, j, k:tDigitIndex; //dd,dd1:tDoubleDigit; begin if N=0 then begin end else if N>=cMaxDigitsCount*16 then begin vlaZeroTillMSD(X); end else begin i:=N div 16; if i>0 then vlaShiftDigitsLeft(X, i); i:=N-16*i; if i>0 then begin j:=X.MostSignificantDigitIndex; if j=(cMaxDigitsCount-1) then X.Uint.Digits[j]:=X.Uint.Digits[j] shl i else begin // dd.DDigit:=(tDDigit(X.Uint.Digits[j]) shl i); // X.Uint.Digits[j+1]:=dd.HiDigit; // X.Uint.Digits[j]:=dd.LoDigit; // if dd.HiDigit>0 then // X.MostSignificantDigitIndex:=j+1; pDoubleDigit(@X.Uint.Digits[j])^.DDigit:=(tDDigit(X.Uint.Digits[j]) shl i); if X.Uint.Digits[j+1]>0 then X.MostSignificantDigitIndex:=j+1; end; if j>0 then for k:=j-1 downto 0 do begin pDoubleDigit(@X.Uint.Digits[k])^.DDigit:= (tDDigit(X.Uint.Digits[k]) shl i) or (tDDigit(X.Uint.Digits[k+1]) shl 16); // dd.DDigit:=(tDDigit(X.Uint.Digits[k]) shl i); // X.Uint.Digits[k+1]:=X.Uint.Digits[k+1] or dd.HiDigit; // X.Uint.Digits[k]:=dd.LoDigit; end; end; end; end; procedure vlaShiftBitsRight(var X:tVeryLongUIntEx; N:word); var i, j, k:tDigitIndex; dd:tDoubleDigit; begin if N=0 then begin end else if N>=(X.MostSignificantDigitIndex+1)*16 then begin vlaZeroTillMSD(X); end else begin i:=N div 16; j:=X.MostSignificantDigitIndex; if i>0 then vlaShiftDigitsRight_(X.Uint, i, j); j:=j-i; i:=N-16*i; if i>0 then begin X.Uint.Digit0:=X.Uint.Digit0 shr i; i:=16-i; for k:=1 to j do begin dd.DDigit:=(tDDigit(X.Uint.Digits[k]) shl i); X.Uint.Digits[k-1]:=X.Uint.Digits[k-1] or dd.LoDigit; X.Uint.Digits[k]:=dd.HiDigit; end; end; if (X.Uint.Digits[j]=0) and (j>0) then X.MostSignificantDigitIndex:=j-1 else X.MostSignificantDigitIndex:=j; end; end; procedure vlaShiftBitsLeft(var X:tVeryLongUIntEx; N:word); var i, j:tDigitIndex; begin if N=0 then begin end else if N>=cMaxDigitsCount*16 then begin vlaZeroTillMSD(X); end else begin i:=N div 16; if i>0 then vlaShiftDigitsLeft(X, i); i:=N-16*i; if i>0 then begin j:=X.MostSignificantDigitIndex; if j=(cMaxDigitsCount-1) then begin X.Uint.Digits[j]:=X.Uint.Digits[j] shl i; // X.MostSignificantDigitIndex:=vlaMostSignificantDigitIndex(X.Uint); end else begin for j:=j downto 0 do begin pDDigit(@X.Uint.Digits[j])^:=(tDDigit(X.Uint.Digits[j]) shl i) or (tDDigit(X.Uint.Digits[j+1]) shl 16); end; // pDDigit(@X.Uint.Digits[j])^:=(tDDigit(X.Uint.Digits[j]) shl i); if X.Uint.Digits[j+1]>0 then X.MostSignificantDigitIndex:=j+1; end; // if j>0 then // for j:=j-1 downto 0 do begin // pDDigit(@X.Uint.Digits[j])^:=(tDDigit(X.Uint.Digits[j]) shl i) or (tDDigit(X.Uint.Digits[j+1]) shl 16); // end; end; end; end; function vlaSub(var X:tVeryLongUIntEx; const Y:tVeryLongUIntEx):tDigit; overload; var i,j:tDigitIndex; dd:tDoubleDigit; begin { переменная в ДВЕ цифры под вычитание с заемом из старшего разряда } dd.DDigit:=0; j:=Y.MostSignificantDigitIndex; { для каждой цифры, начиная с младших разрядов } for i:=0 to j do begin if (dd.HiDigit<>Y.Uint.Digits[i]) then begin { вычитаем соотв. цифры X и Y (x.Digits[i]-y.Digits[i]), и добавляем результат переноса с предыд. вычитания (dd.HiDigit)} dd.LoDigit:=X.Uint.Digits[i]; dd.DDigit:=dd.DDigit-Y.Uint.Digits[i]+dd.HiDigit; { записываем полученную цифру в результат } X.Uint.Digits[i]:=dd.LoDigit; end; end; if (dd.HiDigit>0) and (jj) then begin if X.Uint.Digits[i]=0 then Dec(X.MostSignificantDigitIndex); end else begin vlaMostSignificantDigitIndex(X); end; { возвращаем результат переноса с последнего вычитания (dd.HiDigit) } Result:=dd.HiDigit; end; function localSub(x32,y32:tDigit):tDigit; register; asm mov ecx,dd mov cx,ax mov eax,ecx shr eax, 16 movzx edx,dx sub ecx,edx add eax,ecx mov dd,eax end; *)