//
// 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
//		 MM , Sep 20 1996, interface version
//

//
// Arithmetical operations for type udigit.
// Use of gmp kernel functions.
// RV = return value.
//


#ifndef LIDIA_HAVE_GMP_H
#define LIDIA_HAVE_GMP_H

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)

extern "C" {
#include <LiDIA:gmp.h>
#include <LiDIA:gmp-impl.h>
#include <LiDIA:longlong.h>
};
#else
extern "C" {
#include <LiDIA/gmp.h>
#include <LiDIA/gmp-impl.h>
#include <LiDIA/longlong.h>
};
#endif

#endif


typedef UWtype	udigit;

#define UDIGIT_NBITS W_TYPE_SIZE


inline
udigit max_udigit()
{
  return (udigit)((1<<(UDIGIT_NBITS-1))-1+(1<<(UDIGIT_NBITS-1)));
}

inline
udigit max_udigit_modulus()
{
  return (udigit)((1<<(UDIGIT_NBITS-1))-1+(1<<(UDIGIT_NBITS-1)));
}


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


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
{
  	udigit new_carry;
  	add_ssaaaa(new_carry, sum, 0, a, 0, b);
	if (carry == 0)
	  	return new_carry;
	add_ssaaaa(new_carry, sum, new_carry, sum, 0, carry);
	return new_carry;
}


inline
udigit udigit_subtract(udigit & diff, 
                  udigit a, udigit b,
                  udigit carry = 0)
        //-RV * 2^base + diff = a - b - carry
{
  	udigit new_carry;
	sub_ddmmss(new_carry, diff, 0, a, 0, b);
	if (carry == 0)
	  	return -new_carry;
	sub_ddmmss(new_carry, diff, new_carry, diff, 0, carry);
	return -new_carry;
}

inline
udigit udigit_multiply(udigit & prod, 
                  udigit a, udigit b)
        //RV * 2^base + prod = a * b
{
  	udigit carry;
  	umul_ppmm(carry, prod, a, b);
	return carry;
}

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
{
        udigit rem;

#if !UDIV_NEEDS_NORMALIZATION
        udiv_qrnnd(quot, rem, a1, a0, b);
#else  /* UDIV_NEEDS_NORMALIZATION */
#if 0
        register udigit qd = 0, carry;
        register int i = 0;
        for (i = 0; i < (SIZEOF_LONG)*8; i++)
        {
                carry = a1 >> ((SIZEOF_LONG)*8-1);
                a1 = (a1 << 1) | (a0 >> ((SIZEOF_LONG)*8-1));
                a0 = a0 << 1;
                qd <<= 1;
                if (carry || a1 >= b)
                {
                        qd += 1;
                        a1 -= b;
                }
        }
        quot = qd;
        rem = a1;
#else
        udigit lz;
        count_leading_zeros (lz, b);
        if (lz != 0)
        {
                /* Normalize, i.e. make the most significant bit of the
                   denominator set.  */
                b = b << lz;
                a1 = (a1 << lz) | (a0 >> (SI_TYPE_SIZE - lz));
                a0 = a0 << lz;
        }
        udiv_qrnnd(quot, rem, a1, a0, b);
        rem = rem >> lz;
#endif
#endif  /* UDIV_NEEDS_NORMALIZATION */

        return rem;
}



//
// 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
{
	udigit carry, rem;
	add_ssaaaa(carry, rem, 0, a, 0, b);
	if (carry)
	  	sub_ddmmss(a, rem, carry, rem, 0, p);
	else
	  	if (rem >= p)
		  	rem = rem - p;

	return rem;
}

inline
udigit udigit_subtract_mod(udigit a, udigit b, udigit p)
		//RV = (a - b) % p
		//assumes a,b < p
{
	udigit rem;
	if (a >= b)
	  	rem = a - b;
	else
	  	rem = (p - b) + a;
	return rem;
}

inline
udigit udigit_multiply_mod(udigit a, udigit b, udigit p)
          // a,b must be less than p
{
	udigit carry, prod, quot;
	umul_ppmm(carry, prod, a, b);
	return udigit_divide(quot, carry, prod, p);
}



