runCalculation method Null safety

  1. @override
Future<bool> runCalculation()
override

Run the solve/integrate algorithm.

Implementation

@override
Future<bool> runCalculation() async {
  final DisplayMode precision = model.displayMode;
  // The number of digits being displayed determines how precisely we
  // estimate the integral.
  const int maxIterations = 10;
  // Complexity is... uh... O(a lot)
  // In testing, typical functions converge in 3 or 4 iterations.
  // With the default 50ms/program instruction, and a trivial function
  // that generates a random number, it takes over an hour to get here.
  // 10 is conservatively high, and a nice, round number
  // that's low enough so the thing will terminate before the universe
  // expires.

  _lastEstimate = 0;
  final Value originalY = model.y;
  final Value originalX = model.x;
  double a = model.yF; // lower bound
  double b = model.xF; // upper bound
  final double signResult;
  if (a == b) {
    model.z = originalX;
    model.t = originalY;
    model.x = model.y = Value.zero;
    return true;
  } else if (a > b) {
    signResult = -1;
    final tmp = b;
    b = a;
    a = tmp;
  } else {
    signResult = 1;
  }
  final double span = b - a;

  // This is a port of qromo(), copied from
  // https://www.hpmuseum.org/forum/thread-16523.html
  // The post includes the text "You may freely use any of the code
  // here and please ask questions or PM me if something is not clear."
  /*
    double qromo(double (*f)(double), double a, double b, int n, double eps) {
      double R1[n], R2[n];
      double *Ro = &R1[0], *Ru = &R2[0];
      double h = b-a;
      int i, j;
      unsigned long long k = 1;
      Ro[0] = f((a+b)/2)*h;
      for (i = 1; i < n; ++i) {
        unsigned long long s = 1;
        double sum = 0;
        double *Rt;
        k *= 3;
        h /= 3;
        for (j = 1; j < k; j += 3)
          sum += f(a+(j-1)*h+h/2) + f(a+(j+1)*h+h/2);
        Ru[0] = h*sum + Ro[0]/3;
        for (j = 1; j <= i; ++j) {
          s *= 9;
          Ru[j] = (s*Ru[j-1] - Ro[j-1])/(s-1);
        }
        if (i > 1 && fabs(Ro[i-1]-Ru[i]) <= eps*fabs(Ru[i])+eps)
          return Ru[i];
        Rt = Ro;
        Ro = Ru;
        Ru = Rt;
      }
      return Ro[n-1]; // no convergence, return best result,
                      // error is fabs((Ru[n-2]-Ro[n-1])/Ro[n-1])
    }
   */
  var ro = Float64List(maxIterations);
  var ru = Float64List(maxIterations);
  double h = span;
  int k = 1;
  ro[0] = await runSubroutine((a + b) / 2) * h;
  _lastEstimate = ro[0] * signResult;
  int calls = 1;
  double totalMagnitude = ro[0].abs();
  int i;
  for (i = 1; i < maxIterations; i++) {
    int s = 1;
    double sum = 0;
    k *= 3;
    h /= 3;
    for (int j = 1; j < k; j += 3) {
      double f1 = await runSubroutine(a + (j - 1) * h + h / 2);
      double f2 = await runSubroutine(a + (j + 1) * h + h / 2);
      calls += 2;
      totalMagnitude += f1.abs() + f2.abs();
      sum += f1 + f2;
    }
    ru[0] = h * sum + ro[0] / 3;
    for (int j = 1; j <= i; ++j) {
      s *= 9;
      ru[j] = (s * ru[j - 1] - ro[j - 1]) / (s - 1);
    }
    final double digit;
    final area = (totalMagnitude / calls) * span;
    if (area < 1e-100) {
      digit = precision.leastSignificantDigit(1).toDouble();
    } else {
      digit = log10(area) - 1 + precision.leastSignificantDigit(area);
      // I think log10(area).floor() is closer to what the 15C does, but
      // subtracting 1 instead makes it so the error scales smoothly, which
      // makes more sense to me.  We're so much faster than the real
      // calculator that being overly accurate doesn't hurt, so this is
      // a pretty conservative choice.
    }
    final double eps = fpow(10.0, digit.toDouble());
    final rt = ro;
    ro = ru;
    ru = rt;
    if (i > 1 && (ru[i - 1] - ro[i]).abs() <= eps * ro[i].abs() + eps) {
      break;
    }
    _lastEstimate = ro[i] * signResult;
  }
  final ok = i < maxIterations;
  if (!ok) {
    i--;
  }
  final err = ((ru[i - 1] - ro[i]) / ro[i]).abs();
  model.z = originalX;
  model.t = originalY;
  model.yF = err;
  model.xF = ro[i] * signResult;
  return true; // The 15C never gives CalculatorError on failure to converge
}