/* * From Knuth, The Art of Computer Programming: * * To compute GCD(u,v) * * A1: * if (v == 0) return (u) * A2: * t = u mod v * u = v * v = t * goto A1 */ void m_apm_gcd_traditional(M_APM r, M_APM u, M_APM v) { M_APM tmpD, tmpN, tmpU, tmpV; tmpD = M_get_stack_var(); tmpN = 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); while (TRUE) { if (tmpV->m_apm_sign == 0) break; m_apm_integer_div_rem(tmpD, tmpN, tmpU, tmpV); m_apm_copy(tmpU, tmpV); m_apm_copy(tmpV, tmpN); } m_apm_copy(r, tmpU); M_restore_stack(4); }
/* 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 */ }
/* 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 */ }
void M_apm_round_fixpt(M_APM btmp, int places, M_APM atmp) { int xp, ii; xp = atmp->m_apm_exponent; ii = xp + places - 1; M_set_to_zero(btmp); /* assume number is too small so the net result is 0 */ if (ii >= 0) { m_apm_round(btmp, ii, atmp); } else { if (ii == -1) /* next digit is significant which may round up */ { if (atmp->m_apm_data[0] >= 50) /* digit >= 5, round up */ { m_apm_copy(btmp, atmp); btmp->m_apm_data[0] = 10; btmp->m_apm_exponent += 1; btmp->m_apm_datalength = 1; M_apm_normalize(btmp); } } } }
MAPM XQNumericLiteral::getValue() const { // Use the C API to copy our fake MAPM MAPM copy; m_apm_copy(const_cast<M_APM>(copy.c_struct()), const_cast<M_APM>(&value_)); return copy; }
/* * compute X = (a * X + c) MOD m where c = a */ void m_apm_get_random(M_APM mrnd) { if (M_firsttime2) /* use the system time as the initial seed value */ { M_firsttime2 = FALSE; M_rnd_aa = m_apm_init(); M_rnd_XX = m_apm_init(); M_rnd_mm = m_apm_init(); M_rtmp0 = m_apm_init(); M_rtmp1 = m_apm_init(); /* set the multiplier M_rnd_aa and M_rnd_mm */ m_apm_set_string(M_rnd_aa, "716805947629621"); m_apm_set_string(M_rnd_mm, "1.0E15"); M_get_rnd_seed(M_rnd_XX); } m_apm_multiply(M_rtmp0, M_rnd_XX, M_rnd_aa); m_apm_add(M_rtmp1, M_rtmp0, M_rnd_aa); m_apm_integer_div_rem(M_rtmp0, M_rnd_XX, M_rtmp1, M_rnd_mm); m_apm_copy(mrnd, M_rnd_XX); mrnd->m_apm_exponent -= 15; }
/* 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 */ }
void M_limit_angle_to_pi(M_APM rr, int places, M_APM aa) { M_APM tmp7, tmp8, tmp9; M_check_PI_places(places); tmp9 = M_get_stack_var(); m_apm_copy(tmp9, MM_lc_PI); if (m_apm_compare(aa, tmp9) == 1) /* > PI */ { tmp7 = M_get_stack_var(); tmp8 = M_get_stack_var(); m_apm_add(tmp7, aa, tmp9); m_apm_integer_divide(tmp9, tmp7, MM_lc_2_PI); m_apm_multiply(tmp8, tmp9, MM_lc_2_PI); m_apm_subtract(tmp9, aa, tmp8); m_apm_round(rr, places, tmp9); M_restore_stack(3); return; } tmp9->m_apm_sign = -1; if (m_apm_compare(aa, tmp9) == -1) /* < -PI */ { tmp7 = M_get_stack_var(); tmp8 = M_get_stack_var(); m_apm_add(tmp7, aa, tmp9); m_apm_integer_divide(tmp9, tmp7, MM_lc_2_PI); m_apm_multiply(tmp8, tmp9, MM_lc_2_PI); m_apm_subtract(tmp9, aa, tmp8); m_apm_round(rr, places, tmp9); M_restore_stack(3); return; } m_apm_copy(rr, aa); M_restore_stack(1); }
MAPM::MAPM(M_APM m, bool copy) { if(copy) { create(); m_apm_copy(myVal, m); } else { myVal=(M_APM)m; ref(myVal); } }
M_APM MAPM::val(void) { if (myVal->m_apm_refcount==1) /* Return my private myVal */ return myVal; /* Otherwise, our copy of myVal is shared-- we need to make a new private copy. */ M_APM oldVal=myVal; myVal=makeNew(); m_apm_copy(myVal,oldVal); unref(oldVal); return myVal; }
/* * return the nearest integer >= input */ void m_apm_ceil(M_APM bb, M_APM aa) { M_APM mtmp; m_apm_copy(bb, aa); if (m_apm_is_integer(bb)) /* if integer, we're done */ return; if (bb->m_apm_exponent <= 0) /* if |bb| < 1, result is 0 or 1 */ { if (bb->m_apm_sign < 0) M_set_to_zero(bb); else m_apm_copy(bb, MM_One); return; } if (bb->m_apm_sign < 0) { bb->m_apm_datalength = bb->m_apm_exponent; M_apm_normalize(bb); } else { mtmp = M_get_stack_var(); m_apm_copy(mtmp, bb); mtmp->m_apm_datalength = mtmp->m_apm_exponent; M_apm_normalize(mtmp); m_apm_add(bb, mtmp, MM_One); M_restore_stack(1); } }
/* * check if our local copy of log(2) & log(10) is precise * enough for our purpose. if not, calculate them so it's * as precise as desired, accurate to at least 'places'. */ void M_check_log_places(int places) { M_APM tmp6, tmp7, tmp8, tmp9; int dplaces; dplaces = places + 4; if (dplaces > MM_lc_log_digits) { MM_lc_log_digits = dplaces + 4; tmp6 = M_get_stack_var(); tmp7 = M_get_stack_var(); tmp8 = M_get_stack_var(); tmp9 = M_get_stack_var(); dplaces += 6 + (int)log10((double)places); m_apm_copy(tmp7, MM_One); tmp7->m_apm_exponent = -places; M_log_AGM_R_func(tmp8, dplaces, MM_One, tmp7); m_apm_multiply(tmp6, tmp7, MM_0_5); M_log_AGM_R_func(tmp9, dplaces, MM_One, tmp6); m_apm_subtract(MM_lc_log2, tmp9, tmp8); /* log(2) */ tmp7->m_apm_exponent -= 1; /* divide by 10 */ M_log_AGM_R_func(tmp9, dplaces, MM_One, tmp7); m_apm_subtract(MM_lc_log10, tmp9, tmp8); /* log(10) */ m_apm_reciprocal(MM_lc_log10R, dplaces, MM_lc_log10); /* 1 / log(10) */ M_restore_stack(4); } }
/* * 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_sqrt(M_APM rr, int places, M_APM aa) { M_APM last_x, guess, tmpN, tmp7, tmp8, tmp9; int ii, bflag, nexp, tolerance, dplaces; if (aa->m_apm_sign <= 0) { if (aa->m_apm_sign == -1) { M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_sqrt\', Negative argument"); } M_set_to_zero(rr); return; } last_x = M_get_stack_var(); guess = M_get_stack_var(); tmpN = M_get_stack_var(); tmp7 = M_get_stack_var(); tmp8 = M_get_stack_var(); tmp9 = M_get_stack_var(); m_apm_copy(tmpN, aa); /* normalize the input number (make the exponent near 0) so the 'guess' function will not over/under flow on large magnitude exponents. */ nexp = aa->m_apm_exponent / 2; tmpN->m_apm_exponent -= 2 * nexp; M_get_sqrt_guess(guess, tmpN); /* actually gets 1/sqrt guess */ tolerance = places + 4; dplaces = places + 16; bflag = FALSE; m_apm_negate(last_x, MM_Ten); /* Use the following iteration to calculate 1 / sqrt(N) : X = 0.5 * X * [ 3 - N * X^2 ] n+1 */ ii = 0; while (TRUE) { m_apm_multiply(tmp9, tmpN, guess); m_apm_multiply(tmp8, tmp9, guess); m_apm_round(tmp7, dplaces, tmp8); m_apm_subtract(tmp9, MM_Three, tmp7); m_apm_multiply(tmp8, tmp9, guess); m_apm_multiply(tmp9, tmp8, MM_0_5); if (bflag) break; m_apm_round(guess, dplaces, tmp9); /* force at least 2 iterations so 'last_x' has valid data */ if (ii != 0) { m_apm_subtract(tmp7, guess, last_x); if (tmp7->m_apm_sign == 0) break; /* * if we are within a factor of 4 on the error term, * we will be accurate enough after the *next* iteration * is complete. (note that the sign of the exponent on * the error term will be a negative number). */ if ((-4 * tmp7->m_apm_exponent) > tolerance) bflag = TRUE; } m_apm_copy(last_x, guess); ii++; } /* * multiply by the starting number to get the final * sqrt and then adjust the exponent since we found * the sqrt of the normalized number. */ m_apm_multiply(tmp8, tmp9, tmpN); m_apm_round(rr, places, tmp8); rr->m_apm_exponent += nexp; M_restore_stack(6); }
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 */ }
void m_apm_exp(M_APM r, int places, M_APM x) { M_APM tmp7, tmp8, tmp9; int dplaces, nn, ii; if (MM_firsttime1) { MM_firsttime1 = FALSE; MM_exp_log2R = m_apm_init(); MM_exp_512R = m_apm_init(); m_apm_set_string(MM_exp_log2R, "1.44269504089"); /* ~ 1 / log(2) */ m_apm_set_string(MM_exp_512R, "1.953125E-3"); /* 1 / 512 */ } tmp7 = M_get_stack_var(); tmp8 = M_get_stack_var(); tmp9 = M_get_stack_var(); if (x->m_apm_sign == 0) /* if input == 0, return '1' */ { m_apm_copy(r, MM_One); M_restore_stack(3); return; } if (x->m_apm_exponent <= -3) /* already small enough so call _raw directly */ { M_raw_exp(tmp9, (places + 6), x); m_apm_round(r, places, tmp9); M_restore_stack(3); return; } /* From David H. Bailey's MPFUN Fortran package : exp (t) = (1 + r + r^2 / 2! + r^3 / 3! + r^4 / 4! ...) ^ q * 2 ^ n where q = 256, r = t' / q, t' = t - n Log(2) and where n is chosen so that -0.5 Log(2) < t' <= 0.5 Log(2). Reducing t mod Log(2) and dividing by 256 insures that -0.001 < r <= 0.001, which accelerates convergence in the above series. I use q = 512 and also limit how small 'r' can become. The 'r' used here is limited in magnitude from 1.95E-4 < |r| < 1.35E-3. Forcing 'r' into a narrow range keeps the algorithm 'well behaved'. ( the range is [0.1 / 512] to [log(2) / 512] ) */ if (M_exp_compute_nn(&nn, tmp7, x) != 0) { M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_exp\', Input too large, Overflow"); M_set_to_zero(r); M_restore_stack(3); return; } dplaces = places + 8; /* check to make sure our log(2) is accurate enough */ M_check_log_places(dplaces); m_apm_multiply(tmp8, tmp7, MM_lc_log2); m_apm_subtract(tmp7, x, tmp8); /* * guarantee that |tmp7| is between 0.1 and 0.9999999.... * (in practice, the upper limit only reaches log(2), 0.693... ) */ while (TRUE) { if (tmp7->m_apm_sign != 0) { if (tmp7->m_apm_exponent == 0) break; } if (tmp7->m_apm_sign >= 0) { nn++; m_apm_subtract(tmp8, tmp7, MM_lc_log2); m_apm_copy(tmp7, tmp8); } else { nn--; m_apm_add(tmp8, tmp7, MM_lc_log2); m_apm_copy(tmp7, tmp8); } } m_apm_multiply(tmp9, tmp7, MM_exp_512R); /* perform the series expansion ... */ M_raw_exp(tmp8, dplaces, tmp9); /* * raise result to the 512 power * * note : x ^ 512 = (((x ^ 2) ^ 2) ^ 2) ... 9 times */ ii = 9; while (TRUE) { m_apm_multiply(tmp9, tmp8, tmp8); m_apm_round(tmp8, dplaces, tmp9); if (--ii == 0) break; } /* now compute 2 ^ N */ m_apm_integer_pow(tmp7, dplaces, MM_Two, nn); m_apm_multiply(tmp9, tmp7, tmp8); m_apm_round(r, places, tmp9); M_restore_stack(3); /* restore the 3 locals we used here */ }
void m_apm_integer_pow(M_APM rr, int places, M_APM aa, int mexp) { M_APM tmp0, tmpy, tmpz; int nexp, ii, signflag, local_precision; if (mexp == 0) { m_apm_copy(rr, MM_One); return; } else { if (mexp > 0) { signflag = 0; nexp = mexp; } else { signflag = 1; nexp = -mexp; } } if (aa->m_apm_sign == 0) { M_set_to_zero(rr); return; } tmp0 = M_get_stack_var(); tmpy = M_get_stack_var(); tmpz = M_get_stack_var(); local_precision = places + 8; m_apm_copy(tmpy, MM_One); m_apm_copy(tmpz, aa); while (TRUE) { ii = nexp & 1; nexp = nexp >> 1; if (ii != 0) /* exponent -was- odd */ { m_apm_multiply(tmp0, tmpy, tmpz); m_apm_round(tmpy, local_precision, tmp0); if (nexp == 0) break; } m_apm_multiply(tmp0, tmpz, tmpz); m_apm_round(tmpz, local_precision, tmp0); } if (signflag) { m_apm_reciprocal(rr, places, tmpy); } else { m_apm_round(rr, places, tmpy); } M_restore_stack(3); }
void m_apm_root4(M_APM x, int dec_places, M_APM y) { m_apm_sqrt(x, dec_places, y); m_apm_copy(y, x); m_apm_sqrt(x, dec_places, y); }
void m_apm_arccos(M_APM r, int places, M_APM x) { M_APM tmp0, tmp1, tmp2, tmp3, current_x; int ii, maxiter, maxp, tolerance, local_precision; current_x = M_get_stack_var(); tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); tmp3 = M_get_stack_var(); m_apm_absolute_value(tmp0, x); ii = m_apm_compare(tmp0, MM_One); if (ii == 1) /* |x| > 1 */ { M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_arccos\', |Argument| > 1"); M_set_to_zero(r); M_restore_stack(5); return; } if (ii == 0) /* |x| == 1, arccos = 0, PI */ { if (x->m_apm_sign == 1) { M_set_to_zero(r); } else { M_check_PI_places(places); m_apm_round(r, places, MM_lc_PI); } M_restore_stack(5); return; } if (m_apm_compare(tmp0, MM_0_85) == 1) /* check if > 0.85 */ { M_cos_to_sin(tmp2, (places + 4), x); if (x->m_apm_sign == 1) { m_apm_arcsin(r, places, tmp2); } else { M_check_PI_places(places); m_apm_arcsin(tmp3, (places + 4), tmp2); m_apm_subtract(tmp1, MM_lc_PI, tmp3); m_apm_round(r, places, tmp1); } M_restore_stack(5); return; } if (x->m_apm_sign == 0) /* input == 0 ?? */ { M_check_PI_places(places); m_apm_round(r, places, MM_lc_HALF_PI); M_restore_stack(5); return; } if (x->m_apm_exponent <= -4) /* input close to 0 ?? */ { M_arccos_near_0(r, places, x); M_restore_stack(5); return; } tolerance = -(places + 4); maxp = places + 8; local_precision = 18; /* * compute the maximum number of iterations * that should be needed to calculate to * the desired accuracy. [ constant below ~= 1 / log(2) ] */ maxiter = (int)(log((double)(places + 2)) * 1.442695) + 3; if (maxiter < 5) maxiter = 5; M_get_acos_guess(current_x, x); /* Use the following iteration to solve for arc-cos : cos(X) - N X = X + ------------ n+1 sin(X) */ ii = 0; while (TRUE) { M_4x_cos(tmp1, local_precision, current_x); M_cos_to_sin(tmp2, local_precision, tmp1); if (tmp2->m_apm_sign != 0) tmp2->m_apm_sign = current_x->m_apm_sign; m_apm_subtract(tmp3, tmp1, x); m_apm_divide(tmp0, local_precision, tmp3, tmp2); m_apm_add(tmp2, current_x, tmp0); m_apm_copy(current_x, tmp2); if (ii != 0) { if (((2 * tmp0->m_apm_exponent) < tolerance) || (tmp0->m_apm_sign == 0)) break; } if (++ii == maxiter) { M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_arccos\', max iteration count reached"); break; } local_precision *= 2; if (local_precision > maxp) local_precision = maxp; } m_apm_round(r, places, current_x); M_restore_stack(5); }
void m_apm_integer_pow_nr(M_APM rr, M_APM aa, int mexp) { M_APM tmp0, tmpy, tmpz; int nexp, ii; if (mexp == 0) { m_apm_copy(rr, MM_One); return; } else { if (mexp < 0) { M_apm_log_error_msg(M_APM_RETURN, "Warning! ... \'m_apm_integer_pow_nr\', Negative exponent"); M_set_to_zero(rr); return; } } if (mexp == 1) { m_apm_copy(rr, aa); return; } if (mexp == 2) { m_apm_multiply(rr, aa, aa); return; } nexp = mexp; if (aa->m_apm_sign == 0) { M_set_to_zero(rr); return; } tmp0 = M_get_stack_var(); tmpy = M_get_stack_var(); tmpz = M_get_stack_var(); m_apm_copy(tmpy, MM_One); m_apm_copy(tmpz, aa); while (TRUE) { ii = nexp & 1; nexp = nexp >> 1; if (ii != 0) /* exponent -was- odd */ { m_apm_multiply(tmp0, tmpy, tmpz); if (nexp == 0) break; m_apm_copy(tmpy, tmp0); } m_apm_multiply(tmp0, tmpz, tmpz); m_apm_copy(tmpz, tmp0); } m_apm_copy(rr, tmp0); M_restore_stack(3); }
void m_apm_pow4(M_APM x, M_APM y) { m_apm_square(x, y); m_apm_copy(y, x); m_apm_square(x, y); }
void m_apm_to_fixpt_string(char *ss, int dplaces, M_APM mtmp) { M_APM ctmp; void *vp; int places, i2, ii, jj, kk, xp, dl, numb; UCHAR *ucp, numdiv, numrem; char *cpw, *cpd, sbuf[128]; ctmp = M_get_stack_var(); vp = NULL; cpd = ss; places = dplaces; /* just want integer portion if places == 0 */ if (places == 0) { if (mtmp->m_apm_sign >= 0) m_apm_add(ctmp, mtmp, MM_0_5); else m_apm_subtract(ctmp, mtmp, MM_0_5); m_apm_to_integer_string(cpd, ctmp); M_restore_stack(1); return; } if (places > 0) M_apm_round_fixpt(ctmp, places, mtmp); else m_apm_copy(ctmp, mtmp); /* show ALL digits */ if (ctmp->m_apm_sign == 0) /* result is 0 */ { if (places < 0) { cpd[0] = '0'; /* "0.0" */ cpd[1] = '.'; cpd[2] = '0'; cpd[3] = '\0'; } else { memset(cpd, '0', (places + 2)); /* pre-load string with all '0' */ cpd[1] = '.'; cpd[places + 2] = '\0'; } M_restore_stack(1); return; } xp = ctmp->m_apm_exponent; dl = ctmp->m_apm_datalength; numb = (dl + 1) >> 1; if (places < 0) { if (dl > xp) jj = dl + 16; else jj = xp + 16; } else { jj = places + 16; if (xp > 0) jj += xp; } if (jj > 112) { if ((vp = (void *)MAPM_MALLOC((jj + 16) * sizeof(char))) == NULL) { /* fatal, this does not return */ M_apm_log_error_msg(M_APM_FATAL, "\'m_apm_to_fixpt_string\', Out of memory"); } cpw = (char *)vp; } else { cpw = sbuf; } /* * at this point, the number is non-zero and the the output * string will contain at least 1 significant digit. */ if (ctmp->m_apm_sign == -1) /* negative number */ { *cpd++ = '-'; } ucp = ctmp->m_apm_data; ii = 0; /* convert MAPM num to ASCII digits and store in working char array */ while (TRUE) { M_get_div_rem_10((int)(*ucp++), &numdiv, &numrem); cpw[ii++] = numdiv + '0'; cpw[ii++] = numrem + '0'; if (--numb == 0) break; } i2 = ii; /* save for later */ if (places < 0) /* show ALL digits */ { places = dl - xp; if (places < 1) places = 1; } /* pad with trailing zeros if needed */ kk = xp + places + 2 - ii; if (kk > 0) memset(&cpw[ii], '0', kk); if (xp > 0) /* |num| >= 1, NO lead-in "0.nnn" */ { ii = xp + places + 1; jj = 0; for (kk=0; kk < ii; kk++) { if (kk == xp) cpd[jj++] = '.'; cpd[jj++] = cpw[kk]; } cpd[ii] = '\0'; } else /* |num| < 1, have lead-in "0.nnn" */ { jj = 2 - xp; ii = 2 + places; memset(cpd, '0', (ii + 1)); /* pre-load string with all '0' */ cpd[1] = '.'; /* assign decimal point */ for (kk=0; kk < i2; kk++) { cpd[jj++] = cpw[kk]; } cpd[ii] = '\0'; } if (vp != NULL) MAPM_FREE(vp); M_restore_stack(1); }
/* * Calculate PI using the AGM (Arithmetic-Geometric Mean) * * Init : A0 = 1 * B0 = 1 / sqrt(2) * Sum = 1 * * Iterate: n = 1... * * * A = 0.5 * [ A + B ] * n n-1 n-1 * * * B = sqrt [ A * B ] * n n-1 n-1 * * * * C = 0.5 * [ A - B ] * n n-1 n-1 * * * 2 n+1 * Sum = Sum - C * 2 * n * * * At the end when C is 'small enough' : * n * * 2 * PI = 4 * A / Sum * n+1 * * -OR- * * 2 * PI = ( A + B ) / Sum * n n * */ void M_calculate_PI_AGM(M_APM outv, int places) { M_APM tmp1, tmp2, a0, b0, c0, a1, b1, sum, pow_2; int dplaces, nn; tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); a0 = M_get_stack_var(); b0 = M_get_stack_var(); c0 = M_get_stack_var(); a1 = M_get_stack_var(); b1 = M_get_stack_var(); sum = M_get_stack_var(); pow_2 = M_get_stack_var(); dplaces = places + 16; m_apm_copy(a0, MM_One); m_apm_copy(sum, MM_One); m_apm_copy(pow_2, MM_Four); m_apm_sqrt(b0, dplaces, MM_0_5); /* sqrt(0.5) */ while (TRUE) { m_apm_add(tmp1, a0, b0); m_apm_multiply(a1, MM_0_5, tmp1); m_apm_multiply(tmp1, a0, b0); m_apm_sqrt(b1, dplaces, tmp1); m_apm_subtract(tmp1, a0, b0); m_apm_multiply(c0, MM_0_5, tmp1); /* * the net 'PI' calculated from this iteration will * be accurate to ~4 X the value of (c0)'s exponent. * this was determined experimentally. */ nn = -4 * c0->m_apm_exponent; m_apm_multiply(tmp1, c0, c0); m_apm_multiply(tmp2, tmp1, pow_2); m_apm_subtract(tmp1, sum, tmp2); m_apm_round(sum, dplaces, tmp1); if (nn >= dplaces) break; m_apm_copy(a0, a1); m_apm_copy(b0, b1); m_apm_multiply(tmp1, pow_2, MM_Two); m_apm_copy(pow_2, tmp1); } m_apm_add(tmp1, a1, b1); m_apm_multiply(tmp2, tmp1, tmp1); m_apm_divide(tmp1, dplaces, tmp2, sum); m_apm_round(outv, places, tmp1); M_restore_stack(9); }
void m_apm_reciprocal(M_APM rr, int places, M_APM aa) { M_APM last_x, guess, tmpN, tmp1, tmp2; int ii, bflag, dplaces, nexp, tolerance; if (aa->m_apm_sign == 0) { M_apm_log_error_msg(M_APM_RETURN, "Warning! ... \'m_apm_reciprocal\', Input = 0"); M_set_to_zero(rr); return; } last_x = M_get_stack_var(); guess = M_get_stack_var(); tmpN = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); m_apm_absolute_value(tmpN, aa); /* normalize the input number (make the exponent 0) so the 'guess' below will not over/under flow on large magnitude exponents. */ nexp = aa->m_apm_exponent; tmpN->m_apm_exponent -= nexp; m_apm_set_double(guess, (1.0 / m_apm_get_double(tmpN))); tolerance = places + 4; dplaces = places + 16; bflag = FALSE; m_apm_negate(last_x, MM_Ten); /* Use the following iteration to calculate the reciprocal : X = X * [ 2 - N * X ] n+1 */ ii = 0; while (TRUE) { m_apm_multiply(tmp1, tmpN, guess); m_apm_subtract(tmp2, MM_Two, tmp1); m_apm_multiply(tmp1, tmp2, guess); if (bflag) break; m_apm_round(guess, dplaces, tmp1); /* force at least 2 iterations so 'last_x' has valid data */ if (ii != 0) { m_apm_subtract(tmp2, guess, last_x); if (tmp2->m_apm_sign == 0) break; /* * if we are within a factor of 4 on the error term, * we will be accurate enough after the *next* iteration * is complete. */ if ((-4 * tmp2->m_apm_exponent) > tolerance) bflag = TRUE; } m_apm_copy(last_x, guess); ii++; } m_apm_round(rr, places, tmp1); rr->m_apm_exponent -= nexp; rr->m_apm_sign = aa->m_apm_sign; M_restore_stack(5); }
int main(int argc, char *argv[]) { char version_info[80]; int ct; /* declare the M_APM variables ... */ M_APM aa_mapm; M_APM bb_mapm; M_APM cc_mapm; M_APM dd_mapm; if (argc < 2) { m_apm_lib_short_version(version_info); fprintf(stdout, "Usage: primenum number\t\t\t[Version 1.3, MAPM Version %s]\n", version_info); fprintf(stdout, " find the first 10 prime numbers starting with \'number\'\n"); exit(4); } /* now initialize the M_APM variables ... */ aa_mapm = m_apm_init(); bb_mapm = m_apm_init(); cc_mapm = m_apm_init(); dd_mapm = m_apm_init(); init_working_mapm(); m_apm_set_string(dd_mapm, argv[1]); /* * if input < 3, set start point = 3 */ if (m_apm_compare(dd_mapm, MM_Three) == -1) { m_apm_copy(dd_mapm, MM_Three); } /* * make sure we start with an odd integer */ m_apm_integer_divide(aa_mapm, dd_mapm, MM_Two); m_apm_multiply(bb_mapm, MM_Two, aa_mapm); m_apm_add(aa_mapm, MM_One, bb_mapm); ct = 0; while (TRUE) { if (is_number_prime(aa_mapm)) { m_apm_to_integer_string(buffer, aa_mapm); fprintf(stdout,"%s\n",buffer); if (++ct == 10) break; } m_apm_add(cc_mapm, MM_Two, aa_mapm); m_apm_copy(aa_mapm, cc_mapm); } free_working_mapm(); m_apm_free(aa_mapm); m_apm_free(bb_mapm); m_apm_free(cc_mapm); m_apm_free(dd_mapm); m_apm_free_all_mem(); exit(0); }
void m_apm_add(M_APM r, M_APM a, M_APM b) { int j, carry, sign, aexp, bexp, adigits, bdigits; if (M_add_firsttime) { M_add_firsttime = FALSE; M_work1 = m_apm_init(); M_work2 = m_apm_init(); } if (a->m_apm_sign == 0) { m_apm_copy(r,b); return; } if (b->m_apm_sign == 0) { m_apm_copy(r,a); return; } if (a->m_apm_sign == 1 && b->m_apm_sign == -1) { b->m_apm_sign = 1; m_apm_subtract(r,a,b); b->m_apm_sign = -1; return; } if (a->m_apm_sign == -1 && b->m_apm_sign == 1) { a->m_apm_sign = 1; m_apm_subtract(r,b,a); a->m_apm_sign = -1; return; } sign = a->m_apm_sign; /* signs are the same, result will be same */ aexp = a->m_apm_exponent; bexp = b->m_apm_exponent; m_apm_copy(M_work1, a); m_apm_copy(M_work2, b); /* * scale by at least 1 factor of 10 in case the MSB carrys */ if (aexp == bexp) { M_apm_scale(M_work1, 2); /* shift 2 digits == 1 byte for efficiency */ M_apm_scale(M_work2, 2); } else { if (aexp > bexp) { M_apm_scale(M_work1, 2); M_apm_scale(M_work2, (aexp + 2 - bexp)); } else /* aexp < bexp */ { M_apm_scale(M_work2, 2); M_apm_scale(M_work1, (bexp + 2 - aexp)); } } adigits = M_work1->m_apm_datalength; bdigits = M_work2->m_apm_datalength; if (adigits >= bdigits) { m_apm_copy(r, M_work1); j = (bdigits + 1) >> 1; carry = 0; while (TRUE) { j--; r->m_apm_data[j] += carry + M_work2->m_apm_data[j]; if (r->m_apm_data[j] >= 100) { r->m_apm_data[j] -= 100; carry = 1; } else carry = 0; if (j == 0) break; } }
void M_fast_multiply(M_APM rr, M_APM aa, M_APM bb) { void *vp; int ii, k, nexp, sign; if (M_firsttimef) { M_firsttimef = FALSE; for (k=0; k < M_STACK_SIZE; k++) mul_stack_data_size[k] = 0; size_flag = M_get_sizeof_int(); bit_limit = 8 * size_flag + 1; M_ain = m_apm_init(); M_bin = m_apm_init(); } exp_stack_ptr = -1; M_mul_stack_ptr = -1; m_apm_copy(M_ain, aa); m_apm_copy(M_bin, bb); sign = M_ain->m_apm_sign * M_bin->m_apm_sign; nexp = M_ain->m_apm_exponent + M_bin->m_apm_exponent; if (M_ain->m_apm_datalength >= M_bin->m_apm_datalength) ii = M_ain->m_apm_datalength; else ii = M_bin->m_apm_datalength; ii = (ii + 1) >> 1; ii = M_next_power_of_2(ii); /* Note: 'ii' must be >= 4 here. this is guaranteed by the caller: m_apm_multiply */ k = 2 * ii; /* required size of result, in bytes */ M_apm_pad(M_ain, k); /* fill out the data so the number of */ M_apm_pad(M_bin, k); /* bytes is an exact power of 2 */ if (k > rr->m_apm_malloclength) { if ((vp = MAPM_REALLOC(rr->m_apm_data, (k + 32))) == NULL) { /* fatal, this does not return */ M_apm_log_error_msg(M_APM_FATAL, "\'M_fast_multiply\', Out of memory"); } rr->m_apm_malloclength = k + 28; rr->m_apm_data = (UCHAR *)vp; } #ifdef NO_FFT_MULTIPLY M_fmul_div_conq(rr->m_apm_data, M_ain->m_apm_data, M_bin->m_apm_data, ii); #else /* * if the numbers are *really* big, use the divide-and-conquer * routine first until the numbers are small enough to be handled * by the FFT algorithm. If the numbers are already small enough, * call the FFT multiplication now. * * Note that 'ii' here is (and must be) an exact power of 2. */ if (size_flag == 2) /* if still using 16 bit compilers .... */ { M_fast_mul_fft(rr->m_apm_data, M_ain->m_apm_data, M_bin->m_apm_data, ii); } else /* >= 32 bit compilers */ { if (ii > (MAX_FFT_BYTES + 2)) { M_fmul_div_conq(rr->m_apm_data, M_ain->m_apm_data, M_bin->m_apm_data, ii); } else { M_fast_mul_fft(rr->m_apm_data, M_ain->m_apm_data, M_bin->m_apm_data, ii); } } #endif rr->m_apm_sign = sign; rr->m_apm_exponent = nexp; rr->m_apm_datalength = 4 * ii; M_apm_normalize(rr); }
void M_log_AGM_R_func(M_APM rr, int places, M_APM aa, M_APM bb) { M_APM tmp1, tmp2, tmp3, tmp4, tmpC2, sum, pow_2, tmpA0, tmpB0; int tolerance, dplaces; tmpA0 = M_get_stack_var(); tmpB0 = M_get_stack_var(); tmpC2 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); tmp3 = M_get_stack_var(); tmp4 = M_get_stack_var(); sum = M_get_stack_var(); pow_2 = M_get_stack_var(); tolerance = places + 8; dplaces = places + 16; m_apm_copy(tmpA0, aa); m_apm_copy(tmpB0, bb); m_apm_copy(pow_2, MM_0_5); m_apm_multiply(tmp1, aa, aa); /* 0.5 * [ a ^ 2 - b ^ 2 ] */ m_apm_multiply(tmp2, bb, bb); m_apm_subtract(tmp3, tmp1, tmp2); m_apm_multiply(sum, MM_0_5, tmp3); while (TRUE) { m_apm_subtract(tmp1, tmpA0, tmpB0); /* C n+1 = 0.5 * [ An - Bn ] */ m_apm_multiply(tmp4, MM_0_5, tmp1); /* C n+1 */ m_apm_multiply(tmpC2, tmp4, tmp4); /* C n+1 ^ 2 */ /* do the AGM */ m_apm_add(tmp1, tmpA0, tmpB0); m_apm_multiply(tmp3, MM_0_5, tmp1); m_apm_multiply(tmp2, tmpA0, tmpB0); m_apm_sqrt(tmpB0, dplaces, tmp2); m_apm_round(tmpA0, dplaces, tmp3); /* end AGM */ m_apm_multiply(tmp2, MM_Two, pow_2); m_apm_copy(pow_2, tmp2); m_apm_multiply(tmp1, tmpC2, pow_2); m_apm_add(tmp3, sum, tmp1); if ((tmp1->m_apm_sign == 0) || ((-2 * tmp1->m_apm_exponent) > tolerance)) break; m_apm_round(sum, dplaces, tmp3); } m_apm_subtract(tmp4, MM_One, tmp3); m_apm_reciprocal(rr, places, tmp4); M_restore_stack(9); }
void m_apm_factorial(M_APM moutput, M_APM minput) { int ii, nmul, ndigits, nd, jj, kk, mm, ct; M_APM array[NDIM]; M_APM iprod1, iprod2, tmp1, tmp2; /* return 1 for any input <= 1 */ if (m_apm_compare(minput, MM_One) <= 0) { m_apm_copy(moutput, MM_One); return; } ct = 0; mm = NDIM - 2; ndigits = 256; nd = ndigits - 20; tmp1 = m_apm_init(); tmp2 = m_apm_init(); iprod1 = m_apm_init(); iprod2 = m_apm_init(); array[0] = m_apm_init(); m_apm_copy(tmp2, minput); /* loop until multiply count-down has reached '2' */ while (TRUE) { m_apm_copy(iprod1, MM_One); /* * loop until the number of significant digits in this * partial result is slightly less than 256 */ while (TRUE) { m_apm_multiply(iprod2, iprod1, tmp2); m_apm_subtract(tmp1, tmp2, MM_One); m_apm_multiply(iprod1, iprod2, tmp1); /* * I know, I know. There just isn't a *clean* way * to break out of 2 nested loops. */ if (m_apm_compare(tmp1, MM_Two) <= 0) goto PHASE2; m_apm_subtract(tmp2, tmp1, MM_One); if (iprod1->m_apm_datalength > nd) break; } if (ct == (NDIM - 1)) { /* * if the array has filled up, start multiplying * some of the partial products now. */ m_apm_copy(tmp1, array[mm]); m_apm_multiply(array[mm], iprod1, tmp1); if (mm == 0) { mm = NDIM - 2; ndigits = ndigits << 1; nd = ndigits - 20; } else mm--; } else { /* * store this partial product in the array * and allocate the next array element */ m_apm_copy(array[ct], iprod1); array[++ct] = m_apm_init(); } } PHASE2: m_apm_copy(array[ct], iprod1); kk = ct; while (kk != 0) { ii = 0; jj = 0; nmul = (kk + 1) >> 1; while (TRUE) { /* must use tmp var when ii,jj point to same element */ if (ii == 0) { m_apm_copy(tmp1, array[ii]); m_apm_multiply(array[jj], tmp1, array[ii+1]); } else m_apm_multiply(array[jj], array[ii], array[ii+1]); if (++jj == nmul) break; ii += 2; } if ((kk & 1) == 0) { jj = kk >> 1; m_apm_copy(array[jj], array[kk]); } kk = kk >> 1; }
void m_apm_copy_mt(M_APM dest, M_APM src) { m_apm_enter(); m_apm_copy(dest,src); m_apm_leave(); }