//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : single_precision.c 
// Author      : Andreas M"uller (AM), Thomas Papanikolaou (TP)
// Last change : AM/TP, Feb 7 1995, initial version
//

#include <LiDIA/bigint.h>

int
gcd(int a, int b)
{
   int r;

   if (b == 0)
     return a;
   if (a == 0)
     return b;

   while( 1 )
   {
      r = a % b;
      if ( !r ) return (b);
      a = b; b = r;
   }
}

int
int_xgcd(int &xv, int &yv, int a, int b)
{

  char sa, sb, f;
  int s, t, u, v, xu, yu, hxv, hyv;

  if ((sa = (a < 0)))
    a = (-a);
  if (!b) 
  {
    yv = 0;
    xv = (sa) ? -1 : 1;
    return (a);
  }
  if ((sb = (b < 0)))
    b = (-b);
  if (!a) 
  {
    xv = 0;
    yv = (sb) ? -1 : 1;
    return (b);
  }
  if ((f = (b > a))) 
  {
    t = a;
    a = b;
    b = t;
  }
  xu = hyv = 1;
  yu = hxv = 0;
  u = a;
  v = b;
  s = 0;
  for (;;) 
  {
    if ((u & 1) || (v & 1))
      break;
    s++;
    u >>= 1;
    v >>= 1;
  }
  for (;;) 
  {
    if (u & 1)
      break;
    u >>= 1;
    if ((xu & 1) || (yu & 1)) 
    {
      xu += b;
      yu -= a;
    }
    xu >>= 1;
    yu >>= 1;
  }
  for (;;) 
  {
    if (v & 1)
      break;
    v >>= 1;
    if ((hxv & 1) || (hyv & 1)) 
    {
      hxv += b;
      hyv -= a;
    }
    hxv >>= 1;
    hyv >>= 1;
  }
  for (;;) 
  {
    if (u == v)
      break;
    if (u > v) 
    {
      u -= v;
      xu -= hxv;
      yu -= hyv;
      for (;;) 
      {
        if (u & 1)
          break;
        u >>= 1;
        if ((xu & 1) || (yu & 1)) 
        {
          xu += b;
          yu -= a;
        }
        xu >>= 1;
        yu >>= 1;
      }
    } 
    else 
    {
      v -= u;
      hxv -= xu;
      hyv -= yu;
      for (;;) 
      {
        if (v & 1)
          break;
        v >>= 1;
        if ((hxv & 1) || (hyv & 1)) 
        {
          hxv += b;
          hyv -= a;
        }
        hxv >>= 1;
        hyv >>= 1;
      }
    }
  }

  if (f) 
  {
    t = hxv;
    hxv = hyv;
    hyv = t;
  }
  if (sa)
    hxv = -hxv;
  if (sb)
    hyv = -hyv;
  xv = hxv;
  yv = hyv;
  return (u << s);
}

