<commit_before>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/tex/CStructureParser.cpp,v $
//   $Revision: 1.15 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2011/03/07 19:33:43 $
// End CVS Header

// Copyright (C) 2011 - 2010 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Written by pwilly on 09.07.08

/**
 * NOTE: We use index 1 and 2 to refer to the first and to the second part, resp.,
 * of some elements. These elements are mfrac, msub, and msup.
 */

#include "CStructureParser.h"

#include <QString>
#include <QRegExp>

#include <iostream>
#include "UI/qtUtilities.h"

CStructureParser::CStructureParser(int n)
{
  sumColumns = n;
}

bool CStructureParser::startDocument()
{
  indent = "";
  tex = "";

  indexColumns = -1;

  if (!sumColumns)
    needToWriteColumnAllignment = false;
  else
    needToWriteColumnAllignment = true;

  return TRUE;
}

bool CStructureParser::startElement(const QString& /* str1 */, const QString& /* str2 */,
                                    const QString& qName,
                                    const QXmlAttributes& attr)
{
  tagName = qName;

  QLinkedList<QString>::iterator itL;

  if (qName == "mtable")
    texHead = "\\begin{array}";

  if (qName == "mtr")
    indexColumns = 0;

  if (qName == "mtd")
    {
      if (indexColumns > 0 && indexColumns < sumColumns - 1)
        tex += " \\; &";

      if (indexColumns == 0 && needToWriteColumnAllignment)
        texHead += "{";

      if (indexColumns < sumColumns && needToWriteColumnAllignment)
        {
          if (attr.count())
            {
              if (attr.value("columnalign") == "left")
                texHead += "l";
              else if (attr.value("columnalign") == "center")
                texHead += "c";
              else if (attr.value("columnalign") == "right")
                texHead += "r";
            }
          else
            texHead += "c";
        }

      if (indexColumns == sumColumns - 1)
        {
          if (needToWriteColumnAllignment)
            texHead += "}";

          needToWriteColumnAllignment = false;
        }
    }

  if (qName == "mfrac") // find a mfrac element
    {
      // increment index, if any
      if (!mListOfUncompletedTags.isEmpty())
        {
          QString &last = mListOfUncompletedTags.last();  // must be not empty

          // <mfrac> direct after <mfrac>
          if (last.contains("mfrac"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              tex += "{";
            }
        }

      mListOfUncompletedTags.push_back("mfrac_0");
      tex += "\\frac";
    }

  if (qName == "mfenced")
    {
      mListOfUncompletedTags.push_back("mfenced");
      tex += "\\left(";
    }

  if (qName == "msub")
    {
      if (mListOfUncompletedTags.size() > 0)
        {
          QString &last = mListOfUncompletedTags.last();  // must be not empty

          // <msub> direct after <mfrac>
          if (last.contains("mfrac"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              tex += "{";
            }

          // <msub> direct after <mfenced>
          if (last.contains("mfenced") && (!tex.endsWith("(") && !tex.endsWith("(")))
            {
              tex += ", \\, ";
            }
        }

      mListOfUncompletedTags.push_back("msub_0");
    }

  if (qName == "msup")
    {
      if (mListOfUncompletedTags.size() > 0)
        {
          QString &last = mListOfUncompletedTags.last();  // must be not empty

          // <msup> direct after <mfrac>
          if (last.contains("mfrac"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              tex += "{";
            }

          // <msup> direct after <mfenced>
          if (last.contains("mfenced") && (!tex.endsWith("(") && !tex.endsWith("(")))
            {
              tex += ", \\, ";
            }
        }

      mListOfUncompletedTags.push_back("msup_0");
    }

  if (qName == "mrow")
    {
      // increment index, if any
      if (!mListOfUncompletedTags.isEmpty())
        {
          QString &last = mListOfUncompletedTags.last();  // can be empty

          if (last.contains("mfrac") || last.contains("msub") || last.contains("msup"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              if (lastUncompletedTags.contains("msub") && idx == 2)
                tex += "_";

              if (lastUncompletedTags.contains("msup") && idx == 2)
                tex += "^";
            }
        }

      mListOfUncompletedTags.push_back("mrow");
      tex += " {";
    }

  if (qName == "mi" || qName == "mo" || qName == "mn")
    {
      // increment index, if any
      if (!mListOfUncompletedTags.isEmpty())
        {
          QString &last = mListOfUncompletedTags.last();  // can be empty

          if (last.contains("mfrac") || last.contains("msub") || last.contains("msup"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              if (lastUncompletedTags.contains("msub") && idx == 2)
                tex += "_";

              if (lastUncompletedTags.contains("msup") && idx == 2)
                tex += "^";
            }

          if (last.contains("mfenced") && (!tex.endsWith("(") && !tex.endsWith("(")))
            {
              tex += ", \\, ";
            }
        }
    }

  indent += "    ";

  return TRUE;
}

bool CStructureParser::characters(const QString& str)
{
  QRegExp rx("\\w");
  QString strAux = str.trimmed();
  int pos = rx.indexIn(strAux);

  if (pos != -1)
    {
      // handling word character within <mrow> ... </mrow>
      if (tagName == "mrow")
        {
          if (strAux.length() > 1)
            tex += "{\\text{" + strAux + "}}";
          else  // exactly only one character
            tex += "{" + strAux + "}";
        }

      // handling word character within <mi> ... </mi>
      if (tagName == "mi")
        {
          if (strAux.contains("\\"))    // for, eg., \sin, \cos
            tex += strAux;
          else if (strAux.length() > 1)
            {
              if (strAux == "sech" || strAux == "csch" || strAux == "arcsec" || strAux == "arccsc"
                  || strAux == "arccot" || strAux == "arcsinh" || strAux == "arccosh" || strAux == "arctanh"
                  || strAux == "arcsech" || strAux == "arccsch" || strAux == "arccoth")
                tex += "{\\mathrm{" + strAux + " \\: }}";
              else
                tex += "{\\mathrm{" + strAux + "}}";
            }
          else  // exactly only one character
            tex += "{" + strAux + "}";
        }

      // handling word character within <mo> ... </mo>
      if (tagName == "mo")
        {
          if (strAux.contains("\\"))    // for, eg.,\cdot, \ge, \le, \ne
            {
              if (strAux == "\\log")
                tex += " \\, " + strAux;
              else if (strAux == "\\lt")
                tex += " \\, < \\, ";
              else
                tex += " \\, " + strAux + " \\, ";
            }
          else if (strAux.contains("xor"))
            tex += "\\; \\mathrm{" + strAux + "} \\; ";
          else if (strAux == "e")
            tex += strAux;
          else
            tex += "\\mathrm{" + strAux + "}";
        }

      // handling word character within <mn> ... </mn>
      if (tagName == "mn")
        tex += "{" + strAux + "}";
    }
  // handling non-word character
  else if (strAux == "=" || strAux == "!" || strAux == "|")
    tex += strAux;
  else if (strAux == "-" || strAux == "+" || strAux == ">" || strAux.contains("%"))
    tex += " \\, " + strAux + " \\, ";

  return TRUE;
}

bool CStructureParser::endElement(const QString&, const QString&, const QString& qName)
{
  QLinkedList<QString>::iterator itL;

  indent.remove((uint)0, 4);

  if (qName == "mtable")
    texTail = "\\end{array}";

  if (qName == "mfrac")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must be not empty
        mListOfUncompletedTags.pop_back();

      // </mfrac> direct after </mfrac>
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must be not empty
        tex += " }";
    }

  if (qName == "mtr")
    tex += "\\\\ \n && \\\\ \n";

  if (qName == "mtd")
    {
      if (indexColumns > 0 && indexColumns < sumColumns - 1)
        tex += "& \\; ";

      indexColumns++;
    }

  if (qName == "mrow")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last() == "mrow")  // must not be empty
        mListOfUncompletedTags.pop_back();

      tex += " } ";
    }

  if (qName == "mfenced")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last() == "mfenced") // must be not empty
        mListOfUncompletedTags.pop_back();

      tex += "\\right)";

      QString &last = mListOfUncompletedTags.last();  // must be not empty

      // </msub> direct after </mfenced>
      if (mListOfUncompletedTags.size() > 0 &&
          last.contains("msub"))
        {
          QStringList strList = last.split("_");
          QString &lastUncompletedTags = strList.first();
          QString &idxStr = strList.last();
          int idx = idxStr.toInt();
          idx++;

          // update with incrementally index
          last = lastUncompletedTags + "_" + QString::number(idx);

          if (lastUncompletedTags.contains("msub") && idx == 2)
            tex += "_";
        }

      // </msup> direct after </mfenced>
      if (mListOfUncompletedTags.size() > 0 &&
          last.contains("msup"))
        {
          QStringList strList = last.split("_");
          QString &lastUncompletedTags = strList.first();
          QString &idxStr = strList.last();
          int idx = idxStr.toInt();
          idx++;

          // update with incrementally index
          last = lastUncompletedTags + "_" + QString::number(idx);

          if (mListOfUncompletedTags.size() > 0 &&
              lastUncompletedTags.contains("msup") && idx == 2)
            tex += "^";
        }
    }

  if (qName == "msub")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("msub")) // must be not empty
        mListOfUncompletedTags.pop_back();

      // </mfrac> direct after </msub>
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must ne not empty
        tex += " }";
    }

  if (qName == "msup")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("msup")) // must be not empty
        mListOfUncompletedTags.pop_back();

      // </mfrac> direct after </msup>
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must be not empty
        tex += " }";
    }

  return TRUE;
}

bool CStructureParser::ignorableWhitespace(const QString& /* str */)
{
  return TRUE;
}

bool CStructureParser::skippedEntity(const QString& /* str */)
{
  return TRUE;
}

QString CStructureParser::getTeX()
{
  QString texIntro;

  texIntro = "%%% Attention: \n";
  texIntro += "%%% We provide only the LaTeX code of the Differential Equations. \n";
  texIntro += "%%% You need to include it in your TeX document. \n";
  texIntro += "%%% Some manual adjustments may be needed for too wide equations. \n\n";

  if (!texHead.isNull())
    return texIntro + "$$\n" + texHead + "\n" + tex + texTail + "\n$$";
  else
    return texIntro + "$$\n" + tex + "\n$$";
}
<commit_msg>Fixed Bug 1792. Spaces and underscores are not properly escaped.<commit_after>// Copyright (C) 2010 - 2012 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Written by pwilly on 09.07.08

/**
 * NOTE: We use index 1 and 2 to refer to the first and to the second part, resp.,
 * of some elements. These elements are mfrac, msub, and msup.
 */

#include "CStructureParser.h"

#include <QString>
#include <QRegExp>

#include <iostream>
#include "UI/qtUtilities.h"

CStructureParser::CStructureParser(int n)
{
  sumColumns = n;
}

