void test_linear_ring()
{
    trace.beginBlock("linear ring");

    const Domain domain(Point(-5,-5), Point(5,5));

    typedef DiscreteExteriorCalculus<1, 2, EigenLinearAlgebraBackend> Calculus;
    Calculus calculus;
    calculus.initKSpace<Domain>(domain);

    for (int kk=-8; kk<10; kk++) calculus.insertSCell( calculus.myKSpace.sCell(Point(-8,kk), kk%2 == 0 ? Calculus::KSpace::POS : Calculus::KSpace::NEG) );
    for (int kk=-8; kk<10; kk++) calculus.insertSCell( calculus.myKSpace.sCell(Point(kk,10), kk%2 == 0 ? Calculus::KSpace::POS : Calculus::KSpace::NEG) );
    for (int kk=10; kk>-8; kk--) calculus.insertSCell( calculus.myKSpace.sCell(Point(10,kk)) );
    for (int kk=10; kk>-8; kk--) calculus.insertSCell( calculus.myKSpace.sCell(Point(kk,-8)) );
    calculus.updateIndexes();

    {
        trace.info() << calculus << endl;
        Board2D board;
        board << domain;
        board << calculus;
        board.saveSVG("ring_structure.svg");
    }

    const Calculus::PrimalDerivative0 d0 = calculus.derivative<0, PRIMAL>();
    display_operator_info("d0", d0);

    const Calculus::PrimalHodge0 h0 = calculus.hodge<0, PRIMAL>();
    display_operator_info("h0", h0);

    const Calculus::DualDerivative0 d0p = calculus.derivative<0, DUAL>();
    display_operator_info("d0p", d0p);

    const Calculus::PrimalHodge1 h1 = calculus.hodge<1, PRIMAL>();
    display_operator_info("h1", h1);

    const Calculus::PrimalIdentity0 laplace = calculus.laplace<PRIMAL>();
    display_operator_info("laplace", laplace);

    const int laplace_size = calculus.kFormLength(0, PRIMAL);
    const Eigen::MatrixXd laplace_dense(laplace.myContainer);

    for (int ii=0; ii<laplace_size; ii++)
        FATAL_ERROR( laplace_dense(ii,ii) == 2 );

    FATAL_ERROR( laplace_dense.array().rowwise().sum().abs().sum() == 0 );
    FATAL_ERROR( laplace_dense.transpose() == laplace_dense );

    trace.endBlock();
}
void
set_initial_phase_null(Calculus& calculus, Eigen::VectorXcd& initial_wave)
{
    typedef typename Calculus::Cell Cell;
    typedef typename Calculus::Index Index;
    typedef typename Calculus::Point Point;

    //! [time_phase_null]
    for (int xx=9; xx<13; xx++)
    for (int yy=13; yy<17; yy++)
    {
        const Point point(xx,yy);
        const Cell cell = calculus.myKSpace.uSpel(point);
        const Index index = calculus.getCellIndex(cell);
        initial_wave(index) = 1;
    }
    //! [time_phase_null]
}
void
set_initial_phase_dir_yy(Calculus& calculus, Eigen::VectorXcd& initial_wave)
{
    typedef typename Calculus::Cell Cell;
    typedef typename Calculus::Index Index;
    typedef typename Calculus::Point Point;

    //! [time_phase_yy]
    for (int xx=9; xx<13; xx++)
    for (int yy=13; yy<17; yy++)
    {
        const Point point(xx,yy);
        const Cell cell = calculus.myKSpace.uSpel(point);
        const Index index = calculus.getCellIndex(cell);
        initial_wave(index) = std::exp(std::complex<double>(0,2*M_PI*(yy-14.5)/8));
    }
    //! [time_phase_yy]
}
void test_linear_structure()
{
    trace.beginBlock("creating dec problem with neumann border condition");

    //! [neumann-creation]
    typedef DiscreteExteriorCalculus<1, 2, EigenLinearAlgebraBackend> Calculus;

    typedef std::list<Calculus::SCell> SCells;
    SCells cells;

    // fill cells container
    {
        const Calculus::KSpace kspace;

        for (int kk=20; kk>0; kk--) cells.push_back( kspace.sCell(Point(0,kk), kk%2 != 0 ? Calculus::KSpace::NEG : Calculus::KSpace::POS) );
        for (int kk=0; kk<10; kk++) cells.push_back( kspace.sCell(Point(kk,0)) );
        for (int kk=0; kk<10; kk++) cells.push_back( kspace.sCell(Point(10,kk)) );
        cells.push_back( kspace.sCell(Point(10,10)) );
        cells.push_back( kspace.sCell(Point(9,10), Calculus::KSpace::NEG) );
        for (int kk=10; kk<20; kk++) cells.push_back( kspace.sCell(Point(8,kk)) );
        for (int kk=8; kk<12; kk++) cells.push_back( kspace.sCell(Point(kk,20)) );
        for (int kk=20; kk>0; kk--) cells.push_back( kspace.sCell(Point(12,kk), kk%2 != 0 ? Calculus::KSpace::NEG : Calculus::KSpace::POS) );
        cells.push_back( kspace.sCell(Point(12,0)) );
    }

    // fill calculus
    const Domain domain(Point(-1,-1), Point(10,10));

    // create DEC structure
    Calculus calculus;
    calculus.initKSpace<Domain>(domain);
    for (SCells::const_iterator ci=cells.begin(), ce=cells.end(); ci!=ce; ci++) calculus.insertSCell( *ci );
    calculus.updateIndexes();
    //! [neumann-creation]

    trace.info() << calculus << endl;

    //! [input-dirac]
    Calculus::PrimalForm0 dirac = Calculus::PrimalForm0::dirac(calculus, calculus.myKSpace.uCell(Point(10,4)));
    //! [input-dirac]

    {
        Board2D board;
        board << domain;
        board << calculus;
        board << dirac;
        board.saveSVG("linear_structure_neumann_dirac.svg");
    }

    trace.endBlock();

    {
        trace.beginBlock("solving problem with neumann border condition using sparse qr solver");

        //! [neumann-laplace-definition]
        const Calculus::PrimalIdentity0 laplace = calculus.laplace<PRIMAL>();
        //! [neumann-laplace-definition]
        trace.info() << "laplace = " << laplace << endl;

        const Calculus::PrimalIdentity0 reorder = calculus.reorder<0, PRIMAL>(cells.begin(), cells.end());
        const Calculus::PrimalIdentity0 laplace_ordered = reorder * laplace * reorder.transpose();
        trace.info() << Eigen::MatrixXd(laplace_ordered.myContainer) << endl;

        //! [neumann-solve]
        typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver;
        typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> Solver;

        Solver solver;
        solver.compute(laplace);
        Calculus::PrimalForm0 solved_solution = solver.solve(dirac);
        //! [neumann-solve]
        solved_solution.myContainer.array() -= solved_solution.myContainer(38);
        solved_solution.myContainer.array() /= solved_solution.myContainer.maxCoeff();
        const Calculus::PrimalForm0 solved_solution_ordered = reorder * solved_solution;

        Calculus::PrimalForm0 analytic_solution(calculus);
        {
            const Calculus::Index dirac_position = 17;
            const Calculus::Index length = analytic_solution.length();
            for (Calculus::Index kk=0; kk<length; kk++)
            {
                Calculus::Scalar alpha = 1. * (kk)/dirac_position * (kk+1.)/(dirac_position+1.);
                if (kk>dirac_position)
                {
                    alpha = 1. * (length-kk)/dirac_position * (length-kk-1.)/(dirac_position+1);
                    alpha -= 1. * (length-dirac_position)/dirac_position * (length-dirac_position-1.)/(dirac_position+1);
                    alpha += 1;
                }
                analytic_solution.myContainer(kk) = alpha;
            }
        }

        trace.info() << solver.isValid() << " " << solver.myLinearAlgebraSolver.info() << endl;

        {
            std::ofstream handle("linear_structure_neumann.dat");
            for (Calculus::Index kk=0; kk<analytic_solution.length(); kk++)
                handle << solved_solution_ordered.myContainer(kk) << " " << analytic_solution.myContainer(kk) << endl;
        }

        FATAL_ERROR( (solved_solution_ordered-analytic_solution).myContainer.array().abs().maxCoeff() < 1e-5 );

        {
            Board2D board;
            board << domain;
            board << calculus;
            board << solved_solution;
            board.saveSVG("linear_structure_neumann_solution.svg");
        }

        {
            Calculus::PrimalForm1 solved_solution_gradient = calculus.derivative<0, PRIMAL>() * solved_solution;
            Board2D board;
            board << domain;
            board << calculus;
            board << solved_solution_gradient;
            board << CustomStyle("VectorField", new VectorFieldStyle2D(1));
            board << calculus.sharp(solved_solution_gradient);
            board.saveSVG("linear_structure_neumann_solution_gradient.svg");
        }

        trace.endBlock();
    }

    trace.beginBlock("creating dec problem with dirichlet border condition");

    //! [dirichlet-creation]
    calculus.insertSCell( calculus.myKSpace.sCell(Point(13,0)) );
    calculus.insertSCell( calculus.myKSpace.sCell(Point(1,20), Calculus::KSpace::NEG) );
    calculus.updateIndexes();
    //! [dirichlet-creation]

    {
        Board2D board;
        board << domain;
        board << calculus;
        board << dirac;
        board.saveSVG("linear_structure_dirichlet_dirac.svg");
    }

    trace.endBlock();

    {
        trace.beginBlock("solving problem with dirichlet border condition using sparse qr solver");

        //! [dirichlet-laplace-definition]
        const Calculus::PrimalIdentity0 laplace = calculus.laplace<PRIMAL>();
        //! [dirichlet-laplace-definition]
        trace.info() << "laplace = " << laplace << endl;

        const Calculus::PrimalIdentity0 reorder = calculus.reorder<0, PRIMAL>(cells.begin(), cells.end());
        const Calculus::PrimalIdentity0 laplace_ordered = reorder * laplace * reorder.transpose();
        trace.info() << Eigen::MatrixXd(laplace_ordered.myContainer) << endl;

        //! [dirichlet-solve]
        typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver;
        typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> Solver;

        Solver solver;
        solver.compute(laplace);
        Calculus::PrimalForm0 solved_solution = solver.solve(dirac);
        //! [dirichlet-solve]
        solved_solution.myContainer.array() /= solved_solution.myContainer.maxCoeff();
        const Calculus::PrimalForm0 solved_solution_ordered = reorder * solved_solution;

        Calculus::PrimalForm0 analytic_solution(calculus);
        {
            const Calculus::Index dirac_position = 17;
            const Calculus::Index length = analytic_solution.length();
            for (Calculus::Index kk=0; kk<length; kk++)
            {
                Calculus::Scalar alpha = (kk+1.)/(dirac_position+1.);
                if (kk>dirac_position)
                {
                    alpha = 1. - (kk-dirac_position)/(1.*length-dirac_position);
                }
                analytic_solution.myContainer(kk) = alpha;
            }
        }

        trace.info() << solver.isValid() << " " << solver.myLinearAlgebraSolver.info() << endl;

        {
            std::ofstream handle("linear_structure_dirichlet.dat");
            for (Calculus::Index kk=0; kk<analytic_solution.length(); kk++)
                handle << solved_solution_ordered.myContainer(kk) << " " << analytic_solution.myContainer(kk) << endl;
        }

        FATAL_ERROR( (solved_solution_ordered-analytic_solution).myContainer.array().abs().maxCoeff() < 1e-5 );

        {
            Board2D board;
            board << domain;
            board << calculus;
            board << solved_solution;
            board.saveSVG("linear_structure_dirichlet_solution.svg");
        }

        {
            Calculus::PrimalForm1 solved_solution_gradient = calculus.derivative<0, PRIMAL>() * solved_solution;

            Board2D board;
            board << domain;
            board << calculus;
            board << solved_solution_gradient;
            board << calculus.sharp(solved_solution_gradient);
            board.saveSVG("linear_structure_dirichlet_solution_gradient.svg");
        }

        trace.endBlock();
    }

}
void test_manual_operators_3d()
{
    trace.beginBlock("testing 3d operators");

    const Z3i::Domain domain(Z3i::Point(0,0,0), Z3i::Point(1,1,1));

    typedef DiscreteExteriorCalculus<3, 3, EigenLinearAlgebraBackend> Calculus;

    Calculus calculus;
    calculus.initKSpace<Z3i::Domain>(domain);

    {   // filling primal calculus
        // 0-cells
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,0,0)) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,0,0)) );

        // 1-cells
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,1,0), Calculus::KSpace::POS) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,0,1), Calculus::KSpace::POS) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,0,0), Calculus::KSpace::POS) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,1,0), Calculus::KSpace::NEG) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,0,1), Calculus::KSpace::NEG) );

        // 2-cells
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,1,1), Calculus::KSpace::NEG) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,0,1), Calculus::KSpace::POS) ); //FIXME strange cell orientation
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,1,1), Calculus::KSpace::POS) );
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,1,0), Calculus::KSpace::NEG) );

        // 3-cells
        calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,1,1)) );

        calculus.updateIndexes();

        trace.info() << calculus << endl;
    }

    trace.beginBlock("base operators");

    {
        const Calculus::PrimalDerivative0 d0 = calculus.derivative<0, PRIMAL>();
        const Calculus::DualDerivative2 d2p = calculus.derivative<2, DUAL>();
        display_operator_info("d0", d0);
        display_operator_info("d2p", d2p);

        Eigen::MatrixXd d0_th(5, 2);
        d0_th <<
              -1,  0,
              -1,  0,
              -1,  1,
              0,  1,
              0,  1;

        FATAL_ERROR( Eigen::MatrixXd(d0.myContainer) == d0_th );
        FATAL_ERROR( Eigen::MatrixXd(d2p.myContainer) == d0_th.transpose() );
    }

    {
        const Calculus::PrimalDerivative1 d1 = calculus.derivative<1, PRIMAL>();
        const Calculus::DualDerivative1 d1p = calculus.derivative<1, DUAL>();
        display_operator_info("d1", d1);
        display_operator_info("d1p", d1p);

        Eigen::MatrixXd d1_th(4, 5);
        d1_th <<
              0, -1,  1,  0, -1,
              1,  0, -1,  1,  0,
              0,  0,  0, -1,  1,
              -1,  1,  0,  0,  0;

        FATAL_ERROR( Eigen::MatrixXd(d1.myContainer) == d1_th );
        FATAL_ERROR( Eigen::MatrixXd(d1p.myContainer) == d1_th.transpose() );
    }

    {
        const Calculus::PrimalDerivative2 d2 = calculus.derivative<2, PRIMAL>();
        const Calculus::DualDerivative0 d0p = calculus.derivative<0, DUAL>();
        display_operator_info("d2", d2);
        display_operator_info("d0p", d0p);

        Eigen::MatrixXd d2_th(1, 4);
        d2_th << -1, -1, -1, -1;

        FATAL_ERROR( Eigen::MatrixXd(d2.myContainer) == d2_th );
        FATAL_ERROR( Eigen::MatrixXd(d0p.myContainer) == d2_th.transpose() );
    }

    trace.endBlock();

    /*
    QApplication app(0, NULL);

    typedef Viewer3D<Z3i::Space, Z3i::KSpace> Viewer;
    Viewer* viewer = new Viewer(calculus.myKSpace);
    viewer->show();
    viewer->setWindowTitle("structure");
    (*viewer) << CustomColors3D(DGtal::Color(255,0,0), DGtal::Color(0,0,0));
    (*viewer) << domain;
    Display3DFactory<Z3i::Space, Z3i::KSpace>::draw(*viewer, calculus);
    (*viewer) << Viewer::updateDisplay;

    app.exec();
    */

    trace.endBlock();
}
void propa_2d()
{
    trace.beginBlock("2d propagation");

    typedef DiscreteExteriorCalculus<2, 2, EigenLinearAlgebraBackend> Calculus;
		typedef DiscreteExteriorCalculusFactory<EigenLinearAlgebraBackend> CalculusFactory;

    {
        trace.beginBlock("solving time dependent equation");

        const Calculus::Scalar cc = 8; // px/s
        trace.info() << "cc = " << cc << endl;

        const Z2i::Domain domain(Z2i::Point(0,0), Z2i::Point(29,29));
        const Calculus calculus = CalculusFactory::createFromDigitalSet(generateDiskSet(domain), false);

        //! [time_laplace]
        const Calculus::DualIdentity0 laplace = calculus.laplace<DUAL>() + 1e-8 * calculus.identity<0, DUAL>();
        //! [time_laplace]
        trace.info() << "laplace = " << laplace << endl;

        trace.beginBlock("finding eigen pairs");
        //! [time_eigen]
        typedef Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> EigenSolverMatrix;
        const EigenSolverMatrix eigen_solver(laplace.myContainer);

        const Eigen::VectorXd eigen_values = eigen_solver.eigenvalues();
        const Eigen::MatrixXd eigen_vectors = eigen_solver.eigenvectors();
        //! [time_eigen]
        trace.info() << "eigen_values_range = " << eigen_values.minCoeff() << "/" << eigen_values.maxCoeff() << endl;
        trace.endBlock();

        //! [time_omega]
        const Eigen::VectorXd angular_frequencies = cc * eigen_values.array().sqrt();
        //! [time_omega]

        Eigen::VectorXcd initial_wave = Eigen::VectorXcd::Zero(calculus.kFormLength(0, DUAL));
        //set_initial_phase_null(calculus, initial_wave);
        set_initial_phase_dir_yy(calculus, initial_wave);

        {
            Board2D board;
            board << domain;
            board << CustomStyle("KForm", new KFormStyle2D(-1, 1));
            board << Calculus::DualForm0(calculus, initial_wave.real());
            board.saveSVG("propagation_time_wave_initial_coarse.svg");
        }

        //! [time_init_proj]
        Eigen::VectorXcd initial_projections = eigen_vectors.transpose() * initial_wave;
        //! [time_init_proj]

        // low pass
        //! [time_low_pass]
        const Calculus::Scalar lambda_cutoff = 4.5;
        const Calculus::Scalar angular_frequency_cutoff = 2*M_PI * cc / lambda_cutoff;
        int cutted = 0;
        for (int kk=0; kk<initial_projections.rows(); kk++)
        {
            const Calculus::Scalar angular_frequency = angular_frequencies(kk);
            if (angular_frequency < angular_frequency_cutoff) continue;
            initial_projections(kk) = 0;
            cutted ++;
        }
        //! [time_low_pass]
        trace.info() << "cutted = " << cutted << "/" << initial_projections.rows() << endl;

        {
            const Eigen::VectorXcd wave = eigen_vectors * initial_projections;
            Board2D board;
            board << domain;
            board << CustomStyle("KForm", new KFormStyle2D(-1, 1));
            board << Calculus::DualForm0(calculus, wave.real());
            board.saveSVG("propagation_time_wave_initial_smoothed.svg");
        }

        const int kk_max = 60;
        trace.progressBar(0,kk_max);
        for (int kk=0; kk<kk_max; kk++)
        {
            //! [time_solve_time]
            const Calculus::Scalar time = kk/20.;
            const Eigen::VectorXcd current_projections = (angular_frequencies * std::complex<double>(0,time)).array().exp() * initial_projections.array();
            const Eigen::VectorXcd current_wave = eigen_vectors * current_projections;
            //! [time_solve_time]

            std::stringstream ss;
            ss << "propagation_time_wave_solution_" << std::setfill('0') << std::setw(3) << kk << ".svg";

            Board2D board;
            board << domain;
            board << CustomStyle("KForm", new KFormStyle2D(-1, 1));
            board << Calculus::DualForm0(calculus, current_wave.real());
            board.saveSVG(ss.str().c_str());

            trace.progressBar(kk+1,kk_max);
        }
        trace.info() << endl;

        trace.endBlock();
    }

    {
        trace.beginBlock("forced oscillations");

        const Z2i::Domain domain(Z2i::Point(0,0), Z2i::Point(50,50));
        const Calculus calculus = CalculusFactory::createFromDigitalSet(generateDiskSet(domain), false);

        const Calculus::DualIdentity0 laplace = calculus.laplace<DUAL>();
        trace.info() << "laplace = " << laplace << endl;

        trace.beginBlock("finding eigen pairs");
        typedef Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> EigenSolverMatrix;
        const EigenSolverMatrix eigen_solver(laplace.myContainer);

        const Eigen::VectorXd laplace_eigen_values = eigen_solver.eigenvalues();
        const Eigen::MatrixXd laplace_eigen_vectors = eigen_solver.eigenvectors();
        trace.info() << "eigen_values_range = " << laplace_eigen_values.minCoeff() << "/" << laplace_eigen_values.maxCoeff() << endl;
        trace.endBlock();

        Calculus::DualForm0 concentration(calculus);
        {
            const Z2i::Point point(25,25);
            const Calculus::Cell cell = calculus.myKSpace.uSpel(point);
            const Calculus::Index index = calculus.getCellIndex(cell);
            concentration.myContainer(index) = 1;
        }

        {
            Board2D board;
            board << domain;
            board << concentration;
            board.saveSVG("propagation_forced_concentration.svg");
        }

        for (int ll=0; ll<6; ll++)
        {
            //! [forced_lambda]
            const Calculus::Scalar lambda = 4*20.75/(1+2*ll);
            //! [forced_lambda]
            trace.info() << "lambda = " << lambda << endl;

            //! [forced_dalembert_eigen]
            const Eigen::VectorXd dalembert_eigen_values = (laplace_eigen_values.array() - (2*M_PI/lambda)*(2*M_PI/lambda)).array().inverse();
            const Eigen::MatrixXd concentration_to_wave = laplace_eigen_vectors * dalembert_eigen_values.asDiagonal() * laplace_eigen_vectors.transpose();
            //! [forced_dalembert_eigen]

            //! [forced_wave]
            Calculus::DualForm0 wave(calculus, concentration_to_wave * concentration.myContainer);
            //! [forced_wave]
            wave.myContainer /= wave.myContainer(calculus.getCellIndex(calculus.myKSpace.uSpel(Z2i::Point(25,25))));

            {
                trace.info() << "saving samples" << endl;
                const Calculus::Properties properties = calculus.getProperties();
                {
                    std::stringstream ss;
                    ss << "propagation_forced_samples_hv_" << ll << ".dat";
                    std::ofstream handle(ss.str().c_str());
                    for (int kk=0; kk<=50; kk++)
                    {
                        const Z2i::Point point_horizontal(kk,25);
                        const Z2i::Point point_vertical(25,kk);
                        const Calculus::Scalar xx = 2 * (kk/50. - .5);
                        handle << xx << " ";
                        handle << sample_dual_0_form<Calculus>(properties, wave, point_horizontal) << " ";
                        handle << sample_dual_0_form<Calculus>(properties, wave, point_vertical) << endl;
                    }
                }

                {
                    std::stringstream ss;
                    ss << "propagation_forced_samples_diag_" << ll << ".dat";
                    std::ofstream handle(ss.str().c_str());
                    for (int kk=0; kk<=50; kk++)
                    {
                        const Z2i::Point point_diag_pos(kk,kk);
                        const Z2i::Point point_diag_neg(kk,50-kk);
                        const Calculus::Scalar xx = 2 * sqrt(2) * (kk/50. - .5);
                        handle << xx << " ";
                        handle << sample_dual_0_form<Calculus>(properties, wave, point_diag_pos) << " ";
                        handle << sample_dual_0_form<Calculus>(properties, wave, point_diag_neg) << endl;
                    }
                }
            }

            {
                std::stringstream ss;
                ss << "propagation_forced_wave_" << ll << ".svg";
                Board2D board;
                board << domain;
                board << CustomStyle("KForm", new KFormStyle2D(-.5, 1));
                board << wave;
                board.saveSVG(ss.str().c_str());
            }
        }

        trace.endBlock();
    }
    trace.endBlock();
}
int main(int argc, char* argv[])
{
    using DGtal::trace;
    using std::endl;
    using DGtal::PRIMAL;
    using DGtal::DUAL;

    const Options options = parse_options(argc, argv);

    const KSpace kspace;

    const Calculus calculus;
    const FlatVector original_face_normals;

    if (!ends_with(options.image_filename, ".csv"))
    {
        ASSERT( !options.image_filename.empty() );
        typedef DGtal::Z3i::Domain Domain;
        typedef DGtal::ImageSelector<Domain, unsigned char>::Type ImageUChar;
        trace.info() << "image_filename=" << options.image_filename << endl;
        ImageUChar image_uchar = DGtal::GenericReader<ImageUChar>::import(options.image_filename);
        const Domain domain = image_uchar.domain();
        trace.info() << "domain=" << domain << endl;
        const Point center = (domain.upperBound()+domain.lowerBound())/2;
        trace.info() << "center=" << center << endl;
        const ImageShape<ImageUChar> shape(&image_uchar, center);

        const_cast<KSpace&>(kspace).init(domain.lowerBound()-center-Point::diagonal(1), domain.upperBound()-center+Point::diagonal(1), true);

        std::tie(const_cast<Calculus&>(calculus), const_cast<FlatVector&>(original_face_normals)) =
            initCalculusAndNormalsWithNoise(kspace, shape, options.normal_radius, options.noise_level);
    }

    if (ends_with(options.image_filename, ".csv"))
    {
        ASSERT( !options.image_filename.empty() );
        trace.info() << "csv_filename=" << options.image_filename << endl;
        std::tie(const_cast<Calculus&>(calculus), const_cast<FlatVector&>(original_face_normals)) =
            initCalculusAndNormalsFromSurfelNormalsCSV(options.image_filename);
    }

    const FlatVector original_vertex_normals = vertexNormals(calculus, original_face_normals);

    const FlatVector original_positions;
    const FlatVector regularized_positions;
    const FlatVector original_centers;
    const FlatVector regularized_centers;
    std::tie(const_cast<FlatVector&>(original_positions),
             const_cast<FlatVector&>(regularized_positions),
             const_cast<FlatVector&>(original_centers),
             const_cast<FlatVector&>(regularized_centers)) =
             approximateSurface(calculus, original_face_normals,
             ApproxParams({options.regularization_position, options.regularization_center, options.align, options.fairness, options.barycenter}));
    ASSERT( original_positions.size() == 3*calculus.kFormLength(0, PRIMAL) );
    ASSERT( regularized_positions.size() == 3*calculus.kFormLength(0, PRIMAL) );
    ASSERT( original_centers.size() == 3*calculus.kFormLength(2, PRIMAL) );
    ASSERT( regularized_centers.size() == 3*calculus.kFormLength(2, PRIMAL) );

    {
      trace.beginBlock( "computing energies" );

      {
        double position_energy = 0;
        double align_energy    = 0;
        std::tie( position_energy, align_energy ) = approximateSurfaceEnergies(
        calculus, original_face_normals, original_positions );
        align_energy *= options.align;
        position_energy *= options.regularization_position;
        trace.info() << "original_energies=" << position_energy << " "
                     << align_energy << " " << position_energy + align_energy
                     << endl;
        }

        {
            double position_energy = 0;
            double align_energy = 0;
            std::tie(position_energy, align_energy) = approximateSurfaceEnergies(calculus, original_face_normals, regularized_positions);
            align_energy *= options.align;
            position_energy *= options.regularization_position;
            trace.info() << "regularized_energies=" << position_energy << " " << align_energy << " " << position_energy+align_energy << endl;
        }

        trace.endBlock();
    }

    {
        ASSERT( !options.regularized_obj_filename.empty() );
        trace.info() << "regularized_obj_filename=" << options.regularized_obj_filename << endl;
        exportOBJ(calculus, regularized_positions, options.regularized_obj_filename);
    }

    if (!options.cubical_obj_filename.empty())
    {
        ASSERT( !options.cubical_obj_filename.empty() );
        trace.info() << "cubical_obj_filename=" << options.cubical_obj_filename << endl;
        exportOBJ(calculus, original_positions, options.cubical_obj_filename);
    }

    return 0;
}
int main( int argc, char* argv[] )
{
  using namespace Z2i;
  typedef ImageSelector<Domain, unsigned char>::Type Image;

  // parse command line ----------------------------------------------
  namespace po = boost::program_options;
  po::options_description general_opt("Allowed options are: ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input,i", po::value<string>(), "the input image filename." )
    ("output,o", po::value<string>()->default_value( "AT" ), "the output image basename." )
    ("lambda,l", po::value<double>(), "the parameter lambda." )
    ("lambda-1,1", po::value<double>()->default_value( 0.3125 ), "the initial parameter lambda (l1)." ) // 0.3125
    ("lambda-2,2", po::value<double>()->default_value( 0.00005 ), "the final parameter lambda (l2)." )
    ("lambda-ratio,q", po::value<double>()->default_value( sqrt(2) ), "the division ratio for lambda from l1 to l2." )
    ("alpha,a", po::value<double>()->default_value( 1.0 ), "the parameter alpha." )
    ("epsilon,e", po::value<double>()->default_value( 4.0/64.0 ), "the parameter epsilon." )
    //("gridstep,g", po::value<double>()->default_value( 1.0 ), "the parameter h, i.e. the gridstep." )
    ("nbiter,n", po::value<int>()->default_value( 10 ), "the maximum number of iterations." )
    ("sigma,s", po::value<double>()->default_value( 2.0 ), "the parameter of the first convolution." )
    ("rho,r", po::value<double>()->default_value( 3.0 ), "the parameter of the second convolution." )
    ("image-size,t", po::value<double>()->default_value( 64.0 ), "the size of the image." )
    ;

  bool parseOK=true;
  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, general_opt), vm);
  } catch ( const exception& ex ) {
    parseOK = false;
    cerr << "Error checking program options: "<< ex.what()<< endl;
  }
  po::notify(vm);
  if ( ! parseOK || vm.count("help") || !vm.count("input") )
    {
      cerr << "Usage: " << argv[0] << " -i toto.pgm\n"
                << "Computes the Ambrosio-Tortorelli reconstruction/segmentation of an input image."
                << endl << endl
                << " / "
                << endl
                << " | a.(u-g)^2 + v^2 |grad u|^2 + le.|grad v|^2 + (l/4e).(1-v)^2 "
                << endl
                << " / "
                << endl << endl
                << general_opt << "\n";
      return 1;
    }
  string f1 = vm[ "input" ].as<string>();
  string f2 = vm[ "output" ].as<string>();
  double l1  = vm[ "lambda-1" ].as<double>();
  double l2  = vm[ "lambda-2" ].as<double>();
  double lr  = vm[ "lambda-ratio" ].as<double>();
  if ( vm.count( "lambda" ) ) l1 = l2 = vm[ "lambda" ].as<double>();
  if ( l2 > l1 ) l2 = l1;
  if ( lr <= 1.0 ) lr = sqrt(2);
  double a  = vm[ "alpha" ].as<double>();
  double e  = vm[ "epsilon" ].as<double>();
  double t  = vm[ "image-size" ].as<double>();
  //double h  = vm[ "gridstep" ].as<double>();
  double h = 1.0 / t;
  e = 4.0*h;

  int    n  = vm[ "nbiter" ].as<int>();
  int    s  = vm[ "sigma" ].as<double>();
  int    r  = vm[ "rho" ].as<double>();

  trace.beginBlock("Reading image");
  Image image = GenericReader<Image>::import( f1 );
  trace.endBlock();
  
  // MODIF : ouverture du fichier pour les resultats ***********************************************************************
  const string file = f2 + ".txt";
  ofstream f(file.c_str());
  f << "#  l \t" << " a \t" << " e \t" << "a(u-g)^2 \t" << "v^2|grad u|^2 \t" << "  le|grad v|^2 \t" << "  l(1-v)^2/4e \t" << " l.per \t" << "AT tot"<< endl; 
  // ***********************************************************************************************************************

  trace.beginBlock("Creating calculus");
  typedef DiscreteExteriorCalculus<2,2, EigenLinearAlgebraBackend> Calculus;
  Domain domain = image.domain();
  Point p0 = domain.lowerBound(); p0 *= 2;
  Point p1 = domain.upperBound(); p1 *= 2;
  Domain kdomain( p0, p1 );
  Image dbl_image( kdomain );
  Calculus calculus;
  const KSpace& K = calculus.myKSpace;
  // Les pixels sont des 0-cellules du primal.
  for ( Domain::ConstIterator it = kdomain.begin(), itE = kdomain.end(); it != itE; ++it )
    calculus.insertSCell( K.sCell( *it ) ); // ajoute toutes les cellules de Khalimsky.
  calculus.updateIndexes();
  trace.info() << calculus << endl;
  Calculus::PrimalForm0 g( calculus );
  for ( Calculus::Index index = 0; index < g.myContainer.rows(); index++)
    {
      const Calculus::SCell& cell = g.getSCell( index );
      g.myContainer( index ) = ((double) image( K.sCoords( cell ) )) /
255.0;
    }