long
xgcd(long &xv, long &yv, long a, long b)
{

  char sa, sb, f;
  long s, t, u, v, xu, yu, hxv, hyv;

  if ((sa = (a < 0)))
    a = (-a);
  if (!b) 
  {
    yv = 0;
    xv = (sa) ? -1 : 1;
    return (a);
  }
  if ((sb = (b < 0)))
    b = (-b);
  if (!a) 
  {
    xv = 0;
    yv = (sb) ? -1 : 1;
    return (b);
  }
  if ((f = (b > a))) 
  {
    t = a;
    a = b;
    b = t;
  }
  xu = hyv = 1;
  yu = hxv = 0;
  u = a;
  v = b;
  s = 0;
  for (;;) 
  {
    if ((u & 1) || (v & 1))
      break;
    s++;
    u >>= 1;
    v >>= 1;
  }
  for (;;) 
  {
    if (u & 1)
      break;
    u >>= 1;
    if ((xu & 1) || (yu & 1)) 
    {
      xu += b;
      yu -= a;
    }
    xu >>= 1;
    yu >>= 1;
  }
  for (;;) 
  {
    if (v & 1)
      break;
    v >>= 1;
    if ((hxv & 1) || (hyv & 1)) 
    {
      hxv += b;
      hyv -= a;
    }
    hxv >>= 1;
    hyv >>= 1;
  }
  for (;;) 
  {
    if (u == v)
      break;
    if (u > v) 
    {
      u -= v;
      xu -= hxv;
      yu -= hyv;
      for (;;) 
      {
        if (u & 1)
          break;
        u >>= 1;
        if ((xu & 1) || (yu & 1)) 
        {
          xu += b;
          yu -= a;
        }
        xu >>= 1;
        yu >>= 1;
      }
    } 
    else 
    {
      v -= u;
      hxv -= xu;
      hyv -= yu;
      for (;;) 
      {
        if (v & 1)
          break;
        v >>= 1;
        if ((hxv & 1) || (hyv & 1)) 
        {
          hxv += b;
          hyv -= a;
        }
        hxv >>= 1;
        hyv >>= 1;
      }
    }
  }

  if (f) 
  {
    t = hxv;
    hxv = hyv;
    hyv = t;
  }
  if (sa)
    hxv = -hxv;
  if (sb)
    hyv = -hyv;
  xv = hxv;
  yv = hyv;
  return (u << s);
}

unsigned int
power(unsigned int a, unsigned int b)
{
   unsigned int i, n = a;

   for (i = 1; i < b; i++)  n *= a;

   return n;
}



#define MAXint       2147483647    
#define MAXIH        1073741823
#define SBASIS            65536
#define MAXDBL       1000000000000000.0

int h_mul_mod(int a, int b, int m)
{
  register unsigned int n, n1, n2;
  double nd, d;
  
  if ( m < SBASIS ) 
  {
    n = a * b;
    return( n % m );
  }

  d = 1.0 * a * b;
  
  if (d < MAXint) 
  { 
    n = (int) d; 
    n = n % m; 
  }
  else 
    if (d < MAXDBL ) 
    { 
      n = (int) (d / m);
      nd = 1.0 * n;
      nd = nd * m;
      d = d - nd /* (1.0 * n * m) */ ; 
      n = (int) d; 
    }
    else 
    {
      n1 = h_mul_mod(a % SBASIS, b, m);
      n2 = h_mul_mod(SBASIS , h_mul_mod(a / SBASIS, b, m) , m );
      if ( ( n1 < MAXIH ) && ( n2 < MAXIH ) ) 
        n = ( n1 + n2 ) % m;
      else 
      { 
        d = 1.0 * n1 + 1.0 * n2;
        if ( d < MAXint ) 
        { 
          n = (int) d; 
          n = n % m; 
        }
        else 
        {
          n = (int) (d / m);
          d = d - ( 1.0 * n * m );
          n = (int) d;
        }
      }
    }
 
  return(n);
}

#if defined(sparc) || defined(i386) || defined(mips) || defined(hppa)
int mul_mod(int a, int b, int m)
{
  int q,r;
  if (m <= 1) lidia_error_handler("mul_mod", "modul <= 1");

    q=(int)((double) a * (double) b /m);
    r=a*b-q*m;

    if (r<0) r+=m;
       else  if (r>m) r-=m;

    return r;
}
#else
int mul_mod(int a, int b, int m)
{
  register unsigned int n, n1, n2;
  double nd, d;
  
  if (m <= 1) lidia_error_handler("mul_mod", "modul <= 1");

  a = a % m;
  b = b % m;

  if (a < 0) a = a + m;
  if (b < 0) b = b + m;
  
  if ( m < SBASIS ) 
  {
    n = a * b;
    return( n % m );
  }

  d = 1.0 * a * b;
  
  if (d < MAXint) 
  { 
    n = (int) d; 
    n = n % m; 
  }
  else 
    if (d < MAXDBL ) 
    { 
      n = (int) (d / m);
      nd = 1.0 * n;
      nd = nd * m;
      d = d - nd /* (1.0 * n * m) */ ; 
      n = (int) d; 
    }
    else 
    {
      n1 = h_mul_mod(a % SBASIS, b, m);
      n2 = h_mul_mod(SBASIS , h_mul_mod(a / SBASIS, b, m) , m );
      if ( ( n1 < MAXIH ) && ( n2 < MAXIH ) ) 
        n = ( n1 + n2 ) % m;
      else 
      { 
        d = 1.0 * n1 + 1.0 * n2;
        if ( d < MAXint ) 
        { 
          n = (int) d; 
          n = n % m; 
        }
        else 
        {
          n = (int) (d / m);
          d = d - ( 1.0 * n * m );
          n = (int) d;
        }
      }
    }
  
  return(n);
}
#endif

