多倍長演算ライブラリ ルーチン29



[ 簡単な説明 ]

法 n における剰余を求めます。


プログラム・ソース("lpwrmod.c")           top (トップに戻る)
/*		lpwrmod.c		*/
#include "longint.h"

LINT lmod1(LINT a, LINT *n, LINT *nn, int d, int nntop, int nnlen);

/* modulus of power  LINT ^ LINT (mod LINT) */

LINT lpwrmod(LINT a, LINT b, LINT n)
{
	LINT nn, x;
	int *bnum1, *btop, *cp, *p, *q;
	int alen, d, nntop, nnlen, i, j, t, u;

	if(a.sign < 0 || b.sign < 0 || n.sign < 0 || n.len == 0)
	{
		fprintf(stderr, "Error : illegal parameter  in lpwrmod1()\n");
		exit(-3);
	}
	if(a.len == 0)	return a;
	if(b.len == 0)	return lset(1);

	x.len = x.sign = 0;
	x.len = 1;
	x.num[1] = 1;
	nn = n;
	nnlen = nn.len;
	d = BASE / (nn.num[nnlen] + 1);
	if(d != 1)
	{
		for(i = 1, t = 0; i <= nnlen; i++)
		{
			nn.num[i] = (t += nn.num[i] * d) % BASE;
			t /= BASE;
		}
	}
	nntop = nn.num[nnlen];
	btop = b.num + b.len;
	bnum1 = b.num + 1;
	do
	{
		if(*bnum1 % 2 == 0)
		{
			p = btop;
			t = 0;
			i = btop - b.num;
			while(i--)
			{
				*p-- = (t += *p) / 2;
				t = (t % 2)? BASE: 0;
			}
			while(*btop == 0)	btop--;
			a = lmod1(lsqr(a), &n, &nn, d, nntop, nnlen);
			if(a.len == 0)
			{
				x.len = 0;
				break;
			}
		}
		else
		{
			(*bnum1)--;
			x = lmod1(lmulp(x, a), &n, &nn, d, nntop, nnlen);
			if(x.len == 0)	break;
		}
	} while(btop > bnum1 || *bnum1);
	return x;
}

LINT lmod1(LINT a, LINT *n, LINT *nn, int d, int nntop, int nnlen)
{
	LINT w;
	int *anum1, *nnnum1, *wnum1, *p, *q;
	int alen, i, ql, t, dx;

	if(n->len == 1)	return lset(smod(a, n->num[1]));
	if(n->len > (alen = a.len))	return a;
	else if(alen == n->len)
	{
		a.num[0] = 0;
		n->num[0] = 1;
		p = a.num + alen;
		q = n->num + alen;
		while(*p-- == *q--)	;
		if(p == a.num)
		{
			a.len = 0;
			return a;
		}
		if(*(++p) < *(++q))	return a;
	}

	if(alen == MAXLEN)
	{
		fprintf(stderr, "Error : Too large  in lmod1()\n");
		a.len = 0;
		return a;
	}
	anum1 = a.num + 1;
	if(d != 1)
	{
		t = 0;
		p = anum1;
		i = alen;
		while(i--)
		{
			*p = (t += *p * d) % BASE;
			t /= BASE;
			p++;
		}
		while(t)
		{
			*p++ = t % BASE;
			t /= BASE;
		}
		a.len = alen = --p - a.num;
	}
	ql = alen - nnlen + 1;
	if((dx = a.num[alen] / nntop) == 0)
	{
		ql--;
		dx = (a.num[alen] * BASE + a.num[alen - 1]) / nntop;
	}
	a.num[0] = 1;
	w.len = w.sign = 0;
	w.num[0] = 0;
	nnnum1 = nn->num + 1;
	wnum1 = w.num + 1;
	for(;;)
	{
		ql--;
		for(;;)
		{
			q = wnum1;
			i = ql;
			while(i--)	*q++ = 0;
			t = 0;
			p = nnnum1;
			i = nnlen;
			while(i--)
			{
				*q++ = (t += *p++ * dx) % BASE;
				t /= BASE;
			}
			while(t)
			{
				*q++ = t % BASE;
				t /= BASE;
			}
			if(alen > (w.len = q - wnum1))	break;
			else if(alen == w.len)
			{
				p = a.num + alen;
				q = w.num + alen;
				while(*q-- == *p--)	;
				if(*(++q) < *(++p))	break;
			}
			dx--;
		}
		for(i = w.len + 1, q = w.num + i; i <= alen; i++)	*q++ = 0;
		p = anum1;
		q = wnum1;
		i = alen;
		while(i--)
		{
			if((*p -= *q++) < 0)
			{
				*p += BASE;
				(*q)++;
			}
			p++;
		}
		while(*(--p) == 0)	;
		if((alen = p - a.num) == 0 || ql == 0)	break;
		dx = (a.num[alen] * BASE + a.num[alen - 1]) / nntop;
	}
	if(d != 1)
	{
		p = a.num + (i = alen);
		t = 0;
		while(i--)
		{
			*p = (t += *p) / d;
			t = (t % d) * BASE;
			p--;
		}
		while(a.num[alen] == 0)	alen--;
	}
	a.len = alen;
	return a;
}