1 /* 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
8 Copyright 2018-2020 Free Software Foundation, Inc.
9
10 This file is part of the GNU MP Library.
11
12 The GNU MP Library is free software; you can redistribute it and/or modify
13 it 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
19 or
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
25 or both in parallel, as here.
26
27 The GNU MP Library is distributed in the hope that it will be useful, but
28 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
29 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
30 for more details.
31
32 You should have received copies of the GNU General Public License and the
33 GNU Lesser General Public License along with the GNU MP Library. If not,
34 see 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
51 static mpz_srcptr
mpz_roinit_normal_n(mpz_t x,mp_srcptr xp,mp_size_t xs)52 mpz_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
60 static void
gmp_die(const char * msg)61 gmp_die (const char *msg)
62 {
63 fprintf (stderr, "%s\n", msg);
64 abort();
65 }
66 #endif
67
68
69 /* MPQ helper functions */
70 static mpq_srcptr
mpq_roinit_normal_nn(mpq_t x,mp_srcptr np,mp_size_t ns,mp_srcptr dp,mp_size_t ds)71 mpq_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
79 static mpq_srcptr
mpq_roinit_zz(mpq_t x,mpz_srcptr n,mpz_srcptr d)80 mpq_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
86 static void
mpq_nan_init(mpq_t x)87 mpq_nan_init (mpq_t x)
88 {
89 mpz_init (mpq_numref (x));
90 mpz_init (mpq_denref (x));
91 }
92
93 void
mpq_init(mpq_t x)94 mpq_init (mpq_t x)
95 {
96 mpz_init (mpq_numref (x));
97 mpz_init_set_ui (mpq_denref (x), 1);
98 }
99
100 void
mpq_clear(mpq_t x)101 mpq_clear (mpq_t x)
102 {
103 mpz_clear (mpq_numref (x));
104 mpz_clear (mpq_denref (x));
105 }
106
107 static void
mpq_canonical_sign(mpq_t r)108 mpq_canonical_sign (mpq_t r)
109 {
110 mp_size_t ds = mpq_denref (r)->_mp_size;
111 if (ds <= 0)
112 {
113 if (ds == 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
120 static void
mpq_helper_canonicalize(mpq_t r,const mpz_t num,const mpz_t den,mpz_t g)121 mpq_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
134 void
mpq_canonicalize(mpq_t r)135 mpq_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
144 void
mpq_swap(mpq_t a,mpq_t b)145 mpq_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. */
153 void
mpz_set_q(mpz_t r,const mpq_t q)154 mpz_set_q (mpz_t r, const mpq_t q)
155 {
156 mpz_tdiv_q (r, mpq_numref (q), mpq_denref (q));
157 }
158
159 void
mpq_set(mpq_t r,const mpq_t q)160 mpq_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
166 void
mpq_set_ui(mpq_t r,unsigned long n,unsigned long d)167 mpq_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
173 void
mpq_set_si(mpq_t r,signed long n,unsigned long d)174 mpq_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
180 void
mpq_set_z(mpq_t r,const mpz_t n)181 mpq_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
187 void
mpq_set_num(mpq_t r,const mpz_t z)188 mpq_set_num (mpq_t r, const mpz_t z)
189 {
190 mpz_set (mpq_numref (r), z);
191 }
192
193 void
mpq_set_den(mpq_t r,const mpz_t z)194 mpq_set_den (mpq_t r, const mpz_t z)
195 {
196 mpz_set (mpq_denref (r), z);
197 }
198
199 void
mpq_get_num(mpz_t r,const mpq_t q)200 mpq_get_num (mpz_t r, const mpq_t q)
201 {
202 mpz_set (r, mpq_numref (q));
203 }
204
205 void
mpq_get_den(mpz_t r,const mpq_t q)206 mpq_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. */
213 int
mpq_cmp(const mpq_t a,const mpq_t b)214 mpq_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
230 int
mpq_cmp_z(const mpq_t a,const mpz_t b)231 mpq_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
244 int
mpq_equal(const mpq_t a,const mpq_t b)245 mpq_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
251 int
mpq_cmp_ui(const mpq_t q,unsigned long n,unsigned long d)252 mpq_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
271 int
mpq_cmp_si(const mpq_t q,signed long n,unsigned long d)272 mpq_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
298 int
mpq_sgn(const mpq_t a)299 mpq_sgn (const mpq_t a)
300 {
301 return mpz_sgn (mpq_numref (a));
302 }
303
304
305 /* MPQ arithmetic. */
306 void
mpq_abs(mpq_t r,const mpq_t q)307 mpq_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
313 void
mpq_neg(mpq_t r,const mpq_t q)314 mpq_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
320 void
mpq_add(mpq_t r,const mpq_t a,const mpq_t b)321 mpq_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
356 void
mpq_sub(mpq_t r,const mpq_t a,const mpq_t b)357 mpq_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
366 void
mpq_div(mpq_t r,const mpq_t a,const mpq_t b)367 mpq_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
373 void
mpq_mul(mpq_t r,const mpq_t a,const mpq_t b)374 mpq_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
396 void
mpq_div_2exp(mpq_t r,const mpq_t q,mp_bitcnt_t e)397 mpq_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
405 void
mpq_mul_2exp(mpq_t r,const mpq_t q,mp_bitcnt_t e)406 mpq_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
414 void
mpq_inv(mpq_t r,const mpq_t q)415 mpq_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. */
424 void
mpq_set_d(mpq_t r,double x)425 mpq_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
447 double
mpq_get_d(const mpq_t u)448 mpq_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. */
478 char *
mpq_get_str(char * sp,int base,const mpq_t q)479 mpq_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
510 size_t
mpq_out_str(FILE * stream,int base,const mpq_t x)511 mpq_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 if (!str)
519 return 0;
520 len = strlen (str);
521 len = fwrite (str, 1, len, stream);
522 mp_get_memory_functions (NULL, NULL, &gmp_free_func);
523 gmp_free_func (str, 0);
524 return len;
525 }
526
527 int
mpq_set_str(mpq_t r,const char * sp,int base)528 mpq_set_str (mpq_t r, const char *sp, int base)
529 {
530 const char *slash;
531
532 slash = strchr (sp, '/');
533 if (slash == NULL) {
534 mpz_set_ui (mpq_denref(r), 1);
535 return mpz_set_str (mpq_numref(r), sp, base);
536 } else {
537 char *num;
538 size_t numlen;
539 int ret;
540 void * (*gmp_allocate_func) (size_t);
541 void (*gmp_free_func) (void *, size_t);
542
543 mp_get_memory_functions (&gmp_allocate_func, NULL, &gmp_free_func);
544 numlen = slash - sp;
545 num = (char *) gmp_allocate_func ((numlen + 1) * sizeof (char));
546 memcpy (num, sp, numlen);
547 num[numlen] = '\0';
548 ret = mpz_set_str (mpq_numref(r), num, base);
549 gmp_free_func (num, 0);
550
551 if (ret != 0)
552 return ret;
553
554 return mpz_set_str (mpq_denref(r), slash + 1, base);
555 }
556 }
557