00001 00002 /* 00003 The Extended Integer (XInt) Library 00004 A fast, portable C++ library for multi-precision integer math 00005 Copyright 2010 by Chad Nelson 00006 00007 Distributed under the Boost Software License, Version 1.0. 00008 See accompanying file LICENSE_1_0.txt or copy at 00009 http://www.boost.org/LICENSE_1_0.txt 00010 00011 See http://www.boost.org/libs/xint for library home page. 00012 */ 00013 00020 #ifndef BOOST_INCLUDED_XINT_MONTY_HPP 00021 #define BOOST_INCLUDED_XINT_MONTY_HPP 00022 00024 namespace boost { 00025 namespace xint { 00026 namespace detail { 00027 00029 BOOST_XINT_RAWINT_TPL 00030 digit_t inverse0(const BOOST_XINT_RAWINT n) { 00031 // Using the Dussé and Kalisk simplification 00032 doubledigit_t x = 2, y = 1; 00033 digit_t n0 = n[0]; 00034 for (std::size_t i = 2; i <= bits_per_digit; ++i, x <<= 1) 00035 if (x < ((n0 * y) & ((x << 1) - 1))) y += x; 00036 return digit_t(x - y); 00037 } 00038 00042 BOOST_XINT_RAWINT_TPL 00043 BOOST_XINT_RAWINT montgomeryR(const BOOST_XINT_RAWINT n) { 00044 BOOST_XINT_RAWINT r; 00045 pow2(r, bits_per_digit * n.length); 00046 return r; 00047 } 00048 00050 BOOST_XINT_RAWINT_TPL 00051 void toMontgomeryForm(BOOST_XINT_RAWINT& target, const BOOST_XINT_RAWINT n, 00052 const BOOST_XINT_RAWINT m) 00053 { 00054 target = n * montgomeryR(m) % m; 00055 } 00056 00058 BOOST_XINT_RAWINT_TPL 00059 void fromMontgomeryForm(BOOST_XINT_RAWINT& target, const BOOST_XINT_RAWINT n, 00060 const BOOST_XINT_RAWINT m) 00061 { 00062 BOOST_XINT_RAWINT mont(montgomeryR(m)), inv = invmod(mont, m); 00063 target = n * inv % m; 00064 } 00065 00069 BOOST_XINT_RAWINT_TPL 00070 BOOST_XINT_RAWINT montgomeryMultiplyMod(const BOOST_XINT_RAWINT a, const 00071 BOOST_XINT_RAWINT b, const BOOST_XINT_RAWINT n, digit_t nPrime0) 00072 { 00073 // Using the Dussé and Kalisk simplification 00074 // Unstated parameter B is a power of two representing the number of values 00075 // that a single digit can hold, i.e. digit_overflowbit 00076 // Unstated parameter L is the number of digits in the modulus, i.e. 00077 // n.length 00078 // Unstated parameter r is B^L 00079 // nPrime0 is nPrime mod B, or digit zero of nPrime 00080 00081 const std::size_t L(n.length); 00082 00083 std::size_t i = 0; 00084 BOOST_XINT_RAWINT t(a * b); 00085 00086 do { 00087 BOOST_XINT_RAWINT mi(digit_t(t[i] * nPrime0)); 00088 mi *= n; 00089 mi <<= (bits_per_digit * i); 00090 t += mi; 00091 } while (++i < L); 00092 00093 t >>= (bits_per_digit * L); // Fast divide by r 00094 00095 if (t >= n) t -= n; 00096 return t; 00097 } 00098 00099 // cMaxK sets the balance between memory/precalculations required and the number 00100 // of calculations required for an exponentiation. Increasing it can only reduce 00101 // the calculations by a small amount, whereas it increases the memory 00102 // requirements and number of precalculations by an exponential amount. 8 00103 // provides a good balance. 00104 const std::size_t cMaxK = 8; 00105 typedef boost::uint_t<cMaxK>::fast kbitdigit_t; // k bits have to fit into it 00106 typedef std::vector<kbitdigit_t> vkbitdigit_t; 00107 #define ddPowerOfTwo(p) (doubledigit_t(1) << p) 00108 00109 // The splitIntoKBitDigits function assumes that cMaxK <= bits_per_digit+1, 00110 // it won't work properly if it isn't. 00111 BOOST_STATIC_ASSERT(cMaxK <= bits_per_digit + 1); 00112 00113 // Make it a template so that the static variable won't cause problems in 00114 // header-only mode. 00115 template <typename T = void> 00116 class TUTable { 00117 public: 00118 typedef std::pair<int, int> value_t; 00119 00120 const value_t& operator[](std::size_t x) const { return mTable[x]; } 00121 00122 static const TUTable& get() { 00123 // Construct a singleton instance on demand 00124 if (mPtr.get()==0) mPtr.reset(new TUTable); 00125 return *mPtr; 00126 } 00127 00128 private: 00129 TUTable(): mTable(new value_t[ddPowerOfTwo(cMaxK)]) { 00130 value_t *p=&mTable[0], *pe=p+ddPowerOfTwo(cMaxK); 00131 *p++=std::make_pair(0, 0); 00132 int i=1; 00133 while (p!=pe) *p++=calculateValues(i++); 00134 } 00135 00136 std::pair<int, int> calculateValues(int x) { 00137 int r=0; 00138 while (1) { 00139 if (x & 0x01) return std::make_pair(r, x); 00140 ++r; 00141 x >>= 1; 00142 } 00143 } 00144 00145 static std::auto_ptr<TUTable<T> > mPtr; 00146 00147 boost::scoped_array<value_t> mTable; 00148 }; 00149 00150 template <typename T> 00151 std::auto_ptr<TUTable<T> > TUTable<T>::mPtr; 00152 00153 BOOST_XINT_RAWINT_TPL 00154 int mostEfficientK(const BOOST_XINT_RAWINT e) { 00155 doubledigit_t k = cMaxK, kTarget = log2(e) - 1; 00156 while (k > 1 && ((k - 1) * (k << ((k - 1) << 1)) 00157 / (ddPowerOfTwo(k) - k - 1)) >= kTarget) 00158 --k; 00159 return int(k); 00160 } 00161 00162 BOOST_XINT_RAWINT_TPL 00163 std::vector<BOOST_XINT_RAWINT> precalculateOddPowersOfAa(const BOOST_XINT_RAWINT 00164 a, const BOOST_XINT_RAWINT r, const BOOST_XINT_RAWINT n, std::size_t k) 00165 { 00166 BOOST_XINT_RAWINT zero, one(1); 00167 BOOST_XINT_RAWINT aa = a * r % n, aSquared = a * a % n; 00168 00169 std::vector<BOOST_XINT_RAWINT> rval; 00170 rval.reserve(std::size_t(ddPowerOfTwo(k))); 00171 rval.push_back(one); // Anything to the zeroth power is one 00172 rval.push_back(aa); // Anything to the first power is itself 00173 00174 for (doubledigit_t i = 3, ie = ddPowerOfTwo(k); i < ie; i += 2) { 00175 aa *= aSquared; 00176 aa %= n; 00177 rval.push_back(zero); // Even powers not needed or calculated 00178 rval.push_back(aa); // Odd power 00179 } 00180 00181 return rval; 00182 } 00183 00184 BOOST_XINT_RAWINT_TPL 00185 BOOST_XINT_RAWINT montgomeryPowerMod(const BOOST_XINT_RAWINT a, const 00186 BOOST_XINT_RAWINT e, const BOOST_XINT_RAWINT n) 00187 { 00188 // 0 <= a < n, n is odd 00189 // Returns a^e mod n 00190 00191 const TUTable<> &tuTable(TUTable<>::get()); 00192 00193 if (e.is_zero()) return 1; 00194 if (n.is_even()) exception_handler<>::call(__FILE__, __LINE__, exceptions:: 00195 invalid_modulus("montgomeryPowerMod requires an odd modulus")); 00196 00197 // Precalculate some values 00198 const std::size_t k(mostEfficientK(e)); 00199 const BOOST_XINT_RAWINT r(montgomeryR(n)); 00200 const digit_t nPrime0(inverse0(n)); 00201 const std::vector<BOOST_XINT_RAWINT> oddPowersOfAa( 00202 precalculateOddPowersOfAa(a, r, n, k)); 00203 00204 // Slice the exponent (e) up into k-bit digits 00205 vkbitdigit_t eDigits; 00206 { 00207 bitqueue_t q; 00208 for (const digit_t *dig = e.digits(), *i = dig, *ie = i + e.length; 00209 i <ie; ++i) q.push(*i, bits_per_digit); 00210 00211 // The bitqueue_t returns them low-bits-first, we need to start with the 00212 // high bits. 00213 while (!q.empty()) eDigits.push_back(kbitdigit_t(q.pop(k))); 00214 } 00215 00216 while (eDigits.back() == 0) eDigits.pop_back(); 00217 kbitdigit_t i = eDigits.back(); 00218 eDigits.pop_back(); 00219 00220 BOOST_XINT_RAWINT pp; 00221 if (i == 0) { 00222 pp = r % n; 00223 } else { 00224 std::pair<int, int> tu = tuTable[i]; 00225 pp = oddPowersOfAa[tu.second]; 00226 while (tu.first-- > 0) pp = montgomeryMultiplyMod(pp, pp, n, nPrime0); 00227 } 00228 00229 while (!eDigits.empty()) { 00230 i = eDigits.back(); 00231 eDigits.pop_back(); 00232 00233 if (i == 0) { 00234 int t = int(k); 00235 while (t-- > 0) pp = montgomeryMultiplyMod(pp, pp, n, nPrime0); 00236 } else { 00237 std::pair<int, int> tu = tuTable[i]; 00238 00239 std::size_t s = k - tu.first; 00240 while (s-- > 0) pp = montgomeryMultiplyMod(pp, pp, n, nPrime0); 00241 00242 pp = montgomeryMultiplyMod(pp, oddPowersOfAa[tu.second], n, 00243 nPrime0); 00244 00245 while (tu.first-- > 0) pp = montgomeryMultiplyMod(pp, pp, n, 00246 nPrime0); 00247 } 00248 } 00249 return montgomeryMultiplyMod(pp, BOOST_XINT_RAWINT(1), n, nPrime0); 00250 } 00251 00252 } // namespace detail 00253 } // namespace xint 00254 } // namespace boost 00256 00257 #endif // BOOST_INCLUDED_XINT_MONTY_HPP
© Copyright Chad Nelson, 2010-2011. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)