/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss or reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <nanu-nanu.h>

/*
*	This file contains the C equivalents of the various assembly
*	routines that the package uses. Depending on the definitions
*	in the header, they may or may not be compiled in. If you want
*	to port the package to a new target these are the functions
*	you should realise in assembly to make things fast.
*
*	If a function argument is in capitals, then it represents a long
*	(multi-word) number. Lowercase arguments are normal 32 or 64 bit
*	numbers. The multiword numbers are in little-endian word order.
*/

/****************************************************************************/
/*					Routines that are always in C							*/
/****************************************************************************/

/*
*	Fill the integer array P[ l ] with 0
*/

void	_bc_NanuZero( unsigned *P, unsigned l )
{
#if NANU_USE_LIBC
	memset_fast( P, 0, l * sizeof( unsigned ) );
#else
	while ( l-- ) *P++ = 0;
#endif
}

/*
*	Copy the integer array S[ l ] to D[ l ]. They can overlap.
*/

void	_bc_NanuMove( unsigned *D, unsigned *S, unsigned l )
{
#if NANU_USE_LIBC
	memmove_fast( D, S, l * sizeof( unsigned ) );
#else
	if ( D < S )
		for ( l++ ; --l ; *D++ = *S++ );
	else
		for ( D += l, S += l, l++ ; --l ; *--D = *--S );
#endif
}

/*
*	Compares two numbers represented by little-endian arrays A[ l ] and B[ l ]
*/

int		_bc_NanuComp( unsigned *A, unsigned *B, unsigned l )
{
	for ( A += --l, B += l++, l++ ; --l && *A == *B ; A--, B-- );
	return( l ? *A > *B ? +1 : -1 : 0 );
}

/****************************************************************************/
/*					Addition utilities										*/
/****************************************************************************/

#if ! NANU_ASM_SUB

/*
*	R = A - B where all three has length 'l'
*
*	Returns a flag is there was a borrow or not
*/

unsigned _bc_NanuSub2( unsigned *R, unsigned *A, unsigned *B, unsigned l )
{
signed long long t;

	for ( t = 0 ; l-- ; t >>= 32 ) {

		t += *A++;
		t -= *B++;
		*R++ = t;
	}

	return( t != 0 );
}

/*
*	R = A - ( s ? 1 : 0 ) where A and R are l long.
*
*	Returns if there was a borrow at the end
*/

unsigned _bc_NanuSub1( unsigned *R, unsigned *A, unsigned s, unsigned l )
{
signed long long t;

	for ( t = s ? -1 : 0 ; l-- ; t >>= 32 ) {

		t += *A++;
		*R++ = t;
	}

	return( t != 0 );
}

#endif

/****************************************************************************/
/*					Addition utilities										*/
/****************************************************************************/

#if ! NANU_ASM_ADD

/*
*	Calculates R = A + B, all three has the length of l
*
*	Returns if there was a carry
*/

unsigned _bc_NanuAdd2( unsigned *r, unsigned *a, unsigned *b, unsigned l )
{
unsigned long long t;

	for ( t = 0 ; l-- ; t >>= 32 ) {

		t += *a++;
		t += *b++;
		*r++ = t;
	}

	return( t );
}

/*
*	Calculates R = A + ( s ? 1 : 0 ), R and A are l long
*
*	Returns if there was a carry
*/

unsigned _bc_NanuAdd1( unsigned *r, unsigned *a, unsigned s, unsigned l )
{
unsigned long long t;

	for ( t = s ; l-- ; t >>= 32 ) {

		t += *a++;
		*r++ = t;
	}

	return( t );
}

#endif

/****************************************************************************/
/*					Multiplication utilities								*/
/****************************************************************************/

#if ! NANU_ASM_MULU

/*
*	Calculates the R += A * b function, R and A are 'l' long multiwords,
*	b is single word. The retun value is the most significant 32 bits of
*	the result. That 32 bits do not get stored, only returned.
*/

unsigned _bc_NanuMulu( unsigned *R, unsigned *A, unsigned b, unsigned l )
{
unsigned long long temp;

	for ( temp = 0 ; l-- ; temp >>= 32 ) {

		temp += (unsigned long long) b * *A++ + *R;
		*R++ = temp;
	}

	return( temp );
}