//  {
//    Board2D board;
//    board << calculus;
//    board << CustomStyle( "KForm", new KFormStyle2D( 0.0, 1.0 ) )
//          << g;
//    string str_calculus = f2 + "-calculus.eps";
//    board.saveEPS( str_calculus.c_str() );
//  }
  trace.endBlock();

  trace.beginBlock("building AT functionnals");
  trace.info() << "primal_D0" << endl;
  const Calculus::PrimalDerivative0 	primal_D0 = calculus.derivative<0,PRIMAL>();
  trace.info() << "primal_h0" << endl;
  const Calculus::PrimalHodge0  			primal_h0 = calculus.hodge<0,PRIMAL>();
  trace.info() << "primal_h1" << endl;
  const Calculus::PrimalHodge1  			primal_h1 = calculus.hodge<1,PRIMAL>();
  trace.info() << "dual_D1" << endl;
  const Calculus::DualDerivative1 		dual_D1   = calculus.derivative<1,DUAL>();
  trace.info() << "dual_h2" << endl;
  const Calculus::DualHodge2      		dual_h2   = calculus.hodge<2,DUAL>();
  trace.info() << "primal_D1" << endl;
  const Calculus::PrimalDerivative1 	primal_D1 = calculus.derivative<1,PRIMAL>();
  trace.info() << "primal_h2" << endl;
  const Calculus::PrimalHodge2     		primal_h2 = calculus.hodge<2,PRIMAL>();
  trace.info() << "dual_D0" << endl;
  const Calculus::DualDerivative0     dual_D0   = calculus.derivative<0,DUAL>();
  trace.info() << "dual_h1" << endl;
  const Calculus::DualHodge1         	dual_h1   = calculus.hodge<1,DUAL>();
  
  trace.info() << "ag" << endl;
  const Calculus::PrimalForm0 ag = a * g;
  trace.info() << "u" << endl;
  Calculus::PrimalForm0 u = ag;
  // trace.info() << "A^t*diag(v)^2*A = " << Av2A << endl;
  trace.info() << "v" << endl;
  Calculus::PrimalForm1 v( calculus );
  for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
    v.myContainer( index ) = 1.0;
  trace.endBlock();
  
  // SparseLU is so much faster than SparseQR
  // SimplicialLLT is much faster than SparsLU
  // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver;
  // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver;
  typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> SolverU;
  SolverU solver_u;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV;
  SolverV solver_v;
  
  SolverV solver_tBB;
  const Calculus::PrimalIdentity1 tBB = -1.0 * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 + dual_h1 * dual_D0 * primal_h2 * primal_D1 );
  solver_tBB.compute( tBB );

  while ( l1 >= l2 )
    {
      trace.info() << "************ lambda = " << l1 << " **************" << endl;
      double l = l1;
      trace.info() << "B'B'" << endl;
      const Calculus::PrimalIdentity1 lBB = l * tBB;
//      const Calculus::PrimalIdentity1 lBB = - l * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 + dual_h1 * dual_D0 * primal_h2 * primal_D1 );
      Calculus::PrimalForm1 l_4( calculus );
      for ( Calculus::Index index = 0; index < l_4.myContainer.rows(); index++)
        l_4.myContainer( index ) = l/4.0;
      
      //Calculus::PrimalIdentity1 BB = eps * lBB + (l/(4*eps)) * calculus.identity<1, PRIMAL>();
      //trace.info() << "le*B'^t*B' + l/(4e)Id" << BB << endl;
      
      Calculus::PrimalIdentity1 BB = 0.0 * lBB;
      
      double coef_eps = 2.0;
      double eps = coef_eps*e;
      
      for( int k = 0 ; k < 5 ; ++k )
        {  
          if (eps/coef_eps < h*h)
            break;
          else
            {
              eps /= coef_eps;
              BB = (h * eps) * lBB + (h*l/(4.0*eps)) * calculus.identity<1, PRIMAL>();      
              int i = 0;
              for ( ; i < n; ++i )
                {
                  trace.info() << "------ Iteration " << k << ":" << 	i << "/" << n << " ------" << endl;
                  trace.beginBlock("Solving for u");
                  trace.info() << "Building matrix Av2A" << endl;
				      
                  Calculus::PrimalIdentity1 Mv2 = calculus.identity<1, PRIMAL>();
                  for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
                    Mv2.myContainer.coeffRef( index, index ) = v.myContainer[ index ] * v.myContainer[ index ];
				      
                  const Calculus::PrimalIdentity0 Av2A = 	( - 1.0 * h ) 	* dual_h2 * dual_D1 * primal_h1 * Mv2 * primal_D0
                    + 	( a * h*h ) * calculus.identity<0, PRIMAL>();
                  trace.info() << "Prefactoring matrix Av2A" << endl;
                  solver_u.compute( Av2A );
                  trace.info() << "Solving Av2A u = ag" << endl;
                  u = solver_u.solve( (h*h) * ag );
                  trace.info() << ( solver_u.isValid() ? "OK" : "ERROR" ) << " " << solver_u.myLinearAlgebraSolver.info() << endl;
                  trace.endBlock();

                  trace.beginBlock("Solving for v");
                  trace.info() << "Building matrix BB+Mw2" << endl;
                  const Calculus::PrimalForm1 former_v = v;
                  const Calculus::PrimalForm1 w = primal_D0 * u;
				      
                  Calculus::PrimalIdentity1 Mw2 = calculus.identity<1, PRIMAL>();
                  for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
                    Mw2.myContainer.coeffRef( index, index ) = h * w.myContainer[ index ] * w.myContainer[ index ];
				        
                  trace.info() << "Prefactoring matrix BB+Mw2" << endl;
                  solver_v.compute( BB + Mw2 );
                  trace.info() << 	"Solving (BB+Mw2)v = l_4e" << endl;
                  v = solver_v.solve( ( h * (1.0/eps) ) * l_4 );
                  trace.info() << ( solver_v.isValid() ? "OK" : "ERROR" ) << " " << solver_v.myLinearAlgebraSolver.info() << endl;  						
                  trace.endBlock();
                  double n_infty = 0.0;
                  for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
                    n_infty = max( n_infty, abs( v.myContainer( index ) - former_v.myContainer( index ) ) );
                  trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl;
                  if ( n_infty < 1e-4 ) break;
                }
              //BB = eps * lBB + (l/(4*eps)) * calculus.identity<1, PRIMAL>();
            }
        }
        
        
      // *** MODIF : affichage des energies ***********************************************************************************
      
      typedef Calculus::SparseMatrix SparseMatrix;
      typedef Eigen::Matrix<double,Dynamic,Dynamic> Matrix;

      trace.beginBlock("Computing energies");

      // a(u-g)^2 
      trace.info() << "- a(u-g)^2 " << std::endl;
      double UmG2 = 0.0;
      for ( Calculus::Index index = 0; index < u.myContainer.rows(); index++)
        UmG2 += a * (u.myContainer( index ) - g.myContainer( index )) * (u.myContainer( index ) - g.myContainer( index ));
      
      // v^2|grad u|^2 
      trace.info() << "- v^2|grad u|^2" << std::endl;
      double V2gradU2 = 0.0;
      SolverU solver_Av2A;
      trace.info() << "  - Id" << std::endl;
      Calculus::PrimalIdentity1 Mv2 = calculus.identity<1, PRIMAL>();
      trace.info() << "  - M := Diag(v^2)" << std::endl;
      for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
        Mv2.myContainer.coeffRef( index, index ) = v.myContainer[ index ] * v.myContainer[ index ];
      trace.info() << "  - * D_1 * M * D_0" << std::endl;
      const Calculus::PrimalIdentity0 Av2A = - 1.0 * dual_h2 * dual_D1 * primal_h1 * Mv2 * primal_D0;
      trace.info() << "  - N := compute (* D_1 * M * D_0)" << std::endl;
      solver_Av2A.compute( Av2A );
      // JOL: 1000 * plus rapide !
      trace.info() << "  - u^t N u" << std::endl;
      Calculus::PrimalForm0 u_prime = Av2A * u;
      for ( Calculus::Index index = 0; index < u.myContainer.rows(); index++)
        V2gradU2 += u.myContainer( index ) * u_prime.myContainer( index );
      // for ( Calculus::Index index_i = 0; index_i < u.myContainer.rows(); index_i++)
      // 	for ( Calculus::Index index_j = 0; index_j < u.myContainer.rows(); index_j++)
      //     V2gradU2 += u.myContainer( index_i ) * Av2A.myContainer.coeff( index_i,index_j ) * u.myContainer( index_j ) ;
      
      // le|grad v|^2 
      trace.info() << "- le|grad v|^2" << std::endl;
      Calculus::PrimalForm1 v_prime = tBB * v;
      double gradV2 = 0.0;
      for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
        gradV2 += l * eps * v.myContainer( index ) * v_prime.myContainer( index );
      // for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++)
      //   for ( Calculus::Index index_j = 0; index_j < v.myContainer.rows(); index_j++)
      //     gradV2 += l * eps * v.myContainer( index_i ) * tBB.myContainer.coeff( index_i,index_j ) * v.myContainer( index_j );
      
      // l(1-v)^2/4e 
      trace.info() << "- l(1-v)^2/4e" << std::endl;
      double Vm12 = 0.0;
      for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++)
        Vm12 += (l/(4*eps)) * (1 - 2*v.myContainer( index_i ) + v.myContainer( index_i )*v.myContainer( index_i ));
      
      // l.per
      trace.info() << "- l.per" << std::endl;
      double Lper = h*gradV2 + h*Vm12;
      //      double per = 0.0;
      //      for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++)
      //      {
      //        per += (1/(4*e)) * (1 - 2*v.myContainer( index_i ) + v.myContainer( index_i )*v.myContainer( index_i ));
      //        for ( Calculus::Index index_j = 0; index_j < v.myContainer.rows(); index_j++)
      //            per += e * v.myContainer( index_i ) * tBB.myContainer( index_i,index_j ) * v.myContainer( index_j );
      //      }
      
      // AT tot
      double ATtot = h*h*UmG2 + h*V2gradU2 + h*gradV2 + h*Vm12;
      
      //f << "l  " << "  a  " << "  e  " << "  a(u-g)^2  " << "  v^2|grad u|^2  " << "  le|grad v|^2  " << "  l(1-v)^2/4e  " << "  l.per  " << "  AT tot"<< endl;   
      f << tronc(l,8) << "\t" << a << "\t"  << tronc(eps,4) << "\t"  << tronc(UmG2,5) << "\t"  << tronc(V2gradU2,5) << "\t"  << tronc(gradV2,5) << "\t" << tronc(Vm12,5) << "\t" << tronc(Lper,5)  << "\t" << tronc(ATtot,5) << endl; 

      trace.endBlock();

      // ***********************************************************************************************************************

      //      int int_l = (int) floor(l);
      //      int dec_l = (int) (floor((l-floor(l))*10000));

      //	  std::ostringstream ss;
      //	  ss << (double) tronc(l,7);    
      //    string s = ss.str();
      //	  const int pos = s.find(".");
      //	  string tronc_l = s.substr(0,pos) + "_" + s.substr(pos+1);
			
      {
        // Board2D board;
        // board << calculus;
        // board << CustomStyle( "KForm", new KFormStyle2D( 0.0, 1.0 ) ) << u;
        // ostringstream oss;
        // oss << f2 << "-u-" << i << ".eps";
        // string str_u = oss.str(); //f2 + "-u-" + .eps";
        // board.saveEPS( str_u.c_str() );
        Image image2 = image;
        PrimalForm0ToImage( calculus, u, image2 );
        ostringstream oss2;
        //        oss2 << f2 << "-l" << int_l << "_" << dec_l << "-u.pgm";
        //        oss2 << f2 << "-l" << tronc_l << "-u.pgm";
        oss2 << boost::format("%s-l%.7f-u.pgm") %f2 %l;
        string str_image_u = oss2.str();
  
        //  			boost::regex re("\\.");
        //  			str_image_u = boost::regex_replace(str_image_u, re , "_");
        //	  		const int pos = str_image_u.find(".");
        //	  		str_image_u = str_image_u.substr(0,pos) + "_" + str_image_u.substr(pos+1);
        
        image2 >> str_image_u.c_str();
      }
      {
        // Board2D board;
        // board << calculus;
        // board << CustomStyle( "KForm", new KFormStyle2D( 0.0, 1.0 ) )
        //       << v;
        // ostringstream oss;
        // oss << f2 << "-v-" << i << ".eps";
        // string str_v = oss.str();
        // board.saveEPS( str_v.c_str() );
        PrimalForm1ToImage( calculus, v, dbl_image );
        ostringstream oss3;
        //oss3 << f2 << "-l" << tronc_l << "-v.pgm";
        oss3 << boost::format("%s-l%.7f-v.pgm") %f2 %l;
        string str_image_v = oss3.str();
        
        dbl_image >> str_image_v.c_str();
      }

      l1 /= lr;

    } // while ( l1 >= l2 )

  // typedef SelfAdjointEigenSolver<MatrixXd> EigenSolverMatrix;
  // const EigenSolverMatrix eigen_solver(laplace.myContainer);

  // const VectorXd eigen_values = eigen_solver.eigenvalues();
  // const MatrixXd eigen_vectors = eigen_solver.eigenvectors();

  // for (int kk=0; kk<laplace.myContainer.rows(); kk++)
  // {
  //     Calculus::Scalar eigen_value = eigen_values(kk, 0);

  //     const VectorXd eigen_vector = eigen_vectors.col(kk);
  //     const Calculus::DualForm0 eigen_form = Calculus::DualForm0(calculus, eigen_vector);
  //     std::stringstream ss;
  //     ss << "chladni_eigen_" << kk << ".svg";
  //     const std::string filename = ss.str();
  //     ss << "chladni_eigen_vector_" << kk << ".svg";
  //     trace.info() << kk << " " << eigen_value << " " << sqrt(eigen_value) << " " << eigen_vector.minCoeff() << " " << eigen_vector.maxCoeff() << " " << standard_deviation(eigen_vector) << endl;

  //     Board2D board;
  //     board << domain;
  //     board << calculus;
  //     board << CustomStyle("KForm", new KFormStyle2D(eigen_vectors.minCoeff(),eigen_vectors.maxCoeff()));
  //     board << eigen_form;
  //     board.saveSVG(filename.c_str());
  // }

  f.close();

  return 0;
}
Exemple #9
0
int main( int argc, char* argv[] )
{
  using namespace Z2i;
  typedef ImageContainerBySTLVector<Domain, unsigned char>      Image;
  typedef ImageContainerBySTLVector<Domain, double>             ImageDouble;
  typedef ImageContainerBySTLVector<Domain,
                    SimpleMatrix<double,2,2> >  ImageSimpleMatrix2d;
  typedef ImageContainerBySTLVector<Domain, RealVector>         ImageVector;

  typedef std::vector< unsigned char >::iterator 			ImageIterator;
  typedef std::vector< double >::iterator 					ImageDoubleIterator;
  typedef std::vector< SimpleMatrix<double,2,2> >::iterator ImageSimpleMatrix2dIterator;
  typedef std::vector< RealVector >::iterator 				ImageVectorIterator;

  // parse command line ----------------------------------------------
  namespace po = boost::program_options;
  po::options_description general_opt("Allowed options are: ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input,i", po::value<string>(), "the input image filename." )
    ("output,o", po::value<string>()->default_value( "AT" ), "the output image basename." )
    ("lambda,l", po::value<double>(), "the parameter lambda." )
    ("lambda-1,1", po::value<double>()->default_value( 0.3125 ), "the initial parameter lambda (l1)." ) // 0.3125
    ("lambda-2,2", po::value<double>()->default_value( 0.00005 ), "the final parameter lambda (l2)." )
    ("lambda-ratio,q", po::value<double>()->default_value( sqrt(2) ), "the division ratio for lambda from l1 to l2." )
    ("alpha,a", po::value<double>()->default_value( 1.0 ), "the parameter alpha." )
    ("epsilon,e", po::value<double>()->default_value( 1.0 ), "the initial and final parameter epsilon of AT functional at the same time." )
    ("epsilon-1", po::value<double>(), "the initial parameter epsilon." )
    ("epsilon-2", po::value<double>(), "the final parameter epsilon." )
    ("epsilon-r", po::value<double>()->default_value( 2.0 ), "sets the ratio between two consecutive epsilon values of AT functional." )
    //("gridstep,g", po::value<double>()->default_value( 1.0 ), "the parameter h, i.e. the gridstep." )
    ("nbiter,n", po::value<int>()->default_value( 10 ), "the maximum number of iterations." )
    //("sigma,s", po::value<double>()->default_value( 2.0 ), "the parameter of the first convolution." )
    //("rho,r", po::value<double>()->default_value( 3.0 ), "the parameter of the second convolution." )
    ("image-size,t", po::value<double>()->default_value( 64.0 ), "the size of the image." )
    ;

  bool parseOK=true;
  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, general_opt), vm);
  } catch ( const exception& ex ) {
    parseOK = false;
    cerr << "Error checking program options: "<< ex.what()<< endl;
  }
  po::notify(vm);
  if ( ! parseOK || vm.count("help") || !vm.count("input") )
    {
      cerr << "Usage: " << argv[0] << " -i toto.pgm\n"
       << "Computes the Ambrosio-Tortorelli reconstruction/segmentation of an input image."
       << endl << endl
       << " / "
       << endl
       << " | a.(u-g)^2 + v^2 |grad u|^2 + le.|grad v|^2 + (l/4e).(1-v)^2 "
       << endl
       << " / "
       << endl
       << "Discretized as (u 0-form, v 1-form, A vertex-edge bdry, B edge-face bdy)" << endl
       << "E(u,v) = a(u-g)^t (u-g) +  u^t A^t diag(v)^2 A^t u + l e v^t (A A^t + B^t B) v + l/(4e) (1-v)^t (1-v)" << endl
       << endl
       << general_opt << "\n"
       << "Example: ./at-u0-v1 -i ../Images/cerclesTriangle64b02.pgm -o tmp -a 0.05 -e 1 -1 0.1 -2 0.00001 -t 64"
       << endl;
      return 1;
    }
  string f1 = vm[ "input" ].as<string>();
  string f2 = vm[ "output" ].as<string>();
  double l1  = vm[ "lambda-1" ].as<double>();
  double l2  = vm[ "lambda-2" ].as<double>();
  double lr  = vm[ "lambda-ratio" ].as<double>();
  if ( vm.count( "lambda" ) ) l1 = l2 = vm[ "lambda" ].as<double>();
  if ( l2 > l1 ) l2 = l1;
  if ( lr <= 1.0 ) lr = sqrt(2);
  double a  = vm[ "alpha" ].as<double>();
  double e  = vm[ "epsilon" ].as<double>();
  double e1 = vm.count( "epsilon-1" ) ? vm[ "epsilon-1" ].as<double>() : e;
  double e2 = vm.count( "epsilon-2" ) ? vm[ "epsilon-2" ].as<double>() : e;
  double er = vm[ "epsilon-r" ].as<double>();
  double t  = vm[ "image-size" ].as<double>();
  //double h  = vm[ "gridstep" ].as<double>();
  double h  = 1.0 / t;

  int    n  = vm[ "nbiter" ].as<int>();
  //double s  = vm[ "sigma" ].as<double>();
  //double r  = vm[ "rho" ].as<double>();

  trace.beginBlock("Reading image");
  Image image = GenericReader<Image>::import( f1 );
  Image end_image = image;
  trace.endBlock();

  // opening file
  const string file = f2 + ".txt";
  ofstream f(file.c_str());
  f << "#  l \t" << " a \t" << " e \t" << "a(u-g)^2 \t" << "v^2|grad u|^2 \t" << "  le|grad v|^2 \t" << "  l(1-v)^2/4e \t" << " l.per \t" << "AT tot"<< endl;

  trace.beginBlock("Creating calculus");
  typedef DiscreteExteriorCalculus<2,2, EigenLinearAlgebraBackend> Calculus;
  Domain domain = image.domain();
  Point p0 = domain.lowerBound(); p0 *= 2;
  Point p1 = domain.upperBound(); p1 *= 2;
  Domain kdomain( p0, p1 );
  Image dbl_image( kdomain );
  Calculus calculus;
  calculus.initKSpace( ConstAlias<Domain>( domain ) );
  const KSpace& K = calculus.myKSpace;
  // Les pixels sont des 0-cellules du primal.
  for ( Domain::ConstIterator it = kdomain.begin(), itE = kdomain.end(); it != itE; ++it )
    calculus.insertSCell( K.sCell( *it ) ); // ajoute toutes les cellules de Khalimsky.
  calculus.updateIndexes();
  trace.info() << calculus << endl;
  Calculus::PrimalForm0 g( calculus );
  double min_g = 300.0, max_g = 0.0;
  for ( Calculus::Index index = 0; index < g.myContainer.rows(); index++)
    {
      const Calculus::SCell& cell = g.getSCell( index );
      g.myContainer( index ) = ((double) image( K.sCoords( cell ) )) /
    255.0;
      min_g = std::min( min_g , g.myContainer( index ) );
      max_g = std::max( max_g , g.myContainer( index ) );
    }
  trace.info() << "min_g= " << min_g << " max_g= " << max_g << std::endl;
  trace.endBlock();

  // u = g at the beginning
  trace.info() << "u" << endl;
  Calculus::PrimalForm0 u = g;
  // v = 1 at the beginning
  trace.info() << "v" << endl;
  Calculus::PrimalForm1 v( calculus );
  for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
    v.myContainer( index ) = 1;

  trace.beginBlock("building AT functionnals");
  trace.info() << "primal_D0" << endl;
  const Calculus::PrimalDerivative0 	primal_D0 = calculus.derivative<0,PRIMAL>();
  trace.info() << "primal_D1" << endl;
  const Calculus::PrimalDerivative1 	primal_D1 = calculus.derivative<1,PRIMAL>();
  trace.info() << "dual_D0" << endl;
  const Calculus::DualDerivative0       dual_D0   = calculus.derivative<0,DUAL>();
  trace.info() << "dual_D1" << endl;
  const Calculus::DualDerivative1       dual_D1   = calculus.derivative<1,DUAL>();
  trace.info() << "primal_h0" << endl;
  const Calculus::PrimalHodge0          primal_h0 = calculus.hodge<0,PRIMAL>();
  trace.info() << "primal_h1" << endl;
  const Calculus::PrimalHodge1          primal_h1 = calculus.hodge<1,PRIMAL>();
  trace.info() << "primal_h2" << endl;
  const Calculus::PrimalHodge2          primal_h2 = calculus.hodge<2,PRIMAL>();
  trace.info() << "dual_h1" << endl;
  const Calculus::DualHodge1         	dual_h1   = calculus.hodge<1,DUAL>();
  trace.info() << "dual_h2" << endl;
  const Calculus::DualHodge2            dual_h2   = calculus.hodge<2,DUAL>();

  const DGtal::Dimension dimX = 0, dimY = 1;

  // BEG JOL
  // Calculus::PrimalIdentity1 tSS = calculus.identity<1, PRIMAL>();
  // for ( Calculus::Index index_i = 0; index_i < tSS.myContainer.rows(); index_i++ )
  //   for ( Calculus::Index index_j = 0; index_j < tSS.myContainer.cols(); index_j++ )
  //     {
  //       tSS.myContainer.coeffRef( index_i, index_j ) = 0.0;
  //       for ( Calculus::Index index_k = 0; index_k < tSS.myContainer.rows(); index_k++ )
  //           tSS.myContainer.coeffRef( index_i, index_j ) +=  sharp_x.myContainer.coeffRef( index_k, index_i ) * sharp_x.myContainer.coeffRef( index_k, index_j )
  //                                                          + sharp_y.myContainer.coeffRef( index_k, index_i ) * sharp_y.myContainer.coeffRef( index_k, index_j ) ;
  //     }
  // END JOL
  trace.endBlock();


  const Calculus::PrimalIdentity0 Id0 = calculus.identity<0, PRIMAL>();
  const Calculus::PrimalIdentity1 Id1 = calculus.identity<1, PRIMAL>();
  const Calculus::PrimalIdentity2 Id2 = calculus.identity<2, PRIMAL>();

  // Weight matrices
  //  Calculus::DualIdentity2   G0 		= ( 1.0/(h*h) ) * calculus.identity<2, DUAL>();
  Calculus::PrimalIdentity0 G0 	  = Id0; //	= ( 1.0/(h*h) ) * calculus.identity<0, PRIMAL>();
  Calculus::PrimalIdentity0 invG0 = Id0; //   = 	(h*h) 	* calculus.identity<0, PRIMAL>();

  //  Calculus::DualIdentity1   G1 		= calculus.identity<1, DUAL>();
  Calculus::PrimalIdentity1 G1    = Id1; //	= calculus.identity<1, PRIMAL>();
  Calculus::PrimalIdentity1 invG1 = Id1; //     = calculus.identity<1, PRIMAL>();

  //  Calculus::DualIdentity0   G2 		= 		(h*h) 	* calculus.identity<0, DUAL>();
  Calculus::PrimalIdentity2 G2    = Id2; //	= 		(h*h) 	* calculus.identity<2, PRIMAL>();
  Calculus::PrimalIdentity2 invG2 = Id2; //     = ( 1.0/(h*h) ) * calculus.identity<2, PRIMAL>();

  // Building alpha_G0_1
  Calculus::PrimalIdentity0 alpha_iG0 = a * Id0; // a * calculus.identity<0, PRIMAL>(); // a * invG0; //diag_alpha;
  Calculus::PrimalForm0 alpha_iG0_g   = alpha_iG0 * g;

  // Builds a Laplacian but at distance 2 !
  // const Matrix& M = tA_tS_S_A.myContainer;
  // for (int k = 0; k < M.outerSize(); ++k)
  //   {
  //     trace.info() << "-----------------------------------------------------------------------" << endl;
  //     for ( Matrix::InnerIterator it( M, k ); it; ++it )
  //       trace.info() << "[" << it.row() << "," << it.col() << "] = " << it.value() << endl;
  //   }


  // Building iG1_A_G0_tA_iG1 + tB_iG2_B
