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