/****************************************************************************/
/*	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>

/****************************************************************************/
/*						Local prototypes									*/
/****************************************************************************/

static void	NanuMmul( unsigned *res,
					  unsigned *aop, unsigned *bop, unsigned *mod,
					  unsigned aln,  unsigned bln,  unsigned mln );

/****************************************************************************/
/*						Public API functions								*/
/****************************************************************************/

/** @addtogroup nanu */
/** @{ */

/**
*	Modular multiplication.
*
*	Multiplies the second and third elements of the stack, then divides the
*	result with the topmost element. All three are discarded and the remainder
*	of the division is pushed onto the stack.
*
*	@param	nanu			The natural number machine state descriptor
*
*	@retval	BC_NANU_NANU	The operation succeeded
*	@retval	BC_NANU_SUND	The stack didn't have at least 3 items on it
*	@retval	BC_NANU_SOVR	Stack overflow occured during calculations
*	@retval	BC_NANU_OMEM	Could not allocate temporaries
*	@retval	BC_NANU_NANN	The modulus was 0
*
*	@par Stack image
*		m b a ...  ==> (a*b)%%m ...
*/

int		BC_NanuMmul( BC_NANU nanu )
{
int		vm, va, vb, vr;
char	t0 = 0;

	// Check if we have at least 3 elements, so that we don't have
	// to test the return code of every basic stack operation

	if ( ( vr = _bc_NanuStkSee( nanu, 2 ) ) < 0 )

		return( _bc_NanuError( nanu, vr ) );

	// Check the modulus, we know it's there

	vm = _bc_NanuStkSee( nanu, 0 );

	if ( _bc_NanuVarLen( nanu, vm ) == 1 ) {

		if ( *_bc_NanuVarPtr( nanu, vm ) == 0 ) {

			// The modulus can not be 0.

			return( _bc_NanuError( nanu, BC_NANU_NANN ) );
		}

		if ( *_bc_NanuVarPtr( nanu, vm ) == 1 ) {

			// Anything modulo 1 is 0.

			BC_NanuDrop( nanu );
			BC_NanuDrop( nanu );
			BC_NanuDrop( nanu );
			return( BC_NanuPush( nanu, &t0, 1 ) );
		}
	}

	// The underlying modular multiplication function requires that
	// the 'a' operand is less than the modulus. We can't just call
	// BC_NanuRmod() because it calls us back to do the reduction,
	// since it is done by modular multiply (i.e. this function).
	// Check if the 'a' operand is larger than the modulus. If it is,
	// then we try to swap 'a' and 'b', since it doesn't matter if 'b'
	// is larger or not. However, if both 'a' and 'b' are larger, we
	// have to do a modular reduction first, to guarantee that 'b'
	// is less than the modulus.
													// m b a ...
	BC_NanuRoll( nanu, +2 );						// a m b ...

	if ( BC_NanuComp( nanu, BC_NANU_GE ) ) {		// a m b ...

		// Alas, a is at least as large as the
		// modulus. Swap a and b around.

		BC_NanuSwap( nanu );						// m a b ...
		BC_NanuRoll( nanu, +2 );					// b m a ...

		if ( BC_NanuComp( nanu, BC_NANU_GE ) ) {	// b m a ...

			// Unfortunately, b is also too big.
			// Do modular reduction of b.

			if ( ( vr = BC_NanuOver( nanu ) ) )		// m b m a ...

				return( vr );

			if ( ( vr = BC_NanuRmod( nanu ) ) )		// b' m a ...

				return( vr );
		}
	}

	// When we get here the stack order is Y m X
	// where Y is guaranteed to be less than the
	// modulus and X can be anything. Since we
	// multiply, we can swap the multiplicands
	// at will. So, we will call Y a and X b.
													// a m b ...
	BC_NanuRoll( nanu, -2 );						// m b a ...

	// Pop the variables from the stack; we know that we can.

	vm = _bc_NanuStkGet( nanu );					// b a ...
	vb = _bc_NanuStkGet( nanu );					// a ...
	va = _bc_NanuStkGet( nanu );					// ...

	if ( ( vr = _bc_NanuMmul( nanu, va, vb, vm ) ) < 0 ) {

		// Could not create the result

		return( _bc_NanuError( nanu, vr ) );
	}

	// Get rid of a, b and m and push the result onto the stack

	_bc_NanuVarDec( nanu, vm );
	_bc_NanuVarDec( nanu, va );
	_bc_NanuVarDec( nanu, vb );
	_bc_NanuStkPut( nanu, vr );
	return( BC_NANU_NANU );
}

