/* * find log(N) * * if places < 360 * solve with cubically convergent algorithm above * * else * * let 'X' be 'close' to the solution (we use ~110 decimal places) * * let Y = N * exp(-X) - 1 * * then * * log(N) = X + log(1 + Y) * * since 'Y' will be small, we can use the efficient log_near_1 algorithm. * */ void M_log_basic_iteration(M_APM rr, int places, M_APM nn) { M_APM tmp0, tmp1, tmp2, tmpX; if (places < 360) { M_log_solve_cubic(rr, places, nn); } else { tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); tmpX = M_get_stack_var(); M_log_solve_cubic(tmpX, 110, nn); m_apm_negate(tmp0, tmpX); m_apm_exp(tmp1, (places + 8), tmp0); m_apm_multiply(tmp2, tmp1, nn); m_apm_subtract(tmp1, tmp2, MM_One); M_log_near_1(tmp0, (places - 104), tmp1); m_apm_add(tmp1, tmpX, tmp0); m_apm_round(rr, places, tmp1); M_restore_stack(4); } }
/* * return the nearest integer <= input */ void m_apm_floor(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 -1 or 0 */ { if (bb->m_apm_sign < 0) m_apm_negate(bb, MM_One); else M_set_to_zero(bb); return; } if (bb->m_apm_sign < 0) { mtmp = M_get_stack_var(); m_apm_negate(mtmp, bb); mtmp->m_apm_datalength = mtmp->m_apm_exponent; M_apm_normalize(mtmp); m_apm_add(bb, mtmp, MM_One); bb->m_apm_sign = -1; M_restore_stack(1); } else { bb->m_apm_datalength = bb->m_apm_exponent; M_apm_normalize(bb); } }
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_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); }
void m_apm_negate_mt(M_APM d, M_APM s) { m_apm_enter(); m_apm_negate(d,s); m_apm_leave(); }
MAPM MAPM::neg(void) const { MAPM ret; m_apm_negate(ret.val(),cval()); return ret; }
/* * 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); }