LocalMonotoneCkGenerator.java

  1. package org.drip.spline.pchip;

  2. /*
  3.  * -*- mode: java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  4.  */

  5. /*!
  6.  * Copyright (C) 2020 Lakshmi Krishnamurthy
  7.  * Copyright (C) 2019 Lakshmi Krishnamurthy
  8.  * Copyright (C) 2018 Lakshmi Krishnamurthy
  9.  * Copyright (C) 2017 Lakshmi Krishnamurthy
  10.  * Copyright (C) 2016 Lakshmi Krishnamurthy
  11.  * Copyright (C) 2015 Lakshmi Krishnamurthy
  12.  * Copyright (C) 2014 Lakshmi Krishnamurthy
  13.  * Copyright (C) 2013 Lakshmi Krishnamurthy
  14.  *
  15.  *  This file is part of DROP, an open-source library targeting analytics/risk, transaction cost analytics,
  16.  *      asset liability management analytics, capital, exposure, and margin analytics, valuation adjustment
  17.  *      analytics, and portfolio construction analytics within and across fixed income, credit, commodity,
  18.  *      equity, FX, and structured products. It also includes auxiliary libraries for algorithm support,
  19.  *      numerical analysis, numerical optimization, spline builder, model validation, statistical learning,
  20.  *      and computational support.
  21.  *  
  22.  *      https://lakshmidrip.github.io/DROP/
  23.  *  
  24.  *  DROP is composed of three modules:
  25.  *  
  26.  *  - DROP Product Core - https://lakshmidrip.github.io/DROP-Product-Core/
  27.  *  - DROP Portfolio Core - https://lakshmidrip.github.io/DROP-Portfolio-Core/
  28.  *  - DROP Computational Core - https://lakshmidrip.github.io/DROP-Computational-Core/
  29.  *
  30.  *  DROP Product Core implements libraries for the following:
  31.  *  - Fixed Income Analytics
  32.  *  - Loan Analytics
  33.  *  - Transaction Cost Analytics
  34.  *
  35.  *  DROP Portfolio Core implements libraries for the following:
  36.  *  - Asset Allocation Analytics
  37.  *  - Asset Liability Management Analytics
  38.  *  - Capital Estimation Analytics
  39.  *  - Exposure Analytics
  40.  *  - Margin Analytics
  41.  *  - XVA Analytics
  42.  *
  43.  *  DROP Computational Core implements libraries for the following:
  44.  *  - Algorithm Support
  45.  *  - Computation Support
  46.  *  - Function Analysis
  47.  *  - Model Validation
  48.  *  - Numerical Analysis
  49.  *  - Numerical Optimizer
  50.  *  - Spline Builder
  51.  *  - Statistical Learning
  52.  *
  53.  *  Documentation for DROP is Spread Over:
  54.  *
  55.  *  - Main                     => https://lakshmidrip.github.io/DROP/
  56.  *  - Wiki                     => https://github.com/lakshmiDRIP/DROP/wiki
  57.  *  - GitHub                   => https://github.com/lakshmiDRIP/DROP
  58.  *  - Repo Layout Taxonomy     => https://github.com/lakshmiDRIP/DROP/blob/master/Taxonomy.md
  59.  *  - Javadoc                  => https://lakshmidrip.github.io/DROP/Javadoc/index.html
  60.  *  - Technical Specifications => https://github.com/lakshmiDRIP/DROP/tree/master/Docs/Internal
  61.  *  - Release Versions         => https://lakshmidrip.github.io/DROP/version.html
  62.  *  - Community Credits        => https://lakshmidrip.github.io/DROP/credits.html
  63.  *  - Issues Catalog           => https://github.com/lakshmiDRIP/DROP/issues
  64.  *  - JUnit                    => https://lakshmidrip.github.io/DROP/junit/index.html
  65.  *  - Jacoco                   => https://lakshmidrip.github.io/DROP/jacoco/index.html
  66.  *
  67.  *  Licensed under the Apache License, Version 2.0 (the "License");
  68.  *      you may not use this file except in compliance with the License.
  69.  *  
  70.  *  You may obtain a copy of the License at
  71.  *      http://www.apache.org/licenses/LICENSE-2.0
  72.  *  
  73.  *  Unless required by applicable law or agreed to in writing, software
  74.  *      distributed under the License is distributed on an "AS IS" BASIS,
  75.  *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  76.  *  
  77.  *  See the License for the specific language governing permissions and
  78.  *      limitations under the License.
  79.  */

  80. /**
  81.  * <i>LocalMonotoneCkGenerator</i> generates customized Local Stretch by trading off Ck for local control.
  82.  * This class implements the following variants: Akima, Bessel, Harmonic, Hyman83, Hyman89, Kruger, Monotone
  83.  * Convex, as well as the Van Leer and the Huynh/LeFloch limiters. It also provides the following custom
  84.  * control on the resulting C1:
  85.  *
  86.  * <br><br>
  87.  *  <ul>
  88.  *      <li>
  89.  *          Eliminate the Spurious Extrema in the Input C1 Entry
  90.  *      </li>
  91.  *      <li>
  92.  *          Apply the Monotone Filter in the Input C1 Entry
  93.  *      </li>
  94.  *      <li>
  95.  *          Generate a Vanilla C1 Array from the specified Array of Predictor Ordinates and the Response
  96.  *              Values
  97.  *      </li>
  98.  *      <li>
  99.  *          Verify if the given Quintic Polynomial is Monotone using the Hyman89 Algorithm, and generate it
  100.  *              if necessary
  101.  *      </li>
  102.  *  </ul>
  103.  *
  104.  * <br><br>
  105.  *  <ul>
  106.  *      <li><b>Module </b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/ComputationalCore.md">Computational Core Module</a></li>
  107.  *      <li><b>Library</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/SplineBuilderLibrary.md">Spline Builder Library</a></li>
  108.  *      <li><b>Project</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/spline/README.md">Basis Splines and Linear Compounders across a Broad Family of Spline Basis Functions</a></li>
  109.  *      <li><b>Package</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/spline/pchip/README.md">Monotone Convex Themed PCHIP Splines</a></li>
  110.  *  </ul>
  111.  * <br><br>
  112.  *
  113.  * @author Lakshmi Krishnamurthy
  114.  */

  115. public class LocalMonotoneCkGenerator {

  116.     /**
  117.      * C1 Type: Vanilla
  118.      */

  119.     public static final java.lang.String C1_VANILLA = "C1_VANILLA";

  120.     /**
  121.      * C1 Type: Akima
  122.      */

  123.     public static final java.lang.String C1_AKIMA = "C1_AKIMA";

  124.     /**
  125.      * C1 Type: Bessel
  126.      */

  127.     public static final java.lang.String C1_BESSEL = "C1_BESSEL";

  128.     /**
  129.      * C1 Type: Harmonic
  130.      */

  131.     public static final java.lang.String C1_HARMONIC = "C1_HARMONIC";

  132.     /**
  133.      * C1 Type: Huynh - Le Floch Limiter
  134.      */

  135.     public static final java.lang.String C1_HUYNH_LE_FLOCH = "C1_HUYNH_LE_FLOCH";

  136.     /**
  137.      * C1 Type: Hyman83
  138.      */

  139.     public static final java.lang.String C1_HYMAN83 = "C1_HYMAN83";

  140.     /**
  141.      * C1 Type: Hyman89
  142.      */

  143.     public static final java.lang.String C1_HYMAN89 = "C1_HYMAN89";

  144.     /**
  145.      * C1 Type: Kruger
  146.      */

  147.     public static final java.lang.String C1_KRUGER = "C1_KRUGER";

  148.     /**
  149.      * C1 Type: Monotone Convex
  150.      */

  151.     public static final java.lang.String C1_MONOTONE_CONVEX = "C1_MONOTONE_CONVEX";

  152.     /**
  153.      * C1 Type: Van Leer Limiter
  154.      */

  155.     public static final java.lang.String C1_VAN_LEER = "C1_VAN_LEER";

  156.     private double[] _adblC1 = null;
  157.     private double[] _adblResponseValue = null;
  158.     private double[] _adblPredictorOrdinate = null;

  159.     /**
  160.      * Eliminate the Spurious Extrema in the Input C1 Entry
  161.      *
  162.      * @param adblC1 The C1 Array in which the Spurious Extrema is to be eliminated
  163.      * @param adblLinearC1 Array of the Linear C1 Entries
  164.      *
  165.      * @return The C1 Array with the Spurious Extrema eliminated
  166.      */

  167.     public static final double[] EliminateSpuriousExtrema (
  168.         final double[] adblC1,
  169.         final double[] adblLinearC1)
  170.     {
  171.         if (null == adblC1 || null == adblLinearC1) return null;

  172.         int iNumEntries = adblC1.length;
  173.         double[] adblUpdatedC1 = new double[iNumEntries];
  174.         adblUpdatedC1[0] = adblC1[0];
  175.         adblUpdatedC1[iNumEntries - 1] = adblC1[iNumEntries - 1];

  176.         if (1 >= iNumEntries || iNumEntries != adblLinearC1.length + 1) return null;

  177.         for (int i = 1; i < iNumEntries - 1; ++i)
  178.             adblUpdatedC1[i] = 0. < adblLinearC1[i] ? java.lang.Math.min (java.lang.Math.max (0., adblC1[i]),
  179.                 java.lang.Math.min (adblLinearC1[i], adblLinearC1[i - 1])) : java.lang.Math.max
  180.                     (java.lang.Math.min (0., adblC1[i]), java.lang.Math.max (adblLinearC1[i],
  181.                         adblLinearC1[i - 1]));

  182.         return adblUpdatedC1;
  183.     }

  184.     /**
  185.      * Apply the Monotone Filter in the Input C1 Entry
  186.      *
  187.      * @param adblC1 The C1 Array in which the Monotone Filter is to be applied
  188.      * @param adblLinearC1 Array of the Linear C1 Entries
  189.      *
  190.      * @return The C1 Array with the Monotone Filter applied
  191.      */

  192.     public static final double[] ApplyMonotoneFilter (
  193.         final double[] adblC1,
  194.         final double[] adblLinearC1)
  195.     {
  196.         if (null == adblC1 || null == adblLinearC1) return null;

  197.         int iNumEntries = adblC1.length;
  198.         double[] adblUpdatedC1 = new double[iNumEntries];
  199.         adblUpdatedC1[0] = adblC1[0];

  200.         if (1 >= iNumEntries || iNumEntries != adblLinearC1.length + 1) return null;

  201.         for (int i = 0; i < iNumEntries; ++i) {
  202.             if (0 == i) {
  203.                 if (adblC1[0] * adblLinearC1[0] > 0. && adblLinearC1[0] * adblLinearC1[1] > 0. &&
  204.                     java.lang.Math.abs (adblC1[0]) < 3. * java.lang.Math.abs (adblLinearC1[0]))
  205.                     adblUpdatedC1[0] = 3. * adblLinearC1[0];
  206.                 else if (adblC1[0] * adblLinearC1[0] <= 0.)
  207.                     adblUpdatedC1[0] = 0.;
  208.             } else if (iNumEntries == i) {
  209.                 if (adblC1[i] * adblLinearC1[i - 1] > 0. && adblLinearC1[i - 1] * adblLinearC1[i - 2] > 0. &&
  210.                     java.lang.Math.abs (adblC1[i]) < 3. * java.lang.Math.abs (adblLinearC1[i - 1]))
  211.                     adblUpdatedC1[i] = 3. * adblLinearC1[i - 1];
  212.                 else if (adblC1[i] * adblLinearC1[i - 1] <= 0.)
  213.                     adblUpdatedC1[i] = 0.;
  214.             } else
  215.                 adblUpdatedC1[i] = adblC1[i];
  216.         }

  217.         return adblUpdatedC1;
  218.     }

  219.     /**
  220.      * Generate a Vanilla C1 Array from the specified Array of Predictor Ordinates and the Response Values
  221.      *
  222.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  223.      * @param adblResponseValue The Response Value Array
  224.      *
  225.      * @return The C1 Array
  226.      */

  227.     public static final double[] LinearC1 (
  228.         final double[] adblPredictorOrdinate,
  229.         final double[] adblResponseValue)
  230.     {
  231.         int iNumSegment = adblResponseValue.length - 1;
  232.         double[] adblLinearC1 = new double[iNumSegment];

  233.         for (int i = 0; i < iNumSegment; ++i)
  234.             adblLinearC1[i] = (adblResponseValue[i + 1] - adblResponseValue[i]) /
  235.                 (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i]);

  236.         return adblLinearC1;
  237.     }

  238.     /**
  239.      * Generate a Bessel C1 Array from the specified Array of Predictor Ordinates and the Response Values
  240.      *
  241.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  242.      * @param adblResponseValue The Response Value Array
  243.      *
  244.      * @return The C1 Array
  245.      */

  246.     public static final double[] BesselC1 (
  247.         final double[] adblPredictorOrdinate,
  248.         final double[] adblResponseValue)
  249.     {
  250.         int iNumResponse = adblResponseValue.length;
  251.         double[] adblBesselC1 = new double[iNumResponse];

  252.         for (int i = 0; i < iNumResponse; ++i) {
  253.             if (0 == i) {
  254.                 adblBesselC1[i] = (adblPredictorOrdinate[2] + adblPredictorOrdinate[1] - 2. *
  255.                     adblPredictorOrdinate[0]) * (adblResponseValue[1] - adblResponseValue[0]) /
  256.                         (adblPredictorOrdinate[1] - adblPredictorOrdinate[0]);
  257.                 adblBesselC1[i] -= (adblPredictorOrdinate[1] - adblPredictorOrdinate[0]) *
  258.                     (adblResponseValue[2] - adblResponseValue[1]) / (adblPredictorOrdinate[2] -
  259.                         adblPredictorOrdinate[1]);
  260.                 adblBesselC1[i] /= (adblPredictorOrdinate[2] - adblPredictorOrdinate[0]);
  261.             } else if (iNumResponse - 1 == i) {
  262.                 adblBesselC1[i] = (adblPredictorOrdinate[iNumResponse - 1] -
  263.                     adblPredictorOrdinate[iNumResponse - 2]) * (adblResponseValue[iNumResponse - 2] -
  264.                         adblResponseValue[iNumResponse - 3]) / (adblPredictorOrdinate[iNumResponse - 2] -
  265.                             adblPredictorOrdinate[iNumResponse - 3]);
  266.                 adblBesselC1[i] -= (2. * adblPredictorOrdinate[iNumResponse - 1] -
  267.                     adblPredictorOrdinate[iNumResponse - 2] - adblPredictorOrdinate[iNumResponse - 3]) *
  268.                         (adblResponseValue[iNumResponse - 1] - adblResponseValue[iNumResponse - 2]) /
  269.                             (adblPredictorOrdinate[iNumResponse - 1] -
  270.                                 adblPredictorOrdinate[iNumResponse - 2]);
  271.                 adblBesselC1[i] /= (adblPredictorOrdinate[iNumResponse - 1] -
  272.                     adblPredictorOrdinate[iNumResponse - 3]);
  273.             } else {
  274.                 adblBesselC1[i] = (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i]) *
  275.                     (adblResponseValue[i] - adblResponseValue[i - 1]) / (adblPredictorOrdinate[i] -
  276.                         adblPredictorOrdinate[i - 1]);
  277.                 adblBesselC1[i] += (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]) *
  278.                     (adblResponseValue[i + 1] - adblResponseValue[i]) / (adblPredictorOrdinate[i + 1] -
  279.                         adblPredictorOrdinate[i]);
  280.                 adblBesselC1[i] /= (adblPredictorOrdinate[iNumResponse - 1] -
  281.                     adblPredictorOrdinate[iNumResponse - 3]);
  282.             }
  283.         }

  284.         return adblBesselC1;
  285.     }

  286.     /**
  287.      * Generate a Hyman83 C1 Array from the specified Array of Predictor Ordinates and the Response Values
  288.      *
  289.      *  Hyman (1983) Accurate Monotonicity Preserving Cubic Interpolation -
  290.      *      SIAM J on Numerical Analysis 4 (4), 645-654.
  291.      *
  292.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  293.      * @param adblResponseValue The Response Value Array
  294.      *
  295.      * @return The C1 Array
  296.      */

  297.     public static final double[] Hyman83C1 (
  298.         final double[] adblPredictorOrdinate,
  299.         final double[] adblResponseValue)
  300.     {
  301.         int iNumResponse = adblResponseValue.length;
  302.         double dblLinearSlopePrev = java.lang.Double.NaN;
  303.         double[] adblHyman83C1 = new double[iNumResponse];

  304.         for (int i = 0; i < iNumResponse; ++i) {
  305.             adblHyman83C1[i] = 0.;
  306.             double dblLinearSlope = iNumResponse - 1 != i ? (adblResponseValue[i + 1] - adblResponseValue[i])
  307.                 / (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i]) : java.lang.Double.NaN;

  308.             if (0 != i && iNumResponse - 1 != i) {
  309.                 double dblMonotoneIndicator = dblLinearSlopePrev * dblLinearSlope;

  310.                 if (0. <= dblMonotoneIndicator)
  311.                     adblHyman83C1[i] = 3. * dblMonotoneIndicator / (java.lang.Math.max (dblLinearSlope,
  312.                         dblLinearSlopePrev) + 2. * java.lang.Math.min (dblLinearSlope, dblLinearSlopePrev));
  313.             }

  314.             dblLinearSlopePrev = dblLinearSlope;
  315.         }

  316.         return adblHyman83C1;
  317.     }

  318.     /**
  319.      * Generate a Hyman89 C1 Array from the specified Array of Predictor Ordinates and the Response Values
  320.      *
  321.      *  Doherty, Edelman, and Hyman (1989) Non-negative, monotonic, or convexity preserving cubic and quintic
  322.      *      Hermite interpolation - Mathematics of Computation 52 (186), 471-494.
  323.      *
  324.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  325.      * @param adblResponseValue The Response Value Array
  326.      *
  327.      * @return The C1 Array
  328.      */

  329.     public static final double[] Hyman89C1 (
  330.         final double[] adblPredictorOrdinate,
  331.         final double[] adblResponseValue)
  332.     {
  333.         int iNumResponse = adblResponseValue.length;
  334.         double[] adblHyman89C1 = new double[iNumResponse];

  335.         double[] adblNodeC1 = LinearC1 (adblPredictorOrdinate, adblResponseValue);

  336.         double[] adblBesselC1 = BesselC1 (adblPredictorOrdinate, adblResponseValue);

  337.         for (int i = 0; i < iNumResponse; ++i) {
  338.             if (i < 2 || i >= iNumResponse - 2)
  339.                 adblHyman89C1[i] = adblBesselC1[i];
  340.             else {
  341.                 double dMuMinus = (adblNodeC1[i - 1] * (2. * (adblPredictorOrdinate[i] -
  342.                     adblPredictorOrdinate[i - 1]) + adblPredictorOrdinate[i - 1] -
  343.                         adblPredictorOrdinate[i - 2]) - adblNodeC1[i - 2] * (adblPredictorOrdinate[i] -
  344.                             adblPredictorOrdinate[i - 1])) / (adblPredictorOrdinate[i] -
  345.                                 adblPredictorOrdinate[i - 2]);
  346.                 double dMu0 = (adblNodeC1[i - 1] * (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i])
  347.                     + adblNodeC1[i] * (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1])) /
  348.                         (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i - 1]);
  349.                 double dMuPlus = (adblNodeC1[i] * (2. * (adblPredictorOrdinate[i + 1] -
  350.                     adblPredictorOrdinate[i]) + adblPredictorOrdinate[i + 2] - adblPredictorOrdinate[i + 1])
  351.                         - adblNodeC1[i + 1] * (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i])) /
  352.                             (adblPredictorOrdinate[i + 2] - adblPredictorOrdinate[i]);

  353.                 try {
  354.                     double dblM = 3 * org.drip.numerical.common.NumberUtil.Minimum (new double[]
  355.                         {java.lang.Math.abs (adblNodeC1[i - 1]), java.lang.Math.abs (adblNodeC1[i]),
  356.                             java.lang.Math.abs (dMu0), java.lang.Math.abs (dMuPlus)});

  357.                     if (!org.drip.numerical.common.NumberUtil.SameSign (new double[] {dMu0, dMuMinus,
  358.                             adblNodeC1[i - 1] - adblNodeC1[i - 2], adblNodeC1[i] - adblNodeC1[i - 1]}))
  359.                         dblM = java.lang.Math.max (dblM, 1.5 * java.lang.Math.min (java.lang.Math.abs (dMu0),
  360.                             java.lang.Math.abs (dMuMinus)));
  361.                     else if (!org.drip.numerical.common.NumberUtil.SameSign (new double[] {-dMu0, -dMuPlus,
  362.                             adblNodeC1[i] - adblNodeC1[i - 1], adblNodeC1[i + 1] - adblNodeC1[i]}))
  363.                         dblM = java.lang.Math.max (dblM, 1.5 * java.lang.Math.min (java.lang.Math.abs (dMu0),
  364.                             java.lang.Math.abs (dMuPlus)));

  365.                     adblHyman89C1[i] = 0.;

  366.                     if (adblBesselC1[i] * dMu0 > 0.)
  367.                         adblHyman89C1[i] = adblBesselC1[i] / java.lang.Math.abs (adblBesselC1[i]) *
  368.                             java.lang.Math.min (java.lang.Math.abs (adblBesselC1[i]), dblM);
  369.                 } catch (java.lang.Exception e) {
  370.                     e.printStackTrace();

  371.                     return null;
  372.                 }
  373.             }
  374.         }

  375.         return adblHyman89C1;
  376.     }

  377.     /**
  378.      * Generate a Harmonic C1 Array from the specified Array of Predictor Ordinates and the Response Values
  379.      *
  380.      *  Fritcsh and Butland (1984) A Method for constructing local monotonic piece-wise cubic interpolants -
  381.      *      SIAM J on Scientific and Statistical Computing 5, 300-304.
  382.      *
  383.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  384.      * @param adblResponseValue The Response Value Array
  385.      *
  386.      * @return The C1 Array
  387.      */

  388.     public static final double[] HarmonicC1 (
  389.         final double[] adblPredictorOrdinate,
  390.         final double[] adblResponseValue)
  391.     {
  392.         int iNumResponse = adblResponseValue.length;
  393.         double[] adblHarmonicC1 = new double[iNumResponse];

  394.         double[] adblLinearC1 = LinearC1 (adblPredictorOrdinate, adblResponseValue);

  395.         for (int i = 0; i < iNumResponse; ++i) {
  396.             if (0 == i) {
  397.                 adblHarmonicC1[i] = (adblPredictorOrdinate[2] + adblPredictorOrdinate[1] - 2. *
  398.                     adblPredictorOrdinate[0]) * adblLinearC1[0] / (adblPredictorOrdinate[2] -
  399.                         adblPredictorOrdinate[0]);
  400.                 adblHarmonicC1[i] -= (adblPredictorOrdinate[1] - adblPredictorOrdinate[0]) * adblLinearC1[1]
  401.                     / (adblPredictorOrdinate[2] - adblPredictorOrdinate[0]);
  402.             } else if (iNumResponse - 1 == i) {
  403.                 adblHarmonicC1[i] = -(adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]) *
  404.                     adblLinearC1[i - 2] / (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 2]);
  405.                 adblHarmonicC1[i] += (2. * adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1] -
  406.                     adblPredictorOrdinate[i - 2]) * adblLinearC1[i - 1] / (adblPredictorOrdinate[i] -
  407.                         adblPredictorOrdinate[i - 2]);
  408.             } else {
  409.                 if (adblLinearC1[i - 1] * adblLinearC1[i] <= 0.)
  410.                     adblHarmonicC1[i] = 0.;
  411.                 else {
  412.                     adblHarmonicC1[i] = (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1] + 2. *
  413.                         (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i])) / (3. *
  414.                             (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i])) / adblLinearC1[i - 1];
  415.                     adblHarmonicC1[i] += (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i] + 2. *
  416.                         (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1])) / (3. *
  417.                             (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i])) / adblLinearC1[i];
  418.                     adblHarmonicC1[i] = 1. / adblHarmonicC1[i];
  419.                 }
  420.             }
  421.         }

  422.         return adblHarmonicC1;
  423.     }

  424.     /**
  425.      * Generate a Van Leer Limiter C1 Array from the specified Array of Predictor Ordinates and the Response
  426.      *  Values.
  427.      *
  428.      *  Van Leer (1974) Towards the Ultimate Conservative Difference Scheme. II - Monotonicity and
  429.      *      Conservation combined in a Second-Order Scheme, Journal of Computational Physics 14 (4), 361-370.
  430.      *
  431.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  432.      * @param adblResponseValue The Response Value Array
  433.      *
  434.      * @return The C1 Array
  435.      */

  436.     public static final double[] VanLeerLimiterC1 (
  437.         final double[] adblPredictorOrdinate,
  438.         final double[] adblResponseValue)
  439.     {
  440.         int iNumResponse = adblResponseValue.length;
  441.         double[] dblVanLeerLimiterC1 = new double[iNumResponse];

  442.         double[] adblNodeC1 = LinearC1 (adblPredictorOrdinate, adblResponseValue);

  443.         for (int i = 0; i < iNumResponse; ++i) {
  444.             if (0 == i) {
  445.                 dblVanLeerLimiterC1[i] = (adblPredictorOrdinate[2] + adblPredictorOrdinate[1] - 2. *
  446.                     adblPredictorOrdinate[0]) * adblNodeC1[0] / (adblPredictorOrdinate[2] -
  447.                         adblPredictorOrdinate[0]);
  448.                 dblVanLeerLimiterC1[i] -= (adblPredictorOrdinate[1] - adblPredictorOrdinate[0]) *
  449.                     adblNodeC1[1] / (adblPredictorOrdinate[2] - adblPredictorOrdinate[0]);
  450.             } else if (iNumResponse - 1 == i) {
  451.                 dblVanLeerLimiterC1[i] = -(adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]) *
  452.                     adblNodeC1[i - 2] / (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 2]);
  453.                 dblVanLeerLimiterC1[i] += (2. * adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1] -
  454.                     adblPredictorOrdinate[i - 2]) * adblNodeC1[i - 1] / (adblPredictorOrdinate[i] -
  455.                         adblPredictorOrdinate[i - 2]);
  456.             } else {
  457.                 if (0. != adblNodeC1[i - 1]) {
  458.                     double dblR = adblNodeC1[i] / adblNodeC1[i - 1];

  459.                     double dblRAbsolute = java.lang.Math.abs (dblR);

  460.                     dblVanLeerLimiterC1[i] = adblNodeC1[i] * (dblR + dblRAbsolute) / (1. + dblRAbsolute);
  461.                 } else if (0. >= adblNodeC1[i])
  462.                     dblVanLeerLimiterC1[i] = 0.;
  463.                 else if (0. < adblNodeC1[i])
  464.                     dblVanLeerLimiterC1[i] = 2. * adblNodeC1[i];
  465.             }
  466.         }

  467.         return dblVanLeerLimiterC1;
  468.     }

  469.     /**
  470.      * Generate a Huynh Le Floch Limiter C1 Array from the specified Array of Predictor Ordinates and the
  471.      *  Response Values.
  472.      *
  473.      *  Huynh (1993) Accurate Monotone Cubic Interpolation, SIAM J on Numerical Analysis 30 (1), 57-100.
  474.      *
  475.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  476.      * @param adblResponseValue The Response Value Array
  477.      *
  478.      * @return The C1 Array
  479.      */

  480.     public static final double[] HuynhLeFlochLimiterC1 (
  481.         final double[] adblPredictorOrdinate,
  482.         final double[] adblResponseValue)
  483.     {
  484.         int iNumResponse = adblResponseValue.length;
  485.         double[] adblHuynhLeFlochLimiterC1 = new double[iNumResponse];

  486.         double[] adblNodeC1 = LinearC1 (adblPredictorOrdinate, adblResponseValue);

  487.         for (int i = 0; i < iNumResponse; ++i) {
  488.             if (0 == i) {
  489.                 adblHuynhLeFlochLimiterC1[i] = (adblPredictorOrdinate[2] + adblPredictorOrdinate[1] - 2. *
  490.                     adblPredictorOrdinate[0]) * adblNodeC1[0] / (adblPredictorOrdinate[2] -
  491.                         adblPredictorOrdinate[0]);
  492.                 adblHuynhLeFlochLimiterC1[i] -= (adblPredictorOrdinate[1] - adblPredictorOrdinate[0]) *
  493.                     adblNodeC1[1] / (adblPredictorOrdinate[2] - adblPredictorOrdinate[0]);
  494.             } else if (iNumResponse - 1 == i) {
  495.                 adblHuynhLeFlochLimiterC1[i] = -(adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]) *
  496.                     adblNodeC1[i - 2] / (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 2]);
  497.                 adblHuynhLeFlochLimiterC1[i] += (2. * adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]
  498.                     - adblPredictorOrdinate[i - 2]) * adblNodeC1[i - 1] / (adblPredictorOrdinate[i] -
  499.                         adblPredictorOrdinate[i - 2]);
  500.             } else {
  501.                 double dblMonotoneIndicator = adblNodeC1[i] * adblNodeC1[i - 1];

  502.                 if (0. < dblMonotoneIndicator)
  503.                     adblHuynhLeFlochLimiterC1[i] = 3. * dblMonotoneIndicator * (adblNodeC1[i] +
  504.                         adblNodeC1[i - 1]) / (adblNodeC1[i] * adblNodeC1[i] + adblNodeC1[i - 1] *
  505.                             adblNodeC1[i - 1] * 4. * dblMonotoneIndicator);
  506.                 else
  507.                     adblHuynhLeFlochLimiterC1[i] = 0.;
  508.             }
  509.         }

  510.         return adblHuynhLeFlochLimiterC1;
  511.     }

  512.     /**
  513.      * Generate a Kruger C1 Array from the specified Array of Predictor Ordinates and the Response Values.
  514.      *
  515.      *  Kruger (2002) Constrained Cubic Spline Interpolations for Chemical Engineering Application,
  516.      *      http://www.korf.co.uk/spline.pdf
  517.      *
  518.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  519.      * @param adblResponseValue The Response Value Array
  520.      *
  521.      * @return The C1 Array
  522.      */

  523.     public static final double[] KrugerC1 (
  524.         final double[] adblPredictorOrdinate,
  525.         final double[] adblResponseValue)
  526.     {
  527.         int iNumResponse = adblResponseValue.length;
  528.         double[] adblKrugerSlope = new double[iNumResponse];

  529.         double[] adblSlopeC1 = LinearC1 (adblPredictorOrdinate, adblResponseValue);

  530.         if (null == adblSlopeC1 || adblSlopeC1.length != iNumResponse - 1) return null;

  531.         for (int i = 0; i < iNumResponse; ++i) {
  532.             if (0 != i && iNumResponse - 1 != i) {
  533.                 if (adblSlopeC1[i - 1] * adblSlopeC1[i] <= 0.)
  534.                     adblKrugerSlope[i] = 0.;
  535.                 else
  536.                     adblKrugerSlope[i] = 2. / ((1. / adblSlopeC1[i - 1]) + (1. / adblSlopeC1[i]));
  537.             }
  538.         }

  539.         adblKrugerSlope[0] = 3.5 * adblSlopeC1[0] - 0.5 * adblKrugerSlope[1];
  540.         adblKrugerSlope[iNumResponse - 1] = 3.5 * adblSlopeC1[iNumResponse - 2] - 0.5 *
  541.             adblKrugerSlope[iNumResponse - 2];
  542.         return adblKrugerSlope;
  543.     }

  544.     /**
  545.      * Generate a Akima C1 Array from the specified Array of Predictor Ordinates and the Response Values.
  546.      *
  547.      *  Akima (1970): A New Method of Interpolation and Smooth Curve Fitting based on Local Procedures,
  548.      *      Journal of the Association for the Computing Machinery 17 (4), 589-602.
  549.      *
  550.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  551.      * @param adblResponseValue The Response Value Array
  552.      *
  553.      * @return The C1 Array
  554.      */

  555.     public static final double[] AkimaC1 (
  556.         final double[] adblPredictorOrdinate,
  557.         final double[] adblResponseValue)
  558.     {
  559.         org.drip.spline.pchip.AkimaLocalC1Generator alcr =
  560.             org.drip.spline.pchip.AkimaLocalC1Generator.Create (adblPredictorOrdinate, adblResponseValue);

  561.         return null == alcr ? null : alcr.C1();
  562.     }

  563.     /**
  564.      * Verify if the given Quintic Polynomial is Monotone using the Hyman89 Algorithm
  565.      *
  566.      *  Doherty, Edelman, and Hyman (1989) Non-negative, monotonic, or convexity preserving cubic and quintic
  567.      *      Hermite interpolation - Mathematics of Computation 52 (186), 471-494.
  568.      *
  569.      * @param adblPredictorOrdinate Array of Predictor Ordinates
  570.      * @param adblResponseValue Array of Response Values
  571.      * @param adblFirstDerivative Array of First Derivatives
  572.      * @param adblSecondDerivative Array of Second Derivatives
  573.      *
  574.      * @return TRUE - The given Quintic Polynomial is Monotone
  575.      *
  576.      * @throws java.lang.Exception Thrown if the Monotonicity cannot be determined
  577.      */

  578.     public static final boolean VerifyHyman89QuinticMonotonicity (
  579.         final double[] adblPredictorOrdinate,
  580.         final double[] adblResponseValue,
  581.         final double[] adblFirstDerivative,
  582.         final double[] adblSecondDerivative)
  583.         throws java.lang.Exception
  584.     {
  585.         if (null == adblPredictorOrdinate || null == adblResponseValue || null == adblFirstDerivative || null
  586.             == adblSecondDerivative)
  587.             throw new java.lang.Exception
  588.                 ("LocalMonotoneCkGenerator::VerifyHyman89QuinticMonotonicity => Invalid Inputs");

  589.         int iNumPredictor = adblPredictorOrdinate.length;

  590.         if (1 >= iNumPredictor || iNumPredictor != adblResponseValue.length || iNumPredictor !=
  591.             adblResponseValue.length || iNumPredictor != adblResponseValue.length)
  592.             throw new java.lang.Exception
  593.                 ("LocalMonotoneCkGenerator::VerifyHyman89QuinticMonotonicity => Invalid Inputs");

  594.         for (int i = 1; i < iNumPredictor - 1; ++i) {
  595.             double dblAbsoluteResponseValue = java.lang.Math.abs (adblResponseValue[i]);

  596.             double dblResponseValueSign = adblResponseValue[i] > 0. ? 1. : -1.;
  597.             double dblHMinus = (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]);
  598.             double dblHPlus = (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i]);

  599.             if (-5. * dblAbsoluteResponseValue / dblHPlus > dblResponseValueSign * adblFirstDerivative[i] ||
  600.                 5. * dblAbsoluteResponseValue / dblHMinus < dblResponseValueSign * adblFirstDerivative[i])
  601.                 return false;

  602.             if (dblResponseValueSign * adblSecondDerivative[i] < dblResponseValueSign * java.lang.Math.max
  603.                 (8. * adblFirstDerivative[i] / dblHMinus - 20. * adblResponseValue[i] / dblHMinus /
  604.                     dblHMinus, -8. * adblFirstDerivative[i] / dblHPlus - 20. * adblResponseValue[i] /
  605.                         dblHPlus / dblHPlus))
  606.                 return false;
  607.         }

  608.         return true;
  609.     }

  610.     /**
  611.      * Generate C1 Slope Quintic Polynomial is Monotone using the Hyman89 Algorithm
  612.      *
  613.      *  Doherty, Edelman, and Hyman (1989) Non-negative, monotonic, or convexity preserving cubic and quintic
  614.      *      Hermite interpolation - Mathematics of Computation 52 (186), 471-494.
  615.      *
  616.      * @param adblPredictorOrdinate Array of Predictor Ordinates
  617.      * @param adblResponseValue Array of Response Values
  618.      * @param adblFirstDerivative Array of First Derivatives
  619.      * @param adblSecondDerivative Array of Second Derivatives
  620.      *
  621.      * @return The C1 Slope Quintic Stretch
  622.      */

  623.     public static final double[] Hyman89QuinticMonotoneC1 (
  624.         final double[] adblPredictorOrdinate,
  625.         final double[] adblResponseValue,
  626.         final double[] adblFirstDerivative,
  627.         final double[] adblSecondDerivative)
  628.     {
  629.         if (null == adblPredictorOrdinate || null == adblResponseValue || null == adblFirstDerivative || null
  630.             == adblSecondDerivative)
  631.             return null;

  632.         int iNumPredictor = adblPredictorOrdinate.length;

  633.         if (1 >= iNumPredictor || iNumPredictor != adblResponseValue.length || iNumPredictor !=
  634.             adblResponseValue.length || iNumPredictor != adblResponseValue.length)
  635.             return null;

  636.         double[] adblAdjFirstDerivative = new double[iNumPredictor];

  637.         double[] adblNodeC1 = LinearC1 (adblPredictorOrdinate, adblResponseValue);

  638.         double[] adblBesselC1 = BesselC1 (adblPredictorOrdinate, adblResponseValue);

  639.         for (int i = 0; i < iNumPredictor; ++i) {
  640.             if (i < 2 || i >= iNumPredictor - 2)
  641.                 adblAdjFirstDerivative[i] = adblBesselC1[i];
  642.             else {
  643.                 double dblSign = 0.;
  644.                 double dblHMinus = (adblPredictorOrdinate[i] - adblPredictorOrdinate[i - 1]);
  645.                 double dblHPlus = (adblPredictorOrdinate[i + 1] - adblPredictorOrdinate[i]);

  646.                 if (adblFirstDerivative[i - 1] * adblFirstDerivative[i] < 0.)
  647.                     dblSign = adblResponseValue[i] > 0. ? 1. : -1.;

  648.                 double dblMinSlope = java.lang.Math.min (java.lang.Math.abs (adblFirstDerivative[i - 1]),
  649.                     java.lang.Math.abs (adblFirstDerivative[i]));

  650.                 if (dblSign >= 0.)
  651.                     adblAdjFirstDerivative[i] = java.lang.Math.min (java.lang.Math.max (0.,
  652.                         adblFirstDerivative[i]), 5. * dblMinSlope);
  653.                 else
  654.                     adblAdjFirstDerivative[i] = java.lang.Math.max (java.lang.Math.min (0.,
  655.                         adblFirstDerivative[i]), -5. * dblMinSlope);

  656.                 double dblA = java.lang.Math.max (0., adblAdjFirstDerivative[i] / adblNodeC1[i - 1]);

  657.                 double dblB = java.lang.Math.max (0., adblAdjFirstDerivative[i + 1] / adblNodeC1[i]);

  658.                 double dblDPlus = adblAdjFirstDerivative[i] * adblNodeC1[i] > 0. ? adblAdjFirstDerivative[i]
  659.                     : 0.;
  660.                 double dblDMinus = adblAdjFirstDerivative[i] * adblNodeC1[i - 1] > 0. ?
  661.                     adblAdjFirstDerivative[i] : 0.;
  662.                 double dblALeft = (-7.9 * dblDPlus - 0.26 * dblDPlus * dblB) / dblHPlus;
  663.                 double dblARight = ((20. - 2. * dblB) * adblNodeC1[i] - 8. * dblDPlus - 0.48 * dblDPlus *
  664.                     dblB) / dblHPlus;
  665.                 double dblBLeft = ((2. * dblA - 20.) * adblNodeC1[i - 1] + 8. * dblDMinus - 0.48 * dblDMinus
  666.                     * dblA) / dblHMinus;
  667.                 double dblBRight = (7.9 * dblDMinus + 0.26 * dblDMinus * dblA) / dblHMinus;

  668.                 if (dblARight <= dblBLeft || dblALeft >= dblBRight) {
  669.                     double dblDenom = ((8. + 0.48 * dblB) / dblHPlus) + ((8. + 0.48 * dblA) / dblHMinus);
  670.                     adblAdjFirstDerivative[i] = (20. - 2. * dblB) * adblNodeC1[i] / dblHPlus;
  671.                     adblAdjFirstDerivative[i] += (20. - 2. * dblA) * adblNodeC1[i - 1] / dblHMinus;
  672.                     adblAdjFirstDerivative[i] /= dblDenom;
  673.                 }
  674.             }
  675.         }

  676.         return adblAdjFirstDerivative;
  677.     }

  678.     /**
  679.      * Generate the Local Control Stretch in accordance with the desired Customization Parameters
  680.      *
  681.      * @param adblPredictorOrdinate The Predictor Ordinate Array
  682.      * @param adblResponseValue The Response Value Array
  683.      * @param strGeneratorType The C1 Generator Type
  684.      * @param bEliminateSpuriousExtrema TRUE - Eliminate Spurious Extrema
  685.      * @param bApplyMonotoneFilter TRUE - Apply Monotone Filter
  686.      *
  687.      * @return Instance of the Local Control Stretch
  688.      */

  689.     public static final LocalMonotoneCkGenerator Create (
  690.         final double[] adblPredictorOrdinate,
  691.         final double[] adblResponseValue,
  692.         final java.lang.String strGeneratorType,
  693.         final boolean bEliminateSpuriousExtrema,
  694.         final boolean bApplyMonotoneFilter)
  695.     {
  696.         try {
  697.             LocalMonotoneCkGenerator lcr = new LocalMonotoneCkGenerator (adblPredictorOrdinate,
  698.                 adblResponseValue);

  699.             if (!lcr.generateC1 (strGeneratorType)) return null;

  700.             if (bEliminateSpuriousExtrema && !lcr.eliminateSpuriousExtrema()) return null;

  701.             if (bApplyMonotoneFilter) {
  702.                 if (!lcr.applyMonotoneFilter()) return null;
  703.             }

  704.             return lcr;
  705.         } catch (java.lang.Exception e) {
  706.             e.printStackTrace();
  707.         }

  708.         return null;
  709.     }

  710.     /**
  711.      * Generate the Local Control Stretch in accordance with the desired Customization Parameters
  712.      *
  713.      * @param aiPredictorOrdinate The Predictor Ordinate Array
  714.      * @param adblResponseValue The Response Value Array
  715.      * @param strGeneratorType The C1 Generator Type
  716.      * @param bEliminateSpuriousExtrema TRUE - Eliminate Spurious Extrema
  717.      * @param bApplyMonotoneFilter TRUE - Apply Monotone Filter
  718.      *
  719.      * @return Instance of the Local Control Stretch
  720.      */

  721.     public static final LocalMonotoneCkGenerator Create (
  722.         final int[] aiPredictorOrdinate,
  723.         final double[] adblResponseValue,
  724.         final java.lang.String strGeneratorType,
  725.         final boolean bEliminateSpuriousExtrema,
  726.         final boolean bApplyMonotoneFilter)
  727.     {
  728.         if (null == aiPredictorOrdinate) return null;

  729.         int iNumPredictorOrdinate = aiPredictorOrdinate.length;
  730.         double[] adblPredictorOrdinate = new double[iNumPredictorOrdinate];

  731.         if (0 == iNumPredictorOrdinate) return null;

  732.         for (int i = 0; i < iNumPredictorOrdinate; ++i)
  733.             adblPredictorOrdinate[i] = aiPredictorOrdinate[i];

  734.         return Create (adblPredictorOrdinate, adblResponseValue, strGeneratorType, bEliminateSpuriousExtrema,
  735.             bApplyMonotoneFilter);
  736.     }

  737.     private LocalMonotoneCkGenerator (
  738.         final double[] adblPredictorOrdinate,
  739.         final double[] adblResponseValue)
  740.         throws java.lang.Exception
  741.     {
  742.         if (null == (_adblPredictorOrdinate = adblPredictorOrdinate) || null == (_adblResponseValue =
  743.             adblResponseValue))
  744.             throw new java.lang.Exception ("LocalMonotoneCkGenerator ctr: Invalid Inputs!");

  745.         int iSize = _adblPredictorOrdinate.length;

  746.         if (0 == iSize || iSize != _adblResponseValue.length)
  747.             throw new java.lang.Exception ("LocalMonotoneCkGenerator ctr: Invalid Inputs!");
  748.     }

  749.     private boolean generateC1 (
  750.         final java.lang.String strGeneratorType)
  751.     {
  752.         if (null == strGeneratorType || strGeneratorType.isEmpty()) return false;

  753.         if (C1_AKIMA.equalsIgnoreCase (strGeneratorType))
  754.             return null != (_adblC1 = AkimaC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  755.                 _adblC1.length;

  756.         if (C1_BESSEL.equalsIgnoreCase (strGeneratorType))
  757.             return null != (_adblC1 = BesselC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  758.                 _adblC1.length;

  759.         if (C1_HARMONIC.equalsIgnoreCase (strGeneratorType))
  760.             return null != (_adblC1 = HarmonicC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  761.                 _adblC1.length;

  762.         if (C1_HUYNH_LE_FLOCH.equalsIgnoreCase (strGeneratorType))
  763.             return null != (_adblC1 = HuynhLeFlochLimiterC1 (_adblPredictorOrdinate, _adblResponseValue)) &&
  764.                 0 != _adblC1.length;

  765.         if (C1_HYMAN83.equalsIgnoreCase (strGeneratorType))
  766.             return null != (_adblC1 = Hyman83C1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  767.                 _adblC1.length;

  768.         if (C1_HYMAN89.equalsIgnoreCase (strGeneratorType))
  769.             return null != (_adblC1 = Hyman89C1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  770.                 _adblC1.length;

  771.         if (C1_KRUGER.equalsIgnoreCase (strGeneratorType))
  772.             return null != (_adblC1 = KrugerC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  773.                 _adblC1.length;

  774.         if (C1_MONOTONE_CONVEX.equalsIgnoreCase (strGeneratorType))
  775.             return null != (_adblC1 = BesselC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  776.             _adblC1.length;

  777.         if (C1_VANILLA.equalsIgnoreCase (strGeneratorType))
  778.             return null != (_adblC1 = LinearC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  779.                 _adblC1.length;

  780.         if (C1_VAN_LEER.equalsIgnoreCase (strGeneratorType))
  781.             return null != (_adblC1 = VanLeerLimiterC1 (_adblPredictorOrdinate, _adblResponseValue)) && 0 !=
  782.                 _adblC1.length;

  783.         return false;
  784.     }

  785.     private boolean eliminateSpuriousExtrema()
  786.     {
  787.         return null != (_adblC1 = EliminateSpuriousExtrema (_adblC1, LinearC1 (_adblPredictorOrdinate,
  788.             _adblResponseValue))) && 0 != _adblC1.length;
  789.     }

  790.     private boolean applyMonotoneFilter()
  791.     {
  792.         return null != (_adblC1 = ApplyMonotoneFilter (_adblC1, LinearC1 (_adblPredictorOrdinate,
  793.             _adblResponseValue))) && 0 != _adblC1.length;
  794.     }

  795.     /**
  796.      * Retrieve the C1 Array
  797.      *
  798.      * @return The C1 Array
  799.      */

  800.     public double[] C1()
  801.     {
  802.         return _adblC1;
  803.     }
  804. }