bool CStructureParser::startDocument()
{
  indent = "";
  tex = "";

  indexColumns = -1;

  if (!sumColumns)
    needToWriteColumnAllignment = false;
  else
    needToWriteColumnAllignment = true;

  return TRUE;
}

bool CStructureParser::startElement(const QString& /* str1 */, const QString& /* str2 */,
                                    const QString& qName,
                                    const QXmlAttributes& attr)
{
  tagName = qName;

  QLinkedList<QString>::iterator itL;

  if (qName == "mtable")
    texHead = "\\begin{array}";

  if (qName == "mtr")
    indexColumns = 0;

  if (qName == "mtd")
    {
      if (indexColumns > 0 && indexColumns < sumColumns - 1)
        tex += " \\; &";

      if (indexColumns == 0 && needToWriteColumnAllignment)
        texHead += "{";

      if (indexColumns < sumColumns && needToWriteColumnAllignment)
        {
          if (attr.count())
            {
              if (attr.value("columnalign") == "left")
                texHead += "l";
              else if (attr.value("columnalign") == "center")
                texHead += "c";
              else if (attr.value("columnalign") == "right")
                texHead += "r";
            }
          else
            texHead += "c";
        }

      if (indexColumns == sumColumns - 1)
        {
          if (needToWriteColumnAllignment)
            texHead += "}";

          needToWriteColumnAllignment = false;
        }
    }

  if (qName == "mfrac") // find a mfrac element
    {
      // increment index, if any
      if (!mListOfUncompletedTags.isEmpty())
        {
          QString &last = mListOfUncompletedTags.last();  // must be not empty

          // <mfrac> direct after <mfrac>
          if (last.contains("mfrac"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              tex += "{";
            }
        }

      mListOfUncompletedTags.push_back("mfrac_0");
      tex += "\\frac";
    }

  if (qName == "mfenced")
    {
      mListOfUncompletedTags.push_back("mfenced");
      tex += "\\left(";
    }

  if (qName == "msub")
    {
      if (mListOfUncompletedTags.size() > 0)
        {
          QString &last = mListOfUncompletedTags.last();  // must be not empty

          // <msub> direct after <mfrac>
          if (last.contains("mfrac"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              tex += "{";
            }

          // <msub> direct after <mfenced>
          if (last.contains("mfenced") && (!tex.endsWith("(") && !tex.endsWith("(")))
            {
              tex += ", \\, ";
            }
        }

      mListOfUncompletedTags.push_back("msub_0");
    }

  if (qName == "msup")
    {
      if (mListOfUncompletedTags.size() > 0)
        {
          QString &last = mListOfUncompletedTags.last();  // must be not empty

          // <msup> direct after <mfrac>
          if (last.contains("mfrac"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              tex += "{";
            }

          // <msup> direct after <mfenced>
          if (last.contains("mfenced") && (!tex.endsWith("(") && !tex.endsWith("(")))
            {
              tex += ", \\, ";
            }
        }

      mListOfUncompletedTags.push_back("msup_0");
    }

  if (qName == "mrow")
    {
      // increment index, if any
      if (!mListOfUncompletedTags.isEmpty())
        {
          QString &last = mListOfUncompletedTags.last();  // can be empty

          if (last.contains("mfrac") || last.contains("msub") || last.contains("msup"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              if (lastUncompletedTags.contains("msub") && idx == 2)
                tex += "_";

              if (lastUncompletedTags.contains("msup") && idx == 2)
                tex += "^";
            }
        }

      mListOfUncompletedTags.push_back("mrow");
      tex += " {";
    }

  if (qName == "mi" || qName == "mo" || qName == "mn")
    {
      // increment index, if any
      if (!mListOfUncompletedTags.isEmpty())
        {
          QString &last = mListOfUncompletedTags.last();  // can be empty

          if (last.contains("mfrac") || last.contains("msub") || last.contains("msup"))
            {
              QStringList strList = last.split("_");
              QString &lastUncompletedTags = strList.first();
              QString &idxStr = strList.last();
              int idx = idxStr.toInt();
              idx++;

              // update with incrementally index
              last = lastUncompletedTags + "_" + QString::number(idx);

              if (lastUncompletedTags.contains("msub") && idx == 2)
                tex += "_";

              if (lastUncompletedTags.contains("msup") && idx == 2)
                tex += "^";
            }

          if (last.contains("mfenced") && (!tex.endsWith("(") && !tex.endsWith("(")))
            {
              tex += ", \\, ";
            }
        }
    }

  indent += "    ";

  return TRUE;
}

bool CStructureParser::characters(const QString& str)
{
  QRegExp rx("\\w");
  QString strAux = str.trimmed();
  int pos = rx.indexIn(strAux);

  std::cout << TO_UTF8(strAux) << std::endl;

  if (pos != -1)
    {
      // handling word character within <mrow> ... </mrow>
      if (tagName == "mrow")
        {
          if (strAux.length() > 1)
            tex += "{\\text{" + strAux + "}}";
          else  // exactly only one character
            tex += "{" + strAux + "}";
        }

      // handling word character within <mi> ... </mi>
      if (tagName == "mi")
        {
          if (strAux.contains("\\"))    // for, eg., \sin, \cos
            tex += strAux;
          else if (strAux.length() > 1)
            {
              if (strAux == "sech" || strAux == "csch" || strAux == "arcsec" || strAux == "arccsc"
                  || strAux == "arccot" || strAux == "arcsinh" || strAux == "arccosh" || strAux == "arctanh"
                  || strAux == "arcsech" || strAux == "arccsch" || strAux == "arccoth")
                tex += "{\\mathrm{" + strAux + " \\: }}";
              else
                {
                  strAux.replace("_", "\\_");
                  strAux.replace(" ", "\\;");
                  tex += "{\\mathrm{" + strAux + "}}";
                }
            }
          else  // exactly only one character
            tex += "{" + strAux + "}";
        }

      // handling word character within <mo> ... </mo>
      if (tagName == "mo")
        {
          if (strAux.contains("\\"))    // for, eg.,\cdot, \ge, \le, \ne
            {
              if (strAux == "\\log")
                tex += " \\, " + strAux;
              else if (strAux == "\\lt")
                tex += " \\, < \\, ";
              else
                tex += " \\, " + strAux + " \\, ";
            }
          else if (strAux.contains("xor"))
            tex += "\\; \\mathrm{" + strAux + "} \\; ";
          else if (strAux == "e")
            tex += strAux;
          else
            tex += "\\mathrm{" + strAux + "}";
        }

      // handling word character within <mn> ... </mn>
      if (tagName == "mn")
        tex += "{" + strAux + "}";
    }
  // handling non-word character
  else if (strAux == "=" || strAux == "!" || strAux == "|")
    tex += strAux;
  else if (strAux == "-" || strAux == "+" || strAux == ">" || strAux.contains("%"))
    tex += " \\, " + strAux + " \\, ";

  return TRUE;
}

bool CStructureParser::endElement(const QString&, const QString&, const QString& qName)
{
  QLinkedList<QString>::iterator itL;

  indent.remove((uint)0, 4);

  if (qName == "mtable")
    texTail = "\\end{array}";

  if (qName == "mfrac")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must be not empty
        mListOfUncompletedTags.pop_back();

      // </mfrac> direct after </mfrac>
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must be not empty
        tex += " }";
    }

  if (qName == "mtr")
    tex += "\\\\ \n && \\\\ \n";

  if (qName == "mtd")
    {
      if (indexColumns > 0 && indexColumns < sumColumns - 1)
        tex += "& \\; ";

      indexColumns++;
    }

  if (qName == "mrow")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last() == "mrow")  // must not be empty
        mListOfUncompletedTags.pop_back();

      tex += " } ";
    }

  if (qName == "mfenced")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last() == "mfenced") // must be not empty
        mListOfUncompletedTags.pop_back();

      tex += "\\right)";

      QString &last = mListOfUncompletedTags.last();  // must be not empty

      // </msub> direct after </mfenced>
      if (mListOfUncompletedTags.size() > 0 &&
          last.contains("msub"))
        {
          QStringList strList = last.split("_");
          QString &lastUncompletedTags = strList.first();
          QString &idxStr = strList.last();
          int idx = idxStr.toInt();
          idx++;

          // update with incrementally index
          last = lastUncompletedTags + "_" + QString::number(idx);

          if (lastUncompletedTags.contains("msub") && idx == 2)
            tex += "_";
        }

      // </msup> direct after </mfenced>
      if (mListOfUncompletedTags.size() > 0 &&
          last.contains("msup"))
        {
          QStringList strList = last.split("_");
          QString &lastUncompletedTags = strList.first();
          QString &idxStr = strList.last();
          int idx = idxStr.toInt();
          idx++;

          // update with incrementally index
          last = lastUncompletedTags + "_" + QString::number(idx);

          if (mListOfUncompletedTags.size() > 0 &&
              lastUncompletedTags.contains("msup") && idx == 2)
            tex += "^";
        }
    }

  if (qName == "msub")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("msub")) // must be not empty
        mListOfUncompletedTags.pop_back();

      // </mfrac> direct after </msub>
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must ne not empty
        tex += " }";
    }

  if (qName == "msup")
    {
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("msup")) // must be not empty
        mListOfUncompletedTags.pop_back();

      // </mfrac> direct after </msup>
      if (mListOfUncompletedTags.size() > 0 &&
          mListOfUncompletedTags.last().contains("mfrac"))  // must be not empty
        tex += " }";
    }

  return TRUE;
}

bool CStructureParser::ignorableWhitespace(const QString& /* str */)
{
  return TRUE;
}

bool CStructureParser::skippedEntity(const QString& /* str */)
{
  return TRUE;
}

QString CStructureParser::getTeX()
{
  QString texIntro;

  texIntro = "%%% Attention: \n";
  texIntro += "%%% We provide only the LaTeX code of the Differential Equations. \n";
  texIntro += "%%% You need to include it in your TeX document. \n";
  texIntro += "%%% Some manual adjustments may be needed for too wide equations. \n\n";

  if (!texHead.isNull())
    return texIntro + "$$\n" + texHead + "\n" + tex + texTail + "\n$$";
  else
    return texIntro + "$$\n" + tex + "\n$$";
}
<|endoftext|>


<commit_before>// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11

namespace value_range_detail {
  template<typename T>
  class value_range_iter {
    T t;
  public:
    value_range_iter(const T &t) : t(t) {}
    T operator*() const { return t; }
    bool operator!=(const value_range_iter &o) const { return t != o.t; }
    value_range_iter &operator++() { ++t; return *this; }
  };

  template<typename T>
  struct value_range {
    value_range(const T &a, const T &b) : begin_(a), end_(b) {}
    value_range_iter<T> begin_, end_;
  };

  template<typename T>
  value_range_iter<T> begin(const value_range<T> &r) { return r.begin_; }
  template<typename T>
  value_range_iter<T> end(const value_range<T> &r) { return r.end_; }

