#include <stdio.h>
#include "bn_lcl.h"

#if 1

int bn_mull(BIGNUM *r,BIGNUM *a,BIGNUM *b, BN_CTX *ctx);

int bn_mull(r,a,b,ctx)
BIGNUM *r,*a,*b;
BN_CTX *ctx;
	{
	int top,i,j,k,al,bl;
	BIGNUM *t;

#ifdef BN_COUNT
printf("bn_mull %d * %d\n",a->top,b->top);
#endif

	bn_check_top(a);
	bn_check_top(b);
	bn_check_top(r);

	al=a->top;
	bl=b->top;
	r->neg=a->neg^b->neg;

	top=al+bl;
	if ((al < 4) || (bl < 4))
		{
		if (bn_wexpand(r,top) == NULL) return(0);
		r->top=top;
		bn_mul_normal(r->d,a->d,al,b->d,bl);
		goto end;
		}
	else if (al == bl) /* A good start, they are the same size */
		goto symetric;
	else
		{
		i=(al-bl);
		if ((i ==  1) && !BN_get_flags(b,BN_FLG_STATIC_DATA))
			{
			bn_wexpand(b,al);
			b->d[bl]=0;
			bl++;
			goto symetric;
			}
		else if ((i ==  -1) && !BN_get_flags(a,BN_FLG_STATIC_DATA))
			{
			bn_wexpand(a,bl);
			a->d[al]=0;
			al++;
			goto symetric;
			}
		}

	/* asymetric and >= 4 */ 
	if (bn_wexpand(r,top) == NULL) return(0);
	r->top=top;
	bn_mul_normal(r->d,a->d,al,b->d,bl);

	if (0)
		{
		/* symetric and > 4 */
symetric:
		if (al == 4)
			{
			if (bn_wexpand(r,al*2) == NULL) return(0);
			r->top=top;
			bn_mul_comba4(r->d,a->d,b->d);
			goto end;
			}
		if (al == 8)
			{
			if (bn_wexpand(r,al*2) == NULL) return(0);
			r->top=top;
			bn_mul_comba8(r->d,a->d,b->d);
			goto end;
			}
		if (al <= BN_MULL_NORMAL_SIZE)
			{
			if (bn_wexpand(r,al*2) == NULL) return(0);
			r->top=top;
			bn_mul_normal(r->d,a->d,al,b->d,bl);
			goto end;
			}
		/* 16 or larger */
		j=BN_num_bits_word((BN_ULONG)al);
		j=1<<(j-1);
		k=j+j;
		t= &(ctx->bn[ctx->tos]);
		if (al == j) /* exact multiple */
			{
			bn_wexpand(t,k*2);
			bn_wexpand(r,k*2);
			bn_mul_recursive(r->d,a->d,b->d,al,t->d);
			}
		else
			{
			bn_wexpand(a,k);
			bn_wexpand(b,k);
			bn_wexpand(t,k*4);
			bn_wexpand(r,k*4);
			for (i=a->top; i<k; i++)
				a->d[i]=0;
			for (i=b->top; i<k; i++)
				b->d[i]=0;
			bn_mul_part_recursive(r->d,a->d,b->d,al-j,j,t->d);
			}
		r->top=top;
		}
end:
	bn_fix_top(r);
	return(1);
	}
#endif

void bn_mul_normal(r,a,na,b,nb)
BN_ULONG *r,*a;
int na;
BN_ULONG *b;
int nb;
	{
	BN_ULONG *rr;

#ifdef BN_COUNT
printf(" bn_mul_normal %d * %d\n",na,nb);
#endif

	if (na < nb)
		{
		int itmp;
		BN_ULONG *ltmp;

		itmp=na; na=nb; nb=itmp;
		ltmp=a;   a=b;   b=ltmp;

		}
	rr= &(r[na]);
	rr[0]=bn_mul_words(r,a,na,b[0]);

	for (;;)
		{
		if (--nb <= 0) return;
		rr[1]=bn_mul_add_words(&(r[1]),a,na,b[1]);
		if (--nb <= 0) return;
		rr[2]=bn_mul_add_words(&(r[2]),a,na,b[2]);
		if (--nb <= 0) return;
		rr[3]=bn_mul_add_words(&(r[3]),a,na,b[3]);
		if (--nb <= 0) return;
		rr[4]=bn_mul_add_words(&(r[4]),a,na,b[4]);
		rr+=4;
		r+=4;
		b+=4;
		}
	}

#if 1
void bn_mul_low_normal(r,a,b,n)
BN_ULONG *r,*a,*b;
int n;
	{
#ifdef BN_COUNT
printf(" bn_mul_low_normal %d * %d\n",n,n);
#endif
	bn_mul_words(r,a,n,b[0]);

	for (;;)
		{
		if (--n <= 0) return;
		bn_mul_add_words(&(r[1]),a,n,b[1]);
		if (--n <= 0) return;
		bn_mul_add_words(&(r[2]),a,n,b[2]);
		if (--n <= 0) return;
		bn_mul_add_words(&(r[3]),a,n,b[3]);
		if (--n <= 0) return;
		bn_mul_add_words(&(r[4]),a,n,b[4]);
		r+=4;
		b+=4;
		}
	}
#endif
