Files
ANSCORE/anslicensing/bigint.cpp

1001 lines
15 KiB
C++
Raw Normal View History

2026-03-28 16:54:11 +11:00
//
// Copyright (c) 2014 ANSCENTER. All rights reserved.
//
#include "precomp.h"
#include "bigint.h"
unsigned char bittab[256] =
{ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 };
unsigned bigint_unit::get( unsigned i ) const
{
if ( i >= n )
return 0;
return a[ i ];
}
void bigint_unit::clear()
{
n = 0;
}
bigint_unit::bigint_unit()
{
z = 0;
a = 0;
n = 0;
}
bigint_unit::~bigint_unit()
{
unsigned i = z;
while ( i )
{
i -= 1;
a[i] = 0;
}
delete [] a;
}
void bigint_unit::reserve( unsigned x )
{
if ( x > z )
{
unsigned * na = new unsigned[ x ];
for ( unsigned i = 0; i < n; i += 1 )
na[ i ] = a[ i ];
delete [] a;
a = na;
z = x;
}
}
void bigint_unit::set( unsigned i, unsigned x )
{
if ( i < n )
{
a[ i ] = x;
if ( x == 0 )
{
while ( n && a[ n - 1 ] == 0 )
n -= 1; // normalise
}
}
else if ( x )
{
reserve( i + 1 );
for ( unsigned j = n; j < i; j += 1 )
a[j] = 0;
a[i] = x;
n = i + 1;
}
}
// Macros for doing double precision multiply
#define BPU ( 8 * sizeof( unsigned ) ) // Number of bits in an unsigned
#define lo(x) ( (x) & ( ( 1 << ( BPU / 2 ) ) -1 ) ) // lower half of unsigned
#define hi(x) ( (x) >> ( BPU / 2 ) ) // upper half
#define lh(x) ( (x) << ( BPU / 2 ) ) // make upper half
unsigned do_inner( unsigned n, unsigned m, unsigned * a, unsigned * ya )
{
if (a == NULL || ya == NULL)
return 0;
unsigned c = 0;
#if defined(_MSC_VER) && defined(_M_IX86)
__asm mov edi, a
__asm mov esi, ya
loop1:
__asm mov eax, [esi]
__asm mul m
__asm add esi, 4
__asm add eax, c
__asm adc edx, 0
__asm add [edi], eax
__asm adc edx, 0
__asm add edi, 4
__asm mov c, edx
__asm dec n
__asm jnz loop1
#else
while ( n-- )
{
unsigned v = *a,
p = *ya++;
v += c;
c = ( v < c );
unsigned w;
w = lo( p ) * lo( m );
v += w;
c += ( v < w );
w = lo( p ) * hi( m );
c += hi( w );
w = lh( w );
v += w;
c += ( v < w );
w = hi( p ) * lo( m );
c += hi( w );
w = lh( w );
v += w;
c += ( v < w );
c += hi( p ) * hi( m );
*a++ = v;
}
#endif
return c;
}
void bigint_unit::fast_mul( bigint_unit &x, bigint_unit &y, unsigned keep )
{
unsigned i,
limit = ( keep + BPU - 1 ) / BPU; // size of result in words
reserve( limit );
for ( i = 0; i < limit; i += 1)
a[i] = 0;
unsigned min = x.n;
if ( min > limit )
min = limit;
for ( i = 0; i < min; i += 1 )
{
unsigned m = x.a[ i ];
unsigned min = i + y.n;
if ( min > limit )
min = limit;
unsigned c = do_inner( min - i, m, a + i, y.a );
unsigned j = min;
while ( c && j < limit )
{
a[ j ] += c;
c = a[ j ] < c;
j += 1;
}
}
// eliminate unwanted bits
keep %= BPU;
if ( keep )
a[ limit-1 ] &= ( 1 << keep ) - 1;
// calculate n
while ( limit && a[ limit-1 ] == 0 )
limit -= 1;
n = limit;
};
unsigned bigint_value::to_unsigned()
{
return get( 0 );
}
int bigint_value::is_zero() const
{
return n == 0;
}
unsigned bigint_value::bit( unsigned i ) const
{
return ( get( i / BPU ) & ( 1 << ( i % BPU ) ) ) != 0;
}
void bigint_value::setbit( unsigned i )
{
set( i / BPU, get( i / BPU ) | ( 1 << i % BPU ) );
}
void bigint_value::clearbit( unsigned i )
{
set( i / BPU, get( i / BPU ) & ~( 1 << i % BPU ) );
}
unsigned bigint_value::bits() const
{
unsigned x = n;
if ( x )
{
unsigned w = BPU;
unsigned msw = get( x-1 );
x = ( x-1 ) * BPU;
do
{
w >>= 1;
if ( msw >= ( 1u << w ) )
{
x += w;
msw >>= w;
}
} while ( w > 8 );
x += bittab[ msw ];
}
return x;
}
int bigint_value::cf( bigint_value & x ) const
{
if ( n > x.n )
return +1;
if ( n < x.n )
return -1;
unsigned i = n;
while ( i )
{
i -= 1;
if ( get( i ) > x.get( i ) )
return +1;
if ( get( i ) < x.get( i ) )
return -1;
}
return 0;
}
void bigint_value::shl()
{
unsigned carry = 0;
unsigned N = n; // necessary, since n can change
for ( unsigned i = 0; i <= N; i += 1 )
{
unsigned u = get( i );
set( i, ( u << 1 ) + carry );
carry = u >> ( BPU - 1 );
}
}
int bigint_value::shr()
{
unsigned carry = 0;
unsigned i = n;
while ( i )
{
i -= 1;
unsigned u = get( i );
set( i,( u >> 1 ) + carry );
carry = u << ( BPU - 1 );
}
return carry != 0;
}
void bigint_value::shr( unsigned x )
{
unsigned delta = x / BPU;
x %= BPU;
for ( unsigned i = 0; i < n; i += 1 )
{
unsigned u = get( i + delta );
if ( x )
{
u >>= x;
u += get( i + delta + 1 ) << ( BPU-x );
}
set( i, u );
}
}
void bigint_value::add( bigint_value & x )
{
unsigned carry = 0;
unsigned max = n;
if ( max < x.n )
max = x.n;
reserve( max );
for ( unsigned i = 0; i < max + 1; i += 1 )
{
unsigned u = get( i );
u = u + carry;
carry = ( u < carry );
unsigned ux = x.get( i );
u = u + ux; carry += ( u < ux );
set( i,u );
}
}
void bigint_value::_xor( bigint_value & x )
{
unsigned max = n;
if (max<x.n)
max = x.n;
reserve(max);
for ( unsigned i = 0; i < max; i += 1 )
{
set( i, get( i ) ^ x.get( i ) );
}
}
void bigint_value::_and( bigint_value & x )
{
unsigned max = n;
if ( max < x.n )
max = x.n;
reserve( max );
for ( unsigned i = 0; i < max; i += 1 )
{
set( i, get( i ) & x.get( i ) );
}
}
int bigint_value::product( bigint_value & x ) const
{
unsigned max = n;
unsigned tmp = 0;
unsigned count = 0;
if ( max < x.n ) max = x.n;
for ( unsigned i = 0; i < max; i += 1 )
{
tmp ^= get( i ) & x.get( i );
}
while ( tmp )
{
if ( tmp & 1 ) count += 1;
tmp >>= 1;
}
return count & 1;
}
void bigint_value::subtract( bigint_value & x )
{
unsigned carry = 0;
unsigned N = n;
for ( unsigned i = 0; i < N; i += 1 )
{
unsigned ux = x.get( i );
ux += carry;
if ( ux >= carry )
{
unsigned u = get( i );
unsigned nu = u - ux;
carry = nu > u;
set( i, nu );
}
}
}
void bigint_value::init( unsigned x )
{
clear();
set( 0, x );
}
void bigint_value::copy( bigint_value & x )
{
unsigned i = x.n;
clear();
while ( i )
{
i -= 1;
set( i, x.get( i ) );
}
}
bigint_value::bigint_value()
{
share = 0;
}
void bigint_value::mul( bigint_value & x, bigint_value & y )
{
fast_mul( x, y, x.bits() + y.bits() );
}
void bigint_value::divide( bigint_value & x, bigint_value & y, bigint_value & rem )
{
bigint_value m, s;
init( 0 );
rem.copy( x );
m.copy( y );
s.init( 1 );
int i = 0;
while ( rem.cf( m ) > 0 )
{
m.shl();
s.shl();
i++;
}
while ( rem.cf( y ) >= 0 )
{
while ( rem.cf( m ) < 0 )
{
m.shr();
s.shr();
}
rem.subtract( m );
add( s );
}
}
// Implementation of bigint
int operator !=( const bigint& x, const bigint& y )
{
return x.cf( y ) != 0;
}
int operator ==( const bigint& x, const bigint& y )
{
return x.cf( y ) == 0;
}
int operator >=( const bigint& x, const bigint& y )
{
return x.cf( y ) >= 0;
}
int operator <=( const bigint& x, const bigint& y )
{
return x.cf( y ) <= 0;
}
int operator > ( const bigint& x, const bigint& y )
{
return x.cf( y ) > 0;
}
int operator < ( const bigint& x, const bigint& y )
{
return x.cf( y ) < 0;
}
void bigint::load( const unsigned * a, unsigned n )
{
docopy();
value->clear();
for ( unsigned i = 0; i < n; i += 1 )
value->set( i, a[ i ] );
}
void bigint::store( unsigned * a, unsigned n ) const
{
for ( unsigned i = 0; i < n; i += 1 )
a[ i ] = value->get( i );
}
void bigint::docopy()
{
if ( value->share )
{
value->share -= 1;
bigint_value * nv = new bigint_value;
nv->copy( *value );
value = nv;
}
}
unsigned bigint::bits() const
{
return value->bits();
}
unsigned bigint::bit( unsigned i ) const
{
return value->bit( i );
}
void bigint::setbit( unsigned i )
{
docopy();
value->setbit( i );
}
void bigint::clearbit( unsigned i )
{
docopy();
value->clearbit( i );
}
int bigint::cf( const bigint & x ) const
{
int neg = negative && !value->is_zero();
if ( neg == ( x.negative && !x.value->is_zero() ) )
return value->cf( *x.value );
else
if ( neg )
return -1;
else
return +1;
}
bigint::bigint( unsigned x )
{
value = new bigint_value;
negative = 0;
value->init( x );
}
bigint::bigint( const bigint& x ) // copy constructor
{
negative = x.negative;
value = x.value;
value->share += 1;
}
bigint & bigint::operator =( const bigint & x )
{
if ( value->share )
value->share -=1;
else
delete value;
value = x.value;
value->share += 1;
negative = x.negative;
return *this;
}
bigint::~bigint()
{
if ( value->share )
value->share -= 1;
else
delete value;
}
unsigned to_unsigned ( const bigint & x ) // conversion to unsigned
{
return x.value->to_unsigned();
}
bigint & bigint::operator ^=( const bigint& x )
{
docopy();
value->_xor( *x.value );
return *this;
}
bigint & bigint::operator &=( const bigint & x )
{
docopy();
value->_and( *x.value );
return *this;
}
bigint & bigint::ror( unsigned n )
{
docopy();
int carry = value->shr();
if ( carry )
value->setbit( n - 1 );
return *this;
}
bigint & bigint::rol( unsigned n )
{
docopy();
int carry = value->bit( n - 1 );
if ( carry ) value->clearbit( n - 1 );
value->shl();
if ( carry ) value->setbit( 0 );
return *this;
}
bigint & bigint::operator >>=( unsigned n ) // divide by 2**n
{
docopy();
value->shr(n);
return *this;
}
bigint & bigint::operator +=( const bigint & x )
{
if ( negative == x.negative )
{
docopy();
value->add( *x.value );
}
else if ( value->cf( *x.value ) >= 0 )
{
docopy();
value->subtract( *x.value );
}
else
{
bigint tmp = *this;
*this = x;
*this += tmp;
}
return *this;
}
bigint & bigint::operator -=( const bigint & x )
{
if ( negative != x.negative )
{
docopy();
value->add( *x.value );
}
else if ( value->cf( *x.value ) >= 0 )
{
docopy();
value->subtract( *x.value );
}
else
{
bigint tmp = *this;
*this = x;
*this -= tmp;
negative = 1 - negative;
}
return *this;
}
bigint operator +( const bigint & x, const bigint & y )
{
bigint result = x;
result += y;
return result;
}
bigint operator -( const bigint & x, const bigint & y )
{
bigint result = x;
result -= y;
return result;
}
bigint operator *( const bigint & x, const bigint & y )
{
bigint result;
result.value->mul( *x.value, *y.value );
result.negative = x.negative ^ y.negative;
return result;
}
bigint operator /( const bigint & x, const bigint & y )
{
bigint result;
bigint_value rem;
result.value->divide( *x.value, *y.value, rem );
result.negative = x.negative ^ y.negative;
return result;
}
bigint operator %( const bigint& x, const bigint& y )
{
bigint result;
bigint_value divide;
divide.divide( *x.value, *y.value, *result.value );
result.negative = x.negative; // not sure about this?
return result;
}
bigint operator ^( const bigint& x, const bigint& y )
{
bigint result = x;
result ^= y;
return result;
}
bigint operator &( const bigint & x, const bigint & y )
{
bigint result = x;
result &= y;
return result;
}
bigint operator <<( const bigint & x, unsigned n ) // multiply by 2**n
{
bigint result = x;
while ( n )
{
n -= 1;
result += result;
}
return result;
}
bigint abs( const bigint & x )
{
bigint result = x;
result.negative = 0;
return result;
}
int product ( const bigint & X, const bigint & Y )
{
return X.value->product( *Y.value );
}
bigint pow2( unsigned n )
{
bigint result;
result.setbit( n );
return result;
}
bigint gcd( const bigint &X, const bigint &Y )
{
bigint x = X,
y = Y;
while ( 1 )
{
if ( y == 0 ) return x;
x = x % y;
if ( x == 0 ) return y;
y = y % x;
}
}
bigint modinv( const bigint & a, const bigint & m ) // modular inverse
// returns i in range 1..m-1 such that i*a = 1 mod m
// a must be in range 1..m-1
{
bigint j = 1,
i = 0,
b = m,
c = a,
x,
y;
while ( c != 0 )
{
x = b / c;
y = b - x * c;
b = c;
c = y;
y = j;
j = i - j * x;
i = y;
}
if ( i < 0 )
i += m;
return i;
}
class monty // class for montgomery modular exponentiation
{
bigint m, n1;
bigint T, k; // work registers
unsigned N; // bits for R
void mul( bigint & x, const bigint & y );
public:
bigint R, R1;
bigint exp( const bigint & x, const bigint & e );
bigint monty_exp( const bigint & x, const bigint & e );
monty( const bigint & M );
};
monty::monty( const bigint & M )
{
m = M;
N = 0;
R = 1;
while ( R < M )
{
R += R;
N += 1;
}
R1 = modinv( R - m, m );
n1 = R - modinv( m, R );
}
void monty::mul( bigint & x, const bigint & y )
{
// T = x*y;
T.value->fast_mul( *x.value, *y.value, N * 2 );
// k = ( T * n1 ) % R;
k.value->fast_mul( *T.value, *n1.value, N );
// x = ( T + k*m ) / R;
x.value->fast_mul( *k.value, *m.value, N * 2 );
x += T;
x.value->shr( N );
if ( x >= m ) x -= m;
}
bigint monty::monty_exp( const bigint & x, const bigint & e )
{
bigint result = R - m,
t = x; // don't convert input
unsigned bits = e.value->bits();
unsigned i = 0;
t.docopy(); // careful not to modify input
while ( 1 )
{
if ( e.value->bit( i ) )
mul( result, t );
i += 1;
if ( i == bits ) break;
mul( t, t );
}
return result; // don't convert output
}
bigint monty::exp( const bigint & x, const bigint & e )
{
return ( monty_exp( ( x * R ) % m, e ) * R1 ) % m;
}
bigint modexp( const bigint & x, const bigint & e, const bigint & m )
{
monty me( m );
return me.exp( x, e );
}
static bigint half( const bigint & a, const bigint & p )
{
if ( a.bit( 0 ) )
return ( a + p ) / 2;
return a / 2;
}
bigint lucas ( const bigint & P, const bigint & Z, const bigint & k, const bigint & p ) // P^2 - 4Z != 0
{
bigint D = P * P - 4 * Z,
U = 1,
V = P,
U1,
V1;
unsigned i = k.bits() - 1;
while ( i )
{
i -= 1;
U1 = U * V;
V1 = V * V + D * U * U;
U = U1 % p;
V = half( V1 % p, p );
if ( k.bit( i ) )
{
U1 = P * U + V;
V1 = P * V + D * U;
U = half( U1 % p, p );
V = half( V1 % p, p );
}
}
return V;
}
bigint sqrt( const bigint & g, const bigint & p )
{
bigint result;
if ( p % 4 == 3 )
result = modexp( g, p / 4 + 1, p );
else if ( p % 8 == 5 )
{
bigint gamma = modexp( 2 * g, p / 8, p);
bigint i = 2 * g * gamma * gamma - 1;
result = g * gamma * i;
}
else
{
bigint Z = g;
bigint P = 1;
while ( 1 )
{
bigint D = ( P * P - 4 * g ) % p;
if ( D < 0 ) D += p;
if ( D == 0 )
{
result = half( P, p );
break;
}
if ( modexp( D, ( p - 1 ) / 2, p ) != 1 )
{
result = half( lucas( P, Z, ( p + 1 ) / 2, p ), p );
break;
}
P += 1; // Is this ok (efficient?)
}
}
result = result % p;
if ( result < 0 ) result += p;
return result;
}