/*
 * Legal Notice
 *
 * This document and associated source code (the "Work") is a part of a
 * benchmark specification maintained by the TPC.
 *
 * The TPC reserves all right, title, and interest to the Work as provided
 * under U.S. and international laws, including without limitation all patent
 * and trademark rights therein.
 *
 * No Warranty
 *
 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION
 *     CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE
 *     AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER
 *     WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY,
 *     INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES,
 *     DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR
 *     PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF
 *     WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE.
 *     ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT,
 *     QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT
 *     WITH REGARD TO THE WORK.
 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO
 *     ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE
 *     COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS
 *     OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT,
 *     INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
 *     OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT
 *     RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD
 *     ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * Contributors:
 * Gradient Systems
 */

/*** includes ***/
#include "config.h"
#include "porting.h"
#ifndef USE_STDLIB_H
#include <malloc.h>
#endif
#include <stdio.h>
#include "decimal.h"
#include "mathops.h"

/*
 * Routine: set_precision(decimal_t *dest, int size, int precision)
 * Purpose:
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns: None
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
void set_precision(decimal_t *dest, int scale, int precision) {
	dest->scale = scale;
	dest->precision = precision;
	dest->number = 0;
	dest->flags = 0;

	return;
}

/*
 * Routine: mk_decimal(int size, int precision)
 * Purpose: initialize a decimal_t
 * Algorithm:
 * Data Structures:
 *
 * Params:	int size:		total number of places in the decimal
 *			int precision:	number of places in the fraction
 * Returns: decimal_t *
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
decimal_t *mk_decimal(int s, int p) {
	decimal_t *res;

	if ((s < 0) || (p < 0))
		return (NULL);

	res = (decimal_t *)malloc(sizeof(struct DECIMAL_T));
	MALLOC_CHECK(res);

	res->flags = 0;
	res->scale = s;
	res->precision = p;
	res->flags |= FL_INIT;

	return (res);
}

/*
 * Routine: itodec(int src, decimal_t *dest)
 * Purpose: convert an integer to a decimal_t
 * Algorithm:
 * Data Structures:
 *
 * Params: source integer
 * Returns: decimal_t *
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO:
 * 20000104 need to set errno on error
 */
int itodec(decimal_t *dest, int src) {
	int scale = 1, bound = 1;

	while ((bound * 10) <= src) {
		scale += 1;
		bound *= 10;
	}

	dest->precision = 0;
	dest->scale = scale;
	dest->number = src;

	return (0);
}

/*
 * Routine: ftodec(double f, decimal_t *dec)
 * Purpose: Convert a double to a decimal_t
 * Algorithm:
 * Data Structures:
 *
 * Params: double f
 * Returns: decimal_t *
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int ftodec(decimal_t *dest, double f) {
	static char valbuf[20];

	sprintf(valbuf, "%f", f);

	return (strtodec(dest, valbuf));
}

/*
 * Routine: strtodec()
 * Purpose: Convert an ascii string to a decimal_t structure
 * Algorithm:
 * Data Structures:
 *
 * Params: char *s
 * Returns: decimal_t *
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int strtodec(decimal_t *dest, char *s) {
	int i;
	char *d_pt;
	char valbuf[20];

	strcpy(valbuf, s);
	dest->flags = 0;
	if ((d_pt = strchr(valbuf, '.')) == NULL) {
		dest->scale = strlen(valbuf);
		dest->number = atoi(valbuf);
		dest->precision = 0;
	} else {
		*d_pt = '\0';
		d_pt += 1;
		dest->scale = strlen(valbuf);
		dest->number = atoi(valbuf);
		dest->precision = strlen(d_pt);
		for (i = 0; i < dest->precision; i++)
			dest->number *= 10;
		dest->number += atoi(d_pt);
	}

	if (*s == '-' && dest->number > 0)
		dest->number *= -1;

	return (0);
}

/*
 * Routine: dectostr(decimal_t *d, char *buf)
 * Purpose: convert a decimal structure to a string
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns: char *; NULL on success
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int dectostr(char *dest, decimal_t *d) {
	ds_key_t number;
	int i;
	static char szFormat[20];

	if (!InitConstants::dectostr_init) {
		sprintf(szFormat, "%s.%s", HUGE_FORMAT, HUGE_FORMAT);
		InitConstants::dectostr_init = 1;
	}

	if (d == NULL || dest == NULL)
		return (-1);
	for (number = d->number, i = 0; i < d->precision; i++)
		number /= 10;

	sprintf(dest, szFormat, number, d->number - number);

	return (0);
}

/*
 * Routine: dectof(float *dest, decimal_t *d)
 * Purpose: convert a decimal structure to a double
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns: char *; NULL on success
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int dectoflt(double *dest, decimal_t *d) {
	if ((dest == NULL) || (d == NULL))
		return (-1);
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
	*dest = d->number;
#ifdef _WIN32
#pragma warning(default : 4244)
#endif
	while (--d->precision > 0)
		*dest /= 10.0;

	return (0);
}

/*
 * Routine: decimal_t_op(int op, decimal_t *operand1, decimal_t *operand2)
 * Purpose: execute arbitrary binary operations on decimal_t's
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By:
 * Calls:
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int decimal_t_op(decimal_t *dest, int op, decimal_t *d1, decimal_t *d2) {
	int res, np;
	float f1, f2;

	if ((d1 == NULL) || (d2 == NULL))
		return (-1);

	dest->scale = (d1->scale > d2->scale) ? d1->scale : d2->scale;
	if (d1->precision > d2->precision) {
		dest->precision = d1->precision;
	} else {
		dest->precision = d2->precision;
	}

	switch (op) {
	case OP_PLUS:
		dest->number = d1->number + d2->number;
		break;
	case OP_MINUS:
		dest->number = d1->number - d2->number;
		break;
	case OP_MULT:
		res = d1->precision + d2->precision;
		dest->number = d1->number * d2->number;
		while (res-- > dest->precision)
			dest->number /= 10;
		break;
	case OP_DIV:
		f1 = (float)d1->number;
		np = d1->precision;
		while (np < dest->precision) {
			f1 *= 10.0;
			np += 1;
		}
		np = 0;
		while (np < dest->precision) {
			f1 *= 10.0;
			np += 1;
		}
		f2 = (float)d2->number;
		np = d2->precision;
		while (np < dest->precision) {
			f2 *= 10.0;
			np += 1;
		}

		dest->number = (int)(f1 / f2);
		break;
	default:
		printf("decimal_t_op does not support op %d\n", op);
		exit(1);
		break;
	}

	return (0);
}

#ifdef TEST
main() {
	decimal_t *res;
	int code;

	/* mk_decimal */
	res = mk_decimal(5, 2);
	if (res == NULL) {
		printf("mk_decimal returned NULL\n");
		exit(-1);
	}

	/* itodec */
	itodec(res, 0);
	code = dectoi(res);
	if (code) {
		printf("r_decimal:itodec(0, res) != 0 (%d)\n", code);
		exit(-1);
	}

	itodec(res, 999);
	code = dectoi(res);
	if (code != 999) {
		printf("r_decimal:itodec(999, res) != 0 (%d)\n", code);
		exit(-1);
	}

	exit(0);
}
#endif /* TEST */
