Home | History | Annotate | Line # | Download | only in mini-gmp
mini-mpq.c revision 1.1
      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, 2019 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   int cmp = mpq_denref (r)->_mp_size;
    112   if (cmp <= 0)
    113     {
    114       if (cmp == 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   len = strlen (str);
    525   len = fwrite (str, 1, len, stream);
    526   mp_get_memory_functions (NULL, NULL, &gmp_free_func);
    527   gmp_free_func (str, 0);
    528   return len;
    529 }
    530 
    531 int
    532 mpq_set_str (mpq_t r, const char *sp, int base)
    533 {
    534   const char *slash;
    535 
    536   slash = strchr (sp, '/');
    537   if (slash == NULL) {
    538     mpz_set_ui (mpq_denref(r), 1);
    539     return mpz_set_str (mpq_numref(r), sp, base);
    540   } else {
    541     char *num;
    542     size_t numlen;
    543     int ret;
    544     void * (*gmp_allocate_func) (size_t);
    545     void (*gmp_free_func) (void *, size_t);
    546 
    547     mp_get_memory_functions (&gmp_allocate_func, NULL, &gmp_free_func);
    548     numlen = slash - sp;
    549     num = (char *) gmp_allocate_func ((numlen + 1) * sizeof (char));
    550     memcpy (num, sp, numlen);
    551     num[numlen] = '\0';
    552     ret = mpz_set_str (mpq_numref(r), num, base);
    553     gmp_free_func (num, 0);
    554 
    555     if (ret != 0)
    556       return ret;
    557 
    558     return mpz_set_str (mpq_denref(r), slash + 1, base);
    559   }
    560 }
    561