/*
 *  Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * either version 2, or (at your option) any later version of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "GenerationVisitor.h"

#include <map>

#include <Cauchy/Macros_p.h>
#include <Cauchy/Options.h>
#include <Cauchy/Variable.h>

#include "ExpressionResult.h"
#include <Cauchy/AST/Statement.h>
#include <Cauchy/AST/FunctionDefinition.h>
#include <Cauchy/AST/Expression.h>
#include <Cauchy/DeclarationsRegistry.h>
#include <Cauchy/FunctionDeclaration.h>

#include "GeneratorPrivateBase.h"
#include <complex>

using namespace EigenBackend;
namespace AST = Cauchy::AST;

#define TO_STR(x) \
  (x).scast<EigenBackend::ExpressionResult>()->result()

#define TO_TYPE(x) \
  (x).scast<EigenBackend::ExpressionResult>()->type()

#define BINARY_T(op, type) \
  return new ExpressionResult(TO_STR(arg1) + " " + op + " " + TO_STR(arg2), type);

#define BINARY(op) \
  BINARY_T(op, Cauchy::Type::optype( TO_TYPE(arg1), TO_TYPE(arg2)))

#define BINARY_ADDITIVE(op)                                             \
  if(TO_TYPE(arg1)->isMatrix() and TO_TYPE(arg2)->isNumber())           \
  {                                                                     \
    return new ExpressionResult(TO_STR(arg1) + ".array() " + op + " " + TO_STR(arg2), Cauchy::Type::optype( TO_TYPE(arg1), TO_TYPE(arg2))); \
  } else if(TO_TYPE(arg1)->isNumber() and TO_TYPE(arg2)->isMatrix()) {  \
    return new ExpressionResult(TO_STR(arg1) + " " + op + " " + TO_STR(arg2) + ".array()", Cauchy::Type::optype( TO_TYPE(arg1), TO_TYPE(arg2))); \
  } else {                                                              \
    BINARY(op)                                                          \
  }

#define DOT_BINARY(op, function) \
  if( TO_TYPE(arg1)->dataType() == Cauchy::Type::MATRIX) { \
    return new ExpressionResult(TO_STR(arg1) + "." function "(" + TO_STR(arg2) + ")", TO_TYPE(arg1)); \
  } \
  if( TO_TYPE(arg2)->dataType() == Cauchy::Type::MATRIX) { \
    return new ExpressionResult(TO_STR(arg2) + "." function "(" + TO_STR(arg1) + ")", TO_TYPE(arg1)); \
  } \
  return new ExpressionResult("(" + TO_STR(arg1) + " " + op + " " + TO_STR(arg2) + ")", Cauchy::Type::optype( TO_TYPE(arg1), TO_TYPE(arg2)));

#define UNARY(op) \
  return new ExpressionResult(op + TO_STR(arg1), TO_TYPE(arg1));

struct GenerationVisitor::Private : public GeneratorVisitorPrivate {
  Cauchy::String declaration;
  std::map<Cauchy::Variable*, std::map<const Cauchy::Type*, bool> > declared;
  Cauchy::String body;
  Cauchy::String result;
  bool mainFunction;
  /**
   * Declare that the variable will be used, and if needed it is declared.
   */
  void useVariable(Cauchy::Variable* var);
  void endFunction();
};

void GenerationVisitor::Private::useVariable(Cauchy::Variable* var)
{
  if(var->qualifier() != Cauchy::Variable::Normal) return;
  std::map<const Cauchy::Type*, bool>& vd = declared[var];
  const Cauchy::Type* type = var->type();
  if(vd.find(type) == vd.end())
  {
    vd[type] = true;
    declaration += typeName(type) + " " + var->name() + ";\n";
  }
  CAUCHY_ASSERT(vd[type]);
}

