/* calculate arctan (x) with the following series: x^3 x^5 x^7 x^9 arctan (x) == x - --- + --- - --- + --- ... 3 5 7 9 */ void M_arctan_near_0(M_APM rr, int places, M_APM aa) { M_APM tmp0, tmpR, tmp2, tmpS, digit, term; int tolerance, local_precision; long m1; tmp0 = M_get_stack_var(); tmp2 = M_get_stack_var(); tmpR = M_get_stack_var(); tmpS = M_get_stack_var(); term = M_get_stack_var(); digit = M_get_stack_var(); tolerance = aa->m_apm_exponent - places - 4; local_precision = places + 8 - aa->m_apm_exponent; m_apm_copy(term, aa); m_apm_copy(tmpS, aa); m_apm_multiply(tmp0, aa, aa); m_apm_round(tmp2, (local_precision + 8), tmp0); m1 = 1; while (TRUE) { m1 += 2; m_apm_set_long(digit, m1); m_apm_multiply(tmp0, term, tmp2); m_apm_round(term, local_precision, tmp0); m_apm_divide(tmp0, local_precision, term, digit); m_apm_subtract(tmpR, tmpS, tmp0); if ((tmp0->m_apm_exponent < tolerance) || (tmp0->m_apm_sign == 0)) { m_apm_round(rr, places, tmpR); break; } m1 += 2; m_apm_set_long(digit, m1); m_apm_multiply(tmp0, term, tmp2); m_apm_round(term, local_precision, tmp0); m_apm_divide(tmp0, local_precision, term, digit); m_apm_add(tmpS, tmpR, tmp0); if ((tmp0->m_apm_exponent < tolerance) || (tmp0->m_apm_sign == 0)) { m_apm_round(rr, places, tmpS); break; } } M_restore_stack(6); /* restore the 6 locals we used here */ }
/* compute int *n = round_to_nearest_int(a / log(2)) M_APM b = MAPM version of *n returns 0: OK -1, 1: failure */ int M_exp_compute_nn(int *n, M_APM b, M_APM a) { M_APM tmp0, tmp1; void *vp; char *cp, sbuf[48]; int kk; *n = 0; vp = NULL; cp = sbuf; tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); /* find 'n' and convert it to a normal C int */ /* we just need an approx 1/log(2) for this calculation */ m_apm_multiply(tmp1, a, MM_exp_log2R); /* round to the nearest int */ if (tmp1->m_apm_sign >= 0) { m_apm_add(tmp0, tmp1, MM_0_5); m_apm_floor(tmp1, tmp0); } else { m_apm_subtract(tmp0, tmp1, MM_0_5); m_apm_ceil(tmp1, tmp0); } kk = tmp1->m_apm_exponent; if (kk >= 42) { if ((vp = (void *)MAPM_MALLOC((kk + 16) * sizeof(char))) == NULL) { /* fatal, this does not return */ M_apm_log_error_msg(M_APM_FATAL, "\'M_exp_compute_nn\', Out of memory"); } cp = (char *)vp; } m_apm_to_integer_string(cp, tmp1); *n = atoi(cp); m_apm_set_long(b, (long)(*n)); kk = m_apm_compare(b, tmp1); if (vp != NULL) MAPM_FREE(vp); M_restore_stack(2); return(kk); }
/* calculate log (1 + x) with the following series: x y = ----- ( |y| < 1 ) x + 2 [ 1 + y ] y^3 y^5 y^7 log [-------] = 2 * [ y + --- + --- + --- ... ] [ 1 - y ] 3 5 7 */ void M_log_near_1(M_APM rr, int places, M_APM xx) { M_APM tmp0, tmp1, tmp2, tmpS, term; int tolerance, dplaces, local_precision; long m1; tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); tmpS = M_get_stack_var(); term = M_get_stack_var(); tolerance = xx->m_apm_exponent - (places + 6); dplaces = (places + 12) - xx->m_apm_exponent; m_apm_add(tmp0, xx, MM_Two); m_apm_divide(tmpS, (dplaces + 6), xx, tmp0); m_apm_copy(term, tmpS); m_apm_multiply(tmp0, tmpS, tmpS); m_apm_round(tmp2, (dplaces + 6), tmp0); m1 = 3L; while (TRUE) { m_apm_multiply(tmp0, term, tmp2); if ((tmp0->m_apm_exponent < tolerance) || (tmp0->m_apm_sign == 0)) break; local_precision = dplaces + tmp0->m_apm_exponent; if (local_precision < 20) local_precision = 20; m_apm_set_long(tmp1, m1); m_apm_round(term, local_precision, tmp0); m_apm_divide(tmp0, local_precision, term, tmp1); m_apm_add(tmp1, tmpS, tmp0); m_apm_copy(tmpS, tmp1); m1 += 2; } m_apm_multiply(tmp0, MM_Two, tmpS); m_apm_round(rr, places, tmp0); M_restore_stack(5); /* restore the 5 locals we used here */ }
/* calculate the exponential function using the following series : x^2 x^3 x^4 x^5 exp(x) == 1 + x + --- + --- + --- + --- ... 2! 3! 4! 5! */ void M_raw_exp(M_APM rr, int places, M_APM xx) { M_APM tmp0, digit, term; int tolerance, local_precision, prev_exp; long m1; tmp0 = M_get_stack_var(); term = M_get_stack_var(); digit = M_get_stack_var(); local_precision = places + 8; tolerance = -(places + 4); prev_exp = 0; m_apm_add(rr, MM_One, xx); m_apm_copy(term, xx); m1 = 2L; while (TRUE) { m_apm_set_long(digit, m1); m_apm_multiply(tmp0, term, xx); m_apm_divide(term, local_precision, tmp0, digit); m_apm_add(tmp0, rr, term); m_apm_copy(rr, tmp0); if ((term->m_apm_exponent < tolerance) || (term->m_apm_sign == 0)) break; if (m1 != 2L) { local_precision = local_precision + term->m_apm_exponent - prev_exp; if (local_precision < 20) local_precision = 20; } prev_exp = term->m_apm_exponent; m1++; } M_restore_stack(3); /* restore the 3 locals we used here */ }
/* * functions returns TRUE if the M_APM input number is prime * FALSE if it is not */ int is_number_prime(M_APM input) { int ii, ret, index; char sbuf[32]; /* * for reference: * * table size of 2 to filter multiples of 2 and 3 * table size of 8 to filter multiples of 2, 3 and 5 * table size of 480 to filter multiples of 2,3,5,7, and 11 * * this increment table will filter out all numbers * that are multiples of 2,3,5 and 7. */ static char incr_table[48] = { 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10 }; /* * since the real algorithm starts at 11 (to syncronize * with the increment table), we will cheat for numbers < 10. */ if (m_apm_compare(input, MM_Ten) <= 0) { m_apm_to_integer_string(sbuf, input); ii = atoi(sbuf); if (ii == 2 || ii == 3 || ii == 5 || ii == 7) return(TRUE); else return(FALSE); } ret = FALSE; index = 0; /* * see if the input number is a * multiple of 3, 5, or 7. */ m_apm_integer_div_rem(M_quot, M_rem, input, MM_Three); if (m_apm_sign(M_rem) == 0) /* remainder == 0 */ return(ret); m_apm_integer_div_rem(M_quot, M_rem, input, MM_Five); if (m_apm_sign(M_rem) == 0) return(ret); m_apm_set_long(M_digit, 7L); m_apm_integer_div_rem(M_quot, M_rem, input, M_digit); if (m_apm_sign(M_rem) == 0) return(ret); ii = m_apm_exponent(input) + 16; m_apm_sqrt(M_tmp1, ii, input); m_apm_add(M_limit, MM_Two, M_tmp1); m_apm_set_long(M_digit, 11L); /* now start at '11' to check */ while (TRUE) { if (m_apm_compare(M_digit, M_limit) >= 0) { ret = TRUE; break; } m_apm_integer_div_rem(M_quot, M_rem, input, M_digit); if (m_apm_sign(M_rem) == 0) /* remainder == 0 */ break; m_apm_set_long(M_tmp1, (long)incr_table[index]); m_apm_add(M_tmp0, M_digit, M_tmp1); m_apm_copy(M_digit, M_tmp0); if (++index == 48) index = 0; } return(ret); }
void m_apm_set_long_mt(M_APM atmp, long mm) { m_apm_enter(); m_apm_set_long(atmp,mm); m_apm_leave(); }
MAPM & MAPM::operator=(long l) { m_apm_set_long(val(),l); return *this; }
MAPM::MAPM(long l) { create(); m_apm_set_long(val(),l); }
void m_apm_log(M_APM r, int places, M_APM a) { M_APM tmp0, tmp1, tmp2; int mexp, dplaces; if (a->m_apm_sign <= 0) { M_apm_log_error_msg(M_APM_RETURN, "Warning! ... \'m_apm_log\', Negative argument"); M_set_to_zero(r); return; } tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); dplaces = places + 8; /* * if the input is real close to 1, use the series expansion * to compute the log. * * 0.9999 < a < 1.0001 */ m_apm_subtract(tmp0, a, MM_One); if (tmp0->m_apm_sign == 0) /* is input exactly 1 ?? */ { /* if so, result is 0 */ M_set_to_zero(r); M_restore_stack(3); return; } if (tmp0->m_apm_exponent <= -4) { M_log_near_1(r, places, tmp0); M_restore_stack(3); return; } /* make sure our log(10) is accurate enough for this calculation */ /* (and log(2) which is called from M_log_basic_iteration) */ M_check_log_places(dplaces + 25); mexp = a->m_apm_exponent; if (mexp >= -4 && mexp <= 4) { M_log_basic_iteration(r, places, a); } else { /* * use log (x * y) = log(x) + log(y) * * here we use y = exponent of our base 10 number. * * let 'C' = log(10) = 2.3025850929940.... * * then log(x * y) = log(x) + ( C * base_10_exponent ) */ m_apm_copy(tmp2, a); mexp = tmp2->m_apm_exponent - 2; tmp2->m_apm_exponent = 2; /* force number between 10 & 100 */ M_log_basic_iteration(tmp0, dplaces, tmp2); m_apm_set_long(tmp1, (long)mexp); m_apm_multiply(tmp2, tmp1, MM_lc_log10); m_apm_add(tmp1, tmp2, tmp0); m_apm_round(r, places, tmp1); } M_restore_stack(3); /* restore the 3 locals we used here */ }
/* * From Knuth, The Art of Computer Programming: * * This is the binary GCD algorithm as described * in the book (Algorithm B) */ void m_apm_gcd(M_APM r, M_APM u, M_APM v) { M_APM tmpM, tmpN, tmpT, tmpU, tmpV; int kk, kr, mm; long pow_2; /* 'is_integer' will return 0 || 1 */ if ((m_apm_is_integer(u) + m_apm_is_integer(v)) != 2) { M_apm_log_error_msg(M_APM_RETURN, "Warning! \'m_apm_gcd\', Non-integer input"); M_set_to_zero(r); return; } if (u->m_apm_sign == 0) { m_apm_absolute_value(r, v); return; } if (v->m_apm_sign == 0) { m_apm_absolute_value(r, u); return; } tmpM = M_get_stack_var(); tmpN = M_get_stack_var(); tmpT = M_get_stack_var(); tmpU = M_get_stack_var(); tmpV = M_get_stack_var(); m_apm_absolute_value(tmpU, u); m_apm_absolute_value(tmpV, v); /* Step B1 */ kk = 0; while (TRUE) { mm = 1; if (m_apm_is_odd(tmpU)) break; mm = 0; if (m_apm_is_odd(tmpV)) break; m_apm_multiply(tmpN, MM_0_5, tmpU); m_apm_copy(tmpU, tmpN); m_apm_multiply(tmpN, MM_0_5, tmpV); m_apm_copy(tmpV, tmpN); kk++; } /* Step B2 */ if (mm) { m_apm_negate(tmpT, tmpV); goto B4; } m_apm_copy(tmpT, tmpU); /* Step: */ B3: m_apm_multiply(tmpN, MM_0_5, tmpT); m_apm_copy(tmpT, tmpN); /* Step: */ B4: if (m_apm_is_even(tmpT)) goto B3; /* Step B5 */ if (tmpT->m_apm_sign == 1) m_apm_copy(tmpU, tmpT); else m_apm_negate(tmpV, tmpT); /* Step B6 */ m_apm_subtract(tmpT, tmpU, tmpV); if (tmpT->m_apm_sign != 0) goto B3; /* * result = U * 2 ^ kk */ if (kk == 0) m_apm_copy(r, tmpU); else { if (kk == 1) m_apm_multiply(r, tmpU, MM_Two); if (kk == 2) m_apm_multiply(r, tmpU, MM_Four); if (kk >= 3) { mm = kk / 28; kr = kk % 28; pow_2 = 1L << kr; if (mm == 0) { m_apm_set_long(tmpN, pow_2); m_apm_multiply(r, tmpU, tmpN); } else { m_apm_copy(tmpN, MM_One); m_apm_set_long(tmpM, 0x10000000L); /* 2 ^ 28 */ while (TRUE) { m_apm_multiply(tmpT, tmpN, tmpM); m_apm_copy(tmpN, tmpT); if (--mm == 0) break; } if (kr == 0) { m_apm_multiply(r, tmpU, tmpN); } else { m_apm_set_long(tmpM, pow_2); m_apm_multiply(tmpT, tmpN, tmpM); m_apm_multiply(r, tmpU, tmpT); } } } } M_restore_stack(5); }
void M_apm_sdivide(M_APM r, int places, M_APM a, M_APM b) { int j, k, m, b0, sign, nexp, indexr, icompare, iterations; long trial_numer; void *vp; if (M_div_firsttime) { M_div_firsttime = FALSE; M_div_worka = m_apm_init(); M_div_workb = m_apm_init(); M_div_tmp7 = m_apm_init(); M_div_tmp8 = m_apm_init(); M_div_tmp9 = m_apm_init(); } sign = a->m_apm_sign * b->m_apm_sign; if (sign == 0) /* one number is zero, result is zero */ { if (b->m_apm_sign == 0) { M_apm_log_error_msg(M_APM_RETURN, "\'M_apm_sdivide\', Divide by 0"); } M_set_to_zero(r); return; } /* * Knuth step D1. Since base = 100, base / 2 = 50. * (also make the working copies positive) */ if (b->m_apm_data[0] >= 50) { m_apm_absolute_value(M_div_worka, a); m_apm_absolute_value(M_div_workb, b); } else /* 'normal' step D1 */ { k = 100 / (b->m_apm_data[0] + 1); m_apm_set_long(M_div_tmp9, (long)k); m_apm_multiply(M_div_worka, M_div_tmp9, a); m_apm_multiply(M_div_workb, M_div_tmp9, b); M_div_worka->m_apm_sign = 1; M_div_workb->m_apm_sign = 1; } /* setup trial denominator for step D3 */ b0 = 100 * (int)M_div_workb->m_apm_data[0]; if (M_div_workb->m_apm_datalength >= 3) b0 += M_div_workb->m_apm_data[1]; nexp = M_div_worka->m_apm_exponent - M_div_workb->m_apm_exponent; if (nexp > 0) iterations = nexp + places + 1; else iterations = places + 1; k = (iterations + 1) >> 1; /* required size of result, in bytes */ if (k > r->m_apm_malloclength) { if ((vp = MAPM_REALLOC(r->m_apm_data, (k + 32))) == NULL) { /* fatal, this does not return */ M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_sdivide\', Out of memory"); } r->m_apm_malloclength = k + 28; r->m_apm_data = (UCHAR *)vp; } /* clear the exponent in the working copies */ M_div_worka->m_apm_exponent = 0; M_div_workb->m_apm_exponent = 0; /* if numbers are equal, ratio == 1.00000... */ if ((icompare = m_apm_compare(M_div_worka, M_div_workb)) == 0) { iterations = 1; r->m_apm_data[0] = 10; nexp++; } else /* ratio not 1, do the real division */ { if (icompare == 1) /* numerator > denominator */ { nexp++; /* to adjust the final exponent */ M_div_worka->m_apm_exponent += 1; /* multiply numerator by 10 */ } else /* numerator < denominator */ { M_div_worka->m_apm_exponent += 2; /* multiply numerator by 100 */ } indexr = 0; m = 0; while (TRUE) { /* * Knuth step D3. Only use the 3rd -> 6th digits if the number * actually has that many digits. */ trial_numer = 10000L * (long)M_div_worka->m_apm_data[0]; if (M_div_worka->m_apm_datalength >= 5) { trial_numer += 100 * M_div_worka->m_apm_data[1] + M_div_worka->m_apm_data[2]; } else { if (M_div_worka->m_apm_datalength >= 3) trial_numer += 100 * M_div_worka->m_apm_data[1]; } j = (int)(trial_numer / b0); /* * Since the library 'normalizes' all the results, we need * to look at the exponent of the number to decide if we * have a lead in 0n or 00. */ if ((k = 2 - M_div_worka->m_apm_exponent) > 0) { while (TRUE) { j /= 10; if (--k == 0) break; } } if (j == 100) /* qhat == base ?? */ j = 99; /* if so, decrease by 1 */ m_apm_set_long(M_div_tmp8, (long)j); m_apm_multiply(M_div_tmp7, M_div_tmp8, M_div_workb); /* * Compare our q-hat (j) against the desired number. * j is either correct, 1 too large, or 2 too large * per Theorem B on pg 272 of Art of Compter Programming, * Volume 2, 3rd Edition. * * The above statement is only true if using the 2 leading * digits of the numerator and the leading digit of the * denominator. Since we are using the (3) leading digits * of the numerator and the (2) leading digits of the * denominator, we eliminate the case where our q-hat is * 2 too large, (and q-hat being 1 too large is quite remote). */ if (m_apm_compare(M_div_tmp7, M_div_worka) == 1) { j--; m_apm_subtract(M_div_tmp8, M_div_tmp7, M_div_workb); m_apm_copy(M_div_tmp7, M_div_tmp8); } /* * Since we know q-hat is correct, step D6 is unnecessary. * * Store q-hat, step D5. Since D6 is unnecessary, we can * do D5 before D4 and decide if we are done. */ r->m_apm_data[indexr++] = (UCHAR)j; /* j == 'qhat' */ m += 2; if (m >= iterations) break; /* step D4 */ m_apm_subtract(M_div_tmp9, M_div_worka, M_div_tmp7); /* * if the subtraction yields zero, the division is exact * and we are done early. */ if (M_div_tmp9->m_apm_sign == 0) { iterations = m; break; } /* multiply by 100 and re-save */ M_div_tmp9->m_apm_exponent += 2; m_apm_copy(M_div_worka, M_div_tmp9); } } r->m_apm_sign = sign; r->m_apm_exponent = nexp; r->m_apm_datalength = iterations; M_apm_normalize(r); }