/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.credit.isdastandardmodel;
import java.util.Arrays;
/**
* Utility for combining and truncating ascending arrays of doubles. This is used for CDS pricing.
*/
public abstract class DoublesScheduleGenerator {
private static final double TOL = 1. / 730;
/**
* Combines the knot points on the yield and credit curves into a single (ordered) list of times strictly between the specified
* start and end. The start and end values are added at the beginning and end of the list. If two times are very close (defined as
* less than half a day - 1/730 years different) only the smaller value is kept (with the exception of the end value which takes
* precedence). <p>
* Since ISDACompliantCurve is piecewise constant in the forward rate, this makes the integrals that appear in CDS pricing (i.e.
* $$\int_0^T P(t) \frac{dQ(t)}{dt} dt$$ on the protection leg and $$\sum_{i=0}^{N-1}\int_{T_i}^{T_{i+1}} (t-T_i) P(t) \frac{dQ(t)}{dt} dt$$
* on the premium leg) analytic between the points in the list.
* @param start First time in the list
* @param end last time in the list
* @param yieldCurve The yield curve
* @param creditCurve The credit curve
* @return A list of times used to split CDS pricing integrals into analytic pieces.
*/
public static double[] getIntegrationsPoints(final double start, final double end, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve) {
return getIntegrationsPoints(start, end, yieldCurve.getKnotTimes(), creditCurve.getKnotTimes());
}
/**
* Combines two sets of numbers and return only the values strictly between the specified
* start and end. The start and end values are added at the beginning and end of the list. If two times are very close (defined as
* less than half a day - 1/730 years different) only the smaller value is kept (with the exception of the end value which takes
* precedence).
* @param start First time in the list
* @param end last time in the list
* @param setA the first set
* @param setB the second
* @return Combined list between first and last value
*/
public static double[] getIntegrationsPoints(final double start, final double end, final double[] setA, final double[] setB) {
final double[] set1 = truncateSetExclusive(start, end, setA);
final double[] set2 = truncateSetExclusive(start, end, setB);
final int n1 = set1.length;
final int n2 = set2.length;
final int n = n1 + n2;
final double[] set = new double[n];
System.arraycopy(set1, 0, set, 0, n1);
System.arraycopy(set2, 0, set, n1, n2);
Arrays.sort(set);
final double[] temp = new double[n + 2];
temp[0] = start;
int pos = 0;
for (int i = 0; i < n; i++) {
if (different(temp[pos], set[i])) {
temp[++pos] = set[i];
}
}
if (different(temp[pos], end)) {
pos++;
}
temp[pos] = end; // add the end point (this may replace the last entry in temp if that is not significantly different)
final int resLength = pos + 1;
if (resLength == n + 2) {
return temp; // everything was unique
}
final double[] res = new double[resLength];
System.arraycopy(temp, 0, res, 0, resLength);
return res;
}
/**
* Combines two sets of numbers (times) and return the unique sorted set.
* If two times are very close (defined as less than half a day - 1/730 years different) only the smaller value is kept.
* @param set1 The first set
* @param set2 The second set
* @return The unique sorted set, set1 U set2
*/
public static double[] combineSets(final double[] set1, final double[] set2) {
final int n1 = set1.length;
final int n2 = set2.length;
final int n = n1 + n2;
final double[] set = new double[n];
System.arraycopy(set1, 0, set, 0, n1);
System.arraycopy(set2, 0, set, n1, n2);
Arrays.sort(set);
final double[] temp = new double[n];
temp[0] = set[0];
int pos = 0;
for (int i = 1; i < n; i++) {
if (different(temp[pos], set[i])) {
temp[++pos] = set[i];
}
}
final int resLength = pos + 1;
if (resLength == n) {
return temp; // everything was unique
}
final double[] res = new double[resLength];
System.arraycopy(temp, 0, res, 0, resLength);
return res;
}
private static boolean different(final double a, final double b) {
return Math.abs(a - b) > TOL;
}
/**
* Truncates an array of doubles so it contains only the values between lower and upper, plus the values of lower and higher (as the first
* and last entry respectively). If no values met this criteria an array just containing lower and upper is returned. If the first (last)
* entry of set is too close to lower (upper) - defined by TOL - the first (last) entry of set is replaced by lower (upper).
* @param lower The lower value
* @param upper The upper value
* @param set The numbers must be sorted in ascending order
* @return the truncated array
*/
public static double[] truncateSetInclusive(final double lower, final double upper, final double[] set) {
// this is private, so assume inputs are fine
final double[] temp = truncateSetExclusive(lower, upper, set);
final int n = temp.length;
if (n == 0) {
return new double[] {lower, upper };
}
final boolean addLower = different(lower, temp[0]);
final boolean addUpper = different(upper, temp[n - 1]);
if (!addLower && !addUpper) { // replace first and last entries of set
temp[0] = lower;
temp[n - 1] = upper;
return temp;
}
final int m = n + (addLower ? 1 : 0) + (addUpper ? 1 : 0);
final double[] res = new double[m];
System.arraycopy(temp, 0, res, (addLower ? 1 : 0), n);
res[0] = lower;
res[m - 1] = upper;
return res;
}
/**
* Truncates an array of doubles so it contains only the values between lower and upper exclusive. If no values met this criteria an
* empty array is returned
* @param lower The lower value
* @param upper The upper value
* @param set The numbers must be sorted in ascending order
* @return the truncated array
*/
public static double[] truncateSetExclusive(final double lower, final double upper, final double[] set) {
// this is private, so assume inputs are fine
final int n = set.length;
if (upper < set[0] || lower > set[n - 1]) {
return new double[0];
}
int lIndex;
if (lower < set[0]) {
lIndex = 0;
} else {
final int temp = Arrays.binarySearch(set, lower);
lIndex = temp >= 0 ? temp + 1 : -(temp + 1);
}
int uIndex;
if (upper > set[n - 1]) {
uIndex = n;
} else {
final int temp = Arrays.binarySearch(set, lIndex, n, upper);
uIndex = temp >= 0 ? temp : -(temp + 1);
}
final int m = uIndex - lIndex;
if (m == n) {
return set;
}
final double[] trunc = new double[m];
System.arraycopy(set, lIndex, trunc, 0, m);
return trunc;
}
public static double[] leftTruncate(final double lower, final double[] set) {
final int n = set.length;
if (n == 0) {
return set;
}
if (lower < set[0]) {
return set;
}
if (lower >= set[n - 1]) {
return new double[0];
}
final int index = Arrays.binarySearch(set, lower);
final int chop = index >= 0 ? index + 1 : -(index + 1);
double[] res;
if (chop == 0) {
res = set;
} else {
res = new double[n - chop];
System.arraycopy(set, chop, res, 0, n - chop);
}
return res;
}
}