  struct end_t {};

  template<typename T>
  class value_range_step_iter {
    T it, step;
  public:
    value_range_step_iter(const T &it, const T &step) : it(it), step(step) {}
    T operator*() const { return it; }
    bool operator!=(value_range_step_iter end) const { return it != end.it; }
    value_range_step_iter &operator++() { it += step; return *this; }
  };

  template<typename T>
  class value_range_step {
    T it, step, end_;
  public:
    value_range_step(const T &it, const T &end, const T &step) :
      it(it), end_(end), step(step) {}
    typedef value_range_step_iter<T> iterator;
    iterator begin() const { return iterator(it, step); }
    iterator end() const { return iterator(end_, step); }
  };
}

template<typename T>
value_range_detail::value_range<T> range(const T &a, const T &b) { return value_range_detail::value_range<T>(a, b); }

template<typename T>
value_range_detail::value_range_step<T> range(const T &a, const T &b, const T &step) { return value_range_detail::value_range_step<T>(a, b, step); }

namespace map_range {
  template<typename T>
  class vector {
    T storage[100];
    decltype(sizeof(char)) size;
  public:
    vector() : size() {}
    void push_back(T t) { storage[size++] = t; }
    T *begin() { return storage; }
    T *end() { return storage + size; }
  };

  template<typename T> struct tuple_elem {
    T t;
    tuple_elem() {}
    tuple_elem(T t) : t(t) {}
  };
  template<typename... A>
  struct tuple : tuple_elem<A>... {
    tuple() : tuple_elem<A>()... {}
    tuple(A... a) : tuple_elem<A>(a)... {}
    template<typename B> B &get() { return tuple_elem<B>::t; }
  };

  template<typename F, typename I>
  class map_iter {
    F f;
    I i;
  public:
    map_iter(F f, I i) : f(f), i(i) {}
    auto operator*() const -> decltype(f(*i)) { return f(*i); }
    bool operator!=(const map_iter &o) const { return i != o.i; }
    map_iter &operator++() { ++i; return *this; }
  };

  template<typename T>
  struct iter_pair {
    T begin_, end_;
    iter_pair(T begin, T end) : begin_(begin), end_(end) {}
  };
  template<typename T> T begin(iter_pair<T> p) { return p.begin_; }
  template<typename T> T end(iter_pair<T> p) { return p.end_; }

  template<typename...> class mem_fun_impl;
  template<typename R, typename T, typename... A>
  class mem_fun_impl<R (T::*)(A...)> {
    typedef R (T::*F)(A...);
    F f;
  public:
    mem_fun_impl(F f) : f(f) {}
    R operator()(T &t, A &&...a) const { return (t.*f)(static_cast<A&&>(a)...); }
  };
  template<typename F> mem_fun_impl<F> mem_fun(F f) { return mem_fun_impl<F>(f); }

  template<typename F, typename T>
  auto map(const F &f, T &t) -> iter_pair<map_iter<F, decltype(t.begin())>> {
    typedef map_iter<F, decltype(t.begin())> iter;
    return iter_pair<iter>(iter(f, t.begin()), iter(f, t.end()));
  }
}

#define assert(b) if (!b) { return 1; }
int main() {
  int total = 0;

  for (auto n : range(1, 5)) {
    total += n;
  }
  assert((total == 10));

  for (auto n : range(10, 100, 10)) {
    total += n;
  }
  assert((total == 460));

  map_range::vector<char> chars;
  chars.push_back('a');
  chars.push_back('b');
  chars.push_back('c');
  for (char c : chars) {
    ++total;
  }
  assert((total == 463));

  typedef map_range::tuple<int, double> T;
  map_range::vector<T> pairs;
  pairs.push_back(T(42, 12.9));
  pairs.push_back(T(6, 4.2));
  pairs.push_back(T(9, 1.1));
  for (auto a : map(map_range::mem_fun(&T::get<int>), pairs)) {
    total += a;
  }
  assert((total == 500));
}

// PR11793
namespace test2 {
  class A {
    int xs[10]; // expected-note {{implicitly declared private here}}
  };
  void test(A &a) {
    for (int x : a.xs) { } // expected-error {{'xs' is a private member of 'test2::A'}}
  }
}

namespace test3 {
  // Make sure this doesn't crash
  struct A {};
  struct B { ~B(); operator bool(); };
  struct C { B operator!=(const C&); C& operator++(); int operator*(); };
  C begin(const A&);
  C end(const A&);
  template<typename T> void f() { for (auto a : A()) {} }
  void g() { f<int>(); }
}

namespace test4 {
  void f() {
    int y;

    // Make sure these don't crash. Better diagnostics would be nice.
    for (: {1, 2, 3}) {} // expected-error {{expected expression}} expected-error {{expected ';'}}
    for (x : {1, 2, 3}) {} // expected-error {{undeclared identifier}} expected-error {{expected ';'}}
    for (y : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{result unused}}
  }
}
<commit_msg>Fix test better way.<commit_after>// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11

namespace value_range_detail {
  template<typename T>
  class value_range_iter {
    T t;
  public:
    value_range_iter(const T &t) : t(t) {}
    T operator*() const { return t; }
    bool operator!=(const value_range_iter &o) const { return t != o.t; }
    value_range_iter &operator++() { ++t; return *this; }
  };

  template<typename T>
  struct value_range {
    value_range(const T &a, const T &b) : begin_(a), end_(b) {}
    value_range_iter<T> begin_, end_;
  };

  template<typename T>
  value_range_iter<T> begin(const value_range<T> &r) { return r.begin_; }
  template<typename T>
  value_range_iter<T> end(const value_range<T> &r) { return r.end_; }

  struct end_t {};

  template<typename T>
  class value_range_step_iter {
    T it, step;
  public:
    value_range_step_iter(const T &it, const T &step) : it(it), step(step) {}
    T operator*() const { return it; }
    bool operator!=(value_range_step_iter end) const { return it != end.it; }
    value_range_step_iter &operator++() { it += step; return *this; }
  };

  template<typename T>
  class value_range_step {
    T it, step, end_;
  public:
    value_range_step(const T &it, const T &end, const T &step) :
      it(it), end_(end), step(step) {}
    typedef value_range_step_iter<T> iterator;
    iterator begin() const { return iterator(it, step); }
    iterator end() const { return iterator(end_, step); }
  };
}

template<typename T>
value_range_detail::value_range<T> range(const T &a, const T &b) { return value_range_detail::value_range<T>(a, b); }

template<typename T>
value_range_detail::value_range_step<T> range(const T &a, const T &b, const T &step) { return value_range_detail::value_range_step<T>(a, b, step); }

namespace map_range {
  template<typename T>
  class vector {
    T storage[100];
    decltype(sizeof(char)) size;
  public:
    vector() : size() {}
    void push_back(T t) { storage[size++] = t; }
    T *begin() { return storage; }
    T *end() { return storage + size; }
  };

  template<typename T> struct tuple_elem {
    T t;
    tuple_elem() {}
    tuple_elem(T t) : t(t) {}
  };
  template<typename... A>
  struct tuple : tuple_elem<A>... {
    tuple() : tuple_elem<A>()... {}
    tuple(A... a) : tuple_elem<A>(a)... {}
    template<typename B> B &get() { return tuple_elem<B>::t; }
  };

  template<typename F, typename I>
  class map_iter {
    F f;
    I i;
  public:
    map_iter(F f, I i) : f(f), i(i) {}
    auto operator*() const -> decltype(f(*i)) { return f(*i); }
    bool operator!=(const map_iter &o) const { return i != o.i; }
    map_iter &operator++() { ++i; return *this; }
  };

  template<typename T>
  struct iter_pair {
    T begin_, end_;
    iter_pair(T begin, T end) : begin_(begin), end_(end) {}
  };
  template<typename T> T begin(iter_pair<T> p) { return p.begin_; }
  template<typename T> T end(iter_pair<T> p) { return p.end_; }

  template<typename...> class mem_fun_impl;
  template<typename R, typename T, typename... A>
  class mem_fun_impl<R (T::*)(A...)> {
    typedef R (T::*F)(A...);
    F f;
  public:
    mem_fun_impl(F f) : f(f) {}
    R operator()(T &t, A &&...a) const { return (t.*f)(static_cast<A&&>(a)...); }
  };
  template<typename F> mem_fun_impl<F> mem_fun(F f) { return mem_fun_impl<F>(f); }

  template<typename F, typename T>
  auto map(const F &f, T &t) -> iter_pair<map_iter<F, decltype(t.begin())>> {
    typedef map_iter<F, decltype(t.begin())> iter;
    return iter_pair<iter>(iter(f, t.begin()), iter(f, t.end()));
  }
}

#define assert(b) if (!(b)) { return 1; }
int main() {
  int total = 0;

  for (auto n : range(1, 5)) {
    total += n;
  }
  assert(total == 10);

  for (auto n : range(10, 100, 10)) {
    total += n;
  }
  assert(total == 460);

  map_range::vector<char> chars;
  chars.push_back('a');
  chars.push_back('b');
  chars.push_back('c');
  for (char c : chars) {
    ++total;
  }
  assert(total == 463);

  typedef map_range::tuple<int, double> T;
  map_range::vector<T> pairs;
  pairs.push_back(T(42, 12.9));
  pairs.push_back(T(6, 4.2));
  pairs.push_back(T(9, 1.1));
  for (auto a : map(map_range::mem_fun(&T::get<int>), pairs)) {
    total += a;
  }
  assert(total == 500);
}

// PR11793
namespace test2 {
  class A {
    int xs[10]; // expected-note {{implicitly declared private here}}
  };
  void test(A &a) {
    for (int x : a.xs) { } // expected-error {{'xs' is a private member of 'test2::A'}}
  }
}

namespace test3 {
  // Make sure this doesn't crash
  struct A {};
  struct B { ~B(); operator bool(); };
  struct C { B operator!=(const C&); C& operator++(); int operator*(); };
  C begin(const A&);
  C end(const A&);
  template<typename T> void f() { for (auto a : A()) {} }
  void g() { f<int>(); }
}

namespace test4 {
  void f() {
    int y;

    // Make sure these don't crash. Better diagnostics would be nice.
    for (: {1, 2, 3}) {} // expected-error {{expected expression}} expected-error {{expected ';'}}
    for (x : {1, 2, 3}) {} // expected-error {{undeclared identifier}} expected-error {{expected ';'}}
    for (y : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{result unused}}
  }
}
<|endoftext|>


<commit_before>/** Implementation of canonpy.
 */

#include <canonpy.h>

#include <Python.h>

#include <algorithm>
#include <string>
#include <vector>

#include <libcanon/perm.h>

using libcanon::Simple_perm;
using libcanon::Point;
using libcanon::Point_vec;

