/*************************************************
* Montgomery Reducer Source File                 *
* (C) 1999-2004 The Botan Project                *
*************************************************/

#include <botan/montgom.h>
#include <botan/numthry.h>
#include <botan/mp_types.h>

namespace Botan {

/*************************************************
* Precompute values                              *
*************************************************/
MontgomeryReducer::MontgomeryReducer(const BigInt& mod) : ModularReducer(mod)
   {
   if(modulus.is_even())
      throw Invalid_Argument("MontgomeryReducer: Modulus must be odd");

   rbits = modulus.bits();

   R.set_bit(rbits);
   BigInt R_inv = inverse_mod(R, modulus);
   R_inv <<= rbits;
   R_inv--;
   N_inv = R_inv / modulus;

   RR = BigInt(BigInt::Power2, 2*rbits);
   RR %= modulus;
   }

/*************************************************
* Convert to the Montgomery form                 *
*************************************************/
BigInt MontgomeryReducer::convert_in(const BigInt& x) const
   {
   if(x.is_negative())
      throw Invalid_Argument("MontgomeryReducer::convert_in: Invalid input");
   if(x >= modulus)
      return multiply(x % modulus, RR);
   return multiply(x, RR);
   }

/*************************************************
* Convert from the Montgomery form               *
*************************************************/
BigInt MontgomeryReducer::convert_out(const BigInt& x) const
   {
   return reduce(x);
   }

/*************************************************
* Montgomery Multiplication                      *
*************************************************/
BigInt MontgomeryReducer::multiply(const BigInt& x, const BigInt& y) const
   {
   // FIXME: merge the multiply and the reduction
   return reduce(x * y);
   }

/*************************************************
* Montgomery Reduction                           *
*************************************************/
BigInt MontgomeryReducer::reduce(const BigInt& x) const
   {
   // FIXME: this could be *much* faster
   t1 = x;
   t1.mask_bits(rbits);
   t1 *= N_inv;
   t1.mask_bits(rbits);
   t1 *= modulus;
   t1 += x;
   t1 >>= rbits;

   if(t1 > modulus)
      t1 -= modulus;

   if(x.is_negative() && t1.is_nonzero())
      t1 = modulus - t1;
   return t1;
   }

}
