/// <summary>
 /// This method gives the estimated the option value 
 /// and the estimation standard error using MC simulation
 /// </summary>
 /// <param name="S"> describes the Brownian motion rule of the underlying asset</param>
 /// <param name="D"> chooses the discretization scheme </param>
 /// <param name="numberOfScenarios">decides the number of random paths to be generated</param>
 /// <param name="timeSteps"> decides the number of intervals for discretization scheme </param>
 /// <param name="antitheticFlag"> chooses whether to use antithetic variance reduction techniques</param>
 /// <param name="visualizationFlag"> chooses whether to generate 
 /// the asset price chart for MC simulation</param>
 /// <param name="form"> defines the window to display the graph</param>
 /// <returns> a 2 elements array. The first one gives the estimated option price, 
 /// the second gives the estimation error</returns>
 public abstract double[] PricingByMCSim(StochasticAssetPrice S, IDiscretizationScheme D,
     int numberOfScenarios, int timeSteps, bool antitheticFlag, bool visualizationFlag, Form1 form = null);
 /// <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;
 }