//
// Perm class
// ==========
//
// Internal functions
// ------------------
//
// These functions are not directly set to Python types but are used
// internally.  They are also used by other parts of this extension.
//

/** Builds a tuple object for a permutation.
 *
 * This function creates a pair, where the first field is a list of integers
 * for the preimage array, and the second field is the accompanied action,
 * which is also encoded as an integer.
 */

static PyObject* build_perm_to_tuple(const Simple_perm& perm)
{

    PyObject* pre_images = NULL;
    PyObject* res = NULL;
    PyObject* acc = NULL;
    size_t size = perm.size();

    pre_images = PyList_New(size);
    if (!pre_images)
        goto error;
    for (size_t i = 0; i < size; ++i) {
        PyObject* curr = Py_BuildValue("n", perm >> i);
        if (curr) {
            PyList_SetItem(pre_images, i, curr);
        } else {
            goto error;
        }
    }

    acc = Py_BuildValue("b", perm.acc());
    if (!acc)
        goto error;

    res = PyTuple_New(2);
    if (!res)
        goto error;

    PyTuple_SET_ITEM(res, 0, pre_images);
    PyTuple_SET_ITEM(res, 1, acc);

    return (PyObject*)res;

error:
    Py_XDECREF(pre_images);
    Py_XDECREF(res);
    Py_XDECREF(acc);
    return NULL;
}

/** Builds a permutation from its construction arguments.
 *
 * An iterable of positive integers for the pre-image array needs to be given
 * as the first argument.  The accompanied action can be optionally given as
 * another integral argument, or by the keyword ``acc``.
 *
 * If the arguments are not valid, an integer exception will be thrown and the
 * Python exception will be set.
 *
 * This function is designed to be compatible with the result from the function
 * `build_perm_to_tuple`.  However, more general input format is accepted.
 */

static Simple_perm make_perm_from_args(PyObject* args, PyObject* kwargs)
{
    PyObject* pre_images;
    char acc = 0;

    // We only need a simple code, actual error is set to the Python stack.
    constexpr int err_code = 1;

    static char* kwlist[] = { "pre_images", "acc", NULL };

    auto args_stat = PyArg_ParseTupleAndKeywords(
        args, kwargs, "O|b", kwlist, &pre_images, &acc);

    if (!args_stat)
        throw err_code;

    Point_vec pre_images_vec{};
    std::vector<bool> image_set{};

    PyObject* pre_images_iter = PyObject_GetIter(pre_images);
    if (!pre_images_iter)
        throw error;

    // Set this to true and goto the end of the function, then the iterator can
    // be released.
    bool if_err = false;

    PyObject* pre_image_obj;
    while (pre_image_obj = PyIter_Next(pre_images_iter)) {

        if (!PyLong_Check(pre_image_obj)) {
            if_err = true;
            goto exit;
        }
        Point pre_image = PyLong_AsUnsignedLong(pre_image_obj);

        // Release reference right here since its content is already extracted.
        // In this way, the error handling does not need to worry about it any
        // more.
        Py_DECREF(pre_image_obj);

        if (PyErr_Occurred()) {
            if_err = true;
            goto exit;
        }

        pre_images_vec.push_back(pre_image);
        size_t req_size = pre_image + 1;
        if (image_set.size() < req_size)
            image_set.resize(req_size, false);
        if (image_set[pre_image]) {
            std::string err_msg("The image of ");
            err_msg.append(std::to_string(pre_image));
            err_msg.append(" has already been set.");
            PyErr_SetString(PyExc_ValueError, err_msg.c_str());
            if_err = true;
            goto exist;
        } else {
            image_set[pre_image] = true;
        }
    }

    // Non StopIteration error.
    if (PyErr_Occurred()) {
        if_err = true;
        goto exit;
    }

    auto first_not_set = std::find(image_set.begin(), image_set.end(), false);
    if (first_not_set != image_set.end()) {
        std::string err_msg("The image of ");
        err_msg.append(std::to_string(first_not_set - images_set.begin()));
        err_msg(" is not set.");
        if_err = true;
    }

exit:
    Py_DECREF(pre_images_iter);
    if (if_err) {
        throw err_code;
    } else {
        return Simple_perm(std::move(pre_images_vec), acc);
    }
}

//
// Interface functions
// -------------------
//

const static char* perm_getnewargs_doc
    = "Get the arguments for new to construct the Perm.";

static PyObject* perm_getnewargs(Perm_object* self)
{
    // Here we directly use the tuple format of a perm.

    return build_perm_to_tuple(self->perm);
}

/** Gets the size of the permutation domain of a Perm.
 */

static Py_ssize_t perm_length(Perm_object* self)
{
    // The type should be the same, size_t and Py_ssize_t.

    return self->perm.size();
}

/** Gets the pre-image of a point.
 *
 * A new integer object will be built by this function.
 */

static PyObject* perm_item(Perm_object* self, Py_ssize_t i)
{
    if (i < 0) {
        PyErr_SetString(PyExc_IndexError, "Points should be positive.");
        return NULL;
    }
    size_t idx = i;
    if (idx >= self->perm.size()) {
        PyErr_SetString(PyExc_IndexError, "Point outside permutation domain");
        return NULL;
    }

    return Py_BuildValue("n", self->perm >> i);
}

/** Gets the accompanied action of a Perm.
 */

static PyObject* perm_get_acc(Perm_object* self, void* closure)
{
    // Note that the accompanied action is a byte in simple perm.

    return Py_BuildValue("b", self->perm.acc());
}

/** Deallocates a perm instance.
 */

static void perm_dealloc(Perm_object* self)
{
    self->perm.~Simple_perm();

    // For subclassing.
    Py_TYPE(self)->tp_free((PyObject*)self);
}

/** Forms the string representation of a Perm object.
 */

static PyObject* perm_repr(Perm_object* self)
{
    const Simple_perm& perm = self->perm;

    std::wstring repr(L"Perm(");

    size_t size = perm.size();

    if (size > 0) {
        for (size_t i = 0; i < size; ++i) {
            if (i == 0) {
                repr.append(L"[");
            } else {
                repr.append(L", ");
            }
            repr.append(std::to_wstring(perm >> i));
        }
        repr.append(L"]");

        // Add the accompanied action only when we need.
        char acc = perm.acc();
        if (acc != 0) {
            repr.append(L", ");
            repr.append(std::to_wstring(acc));
        }
    }

    // This is used for empty or non-empty permutation.
    repr.append(L")");

    return PyUnicode_FromUnicode(repr.data(), repr.size());
}

/** Creates a new Perm object.
 *
 * The actual work is delegated to the Python/Perm interface function.
 */

static PyObject* perm_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
    Perm_object* self;

    // Pay attention to subclassing.
    self = (Perm_object*)type->tp_alloc(type, 0);

    if (!self)
        return NULL;

    Simple_perm perm{};
    try {
        perm = make_perm_from_args(args, kwargs);
    } catch (int) {
        Py_DECREF(self);
        return NULL;
    }

    new (&self->perm) Simple_perm(std::move(perm));
    return (PyObject*)self;
}

//
// Class definition
// ----------------
//

/** Methods for Perm objects.
 */

static PyMethodDef perm_methods[] = {
    { "__getnewargs__", (PyCFunction)perm_getnewargs, METH_NOARGS,
        perm_getnewargs_doc },
    { NULL, NULL } /* sentinel */
};

/** Sequence operations for Perm objects.
 *
 * Here we only support size and pre-image query.
 */

// clang-format off
static PySequenceMethods perm_as_sequence = {
    (lenfunc)perm_length,                       /* sq_length */
    0,                                          /* sq_concat */
    0,                                          /* sq_repeat */
    (ssizeargfunc)perm_item,                    /* sq_item */
    0,                                          /* sq_slice */
    0,                                          /* sq_ass_item */
    0,                                          /* sq_ass_slice */
    0                                           /* sq_contains */
};
// clang-format on

/** Accessors for Perms.
 *
 * The accompanied action query is made here.
 */

// clang-format off
static PyGetSetDef perm_getsets[] = {
    { "acc", (getter)perm_get_acc, NULL, "The accompanied action.", NULL },
    { NULL }
};
// clang-format on

/** Perm type doc string.
  */

static const char* perm_doc =
    R"__doc__(Permutation of points with accompanied action.

Permutations can be constructed from an iterable giving the pre-image of the
points and an optional integral value for the accompanied action.  The
accompanied action can be given positionally or by the keyword ``acc``, and it
will be manipulated according to the convention in libcanon.

Querying the length of a Perm object gives the size of the permutation domain,
while indexing it gives the pre-image of the given integral point.  The
accompanied action can be obtained by getting the attribute ``acc``.
Otherwise, this data type is mostly opaque.

)__doc__";

/** Type definition for Perm class.
 */

// clang-format off
static PyTypeObject perm_type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "drudge.canonpy.Perm",
    sizeof(Perm_object),
    0,
    (destructor) perm_dealloc,                  /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc) perm_repr,                       /* tp_repr */
    0,                                          /* tp_as_number */
    &perm_as_sequence,                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0, /* In main. */                           /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
    perm_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    perm_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    perm_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    perm_new,                                   /* tp_new */
    0,                                          /* tp_free */
};
// clang-format on

//
// Python module definition
// ========================
//

/** Docstring for the canonpy module.
 */

static const char* canonpy_docstring
    = R"__doc__(Canonpy, simple wrapper over libcanon for Python.

This wrapper of libcanon is directly targeted towards usage using drudge.
Here, we have a class `Perm`, which wraps over the `Simple_perm` class in
libcanon, another class `SimsTransv`, which wraps over the `Sims_trasv` class
for `Simple_perm`.  And we also have the function `canon_eldag` to canonicalize
an Eldag.

)__doc__";

/** Methods in the canonpy module.
 */

static PyMethodDef canonpy_methods[] = { { NULL, NULL, 0, NULL } };

/** Executes the initialization of the canonpy module.
 */

static int canonpy_exec(PyObject* m)
{
    //
    // Add the class for Perm.
    //

    perm_type.tp_getattro = PyObject_GenericGetAttr;
    if (PyType_Ready(&perm_type) < 0)
        return -1;
    Py_INCREF(&perm_type);
    PyModule_AddObject(m, "Perm", (PyObject*)&perm_type);

    return 0;
}

/** Slots for for canonpy module definition.
 */

static struct PyModuleDef_Slot canonpy_slots[] = {
    { Py_mod_exec, (void*)canonpy_exec }, { 0, NULL },
};

/** Canonpy module definition.
 */

// clang-format off

static struct PyModuleDef canonpy_module = {
    PyModuleDef_HEAD_INIT,
    "drudge.canonpy",
    canonpy_docstring,
    0, // m-size
    canonpy_methods,
    canonpy_slots,
    NULL, // Transverse
    NULL, // Clear
    NULL  // Free
};

