umodarith.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. * Copyright (c) 2008-2010 Stefan Krah. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
  16. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. * SUCH DAMAGE.
  26. */
  27. #ifndef UMODARITH_H
  28. #define UMODARITH_H
  29. #include "constants.h"
  30. #include "mpdecimal.h"
  31. #include "typearith.h"
  32. /**************************************************************************/
  33. /* ANSI C99 modular arithmetic */
  34. /**************************************************************************/
  35. /*
  36. * Restrictions: a < m and b < m
  37. * ACL2 proof: umodarith.lisp: addmod-correct
  38. */
  39. static inline mpd_uint_t
  40. addmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
  41. {
  42. mpd_uint_t s;
  43. s = a + b;
  44. s = (s < a) ? s - m : s;
  45. s = (s >= m) ? s - m : s;
  46. return s;
  47. }
  48. /*
  49. * Restrictions: a < m and b < m
  50. * ACL2 proof: umodarith.lisp: submod-2-correct
  51. */
  52. static inline mpd_uint_t
  53. submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
  54. {
  55. mpd_uint_t d;
  56. d = a - b;
  57. d = (a < b) ? d + m : d;
  58. return d;
  59. }
  60. /*
  61. * Restrictions: a < 2m and b < 2m
  62. * ACL2 proof: umodarith.lisp: ext-submod-2-correct
  63. */
  64. static inline mpd_uint_t
  65. ext_submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
  66. {
  67. mpd_uint_t d;
  68. a = (a >= m) ? a - m : a;
  69. b = (b >= m) ? b - m : b;
  70. d = a - b;
  71. d = (a < b) ? d + m : d;
  72. return d;
  73. }
  74. /* divide 2 words by v, return remainder */
  75. static inline mpd_uint_t
  76. bigmod2(mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t v)
  77. {
  78. mpd_uint_t r1, r2, w;
  79. _mpd_div_word(&w, &r1, hi, v);
  80. _mpd_div_words(&w, &r2, r1, lo, v);
  81. return r2;
  82. }
  83. /* subtract double word hi,lo from a */
  84. static inline mpd_uint_t
  85. dw_submod(mpd_uint_t a, mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
  86. {
  87. mpd_uint_t d, r;
  88. r = bigmod2(hi, lo, m);
  89. d = a - r;
  90. d = (a < r) ? d + m : d;
  91. return d;
  92. }
  93. #ifdef CONFIG_64
  94. /**************************************************************************/
  95. /* 64-bit modular arithmetic */
  96. /**************************************************************************/
  97. /*
  98. * Description of the algorithm in apfloat.pdf, Chapter 7.1.1,
  99. * by Mikko Tommila: http://www.apfloat.org/apfloat/2.41/
  100. *
  101. * ACL2 proof: umodarith.lisp: section "Fast modular reduction"
  102. */
  103. static inline mpd_uint_t
  104. x64_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
  105. {
  106. mpd_uint_t hi, lo, x, y;
  107. _mpd_mul_words(&hi, &lo, a, b);
  108. if (m & (1ULL<<32)) { /* P1 */
  109. /* first reduction */
  110. x = y = hi;
  111. hi >>= 32;
  112. x = lo - x;
  113. if (x > lo) hi--;
  114. y <<= 32;
  115. lo = y + x;
  116. if (lo < y) hi++;
  117. /* second reduction */
  118. x = y = hi;
  119. hi >>= 32;
  120. x = lo - x;
  121. if (x > lo) hi--;
  122. y <<= 32;
  123. lo = y + x;
  124. if (lo < y) hi++;
  125. return (hi || lo >= m ? lo - m : lo);
  126. }
  127. else if (m & (1ULL<<34)) { /* P2 */
  128. /* first reduction */
  129. x = y = hi;
  130. hi >>= 30;
  131. x = lo - x;
  132. if (x > lo) hi--;
  133. y <<= 34;
  134. lo = y + x;
  135. if (lo < y) hi++;
  136. /* second reduction */
  137. x = y = hi;
  138. hi >>= 30;
  139. x = lo - x;
  140. if (x > lo) hi--;
  141. y <<= 34;
  142. lo = y + x;
  143. if (lo < y) hi++;
  144. /* third reduction */
  145. x = y = hi;
  146. hi >>= 30;
  147. x = lo - x;
  148. if (x > lo) hi--;
  149. y <<= 34;
  150. lo = y + x;
  151. if (lo < y) hi++;
  152. return (hi || lo >= m ? lo - m : lo);
  153. }
  154. else { /* P3 */
  155. /* first reduction */
  156. x = y = hi;
  157. hi >>= 24;
  158. x = lo - x;
  159. if (x > lo) hi--;
  160. y <<= 40;
  161. lo = y + x;
  162. if (lo < y) hi++;
  163. /* second reduction */
  164. x = y = hi;
  165. hi >>= 24;
  166. x = lo - x;
  167. if (x > lo) hi--;
  168. y <<= 40;
  169. lo = y + x;
  170. if (lo < y) hi++;
  171. /* third reduction */
  172. x = y = hi;
  173. hi >>= 24;
  174. x = lo - x;
  175. if (x > lo) hi--;
  176. y <<= 40;
  177. lo = y + x;
  178. if (lo < y) hi++;
  179. return (hi || lo >= m ? lo - m : lo);
  180. }
  181. }
  182. static inline void
  183. x64_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
  184. {
  185. *a = x64_mulmod(*a, w, m);
  186. *b = x64_mulmod(*b, w, m);
  187. }
  188. static inline void
  189. x64_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
  190. mpd_uint_t m)
  191. {
  192. *a0 = x64_mulmod(*a0, b0, m);
  193. *a1 = x64_mulmod(*a1, b1, m);
  194. }
  195. static inline mpd_uint_t
  196. x64_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
  197. {
  198. mpd_uint_t r = 1;
  199. while (exp > 0) {
  200. if (exp & 1)
  201. r = x64_mulmod(r, base, umod);
  202. base = x64_mulmod(base, base, umod);
  203. exp >>= 1;
  204. }
  205. return r;
  206. }
  207. /* END CONFIG_64 */
  208. #else /* CONFIG_32 */
  209. /**************************************************************************/
  210. /* 32-bit modular arithmetic */
  211. /**************************************************************************/
  212. #if defined(ANSI)
  213. #if !defined(LEGACY_COMPILER)
  214. /* HAVE_UINT64_T */
  215. static inline mpd_uint_t
  216. std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
  217. {
  218. return ((mpd_uuint_t) a * b) % m;
  219. }
  220. static inline void
  221. std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
  222. {
  223. *a = ((mpd_uuint_t) *a * w) % m;
  224. *b = ((mpd_uuint_t) *b * w) % m;
  225. }
  226. static inline void
  227. std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
  228. mpd_uint_t m)
  229. {
  230. *a0 = ((mpd_uuint_t) *a0 * b0) % m;
  231. *a1 = ((mpd_uuint_t) *a1 * b1) % m;
  232. }
  233. /* END HAVE_UINT64_T */
  234. #else
  235. /* LEGACY_COMPILER */
  236. static inline mpd_uint_t
  237. std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
  238. {
  239. mpd_uint_t hi, lo, q, r;
  240. _mpd_mul_words(&hi, &lo, a, b);
  241. _mpd_div_words(&q, &r, hi, lo, m);
  242. return r;
  243. }
  244. static inline void
  245. std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
  246. {
  247. *a = std_mulmod(*a, w, m);
  248. *b = std_mulmod(*b, w, m);
  249. }
  250. static inline void
  251. std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
  252. mpd_uint_t m)
  253. {
  254. *a0 = std_mulmod(*a0, b0, m);
  255. *a1 = std_mulmod(*a1, b1, m);
  256. }
  257. /* END LEGACY_COMPILER */
  258. #endif
  259. static inline mpd_uint_t
  260. std_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
  261. {
  262. mpd_uint_t r = 1;
  263. while (exp > 0) {
  264. if (exp & 1)
  265. r = std_mulmod(r, base, umod);
  266. base = std_mulmod(base, base, umod);
  267. exp >>= 1;
  268. }
  269. return r;
  270. }
  271. #endif /* ANSI CONFIG_32 */
  272. /**************************************************************************/
  273. /* Pentium Pro modular arithmetic */
  274. /**************************************************************************/
  275. /*
  276. * Description of the algorithm in apfloat.pdf, Chapter 7.1.1, by Mikko Tommila.
  277. * http://www.apfloat.org/apfloat/2.41/
  278. *
  279. * Remarks:
  280. *
  281. * - dinvmod points to an array of three uint32_t, which is interpreted
  282. * as an 80 bit long double.
  283. * - Intel compilers prior to version 11 do not seem to handle the
  284. * __GNUC__ inline assembly correctly.
  285. * - random tests are provided in tests/extended/ppro_mulmod.c
  286. */
  287. #if defined(PPRO)
  288. #if defined(ASM)
  289. /* all operands < dmod */
  290. static inline mpd_uint_t
  291. ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
  292. {
  293. mpd_uint_t retval;
  294. asm (
  295. "fildl %2\n\t"
  296. "fildl %1\n\t"
  297. "fmulp %%st, %%st(1)\n\t"
  298. "fldt (%4)\n\t"
  299. "fmul %%st(1), %%st\n\t"
  300. "flds %5\n\t"
  301. "fadd %%st, %%st(1)\n\t"
  302. "fsubrp %%st, %%st(1)\n\t"
  303. "fldl (%3)\n\t"
  304. "fmulp %%st, %%st(1)\n\t"
  305. "fsubrp %%st, %%st(1)\n\t"
  306. "fistpl %0\n\t"
  307. : "=m" (retval)
  308. : "m" (a), "m" (b), "r" (dmod), "r" (dinvmod), "m" (MPD_TWO63)
  309. : "st", "memory"
  310. );
  311. return retval;
  312. }
  313. /* all operands < dmod */
  314. static inline void
  315. ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
  316. double *dmod, uint32_t *dinvmod)
  317. {
  318. asm (
  319. "fildl %2\n\t"
  320. "fildl (%1)\n\t"
  321. "fmul %%st(1), %%st\n\t"
  322. "fxch %%st(1)\n\t"
  323. "fildl (%0)\n\t"
  324. "fmulp %%st, %%st(1) \n\t"
  325. "fldt (%4)\n\t"
  326. "flds %5\n\t"
  327. "fld %%st(2)\n\t"
  328. "fmul %%st(2)\n\t"
  329. "fadd %%st(1)\n\t"
  330. "fsub %%st(1)\n\t"
  331. "fmull (%3)\n\t"
  332. "fsubrp %%st, %%st(3)\n\t"
  333. "fxch %%st(2)\n\t"
  334. "fistpl (%0)\n\t"
  335. "fmul %%st(2)\n\t"
  336. "fadd %%st(1)\n\t"
  337. "fsubp %%st, %%st(1)\n\t"
  338. "fmull (%3)\n\t"
  339. "fsubrp %%st, %%st(1)\n\t"
  340. "fistpl (%1)\n\t"
  341. : : "r" (a0), "r" (a1), "m" (w),
  342. "r" (dmod), "r" (dinvmod),
  343. "m" (MPD_TWO63)
  344. : "st", "memory"
  345. );
  346. }
  347. /* all operands < dmod */
  348. static inline void
  349. ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
  350. double *dmod, uint32_t *dinvmod)
  351. {
  352. asm (
  353. "fildl %3\n\t"
  354. "fildl (%2)\n\t"
  355. "fmulp %%st, %%st(1)\n\t"
  356. "fildl %1\n\t"
  357. "fildl (%0)\n\t"
  358. "fmulp %%st, %%st(1)\n\t"
  359. "fldt (%5)\n\t"
  360. "fld %%st(2)\n\t"
  361. "fmul %%st(1), %%st\n\t"
  362. "fxch %%st(1)\n\t"
  363. "fmul %%st(2), %%st\n\t"
  364. "flds %6\n\t"
  365. "fldl (%4)\n\t"
  366. "fxch %%st(3)\n\t"
  367. "fadd %%st(1), %%st\n\t"
  368. "fxch %%st(2)\n\t"
  369. "fadd %%st(1), %%st\n\t"
  370. "fxch %%st(2)\n\t"
  371. "fsub %%st(1), %%st\n\t"
  372. "fxch %%st(2)\n\t"
  373. "fsubp %%st, %%st(1)\n\t"
  374. "fxch %%st(1)\n\t"
  375. "fmul %%st(2), %%st\n\t"
  376. "fxch %%st(1)\n\t"
  377. "fmulp %%st, %%st(2)\n\t"
  378. "fsubrp %%st, %%st(3)\n\t"
  379. "fsubrp %%st, %%st(1)\n\t"
  380. "fxch %%st(1)\n\t"
  381. "fistpl (%2)\n\t"
  382. "fistpl (%0)\n\t"
  383. : : "r" (a0), "m" (b0), "r" (a1), "m" (b1),
  384. "r" (dmod), "r" (dinvmod),
  385. "m" (MPD_TWO63)
  386. : "st", "memory"
  387. );
  388. }
  389. /* END PPRO GCC ASM */
  390. #elif defined(MASM)
  391. /* all operands < dmod */
  392. static inline mpd_uint_t __cdecl
  393. ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
  394. {
  395. mpd_uint_t retval;
  396. __asm {
  397. mov eax, dinvmod
  398. mov edx, dmod
  399. fild b
  400. fild a
  401. fmulp st(1), st
  402. fld TBYTE PTR [eax]
  403. fmul st, st(1)
  404. fld MPD_TWO63
  405. fadd st(1), st
  406. fsubp st(1), st
  407. fld QWORD PTR [edx]
  408. fmulp st(1), st
  409. fsubp st(1), st
  410. fistp retval
  411. }
  412. return retval;
  413. }
  414. /* all operands < dmod */
  415. static inline mpd_uint_t __cdecl
  416. ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
  417. double *dmod, uint32_t *dinvmod)
  418. {
  419. __asm {
  420. mov ecx, dmod
  421. mov edx, a1
  422. mov ebx, dinvmod
  423. mov eax, a0
  424. fild w
  425. fild DWORD PTR [edx]
  426. fmul st, st(1)
  427. fxch st(1)
  428. fild DWORD PTR [eax]
  429. fmulp st(1), st
  430. fld TBYTE PTR [ebx]
  431. fld MPD_TWO63
  432. fld st(2)
  433. fmul st, st(2)
  434. fadd st, st(1)
  435. fsub st, st(1)
  436. fmul QWORD PTR [ecx]
  437. fsubp st(3), st
  438. fxch st(2)
  439. fistp DWORD PTR [eax]
  440. fmul st, st(2)
  441. fadd st, st(1)
  442. fsubrp st(1), st
  443. fmul QWORD PTR [ecx]
  444. fsubp st(1), st
  445. fistp DWORD PTR [edx]
  446. }
  447. }
  448. /* all operands < dmod */
  449. static inline void __cdecl
  450. ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
  451. double *dmod, uint32_t *dinvmod)
  452. {
  453. __asm {
  454. mov ecx, dmod
  455. mov edx, a1
  456. mov ebx, dinvmod
  457. mov eax, a0
  458. fild b1
  459. fild DWORD PTR [edx]
  460. fmulp st(1), st
  461. fild b0
  462. fild DWORD PTR [eax]
  463. fmulp st(1), st
  464. fld TBYTE PTR [ebx]
  465. fld st(2)
  466. fmul st, st(1)
  467. fxch st(1)
  468. fmul st, st(2)
  469. fld DWORD PTR MPD_TWO63
  470. fld QWORD PTR [ecx]
  471. fxch st(3)
  472. fadd st, st(1)
  473. fxch st(2)
  474. fadd st, st(1)
  475. fxch st(2)
  476. fsub st, st(1)
  477. fxch st(2)
  478. fsubrp st(1), st
  479. fxch st(1)
  480. fmul st, st(2)
  481. fxch st(1)
  482. fmulp st(2), st
  483. fsubp st(3), st
  484. fsubp st(1), st
  485. fxch st(1)
  486. fistp DWORD PTR [edx]
  487. fistp DWORD PTR [eax]
  488. }
  489. }
  490. #endif /* PPRO MASM (_MSC_VER) */
  491. /* all operands < dmod */
  492. static inline mpd_uint_t
  493. ppro_powmod(mpd_uint_t base, mpd_uint_t exp, double *dmod, uint32_t *dinvmod)
  494. {
  495. mpd_uint_t r = 1;
  496. while (exp > 0) {
  497. if (exp & 1)
  498. r = ppro_mulmod(r, base, dmod, dinvmod);
  499. base = ppro_mulmod(base, base, dmod, dinvmod);
  500. exp >>= 1;
  501. }
  502. return r;
  503. }
  504. #endif /* PPRO */
  505. #endif /* CONFIG_32 */
  506. #endif /* UMODARITH_H */