mini-mpq.c revision 1.1.1.2 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
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
61 gmp_die (const char *msg)
62 {
63 fprintf (stderr, "%s\n", msg);
64 abort();
65 }
66 #endif
67
68
69 /* MPQ helper functions */
71 static mpq_srcptr
72 mpq_roinit_normal_nn (mpq_t x, mp_srcptr np, mp_size_t ns,
73 mp_srcptr dp, mp_size_t ds)
74 {
75 mpz_roinit_normal_n (mpq_numref(x), np, ns);
76 mpz_roinit_normal_n (mpq_denref(x), dp, ds);
77 return x;
78 }
79
80 static mpq_srcptr
81 mpq_roinit_zz (mpq_t x, mpz_srcptr n, mpz_srcptr d)
82 {
83 return mpq_roinit_normal_nn (x, n->_mp_d, n->_mp_size,
84 d->_mp_d, d->_mp_size);
85 }
86
87 static void
88 mpq_nan_init (mpq_t x)
89 {
90 mpz_init (mpq_numref (x));
91 mpz_init (mpq_denref (x));
92 }
93
94 void
95 mpq_init (mpq_t x)
96 {
97 mpz_init (mpq_numref (x));
98 mpz_init_set_ui (mpq_denref (x), 1);
99 }
100
101 void
102 mpq_clear (mpq_t x)
103 {
104 mpz_clear (mpq_numref (x));
105 mpz_clear (mpq_denref (x));
106 }
107
108 static void
109 mpq_canonical_sign (mpq_t r)
110 {
111 mp_size_t ds = mpq_denref (r)->_mp_size;
112 if (ds <= 0)
113 {
114 if (ds == 0)
115 gmp_die("mpq: Fraction with zero denominator.");
116 mpz_neg (mpq_denref (r), mpq_denref (r));
117 mpz_neg (mpq_numref (r), mpq_numref (r));
118 }
119 }
120
121 static void
122 mpq_helper_canonicalize (mpq_t r, const mpz_t num, const mpz_t den, mpz_t g)
123 {
124 if (num->_mp_size == 0)
125 mpq_set_ui (r, 0, 1);
126 else
127 {
128 mpz_gcd (g, num, den);
129 mpz_tdiv_q (mpq_numref (r), num, g);
130 mpz_tdiv_q (mpq_denref (r), den, g);
131 mpq_canonical_sign (r);
132 }
133 }
134
135 void
136 mpq_canonicalize (mpq_t r)
137 {
138 mpz_t t;
139
140 mpz_init (t);
141 mpq_helper_canonicalize (r, mpq_numref (r), mpq_denref (r), t);
142 mpz_clear (t);
143 }
144
145 void
146 mpq_swap (mpq_t a, mpq_t b)
147 {
148 mpz_swap (mpq_numref (a), mpq_numref (b));
149 mpz_swap (mpq_denref (a), mpq_denref (b));
150 }
151
152
153 /* MPQ assignment and conversions. */
155 void
156 mpz_set_q (mpz_t r, const mpq_t q)
157 {
158 mpz_tdiv_q (r, mpq_numref (q), mpq_denref (q));
159 }
160
161 void
162 mpq_set (mpq_t r, const mpq_t q)
163 {
164 mpz_set (mpq_numref (r), mpq_numref (q));
165 mpz_set (mpq_denref (r), mpq_denref (q));
166 }
167
168 void
169 mpq_set_ui (mpq_t r, unsigned long n, unsigned long d)
170 {
171 mpz_set_ui (mpq_numref (r), n);
172 mpz_set_ui (mpq_denref (r), d);
173 }
174
175 void
176 mpq_set_si (mpq_t r, signed long n, unsigned long d)
177 {
178 mpz_set_si (mpq_numref (r), n);
179 mpz_set_ui (mpq_denref (r), d);
180 }
181
182 void
183 mpq_set_z (mpq_t r, const mpz_t n)
184 {
185 mpz_set_ui (mpq_denref (r), 1);
186 mpz_set (mpq_numref (r), n);
187 }
188
189 void
190 mpq_set_num (mpq_t r, const mpz_t z)
191 {
192 mpz_set (mpq_numref (r), z);
193 }
194
195 void
196 mpq_set_den (mpq_t r, const mpz_t z)
197 {
198 mpz_set (mpq_denref (r), z);
199 }
200
201 void
202 mpq_get_num (mpz_t r, const mpq_t q)
203 {
204 mpz_set (r, mpq_numref (q));
205 }
206
207 void
208 mpq_get_den (mpz_t r, const mpq_t q)
209 {
210 mpz_set (r, mpq_denref (q));
211 }
212
213
214 /* MPQ comparisons and the like. */
216 int
217 mpq_cmp (const mpq_t a, const mpq_t b)
218 {
219 mpz_t t1, t2;
220 int res;
221
222 mpz_init (t1);
223 mpz_init (t2);
224 mpz_mul (t1, mpq_numref (a), mpq_denref (b));
225 mpz_mul (t2, mpq_numref (b), mpq_denref (a));
226 res = mpz_cmp (t1, t2);
227 mpz_clear (t1);
228 mpz_clear (t2);
229
230 return res;
231 }
232
233 int
234 mpq_cmp_z (const mpq_t a, const mpz_t b)
235 {
236 mpz_t t;
237 int res;
238
239 mpz_init (t);
240 mpz_mul (t, b, mpq_denref (a));
241 res = mpz_cmp (mpq_numref (a), t);
242 mpz_clear (t);
243
244 return res;
245 }
246
247 int
248 mpq_equal (const mpq_t a, const mpq_t b)
249 {
250 return (mpz_cmp (mpq_numref (a), mpq_numref (b)) == 0) &&
251 (mpz_cmp (mpq_denref (a), mpq_denref (b)) == 0);
252 }
253
254 int
255 mpq_cmp_ui (const mpq_t q, unsigned long n, unsigned long d)
256 {
257 mpq_t t;
258 assert (d != 0);
259 if (ULONG_MAX <= GMP_LIMB_MAX) {
260 mp_limb_t nl = n, dl = d;
261 return mpq_cmp (q, mpq_roinit_normal_nn (t, &nl, n != 0, &dl, 1));
262 } else {
263 int ret;
264
265 mpq_init (t);
266 mpq_set_ui (t, n, d);
267 ret = mpq_cmp (q, t);
268 mpq_clear (t);
269
270 return ret;
271 }
272 }
273
274 int
275 mpq_cmp_si (const mpq_t q, signed long n, unsigned long d)
276 {
277 assert (d != 0);
278
279 if (n >= 0)
280 return mpq_cmp_ui (q, n, d);
281 else
282 {
283 mpq_t t;
284
285 if (ULONG_MAX <= GMP_LIMB_MAX)
286 {
287 mp_limb_t nl = GMP_NEG_CAST (unsigned long, n), dl = d;
288 return mpq_cmp (q, mpq_roinit_normal_nn (t, &nl, -1, &dl, 1));
289 }
290 else
291 {
292 unsigned long l_n = GMP_NEG_CAST (unsigned long, n);
293
294 mpq_roinit_normal_nn (t, mpq_numref (q)->_mp_d, - mpq_numref (q)->_mp_size,
295 mpq_denref (q)->_mp_d, mpq_denref (q)->_mp_size);
296 return - mpq_cmp_ui (t, l_n, d);
297 }
298 }
299 }
300
301 int
302 mpq_sgn (const mpq_t a)
303 {
304 return mpz_sgn (mpq_numref (a));
305 }
306
307
308 /* MPQ arithmetic. */
310 void
311 mpq_abs (mpq_t r, const mpq_t q)
312 {
313 mpz_abs (mpq_numref (r), mpq_numref (q));
314 mpz_set (mpq_denref (r), mpq_denref (q));
315 }
316
317 void
318 mpq_neg (mpq_t r, const mpq_t q)
319 {
320 mpz_neg (mpq_numref (r), mpq_numref (q));
321 mpz_set (mpq_denref (r), mpq_denref (q));
322 }
323
324 void
325 mpq_add (mpq_t r, const mpq_t a, const mpq_t b)
326 {
327 mpz_t t;
328
329 mpz_init (t);
330 mpz_gcd (t, mpq_denref (a), mpq_denref (b));
331 if (mpz_cmp_ui (t, 1) == 0)
332 {
333 mpz_mul (t, mpq_numref (a), mpq_denref (b));
334 mpz_addmul (t, mpq_numref (b), mpq_denref (a));
335 mpz_mul (mpq_denref (r), mpq_denref (a), mpq_denref (b));
336 mpz_swap (mpq_numref (r), t);
337 }
338 else
339 {
340 mpz_t x, y;
341 mpz_init (x);
342 mpz_init (y);
343
344 mpz_tdiv_q (x, mpq_denref (b), t);
345 mpz_tdiv_q (y, mpq_denref (a), t);
346 mpz_mul (x, mpq_numref (a), x);
347 mpz_addmul (x, mpq_numref (b), y);
348
349 mpz_gcd (t, x, t);
350 mpz_tdiv_q (mpq_numref (r), x, t);
351 mpz_tdiv_q (x, mpq_denref (b), t);
352 mpz_mul (mpq_denref (r), x, y);
353
354 mpz_clear (x);
355 mpz_clear (y);
356 }
357 mpz_clear (t);
358 }
359
360 void
361 mpq_sub (mpq_t r, const mpq_t a, const mpq_t b)
362 {
363 mpq_t t;
364
365 mpq_roinit_normal_nn (t, mpq_numref (b)->_mp_d, - mpq_numref (b)->_mp_size,
366 mpq_denref (b)->_mp_d, mpq_denref (b)->_mp_size);
367 mpq_add (r, a, t);
368 }
369
370 void
371 mpq_div (mpq_t r, const mpq_t a, const mpq_t b)
372 {
373 mpq_t t;
374 mpq_mul (r, a, mpq_roinit_zz (t, mpq_denref (b), mpq_numref (b)));
375 }
376
377 void
378 mpq_mul (mpq_t r, const mpq_t a, const mpq_t b)
379 {
380 mpq_t t;
381 mpq_nan_init (t);
382
383 if (a != b) {
384 mpz_t g;
385
386 mpz_init (g);
387 mpq_helper_canonicalize (t, mpq_numref (a), mpq_denref (b), g);
388 mpq_helper_canonicalize (r, mpq_numref (b), mpq_denref (a), g);
389 mpz_clear (g);
390
391 a = r;
392 b = t;
393 }
394
395 mpz_mul (mpq_numref (r), mpq_numref (a), mpq_numref (b));
396 mpz_mul (mpq_denref (r), mpq_denref (a), mpq_denref (b));
397 mpq_clear (t);
398 }
399
400 void
401 mpq_div_2exp (mpq_t r, const mpq_t q, mp_bitcnt_t e)
402 {
403 mp_bitcnt_t z = mpz_scan1 (mpq_numref (q), 0);
404 z = GMP_MIN (z, e);
405 mpz_mul_2exp (mpq_denref (r), mpq_denref (q), e - z);
406 mpz_tdiv_q_2exp (mpq_numref (r), mpq_numref (q), z);
407 }
408
409 void
410 mpq_mul_2exp (mpq_t r, const mpq_t q, mp_bitcnt_t e)
411 {
412 mp_bitcnt_t z = mpz_scan1 (mpq_denref (q), 0);
413 z = GMP_MIN (z, e);
414 mpz_mul_2exp (mpq_numref (r), mpq_numref (q), e - z);
415 mpz_tdiv_q_2exp (mpq_denref (r), mpq_denref (q), z);
416 }
417
418 void
419 mpq_inv (mpq_t r, const mpq_t q)
420 {
421 mpq_set (r, q);
422 mpz_swap (mpq_denref (r), mpq_numref (r));
423 mpq_canonical_sign (r);
424 }
425
426
427 /* MPQ to/from double. */
429 void
430 mpq_set_d (mpq_t r, double x)
431 {
432 mpz_set_ui (mpq_denref (r), 1);
433
434 /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is
435 zero or infinity. */
436 if (x == x * 0.5 || x != x)
437 mpq_numref (r)->_mp_size = 0;
438 else
439 {
440 double B;
441 mp_bitcnt_t e;
442
443 B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
444 for (e = 0; x != x + 0.5; e += GMP_LIMB_BITS)
445 x *= B;
446
447 mpz_set_d (mpq_numref (r), x);
448 mpq_div_2exp (r, r, e);
449 }
450 }
451
452 double
453 mpq_get_d (const mpq_t u)
454 {
455 mp_bitcnt_t ne, de, ee;
456 mpz_t z;
457 double B, ret;
458
459 ne = mpz_sizeinbase (mpq_numref (u), 2);
460 de = mpz_sizeinbase (mpq_denref (u), 2);
461
462 ee = CHAR_BIT * sizeof (double);
463 if (de == 1 || ne > de + ee)
464 ee = 0;
465 else
466 ee = (ee + de - ne) / GMP_LIMB_BITS + 1;
467
468 mpz_init (z);
469 mpz_mul_2exp (z, mpq_numref (u), ee * GMP_LIMB_BITS);
470 mpz_tdiv_q (z, z, mpq_denref (u));
471 ret = mpz_get_d (z);
472 mpz_clear (z);
473
474 B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
475 for (B = 1 / B; ee != 0; --ee)
476 ret *= B;
477
478 return ret;
479 }
480
481
482 /* MPQ and strings/streams. */
484 char *
485 mpq_get_str (char *sp, int base, const mpq_t q)
486 {
487 char *res;
488 char *rden;
489 size_t len;
490
491 res = mpz_get_str (sp, base, mpq_numref (q));
492 if (res == NULL || mpz_cmp_ui (mpq_denref (q), 1) == 0)
493 return res;
494
495 len = strlen (res) + 1;
496 rden = sp ? sp + len : NULL;
497 rden = mpz_get_str (rden, base, mpq_denref (q));
498 assert (rden != NULL);
499
500 if (sp == NULL) {
501 void * (*gmp_reallocate_func) (void *, size_t, size_t);
502 void (*gmp_free_func) (void *, size_t);
503 size_t lden;
504
505 mp_get_memory_functions (NULL, &gmp_reallocate_func, &gmp_free_func);
506 lden = strlen (rden) + 1;
507 res = (char *) gmp_reallocate_func (res, 0, (lden + len) * sizeof (char));
508 memcpy (res + len, rden, lden);
509 gmp_free_func (rden, 0);
510 }
511
512 res [len - 1] = '/';
513 return res;
514 }
515
516 size_t
517 mpq_out_str (FILE *stream, int base, const mpq_t x)
518 {
519 char * str;
520 size_t len;
521 void (*gmp_free_func) (void *, size_t);
522
523 str = mpq_get_str (NULL, base, x);
524 if (!str)
525 return 0;
526 len = strlen (str);
527 len = fwrite (str, 1, len, stream);
528 mp_get_memory_functions (NULL, NULL, &gmp_free_func);
529 gmp_free_func (str, 0);
530 return len;
531 }
532
533 int
534 mpq_set_str (mpq_t r, const char *sp, int base)
535 {
536 const char *slash;
537
538 slash = strchr (sp, '/');
539 if (slash == NULL) {
540 mpz_set_ui (mpq_denref(r), 1);
541 return mpz_set_str (mpq_numref(r), sp, base);
542 } else {
543 char *num;
544 size_t numlen;
545 int ret;
546 void * (*gmp_allocate_func) (size_t);
547 void (*gmp_free_func) (void *, size_t);
548
549 mp_get_memory_functions (&gmp_allocate_func, NULL, &gmp_free_func);
550 numlen = slash - sp;
551 num = (char *) gmp_allocate_func ((numlen + 1) * sizeof (char));
552 memcpy (num, sp, numlen);
553 num[numlen] = '\0';
554 ret = mpz_set_str (mpq_numref(r), num, base);
555 gmp_free_func (num, 0);
556
557 if (ret != 0)
558 return ret;
559
560 return mpz_set_str (mpq_denref(r), slash + 1, base);
561 }
562 }
563