int power_mod(int bas, int exp, int modul)
{
  register int erg, ergz;
   
  if (exp < 0) lidia_error_handler("power_mod", "exponent < 0");
   
  if (modul <= 1) lidia_error_handler("power_mod", "modul <= 1");
   
  if (exp == 0) return(1);
   
  ergz = bas % modul;
  if (ergz == 0) return(0);
  if (ergz < 0) ergz += modul;
   
  erg = 1;
   
  while (1) 
  { 
    if (exp & 1 ) 
    {
      erg = h_mul_mod(erg, ergz, modul);
      if (exp == 1) return(erg);
    }
    exp >>= 1;
    ergz = h_mul_mod(ergz, ergz, modul);
  }
}
int legendre(int n, int p)
{
  if ( n == 0 )          return 0;
  else                   return (power_mod(n, (p-1)>>1, p) == 1 ? 1 : -1 );
}

int invert(int a, int b)
{
  register int r, q,
               xs = 0,
               xss = 1,
               lb = b;

  if (b == 0)       return(-1);
   
  while (1) 
  {
    r = a % lb; 
    if ( r == 0 ) break;
    q = a / lb;
    a = lb; 
    lb = r;
    r = xss - q * xs;
    xss = xs; 
    xs = r;
  }
  
  if ( abs(lb) != 1 )   return (-1);
  
  r = ( lb > 0 ) ? xs : -xs;

  if ( r<0 ) r += b;

  return r;
} 

int jacobi(int b, int n)
{
  int i = 1, k, m;
  unsigned char help;

  for (;;)
  {
    k = 0;
    while ( !(b & 1) )
    {
      k++;
      b >>= 1;
    }
    
    help = n & 15;

    if ((k & 1) && (((help * help - 1) & 15) == 8))
      i = -i;

    if (b == 1)      return (i);

    if (((help - 1) * ((b - 1) & 7) & 7) == 4)
      i = -i;
    
    m = b;
    b = n % b;
    if (b == 0)      return (0);
    n = m;
  }
}

int ressol(int a, int p)
{
  register int r, n, c, z, k, t, s;  
   
  if ( p == 2 )    return(a);
  
                          // (1) p = 3 mod 4 
  if ( (p & 0x3) == 3 )    return( power_mod(a, (p+1) >> 2 , p) );
   
                          // (2) initialisation:
                          // compute k, s : p = 2^s (2k+1) + 1 
  k = p - 1;
  s = 0;
  
  while ( !(k & 1) ) 
  { 
    ++s; 
    k >>= 1; 
  }
  
  k = (k - 1) >> 1;
   
                           // (3) initial values
  r = power_mod(a, k, p);
  
  n = mul_mod(r, r, p);
  n = mul_mod(n, a, p);
  r = mul_mod(r, a, p);

  if ( n == 1 )    return(r);
   
                            // (4) no quadratic residue
  z = 2;
  while ( jacobi(z,p) == 1 ) ++z;
   
  c = power_mod(z, (k << 1) + 1, p);
  
                            // (5) iteration
  while (n > 1)
  {
    k = n;
    t = s;
    s = 0;
    
    while (k != 1)
    {
      k = mul_mod(k, k, p); 
      s++; 
    }
    
    t = t - s;
    
    c = power_mod(c, 1 << (t-1), p);
    r = mul_mod(r, c, p);
    c = mul_mod(c, c, p);
    n = mul_mod(n, c, p);
  } 
  
  return(r);
} 


