
//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : ressol.c 
// Author      : Thomas Denny (TD), Andreas M"uller (AM)
// Last change : AM, Feb 7 1995, initial version
//		 Oliver Morsch, Mar 1996, bigint-version
//		 MM, Sep 24 1996, cleaning up
//


#include<LiDIA/bigint.h>

//
// Input:
//    prime number p
//    0 <= a < p
//
// Output:
//    square root r of a mod p
//    0 <= r < p
// 
// Condition: p = 2^s * (2k+1) + 1
//	      where s fits in a long
//
// Algorithm: Shanks-Ressol
//


void ressol(bigint & x, const bigint & a, const bigint & p)
{
  bigint v, one;
   
  if ( p == (bigint)2 ) {
    x.assign(a);
    return;
  }
 
  //MM: used to avoid repeated casts (bigint)1
  one.assign_one(); 

  // (1) p = 3 mod 4 
  if ( (p.least_significant_digit() & 0x3) == 3 ) {
    if (jacobi(a, p) == 1) {
      add (v, p, one);
      shift_right(v, v, 2);
      power_mod(x, a, v, p);
      return;
    }
    else
      lidia_error_handler("ressol", "a is not a quadratic residue mod p");
  }

  /*
  MM: To be honest declare s and t as longs,
      otherwise you have to use intify in the
      "while (n > one) loop"
  */

  bigint r, n, c, z, k;
  long   s, t;

  // (2) initialisation:
  // compute k, s : p = 2^s (2k+1) + 1 
  // (MM: shift_right as part of the loop)
  subtract(k, p, one);
  s = 0;
  while (k.is_even())
  {
    s++;
    k.divide_by_2(); 
  }
  
  dec(k);
  k.divide_by_2();
  
  // (3) initial values
  power_mod(r, a, k, p);
  
  square(n, r);
  remainder(n, n, p); // n = (r * r) % p;
  multiply(n, n, a);
  remainder(n, n, p); // n = (n * a) % p;
  multiply(r, r, a);
  remainder(r, r, p); // r = (r * a) % p;
  
  if ( n.is_one() ) {
    x.assign(r);
    return;
  }
  
  // (4) non-quadratic residue
  z.assign((bigint)2);
  while ( jacobi(z,p) == 1 ) inc(z);
  
  v.assign(k);
  v.multiply_by_2();
  inc(v); // v = (k << 1) + 1;
  power_mod(c, z, v, p);
  
  // (5) iteration
  while (n > one)
    {
      k.assign(n);
      t  = s;
      s  = 0;
      
      while ( !k.is_one() )
	{
	  square(k, k);
	  remainder(k, k, p); // k = (k * k) % p; 
	  s++; 
	}
      
      t = t - s; 
      if (t == 0)
	lidia_error_handler("ressol", "a is not a quadratic residue mod p");
       
      shift_left(v, one, t-1); // v = 1 << (t-1);
      power_mod(c, c, v, p);
      multiply(r, r, c);
      remainder(r, r, p); // r = (r * c) % p;
      square(c, c);
      remainder(c, c, p); // c = (c * c) % p;
      multiply(n, n, c);
      remainder(n, n, p); // n = (n * c) % p;
    } 
  
  x.assign(r);
  return;
} 


void ressol_p(bigint & x, const bigint & a, const bigint & p)
{
  if (!fermat(p))
    lidia_error_handler("ressol", "p is not prime!");
  else
    ressol(x, a, p);
}