/** @} */

/****************************************************************************/
/*						Library internal functions							*/
/****************************************************************************/

/*
*	Modular multiplication using variables
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	Returns the result variable or a negative error code.
*	It is just an API layer on the NanuMmul() function, below.
*	It does not affect its argument variables in any way.
*/

int		_bc_NanuMmul( BC_NANU nanu, int aop, int bop, int mod )
{
int		res, len;

	len = _bc_NanuVarLen( nanu, mod );

	if ( ( res = _bc_NanuVarNew( nanu, len ) ) >= 0 ) {

		NanuMmul( _bc_NanuVarPtr( nanu, res ),
				  _bc_NanuVarPtr( nanu, aop ),
				  _bc_NanuVarPtr( nanu, bop ),
				  _bc_NanuVarPtr( nanu, mod ),
				  _bc_NanuVarLen( nanu, aop ),
				  _bc_NanuVarLen( nanu, bop ),
				  len );

		_bc_NanuVarNrm( nanu, res );
	}

	return( res );
}

/****************************************************************************/
/*						Local functions										*/
/****************************************************************************/

/*
*	Modular multiplication
*	~~~~~~~~~~~~~~~~~~~~~~
*	res = ( aop * bop ) % mod;
*
*	aop *must* be less than the modulus, bop can be of arbitrary size.
*
*	The algorithm works the following way:
*
*	res = 0;
*
*	for ( i = number_of_words( bop ) - 1 ; i >= 0 ; i== ) {
*
*		tmp = ( res << 32 ) + aop * bop[ i ];
*		qvt = estimate( tmp / mod );
*		tmp = tmp - mod * qvt;
*		while ( tmp < 0 ) tmp = tmp + mod;
*		res = tmp;
*	}
*
*	where res is the result, a long number as long as the modulus,
*	tmp is a temporary, two words longer than res and qvt is a
*	32-bit integer. The bop[ i ] expression gets the i-th word
*	of bop, where bop[ 0 ] is the least significant word and
*	bop[ number_of_words( bop ) - 1 ] is the most significant word,
*	if the word order is little-endian it is actually what it means
*	in C.
*
*	The estimate "function" works out an estimate of tmp / mod. What we
*	really do is that we get the topmost 64 bits of tmp and the topmost
*	32 bits of mod (shifting up both with the same amount so that we have
*	as many useful bits as possible) and do a 64/32 division. Due to the
*	32 bit number guaranteed to be at least 0x80000000, the division
*	result is no more than 33 bits. Now this result is never less than
*	the real quotient and is never more than the real quotient plus 2.
*	If we overestimated the quotient, then the result in tmp will be of
*	course negative, but since our overestimation is never more than 2,
*	we can correct our error by adding the modulus to tmp at most twice.
*	Since we only have 32-bit multiply, if the estimate happens to be
*	33 bits, we separate the reduction to two steps, the first one
*	with 2^32 (which is very easy as no multiplication is needed) and
*	then a regular step, using the lower 32 bits of the quotient.
*	As it turns out, we can predict a 33-bit result before we do
*	the division, so if we see that the division would end up with
*	33 bits, we do a reduction with 2^32 immediately. Thus, we
*	know that the division result will always fit in 32 bits.
*
*	In reality, we don't have a separate tmp, but we have a 64-bit
*	variable that we use as an extension of res: tmp := {ext, res}.
*	Furthermore, if aop is 1, then naturally we do not actually do
*	multiplication so we save a significant amount of time. The aop
*	being 1 case is not as silly as it sounds, modular reduction is
*	actually done by executing a modular multiply with 1.
*
*	res is a pointer to a buffer for the result that is mln words long
*	aop is a pointer to the A operand which is aln words long, A < M
*	bop is a pointer to the B operand which is bln words long
*	mod is a pointer to the modulus M which is mln words long
*
*	If aop is not less than mod, then the function will generate complete
*	garbage, if aop is longer than mod, the function will overflow the
*	result area, with the usual consequences...
*/

