/*     
      SCOL ENVIRONMENT . Magma 1.0 . 1997 . Sylvain HUET

      gestion de grands nombres
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bignum.h"
#include "include/cpu_detect.h"
#include "include/kernel.h"
#include "macros.h"
#include "loadpak.h"
#include "listlab.h"


#define SIZEBV (SIZEV+1)/2


static int BigFromString(mmachine m)
{
	SEPTR s;

	if (MMget(m,0)==NIL) return 0;
	s = MMmalloc(m,SIZEBV,TYPEBUF); 

	if (s==NIL) return MERRMEM;

	CRfromString( MMstartstr(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,s), MMsizestr(m,SEW2P(MMget(m,0))) );

    MMset(m,0,SEP2W(s));
    return 0;
}




static int BigToString(mmachine m)
{
	SEPTR s;
	int l,sl,n;
	
	if (MMget(m,0)==NIL) return 0;
	
	l=SIZEV*2;
    sl=(l+4)>>2;

    s=MMmalloc(m,sl+1,TYPEBUF);
	if (s==NIL) return MERRMEM;

	CRtoString(MMstartstr(m,s),(int16*)MMstart(m,MMget(m,0)>>1),&n);
    MMsetsizestr(m,s,n);

    MMset(m,0,SEP2W(s));
    return 0;
}



static int BigToStringn(mmachine m)
{
	SEPTR s ;
	SEINT n ;
	int l,sl ;

	n = SEW2I(MMpull(m));
	if ( (n<0) || (n>=SIZEV*2) || (MMget(m,0)==NIL) ) { MMset(m,0,NIL); return 0; }

	l=SIZEV*2;
    sl=(l+4)>>2;

    s = MMmalloc(m,sl+1,TYPEBUF);
	if (s==NIL) return MERRMEM;

	CRtoStringn( MMstartstr(m,s), (int16*)MMstart(m,SEW2P(MMget(m,0))), n );
    MMsetsizestr( m, s, n ) ;

    MMset(m,0,SEP2W(s));
    return 0;
}



static int BigFromAsc(mmachine m)
{
	SEPTR s;

	if (MMget(m,0)==NIL) return 0;

	s = MMmalloc(m,SIZEBV,TYPEBUF);
	if (s==NIL) return MERRMEM;

	if( CRfromAsc( MMstartstr( m, SEW2P(MMget(m,0))), (int16*)MMstart(m,s) ) )
	{
		MMset(m,0,NIL);
		return 0;
	}

    MMset(m,0,SEP2W(s));
    return 0;
}



static int BigToAsc(mmachine m)
{
	SEPTR s ;
	int l,sl;

	if (MMget(m,0)==NIL) return 0;

	l=SIZEV*4;
    sl=(l+4)>>2;

    s = MMmalloc(m,sl+1,TYPEBUF); 
	if (s==NIL) return MERRMEM;

	CRtoAsc( MMstartstr(m,s), (int16*)MMstart(m,SEW2P(MMget(m,0)) ) );
    MMsetsizestr(m,s,strlen(MMstartstr(m,s)));

	MMset(m,0,SEP2W(s));
    return 0;
}




static int BigFromDec(mmachine m)
{
	SEPTR s;

	if (MMget(m,0)==NIL) return 0;

	s = MMmalloc(m,SIZEBV,TYPEBUF); 
	if (s==NIL) return MERRMEM;

	if( CRfromDec( MMstartstr(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,s)) )
	{
		MMset(m,0,NIL);
		return 0;
	}

    MMset(m,0,SEP2W(s));
    return 0;
}






static int BigToDec(mmachine m)
{
	char buf[128];

	if (MMget(m,0)==NIL) return 0;
	CRtoDec((int16*)MMstart(m,SEW2P(MMget(m,0))),buf);

	MMpull(m);
	return Mpushstrbloc(m,buf);
}




static int BigRand(mmachine m)
{
	SEPTR s ;

	s = MMmalloc(m,SIZEBV,TYPEBUF); if(s==NIL) return MERRMEM;
	CRrand((int16*)MMstart(m,s));

    return MMpush(m,SEP2W(s));
}




static int BigNul(mmachine m)
{
	SEPTR r = MMmalloc(m,SIZEBV,TYPEBUF) ; 
	if (r==NIL) return MERRMEM;
	
	CRnul( (int16*)MMstart(m,r) );
    
	return MMpush(m,SEP2W(r));
}


static int BigFromInt(mmachine m)
{
	SEPTR s;

	if (MMget(m,0)==NIL) return 0;

	s=MMmalloc(m,SIZEBV,TYPEBUF);
	if (s==NIL) return MERRMEM;

	CRfromInt( SEW2I(MMget(m,0)), (int16*)MMstart(m,s) );

	MMset(m,0,SEP2W(s));
    return 0;
}






static int BigToInt(mmachine m)
{
	SEINT i;

	if (MMget(m,0)==NIL) return 0;
	i = CRtoInt((int16*)MMstart(m,SEW2P(MMget(m,0))));

	MMset(m,0,SEI2W(i));
    return 0;
}




static int BigPrimal(mmachine m)
{
	SEPTR s;
	SEINT i;

	if ( (i=MMget(m,0))==NIL ) return 0;

	s = MMmalloc(m,SIZEBV,TYPEBUF);
	if (s==NIL) return MERRMEM;

	if( CRfindPrimal( (int16*)MMstart(m,s), SEW2I(i) ) )
	{
		MMset(m,0,NIL);
		return 0;
	}

    MMset(m,0,SEP2W(s));
    return 0;
}



static int BigAdd(mmachine m)
{
	SEPTR r ;

	if( MMget(m,0)==NIL || MMget(m,1)==NIL ) { MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }

	r = MMmalloc(m,SIZEBV,TYPEBUF); 
	if ( r==NIL ) return MERRMEM;
	
	CRadd(	 (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,r) ) ;

	MMpull(m) ;
	MMset(m,0,SEP2W(r));
    return 0;
}



static int BigSub(mmachine m)
{
	SEPTR r ;

	if( MMget(m,0)==NIL || MMget(m,1)==NIL ) { MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }

	r = MMmalloc(m,SIZEBV,TYPEBUF); 
	if ( r==NIL ) return MERRMEM;
	
	CRsub(	(int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,r) ) ;

	MMpull(m) ;
	MMset(m,0,SEP2W(r));
    return 0;
}




static int BigNeg(mmachine m)
{
	SEPTR r ;

	if ( MMget(m,0)==NIL ) return 0 ;

	r = MMmalloc(m,SIZEBV,TYPEBUF) ; 
	if( r==NIL ) return MERRMEM;

	CRneg( (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,r) ) ;

	MMset(m,0,SEP2W(r)) ;
    return 0;
}




static int BigXor(mmachine m)
{
	SEPTR r ;

	if( MMget(m,0)==NIL || MMget(m,1)==NIL ) {	MMpull(m); MMset(m,0,NIL) ; return 0 ; }

	r = MMmalloc(m,SIZEBV,TYPEBUF); 
	if ( r==NIL ) return MERRMEM;
	
	CRxor(	(int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,r) ) ;

	MMpull(m);
	MMset(m,0,SEP2W(r));
    return 0;
}




static int BigMod(mmachine m)
{
	SEPTR buff ;

	if ( MMget(m,0)==NIL || MMget(m,1)==NIL ) {	MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }
	
	buff = MMmalloc(m,SIZEBV,TYPEBUF); 
	if( buff==NIL ) return MERRMEM;

	CRmodn( (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,buff) ) ;

	MMpull(m) ;
	MMset(m,0,SEP2W(buff));
    return 0;
}



static int BigMuln(mmachine m)
{
	SEPTR s;

	if ( (MMget(m,0)==NIL) || (MMget(m,1)==NIL) || (MMget(m,2)==NIL) )
	{
		MMpull(m); MMpull(m);
		MMset(m,0,NIL);
		return 0;
	}

	s = MMmalloc(m,SIZEBV,TYPEBUF); if(s==NIL) return MERRMEM;

	CRmuln( (int16*)MMstart(m,SEW2P(MMget(m,2))), (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,s) ) ;

    MMpull(m); MMpull(m);
	MMset(m,0,SEP2W(s));

    return 0;
}



static int BigExpn(mmachine m)
{
	SEPTR s;

	if ( (MMget(m,0)==NIL) || (MMget(m,1)==NIL) || (MMget(m,2)==NIL) )
	{
		MMpull(m); MMpull(m);
		MMset(m,0,NIL);
		return 0;
	}

	s = MMmalloc(m,SIZEBV,TYPEBUF);
	if(s==NIL) return MERRMEM;

	CRexpn( (int16*)MMstart(m,SEW2P(MMget(m,2))), (int16*)MMstart(m,SEW2P(MMget(m,1))),
	         (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,s) );

    MMpull(m); MMpull(m);
	MMset(m,0,SEP2W(s));
    return 0;
}




static int BigInvn(mmachine m)
{
	SEPTR s;

	if( (MMget(m,0)==NIL) || (MMget(m,1)==NIL) )
	{
		MMpull(m);
		MMset(m,0,NIL);
		return 0;
	}

	s = MMmalloc(m,SIZEBV,TYPEBUF);
	if (s==NIL) return MERRMEM;

	CRinv( (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,s) );

    MMpull(m);
	MMset(m,0,SEP2W(s));
    return 0;
}






static int BigDiv(mmachine m)
{
	SEPTR r ;

	if( MMget(m,0)==NIL || MMget(m,1)==NIL ) { MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }

	r = MMmalloc(m,SIZEBV,TYPEBUF) ; 
	if( r==NIL ) return MERRMEM;

	if( CRdiv( (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,r) ) )
	{
		MMpull(m) ; 
		MMset(m,0,NIL) ; 
		return 0 ; 
	}
	
	MMpull(m) ;
	MMset(m,0,SEP2W(r)) ; 
    return 0;
}





static int BigMul(mmachine m)
{
	SEPTR r ;

	if( MMget(m,0)==NIL || MMget(m,1)==NIL ) { MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }

	r = MMmalloc(m,SIZEBV,TYPEBUF) ; 
	if( r==NIL ) return MERRMEM;

	CRmul( (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,r) ) ;

	MMpull(m) ;
	MMset(m,0,SEP2W(r));
    return 0;
}




static int BigPgcd(mmachine m)
{
	SEPTR r ;

	if( MMget(m,0)==NIL || MMget(m,1)==NIL ) { MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }

	r = MMmalloc(m,SIZEBV,TYPEBUF) ; 
	if(r==NIL) return MERRMEM;

	CRpgcd( (int16*)MMstart(m,SEW2P(MMget(m,0))), (int16*)MMstart(m,SEW2P(MMget(m,1))), (int16*)MMstart(m,r) ) ;

	MMpull(m) ;
	MMset(m,0,SEP2W(r));
    return 0;
}



static int BigCmp(mmachine m)
{
	SEINT a, b ;
	b = MMpull( m ) ;
	a = MMget(m,0) ;

	if( a==NIL || b==NIL ) { MMpull(m) ; MMset(m,0,NIL) ; return 0 ; }

	MMset(m,0,SEI2W( CRcmp( (int16*)MMstart(m,SEW2P(a)), (int16*)MMstart(m,SEW2P(b)) ) )) ;

	return 0 ;
}



static int BigRandBuf(char *buf)
{
	int i;

	for(i=0;i<SIZEV*2;i++) buf[i]=rand();

	return 0;
}




static int BigListFromString(mmachine m)
{
	SEINT n ;
	SEPTR s ;

	int ls,pp,nb,check,k,i;
	char buf[SIZEV*2+1];

	n = SEW2I(MMpull(m));
	if ((n<1)||(n>=SIZEV*2)||(MMget(m,0)==NIL)) { MMset(m,0,NIL); return 0; }

	ls=MMsizestr(m,SEW2P(MMget(m,0)));
	i=nb=0;
	pp=MMgetPP(m);
	while(i+n<=ls)
	{
		s=MMmalloc(m,SIZEBV,TYPEBUF); if (s==NIL) return MERRMEM;
		CRfromString(MMstartstr(m,SEW2P(MMgetbase(m,pp,0)))+i,(int16*)MMstart(m,s),n);
		if (MMpush(m,SEP2W(s))) return MERRMEM;
		i+=n;
		nb++;
	};
	check=ls-i;
	if (check)
	{
		BigRandBuf(buf);
		memcpy(buf,MMstartstr(m,SEW2P(MMgetbase(m,pp,0)))+i,check);
		s=MMmalloc(m,SIZEBV,TYPEBUF); if (s==NIL) return MERRMEM;
		CRfromString(buf,(int16*)MMstart(m,s),n);
		if (MMpush(m,SEP2W(s))) return MERRMEM;
		nb++;
	}
	BigRandBuf(buf);
	if (check==0) buf[0]=0;
	else buf[0]=n-check;
	s=MMmalloc(m,SIZEBV,TYPEBUF); if (s==NIL) return MERRMEM;
	CRfromString(buf,(int16*)MMstart(m,s),n);
	if (MMpush(m,SEP2W(s))) return MERRMEM;
	nb++;
	if (MMpush(m,NIL)) return 0;
	for(i=0;i<nb;i++)
	{
		if (MMpush(m,SEI2W(2))) return MERRMEM;
		if (k=MBdeftab(m)) return k;
	}

	MMset(m,1,MMget(m,0));
	MMpull(m);
	return 0;
}



static int BigListToString(mmachine m)
{
	SEINT n ;
	SEPTR s,p ;
	int l,q,sl;
	char *c;

	n = SEW2I(MMpull(m));
	if ((n<1)||(n>=SIZEV*2)||(MMget(m,0)==NIL))
	{
		MMset(m,0,NIL);
		return 0;
	}

	p = SEW2P(MMget(m,0));
	l=0;
	while(p!=NIL)
	{
		q = SEW2P(MMfetch(m,p,OFFLNEXT));
		if (q!=NIL) l+=n;
		else
		{
			s=*(int16*)MMstart(m,SEW2P(MMfetch(m,p,0)));
			s&=255;
			l-=s;
		}
		p=q;
	}
    sl=(l+4+n)>>2;
    s=MMmalloc(m,sl+1,TYPEBUF); if (s==NIL) return MERRMEM;
    MMsetsizestr(m,s,l);

	c=MMstartstr(m,s);
	p=MMget(m,0)>>1;
	while(p!=NIL)
	{
		q = SEW2P(MMfetch(m,p,OFFLNEXT));
		if (q!=NIL)
		{
			CRtoStringn(c,(int16*)MMstart(m,SEW2P(MMfetch(m,p,OFFLVAL))),n);
			c+=n;
		}
		p=q;
	}
	c = MMstartstr(m,s);
	c[l]=0;

	MMset(m,0,SEP2W(s));
	return 0;
}




static int BigTime(mmachine m)
{
	SEPTR s;

	s=MMmalloc(m,SIZEBV,TYPEBUF); 
	if (s==NIL) return MERRMEM;

	CRtime((int16*)MMstart(m,s));

    return MMpush(m,SEP2W(s));
}


static NativeDefinition bignum[] = {	
	{ "BigN",				TYPTYPE,	NULL,							NULL				},	
	{ "BigFromString",		1,			"fun [S] BigN",					BigFromString		},
	{ "BigToString",		1,			"fun [BigN] S",					BigToString			},
	{ "BigFromAsc",			1,			"fun [S] BigN",					BigFromAsc			},
	{ "BigToAsc",			1,			"fun [BigN] S",					BigToAsc			},
	{ "BigRand",			0,			"fun [] BigN",					BigRand				},
	{ "BigPrimal",			1,			"fun [I] BigN",					BigPrimal			},
	{ "BigAdd",				2,			"fun [BigN BigN] BigN",			BigAdd				},
	{ "BigSub",				2,			"fun [BigN BigN] BigN",			BigSub				},
	{ "BigXor",				2,			"fun [BigN BigN] BigN",			BigXor				},
	{ "BigMod",				2,			"fun [BigN BigN] BigN",			BigMod				},
	{ "BigMuln",			3,			"fun [BigN BigN BigN] BigN",	BigMuln				},
	{ "BigExpn",			3,			"fun [BigN BigN BigN] BigN",	BigExpn				},
	{ "BigInvn",			2,			"fun [BigN BigN] BigN",			BigInvn				},
	{ "BigDiv",				2,			"fun [BigN BigN] BigN",			BigDiv				},
	{ "BigMul",				2,			"fun [BigN BigN] BigN",			BigMul				},
	{ "BigPgcd",			2,			"fun [BigN BigN] BigN",			BigPgcd				},
	{ "BigCmp",				2,			"fun [BigN BigN] I",			BigCmp				},
	{ "BigToStringn",		2,			"fun [BigN I] S",				BigToStringn		},
	{ "BigListToString",	2,			"fun [[BigN r1] I] S",			BigListToString		},
	{ "BigListFromString",	2,			"fun [S I] [BigN r1]",			BigListFromString	},
	{ "BigNul",				0,			"fun [] BigN",					BigNul				},
	{ "BigFromInt",			1,			"fun [I] BigN",					BigFromInt			},
	{ "BigToInt",			1,			"fun [BigN] I",					BigToInt			},
	{ "BigNeg",				1,			"fun [BigN] BigN",				BigNeg				},
	{ "BigFromDec",			1,			"fun [S] BigN",					BigFromDec			},
	{ "BigToDec",			1,			"fun [BigN] S",					BigToDec			},
	{ "BigTime",			0,			"fun [] BigN",					BigTime				},
};



int SCOLloadBN(mmachine m)
{
    return PKhardpak2(m, "BigNum", sizeof(bignum)/sizeof(bignum[0]), bignum);
}