void GenerationVisitor::Private::endFunction()
{
  if(body.isEmpty() or declaration.isEmpty())
  {
    CAUCHY_ASSERT(body.isEmpty() and declaration.isEmpty());
    return;
  }
  result += declaration + body;
  if(mainFunction)
  {
    bool hasReturn = false;
    for(std::map<Cauchy::Variable*, std::map<const Cauchy::Type*, bool> >::iterator it = declared.begin();
        it != declared.end(); ++it)
    {
      if(it->first->name() == "retmain")
      {
        hasReturn = true;
        result += "  return (int)retmain;\n";
      }
    }
    if(not hasReturn)
    {
      result += "  return 0;\n";
    }
  }
  result += "}\n";
  declaration = "";
  body = "";
  unindent();
}

GenerationVisitor::GenerationVisitor(const Cauchy::Options& /*options*/) : d(new Private)
{
  d->header =
  "#include <Eigen/Core>\n"
  "#ifndef _CAUCHY_DEFINITIONS_\n"
  "#define _CAUCHY_DEFINITIONS_\n"
  "  namespace Cauchy {\n"
  "    typedef Eigen::MatrixXd Matrix;\n"
  "    typedef double Number;\n"
  "  }\n"
  "  #define Number Cauchy::Number\n"
  "#endif\n\n"
  "// Includes\n"
  "#include <Cauchy/Eigen3/MatrixOperators.h>\n";

  d->indentation = 0;
  d->indentationCache = "";
}

GenerationVisitor::~GenerationVisitor()
{
  delete d;
}

void GenerationVisitor::startMainFunction()
{
  d->endFunction();
  d->indent();
  d->declaration = "int main(int, char**)\n{\n";
  d->mainFunction = true;
}

void GenerationVisitor::startFunction(const AST::FunctionDefinition* functionDefinition)
{
  d->endFunction();
  d->indent();
  CAUCHY_ASSERT(functionDefinition->declaration());
  d->declaration = d->functionDeclaration(functionDefinition->declaration(), functionDefinition);
  
  d->declaration += "\n{\n";
  d->mainFunction = false;
}

void GenerationVisitor::declareGlobal(Cauchy::Variable* global)
{
  CAUCHY_ASSERT(d->body.isEmpty() and d->declaration.isEmpty());
  d->result += d->typeName(global->declaration()->type()) + " " + global->name() + ";\n";
}

Cauchy::String GenerationVisitor::result() const
{
  d->endFunction();
  return d->header + "\n" + d->result;
}

void GenerationVisitor::loadFunctionsDeclarations(Cauchy::DeclarationsRegistry* registry)
{
  if(not registry->load("Cauchy/Eigen3.cfd"))
  {
    abort();
  }
}

AST::ExpressionResultSP GenerationVisitor::generateNumber(const Cauchy::String& arg1, Cauchy::Type::DataType _type, const AST::Annotation& _annotation)
{
  if(_type == Cauchy::Type::INTEGER)
  {
    return new ExpressionResult(arg1, Cauchy::Type::Int32);
  } else {
    return new ExpressionResult(arg1, Cauchy::Type::Double);
  }
}


AST::ExpressionResultSP GenerationVisitor::generateComplexNumber(const Cauchy::String& _real, const Cauchy::String& _imag, Cauchy::Type::DataType _type, const AST::Annotation& _annotation)
{
  Cauchy::String r = "std::complex<double>(" + _real + ", " + _imag + ")";
  if(_type == Cauchy::Type::INTEGER)
  {
    return new ExpressionResult(r, Cauchy::Type::Int32);
  } else {
    return new ExpressionResult(r, Cauchy::Type::Double);
  }
}

AST::ExpressionResultSP GenerationVisitor::generateBoolean(bool arg1, const AST::Annotation& /*_annotation*/)
{
  if(arg1)
  {
    return new ExpressionResult("true", Cauchy::Type::Logical);
  } else {
    return new ExpressionResult("false", Cauchy::Type::Logical);
  }
}