static void	NanuMmul( unsigned *res,
					  unsigned *aop, unsigned *bop, unsigned *mod,
					  unsigned aln,  unsigned bln,  unsigned mln )
{
unsigned	top, sft, div;
long long	ovf;
int			cnt;

	// Clear the result

	_bc_NanuZero( res, mln );

	// If either operand is 0, then we're done

	if ( ( aln == 1 && ! *aop ) || ( bln == 1 && ! *bop ) ) return;

	// If the A operand is 1, then it is a modular reduction that can
	// be done faster than the real multiply

	if ( aln == 1 && *aop == 1 ) aln = 0;

	// Work out how many empty bits the modulus have at the top
	// At the end of this 'top' will contain the most significant bits of
	// the modulus shifted up (so that the MSB of the modulus is at the MSB
	// of 'top') and 'sft' contains the number of bits we had to shift the
	// modulus to achieve that.

	top = mod[ mln - 1 ];
	sft = 0;

	if ( ! ( top >> 16 ) ) { sft += 16; top <<= 16; }
	if ( ! ( top >> 24 ) ) { sft +=  8; top <<=  8; }
	if ( ! ( top >> 28 ) ) { sft +=  4; top <<=  4; }
	if ( ! ( top >> 30 ) ) { sft +=  2; top <<=  2; }
	if ( ! ( top >> 31 ) ) { sft +=  1; top <<=  1; }

	if ( mln > 1 && sft )

		top |= mod[ mln - 2 ] >> ( 32 - sft );

	// Check if we do real multiply or modular reduction, because
	// in the latter case we can save a lot of work up-front.

	if ( ! aln ) {

		// We do modular reduction

		if ( bln < mln ||
			 ( bln == mln && _bc_NanuComp( bop, mod, mln ) < 0 ) ) {

			// The bop argument is smaller than the modulus.
			// Just copy it into the result and return.

			_bc_NanuMove( res, bop, bln );
			return;
		}

		// We know that bop is at least as large as the modulus.
		// However, the first few steps of the algoritm would do
		// nothing else than shift mln - 1 words of bop into res.
		// We can do it faster here.

		_bc_NanuMove( res, bop + bln - mln + 1, mln - 1 );

		// Initialise the counter to the remaining length of b

		cnt = ( bln - 1 ) - ( mln - 1 );
	}
	else {

		// Modular multiply, initialise the counter to the full length of b.

		cnt = bln - 1;
	}

	for ( ; cnt >= 0 ; cnt-- ) {

		// Calculate res = ( res << 32 ) + aop * bop[ cnt ]
		// If aln is 0, then aop is 1, so we don't have to multiply.

		if ( ! aln ) {

			// We know that aop is 1, so we just have to shift res
			// up and copy the next word from bop to the least significant
			// word of res.

			ovf = res[ mln - 1 ];
			_bc_NanuMove( res + 1, res, mln - 1 );
			*res = bop[ cnt ];
		}
		else {

			// We have to do a real multiply

			ovf = _bc_NanuMulr( res, aop, bop[ cnt ], aln, mln );
		}

		// At this point ovf is an at most 33 bit number.

		if ( ! ovf && res[ mln - 1 ] < mod[ mln - 1 ] ) {

			// The result is smaller than the modulus,
			// no need for reduction step.

			continue;
		}

		// Prepare for the estimation of the divisor

		div = sft ? res[ mln - 1 ] >> ( 32 - sft ) : 0;

		if ( (unsigned) ( ovf >> ( 32 - sft ) ) ||
			 ( ( ovf << sft ) | div ) >= top ) {

			// The division would result in a 33-bit number.
			// Do the reduction step by 2^32

			ovf -= _bc_NanuSub2( res+1, res+1, mod, mln-1 ) + mod[ mln-1 ];
		}

		// Since we've possibly done a reduction step, ovf might have
		// become negative. In that case no further reduction steps
		// should be done (we have to restore).

		if ( ovf >= 0 ) {

			// We have to do a reduction step. We know that ovf / top
			// will not be larger than 32 bits

			ovf  = ( ( ovf << 32 ) | res[ mln - 1 ] ) << sft;
			ovf |= ( mln > 1 && sft ) ? res[ mln - 2 ] >> ( 32 - sft ) : 0;
	   		div  = (unsigned long long) ovf / top;
			ovf  = ( (unsigned long long) ovf >> ( 32 + sft ) );
			ovf += _bc_NanuMuls( res, mod, div, mln );
		}

		while ( ovf < 0 ) {

			// The divisor was overestimated, we have to restore
			// the result by adding the modulus to it

			ovf += _bc_NanuAdd2( res, res, mod, mln );
		}
	}
}

