11919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
2ecff6554d3c3be05df7416c780ddec219da72a3dStefan Krah * Copyright (c) 2008-2016 Stefan Krah. All rights reserved.
31919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
41919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Redistribution and use in source and binary forms, with or without
51919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * modification, are permitted provided that the following conditions
61919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * are met:
71919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
81919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * 1. Redistributions of source code must retain the above copyright
91919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *    notice, this list of conditions and the following disclaimer.
101919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
111919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * 2. Redistributions in binary form must reproduce the above copyright
121919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *    notice, this list of conditions and the following disclaimer in the
131919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *    documentation and/or other materials provided with the distribution.
141919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
151919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
161919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * SUCH DAMAGE.
261919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
281919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#ifndef UMODARITH_H
301919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#define UMODARITH_H
311919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
321919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
331919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#include "constants.h"
341919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#include "mpdecimal.h"
351919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#include "typearith.h"
361919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
371919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
381919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* Bignum: Low level routines for unsigned modular arithmetic. These are
391919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah   used in the fast convolution functions for very large coefficients. */
401919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
411919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
421919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
431919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*                        ANSI modular arithmetic                         */
441919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
451919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
461919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
471919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
481919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Restrictions: a < m and b < m
491919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ACL2 proof: umodarith.lisp: addmod-correct
501919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
511919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
521919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahaddmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
531919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
541919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t s;
551919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
561919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    s = a + b;
571919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    s = (s < a) ? s - m : s;
581919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    s = (s >= m) ? s - m : s;
591919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
601919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return s;
611919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
621919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
631919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
641919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Restrictions: a < m and b < m
651919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ACL2 proof: umodarith.lisp: submod-2-correct
661919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
671919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
681919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahsubmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
691919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
701919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t d;
711919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
721919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    d = a - b;
731919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    d = (a < b) ? d + m : d;
741919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
751919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return d;
761919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
771919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
781919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
791919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Restrictions: a < 2m and b < 2m
801919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ACL2 proof: umodarith.lisp: section ext-submod
811919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
821919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
831919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahext_submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
841919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
851919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t d;
861919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
871919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    a = (a >= m) ? a - m : a;
881919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    b = (b >= m) ? b - m : b;
891919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
901919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    d = a - b;
911919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    d = (a < b) ? d + m : d;
921919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
931919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return d;
941919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
951919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
96752bfb71d8cdcdd355e8efcaf7c3eba434573f05Stefan Krah/*
971919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Reduce double word modulo m.
981919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Restrictions: m != 0
991919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ACL2 proof: umodarith.lisp: section dw-reduce
1001919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
1011919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
1021919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahdw_reduce(mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
1031919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
1041919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t r1, r2, w;
1051919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1061919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    _mpd_div_word(&w, &r1, hi, m);
1071919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    _mpd_div_words(&w, &r2, r1, lo, m);
1081919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1091919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return r2;
1101919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
1111919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1121919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
1131919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Subtract double word from a.
1141919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Restrictions: a < m
1151919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * ACL2 proof: umodarith.lisp: section dw-submod
1161919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
1171919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
1181919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahdw_submod(mpd_uint_t a, mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
1191919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
1201919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t d, r;
1211919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1221919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    r = dw_reduce(hi, lo, m);
1231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    d = a - r;
1241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    d = (a < r) ? d + m : d;
1251919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1261919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return d;
1271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
1281919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#ifdef CONFIG_64
1301919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1311919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
1321919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*                        64-bit modular arithmetic                       */
1331919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
1341919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1351919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
1361919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * A proof of the algorithm is in literature/mulmod-64.txt. An ACL2
1371919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * proof is in umodarith.lisp: section "Fast modular reduction".
1381919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
1391919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Algorithm: calculate (a * b) % p:
1401919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
1411919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   a) hi, lo <- a * b       # Calculate a * b.
1421919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
1431919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   b) hi, lo <-  R(hi, lo)  # Reduce modulo p.
1441919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
1451919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   c) Repeat step b) until 0 <= hi * 2**64 + lo < 2*p.
1461919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
1471919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   d) If the result is less than p, return lo. Otherwise return lo - p.
1481919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
1491919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1501919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
1511919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahx64_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
1521919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
1531919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t hi, lo, x, y;
1541919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1551919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1561919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    _mpd_mul_words(&hi, &lo, a, b);
1571919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1581919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    if (m & (1ULL<<32)) { /* P1 */
1591919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1601919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* first reduction */
1611919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
1621919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 32;
1631919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1641919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
1651919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
1661919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1671919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 32;
1681919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
1691919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
1701919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1711919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* second reduction */
1721919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
1731919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 32;
1741919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1751919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
1761919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
1771919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1781919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 32;
1791919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
1801919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
1811919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1821919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        return (hi || lo >= m ? lo - m : lo);
1831919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
1841919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    else if (m & (1ULL<<34)) { /* P2 */
1851919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1861919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* first reduction */
1871919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
1881919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 30;
1891919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1901919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
1911919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
1921919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1931919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 34;
1941919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
1951919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
1961919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
1971919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* second reduction */
1981919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
1991919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 30;
2001919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2011919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
2021919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
2031919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2041919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 34;
2051919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
2061919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
2071919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2081919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* third reduction */
2091919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
2101919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 30;
2111919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2121919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
2131919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
2141919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2151919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 34;
2161919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
2171919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
2181919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2191919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        return (hi || lo >= m ? lo - m : lo);
2201919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
2211919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    else { /* P3 */
2221919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* first reduction */
2241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
2251919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 24;
2261919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
2281919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
2291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2301919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 40;
2311919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
2321919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
2331919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2341919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* second reduction */
2351919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
2361919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 24;
2371919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2381919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
2391919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
2401919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2411919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 40;
2421919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
2431919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
2441919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2451919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        /* third reduction */
2461919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = y = hi;
2471919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        hi >>= 24;
2481919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2491919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        x = lo - x;
2501919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (x > lo) hi--;
2511919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2521919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        y <<= 40;
2531919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        lo = y + x;
2541919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (lo < y) hi++;
2551919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2561919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        return (hi || lo >= m ? lo - m : lo);
2571919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
2581919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
2591919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2601919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
2611919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahx64_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
2621919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
2631919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a = x64_mulmod(*a, w, m);
2641919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *b = x64_mulmod(*b, w, m);
2651919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
2661919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2671919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
2681919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahx64_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
2691919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah            mpd_uint_t m)
2701919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
2711919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a0 = x64_mulmod(*a0, b0, m);
2721919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a1 = x64_mulmod(*a1, b1, m);
2731919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
2741919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2751919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
2761919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahx64_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
2771919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
2781919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t r = 1;
2791919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2801919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    while (exp > 0) {
2811919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (exp & 1)
2821919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah            r = x64_mulmod(r, base, umod);
2831919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        base = x64_mulmod(base, base, umod);
2841919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        exp >>= 1;
2851919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
2861919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2871919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return r;
2881919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
2891919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2901919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* END CONFIG_64 */
2911919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#else /* CONFIG_32 */
2921919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2931919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2941919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
2951919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*                        32-bit modular arithmetic                       */
2961919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
2971919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
2981919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#if defined(ANSI)
2991919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#if !defined(LEGACY_COMPILER)
3001919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* HAVE_UINT64_T */
3011919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
3021919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
3031919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3041919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return ((mpd_uuint_t) a * b) % m;
3051919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3061919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3071919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
3081919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
3091919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3101919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a = ((mpd_uuint_t) *a * w) % m;
3111919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *b = ((mpd_uuint_t) *b * w) % m;
3121919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3131919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3141919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
3151919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
3161919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah            mpd_uint_t m)
3171919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3181919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a0 = ((mpd_uuint_t) *a0 * b0) % m;
3191919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a1 = ((mpd_uuint_t) *a1 * b1) % m;
3201919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3211919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* END HAVE_UINT64_T */
3221919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#else
3231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* LEGACY_COMPILER */
3241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
3251919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
3261919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t hi, lo, q, r;
3281919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    _mpd_mul_words(&hi, &lo, a, b);
3291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    _mpd_div_words(&q, &r, hi, lo, m);
3301919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return r;
3311919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3321919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3331919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
3341919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
3351919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3361919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a = std_mulmod(*a, w, m);
3371919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *b = std_mulmod(*b, w, m);
3381919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3391919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3401919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
3411919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
3421919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah            mpd_uint_t m)
3431919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3441919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a0 = std_mulmod(*a0, b0, m);
3451919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    *a1 = std_mulmod(*a1, b1, m);
3461919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3471919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* END LEGACY_COMPILER */
3481919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#endif
3491919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3501919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
3511919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstd_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
3521919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
3531919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t r = 1;
3541919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3551919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    while (exp > 0) {
3561919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (exp & 1)
3571919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah            r = std_mulmod(r, base, umod);
3581919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        base = std_mulmod(base, base, umod);
3591919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        exp >>= 1;
3601919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
3611919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3621919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return r;
3631919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
3641919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#endif /* ANSI CONFIG_32 */
3651919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3661919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3671919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
3681919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*                    Pentium Pro modular arithmetic                      */
3691919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/**************************************************************************/
3701919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3711919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
3721919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * A proof of the algorithm is in literature/mulmod-ppro.txt. The FPU
3731919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * control word must be set to 64-bit precision and truncation mode
3741919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * prior to using these functions.
3751919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
3761919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Algorithm: calculate (a * b) % p:
3771919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
3781919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   p    := prime < 2**31
3791919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   pinv := (long double)1.0 / p (precalculated)
3801919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
3811919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   a) n = a * b              # Calculate exact product.
3821919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   b) qest = n * pinv        # Calculate estimate for q = n / p.
3831919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   c) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient.
3841919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   d) r = n - q * p          # Calculate remainder.
3851919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
3861919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Remarks:
3871919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *
3881919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   - p = dmod and pinv = dinvmod.
3891919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   - dinvmod points to an array of three uint32_t, which is interpreted
3901919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *     as an 80 bit long double by fldt.
3911919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   - Intel compilers prior to version 11 do not seem to handle the
3921919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *     __GNUC__ inline assembly correctly.
3931919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *   - random tests are provided in tests/extended/ppro_mulmod.c
3941919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
3951919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3961919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#if defined(PPRO)
3971919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#if defined(ASM)
3981919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
3991919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* Return (a * b) % dmod */
4001919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
4011919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
4021919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
4031919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t retval;
4041919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
405a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah    __asm__ (
406a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  %2\n\t"
407a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  %1\n\t"
408a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmulp  %%st, %%st(1)\n\t"
409a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fldt   (%4)\n\t"
410a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(1), %%st\n\t"
411a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "flds   %5\n\t"
412a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fadd   %%st, %%st(1)\n\t"
413a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubrp %%st, %%st(1)\n\t"
414a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fldl   (%3)\n\t"
415a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmulp  %%st, %%st(1)\n\t"
416a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubrp %%st, %%st(1)\n\t"
417a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fistpl %0\n\t"
418a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : "=m" (retval)
419a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : "m" (a), "m" (b), "r" (dmod), "r" (dinvmod), "m" (MPD_TWO63)
420a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : "st", "memory"
4211919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    );
4221919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
4231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return retval;
4241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
4251919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
4261919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
4271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Two modular multiplications in parallel:
4281919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a0 = (*a0 * w) % dmod
4291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a1 = (*a1 * w) % dmod
4301919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
4311919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
4321919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
4331919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah              double *dmod, uint32_t *dinvmod)
4341919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
435a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah    __asm__ (
436a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  %2\n\t"
437a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  (%1)\n\t"
438a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(1), %%st\n\t"
439a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(1)\n\t"
440a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  (%0)\n\t"
441a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmulp  %%st, %%st(1) \n\t"
442a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fldt   (%4)\n\t"
443a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "flds   %5\n\t"
444a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fld    %%st(2)\n\t"
445a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(2)\n\t"
446a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fadd   %%st(1)\n\t"
447a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsub   %%st(1)\n\t"
448a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmull  (%3)\n\t"
449a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubrp %%st, %%st(3)\n\t"
450a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(2)\n\t"
451a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fistpl (%0)\n\t"
452a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(2)\n\t"
453a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fadd   %%st(1)\n\t"
454a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubp  %%st, %%st(1)\n\t"
455a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmull  (%3)\n\t"
456a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubrp %%st, %%st(1)\n\t"
457a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fistpl (%1)\n\t"
458a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : : "r" (a0), "r" (a1), "m" (w),
459a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah                "r" (dmod), "r" (dinvmod),
460a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah                "m" (MPD_TWO63)
461a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : "st", "memory"
4621919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    );
4631919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
4641919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
4651919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
4661919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Two modular multiplications in parallel:
4671919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a0 = (*a0 * b0) % dmod
4681919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a1 = (*a1 * b1) % dmod
4691919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
4701919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void
4711919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
4721919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah             double *dmod, uint32_t *dinvmod)
4731919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
474a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah    __asm__ (
475a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  %3\n\t"
476a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  (%2)\n\t"
477a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmulp  %%st, %%st(1)\n\t"
478a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  %1\n\t"
479a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fildl  (%0)\n\t"
480a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmulp  %%st, %%st(1)\n\t"
481a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fldt   (%5)\n\t"
482a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fld    %%st(2)\n\t"
483a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(1), %%st\n\t"
484a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(1)\n\t"
485a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(2), %%st\n\t"
486a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "flds   %6\n\t"
487a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fldl   (%4)\n\t"
488a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(3)\n\t"
489a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fadd   %%st(1), %%st\n\t"
490a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(2)\n\t"
491a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fadd   %%st(1), %%st\n\t"
492a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(2)\n\t"
493a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsub   %%st(1), %%st\n\t"
494a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(2)\n\t"
495a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubp  %%st, %%st(1)\n\t"
496a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(1)\n\t"
497a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmul   %%st(2), %%st\n\t"
498a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(1)\n\t"
499a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fmulp  %%st, %%st(2)\n\t"
500a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubrp %%st, %%st(3)\n\t"
501a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fsubrp %%st, %%st(1)\n\t"
502a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fxch   %%st(1)\n\t"
503a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fistpl (%2)\n\t"
504a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            "fistpl (%0)\n\t"
505a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : : "r" (a0), "m" (b0), "r" (a1), "m" (b1),
506a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah                "r" (dmod), "r" (dinvmod),
507a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah                "m" (MPD_TWO63)
508a0346e56acc8c5371c7b6356e1862e6e01406dbbStefan Krah            : "st", "memory"
5091919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    );
5101919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
5111919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* END PPRO GCC ASM */
5121919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#elif defined(MASM)
5131919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
5141919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* Return (a * b) % dmod */
5151919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t __cdecl
5161919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
5171919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
5181919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t retval;
5191919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
5201919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    __asm {
521cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     eax, dinvmod
522cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     edx, dmod
5231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    b
5241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    a
525cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fmulp   st(1), st
526cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     TBYTE PTR [eax]
5271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(1)
528cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     MPD_TWO63
5291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fadd    st(1), st
530cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubp   st(1), st
531cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     QWORD PTR [edx]
532cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fmulp   st(1), st
533cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubp   st(1), st
534cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fistp   retval
5351919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
5361919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
5371919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return retval;
5381919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
5391919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
5401919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
5411919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Two modular multiplications in parallel:
5421919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a0 = (*a0 * w) % dmod
5431919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a1 = (*a1 * w) % dmod
5441919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
5451919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t __cdecl
5461919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
5471919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah              double *dmod, uint32_t *dinvmod)
5481919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
5491919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    __asm {
550cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     ecx, dmod
551cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     edx, a1
552cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     ebx, dinvmod
553cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     eax, a0
5541919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    w
5551919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    DWORD PTR [edx]
5561919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(1)
5571919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(1)
5581919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    DWORD PTR [eax]
559cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fmulp   st(1), st
560cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     TBYTE PTR [ebx]
561cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     MPD_TWO63
562cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     st(2)
5631919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(2)
5641919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fadd    st, st(1)
5651919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fsub    st, st(1)
5661919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    QWORD PTR [ecx]
567cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubp   st(3), st
5681919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(2)
569cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fistp   DWORD PTR [eax]
5701919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(2)
5711919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fadd    st, st(1)
572cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubrp  st(1), st
5731919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    QWORD PTR [ecx]
574cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubp   st(1), st
575cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fistp   DWORD PTR [edx]
5761919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
5771919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
5781919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
5791919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/*
5801919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah * Two modular multiplications in parallel:
5811919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a0 = (*a0 * b0) % dmod
5821919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah *      *a1 = (*a1 * b1) % dmod
5831919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah */
5841919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline void __cdecl
5851919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
5861919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah             double *dmod, uint32_t *dinvmod)
5871919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
5881919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    __asm {
589cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     ecx, dmod
590cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     edx, a1
591cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     ebx, dinvmod
592cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        mov     eax, a0
5931919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    b1
5941919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    DWORD PTR [edx]
595cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fmulp   st(1), st
5961919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    b0
5971919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fild    DWORD PTR [eax]
598cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fmulp   st(1), st
599cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     TBYTE PTR [ebx]
600cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     st(2)
6011919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(1)
6021919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(1)
6031919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(2)
604cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     DWORD PTR MPD_TWO63
605cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fld     QWORD PTR [ecx]
6061919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(3)
6071919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fadd    st, st(1)
6081919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(2)
6091919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fadd    st, st(1)
6101919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(2)
6111919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fsub    st, st(1)
6121919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(2)
613cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubrp  st(1), st
6141919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(1)
6151919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fmul    st, st(2)
6161919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(1)
617cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fmulp   st(2), st
618cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubp   st(3), st
619cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fsubp   st(1), st
6201919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        fxch    st(1)
621cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fistp   DWORD PTR [edx]
622cd9e1d02055cb963e9707fbdfe76fe884ccff94aStefan Krah        fistp   DWORD PTR [eax]
6231919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
6241919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
6251919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#endif /* PPRO MASM (_MSC_VER) */
6261919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6271919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6281919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah/* Return (base ** exp) % dmod */
6291919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahstatic inline mpd_uint_t
6301919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krahppro_powmod(mpd_uint_t base, mpd_uint_t exp, double *dmod, uint32_t *dinvmod)
6311919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah{
6321919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    mpd_uint_t r = 1;
6331919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6341919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    while (exp > 0) {
6351919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        if (exp & 1)
6361919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah            r = ppro_mulmod(r, base, dmod, dinvmod);
6371919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        base = ppro_mulmod(base, base, dmod, dinvmod);
6381919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah        exp >>= 1;
6391919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    }
6401919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6411919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah    return r;
6421919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah}
6431919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#endif /* PPRO */
6441919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#endif /* CONFIG_32 */
6451919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6461919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6471919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah#endif /* UMODARITH_H */
6481919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6491919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
6501919b7e72bc43315b32f38a6f5f01e8c717907f4Stefan Krah
651