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 /****************************************************************************/ 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 /****************************************************************************/ 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 /****************************************************************************/ 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 */ 197 int M_get_sizeof_int() 198 { 199 return(sizeof(int)); 200 } 201 /****************************************************************************/ 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 /****************************************************************************/ 251 void M_get_div_rem_addr(UCHAR **ndivp, UCHAR **nremp) 252 { 253 *ndivp = M_mul_div; 254 *nremp = M_mul_rem; 255 } 256 /****************************************************************************/ 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 /****************************************************************************/ 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 /****************************************************************************/ 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 /****************************************************************************/ 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 /****************************************************************************/ 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 /****************************************************************************/ 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