//  // A.tA
//  const Calculus::PrimalIdentity1 lap_operator_v = -1.0 * ( invG1 * primal_D0 * G0 * dual_h2 * dual_D1 * primal_h1 * invG1 );
//  // tB.B
//  const Calculus::PrimalIdentity1 lap_operator_v = -1.0 * ( dual_h1 * dual_D0 * primal_h2 * invG2 * primal_D1 );
  // tB.B + A.tA
  const Calculus::PrimalIdentity1 lap_operator_v = -1.0 * ( invG1 * primal_D0 * G0 * dual_h2 * dual_D1 * primal_h1 * invG1
                                                            + dual_h1 * dual_D0 * primal_h2 * invG2 * primal_D1 );

  // SparseLU is so much faster than SparseQR
  // SimplicialLLT is much faster than SparseLU
  // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver;
  // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver;
  typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> SolverU;
  SolverU solver_u;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV;
  SolverV solver_v;

  while ( l1 >= l2 )
    {
      trace.info() << "************ lambda = " << l1 << " **************" << endl;
      double l = l1;
      trace.info() << "B'B'" << endl;
      const Calculus::PrimalIdentity1 lBB = l * lap_operator_v;
      Calculus::PrimalForm1 l_sur_4( calculus );
      for ( Calculus::Index index = 0; index < l_sur_4.myContainer.rows(); index++)
        l_sur_4.myContainer( index ) = l/4.0;
      l_sur_4 = Id1 * l_sur_4; //tS_S * l_sur_4; //

      double epsilon = 0.0;
      for( int i = 0; i < 2; ++i )  // change alpha
      {
      g = u;
      alpha_iG0 = a * Id0;
      alpha_iG0_g = alpha_iG0 * g;
      for ( double eps = e1; eps >= e2; eps /= er )
        {
          Calculus::PrimalIdentity1 BB = eps * lBB + ( l/(4.0*eps) ) * Id1; // tS_S;
          int i = 0;
          for ( ; i < n; ++i )
            {
              trace.info() << "------ Iteration " << i << "/" << n << " ------" << endl;
              trace.beginBlock("Solving for u");
              trace.info() << "Building matrix Av2A" << endl;

              //double tvtSSv = 0.0;
              Calculus::PrimalIdentity1 diag_v = diag( calculus, v );
              Calculus::PrimalDerivative0 v_A = diag_v * primal_D0;
              Calculus::PrimalIdentity0 Av2A = square( calculus, v_A ) + alpha_iG0;
              trace.info() << "Prefactoring matrix Av2A := tA_v_tv_A + alpha_iG0" << endl;
              trace.info() << "-------------------------------------------------------------------------------" << endl;
              // const Matrix& M = Av2A.myContainer;
              // for (int k = 0; k < M.outerSize(); ++k)
              //   for ( Matrix::InnerIterator it( M, k ); it; ++it )
              //     trace.info() << "[" << it.row() << "," << it.col() << "] = " << it.value() << endl;
              solver_u.compute( Av2A );
              trace.info() << "Solving Av2A u = ag" << endl;
              u = solver_u.solve( alpha_iG0_g );
              trace.info() << ( solver_u.isValid() ? "OK" : "ERROR" ) << " " << solver_u.myLinearAlgebraSolver.info() << endl;
              trace.info() << "-------------------------------------------------------------------------------" << endl;
              trace.endBlock();

              const Calculus::PrimalForm1 former_v = v;
              trace.beginBlock("Solving for v");
              trace.info() << "Building matrix BB+Mw2" << endl;
              const Calculus::PrimalIdentity1 A_u = diag( calculus, primal_D0 * u );
              const Calculus::PrimalIdentity1 tu_tA_A_u = square( calculus, A_u );
              solver_v.compute( tu_tA_A_u + BB );
              v = solver_v.solve( (1.0/eps) * l_sur_4 );
              trace.info() << ( solver_v.isValid() ? "OK" : "ERROR" ) << " " << solver_v.myLinearAlgebraSolver.info() << endl;
              trace.endBlock();

              trace.beginBlock("Checking v, computing norms");
              double m1 = 1.0;
              double m2 = 0.0;
              double ma = 0.0;
              for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
                {
                  double val = v.myContainer( index );
                  m1 = std::min( m1, val );
                  m2 = std::max( m2, val );
                  ma += val;
                }
              trace.info() << "1-form v: min=" << m1 << " avg=" << ( ma/ v.myContainer.rows() )
                           << " max=" << m2 << std::endl;
              for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
                v.myContainer( index ) = std::min( std::max(v.myContainer( index ), 0.0) , 1.0 );

              double n_infty = 0.0;
              double n_2 = 0.0;
              double n_1 = 0.0;

              for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++)
                {
                  n_infty = max( n_infty, fabs( v.myContainer( index ) - former_v.myContainer( index ) ) );
                  n_2    += ( v.myContainer( index ) - former_v.myContainer( index ) )
                            * ( v.myContainer( index ) - former_v.myContainer( index ) );
                  n_1    += fabs( v.myContainer( index ) - former_v.myContainer( index ) );
                }
              n_1 /= v.myContainer.rows();
              n_2 = sqrt( n_2 / v.myContainer.rows() );

              trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl;
              trace.info() << "Variation |v^k+1 - v^k|_2 = " << n_2 << endl;
              trace.info() << "Variation |v^k+1 - v^k|_1 = " << n_1 << endl;
              trace.endBlock();
              if ( n_infty < 1e-4 ) break;
            }
          epsilon = eps;
        }
      a = 0.5;
      }

      // affichage des energies ********************************************************************

      trace.beginBlock("Computing energies");

      // a(u-g)^2
      Calculus::PrimalIdentity0 diag_alpha = a * Id0;
      const Calculus::PrimalForm0 u_minus_g = u - g;
      double alpha_square_u_minus_g = innerProduct( calculus, diag_alpha * u_minus_g, u_minus_g );
      trace.info() << "- a(u-g)^2   = " << alpha_square_u_minus_g << std::endl;
      // v^2|grad u|^2

      const Calculus::PrimalIdentity1 diag_v = diag( calculus, v );
      const Calculus::PrimalForm1 v_A_u = diag_v * primal_D0 * u;
      double square_v_grad_u = innerProduct( calculus, v_A_u, v_A_u );
      trace.info() << "- v^2|grad u|^2 = " << square_v_grad_u << std::endl;
