class JohnsonSUDeltaGammaVaRCalculator implements VaRCalculator<NormalVaRParameters, T> {

  @SuppressWarnings("unchecked")
  @Override
  public VaRCalculationResult evaluate(final NormalVaRParameters parameters, final T... data) {
    ArgumentChecker.notNull(parameters, "parameters");
    ArgumentChecker.notNull(data, "data");
    // TODO if skewness is positive then need to fit to -x and take from upper tail of distribution
    final double k = _kurtosisCalculator.evaluate(data);
    if (k < 0) {
      throw new IllegalArgumentException("Johnson SU distribution cannot be used for data with negative excess kurtosis");
    }
    final double mult = parameters.getTimeScaling();
    final double z = parameters.getZ();
    final double t = _skewCalculator.evaluate(data);
    final double mu = _meanCalculator.evaluate(data) * mult * mult;
    final double sigma = _stdCalculator.evaluate(data) * mult;
    if (t == 0 && k == 0) {
      return new VaRCalculationResult(z * sigma - mu, null);
    }
    final double wUpper = Math.sqrt(Math.sqrt(2 * (k + 2)) - 1);
    final double wLower = Math.max(getW0(t), getW1(k + 3));
    final double w = ROOT_FINDER.getRoot(getFunction(t, k), wLower, wUpper);
    final double w2 = w * w;
    final double l = 4 + 2 * (w2 - (k + 6) / (w2 + 2 * w + 3));
    if (l < 0) {
      throw new IllegalArgumentException("Tried to find the square root of a negative number");
    }
    final double m = -2 + Math.sqrt(l);
    if (m == 0 || (m < 0 && w > -1) || (m > 0 && w < -1) || (w - 1 - m) < 0) {
      throw new IllegalArgumentException("Invalid parameters");
    }
    final double sign = Math.signum(t);
    final double u = Math.sqrt(Math.log(w));
    final double v = Math.sqrt((w + 1) * (w - 1 - m) / (2 * w * m));
    final double omega = -sign * TrigonometricFunctionUtils.asinh(v);
    final double delta = 1. / u;
    final double gamma = omega / u;
    final double lambda = sigma / (w - 1) * Math.sqrt(2 * m / (w + 1));
    final double ksi = mu - sign * sigma * Math.sqrt(w - 1 - m) / (w - 1);
    return new VaRCalculationResult(-lambda * Math.sinh((-z - gamma) / delta) - ksi, null);
  }

  public  JohnsonSUDeltaGammaVaRCalculator(final Function<T, Double> meanCalculator, final Function<T, Double> stdCalculator,
      final Function<T, Double> skewCalculator, final Function<T, Double> kurtosisCalculator);

}