### Question:
CurveGammaCalculator { public CurrencyParameterSensitivity calculateSemiParallelGamma( Curve curve, Currency curveCurrency, Function<Curve, CurrencyParameterSensitivity> sensitivitiesFn) { Delta deltaShift = new Delta(curve, sensitivitiesFn); Function<DoubleArray, DoubleMatrix> gammaFn = fd.differentiate(deltaShift); DoubleArray gamma = gammaFn.apply(DoubleArray.filled(1)).column(0); return curve.createParameterSensitivity(curveCurrency, gamma); } private  CurveGammaCalculator(FiniteDifferenceType fdType, double shift); static CurveGammaCalculator ofForwardDifference(double shift); static CurveGammaCalculator ofCentralDifference(double shift); static CurveGammaCalculator ofBackwardDifference(double shift); CrossGammaParameterSensitivities calculateCrossGammaIntraCurve(
      RatesProvider ratesProvider,
      Function<ImmutableRatesProvider, CurrencyParameterSensitivities> sensitivitiesFn); CrossGammaParameterSensitivities calculateCrossGammaIntraCurve(
      LegalEntityDiscountingProvider ratesProvider,
      Function<ImmutableLegalEntityDiscountingProvider, CurrencyParameterSensitivities> sensitivitiesFn); CrossGammaParameterSensitivities calculateCrossGammaCrossCurve(
      RatesProvider ratesProvider,
      Function<ImmutableRatesProvider, CurrencyParameterSensitivities> sensitivitiesFn); CurrencyParameterSensitivity calculateSemiParallelGamma(
      Curve curve,
      Currency curveCurrency,
      Function<Curve, CurrencyParameterSensitivity> sensitivitiesFn); static final CurveGammaCalculator DEFAULT; }### Answer:
@Test public void semiParallelGammaValue() { ImmutableRatesProvider provider = SINGLE; Currency curveCurrency = SINGLE_CURRENCY; DoubleArray y = USD_SINGLE_CURVE.getYValues(); int nbNode = y.size(); DoubleArray gammaExpected = DoubleArray.of(nbNode, i -> { double[][][] yBumped = new double[2][2][nbNode]; double[][] pv = new double[2][2]; for (int pmi = 0; pmi < 2; pmi++) { for (int pmP = 0; pmP < 2; pmP++) { yBumped[pmi][pmP] = y.toArray(); yBumped[pmi][pmP][i] += (pmi == 0 ? 1.0 : -1.0) * FD_SHIFT; for (int j = 0; j < nbNode; j++) { yBumped[pmi][pmP][j] += (pmP == 0 ? 1.0 : -1.0) * FD_SHIFT; } Curve curveBumped = USD_SINGLE_CURVE.withYValues(DoubleArray.copyOf(yBumped[pmi][pmP])); ImmutableRatesProvider providerBumped = provider.toBuilder() .discountCurves(provider.getDiscountCurves().keySet().stream() .collect(toImmutableMap(Function.identity(), k -> curveBumped))) .indexCurves(provider.getIndexCurves().keySet().stream() .collect(toImmutableMap(Function.identity(), k -> curveBumped))) .build(); pv[pmi][pmP] = PRICER_SWAP.presentValue(SWAP, providerBumped).getAmount(USD).getAmount(); } } return (pv[1][1] - pv[1][0] - pv[0][1] + pv[0][0]) / (4 * FD_SHIFT * FD_SHIFT); }); CurrencyParameterSensitivity sensitivityComputed = GAMMA_CAL.calculateSemiParallelGamma( USD_SINGLE_CURVE, curveCurrency, c -> buildSensitivities(c, provider)); assertThat(sensitivityComputed.getMarketDataName()).isEqualTo(USD_SINGLE_CURVE.getName()); DoubleArray gammaComputed = sensitivityComputed.getSensitivity(); assertThat(gammaComputed.equalWithTolerance(gammaExpected, TOLERANCE_GAMMA)).isTrue(); }