// clang-format on

/** The published canonpy function.
 */

PyMODINIT_FUNC PyInit_canonpy(void)
{
    return PyModuleDef_Init(&canonpy_module);
}
<commit_msg>Revise Perm creation from Python args<commit_after>/** Implementation of canonpy.
 */

#include <canonpy.h>

#include <Python.h>

#include <algorithm>
#include <string>
#include <vector>

#include <libcanon/perm.h>

using libcanon::Simple_perm;
using libcanon::Point;
using libcanon::Point_vec;

//
// Perm class
// ==========
//
// Internal functions
// ------------------
//
// These functions are not directly set to Python types but are used
// internally.  They are also used by other parts of this extension.
//

/** Builds a tuple object for a permutation.
 *
 * This function creates a pair, where the first field is a list of integers
 * for the preimage array, and the second field is the accompanied action,
 * which is also encoded as an integer.
 */

static PyObject* build_perm_to_tuple(const Simple_perm& perm)
{

    PyObject* pre_images = NULL;
    PyObject* res = NULL;
    PyObject* acc = NULL;
    size_t size = perm.size();

    pre_images = PyList_New(size);
    if (!pre_images)
        goto error;
    for (size_t i = 0; i < size; ++i) {
        PyObject* curr = Py_BuildValue("n", perm >> i);
        if (curr) {
            PyList_SetItem(pre_images, i, curr);
        } else {
            goto error;
        }
    }

    acc = Py_BuildValue("b", perm.acc());
    if (!acc)
        goto error;

    res = PyTuple_New(2);
    if (!res)
        goto error;

    PyTuple_SET_ITEM(res, 0, pre_images);
    PyTuple_SET_ITEM(res, 1, acc);

    return (PyObject*)res;

error:
    Py_XDECREF(pre_images);
    Py_XDECREF(res);
    Py_XDECREF(acc);
    return NULL;
}

/** Builds a permutation from its construction arguments.
 *
 * An iterable of positive integers for the pre-image array needs to be given
 * as the first argument.  The accompanied action can be optionally given as
 * another integral argument, or by the keyword ``acc``.
 *
 * If the arguments are not valid, an integer exception will be thrown and the
 * Python exception will be set.
 *
 * This function is designed to be compatible with the result from the function
 * `build_perm_to_tuple`.  However, more general input format is accepted.
 */

static Simple_perm make_perm_from_args(PyObject* args, PyObject* kwargs)
{
    PyObject* pre_images;
    char acc = 0;

    // We only need a simple internal code, actual error is set to the Python
    // stack.
    constexpr int err_code = 1;

    static char* kwlist[] = { "pre_images", "acc", NULL };

    auto args_stat = PyArg_ParseTupleAndKeywords(
        args, kwargs, "O|b", kwlist, &pre_images, &acc);

    if (!args_stat)
        throw err_code;

    Point_vec pre_images_vec{};
    std::vector<bool> image_set{};

    PyObject* pre_images_iter = PyObject_GetIter(pre_images);
    if (!pre_images_iter)
        throw err_code;

    // Iterator of pre-images is always going to be decrefed after the
    // following block.  This boolean controls if we are going to return or
    // throw.
    bool if_err = false;

    try {
        PyObject* pre_image_obj;
        while ((pre_image_obj = PyIter_Next(pre_images_iter))) {

            if (!PyLong_Check(pre_image_obj)) {
                throw err_code;
            }
            Point pre_image = PyLong_AsUnsignedLong(pre_image_obj);

            // Release reference right here since its content is already
            // extracted.  In this way, the error handling does not need to
            // worry about it any more.
            Py_DECREF(pre_image_obj);

            if (PyErr_Occurred()) {
                throw err_code;
            }

            pre_images_vec.push_back(pre_image);
            size_t req_size = pre_image + 1;
            if (image_set.size() < req_size)
                image_set.resize(req_size, false);
            if (image_set[pre_image]) {
                std::string err_msg("The image of ");
                err_msg.append(std::to_string(pre_image));
                err_msg.append(" has already been set.");
                PyErr_SetString(PyExc_ValueError, err_msg.c_str());
                throw err_code;
            } else {
                image_set[pre_image] = true;
            }
        }

        // Non StopIteration error.
        if (PyErr_Occurred()) {
            throw err_code;
        }

        auto first_not_set
            = std::find(image_set.begin(), image_set.end(), false);
        if (first_not_set != image_set.end()) {
            std::string err_msg("The image of ");
            err_msg.append(std::to_string(first_not_set - image_set.begin()));
            err_msg.append(" is not set.");
            throw err_code;
        }

    } catch (int) {
        if_err = true;
    }

    Py_DECREF(pre_images_iter);
    if (if_err) {
        throw err_code;
    } else {
        return Simple_perm(std::move(pre_images_vec), acc);
    }
}

//
// Interface functions
// -------------------
//

const static char* perm_getnewargs_doc
    = "Get the arguments for new to construct the Perm.";

static PyObject* perm_getnewargs(Perm_object* self)
{
    // Here we directly use the tuple format of a perm.

    return build_perm_to_tuple(self->perm);
}

/** Gets the size of the permutation domain of a Perm.
 */

static Py_ssize_t perm_length(Perm_object* self)
{
    // The type should be the same, size_t and Py_ssize_t.

    return self->perm.size();
}

/** Gets the pre-image of a point.
 *
 * A new integer object will be built by this function.
 */

static PyObject* perm_item(Perm_object* self, Py_ssize_t i)
{
    if (i < 0) {
        PyErr_SetString(PyExc_IndexError, "Points should be positive.");
        return NULL;
    }
    size_t idx = i;
    if (idx >= self->perm.size()) {
        PyErr_SetString(PyExc_IndexError, "Point outside permutation domain");
        return NULL;
    }

    return Py_BuildValue("n", self->perm >> i);
}

/** Gets the accompanied action of a Perm.
 */

static PyObject* perm_get_acc(Perm_object* self, void* closure)
{
    // Note that the accompanied action is a byte in simple perm.

    return Py_BuildValue("b", self->perm.acc());
}

/** Deallocates a perm instance.
 */

static void perm_dealloc(Perm_object* self)
{
    self->perm.~Simple_perm();

    // For subclassing.
    Py_TYPE(self)->tp_free((PyObject*)self);
}

/** Forms the string representation of a Perm object.
 */

static PyObject* perm_repr(Perm_object* self)
{
    const Simple_perm& perm = self->perm;

    std::wstring repr(L"Perm(");

    size_t size = perm.size();

    if (size > 0) {
        for (size_t i = 0; i < size; ++i) {
            if (i == 0) {
                repr.append(L"[");
            } else {
                repr.append(L", ");
            }
            repr.append(std::to_wstring(perm >> i));
        }
        repr.append(L"]");

        // Add the accompanied action only when we need.
        char acc = perm.acc();
        if (acc != 0) {
            repr.append(L", ");
            repr.append(std::to_wstring(acc));
        }
    }

    // This is used for empty or non-empty permutation.
    repr.append(L")");

    return PyUnicode_FromUnicode(repr.data(), repr.size());
}

/** Creates a new Perm object.
 *
 * The actual work is delegated to the Python/Perm interface function.
 */

static PyObject* perm_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
    Perm_object* self;

    // Pay attention to subclassing.
    self = (Perm_object*)type->tp_alloc(type, 0);

    if (!self)
        return NULL;

    Simple_perm perm{};
    try {
        perm = make_perm_from_args(args, kwargs);
    } catch (int) {
        Py_DECREF(self);
        return NULL;
    }

    new (&self->perm) Simple_perm(std::move(perm));
    return (PyObject*)self;
}

//
// Class definition
// ----------------
//

/** Methods for Perm objects.
 */

static PyMethodDef perm_methods[] = {
    { "__getnewargs__", (PyCFunction)perm_getnewargs, METH_NOARGS,
        perm_getnewargs_doc },
    { NULL, NULL } /* sentinel */
};

/** Sequence operations for Perm objects.
 *
 * Here we only support size and pre-image query.
 */

// clang-format off
static PySequenceMethods perm_as_sequence = {
    (lenfunc)perm_length,                       /* sq_length */
    0,                                          /* sq_concat */
    0,                                          /* sq_repeat */
    (ssizeargfunc)perm_item,                    /* sq_item */
    0,                                          /* sq_slice */
    0,                                          /* sq_ass_item */
    0,                                          /* sq_ass_slice */
    0                                           /* sq_contains */
};
// clang-format on

/** Accessors for Perms.
 *
 * The accompanied action query is made here.
 */

// clang-format off
static PyGetSetDef perm_getsets[] = {
    { "acc", (getter)perm_get_acc, NULL, "The accompanied action.", NULL },
    { NULL }
};
// clang-format on

/** Perm type doc string.
  */

static const char* perm_doc =
    R"__doc__(Permutation of points with accompanied action.

Permutations can be constructed from an iterable giving the pre-image of the
points and an optional integral value for the accompanied action.  The
accompanied action can be given positionally or by the keyword ``acc``, and it
will be manipulated according to the convention in libcanon.

Querying the length of a Perm object gives the size of the permutation domain,
while indexing it gives the pre-image of the given integral point.  The
accompanied action can be obtained by getting the attribute ``acc``.
Otherwise, this data type is mostly opaque.

)__doc__";

/** Type definition for Perm class.
 */

// clang-format off
static PyTypeObject perm_type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "drudge.canonpy.Perm",
    sizeof(Perm_object),
    0,
    (destructor) perm_dealloc,                  /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc) perm_repr,                       /* tp_repr */
    0,                                          /* tp_as_number */
    &perm_as_sequence,                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0, /* In main. */                           /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
    perm_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    perm_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    perm_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    perm_new,                                   /* tp_new */
    0,                                          /* tp_free */
};
// clang-format on

//
// Python module definition
// ========================
//

/** Docstring for the canonpy module.
 */

static const char* canonpy_docstring
    = R"__doc__(Canonpy, simple wrapper over libcanon for Python.

This wrapper of libcanon is directly targeted towards usage using drudge.
Here, we have a class `Perm`, which wraps over the `Simple_perm` class in
libcanon, another class `SimsTransv`, which wraps over the `Sims_trasv` class
for `Simple_perm`.  And we also have the function `canon_eldag` to canonicalize
an Eldag.

)__doc__";

/** Methods in the canonpy module.
 */

static PyMethodDef canonpy_methods[] = { { NULL, NULL, 0, NULL } };

/** Executes the initialization of the canonpy module.
 */

static int canonpy_exec(PyObject* m)
{
    //
    // Add the class for Perm.
    //

    perm_type.tp_getattro = PyObject_GenericGetAttr;
    if (PyType_Ready(&perm_type) < 0)
        return -1;
    Py_INCREF(&perm_type);
    PyModule_AddObject(m, "Perm", (PyObject*)&perm_type);

    return 0;
}

