/*1* Copyright (c) 2008-2020 Stefan Krah. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/262728#include "mpdecimal.h"2930#include <assert.h>3132#include "constants.h"33#include "crt.h"34#include "numbertheory.h"35#include "typearith.h"36#include "umodarith.h"373839/* Bignum: Chinese Remainder Theorem, extends the maximum transform length. */404142/* Multiply P1P2 by v, store result in w. */43static inline void44_crt_mulP1P2_3(mpd_uint_t w[3], mpd_uint_t v)45{46mpd_uint_t hi1, hi2, lo;4748_mpd_mul_words(&hi1, &lo, LH_P1P2, v);49w[0] = lo;5051_mpd_mul_words(&hi2, &lo, UH_P1P2, v);52lo = hi1 + lo;53if (lo < hi1) hi2++;5455w[1] = lo;56w[2] = hi2;57}5859/* Add 3 words from v to w. The result is known to fit in w. */60static inline void61_crt_add3(mpd_uint_t w[3], mpd_uint_t v[3])62{63mpd_uint_t carry;6465w[0] = w[0] + v[0];66carry = (w[0] < v[0]);6768w[1] = w[1] + v[1];69if (w[1] < v[1]) w[2]++;7071w[1] = w[1] + carry;72if (w[1] < carry) w[2]++;7374w[2] += v[2];75}7677/* Divide 3 words in u by v, store result in w, return remainder. */78static inline mpd_uint_t79_crt_div3(mpd_uint_t *w, const mpd_uint_t *u, mpd_uint_t v)80{81mpd_uint_t r1 = u[2];82mpd_uint_t r2;8384if (r1 < v) {85w[2] = 0;86}87else {88_mpd_div_word(&w[2], &r1, u[2], v); /* GCOV_NOT_REACHED */89}9091_mpd_div_words(&w[1], &r2, r1, u[1], v);92_mpd_div_words(&w[0], &r1, r2, u[0], v);9394return r1;95}969798/*99* Chinese Remainder Theorem:100* Algorithm from Joerg Arndt, "Matters Computational",101* Chapter 37.4.1 [http://www.jjj.de/fxt/]102*103* See also Knuth, TAOCP, Volume 2, 4.3.2, exercise 7.104*/105106/*107* CRT with carry: x1, x2, x3 contain numbers modulo p1, p2, p3. For each108* triple of members of the arrays, find the unique z modulo p1*p2*p3, with109* zmax = p1*p2*p3 - 1.110*111* In each iteration of the loop, split z into result[i] = z % MPD_RADIX112* and carry = z / MPD_RADIX. Let N be the size of carry[] and cmax the113* maximum carry.114*115* Limits for the 32-bit build:116*117* N = 2**96118* cmax = 7711435591312380274119*120* Limits for the 64 bit build:121*122* N = 2**192123* cmax = 627710135393475385904124401220046371710124*125* The following statements hold for both versions:126*127* 1) cmax + zmax < N, so the addition does not overflow.128*129* 2) (cmax + zmax) / MPD_RADIX == cmax.130*131* 3) If c <= cmax, then c_next = (c + zmax) / MPD_RADIX <= cmax.132*/133void134crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize)135{136mpd_uint_t p1 = mpd_moduli[P1];137mpd_uint_t umod;138#ifdef PPRO139double dmod;140uint32_t dinvmod[3];141#endif142mpd_uint_t a1, a2, a3;143mpd_uint_t s;144mpd_uint_t z[3], t[3];145mpd_uint_t carry[3] = {0,0,0};146mpd_uint_t hi, lo;147mpd_size_t i;148149for (i = 0; i < rsize; i++) {150151a1 = x1[i];152a2 = x2[i];153a3 = x3[i];154155SETMODULUS(P2);156s = ext_submod(a2, a1, umod);157s = MULMOD(s, INV_P1_MOD_P2);158159_mpd_mul_words(&hi, &lo, s, p1);160lo = lo + a1;161if (lo < a1) hi++;162163SETMODULUS(P3);164s = dw_submod(a3, hi, lo, umod);165s = MULMOD(s, INV_P1P2_MOD_P3);166167z[0] = lo;168z[1] = hi;169z[2] = 0;170171_crt_mulP1P2_3(t, s);172_crt_add3(z, t);173_crt_add3(carry, z);174175x1[i] = _crt_div3(carry, carry, MPD_RADIX);176}177178assert(carry[0] == 0 && carry[1] == 0 && carry[2] == 0);179}180181182