<commit_before>#include "Mesh.hxx"
#include "Functional.hxx"

#include "../ostream_vector.hxx"

// We convert the optimization problem into a regular linear
// programming problem.
//
// 1) Each polynomial in Block::polys adds two variables.  The
// weights of those polynomials are unbounded, while linear
// programming requires a strictly positive variable.  So we
// substitute W_n = w_n+ - w_n+, where both w_n+ and w_n- are
// strictly positive.  All blocks should have the same number of
// polynomials, so we only need to look at the first one.
//
// 2) Each constraint adds one 'slack' variable s_n.  There is one
// constraint per coordinate 'x', with each block having multiple,
// separate coordinates.
//
// 3) One more global variable delta, which gives the linear
// program something to minimize.
//
// This turns the problem
//
//   A_0 . W > 0
//   A_1 . W > 0
//   ...
//
// into
//
//   min delta
//
// where
//
//   A_0 . (w_+ - w_-) + delta - s_0 = 0
//   A_1 . (w_+ - w_-) + delta - s_1 = 0
//   ...
//   w_n+, w_n-, s_n, delta >= 0
//
// There is a constraint for every point that is sampled.  At the
// beginning, we sample the min and max for each block, so there are
// 2*num_blocks constraints.  In general
//
// => num_rows == num_constraints
// => num_columns == 2*num_weights + num_constraints + 1

std::vector<El::BigFloat> get_new_points(const Mesh &mesh);
El::BigFloat max_value(const Mesh &mesh);

void solve_LP(const El::Matrix<El::BigFloat> &A,
              const El::Matrix<El::BigFloat> &b,
              const El::Matrix<El::BigFloat> &c,
              std::vector<El::BigFloat> &weights);

int main(int argc, char **argv)
{
  El::Environment env(argc, argv);
  const int64_t precision(1024);
  El::gmp::SetPrecision(precision);

  Functional functional("test/toy_polys", "", false);
  size_t num_weights(functional.blocks.at(0).polys.size());

  const size_t num_blocks(functional.blocks.size());
  const El::BigFloat min_x(0.0), max_x(100.0);
  std::vector<El::BigFloat> scale(num_blocks);
  std::vector<El::BigFloat> weights(num_weights, 1);
  for(size_t block(0); block != num_blocks; ++block)
    {
      scale[block]
        = Abs(functional.blocks[block].eval_weighted(max_x, weights));
    }

  std::vector<std::vector<El::BigFloat>> points(num_blocks, {min_x}),
    new_points(num_blocks, {max_x});

  bool has_new_points(true);
  while(has_new_points)
    {
      has_new_points = false;
      size_t num_constraints(0);
      for(size_t block(0); block != num_blocks; ++block)
        {
          for(auto &point : new_points.at(block))
            {
              points.at(block).emplace_back(point);
            }
          num_constraints += points.at(block).size();
        }

      const size_t num_rows(num_constraints),
        num_columns(2 * weights.size() + num_constraints + 1);

      El::Matrix<El::BigFloat> A(num_rows, num_columns);
      El::Zero(A);

      size_t row(0);
      for(size_t block(0); block != num_blocks; ++block)
        {
          // One constraint per point
          for(size_t point(0); point != points.at(block).size(); ++point)
            {
              // delta term
              A(row, 0) = 1;
              // slack term
              A(row, 2 * weights.size() + row + 1) = -1;

              const El::BigFloat &x(points.at(block)[point]);
              for(size_t poly_index(0);
                  poly_index != functional.blocks.at(block).polys.size();
                  ++poly_index)
                {
                  A(row, 2 * poly_index + 1)
                    = functional.blocks.at(block).polys.at(poly_index)(x);
                  A(row, 2 * poly_index + 2) = -A(row, 2 * poly_index + 1);
                }
              ++row;
            }
        }

      El::Matrix<El::BigFloat> b(num_rows, 1), c(num_columns, 1);
      El::Zero(b);
      El::Zero(c);
      c(0, 0) = 1;

      // std::cout << "A: "
      //           << A.Height() << " "
      //           << A.Width() << " "
      //           << b.Height() << " "
      //           << b.Width() << " "
      //           << c.Height() << " "
      //           << c.Width() << " "
      //           << "\n";

      // El::Print(A, "A");
      // El::Print(b, "\nb");
      // El::Print(c, "\nc");
      // std::cout << "\n";

      solve_LP(A, b, c, weights);

      std::cout << "weight: " << weights << "\n";
      for(size_t block(0); block != num_blocks; ++block)
        {
          // std::cout.precision(precision / 3.3);
          // std::cout << "solve: " << points.at(block).size() << " " << weights
          //           << "\n";

          // 0.01 should be a small enough relative error so that we are
          // in the regime of convergence.  Then the error estimates will
          // work
          Mesh mesh(min_x, max_x,
                    [&](const El::BigFloat &x) {
                      return functional.blocks.at(block).eval_weighted(
                        x, weights);
                    },
                    0.01);
          new_points.at(block) = get_new_points(mesh);
          std::cout << "new: " << new_points.at(block) << "\n";
          scale.at(block) = max_value(mesh);
          has_new_points = has_new_points || !new_points.at(block).empty();
        }
    }
  // std::cout.precision(precision / 3.3);
  std::cout << "weights: " << weights << "\n";
}
<commit_msg>Use poles<commit_after>#include "Mesh.hxx"
#include "Functional.hxx"

#include "../ostream_vector.hxx"

// We convert the optimization problem into a regular linear
// programming problem.
//
// 1) Each polynomial in Block::polys adds two variables.  The
// weights of those polynomials are unbounded, while linear
// programming requires a strictly positive variable.  So we
// substitute W_n = w_n+ - w_n+, where both w_n+ and w_n- are
// strictly positive.  All blocks should have the same number of
// polynomials, so we only need to look at the first one.
//
// 2) Each constraint adds one 'slack' variable s_n.  There is one
// constraint per coordinate 'x', with each block having multiple,
// separate coordinates.
//
// 3) One more global variable delta, which gives the linear
// program something to minimize.
//
// This turns the problem
//
//   A_0 . W > 0
//   A_1 . W > 0
//   ...
//
// into
//
//   min delta
//
// where
//
//   A_0 . (w_+ - w_-) + delta - s_0 = 0
//   A_1 . (w_+ - w_-) + delta - s_1 = 0
//   ...
//   w_n+, w_n-, s_n, delta >= 0
//
// There is a constraint for every point that is sampled.  At the
// beginning, we sample the min and max for each block, so there are
// 2*num_blocks constraints.  In general
//
// => num_rows == num_constraints
// => num_columns == 2*num_weights + num_constraints + 1

std::vector<El::BigFloat> get_new_points(const Mesh &mesh);
El::BigFloat max_value(const Mesh &mesh);

void solve_LP(const El::Matrix<El::BigFloat> &A,
              const El::Matrix<El::BigFloat> &b,
              const El::Matrix<El::BigFloat> &c,
              std::vector<El::BigFloat> &weights);

