Austin Schuh | dace2a6 | 2020-08-18 10:56:48 -0700 | [diff] [blame] | 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 */ |
| 70 | static mpq_srcptr |
| 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 |
| 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 |
| 87 | mpq_nan_init (mpq_t x) |
| 88 | { |
| 89 | mpz_init (mpq_numref (x)); |
| 90 | mpz_init (mpq_denref (x)); |
| 91 | } |
| 92 | |
| 93 | void |
| 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 |
| 101 | mpq_clear (mpq_t x) |
| 102 | { |
| 103 | mpz_clear (mpq_numref (x)); |
| 104 | mpz_clear (mpq_denref (x)); |
| 105 | } |
| 106 | |
| 107 | static void |
| 108 | mpq_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 | |
| 120 | static void |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 188 | mpq_set_num (mpq_t r, const mpz_t z) |
| 189 | { |
| 190 | mpz_set (mpq_numref (r), z); |
| 191 | } |
| 192 | |
| 193 | void |
| 194 | mpq_set_den (mpq_t r, const mpz_t z) |
| 195 | { |
| 196 | mpz_set (mpq_denref (r), z); |
| 197 | } |
| 198 | |
| 199 | void |
| 200 | mpq_get_num (mpz_t r, const mpq_t q) |
| 201 | { |
| 202 | mpz_set (r, mpq_numref (q)); |
| 203 | } |
| 204 | |
| 205 | void |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 299 | mpq_sgn (const mpq_t a) |
| 300 | { |
| 301 | return mpz_sgn (mpq_numref (a)); |
| 302 | } |
| 303 | |
| 304 | |
| 305 | /* MPQ arithmetic. */ |
| 306 | void |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 * |
| 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 |
| 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 | 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 | |
| 525 | int |
| 526 | mpq_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 | } |