AST::ExpressionResultSP GenerationVisitor::generateMatrixExpression(const Cauchy::Type* _type, int size1, int size2, const std::list<AST::ExpressionResultSP>& results, const AST::Annotation& /*_annotation*/)
{
  // ie (Eigen::MatrixXd(2,3) << 1, 2, 3, 4, 5, 6).finished()
  Cauchy::String r = "(" + d->matrixTypeName(_type->embeddedType()) + "(" + Cauchy::String::number(size1) + "," + Cauchy::String::number(size2) +") << ";
  
  std::size_t count = 0;
  foreach(AST::ExpressionResultSP er, results)
  {
    r += TO_STR(er);
    if(count != results.size() - 1)
    {
      r += ", ";
    }
    ++count;
  }
  return new ExpressionResult(r + ").finished()", Cauchy::Type::matrixType(Cauchy::Type::Double));
}

AST::ExpressionResultSP GenerationVisitor::generateVariable(Cauchy::Variable* var, AST::ExpressionResultSP idx1, AST::ExpressionResultSP idx2, const Cauchy::AST::Annotation& annotation)
{
  if(var->type() == Cauchy::Type::Unknown)
  {
    CAUCHY_ASSERT(var->declaration() == 0);
    d->useUnknown();
    return new ExpressionResult("Cauchy::Unknown(\"" + var->name() + "\")", var->type());
  }
  d->useVariable(var);
  
  Cauchy::String output;
  if(var->declaration())
  {
    d->useInclude(var->declaration()->header());
    output = var->declaration()->callName();
  } else {
    output = var->name();
  }

  if(idx1.isNull() and idx2.isNull())
  {
    return new ExpressionResult(output, var->type());
  } else if(idx2.isNull())
  {
    CAUCHY_ASSERT(not idx1.isNull());
    return new ExpressionResult(output + ".data()[" + TO_STR(idx1) + " - 1]", var->type()->embeddedType());
  } else {
    if(TO_TYPE(idx1) == Cauchy::Type::InfiniteRange)
    {
      if(TO_TYPE(idx2) == Cauchy::Type::InfiniteRange)
      {
        return new ExpressionResult(output, var->type());
      } else if(TO_TYPE(idx2) == Cauchy::Type::Range) {
        Cauchy::SharedPointer<ExpressionResult> idx2_e = idx2.scast<ExpressionResult>();
        if(idx2_e->step() != "1") reportError("Unsupported step", annotation);
        return new ExpressionResult(output + ".leftCols(" + idx2_e->end() + " - 1).rightCols(" + idx2_e->start() + " - 1)", var->type());        
      } else {
        return new ExpressionResult(output + ".col(" + TO_STR(idx2) + " - 1)", var->type());
      }
    } else if(TO_TYPE(idx1) == Cauchy::Type::Range)
    {
      Cauchy::SharedPointer<ExpressionResult> idx1_e = idx1.scast<ExpressionResult>();
      if(idx1_e->step() != "1") reportError("Unsupported step", annotation);
      if(TO_TYPE(idx2) == Cauchy::Type::InfiniteRange)
      {
        return new ExpressionResult(output + ".topRows(" + idx1_e->end() + " - 1).bottomRows(" + idx1_e->start() + " - 1)", var->type());        
      } else if(TO_TYPE(idx2) == Cauchy::Type::Range) {
        Cauchy::SharedPointer<ExpressionResult> idx2_e = idx2.scast<ExpressionResult>();
        if(idx2_e->step() != "1") reportError("Unsupported step", annotation);
        return new ExpressionResult(output + ".block(" + idx1_e->start() + " - 1, "
                      + idx2_e->start() + " - 1, "
                      + idx1_e->end() + " - " + idx1_e->start() + " + 1, "
                      + idx2_e->end() + " - " + idx2_e->start() + " + 1)", var->type());
      } else {
        return new ExpressionResult(output + ".col(" + TO_STR(idx2) + " - 1).segment("
                      + idx1_e->start() + "- 1, "
                      + idx1_e->end() + " - " + idx1_e->start() + " + 1)", var->type());
      }
      
    }
    else
    {
      if(TO_TYPE(idx2) == Cauchy::Type::InfiniteRange)
      {
        return new ExpressionResult(output + ".row(" + TO_STR(idx1) + " - 1)", var->type());
      } else if(TO_TYPE(idx2) == Cauchy::Type::Range) {
        Cauchy::SharedPointer<ExpressionResult> idx2_e = idx2.scast<ExpressionResult>();
        if(idx2_e->step() != "1") reportError("Unsupported step", annotation);
        return new ExpressionResult(output + ".row(" + TO_STR(idx1) + " - 1).segment("
                      + idx2_e->start() + " - 1, "
                      + idx2_e->end() + " - " + idx2_e->start() + " + 1)", var->type());        
      } else {
        return new ExpressionResult(output + "(" + TO_STR(idx1) + " - 1, " + TO_STR(idx2) + " - 1)", var->type()->embeddedType());
      }
    }
  }
}

