xref: /netbsd-src/external/lgpl3/gmp/dist/mini-gmp/mini-mpq.c (revision 1daf83e636cd998f45e5597a8f995a540e2d5b4a)
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