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

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

/**
*	Regular multiplication.
*
*	Multiplies the the topmost two elements of the stack, discards
*	both and pushes the multiplication result onto the stack.
*
*	@param	nanu			The natural number machine state descriptor
*
*	@retval	BC_NANU_NANU	The operation succeeded
*	@retval	BC_NANU_SUND	There was less that 2 elements on the stack
*	@retval	BC_NANU_OMEM	There's not enough memory in the pool to store
*							the result.
*
*	@par Stack image
*		a b ...  ==> a*b ...
*/

int		BC_NanuRmul( BC_NANU nanu )
{
int					var1, var2, vres;
unsigned int		*ptr1, *ptr2, *vptr;
unsigned int		siz1, siz2, temp;

	if ( ( var1 = _bc_NanuStkGet( nanu ) ) < 0 )

		return( _bc_NanuError( nanu, var1 ) );

	if ( ( var2 = _bc_NanuStkGet( nanu ) ) < 0 )

		return( _bc_NanuError( nanu, var2 ) );

	siz1 = _bc_NanuVarLen( nanu, var1 );
	siz2 = _bc_NanuVarLen( nanu, var2 );

	if ( ( vres = _bc_NanuVarTmp( nanu, var1, var2, siz1 + siz2 ) ) < 0 )

		return( _bc_NanuError( nanu, vres ) );

	if ( vres == var1 ) {

		temp = var1;
		var1 = var2;
		var2 = temp;
		temp = siz1;
		siz1 = siz2;
		siz2 = temp;
	}

	ptr1 = _bc_NanuVarPtr( nanu, var1 );
	vptr = _bc_NanuVarPtr( nanu, vres );

	// At this point the space allocated for the result is either a
	// separate buffer or the same as var2. In the latter case var2
	// has been resized so that it is now as large as the result will
	// be. Now this is what we do then:
	//
	// var1 = ABCDEFG
	// var2 = WXYZ
	//
	// After the allocation of the temporary var2's buffer will be
	// enlarged by the size of var1, its content is junk:
	//
	// var2 = ???????WXYZ
	//
	// Now we move var2 up in its buffer and zero the lower significant
	// digits of the buffer:
	//
	// var2 = WXYZ0000000
	//
	// Then for each digit in var2 we fetch the digit, substitute it with
	// 0, multiply var1 with that digit and add the result to var2:
	//
	// var2 = WXY00000000  mult=Z, do the multiply-accumulate:
	// var2 = WXYabcdefgh
	// var2 = WX0abcdefgh  mult=Y, do the multiply-accumulate:
	// var2 = WXijklmnoph  note that the least significant digit doesn't change
	//
	// and so on. Our actual multiplication routine does not store the
	// highest digit, it just returns it, so we don't actually need to
	// zero the digits, but the idea is the same.

	if ( vres == var2 ) {

		// We will work in place. We have to move the number up and
		// zero the space below it, then we have to adjust the pointer

		ptr2 = vptr + siz1;
		_bc_NanuMove( ptr2, vptr, siz2 );
		_bc_NanuZero( vptr, siz1 );
	}
	else {

		// We have to clear the result buffer

		_bc_NanuZero( vptr, siz1 + siz2 );

		// The second operand is at its real place

		ptr2 = _bc_NanuVarPtr( nanu, var2 );
	}

	while ( siz2-- ) {

		// Fetch the next digit of var2

		temp = *ptr2++;

		// Do the multiply-accumulate, the result is added to vptr
		// and the top 32 bits of the result are returned

		temp = _bc_NanuMulu( vptr, ptr1, temp, siz1 );

		// Store the top 32 bits in vptr[ siz1 ] and increment vptr.
		// Since the [] operator is commutative, we can do it elegantly.

		siz1[ vptr++ ] = temp;
	}

	_bc_NanuVarDec( nanu, var1 );
	_bc_NanuVarDec( nanu, var2 );
	_bc_NanuVarNrm( nanu, vres );
	_bc_NanuStkPut( nanu, vres );
	return( BC_NANU_NANU );
}

/** @} */

