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
111 static UCHAR *M_mul_div = NULL;
112 static UCHAR *M_mul_rem = NULL;
113
114 static UCHAR M_mul_div_10[100];
115 static UCHAR M_mul_rem_10[100];
116
117 static int M_util_firsttime = TRUE;
118 static int M_firsttime3 = TRUE;
119
120 static M_APM M_work_0_5;
121
122 static char *M_init_error_msg = "\'m_apm_init\', Out of memory";
123
124 /****************************************************************************/
m_apm_init()125 M_APM m_apm_init()
126 {
127 M_APM atmp;
128
129 if (M_firsttime3)
130 {
131 M_firsttime3 = FALSE;
132 M_init_util_data();
133 M_init_trig_globals();
134 }
135
136 if ((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
143 atmp->m_apm_id = M_APM_IDENT;
144 atmp->m_apm_malloclength = 80;
145 atmp->m_apm_datalength = 1;
146 atmp->m_apm_refcount = 1; /* not for us, for MAPM C++ class */
147 atmp->m_apm_exponent = 0;
148 atmp->m_apm_sign = 0;
149
150 if ((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
157 atmp->m_apm_data[0] = 0;
158 return(atmp);
159 }
160 /****************************************************************************/
m_apm_free(M_APM atmp)161 void m_apm_free(M_APM atmp)
162 {
163 if (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 }
169 else
170 {
171 M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_free\', Invalid M_APM variable");
172 }
173 }
174 /****************************************************************************/
M_free_all_util()175 void M_free_all_util()
176 {
177 if (M_util_firsttime == FALSE)
178 {
179 m_apm_free(M_work_0_5);
180 M_util_firsttime = TRUE;
181 }
182
183 if (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 */
M_get_sizeof_int()197 int M_get_sizeof_int()
198 {
199 return(sizeof(int));
200 }
201 /****************************************************************************/
M_init_util_data()202 void M_init_util_data()
203 {
204 int k;
205 UCHAR ndiv, nrem;
206
207 if (M_mul_div != NULL)
208 return;
209
210 M_mul_div = (UCHAR *)MAPM_MALLOC(10000 * sizeof(UCHAR));
211 M_mul_rem = (UCHAR *)MAPM_MALLOC(10000 * sizeof(UCHAR));
212
213 if (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
220 ndiv = 0;
221 nrem = 0;
222
223 for (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
235 ndiv = 0;
236 nrem = 0;
237
238 for (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 /****************************************************************************/
M_get_div_rem_addr(UCHAR ** ndivp,UCHAR ** nremp)251 void M_get_div_rem_addr(UCHAR **ndivp, UCHAR **nremp)
252 {
253 *ndivp = M_mul_div;
254 *nremp = M_mul_rem;
255 }
256 /****************************************************************************/
M_get_div_rem(int tbl_lookup,UCHAR * ndiv,UCHAR * nrem)257 void 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 /****************************************************************************/
M_get_div_rem_10(int tbl_lookup,UCHAR * ndiv,UCHAR * nrem)263 void 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 /****************************************************************************/
m_apm_round(M_APM btmp,int places,M_APM atmp)269 void m_apm_round(M_APM btmp, int places, M_APM atmp)
270 {
271 int ii;
272
273 if (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
281 ii = places + 1;
282
283 if (atmp->m_apm_datalength <= ii)
284 {
285 m_apm_copy(btmp,atmp);
286 return;
287 }
288
289 M_work_0_5->m_apm_exponent = atmp->m_apm_exponent - ii;
290
291 if (atmp->m_apm_sign > 0)
292 m_apm_add(btmp, atmp, M_work_0_5);
293 else
294 m_apm_subtract(btmp, atmp, M_work_0_5);
295
296 btmp->m_apm_datalength = ii;
297 M_apm_normalize(btmp);
298 }
299 /****************************************************************************/
M_apm_normalize(M_APM atmp)300 void M_apm_normalize(M_APM atmp)
301 {
302 int i, index, datalength, exponent;
303 UCHAR *ucp, numdiv, numrem, numrem2;
304
305 if (atmp->m_apm_sign == 0)
306 return;
307
308 datalength = atmp->m_apm_datalength;
309 exponent = 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
315 M_apm_pad(atmp, (datalength + 3));
316
317 while (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
358 while (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
400 atmp->m_apm_datalength = datalength;
401 atmp->m_apm_exponent = exponent;
402 }
403 /****************************************************************************/
M_apm_scale(M_APM ctmp,int count)404 void M_apm_scale(M_APM ctmp, int count)
405 {
406 int ii, numb, ct;
407 UCHAR *chp, numdiv, numdiv2, numrem;
408 void *vp;
409
410 ct = count;
411
412 ii = (ctmp->m_apm_datalength + ct + 1) >> 1;
413 if (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
426 if ((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
494 if (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 /****************************************************************************/
M_apm_pad(M_APM ctmp,int new_length)507 void M_apm_pad(M_APM ctmp, int new_length)
508 {
509 int num1, numb, ct;
510 UCHAR numdiv, numrem;
511 void *vp;
512
513 ct = new_length;
514 if (ctmp->m_apm_datalength >= ct)
515 return;
516
517 numb = (ct + 1) >> 1;
518 if (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
531 num1 = (ctmp->m_apm_datalength + 1) >> 1;
532
533 if ((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
539 memset((ctmp->m_apm_data + num1), 0, (numb - num1));
540 ctmp->m_apm_datalength = ct;
541 }
542 /****************************************************************************/
543
544 /*
545 debug_dsp(cc)
546 M_APM cc;
547 {
548 static char buffer[8192];
549
550 m_apm_to_string(buffer, -1, cc);
551 printf("(dsp func) = [%s]\n",buffer);
552
553 }
554 */
555
556