CppADCodeGen  HEAD
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(R"(<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() = default;
163 
164  inline const std::string& getAssignMarkup() const {
165  return _assignStr;
166  }
167 
168  inline void setAssignMarkup(const std::string& assign) {
169  _assignStr = assign;
170  }
171 
172  inline const std::string& getAddAssignMarkup() const {
173  return _assignAddStr;
174  }
175 
176  inline void setAddAssignMarkup(const std::string& assignAdd) {
177  _assignAddStr = assignAdd;
178  }
179 
187  inline const std::string& getMultiplicationMarkup() const {
188  return _multOpStr;
189  }
190 
199  inline void setMultiplicationMarkup(const std::string& multOpStr) {
200  _multOpStr = multOpStr;
201  }
202 
208  inline const std::string& getMultiplicationConstParMarkup() const {
209  return _multValOpStr;
210  }
211 
220  inline void setMultiplicationConstParMarkup(const std::string& multValOpStr) {
221  _multValOpStr = multValOpStr;
222  }
223 
224  inline bool isIgnoreZeroDepAssign() const {
225  return _ignoreZeroDepAssign;
226  }
227 
228  inline void setIgnoreZeroDepAssign(bool ignore) {
229  _ignoreZeroDepAssign = ignore;
230  }
231 
232  void setFilename(const std::string& name) {
233  _filename = name;
234  }
235 
241  void setStyle(const std::string& style) {
242  _style = style;
243  }
244 
245  const std::string& getStyle() const {
246  return _style;
247  }
248 
254  void setJavascript(const std::string& javascript) {
255  _javascript = javascript;
256  }
257 
258  const std::string& getJavascript() const {
259  return _javascript;
260  }
261 
267  void setHeadExtraMarkup(const std::string& headExtra) {
268  _headExtra = headExtra;
269  }
270 
271  const std::string& getHeadExtraMarkup() const {
272  return _headExtra;
273  }
274 
281  virtual void setEquationMarkup(const std::string& begin,
282  const std::string& end) {
283  _startEq = begin;
284  _endEq = end;
285  }
286 
290  virtual const std::string& getEquationStartMarkup() const {
291  return _startEq;
292  }
293 
297  virtual const std::string& getEquationEndMarkup() const {
298  return _endEq;
299  }
300 
307  virtual void setForMarkup(const std::string& begin,
308  const std::string& end) {
309  _forStart = begin;
310  _forEnd = end;
311  }
312 
316  virtual const std::string& getForStartMarkup() const {
317  return _forStart;
318  }
319 
323  virtual const std::string& getForEndMarkup() const {
324  return _forEnd;
325  }
326 
333  virtual void setIfMarkup(const std::string& begin,
334  const std::string& end) {
335  _ifStart = begin;
336  _ifEnd = end;
337  }
338 
342  virtual const std::string& getIfStartMarkup() const {
343  return _ifStart;
344  }
345 
349  virtual const std::string& getIfEndMarkup() const {
350  return _ifEnd;
351  }
352 
359  virtual void setElseIfMarkup(const std::string& begin,
360  const std::string& end) {
361  _elseIfStart = begin;
362  _elseIfEnd = end;
363  }
364 
368  virtual const std::string& getElseIfStartMarkup() const {
369  return _elseIfStart;
370  }
371 
375  virtual const std::string& getElseIfEndMarkup() const {
376  return _elseIfEnd;
377  }
378 
385  virtual void setElseMarkup(const std::string& begin,
386  const std::string& end) {
387  _elseStart = begin;
388  _elseEnd = end;
389  }
390 
394  virtual const std::string& getElseStartMarkup() const {
395  return _elseStart;
396  }
397 
401  virtual const std::string& getElseEndMarkup() const {
402  return _elseEnd;
403  }
404 
411  virtual size_t getParameterPrecision() const {
412  return _parameterPrecision;
413  }
414 
421  virtual void setParameterPrecision(size_t p) {
422  _parameterPrecision = p;
423  }
424 
433  virtual void setAlwaysEnclosePowBase(bool enclose) {
434  _powBaseEnclose = enclose;
435  }
436 
443  virtual bool isAlwaysEnclosePowBase() const {
444  return _powBaseEnclose;
445  }
446 
447  virtual void setMaxAssignmentsPerFunction(size_t maxAssignmentsPerFunction,
448  std::map<std::string, std::string>* sources) {
449  _maxAssignmentsPerFile = maxAssignmentsPerFunction;
450  _sources = sources;
451  }
452 
453  inline void setSaveVariableRelations(bool save) {
454  _saveVariableRelations = save;
455  }
456 
457  bool requiresVariableDependencies() const override {
458  return _saveVariableRelations;
459  }
460 
461  /***************************************************************************
462  * STATIC
463  **************************************************************************/
464  static inline void printIndexCondExpr(std::ostringstream& out,
465  const std::vector<size_t>& info,
466  const std::string& index) {
467  CPPADCG_ASSERT_KNOWN(info.size() > 1 && info.size() % 2 == 0, "Invalid number of information elements for an index condition expression operation")
468 
469  size_t infoSize = info.size();
470  for (size_t e = 0; e < infoSize; e += 2) {
471  if (e > 0) {
472  out << "<mo>&or;</mo>"; // or
473  }
474  size_t min = info[e];
475  size_t max = info[e + 1];
476  if (min == max) {
477  out << "<mi class='index'>" << index << "</mi><mo>==</mo><mn>" << min << "</nm>";
478  } else if (min == 0) {
479  out << "<mi class='index'>" << index << "</mi><mo>&le;</mo><mn>" << max << "</mn>";
480  } else if (max == (std::numeric_limits<size_t>::max)()) {
481  out << "<mn>" << min << "</mn><mo>&le;</mo><mi class='index'>" << index << "</mi>";
482  } else {
483  if (infoSize != 2)
484  out << "<mfenced><mrow>";
485 
486  if (max - min == 1)
487  out << "<mn>" << min << "</mn><mo>==</mo><mi class='index'>" << index << "</mi><mo>&or;</mo><mi class='index'>" << index << "</mi><mo>==</mo><mn>" << max << "</mn>";
488  else
489  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";
490 
491  if (infoSize != 2)
492  out << "</mrow></mfenced>";
493  }
494  }
495  }
496 
497  /***************************************************************************
498  *
499  **************************************************************************/
500 
501  inline void printStaticIndexArray(std::ostringstream& os,
502  const std::string& name,
503  const std::vector<size_t>& values);
504 
505  inline void printStaticIndexMatrix(std::ostringstream& os,
506  const std::string& name,
507  const std::map<size_t, std::map<size_t, size_t> >& values);
508 
509  /***************************************************************************
510  * index patterns
511  **************************************************************************/
512  static inline void generateNames4RandomIndexPatterns(const std::set<RandomIndexPattern*>& randomPatterns);
513 
514  inline void printRandomIndexPatternDeclaration(std::ostringstream& os,
515  const std::string& identation,
516  const std::set<RandomIndexPattern*>& randomPatterns);
517 
518  static void indexPattern2String(std::ostream& os,
519  const IndexPattern& ip,
520  const Node& index);
521 
522  static void indexPattern2String(std::ostream& os,
523  const IndexPattern& ip,
524  const std::vector<const Node*>& indexes);
525 
526  static inline void linearIndexPattern2String(std::ostream& os,
527  const LinearIndexPattern& lip,
528  const Node& index);
529 
530  /***************************************************************************
531  * protected
532  **************************************************************************/
533 protected:
534 
535  void generateSourceCode(std::ostream& out,
536  std::unique_ptr<LanguageGenerationData<Base> > info) override {
537 
538  const bool multiFile = _maxAssignmentsPerFile > 0 && _sources != nullptr;
539 
540  // clean up
541  _code.str("");
542  _ss.str("");
543  _indentationLevel = 0;
544  auxArrayName_ = "";
545  _currentLoops.clear();
546  depConstIds_.clear();
547  depIsIndepIds_.clear();
548  _dependentIDs.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.empty() && !depArg.empty(),
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  auto 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.empty() && 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.empty()) {
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,
1007  size_t opCount) const override {
1008  CGOpCode op = var.getOperationType();
1009  if (totalUseCount > 1) {
1010  return op != CGOpCode::ArrayElement && op != CGOpCode::Index && op != CGOpCode::IndexDeclaration && op != CGOpCode::Tmp;
1011  } else {
1012  return ( op == CGOpCode::ArrayCreation ||
1013  op == CGOpCode::SparseArrayCreation ||
1014  op == CGOpCode::AtomicForward ||
1015  op == CGOpCode::AtomicReverse ||
1016  op == CGOpCode::ComLt ||
1017  op == CGOpCode::ComLe ||
1018  op == CGOpCode::ComEq ||
1019  op == CGOpCode::ComGe ||
1020  op == CGOpCode::ComGt ||
1021  op == CGOpCode::ComNe ||
1022  op == CGOpCode::LoopIndexedDep ||
1023  op == CGOpCode::LoopIndexedTmp ||
1024  op == CGOpCode::IndexAssign ||
1025  op == CGOpCode::Assign) &&
1026  op != CGOpCode::CondResult;
1027  }
1028  }
1029 
1030  virtual bool requiresVariableName(const Node& var) const {
1031  CGOpCode op = var.getOperationType();
1032  return (_info->totalUseCount.get(var) > 1 &&
1033  op != CGOpCode::AtomicForward &&
1034  op != CGOpCode::AtomicReverse &&
1035  op != CGOpCode::LoopStart &&
1036  op != CGOpCode::LoopEnd &&
1037  op != CGOpCode::Index &&
1038  op != CGOpCode::IndexAssign &&
1039  op != CGOpCode::StartIf &&
1040  op != CGOpCode::ElseIf &&
1041  op != CGOpCode::Else &&
1042  op != CGOpCode::EndIf &&
1043  op != CGOpCode::CondResult &&
1044  op != CGOpCode::LoopIndexedTmp &&
1045  op != CGOpCode::Tmp);
1046  }
1047 
1055  virtual bool directlyAssignsVariable(const Node& var) const {
1056  CGOpCode op = var.getOperationType();
1057  return isCondAssign(op) ||
1058  op == CGOpCode::ArrayCreation ||
1059  op == CGOpCode::SparseArrayCreation ||
1060  op == CGOpCode::AtomicForward ||
1061  op == CGOpCode::AtomicReverse ||
1062  op == CGOpCode::DependentMultiAssign ||
1063  op == CGOpCode::LoopStart ||
1064  op == CGOpCode::LoopEnd ||
1065  op == CGOpCode::IndexAssign ||
1066  op == CGOpCode::StartIf ||
1067  op == CGOpCode::ElseIf ||
1068  op == CGOpCode::Else ||
1069  op == CGOpCode::EndIf ||
1070  op == CGOpCode::CondResult ||
1071  op == CGOpCode::IndexDeclaration;
1072  }
1073 
1074  bool requiresVariableArgument(enum CGOpCode op, size_t argIndex) const override {
1075  return op == CGOpCode::CondResult;
1076  }
1077 
1078  inline const std::string& createVariableName(Node& var) {
1079  CGOpCode op = var.getOperationType();
1080  CPPADCG_ASSERT_UNKNOWN(getVariableID(var) > 0)
1081  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicForward)
1082  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicReverse)
1083  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopStart)
1084  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopEnd)
1085  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::Index)
1086  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexAssign)
1087  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexDeclaration)
1088 
1089  if (var.getName() == nullptr) {
1090  if (op == CGOpCode::ArrayCreation) {
1091  var.setName(_nameGen->generateTemporaryArray(var, getVariableID(var)));
1092 
1093  } else if (op == CGOpCode::SparseArrayCreation) {
1094  var.setName(_nameGen->generateTemporarySparseArray(var, getVariableID(var)));
1095 
1096  } else if (op == CGOpCode::LoopIndexedDep) {
1097  size_t pos = var.getInfo()[0];
1098  const IndexPattern* ip = _info->loopDependentIndexPatterns[pos];
1099  var.setName(_nameGen->generateIndexedDependent(var, getVariableID(var), *ip));
1100 
1101  } else if (op == CGOpCode::LoopIndexedIndep) {
1102  size_t pos = var.getInfo()[1];
1103  const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1104  var.setName(_nameGen->generateIndexedIndependent(var, getVariableID(var), *ip));
1105 
1106  } else if (getVariableID(var) <= _independentSize) {
1107  // independent variable
1108  var.setName(_nameGen->generateIndependent(var, getVariableID(var)));
1109 
1110  } else if (getVariableID(var) < _minTemporaryVarID) {
1111  // dependent variable
1112  auto it = _dependentIDs.find(getVariableID(var));
1113  CPPADCG_ASSERT_UNKNOWN(it != _dependentIDs.end())
1114 
1115  size_t index = it->second;
1116  var.setName(_nameGen->generateDependent(index));
1117 
1118  } else if (op == CGOpCode::LoopIndexedTmp || op == CGOpCode::Tmp) {
1119  CPPADCG_ASSERT_KNOWN(var.getArguments().size() >= 1, "Invalid number of arguments for loop indexed temporary operation")
1120  Node* tmpVar = var.getArguments()[0].getOperation();
1121  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1122  return createVariableName(*tmpVar);
1123 
1124  } else {
1125  // temporary variable
1126  var.setName(_nameGen->generateTemporary(var, getVariableID(var)));
1127  }
1128  }
1129 
1130 
1131  return *var.getName();
1132  }
1133 
1137  inline size_t getHtmlID(const Node& var) const {
1138  return var.getHandlerPosition(); // always unique and higher than zero
1139  }
1140 
1144  inline std::string createHtmlID(const Node* var) {
1145  return createHtmlID(*var);
1146  }
1147 
1151  virtual std::string createHtmlID(const Node& var) {
1152  size_t id = getHtmlID(var);
1153  if (varIds_.size() <= id) {
1154  varIds_.resize(id + 1 + varIds_.size() * 3 / 2, 0);
1155  }
1156 
1157  int n = varIds_[id];
1158  varIds_[id]++;
1159 
1160  if (n == 0)
1161  return "v" + std::to_string(id);
1162  else
1163  return "v" + std::to_string(id) + "_" + std::to_string(n);
1164  }
1165 
1166  virtual void printIndependentVariableName(Node& op) {
1167  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 0, "Invalid number of arguments for independent variable")
1168  _code << "<mrow id='" << createHtmlID(op) << "' class='indep'>" << _nameGen->generateIndependent(op, getVariableID(op)) << "</mrow>";
1169  }
1170 
1171  virtual unsigned print(const Arg& arg) {
1172  if (arg.getOperation() != nullptr) {
1173  // expression
1174  return printExpression(*arg.getOperation());
1175  } else {
1176  // parameter
1177  printParameter(*arg.getParameter());
1178  return 1;
1179  }
1180  }
1181 
1182  virtual unsigned printExpression(Node& node) {
1183  if (getVariableID(node) > 0) {
1184  const std::string& name = createVariableName(node); // use variable name
1185 
1186  CGOpCode op = node.getOperationType();
1187  if (getVariableID(node) >= _minTemporaryVarID || op == CGOpCode::ArrayCreation || op == CGOpCode::SparseArrayCreation || op == CGOpCode::LoopIndexedDep || op == CGOpCode::LoopIndexedIndep) {
1188 
1189  _code << "<mrow id='" << createHtmlID(node) << "' class='tmp'>" << name << "</mrow>"; // TODO!!!!!!!!!!!!!!!!!!!!!!!
1190 
1191  } else if (getVariableID(node) <= _independentSize) {
1192  // independent variable
1193  _code << "<mrow id='" << createHtmlID(node) << "' class='indep'>" << name << "</mrow>";
1194 
1195  } else {
1196  // dependent variable
1197  _code << "<mrow id='" << createHtmlID(node) << "' class='dep'>" << name << "</mrow>";
1198 
1199  }
1200 
1201  return 1;
1202  } else {
1203  // print expression code
1204  return printExpressionNoVarCheck(node);
1205  }
1206  }
1207 
1208  virtual unsigned printExpressionNoVarCheck(Node& node) {
1209  CGOpCode op = node.getOperationType();
1210  switch (op) {
1211  case CGOpCode::ArrayCreation:
1212  printArrayCreationOp(node);
1213  break;
1214  case CGOpCode::SparseArrayCreation:
1215  printSparseArrayCreationOp(node);
1216  break;
1217  case CGOpCode::ArrayElement:
1218  printArrayElementOp(node);
1219  break;
1220  case CGOpCode::Assign:
1221  return printAssignOp(node);
1222 
1223  case CGOpCode::Abs:
1224  case CGOpCode::Acos:
1225  case CGOpCode::Asin:
1226  case CGOpCode::Atan:
1227  case CGOpCode::Cosh:
1228  case CGOpCode::Cos:
1229  case CGOpCode::Exp:
1230  case CGOpCode::Log:
1231  case CGOpCode::Sign:
1232  case CGOpCode::Sinh:
1233  case CGOpCode::Sin:
1234  case CGOpCode::Sqrt:
1235  case CGOpCode::Tanh:
1236  case CGOpCode::Tan:
1237 #if CPPAD_USE_CPLUSPLUS_2011
1238  case CGOpCode::Erf:
1239  case CGOpCode::Erfc:
1240  case CGOpCode::Asinh:
1241  case CGOpCode::Acosh:
1242  case CGOpCode::Atanh:
1243  case CGOpCode::Expm1:
1244  case CGOpCode::Log1p:
1245 #endif
1246  printUnaryFunction(node);
1247  break;
1248  case CGOpCode::AtomicForward: // atomicFunction.forward(q, p, vx, vy, tx, ty)
1249  printAtomicForwardOp(node);
1250  break;
1251  case CGOpCode::AtomicReverse: // atomicFunction.reverse(p, tx, ty, px, py)
1252  printAtomicReverseOp(node);
1253  break;
1254  case CGOpCode::Add:
1255  printOperationAdd(node);
1256  break;
1257  case CGOpCode::Alias:
1258  return printOperationAlias(node);
1259 
1260  case CGOpCode::ComLt:
1261  case CGOpCode::ComLe:
1262  case CGOpCode::ComEq:
1263  case CGOpCode::ComGe:
1264  case CGOpCode::ComGt:
1265  case CGOpCode::ComNe:
1266  printConditionalAssignment(node);
1267  break;
1268  case CGOpCode::Div:
1269  printOperationDiv(node);
1270  break;
1271  case CGOpCode::Inv:
1272  printIndependentVariableName(node);
1273  break;
1274  case CGOpCode::Mul:
1275  printOperationMul(node);
1276  break;
1277  case CGOpCode::Pow:
1278  printPowFunction(node);
1279  break;
1280  case CGOpCode::Pri:
1281  // do nothing
1282  break;
1283  case CGOpCode::Sub:
1284  printOperationMinus(node);
1285  break;
1286 
1287  case CGOpCode::UnMinus:
1288  printOperationUnaryMinus(node);
1289  break;
1290 
1291  case CGOpCode::DependentMultiAssign:
1292  return printDependentMultiAssign(node);
1293 
1294  case CGOpCode::Index:
1295  return 0; // nothing to do
1296  case CGOpCode::IndexAssign:
1297  printIndexAssign(node);
1298  break;
1299  case CGOpCode::IndexDeclaration:
1300  return 0; // already done
1301 
1302  case CGOpCode::LoopStart:
1303  printLoopStart(node);
1304  break;
1305  case CGOpCode::LoopIndexedIndep:
1306  printLoopIndexedIndep(node);
1307  break;
1308  case CGOpCode::LoopIndexedDep:
1309  printLoopIndexedDep(node);
1310  break;
1311  case CGOpCode::LoopIndexedTmp:
1312  printLoopIndexedTmp(node);
1313  break;
1314  case CGOpCode::TmpDcl:
1315  // nothing to do
1316  return 0;
1317  case CGOpCode::Tmp:
1318  printTmpVar(node);
1319  break;
1320  case CGOpCode::LoopEnd:
1321  printLoopEnd(node);
1322  break;
1323  case CGOpCode::IndexCondExpr:
1324  printIndexCondExprOp(node);
1325  break;
1326  case CGOpCode::StartIf:
1327  printStartIf(node);
1328  break;
1329  case CGOpCode::ElseIf:
1330  printElseIf(node);
1331  break;
1332  case CGOpCode::Else:
1333  printElse(node);
1334  break;
1335  case CGOpCode::EndIf:
1336  printEndIf(node);
1337  break;
1338  case CGOpCode::CondResult:
1339  printCondResult(node);
1340  break;
1341  case CGOpCode::UserCustom:
1342  printUserCustom(node);
1343  break;
1344  default:
1345  throw CGException("Unknown operation code '", op, "'.");
1346  }
1347  return 1;
1348  }
1349 
1350  virtual unsigned printAssignOp(Node& node) {
1351  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for assign operation")
1352 
1353  return print(node.getArguments()[0]);
1354  }
1355 
1356  virtual void printUnaryFunction(Node& op) {
1357  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary function")
1358 
1359  switch (op.getOperationType()) {
1360  case CGOpCode::Abs:
1361  _code << "<mi>abs</mi>";
1362  break;
1363  case CGOpCode::Acos:
1364  _code << "<mi>arccos</mi>";
1365  break;
1366  case CGOpCode::Asin:
1367  _code << "<mi>arcsin</mi>";
1368  break;
1369  case CGOpCode::Atan:
1370  _code << "<mi>arctan</mi>";
1371  break;
1372  case CGOpCode::Cosh:
1373  _code << "<mi>cosh</mi>";
1374  break;
1375  case CGOpCode::Cos:
1376  _code << "<mi>cos</mi>";
1377  break;
1378  case CGOpCode::Exp:
1379  _code << "<mi>exp</mi>";
1380  break;
1381  case CGOpCode::Log:
1382  _code << "<mi>ln</mi>";
1383  break;
1384  case CGOpCode::Sinh:
1385  _code << "<mi>sinh</mi>";
1386  break;
1387  case CGOpCode::Sign:
1388  _code << "<mi>sgn</mi>";
1389  break;
1390  case CGOpCode::Sin:
1391  _code << "<mi>sin</mi>";
1392  break;
1393  case CGOpCode::Sqrt:
1394  _code << "<msqrt><mrow>";
1395  print(op.getArguments()[0]);
1396  _code << "</mrow></msqrt>";
1397  return;
1398  case CGOpCode::Tanh:
1399  _code << "<mi>tanh</mi>";
1400  break;
1401  case CGOpCode::Tan:
1402  _code << "<mi>tan</mi>";
1403  break;
1404 #if CPPAD_USE_CPLUSPLUS_2011
1405  case CGOpCode::Erf:
1406  _code << "<mi>erf</mi>";
1407  break;
1408  case CGOpCode::Erfc:
1409  _code << "<mi>erfc</mi>";
1410  break;
1411  case CGOpCode::Asinh:
1412  _code << "<mi>asinh</mi>";
1413  break;
1414  case CGOpCode::Acosh:
1415  _code << "<mi>acosh</mi>";
1416  break;
1417  case CGOpCode::Atanh:
1418  _code << "<mi>atanh</mi>";
1419  break;
1420  case CGOpCode::Expm1:
1421  _code << "<mi>expm1</mi>";
1422  break;
1423  case CGOpCode::Log1p:
1424  _code << "<mi>log1p</mi>";
1425  break;
1426 #endif
1427  default:
1428  throw CGException("Unknown function name for operation code '", op.getOperationType(), "'.");
1429  }
1430 
1431  _code << "<mo>&ApplyFunction;</mo>"
1432  "<mfenced><mrow>";
1433  print(op.getArguments()[0]);
1434  _code << "</mrow></mfenced>";
1435  }
1436 
1437  virtual void printPowFunction(Node& op) {
1438  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for pow() function")
1439 
1440  auto encloseInParentheses = [this](const Node* node) {
1441  while (node != nullptr) {
1442  if (getVariableID(*node) != 0)
1443  return false;
1444  if (node->getOperationType() == CGOpCode::Alias)
1445  node = node->getArguments()[0].getOperation();
1446  else
1447  break;
1448  }
1449  return node != nullptr &&
1450  getVariableID(*node) == 0 &&
1451  !isFunction(node->getOperationType());
1452  };
1453 
1454 
1455  bool encloseBase = _powBaseEnclose || encloseInParentheses(op.getArguments()[0].getOperation());
1456  bool encloseExpo = encloseInParentheses(op.getArguments()[1].getOperation());
1457 
1458  _code << "<msup>";
1459  if (encloseBase)
1460  _code << "<mfenced>";
1461  _code << "<mrow>";
1462  print(op.getArguments()[0]);
1463  _code << "</mrow>";
1464  if (encloseBase)
1465  _code << "</mfenced>";
1466  if (encloseExpo)
1467  _code << "<mfenced>";
1468  _code << "<mrow>";
1469  print(op.getArguments()[1]);
1470  _code << "</mrow>";
1471  if (encloseExpo)
1472  _code << "</mfenced>";
1473  _code << "</msup>";
1474  }
1475 
1476  virtual unsigned printOperationAlias(Node& op) {
1477  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for alias")
1478  return print(op.getArguments()[0]);
1479  }
1480 
1481  virtual void printOperationAdd(Node& op) {
1482  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for addition")
1483 
1484  const Arg& left = op.getArguments()[0];
1485  const Arg& right = op.getArguments()[1];
1486 
1487  if(right.getParameter() == nullptr || (*right.getParameter() >= 0)) {
1488  print(left);
1489  _code << "<mo>+</mo>";
1490  print(right);
1491  } else {
1492  // right has a negative parameter so we would get v0 + -v1
1493  print(left);
1494  _code << "<mo>-</mo>";
1495  printParameter(-*right.getParameter()); // make it positive
1496  }
1497  }
1498 
1499  virtual void printOperationMinus(Node& op) {
1500  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for subtraction")
1501 
1502  const Arg& left = op.getArguments()[0];
1503  const Arg& right = op.getArguments()[1];
1504 
1505  // whether or not to flip + to - and change sign
1506  if(right.getParameter() == nullptr || (*right.getParameter() >= 0)) {
1507  bool encloseRight = encloseInParenthesesMul(right);
1508 
1509  print(left);
1510  _code << "<mo>-</mo>";
1511  if (encloseRight) {
1512  _code << "<mfenced><mrow>";
1513  }
1514  print(right);
1515  if (encloseRight) {
1516  _code << "</mrow></mfenced>";
1517  }
1518  } else {
1519  // right has a negative parameter so we would get v0 - -v1
1520  print(left);
1521  _code << "<mo>+</mo>";
1522  printParameter(-*right.getParameter()); // make it positive
1523  }
1524  }
1525 
1526  virtual void printOperationDiv(Node& op) {
1527  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for division")
1528 
1529  const Arg& left = op.getArguments()[0];
1530  const Arg& right = op.getArguments()[1];
1531 
1532  _code << "<mfrac>";
1533  _code << "<mrow>";
1534  print(left);
1535  _code << "</mrow>";
1536  _code << "<mrow>";
1537  print(right);
1538  _code << "</mrow>";
1539  _code << "</mfrac>";
1540  }
1541 
1542  inline bool encloseInParenthesesMul(const Arg& arg) const {
1543  if (arg.getParameter() != nullptr) {
1544  return ((*arg.getParameter()) < 0);
1545  } else {
1546  return encloseInParenthesesMul(arg.getOperation());
1547  }
1548  }
1549 
1550  inline bool encloseInParenthesesMul(const Node* node) const {
1551  while (node != nullptr) {
1552  if (getVariableID(*node) != 0) {
1553  return false;
1554  } else if (node->getOperationType() == CGOpCode::Alias) {
1555  node = node->getArguments()[0].getOperation();
1556  } else {
1557  break;
1558  }
1559  }
1560  return node != nullptr &&
1561  getVariableID(*node) == 0 &&
1562  node->getOperationType() != CGOpCode::Div &&
1563  node->getOperationType() != CGOpCode::Mul &&
1564  !isFunction(node->getOperationType());
1565  }
1566 
1567  virtual void printOperationMul(Node& op) {
1568  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for multiplication")
1569 
1570  const Arg& left = op.getArguments()[0];
1571  const Arg& right = op.getArguments()[1];
1572 
1573  bool encloseLeft = encloseInParenthesesMul(left);
1574  bool encloseRight = encloseInParenthesesMul(right);
1575 
1576  auto isNumber = [this](const Node* node, int pos) -> bool {
1577  while (node != nullptr) {
1578  if (getVariableID(*node) != 0) {
1579  return false;
1580  }
1581  CGOpCode op = node->getOperationType();
1582  if (op == CGOpCode::Alias) {
1583  node = node->getArguments()[0].getOperation();
1584  continue;
1585  } else if (op == CGOpCode::Mul) {
1586  node = node->getArguments()[pos].getOperation();
1587  } else if (pos == 0 && op == CGOpCode::Pow) {
1588  node = node->getArguments()[0].getOperation();
1589  } else {
1590  return false;
1591  }
1592  }
1593  return true; // a constant number
1594  };
1595 
1596  if (encloseLeft) {
1597  _code << "<mfenced><mrow>";
1598  }
1599  print(left);
1600  if (encloseLeft) {
1601  _code << "</mrow></mfenced>";
1602  }
1603 
1604  if (isNumber(left.getOperation(), 1) && isNumber(right.getOperation(), 0))
1605  _code << _multValOpStr; // numbers too close together are difficult to distinguish
1606  else
1607  _code << _multOpStr; // e.g. invisible times
1608 
1609  if (encloseRight) {
1610  _code << "<mfenced><mrow>";
1611  }
1612  print(right);
1613  if (encloseRight) {
1614  _code << "</mrow></mfenced>";
1615  }
1616  }
1617 
1618  virtual void printOperationUnaryMinus(Node& op) {
1619  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary minus")
1620 
1621  const Arg& arg = op.getArguments()[0];
1622 
1623  bool enclose = encloseInParenthesesMul(arg);
1624 
1625  _code << "<mo>-</mo>";
1626  if (enclose) {
1627  _code << "<mfenced><mrow>";
1628  }
1629  print(arg);
1630  if (enclose) {
1631  _code << "</mrow></mfenced>";
1632  }
1633  }
1634 
1635  virtual void printConditionalAssignment(Node& node) {
1636  CPPADCG_ASSERT_UNKNOWN(getVariableID(node) > 0)
1637 
1638  const std::vector<Arg>& args = node.getArguments();
1639  const Arg &left = args[0];
1640  const Arg &right = args[1];
1641  const Arg &trueCase = args[2];
1642  const Arg &falseCase = args[3];
1643 
1644  bool isDep = isDependent(node);
1645  const std::string& varName = createVariableName(node);
1646 
1647  if ((trueCase.getParameter() != nullptr && falseCase.getParameter() != nullptr && *trueCase.getParameter() == *falseCase.getParameter()) ||
1648  (trueCase.getOperation() != nullptr && falseCase.getOperation() != nullptr && trueCase.getOperation() == falseCase.getOperation())) {
1649  // true and false cases are the same
1650  printAssignmentStart(node, varName, isDep);
1651  print(trueCase);
1652  printAssignmentEnd(node);
1653  } else {
1654 
1655  _code << _ifStart << _startEq << "<mi>if</mi>"
1656  "<mfenced><mrow>";
1657  print(left);
1658  _code << "<mo>";
1659  getComparison(_code, node.getOperationType());
1660  _code << "</mo>";
1661  print(right);
1662  _code << "</mrow></mfenced>" << _endEq << _endline
1663  << _condBodyStart << _endline;
1664 
1665  //checkEquationEnvStart(); // no need
1666  printAssignmentStart(node, varName, isDep);
1667  print(trueCase);
1668  printAssignmentEnd(node);
1669  _code << _condBodyEnd << _endline << _ifEnd << _endline;
1670 
1671  // else
1672  _code << _elseStart << _startEq << "<mi>else</mi>" << _endEq << _endline
1673  << _condBodyStart << _endline;
1674  //checkEquationEnvStart(); // no need
1675  printAssignmentStart(node, varName, isDep);
1676  print(falseCase);
1677  printAssignmentEnd(node);
1678  _code << _condBodyEnd << _endline << _elseEnd << _endline; // end if
1679  }
1680  }
1681 
1682  inline bool isSameArgument(const Arg& newArg,
1683  const Arg* oldArg) {
1684  if (oldArg != nullptr) {
1685  if (oldArg->getParameter() != nullptr) {
1686  if (newArg.getParameter() != nullptr) {
1687  return (*newArg.getParameter() == *oldArg->getParameter());
1688  }
1689  } else {
1690  return (newArg.getOperation() == oldArg->getOperation());
1691  }
1692  }
1693  return false;
1694  }
1695 
1696  virtual void printArrayCreationOp(Node& op);
1697 
1698  virtual void printSparseArrayCreationOp(Node& op);
1699 
1700  inline void printArrayStructInit(const std::string& dataArrayName,
1701  size_t pos,
1702  const std::vector<Node*>& arrays,
1703  size_t k);
1704 
1705  inline void printArrayStructInit(const std::string& dataArrayName,
1706  Node& array);
1707 
1708  inline void markArrayChanged(Node& ty);
1709 
1710  inline size_t printArrayCreationUsingLoop(size_t startPos,
1711  Node& array,
1712  size_t startj,
1713  std::vector<const Arg*>& tmpArrayValues);
1714 
1715  inline std::string getTempArrayName(const Node& op);
1716 
1717  virtual void printArrayElementOp(Node& op);
1718 
1719  virtual void printAtomicForwardOp(Node& atomicFor) {
1720  CPPADCG_ASSERT_KNOWN(atomicFor.getInfo().size() == 3, "Invalid number of information elements for atomic forward operation")
1721  int q = atomicFor.getInfo()[1];
1722  int p = atomicFor.getInfo()[2];
1723  size_t p1 = p + 1;
1724  const std::vector<Arg>& opArgs = atomicFor.getArguments();
1725  CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 2, "Invalid number of arguments for atomic forward operation")
1726 
1727  size_t id = atomicFor.getInfo()[0];
1728  std::vector<Node*> tx(p1), ty(p1);
1729  for (size_t k = 0; k < p1; k++) {
1730  tx[k] = opArgs[0 * p1 + k].getOperation();
1731  ty[k] = opArgs[1 * p1 + k].getOperation();
1732  }
1733 
1734  CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1735  CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1736  CPPADCG_ASSERT_KNOWN(ty[p]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1737 
1738  // tx
1739  for (size_t k = 0; k < p1; k++) {
1740  printArrayStructInit(_ATOMIC_TX, k, tx, k);
1741  }
1742  // ty
1743  printArrayStructInit(_ATOMIC_TY, *ty[p]);
1744  _ss.str("");
1745 
1746  _code << _startEq
1747  << _info->atomicFunctionId2Name.at(id) << "<mo>.</mo><mo>forward</mo>"
1748  "<mfenced separators=','>"
1749  "<mn>" << q << "</mn>"
1750  "<mn>" << p << "</mn>"
1751  "<mrow class='tmp'>" << _ATOMIC_TX << "</mrow>"
1752  "<mrow><mo>&amp;</mo><mrow class='tmp'>" << _ATOMIC_TY << "</mrow></mrow>"
1753  "</mfenced>"
1754  << _endEq << _endline;
1755 
1759  markArrayChanged(*ty[p]);
1760  }
1761 
1762  virtual void printAtomicReverseOp(Node& atomicRev) {
1763  CPPADCG_ASSERT_KNOWN(atomicRev.getInfo().size() == 2, "Invalid number of information elements for atomic reverse operation")
1764  int p = atomicRev.getInfo()[1];
1765  size_t p1 = p + 1;
1766  const std::vector<Arg>& opArgs = atomicRev.getArguments();
1767  CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 4, "Invalid number of arguments for atomic reverse operation")
1768 
1769  size_t id = atomicRev.getInfo()[0];
1770  std::vector<Node*> tx(p1), px(p1), py(p1);
1771  for (size_t k = 0; k < p1; k++) {
1772  tx[k] = opArgs[0 * p1 + k].getOperation();
1773  px[k] = opArgs[2 * p1 + k].getOperation();
1774  py[k] = opArgs[3 * p1 + k].getOperation();
1775  }
1776 
1777  CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1778  CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1779 
1780  CPPADCG_ASSERT_KNOWN(px[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1781 
1782  CPPADCG_ASSERT_KNOWN(py[0]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1783  CPPADCG_ASSERT_KNOWN(p == 0 || py[1]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1784 
1785  // tx
1786  for (size_t k = 0; k < p1; k++) {
1787  printArrayStructInit(_ATOMIC_TX, k, tx, k);
1788  }
1789  // py
1790  for (size_t k = 0; k < p1; k++) {
1791  printArrayStructInit(_ATOMIC_PY, k, py, k);
1792  }
1793  // px
1794  printArrayStructInit(_ATOMIC_PX, *px[0]);
1795  _ss.str("");
1796 
1797  _code << _startEq
1798  << _info->atomicFunctionId2Name.at(id) << "<mo>.</mo><mo>reverse</mo>"
1799  "<mfenced separators=','>"
1800  "<mn>" << p << "</mn>"
1801  "<mrow class='tmp'>" << _ATOMIC_TX << "</mrow>"
1802  "<mrow><mo>&amp;</mo><mrow class='tmp'>" << _ATOMIC_PX << "</mrow></mrow>"
1803  "<mrow class='tmp'>" << _ATOMIC_PY << "</mrow>"
1804  "</mfenced>"
1805  << _endEq << _endline;
1806 
1810  markArrayChanged(*px[0]);
1811  }
1812 
1813  virtual unsigned printDependentMultiAssign(Node& node) {
1814  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::DependentMultiAssign, "Invalid node type")
1815  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments")
1816 
1817  const std::vector<Arg>& args = node.getArguments();
1818  for (size_t a = 0; a < args.size(); a++) {
1819  bool useArg;
1820  const Arg& arg = args[a];
1821  if (arg.getParameter() != nullptr) {
1822  useArg = true;
1823  } else {
1824  CGOpCode op = arg.getOperation()->getOperationType();
1825  useArg = op != CGOpCode::DependentRefRhs && op != CGOpCode::LoopEnd && op != CGOpCode::EndIf;
1826  }
1827 
1828  if (useArg) {
1829  printAssignment(node, arg); // ignore other arguments!
1830  return 1;
1831  }
1832  }
1833  return 0;
1834  }
1835 
1836  virtual void printLoopStart(Node& node) {
1837  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopStart, "Invalid node type")
1838 
1839  auto& lnode = static_cast<LoopStartOperationNode<Base>&> (node);
1840  _currentLoops.push_back(&lnode);
1841 
1842  const std::string& jj = *lnode.getIndex().getName();
1843  std::string lastIt;
1844  if (lnode.getIterationCountNode() != nullptr) {
1845  lastIt = *lnode.getIterationCountNode()->getIndex().getName() + " <mo>-</mo> <mn>1</mn>";
1846  } else {
1847  lastIt = "<mn>" + std::to_string(lnode.getIterationCount() - 1) + "</mn>";
1848  }
1849 
1850  _code << _forStart << _startEq << "<mi>for</mi>"
1851  "<mfenced><mrow><mi class='index'>"
1852  << jj << "</mi><mo>&isin;</mo>"
1853  "<mfenced open='[' close='[' separators=';'>"
1854  "<mn>0</mn>" << lastIt <<
1855  "</mfenced>"
1856  "</mrow></mfenced>" << _endEq << _endline
1857  << _forBodyStart;
1858  _indentationLevel++;
1859  }
1860 
1861  virtual void printLoopEnd(Node& node) {
1862  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopEnd, "Invalid node type")
1863 
1864  _indentationLevel--;
1865 
1866  _code << _forBodyEnd << _forEnd << _endline;
1867 
1868  _currentLoops.pop_back();
1869  }
1870 
1871  virtual void printLoopIndexedDep(Node& node) {
1872  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for loop indexed dependent operation")
1873 
1874  // LoopIndexedDep
1875  print(node.getArguments()[0]);
1876  }
1877 
1878  virtual void printLoopIndexedIndep(Node& node) {
1879  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedIndep, "Invalid node type")
1880  CPPADCG_ASSERT_KNOWN(node.getInfo().size() == 1, "Invalid number of information elements for loop indexed independent operation")
1881 
1882  // CGLoopIndexedIndepOp
1883  size_t pos = node.getInfo()[1];
1884  const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1885  _code << _nameGen->generateIndexedIndependent(node, getVariableID(node), *ip);
1886  }
1887 
1888  virtual void printLoopIndexedTmp(Node& node) {
1889  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedTmp, "Invalid node type")
1890  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for loop indexed temporary operation")
1891  Node* tmpVar = node.getArguments()[0].getOperation();
1892  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1893 
1894  print(node.getArguments()[1]);
1895  }
1896 
1897  virtual void printTmpVar(Node& node) {
1898  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Tmp, "Invalid node type")
1899  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for temporary variable usage operation")
1900  Node* tmpVar = node.getArguments()[0].getOperation();
1901  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1902 
1903  _code << "<mrow id='" << createHtmlID(tmpVar) << "' class='tmp'>" << *tmpVar->getName() << "</mrow>";
1904  }
1905 
1906  virtual void printIndexAssign(Node& node) {
1907  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexAssign, "Invalid node type")
1908  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for an index assignment operation")
1909 
1910  auto& inode = static_cast<IndexAssignOperationNode<Base>&> (node);
1911 
1912  const IndexPattern& ip = inode.getIndexPattern();
1913  _code << _startEq
1914  << "<mi class='index'>"<< (*inode.getIndex().getName()) << "</mi>"
1915  << _assignStr;
1916  indexPattern2String(_code, ip, inode.getIndexPatternIndexes());
1917  _code << _endEq << _endline;
1918  }
1919 
1920  virtual void printIndexCondExprOp(Node& node) {
1921  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexCondExpr, "Invalid node type")
1922  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for an index condition expression operation")
1923  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an index condition expression operation")
1924  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation()->getOperationType() == CGOpCode::Index, "Invalid argument for an index condition expression operation")
1925 
1926  const std::vector<size_t>& info = node.getInfo();
1927 
1928  auto& iterationIndexOp = static_cast<IndexOperationNode<Base>&> (*node.getArguments()[0].getOperation());
1929  const std::string& index = *iterationIndexOp.getIndex().getName();
1930 
1931  printIndexCondExpr(_code, info, index);
1932  }
1933 
1934  virtual void printStartIf(Node& node) {
1939  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::StartIf, "Invalid node type")
1940  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'if start' operation")
1941  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'if start' operation")
1942 
1943  _code << _ifStart << _startEq << "<mi>if</mi>"
1944  "<mfenced><mrow>";
1945  printIndexCondExprOp(*node.getArguments()[0].getOperation());
1946  _code << "</mrow></mfenced>" << _endEq << _endline
1947  << _condBodyStart << _endline;
1948 
1949  _indentationLevel++;
1950  }
1951 
1952  virtual void printElseIf(Node& node) {
1958  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::ElseIf, "Invalid node type")
1959  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 2, "Invalid number of arguments for an 'else if' operation")
1960  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'else if' operation")
1961  CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an 'else if' operation")
1962 
1963  _indentationLevel--;
1964 
1965  // close previous environment
1966  _code << _condBodyEnd << _endline;
1967  CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1968  if (nType == CGOpCode::StartIf) {
1969  _code << _ifEnd << _endline;
1970  } else if (nType == CGOpCode::ElseIf) {
1971  _code << _elseIfEnd << _endline;
1972  }
1973 
1974  // start new else if
1975  _code << _elseIfStart << _startEq << "<mi>else if</mi>"
1976  "<mfenced><mrow>";
1977  printIndexCondExprOp(*node.getArguments()[1].getOperation());
1978  _code << "</mrow></mfenced>" << _endEq << _endline
1979  << _condBodyStart << _endline;
1980 
1981  _indentationLevel++;
1982  }
1983 
1984  virtual void printElse(Node& node) {
1989  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Else, "Invalid node type")
1990  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'else' operation")
1991 
1992  _indentationLevel--;
1993 
1994  // close previous environment
1995  _code << _condBodyEnd << _endline;
1996  CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1997  if (nType == CGOpCode::StartIf) {
1998  _code << _ifEnd << _endline;
1999  } else if (nType == CGOpCode::ElseIf) {
2000  _code << _elseIfEnd << _endline;
2001  }
2002 
2003  // start else
2004  _code << _elseStart << _startEq << "<mi>else</mi>" << _endEq << _endline
2005  << _condBodyStart << _endline;
2006  _code << _elseStart;
2007 
2008  _indentationLevel++;
2009  }
2010 
2011  virtual void printEndIf(Node& node) {
2012  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::EndIf, "Invalid node type for an 'end if' operation")
2013 
2014  _indentationLevel--;
2015 
2016  // close previous environment
2017  _code << _condBodyEnd << _endline;
2018  CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
2019  if (nType == CGOpCode::StartIf) {
2020  _code << _ifEnd << _endline;
2021  } else if (nType == CGOpCode::ElseIf) {
2022  _code << _elseIfEnd << _endline;
2023  } else {
2024  assert(nType == CGOpCode::Else);
2025  _code << _elseEnd << _endline;
2026  }
2027  }
2028 
2029  virtual void printCondResult(Node& node) {
2030  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::CondResult, "Invalid node type")
2031  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for an assignment inside an if/else operation")
2032  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation")
2033  CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation")
2034 
2035  // just follow the argument
2036  Node& nodeArg = *node.getArguments()[1].getOperation();
2037  printAssignment(nodeArg);
2038  }
2039 
2040  virtual void printUserCustom(Node& node) {
2041  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::UserCustom, "Invalid node type")
2042 
2043  throw CGException("Unable to generate MathML for user custom operation nodes.");
2044  }
2045 
2046  inline bool isDependent(const Node& arg) const {
2047  if (arg.getOperationType() == CGOpCode::LoopIndexedDep) {
2048  return true;
2049  }
2050  size_t id = getVariableID(arg);
2051  return id > _independentSize && id < _minTemporaryVarID;
2052  }
2053 
2054  virtual void printParameter(const Base& value) {
2055  // make sure all digits of floating point values are printed
2056  std::ostringstream os;
2057  os << std::setprecision(_parameterPrecision) << value;
2058 
2059  std::string number = os.str();
2060  size_t pos = number.find('e');
2061  if (pos != std::string::npos) {
2062  _code << "<mn>" << number.substr(0, pos) << "</mn><mo>&times;</mo>";
2063  _code << "<msup><mn>10</mn><mn>";
2064  pos++;
2065  if (number[pos] == '-') {
2066  _code << "-";
2067  pos++;
2068  } else if (number[pos] == '+') {
2069  pos++;
2070  }
2071  while (pos < number.size() - 1 && number[pos] == '0')
2072  pos++; // remove zeros
2073 
2074  _code << number.substr(pos) << "</mn></msup>";
2075 
2076  } else {
2077  _code << "<mn>" << number << "</mn>";
2078  }
2079 
2080 
2081  }
2082 
2083  virtual void getComparison(std::ostream& os, enum CGOpCode op) const {
2084  switch (op) {
2085  case CGOpCode::ComLt:
2086  os << "&lt;";
2087  return;
2088 
2089  case CGOpCode::ComLe:
2090  os << "&le;";
2091  return;
2092 
2093  case CGOpCode::ComEq:
2094  os << "==";
2095  return;
2096 
2097  case CGOpCode::ComGe:
2098  os << "&ge;";
2099  return;
2100 
2101  case CGOpCode::ComGt:
2102  os << "&gt;";
2103  return;
2104 
2105  case CGOpCode::ComNe:
2106  os << "&ne;";
2107  return;
2108 
2109  default:
2110  CPPAD_ASSERT_UNKNOWN(0)
2111  break;
2112  }
2113  throw CGException("Invalid comparison operator code"); // should never get here
2114  }
2115 
2116  static bool isFunction(enum CGOpCode op) {
2117  return isUnaryFunction(op) || op == CGOpCode::Pow;
2118  }
2119 
2120  static bool isUnaryFunction(enum CGOpCode op) {
2121  switch (op) {
2122  case CGOpCode::Abs:
2123  case CGOpCode::Acos:
2124  case CGOpCode::Asin:
2125  case CGOpCode::Atan:
2126  case CGOpCode::Cosh:
2127  case CGOpCode::Cos:
2128  case CGOpCode::Exp:
2129  case CGOpCode::Log:
2130  case CGOpCode::Sign:
2131  case CGOpCode::Sinh:
2132  case CGOpCode::Sin:
2133  case CGOpCode::Sqrt:
2134  case CGOpCode::Tanh:
2135  case CGOpCode::Tan:
2136 #if CPPAD_USE_CPLUSPLUS_2011
2137  case CGOpCode::Erf:
2138  case CGOpCode::Erfc:
2139  case CGOpCode::Asinh:
2140  case CGOpCode::Acosh:
2141  case CGOpCode::Atanh:
2142  case CGOpCode::Expm1:
2143  case CGOpCode::Log1p:
2144 #endif
2145  return true;
2146  default:
2147  return false;
2148  }
2149  }
2150 
2151  static bool isCondAssign(enum CGOpCode op) {
2152  switch (op) {
2153  case CGOpCode::ComLt:
2154  case CGOpCode::ComLe:
2155  case CGOpCode::ComEq:
2156  case CGOpCode::ComGe:
2157  case CGOpCode::ComGt:
2158  case CGOpCode::ComNe:
2159  return true;
2160  default:
2161  return false;
2162  }
2163  }
2164 };
2165 
2166 template<class Base>
2167 const std::string LanguageMathML<Base>::_C_STATIC_INDEX_ARRAY = "index"; // NOLINT(cert-err58-cpp)
2168 
2169 template<class Base>
2170 const std::string LanguageMathML<Base>::_C_SPARSE_INDEX_ARRAY = "idx"; // NOLINT(cert-err58-cpp)
2171 
2172 template<class Base>
2173 const std::string LanguageMathML<Base>::_ATOMIC_TX = "atx"; // NOLINT(cert-err58-cpp)
2174 
2175 template<class Base>
2176 const std::string LanguageMathML<Base>::_ATOMIC_TY = "aty"; // NOLINT(cert-err58-cpp)
2177 
2178 template<class Base>
2179 const std::string LanguageMathML<Base>::_ATOMIC_PX = "apx"; // NOLINT(cert-err58-cpp)
2180 
2181 template<class Base>
2182 const std::string LanguageMathML<Base>::_ATOMIC_PY = "apy"; // NOLINT(cert-err58-cpp)
2183 
2184 } // END cg namespace
2185 } // END CppAD namespace
2186 
2187 #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:69
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:49
void generateSourceCode(std::ostream &out, std::unique_ptr< LanguageGenerationData< Base > > info) override
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:95
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
virtual const std::string & getElseIfStartMarkup() const
virtual void setElseMarkup(const std::string &begin, const std::string &end)
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
bool createsNewVariable(const Node &var, size_t totalUseCount, size_t opCount) const override
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:57
virtual std::string createHtmlID(const Node &var)
const std::vector< Node * > & variableOrder
Definition: language.hpp:53
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