/*
 * const - constant number storage module
 *
 * Copyright (C) 1999-2007,2021,2022  David I. Bell
 *
 * Calc is open software; you can redistribute it and/or modify it under
 * the terms of the version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * Calc 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 Lesser General
 * Public License for more details.
 *
 * A copy of version 2.1 of the GNU Lesser General Public License is
 * distributed with calc under the filename COPYING-LGPL.  You should have
 * received a copy with calc; if not, write to Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Under source code control:	1990/02/15 01:48:14
 * File existed as early as:	before 1990
 *
 * Share and enjoy!  :-)	http://www.isthe.com/chongo/tech/comp/calc/
 */


#include <stdio.h>
#include "calc.h"
#include "qmath.h"


#include "attribute.h"
#include "banned.h"	/* include after system header <> includes */


#define CONSTALLOCSIZE 400	/* number of constants to allocate */

STATIC unsigned long constcount;/* number of constants defined */
STATIC long constavail;		/* number of constants available */
STATIC NUMBER **consttable;	/* table of constants */


void
initconstants(void)
{
	int i;

	consttable = (NUMBER **) calloc(sizeof(NUMBER *), CONSTALLOCSIZE);
	if (consttable == NULL) {
		math_error("Unable to allocate constant table");
		not_reached();
	}
	for (i = 0; initnumbs[i] != NULL; i++) {
		consttable[i] = initnumbs[i];
	}
	constcount = i-1;
	constavail = CONSTALLOCSIZE - constcount;
}


/*
 * Read in a literal real number and add it to the table of constant numbers,
 * creating a new entry if necessary.  The incoming number is a string
 * value which must have a correct format.
 * Returns the index of the number in the constant table.
 *
 * given:
 *	str		string representation of number
 */
long
addnumber(char *str)
{
	NUMBER *q;

	q = str2q(str);
	return addqconstant(q);
}


/*
 * Add a particular number to the constant table.
 * Returns the index of the number in the constant table, or zero
 * if the number could not be saved.  The incoming number if freed
 * if it is already in the table.
 *
 * XXX - we should hash the constant table
 *
 * given:
 *	q		number to be added
 */
long
addqconstant(NUMBER *q)
{
	register NUMBER **tp;	/* pointer to current number */
	register NUMBER *t;	/* number being tested */
	unsigned long index;	/* index into constant table */
	long numlen;		/* numerator length */
	long denlen;		/* denominator length */
	HALF numlow;		/* bottom value of numerator */
	HALF denlow;		/* bottom value of denominator */
	long first;		/* first non-null position found */
	BOOL havefirst;

	if (constavail <= 0) {
		if (consttable == NULL) {
			initconstants();
		} else {
			tp = (NUMBER **) realloc((char *) consttable,
			sizeof(NUMBER *) * (constcount + CONSTALLOCSIZE));
			if (tp == NULL) {
				math_error("Unable to reallocate const table");
				not_reached();
			}
			consttable = tp;
			constavail = CONSTALLOCSIZE;
		}
	}
	numlen = q->num.len;
	denlen = q->den.len;
	numlow = q->num.v[0];
	denlow = q->den.v[0];
	first = 0;
	havefirst = FALSE;
	tp = consttable;
	for (index = 0; index < constcount; index++, tp++) {
		t = *tp;
		if (t == NULL) {	/* paranoia */
			break;
		}
		if (t->links == 0) {
			if (!havefirst) {
				havefirst = TRUE;
				first = index;
			}
			continue;
		}
		if (q == t) {
			if (q->links == 1) {
				if (havefirst) {
					*tp = consttable[first];
					consttable[first] = q;
				} else {
					havefirst = TRUE;
					first = index;
				}
				continue;
			}
			return index;
		}

		if ((numlen != t->num.len) || (numlow != t->num.v[0]))
			continue;
		if ((denlen != t->den.len) || (denlow != t->den.v[0]))
			continue;
		if (q->num.sign != t->num.sign)
			continue;
		if (qcmp(q, t) == 0) {
			t->links++;
			qfree(q);
			return index;
		}
	}
	if (havefirst) {
		consttable[first] = q;
		return first;
	}
	constavail--;
	consttable[constcount++] = q;
	return index;
}


/*
 * Return the value of a constant number given its index.
 * Returns address of the number, or NULL if the index is illegal
 * or points to freed position.
 */
NUMBER *
constvalue(unsigned long index)
{
	if (index >= constcount) {
		math_error("Bad index value for constvalue");
		not_reached();
	}
	if (consttable[index]->links == 0) {
		math_error("Constvalue has been freed!!!");
		not_reached();
	}
	return consttable[index];
}


void
freeconstant(unsigned long index)
{
	NUMBER *q;

	if (index >= constcount) {
		math_error("Bad index value for freeconst");
		not_reached();
	}
	q = consttable[index];
	if (q->links == 0) {
		math_error("Attempting to free freed const location");
		not_reached();
	}
	qfree(q);
	if (index == constcount - 1) {
		trimconstants();
	}
}


void
trimconstants(void)
{
	while (constcount > 0 &&
	       (consttable[constcount-1] == NULL ||
		consttable[constcount-1]->links == 0)) {
		constcount--;
		constavail++;
	}
}

void
showconstants(void)
{
	unsigned long index;
	NUMBER **qp;
	long count;

	qp = consttable;
	count = 0;
	for (index = 0; index < constcount; index++, qp++) {
		if ((*qp)->links) {
			if (!count) {
				printf("\n   Index   Links   Value\n");
			}
			count++;
			printf("\n%8ld%8ld    ", index, (*qp)->links);
			fitprint(*qp, 40);
		}
	}
	printf("\n\nNumber = %ld\n", count);
}


/* END CODE */
