TEST(CppADCGLatexTest, latex) {
    // use a special object for source code generation
    typedef CG<double> CGD;
    typedef AD<CGD> ADCG;

    // independent variable vector
    CppAD::vector<ADCG> x(2);
    x[0] = 2.;
    x[1] = 3.;
    Independent(x);

    // dependent variable vector 
    CppAD::vector<ADCG> y(2);

    // the model
    ADCG a = x[0] / 1. + x[1] * x[1];
    ADCG b = a / 2e-6;
    y[0] = b + 1 / (sign(b)*5 * a);
    y[1] = x[1];

    ADFun<CGD> fun(x, y); // the model tape

    /**
     * start the special steps for source code generation
     * for a Jacobian
     */
    CodeHandler<double> handler;

    CppAD::vector<CGD> indVars(2);
    handler.makeVariables(indVars);

    //CppAD::vector<CGD> jac = fun.SparseJacobian(indVars);
    CppAD::vector<CGD> vals = fun.Forward(0, indVars);

    LanguageLatex<double> langLatex;
    LangLatexDefaultVariableNameGenerator<double> nameGen;

    std::ofstream texfile;
    texfile.open("algorithm.tex");

    handler.generateCode(texfile, langLatex, vals, nameGen);

    texfile.close();

    std::string dir = system::getWorkingDirectory();

    ASSERT_NO_THROW(system::callExecutable(PDFLATEX_COMPILER,{"-halt-on-error", "-shell-escape", system::createPath(dir, "latexTemplate.tex")}));

}
TEST_F(CppADCGEvaluatorTest, Print) {
    CodeHandler<double> handler;

    std::vector<CG<double>> x(3);
    handler.makeVariables(x);

    std::vector<CG<double>> y(3);

    y[0] = makePrintValue("y[0]=", x[0] + x[1] + x[2], "\n");
    y[1] = makePrintValue("x[0]=", x[0], "\n") * 2;
    y[2] = makePrintValue("y[2]=", y[1], "\n") * 2;

    CppAD::cg::Evaluator<double, double, CppAD::AD<double>> evaluator(handler);

    std::vector<CppAD::AD<double>> xNew(x.size());
    CppAD::Independent(xNew);

    std::vector<CppAD::AD<double>> yNew(y.size());

    std::cout << "Evaluating..."<<std::endl;
    evaluator.evaluate(xNew, yNew, y);

    std::cout << "Using tape..."<<std::endl;
    CppAD::ADFun<double> fun(xNew, yNew);

    std::vector<double> x2(x.size());
    for(size_t i = 0; i< x2.size(); ++i)
        x2[i] = i + 1;

    std::vector<double> y2 = fun.Forward(0, x2);

    // validate results
    ASSERT_EQ(y2[0], x2[0] + x2[1] + x2[2]);
    ASSERT_EQ(y2[1], x2[0] * 2);
    ASSERT_EQ(y2[2], y2[1] * 2);
}
std::vector<CG<Base> > prepareGraphForward0WithLoops(CodeHandler<Base>& handler,
                                                     size_t m, /// range
                                                     const std::vector<CG<Base>>& x, /// independent variables
                                                     LoopFreeModel<Base>* funNoLoops, /// possibly null
                                                     const std::set<LoopModel<Base>*>& loopTapes) {
    using namespace std;
    using namespace loops;

    using CGBase = CG<Base>;

    std::vector<CGBase> y(m);

    // temporaries
    std::vector<CGBase> tmps;

    /**
     * original equations outside the loops 
     */
    if (funNoLoops != nullptr) {
        const std::vector<size_t>& origEq = funNoLoops->getOrigDependentIndexes();

        std::vector<CGBase> depNL = funNoLoops->getTape().Forward(0, x);

        // original equations
        for (size_t e = 0; e < origEq.size(); e++) {
            y[origEq[e]] = depNL[e];
        }

        tmps.resize(depNL.size() - origEq.size());
        for (size_t i = origEq.size(); i < depNL.size(); i++)
            tmps[i - origEq.size()] = depNL[i];
    }

    /**
     * equations in loops
     */
    OperationNode<Base>* iterationIndexDcl = handler.makeIndexDclrNode(LoopModel<Base>::ITERATION_INDEX_NAME);

    for (LoopModel<Base>* itl : loopTapes) {
        LoopModel<Base>& lModel = *itl;
        size_t nIterations = lModel.getIterationCount();
        const std::vector<std::vector<LoopPosition> >& dependents = lModel.getDependentIndexes();

        /**
         * make the loop start
         */
        LoopStartOperationNode<Base>* loopStart = handler.makeLoopStartNode(*iterationIndexDcl, nIterations);

        IndexOperationNode<Base>* iterationIndexOp = handler.makeIndexNode(*loopStart);
        std::set<IndexOperationNode<Base>*> indexesOps;
        indexesOps.insert(iterationIndexOp);

        std::vector<IfElseInfo<Base> > ifElses;

        /**
         * evaluate the loop body
         */
        std::vector<CGBase> indexedIndeps = createIndexedIndependents(handler, lModel, *iterationIndexOp);
        std::vector<CGBase> xl = createLoopIndependentVector(handler, lModel, indexedIndeps, x, tmps);
        if (xl.size() == 0) {
            xl.resize(1); // does not depend on any variable but CppAD requires at least one
            xl[0] = Base(0);
        }
        std::vector<CGBase> yl = lModel.getTape().Forward(0, xl);

        /**
         * make the loop end
         */
        size_t assignOrAdd = 0;

        const std::vector<IndexPattern*>& depPatterns = lModel.getDependentIndexPatterns();
        std::vector<std::pair<CGBase, IndexPattern*> > indexedLoopResults(yl.size());
        for (size_t i = 0; i < yl.size(); i++) {
            std::map<size_t, size_t> locationsIter2Pos;

            for (size_t it = 0; it < nIterations; it++) {
                if (dependents[i][it].original < m) {
                    locationsIter2Pos[it] = dependents[i][it].original;
                }
            }

            indexedLoopResults[i] = createLoopResult(handler, locationsIter2Pos, nIterations,
                                                     yl[i], depPatterns[i], assignOrAdd,
                                                     *iterationIndexOp, ifElses);
        }

        LoopEndOperationNode<Base>* loopEnd = createLoopEnd(handler, *loopStart, indexedLoopResults, indexesOps, assignOrAdd);

        for (size_t i = 0; i < dependents.size(); i++) {
            for (size_t it = 0; it < nIterations; it++) {
                // an additional alias variable is required so that each dependent variable can have its own ID
                size_t e = dependents[i][it].original;
                if (e < m) { // some equations are not present in all iteration
                    y[e] = handler.createCG(*handler.makeNode(CGOpCode::DependentRefRhs,{e}, {*loopEnd}));
                }
            }
        }

        /**
         * move non-indexed expressions outside loop
         */
        moveNonIndexedOutsideLoop(handler, *loopStart, *loopEnd);
    }

    return y;
}