FdmDividendHandler::FdmDividendHandler(
    const DividendSchedule& schedule,
    const boost::shared_ptr<FdmMesher>& mesher,
    const Date& referenceDate,
    const DayCounter& dayCounter,
    Size equityDirection)
    : x_(mesher->layout()->dim()[equityDirection]),
      mesher_(mesher),
      equityDirection_(equityDirection) {

    dividends_.reserve(schedule.size());
    dividendDates_.reserve(schedule.size());
    dividendTimes_.reserve(schedule.size());
    for (DividendSchedule::const_iterator iter=schedule.begin();
            iter!=schedule.end(); ++iter) {
        dividends_.push_back((*iter)->amount());
        dividendDates_.push_back((*iter)->date());
        dividendTimes_.push_back(
            dayCounter.yearFraction(referenceDate,(*iter)->date()));
    }

    Array tmp = mesher_->locations(equityDirection);
    Size spacing = mesher_->layout()->spacing()[equityDirection];
    for (Size i = 0; i < x_.size(); ++i) {
        x_[i] = std::exp(tmp[i*spacing]);
    }
}
    FdmBlackScholesMesher::FdmBlackScholesMesher(
        Size size,
        const ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
        Time maturity, Real strike,
        Real xMinConstraint, Real xMaxConstraint,
        Real eps, Real scaleFactor,
        const std::pair<Real, Real>& cPoint,
        const DividendSchedule& dividendSchedule)
    : Fdm1dMesher(size) {

        const Real S = process->x0();
        QL_REQUIRE(S > 0.0, "negative or null underlying given");

        std::vector<std::pair<Time, Real> > intermediateSteps;
        for (Size i=0; i < dividendSchedule.size()
            && process->time(dividendSchedule[i]->date()) <= maturity; ++i)
            intermediateSteps.push_back(
                std::make_pair(
                    process->time(dividendSchedule[i]->date()),
                    dividendSchedule[i]->amount()
                ) );

        const Size intermediateTimeSteps = std::max<Size>(2, Size(24.0*maturity));
        for (Size i=0; i < intermediateTimeSteps; ++i)
            intermediateSteps.push_back(
                std::make_pair((i+1)*(maturity/intermediateTimeSteps), 0.0));

        std::sort(intermediateSteps.begin(), intermediateSteps.end());

        const Handle<YieldTermStructure> rTS = process->riskFreeRate();
        const Handle<YieldTermStructure> qTS = process->dividendYield();

        Time lastDivTime = 0.0;
        Real fwd = S, mi = S, ma = S;

        for (Size i=0; i < intermediateSteps.size(); ++i) {
            const Time divTime = intermediateSteps[i].first;
            const Real divAmount = intermediateSteps[i].second;

            fwd = fwd / rTS->discount(divTime) * rTS->discount(lastDivTime)
                      * qTS->discount(divTime) / qTS->discount(lastDivTime);

            mi  = std::min(mi, fwd); ma = std::max(ma, fwd);

            fwd-= divAmount;

            mi  = std::min(mi, fwd); ma = std::max(ma, fwd);

            lastDivTime = divTime;
        }

        // Set the grid boundaries
        const Real normInvEps = InverseCumulativeNormal()(1-eps);
        const Real sigmaSqrtT 
            = process->blackVolatility()->blackVol(maturity, strike)
                                                        *std::sqrt(maturity);
        
        Real xMin = std::log(mi) - sigmaSqrtT*normInvEps*scaleFactor;
        Real xMax = std::log(ma) + sigmaSqrtT*normInvEps*scaleFactor;

        if (xMinConstraint != Null<Real>()) {
            xMin = xMinConstraint;
        }
        if (xMaxConstraint != Null<Real>()) {
            xMax = xMaxConstraint;
        }

        ext::shared_ptr<Fdm1dMesher> helper;
        if (   cPoint.first != Null<Real>() 
            && std::log(cPoint.first) >=xMin && std::log(cPoint.first) <=xMax) {
            
            helper = ext::shared_ptr<Fdm1dMesher>(
                new Concentrating1dMesher(xMin, xMax, size, 
                    std::pair<Real,Real>(std::log(cPoint.first),
                                         cPoint.second)));
        }
        else {
            helper = ext::shared_ptr<Fdm1dMesher>(
                                        new Uniform1dMesher(xMin, xMax, size));
            
        }
        
        locations_ = helper->locations();
        for (Size i=0; i < locations_.size(); ++i) {
            dplus_[i]  = helper->dplus(i);
            dminus_[i] = helper->dminus(i);
        }
    }