int main(int argc, char **argv)
{
  El::Environment env(argc, argv);
  const int64_t precision(1024);
  El::gmp::SetPrecision(precision);

  // Functional functional("polys", "test/single_corr_poles");
  // Functional functional("test/single_corr_polys", "test/single_corr_poles");
  Functional functional("test/toy_polys", "");
  size_t num_weights(functional.blocks.at(0).polys.size());

  // for(auto &pole: functional.blocks[0].poles)
  //   {
  //     std::cout << "pole: " << pole << "\n";
  //   }

  // std::cout << "prefactor: "
  //           << (functional.prefactor(1.44) *
  //           functional.blocks[0].pole_prefactor(1.44)) << " "
  //           << functional.blocks.at(0).polys.at(0)(1.44) << " "
  //           << "\n";
  // // std::cout << functional.blocks.at(0).polys.at(0) << "\n";

  // for(size_t index(0); index<functional.blocks[0].polys.size(); ++index)
  //   {
  //     std::cout << index << " "
  //               << (functional.blocks.at(0).polys.at(index)(0.0)
  //                   * functional.prefactor(0.0)
  //                   * functional.blocks[0].pole_prefactor(0.0))
  //               << "\n";
  //   }
  // exit(0);

  const size_t num_blocks(functional.blocks.size());
  const El::BigFloat min_x(0.0), max_x(100.0);
  std::vector<El::BigFloat> scale(num_blocks);
  std::vector<El::BigFloat> weights(num_weights, 1);

  const El::BigFloat prefactor(functional.prefactor(max_x));
  for(size_t block(0); block != num_blocks; ++block)
    {
      scale[block] = Abs(
        prefactor * functional.blocks[block].eval_weighted(max_x, weights));
    }

  // const El::BigFloat scalar_gap(1.44);
  // std::vector<std::vector<El::BigFloat>> points(num_blocks,
  //                                               {min_x, scalar_gap}),
  std::vector<std::vector<El::BigFloat>> points(num_blocks,
                                                {min_x}),
    new_points(num_blocks, {max_x});

  bool has_new_points(true);
  while(has_new_points)
    {
      has_new_points = false;
      size_t num_constraints(0);
      for(size_t block(0); block != num_blocks; ++block)
        {
          for(auto &point : new_points.at(block))
            {
              points.at(block).emplace_back(point);
            }
          num_constraints += points.at(block).size();
        }

      const size_t num_rows(num_constraints),
        num_columns(2 * weights.size() + num_constraints + 1);

      El::Matrix<El::BigFloat> A(num_rows, num_columns);
      El::Zero(A);

      size_t row(0);
      for(size_t block(0); block != num_blocks; ++block)
        {
          // One constraint per point
          for(size_t point(0); point != points.at(block).size(); ++point)
            {
              // delta term
              A(row, 0) = 1;
              // slack term
              A(row, 2 * weights.size() + row + 1) = -1;

              const El::BigFloat &x(points.at(block)[point]);
              const El::BigFloat prefactor(
                functional.prefactor(x)
                * functional.blocks.at(block).pole_prefactor(x));
              for(size_t poly_index(0);
                  poly_index != functional.blocks.at(block).polys.size();
                  ++poly_index)
                {
                  A(row, 2 * poly_index + 1)
                    = prefactor
                      * functional.blocks.at(block).polys.at(poly_index)(x);
                  A(row, 2 * poly_index + 2) = -A(row, 2 * poly_index + 1);
                }
              ++row;
            }
        }

      El::Matrix<El::BigFloat> b(num_rows, 1), c(num_columns, 1);
      El::Zero(b);
      El::Zero(c);
      c(0, 0) = 1;

      // std::cout << "A: "
      //           << A.Height() << " "
      //           << A.Width() << " "
      //           << b.Height() << " "
      //           << b.Width() << " "
      //           << c.Height() << " "
      //           << c.Width() << " "
      //           << "\n";

      // El::Print(A, "A");
      // El::Print(b, "\nb");
      // El::Print(c, "\nc");
      // std::cout << "\n";

      solve_LP(A, b, c, weights);

      std::cout << "weight: " << weights << "\n";
      for(size_t block(0); block != num_blocks; ++block)
        {
          // std::cout.precision(precision / 3.3);
          // std::cout << "solve: " << points.at(block).size() << " " << weights
          //           << "\n";

          // 0.01 should be a small enough relative error so that we are
          // in the regime of convergence.  Then the error estimates will
          // work
          Mesh mesh(min_x, max_x,
                    [&](const El::BigFloat &x) {
                      return functional.prefactor(x)
                             * functional.blocks.at(block).eval_weighted(
                                 x, weights);
                    },
                    0.01);
          new_points.at(block) = get_new_points(mesh);
          std::cout << "new: " << block << " " << new_points.at(block) << "\n";
          scale.at(block) = max_value(mesh);
          has_new_points = has_new_points || !new_points.at(block).empty();
        }
    }
  // std::cout.precision(precision / 3.3);
  std::cout << "weights: " << weights << "\n";
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010-2012 Jeremy Lainé
 * Contact: http://code.google.com/p/qdjango/
 *
 * This file is part of the QDjango Library.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QSqlDriver>
#include <QSqlError>
#include <QSqlQuery>
#include <QStringList>
#include <QThread>

#include "QDjango.h"

static const char *connectionPrefix = "_qdjango_";

QMap<QString, QDjangoMetaModel> globalMetaModels = QMap<QString, QDjangoMetaModel>();
static QDjangoDatabase *globalDatabase = 0;
static bool globalDebugEnabled = false;

QDjangoDatabase::QDjangoDatabase(QObject *parent)
    : QObject(parent), connectionId(0)
{
}

void QDjangoDatabase::threadFinished()
{
    QThread *thread = qobject_cast<QThread*>(sender());
    if (!thread)
        return;

    // cleanup database connection for the thread
    QMutexLocker locker(&mutex);
    disconnect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
    const QString connectionName = copies.value(thread).connectionName();
    copies.remove(thread);
    if (connectionName.startsWith(QLatin1String(connectionPrefix)))
        QSqlDatabase::removeDatabase(connectionName);
}

static void closeDatabase()
{
    delete globalDatabase;
}

QDjangoQuery::QDjangoQuery(QSqlDatabase db)
    : QSqlQuery(db)
{
}

void QDjangoQuery::addBindValue(const QVariant &val, QSql::ParamType paramType)
{
    // this hack is required so that we do not store a mix of local
    // and UTC times
    if (val.type() == QVariant::DateTime)
        QSqlQuery::addBindValue(val.toDateTime().toLocalTime(), paramType);
    else
        QSqlQuery::addBindValue(val, paramType);
}

bool QDjangoQuery::exec()
{
    if (globalDebugEnabled) {
        qDebug() << "SQL query" << lastQuery();
        QMapIterator<QString, QVariant> i(boundValues());
        while (i.hasNext()) {
            i.next();
            qDebug() << "SQL   " << i.key().toAscii().data() << "="
                     << i.value().toString().toAscii().data();
        }
    }
    if (!QSqlQuery::exec()) {
        if (globalDebugEnabled)
            qWarning() << "SQL error" << lastError();
        return false;
    }
    return true;
}

bool QDjangoQuery::exec(const QString &query)
{
    if (globalDebugEnabled)
        qDebug() << "SQL query" << query;
    if (!QSqlQuery::exec(query)) {
        if (globalDebugEnabled)
            qWarning() << "SQL error" << lastError();
        return false;
    }
    return true;
}

/*! \mainpage
 *
 * QDjango is a simple yet powerful Object Relation Mapper (ORM) built
 * on top of the Qt library. Where possible it tries to follow django's
 * ORM API, hence its name.
 *
 * \sa QDjango
 * \sa QDjangoModel
 * \sa QDjangoWhere
 * \sa QDjangoQuerySet
 */

/** Returns the database used by QDjango.
 *
 *  If you call this method from any thread but the application's main thread,
 *  a new connection to the database will be created. The connection will
 *  automatically be torn down once the thread finishes.
 *
 *  \sa setDatabase()
 */
QSqlDatabase QDjango::database()
{
    Q_ASSERT(globalDatabase != 0);
    QThread *thread = QThread::currentThread();

    // if we are in the main thread, return reference connection
    if (thread == globalDatabase->thread())
        return globalDatabase->reference;

    // if we have a connection for this thread, return it
    QMutexLocker locker(&globalDatabase->mutex);
    if (globalDatabase->copies.contains(thread))
        return globalDatabase->copies[thread];

    // create a new connection for this thread
    QObject::connect(thread, SIGNAL(finished()), globalDatabase, SLOT(threadFinished()));
    QSqlDatabase db = QSqlDatabase::cloneDatabase(globalDatabase->reference,
        QLatin1String(connectionPrefix) + QString::number(globalDatabase->connectionId++));
    db.open();
    globalDatabase->copies.insert(thread, db);
    return db;
}

/** Sets the database used by QDjango.
 *
 *  You must call this method from your application's main thread.
 *
 *  \sa database()
 */
void QDjango::setDatabase(QSqlDatabase database)
{
    if (database.driverName() != QLatin1String("QSQLITE") &&
        database.driverName() != QLatin1String("QSQLITE2") &&
        database.driverName() != QLatin1String("QMYSQL") &&
        database.driverName() != QLatin1String("QPSQL"))
    {
        qWarning() << "Unsupported database driver" << database.driverName();
    }
    if (!globalDatabase)
    {
        globalDatabase = new QDjangoDatabase();
        qAddPostRoutine(closeDatabase);
    }
    globalDatabase->reference = database;
}

/** Returns whether debugging information should be printed.
 *
 * \sa setDebugEnabled()
 */
bool QDjango::isDebugEnabled()
{
    return globalDebugEnabled;
}

/** Sets whether debugging information should be printed.
 *
 * \sa isDebugEnabled()
 */
void QDjango::setDebugEnabled(bool enabled)
{
    globalDebugEnabled = enabled;
}

/** Creates the database tables for all registered models.
 */
bool QDjango::createTables()
{
    bool ret = true;
    foreach (const QString &key, globalMetaModels.keys())
        if (!globalMetaModels[key].createTable())
            ret = false;
    return ret;
}

/** Drops the database tables for all registered models.
 */
bool QDjango::dropTables()
{
    bool ret = true;
    foreach (const QString &key, globalMetaModels.keys())
        if (!globalMetaModels[key].dropTable())
            ret = false;
    return ret;
}

/** Returns the QDjangoMetaModel with the given name.
 *
 * \param name
 */
QDjangoMetaModel QDjango::metaModel(const QString &name)
{
    return globalMetaModels.value(name);
}

QDjangoMetaModel QDjango::registerModel(const QObject *model)
{
    const QString name = model->metaObject()->className();
    if (!globalMetaModels.contains(name))
        globalMetaModels.insert(name, QDjangoMetaModel(model));
    return globalMetaModels[name];
}

/** Returns the empty SQL limit clause.
 */
QString QDjango::noLimitSql()
{
    const QString driverName = QDjango::database().driverName();
    if (driverName == QLatin1String("QSQLITE") ||
        driverName == QLatin1String("QSQLITE2"))
        return QLatin1String(" LIMIT -1");
    else if (driverName == QLatin1String("QMYSQL"))
        // 2^64 - 1, as recommended by the MySQL documentation
        return QLatin1String(" LIMIT 18446744073709551615");
    else
        return QString();
}

<commit_msg>silence doc warnings<commit_after>/*
 * Copyright (C) 2010-2012 Jeremy Lainé
 * Contact: http://code.google.com/p/qdjango/
 *
 * This file is part of the QDjango Library.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QSqlDriver>
#include <QSqlError>
#include <QSqlQuery>
#include <QStringList>
#include <QThread>

#include "QDjango.h"

static const char *connectionPrefix = "_qdjango_";

QMap<QString, QDjangoMetaModel> globalMetaModels = QMap<QString, QDjangoMetaModel>();
static QDjangoDatabase *globalDatabase = 0;
static bool globalDebugEnabled = false;

/// \cond

QDjangoDatabase::QDjangoDatabase(QObject *parent)
    : QObject(parent), connectionId(0)
{
}

void QDjangoDatabase::threadFinished()
{
    QThread *thread = qobject_cast<QThread*>(sender());
    if (!thread)
        return;

    // cleanup database connection for the thread
    QMutexLocker locker(&mutex);
    disconnect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
    const QString connectionName = copies.value(thread).connectionName();
    copies.remove(thread);
    if (connectionName.startsWith(QLatin1String(connectionPrefix)))
        QSqlDatabase::removeDatabase(connectionName);
}

static void closeDatabase()
{
    delete globalDatabase;
}

QDjangoQuery::QDjangoQuery(QSqlDatabase db)
    : QSqlQuery(db)
{
}

void QDjangoQuery::addBindValue(const QVariant &val, QSql::ParamType paramType)
{
    // this hack is required so that we do not store a mix of local
    // and UTC times
    if (val.type() == QVariant::DateTime)
        QSqlQuery::addBindValue(val.toDateTime().toLocalTime(), paramType);
    else
        QSqlQuery::addBindValue(val, paramType);
}

bool QDjangoQuery::exec()
{
    if (globalDebugEnabled) {
        qDebug() << "SQL query" << lastQuery();
        QMapIterator<QString, QVariant> i(boundValues());
        while (i.hasNext()) {
            i.next();
            qDebug() << "SQL   " << i.key().toAscii().data() << "="
                     << i.value().toString().toAscii().data();
        }
    }
    if (!QSqlQuery::exec()) {
        if (globalDebugEnabled)
            qWarning() << "SQL error" << lastError();
        return false;
    }
    return true;
}

bool QDjangoQuery::exec(const QString &query)
{
    if (globalDebugEnabled)
        qDebug() << "SQL query" << query;
    if (!QSqlQuery::exec(query)) {
        if (globalDebugEnabled)
            qWarning() << "SQL error" << lastError();
        return false;
    }
    return true;
}

/// \endcond

/*! \mainpage
 *
 * QDjango is a simple yet powerful Object Relation Mapper (ORM) built
 * on top of the Qt library. Where possible it tries to follow django's
 * ORM API, hence its name.
 *
 * \sa QDjango
 * \sa QDjangoModel
 * \sa QDjangoWhere
 * \sa QDjangoQuerySet
 */

/** Returns the database used by QDjango.
 *
 *  If you call this method from any thread but the application's main thread,
 *  a new connection to the database will be created. The connection will
 *  automatically be torn down once the thread finishes.
 *
 *  \sa setDatabase()
 */
QSqlDatabase QDjango::database()
{
    Q_ASSERT(globalDatabase != 0);
    QThread *thread = QThread::currentThread();

    // if we are in the main thread, return reference connection
    if (thread == globalDatabase->thread())
        return globalDatabase->reference;

    // if we have a connection for this thread, return it
    QMutexLocker locker(&globalDatabase->mutex);
    if (globalDatabase->copies.contains(thread))
        return globalDatabase->copies[thread];

    // create a new connection for this thread
    QObject::connect(thread, SIGNAL(finished()), globalDatabase, SLOT(threadFinished()));
    QSqlDatabase db = QSqlDatabase::cloneDatabase(globalDatabase->reference,
        QLatin1String(connectionPrefix) + QString::number(globalDatabase->connectionId++));
    db.open();
    globalDatabase->copies.insert(thread, db);
    return db;
}

/** Sets the database used by QDjango.
 *
 *  You must call this method from your application's main thread.
 *
 *  \sa database()
 */
void QDjango::setDatabase(QSqlDatabase database)
{
    if (database.driverName() != QLatin1String("QSQLITE") &&
        database.driverName() != QLatin1String("QSQLITE2") &&
        database.driverName() != QLatin1String("QMYSQL") &&
        database.driverName() != QLatin1String("QPSQL"))
    {
        qWarning() << "Unsupported database driver" << database.driverName();
    }
    if (!globalDatabase)
    {
        globalDatabase = new QDjangoDatabase();
        qAddPostRoutine(closeDatabase);
    }
    globalDatabase->reference = database;
}

/** Returns whether debugging information should be printed.
 *
 * \sa setDebugEnabled()
 */
bool QDjango::isDebugEnabled()
{
    return globalDebugEnabled;
}

/** Sets whether debugging information should be printed.
 *
 * \sa isDebugEnabled()
 */
void QDjango::setDebugEnabled(bool enabled)
{
    globalDebugEnabled = enabled;
}

/** Creates the database tables for all registered models.
 */
bool QDjango::createTables()
{
    bool ret = true;
    foreach (const QString &key, globalMetaModels.keys())
        if (!globalMetaModels[key].createTable())
            ret = false;
    return ret;
}

/** Drops the database tables for all registered models.
 */
bool QDjango::dropTables()
{
    bool ret = true;
    foreach (const QString &key, globalMetaModels.keys())
        if (!globalMetaModels[key].dropTable())
            ret = false;
    return ret;
}

/** Returns the QDjangoMetaModel with the given name.
 *
 * \param name
 */
QDjangoMetaModel QDjango::metaModel(const QString &name)
{
    return globalMetaModels.value(name);
}

QDjangoMetaModel QDjango::registerModel(const QObject *model)
{
    const QString name = model->metaObject()->className();
    if (!globalMetaModels.contains(name))
        globalMetaModels.insert(name, QDjangoMetaModel(model));
    return globalMetaModels[name];
}

/** Returns the empty SQL limit clause.
 */
QString QDjango::noLimitSql()
{
    const QString driverName = QDjango::database().driverName();
    if (driverName == QLatin1String("QSQLITE") ||
        driverName == QLatin1String("QSQLITE2"))
        return QLatin1String(" LIMIT -1");
    else if (driverName == QLatin1String("QMYSQL"))
        // 2^64 - 1, as recommended by the MySQL documentation
        return QLatin1String(" LIMIT 18446744073709551615");
    else
        return QString();
}

<|endoftext|>


<commit_before>/**
 * Copyright 2008 Matthew Graham
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "dispatcher.h"
#include "action.h"
#include "protocol.h"
#include "request.h"
#include "response.h"
#include "../message/message.h"
#include <iostream>

dispatcher_c::dispatcher_c( server_queue_i &queue )
: m_queue( queue )
, m_protocol()
{}

dispatcher_c::~dispatcher_c() {}

void dispatcher_c::add_protocol( short port, protocol_c &protocol )
{
	m_protocol[ port ] = &protocol;
}

bool dispatcher_c::main_loop()
{
	bool success( false );
	for (;;) {
		std::auto_ptr< request_message_i > msg( m_queue.pop() );
		if ( ! msg.get() ) {
			// sleep or yield or something, then reiterate
			continue;
		}

		dispatch( *req, *resp );
	}

	return success;
}

void request_router_c::dispatch( const request_c &req, response_c &resp )
{
	bool success( false );
	std::auto_ptr< response_c > resp( new response_c() );

	protocol_c *protocol = find_protocol( port );
	if ( ! protocol ) {
		// can't do anything.
		// write to the response and return
		std::ostringstream err;
		err << "No protocol for port " << port;
		resp.err( err.str() );
		return;
	}

	action_i *action = protocol->action( req.type() );
	if ( ! action ) {
		std::cerr << "no request processor for " << req.type()
			<< std::endl;
		resp.err( "unknown request type: '"+ req.type()
		       +"'" );
		return;
	}

	action->execute( req, resp );

	if ( ! protocol->silent() ) {
		m_queue.push( resp.release() );
	}
}

protocol_i * request_router_c::find_protocol( int port )
{
	protocol_iterator it( m_protocol.find( port ) );
	if ( it == m_protocol.end() ) {
		std::cerr << "port " << port << " has no protocol\n";
		// maybe should close this connection also?
		return NULL;
	}
	return it->second;
}

<commit_msg>fixed the include path for dispatcher now that everything is back in the same directory.<commit_after>/**
 * Copyright 2008 Matthew Graham
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "dispatcher.h"
#include "action.h"
#include "protocol.h"
#include "request.h"
#include "response.h"
#include "message.h"
#include <iostream>

dispatcher_c::dispatcher_c( server_queue_i &queue )
: m_queue( queue )
, m_protocol()
{}

dispatcher_c::~dispatcher_c() {}

void dispatcher_c::add_protocol( short port, protocol_c &protocol )
{
	m_protocol[ port ] = &protocol;
}

bool dispatcher_c::main_loop()
{
	bool success( false );
	for (;;) {
		std::auto_ptr< request_message_i > msg( m_queue.pop() );
		if ( ! msg.get() ) {
			// sleep or yield or something, then reiterate
			continue;
		}

		dispatch( *req, *resp );
	}

	return success;
}

void request_router_c::dispatch( const request_c &req, response_c &resp )
{
	bool success( false );
	std::auto_ptr< response_c > resp( new response_c() );

	protocol_c *protocol = find_protocol( port );
	if ( ! protocol ) {
		// can't do anything.
		// write to the response and return
		std::ostringstream err;
		err << "No protocol for port " << port;
		resp.err( err.str() );
		return;
	}

	action_i *action = protocol->action( req.type() );
	if ( ! action ) {
		std::cerr << "no request processor for " << req.type()
			<< std::endl;
		resp.err( "unknown request type: '"+ req.type()
		       +"'" );
		return;
	}

	action->execute( req, resp );

	if ( ! protocol->silent() ) {
		m_queue.push( resp.release() );
	}
}

protocol_i * request_router_c::find_protocol( int port )
{
	protocol_iterator it( m_protocol.find( port ) );
	if ( it == m_protocol.end() ) {
		std::cerr << "port " << port << " has no protocol\n";
		// maybe should close this connection also?
		return NULL;
	}
	return it->second;
}

<|endoftext|>


<commit_before>// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <policy/rbf.h>
#include <util/rbf.h>

RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
    AssertLockHeld(pool.cs);

    CTxMemPool::setEntries setAncestors;

    // First check the transaction itself.
    if (SignalsOptInRBF(tx)) {
        return RBFTransactionState::REPLACEABLE_BIP125;
    }

    // If this transaction is not in our mempool, then we can't be sure
    // we will know about all its inputs.
    if (!pool.exists(tx.GetHash())) {
        return RBFTransactionState::UNKNOWN;
    }

    // If all the inputs have nSequence >= maxint-1, it still might be
    // signaled for RBF if any unconfirmed parents have signaled.
    uint64_t noLimit = std::numeric_limits<uint64_t>::max();
    std::string dummy;
    CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
    pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);

    for (CTxMemPool::txiter it : setAncestors) {
        if (SignalsOptInRBF(it->GetTx())) {
            return RBFTransactionState::REPLACEABLE_BIP125;
        }
    }
    return RBFTransactionState::FINAL;
}
// SYSCOIN
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool, CTxMemPool::setEntries &setAncestors)
{
    AssertLockHeld(pool.cs);

    // First check the transaction itself.
    if (SignalsOptInRBF(tx)) {
        return RBFTransactionState::REPLACEABLE_BIP125;
    }

    // If this transaction is not in our mempool, then we can't be sure
    // we will know about all its inputs.
    if (!pool.exists(tx.GetHash())) {
        return RBFTransactionState::UNKNOWN;
    }

    // If all the inputs have nSequence >= maxint-1, it still might be
    // signaled for RBF if any unconfirmed parents have signaled.
    uint64_t noLimit = std::numeric_limits<uint64_t>::max();
    std::string dummy;
    CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
    pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);

    for (CTxMemPool::txiter it : setAncestors) {
        if (SignalsOptInRBF(it->GetTx())) {
            return RBFTransactionState::REPLACEABLE_BIP125;
        }
    }
    return RBFTransactionState::FINAL;

RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
{
    // If we don't have a local mempool we can only check the transaction itself.
    return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
}
<commit_msg>compile<commit_after>// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <policy/rbf.h>
#include <util/rbf.h>

RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
    AssertLockHeld(pool.cs);

    CTxMemPool::setEntries setAncestors;

    // First check the transaction itself.
    if (SignalsOptInRBF(tx)) {
        return RBFTransactionState::REPLACEABLE_BIP125;
    }

    // If this transaction is not in our mempool, then we can't be sure
    // we will know about all its inputs.
    if (!pool.exists(tx.GetHash())) {
        return RBFTransactionState::UNKNOWN;
    }

    // If all the inputs have nSequence >= maxint-1, it still might be
    // signaled for RBF if any unconfirmed parents have signaled.
    uint64_t noLimit = std::numeric_limits<uint64_t>::max();
    std::string dummy;
    CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
    pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);

    for (CTxMemPool::txiter it : setAncestors) {
        if (SignalsOptInRBF(it->GetTx())) {
            return RBFTransactionState::REPLACEABLE_BIP125;
        }
    }
    return RBFTransactionState::FINAL;
}
// SYSCOIN
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool, CTxMemPool::setEntries &setAncestors)
{
    AssertLockHeld(pool.cs);

    // First check the transaction itself.
    if (SignalsOptInRBF(tx)) {
        return RBFTransactionState::REPLACEABLE_BIP125;
    }

    // If this transaction is not in our mempool, then we can't be sure
    // we will know about all its inputs.
    if (!pool.exists(tx.GetHash())) {
        return RBFTransactionState::UNKNOWN;
    }

    // If all the inputs have nSequence >= maxint-1, it still might be
    // signaled for RBF if any unconfirmed parents have signaled.
    uint64_t noLimit = std::numeric_limits<uint64_t>::max();
    std::string dummy;
    CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
    pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);

    for (CTxMemPool::txiter it : setAncestors) {
        if (SignalsOptInRBF(it->GetTx())) {
            return RBFTransactionState::REPLACEABLE_BIP125;
        }
    }
    return RBFTransactionState::FINAL;
}
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
{
    // If we don't have a local mempool we can only check the transaction itself.
    return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
}
<|endoftext|>


<commit_before>///
/// @file   primecount.cpp
/// @brief  primecount C++ API
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primecount.hpp>
#include <calculator.hpp>
#include <int128.hpp>
#include <pmath.hpp>

#include <algorithm>
#include <ctime>
#include <cmath>
#include <limits>
#include <sstream>
#include <string>
#include <stdint.h>

#ifdef _OPENMP
  #include <omp.h>
#endif

#ifdef HAVE_MPI

#include <mpi.h>

namespace primecount {

int mpi_num_procs()
{
  int procs;
  MPI_Comm_size(MPI_COMM_WORLD, &procs);
  return procs;
}

int mpi_proc_id()
{
  int proc_id;
  MPI_Comm_rank(MPI_COMM_WORLD, &proc_id);
  return proc_id;
}

int mpi_master_proc_id()
{
  return 0;
}

bool is_mpi_master_proc()
{
  return mpi_proc_id() == mpi_master_proc_id();
}

} // namespace

#endif

using namespace std;

namespace {

#ifdef _OPENMP
  int threads_ = max(1, omp_get_max_threads());
#else
  int threads_ = 1;
#endif

int status_precision_ = -1;

double alpha_ = -1;

// Below 10^7 the Deleglise-Rivat algorithm is slower than LMO
const int deleglise_rivat_threshold = 10000000;

}

namespace primecount {

int64_t pi(int64_t x)
{
  return pi(x, threads_);
}

int64_t pi(int64_t x, int threads)
{
  if (x < deleglise_rivat_threshold)
    return pi_lmo(x, threads);
  else
    return pi_deleglise_rivat(x, threads);
}

#ifdef HAVE_INT128_T

int128_t pi(int128_t x)
{
  return pi(x, threads_);
}

int128_t pi(int128_t x, int threads)
{
  // use 64-bit if possible
  if (x <= numeric_limits<int64_t>::max())
    return pi((int64_t) x, threads);
  else
    return pi_deleglise_rivat(x, threads);
}

#endif

/// Alias for the fastest prime counting function in primecount.
/// @param x  integer arithmetic expression e.g. "10^12".
/// @pre   x  <= get_max_x().
///
string pi(const string& x)
{
  return pi(x, threads_);
}

/// Alias for the fastest prime counting function in primecount.
/// @param x  integer arithmetic expression e.g. "10^12".
/// @pre   x  <= get_max_x().
///
string pi(const string& x, int threads)
{
  maxint_t pi_x = pi(to_maxint(x), threads);
  ostringstream oss;
  oss << pi_x;
  return oss.str();
}

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int64_t pi_deleglise_rivat(int64_t x)
{
  return pi_deleglise_rivat(x, threads_);
}

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int64_t pi_deleglise_rivat(int64_t x, int threads)
{
  return pi_deleglise_rivat_parallel2(x, threads);
}

#ifdef HAVE_INT128_T

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int128_t pi_deleglise_rivat(int128_t x)
{
  return pi_deleglise_rivat(x, threads_);
}

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int128_t pi_deleglise_rivat(int128_t x, int threads)
{
  // use 64-bit if possible
  if (x <= numeric_limits<int64_t>::max())
    return pi_deleglise_rivat((int64_t) x, threads);
  else
    return pi_deleglise_rivat_parallel3(x, threads);
}

#endif

/// Calculate the number of primes below x using Legendre's formula.
/// Run time: O(x) operations, O(x^(1/2)) space.
///
int64_t pi_legendre(int64_t x)
{
  return pi_legendre(x, threads_);
}

/// Calculate the number of primes below x using Lehmer's formula.
/// Run time: O(x/(log x)^4) operations, O(x^(1/2)) space.
///
int64_t pi_lehmer(int64_t x)
{
  return pi_lehmer(x, threads_);
}

/// Calculate the number of primes below x using the
/// Lagarias-Miller-Odlyzko algorithm.
/// Run time: O(x^(2/3) / log x) operations, O(x^(1/3) * (log x)^2) space.
///
int64_t pi_lmo(int64_t x)
{
  return pi_lmo(x, threads_);
}

/// Parallel implementation of the Lagarias-Miller-Odlyzko
/// prime counting algorithm using OpenMP.
/// Run time: O(x^(2/3) / log x) operations, O(x^(1/3) * (log x)^2) space.
///
int64_t pi_lmo(int64_t x, int threads)
{
  return pi_lmo_parallel3(x, threads);
}

/// Calculate the number of primes below x using Meissel's formula.
/// Run time: O(x/(log x)^3) operations, O(x^(1/2) / log x) space.
///
int64_t pi_meissel(int64_t x)
{
  return pi_meissel(x, threads_);
}

/// Calculate the number of primes below x using an optimized
/// segmented sieve of Eratosthenes implementation.
/// Run time: O(x log log x) operations, O(x^(1/2)) space.
///
int64_t pi_primesieve(int64_t x)
{
  return pi_primesieve(x, threads_);
}

/// Calculate the nth prime using a combination of the prime
/// counting function and the sieve of Eratosthenes.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/2)) space.
///
int64_t nth_prime(int64_t n)
{
  return nth_prime(n, threads_);
}

/// Partial sieve function (a.k.a. Legendre-sum).
/// phi(x, a) counts the numbers <= x that are not divisible
/// by any of the first a primes.
///
int64_t phi(int64_t x, int64_t a)
{
  return phi(x, a, threads_);
}

/// Returns the largest integer that can be used with
/// pi(string x). The return type is a string as max can be a 128-bit
/// integer which is not supported by all compilers.
///
string get_max_x(double alpha)
{
  ostringstream oss;

#ifdef HAVE_INT128_T
  // primecount is limited by:
  // z < 2^62, with z = x^(2/3) / alpha
  // x^(2/3) / alpha < 2^62
  // x < (2^62 * alpha)^(3/2)

  // safety buffer: use 61 instead of 62
  double max_x = pow(pow(2.0, 61.0) * alpha, 3.0 / 2.0);
  oss << (int128_t) max_x;
#else
  unused_param(alpha);
  oss << numeric_limits<int64_t>::max();
#endif

  return oss.str();
}

/// Get the wall time in seconds.
double get_wtime()
{
#ifdef _OPENMP
  return omp_get_wtime();
#else
  return (double) (std::clock() / CLOCKS_PER_SEC);
#endif
}

int ideal_num_threads(int threads, int64_t sieve_limit, int64_t thread_threshold)
{
  thread_threshold = max((int64_t) 1, thread_threshold);
  threads = (int) min((int64_t) threads, sieve_limit / thread_threshold);
  threads = max(1, threads);
  return threads;
}

void set_alpha(double alpha)
{
  alpha_ = alpha;
}

double get_alpha()
{
  return alpha_;
}

double get_alpha(maxint_t x, int64_t y)
{
  // y = x13 * alpha, thus alpha = y / x13
  double x13 = (double) iroot<3>(x);
  return (double) y / x13;
}

/// Calculate the Lagarias-Miller-Odlyzko alpha tuning factor.
/// alpha = a log(x)^2 + b log(x) + c
/// a, b and c are constants that should be determined empirically.
/// @see ../doc/alpha-factor-tuning.pdf
///
double get_alpha_lmo(maxint_t x)
{
  double alpha = get_alpha();

  // use default alpha if no command-line alpha provided
  if (alpha < 1)
  {
    double a = 0.00156512;
    double b = -0.0261411;
    double c = 0.990948;
    double logx = log((double) x);

    alpha = a * pow(logx, 2) + b * logx + c;
  }

  return in_between(1, alpha, iroot<6>(x));
}

/// Calculate the Deleglise-Rivat alpha tuning factor.
/// alpha = a log(x)^3 + b log(x)^2 + c log(x) + d
/// a, b, c and d are constants that should be determined empirically.
/// @see ../doc/alpha-tuning-factor.pdf
///
double get_alpha_deleglise_rivat(maxint_t x)
{
  double alpha = get_alpha();
  double x2 = (double) x;

  // use default alpha if no command-line alpha provided
  if (alpha < 1)
  {
    if (x2 <= 1e21)
    {
      double a = 0.000711339;
      double b = -0.0160586;
      double c = 0.123034;
      double d = 0.802942;
      double logx = log(x2);

      alpha = a * pow(logx, 3) + b * pow(logx, 2) + c * logx + d;
    }
    else
    {
      // Because of CPU cache misses sieving (S2_hard(x) and P2(x))
      // becomes the main bottleneck above 10^21 . Hence we use a
      // different alpha formula when x > 10^21 which returns a larger
      // alpha which reduces sieving but increases S2_easy(x) work.
      double a = 0.00149066;
      double b = -0.0375705;
      double c = 0.282139;
      double d = 0.591972;
      double logx = log(x2);

      alpha = a * pow(logx, 3) + b * pow(logx, 2) + c * logx + d;
    }
  }

  return in_between(1, alpha, iroot<6>(x));
}

void set_num_threads(int threads)
{
#ifdef _OPENMP
  int threads_ = in_between(1, threads, omp_get_max_threads());
#else
  unused_param(threads);
#endif
}

int get_num_threads()
{
  return threads_;
}

void set_status_precision(int precision)
{
  status_precision_ = in_between(0, precision, 5);
}

int get_status_precision(maxint_t x)
{
  // use default precision when no command-line precision provided
  if (status_precision_ < 0)
  {
    if ((double) x >= 1e23)
      return 2;
    if ((double) x >= 1e21)
      return 1;
  }

  return (status_precision_ > 0) ? status_precision_ : 0;
}

maxint_t to_maxint(const string& expr)
{
  maxint_t n = calculator::eval<maxint_t>(expr);
  return n;
}

/// Get the primecount version number, in the form “i.j”.
string primecount_version()
{
  return PRIMECOUNT_VERSION;
}

} // namespace
<commit_msg>Fix typo<commit_after>///
/// @file   primecount.cpp
/// @brief  primecount C++ API
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primecount.hpp>
#include <calculator.hpp>
#include <int128.hpp>
#include <pmath.hpp>

#include <algorithm>
#include <ctime>
#include <cmath>
#include <limits>
#include <sstream>
#include <string>
#include <stdint.h>

#ifdef _OPENMP
  #include <omp.h>
#endif

#ifdef HAVE_MPI

#include <mpi.h>

namespace primecount {

int mpi_num_procs()
{
  int procs;
  MPI_Comm_size(MPI_COMM_WORLD, &procs);
  return procs;
}

int mpi_proc_id()
{
  int proc_id;
  MPI_Comm_rank(MPI_COMM_WORLD, &proc_id);
  return proc_id;
}

int mpi_master_proc_id()
{
  return 0;
}

bool is_mpi_master_proc()
{
  return mpi_proc_id() == mpi_master_proc_id();
}

} // namespace

#endif

using namespace std;

namespace {

#ifdef _OPENMP
  int threads_ = max(1, omp_get_max_threads());
#else
  int threads_ = 1;
#endif

int status_precision_ = -1;

double alpha_ = -1;

// Below 10^7 the Deleglise-Rivat algorithm is slower than LMO
const int deleglise_rivat_threshold = 10000000;

}

namespace primecount {

int64_t pi(int64_t x)
{
  return pi(x, threads_);
}

int64_t pi(int64_t x, int threads)
{
  if (x < deleglise_rivat_threshold)
    return pi_lmo(x, threads);
  else
    return pi_deleglise_rivat(x, threads);
}

#ifdef HAVE_INT128_T

int128_t pi(int128_t x)
{
  return pi(x, threads_);
}

int128_t pi(int128_t x, int threads)
{
  // use 64-bit if possible
  if (x <= numeric_limits<int64_t>::max())
    return pi((int64_t) x, threads);
  else
    return pi_deleglise_rivat(x, threads);
}

#endif

/// Alias for the fastest prime counting function in primecount.
/// @param x  integer arithmetic expression e.g. "10^12".
/// @pre   x  <= get_max_x().
///
string pi(const string& x)
{
  return pi(x, threads_);
}

/// Alias for the fastest prime counting function in primecount.
/// @param x  integer arithmetic expression e.g. "10^12".
/// @pre   x  <= get_max_x().
///
string pi(const string& x, int threads)
{
  maxint_t pi_x = pi(to_maxint(x), threads);
  ostringstream oss;
  oss << pi_x;
  return oss.str();
}

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int64_t pi_deleglise_rivat(int64_t x)
{
  return pi_deleglise_rivat(x, threads_);
}

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int64_t pi_deleglise_rivat(int64_t x, int threads)
{
  return pi_deleglise_rivat_parallel2(x, threads);
}

#ifdef HAVE_INT128_T

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int128_t pi_deleglise_rivat(int128_t x)
{
  return pi_deleglise_rivat(x, threads_);
}

/// Calculate the number of primes below x using the
/// Deleglise-Rivat algorithm.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/3) * (log x)^3) space.
///
int128_t pi_deleglise_rivat(int128_t x, int threads)
{
  // use 64-bit if possible
  if (x <= numeric_limits<int64_t>::max())
    return pi_deleglise_rivat((int64_t) x, threads);
  else
    return pi_deleglise_rivat_parallel3(x, threads);
}

#endif

/// Calculate the number of primes below x using Legendre's formula.
/// Run time: O(x) operations, O(x^(1/2)) space.
///
int64_t pi_legendre(int64_t x)
{
  return pi_legendre(x, threads_);
}

/// Calculate the number of primes below x using Lehmer's formula.
/// Run time: O(x/(log x)^4) operations, O(x^(1/2)) space.
///
int64_t pi_lehmer(int64_t x)
{
  return pi_lehmer(x, threads_);
}

/// Calculate the number of primes below x using the
/// Lagarias-Miller-Odlyzko algorithm.
/// Run time: O(x^(2/3) / log x) operations, O(x^(1/3) * (log x)^2) space.
///
int64_t pi_lmo(int64_t x)
{
  return pi_lmo(x, threads_);
}

/// Parallel implementation of the Lagarias-Miller-Odlyzko
/// prime counting algorithm using OpenMP.
/// Run time: O(x^(2/3) / log x) operations, O(x^(1/3) * (log x)^2) space.
///
int64_t pi_lmo(int64_t x, int threads)
{
  return pi_lmo_parallel3(x, threads);
}

/// Calculate the number of primes below x using Meissel's formula.
/// Run time: O(x/(log x)^3) operations, O(x^(1/2) / log x) space.
///
int64_t pi_meissel(int64_t x)
{
  return pi_meissel(x, threads_);
}

/// Calculate the number of primes below x using an optimized
/// segmented sieve of Eratosthenes implementation.
/// Run time: O(x log log x) operations, O(x^(1/2)) space.
///
int64_t pi_primesieve(int64_t x)
{
  return pi_primesieve(x, threads_);
}

/// Calculate the nth prime using a combination of the prime
/// counting function and the sieve of Eratosthenes.
/// Run time: O(x^(2/3) / (log x)^2) operations, O(x^(1/2)) space.
///
int64_t nth_prime(int64_t n)
{
  return nth_prime(n, threads_);
}

/// Partial sieve function (a.k.a. Legendre-sum).
/// phi(x, a) counts the numbers <= x that are not divisible
/// by any of the first a primes.
///
int64_t phi(int64_t x, int64_t a)
{
  return phi(x, a, threads_);
}

/// Returns the largest integer that can be used with
/// pi(string x). The return type is a string as max can be a 128-bit
/// integer which is not supported by all compilers.
///
string get_max_x(double alpha)
{
  ostringstream oss;

#ifdef HAVE_INT128_T
  // primecount is limited by:
  // z < 2^62, with z = x^(2/3) / alpha
  // x^(2/3) / alpha < 2^62
  // x < (2^62 * alpha)^(3/2)

  // safety buffer: use 61 instead of 62
  double max_x = pow(pow(2.0, 61.0) * alpha, 3.0 / 2.0);
  oss << (int128_t) max_x;
#else
  unused_param(alpha);
  oss << numeric_limits<int64_t>::max();
#endif

  return oss.str();
}

/// Get the wall time in seconds.
double get_wtime()
{
#ifdef _OPENMP
  return omp_get_wtime();
#else
  return (double) (std::clock() / CLOCKS_PER_SEC);
#endif
}

int ideal_num_threads(int threads, int64_t sieve_limit, int64_t thread_threshold)
{
  thread_threshold = max((int64_t) 1, thread_threshold);
  threads = (int) min((int64_t) threads, sieve_limit / thread_threshold);
  threads = max(1, threads);
  return threads;
}

void set_alpha(double alpha)
{
  alpha_ = alpha;
}

double get_alpha()
{
  return alpha_;
}

double get_alpha(maxint_t x, int64_t y)
{
  // y = x13 * alpha, thus alpha = y / x13
  double x13 = (double) iroot<3>(x);
  return (double) y / x13;
}

/// Calculate the Lagarias-Miller-Odlyzko alpha tuning factor.
/// alpha = a log(x)^2 + b log(x) + c
/// a, b and c are constants that should be determined empirically.
/// @see ../doc/alpha-factor-tuning.pdf
///
double get_alpha_lmo(maxint_t x)
{
  double alpha = get_alpha();

  // use default alpha if no command-line alpha provided
  if (alpha < 1)
  {
    double a = 0.00156512;
    double b = -0.0261411;
    double c = 0.990948;
    double logx = log((double) x);

    alpha = a * pow(logx, 2) + b * logx + c;
  }

  return in_between(1, alpha, iroot<6>(x));
}

/// Calculate the Deleglise-Rivat alpha tuning factor.
/// alpha = a log(x)^3 + b log(x)^2 + c log(x) + d
/// a, b, c and d are constants that should be determined empirically.
/// @see ../doc/alpha-tuning-factor.pdf
///
double get_alpha_deleglise_rivat(maxint_t x)
{
  double alpha = get_alpha();
  double x2 = (double) x;

  // use default alpha if no command-line alpha provided
  if (alpha < 1)
  {
    if (x2 <= 1e21)
    {
      double a = 0.000711339;
      double b = -0.0160586;
      double c = 0.123034;
      double d = 0.802942;
      double logx = log(x2);

      alpha = a * pow(logx, 3) + b * pow(logx, 2) + c * logx + d;
    }
    else
    {
      // Because of CPU cache misses sieving (S2_hard(x) and P2(x))
      // becomes the main bottleneck above 10^21 . Hence we use a
      // different alpha formula when x > 10^21 which returns a larger
      // alpha which reduces sieving but increases S2_easy(x) work.
      double a = 0.00149066;
      double b = -0.0375705;
      double c = 0.282139;
      double d = 0.591972;
      double logx = log(x2);

      alpha = a * pow(logx, 3) + b * pow(logx, 2) + c * logx + d;
    }
  }

  return in_between(1, alpha, iroot<6>(x));
}

void set_num_threads(int threads)
{
#ifdef _OPENMP
  threads_ = in_between(1, threads, omp_get_max_threads());
#else
  unused_param(threads);
#endif
}

int get_num_threads()
{
  return threads_;
}

void set_status_precision(int precision)
{
  status_precision_ = in_between(0, precision, 5);
}

int get_status_precision(maxint_t x)
{
  // use default precision when no command-line precision provided
  if (status_precision_ < 0)
  {
    if ((double) x >= 1e23)
      return 2;
    if ((double) x >= 1e21)
      return 1;
  }

  return (status_precision_ > 0) ? status_precision_ : 0;
}

maxint_t to_maxint(const string& expr)
{
  maxint_t n = calculator::eval<maxint_t>(expr);
  return n;
}

/// Get the primecount version number, in the form “i.j”.
string primecount_version()
{
  return PRIMECOUNT_VERSION;
}

} // namespace
<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 1999-2016 Vaclav Slavik
 *  Copyright (C) 2005 Olivier Sannier
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "edlistctrl.h"

#include "hidpi.h"
#include "language.h"
#include "cat_sorting.h"
#include "colorscheme.h"
#include "unicode_helpers.h"
#include "utility.h"

#include <wx/wx.h>
#include <wx/imaglist.h>
#include <wx/artprov.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/wupdlock.h>

#ifdef __WXMSW__
#include <wx/headerctrl.h>
#include <wx/itemattr.h>
#include <wx/msw/uxtheme.h>
#include <vssym32.h>
#endif

#include <algorithm>

namespace
{

class SelectionPreserver
{
public:
    SelectionPreserver(PoeditListCtrl *list_) : list(list_), focus(-1)
    {
        if (!list)
            return;
        selection = list->GetSelectedCatalogItemIndexes();
        focus = list->ListItemToCatalogIndex(list->GetCurrentItem());
    }

    ~SelectionPreserver()
    {
        if (!list)
            return;
        if (!selection.empty())
            list->SetSelectedCatalogItemIndexes(selection);
        if (focus != -1)
        {
            auto item = list->CatalogIndexToListItem(focus);
            list->EnsureVisible(item);
            list->SetCurrentItem(item);
        }
    }

private:
    PoeditListCtrl *list;
    std::vector<int> selection;
    int focus;
};

} // anonymous namespace

PoeditListCtrl::Model::Model(TextDirection appTextDir, wxVisualAttributes visual)
    : m_sourceTextDir(TextDirection::LTR),
      m_transTextDir(TextDirection::LTR),
      m_appTextDir(appTextDir)
{
    sortOrder = SortOrder::Default();

    // configure items colors & fonts:

    m_clrID = ColorScheme::Get(Color::ItemID, visual);
    m_clrFuzzy = ColorScheme::Get(Color::ItemFuzzy, visual);
    m_clrInvalid = ColorScheme::Get(Color::ItemError, visual);

    m_iconPreTranslated = wxArtProvider::GetBitmap("poedit-status-automatic");
    m_iconComment = wxArtProvider::GetBitmap("poedit-status-comment");
    m_iconBookmark = wxArtProvider::GetBitmap("poedit-status-bookmark");
}

void PoeditListCtrl::Model::SetCatalog(CatalogPtr catalog)
{
    m_catalog = catalog;

    if (!catalog)
    {
        Reset(0);
        return;
    }

    auto srclang = catalog->GetSourceLanguage();
    auto lang = catalog->GetLanguage();
    m_sourceTextDir = srclang.Direction();
    m_transTextDir = lang.Direction();

    // sort catalog items, create indexes mapping
    CreateSortMap();

    Reset(catalog->GetCount());
}

void PoeditListCtrl::Model::UpdateSort()
{
    if (!m_catalog)
        return;
    CreateSortMap();
    Reset(m_catalog->GetCount());
}

wxString PoeditListCtrl::Model::GetColumnType(unsigned int col) const
{
    switch (col)
    {
        case Col_ID:
            return "string";

        case Col_Icon:
            return "wxBitmap";

        case Col_Source:
        case Col_Translation:
            return "string";

        default:
            return "null";
    }
}

void PoeditListCtrl::Model::GetValueByRow(wxVariant& variant, unsigned row, unsigned col) const
{
    auto d = Item(row);
    wxCHECK_RET(d, "invalid row");

    switch (col)
    {
        case Col_ID:
        {
            variant = wxString::Format("%d", d->GetId());
            break;
        }

        case Col_Icon:
        {
            if (d->GetBookmark() != NO_BOOKMARK)
                variant << m_iconBookmark;
            else if (d->HasComment() || d->HasExtractedComments())
                variant << m_iconComment;
            else if (d->IsPreTranslated())
                variant << m_iconPreTranslated;
            else
                variant = wxNullVariant;
            break;
        }

        case Col_Source:
        {
            wxString orig;
#if wxCHECK_VERSION(3,1,1)
            if (d->HasContext())
            {
                // Work around a problem with GTK+'s coloring of markup that begins with colorizing :
                #ifdef __WXGTK__
                    #define MARKUP(x) L"\u200B" L##x
                #else
                    #define MARKUP(x) x
                #endif
                orig.Printf(MARKUP("[%s] %s"),
                            EscapeMarkup(d->GetContext()), EscapeMarkup(d->GetString()));
            }
            else
            {
                orig = EscapeMarkup(d->GetString());
            }
#else // wx 3.0
            if (d->HasContext())
                orig.Printf("[%s] %s", d->GetContext(), d->GetString());
            else
                orig = d->GetString();
#endif

            // FIXME: use syntax highlighting or typographic marks
            orig.Replace("\n", " ");

            // Add RTL Unicode mark to render bidi texts correctly
            if (m_appTextDir != m_sourceTextDir)
                variant = bidi::mark_direction(orig, m_sourceTextDir);
            else
                variant = orig;
            break;
        }

        case Col_Translation:
        {
            auto trans = d->GetTranslation();

            // FIXME: use syntax highlighting or typographic marks
            trans.Replace("\n", " ");

            // Add RTL Unicode mark to render bidi texts correctly
            if (m_appTextDir != m_transTextDir)
                variant = bidi::mark_direction(trans, m_transTextDir);
            else
                variant = trans;
            break;
        }

        default:
            variant.Clear();
            break;
    };
}

bool PoeditListCtrl::Model::SetValueByRow(const wxVariant&, unsigned, unsigned)
{
    wxFAIL_MSG("setting values in dataview not implemented");
    return false;
}

bool PoeditListCtrl::Model::GetAttrByRow(unsigned row, unsigned col, wxDataViewItemAttr& attr) const
{
    if (!m_catalog)
        return false;

    switch (col)
    {
        case Col_ID:
        {
            attr.SetColour(m_clrID);
            return true;
        }

        case Col_Source:
        case Col_Translation:
        {
            auto d = Item(row);
            if (d->GetValidity() == CatalogItem::Val_Invalid)
            {
                attr.SetColour(m_clrInvalid);
                return true;
            }
            else if (d->IsFuzzy())
            {
                attr.SetColour(m_clrFuzzy);
                return true;
            }
            else
            {
                return false;
            }
        }

        default:
            return false;
    }
}

void PoeditListCtrl::Model::CreateSortMap()
{
    // FIXME: Use native wxDataViewCtrl sorting instead

    int count = (int)m_catalog->GetCount();

    m_mapListToCatalog.resize(count);
    m_mapCatalogToList.resize(count);

    // First create identity mapping for the sort order.
    for ( int i = 0; i < count; i++ )
        m_mapListToCatalog[i] = i;

    // m_mapListToCatalog will hold our desired sort order. Sort it in place
    // now, using the desired sort criteria.
    CatalogItemsComparator comparator(*m_catalog, sortOrder);
    std::sort
    (
        m_mapListToCatalog.begin(),
        m_mapListToCatalog.end(),
        std::ref(comparator)
    );

    // Finally, construct m_mapCatalogToList to be the inverse mapping to
    // m_mapListToCatalog.
    for ( int i = 0; i < count; i++ )
        m_mapCatalogToList[m_mapListToCatalog[i]] = i;
}

PoeditListCtrl::PoeditListCtrl(wxWindow *parent, wxWindowID id, bool dispIDs)
     : wxDataViewCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE | wxDV_ROW_LINES | wxNO_BORDER, wxDefaultValidator, "translations list")
{
    m_displayIDs = dispIDs;

    m_appTextDir = (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) ? TextDirection::RTL : TextDirection::LTR;

    m_model.reset(new Model(m_appTextDir, GetDefaultAttributes()));
    AssociateModel(m_model.get());

    CreateColumns();

    UpdateHeaderAttrs();

    Bind(wxEVT_SIZE, &PoeditListCtrl::OnSize, this);
}

PoeditListCtrl::~PoeditListCtrl()
{
    sortOrder().Save();
}

void PoeditListCtrl::UpdateHeaderAttrs()
{
#ifdef __WXMSW__
    // Setup custom header font & color on Windows 10, where the default look is a bit odd
    if (IsWindows10OrGreater())
    {
        // Use the same text color as Explorer's headers use
        const wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
        if (theme)
        {
            wxUxThemeHandle hTheme(this->GetParent(), L"ItemsView::Header");
            COLORREF clr;
            HRESULT hr = theme->GetThemeColor(hTheme, HP_HEADERITEM, 0, TMT_TEXTCOLOR, &clr);
            if (SUCCEEDED(hr))
            {
                wxItemAttr headerAttr;
                headerAttr.SetTextColour(wxRGBToColour(clr));
                SetHeaderAttr(headerAttr);
            }
        }

        // Standard header has smaller height than Explorer's and it isn't
        // separated from the content well -- especially in HiDPI modes.
        // Match Explorer's header size too:
        int headerHeight = HiDPIScalingFactor() > 1.0 ? PX(26) : PX(25);
        GenericGetHeader()->SetMinSize(wxSize(-1, headerHeight));
    }
#endif // __WXMSW__
}

void PoeditListCtrl::SetCustomFont(wxFont font_)
{
    wxFont font(font_);

    if ( !font.IsOk() )
        font = GetDefaultAttributes().font;

    SetFont(font);

    UpdateHeaderAttrs();
    CreateColumns();
}

void PoeditListCtrl::SetDisplayLines(bool dl)
{
    m_displayIDs = dl;
    CreateColumns();
}

void PoeditListCtrl::CreateColumns()
{
    if (GetColumnCount() > 0)
        ClearColumns();

    m_colID = m_colIcon = m_colSource = m_colTrans = nullptr;

    Language srclang, lang;
    if (m_catalog)
    {
        srclang = m_catalog->GetSourceLanguage();
        lang = m_catalog->GetLanguage();
    }

    auto isRTL = lang.IsRTL();
#ifdef __WXMSW__
    // a quirk of wx API: if the current locale is RTL, the meaning of L and R is reversed
    if (m_appTextDir == TextDirection::RTL)
        isRTL = !isRTL;
#endif

    m_colIcon = AppendBitmapColumn(L"∙", Model::Col_Icon, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_CENTER, 0);

    wxString sourceTitle = srclang.IsValid()
                             ? wxString::Format(_(L"Source text — %s"), srclang.DisplayName())
                             : _("Source text");
    auto sourceRenderer = new wxDataViewTextRenderer();
    sourceRenderer->EnableEllipsize(wxELLIPSIZE_END);
#if wxCHECK_VERSION(3,1,1)
    sourceRenderer->EnableMarkup();
#endif
    m_colSource = new wxDataViewColumn(sourceTitle, sourceRenderer, Model::Col_Source, wxCOL_WIDTH_DEFAULT, wxALIGN_LEFT, 0);
    AppendColumn(m_colSource);

    if (m_model->m_catalog && m_model->m_catalog->HasCapability(Catalog::Cap::Translations))
    {
        wxString langname = lang.IsValid() ? lang.DisplayName() : _("unknown language");;
        wxString transTitle = wxString::Format(_(L"Translation — %s"), langname);
        auto transRenderer = new wxDataViewTextRenderer();
        transRenderer->EnableEllipsize(wxELLIPSIZE_END);
        m_colTrans = new wxDataViewColumn(transTitle, transRenderer, Model::Col_Translation, wxCOL_WIDTH_DEFAULT, isRTL ? wxALIGN_RIGHT : wxALIGN_LEFT, 0);
        AppendColumn(m_colTrans);
    }

    if (m_displayIDs)
    {
        m_colID = AppendTextColumn(_("ID"), Model::Col_ID, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_RIGHT, 0);
    }

    // wxDVC insists on having an expander column, but we really don't want one:
    auto fake = AppendTextColumn("", Model::Col_ID);
    fake->SetHidden(true);
    SetExpanderColumn(fake);

#ifdef __WXMSW__
    if (m_appTextDir == TextDirection::RTL)
        m_colSource->SetAlignment(wxALIGN_RIGHT);
#endif

    SizeColumns();

#ifdef __WXGTK__
    // wxGTK has delayed sizing computation, apparently
    CallAfter([=]{ SizeColumns(); });
#endif
}

void PoeditListCtrl::SizeColumns()
{
    int w = GetClientSize().x;

#if defined(__WXOSX__)
    w -= (GetColumnCount() - 1) * 3;
#elif defined(__WXGTK__)
    w -= 2;
#endif

    if (m_colIcon)
        w -= m_colIcon->GetWidth();

    if (m_colID)
        w -= m_colID->GetWidth();

    if (m_colTrans)
    {
        m_colSource->SetWidth(w / 2);
        m_colTrans->SetWidth(w - w / 2);
    }
    else
    {
        m_colSource->SetWidth(w);
    }
}

void PoeditListCtrl::CatalogChanged(const CatalogPtr& catalog)
{
    wxWindowUpdateLocker no_updates(this);

    const int oldCount = m_catalog ? m_catalog->GetCount() : 0;
    const int newCount = catalog ? catalog->GetCount() : 0;
    const bool isSameCatalog = (catalog == m_catalog);
    const bool sizeOrCatalogChanged = !isSameCatalog || (oldCount != newCount);

    SelectionPreserver preserve(!sizeOrCatalogChanged ? this : nullptr);

    // now read the new catalog:
    m_catalog = catalog;
    m_model->SetCatalog(catalog);

    if (!m_catalog)
        return;

    CreateColumns();

    if (sizeOrCatalogChanged && GetItemCount() > 0)
        CallAfter([=]{ SelectAndFocus(0); });
}

void PoeditListCtrl::Sort()
{
    if (!m_catalog)
        return;

    SelectionPreserver preserve(this);
    m_model->UpdateSort();
}

void PoeditListCtrl::OnSize(wxSizeEvent& event)
{
    wxWindowUpdateLocker lock(this);

    SizeColumns();
    event.Skip();
}
<commit_msg>Fix columns sizing under GTK+<commit_after>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 1999-2016 Vaclav Slavik
 *  Copyright (C) 2005 Olivier Sannier
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "edlistctrl.h"

#include "hidpi.h"
#include "language.h"
#include "cat_sorting.h"
#include "colorscheme.h"
#include "unicode_helpers.h"
#include "utility.h"

#include <wx/wx.h>
#include <wx/imaglist.h>
#include <wx/artprov.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/wupdlock.h>

#ifdef __WXMSW__
#include <wx/headerctrl.h>
#include <wx/itemattr.h>
#include <wx/msw/uxtheme.h>
#include <vssym32.h>
#endif

#include <algorithm>

namespace
{

class SelectionPreserver
{
public:
    SelectionPreserver(PoeditListCtrl *list_) : list(list_), focus(-1)
    {
        if (!list)
            return;
        selection = list->GetSelectedCatalogItemIndexes();
        focus = list->ListItemToCatalogIndex(list->GetCurrentItem());
    }

    ~SelectionPreserver()
    {
        if (!list)
            return;
        if (!selection.empty())
            list->SetSelectedCatalogItemIndexes(selection);
        if (focus != -1)
        {
            auto item = list->CatalogIndexToListItem(focus);
            list->EnsureVisible(item);
            list->SetCurrentItem(item);
        }
    }

private:
    PoeditListCtrl *list;
    std::vector<int> selection;
    int focus;
};

} // anonymous namespace

PoeditListCtrl::Model::Model(TextDirection appTextDir, wxVisualAttributes visual)
    : m_sourceTextDir(TextDirection::LTR),
      m_transTextDir(TextDirection::LTR),
      m_appTextDir(appTextDir)
{
    sortOrder = SortOrder::Default();

    // configure items colors & fonts:

    m_clrID = ColorScheme::Get(Color::ItemID, visual);
    m_clrFuzzy = ColorScheme::Get(Color::ItemFuzzy, visual);
    m_clrInvalid = ColorScheme::Get(Color::ItemError, visual);

    m_iconPreTranslated = wxArtProvider::GetBitmap("poedit-status-automatic");
    m_iconComment = wxArtProvider::GetBitmap("poedit-status-comment");
    m_iconBookmark = wxArtProvider::GetBitmap("poedit-status-bookmark");
}

void PoeditListCtrl::Model::SetCatalog(CatalogPtr catalog)
{
    m_catalog = catalog;

    if (!catalog)
    {
        Reset(0);
        return;
    }

    auto srclang = catalog->GetSourceLanguage();
    auto lang = catalog->GetLanguage();
    m_sourceTextDir = srclang.Direction();
    m_transTextDir = lang.Direction();

    // sort catalog items, create indexes mapping
    CreateSortMap();

    Reset(catalog->GetCount());
}

void PoeditListCtrl::Model::UpdateSort()
{
    if (!m_catalog)
        return;
    CreateSortMap();
    Reset(m_catalog->GetCount());
}

wxString PoeditListCtrl::Model::GetColumnType(unsigned int col) const
{
    switch (col)
    {
        case Col_ID:
            return "string";

        case Col_Icon:
            return "wxBitmap";

        case Col_Source:
        case Col_Translation:
            return "string";

        default:
            return "null";
    }
}

void PoeditListCtrl::Model::GetValueByRow(wxVariant& variant, unsigned row, unsigned col) const
{
    auto d = Item(row);
    wxCHECK_RET(d, "invalid row");

    switch (col)
    {
        case Col_ID:
        {
            variant = wxString::Format("%d", d->GetId());
            break;
        }

        case Col_Icon:
        {
            if (d->GetBookmark() != NO_BOOKMARK)
                variant << m_iconBookmark;
            else if (d->HasComment() || d->HasExtractedComments())
                variant << m_iconComment;
            else if (d->IsPreTranslated())
                variant << m_iconPreTranslated;
            else
                variant = wxNullVariant;
            break;
        }

        case Col_Source:
        {
            wxString orig;
#if wxCHECK_VERSION(3,1,1)
            if (d->HasContext())
            {
                // Work around a problem with GTK+'s coloring of markup that begins with colorizing :
                #ifdef __WXGTK__
                    #define MARKUP(x) L"\u200B" L##x
                #else
                    #define MARKUP(x) x
                #endif
                orig.Printf(MARKUP("[%s] %s"),
                            EscapeMarkup(d->GetContext()), EscapeMarkup(d->GetString()));
            }
            else
            {
                orig = EscapeMarkup(d->GetString());
            }
#else // wx 3.0
            if (d->HasContext())
                orig.Printf("[%s] %s", d->GetContext(), d->GetString());
            else
                orig = d->GetString();
#endif

            // FIXME: use syntax highlighting or typographic marks
            orig.Replace("\n", " ");

            // Add RTL Unicode mark to render bidi texts correctly
            if (m_appTextDir != m_sourceTextDir)
                variant = bidi::mark_direction(orig, m_sourceTextDir);
            else
                variant = orig;
            break;
        }

        case Col_Translation:
        {
            auto trans = d->GetTranslation();

            // FIXME: use syntax highlighting or typographic marks
            trans.Replace("\n", " ");

            // Add RTL Unicode mark to render bidi texts correctly
            if (m_appTextDir != m_transTextDir)
                variant = bidi::mark_direction(trans, m_transTextDir);
            else
                variant = trans;
            break;
        }

        default:
            variant.Clear();
            break;
    };
}

bool PoeditListCtrl::Model::SetValueByRow(const wxVariant&, unsigned, unsigned)
{
    wxFAIL_MSG("setting values in dataview not implemented");
    return false;
}

bool PoeditListCtrl::Model::GetAttrByRow(unsigned row, unsigned col, wxDataViewItemAttr& attr) const
{
    if (!m_catalog)
        return false;

    switch (col)
    {
        case Col_ID:
        {
            attr.SetColour(m_clrID);
            return true;
        }

        case Col_Source:
        case Col_Translation:
        {
            auto d = Item(row);
            if (d->GetValidity() == CatalogItem::Val_Invalid)
            {
                attr.SetColour(m_clrInvalid);
                return true;
            }
            else if (d->IsFuzzy())
            {
                attr.SetColour(m_clrFuzzy);
                return true;
            }
            else
            {
                return false;
            }
        }

        default:
            return false;
    }
}

void PoeditListCtrl::Model::CreateSortMap()
{
    // FIXME: Use native wxDataViewCtrl sorting instead

    int count = (int)m_catalog->GetCount();

    m_mapListToCatalog.resize(count);
    m_mapCatalogToList.resize(count);

    // First create identity mapping for the sort order.
    for ( int i = 0; i < count; i++ )
        m_mapListToCatalog[i] = i;

    // m_mapListToCatalog will hold our desired sort order. Sort it in place
    // now, using the desired sort criteria.
    CatalogItemsComparator comparator(*m_catalog, sortOrder);
    std::sort
    (
        m_mapListToCatalog.begin(),
        m_mapListToCatalog.end(),
        std::ref(comparator)
    );

    // Finally, construct m_mapCatalogToList to be the inverse mapping to
    // m_mapListToCatalog.
    for ( int i = 0; i < count; i++ )
        m_mapCatalogToList[m_mapListToCatalog[i]] = i;
}

PoeditListCtrl::PoeditListCtrl(wxWindow *parent, wxWindowID id, bool dispIDs)
     : wxDataViewCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE | wxDV_ROW_LINES | wxNO_BORDER, wxDefaultValidator, "translations list")
{
    m_displayIDs = dispIDs;

    m_appTextDir = (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) ? TextDirection::RTL : TextDirection::LTR;

    m_model.reset(new Model(m_appTextDir, GetDefaultAttributes()));
    AssociateModel(m_model.get());

    CreateColumns();

    UpdateHeaderAttrs();

    Bind(wxEVT_SIZE, &PoeditListCtrl::OnSize, this);
}

PoeditListCtrl::~PoeditListCtrl()
{
    sortOrder().Save();
}

void PoeditListCtrl::UpdateHeaderAttrs()
{
#ifdef __WXMSW__
    // Setup custom header font & color on Windows 10, where the default look is a bit odd
    if (IsWindows10OrGreater())
    {
        // Use the same text color as Explorer's headers use
        const wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
        if (theme)
        {
            wxUxThemeHandle hTheme(this->GetParent(), L"ItemsView::Header");
            COLORREF clr;
            HRESULT hr = theme->GetThemeColor(hTheme, HP_HEADERITEM, 0, TMT_TEXTCOLOR, &clr);
            if (SUCCEEDED(hr))
            {
                wxItemAttr headerAttr;
                headerAttr.SetTextColour(wxRGBToColour(clr));
                SetHeaderAttr(headerAttr);
            }
        }

        // Standard header has smaller height than Explorer's and it isn't
        // separated from the content well -- especially in HiDPI modes.
        // Match Explorer's header size too:
        int headerHeight = HiDPIScalingFactor() > 1.0 ? PX(26) : PX(25);
        GenericGetHeader()->SetMinSize(wxSize(-1, headerHeight));
    }
#endif // __WXMSW__
}

void PoeditListCtrl::SetCustomFont(wxFont font_)
{
    wxFont font(font_);

    if ( !font.IsOk() )
        font = GetDefaultAttributes().font;

    SetFont(font);

    UpdateHeaderAttrs();
    CreateColumns();
}

void PoeditListCtrl::SetDisplayLines(bool dl)
{
    m_displayIDs = dl;
    CreateColumns();
}

void PoeditListCtrl::CreateColumns()
{
    if (GetColumnCount() > 0)
        ClearColumns();

    m_colID = m_colIcon = m_colSource = m_colTrans = nullptr;

    Language srclang, lang;
    if (m_catalog)
    {
        srclang = m_catalog->GetSourceLanguage();
        lang = m_catalog->GetLanguage();
    }

    auto isRTL = lang.IsRTL();
#ifdef __WXMSW__
    // a quirk of wx API: if the current locale is RTL, the meaning of L and R is reversed
    if (m_appTextDir == TextDirection::RTL)
        isRTL = !isRTL;
#endif

    m_colIcon = AppendBitmapColumn(L"∙", Model::Col_Icon, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_CENTER, 0);

    wxString sourceTitle = srclang.IsValid()
                             ? wxString::Format(_(L"Source text — %s"), srclang.DisplayName())
                             : _("Source text");
    auto sourceRenderer = new wxDataViewTextRenderer();
    sourceRenderer->EnableEllipsize(wxELLIPSIZE_END);
#if wxCHECK_VERSION(3,1,1)
    sourceRenderer->EnableMarkup();
#endif
    m_colSource = new wxDataViewColumn(sourceTitle, sourceRenderer, Model::Col_Source, wxCOL_WIDTH_DEFAULT, wxALIGN_LEFT, 0);
    AppendColumn(m_colSource);

    if (m_model->m_catalog && m_model->m_catalog->HasCapability(Catalog::Cap::Translations))
    {
        wxString langname = lang.IsValid() ? lang.DisplayName() : _("unknown language");;
        wxString transTitle = wxString::Format(_(L"Translation — %s"), langname);
        auto transRenderer = new wxDataViewTextRenderer();
        transRenderer->EnableEllipsize(wxELLIPSIZE_END);
        m_colTrans = new wxDataViewColumn(transTitle, transRenderer, Model::Col_Translation, wxCOL_WIDTH_DEFAULT, isRTL ? wxALIGN_RIGHT : wxALIGN_LEFT, 0);
        AppendColumn(m_colTrans);
    }

    if (m_displayIDs)
    {
        m_colID = AppendTextColumn(_("ID"), Model::Col_ID, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_RIGHT, 0);
    }

    // wxDVC insists on having an expander column, but we really don't want one:
    auto fake = AppendTextColumn("", Model::Col_ID);
    fake->SetHidden(true);
    SetExpanderColumn(fake);

#ifdef __WXMSW__
    if (m_appTextDir == TextDirection::RTL)
        m_colSource->SetAlignment(wxALIGN_RIGHT);
#endif

    SizeColumns();

#ifdef __WXGTK__
    // wxGTK has delayed sizing computation, apparently
    CallAfter([=]{ SizeColumns(); });
#endif
}

void PoeditListCtrl::SizeColumns()
{
    int w = GetClientSize().x;

#if defined(__WXOSX__)
    w -= (GetColumnCount() - 1) * 3;
#elif defined(__WXGTK__)
    w -= 2;
#endif

    if (m_colIcon)
        w -= m_colIcon->GetWidth();

    if (m_colID)
    {
        w -= m_colID->GetWidth();
#ifdef __WXGTK__
        w += 2;
#endif
    }

    if (m_colTrans)
    {
        m_colSource->SetWidth(w / 2);
        m_colTrans->SetWidth(w - w / 2);
    }
    else
    {
        m_colSource->SetWidth(w);
    }
}

void PoeditListCtrl::CatalogChanged(const CatalogPtr& catalog)
{
    wxWindowUpdateLocker no_updates(this);

    const int oldCount = m_catalog ? m_catalog->GetCount() : 0;
    const int newCount = catalog ? catalog->GetCount() : 0;
    const bool isSameCatalog = (catalog == m_catalog);
    const bool sizeOrCatalogChanged = !isSameCatalog || (oldCount != newCount);

    SelectionPreserver preserve(!sizeOrCatalogChanged ? this : nullptr);

    // now read the new catalog:
    m_catalog = catalog;
    m_model->SetCatalog(catalog);

    if (!m_catalog)
        return;

    CreateColumns();

    if (sizeOrCatalogChanged && GetItemCount() > 0)
        CallAfter([=]{ SelectAndFocus(0); });
}

void PoeditListCtrl::Sort()
{
    if (!m_catalog)
        return;

    SelectionPreserver preserve(this);
    m_model->UpdateSort();
}

void PoeditListCtrl::OnSize(wxSizeEvent& event)
{
    wxWindowUpdateLocker lock(this);

    SizeColumns();
    event.Skip();
}
<|endoftext|>


<commit_before>#pragma once

#include <agency/detail/config.hpp>
#include <agency/detail/memory/unique_ptr.hpp>
#include <agency/cuda/memory/allocator.hpp>
#include <stdexcept>
#include <cassert>
#include <utility>
#include <type_traits>
#include <memory>

namespace agency
{
namespace detail
{

class bad_function_call : public std::exception
{
  public:
    virtual const char* what() const noexcept
    {
      return "bad_function_call: unique_function has no target";
    }
};

namespace unique_function_detail
{

__AGENCY_ANNOTATION
inline void throw_bad_function_call()
{
#ifdef __CUDA_ARCH__
  printf("bad_function_call: unique_function has no target\n");
  assert(0);
#else
  throw bad_function_call();
#endif
}

} // end unique_function_detail

template<class>
class unique_function;

template<class Result, class... Args>
class unique_function<Result(Args...)>
{
  public:
    using result_type = Result;

    __AGENCY_ANNOTATION
    unique_function() = default;

    __AGENCY_ANNOTATION
    unique_function(std::nullptr_t)
      : f_ptr_(nullptr)
    {}

    __AGENCY_ANNOTATION
    unique_function(unique_function&& other) = default;

    template<class Function>
    __AGENCY_ANNOTATION
    unique_function(Function&& f)
      : unique_function(std::allocator_arg, default_allocator<typename std::decay<Function>::type>{}, std::forward<Function>(f))
    {}

    template<class Alloc>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc, std::nullptr_t)
      : unique_function(nullptr)
    {}

    template<class Alloc>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc)
      : unique_function(std::allocator_arg, alloc, nullptr)
    {}

    template<class Alloc>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc, unique_function&& other)
      : f_ptr_(std::move(other.f_ptr_))
    {}

    template<class Alloc, class Function>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc, Function&& f)
      : f_ptr_(allocate_function_pointer(alloc, std::forward<Function>(f)))
    {}

    __AGENCY_ANNOTATION
    unique_function& operator=(unique_function&& other) = default;

    __AGENCY_ANNOTATION
    Result operator()(Args... args) const
    {
      if(!*this)
      {
        unique_function_detail::throw_bad_function_call();
      }

      return (*f_ptr_)(args...);
    }

    __AGENCY_ANNOTATION
    operator bool () const
    {
      return f_ptr_;
    }

  private:
    // this is the abstract base class for a type
    // which is both
    // 1. callable like a function and
    // 2. deallocates itself inside its destructor
    struct callable_self_deallocator_base
    {
      using self_deallocate_function_type = void(*)(callable_self_deallocator_base*);

      self_deallocate_function_type self_deallocate_function;

      template<class Function>
      __AGENCY_ANNOTATION
      callable_self_deallocator_base(Function callback)
        : self_deallocate_function(callback)
      {}

      __AGENCY_ANNOTATION
      virtual ~callable_self_deallocator_base()
      {
        self_deallocate_function(this);
      }

      __AGENCY_ANNOTATION
      virtual Result operator()(Args... args) const = 0;
    };

    template<class Function, class Alloc>
    struct callable : callable_self_deallocator_base
    {
      using super_t = callable_self_deallocator_base;
      using allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<callable>;

      mutable Function f_;

      template<class OtherFunction,
               class = typename std::enable_if<
                 std::is_constructible<Function,OtherFunction&&>::value
               >::type>
      __AGENCY_ANNOTATION
      callable(const Alloc& alloc, OtherFunction&& f)
        : super_t(deallocate),
          f_(std::forward<OtherFunction>(f))
      {}

      __AGENCY_ANNOTATION
      virtual Result operator()(Args... args) const
      {
        return f_(args...);
      }

      __AGENCY_ANNOTATION
      static void deallocate(callable_self_deallocator_base* ptr)
      {
        // upcast to the right type of pointer
        callable* self = static_cast<callable*>(ptr);

        // XXX seems like creating a new allocator here is cheating
        //     we should use some member allocator, but it's not clear where to put it
        allocator_type alloc_;
        alloc_.deallocate(self, 1);
      }
    };

    // this deleter calls the destructor of its argument but does not
    // deallocate the ptr
    // T will deallocate itself inside ~T()
    struct self_deallocator_deleter
    {
      template<class T>
      __AGENCY_ANNOTATION
      void operator()(T* ptr) const
      {
        ptr->~T();
      }
    };

    using function_pointer = detail::unique_ptr<callable_self_deallocator_base, self_deallocator_deleter>;

    template<class Alloc, class Function>
    __AGENCY_ANNOTATION
    static function_pointer allocate_function_pointer(const Alloc& alloc, Function&& f)
    {
      using concrete_function_type = callable<typename std::decay<Function>::type, Alloc>;
      return agency::detail::allocate_unique_with_deleter<concrete_function_type>(alloc, self_deallocator_deleter(), alloc, std::forward<Function>(f));
    }

    template<class T>
    struct default_allocator
    {
      using value_type = T;

      __AGENCY_ANNOTATION
      default_allocator() = default;

      __AGENCY_ANNOTATION
      default_allocator(const default_allocator&) = default;

      template<class U>
      __AGENCY_ANNOTATION
      default_allocator(const default_allocator<U>&) {}

      // XXX we have to implement this member function superfluously because
      //     agency::detail::allocate_unique calls it directly instead of using std::allocator_traits
      template<class U, class... OtherArgs>
      __AGENCY_ANNOTATION
      void construct(U* ptr, OtherArgs&&... args)
      {
        ::new(ptr) U(std::forward<OtherArgs>(args)...);
      }

      __AGENCY_ANNOTATION
      value_type* allocate(size_t n)
      {
#ifdef __CUDA_ARCH__
        agency::cuda::detail::allocator<T> alloc;
#else
        std::allocator<T> alloc;
#endif
        return alloc.allocate(n);
      }

      __AGENCY_ANNOTATION
      void deallocate(value_type* ptr, std::size_t n)
      {
#ifdef __CUDA_ARCH__
        agency::cuda::detail::allocator<value_type> alloc;
#else
        std::allocator<value_type> alloc;
#endif
        alloc.deallocate(ptr, n);
      }
    };

    function_pointer f_ptr_;
};

} // end detail
} // end agency

<commit_msg>Eliminate dependencies on the cuda-specific headers from unique_function<commit_after>#pragma once

#include <agency/detail/config.hpp>
#include <agency/detail/memory/unique_ptr.hpp>
#include <stdexcept>
#include <cassert>
#include <utility>
#include <type_traits>
#include <memory>

namespace agency
{
namespace detail
{

class bad_function_call : public std::exception
{
  public:
    virtual const char* what() const noexcept
    {
      return "bad_function_call: unique_function has no target";
    }
};

namespace unique_function_detail
{

__AGENCY_ANNOTATION
inline void throw_bad_function_call()
{
#ifdef __CUDA_ARCH__
  printf("bad_function_call: unique_function has no target\n");
  assert(0);
#else
  throw bad_function_call();
#endif
}

} // end unique_function_detail

template<class>
class unique_function;

template<class Result, class... Args>
class unique_function<Result(Args...)>
{
  public:
    using result_type = Result;

    __AGENCY_ANNOTATION
    unique_function() = default;

    __AGENCY_ANNOTATION
    unique_function(std::nullptr_t)
      : f_ptr_(nullptr)
    {}

    __AGENCY_ANNOTATION
    unique_function(unique_function&& other) = default;

    template<class Function>
    __AGENCY_ANNOTATION
    unique_function(Function&& f)
      : unique_function(std::allocator_arg, default_allocator<typename std::decay<Function>::type>{}, std::forward<Function>(f))
    {}

    template<class Alloc>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc, std::nullptr_t)
      : unique_function(nullptr)
    {}

    template<class Alloc>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc)
      : unique_function(std::allocator_arg, alloc, nullptr)
    {}

    template<class Alloc>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc, unique_function&& other)
      : f_ptr_(std::move(other.f_ptr_))
    {}

    template<class Alloc, class Function>
    __AGENCY_ANNOTATION
    unique_function(std::allocator_arg_t, const Alloc& alloc, Function&& f)
      : f_ptr_(allocate_function_pointer(alloc, std::forward<Function>(f)))
    {}

    __AGENCY_ANNOTATION
    unique_function& operator=(unique_function&& other) = default;

    __AGENCY_ANNOTATION
    Result operator()(Args... args) const
    {
      if(!*this)
      {
        unique_function_detail::throw_bad_function_call();
      }

      return (*f_ptr_)(args...);
    }

    __AGENCY_ANNOTATION
    operator bool () const
    {
      return f_ptr_;
    }

  private:
    // this is the abstract base class for a type
    // which is both
    // 1. callable like a function and
    // 2. deallocates itself inside its destructor
    struct callable_self_deallocator_base
    {
      using self_deallocate_function_type = void(*)(callable_self_deallocator_base*);

      self_deallocate_function_type self_deallocate_function;

      template<class Function>
      __AGENCY_ANNOTATION
      callable_self_deallocator_base(Function callback)
        : self_deallocate_function(callback)
      {}

      __AGENCY_ANNOTATION
      virtual ~callable_self_deallocator_base()
      {
        self_deallocate_function(this);
      }

      __AGENCY_ANNOTATION
      virtual Result operator()(Args... args) const = 0;
    };

    template<class Function, class Alloc>
    struct callable : callable_self_deallocator_base
    {
      using super_t = callable_self_deallocator_base;
      using allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<callable>;

      mutable Function f_;

      template<class OtherFunction,
               class = typename std::enable_if<
                 std::is_constructible<Function,OtherFunction&&>::value
               >::type>
      __AGENCY_ANNOTATION
      callable(const Alloc& alloc, OtherFunction&& f)
        : super_t(deallocate),
          f_(std::forward<OtherFunction>(f))
      {}

      __AGENCY_ANNOTATION
      virtual Result operator()(Args... args) const
      {
        return f_(args...);
      }

      __AGENCY_ANNOTATION
      static void deallocate(callable_self_deallocator_base* ptr)
      {
        // upcast to the right type of pointer
        callable* self = static_cast<callable*>(ptr);

        // XXX seems like creating a new allocator here is cheating
        //     we should use some member allocator, but it's not clear where to put it
        allocator_type alloc_;
        alloc_.deallocate(self, 1);
      }
    };

    // this deleter calls the destructor of its argument but does not
    // deallocate the ptr
    // T will deallocate itself inside ~T()
    struct self_deallocator_deleter
    {
      template<class T>
      __AGENCY_ANNOTATION
      void operator()(T* ptr) const
      {
        ptr->~T();
      }
    };

    using function_pointer = detail::unique_ptr<callable_self_deallocator_base, self_deallocator_deleter>;

    template<class Alloc, class Function>
    __AGENCY_ANNOTATION
    static function_pointer allocate_function_pointer(const Alloc& alloc, Function&& f)
    {
      using concrete_function_type = callable<typename std::decay<Function>::type, Alloc>;
      return agency::detail::allocate_unique_with_deleter<concrete_function_type>(alloc, self_deallocator_deleter(), alloc, std::forward<Function>(f));
    }

    template<class T>
    struct default_allocator
    {
      using value_type = T;

      __AGENCY_ANNOTATION
      default_allocator() = default;

      __AGENCY_ANNOTATION
      default_allocator(const default_allocator&) = default;

      template<class U>
      __AGENCY_ANNOTATION
      default_allocator(const default_allocator<U>&) {}

      // XXX we have to implement this member function superfluously because
      //     agency::detail::allocate_unique calls it directly instead of using std::allocator_traits
      template<class U, class... OtherArgs>
      __AGENCY_ANNOTATION
      void construct(U* ptr, OtherArgs&&... args)
      {
        ::new(ptr) U(std::forward<OtherArgs>(args)...);
      }

      value_type* allocate(size_t n)
      {
        std::allocator<T> alloc;
        return alloc.allocate(n);
      }

      void deallocate(value_type* ptr, std::size_t n)
      {
        std::allocator<value_type> alloc;
        alloc.deallocate(ptr, n);
      }
    };

    function_pointer f_ptr_;
};

} // end detail
} // end agency

<|endoftext|>