//      // JOL: 1000 * plus rapide !
//      trace.info() << "  - u^t N u" << std::endl;
//      Calculus::PrimalForm0 u_prime = Av2A * u;
//      for ( Calculus::Index index = 0; index < u.myContainer.rows(); index++)
//        V2gradU2 += u.myContainer( index ) * u_prime.myContainer( index );
//      // for ( Calculus::Index index_i = 0; index_i < u.myContainer.rows(); index_i++)
//      // 	for ( Calculus::Index index_j = 0; index_j < u.myContainer.rows(); index_j++)
//      //     V2gradU2 += u.myContainer( index_i ) * Av2A.myContainer.coeff( index_i,index_j ) * u.myContainer( index_j ) ;

//      // le|grad v|^2
      Calculus::PrimalForm1 v_prime = lap_operator_v * v;
      double le_square_grad_v = l * epsilon * innerProduct( calculus, v, v_prime );
      trace.info() << "- le|grad v|^2  = " << le_square_grad_v << std::endl;

      // l(1-v)^2/4e
      Calculus::PrimalForm1 one_minus_v = v;
      for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++)
        one_minus_v.myContainer( index_i ) = 1.0 - one_minus_v.myContainer( index_i );
      double l_over_4e_square_1_minus_v
        = l / (4*epsilon) * innerProduct( calculus, one_minus_v, one_minus_v );
      trace.info() << "- l(1-v)^2/4e   = " << l_over_4e_square_1_minus_v << std::endl;

      // l.per
      double Lper = 2.0* l_over_4e_square_1_minus_v; //le_square_grad_v + l_over_4e_square_1_minus_v;
      trace.info() << "- l.per         = " << Lper << std::endl;

      // AT tot
      double ATtot = alpha_square_u_minus_g + square_v_grad_u + Lper;


      // f << "l  " << "  a  " << "  e  " << "  a(u-g)^2  " << "  v^2|grad u|^2  " << "  le|grad v|^2  " << "  l(1-v)^2/4e  " << "  l.per  " << "  AT tot"<< endl;
      f << tronc(l,8) << "\t" << a << "\t"  << tronc(epsilon,4)
        << "\t" << tronc(alpha_square_u_minus_g,5)
        << "\t" << tronc(square_v_grad_u,5)
        << "\t" << tronc(le_square_grad_v,5)
        << "\t" << tronc(l_over_4e_square_1_minus_v,5)
        << "\t" << tronc(Lper,5)
        << "\t" << tronc(ATtot,5) << endl;

      trace.endBlock();

      // ***********************************************************************************************************************

      int int_l = (int) floor(l);
      int dec_l = (int) (floor((l-floor(l))*10000000));

      ostringstream ossU;
      ossU << boost::format("%s-l%.7f-u.pgm") %f2 %l;
      string str_image_u = ossU.str();
      savePrimalForm0ToImage( calculus, end_image, u, str_image_u);

      ostringstream ossV;
      ossV << boost::format("%s-l%.7f-v.pgm") %f2 %l;
      string str_image_v = ossV.str();
      savePrimalForm1ToImage( calculus, dbl_image, v, str_image_v );

      l1 /= lr;
    }


  f.close();

  return 0;
}
int main( int argc, char** argv )
{
  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input,i", po::value< std::string >(), ".vol file")
    ("trivial-radius,t", po::value<double>()->default_value( 3 ), "the parameter t defining the radius for the Trivial estimator, which is used for reorienting II or VCM normal estimations." )
    ("r-radius,r",  po::value< double >(), "Kernel radius r for IntegralInvariant estimator" )
    ("noise,k",  po::value< double >()->default_value(0.5), "Level of Kanungo noise ]0;1[" )
    ("min,l",  po::value<  int >()->default_value(0), "set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]min, max ] )." )
    ("max,u",  po::value<  int >()->default_value(255), "set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]min, max] )." )
    ("lambda,L", po::value<double>()->default_value( 0.05 ), "the parameter lambda of AT functional." )
    ("alpha,a", po::value<double>()->default_value( 0.1 ), "the parameter alpha of AT functional." )
    ("epsilon,e", po::value<double>()->default_value( 4.0 ), "the initial parameter epsilon of AT functional." );

  bool parseOK = true;
  po::variables_map vm;
  try
    {
      po::store( po::parse_command_line( argc, argv, general_opt ), vm );
    }
  catch( const std::exception & ex )
    {
      parseOK = false;
      trace.error() << " Error checking program options: " << ex.what() << std::endl;
    }
  bool neededArgsGiven=true;

  if (parseOK && !(vm.count("input"))){
    missingParam("--input");
    neededArgsGiven=false;
  }
  if (parseOK && !(vm.count("r-radius"))){
    missingParam("--r-radius");
    neededArgsGiven=false;
  }

  double noiseLevel = vm["noise"].as< double >();
  if( noiseLevel < 0.0 || noiseLevel > 1.0 )
    {
      parseOK = false;
      trace.error() << "The noise level should be in the interval: [0, 1]"<< std::endl;
    }

  if(!neededArgsGiven || !parseOK || vm.count("help") || argc <= 1 )
    {
      trace.info()<< "Vol file viewer, with normals regularized by Ambrosio-Tortorelli functionnal" <<std::endl
                  << general_opt << "\n"
                  << "Basic usage: "<<std::endl
                  << "\t at-3d-normals -i file.vol -r 5 --noise 0.1"<<std::endl
                  << std::endl;
      return 0;
    }

  QApplication application(argc,argv);


  int min        =  vm["min"].as<  int >();
  int max        =  vm["max"].as<  int >();
  const double h = 1.0; // not pertinent for now.


  //-----------------------------------------------------------------------------
  // Types.
  typedef Z3i::Space                                 Space;
  typedef Z3i::KSpace                                KSpace;
  typedef Space::RealVector                          RealVector;
  typedef KSpace::SCell                              SCell;
  typedef KSpace::Cell                               Cell;
  typedef KSpace::Surfel                             Surfel;
  typedef Z3i::Domain                                Domain;
  typedef ImageSelector<Domain, unsigned char>::Type Image;
  typedef IntervalForegroundPredicate< Image >       Object;
  typedef KanungoNoise< Object, Domain >             KanungoPredicate;
  typedef BinaryPointPredicate<DomainPredicate<Domain>, KanungoPredicate, AndBoolFct2  > NoisyObject;
  typedef KSpace::SurfelSet                          SurfelSet;
  typedef SetOfSurfels< KSpace, SurfelSet >          MySetOfSurfels;
  typedef DigitalSurface< MySetOfSurfels >           MyDigitalSurface;
  typedef MyDigitalSurface::ConstIterator            ConstIterator;

  //-----------------------------------------------------------------------------
  // Loading vol file.
  trace.beginBlock( "Loading vol file." );
  std::string inputFile = vm[ "input" ].as< std::string >();
  std::string extension = inputFile.substr(inputFile.find_last_of(".") + 1);
  if(extension!="vol" && extension != "p3d" && extension != "pgm3D" && extension != "pgm3d" && extension != "sdp" && extension != "pgm" )
    {
    trace.info() << "File extension not recognized: "<< extension << std::endl;
    return 0;
  }
  Image image = GenericReader<Image>::import (inputFile );
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Extracting object with possible noise.
  trace.beginBlock( "Extracting object with possible noise." );
  Object                  object( image,  min, max );
  KanungoPredicate        kanungo_pred( object, image.domain(), noiseLevel );
  DomainPredicate<Domain> domain_pred( image.domain() );
  AndBoolFct2             andF;
  NoisyObject             noisy_object(domain_pred, kanungo_pred, andF  );
  Domain                  domain = image.domain();
  KSpace                  K;
  bool space_ok = K.init( domain.lowerBound()-Z3i::Domain::Point::diagonal(),
                          domain.upperBound()+Z3i::Domain::Point::diagonal(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khalimsky space construction."<<std::endl;
      return 2;
    }
  CanonicSCellEmbedder< KSpace >       embedder( K );
  SurfelAdjacency< KSpace::dimension > surfAdj( true );
  trace.endBlock();

  //-----------------------------------------------------------------------------
  //! [3dVolBoundaryViewer-ExtractingSurface]
  trace.beginBlock( "Extracting boundary by scanning the space. " );
  MySetOfSurfels theSetOfSurfels( K, surfAdj );
  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
                                   K, image,
                                   domain.lowerBound(),
                                   domain.upperBound() );
  MyDigitalSurface digSurf( theSetOfSurfels );
  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
               << std::endl;
  trace.endBlock();
  //! [3dVolBoundaryViewer-ExtractingSurface]

  // Map surfel -> estimated normals.
  std::map<SCell,RealVector> n_estimations;

  //-----------------------------------------------------------------------------
  // Estimating orientation of normals
  trace.beginBlock( "Estimating orientation of normals by simple convolutions of trivial surfel normals." );
  double t = vm["trivial-radius"].as<double>();
  typedef RealVector::Component                    Scalar;
  typedef functors::HatFunction<Scalar>            Functor;
  typedef functors::ElementaryConvolutionNormalVectorEstimator< Surfel, CanonicSCellEmbedder<KSpace> > SurfelFunctor;
  typedef ExactPredicateLpSeparableMetric<Space,2> Metric;
  typedef LocalEstimatorFromSurfelFunctorAdapter< MySetOfSurfels, Metric, SurfelFunctor, Functor>      NormalEstimator;
  Functor                      fct( 1.0, t );
  CanonicSCellEmbedder<KSpace> canonic_embedder( K );
  SurfelFunctor                surfelFct( canonic_embedder, 1.0 );
  NormalEstimator              nt_estimator;
  Metric                       aMetric;
  std::vector<RealVector>      nt_estimations;
  nt_estimator.attach( digSurf );
  nt_estimator.setParams( aMetric, surfelFct, fct, t );
  nt_estimator.init( h, digSurf.begin(), digSurf.end());
  nt_estimator.eval( digSurf.begin(), digSurf.end(), std::back_inserter( nt_estimations ) );
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Estimating normals
  trace.beginBlock( "Estimating normals with II." );
  typedef typename Domain::ConstIterator DomainConstIterator;
  typedef functors::IINormalDirectionFunctor<Space> IINormalFunctor;
  typedef IntegralInvariantCovarianceEstimator<KSpace, NoisyObject, IINormalFunctor> IINormalEstimator;
  std::vector<RealVector> nii_estimations;
  const double            r = vm["r-radius"].as<double>();
  IINormalEstimator       nii_estimator( K, noisy_object );
  trace.info() << " r=" << r << std::endl;
  nii_estimator.setParams( r );
  nii_estimator.init( h, digSurf.begin(), digSurf.end() );
  nii_estimator.eval( digSurf.begin(), digSurf.end(), std::back_inserter( nii_estimations ) );
  // Fix orientations of ii.
  for ( unsigned int i = 0; i < nii_estimations.size(); ++i )
    {
      if ( nii_estimations[ i ].dot( nt_estimations[ i ] ) < 0.0 )
        nii_estimations[ i ] *= -1.0;
    }
  trace.info() << "- nb estimations  = " << nii_estimations.size() << std::endl;
  trace.endBlock();

  // The chosen estimator is II.
  {
    unsigned int i = 0;
    for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it, ++i )
      {
        RealVector nii = nii_estimations[ i ];
        nii /= nii.norm();
        n_estimations[ *it ] = nii;
      }
  }

  //-----------------------------------------------------------------------------
  //! [3dVolBoundaryViewer-ViewingSurface]
  trace.beginBlock( "Displaying everything. " );
  Viewer3D<Space,KSpace> viewer( K );
  viewer.setWindowTitle("Simple boundary of volume Viewer");
  viewer.show();
  viewer << SetMode3D(K.unsigns( *(digSurf.begin()) ).className(), "Basic");
  unsigned int i = 0;
  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it, ++i )
    {
      viewer.setFillColor( Color( 200, 200, 250 ) );
      Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewer, *it, n_estimations[ *it ], false );
    }
  viewer << Viewer3D<>::updateDisplay;
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Defining Discrete Calculus.
  typedef CubicalComplex< KSpace >                                 CComplex;
  typedef DiscreteExteriorCalculus<2,3, EigenLinearAlgebraBackend> Calculus;
  typedef Calculus::Index                                          Index;
  typedef Calculus::PrimalForm0                                    PrimalForm0;
  typedef Calculus::PrimalForm1                                    PrimalForm1;
  typedef Calculus::PrimalForm2                                    PrimalForm2;
  typedef Calculus::PrimalIdentity0                                PrimalIdentity0;
  typedef Calculus::PrimalIdentity1                                PrimalIdentity1;
  typedef Calculus::PrimalIdentity2                                PrimalIdentity2;
  trace.beginBlock( "Creating Discrete Exterior Calculus. " );
  Calculus calculus;
  calculus.initKSpace<Domain>( domain );
  const KSpace& Kc = calculus.myKSpace; // should not be used.
  // Use a cubical complex to find all lower incident cells easily.
  CComplex complex( K );
  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
    complex.insertCell( 2, K.unsigns( *it ) );
  complex.close();
  for ( CComplex::CellMapIterator it = complex.begin( 0 ), itE = complex.end( 0 ); it != itE; ++it )
    calculus.insertSCell( K.signs( it->first, K.POS ) );
  
  for ( CComplex::CellMapIterator it = complex.begin( 1 ), itE = complex.end( 1 ); it != itE; ++it )
    {
      SCell     linel = K.signs( it->first, K.POS );
      Dimension k     = * K.sDirs( linel );
      bool      pos   = K.sDirect( linel, k );
      calculus.insertSCell( pos ? linel : K.sOpp( linel ) );
      // calculus.insertSCell( K.signs( it->first, K.POS ) );
    }

  // for ( CComplex::CellMapIterator it = complex.begin( 2 ), itE = complex.end( 2 ); it != itE; ++it )
  // calculus.insertSCell( K.signs( it->first, K.POS ) );
  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
    {
      calculus.insertSCell( *it );
      // SCell     surfel = *it;
      // Dimension k      = K.sOrthDir( surfel );
      // bool      pos    = K.sDirect( surfel, k );
      // calculus.insertSCell( pos ? *it : K.sOpp( *it ) );
    }
  calculus.updateIndexes();
  trace.info() << calculus << endl;

  std::vector<PrimalForm2> g;
  g.reserve( 3 );
  g.push_back( PrimalForm2( calculus ) );
  g.push_back( PrimalForm2( calculus ) );
  g.push_back( PrimalForm2( calculus ) );
  Index nb2 = g[ 0 ].myContainer.rows();
  
  for ( Index index = 0; index < nb2; index++)
    {
      const Calculus::SCell& cell = g[ 0 ].getSCell( index );
      if ( theSetOfSurfels.isInside( cell ) ) 
        {
          const RealVector&      n    = n_estimations[ cell ];
          g[ 0 ].myContainer( index ) = n[ 0 ];
          g[ 1 ].myContainer( index ) = n[ 1 ];
          g[ 2 ].myContainer( index ) = n[ 2 ];
        }
      else
        {
          const RealVector&      n    = n_estimations[ K.sOpp( cell ) ];
          g[ 0 ].myContainer( index ) = n[ 0 ];
          g[ 1 ].myContainer( index ) = n[ 1 ];
          g[ 2 ].myContainer( index ) = n[ 2 ];
        }
    }
  cout << endl;
  trace.info() << "primal_D0" << endl;
  const Calculus::PrimalDerivative0 	primal_D0 = calculus.derivative<0,PRIMAL>();
  trace.info() << "primal_D1" << endl;
  const Calculus::PrimalDerivative1 	primal_D1 = calculus.derivative<1,PRIMAL>();
  trace.info() << "dual_D0" << endl;
  const Calculus::DualDerivative0       dual_D0   = calculus.derivative<0,DUAL>();
  trace.info() << "dual_D1" << endl;
  const Calculus::DualDerivative1 	dual_D1   = calculus.derivative<1,DUAL>();
  trace.info() << "primal_h0" << endl;
  const Calculus::PrimalHodge0  	primal_h0 = calculus.hodge<0,PRIMAL>();
  trace.info() << "primal_h1" << endl;
  const Calculus::PrimalHodge1  	primal_h1 = calculus.hodge<1,PRIMAL>();
  trace.info() << "primal_h2" << endl;
  const Calculus::PrimalHodge2     	primal_h2 = calculus.hodge<2,PRIMAL>();
  trace.info() << "dual_h1" << endl;
  const Calculus::DualHodge1         	dual_h1   = calculus.hodge<1,DUAL>();
  trace.info() << "dual_h2" << endl;
  const Calculus::DualHodge2      	dual_h2   = calculus.hodge<2,DUAL>();
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Building AT functional.
  trace.beginBlock( "Building AT functional. " );
  double a  = vm[ "alpha" ].as<double>();
  double e  = vm[ "epsilon" ].as<double>();
  double l  = vm[ "lambda" ].as<double>();

  // u = g at the beginning
  trace.info() << "u[0,1,2]" << endl;
  std::vector<PrimalForm2> u;
  u.push_back( g[ 0 ] ); u.push_back( g[ 1 ] ); u.push_back( g[ 2 ] );
  // v = 1 at the beginning
  trace.info() << "v" << endl;
  PrimalForm1 v( calculus );
  Index nb1 = v.myContainer.rows();
  for ( Index index = 0; index < nb1; index++)  v.myContainer( index ) = 1.0;
  const PrimalIdentity0 Id0 = calculus.identity<0, PRIMAL>();
  const PrimalIdentity1 Id1 = calculus.identity<1, PRIMAL>();
  const PrimalIdentity2 Id2 = calculus.identity<2, PRIMAL>();
  // Building alpha_
  trace.info() << "alpha_g" << endl;
  const PrimalIdentity2 alpha_Id2 = a * Id2; // a * invG0;
  vector<PrimalForm2> alpha_g;
  alpha_g.push_back( alpha_Id2 * g[ 0 ] );
  alpha_g.push_back( alpha_Id2 * g[ 1 ] );
  alpha_g.push_back( alpha_Id2 * g[ 2 ] );
  trace.info() << "lap_operator_v" << endl;
  const PrimalIdentity1 lap_operator_v = -1.0 * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 
                                                  + dual_h1 * dual_D0 * primal_h2 * primal_D1 );
  // SparseLU is so much faster than SparseQR
  // SimplicialLLT is much faster than SparseLU
  // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver;
  // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver;
  typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 2, PRIMAL, 2, PRIMAL> SolverU;
  SolverU solver_u;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV;
  SolverV solver_v;

  trace.info() << "lB'B'" << endl;
  const PrimalIdentity1 lBB = l * lap_operator_v;
  PrimalForm1 l_sur_4( calculus );
  for ( Index index = 0; index < nb1; index++)
    l_sur_4.myContainer( index ) = l / 4.0;
  double coef_eps = 2.0;
  double eps      = 2.0 * e;
  const int n     = 10;
  trace.endBlock();
      
  //-----------------------------------------------------------------------------
  // Solving AT functional.
  trace.beginBlock( "Solving AT functional. " );
  while ( eps / coef_eps >= h )
    {
      eps /= coef_eps;
      trace.info() << "************** epsilon = " << eps << "***************************************" << endl;
      const PrimalIdentity1 BB = eps * lBB + ( l/(4.0*eps) ) * Id1; // tS_S;
      for ( int i = 0; i < n; ++i )
        {
          trace.info() << "---------- Iteration " << i << "/" << n << " ------------------------------" << endl;
          trace.info() << "-------------------------------------------------------------------------------" << endl;
          trace.beginBlock("Solving for u");
          trace.info() << "Building matrix Av2A" << endl;
          PrimalIdentity1 diag_v  = diag( calculus, v );
          PrimalIdentity2 U_Id2 = -1.0 * primal_D1 * diag_v * diag_v * dual_h1 * dual_D0 * primal_h2
            + alpha_Id2;
          trace.info() << "Prefactoring matrix Av2A + alpha_iG0" << endl;
          solver_u.compute( U_Id2 );
          for ( unsigned int d = 0; d < 3; ++d )
            {
              trace.info() << "Solving (Av2A + alpha_iG0) u[" << d << "] = ag[" << d << "]" << endl;
              u[ d ] = solver_u.solve( alpha_g[ d ] );
              trace.info() << "  => " << ( solver_u.isValid() ? "OK" : "ERROR" ) 
                           << " " << solver_u.myLinearAlgebraSolver.info() << endl;
            }
          trace.info() << "-------------------------------------------------------------------------------" << endl;
          trace.endBlock();

          trace.beginBlock("Solving for v");
          const PrimalForm1 former_v = v;
          trace.info() << "Building matrix tu_tA_A_u + BB + Mw2" << endl;
          PrimalIdentity1 V_Id1 = BB;
          for ( unsigned int d = 0; d < 3; ++d )
            {
              const PrimalIdentity1 A_u = diag( calculus, dual_h1 * dual_D0 * primal_h2 * u[ d ] );
              V_Id1.myContainer += square( calculus, A_u ).myContainer;
            }
          trace.info() << "Prefactoring matrix tu_tA_A_u + BB + Mw2" << endl;
          solver_v.compute( V_Id1 );
          trace.info() << "Solving (tu_tA_A_u + BB + Mw2) v = 1/(4eps) * l" << endl;
          v = solver_v.solve( (1.0/eps) * l_sur_4 );
          trace.info() << "  => " << ( solver_v.isValid() ? "OK" : "ERROR" ) 
                       << " " << solver_v.myLinearAlgebraSolver.info() << endl;
          trace.info() << "-------------------------------------------------------------------------------" << endl;
          trace.endBlock();

          for ( Index index = 0; index < nb2; index++)
            {
              double n2 = 0.0;
              for ( unsigned int d = 0; d < 3; ++d )
                n2 += u[ d ].myContainer( index ) * u[ d ].myContainer( index );
              double norm = sqrt( n2 );
              for ( unsigned int d = 0; d < 3; ++d )
                u[ d ].myContainer( index ) /= norm;
            }

          trace.beginBlock("Checking v, computing norms");
          double m1 = 1.0;
          double m2 = 0.0;
          double ma = 0.0;
          for ( Index index = 0; index < nb1; index++)
            {
              double val = v.myContainer( index );
              m1 = std::min( m1, val );
              m2 = std::max( m2, val );
              ma += val;
            }
          trace.info() << "1-form v: min=" << m1 << " avg=" << ( ma / nb1 ) << " max=" << m2 << std::endl;
          for ( Index index = 0; index < nb1; index++)
            v.myContainer( index ) = std::min( std::max(v.myContainer( index ), 0.0) , 1.0 );
          double n_infty = 0.0;
          double n_2 = 0.0;
          double n_1 = 0.0;
          for ( Index index = 0; index < nb1; index++)
            {
              n_infty = std::max( n_infty, fabs( v.myContainer( index ) - former_v.myContainer( index ) ) );
              n_2    += ( v.myContainer( index ) - former_v.myContainer( index ) )
                * ( v.myContainer( index ) - former_v.myContainer( index ) );
              n_1    += fabs( v.myContainer( index ) - former_v.myContainer( index ) );
            }
          n_1 /= v.myContainer.rows();
          n_2 = sqrt( n_2 / v.myContainer.rows() );
          
          trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl;
          trace.info() << "Variation |v^k+1 - v^k|_2 = " << n_2 << endl;
          trace.info() << "Variation |v^k+1 - v^k|_1 = " << n_1 << endl;
          trace.endBlock();
          if ( n_infty < 1e-4 ) break;
        } // for ( int i = 0; i < n; ++i )
    }
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Displaying regularized normals
  trace.beginBlock( "Displaying regularized normals. " );
  Viewer3D<Space,KSpace> viewerR( K );
  viewerR.setWindowTitle("Regularized normals");
  viewerR.show();
  viewerR << SetMode3D(K.unsigns( *(digSurf.begin()) ).className(), "Basic");
  viewerR.setFillColor( Color( 200, 200, 250 ) );
  for ( Index index = 0; index < nb2; index++)
    {
      const SCell& cell    = u[ 0 ].getSCell( index );
      // const RealVector& n  = n_estimations[ cell ];
      RealVector nr        = RealVector( u[ 0 ].myContainer( index ), 
                                         u[ 1 ].myContainer( index ), 
                                         u[ 2 ].myContainer( index ) );
      nr /= nr.norm();
      if ( theSetOfSurfels.isInside( cell ) ) 
        Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewerR, cell, nr, false );
      else
        Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewerR, K.sOpp( cell ), nr, false );
    }
  viewerR.setLineColor( Color( 255, 0, 0 ) );
  for ( Index index = 0; index < nb1; index++)
    {
      const SCell& cell    = v.getSCell( index );
      Dimension    k       = * K.sDirs( cell ); 
      const SCell  p0      = K.sIncident( cell, k, true );
      const SCell  p1      = K.sIncident( cell, k, false );
      if ( v.myContainer( index ) >= 0.5 ) continue;
      viewerR.addLine( embedder.embed( p0 ), embedder.embed( p1 ), (0.5 - v.myContainer( index ))/ 5.0 );
    }
  viewerR << Viewer3D<>::updateDisplay;
  trace.endBlock();

  return application.exec();

}