/** Slots for for canonpy module definition.
 */

static struct PyModuleDef_Slot canonpy_slots[] = {
    { Py_mod_exec, (void*)canonpy_exec }, { 0, NULL },
};

/** Canonpy module definition.
 */

// clang-format off

static struct PyModuleDef canonpy_module = {
    PyModuleDef_HEAD_INIT,
    "drudge.canonpy",
    canonpy_docstring,
    0, // m-size
    canonpy_methods,
    canonpy_slots,
    NULL, // Transverse
    NULL, // Clear
    NULL  // Free
};

// clang-format on

/** The published canonpy function.
 */

PyMODINIT_FUNC PyInit_canonpy(void)
{
    return PyModuleDef_Init(&canonpy_module);
}
<|endoftext|>


<commit_before>#ifndef MEMCACHED_PROTOCOL_JSON_ADAPTER_TCC_
#define MEMCACHED_PROTOCOL_JSON_ADAPTER_TCC_

#include "memcached/protocol_json_adapter.hpp"

#include <exception>

#include "http/http.hpp"
#include "http/json.hpp"

//json adapter concept for `store_key_t`
template <class ctx_t>
typename json_adapter_if_t<ctx_t>::json_adapter_map_t get_json_subfields(store_key_t *, const ctx_t &) {
    return typename json_adapter_if_t<ctx_t>::json_adapter_map_t();
}

template<class ctx_t>
cJSON *render_as_json(store_key_t *target, const ctx_t &) {
    return cJSON_CreateString(percent_escaped_string(key_to_unescaped_str(*target)).c_str());
}

template <class ctx_t>
void apply_json_to(cJSON *change, store_key_t *target, const ctx_t &) {
    std::string str(percent_unescaped_string(get_string(change)));
    if (!unescaped_str_to_key(str.c_str(), str.length(), target)) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a store_key_t.\n", get_string(change).c_str()));
    }
}

template <class ctx_t>
void  on_subfield_change(store_key_t *, const ctx_t &) { }

// json adapter for key_range_t
template <class ctx_t>
typename json_adapter_if_t<ctx_t>::json_adapter_map_t get_json_subfields(key_range_t *, const ctx_t &) {
    return typename json_adapter_if_t<ctx_t>::json_adapter_map_t();
}

template <class ctx_t>
cJSON *render_as_json(key_range_t *target, const ctx_t &c) {
    scoped_cJSON_t res(cJSON_CreateArray());

    cJSON_AddItemToArray(res.get(), render_as_json(&target->left, c));

    if (!target->right.unbounded) {
        cJSON_AddItemToArray(res.get(), render_as_json(&target->right.key, c));
    } else {
        cJSON_AddItemToArray(res.get(), cJSON_CreateNull());
    }

    std::string val = cJSON_print_std_string(res.get());
    return cJSON_CreateString(val.c_str());
}

template <class ctx_t>
void apply_json_to(cJSON *change, key_range_t *target, const ctx_t &c) {
    scoped_cJSON_t js(cJSON_Parse(get_string(change).c_str()));
    if (js.get() == NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a memcached_protocol_t::region_t.\n", get_string(change).c_str()));
    }

    /* TODO: If something other than an array is passed here, then it will crash
    rather than report the error to the user. */
    json_array_iterator_t it(js.get());

    cJSON *first = it.next();
    if (first == NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a memcached_protocol_t::region_t.\n", get_string(change).c_str()));
    }

    cJSON *second = it.next();
    if (second == NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a memcached_protocol_t::region_t.\n", get_string(change).c_str()));
    }

    if (it.next() != NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a memcached_protocol_t::region_t.\n", get_string(change).c_str()));
    }

    try {
        store_key_t left;
        apply_json_to(first, &left, c);
        if (second->type == cJSON_NULL) {
            *target = key_range_t(key_range_t::closed, left,
                                  key_range_t::none,   store_key_t(""));
        } else {
            store_key_t right;
            apply_json_to(second, &right, c);
            *target = key_range_t(key_range_t::closed, left,
                                  key_range_t::open,   right);
        }
    } catch (std::runtime_error) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a memcached_protocol_t::region_t.\n", get_string(change).c_str()));
    }
}

template <class ctx_t>
void  on_subfield_change(key_range_t *, const ctx_t &) { }

// json adapter for hash_region_t<key_range_t>
// TODO(sam): I don't know what the heck I'm doing.

template <class ctx_t>
typename json_adapter_if_t<ctx_t>::json_adapter_map_t get_json_subfields(hash_region_t<key_range_t> *target, const ctx_t &) {
    typename json_adapter_if_t<ctx_t>::json_adapter_map_t ret;
    ret["beg"] = boost::shared_ptr<json_adapter_if_t<ctx_t> >(new json_adapter_t<uint64_t, ctx_t>(&target->beg));
    ret["end"] = boost::shared_ptr<json_adapter_if_t<ctx_t> >(new json_adapter_t<uint64_t, ctx_t>(&target->end));
    ret["key_range"] = boost::shared_ptr<json_adapter_if_t<ctx_t> >(new json_adapter_t<key_range_t, ctx_t>(&target->inner));
    return ret;
}

template <class ctx_t>
cJSON *render_as_json(hash_region_t<key_range_t> *target, const ctx_t &c) {
    return render_as_directory(target, c);
}

template <class ctx_t>
void apply_json_to(cJSON *change, hash_region_t<key_range_t> *target, const ctx_t &ctx) {
    apply_as_directory(change, target, ctx);
}

template <class ctx_t>
void on_subfield_change(hash_region_t<key_range_t> *, const ctx_t &) { }

#endif  // MEMCACHED_PROTOCOL_JSON_ADAPTER_TCC_
<commit_msg>Made hash_region_t json adapter just do assertion that there is no hash sharding.<commit_after>#ifndef MEMCACHED_PROTOCOL_JSON_ADAPTER_TCC_
#define MEMCACHED_PROTOCOL_JSON_ADAPTER_TCC_

#include "memcached/protocol_json_adapter.hpp"

#include <exception>

#include "http/http.hpp"
#include "http/json.hpp"

//json adapter concept for `store_key_t`
template <class ctx_t>
typename json_adapter_if_t<ctx_t>::json_adapter_map_t get_json_subfields(store_key_t *, const ctx_t &) {
    return typename json_adapter_if_t<ctx_t>::json_adapter_map_t();
}

template<class ctx_t>
cJSON *render_as_json(store_key_t *target, const ctx_t &) {
    return cJSON_CreateString(percent_escaped_string(key_to_unescaped_str(*target)).c_str());
}

template <class ctx_t>
void apply_json_to(cJSON *change, store_key_t *target, const ctx_t &) {
    std::string str(percent_unescaped_string(get_string(change)));
    if (!unescaped_str_to_key(str.c_str(), str.length(), target)) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a store_key_t.\n", get_string(change).c_str()));
    }
}

template <class ctx_t>
void  on_subfield_change(store_key_t *, const ctx_t &) { }

// json adapter for key_range_t
template <class ctx_t>
typename json_adapter_if_t<ctx_t>::json_adapter_map_t get_json_subfields(key_range_t *, const ctx_t &) {
    return typename json_adapter_if_t<ctx_t>::json_adapter_map_t();
}

template <class ctx_t>
cJSON *render_as_json(key_range_t *target, const ctx_t &c) {
    scoped_cJSON_t res(cJSON_CreateArray());

    cJSON_AddItemToArray(res.get(), render_as_json(&target->left, c));

    if (!target->right.unbounded) {
        cJSON_AddItemToArray(res.get(), render_as_json(&target->right.key, c));
    } else {
        cJSON_AddItemToArray(res.get(), cJSON_CreateNull());
    }

    std::string val = cJSON_print_std_string(res.get());
    return cJSON_CreateString(val.c_str());
}

template <class ctx_t>
void apply_json_to(cJSON *change, key_range_t *target, const ctx_t &c) {
    // TODO: Can we so casually call get_string on a cJSON object?  What if it's not a string?
    scoped_cJSON_t js(cJSON_Parse(get_string(change).c_str()));
    if (js.get() == NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a key_range_t.", get_string(change).c_str()));
    }

    /* TODO: If something other than an array is passed here, then it will crash
    rather than report the error to the user. */
    json_array_iterator_t it(js.get());

    cJSON *first = it.next();
    if (first == NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a key_range_t.", get_string(change).c_str()));
    }

    cJSON *second = it.next();
    if (second == NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a key_range_t.", get_string(change).c_str()));
    }

    if (it.next() != NULL) {
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a key_range_t.", get_string(change).c_str()));
    }

    try {
        store_key_t left;
        apply_json_to(first, &left, c);
        if (second->type == cJSON_NULL) {
            *target = key_range_t(key_range_t::closed, left,
                                  key_range_t::none,   store_key_t(""));
        } else {
            store_key_t right;
            apply_json_to(second, &right, c);
            *target = key_range_t(key_range_t::closed, left,
                                  key_range_t::open,   right);
        }
    } catch (std::runtime_error) {  // TODO Explain wtf can throw a std::runtime_error to us here.
        throw schema_mismatch_exc_t(strprintf("Failed to parse %s as a memcached_protocol_t::region_t.\n", get_string(change).c_str()));
    }
}

template <class ctx_t>
void  on_subfield_change(key_range_t *, const ctx_t &) { }

// json adapter for hash_region_t<key_range_t>

// TODO: This is extremely ghetto: we assert that the hash region isn't split by hash value (because why should the UI ever be exposed to that?) and then only serialize the key range.

template <class ctx_t>
typename json_adapter_if_t<ctx_t>::json_adapter_map_t get_json_subfields(UNUSED hash_region_t<key_range_t> *target, UNUSED const ctx_t &) {
    return typename json_adapter_if_t<ctx_t>::json_adapter_map_t();
}

template <class ctx_t>
cJSON *render_as_json(hash_region_t<key_range_t> *target, const ctx_t &c) {
    // TODO: ghetto low level hash_region_t assertion.
    guarantee(target->beg == 0 && target->end == HASH_REGION_HASH_SIZE);

    return render_as_json(&target->inner, c);
}

template <class ctx_t>
void apply_json_to(cJSON *change, hash_region_t<key_range_t> *target, const ctx_t &ctx) {
    target->beg = 0;
    target->end = HASH_REGION_HASH_SIZE;
    apply_json_to(change, &target->inner, ctx);
}

template <class ctx_t>
void on_subfield_change(hash_region_t<key_range_t> *, const ctx_t &) { }

#endif  // MEMCACHED_PROTOCOL_JSON_ADAPTER_TCC_
<|endoftext|>


<commit_before><commit_msg>add cl.hpp<commit_after><|endoftext|>


<commit_before>
/* vim: set shiftwidth=4 tabstop=4: */

