public virtual void test_trinomialTree()
        {
            int nSteps = 135;

            LatticeSpecification[] lattices = new LatticeSpecification[]
            {
                new CoxRossRubinsteinLatticeSpecification(),
                new TrigeorgisLatticeSpecification()
            };
            double tol = 5.0e-3;

            foreach (bool isCall in new bool[] { true, false })
            {
                foreach (double strike in STRIKES)
                {
                    foreach (double interest in INTERESTS)
                    {
                        foreach (double vol in VOLS)
                        {
                            foreach (double dividend in DIVIDENDS)
                            {
                                OptionFunction function = EuropeanVanillaOptionFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps);
                                double         exact    = BlackScholesFormulaRepository.price(SPOT, strike, TIME, vol, interest, interest - dividend, isCall);
                                foreach (LatticeSpecification lattice in lattices)
                                {
                                    double computed = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend);
                                    assertEquals(computed, exact, Math.Max(exact, 1d) * tol);
                                }
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Price an option under the specified trinomial lattice.
        /// <para>
        /// It is assumed that the volatility, interest rate and continuous dividend rate are constant
        /// over the lifetime of the option.
        ///
        /// </para>
        /// </summary>
        /// <param name="function">  the option </param>
        /// <param name="lattice">  the lattice specification </param>
        /// <param name="spot">  the spot </param>
        /// <param name="volatility">  the volatility </param>
        /// <param name="interestRate">  the interest rate </param>
        /// <param name="dividendRate">  the dividend rate </param>
        /// <returns> the option price </returns>
        public virtual double optionPrice(OptionFunction function, LatticeSpecification lattice, double spot, double volatility, double interestRate, double dividendRate)
        {
            int         nSteps          = function.NumberOfSteps;
            double      timeToExpiry    = function.TimeToExpiry;
            double      dt              = timeToExpiry / (double)nSteps;
            double      discount        = Math.Exp(-interestRate * dt);
            DoubleArray @params         = lattice.getParametersTrinomial(volatility, interestRate - dividendRate, dt);
            double      middleFactor    = @params.get(1);
            double      downFactor      = @params.get(2);
            double      upProbability   = @params.get(3);
            double      midProbability  = @params.get(4);
            double      downProbability = @params.get(5);

            ArgChecker.isTrue(upProbability > 0d, "upProbability should be greater than 0");
            ArgChecker.isTrue(upProbability < 1d, "upProbability should be smaller than 1");
            ArgChecker.isTrue(midProbability > 0d, "midProbability should be greater than 0");
            ArgChecker.isTrue(midProbability < 1d, "midProbability should be smaller than 1");
            ArgChecker.isTrue(downProbability > 0d, "downProbability should be greater than 0");
            DoubleArray values = function.getPayoffAtExpiryTrinomial(spot, downFactor, middleFactor);

            for (int i = nSteps - 1; i > -1; --i)
            {
                values = function.getNextOptionValues(discount, upProbability, midProbability, downProbability, values, spot, downFactor, middleFactor, i);
            }
            return(values.get(0));
        }