diff options
Diffstat (limited to 'src/UnaryCRFunction.java')
-rw-r--r-- | src/UnaryCRFunction.java | 778 |
1 files changed, 389 insertions, 389 deletions
diff --git a/src/UnaryCRFunction.java b/src/UnaryCRFunction.java index 12dd6dd..1062918 100644 --- a/src/UnaryCRFunction.java +++ b/src/UnaryCRFunction.java @@ -1,9 +1,9 @@ -// Copyright (c) 1999, Silicon Graphics, Inc. -- ALL RIGHTS RESERVED -// +// Copyright (c) 1999, Silicon Graphics, Inc. -- ALL RIGHTS RESERVED +// // Permission is granted free of charge to copy, modify, use and distribute // this software provided you include the entirety of this notice in all // copies made. -// +// // THIS SOFTWARE IS PROVIDED ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, // WARRANTIES THAT THE SUBJECT SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT @@ -13,7 +13,7 @@ // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES // AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY SUBJECT SOFTWARE IS // AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. -// +// // UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING, // WITHOUT LIMITATION, NEGLIGENCE OR STRICT LIABILITY), CONTRACT, OR // OTHERWISE, SHALL SGI BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, @@ -26,7 +26,7 @@ // LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE // EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT // EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. -// +// // These license terms shall be governed by and construed in accordance with // the laws of the United States and the State of California as applied to // agreements entered into and to be performed entirely within California @@ -34,7 +34,7 @@ // terms shall be subject to the exclusive jurisdiction of the Federal Courts // of the Northern District of California (or, absent subject matter // jurisdiction in such courts, the courts of the State of California), with -// venue lying exclusively in Santa Clara County, California. +// venue lying exclusively in Santa Clara County, California. package com.sgi.math; @@ -54,49 +54,49 @@ public abstract class UnaryCRFunction { * The function object corresponding to the identity function. */ public static final UnaryCRFunction identityFunction = - new identity_UnaryCRFunction(); + new identity_UnaryCRFunction(); /** * The function object corresponding to the <TT>negate</tt> method of CR. */ public static final UnaryCRFunction negateFunction = - new negate_UnaryCRFunction(); + new negate_UnaryCRFunction(); /** * The function object corresponding to the <TT>inverse</tt> method of CR. */ public static final UnaryCRFunction inverseFunction = - new inverse_UnaryCRFunction(); + new inverse_UnaryCRFunction(); /** * The function object corresponding to the <TT>abs</tt> method of CR. */ public static final UnaryCRFunction absFunction = - new abs_UnaryCRFunction(); + new abs_UnaryCRFunction(); /** * The function object corresponding to the <TT>exp</tt> method of CR. */ public static final UnaryCRFunction expFunction = - new exp_UnaryCRFunction(); + new exp_UnaryCRFunction(); /** * The function object corresponding to the <TT>cos</tt> method of CR. */ public static final UnaryCRFunction cosFunction = - new cos_UnaryCRFunction(); + new cos_UnaryCRFunction(); /** * The function object corresponding to the <TT>sin</tt> method of CR. */ public static final UnaryCRFunction sinFunction = - new sin_UnaryCRFunction(); + new sin_UnaryCRFunction(); /** * The function object corresponding to the tangent function. */ public static final UnaryCRFunction tanFunction = - new tan_UnaryCRFunction(); + new tan_UnaryCRFunction(); // Helper for some of the following public members. static CR half_pi = CR.PI.divide(CR.valueOf(2)); @@ -106,7 +106,7 @@ public abstract class UnaryCRFunction { * -PI/2 and PI/2. */ public static final UnaryCRFunction asinFunction = - UnaryCRFunction.sinFunction.inverseMonotone(half_pi.negate(), half_pi); + UnaryCRFunction.sinFunction.inverseMonotone(half_pi.negate(), half_pi); /** * The function object corresponding to the inverse cosine (arccosine) function. @@ -114,32 +114,32 @@ public abstract class UnaryCRFunction { * 0 and PI. */ public static final UnaryCRFunction acosFunction = - new acos_UnaryCRFunction(); + new acos_UnaryCRFunction(); /** * The function object corresponding to the inverse cosine (arctangent) function. * The result is between -PI/2 and PI/2. */ public static final UnaryCRFunction atanFunction = - new atan_UnaryCRFunction(); + new atan_UnaryCRFunction(); /** * The function object corresponding to the <TT>ln</tt> method of CR. */ public static final UnaryCRFunction lnFunction = - new ln_UnaryCRFunction(); - + new ln_UnaryCRFunction(); + /** * The function object corresponding to the <TT>sqrt</tt> method of CR. */ public static final UnaryCRFunction sqrtFunction = - new sqrt_UnaryCRFunction(); - + new sqrt_UnaryCRFunction(); + /** * Compose this function with <TT>f2</tt>. */ public UnaryCRFunction compose(UnaryCRFunction f2) { - return new compose_UnaryCRFunction(this, f2); + return new compose_UnaryCRFunction(this, f2); } /** @@ -150,7 +150,7 @@ public abstract class UnaryCRFunction { * The original function may be either increasing or decreasing. */ public UnaryCRFunction inverseMonotone(CR low, CR high) { - return new inverseMonotone_UnaryCRFunction(this, low, high); + return new inverseMonotone_UnaryCRFunction(this, low, high); } /** @@ -161,7 +161,7 @@ public abstract class UnaryCRFunction { * The result is defined only in the open interval. */ public UnaryCRFunction monotoneDerivative(CR low, CR high) { - return new monotoneDerivative_UnaryCRFunction(this, low, high); + return new monotoneDerivative_UnaryCRFunction(this, low, high); } } @@ -169,25 +169,25 @@ public abstract class UnaryCRFunction { // Subclasses of UnaryCRFunction for various built-in functions. class sin_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.sin(); + return x.sin(); } } class cos_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.cos(); + return x.cos(); } } class tan_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.sin().divide(x.cos()); + return x.sin().divide(x.cos()); } } class acos_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return half_pi.subtract(asinFunction.execute(x)); + return half_pi.subtract(asinFunction.execute(x)); } } @@ -198,52 +198,52 @@ class acos_UnaryCRFunction extends UnaryCRFunction { class atan_UnaryCRFunction extends UnaryCRFunction { CR one = CR.valueOf(1); public CR execute(CR x) { - CR x2 = x.multiply(x); - CR abs_sin_atan = x2.divide(one.add(x2)).sqrt(); - CR sin_atan = x.select(abs_sin_atan.negate(), abs_sin_atan); - return asinFunction.execute(sin_atan); + CR x2 = x.multiply(x); + CR abs_sin_atan = x2.divide(one.add(x2)).sqrt(); + CR sin_atan = x.select(abs_sin_atan.negate(), abs_sin_atan); + return asinFunction.execute(sin_atan); } } class exp_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.exp(); + return x.exp(); } } class ln_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.ln(); + return x.ln(); } } class identity_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x; + return x; } } class negate_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.negate(); + return x.negate(); } } class inverse_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.inverse(); + return x.inverse(); } } class abs_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.abs(); + return x.abs(); } } class sqrt_UnaryCRFunction extends UnaryCRFunction { public CR execute(CR x) { - return x.sqrt(); + return x.sqrt(); } } @@ -251,11 +251,11 @@ class compose_UnaryCRFunction extends UnaryCRFunction { UnaryCRFunction f1; UnaryCRFunction f2; compose_UnaryCRFunction(UnaryCRFunction func1, - UnaryCRFunction func2) { - f1 = func1; f2 = func2; + UnaryCRFunction func2) { + f1 = func1; f2 = func2; } public CR execute(CR x) { - return f1.execute(f2.execute(x)); + return f1.execute(f2.execute(x)); } } @@ -265,297 +265,297 @@ class inverseMonotone_UnaryCRFunction extends UnaryCRFunction { // I couldn't find a way to initialize these such that the // compiler accepted them as final without turning them into arrays. final UnaryCRFunction f[] = new UnaryCRFunction[1]; - // Monotone increasing. - // If it was monotone decreasing, we - // negate it. + // Monotone increasing. + // If it was monotone decreasing, we + // negate it. final boolean f_negated[] = new boolean[1]; final CR low[] = new CR[1]; final CR high[] = new CR[1]; final CR f_low[] = new CR[1]; final CR f_high[] = new CR[1]; final int max_msd[] = new int[1]; - // Bound on msd of both f(high) and f(low) + // Bound on msd of both f(high) and f(low) final int max_arg_prec[] = new int[1]; - // base**max_arg_prec is a small fraction - // of low - high. + // base**max_arg_prec is a small fraction + // of low - high. final int deriv_msd[] = new int[1]; - // Rough approx. of msd of first - // derivative. + // Rough approx. of msd of first + // derivative. inverseMonotone_UnaryCRFunction(UnaryCRFunction func, CR l, CR h) { - low[0] = l; high[0] = h; + low[0] = l; high[0] = h; CR tmp_f_low = func.execute(l); CR tmp_f_high = func.execute(h); - // Since func is monotone and low < high, the following test - // converges. - if (tmp_f_low.compareTo(tmp_f_high) > 0) { - f[0] = UnaryCRFunction.negateFunction.compose(func); - f_negated[0] = true; - f_low[0] = tmp_f_low.negate(); - f_high[0] = tmp_f_high.negate(); - } else { - f[0] = func; - f_negated[0] = false; - f_low[0] = tmp_f_low; - f_high[0] = tmp_f_high; - } + // Since func is monotone and low < high, the following test + // converges. + if (tmp_f_low.compareTo(tmp_f_high) > 0) { + f[0] = UnaryCRFunction.negateFunction.compose(func); + f_negated[0] = true; + f_low[0] = tmp_f_low.negate(); + f_high[0] = tmp_f_high.negate(); + } else { + f[0] = func; + f_negated[0] = false; + f_low[0] = tmp_f_low; + f_high[0] = tmp_f_high; + } max_msd[0] = low[0].abs().max(high[0].abs()).msd(); - max_arg_prec[0] = high[0].subtract(low[0]).msd() - 4; - deriv_msd[0] = f_high[0].subtract(f_low[0]) - .divide(high[0].subtract(low[0])).msd(); + max_arg_prec[0] = high[0].subtract(low[0]).msd() - 4; + deriv_msd[0] = f_high[0].subtract(f_low[0]) + .divide(high[0].subtract(low[0])).msd(); } class inverseIncreasingCR extends CR { - final CR arg; - inverseIncreasingCR(CR x) { - arg = f_negated[0]? x.negate() : x; - } - // Comparison with a difference of one treated as equality. - int sloppy_compare(BigInteger x, BigInteger y) { - BigInteger difference = x.subtract(y); - if (difference.compareTo(big1) > 0) { - return 1; - } - if (difference.compareTo(bigm1) < 0) { - return -1; - } - return 0; - } - protected BigInteger approximate(int p) { - final boolean trace = false; // Change to generate trace - final int extra_arg_prec = 4; - final UnaryCRFunction fn = f[0]; - int small_steps = 0; // Number of preceding ineffective - // steps. If this number gets >= 2, - // we perform a binary search step - // to ensure forward progress. - int digits_needed = max_msd[0] - p; - if (digits_needed < 0) return big0; - int working_arg_prec = p - extra_arg_prec; - if (working_arg_prec > max_arg_prec[0]) { - working_arg_prec = max_arg_prec[0]; - } - int working_eval_prec = working_arg_prec + deriv_msd[0] - 20; - // initial guess - // We use a combination of binary search and something like - // the secant method. This always converges linearly, - // and should converge quadratically for well-behaved - // functions. - // F_l and f_h are always the approximate images of l and h. - // At any point, arg is between f_l and f_h, or no more than - // one outside [f_l, f_h]. - // L and h are implicitly scaled by working_arg_prec. - // The scaled values of l and h are strictly between low and high. - // If at_left is true, then l is logically at the left - // end of the interval. We approximate this by setting l to - // a point slightly inside the interval, and letting f_l - // approximate the function value at the endpoint. - // If at_right is true, r and f_r are set correspondingly. - // At the endpoints of the interval, f_l and f_h may correspond - // to the endpoints, even if l and h are slightly inside. - // F_l and f_u are scaled by working_eval_prec. - // Working_eval_prec may need to be adjusted depending - // on the derivative of f. - boolean at_left, at_right; - BigInteger l, f_l; - BigInteger h, f_h; - BigInteger low_appr = low[0].get_appr(working_arg_prec) - .add(big1); - BigInteger high_appr = high[0].get_appr(working_arg_prec) - .subtract(big1); - BigInteger arg_appr = arg.get_appr(working_eval_prec); - boolean have_good_appr = (appr_valid && min_prec < max_msd[0]); - if (digits_needed < 30 && !have_good_appr) { - if (trace) { - System.out.println("Setting interval to entire domain"); - } - h = high_appr; - f_h = f_high[0].get_appr(working_eval_prec); - l = low_appr; - f_l = f_low[0].get_appr(working_eval_prec); - // Check for clear out-of-bounds case. - // Close cases may fail in other ways. - if (f_h.compareTo(arg_appr.subtract(big1)) < 0 - || f_l.compareTo(arg_appr.add(big1)) > 0) { - throw new ArithmeticException(); - } - at_left = true; - at_right = true; - small_steps = 2; // Start with bin search step. - } else { - int rough_prec = p + digits_needed/2; - - if (have_good_appr && - (digits_needed < 30 || min_prec < p + 3*digits_needed/4)) { - rough_prec = min_prec; - } - BigInteger rough_appr = get_appr(rough_prec); - if (trace) { - System.out.println("Setting interval based on prev. appr"); - System.out.println("prev. prec = " + rough_prec - + " appr = " + rough_appr); - } - h = rough_appr.add(big1) - .shiftLeft(rough_prec - working_arg_prec); - l = rough_appr.subtract(big1) - .shiftLeft(rough_prec - working_arg_prec); - if (h.compareTo(high_appr) > 0) { - h = high_appr; - f_h = f_high[0].get_appr(working_eval_prec); - at_right = true; - } else { - CR h_cr = CR.valueOf(h).shiftLeft(working_arg_prec); - f_h = fn.execute(h_cr).get_appr(working_eval_prec); - at_right = false; - } - if (l.compareTo(low_appr) < 0) { - l = low_appr; - f_l = f_low[0].get_appr(working_eval_prec); - at_left = true; - } else { - CR l_cr = CR.valueOf(l).shiftLeft(working_arg_prec); - f_l = fn.execute(l_cr).get_appr(working_eval_prec); - at_left = false; - } - } - BigInteger difference = h.subtract(l); - for(int i = 0;; ++i) { - if (Thread.interrupted() || please_stop) - throw new AbortedError(); - if (trace) { - System.out.println("***Iteration: " + i); - System.out.println("Arg prec = " + working_arg_prec - + " eval prec = " + working_eval_prec - + " arg appr. = " + arg_appr); - System.out.println("l = " + l + "; h = " + h); - System.out.println("f(l) = " + f_l + "; f(h) = " + f_h); - } - if (difference.compareTo(big6) < 0) { - // Answer is less than 1/2 ulp away from h. - return scale(h, -extra_arg_prec); - } - BigInteger f_difference = f_h.subtract(f_l); - // Narrow the interval by dividing at a cleverly - // chosen point (guess) in the middle. - { - BigInteger guess; - if (small_steps >= 2 || f_difference.signum() == 0) { - // Do a binary search step to guarantee linear - // convergence. - guess = l.add(h).shiftRight(1); - } else { - // interpolate. - // f_difference is nonzero here. - BigInteger arg_difference = arg_appr.subtract(f_l); - BigInteger t = arg_difference.multiply(difference); - BigInteger adj = t.divide(f_difference); - if (adj.compareTo(difference.shiftRight(2)) < 0) { - // Very close to left side of interval; - // move closer to center. - // If one of the endpoints is very close to - // the answer, this slows conversion a bit. - // But it greatly increases the probability - // that the answer will be in the smaller - // subinterval. - adj = adj.shiftLeft(1); - } else if (adj.compareTo(difference.multiply(CR.big3) - .shiftRight(2)) > 0){ - adj = difference.subtract(difference.subtract(adj) - .shiftLeft(1)); - } - if (adj.signum() <= 0) - adj = big2; - if (adj.compareTo(difference) >= 0) - adj = difference.subtract(big2); - guess = (adj.signum() <= 0? l.add(big2) : l.add(adj)); - } - int outcome; - BigInteger tweak = big2; - BigInteger f_guess; - for(boolean adj_prec = false;; adj_prec = !adj_prec) { - CR guess_cr = CR.valueOf(guess) - .shiftLeft(working_arg_prec); - if (trace) { - System.out.println("Evaluating at " + guess_cr - + " with precision " - + working_eval_prec); - } - CR f_guess_cr = fn.execute(guess_cr); - if (trace) { - System.out.println("fn value = " + f_guess_cr); - } - f_guess = f_guess_cr.get_appr(working_eval_prec); - outcome = sloppy_compare(f_guess, arg_appr); - if (outcome != 0) break; - // Alternately increase evaluation precision - // and adjust guess slightly. - // This should be an unlikely case. - if (adj_prec) { - // adjust working_eval_prec to get enough - // resolution. - int adjustment = deriv_msd[0] > 0 ? -20 : - deriv_msd[0] - 20; - CR l_cr = CR.valueOf(l) - .shiftLeft(working_arg_prec); - CR h_cr = CR.valueOf(h) - .shiftLeft(working_arg_prec); - working_eval_prec += adjustment; - if (trace) { - System.out.println("New eval prec = " - + working_eval_prec - + (at_left? "(at left)" : "") - + (at_right? "(at right)" : "")); - } - if (at_left) { - f_l = f_low[0].get_appr(working_eval_prec); - } else { - f_l = fn.execute(l_cr) - .get_appr(working_eval_prec); - } - if (at_right) { - f_h = f_high[0].get_appr(working_eval_prec); - } else { - f_h = fn.execute(h_cr) - .get_appr(working_eval_prec); - } - arg_appr = arg.get_appr(working_eval_prec); - } else { - // guess might be exactly right; tweak it - // slightly. - if (trace) System.out.println("tweaking guess"); - BigInteger new_guess = guess.add(tweak); - if (new_guess.compareTo(h) >= 0) { - guess = guess.subtract(tweak); - } else { - guess = new_guess; - } - // If we keep hitting the right answer, it's - // important to alternate which side we move it - // to, so that the interval shrinks rapidly. - tweak = tweak.negate(); - } - } - if (outcome > 0) { - h = guess; - f_h = f_guess; - at_right = false; - } else { - l = guess; - f_l = f_guess; - at_left = false; - } - BigInteger new_difference = h.subtract(l); - if (new_difference.compareTo(difference - .shiftRight(1)) >= 0) { - ++small_steps; - } else { - small_steps = 0; - } - difference = new_difference; - } - } - } + final CR arg; + inverseIncreasingCR(CR x) { + arg = f_negated[0]? x.negate() : x; + } + // Comparison with a difference of one treated as equality. + int sloppy_compare(BigInteger x, BigInteger y) { + BigInteger difference = x.subtract(y); + if (difference.compareTo(big1) > 0) { + return 1; + } + if (difference.compareTo(bigm1) < 0) { + return -1; + } + return 0; + } + protected BigInteger approximate(int p) { + final boolean trace = false; // Change to generate trace + final int extra_arg_prec = 4; + final UnaryCRFunction fn = f[0]; + int small_steps = 0; // Number of preceding ineffective + // steps. If this number gets >= 2, + // we perform a binary search step + // to ensure forward progress. + int digits_needed = max_msd[0] - p; + if (digits_needed < 0) return big0; + int working_arg_prec = p - extra_arg_prec; + if (working_arg_prec > max_arg_prec[0]) { + working_arg_prec = max_arg_prec[0]; + } + int working_eval_prec = working_arg_prec + deriv_msd[0] - 20; + // initial guess + // We use a combination of binary search and something like + // the secant method. This always converges linearly, + // and should converge quadratically for well-behaved + // functions. + // F_l and f_h are always the approximate images of l and h. + // At any point, arg is between f_l and f_h, or no more than + // one outside [f_l, f_h]. + // L and h are implicitly scaled by working_arg_prec. + // The scaled values of l and h are strictly between low and high. + // If at_left is true, then l is logically at the left + // end of the interval. We approximate this by setting l to + // a point slightly inside the interval, and letting f_l + // approximate the function value at the endpoint. + // If at_right is true, r and f_r are set correspondingly. + // At the endpoints of the interval, f_l and f_h may correspond + // to the endpoints, even if l and h are slightly inside. + // F_l and f_u are scaled by working_eval_prec. + // Working_eval_prec may need to be adjusted depending + // on the derivative of f. + boolean at_left, at_right; + BigInteger l, f_l; + BigInteger h, f_h; + BigInteger low_appr = low[0].get_appr(working_arg_prec) + .add(big1); + BigInteger high_appr = high[0].get_appr(working_arg_prec) + .subtract(big1); + BigInteger arg_appr = arg.get_appr(working_eval_prec); + boolean have_good_appr = (appr_valid && min_prec < max_msd[0]); + if (digits_needed < 30 && !have_good_appr) { + if (trace) { + System.out.println("Setting interval to entire domain"); + } + h = high_appr; + f_h = f_high[0].get_appr(working_eval_prec); + l = low_appr; + f_l = f_low[0].get_appr(working_eval_prec); + // Check for clear out-of-bounds case. + // Close cases may fail in other ways. + if (f_h.compareTo(arg_appr.subtract(big1)) < 0 + || f_l.compareTo(arg_appr.add(big1)) > 0) { + throw new ArithmeticException(); + } + at_left = true; + at_right = true; + small_steps = 2; // Start with bin search step. + } else { + int rough_prec = p + digits_needed/2; + + if (have_good_appr && + (digits_needed < 30 || min_prec < p + 3*digits_needed/4)) { + rough_prec = min_prec; + } + BigInteger rough_appr = get_appr(rough_prec); + if (trace) { + System.out.println("Setting interval based on prev. appr"); + System.out.println("prev. prec = " + rough_prec + + " appr = " + rough_appr); + } + h = rough_appr.add(big1) + .shiftLeft(rough_prec - working_arg_prec); + l = rough_appr.subtract(big1) + .shiftLeft(rough_prec - working_arg_prec); + if (h.compareTo(high_appr) > 0) { + h = high_appr; + f_h = f_high[0].get_appr(working_eval_prec); + at_right = true; + } else { + CR h_cr = CR.valueOf(h).shiftLeft(working_arg_prec); + f_h = fn.execute(h_cr).get_appr(working_eval_prec); + at_right = false; + } + if (l.compareTo(low_appr) < 0) { + l = low_appr; + f_l = f_low[0].get_appr(working_eval_prec); + at_left = true; + } else { + CR l_cr = CR.valueOf(l).shiftLeft(working_arg_prec); + f_l = fn.execute(l_cr).get_appr(working_eval_prec); + at_left = false; + } + } + BigInteger difference = h.subtract(l); + for(int i = 0;; ++i) { + if (Thread.interrupted() || please_stop) + throw new AbortedError(); + if (trace) { + System.out.println("***Iteration: " + i); + System.out.println("Arg prec = " + working_arg_prec + + " eval prec = " + working_eval_prec + + " arg appr. = " + arg_appr); + System.out.println("l = " + l + "; h = " + h); + System.out.println("f(l) = " + f_l + "; f(h) = " + f_h); + } + if (difference.compareTo(big6) < 0) { + // Answer is less than 1/2 ulp away from h. + return scale(h, -extra_arg_prec); + } + BigInteger f_difference = f_h.subtract(f_l); + // Narrow the interval by dividing at a cleverly + // chosen point (guess) in the middle. + { + BigInteger guess; + if (small_steps >= 2 || f_difference.signum() == 0) { + // Do a binary search step to guarantee linear + // convergence. + guess = l.add(h).shiftRight(1); + } else { + // interpolate. + // f_difference is nonzero here. + BigInteger arg_difference = arg_appr.subtract(f_l); + BigInteger t = arg_difference.multiply(difference); + BigInteger adj = t.divide(f_difference); + if (adj.compareTo(difference.shiftRight(2)) < 0) { + // Very close to left side of interval; + // move closer to center. + // If one of the endpoints is very close to + // the answer, this slows conversion a bit. + // But it greatly increases the probability + // that the answer will be in the smaller + // subinterval. + adj = adj.shiftLeft(1); + } else if (adj.compareTo(difference.multiply(CR.big3) + .shiftRight(2)) > 0){ + adj = difference.subtract(difference.subtract(adj) + .shiftLeft(1)); + } + if (adj.signum() <= 0) + adj = big2; + if (adj.compareTo(difference) >= 0) + adj = difference.subtract(big2); + guess = (adj.signum() <= 0? l.add(big2) : l.add(adj)); + } + int outcome; + BigInteger tweak = big2; + BigInteger f_guess; + for(boolean adj_prec = false;; adj_prec = !adj_prec) { + CR guess_cr = CR.valueOf(guess) + .shiftLeft(working_arg_prec); + if (trace) { + System.out.println("Evaluating at " + guess_cr + + " with precision " + + working_eval_prec); + } + CR f_guess_cr = fn.execute(guess_cr); + if (trace) { + System.out.println("fn value = " + f_guess_cr); + } + f_guess = f_guess_cr.get_appr(working_eval_prec); + outcome = sloppy_compare(f_guess, arg_appr); + if (outcome != 0) break; + // Alternately increase evaluation precision + // and adjust guess slightly. + // This should be an unlikely case. + if (adj_prec) { + // adjust working_eval_prec to get enough + // resolution. + int adjustment = deriv_msd[0] > 0 ? -20 : + deriv_msd[0] - 20; + CR l_cr = CR.valueOf(l) + .shiftLeft(working_arg_prec); + CR h_cr = CR.valueOf(h) + .shiftLeft(working_arg_prec); + working_eval_prec += adjustment; + if (trace) { + System.out.println("New eval prec = " + + working_eval_prec + + (at_left? "(at left)" : "") + + (at_right? "(at right)" : "")); + } + if (at_left) { + f_l = f_low[0].get_appr(working_eval_prec); + } else { + f_l = fn.execute(l_cr) + .get_appr(working_eval_prec); + } + if (at_right) { + f_h = f_high[0].get_appr(working_eval_prec); + } else { + f_h = fn.execute(h_cr) + .get_appr(working_eval_prec); + } + arg_appr = arg.get_appr(working_eval_prec); + } else { + // guess might be exactly right; tweak it + // slightly. + if (trace) System.out.println("tweaking guess"); + BigInteger new_guess = guess.add(tweak); + if (new_guess.compareTo(h) >= 0) { + guess = guess.subtract(tweak); + } else { + guess = new_guess; + } + // If we keep hitting the right answer, it's + // important to alternate which side we move it + // to, so that the interval shrinks rapidly. + tweak = tweak.negate(); + } + } + if (outcome > 0) { + h = guess; + f_h = f_guess; + at_right = false; + } else { + l = guess; + f_l = f_guess; + at_left = false; + } + BigInteger new_difference = h.subtract(l); + if (new_difference.compareTo(difference + .shiftRight(1)) >= 0) { + ++small_steps; + } else { + small_steps = 0; + } + difference = new_difference; + } + } + } } public CR execute(CR x) { - return new inverseIncreasingCR(x); + return new inverseIncreasingCR(x); } } @@ -563,9 +563,9 @@ class monotoneDerivative_UnaryCRFunction extends UnaryCRFunction { // The following variables are final, so that they // can be referenced from the inner class inverseIncreasingCR. final UnaryCRFunction f[] = new UnaryCRFunction[1]; - // Monotone increasing. - // If it was monotone decreasing, we - // negate it. + // Monotone increasing. + // If it was monotone decreasing, we + // negate it. final CR low[] = new CR[1]; // endpoints and mispoint of interval final CR mid[] = new CR[1]; final CR high[] = new CR[1]; @@ -574,84 +574,84 @@ class monotoneDerivative_UnaryCRFunction extends UnaryCRFunction { final CR f_high[] = new CR[1]; final int difference_msd[] = new int[1]; // msd of interval len. final int deriv2_msd[] = new int[1]; - // Rough approx. of msd of second - // derivative. - // This is increased to be an appr. bound - // on the msd of |(f'(y)-f'(x))/(x-y)| - // for any pair of points x and y - // we have considered. - // It may be better to keep a copy per - // derivative value. + // Rough approx. of msd of second + // derivative. + // This is increased to be an appr. bound + // on the msd of |(f'(y)-f'(x))/(x-y)| + // for any pair of points x and y + // we have considered. + // It may be better to keep a copy per + // derivative value. monotoneDerivative_UnaryCRFunction(UnaryCRFunction func, CR l, CR h) { - f[0] = func; - low[0] = l; high[0] = h; - mid[0] = l.add(h).shiftRight(1); + f[0] = func; + low[0] = l; high[0] = h; + mid[0] = l.add(h).shiftRight(1); f_low[0] = func.execute(l); f_mid[0] = func.execute(mid[0]); f_high[0] = func.execute(h); - CR difference = h.subtract(l); - // compute approximate msd of - // ((f_high - f_mid) - (f_mid - f_low))/(high - low) - // This should be a very rough appr to the second derivative. - // We add a little slop to err on the high side, since - // a low estimate will cause extra iterations. - CR appr_diff2 = f_high[0].subtract(f_mid[0].shiftLeft(1)).add(f_low[0]); - difference_msd[0] = difference.msd(); - deriv2_msd[0] = appr_diff2.msd() - difference_msd[0] + 4; + CR difference = h.subtract(l); + // compute approximate msd of + // ((f_high - f_mid) - (f_mid - f_low))/(high - low) + // This should be a very rough appr to the second derivative. + // We add a little slop to err on the high side, since + // a low estimate will cause extra iterations. + CR appr_diff2 = f_high[0].subtract(f_mid[0].shiftLeft(1)).add(f_low[0]); + difference_msd[0] = difference.msd(); + deriv2_msd[0] = appr_diff2.msd() - difference_msd[0] + 4; } class monotoneDerivativeCR extends CR { - CR arg; - CR f_arg; - int max_delta_msd; - monotoneDerivativeCR(CR x) { - arg = x; - f_arg = f[0].execute(x); - // The following must converge, since arg must be in the - // open interval. - CR left_diff = arg.subtract(low[0]); - int max_delta_left_msd = left_diff.msd(); - CR right_diff = high[0].subtract(arg); - int max_delta_right_msd = right_diff.msd(); - if (left_diff.signum() < 0 || right_diff.signum() < 0) { - throw new ArithmeticException(); - } - max_delta_msd = (max_delta_left_msd < max_delta_right_msd? - max_delta_left_msd - : max_delta_right_msd); - } - protected BigInteger approximate(int p) { - final int extra_prec = 4; - int log_delta = p - deriv2_msd[0]; - // Ensure that we stay within the interval. - if (log_delta > max_delta_msd) log_delta = max_delta_msd; - log_delta -= extra_prec; - CR delta = one.shiftLeft(log_delta); - - CR left = arg.subtract(delta); - CR right = arg.add(delta); - CR f_left = f[0].execute(left); - CR f_right = f[0].execute(right); - CR left_deriv = f_arg.subtract(f_left).shiftRight(log_delta); - CR right_deriv = f_right.subtract(f_arg).shiftRight(log_delta); - int eval_prec = p - extra_prec; - BigInteger appr_left_deriv = left_deriv.get_appr(eval_prec); - BigInteger appr_right_deriv = right_deriv.get_appr(eval_prec); - BigInteger deriv_difference = - appr_right_deriv.subtract(appr_left_deriv).abs(); - if (deriv_difference.compareTo(big8) < 0) { - return scale(appr_left_deriv, -extra_prec); - } else { - if (Thread.interrupted() || please_stop) - throw new AbortedError(); - deriv2_msd[0] = - eval_prec + deriv_difference.bitLength() + 4/*slop*/; - deriv2_msd[0] -= log_delta; - return approximate(p); - } + CR arg; + CR f_arg; + int max_delta_msd; + monotoneDerivativeCR(CR x) { + arg = x; + f_arg = f[0].execute(x); + // The following must converge, since arg must be in the + // open interval. + CR left_diff = arg.subtract(low[0]); + int max_delta_left_msd = left_diff.msd(); + CR right_diff = high[0].subtract(arg); + int max_delta_right_msd = right_diff.msd(); + if (left_diff.signum() < 0 || right_diff.signum() < 0) { + throw new ArithmeticException(); + } + max_delta_msd = (max_delta_left_msd < max_delta_right_msd? + max_delta_left_msd + : max_delta_right_msd); + } + protected BigInteger approximate(int p) { + final int extra_prec = 4; + int log_delta = p - deriv2_msd[0]; + // Ensure that we stay within the interval. + if (log_delta > max_delta_msd) log_delta = max_delta_msd; + log_delta -= extra_prec; + CR delta = one.shiftLeft(log_delta); + + CR left = arg.subtract(delta); + CR right = arg.add(delta); + CR f_left = f[0].execute(left); + CR f_right = f[0].execute(right); + CR left_deriv = f_arg.subtract(f_left).shiftRight(log_delta); + CR right_deriv = f_right.subtract(f_arg).shiftRight(log_delta); + int eval_prec = p - extra_prec; + BigInteger appr_left_deriv = left_deriv.get_appr(eval_prec); + BigInteger appr_right_deriv = right_deriv.get_appr(eval_prec); + BigInteger deriv_difference = + appr_right_deriv.subtract(appr_left_deriv).abs(); + if (deriv_difference.compareTo(big8) < 0) { + return scale(appr_left_deriv, -extra_prec); + } else { + if (Thread.interrupted() || please_stop) + throw new AbortedError(); + deriv2_msd[0] = + eval_prec + deriv_difference.bitLength() + 4/*slop*/; + deriv2_msd[0] -= log_delta; + return approximate(p); + } } } public CR execute(CR x) { - return new monotoneDerivativeCR(x); + return new monotoneDerivativeCR(x); } } |