If I see it right you are looking for Inverse modpow. The math is like this:
ab = a^b % p
ab + c*p = a^b
log(ab+c*p)/log(a) = b
(ab+c*p)^(1/b) = a
where c
is integer c={ 0,1,2,3,4... }
converting between normal and modular arithmetic. So in your case you want to compute b
. The problem is that log(ab+c*p)/log(a)
grows very slow with increasing c
if p
is not much bigger than a
. So in such case it's faster to use all combinations of b
instead until a fit is found something like this in C++:
//---------------------------------------------------------------------------
ALU32 alu;
DWORD modmul(DWORD a,DWORD b,DWORD p) // ans = a*b % p
{
DWORD ch,cl,c,d;
alu.mul(ch,cl,a,b);
alu.div(c,d,ch,cl,p);
return d;
}
//---------------------------------------------------------------------------
DWORD modinv(DWORD a,DWORD p) // a * ans % p = 1
{
DWORD b,c,db,dc,i=0;
db=p/a;
dc=db*a;
for (b=1,c=a;b<p;i++)
{
if (c==1) return b;
b+=db; c+=dc;
while (c<p){ b++; c+=a; }
c-=p;
}
return 0;
}
//---------------------------------------------------------------------------
DWORD modpow(DWORD a,DWORD b,DWORD p) // ans = a^b % p
{ // b is not mod(p) !
DWORD i,d=1;
for (a%=p,i=0;i<32;i++,b<<=1)
{
d=modmul(d,d,p);
if (DWORD(b&0x80000000)) d=modmul(d,a,p);
}
return d;
}
//---------------------------------------------------------------------------
DWORD imodpow(DWORD ab,DWORD a,DWORD p) // ab = a^ans % p
{ // ans is not mod(p) !
DWORD b,AB;
for (AB=1,b=0;;)
{
if (AB==ab) return b;
b++; if (!b) return 0;
AB=modmul(AB,a,p);
}
}
//---------------------------------------------------------------------------
Of coarse this is SLOOOOW, which is why is this used for cryptography. Also beware there are multiple valid solutions and the first one found might not be the one you're seeking so you need to add additional conditions ...
The ALU32.h
can be found in here Can't make value propagate through carry
And the modular arithmetic is based on this: Modular arithmetics and NTT (finite field DFT) optimizations
Here a sample for comparison (ignore VCL and tbeg/tend/tstr functions):
DWORD a=87654321,b=12345678,p=0xC0000001,ab,bb;
tbeg(); ab=modpow(a,b,p); tend(); mm_log->Lines->Add(AnsiString().sprintf("%8u^%8u mod %u = %u ",a,b ,p,ab)+tstr(1));
tbeg(); bb=imodpow(ab,a,p); tend(); mm_log->Lines->Add(AnsiString().sprintf("%8u^%8u mod %u = %u ",a,bb,p,ab)+tstr(1));
and output:
87654321^12345678 mod 3221225473 = 3038293251 [ 0.002 ms]
87654321^12345678 mod 3221225473 = 3038293251 [ 421.910 ms]
PS.
There might be some more advanced approaches from number theory if the p
is special like prime, composite of two primes or even n-th root of unity ... but that is in galaxy far far away from mine reach of expertise.
Edit 1
From your newly posted question it's finally clearer that you really just wanted modular inverse and has nothing to do with imodpow
. So what you want is this:
a*b % p = 1
where b
is unknown so simply try all b
in increasing manner where a*b % p
is just truncated by p
towards zero and if the result is 1 you found your answer. I updated the code above with modinv
function doing exactly that + some optimization. However I think there are even faster approaches for this using GCD or something.
Here another test sample:
DWORD a=87654321,b=12345678,p=0xC0000001,ab,bb;
ab=modmul(a,b,p);
tbeg(); bb=modinv(b,p); tend(); mm_log->Lines->Add(AnsiString().sprintf(" 1/%8u mod %u = %u ",b,p,bb)+tstr(1));
tbeg(); a =modmul(b,bb,p); tend(); mm_log->Lines->Add(AnsiString().sprintf("%8u*%8u mod %u = %u ",b,bb,p,a)+tstr(1));
tbeg(); a =modmul(ab,bb,p); tend(); mm_log->Lines->Add(AnsiString().sprintf("%8u*%8u mod %u = %u ",ab,bb,p,a)+tstr(1));
And output:
1/12345678 mod 3221225473 = 165081805 [ 4.999 ms]
12345678*165081805 mod 3221225473 = 1 [ 0.000 ms]
652073126*165081805 mod 3221225473 = 87654321 [ 0.000 ms]