/**
     * General method when you wish to compute the expected variance from a log-moneyness
     * parametrised surface to within a certain tolerance
     *
     * @param surface log-moneyness parametrised volatility surface
     * @return expected variance
     */
    @SuppressWarnings("synthetic-access")
    @Override
    public Double visitLogMoneyness(final BlackVolatilitySurfaceLogMoneyness surface) {
      final double atmVol = surface.getVolatilityForLogMoneyness(_t, 0.0);
      if (_t < 1e-4) {
        return atmVol * atmVol;
      }
      final double rootT = Math.sqrt(_t);
      final double invNorTol = NORMAL.getInverseCDF(_tol);

      final Function1D<Double, Double> integrand = getLogMoneynessIntegrand(surface);

      double putPart;
      if (_addResidual) {
        putPart = _integrator.integrate(integrand, Math.log(_lowStrikeCutoff / _f), 0.0);
        putPart += _residual;
      } else {
        final double l = invNorTol * atmVol * rootT; // initial estimate of lower limit
        putPart = _integrator.integrate(integrand, l, 0.0);
        double rem = integrand.evaluate(l);
        double error = rem / putPart;
        int step = 1;
        while (error > _tol) {
          putPart += _integrator.integrate(integrand, (step + 1) * l, step * l);
          step++;
          rem = integrand.evaluate((step + 1) * l);
          error = rem / putPart;
        }
        putPart +=
            rem; // add on the (very small) remainder estimate otherwise we'll always underestimate
                 // variance
      }

      final double u =
          _f * Math.exp(-invNorTol * atmVol * rootT); // initial estimate of upper limit
      double callPart = _integrator.integrate(integrand, 0.0, u);
      double rem = integrand.evaluate(u);
      double error = rem / callPart;
      int step = 1;
      while (error > _tol) {
        callPart += _integrator.integrate(integrand, step * u, (1 + step) * u);
        step++;
        rem = integrand.evaluate((1 + step) * u);
        error = rem / putPart;
      }
      // callPart += rem;
      // don't add on the remainder estimate as it is very conservative, and likely too large

      return 2 * (putPart + callPart) / _t;
    }
    @SuppressWarnings("synthetic-access")
    @Override
    public Double visitStrike(final BlackVolatilitySurfaceStrike surface) {

      final double atmVol = surface.getVolatility(_t, _f);
      if (_t < 1e-4) {
        return atmVol * atmVol;
      }
      final double rootT = Math.sqrt(_t);
      final double invNorTol = NORMAL.getInverseCDF(_tol);

      final Function1D<Double, Double> integrand = getStrikeIntegrand(surface);
      final Function1D<Double, Double> remainder =
          new Function1D<Double, Double>() {
            @Override
            public Double evaluate(final Double strike) {
              if (strike == 0) {
                return 0.0;
              }
              final boolean isCall = strike >= _f;
              final double vol = surface.getVolatility(_t, strike);
              final double otmPrice = BlackFormulaRepository.price(_f, strike, _t, vol, isCall);
              final double res = (isCall ? otmPrice / strike : otmPrice / 2 / strike);
              return res;
            }
          };

      double putPart;
      if (_addResidual) {
        putPart = _integrator.integrate(integrand, _lowStrikeCutoff, _f);
        putPart += _residual;
      } else {
        double l = _f * Math.exp(invNorTol * atmVol * rootT); // initial estimate of lower limit
        putPart = _integrator.integrate(integrand, l, _f);
        double rem = remainder.evaluate(l);
        double error = rem / putPart;
        while (error > _tol) {
          l /= 2.0;
          putPart += _integrator.integrate(integrand, l, 2 * l);
          rem = remainder.evaluate(l);
          error = rem / putPart;
        }
        putPart +=
            rem; // add on the (very small) remainder estimate otherwise we'll always underestimate
                 // variance
      }

      double u = _f * Math.exp(-invNorTol * atmVol * rootT); // initial estimate of upper limit
      double callPart = _integrator.integrate(integrand, _f, u);
      double rem = remainder.evaluate(u);
      double error = rem / callPart;
      while (error > _tol) {
        callPart += _integrator.integrate(integrand, u, 2 * u);
        u *= 2.0;
        rem = remainder.evaluate(u);
        error = rem / putPart;
      }
      // callPart += rem/2.0;
      // don't add on the remainder estimate as it is very conservative, and likely too large

      return 2 * (putPart + callPart) / _t;
    }