/* Calculate the POW function by calling EXP : Y A X = e where A = Y * log(X) */ void m_apm_pow(M_APM rr, int places, M_APM xx, M_APM yy) { int iflag, pflag; char sbuf[64]; M_APM tmp8, tmp9; /* if yy == 0, return 1 */ if (yy->m_apm_sign == 0) { m_apm_copy(rr, MM_One); return; } /* if xx == 0, return 0 */ if (xx->m_apm_sign == 0) { M_set_to_zero(rr); return; } if (M_size_flag == 0) /* init locals on first call */ { M_size_flag = M_get_sizeof_int(); M_last_log_digits = 0; M_last_xx_input = m_apm_init(); M_last_xx_log = m_apm_init(); } /* * if 'yy' is a small enough integer, call the more * efficient _integer_pow function. */ if (m_apm_is_integer(yy)) { iflag = FALSE; if (M_size_flag == 2) /* 16 bit compilers */ { if (yy->m_apm_exponent <= 4) iflag = TRUE; } else /* >= 32 bit compilers */ { if (yy->m_apm_exponent <= 7) iflag = TRUE; } if (iflag) { m_apm_to_integer_string(sbuf, yy); m_apm_integer_pow(rr, places, xx, atoi(sbuf)); return; } } tmp8 = M_get_stack_var(); tmp9 = M_get_stack_var(); /* * If parameter 'X' is the same this call as it * was the previous call, re-use the saved log * calculation from last time. */ pflag = FALSE; if (M_last_log_digits >= places) { if (m_apm_compare(xx, M_last_xx_input) == 0) pflag = TRUE; } if (pflag) { m_apm_round(tmp9, (places + 8), M_last_xx_log); } else { m_apm_log(tmp9, (places + 8), xx); M_last_log_digits = places + 2; /* save the 'X' input value and the log calculation */ m_apm_copy(M_last_xx_input, xx); m_apm_copy(M_last_xx_log, tmp9); } m_apm_multiply(tmp8, tmp9, yy); m_apm_exp(rr, places, tmp8); M_restore_stack(2); /* restore the 2 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; if (u->m_apm_error || v->m_apm_error) { M_set_to_error(r); return; } /* '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, "\'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_log(M_APM r, int places, M_APM a) { M_APM tmp0, tmp1, tmp2; int mexp, dplaces; if (a->m_apm_error) { M_set_to_error(r); return; } if (a->m_apm_sign <= 0) { M_apm_log_error_msg(M_APM_RETURN, "\'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 */ mexp = a->m_apm_exponent; if (mexp == 0 || mexp == 1) { 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); if (abs(mexp) <= 3) { 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 */ }