//
// LiDIA - a library for computational number theory
//   Copyright (c) 1996 by the LiDIA Group
//
// File        : udigit_def.h
// Author      : Thomas Pfahler (TPf)
//               Volker Mueller (VM)
// Last change : TPf, Apr 10 1995, initial version
//               VM , Sep  3 1996, libI version
//		 MM , Oct  9 1996, interface version
//		 MM , Dez  4 1996, use "LIDIA_DOUBLES_LOW_HIGH"
//


#include <LiDIA/lidia.h>

typedef unsigned long udigit;

typedef union {
  double d; 
  unsigned long rep[2]; } UDIGIT_d_or_rep;


#define UDIGIT_NBITS	26
#define UDIGIT_RADIX	(1<<UDIGIT_NBITS)


#ifdef LIDIA_DOUBLES_LOW_HIGH
#define UDIGIT_LO_WD 0
#define UDIGIT_HI_WD 1
#else
#define UDIGIT_LO_WD 1
#define UDIGIT_HI_WD 0
#endif

#define UDIGIT_FetchHiLo(hi,lo,x) \
{ \
   UDIGIT_d_or_rep _l_xx; \
   _l_xx.d = x; \
   hi = _l_xx.rep[UDIGIT_HI_WD]; \
   lo = _l_xx.rep[UDIGIT_LO_WD]; \
}


//
// Arithmetical operations for type udigit.
// RV = return value.
//

inline
udigit max_udigit()
{
  return (udigit)(UDIGIT_RADIX-1);
}

inline
unsigned int bits_per_udigit()
{
  return (unsigned int)UDIGIT_NBITS;
}

inline
udigit max_udigit_modulus()
{
  return (udigit)(UDIGIT_RADIX-1);
}

inline
unsigned int bits_per_udigit_modulus()
{
  return (unsigned int)UDIGIT_NBITS;
}

inline
udigit udigit_add(udigit & sum,
                  udigit a, udigit b, udigit carry = 0)
        //RV * 2^base + sum = a + b + carry
{
  sum = a + carry;
  if (sum < (unsigned long)UDIGIT_RADIX)
    {carry = 0;}
  else
    {carry = 1; sum -= (unsigned long)UDIGIT_RADIX;}

  sum = a + b;
  if (sum >= (unsigned long)UDIGIT_RADIX)
    {carry++; sum -= (unsigned long)UDIGIT_RADIX;}

  return carry;
}

inline
udigit udigit_subtract(udigit & diff, 
                  udigit a, udigit b,
                  udigit carry = 0)
        //-RV * 2^base + diff = a - b - carry
{
  if (a < carry)
    {carry = 1; a = (unsigned long)UDIGIT_RADIX-1;}
  else
    {a -= carry; carry = 0;}

  if (a < b)

    {carry = 1; diff = (unsigned long)UDIGIT_RADIX - (b-a);}
  else
    {diff = a-b;}

  return carry;
}


inline
udigit udigit_multiply(udigit & prod, 
                       udigit a, udigit b)
        //RV * 2^base + prod = a * b
{
   register double xx;
   register unsigned long lo_wd;
   register unsigned long hi_wd;

   xx = (((double)a) * ((double)b)) + 4503599627370496.0;
   UDIGIT_FetchHiLo(hi_wd, lo_wd, xx);
   prod = lo_wd & 0x3FFFFFF;  			  // lo-part 
   return (((hi_wd<<6)|(lo_wd>>26)) & 0x3FFFFFF); // hi-part
}

inline
udigit udigit_divide(udigit & quot, 
                  udigit a1, udigit a0, udigit b)
       //a1 * 2^base + a0 = quot * b + RV
       //a1 must be less than b
{
cout << "Not yet implemented\n";
cout.flush();
return 0;
}



//
// modular functions, we assume that input is reduced modulo modul
//

inline
udigit udigit_add_mod(udigit a, udigit b, udigit p)
		//RV = (a + b) % p
		//assumes a,b < p, p > 1
{
  double c = (double) a + (double) b;
  if (c >= (double) p) 
     c -= (double) p;
  return (udigit) c;
}

inline
udigit udigit_subtract_mod(udigit a, udigit b, udigit p)
		//RV = (a - b) % p
		//assumes a,b < p, p > 1
{
  double c = (double) a - (double) b;
  if (c < 0)
     c += (double) p;
  return (udigit) c;
}

inline
udigit udigit_multiply_mod(udigit a, udigit b, udigit p)
          // a,b must be less than p, p > 1
{
  double ab = ((double) a) * ((double) b);
  register udigit q = (udigit) (ab / ((double) p));
  return (udigit) (ab - (((double) q) * ((double) p)));
}









