1
2/*
3 *  M_APM  -  mapmutil.c
4 *
5 *  Copyright (C) 1999 - 2007   Michael C. Ring
6 *
7 *  Permission to use, copy, and distribute this software and its
8 *  documentation for any purpose with or without fee is hereby granted,
9 *  provided that the above copyright notice appear in all copies and
10 *  that both that copyright notice and this permission notice appear
11 *  in supporting documentation.
12 *
13 *  Permission to modify the software is granted. Permission to distribute
14 *  the modified code is granted. Modifications are to be distributed by
15 *  using the file 'license.txt' as a template to modify the file header.
16 *  'license.txt' is available in the official MAPM distribution.
17 *
18 *  This software is provided "as is" without express or implied warranty.
19 */
20
21/*
22 *      $Id: mapmutil.c,v 1.26 2007/12/03 01:58:49 mike Exp $
23 *
24 *      This file contains various utility functions needed by the
25 *	library in addition to some basic user callable functions.
26 *
27 *      $Log: mapmutil.c,v $
28 *      Revision 1.26  2007/12/03 01:58:49  mike
29 *      Update license
30 *
31 *      Revision 1.25  2003/07/21 20:51:34  mike
32 *      Modify error messages to be in a consistent format.
33 *
34 *      Revision 1.24  2003/03/31 22:03:54  mike
35 *      call generic error handling function
36 *
37 *      Revision 1.23  2002/11/04 20:47:02  mike
38 *      change m_apm_init so it compiles clean with a real C++ compiler
39 *
40 *      Revision 1.22  2002/11/03 22:50:58  mike
41 *      Updated function parameters to use the modern style
42 *
43 *      Revision 1.21  2002/05/17 22:26:49  mike
44 *      move some functions into another file
45 *
46 *      Revision 1.20  2002/02/12 20:21:53  mike
47 *      eliminate unneeded working arrays in _scale
48 *      by processing the scaling operation in reverse
49 *
50 *      Revision 1.19  2001/07/24 18:29:18  mike
51 *      add util function to get address of
52 *      the div/rem lookup tables
53 *
54 *      Revision 1.18  2001/07/20 16:14:05  mike
55 *      optimize normalize yet again
56 *
57 *      Revision 1.17  2001/07/17 18:17:56  mike
58 *      another optimization to _normalize
59 *
60 *      Revision 1.16  2001/07/16 22:33:43  mike
61 *      update free_all_util
62 *
63 *      Revision 1.15  2001/07/16 19:56:26  mike
64 *      add function M_free_all_util
65 *
66 *      Revision 1.14  2001/07/16 18:10:21  mike
67 *      optimize M_apm_normalize when moving multiple '00' bytes
68 *
69 *      Revision 1.13  2001/02/11 22:36:43  mike
70 *      modify parameters to REALLOC
71 *
72 *      Revision 1.12  2001/01/23 21:17:38  mike
73 *      add dedicated long->ascii conversion (instead of sprintf)
74 *
75 *      Revision 1.11  2000/08/22 20:21:54  mike
76 *      fix m_apm_exponent with exactly 0 as the input
77 *
78 *      Revision 1.10  2000/08/22 00:01:26  mike
79 *      add zero check in is_integer
80 *
81 *      Revision 1.9  2000/08/21 23:34:44  mike
82 *      add new function _is_integer
83 *
84 *      Revision 1.8  2000/08/01 22:29:02  mike
85 *      add sizeof int function call
86 *
87 *      Revision 1.7  2000/05/19 16:21:03  mike
88 *      delete M_check_dec_places, no longer needed
89 *
90 *      Revision 1.6  2000/04/04 17:06:37  mike
91 *      initialize C++ refcount struct element to 1
92 *
93 *      Revision 1.5  2000/02/03 22:49:56  mike
94 *      use MAPM_* generic memory function
95 *
96 *      Revision 1.4  1999/09/18 03:06:41  mike
97 *      fix m_apm_exponent
98 *
99 *      Revision 1.3  1999/09/18 02:59:11  mike
100 *      added new functions
101 *
102 *      Revision 1.2  1999/05/15 02:21:14  mike
103 *      add check for number of decimal places
104 *
105 *      Revision 1.1  1999/05/10 20:56:31  mike
106 *      Initial revision
107 */
108
109#include "m_apm_lc.h"
110
111static  UCHAR	*M_mul_div = NULL;
112static  UCHAR   *M_mul_rem = NULL;
113
114static  UCHAR   M_mul_div_10[100];
115static	UCHAR   M_mul_rem_10[100];
116
117static	int	M_util_firsttime = TRUE;
118static	int     M_firsttime3 = TRUE;
119
120static	M_APM	M_work_0_5;
121
122static  char    *M_init_error_msg = "\'m_apm_init\', Out of memory";
123
124/****************************************************************************/
125M_APM	m_apm_init()
126{
127M_APM	atmp;
128
129if (M_firsttime3)
130  {
131   M_firsttime3 = FALSE;
132   M_init_util_data();
133   M_init_trig_globals();
134  }
135
136if ((atmp = (M_APM)MAPM_MALLOC(sizeof(M_APM_struct))) == NULL)
137  {
138   /* fatal, this does not return */
139
140   M_apm_log_error_msg(M_APM_FATAL, M_init_error_msg);
141  }
142
143atmp->m_apm_id           = M_APM_IDENT;
144atmp->m_apm_malloclength = 80;
145atmp->m_apm_datalength   = 1;
146atmp->m_apm_refcount     = 1;           /* not for us, for MAPM C++ class */
147atmp->m_apm_exponent     = 0;
148atmp->m_apm_sign         = 0;
149
150if ((atmp->m_apm_data = (UCHAR *)MAPM_MALLOC(84)) == NULL)
151  {
152   /* fatal, this does not return */
153
154   M_apm_log_error_msg(M_APM_FATAL, M_init_error_msg);
155  }
156
157atmp->m_apm_data[0] = 0;
158return(atmp);
159}
160/****************************************************************************/
161void	m_apm_free(M_APM atmp)
162{
163if (atmp->m_apm_id == M_APM_IDENT)
164  {
165   atmp->m_apm_id = 0x0FFFFFF0L;
166   MAPM_FREE(atmp->m_apm_data);
167   MAPM_FREE(atmp);
168  }
169else
170  {
171   M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_free\', Invalid M_APM variable");
172  }
173}
174/****************************************************************************/
175void	M_free_all_util()
176{
177if (M_util_firsttime == FALSE)
178  {
179   m_apm_free(M_work_0_5);
180   M_util_firsttime = TRUE;
181  }
182
183if (M_firsttime3 == FALSE)
184  {
185   MAPM_FREE(M_mul_div);
186   MAPM_FREE(M_mul_rem);
187
188   M_mul_div    = NULL;
189   M_mul_rem    = NULL;
190   M_firsttime3 = TRUE;
191  }
192}
193/****************************************************************************/
194/*
195 *      just a dummy wrapper to keep some compilers from complaining
196 */
197int 	M_get_sizeof_int()
198{
199return(sizeof(int));
200}
201/****************************************************************************/
202void	M_init_util_data()
203{
204int	k;
205UCHAR   ndiv, nrem;
206
207if (M_mul_div != NULL)
208  return;
209
210M_mul_div = (UCHAR *)MAPM_MALLOC(10000 * sizeof(UCHAR));
211M_mul_rem = (UCHAR *)MAPM_MALLOC(10000 * sizeof(UCHAR));
212
213if (M_mul_div == NULL || M_mul_rem == NULL)
214  {
215   /* fatal, this does not return */
216
217   M_apm_log_error_msg(M_APM_FATAL, "\'M_init_util_data\', Out of memory");
218  }
219
220ndiv = 0;
221nrem = 0;
222
223for (k=0; k < 100; k++)
224  {
225   M_mul_div_10[k] = ndiv;
226   M_mul_rem_10[k] = nrem;
227
228   if (++nrem == 10)
229     {
230      nrem = 0;
231      ndiv++;
232     }
233  }
234
235ndiv = 0;
236nrem = 0;
237
238for (k=0; k < 10000; k++)
239  {
240   M_mul_div[k] = ndiv;
241   M_mul_rem[k] = nrem;
242
243   if (++nrem == 100)
244     {
245      nrem = 0;
246      ndiv++;
247     }
248  }
249}
250/****************************************************************************/
251void	M_get_div_rem_addr(UCHAR **ndivp, UCHAR **nremp)
252{
253*ndivp = M_mul_div;
254*nremp = M_mul_rem;
255}
256/****************************************************************************/
257void	M_get_div_rem(int tbl_lookup, UCHAR *ndiv, UCHAR *nrem)
258{
259*ndiv = M_mul_div[tbl_lookup];
260*nrem = M_mul_rem[tbl_lookup];
261}
262/****************************************************************************/
263void	M_get_div_rem_10(int tbl_lookup, UCHAR *ndiv, UCHAR *nrem)
264{
265*ndiv = M_mul_div_10[tbl_lookup];
266*nrem = M_mul_rem_10[tbl_lookup];
267}
268/****************************************************************************/
269void	m_apm_round(M_APM btmp, int places, M_APM atmp)
270{
271int	ii;
272
273if (M_util_firsttime)
274  {
275   M_util_firsttime = FALSE;
276
277   M_work_0_5 = m_apm_init();
278   m_apm_set_string(M_work_0_5, "5");
279  }
280
281ii = places + 1;
282
283if (atmp->m_apm_datalength <= ii)
284  {
285   m_apm_copy(btmp,atmp);
286   return;
287  }
288
289M_work_0_5->m_apm_exponent = atmp->m_apm_exponent - ii;
290
291if (atmp->m_apm_sign > 0)
292  m_apm_add(btmp, atmp, M_work_0_5);
293else
294  m_apm_subtract(btmp, atmp, M_work_0_5);
295
296btmp->m_apm_datalength = ii;
297M_apm_normalize(btmp);
298}
299/****************************************************************************/
300void	M_apm_normalize(M_APM atmp)
301{
302int	i, index, datalength, exponent;
303UCHAR   *ucp, numdiv, numrem, numrem2;
304
305if (atmp->m_apm_sign == 0)
306  return;
307
308datalength = atmp->m_apm_datalength;
309exponent   = atmp->m_apm_exponent;
310
311/* make sure trailing bytes/chars are 0                */
312/* the following function will adjust the 'datalength' */
313/* we want the original value and will fix it later    */
314
315M_apm_pad(atmp, (datalength + 3));
316
317while (TRUE)			/* remove lead-in '0' if any */
318  {
319   M_get_div_rem_10((int)atmp->m_apm_data[0], &numdiv, &numrem);
320
321   if (numdiv >= 1)      /* number is normalized, done here */
322     break;
323
324   index = (datalength + 1) >> 1;
325
326   if (numrem == 0)      /* both nibbles are 0, we can move full bytes */
327     {
328      i = 0;
329      ucp = atmp->m_apm_data;
330
331      while (TRUE)	 /* find out how many '00' bytes we can move */
332        {
333	 if (*ucp != 0)
334	   break;
335
336         ucp++;
337	 i++;
338	}
339
340      memmove(atmp->m_apm_data, ucp, (index + 1 - i));
341      datalength -= 2 * i;
342      exponent -= 2 * i;
343     }
344   else
345     {
346      for (i=0; i < index; i++)
347        {
348         M_get_div_rem_10((int)atmp->m_apm_data[i+1], &numdiv, &numrem2);
349         atmp->m_apm_data[i] = 10 * numrem + numdiv;
350	 numrem = numrem2;
351        }
352
353      datalength--;
354      exponent--;
355     }
356  }
357
358while (TRUE)			/* remove trailing '0' if any */
359  {
360   index = ((datalength + 1) >> 1) - 1;
361
362   if ((datalength & 1) == 0)   /* back-up full bytes at a time if the */
363     {				/* current length is an even number    */
364      ucp = atmp->m_apm_data + index;
365      if (*ucp == 0)
366        {
367	 while (TRUE)
368	   {
369	    datalength -= 2;
370	    index--;
371	    ucp--;
372
373	    if (*ucp != 0)
374	      break;
375	   }
376	}
377     }
378
379   M_get_div_rem_10((int)atmp->m_apm_data[index], &numdiv, &numrem);
380
381   if (numrem != 0)		/* last digit non-zero, all done */
382     break;
383
384   if ((datalength & 1) != 0)   /* if odd, then first char must be non-zero */
385     {
386      if (numdiv != 0)
387        break;
388     }
389
390   if (datalength == 1)
391     {
392      atmp->m_apm_sign = 0;
393      exponent = 0;
394      break;
395     }
396
397   datalength--;
398  }
399
400atmp->m_apm_datalength = datalength;
401atmp->m_apm_exponent   = exponent;
402}
403/****************************************************************************/
404void	M_apm_scale(M_APM ctmp, int count)
405{
406int	ii, numb, ct;
407UCHAR	*chp, numdiv, numdiv2, numrem;
408void	*vp;
409
410ct = count;
411
412ii = (ctmp->m_apm_datalength + ct + 1) >> 1;
413if (ii > ctmp->m_apm_malloclength)
414  {
415   if ((vp = MAPM_REALLOC(ctmp->m_apm_data, (ii + 32))) == NULL)
416     {
417      /* fatal, this does not return */
418
419      M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_scale\', Out of memory");
420     }
421
422   ctmp->m_apm_malloclength = ii + 28;
423   ctmp->m_apm_data = (UCHAR *)vp;
424  }
425
426if ((ct & 1) != 0)          /* move odd number first */
427  {
428   ct--;
429   chp = ctmp->m_apm_data;
430   ii  = ((ctmp->m_apm_datalength + 1) >> 1) - 1;
431
432   if ((ctmp->m_apm_datalength & 1) == 0)
433     {
434      /*
435       *   original datalength is even:
436       *
437       *   uv  wx  yz   becomes  -->   0u  vw  xy  z0
438       */
439
440      numdiv = 0;
441
442      while (TRUE)
443        {
444         M_get_div_rem_10((int)chp[ii], &numdiv2, &numrem);
445
446	 chp[ii + 1] = 10 * numrem + numdiv;
447	 numdiv = numdiv2;
448
449	 if (ii == 0)
450	   break;
451
452         ii--;
453	}
454
455      chp[0] = numdiv2;
456     }
457   else
458     {
459      /*
460       *   original datalength is odd:
461       *
462       *   uv  wx  y0   becomes  -->   0u  vw  xy
463       */
464
465      M_get_div_rem_10((int)chp[ii], &numdiv2, &numrem);
466
467      if (ii == 0)
468        {
469         chp[0] = numdiv2;
470        }
471      else
472        {
473         while (TRUE)
474           {
475            M_get_div_rem_10((int)chp[ii - 1], &numdiv, &numrem);
476
477	    chp[ii] = 10 * numrem + numdiv2;
478	    numdiv2 = numdiv;
479
480	    if (--ii == 0)
481	      break;
482	   }
483
484         chp[0] = numdiv;
485        }
486     }
487
488   ctmp->m_apm_exponent++;
489   ctmp->m_apm_datalength++;
490  }
491
492/* ct is even here */
493
494if (ct > 0)
495  {
496   numb = (ctmp->m_apm_datalength + 1) >> 1;
497   ii   = ct >> 1;
498
499   memmove((ctmp->m_apm_data + ii), ctmp->m_apm_data, numb);
500   memset(ctmp->m_apm_data, 0, ii);
501
502   ctmp->m_apm_datalength += ct;
503   ctmp->m_apm_exponent += ct;
504  }
505}
506/****************************************************************************/
507void	M_apm_pad(M_APM ctmp, int new_length)
508{
509int	num1, numb, ct;
510UCHAR	numdiv, numrem;
511void	*vp;
512
513ct = new_length;
514if (ctmp->m_apm_datalength >= ct)
515  return;
516
517numb = (ct + 1) >> 1;
518if (numb > ctmp->m_apm_malloclength)
519  {
520   if ((vp = MAPM_REALLOC(ctmp->m_apm_data, (numb + 32))) == NULL)
521     {
522      /* fatal, this does not return */
523
524      M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_pad\', Out of memory");
525     }
526
527   ctmp->m_apm_malloclength = numb + 28;
528   ctmp->m_apm_data = (UCHAR *)vp;
529  }
530
531num1 = (ctmp->m_apm_datalength + 1) >> 1;
532
533if ((ctmp->m_apm_datalength & 1) != 0)
534  {
535   M_get_div_rem_10((int)ctmp->m_apm_data[num1 - 1], &numdiv, &numrem);
536   ctmp->m_apm_data[num1 - 1] = 10 * numdiv;
537  }
538
539memset((ctmp->m_apm_data + num1), 0, (numb - num1));
540ctmp->m_apm_datalength = ct;
541}
542/****************************************************************************/
543
544/*
545      debug_dsp(cc)
546      M_APM cc;
547      {
548static char buffer[8192];
549
550m_apm_to_string(buffer, -1, cc);
551printf("(dsp func) = [%s]\n",buffer);
552
553      }
554*/
555
556