/*************************************************
* GNU MP Reducer Source File                     *
* (C) 1999-2003 The Botan Project                *
*************************************************/

#include <botan/gmp_redu.h>
#include <botan/mp_types.h>
#include <gmp.h>

#define __GNU_MP_VERSION_CODE_FOR(a,b,c) ((a << 16) | (b << 8) | (c))

#define __GNU_MP_VERSION_CODE \
__GNU_MP_VERSION_CODE_FOR(__GNU_MP_VERSION, __GNU_MP_VERSION_MINOR, \
                          __GNU_MP_VERSION_PATCHLEVEL)

#if __GNU_MP_VERSION_CODE < __GNU_MP_VERSION_CODE_FOR(4,1,0)
  #error Your GNU MP install is too old, upgrade to 4.1 or later
#endif

namespace Botan {

/*************************************************
* Container for GNU MP values                    *
*************************************************/
struct mp_holder
   {
   mpz_t num;
   };

/*************************************************
* Precompute values                              *
*************************************************/
GMP_Reducer::GMP_Reducer(const BigInt& mod) : ModularReducer(mod)
   {
   gmp_mod = new mp_holder;
   mpz_init(gmp_mod->num);
   mpz_import(gmp_mod->num, modulus.sig_words(), -1, sizeof(word), 0, 0,
              modulus.data());
   }

/*************************************************
* GMP_Reducer Destructor                         *
*************************************************/
GMP_Reducer::~GMP_Reducer()
   {
   mpz_clear(gmp_mod->num);
   delete gmp_mod;
   }

/*************************************************
* Modular Multiplication                         *
*************************************************/
BigInt GMP_Reducer::multiply(const BigInt& a, const BigInt& b) const
   {
   mpz_t gmp_a, gmp_b;

   mpz_init(gmp_a);
   mpz_init(gmp_b);
   mpz_import(gmp_a, a.sig_words(), -1, sizeof(word), 0, 0, a.data());
   mpz_import(gmp_b, b.sig_words(), -1, sizeof(word), 0, 0, b.data());

   mpz_mul(gmp_a, gmp_a, gmp_b);
   mpz_mod(gmp_a, gmp_a, gmp_mod->num);

   BigInt result(BigInt::Positive, mpz_size(gmp_a));
   size_t dummy = 0;
   mpz_export(result.get_reg(), &dummy, -1, sizeof(word), 0, 0, gmp_a);

   mpz_clear(gmp_a);
   mpz_clear(gmp_b);

   if((a.sign() != b.sign()) && result.is_nonzero())
      { result = modulus - result; }
   return result;
   }

/*************************************************
* Modular Squaring                               *
*************************************************/
BigInt GMP_Reducer::square(const BigInt& n) const
   {
   mpz_t gmp_n;
   mpz_init(gmp_n);
   mpz_import(gmp_n, n.sig_words(), -1, sizeof(word), 0, 0, n.data());

   mpz_mul(gmp_n, gmp_n, gmp_n);
   mpz_mod(gmp_n, gmp_n, gmp_mod->num);

   BigInt result(BigInt::Positive, mpz_size(gmp_n));
   size_t dummy = 0;
   mpz_export(result.get_reg(), &dummy, -1, sizeof(word), 0, 0, gmp_n);

   mpz_clear(gmp_n);

   if(n.is_negative() && result.is_nonzero())
      { result = modulus - result; }
   return result;
   }

/*************************************************
* Modular Reduction                              *
*************************************************/
BigInt GMP_Reducer::reduce(const BigInt& n) const
   {
   mpz_t gmp_n;
   mpz_init(gmp_n);
   mpz_import(gmp_n, n.sig_words(), -1, sizeof(word), 0, 0, n.data());

   mpz_mod(gmp_n, gmp_n, gmp_mod->num);

   BigInt result(BigInt::Positive, mpz_size(gmp_n));
   size_t dummy = 0;
   mpz_export(result.get_reg(), &dummy, -1, sizeof(word), 0, 0, gmp_n);

   mpz_clear(gmp_n);

   if(n.is_negative() && result.is_nonzero())
      { result = modulus - result; }
   return result;
   }

}
