blob: fd9b43990706002281444177f472eda35b680eaa [file] [log] [blame]
Austin Schuhdace2a62020-08-18 10:56:48 -07001/* mini-mpq, a minimalistic implementation of a GNU GMP subset.
2
3 Contributed to the GNU project by Marco Bodrato
4
5 Acknowledgment: special thanks to Bradley Lucier for his comments
6 to the preliminary version of this code.
7
8Copyright 2018, 2019 Free Software Foundation, Inc.
9
10This file is part of the GNU MP Library.
11
12The GNU MP Library is free software; you can redistribute it and/or modify
13it under the terms of either:
14
15 * the GNU Lesser General Public License as published by the Free
16 Software Foundation; either version 3 of the License, or (at your
17 option) any later version.
18
19or
20
21 * the GNU General Public License as published by the Free Software
22 Foundation; either version 2 of the License, or (at your option) any
23 later version.
24
25or both in parallel, as here.
26
27The GNU MP Library is distributed in the hope that it will be useful, but
28WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
29or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
30for more details.
31
32You should have received copies of the GNU General Public License and the
33GNU Lesser General Public License along with the GNU MP Library. If not,
34see https://www.gnu.org/licenses/. */
35
36#include <assert.h>
37#include <limits.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41
42#include "mini-mpq.h"
43
44#ifndef GMP_LIMB_HIGHBIT
45/* Define macros and static functions already defined by mini-gmp.c */
46#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT)
47#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1))
48#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1))
49#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b))
50
51static mpz_srcptr
52mpz_roinit_normal_n (mpz_t x, mp_srcptr xp, mp_size_t xs)
53{
54 x->_mp_alloc = 0;
55 x->_mp_d = (mp_ptr) xp;
56 x->_mp_size = xs;
57 return x;
58}
59
60static void
61gmp_die (const char *msg)
62{
63 fprintf (stderr, "%s\n", msg);
64 abort();
65}
66#endif
67
68
69/* MPQ helper functions */
70static mpq_srcptr
71mpq_roinit_normal_nn (mpq_t x, mp_srcptr np, mp_size_t ns,
72 mp_srcptr dp, mp_size_t ds)
73{
74 mpz_roinit_normal_n (mpq_numref(x), np, ns);
75 mpz_roinit_normal_n (mpq_denref(x), dp, ds);
76 return x;
77}
78
79static mpq_srcptr
80mpq_roinit_zz (mpq_t x, mpz_srcptr n, mpz_srcptr d)
81{
82 return mpq_roinit_normal_nn (x, n->_mp_d, n->_mp_size,
83 d->_mp_d, d->_mp_size);
84}
85
86static void
87mpq_nan_init (mpq_t x)
88{
89 mpz_init (mpq_numref (x));
90 mpz_init (mpq_denref (x));
91}
92
93void
94mpq_init (mpq_t x)
95{
96 mpz_init (mpq_numref (x));
97 mpz_init_set_ui (mpq_denref (x), 1);
98}
99
100void
101mpq_clear (mpq_t x)
102{
103 mpz_clear (mpq_numref (x));
104 mpz_clear (mpq_denref (x));
105}
106
107static void
108mpq_canonical_sign (mpq_t r)
109{
110 int cmp = mpq_denref (r)->_mp_size;
111 if (cmp <= 0)
112 {
113 if (cmp == 0)
114 gmp_die("mpq: Fraction with zero denominator.");
115 mpz_neg (mpq_denref (r), mpq_denref (r));
116 mpz_neg (mpq_numref (r), mpq_numref (r));
117 }
118}
119
120static void
121mpq_helper_canonicalize (mpq_t r, const mpz_t num, const mpz_t den, mpz_t g)
122{
123 if (num->_mp_size == 0)
124 mpq_set_ui (r, 0, 1);
125 else
126 {
127 mpz_gcd (g, num, den);
128 mpz_tdiv_q (mpq_numref (r), num, g);
129 mpz_tdiv_q (mpq_denref (r), den, g);
130 mpq_canonical_sign (r);
131 }
132}
133
134void
135mpq_canonicalize (mpq_t r)
136{
137 mpz_t t;
138
139 mpz_init (t);
140 mpq_helper_canonicalize (r, mpq_numref (r), mpq_denref (r), t);
141 mpz_clear (t);
142}
143
144void
145mpq_swap (mpq_t a, mpq_t b)
146{
147 mpz_swap (mpq_numref (a), mpq_numref (b));
148 mpz_swap (mpq_denref (a), mpq_denref (b));
149}
150
151
152/* MPQ assignment and conversions. */
153void
154mpz_set_q (mpz_t r, const mpq_t q)
155{
156 mpz_tdiv_q (r, mpq_numref (q), mpq_denref (q));
157}
158
159void
160mpq_set (mpq_t r, const mpq_t q)
161{
162 mpz_set (mpq_numref (r), mpq_numref (q));
163 mpz_set (mpq_denref (r), mpq_denref (q));
164}
165
166void
167mpq_set_ui (mpq_t r, unsigned long n, unsigned long d)
168{
169 mpz_set_ui (mpq_numref (r), n);
170 mpz_set_ui (mpq_denref (r), d);
171}
172
173void
174mpq_set_si (mpq_t r, signed long n, unsigned long d)
175{
176 mpz_set_si (mpq_numref (r), n);
177 mpz_set_ui (mpq_denref (r), d);
178}
179
180void
181mpq_set_z (mpq_t r, const mpz_t n)
182{
183 mpz_set_ui (mpq_denref (r), 1);
184 mpz_set (mpq_numref (r), n);
185}
186
187void
188mpq_set_num (mpq_t r, const mpz_t z)
189{
190 mpz_set (mpq_numref (r), z);
191}
192
193void
194mpq_set_den (mpq_t r, const mpz_t z)
195{
196 mpz_set (mpq_denref (r), z);
197}
198
199void
200mpq_get_num (mpz_t r, const mpq_t q)
201{
202 mpz_set (r, mpq_numref (q));
203}
204
205void
206mpq_get_den (mpz_t r, const mpq_t q)
207{
208 mpz_set (r, mpq_denref (q));
209}
210
211
212/* MPQ comparisons and the like. */
213int
214mpq_cmp (const mpq_t a, const mpq_t b)
215{
216 mpz_t t1, t2;
217 int res;
218
219 mpz_init (t1);
220 mpz_init (t2);
221 mpz_mul (t1, mpq_numref (a), mpq_denref (b));
222 mpz_mul (t2, mpq_numref (b), mpq_denref (a));
223 res = mpz_cmp (t1, t2);
224 mpz_clear (t1);
225 mpz_clear (t2);
226
227 return res;
228}
229
230int
231mpq_cmp_z (const mpq_t a, const mpz_t b)
232{
233 mpz_t t;
234 int res;
235
236 mpz_init (t);
237 mpz_mul (t, b, mpq_denref (a));
238 res = mpz_cmp (mpq_numref (a), t);
239 mpz_clear (t);
240
241 return res;
242}
243
244int
245mpq_equal (const mpq_t a, const mpq_t b)
246{
247 return (mpz_cmp (mpq_numref (a), mpq_numref (b)) == 0) &&
248 (mpz_cmp (mpq_denref (a), mpq_denref (b)) == 0);
249}
250
251int
252mpq_cmp_ui (const mpq_t q, unsigned long n, unsigned long d)
253{
254 mpq_t t;
255 assert (d != 0);
256 if (ULONG_MAX <= GMP_LIMB_MAX) {
257 mp_limb_t nl = n, dl = d;
258 return mpq_cmp (q, mpq_roinit_normal_nn (t, &nl, n != 0, &dl, 1));
259 } else {
260 int ret;
261
262 mpq_init (t);
263 mpq_set_ui (t, n, d);
264 ret = mpq_cmp (q, t);
265 mpq_clear (t);
266
267 return ret;
268 }
269}
270
271int
272mpq_cmp_si (const mpq_t q, signed long n, unsigned long d)
273{
274 assert (d != 0);
275
276 if (n >= 0)
277 return mpq_cmp_ui (q, n, d);
278 else
279 {
280 mpq_t t;
281
282 if (ULONG_MAX <= GMP_LIMB_MAX)
283 {
284 mp_limb_t nl = GMP_NEG_CAST (unsigned long, n), dl = d;
285 return mpq_cmp (q, mpq_roinit_normal_nn (t, &nl, -1, &dl, 1));
286 }
287 else
288 {
289 unsigned long l_n = GMP_NEG_CAST (unsigned long, n);
290
291 mpq_roinit_normal_nn (t, mpq_numref (q)->_mp_d, - mpq_numref (q)->_mp_size,
292 mpq_denref (q)->_mp_d, mpq_denref (q)->_mp_size);
293 return - mpq_cmp_ui (t, l_n, d);
294 }
295 }
296}
297
298int
299mpq_sgn (const mpq_t a)
300{
301 return mpz_sgn (mpq_numref (a));
302}
303
304
305/* MPQ arithmetic. */
306void
307mpq_abs (mpq_t r, const mpq_t q)
308{
309 mpz_abs (mpq_numref (r), mpq_numref (q));
310 mpz_set (mpq_denref (r), mpq_denref (q));
311}
312
313void
314mpq_neg (mpq_t r, const mpq_t q)
315{
316 mpz_neg (mpq_numref (r), mpq_numref (q));
317 mpz_set (mpq_denref (r), mpq_denref (q));
318}
319
320void
321mpq_add (mpq_t r, const mpq_t a, const mpq_t b)
322{
323 mpz_t t;
324
325 mpz_init (t);
326 mpz_gcd (t, mpq_denref (a), mpq_denref (b));
327 if (mpz_cmp_ui (t, 1) == 0)
328 {
329 mpz_mul (t, mpq_numref (a), mpq_denref (b));
330 mpz_addmul (t, mpq_numref (b), mpq_denref (a));
331 mpz_mul (mpq_denref (r), mpq_denref (a), mpq_denref (b));
332 mpz_swap (mpq_numref (r), t);
333 }
334 else
335 {
336 mpz_t x, y;
337 mpz_init (x);
338 mpz_init (y);
339
340 mpz_tdiv_q (x, mpq_denref (b), t);
341 mpz_tdiv_q (y, mpq_denref (a), t);
342 mpz_mul (x, mpq_numref (a), x);
343 mpz_addmul (x, mpq_numref (b), y);
344
345 mpz_gcd (t, x, t);
346 mpz_tdiv_q (mpq_numref (r), x, t);
347 mpz_tdiv_q (x, mpq_denref (b), t);
348 mpz_mul (mpq_denref (r), x, y);
349
350 mpz_clear (x);
351 mpz_clear (y);
352 }
353 mpz_clear (t);
354}
355
356void
357mpq_sub (mpq_t r, const mpq_t a, const mpq_t b)
358{
359 mpq_t t;
360
361 mpq_roinit_normal_nn (t, mpq_numref (b)->_mp_d, - mpq_numref (b)->_mp_size,
362 mpq_denref (b)->_mp_d, mpq_denref (b)->_mp_size);
363 mpq_add (r, a, t);
364}
365
366void
367mpq_div (mpq_t r, const mpq_t a, const mpq_t b)
368{
369 mpq_t t;
370 mpq_mul (r, a, mpq_roinit_zz (t, mpq_denref (b), mpq_numref (b)));
371}
372
373void
374mpq_mul (mpq_t r, const mpq_t a, const mpq_t b)
375{
376 mpq_t t;
377 mpq_nan_init (t);
378
379 if (a != b) {
380 mpz_t g;
381
382 mpz_init (g);
383 mpq_helper_canonicalize (t, mpq_numref (a), mpq_denref (b), g);
384 mpq_helper_canonicalize (r, mpq_numref (b), mpq_denref (a), g);
385 mpz_clear (g);
386
387 a = r;
388 b = t;
389 }
390
391 mpz_mul (mpq_numref (r), mpq_numref (a), mpq_numref (b));
392 mpz_mul (mpq_denref (r), mpq_denref (a), mpq_denref (b));
393 mpq_clear (t);
394}
395
396void
397mpq_div_2exp (mpq_t r, const mpq_t q, mp_bitcnt_t e)
398{
399 mp_bitcnt_t z = mpz_scan1 (mpq_numref (q), 0);
400 z = GMP_MIN (z, e);
401 mpz_mul_2exp (mpq_denref (r), mpq_denref (q), e - z);
402 mpz_tdiv_q_2exp (mpq_numref (r), mpq_numref (q), z);
403}
404
405void
406mpq_mul_2exp (mpq_t r, const mpq_t q, mp_bitcnt_t e)
407{
408 mp_bitcnt_t z = mpz_scan1 (mpq_denref (q), 0);
409 z = GMP_MIN (z, e);
410 mpz_mul_2exp (mpq_numref (r), mpq_numref (q), e - z);
411 mpz_tdiv_q_2exp (mpq_denref (r), mpq_denref (q), z);
412}
413
414void
415mpq_inv (mpq_t r, const mpq_t q)
416{
417 mpq_set (r, q);
418 mpz_swap (mpq_denref (r), mpq_numref (r));
419 mpq_canonical_sign (r);
420}
421
422
423/* MPQ to/from double. */
424void
425mpq_set_d (mpq_t r, double x)
426{
427 mpz_set_ui (mpq_denref (r), 1);
428
429 /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is
430 zero or infinity. */
431 if (x == x * 0.5 || x != x)
432 mpq_numref (r)->_mp_size = 0;
433 else
434 {
435 double B;
436 mp_bitcnt_t e;
437
438 B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
439 for (e = 0; x != x + 0.5; e += GMP_LIMB_BITS)
440 x *= B;
441
442 mpz_set_d (mpq_numref (r), x);
443 mpq_div_2exp (r, r, e);
444 }
445}
446
447double
448mpq_get_d (const mpq_t u)
449{
450 mp_bitcnt_t ne, de, ee;
451 mpz_t z;
452 double B, ret;
453
454 ne = mpz_sizeinbase (mpq_numref (u), 2);
455 de = mpz_sizeinbase (mpq_denref (u), 2);
456
457 ee = CHAR_BIT * sizeof (double);
458 if (de == 1 || ne > de + ee)
459 ee = 0;
460 else
461 ee = (ee + de - ne) / GMP_LIMB_BITS + 1;
462
463 mpz_init (z);
464 mpz_mul_2exp (z, mpq_numref (u), ee * GMP_LIMB_BITS);
465 mpz_tdiv_q (z, z, mpq_denref (u));
466 ret = mpz_get_d (z);
467 mpz_clear (z);
468
469 B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
470 for (B = 1 / B; ee != 0; --ee)
471 ret *= B;
472
473 return ret;
474}
475
476
477/* MPQ and strings/streams. */
478char *
479mpq_get_str (char *sp, int base, const mpq_t q)
480{
481 char *res;
482 char *rden;
483 size_t len;
484
485 res = mpz_get_str (sp, base, mpq_numref (q));
486 if (res == NULL || mpz_cmp_ui (mpq_denref (q), 1) == 0)
487 return res;
488
489 len = strlen (res) + 1;
490 rden = sp ? sp + len : NULL;
491 rden = mpz_get_str (rden, base, mpq_denref (q));
492 assert (rden != NULL);
493
494 if (sp == NULL) {
495 void * (*gmp_reallocate_func) (void *, size_t, size_t);
496 void (*gmp_free_func) (void *, size_t);
497 size_t lden;
498
499 mp_get_memory_functions (NULL, &gmp_reallocate_func, &gmp_free_func);
500 lden = strlen (rden) + 1;
501 res = (char *) gmp_reallocate_func (res, 0, (lden + len) * sizeof (char));
502 memcpy (res + len, rden, lden);
503 gmp_free_func (rden, 0);
504 }
505
506 res [len - 1] = '/';
507 return res;
508}
509
510size_t
511mpq_out_str (FILE *stream, int base, const mpq_t x)
512{
513 char * str;
514 size_t len;
515 void (*gmp_free_func) (void *, size_t);
516
517 str = mpq_get_str (NULL, base, x);
518 len = strlen (str);
519 len = fwrite (str, 1, len, stream);
520 mp_get_memory_functions (NULL, NULL, &gmp_free_func);
521 gmp_free_func (str, 0);
522 return len;
523}
524
525int
526mpq_set_str (mpq_t r, const char *sp, int base)
527{
528 const char *slash;
529
530 slash = strchr (sp, '/');
531 if (slash == NULL) {
532 mpz_set_ui (mpq_denref(r), 1);
533 return mpz_set_str (mpq_numref(r), sp, base);
534 } else {
535 char *num;
536 size_t numlen;
537 int ret;
538 void * (*gmp_allocate_func) (size_t);
539 void (*gmp_free_func) (void *, size_t);
540
541 mp_get_memory_functions (&gmp_allocate_func, NULL, &gmp_free_func);
542 numlen = slash - sp;
543 num = (char *) gmp_allocate_func ((numlen + 1) * sizeof (char));
544 memcpy (num, sp, numlen);
545 num[numlen] = '\0';
546 ret = mpz_set_str (mpq_numref(r), num, base);
547 gmp_free_func (num, 0);
548
549 if (ret != 0)
550 return ret;
551
552 return mpz_set_str (mpq_denref(r), slash + 1, base);
553 }
554}