#endif

#if ! NANU_ASM_MULM

/*
*	Calculates R = ( R + A * b + M * t + ((f?1:0) << 32*l) ) >> 32
*	The return value is the overflow, if any. Note that despite the
*	32-bit shift to the right there can be an overflow. Example:
*	Assume that al=l=2, all numbers are all bits set, except f which
*	is 1 (it is the previous overflow). Then the two muls both result
*	fffffffe:ffffffff:00000001, so the add looks like this:
*
*		fffffffe:ffffffff:00000001		M * f
*		fffffffe:ffffffff:00000001		A * b
*				 ffffffff:ffffffff		R
*	+		   1:00000000:00000000		f << 32*l
*	------------------------------
*	  1:ffffffff:fffffffe:00000001
*
*	of that the 00000001 bit gets thrown away, the ffffffff:fffffffe part
*	is stored in R and the leading 1 is the overflow that gets returned.
*	Note that the overflow is at most 1.
*
*	l is the length of R and M, a is the length of A.
*/

unsigned	_bc_NanuMulm( unsigned *R, unsigned *A, unsigned *M,
						  unsigned t,  unsigned b,  unsigned f,
						  unsigned a, unsigned l )
{
unsigned long long	p, q;
unsigned int		c;

	a = a - 1;
	p = (unsigned long long) *A++ * b;
	q = (unsigned long long) *M++ * t;

	q = q + *R;
	p = p + (unsigned) q;
	p = ( p >> 32 ) + ( q >> 32 );
	c = p >> 32;
	p = (unsigned) p;

	while ( --l ) {

		if ( (int) --a >= 0 ) p += (unsigned long long) *A++ * b;
		q  = R[ 1 ];
		q += (unsigned long long) *M++ * t;
		q += (unsigned) p;
		p  = ( q >> 32 ) + ( p >> 32 ) + c;
		c  = p >> 32;
		p  = (unsigned) p;
		*R++ = q;
	}

	p = p + f;
	*R = p;
	return( c + ( p >> 32 ) );
}

#endif

#if ! NANU_ASM_MULR

/*
*	Calculates R = ( R << 32 ) + Y * x where R is 'l' long and Y is 's' long.
*	Only the lower 'l' words of the result are stored, the up to 33 most
*	significant bits are returned.
*/

unsigned long long _bc_NanuMulr( unsigned *R, unsigned *Y, unsigned x,
								 unsigned s, unsigned l )
{
unsigned long long t;
unsigned i;
unsigned w;

	for ( t = 0, i = 0 ; i < l ; i++ ) {

		if ( i < s ) t += Y[ i ] * (unsigned long long) x;
		w  = t;
		t >>= 32;
		t  += R[ i ];
		R[ i ] = w;
	}

	return( t );
}

#endif

#if ! NANU_ASM_MULS

/*
*	Calculates R = R - M * q
*	~~~~~~~~~~~~~~~~~~~~~~~~~
*	R and M are l-long multiwords, q is a single word. The return value
*	is the borrow, a signed 64-bit quantity. Here's an example:
*
*			 ___________ M ____________   ___q____
*			 ffffffff:ffffffff:ffffffff * ffffffff
*   -----------------------------------
*	fffffffe:ffffffff:ffffffff:00000001
*
*	Although this starts with fff.., we consider it as a *positive* number.
*	Thus, the subtraction might produce a negative number. Although we do
*	not handle negative numbers in general, in this particular case we do.

*			          00000001:00000000:00000000	<- R
*   -	     fffffffe:ffffffff:ffffffff:00000001	<- Result of the multiply
*   --------------------------------------------
*	ffffffff:00000001:00000001:00000000:ffffffff
*	^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
*	     borrow				stored in R
*		(signed)			(unsigned)
*/

long long _bc_NanuMuls( unsigned *R, unsigned *M, unsigned q, unsigned l )
{
unsigned long long	u;
signed long long	s;

	for ( s = 0 ; l-- ; R++ ) {

		u   = *M++ * (unsigned long long) q;
		s  += *R;
		s  -= (unsigned) u;
		*R  = s;
		s >>= 32;
		s  -= u >> 32;
	}

	return( s );
}

#endif

