--- /dev/null
+/************************************************************************
+ ************************************************************************
+ FAUST compiler
+ Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ************************************************************************
+ ************************************************************************/
+
+
+
+ /**********************************************************************
+ - lateq.cpp : the Lateq methods definition (FAUST project) -
+ - for automatic generation of documentation -
+ - "lateq" stands for "LaTeX equations" -
+ - The crucial method here is println -
+
+ Historique :
+ -----------
+ 17-10-2001 : (klass.cpp) implementation initiale (yo)
+ 18-10-2001 : (klass.cpp) Ajout de getFreshID (yo)
+ 02-11-2001 : (klass.cpp) Ajout de sous classes (yo)
+ 06-11-2001 : (klass.cpp) modif impression des classes (yo)
+ 16-08-2009 : (lateq.cpp) Creation de lateq depuis klass.cpp (kb)
+ 2009-11-21 : (lateq.cpp) Remodelage et documentation doxygen (kb)
+
+***********************************************************************/
+
+#include <stdio.h>
+#include <iostream>
+#include <cstdlib>
+#include <set>
+#include <sstream>
+
+#include "lateq.hh"
+#include "Text.hh"
+
+
+map<string, string> gDocMathStringMap;
+set<string> gDocMathKeySet;
+
+static int getLateqIndex(const string& s);
+static bool compLateqIndexes(const string& s1, const string& s2);
+static void initDocMathKeySet();
+
+
+template <class T>
+inline std::string to_string (const T& t)
+{
+ std::stringstream ss;
+ ss << t;
+ return ss.str();
+}
+
+
+/****************************************************************
+ Top-level "println" method (public).
+ *****************************************************************/
+
+
+/**
+ * @brief
+ * Top-level method to print a whole set of compiled LaTeX formulas,
+ * corresponding to an <equation> faustdoc tag.
+ *
+ * @remark
+ * These formulas must have been previously compiled,
+ * via the DocCompile class,
+ * and stored in Lateq fields as LaTeX strings.
+ */
+void Lateq::println(ostream& docout)
+{
+ /* 1. Make titles of sub-sets of formulas. */
+ string suchthat = gDocMathStringMap["suchthat"];
+
+ string sInputs = makeItemTitle(fInputSigsFormulas.size(), "inputsigtitle") + makeSignamesList(fInputSigsFormulas, "");
+ string sOutputs = makeItemTitle(fOutputSigsFormulas.size(), "outputsigtitle") + makeSignamesList(fOutputSigsFormulas, suchthat);
+ string sConstants = makeItemTitle(fConstSigsFormulas.size(), "constsigtitle") + makeSignamesList(fConstSigsFormulas, suchthat);
+
+ vector<list<string> > UISignamesVector = makeUISignamesVector(fUISigsFormulas);
+ string sUIElements = makeItemTitle(fUISigsFormulas.size(), "uisigtitle") + makeSignamesList(UISignamesVector, suchthat);
+
+ unsigned int internalSigsCount = fParamSigsFormulas.size() + fStoreSigsFormulas.size() + fRecurSigsFormulas.size() + fRDTblSigsFormulas.size() + fRWTblSigsFormulas.size() + fSelectSigsFormulas.size() + fPrefixSigsFormulas.size();
+
+ vector<list<string> > internalSigsFormulasList;
+ if( ! fParamSigsFormulas.empty() ) internalSigsFormulasList.push_back(fParamSigsFormulas);
+ if( ! fStoreSigsFormulas.empty() ) internalSigsFormulasList.push_back(fStoreSigsFormulas);
+ if( ! fRecurSigsFormulas.empty() ) internalSigsFormulasList.push_back(fRecurSigsFormulas);
+ if( ! fRDTblSigsFormulas.empty() ) internalSigsFormulasList.push_back(fRDTblSigsFormulas);
+ if( ! fRWTblSigsFormulas.empty() ) internalSigsFormulasList.push_back(fRWTblSigsFormulas);
+ if( ! fSelectSigsFormulas.empty() ) internalSigsFormulasList.push_back(fSelectSigsFormulas);
+ if( ! fPrefixSigsFormulas.empty() ) internalSigsFormulasList.push_back(fPrefixSigsFormulas);
+
+ string sInternals = makeItemTitle(internalSigsCount, "intermedsigtitle") + makeSignamesList(internalSigsFormulasList, suchthat);
+
+ /* 2. Successively print each Lateq field containing LaTeX formulas, with a title. */
+
+ docout << endl << gDocMathStringMap["lateqcomment"] << endl;
+ docout << "\\begin{enumerate}" << endl << endl;
+
+ printDGroup (sOutputs, fOutputSigsFormulas, docout);
+ printOneLine (sInputs, docout);
+ const string outputsTitle = "\\item " + sOutputs + "\\ $y_i$\\ " + gDocMathStringMap["for"] + " $i \\in [1," + to_string(fOutputSigsFormulas.size()) + "]$: ";
+ printHierarchy (sUIElements, fUISigsFormulas, docout);
+
+ /* The "Internal signals" item gather several fields, like a "super-item"... */
+ if( internalSigsCount > 0 ) {
+ docout << sInternals;
+ }
+ fStoreSigsFormulas.sort(compLateqIndexes);
+ printDGroup ("", fParamSigsFormulas, docout);
+ printDGroup ("", fStoreSigsFormulas, docout);
+ printDGroup ("", fRecurSigsFormulas, docout);
+ printDGroup ("", fRDTblSigsFormulas, docout);
+ printMath ("", fRWTblSigsFormulas, docout);
+ printMath ("", fSelectSigsFormulas, docout);
+ printMath ("", fPrefixSigsFormulas, docout);
+
+ printDGroup (sConstants, fConstSigsFormulas, docout);
+
+ docout << "\\end{enumerate}" << endl << endl;
+}
+
+
+
+/****************************************************************
+ Item title making functions (public).
+ *****************************************************************/
+
+
+string Lateq::makeItemTitle(const unsigned int formulasListSize, const string& titleName)
+{
+ string item = "\\item ";
+
+ /* Plural handling for titles of sub-sets of formulas. */
+ string title = formulasListSize > 1 ? gDocMathStringMap[titleName + "2"] : gDocMathStringMap[titleName + "1"];
+
+ return item + title;
+}
+
+
+string Lateq::makeSigDomain(const list<string>& formulasList)
+{
+ string signame = "";
+ string sigDomain = "";
+
+ if (formulasList.size() > 0) {
+ string firstEq = *(formulasList.begin());
+ signame = getSigName(firstEq);
+
+ if(formulasList.size() > 1) {
+ sigDomain = " $" + signame + "_i$ " + gDocMathStringMap["for"] + " $i \\in [1," + to_string(formulasList.size()) + "]$";
+ } else {
+ if(signame == "x" || signame == "y") {
+ sigDomain = " $" + signame + "$"; ///< No indices for single input neither single output.
+ } else {
+ sigDomain = " $" + signame + "_1$"; ///< Indices "1" for all other single signal.
+ }
+ }
+ } else {
+ sigDomain = gDocMathStringMap["emptyformulafield"];
+ }
+ return sigDomain;
+}
+
+
+string Lateq::makeSignamesList(const list<string>& formulasList, const string& ending)
+{
+ if (formulasList.size() > 0) {
+ return makeSigDomain(formulasList) + " " + ending;
+ } else {
+ return " (" + gDocMathStringMap["emptyformulafield"] + ")";
+ }
+}
+
+
+string Lateq::makeSignamesList(const vector<list<string> >& formulasListsVector, const string& ending)
+{
+ if (formulasListsVector.size() > 0) {
+ vector<list<string> >::const_iterator it;
+ string signames = "";
+ string sep = " ";
+ for (it = formulasListsVector.begin(); it != formulasListsVector.end(); ++it) {
+ signames += sep + makeSigDomain(*it);
+ (it != (formulasListsVector.end() - 2)) ? sep = ", " : sep = " " + gDocMathStringMap["and"] + " ";
+ }
+ return signames + " " + ending;
+ } else {
+ return " (" + gDocMathStringMap["emptyformulafield"] + ")";
+ }
+}
+
+
+string Lateq::getSigName(const string& s)
+{
+ size_t found;
+ string signame;
+
+ found = s.find(" =");
+ if (found != string::npos) { ///< Looking for a left member.
+ signame = s.substr (0, found);
+ }
+ found = s.find("(t)");
+ if (found != string::npos) { ///< Strip "(t)" argument if exists.
+ signame = s.substr (0, found);
+ }
+ found = signame.find("[t]");
+ if (found != string::npos) { ///< Strip "[t]" argument if exists (for tables).
+ signame = s.substr (0, found);
+ }
+ found = signame.find_last_of("_");
+ if (found != string::npos) { ///< Strip indice if exists.
+ signame = signame.substr (0, found);
+ }
+
+ return signame;
+}
+
+
+vector<list<string> > Lateq::makeUISignamesVector(const multimap<string,string>& field)
+{
+ map<char,unsigned int> uiTypesMap;
+ vector<list<string> > uiSignamesVector;
+ unsigned int vIndex = 0;
+
+ multimap<string,string>::const_iterator it;
+
+ for (it = field.begin(); it != field.end(); ++it) {
+ char type = getUISigType(it->second);
+ string signame = getUISigName(it->second);
+
+ map<char,unsigned int>::iterator uiTypesIt;
+ uiTypesIt = uiTypesMap.find(type);
+ if( uiTypesIt != uiTypesMap.end()) {
+ uiSignamesVector[uiTypesMap[uiTypesIt->second]].push_back(signame);
+ } else {
+ ++vIndex;
+ uiTypesMap.insert(pair<char,unsigned int>(type, vIndex));
+ list<string>* tmpList = new(list<string>);
+ tmpList->push_back(signame);
+ uiSignamesVector.push_back(*tmpList);
+ }
+ }
+
+ return uiSignamesVector;
+}
+
+
+string Lateq::getUISigName(const string& s)
+{
+ size_t found;
+ string signame;
+
+ found = s.find("${u_");
+ if (found != string::npos) { ///< Looking for a UI signal name "{u_?}_{i}(t)".
+ signame = s.substr (found+1, 12);
+ }
+
+ return signame;
+}
+
+
+char Lateq::getUISigType(const string& s)
+{
+ size_t found;
+ char sigtype = '0';
+
+ found = s.find("${u_");
+ if (found != string::npos) { ///< Looking for a UI signal name "{u_?}_{i}".
+ sigtype = s.at (found+4);
+ }
+
+ return sigtype;
+}
+
+
+
+/****************************************************************
+ Secondary printing methods (private).
+ *****************************************************************/
+
+
+/**
+ * Print a sorted list of input signals names ("x_i"),
+ * on a single line, separated by commas.
+ *
+ * @param[in] section The title to print for these formulas.
+ * @param[out] docout The LaTeX output file to print into.
+ */
+void Lateq::printOneLine(const string& section, ostream& docout)
+{
+ docout << section << endl << endl;
+}
+
+
+/**
+ * @brief Print a dgroup environment to auto-break long formulas.
+ *
+ * @remark
+ * The "dgroup" and "dmath" environments belong to the "breqn" LaTeX package.
+ * The stared variants "dgroup*" and "dmath*" force unnumbered equations.
+ *
+ * @param[in] section The title to print for these formulas.
+ * @param[in] field The list of LaTeX formulas.
+ * @param[out] docout The LaTeX output file to print into.
+ */
+void Lateq::printDGroup(const string& section, list<string>& field, ostream& docout)
+{
+ if (field.size() > 0) {
+ docout << section << endl;
+ tab(1,docout); docout << "\\begin{dgroup*}" << endl;
+ list<string>::const_iterator s;
+ for (s = field.begin(); s != field.end(); ++s) {
+ tab(2,docout); docout << "\\begin{" << "dmath*" << "}" << endl;
+ tab(3,docout); docout << "\t" << *s << endl;
+ tab(2,docout); docout << "\\end{" << "dmath*" << "}" << endl;
+ }
+ tab(1,docout); docout << "\\end{dgroup*}" << endl;
+ docout << endl;
+ }
+}
+
+
+/**
+ * @brief Print formulas for user interface signals.
+ *
+ * @param[in] section The title to print for these formulas.
+ * @param[in] field This multimap contains pairs :
+ * 1. the path_string is printed as a sub-title item, when new;
+ * 2. each latex_string is printed as a preformated row of the
+ * supertabular environment (needed to handle long tables).
+ * @param[out] docout The LaTeX output file to print into.
+ *
+ * @remark
+ * To decide when one should avoid to print an itemize environment,
+ * a "global" strategy is applied : in the particular case where
+ * ONLY empty paths were detected in the WHOLE container (all UIs
+ * are at the top level).
+ * In this particular case, UI strings are directly printed,
+ * and their (empty!) path is ignored...
+ * In the other case, we have to print an itemize environment
+ * and manage paths printing (empty AND non-empty paths) as items.
+ *
+ * @see DocCompiler::prepareIntervallicUI
+ * @see DocCompiler::prepareBinaryUI
+ */
+void Lateq::printHierarchy(const string& section, multimap<string,string>& field, ostream& docout)
+{
+ if (field.size() > 0) {
+ docout << section << endl;
+
+ bool hasSomePaths = hasNotOnlyEmptyKeys(field); ///< Manage itemize printing for pathnames.
+ unsigned int n; ///< Manage latex indentation offset.
+
+ if (hasSomePaths) {
+ tab(0,docout); docout << "\\begin{itemize}" << endl;
+ n = 1;
+ } else {
+ n = 0;
+ }
+
+ multimap<string,string>::iterator it;
+ string uidir = "improbable_starting_dirname";
+ bool startFlag = true;
+
+ for (it = field.begin(); it != field.end(); ++it) {
+ /* Manage supertabular environment bounds and pathname printing. */
+ if (it->first != uidir) {
+ if (!startFlag) {
+ tab(n+2,docout); docout << "\\end{supertabular}" << endl;
+ tab(n+1,docout); docout << "\\end{center}" << endl;
+ } else {
+ startFlag = false;
+ }
+ if (hasSomePaths) {
+ /* Print the current pathname if new and if pathnames requested. */
+ if (it->first != "") {
+ tab(n+0,docout); docout << "\\item \\textsf{" << it->first << "}" << endl;
+ } else {
+ tab(n+0,docout); docout << "\\item \\emph{" << gDocMathStringMap["rootlevel"] << "}" << endl;
+ }
+ }
+ tab(n+1,docout); docout << "\\begin{center}" << endl;
+ tab(n+2,docout); docout << "\\begin{supertabular}{lll}" << endl;
+ }
+ /* Print the current formula. */
+ tab(n+3,docout); docout << it->second << endl;
+ uidir = it->first;
+ }
+ tab(n+2,docout); docout << "\\end{supertabular}" << endl;
+ tab(n+1,docout); docout << "\\end{center}" << endl;
+ if (hasSomePaths) {
+ tab(n+0,docout); docout << "\\end{itemize}" << endl;
+ }
+ docout << endl;
+ }
+}
+
+
+/**
+ * @brief Print formulas for select2, select3 and prefix signals.
+ *
+ * @param[in] section The title to print for these formulas.
+ * @param[in] field The list of LaTeX arrays (for braces with two lines).
+ * @param[out] docout The LaTeX output file to print into.
+ *
+ * @see DocCompiler::generateSelect2
+ * @see DocCompiler::generateSelect3
+ * @see DocCompiler::generatePrefix
+ */
+void Lateq::printMath(const string& section, list<string>& field, ostream& docout)
+{
+ if (field.size() > 0) {
+ docout << section;
+ docout << "\\begin{displaymath}" << endl;
+ list<string>::iterator s;
+ for (s = field.begin(); s != field.end(); ++s) {
+ docout << *s << endl;
+ }
+ docout << "\\end{displaymath}" << endl;
+ docout << endl;
+ }
+}
+
+
+/** Simple handling of indentation. */
+void Lateq::tab (int n, ostream& docout) const
+{
+ while (n--) docout << '\t';
+}
+
+
+/**
+ * @brief Find out whether all keys of the multimap are empty or not.
+ *
+ * In other words :
+ * Check that some UIs have a path (non empty).
+ *
+ * In other other words :
+ * Check that all UIs are not at top-level.
+ */
+bool Lateq::hasNotOnlyEmptyKeys(multimap<string,string>& mm)
+{
+ typedef multimap<string,string>::iterator MMIT;
+ pair<MMIT,MMIT> range;
+ range = mm.equal_range(""); ///< Look for pairs with empty keys.
+ bool hasOnlyEmptyPaths = (range.first == mm.begin()) && (range.second == mm.end());
+ return !hasOnlyEmptyPaths;
+}
+
+
+
+/**
+ * Dispatch initialization of autodoc container.
+ */
+void initDocMath()
+{
+ initDocMathKeySet();
+}
+
+
+/****************************************************************
+ Internal static functions.
+ *****************************************************************/
+
+
+/**
+ * Compare indexes of two LaTeX strings,
+ * for the sort() method applied on list<string> fields.
+ */
+static bool compLateqIndexes(const string& s1, const string& s2)
+{
+ return getLateqIndex(s1) < getLateqIndex(s2);
+}
+
+
+/**
+ * Find out the index of signals in LaTeX signal definition strings,
+ * between the first "_{" and "}" patterns.
+ *
+ * @param[in] s A LaTeX string to parse.
+ * @return <int> The index found, as an integer.
+ */
+static int getLateqIndex(const string& s)
+{
+ size_t p1;
+ size_t p2;
+ string sIndex;
+
+ p1 = s.find("_{");
+ if (p1==string::npos) {
+ cerr << "Error : getLateqIndex found no \"{_\" substring.\n";
+ exit(1); }
+ p1 += 2;
+
+ p2 = s.find("}", p1);
+ if (p2==string::npos) {
+ cerr << "Error : getLateqIndex found no \"}\" substring\n.";
+ exit(1); }
+ p2 -= 3;
+
+ sIndex = s.substr (p1, p2);
+
+ return atoi(sIndex.c_str());
+}
+
+
+/**
+ * Initialize gDocMathKeySet, a set containing all the keywords.
+ */
+static void initDocMathKeySet()
+{
+ gDocMathKeySet.insert("inputsigtitle1");
+ gDocMathKeySet.insert("inputsigtitle2");
+ gDocMathKeySet.insert("outputsigtitle1");
+ gDocMathKeySet.insert("outputsigtitle2");
+ gDocMathKeySet.insert("constsigtitle1");
+ gDocMathKeySet.insert("constsigtitle2");
+ gDocMathKeySet.insert("uisigtitle1");
+ gDocMathKeySet.insert("uisigtitle2");
+ gDocMathKeySet.insert("intermedsigtitle1");
+ gDocMathKeySet.insert("intermedsigtitle2");
+ gDocMathKeySet.insert("lateqcomment");
+ gDocMathKeySet.insert("emptyformulafield");
+ gDocMathKeySet.insert("defaultvalue");
+ gDocMathKeySet.insert("suchthat");
+ gDocMathKeySet.insert("and");
+ gDocMathKeySet.insert("for");
+ gDocMathKeySet.insert("rootlevel");
+
+ gDocMathKeySet.insert("dgmcaption");
+}
+
+