2 * qmod - modular arithmetic routines for normal numbers and REDC numbers
4 * Copyright (C) 1999-2007 David I. Bell and Ernest Bowen
6 * Primary author: David I. Bell
8 * Calc is open software; you can redistribute it and/or modify it under
9 * the terms of the version 2.1 of the GNU Lesser General Public License
10 * as published by the Free Software Foundation.
12 * Calc is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15 * Public License for more details.
17 * A copy of version 2.1 of the GNU Lesser General Public License is
18 * distributed with calc under the filename COPYING-LGPL. You should have
19 * received a copy with calc; if not, write to Free Software Foundation, Inc.
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * @(#) $Revision: 30.1 $
23 * @(#) $Id: qmod.c,v 30.1 2007/03/16 11:09:46 chongo Exp $
24 * @(#) $Source: /usr/local/src/bin/calc/RCS/qmod.c,v $
26 * Under source code control: 1991/05/22 23:15:07
27 * File existed as early as: 1991
29 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
39 * Structure used for caching REDC information.
42 NUMBER
*rnum
; /* modulus being cached */
43 REDC
*redc
; /* REDC information for modulus */
44 long age
; /* age counter for reallocation */
48 STATIC
long redc_age
; /* current age counter */
49 STATIC REDC_CACHE redc_cache
[MAXREDC
]; /* cached REDC info */
52 S_FUNC REDC
*qfindredc(NUMBER
*q
);
56 * qmod(q1, q2, rnd) returns zero if q1 is a multiple of q2; it
57 * q1 if q2 is zero. For other q1 and q2, it returns one of
58 * the two remainders with absolute value less than abs(q2)
59 * when q1 is divided by q2; which remainder is returned is
60 * determined by rnd and the signs and relative sizes of q1 and q2.
63 qmod(NUMBER
*q1
, NUMBER
*q2
, long rnd
)
65 ZVALUE tmp
, tmp1
, tmp2
;
68 if (qiszero(q2
)) return qlink(q1
);
69 if (qiszero(q1
)) return qlink(&_qzero_
);
70 if (qisint(q1
) && qisint(q2
)) { /* easy case */
71 zmod(q1
->num
, q2
->num
, &tmp
, rnd
);
74 return qlink(&_qzero_
);
78 return qlink(&_qone_
);
84 zmul(q1
->num
, q2
->den
, &tmp1
);
85 zmul(q2
->num
, q1
->den
, &tmp2
);
86 zmod(tmp1
, tmp2
, &tmp
, rnd
);
91 return qlink(&_qzero_
);
93 zmul(q1
->den
, q2
->den
, &tmp1
);
95 zreduce(tmp
, tmp1
, &q
->num
, &q
->den
);
103 * Given two numbers q1, q2, qquomod(q1, q2, quo, mod, rnd) assigns
104 * quo(q1, q2, rnd) to quo and mod(q1, q2, rnd) to mod. The quotient
105 * quo is always an integer, q1 = q2 * quo + mod, and if q2 is non-zero,
106 * abs(mod) < abs(q2). If q2 is zero, quo is assigned the value zero and
108 * Which of the two possible quotient-remainder pairs is assigned
109 * to quo and mod is determined by the fifth argument rnd.
110 * If rnd is zero, the remainder has the sign of q2
111 * and the quotient is rounded towards zero.
113 * The function returns TRUE or FALSE according as the remainder is
117 * q1 number to be divided
121 * rnd rounding-type specifier
124 qquomod(NUMBER
*q1
, NUMBER
*q2
, NUMBER
**quo
, NUMBER
**mod
, long rnd
)
127 ZVALUE tmp1
, tmp2
, tmp3
, tmp4
;
129 if (qiszero(q2
)) { /* zero divisor case */
130 qq
= qlink(&_qzero_
);
132 } else if (qisint(q1
) && qisint(q2
)) { /* integer args case */
133 zdiv(q1
->num
, q2
->num
, &tmp1
, &tmp2
, rnd
);
137 qq
= qlink(&_qzero_
);
144 qm
= qlink(&_qzero_
);
150 } else { /* fractional case */
151 zmul(q1
->num
, q2
->den
, &tmp1
);
152 zmul(q2
->num
, q1
->den
, &tmp2
);
153 zdiv(tmp1
, tmp2
, &tmp3
, &tmp4
, rnd
);
159 qq
= qlink(&_qzero_
);
166 qm
= qlink(&_qzero_
);
169 zmul(q1
->den
, q2
->den
, &tmp1
);
170 zreduce(tmp4
, tmp1
, &qm
->num
, &qm
->den
);
183 * Return whether or not two integers are congruent modulo a third integer.
184 * Returns TRUE if the numbers are not congruent, and FALSE if they are.
187 qcmpmod(NUMBER
*q1
, NUMBER
*q2
, NUMBER
*q3
)
189 if (qisneg(q3
) || qiszero(q3
))
190 math_error("Non-positive modulus");
191 if (qisfrac(q1
) || qisfrac(q2
) || qisfrac(q3
))
192 math_error("Non-integers for qcmpmod");
195 return zcmpmod(q1
->num
, q2
->num
, q3
->num
);
200 * Convert an integer into REDC format for use in faster modular arithmetic.
201 * The number can be negative or out of modulus range.
204 * q1 number to convert into REDC format
208 qredcin(NUMBER
*q1
, NUMBER
*q2
)
210 REDC
*rp
; /* REDC information */
211 NUMBER
*r
; /* result */
214 math_error("Non-integer for qredcin");
217 zredcencode(rp
, q1
->num
, &r
->num
);
220 return qlink(&_qzero_
);
227 * Convert a REDC format number back into a normal integer.
228 * The resulting number is in the range 0 to the modulus - 1.
231 * q1 number to convert into REDC format
235 qredcout(NUMBER
*q1
, NUMBER
*q2
)
237 REDC
*rp
; /* REDC information */
238 NUMBER
*r
; /* result */
241 math_error("Non-integer argument for rcout");
243 if (qiszero(q1
) || qisunit(q2
))
244 return qlink(&_qzero_
);
246 zredcdecode(rp
, q1
->num
, &r
->num
);
247 if (zisunit(r
->num
)) {
256 * Multiply two REDC format numbers together producing a REDC format result.
257 * This multiplication is done modulo the specified modulus.
260 * q1 REDC numbers to be multiplied
261 * q2 REDC numbers to be multiplied
265 qredcmul(NUMBER
*q1
, NUMBER
*q2
, NUMBER
*q3
)
267 REDC
*rp
; /* REDC information */
268 NUMBER
*r
; /* result */
270 if (qisfrac(q1
) || qisfrac(q2
))
271 math_error("Non-integer argument for rcmul");
273 if (qiszero(q1
) || qiszero(q2
) || qisunit(q3
))
274 return qlink(&_qzero_
);
276 zredcmul(rp
, q1
->num
, q2
->num
, &r
->num
);
282 * Square a REDC format number to produce a REDC format result.
283 * This squaring is done modulo the specified modulus.
286 * q1 REDC numbers to be squared
290 qredcsquare(NUMBER
*q1
, NUMBER
*q2
)
292 REDC
*rp
; /* REDC information */
293 NUMBER
*r
; /* result */
296 math_error("Non-integer argument for rcsq");
298 if (qiszero(q1
) || qisunit(q2
))
299 return qlink(&_qzero_
);
301 zredcsquare(rp
, q1
->num
, &r
->num
);
307 * Raise a REDC format number to the indicated power producing a REDC
308 * format result. This is done modulo the specified modulus. The
309 * power to be raised to is a normal number.
312 * q1 REDC number to be raised
313 * q2 power to be raised to
317 qredcpower(NUMBER
*q1
, NUMBER
*q2
, NUMBER
*q3
)
319 REDC
*rp
; /* REDC information */
320 NUMBER
*r
; /* result */
322 if (qisfrac(q1
) || qisfrac(q2
) || qisfrac(q2
))
323 math_error("Non-integer argument for rcpow");
325 math_error("Negative exponent argument for rcpow");
328 zredcpower(rp
, q1
->num
, q2
->num
, &r
->num
);
334 * Search for and return the REDC information for the specified number.
335 * The information is cached into a local table so that future calls
336 * for this information will be quick. If the table fills up, then
337 * the oldest cached entry is reused.
340 * q modulus to find REDC information of
345 register REDC_CACHE
*rcp
;
349 * First try for an exact pointer match in the table.
351 for (rcp
= redc_cache
; rcp
<= &redc_cache
[MAXREDC
-1]; rcp
++) {
352 if (q
== rcp
->rnum
) {
353 rcp
->age
= ++redc_age
;
359 * Search the table again looking for a value which matches.
361 for (rcp
= redc_cache
; rcp
<= &redc_cache
[MAXREDC
-1]; rcp
++) {
362 if (rcp
->age
&& (qcmp(q
, rcp
->rnum
) == 0)) {
363 rcp
->age
= ++redc_age
;
369 * Must invalidate an existing entry in the table.
370 * Find the oldest (or first unused) entry.
371 * But first make sure the modulus will be reasonable.
373 if (qisfrac(q
) || qisneg(q
)) {
374 math_error("REDC modulus must be positive odd integer");
379 for (rcp
= redc_cache
; rcp
<= &redc_cache
[MAXREDC
-1]; rcp
++) {
380 if ((bestrcp
== NULL
) || (rcp
->age
< bestrcp
->age
))
385 * Found the best entry.
386 * Free the old information for the entry if necessary,
387 * then initialize it.
393 zredcfree(rcp
->redc
);
396 rcp
->redc
= zredcalloc(q
->num
);
397 rcp
->rnum
= qlink(q
);
398 rcp
->age
= ++redc_age
;
408 for (i
= 0, rcp
= redc_cache
; i
< MAXREDC
; i
++, rcp
++) {
410 printf("%-8ld%-8ld", i
, rcp
->age
);
411 qprintnum(rcp
->rnum
, 0);
423 for (i
= 0, rcp
= redc_cache
; i
< MAXREDC
; i
++, rcp
++) {
427 zredcfree(rcp
->redc
);