/// <summary>
        /// override the <method>DipathByAntitheticMethod</method>.
        /// For Black Scholes model, mu and sigma is constant, and only one random
        /// variate is needed to generate the state for next point
        /// </summary>

        public double[][] DipathByAntitheticMethod(StochasticAssetPrice S, GaussianGenerator nrandom,
                                                   double totalTime, int timeSteps, bool visualizationFlag = false, Form1 form = null)
        {
            double[][] pricePath = new double[2][];
            pricePath[0] = new double[timeSteps + 1];
            pricePath[1] = new double[timeSteps + 1];
            StochasticAssetPrice S2 = new StochasticAssetPrice(S);
            double dt = totalTime / (double)timeSteps;

            pricePath[0][0] = S.CurrentPrice;
            pricePath[1][0] = S.CurrentPrice;
            for (int i = 1; i <= timeSteps; i++)
            {
                double z  = nrandom.NextGaussian();
                double z2 = -z;
                pricePath[0][i] = GetNextPrice(S, dt, z);
                pricePath[1][i] = GetNextPrice(S2, dt, z2);
            }
            if (visualizationFlag & form != null)
            {
                double[] X = new double[timeSteps + 1];
                for (int i = 0; i <= timeSteps; i++)
                {
                    X[i] = i;
                }
                form.add(X, pricePath[0], "");
                form.add(X, pricePath[1], "");
            }
            return(pricePath);
        }
        /// <summary>
        /// override the <method>GeneratingRandomPricePath</method>.
        /// For Black Scholes model, mu and sigma is constant, and only one random
        /// variate is needed to generate the state for next point.
        /// </summary>

        public double[] GeneratingRandomPricePath(StochasticAssetPrice S, GaussianGenerator nrandom,
                                                  double totalTime, int timeSteps)
        {
            double[] pricePath = new double[timeSteps + 1];
            double   dt        = totalTime / (double)timeSteps;

            pricePath[0] = S.CurrentPrice;
            for (int i = 1; i <= timeSteps; i++)
            {
                double z = nrandom.NextGaussian();
                pricePath[i] = GetNextPrice(S, dt, z);
            }
            return(pricePath);
        }
        /// <summary>
        /// override the <method>PricingByMCSim</method>
        /// In each scenario, a price path is generated and the option value under
        /// this scenario is only decided by the final price. So, we take the average
        /// for the 'value at maturity' of all the simulated scenarios and
        /// discounted as the approximation for the option value.
        /// This method also outputs the standard error for the approximation.
        /// </summary>

        public override double[] PricingByMCSim(StochasticAssetPrice S, IDiscretizationScheme D, int numOfScenarios,
                                                int timeSteps, bool antitheticFlag)
        {
            double[]          Result   = new double[2];              //record the approximated price and standard error
            double[]          Sample   = new double[numOfScenarios]; //record value at maturity for each scenario
            double            sumValue = 0.0;                        //record cumulative sum of Sample array
            GaussianGenerator nrand    = new GaussianGenerator();

            if (antitheticFlag)
            {
                //when antithetic variance reduction technique is used
                for (int i = 0; i < numOfScenarios; i++)
                {
                    StochasticAssetPrice S1 = new StochasticAssetPrice(S);
                    double[][]           PricePath;
                    PricePath = D.DipathByAntitheticMethod(S1, nrand, T, timeSteps);
                    Sample[i] = (ValueAtMaturity(PricePath[0][timeSteps])
                                 + ValueAtMaturity(PricePath[1][timeSteps])) / 2.0;
                    sumValue += Sample[i];
                }
            }
            else
            {
                //when antithetic variance reduction technique is not used
                for (int i = 0; i < numOfScenarios; i++)
                {
                    StochasticAssetPrice S1 = new StochasticAssetPrice(S);
                    double[]             PricePath;
                    PricePath = D.GeneratingRandomPricePath(S1, nrand, T, timeSteps);
                    Sample[i] = ValueAtMaturity(PricePath[timeSteps]);
                    sumValue += Sample[i];
                }
            }
            double disFactor = Math.Exp(-S.Mu * T);             //discount factor to convert value at maturity to current

            Result[0] = sumValue / (double)numOfScenarios * disFactor;
            double totalVariance = 0.0;

            for (int i = 0; i < numOfScenarios; i++)
            {
                totalVariance += Math.Pow(Sample[i] - Result[0], 2.0);
            }
            if (numOfScenarios > 1)
            {
                double std = Math.Sqrt(totalVariance / (numOfScenarios - 1));
                Result[1] = std / Math.Sqrt(numOfScenarios) * disFactor;
            }
            return(Result);
        }
 /// <summary>
 /// override the <method>DipathByAntitheticMethod</method>.
 /// For Black Scholes model, mu and sigma is constant, and only one random
 /// variate is needed to generate the state for next point
 /// </summary>
 public double[][] DipathByAntitheticMethod(StochasticAssetPrice S, GaussianGenerator nrandom,
     double totalTime, int timeSteps)
 {
     double[][] pricePath = new double[2][];
     pricePath[0] = new double[timeSteps + 1];
     pricePath[1] = new double[timeSteps + 1];
     StochasticAssetPrice S2 = new StochasticAssetPrice(S);
     double dt = totalTime / (double)timeSteps;
     pricePath[0][0] = S.CurrentPrice;
     pricePath[1][0] = S.CurrentPrice;
     for (int i = 1; i <= timeSteps; i++)
     {
         double z = nrandom.NextGaussian();
         double z2 = -z;
         pricePath[0][i] = GetNextPrice(S, dt, z);
         pricePath[1][i] = GetNextPrice(S2, dt, z2);
     }
     return pricePath;
 }
        /// <summary>
        /// override the <method>DipathByAntitheticMethod</method>.
        /// For Black Scholes model, mu and sigma is constant, and only one random
        /// variate is needed to generate the state for next point
        /// </summary>

        public double[][] DipathByAntitheticMethod(StochasticAssetPrice S, GaussianGenerator nrandom,
                                                   double totalTime, int timeSteps)
        {
            double[][] pricePath = new double[2][];
            pricePath[0] = new double[timeSteps + 1];
            pricePath[1] = new double[timeSteps + 1];
            StochasticAssetPrice S2 = new StochasticAssetPrice(S);
            double dt = totalTime / (double)timeSteps;

            pricePath[0][0] = S.CurrentPrice;
            pricePath[1][0] = S.CurrentPrice;
            for (int i = 1; i <= timeSteps; i++)
            {
                double z  = nrandom.NextGaussian();
                double z2 = -z;
                pricePath[0][i] = GetNextPrice(S, dt, z);
                pricePath[1][i] = GetNextPrice(S2, dt, z2);
            }
            return(pricePath);
        }
        /// <summary>
        /// override the <method>GeneratingRandomPricePath</method>.
        /// For Black Scholes model, mu and sigma is constant, and only one random
        /// variate is needed to generate the state for next point.
        /// </summary>

        public double[] GeneratingRandomPricePath(StochasticAssetPrice S, GaussianGenerator nrandom,
                                                  double totalTime, int timeSteps, bool visualizationFlag = false, Form1 form = null)
        {
            double[] pricePath = new double[timeSteps + 1];
            double   dt        = totalTime / (double)timeSteps;

            pricePath[0] = S.CurrentPrice;
            for (int i = 1; i <= timeSteps; i++)
            {
                double z = nrandom.NextGaussian();
                pricePath[i] = GetNextPrice(S, dt, z);
            }
            if (visualizationFlag & form != null)
            {
                double[] X = new double[timeSteps + 1];
                for (int i = 0; i <= timeSteps; i++)
                {
                    X[i] = i;
                }
                form.add(X, pricePath, "");
            }
            return(pricePath);
        }
 /// <summary>
 /// override the <method>DipathByAntitheticMethod</method>.
 /// For Black Scholes model, mu and sigma is constant, and only one random
 /// variate is needed to generate the state for next point
 /// </summary>
 public double[][] DipathByAntitheticMethod(StochasticAssetPrice S, GaussianGenerator nrandom,
     double totalTime, int timeSteps, bool visualizationFlag = false, Form1 form = null)
 {
     double[][] pricePath = new double[2][];
     pricePath[0] = new double[timeSteps + 1];
     pricePath[1] = new double[timeSteps + 1];
     StochasticAssetPrice S2 = new StochasticAssetPrice(S);
     double dt = totalTime / (double)timeSteps;
     pricePath[0][0] = S.CurrentPrice;
     pricePath[1][0] = S.CurrentPrice;
     for (int i = 1; i <= timeSteps; i++)
     {
         double z = nrandom.NextGaussian();
         double z2 = -z;
         pricePath[0][i] = GetNextPrice(S, dt, z);
         pricePath[1][i] = GetNextPrice(S2, dt, z2);
     }
     if (visualizationFlag & form != null)
     {
         double[] X = new double[timeSteps + 1];
         for (int i = 0; i <= timeSteps; i++)
         {
             X[i] = i;
         }
         form.add(X, pricePath[0], "");
         form.add(X, pricePath[1], "");
     }
     return pricePath;
 }
 /// <summary>
 /// override the <method>PricingByMCSim</method>
 /// In each scenario, a price path is generated and the option value under 
 /// this scenario is only decided by the final price. So, we take the average 
 /// for the 'value at maturity' of all the simulated scenarios and 
 /// discounted as the approximation for the option value. 
 /// This method also outputs the standard error for the approximation.
 /// </summary>
 public override double[] PricingByMCSim(StochasticAssetPrice S, IDiscretizationScheme D, int numOfScenarios,
     int timeSteps, bool antitheticFlag, bool visualizationFlag = false, Form1 form = null)
 {
     double[] Result = new double[2];                     //record the approximated price and standard error
     double[] Sample = new double[numOfScenarios];        //record value at maturity for each scenario
     double sumValue = 0.0;                               //record cumulative sum of Sample array
     GaussianGenerator nrand = new GaussianGenerator();
     if (antitheticFlag)
     {
         //when antithetic variance reduction technique is used
         for (int i = 0; i < numOfScenarios; i++)
         {
             StochasticAssetPrice S1 = new StochasticAssetPrice(S);
             double[][] PricePath;
             PricePath = D.DipathByAntitheticMethod(S1, nrand, T, timeSteps, visualizationFlag, form);
             Sample[i] = (ValueAtMaturity(PricePath[0][timeSteps])
                 + ValueAtMaturity(PricePath[1][timeSteps])) / 2.0;
             sumValue += Sample[i];
         }
     }
     else
     {
         //when antithetic variance reduction technique is not used
         for (int i = 0; i < numOfScenarios; i++)
         {
             StochasticAssetPrice S1 = new StochasticAssetPrice(S);
             double[] PricePath;
             PricePath = D.GeneratingRandomPricePath(S1, nrand, T, timeSteps, visualizationFlag, form);
             Sample[i] = ValueAtMaturity(PricePath[timeSteps]);
             sumValue += Sample[i];
         }
     }
     double disFactor = Math.Exp(-S.Mu * T);             //discount factor to convert value at maturity to current
     Result[0] = sumValue / (double)numOfScenarios * disFactor;
     double totalVariance = 0.0;
     for (int i = 0; i < numOfScenarios; i++)
     {
         totalVariance += Math.Pow(Sample[i] - Result[0], 2.0);
     }
     if (numOfScenarios > 1)
     {
         double std = Math.Sqrt(totalVariance / (numOfScenarios - 1));
         Result[1] = std / Math.Sqrt(numOfScenarios) * disFactor;
     }
     return Result;
 }
 /// <summary>
 /// override the <method>GeneratingRandomPricePath</method>.
 /// For Black Scholes model, mu and sigma is constant, and only one random
 /// variate is needed to generate the state for next point.
 /// </summary>
 public double[] GeneratingRandomPricePath(StochasticAssetPrice S, GaussianGenerator nrandom,
     double totalTime, int timeSteps, bool visualizationFlag = false, Form1 form = null)
 {
     double[] pricePath = new double[timeSteps + 1];
     double dt = totalTime / (double)timeSteps;
     pricePath[0] = S.CurrentPrice;
     for (int i = 1; i <= timeSteps; i++)
     {
         double z = nrandom.NextGaussian();
         pricePath[i] = GetNextPrice(S, dt, z);
     }
     if (visualizationFlag & form != null)
     {
         double[] X = new double[timeSteps + 1];
         for (int i = 0; i <= timeSteps; i++)
         {
             X[i] = i;
         }
         form.add(X, pricePath, "");
     }
     return pricePath;
 }
 /// <summary>
 /// override the <method>GeneratingRandomPricePath</method>.
 /// For Black Scholes model, mu and sigma is constant, and only one random
 /// variate is needed to generate the state for next point.
 /// </summary>
 public double[] GeneratingRandomPricePath(StochasticAssetPrice S, GaussianGenerator nrandom,
     double totalTime, int timeSteps)
 {
     double[] pricePath = new double[timeSteps + 1];
     double dt = totalTime / (double)timeSteps;
     pricePath[0] = S.CurrentPrice;
     for (int i = 1; i <= timeSteps; i++)
     {
         double z = nrandom.NextGaussian();
         pricePath[i] = GetNextPrice(S, dt, z);
     }
     return pricePath;
 }