@Test
 /** Tests the adjoint implementation (with computation of the derivatives). */
 public void adjointPrice() {
   final double[] derivatives = new double[5];
   final double priceDI =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
   final double priceDIAdjoint =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_DOWN_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   assertEquals("Black single barrier: Adjoint price Down In", priceDI, priceDIAdjoint, 1.0E-10);
   final double priceDO =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
   final double priceDOAdjoint =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_DOWN_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   assertEquals("Black single barrier: Adjoint price Down Out", priceDO, priceDOAdjoint, 1.0E-10);
   final double priceUI =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
   final double priceUIAdjoint =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_UP_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   assertEquals("Black single barrier: Adjoint price Up In", priceUI, priceUIAdjoint, 1.0E-10);
   final double priceUO =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
   final double priceUOAdjoint =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_UP_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   assertEquals("Black single barrier: Adjoint price Up Out", priceUO, priceUOAdjoint, 1.0E-10);
 }
 @Test
 /** Tests the adjoint implementation (with computation of the derivatives). */
 public void adjointDerivatives() {
   final double shiftSpot = 0.001;
   final double shiftRate = 1.0E-8;
   final double shiftCoC = 1.0E-8;
   final double shiftVol = 1.0E-8;
   final double[] derivatives = new double[5];
   // DOWN-IN
   final double priceDI =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_DOWN_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   final double priceDISpot =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_IN,
           REBATE,
           SPOT + shiftSpot,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint spot derivative - Down In",
       (priceDISpot - priceDI) / shiftSpot,
       derivatives[0],
       1.0E-5);
   final double priceDIRate =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM + shiftRate,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint rate derivative - Down In",
       (priceDIRate - priceDI) / shiftRate,
       derivatives[2],
       1.0E-5);
   final double priceDICoC =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY + shiftCoC,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Down In",
       (priceDICoC - priceDI) / shiftCoC,
       derivatives[3],
       1.0E-5);
   final double priceDIVol =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY + shiftVol);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Down In",
       (priceDIVol - priceDI) / shiftVol,
       derivatives[4],
       1.0E-4);
   // DOWN-OUT
   final double priceDO =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_DOWN_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   final double priceDOSpot =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_OUT,
           REBATE,
           SPOT + shiftSpot,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint spot derivative - Down Out",
       (priceDOSpot - priceDO) / shiftSpot,
       derivatives[0],
       2.0E-4);
   final double priceDORate =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM + shiftRate,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint rate derivative - Down Out",
       (priceDORate - priceDO) / shiftRate,
       derivatives[2],
       1.0E-5);
   final double priceDOCoC =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY + shiftCoC,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Down Out",
       (priceDOCoC - priceDO) / shiftCoC,
       derivatives[3],
       1.0E-4);
   final double priceDOVol =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_DOWN_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY + shiftVol);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Down Out",
       (priceDOVol - priceDO) / shiftVol,
       derivatives[4],
       1.0E-4);
   // UP-IN
   final double priceUI =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_UP_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   final double priceUISpot =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_IN,
           REBATE,
           SPOT + shiftSpot,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint spot derivative - Up In",
       (priceUISpot - priceUI) / shiftSpot,
       derivatives[0],
       2.0E-4);
   final double priceUIRate =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM + shiftRate,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint rate derivative - Up In",
       (priceUIRate - priceUI) / shiftRate,
       derivatives[2],
       1.0E-5);
   final double priceUICoC =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY + shiftCoC,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Up In",
       (priceUICoC - priceUI) / shiftCoC,
       derivatives[3],
       1.0E-4);
   final double priceUIVol =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_IN,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY + shiftVol);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Up In",
       (priceUIVol - priceUI) / shiftVol,
       derivatives[4],
       1.0E-5);
   // UP-OUT
   final double priceUO =
       BARRIER_FUNCTION.getPriceAdjoint(
           VANILLA_CALL_K100,
           BARRIER_UP_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY,
           derivatives);
   final double priceUOSpot =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_OUT,
           REBATE,
           SPOT + shiftSpot,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint spot derivative - Up Out",
       (priceUOSpot - priceUO) / shiftSpot,
       derivatives[0],
       1.0E-4);
   final double priceUORate =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM + shiftRate,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint rate derivative - Up Out",
       (priceUORate - priceUO) / shiftRate,
       derivatives[2],
       1.0E-5);
   final double priceUOCoC =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY + shiftCoC,
           RATE_DOM,
           VOLATILITY);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Up Out",
       (priceUOCoC - priceUO) / shiftCoC,
       derivatives[3],
       1.0E-5);
   final double priceUOVol =
       BARRIER_FUNCTION.getPrice(
           VANILLA_CALL_K100,
           BARRIER_UP_OUT,
           REBATE,
           SPOT,
           COST_OF_CARRY,
           RATE_DOM,
           VOLATILITY + shiftVol);
   assertEquals(
       "Black single barrier: Adjoint cost-of-carry derivative - Up Out",
       (priceUOVol - priceUO) / shiftVol,
       derivatives[4],
       2.0E-5);
 }
  @Test
  /**
   * Tests the 'In-Out Parity' condition: The price of a Knock-In plus a Knock-Out of arbitrary
   * barrier level must equal that of the underlying vanilla option + value of the rebate
   */
  public void impossibleToHitBarrierIsVanilla() {

    final Barrier veryLowKnockIn =
        new Barrier(KnockType.IN, BarrierType.DOWN, ObservationType.CONTINUOUS, 1e-6);
    final Barrier veryLowKnockOut =
        new Barrier(KnockType.OUT, BarrierType.DOWN, ObservationType.CONTINUOUS, 1e-6);
    final Barrier veryHighKnockIn =
        new Barrier(KnockType.IN, BarrierType.UP, ObservationType.CONTINUOUS, 1e6);
    final Barrier veryHighKnockOut =
        new Barrier(KnockType.OUT, BarrierType.UP, ObservationType.CONTINUOUS, 1e6);

    final double pxRebate = DF_DOM * REBATE;
    final Function1D<BlackFunctionData, Double> fcnVanillaCall =
        BLACK_FUNCTION.getPriceFunction(VANILLA_CALL_K100);
    final double pxVanillaCall = fcnVanillaCall.evaluate(DATA_BLACK);

    // KnockIn's with impossible to reach barrier's are guaranteed to pay the rebate at maturity
    final double pxDownInPut =
        BARRIER_FUNCTION.getPrice(
            VANILLA_PUT_K100, veryLowKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
    assertTrue("VeryLowKnockInBarrier doesn't match rebate", pxDownInPut / pxRebate - 1 < 1e-6);
    final double pxDownInCall =
        BARRIER_FUNCTION.getPrice(
            VANILLA_CALL_K100, veryLowKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
    assertTrue("VeryLowKnockInBarrier doesn't match rebate", pxDownInCall / pxRebate - 1 < 1e-6);
    final double pxUpInCall =
        BARRIER_FUNCTION.getPrice(
            VANILLA_CALL_K100, veryHighKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
    assertTrue("VeryHighKnockInBarrier doesn't match rebate", pxUpInCall / pxRebate - 1 < 1e-6);

    // KnockOut's with impossible to reach barrier's are guaranteed to pay the value of the
    // underlying vanilla
    final double pxDownOutCall =
        BARRIER_FUNCTION.getPrice(
            VANILLA_CALL_K100, veryLowKnockOut, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY);
    assertTrue(
        "VeryLowKnockInBarrier doesn't match rebate",
        Math.abs(pxDownOutCall / pxVanillaCall - 1) < 1e-6);

    // Derivatives
    final double[] derivs = new double[5];
    BARRIER_FUNCTION.getPriceAdjoint(
        VANILLA_CALL_K100,
        veryLowKnockIn,
        REBATE,
        SPOT,
        COST_OF_CARRY,
        RATE_DOM,
        VOLATILITY,
        derivs);
    assertTrue(
        "Impossible KnockIn: rate sens is incorrect",
        derivs[2] / Math.abs((-1 * EXPIRY_TIME * DF_DOM * REBATE) - 1) < 1e-6);
    assertEquals(
        "Impossible KnockIn: Encountered derivative, other than d/dr, != 0",
        0.0,
        derivs[0] + derivs[1] + derivs[3] + derivs[4],
        1.0e-6);

    BARRIER_FUNCTION.getPriceAdjoint(
        VANILLA_CALL_K100,
        veryHighKnockIn,
        REBATE,
        SPOT,
        COST_OF_CARRY,
        RATE_DOM,
        VOLATILITY,
        derivs);
    assertTrue(
        "Impossible KnockIn: rate sens is incorrect",
        derivs[2] / Math.abs((-1 * EXPIRY_TIME * DF_DOM * REBATE) - 1) < 1e-6);
    assertEquals(
        "Impossible KnockIn: Encountered derivative, other than d/dr, != 0",
        0.0,
        derivs[0] + derivs[1] + derivs[3] + derivs[4],
        1.0e-6);

    // Barrier: [0] spot, [1] strike, [2] rate, [3] cost-of-carry, [4] volatility.
    BARRIER_FUNCTION.getPriceAdjoint(
        VANILLA_CALL_K100,
        veryLowKnockOut,
        REBATE,
        SPOT,
        COST_OF_CARRY,
        RATE_DOM,
        VOLATILITY,
        derivs);
    // Vanilla: [0] the price, [1] the derivative with respect to the forward, [2] the derivative
    // with respect to the volatility and [3] the derivative with respect to the strike.
    final double[] vanillaDerivs = BLACK_FUNCTION.getPriceAdjoint(VANILLA_CALL_K100, DATA_BLACK);
    assertEquals(
        "Impossible KnockOut: Vega doesn't match vanilla", vanillaDerivs[2], derivs[4], 1e-6);
    assertEquals(
        "Impossible KnockOut: Dual Delta (d/dK) doesn't match vanilla",
        vanillaDerivs[3],
        derivs[1],
        1e-6);
    assertEquals(
        "Impossible KnockOut: Delta doesn't match vanilla",
        vanillaDerivs[1] * DF_FOR / DF_DOM,
        derivs[0],
        1e-6);

    BARRIER_FUNCTION.getPriceAdjoint(
        VANILLA_CALL_K100,
        veryHighKnockOut,
        REBATE,
        SPOT,
        COST_OF_CARRY,
        RATE_DOM,
        VOLATILITY,
        derivs);
    assertEquals(
        "Impossible KnockOut: Vega doesn't match vanilla", vanillaDerivs[2], derivs[4], 1e-6);
    assertEquals(
        "Impossible KnockOut: Dual Delta (d/dK) doesn't match vanilla",
        vanillaDerivs[3],
        derivs[1],
        1e-6);
    assertEquals(
        "Impossible KnockOut: Delta doesn't match vanilla",
        vanillaDerivs[1] * DF_FOR / DF_DOM,
        derivs[0],
        1e-6);
  }