AST::ExpressionResultSP GenerationVisitor::generateRangeExpression(AST::ExpressionResultSP startExpr, AST::ExpressionResultSP endExpr, AST::ExpressionResultSP stepExpr, const AST::Annotation& /*arg4*/)
{
  Cauchy::String step = stepExpr.isNull() ? Cauchy::String("1") : TO_STR(stepExpr);
  return new ExpressionResult(TO_STR(startExpr), step, TO_STR(endExpr));
}

Cauchy::AST::ExpressionResultSP GenerationVisitor::generateInfiniteRangeExpression()
{
  return new ExpressionResult("", Cauchy::Type::InfiniteRange);
}

AST::ExpressionResultSP GenerationVisitor::generateFunctionCall(const Cauchy::String& _function, const Cauchy::FunctionDeclaration* declaration, const Cauchy::Variable* _variable, const std::list< AST::ExpressionResultSP >& _arguments, const std::vector< AST::ExpressionResultSP >& _returns, const Cauchy::AST::Annotation& /*_annotation*/ /*_annotation*/)
{
  Cauchy::String r;
  if(_returns.size() >= 1)
  {
    r = TO_STR(_returns[0]) + " = ";
  }
  if(declaration)
  {
    r += declaration->callName() + "(";
    d->useInclude(declaration->header());
  } else if(_variable) {
    r += "*" + _function + "(";
  } else {
    r += _function + "(";
  }
  if(not _arguments.empty())
  {
    std::size_t count = 0;
    for(std::list<AST::ExpressionResultSP>::const_iterator it = _arguments.begin();
        it != _arguments.end(); ++it)
    {
      r += TO_STR(*it);
      if(count != _arguments.size() - 1)
      {
        r += ", ";
      }
      ++count;
    }
  }
  for(std::size_t i = 1; i < _returns.size(); ++i)
  {
    if( i != 1 or not _arguments.empty())
    {
      r += ", ";
    }
    r += "&" + TO_STR(_returns[i]);
  }
  r += ")";
  const Cauchy::Type* returnType = 0;
  if(declaration)
  {
    returnType = declaration->returns().size() > 0 ? declaration->returns()[0] : Cauchy::Type::Void;
  } else {
    CAUCHY_ASSERT(_variable);
    returnType = _variable->type()->returnType();
  }
  CAUCHY_ASSERT(returnType);
  return new ExpressionResult(r, returnType );
}

AST::ExpressionResultSP GenerationVisitor::generateAssignementExpression(Cauchy::Variable* var, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  d->useVariable(var);
  return new EigenBackend::ExpressionResult( var->name() + " = " + TO_STR(arg2), var->type());
}

AST::ExpressionResultSP GenerationVisitor::generateString(const Cauchy::String& arg1, const AST::Annotation& /*_annotation*/)
{
  return new ExpressionResult("\"" + arg1 + "\"", Cauchy::Type::String);
}

Cauchy::AST::ExpressionResultSP GenerationVisitor::generateFunctionHandle(const Cauchy::String& arg1, const Cauchy::AST::Annotation& )
{
  return new ExpressionResult("&" + arg1, Cauchy::Type::functionHandleType(Cauchy::Type::defaultType(), Cauchy::Type::defaultType()));
}

