CppADCodeGen  2.3.0
A C++ Algorithmic Differentiation Package with Source Code Generation
language_mathml.hpp
1 #ifndef CPPAD_CG_LANGUAGE_MATHML_INCLUDED
2 #define CPPAD_CG_LANGUAGE_MATHML_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2015 Ciengis
6  * Copyright (C) 2018 Joao Leal
7  *
8  * CppADCodeGen is distributed under multiple licenses:
9  *
10  * - Eclipse Public License Version 1.0 (EPL1), and
11  * - GNU General Public License Version 3 (GPL3).
12  *
13  * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14  * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15  * ----------------------------------------------------------------------------
16  * Author: Joao Leal
17  */
18 
19 namespace CppAD {
20 namespace cg {
21 
27 template<class Base>
28 class LanguageMathML : public Language<Base> {
29 public:
30  using Node = OperationNode<Base>;
31  using Arg = Argument<Base>;
32 protected:
33  static const std::string _C_STATIC_INDEX_ARRAY;
34  static const std::string _C_SPARSE_INDEX_ARRAY;
35  static const std::string _ATOMIC_TX;
36  static const std::string _ATOMIC_TY;
37  static const std::string _ATOMIC_PX;
38  static const std::string _ATOMIC_PY;
39 protected:
40  // information from the code handler (not owned)
42  // current indentation
43  size_t _indentationLevel;
44  // css style
45  std::string _style;
46  // javascript source code
47  std::string _javascript;
48  // additional markup for the head
49  std::string _headExtra;
50  std::string _startEq;
51  std::string _endEq;
52  std::string _forStart;
53  std::string _forEnd;
54  std::string _forBodyStart;
55  std::string _forBodyEnd;
56  std::string _ifStart;
57  std::string _ifEnd;
58  std::string _elseIfStart;
59  std::string _elseIfEnd;
60  std::string _elseStart;
61  std::string _elseEnd;
62  std::string _condBodyStart;
63  std::string _condBodyEnd;
64  std::string _assignStr;
65  std::string _assignAddStr;
66  // markup for multiplications
67  std::string _multOpStr;
68  // markup for multiplications with parameters
69  std::string _multValOpStr;
70  // new line characters
71  std::string _endline;
72  // output stream for the generated source code
73  std::ostringstream _code;
74  // creates the variable names
76  // auxiliary string stream
77  std::ostringstream _ss;
78  //
79  size_t _independentSize;
80  //
81  size_t _minTemporaryVarID;
82  // maps the variable IDs to the their position in the dependent vector
83  // (some IDs may be the same as the independent variables when dep = indep)
84  std::map<size_t, size_t> _dependentIDs;
85  // the dependent variable vector
86  const ArrayView<CG<Base> >* _dependent;
87  // whether or not to ignore assignment of constant zero values to dependent variables
88  bool _ignoreZeroDepAssign;
89  // the name of the file to be created without the extension
90  std::string _filename;
91  // the maximum number of assignment (~lines) per local file
92  size_t _maxAssignmentsPerFile;
93  // maps filenames to their content
94  std::map<std::string, std::string>* _sources;
95  // the values in the temporary array
96  std::vector<const Arg*> _tmpArrayValues;
97  // the values in the temporary sparse array
98  std::vector<const Arg*> _tmpSparseArrayValues;
99  //
100  std::vector<const LoopStartOperationNode<Base>*> _currentLoops;
101  // the maximum precision used to print values
102  size_t _parameterPrecision;
103  // whether or not to always enclose the base of a power within parenthesis
104  bool _powBaseEnclose;
105  // whether or not to save dependencies among variables in javascript
106  bool _saveVariableRelations;
107 private:
108  std::string auxArrayName_;
109  std::vector<int> varIds_;
110  std::vector<std::string> depConstIds_;
111  std::vector<std::string> depIsIndepIds_;
112 public:
113 
118  _info(nullptr),
119  _indentationLevel(0),
120  _style(".loop{}\n"
121  ".loopBody{padding-left: 2em;}\n"
122  ".condIf{}\n"
123  ".condElseIf{}\n"
124  ".condElse{}\n"
125  ".condBody{padding-left: 2em;}\n"
126  ".dep{color:#600;}\n"
127  ".indep{color:#060;}\n"
128  ".tmp{color:#006;}\n"
129  ".index{color:#00f;}\n"),
130  _startEq("<math display=\"block\" class=\"equation\">"),
131  _endEq("</math>"),
132  _forStart("<div class='loop'>"),
133  _forEnd("</div>"),
134  _forBodyStart("<div class='loopBody'>"),
135  _forBodyEnd("</div>"),
136  _ifStart("<div class='condIf'>"),
137  _ifEnd("</div>"),
138  _elseIfStart("<div class='condElseIf'>"),
139  _elseIfEnd("</div>"),
140  _elseStart("<div class='condElse'>"),
141  _elseEnd("</div>"),
142  _condBodyStart("<div class='condBody'>"),
143  _condBodyEnd("</div>"),
144  _assignStr("<mo>=</mo>"),
145  _assignAddStr("<mo>+=</mo>"),
146  _multOpStr("<mo>&it;</mo>"),
147  _multValOpStr("<mo>&times;</mo>"),
148  _endline("\n"),
149  _nameGen(nullptr),
150  _independentSize(0), // not really required (but it avoids warnings)
151  _minTemporaryVarID(0), // not really required (but it avoids warnings)
152  _dependent(nullptr),
153  _ignoreZeroDepAssign(false),
154  _filename("algorithm"),
155  _maxAssignmentsPerFile(0),
156  _sources(nullptr),
157  _parameterPrecision(std::numeric_limits<Base>::digits10),
158  _powBaseEnclose(false),
159  _saveVariableRelations(false) {
160  }
161 
162  inline virtual ~LanguageMathML() {
163  }
164 
165  inline const std::string& getAssignMarkup() const {
166  return _assignStr;
167  }
168 
169  inline void setAssignMarkup(const std::string& assign) {
170  _assignStr = assign;
171  }
172 
173  inline const std::string& getAddAssignMarkup() const {
174  return _assignAddStr;
175  }
176 
177  inline void setAddAssignMarkup(const std::string& assignAdd) {
178  _assignAddStr = assignAdd;
179  }
180 
188  inline const std::string& getMultiplicationMarkup() const {
189  return _multOpStr;
190  }
191 
200  inline void setMultiplicationMarkup(const std::string& multOpStr) {
201  _multOpStr = multOpStr;
202  }
203 
209  inline const std::string& getMultiplicationConstParMarkup() const {
210  return _multValOpStr;
211  }
212 
221  inline void setMultiplicationConstParMarkup(const std::string& multValOpStr) {
222  _multValOpStr = multValOpStr;
223  }
224 
225  inline bool isIgnoreZeroDepAssign() const {
226  return _ignoreZeroDepAssign;
227  }
228 
229  inline void setIgnoreZeroDepAssign(bool ignore) {
230  _ignoreZeroDepAssign = ignore;
231  }
232 
233  void setFilename(const std::string& name) {
234  _filename = name;
235  }
236 
242  void setStyle(const std::string& style) {
243  _style = style;
244  }
245 
246  const std::string& getStyle() const {
247  return _style;
248  }
249 
255  void setJavascript(const std::string& javascript) {
256  _javascript = javascript;
257  }
258 
259  const std::string& getJavascript() const {
260  return _javascript;
261  }
262 
268  void setHeadExtraMarkup(const std::string& headExtra) {
269  _headExtra = headExtra;
270  }
271 
272  const std::string& getHeadExtraMarkup() const {
273  return _headExtra;
274  }
275 
282  virtual void setEquationMarkup(const std::string& begin,
283  const std::string& end) {
284  _startEq = begin;
285  _endEq = end;
286  }
287 
291  virtual const std::string& getEquationStartMarkup() const {
292  return _startEq;
293  }
294 
298  virtual const std::string& getEquationEndMarkup() const {
299  return _endEq;
300  }
301 
308  virtual void setForMarkup(const std::string& begin,
309  const std::string& end) {
310  _forStart = begin;
311  _forEnd = end;
312  }
313 
317  virtual const std::string& getForStartMarkup() const {
318  return _forStart;
319  }
320 
324  virtual const std::string& getForEndMarkup() const {
325  return _forEnd;
326  }
327 
334  virtual void setIfMarkup(const std::string& begin,
335  const std::string& end) {
336  _ifStart = begin;
337  _ifEnd = end;
338  }
339 
343  virtual const std::string& getIfStartMarkup() const {
344  return _ifStart;
345  }
346 
350  virtual const std::string& getIfEndMarkup() const {
351  return _ifEnd;
352  }
353 
360  virtual void setElseIfMarkup(const std::string& begin,
361  const std::string& end) {
362  _elseIfStart = begin;
363  _elseIfEnd = end;
364  }
365 
369  virtual const std::string& getElseIfStartMarkup() const {
370  return _elseIfStart;
371  }
372 
376  virtual const std::string& getElseIfEndMarkup() const {
377  return _elseIfEnd;
378  }
379 
386  virtual void setElseMarkup(const std::string& begin,
387  const std::string& end) {
388  _elseStart = begin;
389  _elseEnd = end;
390  }
391 
395  virtual const std::string& getElseStartMarkup() const {
396  return _elseStart;
397  }
398 
402  virtual const std::string& getElseEndMarkup() const {
403  return _elseEnd;
404  }
405 
412  virtual size_t getParameterPrecision() const {
413  return _parameterPrecision;
414  }
415 
422  virtual void setParameterPrecision(size_t p) {
423  _parameterPrecision = p;
424  }
425 
434  virtual void setAlwaysEnclosePowBase(bool enclose) {
435  _powBaseEnclose = enclose;
436  }
437 
444  virtual bool isAlwaysEnclosePowBase() const {
445  return _powBaseEnclose;
446  }
447 
448  virtual void setMaxAssignmentsPerFunction(size_t maxAssignmentsPerFunction,
449  std::map<std::string, std::string>* sources) {
450  _maxAssignmentsPerFile = maxAssignmentsPerFunction;
451  _sources = sources;
452  }
453 
454  inline void setSaveVariableRelations(bool save) {
455  _saveVariableRelations = save;
456  }
457 
458  bool requiresVariableDependencies() const override {
459  return _saveVariableRelations;
460  }
461 
462  /***************************************************************************
463  * STATIC
464  **************************************************************************/
465  static inline void printIndexCondExpr(std::ostringstream& out,
466  const std::vector<size_t>& info,
467  const std::string& index) {
468  CPPADCG_ASSERT_KNOWN(info.size() > 1 && info.size() % 2 == 0, "Invalid number of information elements for an index condition expression operation");
469 
470  size_t infoSize = info.size();
471  for (size_t e = 0; e < infoSize; e += 2) {
472  if (e > 0) {
473  out << "<mo>&or;</mo>"; // or
474  }
475  size_t min = info[e];
476  size_t max = info[e + 1];
477  if (min == max) {
478  out << "<mi class='index'>" << index << "</mi><mo>==</mo><mn>" << min << "</nm>";
479  } else if (min == 0) {
480  out << "<mi class='index'>" << index << "</mi><mo>&le;</mo><mn>" << max << "</mn>";
481  } else if (max == std::numeric_limits<size_t>::max()) {
482  out << "<mn>" << min << "</mn><mo>&le;</mo><mi class='index'>" << index << "</mi>";
483  } else {
484  if (infoSize != 2)
485  out << "<mfenced><mrow>";
486 
487  if (max - min == 1)
488  out << "<mn>" << min << "</mn><mo>==</mo><mi class='index'>" << index << "</mi><mo>&or;</mo><mi class='index'>" << index << "</mi><mo>==</mo><mn>" << max << "</mn>";
489  else
490  out << "<mn>" << min << "</mn><mo>&le;</mo><mi class='index'>" << index << "</mi><mo>&and;</mo><mi class='index'>" << index << "</mi><mo>&le;</mo><mn>" << max << "</mn";
491 
492  if (infoSize != 2)
493  out << "</mrow></mfenced>";
494  }
495  }
496  }
497 
498  /***************************************************************************
499  *
500  **************************************************************************/
501 
502  inline void printStaticIndexArray(std::ostringstream& os,
503  const std::string& name,
504  const std::vector<size_t>& values);
505 
506  inline void printStaticIndexMatrix(std::ostringstream& os,
507  const std::string& name,
508  const std::map<size_t, std::map<size_t, size_t> >& values);
509 
510  /***************************************************************************
511  * index patterns
512  **************************************************************************/
513  static inline void generateNames4RandomIndexPatterns(const std::set<RandomIndexPattern*>& randomPatterns);
514 
515  inline void printRandomIndexPatternDeclaration(std::ostringstream& os,
516  const std::string& identation,
517  const std::set<RandomIndexPattern*>& randomPatterns);
518 
519  static void indexPattern2String(std::ostream& os,
520  const IndexPattern& ip,
521  const Node& index);
522 
523  static void indexPattern2String(std::ostream& os,
524  const IndexPattern& ip,
525  const std::vector<const Node*>& indexes);
526 
527  static inline void linearIndexPattern2String(std::ostream& os,
528  const LinearIndexPattern& lip,
529  const Node& index);
530 
531  /***************************************************************************
532  * protected
533  **************************************************************************/
534 protected:
535 
536  void generateSourceCode(std::ostream& out,
537  const std::unique_ptr<LanguageGenerationData<Base> >& info) override {
538 
539  const bool multiFile = _maxAssignmentsPerFile > 0 && _sources != nullptr;
540 
541  // clean up
542  _code.str("");
543  _ss.str("");
544  _indentationLevel = 0;
545  auxArrayName_ = "";
546  _currentLoops.clear();
547  depConstIds_.clear();
548  depIsIndepIds_.clear();
549 
550 
551  // save some info
552  _info = info.get();
553  _independentSize = info->independent.size();
554  _dependent = &info->dependent;
555  _nameGen = &info->nameGen;
556  _minTemporaryVarID = info->minTemporaryVarID;
557  const ArrayView<CG<Base> >& dependent = info->dependent;
558  const std::vector<Node*>& variableOrder = info->variableOrder;
559 
560  _tmpArrayValues.resize(_nameGen->getMaxTemporaryArrayVariableID());
561  std::fill(_tmpArrayValues.begin(), _tmpArrayValues.end(), nullptr);
562  _tmpSparseArrayValues.resize(_nameGen->getMaxTemporarySparseArrayVariableID());
563  std::fill(_tmpSparseArrayValues.begin(), _tmpSparseArrayValues.end(), nullptr);
564 
565  varIds_.resize(_minTemporaryVarID + variableOrder.size());
566  std::fill(varIds_.begin(), varIds_.end(), 0);
567 
571  generateNames4RandomIndexPatterns(info->indexRandomPatterns);
572 
576  //generate names for the independent variables
577  for (size_t j = 0; j < _independentSize; j++) {
578  Node& op = *info->independent[j];
579  if (op.getName() == nullptr) {
580  op.setName(_nameGen->generateIndependent(op, getVariableID(op)));
581  }
582  }
583 
584  // generate names for the dependent variables (must be after naming independents)
585  for (size_t i = 0; i < dependent.size(); i++) {
586  Node* node = dependent[i].getOperationNode();
587  if (node != nullptr && node->getOperationType() != CGOpCode::LoopEnd && node->getName() == nullptr) {
588  if (node->getOperationType() == CGOpCode::LoopIndexedDep) {
589  size_t pos = node->getInfo()[0];
590  const IndexPattern* ip = info->loopDependentIndexPatterns[pos];
591  node->setName(_nameGen->generateIndexedDependent(*node, getVariableID(*node), *ip));
592 
593  } else {
594  node->setName(_nameGen->generateDependent(i));
595  }
596  }
597  }
598 
602  const std::vector<FuncArgument>& indArg = _nameGen->getIndependent();
603  const std::vector<FuncArgument>& depArg = _nameGen->getDependent();
604  const std::vector<FuncArgument>& tmpArg = _nameGen->getTemporary();
605  CPPADCG_ASSERT_KNOWN(indArg.size() > 0 && depArg.size() > 0,
606  "There must be at least one dependent and one independent argument");
607  CPPADCG_ASSERT_KNOWN(tmpArg.size() == 3,
608  "There must be three temporary variables");
609 
610  auxArrayName_ = tmpArg[1].name + "p";
611 
615  // dependent variables indexes that are copies of other dependent variables
616  std::set<size_t> dependentDuplicates;
617 
618  for (size_t i = 0; i < dependent.size(); i++) {
619  Node* node = dependent[i].getOperationNode();
620  if (node != nullptr) {
621  CGOpCode type = node->getOperationType();
622  if (type != CGOpCode::Inv && type != CGOpCode::LoopEnd) {
623  size_t varID = getVariableID(*node);
624  if (varID > 0) {
625  std::map<size_t, size_t>::const_iterator it2 = _dependentIDs.find(varID);
626  if (it2 == _dependentIDs.end()) {
627  _dependentIDs[getVariableID(*node)] = i;
628  } else {
629  // there can be several dependent variables with the same ID
630  dependentDuplicates.insert(i);
631  }
632  }
633  }
634  }
635  }
636 
637  // the names of local functions
638  std::vector<std::string> mathMLFiles;
639  if (multiFile) {
640  mathMLFiles.reserve(variableOrder.size() / _maxAssignmentsPerFile);
641  }
642 
646  if (variableOrder.size() > 0) {
647  // generate names for temporary variables
648  for (Node* node : variableOrder) {
649  CGOpCode op = node->getOperationType();
650  if (!isDependent(*node) && op != CGOpCode::IndexDeclaration) {
651  // variable names for temporaries must always be created since they might have been used before with a different name/id
652  if (requiresVariableName(*node) && op != CGOpCode::ArrayCreation && op != CGOpCode::SparseArrayCreation) {
653  node->setName(_nameGen->generateTemporary(*node, getVariableID(*node)));
654  } else if (op == CGOpCode::ArrayCreation) {
655  node->setName(_nameGen->generateTemporaryArray(*node, getVariableID(*node)));
656  } else if (op == CGOpCode::SparseArrayCreation) {
657  node->setName(_nameGen->generateTemporarySparseArray(*node, getVariableID(*node)));
658  }
659  }
660  }
661 
665  if (info->zeroDependents) {
666  // zero initial values
667  const std::vector<FuncArgument>& depArg = _nameGen->getDependent();
668  for (size_t i = 0; i < depArg.size(); i++) {
669  _code << _startEq;
670  const FuncArgument& a = depArg[i];
671  if (a.array) {
672  _code << a.name;
673  } else {
674  _code << "<mrow id='" << createHtmlID(*(*_dependent)[i].getOperationNode()) << "' class='dep'>" << _nameGen->generateDependent(i) << "</mrow>";
675  }
676  _code << _assignStr;
677  printParameter(Base(0.0));
678  _code << _endEq << _endline;
679  }
680  }
681 
682  size_t assignCount = 0;
683  for (Node* it : variableOrder) {
684  // check if a new function should start
685  if (assignCount >= _maxAssignmentsPerFile && multiFile && _currentLoops.empty()) {
686  assignCount = 0;
687  saveLocalFunction(mathMLFiles, mathMLFiles.empty() && info->zeroDependents);
688  }
689 
690  Node& node = *it;
691 
692  // a dependent variable assigned by a loop does require any source code (its done inside the loop)
693  if (node.getOperationType() == CGOpCode::DependentRefRhs) {
694  continue; // nothing to do (this operation is right hand side only)
695  } else if (node.getOperationType() == CGOpCode::TmpDcl) { // temporary variable declaration does not need any source code here
696  continue; // nothing to do (bogus operation)
697  }
698 
699  assignCount += printAssignment(node);
700  }
701 
702  if (mathMLFiles.size() > 0 && assignCount > 0) {
703  assignCount = 0;
704  saveLocalFunction(mathMLFiles, false);
705  }
706  }
707 
708  if (!mathMLFiles.empty()) {
712  CPPADCG_ASSERT_KNOWN(tmpArg[0].array,
713  "The temporary variables must be saved in an array in order to generate multiple functions");
714  printAlgorithmFileStart(_code);
715  for (size_t i = 0; i < mathMLFiles.size(); i++) {
716  _code << "<a href='" << mathMLFiles[i] << ".html'>part " << (i + 1) << "</a><br/>" << _endline;
717  }
718  printAlgorithmFileEnd(_code);
719  }
720 
721  // dependent duplicates
722  if (dependentDuplicates.size() > 0) {
723  _code << "<!-- variable duplicates: " << dependentDuplicates.size() << " -->" << _endline;
724 
725  for (size_t index : dependentDuplicates) {
726  const CG<Base>& dep = dependent[index];
727  std::string varName = _nameGen->generateDependent(index);
728  Node* depNode = dep.getOperationNode();
729  const std::string& origVarName = *depNode->getName();
730 
731  _code << _startEq
732  << "<mrow id='" << createHtmlID(depNode) << "' class='dep'>" << varName << "</mrow>"
733  << _assignStr
734  << "<mrow id='" << createHtmlID(depNode) << "' class='dep'>" << origVarName << "</mrow>";
735  printAssignmentEnd();
736  }
737  }
738 
739  // constant dependent variables
740  bool commentWritten = false;
741  for (size_t i = 0; i < dependent.size(); i++) {
742  if (dependent[i].isParameter()) {
743  if (!_ignoreZeroDepAssign || !dependent[i].isIdenticalZero()) {
744  if (!commentWritten) {
745  _code << "<!-- dependent variables without operations -->" << _endline;
746  commentWritten = true;
747  }
748 
749  std::string depId = "d" + std::to_string(i);
750  if (_saveVariableRelations) {
751  depConstIds_.push_back(depId);
752  }
753 
754  std::string varName = _nameGen->generateDependent(i);
755  _code << _startEq
756  << "<mrow id='" << depId << "' class='dep'>" << varName << "</mrow>" << _assignStr; // id='" << createID(??)
757  printParameter(dependent[i].getValue());
758  printAssignmentEnd();
759  }
760  } else if (dependent[i].getOperationNode()->getOperationType() == CGOpCode::Inv) {
761  if (!commentWritten) {
762  _code << "<!-- dependent variables without operations -->" << _endline;
763  commentWritten = true;
764  }
765 
766  std::string varName = _nameGen->generateDependent(i);
767  const std::string& indepName = *dependent[i].getOperationNode()->getName();
768  std::string depId = createHtmlID(dependent[i].getOperationNode());
769  if (_saveVariableRelations) {
770  depIsIndepIds_.push_back(depId);
771  }
772  _code << _startEq
773  << "<mrow id='" << depId << "' class='dep'>" << varName << "</mrow>"
774  << _assignStr
775  << "<mrow id='" << createHtmlID(dependent[i].getOperationNode()) << "' class='indep'>" << indepName << "</mrow>";
776  printAssignmentEnd(*dependent[i].getOperationNode());
777  }
778  }
779 
783  if (mathMLFiles.empty()) {
784  // a single source file
785  printAlgorithmFileStart(_ss);
786  _ss << _code.str();
787  printAlgorithmFileEnd(_ss);
788 
789  out << _ss.str();
790 
791  if (_sources != nullptr) {
792  (*_sources)[_filename + ".html"] = _ss.str();
793  }
794  } else {
795  // there are multiple source files (this last one is the master)
796  (*_sources)[_filename + ".html"] = _code.str();
797  }
798 
799  }
800 
801  inline size_t getVariableID(const Node& node) const {
802  return _info->varId[node]; // some of these values are 0 and std::numeric_limits<size_t>::max()
803  }
804 
805  inline virtual void printAlgorithmFileStart(std::ostream& out) {
806  out << "<!DOCTYPE html>" << _endline <<
807  "<html lang=\"en\">" << _endline <<
808  "<head>" << _endline <<
809  "<meta charset=\"utf-8\">" << _endline <<
810  "<title>" << _filename << "</title>" << _endline;
811 
812  if (!_headExtra.empty()) {
813  out << _headExtra << _endline;
814  }
815 
816  if (!_style.empty()) {
817  out << "<style>" << _endline
818  << _style << _endline
819  << "</style>" << _endline;
820  }
821 
822  if (_saveVariableRelations) {
823  CPPADCG_ASSERT_UNKNOWN(_info->variableDependencies.size() == _info->variableOrder.size());
824 
825  out << "<script type=\"text/javascript\">" << _endline;
826  out << " var depConst = [];" << _endline;
827  out << " var depIsVar = [];" << _endline;
828  out << " var var2dep = {" << _endline;
829 
830  const auto& varDeps = _info->variableDependencies;
831  const auto& varOrder = _info->variableOrder;
832  bool firstI = true;
833  for (size_t i = 0; i < varDeps.size(); ++i) {
834  if (!varDeps[i].empty()) {
835  if (firstI) {
836  firstI = false;
837  } else {
838  out << "," << _endline;
839  }
840  out << " \"" << getHtmlID(*varOrder[i]) << "\": [";
841  for (const auto* n: varDeps[i]) {
842  if (n != *varDeps[i].begin()) out << ", ";
843  out << getHtmlID(*n);
844  }
845  out << "]";
846  }
847  }
848  out << _endline << " };" << _endline;
849 
850  // transpose of varDeps
851  out << " var dep2var = {" << _endline;
852 
853  std::map<size_t, std::set<size_t> > deps2Var;
854  for (size_t i = 0; i < varDeps.size(); ++i) {
855  size_t idi = getHtmlID(*varOrder[i]);
856  for (const auto* n: varDeps[i]) {
857  size_t idj = getHtmlID(*n);
858  deps2Var[idj].insert(idi);
859  }
860  }
861 
862  for (const auto& pair: deps2Var) {
863  if (pair.first != deps2Var.begin()->first) {
864  out << "," << _endline;
865  }
866  out << " \"" << pair.first << "\": [";
867  for (size_t j: pair.second) {
868  if (j != *pair.second.begin()) out << ", ";
869  out << j;
870  }
871  out << "]";
872  }
873  out << _endline << " };" << _endline;
874 
875  out << "</script>" << _endline;
876  }
877 
878  if (!_javascript.empty()) {
879  out << "<script type=\"text/javascript\">" << _endline <<
880  _javascript << _endline
881  << "</script>" << _endline;
882  }
883 
884  out << "</head>" << _endline <<
885  "" << _endline <<
886  "<body>" << _endline <<
887  "<!-- source file for '" << _filename << "' (automatically generated by CppADCodeGen) -->" << _endline <<
888  "<div id='algorithm'>" << _endline;
889  }
890 
891  inline virtual void printAlgorithmFileEnd(std::ostream& out) {
892 
893  if(_saveVariableRelations) {
894  out << "<script type=\"text/javascript\">" << _endline;
895  out << " depConst = [";
896  bool first = true;
897  for (const auto& d: depConstIds_) {
898  if (first)
899  first = false;
900  else
901  out << ", ";
902  out << "\'" << d << "\'";
903  }
904  out << "];" << _endline;
905  out << " depIsVar = [";
906  first = true;
907  for (const auto& d: depIsIndepIds_) {
908  if (first)
909  first = false;
910  else
911  out << ", ";
912  out << "\'" << d << "\'";
913  }
914  out << "];" << _endline;
915  out << "</script>" << _endline;
916  }
917 
918  out << "</div>" << _endline <<
919  "</body>" << _endline <<
920  "</html>";
921  }
922 
923  inline unsigned printAssignment(Node& node) {
924  return printAssignment(node, node);
925  }
926 
927  inline unsigned printAssignment(Node& nodeName,
928  const Arg& nodeRhs) {
929  if (nodeRhs.getOperation() != nullptr) {
930  return printAssignment(nodeName, *nodeRhs.getOperation());
931  } else {
932  printAssignmentStart(nodeName);
933  printParameter(*nodeRhs.getParameter());
934  printAssignmentEnd(nodeName);
935  return 1;
936  }
937  }
938 
939  inline unsigned printAssignment(Node& nodeName,
940  Node& nodeRhs) {
941  bool createsVar = directlyAssignsVariable(nodeRhs); // do we need to do the assignment here?
942  if (!createsVar) {
943  printAssignmentStart(nodeName);
944  }
945  unsigned lines = printExpressionNoVarCheck(nodeRhs);
946  if (!createsVar) {
947  printAssignmentEnd(nodeRhs);
948  }
949 
950  if (nodeRhs.getOperationType() == CGOpCode::ArrayElement) {
951  Node* array = nodeRhs.getArguments()[0].getOperation();
952  size_t arrayId = getVariableID(*array);
953  size_t pos = nodeRhs.getInfo()[0];
954  if (array->getOperationType() == CGOpCode::ArrayCreation)
955  _tmpArrayValues[arrayId - 1 + pos] = nullptr; // this could probably be removed!
956  else
957  _tmpSparseArrayValues[arrayId - 1 + pos] = nullptr; // this could probably be removed!
958  }
959 
960  return lines;
961  }
962 
963  inline virtual void printAssignmentStart(Node& op) {
964  printAssignmentStart(op, createVariableName(op), isDependent(op));
965  }
966 
967  inline virtual void printAssignmentStart(Node& node, const std::string& varName, bool isDep) {
968  _code << _startEq;
969  _code << "<mrow id='" << createHtmlID(node) << "' class='" << (isDep ? "dep" : "tmp") << "'>" << varName << "</mrow>";
970 
971  CGOpCode op = node.getOperationType();
972  if (op == CGOpCode::DependentMultiAssign || (op == CGOpCode::LoopIndexedDep && node.getInfo()[1] == 1)) {
973  _code << _assignAddStr; // +=
974  } else {
975  _code << _assignStr; // =
976  }
977  }
978 
979  inline virtual void printAssignmentEnd() {
980  _code << _endEq << _endline;
981  }
982 
983  inline virtual void printAssignmentEnd(Node& op) {
984  printAssignmentEnd();
985  }
986 
987  virtual void saveLocalFunction(std::vector<std::string>& localFuncNames,
988  bool zeroDependentArray) {
989  _ss << _filename << "__part_" << (localFuncNames.size() + 1);
990  std::string funcName = _ss.str();
991  _ss.str("");
992 
993  // loop indexes
994  _nameGen->prepareCustomFunctionVariables(_ss);
995  _ss << _code.str();
996  _nameGen->finalizeCustomFunctionVariables(_ss);
997 
998  (*_sources)[funcName + ".html"] = _ss.str();
999  localFuncNames.push_back(funcName);
1000 
1001  _code.str("");
1002  _ss.str("");
1003  }
1004 
1005  bool createsNewVariable(const Node& var,
1006  size_t totalUseCount) const override {
1007  CGOpCode op = var.getOperationType();
1008  if (totalUseCount > 1) {
1009  return op != CGOpCode::ArrayElement && op != CGOpCode::Index && op != CGOpCode::IndexDeclaration && op != CGOpCode::Tmp;
1010  } else {
1011  return ( op == CGOpCode::ArrayCreation ||
1012  op == CGOpCode::SparseArrayCreation ||
1013  op == CGOpCode::AtomicForward ||
1014  op == CGOpCode::AtomicReverse ||
1015  op == CGOpCode::ComLt ||
1016  op == CGOpCode::ComLe ||
1017  op == CGOpCode::ComEq ||
1018  op == CGOpCode::ComGe ||
1019  op == CGOpCode::ComGt ||
1020  op == CGOpCode::ComNe ||
1021  op == CGOpCode::LoopIndexedDep ||
1022  op == CGOpCode::LoopIndexedTmp ||
1023  op == CGOpCode::IndexAssign ||
1024  op == CGOpCode::Assign) &&
1025  op != CGOpCode::CondResult;
1026  }
1027  }
1028 
1029  virtual bool requiresVariableName(const Node& var) const {
1030  CGOpCode op = var.getOperationType();
1031  return (_info->totalUseCount.get(var) > 1 &&
1032  op != CGOpCode::AtomicForward &&
1033  op != CGOpCode::AtomicReverse &&
1034  op != CGOpCode::LoopStart &&
1035  op != CGOpCode::LoopEnd &&
1036  op != CGOpCode::Index &&
1037  op != CGOpCode::IndexAssign &&
1038  op != CGOpCode::StartIf &&
1039  op != CGOpCode::ElseIf &&
1040  op != CGOpCode::Else &&
1041  op != CGOpCode::EndIf &&
1042  op != CGOpCode::CondResult &&
1043  op != CGOpCode::LoopIndexedTmp &&
1044  op != CGOpCode::Tmp);
1045  }
1046 
1054  virtual bool directlyAssignsVariable(const Node& var) const {
1055  CGOpCode op = var.getOperationType();
1056  return isCondAssign(op) ||
1057  op == CGOpCode::ArrayCreation ||
1058  op == CGOpCode::SparseArrayCreation ||
1059  op == CGOpCode::AtomicForward ||
1060  op == CGOpCode::AtomicReverse ||
1061  op == CGOpCode::DependentMultiAssign ||
1062  op == CGOpCode::LoopStart ||
1063  op == CGOpCode::LoopEnd ||
1064  op == CGOpCode::IndexAssign ||
1065  op == CGOpCode::StartIf ||
1066  op == CGOpCode::ElseIf ||
1067  op == CGOpCode::Else ||
1068  op == CGOpCode::EndIf ||
1069  op == CGOpCode::CondResult ||
1070  op == CGOpCode::IndexDeclaration;
1071  }
1072 
1073  bool requiresVariableArgument(enum CGOpCode op, size_t argIndex) const override {
1074  return op == CGOpCode::CondResult;
1075  }
1076 
1077  inline const std::string& createVariableName(Node& var) {
1078  CGOpCode op = var.getOperationType();
1079  CPPADCG_ASSERT_UNKNOWN(getVariableID(var) > 0);
1080  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicForward);
1081  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicReverse);
1082  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopStart);
1083  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopEnd);
1084  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::Index);
1085  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexAssign);
1086  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexDeclaration);
1087 
1088  if (var.getName() == nullptr) {
1089  if (op == CGOpCode::ArrayCreation) {
1090  var.setName(_nameGen->generateTemporaryArray(var, getVariableID(var)));
1091 
1092  } else if (op == CGOpCode::SparseArrayCreation) {
1093  var.setName(_nameGen->generateTemporarySparseArray(var, getVariableID(var)));
1094 
1095  } else if (op == CGOpCode::LoopIndexedDep) {
1096  size_t pos = var.getInfo()[0];
1097  const IndexPattern* ip = _info->loopDependentIndexPatterns[pos];
1098  var.setName(_nameGen->generateIndexedDependent(var, getVariableID(var), *ip));
1099 
1100  } else if (op == CGOpCode::LoopIndexedIndep) {
1101  size_t pos = var.getInfo()[1];
1102  const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1103  var.setName(_nameGen->generateIndexedIndependent(var, getVariableID(var), *ip));
1104 
1105  } else if (getVariableID(var) <= _independentSize) {
1106  // independent variable
1107  var.setName(_nameGen->generateIndependent(var, getVariableID(var)));
1108 
1109  } else if (getVariableID(var) < _minTemporaryVarID) {
1110  // dependent variable
1111  std::map<size_t, size_t>::const_iterator it = _dependentIDs.find(getVariableID(var));
1112  CPPADCG_ASSERT_UNKNOWN(it != _dependentIDs.end());
1113 
1114  size_t index = it->second;
1115  var.setName(_nameGen->generateDependent(index));
1116 
1117  } else if (op == CGOpCode::LoopIndexedTmp || op == CGOpCode::Tmp) {
1118  CPPADCG_ASSERT_KNOWN(var.getArguments().size() >= 1, "Invalid number of arguments for loop indexed temporary operation");
1119  Node* tmpVar = var.getArguments()[0].getOperation();
1120  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation");
1121  return createVariableName(*tmpVar);
1122 
1123  } else {
1124  // temporary variable
1125  var.setName(_nameGen->generateTemporary(var, getVariableID(var)));
1126  }
1127  }
1128 
1129 
1130  return *var.getName();
1131  }
1132 
1136  inline size_t getHtmlID(const Node& var) const {
1137  return var.getHandlerPosition(); // always unique and higher than zero
1138  }
1139 
1143  inline std::string createHtmlID(const Node* var) {
1144  return createHtmlID(*var);
1145  }
1146 
1150  virtual std::string createHtmlID(const Node& var) {
1151  size_t id = getHtmlID(var);
1152  if (varIds_.size() <= id) {
1153  varIds_.resize(id + 1 + varIds_.size() * 3 / 2, 0);
1154  }
1155 
1156  int n = varIds_[id];
1157  varIds_[id]++;
1158 
1159  if (n == 0)
1160  return "v" + std::to_string(id);
1161  else
1162  return "v" + std::to_string(id) + "_" + std::to_string(n);
1163  }
1164 
1165  virtual void printIndependentVariableName(Node& op) {
1166  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 0, "Invalid number of arguments for independent variable");
1167  _code << "<mrow id='" << createHtmlID(op) << "' class='indep'>" << _nameGen->generateIndependent(op, getVariableID(op)) << "</mrow>";
1168  }
1169 
1170  virtual unsigned print(const Arg& arg) {
1171  if (arg.getOperation() != nullptr) {
1172  // expression
1173  return printExpression(*arg.getOperation());
1174  } else {
1175  // parameter
1176  printParameter(*arg.getParameter());
1177  return 1;
1178  }
1179  }
1180 
1181  virtual unsigned printExpression(Node& node) {
1182  if (getVariableID(node) > 0) {
1183  const std::string& name = createVariableName(node); // use variable name
1184 
1185  CGOpCode op = node.getOperationType();
1186  if (getVariableID(node) >= _minTemporaryVarID || op == CGOpCode::ArrayCreation || op == CGOpCode::SparseArrayCreation || op == CGOpCode::LoopIndexedDep || op == CGOpCode::LoopIndexedIndep) {
1187 
1188  _code << "<mrow id='" << createHtmlID(node) << "' class='tmp'>" << name << "</mrow>"; // TODO!!!!!!!!!!!!!!!!!!!!!!!
1189 
1190  } else if (getVariableID(node) <= _independentSize) {
1191  // independent variable
1192  _code << "<mrow id='" << createHtmlID(node) << "' class='indep'>" << name << "</mrow>";
1193 
1194  } else {
1195  // dependent variable
1196  _code << "<mrow id='" << createHtmlID(node) << "' class='dep'>" << name << "</mrow>";
1197 
1198  }
1199 
1200  return 1;
1201  } else {
1202  // print expression code
1203  return printExpressionNoVarCheck(node);
1204  }
1205  }
1206 
1207  virtual unsigned printExpressionNoVarCheck(Node& node) {
1208  CGOpCode op = node.getOperationType();
1209  switch (op) {
1210  case CGOpCode::ArrayCreation:
1211  printArrayCreationOp(node);
1212  break;
1213  case CGOpCode::SparseArrayCreation:
1214  printSparseArrayCreationOp(node);
1215  break;
1216  case CGOpCode::ArrayElement:
1217  printArrayElementOp(node);
1218  break;
1219  case CGOpCode::Assign:
1220  return printAssignOp(node);
1221 
1222  case CGOpCode::Abs:
1223  case CGOpCode::Acos:
1224  case CGOpCode::Asin:
1225  case CGOpCode::Atan:
1226  case CGOpCode::Cosh:
1227  case CGOpCode::Cos:
1228  case CGOpCode::Exp:
1229  case CGOpCode::Log:
1230  case CGOpCode::Sign:
1231  case CGOpCode::Sinh:
1232  case CGOpCode::Sin:
1233  case CGOpCode::Sqrt:
1234  case CGOpCode::Tanh:
1235  case CGOpCode::Tan:
1236  printUnaryFunction(node);
1237  break;
1238  case CGOpCode::AtomicForward: // atomicFunction.forward(q, p, vx, vy, tx, ty)
1239  printAtomicForwardOp(node);
1240  break;
1241  case CGOpCode::AtomicReverse: // atomicFunction.reverse(p, tx, ty, px, py)
1242  printAtomicReverseOp(node);
1243  break;
1244  case CGOpCode::Add:
1245  printOperationAdd(node);
1246  break;
1247  case CGOpCode::Alias:
1248  return printOperationAlias(node);
1249 
1250  case CGOpCode::ComLt:
1251  case CGOpCode::ComLe:
1252  case CGOpCode::ComEq:
1253  case CGOpCode::ComGe:
1254  case CGOpCode::ComGt:
1255  case CGOpCode::ComNe:
1256  printConditionalAssignment(node);
1257  break;
1258  case CGOpCode::Div:
1259  printOperationDiv(node);
1260  break;
1261  case CGOpCode::Inv:
1262  printIndependentVariableName(node);
1263  break;
1264  case CGOpCode::Mul:
1265  printOperationMul(node);
1266  break;
1267  case CGOpCode::Pow:
1268  printPowFunction(node);
1269  break;
1270  case CGOpCode::Pri:
1271  // do nothing
1272  break;
1273  case CGOpCode::Sub:
1274  printOperationMinus(node);
1275  break;
1276 
1277  case CGOpCode::UnMinus:
1278  printOperationUnaryMinus(node);
1279  break;
1280 
1281  case CGOpCode::DependentMultiAssign:
1282  return printDependentMultiAssign(node);
1283 
1284  case CGOpCode::Index:
1285  return 0; // nothing to do
1286  case CGOpCode::IndexAssign:
1287  printIndexAssign(node);
1288  break;
1289  case CGOpCode::IndexDeclaration:
1290  return 0; // already done
1291 
1292  case CGOpCode::LoopStart:
1293  printLoopStart(node);
1294  break;
1295  case CGOpCode::LoopIndexedIndep:
1296  printLoopIndexedIndep(node);
1297  break;
1298  case CGOpCode::LoopIndexedDep:
1299  printLoopIndexedDep(node);
1300  break;
1301  case CGOpCode::LoopIndexedTmp:
1302  printLoopIndexedTmp(node);
1303  break;
1304  case CGOpCode::TmpDcl:
1305  // nothing to do
1306  return 0;
1307  case CGOpCode::Tmp:
1308  printTmpVar(node);
1309  break;
1310  case CGOpCode::LoopEnd:
1311  printLoopEnd(node);
1312  break;
1313  case CGOpCode::IndexCondExpr:
1314  printIndexCondExprOp(node);
1315  break;
1316  case CGOpCode::StartIf:
1317  printStartIf(node);
1318  break;
1319  case CGOpCode::ElseIf:
1320  printElseIf(node);
1321  break;
1322  case CGOpCode::Else:
1323  printElse(node);
1324  break;
1325  case CGOpCode::EndIf:
1326  printEndIf(node);
1327  break;
1328  case CGOpCode::CondResult:
1329  printCondResult(node);
1330  break;
1331  case CGOpCode::UserCustom:
1332  printUserCustom(node);
1333  break;
1334  default:
1335  throw CGException("Unknown operation code '", op, "'.");
1336  }
1337  return 1;
1338  }
1339 
1340  virtual unsigned printAssignOp(Node& node) {
1341  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for assign operation");
1342 
1343  return print(node.getArguments()[0]);
1344  }
1345 
1346  virtual void printUnaryFunction(Node& op) {
1347  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary function");
1348 
1349  switch (op.getOperationType()) {
1350  case CGOpCode::Abs:
1351  _code << "<mi>abs</mi>";
1352  break;
1353  case CGOpCode::Acos:
1354  _code << "<mi>arccos</mi>";
1355  break;
1356  case CGOpCode::Asin:
1357  _code << "<mi>arcsin</mi>";
1358  break;
1359  case CGOpCode::Atan:
1360  _code << "<mi>arctan</mi>";
1361  break;
1362  case CGOpCode::Cosh:
1363  _code << "<mi>cosh</mi>";
1364  break;
1365  case CGOpCode::Cos:
1366  _code << "<mi>cos</mi>";
1367  break;
1368  case CGOpCode::Exp:
1369  _code << "<mi>exp</mi>";
1370  break;
1371  case CGOpCode::Log:
1372  _code << "<mi>ln</mi>";
1373  break;
1374  case CGOpCode::Sinh:
1375  _code << "<mi>sinh</mi>";
1376  break;
1377  case CGOpCode::Sign:
1378  _code << "<mi>sgn</mi>";
1379  break;
1380  case CGOpCode::Sin:
1381  _code << "<mi>sin</mi>";
1382  break;
1383  case CGOpCode::Sqrt:
1384  _code << "<msqrt><mrow>";
1385  print(op.getArguments()[0]);
1386  _code << "</mrow></msqrt>";
1387  return;
1388  case CGOpCode::Tanh:
1389  _code << "<mi>tanh</mi>";
1390  break;
1391  case CGOpCode::Tan:
1392  _code << "<mi>tan</mi>";
1393  break;
1394  default:
1395  throw CGException("Unknown function name for operation code '", op.getOperationType(), "'.");
1396  }
1397 
1398  _code << "<mo>&ApplyFunction;</mo>"
1399  "<mfenced><mrow>";
1400  print(op.getArguments()[0]);
1401  _code << "</mrow></mfenced>";
1402  }
1403 
1404  virtual void printPowFunction(Node& op) {
1405  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for pow() function");
1406 
1407  auto encloseInParentheses = [this](const Node* node) {
1408  while (node != nullptr) {
1409  if (getVariableID(*node) != 0)
1410  return false;
1411  if (node->getOperationType() == CGOpCode::Alias)
1412  node = node->getArguments()[0].getOperation();
1413  else
1414  break;
1415  }
1416  return node != nullptr &&
1417  getVariableID(*node) == 0 &&
1418  !isFunction(node->getOperationType());
1419  };
1420 
1421 
1422  bool encloseBase = _powBaseEnclose || encloseInParentheses(op.getArguments()[0].getOperation());
1423  bool encloseExpo = encloseInParentheses(op.getArguments()[1].getOperation());
1424 
1425  _code << "<msup>";
1426  if (encloseBase)
1427  _code << "<mfenced>";
1428  _code << "<mrow>";
1429  print(op.getArguments()[0]);
1430  _code << "</mrow>";
1431  if (encloseBase)
1432  _code << "</mfenced>";
1433  if (encloseExpo)
1434  _code << "<mfenced>";
1435  _code << "<mrow>";
1436  print(op.getArguments()[1]);
1437  _code << "</mrow>";
1438  if (encloseExpo)
1439  _code << "</mfenced>";
1440  _code << "</msup>";
1441  }
1442 
1443  virtual unsigned printOperationAlias(Node& op) {
1444  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for alias");
1445  return print(op.getArguments()[0]);
1446  }
1447 
1448  virtual void printOperationAdd(Node& op) {
1449  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for addition");
1450 
1451  const Arg& left = op.getArguments()[0];
1452  const Arg& right = op.getArguments()[1];
1453 
1454  if(right.getParameter() == nullptr || (*right.getParameter() >= 0)) {
1455  print(left);
1456  _code << "<mo>+</mo>";
1457  print(right);
1458  } else {
1459  // right has a negative parameter so we would get v0 + -v1
1460  print(left);
1461  _code << "<mo>-</mo>";
1462  printParameter(-*right.getParameter()); // make it positive
1463  }
1464  }
1465 
1466  virtual void printOperationMinus(Node& op) {
1467  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for subtraction");
1468 
1469  const Arg& left = op.getArguments()[0];
1470  const Arg& right = op.getArguments()[1];
1471 
1472  // whether or not to flip + to - and change sign
1473  if(right.getParameter() == nullptr || (*right.getParameter() >= 0)) {
1474  bool encloseRight = encloseInParenthesesMul(right);
1475 
1476  print(left);
1477  _code << "<mo>-</mo>";
1478  if (encloseRight) {
1479  _code << "<mfenced><mrow>";
1480  }
1481  print(right);
1482  if (encloseRight) {
1483  _code << "</mrow></mfenced>";
1484  }
1485  } else {
1486  // right has a negative parameter so we would get v0 - -v1
1487  print(left);
1488  _code << "<mo>+</mo>";
1489  printParameter(-*right.getParameter()); // make it positive
1490  }
1491  }
1492 
1493  virtual void printOperationDiv(Node& op) {
1494  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for division");
1495 
1496  const Arg& left = op.getArguments()[0];
1497  const Arg& right = op.getArguments()[1];
1498 
1499  _code << "<mfrac>";
1500  _code << "<mrow>";
1501  print(left);
1502  _code << "</mrow>";
1503  _code << "<mrow>";
1504  print(right);
1505  _code << "</mrow>";
1506  _code << "</mfrac>";
1507  }
1508 
1509  inline bool encloseInParenthesesMul(const Arg& arg) const {
1510  if (arg.getParameter() != nullptr) {
1511  return ((*arg.getParameter()) < 0);
1512  } else {
1513  return encloseInParenthesesMul(arg.getOperation());
1514  }
1515  }
1516 
1517  inline bool encloseInParenthesesMul(const Node* node) const {
1518  while (node != nullptr) {
1519  if (getVariableID(*node) != 0) {
1520  return false;
1521  } else if (node->getOperationType() == CGOpCode::Alias) {
1522  node = node->getArguments()[0].getOperation();
1523  } else {
1524  break;
1525  }
1526  }
1527  return node != nullptr &&
1528  getVariableID(*node) == 0 &&
1529  node->getOperationType() != CGOpCode::Div &&
1530  node->getOperationType() != CGOpCode::Mul &&
1531  !isFunction(node->getOperationType());
1532  }
1533 
1534  virtual void printOperationMul(Node& op) {
1535  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for multiplication");
1536 
1537  const Arg& left = op.getArguments()[0];
1538  const Arg& right = op.getArguments()[1];
1539 
1540  bool encloseLeft = encloseInParenthesesMul(left);
1541  bool encloseRight = encloseInParenthesesMul(right);
1542 
1543  auto isNumber = [this](const Node* node, int pos) -> bool {
1544  while (node != nullptr) {
1545  if (getVariableID(*node) != 0) {
1546  return false;
1547  }
1548  CGOpCode op = node->getOperationType();
1549  if (op == CGOpCode::Alias) {
1550  node = node->getArguments()[0].getOperation();
1551  continue;
1552  } else if (op == CGOpCode::Mul) {
1553  node = node->getArguments()[pos].getOperation();
1554  } else if (pos == 0 && op == CGOpCode::Pow) {
1555  node = node->getArguments()[0].getOperation();
1556  } else {
1557  return false;
1558  }
1559  }
1560  return true; // a constant number
1561  };
1562 
1563  if (encloseLeft) {
1564  _code << "<mfenced><mrow>";
1565  }
1566  print(left);
1567  if (encloseLeft) {
1568  _code << "</mrow></mfenced>";
1569  }
1570 
1571  if (isNumber(left.getOperation(), 1) && isNumber(right.getOperation(), 0))
1572  _code << _multValOpStr; // numbers too close together are difficult to distinguish
1573  else
1574  _code << _multOpStr; // e.g. invisible times
1575 
1576  if (encloseRight) {
1577  _code << "<mfenced><mrow>";
1578  }
1579  print(right);
1580  if (encloseRight) {
1581  _code << "</mrow></mfenced>";
1582  }
1583  }
1584 
1585  virtual void printOperationUnaryMinus(Node& op) {
1586  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary minus");
1587 
1588  const Arg& arg = op.getArguments()[0];
1589 
1590  bool enclose = encloseInParenthesesMul(arg);
1591 
1592  _code << "<mo>-</mo>";
1593  if (enclose) {
1594  _code << "<mfenced><mrow>";
1595  }
1596  print(arg);
1597  if (enclose) {
1598  _code << "</mrow></mfenced>";
1599  }
1600  }
1601 
1602  virtual void printConditionalAssignment(Node& node) {
1603  CPPADCG_ASSERT_UNKNOWN(getVariableID(node) > 0);
1604 
1605  const std::vector<Arg>& args = node.getArguments();
1606  const Arg &left = args[0];
1607  const Arg &right = args[1];
1608  const Arg &trueCase = args[2];
1609  const Arg &falseCase = args[3];
1610 
1611  bool isDep = isDependent(node);
1612  const std::string& varName = createVariableName(node);
1613 
1614  if ((trueCase.getParameter() != nullptr && falseCase.getParameter() != nullptr && *trueCase.getParameter() == *falseCase.getParameter()) ||
1615  (trueCase.getOperation() != nullptr && falseCase.getOperation() != nullptr && trueCase.getOperation() == falseCase.getOperation())) {
1616  // true and false cases are the same
1617  printAssignmentStart(node, varName, isDep);
1618  print(trueCase);
1619  printAssignmentEnd(node);
1620  } else {
1621 
1622  _code << _ifStart << _startEq << "<mi>if</mi>"
1623  "<mfenced><mrow>";
1624  print(left);
1625  _code << "<mo>";
1626  getComparison(_code, node.getOperationType());
1627  _code << "</mo>";
1628  print(right);
1629  _code << "</mrow></mfenced>" << _endEq << _endline
1630  << _condBodyStart << _endline;
1631 
1632  //checkEquationEnvStart(); // no need
1633  printAssignmentStart(node, varName, isDep);
1634  print(trueCase);
1635  printAssignmentEnd(node);
1636  _code << _condBodyEnd << _endline << _ifEnd << _endline;
1637 
1638  // else
1639  _code << _elseStart << _startEq << "<mi>else</mi>" << _endEq << _endline
1640  << _condBodyStart << _endline;
1641  //checkEquationEnvStart(); // no need
1642  printAssignmentStart(node, varName, isDep);
1643  print(falseCase);
1644  printAssignmentEnd(node);
1645  _code << _condBodyEnd << _endline << _elseEnd << _endline; // end if
1646  }
1647  }
1648 
1649  inline bool isSameArgument(const Arg& newArg,
1650  const Arg* oldArg) {
1651  if (oldArg != nullptr) {
1652  if (oldArg->getParameter() != nullptr) {
1653  if (newArg.getParameter() != nullptr) {
1654  return (*newArg.getParameter() == *oldArg->getParameter());
1655  }
1656  } else {
1657  return (newArg.getOperation() == oldArg->getOperation());
1658  }
1659  }
1660  return false;
1661  }
1662 
1663  virtual void printArrayCreationOp(Node& op);
1664 
1665  virtual void printSparseArrayCreationOp(Node& op);
1666 
1667  inline void printArrayStructInit(const std::string& dataArrayName,
1668  size_t pos,
1669  const std::vector<Node*>& arrays,
1670  size_t k);
1671 
1672  inline void printArrayStructInit(const std::string& dataArrayName,
1673  Node& array);
1674 
1675  inline void markArrayChanged(Node& ty);
1676 
1677  inline size_t printArrayCreationUsingLoop(size_t startPos,
1678  Node& array,
1679  size_t startj,
1680  std::vector<const Arg*>& tmpArrayValues);
1681 
1682  inline std::string getTempArrayName(const Node& op);
1683 
1684  virtual void printArrayElementOp(Node& op);
1685 
1686  virtual void printAtomicForwardOp(Node& atomicFor) {
1687  CPPADCG_ASSERT_KNOWN(atomicFor.getInfo().size() == 3, "Invalid number of information elements for atomic forward operation");
1688  int q = atomicFor.getInfo()[1];
1689  int p = atomicFor.getInfo()[2];
1690  size_t p1 = p + 1;
1691  const std::vector<Arg>& opArgs = atomicFor.getArguments();
1692  CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 2, "Invalid number of arguments for atomic forward operation");
1693 
1694  size_t id = atomicFor.getInfo()[0];
1695  std::vector<Node*> tx(p1), ty(p1);
1696  for (size_t k = 0; k < p1; k++) {
1697  tx[k] = opArgs[0 * p1 + k].getOperation();
1698  ty[k] = opArgs[1 * p1 + k].getOperation();
1699  }
1700 
1701  CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type");
1702  CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type");
1703  CPPADCG_ASSERT_KNOWN(ty[p]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type");
1704 
1705  // tx
1706  for (size_t k = 0; k < p1; k++) {
1707  printArrayStructInit(_ATOMIC_TX, k, tx, k);
1708  }
1709  // ty
1710  printArrayStructInit(_ATOMIC_TY, *ty[p]);
1711  _ss.str("");
1712 
1713  _code << _startEq
1714  << _info->atomicFunctionId2Name.at(id) << "<mo>.</mo><mo>forward</mo>"
1715  "<mfenced separators=','>"
1716  "<mn>" << q << "</mn>"
1717  "<mn>" << p << "</mn>"
1718  "<mrow class='tmp'>" << _ATOMIC_TX << "</mrow>"
1719  "<mrow><mo>&amp;</mo><mrow class='tmp'>" << _ATOMIC_TY << "</mrow></mrow>"
1720  "</mfenced>"
1721  << _endEq << _endline;
1722 
1726  markArrayChanged(*ty[p]);
1727  }
1728 
1729  virtual void printAtomicReverseOp(Node& atomicRev) {
1730  CPPADCG_ASSERT_KNOWN(atomicRev.getInfo().size() == 2, "Invalid number of information elements for atomic reverse operation");
1731  int p = atomicRev.getInfo()[1];
1732  size_t p1 = p + 1;
1733  const std::vector<Arg>& opArgs = atomicRev.getArguments();
1734  CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 4, "Invalid number of arguments for atomic reverse operation");
1735 
1736  size_t id = atomicRev.getInfo()[0];
1737  std::vector<Node*> tx(p1), px(p1), py(p1);
1738  for (size_t k = 0; k < p1; k++) {
1739  tx[k] = opArgs[0 * p1 + k].getOperation();
1740  px[k] = opArgs[2 * p1 + k].getOperation();
1741  py[k] = opArgs[3 * p1 + k].getOperation();
1742  }
1743 
1744  CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type");
1745  CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type");
1746 
1747  CPPADCG_ASSERT_KNOWN(px[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type");
1748 
1749  CPPADCG_ASSERT_KNOWN(py[0]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type");
1750  CPPADCG_ASSERT_KNOWN(p == 0 || py[1]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type");
1751 
1752  // tx
1753  for (size_t k = 0; k < p1; k++) {
1754  printArrayStructInit(_ATOMIC_TX, k, tx, k);
1755  }
1756  // py
1757  for (size_t k = 0; k < p1; k++) {
1758  printArrayStructInit(_ATOMIC_PY, k, py, k);
1759  }
1760  // px
1761  printArrayStructInit(_ATOMIC_PX, *px[0]);
1762  _ss.str("");
1763 
1764  _code << _startEq
1765  << _info->atomicFunctionId2Name.at(id) << "<mo>.</mo><mo>reverse</mo>"
1766  "<mfenced separators=','>"
1767  "<mn>" << p << "</mn>"
1768  "<mrow class='tmp'>" << _ATOMIC_TX << "</mrow>"
1769  "<mrow><mo>&amp;</mo><mrow class='tmp'>" << _ATOMIC_PX << "</mrow></mrow>"
1770  "<mrow class='tmp'>" << _ATOMIC_PY << "</mrow>"
1771  "</mfenced>"
1772  << _endEq << _endline;
1773 
1777  markArrayChanged(*px[0]);
1778  }
1779 
1780  virtual unsigned printDependentMultiAssign(Node& node) {
1781  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::DependentMultiAssign, "Invalid node type");
1782  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments");
1783 
1784  const std::vector<Arg>& args = node.getArguments();
1785  for (size_t a = 0; a < args.size(); a++) {
1786  bool useArg = false;
1787  const Arg& arg = args[a];
1788  if (arg.getParameter() != nullptr) {
1789  useArg = true;
1790  } else {
1791  CGOpCode op = arg.getOperation()->getOperationType();
1792  useArg = op != CGOpCode::DependentRefRhs && op != CGOpCode::LoopEnd && op != CGOpCode::EndIf;
1793  }
1794 
1795  if (useArg) {
1796  printAssignment(node, arg); // ignore other arguments!
1797  return 1;
1798  }
1799  }
1800  return 0;
1801  }
1802 
1803  virtual void printLoopStart(Node& node) {
1804  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopStart, "Invalid node type");
1805 
1806  LoopStartOperationNode<Base>& lnode = static_cast<LoopStartOperationNode<Base>&> (node);
1807  _currentLoops.push_back(&lnode);
1808 
1809  const std::string& jj = *lnode.getIndex().getName();
1810  std::string lastIt;
1811  if (lnode.getIterationCountNode() != nullptr) {
1812  lastIt = *lnode.getIterationCountNode()->getIndex().getName() + " <mo>-</mo> <mn>1</mn>";
1813  } else {
1814  lastIt = "<mn>" + std::to_string(lnode.getIterationCount() - 1) + "</mn>";
1815  }
1816 
1817  _code << _forStart << _startEq << "<mi>for</mi>"
1818  "<mfenced><mrow><mi class='index'>"
1819  << jj << "</mi><mo>&isin;</mo>"
1820  "<mfenced open='[' close='[' separators=';'>"
1821  "<mn>0</mn>" << lastIt <<
1822  "</mfenced>"
1823  "</mrow></mfenced>" << _endEq << _endline
1824  << _forBodyStart;
1825  _indentationLevel++;
1826  }
1827 
1828  virtual void printLoopEnd(Node& node) {
1829  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopEnd, "Invalid node type");
1830 
1831  _indentationLevel--;
1832 
1833  _code << _forBodyEnd << _forEnd << _endline;
1834 
1835  _currentLoops.pop_back();
1836  }
1837 
1838  virtual void printLoopIndexedDep(Node& node) {
1839  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for loop indexed dependent operation");
1840 
1841  // LoopIndexedDep
1842  print(node.getArguments()[0]);
1843  }
1844 
1845  virtual void printLoopIndexedIndep(Node& node) {
1846  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedIndep, "Invalid node type");
1847  CPPADCG_ASSERT_KNOWN(node.getInfo().size() == 1, "Invalid number of information elements for loop indexed independent operation");
1848 
1849  // CGLoopIndexedIndepOp
1850  size_t pos = node.getInfo()[1];
1851  const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1852  _code << _nameGen->generateIndexedIndependent(node, getVariableID(node), *ip);
1853  }
1854 
1855  virtual void printLoopIndexedTmp(Node& node) {
1856  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedTmp, "Invalid node type");
1857  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for loop indexed temporary operation");
1858  Node* tmpVar = node.getArguments()[0].getOperation();
1859  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation");
1860 
1861  print(node.getArguments()[1]);
1862  }
1863 
1864  virtual void printTmpVar(Node& node) {
1865  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Tmp, "Invalid node type");
1866  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for temporary variable usage operation");
1867  Node* tmpVar = node.getArguments()[0].getOperation();
1868  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation");
1869 
1870  _code << "<mrow id='" << createHtmlID(tmpVar) << "' class='tmp'>" << *tmpVar->getName() << "</mrow>";
1871  }
1872 
1873  virtual void printIndexAssign(Node& node) {
1874  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexAssign, "Invalid node type");
1875  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for an index assignment operation");
1876 
1877  IndexAssignOperationNode<Base>& inode = static_cast<IndexAssignOperationNode<Base>&> (node);
1878 
1879  const IndexPattern& ip = inode.getIndexPattern();
1880  _code << _startEq
1881  << "<mi class='index'>"<< (*inode.getIndex().getName()) << "</mi>"
1882  << _assignStr;
1883  indexPattern2String(_code, ip, inode.getIndexPatternIndexes());
1884  _code << _endEq << _endline;
1885  }
1886 
1887  virtual void printIndexCondExprOp(Node& node) {
1888  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexCondExpr, "Invalid node type");
1889  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for an index condition expression operation");
1890  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an index condition expression operation");
1891  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation()->getOperationType() == CGOpCode::Index, "Invalid argument for an index condition expression operation");
1892 
1893  const std::vector<size_t>& info = node.getInfo();
1894 
1895  IndexOperationNode<Base>& iterationIndexOp = static_cast<IndexOperationNode<Base>&> (*node.getArguments()[0].getOperation());
1896  const std::string& index = *iterationIndexOp.getIndex().getName();
1897 
1898  printIndexCondExpr(_code, info, index);
1899  }
1900 
1901  virtual void printStartIf(Node& node) {
1906  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::StartIf, "Invalid node type");
1907  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'if start' operation");
1908  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'if start' operation");
1909 
1910  _code << _ifStart << _startEq << "<mi>if</mi>"
1911  "<mfenced><mrow>";
1912  printIndexCondExprOp(*node.getArguments()[0].getOperation());
1913  _code << "</mrow></mfenced>" << _endEq << _endline
1914  << _condBodyStart << _endline;
1915 
1916  _indentationLevel++;
1917  }
1918 
1919  virtual void printElseIf(Node& node) {
1925  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::ElseIf, "Invalid node type");
1926  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 2, "Invalid number of arguments for an 'else if' operation");
1927  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'else if' operation");
1928  CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an 'else if' operation");
1929 
1930  _indentationLevel--;
1931 
1932  // close previous environment
1933  _code << _condBodyEnd << _endline;
1934  CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1935  if (nType == CGOpCode::StartIf) {
1936  _code << _ifEnd << _endline;
1937  } else if (nType == CGOpCode::ElseIf) {
1938  _code << _elseIfEnd << _endline;
1939  }
1940 
1941  // start new else if
1942  _code << _elseIfStart << _startEq << "<mi>else if</mi>"
1943  "<mfenced><mrow>";
1944  printIndexCondExprOp(*node.getArguments()[1].getOperation());
1945  _code << "</mrow></mfenced>" << _endEq << _endline
1946  << _condBodyStart << _endline;
1947 
1948  _indentationLevel++;
1949  }
1950 
1951  virtual void printElse(Node& node) {
1956  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Else, "Invalid node type");
1957  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'else' operation");
1958 
1959  _indentationLevel--;
1960 
1961  // close previous environment
1962  _code << _condBodyEnd << _endline;
1963  CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1964  if (nType == CGOpCode::StartIf) {
1965  _code << _ifEnd << _endline;
1966  } else if (nType == CGOpCode::ElseIf) {
1967  _code << _elseIfEnd << _endline;
1968  }
1969 
1970  // start else
1971  _code << _elseStart << _startEq << "<mi>else</mi>" << _endEq << _endline
1972  << _condBodyStart << _endline;
1973  _code << _elseStart;
1974 
1975  _indentationLevel++;
1976  }
1977 
1978  virtual void printEndIf(Node& node) {
1979  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::EndIf, "Invalid node type for an 'end if' operation");
1980 
1981  _indentationLevel--;
1982 
1983  // close previous environment
1984  _code << _condBodyEnd << _endline;
1985  CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1986  if (nType == CGOpCode::StartIf) {
1987  _code << _ifEnd << _endline;
1988  } else if (nType == CGOpCode::ElseIf) {
1989  _code << _elseIfEnd << _endline;
1990  } else {
1991  assert(nType == CGOpCode::Else);
1992  _code << _elseEnd << _endline;
1993  }
1994  }
1995 
1996  virtual void printCondResult(Node& node) {
1997  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::CondResult, "Invalid node type");
1998  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for an assignment inside an if/else operation");
1999  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation");
2000  CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation");
2001 
2002  // just follow the argument
2003  Node& nodeArg = *node.getArguments()[1].getOperation();
2004  printAssignment(nodeArg);
2005  }
2006 
2007  virtual void printUserCustom(Node& node) {
2008  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::UserCustom, "Invalid node type");
2009 
2010  throw CGException("Unable to generate MathML for user custom operation nodes.");
2011  }
2012 
2013  inline bool isDependent(const Node& arg) const {
2014  if (arg.getOperationType() == CGOpCode::LoopIndexedDep) {
2015  return true;
2016  }
2017  size_t id = getVariableID(arg);
2018  return id > _independentSize && id < _minTemporaryVarID;
2019  }
2020 
2021  virtual void printParameter(const Base& value) {
2022  // make sure all digits of floating point values are printed
2023  std::ostringstream os;
2024  os << std::setprecision(_parameterPrecision) << value;
2025 
2026  std::string number = os.str();
2027  size_t pos = number.find('e');
2028  if (pos != std::string::npos) {
2029  _code << "<mn>" << number.substr(0, pos) << "</mn><mo>&times;</mo>";
2030  _code << "<msup><mn>10</mn><mn>";
2031  pos++;
2032  if (number[pos] == '-') {
2033  _code << "-";
2034  pos++;
2035  } else if (number[pos] == '+') {
2036  pos++;
2037  }
2038  while (pos < number.size() - 1 && number[pos] == '0')
2039  pos++; // remove zeros
2040 
2041  _code << number.substr(pos) << "</mn></msup>";
2042 
2043  } else {
2044  _code << "<mn>" << number << "</mn>";
2045  }
2046 
2047 
2048  }
2049 
2050  virtual void getComparison(std::ostream& os, enum CGOpCode op) const {
2051  switch (op) {
2052  case CGOpCode::ComLt:
2053  os << "&lt;";
2054  return;
2055 
2056  case CGOpCode::ComLe:
2057  os << "&le;";
2058  return;
2059 
2060  case CGOpCode::ComEq:
2061  os << "==";
2062  return;
2063 
2064  case CGOpCode::ComGe:
2065  os << "&ge;";
2066  return;
2067 
2068  case CGOpCode::ComGt:
2069  os << "&gt;";
2070  return;
2071 
2072  case CGOpCode::ComNe:
2073  os << "&ne;";
2074  return;
2075 
2076  default:
2077  CPPAD_ASSERT_UNKNOWN(0);
2078  }
2079  throw CGException("Invalid comparison operator code"); // should never get here
2080  }
2081 
2082  static bool isFunction(enum CGOpCode op) {
2083  return isUnaryFunction(op) || op == CGOpCode::Pow;
2084  }
2085 
2086  static bool isUnaryFunction(enum CGOpCode op) {
2087  switch (op) {
2088  case CGOpCode::Abs:
2089  case CGOpCode::Acos:
2090  case CGOpCode::Asin:
2091  case CGOpCode::Atan:
2092  case CGOpCode::Cosh:
2093  case CGOpCode::Cos:
2094  case CGOpCode::Exp:
2095  case CGOpCode::Log:
2096  case CGOpCode::Sign:
2097  case CGOpCode::Sinh:
2098  case CGOpCode::Sin:
2099  case CGOpCode::Sqrt:
2100  case CGOpCode::Tanh:
2101  case CGOpCode::Tan:
2102  return true;
2103  default:
2104  return false;
2105  }
2106  }
2107 
2108  static bool isCondAssign(enum CGOpCode op) {
2109  switch (op) {
2110  case CGOpCode::ComLt:
2111  case CGOpCode::ComLe:
2112  case CGOpCode::ComEq:
2113  case CGOpCode::ComGe:
2114  case CGOpCode::ComGt:
2115  case CGOpCode::ComNe:
2116  return true;
2117  default:
2118  return false;
2119  }
2120  }
2121 };
2122 
2123 template<class Base>
2124 const std::string LanguageMathML<Base>::_C_STATIC_INDEX_ARRAY = "index";
2125 
2126 template<class Base>
2127 const std::string LanguageMathML<Base>::_C_SPARSE_INDEX_ARRAY = "idx";
2128 
2129 template<class Base>
2130 const std::string LanguageMathML<Base>::_ATOMIC_TX = "atx";
2131 
2132 template<class Base>
2133 const std::string LanguageMathML<Base>::_ATOMIC_TY = "aty";
2134 
2135 template<class Base>
2136 const std::string LanguageMathML<Base>::_ATOMIC_PX = "apx";
2137 
2138 template<class Base>
2139 const std::string LanguageMathML<Base>::_ATOMIC_PY = "apy";
2140 
2141 } // END cg namespace
2142 } // END CppAD namespace
2143 
2144 #endif
virtual void setEquationMarkup(const std::string &begin, const std::string &end)
void setHeadExtraMarkup(const std::string &headExtra)
virtual const std::string & getElseIfEndMarkup() const
virtual void printElse(Node &node)
const std::map< size_t, std::string > & atomicFunctionId2Name
Definition: language.hpp:68
virtual std::string generateTemporary(const OperationNode< Base > &variable, size_t id)=0
void printRandomIndexPatternDeclaration(std::ostringstream &os, const std::string &identation, const std::set< RandomIndexPattern *> &randomPatterns)
virtual std::string generateIndexedDependent(const OperationNode< Base > &var, size_t id, const IndexPattern &ip)=0
virtual bool isAlwaysEnclosePowBase() const
virtual size_t getMaxTemporaryArrayVariableID() const =0
virtual const std::string & getEquationEndMarkup() const
virtual void printElseIf(Node &node)
virtual const std::string & getEquationStartMarkup() const
virtual std::string generateIndexedIndependent(const OperationNode< Base > &var, size_t id, const IndexPattern &ip)=0
virtual void setForMarkup(const std::string &begin, const std::string &end)
virtual const std::string & getElseEndMarkup() const
const std::string * getName() const
const std::vector< Argument< Base > > & getArguments() const
virtual const std::vector< FuncArgument > & getTemporary() const
virtual size_t getParameterPrecision() const
STL namespace.
virtual void setAlwaysEnclosePowBase(bool enclose)
size_t getHandlerPosition() const
virtual void printStartIf(Node &node)
const CodeHandlerVector< Base, size_t > & varId
Definition: language.hpp:48
virtual std::string generateDependent(size_t index)=0
size_t getHtmlID(const Node &var) const
virtual const std::string & getIfEndMarkup() const
void setStyle(const std::string &style)
void setJavascript(const std::string &javascript)
virtual void setParameterPrecision(size_t p)
const std::string & getMultiplicationMarkup() const
const CodeHandlerVector< Base, size_t > & totalUseCount
Definition: language.hpp:94
const std::string & getMultiplicationConstParMarkup() const
CGOpCode getOperationType() const
std::string createHtmlID(const Node *var)
virtual void setIfMarkup(const std::string &begin, const std::string &end)
virtual bool directlyAssignsVariable(const Node &var) const
void generateSourceCode(std::ostream &out, const std::unique_ptr< LanguageGenerationData< Base > > &info) override
virtual const std::string & getElseIfStartMarkup() const
virtual void setElseMarkup(const std::string &begin, const std::string &end)
bool createsNewVariable(const Node &var, size_t totalUseCount) const override
virtual std::string generateTemporaryArray(const OperationNode< Base > &variable, size_t id)=0
size_t printArrayCreationUsingLoop(size_t startPos, Node &array, size_t startj, std::vector< const Arg *> &tmpArrayValues)
virtual size_t getMaxTemporarySparseArrayVariableID() const =0
virtual const std::vector< FuncArgument > & getDependent() const
virtual std::string generateTemporarySparseArray(const OperationNode< Base > &variable, size_t id)=0
virtual void setElseIfMarkup(const std::string &begin, const std::string &end)
virtual const std::string & getForEndMarkup() const
void setMultiplicationMarkup(const std::string &multOpStr)
virtual const std::vector< FuncArgument > & getIndependent() const
virtual const std::string & getForStartMarkup() const
virtual std::string generateIndependent(const OperationNode< Base > &variable, size_t id)=0
void setName(const std::string &name)
virtual void printAtomicForwardOp(Node &atomicFor)
bool requiresVariableDependencies() const override
virtual const std::string & getIfStartMarkup() const
const std::vector< std::set< Node * > > & variableDependencies
Definition: language.hpp:56
virtual std::string createHtmlID(const Node &var)
const std::vector< Node * > & variableOrder
Definition: language.hpp:52
size_t size() const noexcept
Definition: array_view.hpp:202
virtual void printAtomicReverseOp(Node &atomicRev)
void printStaticIndexMatrix(std::ostringstream &os, const std::string &name, const std::map< size_t, std::map< size_t, size_t > > &values)
virtual const std::string & getElseStartMarkup() const
void setMultiplicationConstParMarkup(const std::string &multValOpStr)
const std::vector< size_t > & getInfo() const