12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109 |
- /* glpgmp.c */
- /***********************************************************************
- * This code is part of GLPK (GNU Linear Programming Kit).
- *
- * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- * 2009, 2010 Andrew Makhorin, Department for Applied Informatics,
- * Moscow Aviation Institute, Moscow, Russia. All rights reserved.
- * E-mail: <mao@gnu.org>.
- *
- * GLPK is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GLPK is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
- ***********************************************************************/
- #define _GLPSTD_STDIO
- #include "glpdmp.h"
- #include "glpgmp.h"
- #define xfault xerror
- #ifdef HAVE_GMP /* use GNU MP bignum library */
- int gmp_pool_count(void) { return 0; }
- void gmp_free_mem(void) { return; }
- #else /* use GLPK bignum module */
- static DMP *gmp_pool = NULL;
- static int gmp_size = 0;
- static unsigned short *gmp_work = NULL;
- void *gmp_get_atom(int size)
- { if (gmp_pool == NULL)
- gmp_pool = dmp_create_pool();
- return dmp_get_atom(gmp_pool, size);
- }
- void gmp_free_atom(void *ptr, int size)
- { xassert(gmp_pool != NULL);
- dmp_free_atom(gmp_pool, ptr, size);
- return;
- }
- int gmp_pool_count(void)
- { if (gmp_pool == NULL)
- return 0;
- else
- return dmp_in_use(gmp_pool).lo;
- }
- unsigned short *gmp_get_work(int size)
- { xassert(size > 0);
- if (gmp_size < size)
- { if (gmp_size == 0)
- { xassert(gmp_work == NULL);
- gmp_size = 100;
- }
- else
- { xassert(gmp_work != NULL);
- xfree(gmp_work);
- }
- while (gmp_size < size) gmp_size += gmp_size;
- gmp_work = xcalloc(gmp_size, sizeof(unsigned short));
- }
- return gmp_work;
- }
- void gmp_free_mem(void)
- { if (gmp_pool != NULL) dmp_delete_pool(gmp_pool);
- if (gmp_work != NULL) xfree(gmp_work);
- gmp_pool = NULL;
- gmp_size = 0;
- gmp_work = NULL;
- return;
- }
- /*====================================================================*/
- mpz_t _mpz_init(void)
- { /* initialize x, and set its value to 0 */
- mpz_t x;
- x = gmp_get_atom(sizeof(struct mpz));
- x->val = 0;
- x->ptr = NULL;
- return x;
- }
- void mpz_clear(mpz_t x)
- { /* free the space occupied by x */
- mpz_set_si(x, 0);
- xassert(x->ptr == NULL);
- /* free the number descriptor */
- gmp_free_atom(x, sizeof(struct mpz));
- return;
- }
- void mpz_set(mpz_t z, mpz_t x)
- { /* set the value of z from x */
- struct mpz_seg *e, *ee, *es;
- if (z != x)
- { mpz_set_si(z, 0);
- z->val = x->val;
- xassert(z->ptr == NULL);
- for (e = x->ptr, es = NULL; e != NULL; e = e->next)
- { ee = gmp_get_atom(sizeof(struct mpz_seg));
- memcpy(ee->d, e->d, 12);
- ee->next = NULL;
- if (z->ptr == NULL)
- z->ptr = ee;
- else
- es->next = ee;
- es = ee;
- }
- }
- return;
- }
- void mpz_set_si(mpz_t x, int val)
- { /* set the value of x to val */
- struct mpz_seg *e;
- /* free existing segments, if any */
- while (x->ptr != NULL)
- { e = x->ptr;
- x->ptr = e->next;
- gmp_free_atom(e, sizeof(struct mpz_seg));
- }
- /* assign new value */
- if (val == 0x80000000)
- { /* long format is needed */
- x->val = -1;
- x->ptr = e = gmp_get_atom(sizeof(struct mpz_seg));
- memset(e->d, 0, 12);
- e->d[1] = 0x8000;
- e->next = NULL;
- }
- else
- { /* short format is enough */
- x->val = val;
- }
- return;
- }
- double mpz_get_d(mpz_t x)
- { /* convert x to a double, truncating if necessary */
- struct mpz_seg *e;
- int j;
- double val, deg;
- if (x->ptr == NULL)
- val = (double)x->val;
- else
- { xassert(x->val != 0);
- val = 0.0;
- deg = 1.0;
- for (e = x->ptr; e != NULL; e = e->next)
- { for (j = 0; j <= 5; j++)
- { val += deg * (double)((int)e->d[j]);
- deg *= 65536.0;
- }
- }
- if (x->val < 0) val = - val;
- }
- return val;
- }
- double mpz_get_d_2exp(int *exp, mpz_t x)
- { /* convert x to a double, truncating if necessary (i.e. rounding
- towards zero), and returning the exponent separately;
- the return value is in the range 0.5 <= |d| < 1 and the
- exponent is stored to *exp; d*2^exp is the (truncated) x value;
- if x is zero, the return is 0.0 and 0 is stored to *exp;
- this is similar to the standard C frexp function */
- struct mpz_seg *e;
- int j, n, n1;
- double val;
- if (x->ptr == NULL)
- val = (double)x->val, n = 0;
- else
- { xassert(x->val != 0);
- val = 0.0, n = 0;
- for (e = x->ptr; e != NULL; e = e->next)
- { for (j = 0; j <= 5; j++)
- { val += (double)((int)e->d[j]);
- val /= 65536.0, n += 16;
- }
- }
- if (x->val < 0) val = - val;
- }
- val = frexp(val, &n1);
- *exp = n + n1;
- return val;
- }
- void mpz_swap(mpz_t x, mpz_t y)
- { /* swap the values x and y efficiently */
- int val;
- void *ptr;
- val = x->val, ptr = x->ptr;
- x->val = y->val, x->ptr = y->ptr;
- y->val = val, y->ptr = ptr;
- return;
- }
- static void normalize(mpz_t x)
- { /* normalize integer x that includes removing non-significant
- (leading) zeros and converting to short format, if possible */
- struct mpz_seg *es, *e;
- /* if the integer is in short format, it remains unchanged */
- if (x->ptr == NULL)
- { xassert(x->val != 0x80000000);
- goto done;
- }
- xassert(x->val == +1 || x->val == -1);
- /* find the last (most significant) non-zero segment */
- es = NULL;
- for (e = x->ptr; e != NULL; e = e->next)
- { if (e->d[0] || e->d[1] || e->d[2] ||
- e->d[3] || e->d[4] || e->d[5]) es = e;
- }
- /* if all segments contain zeros, the integer is zero */
- if (es == NULL)
- { mpz_set_si(x, 0);
- goto done;
- }
- /* remove non-significant (leading) zero segments */
- while (es->next != NULL)
- { e = es->next;
- es->next = e->next;
- gmp_free_atom(e, sizeof(struct mpz_seg));
- }
- /* convert the integer to short format, if possible */
- e = x->ptr;
- if (e->next == NULL && e->d[1] <= 0x7FFF &&
- !e->d[2] && !e->d[3] && !e->d[4] && !e->d[5])
- { int val;
- val = (int)e->d[0] + ((int)e->d[1] << 16);
- if (x->val < 0) val = - val;
- mpz_set_si(x, val);
- }
- done: return;
- }
- void mpz_add(mpz_t z, mpz_t x, mpz_t y)
- { /* set z to x + y */
- static struct mpz_seg zero = { { 0, 0, 0, 0, 0, 0 }, NULL };
- struct mpz_seg dumx, dumy, *ex, *ey, *ez, *es, *ee;
- int k, sx, sy, sz;
- unsigned int t;
- /* if [x] = 0 then [z] = [y] */
- if (x->val == 0)
- { xassert(x->ptr == NULL);
- mpz_set(z, y);
- goto done;
- }
- /* if [y] = 0 then [z] = [x] */
- if (y->val == 0)
- { xassert(y->ptr == NULL);
- mpz_set(z, x);
- goto done;
- }
- /* special case when both [x] and [y] are in short format */
- if (x->ptr == NULL && y->ptr == NULL)
- { int xval = x->val, yval = y->val, zval = x->val + y->val;
- xassert(xval != 0x80000000 && yval != 0x80000000);
- if (!(xval > 0 && yval > 0 && zval <= 0 ||
- xval < 0 && yval < 0 && zval >= 0))
- { mpz_set_si(z, zval);
- goto done;
- }
- }
- /* convert [x] to long format, if necessary */
- if (x->ptr == NULL)
- { xassert(x->val != 0x80000000);
- if (x->val >= 0)
- { sx = +1;
- t = (unsigned int)(+ x->val);
- }
- else
- { sx = -1;
- t = (unsigned int)(- x->val);
- }
- ex = &dumx;
- ex->d[0] = (unsigned short)t;
- ex->d[1] = (unsigned short)(t >> 16);
- ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0;
- ex->next = NULL;
- }
- else
- { sx = x->val;
- xassert(sx == +1 || sx == -1);
- ex = x->ptr;
- }
- /* convert [y] to long format, if necessary */
- if (y->ptr == NULL)
- { xassert(y->val != 0x80000000);
- if (y->val >= 0)
- { sy = +1;
- t = (unsigned int)(+ y->val);
- }
- else
- { sy = -1;
- t = (unsigned int)(- y->val);
- }
- ey = &dumy;
- ey->d[0] = (unsigned short)t;
- ey->d[1] = (unsigned short)(t >> 16);
- ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0;
- ey->next = NULL;
- }
- else
- { sy = y->val;
- xassert(sy == +1 || sy == -1);
- ey = y->ptr;
- }
- /* main fragment */
- sz = sx;
- ez = es = NULL;
- if (sx > 0 && sy > 0 || sx < 0 && sy < 0)
- { /* [x] and [y] have identical signs -- addition */
- t = 0;
- for (; ex || ey; ex = ex->next, ey = ey->next)
- { if (ex == NULL) ex = &zero;
- if (ey == NULL) ey = &zero;
- ee = gmp_get_atom(sizeof(struct mpz_seg));
- for (k = 0; k <= 5; k++)
- { t += (unsigned int)ex->d[k];
- t += (unsigned int)ey->d[k];
- ee->d[k] = (unsigned short)t;
- t >>= 16;
- }
- ee->next = NULL;
- if (ez == NULL)
- ez = ee;
- else
- es->next = ee;
- es = ee;
- }
- if (t)
- { /* overflow -- one extra digit is needed */
- ee = gmp_get_atom(sizeof(struct mpz_seg));
- ee->d[0] = 1;
- ee->d[1] = ee->d[2] = ee->d[3] = ee->d[4] = ee->d[5] = 0;
- ee->next = NULL;
- xassert(es != NULL);
- es->next = ee;
- }
- }
- else
- { /* [x] and [y] have different signs -- subtraction */
- t = 1;
- for (; ex || ey; ex = ex->next, ey = ey->next)
- { if (ex == NULL) ex = &zero;
- if (ey == NULL) ey = &zero;
- ee = gmp_get_atom(sizeof(struct mpz_seg));
- for (k = 0; k <= 5; k++)
- { t += (unsigned int)ex->d[k];
- t += (0xFFFF - (unsigned int)ey->d[k]);
- ee->d[k] = (unsigned short)t;
- t >>= 16;
- }
- ee->next = NULL;
- if (ez == NULL)
- ez = ee;
- else
- es->next = ee;
- es = ee;
- }
- if (!t)
- { /* |[x]| < |[y]| -- result in complement coding */
- sz = - sz;
- t = 1;
- for (ee = ez; ee != NULL; ee = ee->next)
- for (k = 0; k <= 5; k++)
- { t += (0xFFFF - (unsigned int)ee->d[k]);
- ee->d[k] = (unsigned short)t;
- t >>= 16;
- }
- }
- }
- /* contruct and normalize result */
- mpz_set_si(z, 0);
- z->val = sz;
- z->ptr = ez;
- normalize(z);
- done: return;
- }
- void mpz_sub(mpz_t z, mpz_t x, mpz_t y)
- { /* set z to x - y */
- if (x == y)
- mpz_set_si(z, 0);
- else
- { y->val = - y->val;
- mpz_add(z, x, y);
- if (y != z) y->val = - y->val;
- }
- return;
- }
- void mpz_mul(mpz_t z, mpz_t x, mpz_t y)
- { /* set z to x * y */
- struct mpz_seg dumx, dumy, *ex, *ey, *es, *e;
- int sx, sy, k, nx, ny, n;
- unsigned int t;
- unsigned short *work, *wx, *wy;
- /* if [x] = 0 then [z] = 0 */
- if (x->val == 0)
- { xassert(x->ptr == NULL);
- mpz_set_si(z, 0);
- goto done;
- }
- /* if [y] = 0 then [z] = 0 */
- if (y->val == 0)
- { xassert(y->ptr == NULL);
- mpz_set_si(z, 0);
- goto done;
- }
- /* special case when both [x] and [y] are in short format */
- if (x->ptr == NULL && y->ptr == NULL)
- { int xval = x->val, yval = y->val, sz = +1;
- xassert(xval != 0x80000000 && yval != 0x80000000);
- if (xval < 0) xval = - xval, sz = - sz;
- if (yval < 0) yval = - yval, sz = - sz;
- if (xval <= 0x7FFFFFFF / yval)
- { mpz_set_si(z, sz * (xval * yval));
- goto done;
- }
- }
- /* convert [x] to long format, if necessary */
- if (x->ptr == NULL)
- { xassert(x->val != 0x80000000);
- if (x->val >= 0)
- { sx = +1;
- t = (unsigned int)(+ x->val);
- }
- else
- { sx = -1;
- t = (unsigned int)(- x->val);
- }
- ex = &dumx;
- ex->d[0] = (unsigned short)t;
- ex->d[1] = (unsigned short)(t >> 16);
- ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0;
- ex->next = NULL;
- }
- else
- { sx = x->val;
- xassert(sx == +1 || sx == -1);
- ex = x->ptr;
- }
- /* convert [y] to long format, if necessary */
- if (y->ptr == NULL)
- { xassert(y->val != 0x80000000);
- if (y->val >= 0)
- { sy = +1;
- t = (unsigned int)(+ y->val);
- }
- else
- { sy = -1;
- t = (unsigned int)(- y->val);
- }
- ey = &dumy;
- ey->d[0] = (unsigned short)t;
- ey->d[1] = (unsigned short)(t >> 16);
- ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0;
- ey->next = NULL;
- }
- else
- { sy = y->val;
- xassert(sy == +1 || sy == -1);
- ey = y->ptr;
- }
- /* determine the number of digits of [x] */
- nx = n = 0;
- for (e = ex; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++)
- { n++;
- if (e->d[k]) nx = n;
- }
- xassert(nx > 0);
- /* determine the number of digits of [y] */
- ny = n = 0;
- for (e = ey; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++)
- { n++;
- if (e->d[k]) ny = n;
- }
- xassert(ny > 0);
- /* we need working array containing at least nx+ny+ny places */
- work = gmp_get_work(nx+ny+ny);
- /* load digits of [x] */
- wx = &work[0];
- for (n = 0; n < nx; n++) wx[ny+n] = 0;
- for (n = 0, e = ex; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++, n++)
- if (e->d[k]) wx[ny+n] = e->d[k];
- /* load digits of [y] */
- wy = &work[nx+ny];
- for (n = 0; n < ny; n++) wy[n] = 0;
- for (n = 0, e = ey; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++, n++)
- if (e->d[k]) wy[n] = e->d[k];
- /* compute [x] * [y] */
- bigmul(nx, ny, wx, wy);
- /* construct and normalize result */
- mpz_set_si(z, 0);
- z->val = sx * sy;
- es = NULL;
- k = 6;
- for (n = 0; n < nx+ny; n++)
- { if (k > 5)
- { e = gmp_get_atom(sizeof(struct mpz_seg));
- e->d[0] = e->d[1] = e->d[2] = 0;
- e->d[3] = e->d[4] = e->d[5] = 0;
- e->next = NULL;
- if (z->ptr == NULL)
- z->ptr = e;
- else
- es->next = e;
- es = e;
- k = 0;
- }
- es->d[k++] = wx[n];
- }
- normalize(z);
- done: return;
- }
- void mpz_neg(mpz_t z, mpz_t x)
- { /* set z to 0 - x */
- mpz_set(z, x);
- z->val = - z->val;
- return;
- }
- void mpz_abs(mpz_t z, mpz_t x)
- { /* set z to the absolute value of x */
- mpz_set(z, x);
- if (z->val < 0) z->val = - z->val;
- return;
- }
- void mpz_div(mpz_t q, mpz_t r, mpz_t x, mpz_t y)
- { /* divide x by y, forming quotient q and/or remainder r
- if q = NULL then quotient is not stored; if r = NULL then
- remainder is not stored
- the sign of quotient is determined as in algebra while the
- sign of remainder is the same as the sign of dividend:
- +26 : +7 = +3, remainder is +5
- -26 : +7 = -3, remainder is -5
- +26 : -7 = -3, remainder is +5
- -26 : -7 = +3, remainder is -5 */
- struct mpz_seg dumx, dumy, *ex, *ey, *es, *e;
- int sx, sy, k, nx, ny, n;
- unsigned int t;
- unsigned short *work, *wx, *wy;
- /* divide by zero is not allowed */
- if (y->val == 0)
- { xassert(y->ptr == NULL);
- xfault("mpz_div: divide by zero not allowed\n");
- }
- /* if [x] = 0 then [q] = [r] = 0 */
- if (x->val == 0)
- { xassert(x->ptr == NULL);
- if (q != NULL) mpz_set_si(q, 0);
- if (r != NULL) mpz_set_si(r, 0);
- goto done;
- }
- /* special case when both [x] and [y] are in short format */
- if (x->ptr == NULL && y->ptr == NULL)
- { int xval = x->val, yval = y->val;
- xassert(xval != 0x80000000 && yval != 0x80000000);
- if (q != NULL) mpz_set_si(q, xval / yval);
- if (r != NULL) mpz_set_si(r, xval % yval);
- goto done;
- }
- /* convert [x] to long format, if necessary */
- if (x->ptr == NULL)
- { xassert(x->val != 0x80000000);
- if (x->val >= 0)
- { sx = +1;
- t = (unsigned int)(+ x->val);
- }
- else
- { sx = -1;
- t = (unsigned int)(- x->val);
- }
- ex = &dumx;
- ex->d[0] = (unsigned short)t;
- ex->d[1] = (unsigned short)(t >> 16);
- ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0;
- ex->next = NULL;
- }
- else
- { sx = x->val;
- xassert(sx == +1 || sx == -1);
- ex = x->ptr;
- }
- /* convert [y] to long format, if necessary */
- if (y->ptr == NULL)
- { xassert(y->val != 0x80000000);
- if (y->val >= 0)
- { sy = +1;
- t = (unsigned int)(+ y->val);
- }
- else
- { sy = -1;
- t = (unsigned int)(- y->val);
- }
- ey = &dumy;
- ey->d[0] = (unsigned short)t;
- ey->d[1] = (unsigned short)(t >> 16);
- ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0;
- ey->next = NULL;
- }
- else
- { sy = y->val;
- xassert(sy == +1 || sy == -1);
- ey = y->ptr;
- }
- /* determine the number of digits of [x] */
- nx = n = 0;
- for (e = ex; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++)
- { n++;
- if (e->d[k]) nx = n;
- }
- xassert(nx > 0);
- /* determine the number of digits of [y] */
- ny = n = 0;
- for (e = ey; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++)
- { n++;
- if (e->d[k]) ny = n;
- }
- xassert(ny > 0);
- /* if nx < ny then [q] = 0 and [r] = [x] */
- if (nx < ny)
- { if (r != NULL) mpz_set(r, x);
- if (q != NULL) mpz_set_si(q, 0);
- goto done;
- }
- /* we need working array containing at least nx+ny+1 places */
- work = gmp_get_work(nx+ny+1);
- /* load digits of [x] */
- wx = &work[0];
- for (n = 0; n < nx; n++) wx[n] = 0;
- for (n = 0, e = ex; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++, n++)
- if (e->d[k]) wx[n] = e->d[k];
- /* load digits of [y] */
- wy = &work[nx+1];
- for (n = 0; n < ny; n++) wy[n] = 0;
- for (n = 0, e = ey; e != NULL; e = e->next)
- for (k = 0; k <= 5; k++, n++)
- if (e->d[k]) wy[n] = e->d[k];
- /* compute quotient and remainder */
- xassert(wy[ny-1] != 0);
- bigdiv(nx-ny, ny, wx, wy);
- /* construct and normalize quotient */
- if (q != NULL)
- { mpz_set_si(q, 0);
- q->val = sx * sy;
- es = NULL;
- k = 6;
- for (n = ny; n <= nx; n++)
- { if (k > 5)
- { e = gmp_get_atom(sizeof(struct mpz_seg));
- e->d[0] = e->d[1] = e->d[2] = 0;
- e->d[3] = e->d[4] = e->d[5] = 0;
- e->next = NULL;
- if (q->ptr == NULL)
- q->ptr = e;
- else
- es->next = e;
- es = e;
- k = 0;
- }
- es->d[k++] = wx[n];
- }
- normalize(q);
- }
- /* construct and normalize remainder */
- if (r != NULL)
- { mpz_set_si(r, 0);
- r->val = sx;
- es = NULL;
- k = 6;
- for (n = 0; n < ny; n++)
- { if (k > 5)
- { e = gmp_get_atom(sizeof(struct mpz_seg));
- e->d[0] = e->d[1] = e->d[2] = 0;
- e->d[3] = e->d[4] = e->d[5] = 0;
- e->next = NULL;
- if (r->ptr == NULL)
- r->ptr = e;
- else
- es->next = e;
- es = e;
- k = 0;
- }
- es->d[k++] = wx[n];
- }
- normalize(r);
- }
- done: return;
- }
- void mpz_gcd(mpz_t z, mpz_t x, mpz_t y)
- { /* set z to the greatest common divisor of x and y */
- /* in case of arbitrary integers GCD(x, y) = GCD(|x|, |y|), and,
- in particular, GCD(0, 0) = 0 */
- mpz_t u, v, r;
- mpz_init(u);
- mpz_init(v);
- mpz_init(r);
- mpz_abs(u, x);
- mpz_abs(v, y);
- while (mpz_sgn(v))
- { mpz_div(NULL, r, u, v);
- mpz_set(u, v);
- mpz_set(v, r);
- }
- mpz_set(z, u);
- mpz_clear(u);
- mpz_clear(v);
- mpz_clear(r);
- return;
- }
- int mpz_cmp(mpz_t x, mpz_t y)
- { /* compare x and y; return a positive value if x > y, zero if
- x = y, or a nefative value if x < y */
- static struct mpz_seg zero = { { 0, 0, 0, 0, 0, 0 }, NULL };
- struct mpz_seg dumx, dumy, *ex, *ey;
- int cc, sx, sy, k;
- unsigned int t;
- if (x == y)
- { cc = 0;
- goto done;
- }
- /* special case when both [x] and [y] are in short format */
- if (x->ptr == NULL && y->ptr == NULL)
- { int xval = x->val, yval = y->val;
- xassert(xval != 0x80000000 && yval != 0x80000000);
- cc = (xval > yval ? +1 : xval < yval ? -1 : 0);
- goto done;
- }
- /* special case when [x] and [y] have different signs */
- if (x->val > 0 && y->val <= 0 || x->val == 0 && y->val < 0)
- { cc = +1;
- goto done;
- }
- if (x->val < 0 && y->val >= 0 || x->val == 0 && y->val > 0)
- { cc = -1;
- goto done;
- }
- /* convert [x] to long format, if necessary */
- if (x->ptr == NULL)
- { xassert(x->val != 0x80000000);
- if (x->val >= 0)
- { sx = +1;
- t = (unsigned int)(+ x->val);
- }
- else
- { sx = -1;
- t = (unsigned int)(- x->val);
- }
- ex = &dumx;
- ex->d[0] = (unsigned short)t;
- ex->d[1] = (unsigned short)(t >> 16);
- ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0;
- ex->next = NULL;
- }
- else
- { sx = x->val;
- xassert(sx == +1 || sx == -1);
- ex = x->ptr;
- }
- /* convert [y] to long format, if necessary */
- if (y->ptr == NULL)
- { xassert(y->val != 0x80000000);
- if (y->val >= 0)
- { sy = +1;
- t = (unsigned int)(+ y->val);
- }
- else
- { sy = -1;
- t = (unsigned int)(- y->val);
- }
- ey = &dumy;
- ey->d[0] = (unsigned short)t;
- ey->d[1] = (unsigned short)(t >> 16);
- ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0;
- ey->next = NULL;
- }
- else
- { sy = y->val;
- xassert(sy == +1 || sy == -1);
- ey = y->ptr;
- }
- /* main fragment */
- xassert(sx > 0 && sy > 0 || sx < 0 && sy < 0);
- cc = 0;
- for (; ex || ey; ex = ex->next, ey = ey->next)
- { if (ex == NULL) ex = &zero;
- if (ey == NULL) ey = &zero;
- for (k = 0; k <= 5; k++)
- { if (ex->d[k] > ey->d[k]) cc = +1;
- if (ex->d[k] < ey->d[k]) cc = -1;
- }
- }
- if (sx < 0) cc = - cc;
- done: return cc;
- }
- int mpz_sgn(mpz_t x)
- { /* return +1 if x > 0, 0 if x = 0, and -1 if x < 0 */
- int s;
- s = (x->val > 0 ? +1 : x->val < 0 ? -1 : 0);
- return s;
- }
- int mpz_out_str(void *_fp, int base, mpz_t x)
- { /* output x on stream fp, as a string in given base; the base
- may vary from 2 to 36;
- return the number of bytes written, or if an error occurred,
- return 0 */
- FILE *fp = _fp;
- mpz_t b, y, r;
- int n, j, nwr = 0;
- unsigned char *d;
- static char *set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- if (!(2 <= base && base <= 36))
- xfault("mpz_out_str: base = %d; invalid base\n", base);
- mpz_init(b);
- mpz_set_si(b, base);
- mpz_init(y);
- mpz_init(r);
- /* determine the number of digits */
- mpz_abs(y, x);
- for (n = 0; mpz_sgn(y) != 0; n++)
- mpz_div(y, NULL, y, b);
- if (n == 0) n = 1;
- /* compute the digits */
- d = xmalloc(n);
- mpz_abs(y, x);
- for (j = 0; j < n; j++)
- { mpz_div(y, r, y, b);
- xassert(0 <= r->val && r->val < base && r->ptr == NULL);
- d[j] = (unsigned char)r->val;
- }
- /* output the integer to the stream */
- if (fp == NULL) fp = stdout;
- if (mpz_sgn(x) < 0)
- fputc('-', fp), nwr++;
- for (j = n-1; j >= 0; j--)
- fputc(set[d[j]], fp), nwr++;
- if (ferror(fp)) nwr = 0;
- mpz_clear(b);
- mpz_clear(y);
- mpz_clear(r);
- xfree(d);
- return nwr;
- }
- /*====================================================================*/
- mpq_t _mpq_init(void)
- { /* initialize x, and set its value to 0/1 */
- mpq_t x;
- x = gmp_get_atom(sizeof(struct mpq));
- x->p.val = 0;
- x->p.ptr = NULL;
- x->q.val = 1;
- x->q.ptr = NULL;
- return x;
- }
- void mpq_clear(mpq_t x)
- { /* free the space occupied by x */
- mpz_set_si(&x->p, 0);
- xassert(x->p.ptr == NULL);
- mpz_set_si(&x->q, 0);
- xassert(x->q.ptr == NULL);
- /* free the number descriptor */
- gmp_free_atom(x, sizeof(struct mpq));
- return;
- }
- void mpq_canonicalize(mpq_t x)
- { /* remove any factors that are common to the numerator and
- denominator of x, and make the denominator positive */
- mpz_t f;
- xassert(x->q.val != 0);
- if (x->q.val < 0)
- { mpz_neg(&x->p, &x->p);
- mpz_neg(&x->q, &x->q);
- }
- mpz_init(f);
- mpz_gcd(f, &x->p, &x->q);
- if (!(f->val == 1 && f->ptr == NULL))
- { mpz_div(&x->p, NULL, &x->p, f);
- mpz_div(&x->q, NULL, &x->q, f);
- }
- mpz_clear(f);
- return;
- }
- void mpq_set(mpq_t z, mpq_t x)
- { /* set the value of z from x */
- if (z != x)
- { mpz_set(&z->p, &x->p);
- mpz_set(&z->q, &x->q);
- }
- return;
- }
- void mpq_set_si(mpq_t x, int p, unsigned int q)
- { /* set the value of x to p/q */
- if (q == 0)
- xfault("mpq_set_si: zero denominator not allowed\n");
- mpz_set_si(&x->p, p);
- xassert(q <= 0x7FFFFFFF);
- mpz_set_si(&x->q, q);
- return;
- }
- double mpq_get_d(mpq_t x)
- { /* convert x to a double, truncating if necessary */
- int np, nq;
- double p, q;
- p = mpz_get_d_2exp(&np, &x->p);
- q = mpz_get_d_2exp(&nq, &x->q);
- return ldexp(p / q, np - nq);
- }
- void mpq_set_d(mpq_t x, double val)
- { /* set x to val; there is no rounding, the conversion is exact */
- int s, n, d, j;
- double f;
- mpz_t temp;
- xassert(-DBL_MAX <= val && val <= +DBL_MAX);
- mpq_set_si(x, 0, 1);
- if (val > 0.0)
- s = +1;
- else if (val < 0.0)
- s = -1;
- else
- goto done;
- f = frexp(fabs(val), &n);
- /* |val| = f * 2^n, where 0.5 <= f < 1.0 */
- mpz_init(temp);
- while (f != 0.0)
- { f *= 16.0, n -= 4;
- d = (int)f;
- xassert(0 <= d && d <= 15);
- f -= (double)d;
- /* x := 16 * x + d */
- mpz_set_si(temp, 16);
- mpz_mul(&x->p, &x->p, temp);
- mpz_set_si(temp, d);
- mpz_add(&x->p, &x->p, temp);
- }
- mpz_clear(temp);
- /* x := x * 2^n */
- if (n > 0)
- { for (j = 1; j <= n; j++)
- mpz_add(&x->p, &x->p, &x->p);
- }
- else if (n < 0)
- { for (j = 1; j <= -n; j++)
- mpz_add(&x->q, &x->q, &x->q);
- mpq_canonicalize(x);
- }
- if (s < 0) mpq_neg(x, x);
- done: return;
- }
- void mpq_add(mpq_t z, mpq_t x, mpq_t y)
- { /* set z to x + y */
- mpz_t p, q;
- mpz_init(p);
- mpz_init(q);
- mpz_mul(p, &x->p, &y->q);
- mpz_mul(q, &x->q, &y->p);
- mpz_add(p, p, q);
- mpz_mul(q, &x->q, &y->q);
- mpz_set(&z->p, p);
- mpz_set(&z->q, q);
- mpz_clear(p);
- mpz_clear(q);
- mpq_canonicalize(z);
- return;
- }
- void mpq_sub(mpq_t z, mpq_t x, mpq_t y)
- { /* set z to x - y */
- mpz_t p, q;
- mpz_init(p);
- mpz_init(q);
- mpz_mul(p, &x->p, &y->q);
- mpz_mul(q, &x->q, &y->p);
- mpz_sub(p, p, q);
- mpz_mul(q, &x->q, &y->q);
- mpz_set(&z->p, p);
- mpz_set(&z->q, q);
- mpz_clear(p);
- mpz_clear(q);
- mpq_canonicalize(z);
- return;
- }
- void mpq_mul(mpq_t z, mpq_t x, mpq_t y)
- { /* set z to x * y */
- mpz_mul(&z->p, &x->p, &y->p);
- mpz_mul(&z->q, &x->q, &y->q);
- mpq_canonicalize(z);
- return;
- }
- void mpq_div(mpq_t z, mpq_t x, mpq_t y)
- { /* set z to x / y */
- mpz_t p, q;
- if (mpq_sgn(y) == 0)
- xfault("mpq_div: zero divisor not allowed\n");
- mpz_init(p);
- mpz_init(q);
- mpz_mul(p, &x->p, &y->q);
- mpz_mul(q, &x->q, &y->p);
- mpz_set(&z->p, p);
- mpz_set(&z->q, q);
- mpz_clear(p);
- mpz_clear(q);
- mpq_canonicalize(z);
- return;
- }
- void mpq_neg(mpq_t z, mpq_t x)
- { /* set z to 0 - x */
- mpq_set(z, x);
- mpz_neg(&z->p, &z->p);
- return;
- }
- void mpq_abs(mpq_t z, mpq_t x)
- { /* set z to the absolute value of x */
- mpq_set(z, x);
- mpz_abs(&z->p, &z->p);
- xassert(mpz_sgn(&x->q) > 0);
- return;
- }
- int mpq_cmp(mpq_t x, mpq_t y)
- { /* compare x and y; return a positive value if x > y, zero if
- x = y, or a nefative value if x < y */
- mpq_t temp;
- int s;
- mpq_init(temp);
- mpq_sub(temp, x, y);
- s = mpq_sgn(temp);
- mpq_clear(temp);
- return s;
- }
- int mpq_sgn(mpq_t x)
- { /* return +1 if x > 0, 0 if x = 0, and -1 if x < 0 */
- int s;
- s = mpz_sgn(&x->p);
- xassert(mpz_sgn(&x->q) > 0);
- return s;
- }
- int mpq_out_str(void *_fp, int base, mpq_t x)
- { /* output x on stream fp, as a string in given base; the base
- may vary from 2 to 36; output is in the form 'num/den' or if
- the denominator is 1 then just 'num';
- if the parameter fp is a null pointer, stdout is assumed;
- return the number of bytes written, or if an error occurred,
- return 0 */
- FILE *fp = _fp;
- int nwr;
- if (!(2 <= base && base <= 36))
- xfault("mpq_out_str: base = %d; invalid base\n", base);
- if (fp == NULL) fp = stdout;
- nwr = mpz_out_str(fp, base, &x->p);
- if (x->q.val == 1 && x->q.ptr == NULL)
- ;
- else
- { fputc('/', fp), nwr++;
- nwr += mpz_out_str(fp, base, &x->q);
- }
- if (ferror(fp)) nwr = 0;
- return nwr;
- }
- #endif
- /* eof */
|