#include "WorkerThreadPool.h"
#include "WTPExceptions.h"

#include <pthread.h>
#include <string>
#include <iostream>

using namespace std;
using namespace WTP;

static void *thread_start(void *arg)
{
    WorkerThreadPool *wtp = (WorkerThreadPool *)arg;
    wtp->_threadStart();

    return NULL;
}

WTP::WorkerThreadPool::WorkerThreadPool(int nthreads) :
                   _nthreads(nthreads), _nqueued(0), _nprocessing(0),
                    _totalItems(0), _shuttingDown(false), _gen(0)
{
    int rc = pthread_mutex_init(&_mutex, NULL);
    if (rc != 0)
        throw InternalError("Failed to initialize mutex", __FILE__, __LINE__);
    rc = pthread_cond_init(&_cond, NULL);
    if (rc != 0)
        throw InternalError("Failed to initialize conditional variable",
                            __FILE__, __LINE__);
    rc = pthread_cond_init(&_empty_cond, NULL);
    if (rc != 0)
        throw InternalError("Failed to initialize conditional variable",
                            __FILE__, __LINE__);

    WTPqueue defqueue;
    queuelist.push_back(defqueue);
    _threadpool = new pthread_t[nthreads];
}

void WTP::WorkerThreadPool::startThreads()
{
    int i;
    for (i=0; i<_nthreads; i++) {
        pthread_t thread;
        int rc = pthread_create(&thread, NULL, thread_start, (void *)this);
        if (rc) {
            throw InternalError("Thread creation failed", __FILE__, __LINE__);
        }
        _threadpool[i] = thread;
    }
}

bool WTP::WorkerThreadPool::isEmpty()
{
    lock();
    int ret = _totalItems;
    unlock();
    return (ret == 0);

}

void WTP::WorkerThreadPool::shutDown()
{
    lock();
    _shuttingDown = true;
    pthread_cond_broadcast(&_cond);
    unlock();
    // Wait for all threads to die.
    int i;
    for (i=0; i< _nthreads; i++) {
        pthread_join(_threadpool[i], NULL);
    }

    // No more worker threads running. We don't need the lock now.
    // Drain and free up queues.

    while(!queuelist.empty()) {
        WTPqueue& q = queuelist.back();
        q.drain();
        queuelist.pop_back();
    }

}

void WTP::WorkerThreadPool::_threadStart()
{
    uint32_t total_items = 0;
    while (1) {
        lock();  /////////////// Begin critical region /////////////////
        if(_shuttingDown == true) {
            unlock();
            return;
        }

        unsigned int qid;
        WTPqueue* pqueue = _getNextQueue(qid);
        if (pqueue == NULL) {
            pthread_cond_wait(&_cond, &_mutex);
            if(_shuttingDown == true) {
                unlock();
                return;
            }
            unlock();
            continue;
        }

        WorkItem *wi = pqueue->front(); pqueue->pop();
        _nqueued--; _nprocessing++;
        wi->incrRefcount();
        pqueue->deliverEvent(WTPqueue::WI_START);

        unlock(); /////////////// End critical region /////////////////

        if (wi->getState() != WorkItem::QUEUED)
            throw CallerError("Workitem not in queued state"); // Caller added an illegal WorkItem
        wi->setState(WorkItem::RUNNING);
        wi->setThreadPool(this);
        wi->setQID(qid);

        wi->run(); // Need to catch Exceptions. TBD.

        wi->setThreadPool(NULL);
        wi->setState(WorkItem::IDLE);

        lock(); /////////////// Begin critical region /////////////////
        _nprocessing--;

        pqueue->deliverEvent(WTPqueue::WI_END);
        int refcount = wi->decrRefcount();
        if (refcount == 0) {
            decrTotalItems();
            pqueue->deliverEvent(WTPqueue::ALL_DONE);
        }

        WorkItem *parent = NULL;
        if (wi->isSubItem()) {
            parent = wi->getParent();
            int prefcount = parent->decrRefcount();
            unsigned int pqid = parent->getQID();
            WTPqueue& parentQueue = getQueue(pqid);
            parentQueue.deliverEvent(WTPqueue::SI_END);
            if (prefcount == 0) {
                decrTotalItems();
                parentQueue.deliverEvent(WTPqueue::ALL_DONE);
            }
            else
                parent = NULL;
        }

        if (refcount > 0)
            wi = NULL; // Don't deallocate a WI with subitems.

        total_items = _totalItems;
        pthread_cond_signal(&_cond);
        unlock(); /////////////// End critical region /////////////////

        completeItem(wi); // Note, wi may be NULL. This is OK.
        completeItem(parent);

        if (total_items == 0) {
            lock();
            pthread_cond_signal(&_empty_cond);
            unlock();
        }
    }
}

void WTP::WorkerThreadPool::completeItem(WorkItem *wi)
{
    if (wi == NULL)
        return;
    wi->subItemsComplete(); // This is a caller-defined function

    // cout << "Deallocating workitem " << wi << endl;
    if (wi->fromFreelist()) {
        wi->reset();
        wi->setState(WorkItem::FREELIST);
        wi->returnToFreelist();
    } else {
        wi->setState(WorkItem::INVALID);
        delete wi;
    }

}

void WTP::WorkerThreadPool::waitEmpty()
{
    lock();
    if (_threadIsInternal()) {
        unlock();
        throw CallerError("waitEmpty() called from within WorkItem.");
    }
    if (_nqueued == 0 and _nprocessing == 0) {
        unlock();
        return;
    }
    pthread_cond_wait(&_empty_cond, &_mutex);
    unlock();
}

WTPqueue& WTP::WorkerThreadPool::getQueue(unsigned int qid)
{
    unsigned int size = queuelist.size();
    if (qid >= size)
        throw CallerError("Illegal queue ID specified");
    return queuelist.at(qid);
}

WTPqueue* WTP::WorkerThreadPool::_getNextQueue(unsigned int& qid)
{
    int size = queuelist.size();
    int i;
    for (i=0; i<size; i++) {
        WTPqueue& q = queuelist.at(i);
        if(!q.empty() ) {
            if (i == 0) {
                qid = i;
                return &q;
            } else {
                if (q.getState() == WTPqueue::IDLE) {
                    qid = i;
                    return &q;
                }
            }
        }
    }

    return NULL;
}

/*
 * Pause the donor queue. Subitems can only be added to parallel queue.
 */
void WTP::WorkerThreadPool::_addSubItem(WorkItem *item, WorkItem *parent)
{
    unsigned int donorQid = parent->getQID();
    if (donorQid == 0)
        throw CallerError("Attempt to create subitems from parallel queue");
    lock();
    WTPqueue& donorQ = queuelist.at(donorQid);
    donorQ.setWaitingWI(parent);
    donorQ.deliverEvent(WTPqueue::SI_START);
    unlock();
    addWorkItem(item, 0); // Add to parallel queue.
}

void WTP::WorkerThreadPool::addWorkItem(WorkItem *item, unsigned int qid)
{
    if (item->getState() != WorkItem::IDLE)
        throw CallerError("Attempt to add non-idle WorkItem to threadpool");
    lock();
    // cout << "Added workitem " << item << endl;
    unsigned int size = queuelist.size();
    if (qid >= size) {
        unlock();
        throw CallerError("Attempt to add WorkItem to illegal queue ID");
    }
    WTPqueue& pqueue = queuelist.at(qid);
    item->setState(WorkItem::QUEUED);
    item->setGen(createGen());
    pqueue.push(item);
    _nqueued++;
    incrTotalItems();

    pthread_cond_signal(&_cond);
    unlock();
}

void WTP::WorkerThreadPool::incrTotalItems()
{
    _totalItems++;
    // cout << "Total Items=" << _totalItems << endl;
}

void WTP::WorkerThreadPool::decrTotalItems()
{
    _totalItems--;
    // cout << "Total Items=" << _totalItems << endl;
}

unsigned int WTP::WorkerThreadPool::addQueue()
{
    WTPqueue newqueue;
    lock();
    int qid = queuelist.size();
    newqueue.setQID(qid);
    queuelist.push_back(newqueue);
    unlock();
    return qid;
}

bool WTP::WorkerThreadPool::_threadIsInternal()
{
    pthread_t mine = pthread_self();
    int i;
    for (i=0; i<_nthreads; i++) {
        if (_threadpool[i] == mine)
            return true;
    }
    return false;
}

void WorkerThreadPool::lock()
{
   int rc = pthread_mutex_lock(&_mutex);
   if (rc != 0)
       throw InternalError("Failed to lock mutex", __FILE__, __LINE__);
}

void WorkerThreadPool::unlock()
{
       int rc = pthread_mutex_unlock(&_mutex);
       if (rc != 0)
		   throw InternalError("Failed to unlock mutex", __FILE__, __LINE__);
}

uint32_t WorkerThreadPool::getTotalItems()
{
    uint32_t ret = 0;
    lock();
    ret = _totalItems;
    unlock();
    return ret;
}

uint32_t WorkerThreadPool::getTotalProcessing()
{
    uint32_t ret = 0;
    lock();
    ret = _nprocessing;
    unlock();
    return ret;
}

uint32_t WorkerThreadPool::getTotalQueued()
{
    uint32_t ret = 0;
    lock();
    ret = _nqueued;
    unlock();
    return ret;
}

void WTPqueue::drain()
{
    while(!this->empty()) {
        WorkItem *wi = this->front(); this->pop();
        if (wi->fromFreelist()) {
            wi->reset();
            wi->setState(WorkItem::FREELIST);
            wi->returnToFreelist();
        } else {
            delete wi;
        }
    }
}

void WTPqueue::deliverEvent(enum QueueEvent evt)
{
    if (_qid == 0)
        return;  // No states for the concurrent queue.
    bool illegal = false;
    // cout << "Event " << evt << " caused queue " << _qid << " to transition from " << _state << " to ";
    switch (evt) {
    case WI_START:
        if (_state == IDLE)
            _state = PROCESSING;
        else
            illegal = true;
        break;
    case SI_START:
        if (_state == PROCESSING or _state == PROCESSING_SI)
            _state = PROCESSING_SI;
        else
            illegal = true;
        break;
    case WI_END:
        if (_state == PROCESSING)
            _state = IDLE;
        else if (_state == PROCESSING_SI)
            _state = PAUSED;
        else
            illegal = true;

        break;
    case SI_END:
        if (_state == IDLE or _state == PROCESSING)
            illegal = true;
        break;
    case ALL_DONE:
        _state = IDLE;
        break;
    default:
        throw InternalError("Unhandled event to WTPqueue", __FILE__, __LINE__);
    }
    // cout << _state << endl;
    if (illegal)
        throw InternalError("Illegal state transition to WTPqueue", __FILE__, __LINE__);

}
<commit_msg>Some cleanup in WorkerThreadPool.cpp<commit_after>
/* vim: set shiftwidth=4 tabstop=4: */

