//Cumulative Normale
double CN(double x){
	static double a[5] = {0.319381530, -0.356563782, 1.781477937, -1.821255978, 1.330274429};
    double result;

	if(x<-7.0) result = NormalDensity(x) / sqrt(1.+x*x); 
	else{
	   if(x>7.0) result = 1.0 - CN(-x);
	   else{
	       double tmp = 1.0 / (1.0+0.2316419*fabs(x));
		   result = 1 - NormalDensity(x)* (tmp*(a[0]+tmp*(a[1]+tmp*(a[2]+tmp*(a[3]+tmp*a[4])))));
		   if(x<=0.0) result = 1.0 - result;
	   }
	}
	return result;
}
double BlackAndScholes(	double	dRateDomestic,
						double	dRateForeign,
						double	dHTBRate,
						double	dSpotPrice,
						double	dStrike,
						double  dYte,
						double	dVolatility,
						bool	bIsCall,
						double* pdDivAmnt,						   
						double* pdDivYte,						    
						int		nDivCount,
						GREEKS*	pGreeks/*out*/)
{
	if (dYte < 0.) 
		return max(bIsCall ? dSpotPrice - dStrike : dStrike - dSpotPrice, 0.);

	double dContRd = RateDiscToCont(dRateDomestic , dYte * cdDaysPerYear365);
	double dContRf = RateDiscToCont(dRateForeign, dYte * cdDaysPerYear365);
	//double dContRb = IsBadValue(dHTBRate) ? 0 : (dContRd - RateDiscToCont(dHTBRate, nDte)); //actual stock borrowing rate (b in spec)
	double dContRb = IsBadValue(dHTBRate) ? 0 : (RateDiscToCont(dRateDomestic - dHTBRate, dYte * cdDaysPerYear365)); //actual stock borrowing rate (b in spec)
	
	double dDiscountedSpot = DiscountForDividends(dSpotPrice,
												dContRd,
												dContRb,
												pdDivAmnt,
												pdDivYte,
												nDivCount,
												dYte);
	if(IsBadValue(dDiscountedSpot))
		return BadDoubleValue;

	const double A = dDiscountedSpot * (1. / exp( (dContRf + dContRb) * dYte));
	const double B = dStrike * (1. / exp( dContRd * dYte));

	const double D = dVolatility * sqrt(dYte);

	const double d1	= log(dDiscountedSpot/dStrike) / D + D * (((dContRd - (dContRf + dContRb)) / (dVolatility * dVolatility)) + .5);
	const double d2	= d1 - D;

	const double N1	= NormalC(d1);
	const double N2	= NormalC(d2);

	long cCalculatedGreeksMask = GT_NOTHING;

	double dTheoPrice = bIsCall ? A * N1 - B * N2 : A * ( N1 - 1.) - B * ( N2 - 1.);
	
	if(!IsValidTheoPrice(dTheoPrice))
	{
		dTheoPrice = BadDoubleValue;
	}
	else
	{
		cCalculatedGreeksMask |= GT_THEOPRICE;
	}

	if(pGreeks && pGreeks->nMask & ~GT_THEOPRICE && !IsBadValue(dTheoPrice))
	{
        const double	dd1dS = 1. / (D * dDiscountedSpot);
        const double	dd1dT = -d2 / (2. * dYte) + (dContRd - (dContRf + dContRb)) / D;
        const double	dd2dT = -d1 / (2. * dYte) + (dContRd - (dContRf + dContRb)) / D;
        const double	dd1dVol = - d2 / dVolatility;
        const double	dd2dVol = - d1 / dVolatility;
        const double	dd12dR = dYte / D;
		const double	d2d1dVol2 = 2 * log(A/B) / (D * dVolatility * dVolatility);
        const double    n1 = NormalDensity(d1);
        const double    n2 = NormalDensity(d2);
		

    	// DELTA
	    if (pGreeks->nMask & GT_DELTA)
	    {
            pGreeks->dDelta = exp(-(dContRf + dContRb) * dYte) * (N1 - (bIsCall ? 0. : 1.));
		    cCalculatedGreeksMask |= GT_DELTA;
	    }

    	// GAMMA
	    if (pGreeks->nMask & GT_GAMMA)
	    {
		    pGreeks->dGamma = exp(-(dContRf + dContRb) * dYte) * dd1dS * n1;
		    cCalculatedGreeksMask |= GT_GAMMA;
	    }

	    // THETA
	    if (pGreeks->nMask & GT_THETA)
	    {
		    pGreeks->dTheta = ( A * ((dContRf + dContRb) * (N1 - (bIsCall ? 0. : 1.)) - dd1dT * n1) -
                B * (dContRd * (N2 - (bIsCall ? 0. : 1.)) - dd2dT * n2) ) * cdDeltaTime;            
		    cCalculatedGreeksMask |= GT_THETA;
	    }

	    // DELTA THETA
	    if (pGreeks->nMask & GT_DELTA_THETA)
	    {
		    pGreeks->dDeltaTheta = ( (dContRf + dContRb) * (pGreeks->dDelta - (bIsCall ? 0. : exp(-(dContRf + dContRb) * dYte) )) - 
				pGreeks->dGamma * dd1dT / dd1dS ) * cdDeltaTime;
		    cCalculatedGreeksMask |= GT_DELTA_THETA;
	    }

	    // GAMMA THETA
	    if (pGreeks->nMask & GT_GAMMA_THETA)
	    {
		    pGreeks->dGammaTheta = ( pGreeks->dGamma * ((dContRf + dContRb) + 1. / (2. * dYte) + d1 * dd1dT) ) * cdDeltaTime;
		    cCalculatedGreeksMask |= GT_GAMMA_THETA;
	    }

        // DELTA VEGA
        if (pGreeks->nMask & GT_DELTA_VEGA)
        {
	        pGreeks->dDeltaVega = ( pGreeks->dGamma * dd1dVol / dd1dS ) * cdDeltaVolatility;
	        cCalculatedGreeksMask |= GT_DELTA_VEGA;
        }

        // GAMMA VEGA
        if (pGreeks->nMask & GT_GAMMA_VEGA)
        {
	        pGreeks->dGammaVega = ( -pGreeks->dGamma * ( 1. / dVolatility + d1 * dd1dVol) ) * cdDeltaVolatility;
	        cCalculatedGreeksMask |= GT_GAMMA_VEGA;
        }

        // VEGA
        if (pGreeks->nMask & GT_VEGA)
        {
	        pGreeks->dVega = ( A * dd1dVol * n1 - B * dd2dVol * n2 ) * cdDeltaVolatility;
	        cCalculatedGreeksMask |= GT_VEGA;
        }

        // RHO
        if (pGreeks->nMask & GT_RHO)
   	    {
	        pGreeks->dRho = ( (A * n1 - B * n2) * dd12dR + dYte * B * (N2 - (bIsCall ? 0. : 1.)) ) * cdDeltaRate;
	        cCalculatedGreeksMask |= GT_RHO;
        }

		// VOLGA
		if (pGreeks->nMask & GT_VOLGA)
		{
			pGreeks->dVolga = (A * n1 * (-d1 * (dd1dVol * dd1dVol) + d2d1dVol2) - 
							   B * n2 * (-d2 * (dd2dVol * dd2dVol) + d2d1dVol2)) * cdDeltaVolatility * cdDeltaVolatility;
			cCalculatedGreeksMask |= GT_VOLGA;
		}

		// Check values
		if (cCalculatedGreeksMask & GT_DELTA)
			pGreeks->dDelta = min(max(pGreeks->dDelta, -1.), 1.);
				
		if (cCalculatedGreeksMask & GT_GAMMA)
			pGreeks->dGamma = max(pGreeks->dGamma, 0.);

		if (cCalculatedGreeksMask & GT_VEGA)
			pGreeks->dVega = max(pGreeks->dVega, 0.);

		if (cCalculatedGreeksMask & GT_THETA)
			pGreeks->dTheta = min(pGreeks->dTheta, 0.);

		// ALPHA
		if ((pGreeks->nMask & GT_ALPHA) && (ValueNEQZero(pGreeks->dTheta)))
		{
			pGreeks->dAlpha = pGreeks->dGamma / pGreeks->dTheta;
			cCalculatedGreeksMask |= GT_ALPHA;
		}
	}
	// Set up greeks mask
	if(pGreeks)
		pGreeks->nMask = cCalculatedGreeksMask;

	return  dTheoPrice;
}