/**
   * This calculates the sensitivity of the present value (PV) to the lognormal Black implied
   * volatities at the knot points of the surface.
   *
   * <p>The return format is a DoubleMatrix2D with rows equal to the total number of maturities and
   * columns equal to the number of strikes.
   *
   * <p>Note - the change of the surface due to the movement of a single node is
   * interpolator-dependent, so an instrument may have non-local sensitivity
   *
   * @param swap the VarianceSwap
   * @param market the VarianceSwapDataBundle
   * @param shift Size of shift made in centered-finite difference approximation. e.g. 1% would be
   *     0.01, and 1bp 0.0001
   * @return A NodalDoublesSurface with same axes as market.getVolatilitySurface(). Contains
   *     currency amount per unit amount change in the black volatility of each node
   */
  public NodalDoublesSurface calcBlackVegaForEntireSurface(
      final VarianceSwap swap, final VarianceSwapDataBundle market, final double shift) {
    Validate.notNull(swap, "null VarianceSwap");
    Validate.notNull(market, "null VarianceSwapDataBundle");

    // Unpack market data
    final Surface<Double, Double, Double> surface = market.getVolatilitySurface().getSurface();
    Validate.isTrue(
        surface instanceof InterpolatedDoublesSurface,
        "Currently will only accept a Equity VolatilitySurfaces based on an InterpolatedDoublesSurface");

    final InterpolatedDoublesSurface blackSurf = (InterpolatedDoublesSurface) surface;
    final Double[] maturities = blackSurf.getXData();
    final Double[] strikes = blackSurf.getYData();
    final int nNodes = maturities.length;
    Validate.isTrue(nNodes == strikes.length);

    // Bump and reprice
    final Double[] vegas = new Double[nNodes];
    for (int j = 0; j < nNodes; j++) {
      vegas[j] = calcBlackVegaForSinglePoint(swap, market, maturities[j], strikes[j], shift);
    }

    return new NodalDoublesSurface(maturities, strikes, vegas);
  }
  /**
   * This calculates the sensitivity of the present value (PV) to the continuously-compounded
   * discount rates at the knot points of the funding curve.
   *
   * <p>The return format is a DoubleMatrix1D (i.e. a vector) with length equal to the total number
   * of knots in the curve
   *
   * <p>The change of a curve due to the movement of a single knot is interpolator-dependent, so an
   * instrument can have sensitivity to knots at times beyond its maturity
   *
   * @param swap the VarianceSwap
   * @param market the VarianceSwapDataBundle
   * @return A DoubleMatrix1D containing bucketed delta in order and length of
   *     market.getDiscountCurve(). Currency amount per unit amount change in discount rate
   */
  public DoubleMatrix1D calcDeltaBucketed(
      final VarianceSwap swap, final VarianceSwapDataBundle market) {
    Validate.notNull(swap, "null VarianceSwap");
    Validate.notNull(market, "null VarianceSwapDataBundle");

    // We know that the VarianceSwap only has true sensitivity to one maturity on one curve.
    // A function written for interestRate sensitivities spreads this sensitivity across yield nodes
    // NodeSensitivityCalculator.curveToNodeSensitivities(curveSensitivities, interpolatedCurves)

    // 2nd arg = LinkedHashMap<String, YieldAndDiscountCurve> interpolatedCurves
    final YieldAndDiscountCurve discCrv = market.getDiscountCurve();
    final String discCrvName = discCrv.getCurve().getName();
    final YieldCurveBundle interpolatedCurves = new YieldCurveBundle();
    interpolatedCurves.setCurve(discCrvName, discCrv);

    // 1st arg = Map<String, List<DoublesPair>> curveSensitivities = <curveName,
    // List<(maturity,sensitivity)>>
    final double settlement = swap.getTimeToSettlement();
    final Double sens = calcDiscountRateSensitivity(swap, market);
    final Map<String, List<DoublesPair>> curveSensitivities =
        new HashMap<String, List<DoublesPair>>();
    curveSensitivities.put(discCrvName, Lists.newArrayList(new DoublesPair(settlement, sens)));

    final NodeSensitivityCalculator distributor =
        PresentValueNodeSensitivityCalculator.getDefaultInstance();
    return distributor.curveToNodeSensitivities(curveSensitivities, interpolatedCurves);
  }
  /**
   * This calculates the sensitivity of the present value (PV) to a unit move in the forward. The
   * volatility surface remains unchanged.
   *
   * @param swap the VarianceSwap
   * @param market the VarianceSwapDataBundle
   * @param relShift Relative size of shift made in centered-finite difference approximation.
   * @return A Double. Currency amount per unit amount change in the black volatility
   */
  @SuppressWarnings({})
  public Double calcForwardSensitivity(
      final VarianceSwap swap, final VarianceSwapDataBundle market, final double relShift) {
    Validate.notNull(swap, "null VarianceSwap");
    Validate.notNull(market, "null VarianceSwapDataBundle");

    final VarianceSwapPresentValueCalculator pricer =
        VarianceSwapPresentValueCalculator.getInstance();

    // Shift UP
    VarianceSwapDataBundle bumpedMarket =
        new VarianceSwapDataBundle(
            market.getVolatilitySurface(),
            market.getDiscountCurve(),
            market.getForwardCurve().withFractionalShift(relShift));
    final double pvUp = pricer.visitVarianceSwap(swap, bumpedMarket);

    // Shift Down
    bumpedMarket =
        new VarianceSwapDataBundle(
            market.getVolatilitySurface(),
            market.getDiscountCurve(),
            market.getForwardCurve().withFractionalShift(-relShift));
    final double pvDown = pricer.visitVarianceSwap(swap, bumpedMarket);

    final double t = swap.getTimeToSettlement();
    final double fwd = market.getForwardCurve().getForward(t);
    // Centered-difference result
    return (pvUp - pvDown) / 2.0 / relShift / fwd;
  }
  /**
   * This calculates the sensitivity of the present value (PV) to the lognormal Black implied
   * volatities at the knot points of the surface.
   *
   * <p>Note - the change of the surface due to the movement of a single node is
   * interpolator-dependent, so an instrument may have non-local sensitivity
   *
   * @param swap the VarianceSwap
   * @param market the VarianceSwapDataBundle
   * @param shift Size of shift made in centered-finite difference approximation. e.g. 1% would be
   *     0.01, and 1bp 0.0001
   * @return A Double. Currency amount per unit amount change in the black volatility
   */
  @SuppressWarnings({})
  public Double calcBlackVegaParallel(
      final VarianceSwap swap, final VarianceSwapDataBundle market, final double shift) {
    Validate.notNull(swap, "null VarianceSwap");
    Validate.notNull(market, "null VarianceSwapDataBundle");

    final VarianceSwapPresentValueCalculator pricer =
        VarianceSwapPresentValueCalculator.getInstance();

    // Parallel shift UP
    final BlackVolatilitySurface<?> upSurface =
        market.getVolatilitySurface().withShift(shift, true);
    final double pvUp =
        pricer.visitVarianceSwap(
            swap,
            new VarianceSwapDataBundle(
                upSurface, market.getDiscountCurve(), market.getForwardCurve()));

    // Parallel shift DOWN
    final BlackVolatilitySurface<?> downSurface =
        market.getVolatilitySurface().withShift(-shift, true);
    final double pvDown =
        pricer.visitVarianceSwap(
            swap,
            new VarianceSwapDataBundle(
                downSurface, market.getDiscountCurve(), market.getForwardCurve()));

    // Centered-difference result
    return (pvUp - pvDown) / (2.0 * shift);
  }
  /**
   * Calculates the sensitivity of the present value (PV) to a change in the funding rate from
   * valuation to settlement. Also know as PVBP and DV01, though note this return per UNIT change in
   * rate. calcPV01 returns per basis point change in rates.
   *
   * <p>
   *
   * <p>Rates enter the pricing of a VarianceSwap in two places: in the discounting and forward
   * projection.
   *
   * <p>The presentValue has been structured such that the form of the PV = Z(t,T) * FwdPrice(t,T)
   * with Z a zero coupon bond, and t and T the valuation and settlement times respectively. The
   * form of our discounting rates is such that Z(t,T) = exp[- R(t,T) * (T-t)], hence dZ/dR =
   * -(T-t)*Z(t,T) and d(PV)/dR = PV * dZ/dR The forward's dependence on the discounting rate is
   * similar to the zero coupon bonds, but of opposite sign, dF/dR = (T-t)*F(t,T)
   *
   * @param swap the VarianceSwap
   * @param market the VarianceSwapDataBundle
   * @param shift Relative size of shift made in centered-finite difference approximation.
   * @return A Double in the currency, deriv.getCurrency(). Currency amount per unit amount change
   *     in discount rate
   */
  @SuppressWarnings({})
  public Double calcDiscountRateSensitivity(
      final VarianceSwap swap, final VarianceSwapDataBundle market, final double shift) {
    Validate.notNull(market);
    Validate.notNull(swap);
    // Sensitivity from the discounting

    final VarianceSwapStaticReplication pricer = new VarianceSwapStaticReplication();
    final double pv = pricer.presentValue(swap, market);
    final double timeToSettlement = swap.getTimeToSettlement();

    // Sensitivity from forward projection
    final double fwdSens = calcForwardSensitivity(swap, market, shift);
    final double fwd = market.getForwardCurve().getForward(timeToSettlement);

    return timeToSettlement * (fwd * fwdSens - pv);
  }
  /**
   * Compute the price sensitivity to a shift of the Black volatility at a given maturity and
   * strike.
   *
   * <p>Note - the change of the surface due to the movement of a single node is
   * interpolator-dependent, so an instrument may have non-local sensitivity.
   *
   * <p>Important!!! If the <i>(x, y)</i> value(s) of the shift(s) are not in the nodal points of
   * the original surface, they are added (with shift) to the nodal points of the new surface.
   *
   * @param swap the VarianceSwap
   * @param market the VarianceSwapDataBundle
   * @param maturity a double in same unit as VolatilitySurface
   * @param strike a double in same unit as VolatilitySurface
   * @param shift Size of shift made in centered-finite difference approximation. e.g. 1% would be
   *     0.01, and 1bp 0.0001
   * @return Currency amount per unit amount change in the black volatility at the point provided
   */
  public double calcBlackVegaForSinglePoint(
      final VarianceSwap swap,
      final VarianceSwapDataBundle market,
      final double maturity,
      final double strike,
      final double shift) {

    final VarianceSwapPresentValueCalculator pricer =
        VarianceSwapPresentValueCalculator.getInstance();

    final Surface<Double, Double, Double> surface = market.getVolatilitySurface().getSurface();
    Validate.isTrue(
        surface instanceof InterpolatedDoublesSurface,
        "Currently will only accept a Equity VolatilitySurfaces based on an InterpolatedDoublesSurface");

    final InterpolatedDoublesSurface blackSurf = (InterpolatedDoublesSurface) surface;
    final InterpolatedSurfaceAdditiveShiftFunction volShifter =
        new InterpolatedSurfaceAdditiveShiftFunction();

    // shift UP
    final InterpolatedDoublesSurface bumpedVolUp =
        volShifter.evaluate(blackSurf, maturity, strike, shift);
    VarianceSwapDataBundle bumpedMarket =
        new VarianceSwapDataBundle(
            market.getVolatilitySurface().withSurface(bumpedVolUp),
            market.getDiscountCurve(),
            market.getForwardCurve());
    final double pvUp = pricer.visitVarianceSwap(swap, bumpedMarket);

    // shift DOWN
    final InterpolatedDoublesSurface bumpedVolDown =
        volShifter.evaluate(blackSurf, maturity, strike, -shift);
    bumpedMarket =
        new VarianceSwapDataBundle(
            market.getVolatilitySurface().withSurface(bumpedVolDown),
            market.getDiscountCurve(),
            market.getForwardCurve());
    final double pvDown = pricer.visitVarianceSwap(swap, bumpedMarket);

    // Centered-difference result
    return (pvUp - pvDown) / (2.0 * shift);
  }