Cauchy::AST::ExpressionResultSP GenerationVisitor::generateGroupExpression(Cauchy::AST::ExpressionResultSP arg1, const Cauchy::AST::Annotation& /*_annotation*/)
{
  return new ExpressionResult("(" + TO_STR(arg1) + ")", TO_TYPE(arg1));
}

AST::ExpressionResultSP GenerationVisitor::generateAndExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T("&&", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateOrExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T("||", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateEqualExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T("==", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateDifferentExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T("!=", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateInferiorExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T("<", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateInferiorEqualExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T("<=", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateSupperiorExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T(">", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateSupperiorEqualExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_T(">=", Cauchy::Type::Logical);
}

AST::ExpressionResultSP GenerationVisitor::generateAdditionExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_ADDITIVE("+");
}

AST::ExpressionResultSP GenerationVisitor::generateSubtractionExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY_ADDITIVE("-");
}

AST::ExpressionResultSP GenerationVisitor::generateMultiplicationExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY("*");
}

AST::ExpressionResultSP GenerationVisitor::generateElementWiseMultiplicationExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  DOT_BINARY("*", "cwiseProduct");
}

AST::ExpressionResultSP GenerationVisitor::generateDivisionExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  BINARY("/");
}

AST::ExpressionResultSP GenerationVisitor::generateElementWiseDivisionExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  DOT_BINARY("/", "cwiseQuotient");
}

AST::ExpressionResultSP GenerationVisitor::generatePowerExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  return new ExpressionResult( "Cauchy::pow(" + TO_STR(arg1) + ", " + TO_STR(arg2) + ")", Cauchy::Type::optype(TO_TYPE(arg1), TO_TYPE(arg2)));
}

AST::ExpressionResultSP GenerationVisitor::generateElementWisePowerExpresion(AST::ExpressionResultSP arg1, AST::ExpressionResultSP arg2, const AST::Annotation& /*_annotation*/)
{
  return new ExpressionResult( "Cauchy::pow_ew(" + TO_STR(arg1) + ", " + TO_STR(arg2) + ")", Cauchy::Type::optype(TO_TYPE(arg1), TO_TYPE(arg2)));
}

AST::ExpressionResultSP GenerationVisitor::generateTildExpression(AST::ExpressionResultSP arg1, const AST::Annotation& /*_annotation*/)
{
  UNARY("~");
}

AST::ExpressionResultSP GenerationVisitor::generateTransposeExpression(AST::ExpressionResultSP arg1, const AST::Annotation& /*_annotation*/)
{
  return new ExpressionResult(TO_STR(arg1) + ".transpose()", TO_TYPE(arg1));
}

AST::ExpressionResultSP GenerationVisitor::generateMinusExpression(AST::ExpressionResultSP arg1, const AST::Annotation& /*_annotation*/)
{
  UNARY("-");
}

AST::ExpressionResultSP GenerationVisitor::generateNotExpression(AST::ExpressionResultSP arg1, const AST::Annotation& /*_annotation*/)
{
  UNARY("!");
}

void GenerationVisitor::generateExpression(AST::ExpressionResultSP arg, const Cauchy::String& comment, const AST::Annotation& /*_annotation*/)
{
  d->body += d->indentationCache + TO_STR(arg) + ";";
  if(not comment.isEmpty())
  {
    d->body += " //" + comment;
  }
  d->body += "\n";
}

void GenerationVisitor::generateComment(const Cauchy::String& arg1, const AST::Annotation& /*_annotation*/)
{
  d->body += "//" + arg1 + "\n";
}

void GenerationVisitor::generatePrintStatement(AST::ExpressionResultSP arg1, const Cauchy::String& comment, const AST::Annotation& /*_annotation*/)
{
  d->useInclude("iostream");
  d->body += d->indentationCache + "std::cout << (" + TO_STR(arg1) + ") << std::endl;";
  if(not comment.isEmpty())
  {
    d->body += " //" + comment;
  }
  d->body += "\n";
}

