/***************************************************************************
*
Copyright 2013 CertiVox UK Ltd. *
*
This file is part of CertiVox MIRACL Crypto SDK. *
*
The CertiVox MIRACL Crypto SDK provides developers with an *
extensive and efficient set of cryptographic functions. *
For further information about its features and functionalities please *
refer to http://www.certivox.com *
*
* The CertiVox MIRACL Crypto SDK is free software: you can *
redistribute it and/or modify it under the terms of the *
GNU Affero General Public License as published by the *
Free Software Foundation, either version 3 of the License, *
or (at your option) any later version. *
*
* The CertiVox MIRACL Crypto SDK is distributed in the hope *
that it will be useful, but WITHOUT ANY WARRANTY; without even the *
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
See the GNU Affero General Public License for more details. *
*
* You should have received a copy of the GNU Affero General Public *
License along with CertiVox MIRACL Crypto SDK. *
If not, see . *
*
You can be released from the requirements of the license by purchasing *
a commercial license. Buying such a license is mandatory as soon as you *
develop commercial activities involving the CertiVox MIRACL Crypto SDK *
without disclosing the source code of your own applications, or shipping *
the CertiVox MIRACL Crypto SDK with a closed source product. *
*
***************************************************************************/
/*
* MIRACL arithmetic routines 2 - multiplying and dividing BIG NUMBERS.
* mrarth2.c
*
*/
#include "miracl.h"
#ifdef MR_FP
#include
#endif
#ifdef MR_WIN64
#include
#endif
/* If a number has more than this number of digits, then squaring is faster */
#define SQR_FASTER_THRESHOLD 5
mr_small normalise(_MIPD_ big x,big y)
{ /* normalise divisor */
mr_small norm,r;
#ifdef MR_FP
mr_small dres;
#endif
int len;
#ifdef MR_OS_THREADS
miracl *mr_mip=get_mip();
#endif
MR_IN(4)
if (x!=y) copy(x,y);
len=(int)(y->len&MR_OBITS);
#ifndef MR_SIMPLE_BASE
if (mr_mip->base==0)
{
#endif
#ifndef MR_NOFULLWIDTH
if ((r=y->w[len-1]+1)==0) norm=1;
#ifdef MR_NOASM
else norm=(mr_small)(((mr_large)1 << MIRACL)/r);
#else
else norm=muldvm((mr_small)1,(mr_small)0,r,&r);
#endif
if (norm!=1) mr_pmul(_MIPP_ y,norm,y);
#endif
#ifndef MR_SIMPLE_BASE
}
else
{
norm=MR_DIV(mr_mip->base,(mr_small)(y->w[len-1]+1));
if (norm!=1) mr_pmul(_MIPP_ y,norm,y);
}
#endif
MR_OUT
return norm;
}
void multiply(_MIPD_ big x,big y,big z)
{ /* multiply two big numbers: z=x.y */
int i,xl,yl,j,ti;
mr_small carry,*xg,*yg,*w0g;
#ifdef MR_ITANIUM
mr_small tm;
#endif
#ifdef MR_WIN64
mr_small tm,tr;
#endif
mr_lentype sz;
big w0;
#ifdef MR_NOASM
union doubleword dble;
mr_large dbled;
mr_large ldres;
#endif
#ifdef MR_OS_THREADS
miracl *mr_mip=get_mip();
#endif
if (mr_mip->ERNUM) return;
if (y->len==0 || x->len==0)
{
zero(z);
return;
}
if (x!=mr_mip->w5 && y!=mr_mip->w5 && z==mr_mip->w5) w0=mr_mip->w5;
else w0=mr_mip->w0; /* local pointer */
MR_IN(5)
#ifdef MR_FLASH
if (mr_notint(x) || mr_notint(y))
{
mr_berror(_MIPP_ MR_ERR_INT_OP);
MR_OUT
return;
}
#endif
sz=((x->len&MR_MSBIT)^(y->len&MR_MSBIT));
xl=(int)(x->len&MR_OBITS);
yl=(int)(y->len&MR_OBITS);
zero(w0);
if (mr_mip->check && xl+yl>mr_mip->nib)
{
mr_berror(_MIPP_ MR_ERR_OVERFLOW);
MR_OUT
return;
}
#ifndef MR_SIMPLE_BASE
if (mr_mip->base==0)
{
#endif
#ifndef MR_NOFULLWIDTH
xg=x->w; yg=y->w; w0g=w0->w;
if (x==y && xl>SQR_FASTER_THRESHOLD)
/* extra hassle make it not */
/* worth it for small numbers */
{ /* fast squaring */
for (i=0;iw[i]*x->w[j]+carry+w0->w[i+j];
w0->w[i+j]=dble.h[MR_BOT];
carry=dble.h[MR_TOP];
#else
muldvd2(x->w[i],x->w[j],&carry,&w0->w[i+j]);
#endif
}
w0->w[xl+i]=carry;
#endif
}
#ifdef INLINE_ASM
#if INLINE_ASM == 1
ASM mov cx,xl
ASM shl cx,1
#ifdef MR_LMM
ASM push ds
ASM push es
ASM les bx,DWORD PTR w0g
#else
ASM mov bx,w0g
#endif
tcl5:
#ifdef MR_LMM
ASM rcl WORD PTR es:[bx],1
#else
ASM rcl WORD PTR [bx],1
#endif
ASM inc bx
ASM inc bx
ASM loop tcl5
ASM cld
ASM mov cx,xl
#ifdef MR_LMM
ASM les di,DWORD PTR w0g
ASM lds si,DWORD PTR xg
#else
ASM mov di,w0g
ASM mov si,xg
#endif
ASM xor bx,bx
tcl7:
ASM lodsw
ASM mul ax
ASM add ax,bx
ASM adc dx,0
#ifdef MR_LMM
ASM add es:[di],ax
#else
ASM add [di],ax
#endif
ASM adc dx,0
ASM xor bx,bx
ASM inc di
ASM inc di
#ifdef MR_LMM
ASM add es:[di],dx
#else
ASM add [di],dx
#endif
ASM adc bx,0
ASM inc di
ASM inc di
ASM loop tcl7
#ifdef MR_LMM
ASM pop es
ASM pop ds
#endif
#endif
#if INLINE_ASM == 2
ASM mov cx,xl
ASM shl cx,1
#ifdef MR_LMM
ASM push ds
ASM push es
ASM les bx,DWORD PTR w0g
#else
ASM mov bx,w0g
#endif
tcl5:
#ifdef MR_LMM
ASM rcl DWORD PTR es:[bx],1
#else
ASM rcl DWORD PTR [bx],1
#endif
ASM inc bx
ASM inc bx
ASM inc bx
ASM inc bx
ASM loop tcl5
ASM cld
ASM mov cx,xl
#ifdef MR_LMM
ASM les di,DWORD PTR w0g
ASM lds si,DWORD PTR xg
#else
ASM mov di,w0g
ASM mov si,xg
#endif
ASM xor ebx,ebx
tcl7:
ASM lodsd
ASM mul eax
ASM add eax,ebx
ASM adc edx,0
#ifdef MR_LMM
ASM add es:[di],eax
#else
ASM add [di],eax
#endif
ASM adc edx,0
ASM xor ebx,ebx
ASM add di,4
#ifdef MR_LMM
ASM add es:[di],edx
#else
ASM add [di],edx
#endif
ASM adc ebx,0
ASM add di,4
ASM loop tcl7
#ifdef MR_LMM
ASM pop es
ASM pop ds
#endif
#endif
#if INLINE_ASM == 3
ASM mov ecx,xl
ASM shl ecx,1
ASM mov edi,w0g
tcl5:
ASM rcl DWORD PTR [edi],1
ASM inc edi
ASM inc edi
ASM inc edi
ASM inc edi
ASM loop tcl5
ASM mov ecx,xl
ASM mov esi,xg
ASM mov edi,w0g
ASM xor ebx,ebx
tcl7:
ASM mov eax,[esi]
ASM add esi,4
ASM mul eax
ASM add eax,ebx
ASM adc edx,0
ASM add [edi],eax
ASM adc edx,0
ASM xor ebx,ebx
ASM add edi,4
ASM add [edi],edx
ASM adc ebx,0
ASM add edi,4
ASM dec ecx
ASM jnz tcl7
#endif
#if INLINE_ASM == 4
ASM (
"movl %0,%%ecx\n"
"shll $1,%%ecx\n"
"movl %1,%%edi\n"
"tcl5:\n"
"rcll $1,(%%edi)\n"
"incl %%edi\n"
"incl %%edi\n"
"incl %%edi\n"
"incl %%edi\n"
"loop tcl5\n"
"movl %0,%%ecx\n"
"movl %2,%%esi\n"
"movl %1,%%edi\n"
"xorl %%ebx,%%ebx\n"
"tcl7:\n"
"movl (%%esi),%%eax\n"
"addl $4,%%esi\n"
"mull %%eax\n"
"addl %%ebx,%%eax\n"
"adcl $0,%%edx\n"
"addl %%eax,(%%edi)\n"
"adcl $0,%%edx\n"
"xorl %%ebx,%%ebx\n"
"addl $4,%%edi\n"
"addl %%edx,(%%edi)\n"
"adcl $0,%%ebx\n"
"addl $4,%%edi\n"
"decl %%ecx\n"
"jnz tcl7\n"
:
:"m"(xl),"m"(w0g),"m"(xg)
:"eax","edi","esi","ebx","ecx","edx","memory"
);
#endif
#endif
#ifndef INLINE_ASM
w0->len=xl+xl-1;
mr_padd(_MIPP_ w0,w0,w0); /* double it */
carry=0;
for (i=0;iw[i]*x->w[i]+carry+w0->w[ti];
w0->w[ti]=dble.h[MR_BOT];
carry=dble.h[MR_TOP];
#else
muldvd2(x->w[i],x->w[i],&carry,&w0->w[ti]);
#endif
w0->w[ti+1]+=carry;
if (w0->w[ti+1]w[i]*y->w[j]+carry+w0->w[i+j];
w0->w[i+j]=dble.h[MR_BOT];
carry=dble.h[MR_TOP];
#else
muldvd2(x->w[i],y->w[j],&carry,&w0->w[i+j]);
#endif
}
w0->w[yl+i]=carry;
#endif
}
#endif
#ifndef MR_SIMPLE_BASE
}
else
{
if (x==y && xl>SQR_FASTER_THRESHOLD)
{ /* squaring can be done nearly twice as fast */
for (i=0;iw[i]*x->w[j]+w0->w[i+j]+carry;
#ifdef MR_FP_ROUNDING
carry=(mr_small)MR_LROUND(dbled*mr_mip->inverse_base);
#else
#ifndef MR_FP
if (mr_mip->base==mr_mip->base2)
carry=(mr_small)(dbled>>mr_mip->lg2b);
else
#endif
carry=(mr_small)MR_LROUND(dbled/mr_mip->base);
#endif
w0->w[i+j]=(mr_small)(dbled-(mr_large)carry*mr_mip->base);
#else
#ifdef MR_FP_ROUNDING
carry=imuldiv(x->w[i],x->w[j],w0->w[i+j]+carry,mr_mip->base,mr_mip->inverse_base,&w0->w[i+j]);
#else
carry=muldiv(x->w[i],x->w[j],w0->w[i+j]+carry,mr_mip->base,&w0->w[i+j]);
#endif
#endif
}
w0->w[xl+i]=carry;
}
w0->len=xl+xl-1;
mr_padd(_MIPP_ w0,w0,w0); /* double it */
carry=0;
for (i=0;iw[i]*x->w[i]+w0->w[ti]+carry;
#ifdef MR_FP_ROUNDING
carry=(mr_small)MR_LROUND(dbled*mr_mip->inverse_base);
#else
#ifndef MR_FP
if (mr_mip->base==mr_mip->base2)
carry=(mr_small)(dbled>>mr_mip->lg2b);
else
#endif
carry=(mr_small)MR_LROUND(dbled/mr_mip->base);
#endif
w0->w[ti]=(mr_small)(dbled-(mr_large)carry*mr_mip->base);
#else
#ifdef MR_FP_ROUNDING
carry=imuldiv(x->w[i],x->w[i],w0->w[ti]+carry,mr_mip->base,mr_mip->inverse_base,&w0->w[ti]);
#else
carry=muldiv(x->w[i],x->w[i],w0->w[ti]+carry,mr_mip->base,&w0->w[ti]);
#endif
#endif
w0->w[ti+1]+=carry;
carry=0;
if (w0->w[ti+1]>=mr_mip->base)
{
carry=1;
w0->w[ti+1]-=mr_mip->base;
}
}
}
else for (i=0;iw[i]*y->w[j]+w0->w[i+j]+carry;
#ifdef MR_FP_ROUNDING
carry=(mr_small)MR_LROUND(dbled*mr_mip->inverse_base);
#else
#ifndef MR_FP
if (mr_mip->base==mr_mip->base2)
carry=(mr_small)(dbled>>mr_mip->lg2b);
else
#endif
carry=(mr_small)MR_LROUND(dbled/mr_mip->base);
#endif
w0->w[i+j]=(mr_small)(dbled-(mr_large)carry*mr_mip->base);
#else
#ifdef MR_FP_ROUNDING
carry=imuldiv(x->w[i],y->w[j],w0->w[i+j]+carry,mr_mip->base,mr_mip->inverse_base,&w0->w[i+j]);
#else
carry=muldiv(x->w[i],y->w[j],w0->w[i+j]+carry,mr_mip->base,&w0->w[i+j]);
#endif
#endif
}
w0->w[yl+i]=carry;
}
}
#endif
w0->len=(sz|(xl+yl)); /* set length and sign of result */
mr_lzero(w0);
copy(w0,z);
MR_OUT
}
void divide(_MIPD_ big x,big y,big z)
{ /* divide two big numbers z=x/y : x=x mod y *
* returns quotient only if divide(x,y,x) *
* returns remainder only if divide(x,y,y) */
mr_small carry,attemp,ldy,sdy,ra,r,d,tst,psum;
#ifdef MR_FP
mr_small dres;
#endif
mr_lentype sx,sy,sz;
mr_small borrow,dig,*w0g,*yg;
int i,k,m,x0,y0,w00;
big w0;
#ifdef MR_ITANIUM
mr_small tm;
#endif
#ifdef MR_WIN64
mr_small tm;
#endif
#ifdef MR_NOASM
union doubleword dble;
mr_large dbled;
mr_large ldres;
#endif
BOOL check;
#ifdef MR_OS_THREADS
miracl *mr_mip=get_mip();
#endif
if (mr_mip->ERNUM) return;
w0=mr_mip->w0;
MR_IN(6)
if (x==y) mr_berror(_MIPP_ MR_ERR_BAD_PARAMETERS);
#ifdef MR_FLASH
if (mr_notint(x) || mr_notint(y)) mr_berror(_MIPP_ MR_ERR_INT_OP);
#endif
if (y->len==0) mr_berror(_MIPP_ MR_ERR_DIV_BY_ZERO);
if (mr_mip->ERNUM)
{
MR_OUT
return;
}
sx=(x->len&MR_MSBIT); /* extract signs ... */
sy=(y->len&MR_MSBIT);
sz=(sx^sy);
x->len&=MR_OBITS; /* ... and force operands to positive */
y->len&=MR_OBITS;
x0=(int)x->len;
y0=(int)y->len;
copy(x,w0);
w00=(int)w0->len;
if (mr_mip->check && (w00-y0+1>mr_mip->nib))
{
mr_berror(_MIPP_ MR_ERR_OVERFLOW);
MR_OUT
return;
}
d=0;
if (x0==y0)
{
if (x0==1) /* special case - x and y are both mr_smalls */
{
d=MR_DIV(w0->w[0],y->w[0]);
w0->w[0]=MR_REMAIN(w0->w[0],y->w[0]);
mr_lzero(w0);
}
else if (MR_DIV(w0->w[x0-1],4)w[x0-1])
while (mr_compare(w0,y)>=0)
{ /* mr_small quotient - so do up to four subtracts instead */
mr_psub(_MIPP_ w0,y,w0);
d++;
}
}
if (mr_compare(w0,y)<0)
{ /* x less than y - so x becomes remainder */
if (x!=z) /* testing parameters */
{
copy(w0,x);
if (x->len!=0) x->len|=sx;
}
if (y!=z)
{
zero(z);
z->w[0]=d;
if (d>0) z->len=(sz|1);
}
y->len|=sy;
MR_OUT
return;
}
if (y0==1)
{ /* y is int - so use subdiv instead */
#ifdef MR_FP_ROUNDING
r=mr_sdiv(_MIPP_ w0,y->w[0],mr_invert(y->w[0]),w0);
#else
r=mr_sdiv(_MIPP_ w0,y->w[0],w0);
#endif
if (y!=z)
{
copy(w0,z);
z->len|=sz;
}
if (x!=z)
{
zero(x);
x->w[0]=r;
if (r>0) x->len=(sx|1);
}
y->len|=sy;
MR_OUT
return;
}
if (y!=z) zero(z);
d=normalise(_MIPP_ y,y);
check=mr_mip->check;
mr_mip->check=OFF;
#ifndef MR_SIMPLE_BASE
if (mr_mip->base==0)
{
#endif
#ifndef MR_NOFULLWIDTH
if (d!=1) mr_pmul(_MIPP_ w0,d,w0);
ldy=y->w[y0-1];
sdy=y->w[y0-2];
w0g=w0->w; yg=y->w;
for (k=w00-1;k>=y0-1;k--)
{ /* long division */
#ifdef INLINE_ASM
#if INLINE_ASM == 1
#ifdef MR_LMM
ASM push ds
ASM lds bx,DWORD PTR w0g
#else
ASM mov bx,w0g
#endif
ASM mov si,k
ASM shl si,1
ASM add bx,si
ASM mov dx,[bx+2]
ASM mov ax,[bx]
ASM cmp dx,ldy
ASM jne tcl8
ASM mov di,0xffff
ASM mov si,ax
ASM add si,ldy
ASM jc tcl12
ASM jmp tcl10
tcl8:
ASM div WORD PTR ldy
ASM mov di,ax
ASM mov si,dx
tcl10:
ASM mov ax,sdy
ASM mul di
ASM cmp dx,si
ASM jb tcl12
ASM jne tcl11
ASM cmp ax,[bx-2]
ASM jbe tcl12
tcl11:
ASM dec di
ASM add si,ldy
ASM jnc tcl10
tcl12:
ASM mov attemp,di
#ifdef MR_LMM
ASM pop ds
#endif
#endif
/* NOTE push and pop of esi/edi should not be necessary - Borland C bug *
* These pushes are needed here even if register variables are disabled */
#if INLINE_ASM == 2
ASM push esi
ASM push edi
#ifdef MR_LMM
ASM push ds
ASM lds bx,DWORD PTR w0g
#else
ASM mov bx,w0g
#endif
ASM mov si,k
ASM shl si,2
ASM add bx,si
ASM mov edx,[bx+4]
ASM mov eax,[bx]
ASM cmp edx,ldy
ASM jne tcl8
ASM mov edi,0xffffffff
ASM mov esi,eax
ASM add esi,ldy
ASM jc tcl12
ASM jmp tcl10
tcl8:
ASM div DWORD PTR ldy
ASM mov edi,eax
ASM mov esi,edx
tcl10:
ASM mov eax,sdy
ASM mul edi
ASM cmp edx,esi
ASM jb tcl12
ASM jne tcl11
ASM cmp eax,[bx-4]
ASM jbe tcl12
tcl11:
ASM dec edi
ASM add esi,ldy
ASM jnc tcl10
tcl12:
ASM mov attemp,edi
#ifdef MR_LMM
ASM pop ds
#endif
ASM pop edi
ASM pop esi
#endif
#if INLINE_ASM == 3
ASM push esi
ASM push edi
ASM mov ebx,w0g
ASM mov esi,k
ASM shl esi,2
ASM add ebx,esi
ASM mov edx,[ebx+4]
ASM mov eax,[ebx]
ASM cmp edx,ldy
ASM jne tcl8
ASM mov edi,0xffffffff
ASM mov esi,eax
ASM add esi,ldy
ASM jc tcl12
ASM jmp tcl10
tcl8:
ASM div DWORD PTR ldy
ASM mov edi,eax
ASM mov esi,edx
tcl10:
ASM mov eax,sdy
ASM mul edi
ASM cmp edx,esi
ASM jb tcl12
ASM jne tcl11
ASM cmp eax,[ebx-4]
ASM jbe tcl12
tcl11:
ASM dec edi
ASM add esi,ldy
ASM jnc tcl10
tcl12:
ASM mov attemp,edi
ASM pop edi
ASM pop esi
#endif
#if INLINE_ASM == 4
ASM (
"movl %1,%%ebx\n"
"movl %2,%%esi\n"
"shll $2,%%esi\n"
"addl %%esi,%%ebx\n"
"movl 4(%%ebx),%%edx\n"
"movl (%%ebx),%%eax\n"
"cmpl %3,%%edx\n"
"jne tcl8\n"
"movl $0xffffffff,%%edi\n"
"movl %%eax,%%esi\n"
"addl %3,%%esi\n"
"jc tcl12\n"
"jmp tcl10\n"
"tcl8:\n"
"divl %3\n"
"movl %%eax,%%edi\n"
"movl %%edx,%%esi\n"
"tcl10:\n"
"movl %4,%%eax\n"
"mull %%edi\n"
"cmpl %%esi,%%edx\n"
"jb tcl12\n"
"jne tcl11\n"
"cmpl -4(%%ebx),%%eax\n"
"jbe tcl12\n"
"tcl11:\n"
"decl %%edi\n"
"addl %3,%%esi\n"
"jnc tcl10\n"
"tcl12:\n"
"movl %%edi,%0\n"
:"=m"(attemp)
:"m"(w0g),"m"(k),"m"(ldy),"m"(sdy)
:"eax","edi","esi","ebx","ecx","edx","memory"
);
#endif
#endif
#ifndef INLINE_ASM
carry=0;
if (w0->w[k+1]==ldy) /* guess next quotient digit */
{
attemp=(mr_small)(-1);
ra=ldy+w0->w[k];
if (raw[k];
dble.h[MR_TOP]=w0->w[k+1];
attemp=(mr_small)(dble.d/ldy);
ra=(mr_small)(dble.d-(mr_large)attemp*ldy);
}
#else
else attemp=muldvm(w0->w[k+1],w0->w[k],ldy,&ra);
#endif
while (carry==0)
{
#ifdef MR_NOASM
dble.d=(mr_large)attemp*sdy;
r=dble.h[MR_BOT];
tst=dble.h[MR_TOP];
#else
tst=muldvd(sdy,attemp,(mr_small)0,&r);
#endif
if (tst< ra || (tst==ra && r<=w0->w[k-1])) break;
attemp--; /* refine guess */
ra+=ldy;
if (ra0)
{ /* do partial subtraction */
borrow=0;
/* inline - substitutes for loop below */
#ifdef INLINE_ASM
#if INLINE_ASM == 1
ASM cld
ASM mov cx,y0
ASM mov si,m
ASM shl si,1
ASM mov di,attemp
#ifdef MR_LMM
ASM push ds
ASM push es
ASM les bx,DWORD PTR w0g
ASM add bx,si
ASM sub bx,2
ASM lds si,DWORD PTR yg
#else
ASM mov bx,w0g
ASM add bx,si
ASM sub bx,2
ASM mov si,yg
#endif
ASM push bp
ASM xor bp,bp
tcl3:
ASM lodsw
ASM mul di
ASM add ax,bp
ASM adc dx,0
ASM inc bx
ASM inc bx
#ifdef MR_LMM
ASM sub es:[bx],ax
#else
ASM sub [bx],ax
#endif
ASM adc dx,0
ASM mov bp,dx
ASM loop tcl3
ASM mov ax,bp
ASM pop bp
#ifdef MR_LMM
ASM pop es
ASM pop ds
#endif
ASM mov borrow,ax
#endif
/* NOTE push and pop of esi/edi should not be necessary - Borland C bug *
* These pushes are needed here even if register variables are disabled */
#if INLINE_ASM == 2
ASM push esi
ASM push edi
ASM cld
ASM mov cx,y0
ASM mov si,m
ASM shl si,2
ASM mov edi,attemp
#ifdef MR_LMM
ASM push ds
ASM push es
ASM les bx,DWORD PTR w0g
ASM add bx,si
ASM sub bx,4
ASM lds si,DWORD PTR yg
#else
ASM mov bx,w0g
ASM add bx,si
ASM sub bx,4
ASM mov si,yg
#endif
ASM push ebp
ASM xor ebp,ebp
tcl3:
ASM lodsd
ASM mul edi
ASM add eax,ebp
ASM adc edx,0
ASM add bx,4
#ifdef MR_LMM
ASM sub es:[bx],eax
#else
ASM sub [bx],eax
#endif
ASM adc edx,0
ASM mov ebp,edx
ASM loop tcl3
ASM mov eax,ebp
ASM pop ebp
#ifdef MR_LMM
ASM pop es
ASM pop ds
#endif
ASM mov borrow,eax
ASM pop edi
ASM pop esi
#endif
#if INLINE_ASM == 3
ASM push esi
ASM push edi
ASM mov ecx,y0
ASM mov esi,m
ASM shl esi,2
ASM mov edi,attemp
ASM mov ebx,w0g
ASM add ebx,esi
ASM mov esi,yg
ASM sub ebx,esi
ASM sub ebx,4
ASM push ebp
ASM xor ebp,ebp
tcl3:
ASM mov eax,[esi]
ASM add esi,4
ASM mul edi
ASM add eax,ebp
ASM mov ebp,[esi+ebx]
ASM adc edx,0
ASM sub ebp,eax
ASM adc edx,0
ASM mov [esi+ebx],ebp
ASM dec ecx
ASM mov ebp,edx
ASM jnz tcl3
ASM mov eax,ebp
ASM pop ebp
ASM mov borrow,eax
ASM pop edi
ASM pop esi
#endif
#if INLINE_ASM == 4
ASM (
"movl %1,%%ecx\n"
"movl %2,%%esi\n"
"shll $2,%%esi\n"
"movl %3,%%edi\n"
"movl %4,%%ebx\n"
"addl %%esi,%%ebx\n"
"movl %5,%%esi\n"
"subl %%esi,%%ebx\n"
"subl $4,%%ebx\n"
"pushl %%ebp\n"
"xorl %%ebp,%%ebp\n"
"tcl3:\n"
"movl (%%esi),%%eax\n"
"addl $4,%%esi\n"
"mull %%edi\n"
"addl %%ebp,%%eax\n"
"movl (%%esi,%%ebx),%%ebp\n"
"adcl $0,%%edx\n"
"subl %%eax,%%ebp\n"
"adcl $0,%%edx\n"
"movl %%ebp,(%%esi,%%ebx)\n"
"decl %%ecx\n"
"movl %%edx,%%ebp\n"
"jnz tcl3\n"
"movl %%ebp,%%eax\n"
"popl %%ebp\n"
"movl %%eax,%0\n"
:"=m"(borrow)
:"m"(y0),"m"(m),"m"(attemp),"m"(w0g),"m"(yg)
:"eax","edi","esi","ebx","ecx","edx","memory"
);
#endif
#endif
#ifndef INLINE_ASM
for (i=0;iw[i]+borrow;
dig=dble.h[MR_BOT];
borrow=dble.h[MR_TOP];
#else
borrow=muldvd(attemp,y->w[i],borrow,&dig);
#endif
if (w0->w[m+i]w[m+i]-=dig;
}
#endif
if (w0->w[k+1]w[k+1]=0;
carry=0;
for (i=0;iw[m+i]+y->w[i]+carry;
if (psum>y->w[i]) carry=0;
if (psumw[i]) carry=1;
w0->w[m+i]=psum;
}
attemp--; /* ... and adjust guess */
}
else w0->w[k+1]-=borrow;
}
if (k==w00-1 && attemp==0) w00--;
else if (y!=z) z->w[m]=attemp;
}
#endif
#ifndef MR_SIMPLE_BASE
}
else
{ /* have to do it the hard way */
if (d!=1) mr_pmul(_MIPP_ w0,d,w0);
ldy=y->w[y0-1];
sdy=y->w[y0-2];
for (k=w00-1;k>=y0-1;k--)
{ /* long division */
if (w0->w[k+1]==ldy) /* guess next quotient digit */
{
attemp=mr_mip->base-1;
ra=ldy+w0->w[k];
}
#ifdef MR_NOASM
else
{
dbled=(mr_large)w0->w[k+1]*mr_mip->base+w0->w[k];
attemp=(mr_small)MR_LROUND(dbled/ldy);
ra=(mr_small)(dbled-(mr_large)attemp*ldy);
}
#else
else attemp=muldiv(w0->w[k+1],mr_mip->base,w0->w[k],ldy,&ra);
#endif
while (rabase)
{
#ifdef MR_NOASM
dbled=(mr_large)sdy*attemp;
#ifdef MR_FP_ROUNDING
tst=(mr_small)MR_LROUND(dbled*mr_mip->inverse_base);
#else
#ifndef MR_FP
if (mr_mip->base==mr_mip->base2)
tst=(mr_small)(dbled>>mr_mip->lg2b);
else
#endif
tst=(mr_small)MR_LROUND(dbled/mr_mip->base);
#endif
r=(mr_small)(dbled-(mr_large)tst*mr_mip->base);
#else
#ifdef MR_FP_ROUNDING
tst=imuldiv(sdy,attemp,(mr_small)0,mr_mip->base,mr_mip->inverse_base,&r);
#else
tst=muldiv(sdy,attemp,(mr_small)0,mr_mip->base,&r);
#endif
#endif
if (tst< ra || (tst==ra && r<=w0->w[k-1])) break;
attemp--; /* refine guess */
ra+=ldy;
}
m=k-y0+1;
if (attemp>0)
{ /* do partial subtraction */
borrow=0;
for (i=0;iw[i]+borrow;
#ifdef MR_FP_ROUNDING
borrow=(mr_small)MR_LROUND(dbled*mr_mip->inverse_base);
#else
#ifndef MR_FP
if (mr_mip->base==mr_mip->base2)
borrow=(mr_small)(dbled>>mr_mip->lg2b);
else
#endif
borrow=(mr_small)MR_LROUND(dbled/mr_mip->base);
#endif
dig=(mr_small)(dbled-(mr_large)borrow*mr_mip->base);
#else
#ifdef MR_FP_ROUNDING
borrow=imuldiv(attemp,y->w[i],borrow,mr_mip->base,mr_mip->inverse_base,&dig);
#else
borrow=muldiv(attemp,y->w[i],borrow,mr_mip->base,&dig);
#endif
#endif
if (w0->w[m+i]w[m+i]+=(mr_mip->base-dig);
}
else w0->w[m+i]-=dig;
}
if (w0->w[k+1]w[k+1]=0;
carry=0;
for (i=0;iw[m+i]+y->w[i]+carry;
carry=0;
if (psum>=mr_mip->base)
{
carry=1;
psum-=mr_mip->base;
}
w0->w[m+i]=psum;
}
attemp--; /* ... and adjust guess */
}
else
w0->w[k+1]-=borrow;
}
if (k==w00-1 && attemp==0) w00--;
else if (y!=z) z->w[m]=attemp;
}
}
#endif
if (y!=z) z->len=((w00-y0+1)|sz); /* set sign and length of result */
w0->len=y0;
mr_lzero(y);
mr_lzero(z);
if (x!=z)
{
mr_lzero(w0);
#ifdef MR_FP_ROUNDING
if (d!=1) mr_sdiv(_MIPP_ w0,d,mr_invert(d),x);
#else
if (d!=1) mr_sdiv(_MIPP_ w0,d,x);
#endif
else copy(w0,x);
if (x->len!=0) x->len|=sx;
}
#ifdef MR_FP_ROUNDING
if (d!=1) mr_sdiv(_MIPP_ y,d,mr_invert(d),y);
#else
if (d!=1) mr_sdiv(_MIPP_ y,d,y);
#endif
y->len|=sy;
mr_mip->check=check;
MR_OUT
}
BOOL divisible(_MIPD_ big x,big y)
{ /* returns y|x, that is TRUE if y divides x exactly */
#ifdef MR_OS_THREADS
miracl *mr_mip=get_mip();
#endif
if (mr_mip->ERNUM) return FALSE;
MR_IN(87)
copy (x,mr_mip->w0);
divide(_MIPP_ mr_mip->w0,y,y);
MR_OUT
if (size(mr_mip->w0)==0) return TRUE;
else return FALSE;
}
void mad(_MIPD_ big x,big y,big z,big w,big q,big r)
{ /* Multiply, Add and Divide; q=(x*y+z)/w remainder r *
* returns remainder only if w=q, quotient only if q=r *
* add done only if x, y and z are distinct. */
#ifdef MR_OS_THREADS
miracl *mr_mip=get_mip();
#endif
BOOL check;
if (mr_mip->ERNUM) return;
MR_IN(24)
if (w==r)
{
mr_berror(_MIPP_ MR_ERR_BAD_PARAMETERS);
MR_OUT
return;
}
check=mr_mip->check;
mr_mip->check=OFF; /* turn off some error checks */
multiply(_MIPP_ x,y,mr_mip->w0);
if (x!=z && y!=z) add(_MIPP_ mr_mip->w0,z,mr_mip->w0);
divide(_MIPP_ mr_mip->w0,w,q);
if (q!=r) copy(mr_mip->w0,r);
mr_mip->check=check;
MR_OUT
}