public MultipleCurrencyAmount presentValue(final SwaptionPhysicalFixedIbor swaption, final LiborMarketModelDisplacedDiffusionProviderInterface lmmData) {
ArgumentChecker.notNull(swaption, "Swaption");
ArgumentChecker.notNull(lmmData, "LMM provider");
final Currency ccy = swaption.getCurrency();
final MulticurveProviderInterface multicurves = lmmData.getMulticurveProvider();
final LiborMarketModelDisplacedDiffusionParameters parameters = lmmData.getLMMParameters();
// 1. Swaption CFE preparation
final AnnuityPaymentFixed cfe = swaption.getUnderlyingSwap().accept(CFEC, multicurves);
final int nbCFInit = cfe.getNumberOfPayments();
final double multFact = Math.signum(cfe.getNthPayment(0).getAmount());
final boolean isCall = (cfe.getNthPayment(0).getAmount() < 0);
final double[] cftInit = new double[nbCFInit];
final double[] cfaInit = new double[nbCFInit];
for (int loopcf = 0; loopcf < nbCFInit; loopcf++) {
cftInit[loopcf] = cfe.getNthPayment(loopcf).getPaymentTime();
cfaInit[loopcf] = cfe.getNthPayment(loopcf).getAmount() * -multFact;
}
final double timeToExpiry = swaption.getTimeToExpiry();
// 2. Model data
final int nbFactor = parameters.getNbFactor();
final double[][] volLMM = parameters.getVolatility();
final double[] timeLMM = parameters.getIborTime();
// 3. Link cfe dates to lmm
final int[] indCFDate = new int[nbCFInit];
int indStart = nbCFInit - 1;
int indEnd = 0;
for (int loopcf = 0; loopcf < nbCFInit; loopcf++) {
indCFDate[loopcf] = Arrays.binarySearch(timeLMM, cftInit[loopcf]);
if (indCFDate[loopcf] < 0) {
if (timeLMM[-indCFDate[loopcf] - 1] - cftInit[loopcf] < TIME_TOLERANCE) {
indCFDate[loopcf] = -indCFDate[loopcf] - 1;
} else {
if (cftInit[loopcf] - timeLMM[-indCFDate[loopcf] - 2] < TIME_TOLERANCE) {
indCFDate[loopcf] = -indCFDate[loopcf] - 2;
} else {
Validate.isTrue(true, "Instrument time incompatible with LMM parametrisation");
}
}
}
if (indCFDate[loopcf] < indStart) {
indStart = indCFDate[loopcf];
}
if (indCFDate[loopcf] > indEnd) {
indEnd = indCFDate[loopcf];
}
}
final int nbCF = indEnd - indStart + 1;
final double[] cfa = new double[nbCF];
for (int loopcf = 0; loopcf < nbCFInit; loopcf++) {
cfa[indCFDate[loopcf] - indStart] = cfaInit[loopcf];
}
final double[] cft = new double[nbCF];
System.arraycopy(timeLMM, indStart, cft, 0, nbCF);
final double[] dfLMM = new double[nbCF];
for (int loopcf = 0; loopcf < nbCF; loopcf++) {
dfLMM[loopcf] = multicurves.getDiscountFactor(ccy, cft[loopcf]);
}
final double[][] gammaLMM = new double[nbCF - 1][nbFactor];
final double[] deltaLMM = new double[nbCF - 1];
System.arraycopy(parameters.getAccrualFactor(), indStart, deltaLMM, 0, nbCF - 1);
final double[] aLMM = new double[nbCF - 1];
System.arraycopy(parameters.getDisplacement(), indStart, aLMM, 0, nbCF - 1);
final double[] liborLMM = new double[nbCF - 1];
final double amr = parameters.getMeanReversion();
for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) {
gammaLMM[loopcf] = volLMM[indStart + loopcf];
liborLMM[loopcf] = (dfLMM[loopcf] / dfLMM[loopcf + 1] - 1.0d) / deltaLMM[loopcf];
}
// TODO: 4. cfe modification (for roller coasters)