class SmileInterpolatorSABRWithExtrapolation extends SmileInterpolatorSABR {

  @Override
  public Function1D<Double, Double> getVolatilityFunction(final double forward, final double[] strikes,
      final double expiry, final double[] impliedVols) {
    ArgumentChecker.notNull(strikes, "strikes");
    ArgumentChecker.notNull(impliedVols, "impliedVols");
    ArgumentChecker.isTrue(strikes.length == impliedVols.length, "strikes and impliedVols should have the same length");

    final int nStrikes = strikes.length;
    final double cutOffStrikeLow = strikes[0];
    final double cutOffStrikeHigh = strikes[nStrikes - 1];

    SABRFormulaData sabrDataLow;
    SABRFormulaData sabrDataHigh;
    final List<SABRFormulaData> modelParams;
    final int n;

    List<SABRFormulaData> modelParamsTmp;
    int nTmp;
    try {
      modelParamsTmp = getFittedModelParameters(forward, strikes, expiry, impliedVols);
      nTmp = strikes.length;
      int ref = Math.max(0, nTmp - 3);
      sabrDataLow = modelParamsTmp.get(0);
      sabrDataHigh = modelParamsTmp.get(ref);
    } catch (final Exception e) { //try global fit if local fit failed
      nTmp = 1;
      modelParamsTmp = getFittedModelParametersGlobal(forward, strikes, expiry, impliedVols);
      sabrDataLow = modelParamsTmp.get(0);
      sabrDataHigh = modelParamsTmp.get(0);
    }
    modelParams = modelParamsTmp;
    n = nTmp;

    final Function1D<Double, Double> interpFunc;
    if (n == 1) {
      interpFunc = new Function1D<Double, Double>() {
        @SuppressWarnings("synthetic-access")
        @Override
        public Double evaluate(final Double strike) {
          final EuropeanVanillaOption option = new EuropeanVanillaOption(strike, expiry, true);
          final Function1D<SABRFormulaData, Double> volFunc = getModel().getVolatilityFunction(option, forward);
          return volFunc.evaluate(modelParams.get(0));
        }
      };
    } else {
      interpFunc = getVolatilityFunctionFromModelParameters(forward, strikes, expiry, modelParams);
    }

    final Function1D<Double, Double> extrapFunc = _extrapolationFunctionProvider.getExtrapolationFunction(sabrDataLow,
        sabrDataHigh, getModel(), forward, expiry, cutOffStrikeLow, cutOffStrikeHigh);

    return new Function1D<Double, Double>() {
      @Override
      public Double evaluate(final Double strike) {
        ArgumentChecker.notNegative(strike, "strike");
        if (strike < cutOffStrikeLow) {
          return extrapFunc.evaluate(strike);
        }
        if (strike > cutOffStrikeHigh) {
          return extrapFunc.evaluate(strike);
        }
        return interpFunc.evaluate(strike);
      }
    };
  }

  public  SmileInterpolatorSABRWithExtrapolation(
      final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider);
  public  SmileInterpolatorSABRWithExtrapolation(final VolatilityFunctionProvider<SABRFormulaData> formula,
      final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider);
  public  SmileInterpolatorSABRWithExtrapolation(final int seed,
      final VolatilityFunctionProvider<SABRFormulaData> formula, final double beta,
      final WeightingFunction weightFunction, final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider);

}