void GenerationVisitor::startWhileStatement(AST::ExpressionResultSP arg1, const Cauchy::String& comment, const AST::Annotation& /*arg3*/)
{
  d->body += d->indentationCache + "while( " + TO_STR(arg1) + ")";
  if(not comment.isEmpty())
  {
    d->body += " //" + comment;
  }
  d->body += "\n"+ d->indentationCache + "{\n";
  d->indent();
}

void GenerationVisitor::endWhileStatement(const AST::Annotation& /*arg1*/)
{
  d->unindent();
  d->body += d->indentationCache + "}\n";
}

void GenerationVisitor::generateIfElseStatement(AST::ExpressionResultSP arg1, AST::Statement* ifStatements, const std::vector< std::pair<AST::Expression*, AST::Statement*> >& _elseIfStatements, AST::Statement* elseStatement, const Cauchy::String& comment, const AST::Annotation& /*arg3*/)
{
  d->body += d->indentationCache + "if( " + TO_STR(arg1) + ")";
  if(not comment.isEmpty())
  {
    d->body += " //" + comment;
  }
  d->body += "\n" + d->indentationCache + "{\n";
  d->indent();
  ifStatements->generateStatement(this);
  d->unindent();
  typedef std::pair<AST::Expression*, AST::Statement*> pair;
  foreach( const pair& eis, _elseIfStatements)
  {
    AST::ExpressionResultSP r = eis.first->generateValue(this);
    d->body += d->indentationCache + "} else if(" + TO_STR(r) + ") {\n";
    d->indent();
    eis.second->generateStatement(this);
    d->unindent();
  }
  if(elseStatement)
  {
    d->body += d->indentationCache + "} else {\n";
    d->indent();
    elseStatement->generateStatement(this);
    d->unindent();
  }
  d->body += d->indentationCache + "}\n";
}

void GenerationVisitor::generateForStatement(Cauchy::Variable* variable, AST::ExpressionResultSP expr, AST::Statement* forStatements, Cauchy::String comment, const AST::Annotation& annotation)
{
  d->useVariable(variable);
  d->body += d->indentationCache;
  
  if(TO_TYPE(expr) == Cauchy::Type::Range)
  {
    d->useInclude("Cauchy/Eigen3/RangeIterator.h");
    d->body += "for(Cauchy::RangeIterator cauchy_ri(" + TO_STR(generateVariable(variable, 0, 0, annotation)) + ", " + TO_STR(expr);
  } else {
    d->useInclude("Cauchy/Eigen3/MatrixIterator.h");
    d->body += "for(Cauchy::MatrixIterator<" + d->matrixTypeName(TO_TYPE(expr)->embeddedType()) + "> cauchy_ri(" + TO_STR(generateVariable(variable, 0, 0, annotation)) + ", " + TO_STR(expr);
  }
  
  d->body += "); cauchy_ri.isFinished(); cauchy_ri.next())";
  if(not comment.isEmpty())
  {
    d->body += " //" + comment;
  }
  d->body += "\n" + d->indentationCache + "{\n";
  d->indent();
  forStatements->generateStatement(this);
  d->unindent();
  d->body += d->indentationCache + "}\n";
}

void GenerationVisitor::generateBreak(const AST::Annotation& /*arg3*/)
{
  d->body += d->indentationCache + "break;\n";
}

void GenerationVisitor::generateReturnStatement(AST::FunctionDefinition* arg1, const Cauchy::String& comment, const AST::Annotation& /*arg3*/)
{
  if(arg1->returns().size() == 0)
  {
    d->body += d->indentationCache + "return;";
  } else {
    if(arg1->returns().size() > 1)
    {
      for(std::size_t i = 1; i < arg1->returns().size(); ++i)
      {
        const Cauchy::String& ret = arg1->returns()[i];
        d->body += d->indentationCache + "if(" + ret + "_) *" + ret + "_ = " + ret + ";\n";
      }
    }
    d->body += d->indentationCache + "return " + arg1->returns()[0] + ";";
  }
  if(not comment.isEmpty())
  {
    d->body += " //" + comment;
  }
  d->body += "\n";
}
