* M_APM - mapmutil.c
*
* Copyright (C) 1999 - 2007 Michael C. Ring
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted. Permission to distribute
* the modified code is granted. Modifications are to be distributed by
* using the file 'license.txt' as a template to modify the file header.
* 'license.txt' is available in the official MAPM distribution.
*
* This software is provided "as is" without express or implied warranty.
*/
* $Id: mapmutil.c,v 1.26 2007/12/03 01:58:49 mike Exp $
*
* This file contains various utility functions needed by the
* library in addition to some basic user callable functions.
*
* $Log: mapmutil.c,v $
* Revision 1.26 2007/12/03 01:58:49 mike
* Update license
*
* Revision 1.25 2003/07/21 20:51:34 mike
* Modify error messages to be in a consistent format.
*
* Revision 1.24 2003/03/31 22:03:54 mike
* call generic error handling function
*
* Revision 1.23 2002/11/04 20:47:02 mike
* change m_apm_init so it compiles clean with a real C++ compiler
*
* Revision 1.22 2002/11/03 22:50:58 mike
* Updated function parameters to use the modern style
*
* Revision 1.21 2002/05/17 22:26:49 mike
* move some functions into another file
*
* Revision 1.20 2002/02/12 20:21:53 mike
* eliminate unneeded working arrays in _scale
* by processing the scaling operation in reverse
*
* Revision 1.19 2001/07/24 18:29:18 mike
* add util function to get address of
* the div/rem lookup tables
*
* Revision 1.18 2001/07/20 16:14:05 mike
* optimize normalize yet again
*
* Revision 1.17 2001/07/17 18:17:56 mike
* another optimization to _normalize
*
* Revision 1.16 2001/07/16 22:33:43 mike
* update free_all_util
*
* Revision 1.15 2001/07/16 19:56:26 mike
* add function M_free_all_util
*
* Revision 1.14 2001/07/16 18:10:21 mike
* optimize M_apm_normalize when moving multiple '00' bytes
*
* Revision 1.13 2001/02/11 22:36:43 mike
* modify parameters to REALLOC
*
* Revision 1.12 2001/01/23 21:17:38 mike
* add dedicated long->ascii conversion (instead of sprintf)
*
* Revision 1.11 2000/08/22 20:21:54 mike
* fix m_apm_exponent with exactly 0 as the input
*
* Revision 1.10 2000/08/22 00:01:26 mike
* add zero check in is_integer
*
* Revision 1.9 2000/08/21 23:34:44 mike
* add new function _is_integer
*
* Revision 1.8 2000/08/01 22:29:02 mike
* add sizeof int function call
*
* Revision 1.7 2000/05/19 16:21:03 mike
* delete M_check_dec_places, no longer needed
*
* Revision 1.6 2000/04/04 17:06:37 mike
* initialize C++ refcount struct element to 1
*
* Revision 1.5 2000/02/03 22:49:56 mike
* use MAPM_* generic memory function
*
* Revision 1.4 1999/09/18 03:06:41 mike
* fix m_apm_exponent
*
* Revision 1.3 1999/09/18 02:59:11 mike
* added new functions
*
* Revision 1.2 1999/05/15 02:21:14 mike
* add check for number of decimal places
*
* Revision 1.1 1999/05/10 20:56:31 mike
* Initial revision
*/
#include "m_apm_lc.h"
static UCHAR *M_mul_div = NULL;
static UCHAR *M_mul_rem = NULL;
static UCHAR M_mul_div_10[100];
static UCHAR M_mul_rem_10[100];
static int M_util_firsttime = TRUE;
static int M_firsttime3 = TRUE;
static M_APM M_work_0_5;
static char *M_init_error_msg = "\'m_apm_init\', Out of memory";
M_APM m_apm_init()
{
M_APM atmp;
if (M_firsttime3)
{
M_firsttime3 = FALSE;
M_init_util_data();
M_init_trig_globals();
}
if ((atmp = (M_APM)MAPM_MALLOC(sizeof(M_APM_struct))) == NULL)
{
M_apm_log_error_msg(M_APM_FATAL, M_init_error_msg);
}
atmp->m_apm_id = M_APM_IDENT;
atmp->m_apm_malloclength = 80;
atmp->m_apm_datalength = 1;
atmp->m_apm_refcount = 1;
atmp->m_apm_exponent = 0;
atmp->m_apm_sign = 0;
if ((atmp->m_apm_data = (UCHAR *)MAPM_MALLOC(84)) == NULL)
{
M_apm_log_error_msg(M_APM_FATAL, M_init_error_msg);
}
atmp->m_apm_data[0] = 0;
return(atmp);
}
void m_apm_free(M_APM atmp)
{
if (atmp->m_apm_id == M_APM_IDENT)
{
atmp->m_apm_id = 0x0FFFFFF0L;
MAPM_FREE(atmp->m_apm_data);
MAPM_FREE(atmp);
}
else
{
M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_free\', Invalid M_APM variable");
}
}
void M_free_all_util()
{
if (M_util_firsttime == FALSE)
{
m_apm_free(M_work_0_5);
M_util_firsttime = TRUE;
}
if (M_firsttime3 == FALSE)
{
MAPM_FREE(M_mul_div);
MAPM_FREE(M_mul_rem);
M_mul_div = NULL;
M_mul_rem = NULL;
M_firsttime3 = TRUE;
}
}
* just a dummy wrapper to keep some compilers from complaining
*/
int M_get_sizeof_int()
{
return(sizeof(int));
}
void M_init_util_data()
{
int k;
UCHAR ndiv, nrem;
if (M_mul_div != NULL)
return;
M_mul_div = (UCHAR *)MAPM_MALLOC(10000 * sizeof(UCHAR));
M_mul_rem = (UCHAR *)MAPM_MALLOC(10000 * sizeof(UCHAR));
if (M_mul_div == NULL || M_mul_rem == NULL)
{
M_apm_log_error_msg(M_APM_FATAL, "\'M_init_util_data\', Out of memory");
}
ndiv = 0;
nrem = 0;
for (k=0; k < 100; k++)
{
M_mul_div_10[k] = ndiv;
M_mul_rem_10[k] = nrem;
if (++nrem == 10)
{
nrem = 0;
ndiv++;
}
}
ndiv = 0;
nrem = 0;
for (k=0; k < 10000; k++)
{
M_mul_div[k] = ndiv;
M_mul_rem[k] = nrem;
if (++nrem == 100)
{
nrem = 0;
ndiv++;
}
}
}
void M_get_div_rem_addr(UCHAR **ndivp, UCHAR **nremp)
{
*ndivp = M_mul_div;
*nremp = M_mul_rem;
}
void M_get_div_rem(int tbl_lookup, UCHAR *ndiv, UCHAR *nrem)
{
*ndiv = M_mul_div[tbl_lookup];
*nrem = M_mul_rem[tbl_lookup];
}
void M_get_div_rem_10(int tbl_lookup, UCHAR *ndiv, UCHAR *nrem)
{
*ndiv = M_mul_div_10[tbl_lookup];
*nrem = M_mul_rem_10[tbl_lookup];
}
void m_apm_round(M_APM btmp, int places, M_APM atmp)
{
int ii;
if (M_util_firsttime)
{
M_util_firsttime = FALSE;
M_work_0_5 = m_apm_init();
m_apm_set_string(M_work_0_5, "5");
}
ii = places + 1;
if (atmp->m_apm_datalength <= ii)
{
m_apm_copy(btmp,atmp);
return;
}
M_work_0_5->m_apm_exponent = atmp->m_apm_exponent - ii;
if (atmp->m_apm_sign > 0)
m_apm_add(btmp, atmp, M_work_0_5);
else
m_apm_subtract(btmp, atmp, M_work_0_5);
btmp->m_apm_datalength = ii;
M_apm_normalize(btmp);
}
void M_apm_normalize(M_APM atmp)
{
int i, index, datalength, exponent;
UCHAR *ucp, numdiv, numrem, numrem2;
if (atmp->m_apm_sign == 0)
return;
datalength = atmp->m_apm_datalength;
exponent = atmp->m_apm_exponent;
M_apm_pad(atmp, (datalength + 3));
while (TRUE)
{
M_get_div_rem_10((int)atmp->m_apm_data[0], &numdiv, &numrem);
if (numdiv >= 1)
break;
index = (datalength + 1) >> 1;
if (numrem == 0)
{
i = 0;
ucp = atmp->m_apm_data;
while (TRUE)
{
if (*ucp != 0)
break;
ucp++;
i++;
}
memmove(atmp->m_apm_data, ucp, (index + 1 - i));
datalength -= 2 * i;
exponent -= 2 * i;
}
else
{
for (i=0; i < index; i++)
{
M_get_div_rem_10((int)atmp->m_apm_data[i+1], &numdiv, &numrem2);
atmp->m_apm_data[i] = 10 * numrem + numdiv;
numrem = numrem2;
}
datalength--;
exponent--;
}
}
while (TRUE)
{
index = ((datalength + 1) >> 1) - 1;
if ((datalength & 1) == 0)
{
ucp = atmp->m_apm_data + index;
if (*ucp == 0)
{
while (TRUE)
{
datalength -= 2;
index--;
ucp--;
if (*ucp != 0)
break;
}
}
}
M_get_div_rem_10((int)atmp->m_apm_data[index], &numdiv, &numrem);
if (numrem != 0)
break;
if ((datalength & 1) != 0)
{
if (numdiv != 0)
break;
}
if (datalength == 1)
{
atmp->m_apm_sign = 0;
exponent = 0;
break;
}
datalength--;
}
atmp->m_apm_datalength = datalength;
atmp->m_apm_exponent = exponent;
}
void M_apm_scale(M_APM ctmp, int count)
{
int ii, numb, ct;
UCHAR *chp, numdiv, numdiv2, numrem;
void *vp;
ct = count;
ii = (ctmp->m_apm_datalength + ct + 1) >> 1;
if (ii > ctmp->m_apm_malloclength)
{
if ((vp = MAPM_REALLOC(ctmp->m_apm_data, (ii + 32))) == NULL)
{
M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_scale\', Out of memory");
}
ctmp->m_apm_malloclength = ii + 28;
ctmp->m_apm_data = (UCHAR *)vp;
}
if ((ct & 1) != 0)
{
ct--;
chp = ctmp->m_apm_data;
ii = ((ctmp->m_apm_datalength + 1) >> 1) - 1;
if ((ctmp->m_apm_datalength & 1) == 0)
{
* original datalength is even:
*
* uv wx yz becomes --> 0u vw xy z0
*/
numdiv = 0;
while (TRUE)
{
M_get_div_rem_10((int)chp[ii], &numdiv2, &numrem);
chp[ii + 1] = 10 * numrem + numdiv;
numdiv = numdiv2;
if (ii == 0)
break;
ii--;
}
chp[0] = numdiv2;
}
else
{
* original datalength is odd:
*
* uv wx y0 becomes --> 0u vw xy
*/
M_get_div_rem_10((int)chp[ii], &numdiv2, &numrem);
if (ii == 0)
{
chp[0] = numdiv2;
}
else
{
while (TRUE)
{
M_get_div_rem_10((int)chp[ii - 1], &numdiv, &numrem);
chp[ii] = 10 * numrem + numdiv2;
numdiv2 = numdiv;
if (--ii == 0)
break;
}
chp[0] = numdiv;
}
}
ctmp->m_apm_exponent++;
ctmp->m_apm_datalength++;
}
if (ct > 0)
{
numb = (ctmp->m_apm_datalength + 1) >> 1;
ii = ct >> 1;
memmove((ctmp->m_apm_data + ii), ctmp->m_apm_data, numb);
memset(ctmp->m_apm_data, 0, ii);
ctmp->m_apm_datalength += ct;
ctmp->m_apm_exponent += ct;
}
}
void M_apm_pad(M_APM ctmp, int new_length)
{
int num1, numb, ct;
UCHAR numdiv, numrem;
void *vp;
ct = new_length;
if (ctmp->m_apm_datalength >= ct)
return;
numb = (ct + 1) >> 1;
if (numb > ctmp->m_apm_malloclength)
{
if ((vp = MAPM_REALLOC(ctmp->m_apm_data, (numb + 32))) == NULL)
{
M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_pad\', Out of memory");
}
ctmp->m_apm_malloclength = numb + 28;
ctmp->m_apm_data = (UCHAR *)vp;
}
num1 = (ctmp->m_apm_datalength + 1) >> 1;
if ((ctmp->m_apm_datalength & 1) != 0)
{
M_get_div_rem_10((int)ctmp->m_apm_data[num1 - 1], &numdiv, &numrem);
ctmp->m_apm_data[num1 - 1] = 10 * numdiv;
}
memset((ctmp->m_apm_data + num1), 0, (numb - num1));
ctmp->m_apm_datalength = ct;
}
debug_dsp(cc)
M_APM cc;
{
static char buffer[8192];
m_apm_to_string(buffer, -1, cc);
printf("(dsp func) = [%s]\n",buffer);
}
*/