/// <summary>
        /// Compute option price and delta under the specified trinomial tree gird.
        /// <para>
        /// The delta is the first derivative of the price with respect to spot, and approximated by the data embedded in
        /// the trinomial tree.
        ///
        /// </para>
        /// </summary>
        /// <param name="function">  the option </param>
        /// <param name="data">  the trinomial tree data </param>
        /// <returns> the option price and spot delta </returns>
        public virtual ValueDerivatives optionPriceAdjoint(OptionFunction function, RecombiningTrinomialTreeData data)
        {
            int nSteps = data.NumberOfSteps;

            ArgChecker.isTrue(nSteps == function.NumberOfSteps, "mismatch in number of steps");
            DoubleArray values = function.getPayoffAtExpiryTrinomial(data.getStateValueAtLayer(nSteps));
            double      delta  = 0d;

            for (int i = nSteps - 1; i > -1; --i)
            {
                values = function.getNextOptionValues(data.getDiscountFactorAtLayer(i), data.getProbabilityAtLayer(i), data.getStateValueAtLayer(i), values, i);
                if (i == 1)
                {
                    DoubleArray stateValue = data.getStateValueAtLayer(1);
                    double      d1         = (values.get(2) - values.get(1)) / (stateValue.get(2) - stateValue.get(1));
                    double      d2         = (values.get(1) - values.get(0)) / (stateValue.get(1) - stateValue.get(0));
                    delta = 0.5 * (d1 + d2);
                }
            }
            return(ValueDerivatives.of(values.get(0), DoubleArray.of(delta)));
        }
        /// <summary>
        /// Price an option under the specified trinomial tree gird.
        /// </summary>
        /// <param name="function">  the option </param>
        /// <param name="data">  the trinomial tree data </param>
        /// <returns> the option price </returns>
        public virtual double optionPrice(OptionFunction function, RecombiningTrinomialTreeData data)
        {
            int nSteps = data.NumberOfSteps;

            ArgChecker.isTrue(nSteps == function.NumberOfSteps, "mismatch in number of steps");
            DoubleArray values = function.getPayoffAtExpiryTrinomial(data.getStateValueAtLayer(nSteps));

            for (int i = nSteps - 1; i > -1; --i)
            {
                values = function.getNextOptionValues(data.getDiscountFactorAtLayer(i), data.getProbabilityAtLayer(i), data.getStateValueAtLayer(i), values, i);
            }
            return(values.get(0));
        }