#include "WorkerThreadPool.h"
#include "WTPExceptions.h"

#include <pthread.h>
#include <string>
#include <iostream>

using namespace std;
using namespace WTP;

static void *thread_start(void *arg)
{
    WorkerThreadPool *wtp = (WorkerThreadPool *)arg;
    wtp->_threadStart();

    return NULL;
}

WTP::WorkerThreadPool::WorkerThreadPool(int nthreads) :
                   _nthreads(nthreads), _nqueued(0), _nprocessing(0),
                    _totalItems(0), _shuttingDown(false), _gen(0)
{
    int rc = pthread_mutex_init(&_mutex, NULL);
    if (rc != 0)
        throw InternalError("Failed to initialize mutex", __FILE__, __LINE__);
    rc = pthread_cond_init(&_cond, NULL);
    if (rc != 0)
        throw InternalError("Failed to initialize conditional variable",
                            __FILE__, __LINE__);
    rc = pthread_cond_init(&_empty_cond, NULL);
    if (rc != 0)
        throw InternalError("Failed to initialize conditional variable",
                            __FILE__, __LINE__);

    WTPqueue defqueue;
    queuelist.push_back(defqueue);
    _threadpool = new pthread_t[nthreads];
}

void WTP::WorkerThreadPool::startThreads()
{
    int i;
    for (i=0; i<_nthreads; i++) {
        pthread_t thread;
        int rc = pthread_create(&thread, NULL, thread_start, (void *)this);
        if (rc) {
            throw InternalError("Thread creation failed", __FILE__, __LINE__);
        }
        _threadpool[i] = thread;
    }
}

bool WTP::WorkerThreadPool::isEmpty()
{
    lock();
    int ret = _totalItems;
    unlock();
    return (ret == 0);

}

void WTP::WorkerThreadPool::shutDown()
{
    lock();
    _shuttingDown = true;
    pthread_cond_broadcast(&_cond);
    unlock();
    // Wait for all threads to die.
    int i;
    for (i=0; i< _nthreads; i++) {
        pthread_join(_threadpool[i], NULL);
    }

    // No more worker threads running. We don't need the lock now.
    // Drain and free up queues.

    while(!queuelist.empty()) {
        WTPqueue& q = queuelist.back();
        q.drain();
        queuelist.pop_back();
    }

}

void WTP::WorkerThreadPool::_threadStart()
{
    uint32_t total_items = 0;
    while (1) {
        lock();  /////////////// Begin critical region /////////////////

        unsigned int qid = 0;
        WTPqueue* pqueue = NULL;
        while ((pqueue = _getNextQueue(qid)) == NULL && !_shuttingDown) {
            pthread_cond_wait(&_cond, &_mutex);
        }

        if(_shuttingDown == true) {
            unlock();
            return;
        }

        WorkItem *wi = pqueue->front(); pqueue->pop();
        _nqueued--; _nprocessing++;
        wi->incrRefcount();
        pqueue->deliverEvent(WTPqueue::WI_START);

        unlock(); /////////////// End critical region /////////////////

        if (wi->getState() != WorkItem::QUEUED)
            throw CallerError("Workitem not in queued state"); // Caller added an illegal WorkItem
        wi->setState(WorkItem::RUNNING);
        wi->setThreadPool(this);
        wi->setQID(qid);

        wi->run(); // Need to catch Exceptions. TBD.

        wi->setThreadPool(NULL);
        wi->setState(WorkItem::IDLE);

        lock(); /////////////// Begin critical region /////////////////
        _nprocessing--;

        pqueue->deliverEvent(WTPqueue::WI_END);
        int refcount = wi->decrRefcount();
        if (refcount == 0) {
            decrTotalItems();
            pqueue->deliverEvent(WTPqueue::ALL_DONE);
        }

        WorkItem *parent = NULL;
        if (wi->isSubItem()) {
            parent = wi->getParent();
            int prefcount = parent->decrRefcount();
            unsigned int pqid = parent->getQID();
            WTPqueue& parentQueue = getQueue(pqid);
            parentQueue.deliverEvent(WTPqueue::SI_END);
            if (prefcount == 0) {
                decrTotalItems();
                parentQueue.deliverEvent(WTPqueue::ALL_DONE);
            }
            else
                parent = NULL;
        }

        if (refcount > 0)
            wi = NULL; // Don't deallocate a WI with subitems.

        total_items = _totalItems;
        pthread_cond_signal(&_cond);
        unlock(); /////////////// End critical region /////////////////

        completeItem(wi); // Note, wi may be NULL. This is OK.
        completeItem(parent);

        if (total_items == 0) {
            lock();
            pthread_cond_signal(&_empty_cond);
            unlock();
        }
    }
}

void WTP::WorkerThreadPool::completeItem(WorkItem *wi)
{
    if (wi == NULL)
        return;
    wi->subItemsComplete(); // This is a caller-defined function

    // cout << "Deallocating workitem " << wi << endl;
    if (wi->fromFreelist()) {
        wi->reset();
        wi->setState(WorkItem::FREELIST);
        wi->returnToFreelist();
    } else {
        wi->setState(WorkItem::INVALID);
        delete wi;
    }

}

void WTP::WorkerThreadPool::waitEmpty()
{
    lock();
    if (_threadIsInternal()) {
        unlock();
        throw CallerError("waitEmpty() called from within WorkItem.");
    }
    if (_nqueued == 0 and _nprocessing == 0) {
        unlock();
        return;
    }
    pthread_cond_wait(&_empty_cond, &_mutex);
    unlock();
}

WTPqueue& WTP::WorkerThreadPool::getQueue(unsigned int qid)
{
    unsigned int size = queuelist.size();
    if (qid >= size)
        throw CallerError("Illegal queue ID specified");
    return queuelist.at(qid);
}

WTPqueue* WTP::WorkerThreadPool::_getNextQueue(unsigned int& qid)
{
    int size = queuelist.size();
    int i;
    for (i=0; i<size; i++) {
        WTPqueue& q = queuelist.at(i);
        if(!q.empty() ) {
            if (i == 0) {
                qid = i;
                return &q;
            } else {
                if (q.getState() == WTPqueue::IDLE) {
                    qid = i;
                    return &q;
                }
            }
        }
    }

    return NULL;
}

/*
 * Pause the donor queue. Subitems can only be added to parallel queue.
 */
void WTP::WorkerThreadPool::_addSubItem(WorkItem *item, WorkItem *parent)
{
    unsigned int donorQid = parent->getQID();
    if (donorQid == 0)
        throw CallerError("Attempt to create subitems from parallel queue");
    lock();
    WTPqueue& donorQ = queuelist.at(donorQid);
    donorQ.setWaitingWI(parent);
    donorQ.deliverEvent(WTPqueue::SI_START);
    unlock();
    addWorkItem(item, 0); // Add to parallel queue.
}

void WTP::WorkerThreadPool::addWorkItem(WorkItem *item, unsigned int qid)
{
    if (item->getState() != WorkItem::IDLE)
        throw CallerError("Attempt to add non-idle WorkItem to threadpool");
    lock();
    // cout << "Added workitem " << item << endl;
    unsigned int size = queuelist.size();
    if (qid >= size) {
        unlock();
        throw CallerError("Attempt to add WorkItem to illegal queue ID");
    }
    WTPqueue& pqueue = queuelist.at(qid);
    item->setState(WorkItem::QUEUED);
    item->setGen(createGen());
    pqueue.push(item);
    _nqueued++;
    incrTotalItems();

    pthread_cond_signal(&_cond);
    unlock();
}

void WTP::WorkerThreadPool::incrTotalItems()
{
    _totalItems++;
    // cout << "Total Items=" << _totalItems << endl;
}

void WTP::WorkerThreadPool::decrTotalItems()
{
    _totalItems--;
    // cout << "Total Items=" << _totalItems << endl;
}

unsigned int WTP::WorkerThreadPool::addQueue()
{
    WTPqueue newqueue;
    lock();
    int qid = queuelist.size();
    newqueue.setQID(qid);
    queuelist.push_back(newqueue);
    unlock();
    return qid;
}

bool WTP::WorkerThreadPool::_threadIsInternal()
{
    pthread_t mine = pthread_self();
    int i;
    for (i=0; i<_nthreads; i++) {
        if (_threadpool[i] == mine)
            return true;
    }
    return false;
}

void WorkerThreadPool::lock()
{
   int rc = pthread_mutex_lock(&_mutex);
   if (rc != 0)
       throw InternalError("Failed to lock mutex", __FILE__, __LINE__);
}

void WorkerThreadPool::unlock()
{
       int rc = pthread_mutex_unlock(&_mutex);
       if (rc != 0)
		   throw InternalError("Failed to unlock mutex", __FILE__, __LINE__);
}

uint32_t WorkerThreadPool::getTotalItems()
{
    uint32_t ret = 0;
    lock();
    ret = _totalItems;
    unlock();
    return ret;
}

uint32_t WorkerThreadPool::getTotalProcessing()
{
    uint32_t ret = 0;
    lock();
    ret = _nprocessing;
    unlock();
    return ret;
}

uint32_t WorkerThreadPool::getTotalQueued()
{
    uint32_t ret = 0;
    lock();
    ret = _nqueued;
    unlock();
    return ret;
}

void WTPqueue::drain()
{
    while(!this->empty()) {
        WorkItem *wi = this->front(); this->pop();
        if (wi->fromFreelist()) {
            wi->reset();
            wi->setState(WorkItem::FREELIST);
            wi->returnToFreelist();
        } else {
            delete wi;
        }
    }
}

void WTPqueue::deliverEvent(enum QueueEvent evt)
{
    if (_qid == 0)
        return;  // No states for the concurrent queue.
    bool illegal = false;
    // cout << "Event " << evt << " caused queue " << _qid << " to transition from " << _state << " to ";
    switch (evt) {
    case WI_START:
        if (_state == IDLE)
            _state = PROCESSING;
        else
            illegal = true;
        break;
    case SI_START:
        if (_state == PROCESSING or _state == PROCESSING_SI)
            _state = PROCESSING_SI;
        else
            illegal = true;
        break;
    case WI_END:
        if (_state == PROCESSING)
            _state = IDLE;
        else if (_state == PROCESSING_SI)
            _state = PAUSED;
        else
            illegal = true;

        break;
    case SI_END:
        if (_state == IDLE or _state == PROCESSING)
            illegal = true;
        break;
    case ALL_DONE:
        _state = IDLE;
        break;
    default:
        throw InternalError("Unhandled event to WTPqueue", __FILE__, __LINE__);
    }
    // cout << _state << endl;
    if (illegal)
        throw InternalError("Illegal state transition to WTPqueue", __FILE__, __LINE__);

}
<|endoftext|>