<commit_before>/***************************************************************************
 *   Copyright (C) 2006 by FThauer FHammer   *
 *   f.thauer@web.de   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "chattools.h"
#include "session.h"
#include "configfile.h"
#include "gametablestylereader.h"
#include "gamelobbydialogimpl.h"
#include <iostream>

using namespace std;

ChatTools::ChatTools(QLineEdit* l, ConfigFile *c, ChatType ct, QTextBrowser *b, QStandardItemModel *m, gameLobbyDialogImpl *lo) : nickAutoCompletitionCounter(0), myLineEdit(l), myNickListModel(m), myNickStringList(NULL), myTextBrowser(b), myChatType(ct), myConfig(c), myNick(""), myLobby(lo)
{
    myNick = QString::fromUtf8(myConfig->readConfigString("MyName").c_str());
    ignoreList = myConfig->readConfigStringList("PlayerIgnoreList");

}

ChatTools::~ChatTools()
{
}

void ChatTools::sendMessage() {

    if(myLineEdit->text().size() && mySession) {
        fillChatLinesHistory(myLineEdit->text());
        if(myChatType == INGAME_CHAT) {
            mySession->sendGameChatMessage(myLineEdit->text().toUtf8().constData());
        }
        else {
            mySession->sendLobbyChatMessage(myLineEdit->text().toUtf8().constData());
        }
        myLineEdit->setText("");
    }
}

void ChatTools::receiveMessage(QString playerName, QString message) {

    if(myTextBrowser) {

        message = message.replace("<","<");
        message = message.replace(">",">");

        //refresh myNick if it was changed during runtime
        myNick = QString::fromUtf8(myConfig->readConfigString("MyName").c_str());

        QString tempMsg;

        if(myChatType == INET_LOBBY_CHAT && playerName == "(chat bot)" && message.startsWith(myNick)) {

            tempMsg = QString(""+message+"");
            //play beep sound only in INET-lobby-chat
            if(myLobby->isVisible() && myConfig->readConfigInt("PlayLobbyChatNotification")) {
                myLobby->getMyW()->getMySDLPlayer()->playSound("lobbychatnotify",0);
            }
        }
        else if(message.contains(myNick, Qt::CaseInsensitive)) {

            switch (myChatType) {
            case INET_LOBBY_CHAT: {
                    tempMsg = QString("palette().link().color().name()+";\">"+message+"");
                    //play beep sound only in INET-lobby-chat
                    //						TODO dont play when message is from yourself
                    if(myLobby->isVisible() && myConfig->readConfigInt("PlayLobbyChatNotification")) {
                        myLobby->getMyW()->getMySDLPlayer()->playSound("lobbychatnotify",0);
                    }
                }
                break;
            case LAN_LOBBY_CHAT: tempMsg = QString(""+message+"");
                break;
            case INGAME_CHAT: tempMsg = QString("getChatTextNickNotifyColor()+";\">"+message+"");
                break;
            default: tempMsg = message;
            }
        }
        else if(playerName == myNick) {
            switch (myChatType) {
            case INET_LOBBY_CHAT: tempMsg = QString("palette().link().color().name()+";\">"+message+"");
                break;
            case LAN_LOBBY_CHAT: tempMsg = QString(""+message+"");
                break;
            case INGAME_CHAT: tempMsg = QString("getChatLogTextColor()+";\">"+message+"");
                break;
            default: tempMsg = message;
            }
        }
        else {
            switch (myChatType) {
            case INET_LOBBY_CHAT: tempMsg = QString("palette().text().color().name()+";\">"+message+"");
                break;
            case LAN_LOBBY_CHAT: tempMsg = QString(""+message+"");
                break;
            case INGAME_CHAT: tempMsg = QString("getChatLogTextColor()+";\">"+message+"");
                break;
            default: tempMsg = message;
            }

        }

        bool nickFoundOnIgnoreList = false;
        list<std::string>::iterator it1;
        for(it1=ignoreList.begin(); it1 != ignoreList.end(); it1++) {

            if(playerName == QString::fromUtf8(it1->c_str())) {
                nickFoundOnIgnoreList = true;
            }
        }

        if(!nickFoundOnIgnoreList) {
            myTextBrowser->append(playerName + ": " + tempMsg);
        }
    }
}

void ChatTools::clearChat() {

    if(myTextBrowser)
        myTextBrowser->clear();
}

void ChatTools::checkInputLength(QString string) {

    if(string.toUtf8().length() > 120) myLineEdit->setMaxLength(string.length());
}

void ChatTools::fillChatLinesHistory(QString fillString) {

    chatLinesHistory << fillString;
    if(chatLinesHistory.size() > 50) chatLinesHistory.removeFirst();

}

void ChatTools::showChatHistoryIndex(int index) {

    if(index <= chatLinesHistory.size()) {

        // 		cout << chatLinesHistory.size() << " : " <<  index << endl;
        if(index > 0)
            myLineEdit->setText(chatLinesHistory.at(chatLinesHistory.size()-(index)));
        else
            myLineEdit->setText("");
    }
}

void ChatTools::nickAutoCompletition() {

    QString myChatString = myLineEdit->text();
    QStringList myChatStringList = myChatString.split(" ");

    QStringList matchStringList;

    if(nickAutoCompletitionCounter == 0) {

        if(myNickListModel) {
            int it = 0;
            while (myNickListModel->item(it)) {
                QString text = myNickListModel->item(it, 0)->data(Qt::DisplayRole).toString();
                if(text.startsWith(myChatStringList.last(), Qt::CaseInsensitive) && myChatStringList.last() != "") {
                    matchStringList << text;
                }
                ++it;
            }
        }

        if(!myNickStringList.isEmpty()) {

            QStringListIterator it(myNickStringList);
            while (it.hasNext()) {
                QString next = it.next();
                if (next.startsWith(myChatStringList.last(), Qt::CaseInsensitive) && myChatStringList.last() != "")
                    matchStringList << next;
            }
        }
    }

    if(!matchStringList.isEmpty() || nickAutoCompletitionCounter > 0) {

        myChatStringList.removeLast();

        // 		cout << nickAutoCompletitionCounter << endl;

        if(nickAutoCompletitionCounter == 0) {
            //first one
            lastChatString = myChatStringList.join(" ");
            lastMatchStringList = matchStringList;
        }

        if(nickAutoCompletitionCounter == lastMatchStringList.size()) nickAutoCompletitionCounter = 0;

        // 		cout << nickAutoCompletitionCounter << "\n";

        if(lastChatString == "")
            myLineEdit->setText(lastMatchStringList.at(nickAutoCompletitionCounter)+": ");
        else
            myLineEdit->setText(lastChatString+" "+lastMatchStringList.at(nickAutoCompletitionCounter)+" ");

        nickAutoCompletitionCounter++;
    }
}

void ChatTools::setChatTextEdited() {

    nickAutoCompletitionCounter = 0;
}

void ChatTools::refreshIgnoreList()
{
    ignoreList = myConfig->readConfigStringList("PlayerIgnoreList");
}
<commit_msg>add /me messages to chattools (thx drull)<commit_after>/***************************************************************************
 *   Copyright (C) 2006 by FThauer FHammer   *
 *   f.thauer@web.de   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "chattools.h"
#include "session.h"
#include "configfile.h"
#include "gametablestylereader.h"
#include "gamelobbydialogimpl.h"
#include <iostream>

using namespace std;

ChatTools::ChatTools(QLineEdit* l, ConfigFile *c, ChatType ct, QTextBrowser *b, QStandardItemModel *m, gameLobbyDialogImpl *lo) : nickAutoCompletitionCounter(0), myLineEdit(l), myNickListModel(m), myNickStringList(NULL), myTextBrowser(b), myChatType(ct), myConfig(c), myNick(""), myLobby(lo)
{
    myNick = QString::fromUtf8(myConfig->readConfigString("MyName").c_str());
    ignoreList = myConfig->readConfigStringList("PlayerIgnoreList");

}

ChatTools::~ChatTools()
{
}

void ChatTools::sendMessage() {

    if(myLineEdit->text().size() && mySession) {
        fillChatLinesHistory(myLineEdit->text());
        if(myChatType == INGAME_CHAT) {
            mySession->sendGameChatMessage(myLineEdit->text().toUtf8().constData());
        }
        else {
            mySession->sendLobbyChatMessage(myLineEdit->text().toUtf8().constData());
        }
        myLineEdit->setText("");
    }
}

void ChatTools::receiveMessage(QString playerName, QString message) {

    if(myTextBrowser) {

        message = message.replace("<","<");
        message = message.replace(">",">");

        //refresh myNick if it was changed during runtime
        myNick = QString::fromUtf8(myConfig->readConfigString("MyName").c_str());

        QString tempMsg;

        if(myChatType == INET_LOBBY_CHAT && playerName == "(chat bot)" && message.startsWith(myNick)) {

            tempMsg = QString(""+message+"");
            //play beep sound only in INET-lobby-chat
            if(myLobby->isVisible() && myConfig->readConfigInt("PlayLobbyChatNotification")) {
                myLobby->getMyW()->getMySDLPlayer()->playSound("lobbychatnotify",0);
            }
        }
        else if(message.contains(myNick, Qt::CaseInsensitive)) {

            switch (myChatType) {
            case INET_LOBBY_CHAT: {
                    tempMsg = QString("palette().link().color().name()+";\">"+message+"");
                    //play beep sound only in INET-lobby-chat
                    //						TODO dont play when message is from yourself
                    if(myLobby->isVisible() && myConfig->readConfigInt("PlayLobbyChatNotification")) {
                        myLobby->getMyW()->getMySDLPlayer()->playSound("lobbychatnotify",0);
                    }
                }
                break;
            case LAN_LOBBY_CHAT: tempMsg = QString(""+message+"");
                break;
            case INGAME_CHAT: tempMsg = QString("getChatTextNickNotifyColor()+";\">"+message+"");
                break;
            default: tempMsg = message;
            }
        }
        else if(playerName == myNick) {
            switch (myChatType) {
            case INET_LOBBY_CHAT: tempMsg = QString("palette().link().color().name()+";\">"+message+"");
                break;
            case LAN_LOBBY_CHAT: tempMsg = QString(""+message+"");
                break;
            case INGAME_CHAT: tempMsg = QString("getChatLogTextColor()+";\">"+message+"");
                break;
            default: tempMsg = message;
            }
        }
        else {
            switch (myChatType) {
            case INET_LOBBY_CHAT: tempMsg = QString("palette().text().color().name()+";\">"+message+"");
                break;
            case LAN_LOBBY_CHAT: tempMsg = QString(""+message+"");
                break;
            case INGAME_CHAT: tempMsg = QString("getChatLogTextColor()+";\">"+message+"");
                break;
            default: tempMsg = message;
            }

        }

        bool nickFoundOnIgnoreList = false;
        list<std::string>::iterator it1;
        for(it1=ignoreList.begin(); it1 != ignoreList.end(); it1++) {

            if(playerName == QString::fromUtf8(it1->c_str())) {
                nickFoundOnIgnoreList = true;
            }
        }

        if(!nickFoundOnIgnoreList) {

            if(message.indexOf(QString("/me"))==0) {
                myTextBrowser->append(tempMsg.replace("/me","*"+playerName+" ")+"");
            }
            else {
                myTextBrowser->append(playerName + ": " + tempMsg);
            }
        }
    }
}

void ChatTools::clearChat() {

    if(myTextBrowser)
        myTextBrowser->clear();
}

void ChatTools::checkInputLength(QString string) {

    if(string.toUtf8().length() > 120) myLineEdit->setMaxLength(string.length());
}

void ChatTools::fillChatLinesHistory(QString fillString) {

    chatLinesHistory << fillString;
    if(chatLinesHistory.size() > 50) chatLinesHistory.removeFirst();

}

void ChatTools::showChatHistoryIndex(int index) {

    if(index <= chatLinesHistory.size()) {

        // 		cout << chatLinesHistory.size() << " : " <<  index << endl;
        if(index > 0)
            myLineEdit->setText(chatLinesHistory.at(chatLinesHistory.size()-(index)));
        else
            myLineEdit->setText("");
    }
}

void ChatTools::nickAutoCompletition() {

    QString myChatString = myLineEdit->text();
    QStringList myChatStringList = myChatString.split(" ");

    QStringList matchStringList;

    if(nickAutoCompletitionCounter == 0) {

        if(myNickListModel) {
            int it = 0;
            while (myNickListModel->item(it)) {
                QString text = myNickListModel->item(it, 0)->data(Qt::DisplayRole).toString();
                if(text.startsWith(myChatStringList.last(), Qt::CaseInsensitive) && myChatStringList.last() != "") {
                    matchStringList << text;
                }
                ++it;
            }
        }

        if(!myNickStringList.isEmpty()) {

            QStringListIterator it(myNickStringList);
            while (it.hasNext()) {
                QString next = it.next();
                if (next.startsWith(myChatStringList.last(), Qt::CaseInsensitive) && myChatStringList.last() != "")
                    matchStringList << next;
            }
        }
    }

    if(!matchStringList.isEmpty() || nickAutoCompletitionCounter > 0) {

        myChatStringList.removeLast();

        // 		cout << nickAutoCompletitionCounter << endl;

        if(nickAutoCompletitionCounter == 0) {
            //first one
            lastChatString = myChatStringList.join(" ");
            lastMatchStringList = matchStringList;
        }

        if(nickAutoCompletitionCounter == lastMatchStringList.size()) nickAutoCompletitionCounter = 0;

        // 		cout << nickAutoCompletitionCounter << "\n";

        if(lastChatString == "")
            myLineEdit->setText(lastMatchStringList.at(nickAutoCompletitionCounter)+": ");
        else
            myLineEdit->setText(lastChatString+" "+lastMatchStringList.at(nickAutoCompletitionCounter)+" ");

        nickAutoCompletitionCounter++;
    }
}

void ChatTools::setChatTextEdited() {

    nickAutoCompletitionCounter = 0;
}

void ChatTools::refreshIgnoreList()
{
    ignoreList = myConfig->readConfigStringList("PlayerIgnoreList");
}
<|endoftext|>


<commit_before>
#pragma once

#include "numerics/frequency_analysis.hpp"

#include <algorithm>
#include <functional>
#include <vector>

#include "base/status.hpp"
#include "base/tags.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/hilbert.hpp"
#include "numerics/poisson_series_basis.hpp"
#include "numerics/root_finders.hpp"
#include "numerics/unbounded_arrays.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace numerics {
namespace frequency_analysis {
namespace internal_frequency_analysis {

using base::Error;
using base::Status;
using base::uninitialized;
using geometry::Hilbert;
using geometry::Vector;
using quantities::Inverse;
using quantities::IsFinite;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;

// Appends basis elements for |ω| to |basis| and |basis_subspaces|.  Returns the
// number of elements that were appended.
template<int aperiodic_degree, int periodic_degree,
         typename BasisSeries>
int MakeBasis(std::optional<AngularFrequency> const& ω,
              Instant const& t_min,
              Instant const& t_max,
              std::vector<BasisSeries>& basis,
              std::vector<PoissonSeriesSubspace>& basis_subspaces) {
  if (ω.value() == AngularFrequency{}) {
    using Generator =
        PoissonSeriesBasisGenerator<BasisSeries, aperiodic_degree>;
    auto const ω_basis = Generator::Basis(t_min, t_max);
    auto const ω_basis_subspaces = Generator::Subspaces();
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
    return std::tuple_size_v<decltype(ω_basis)>;
  } else {
    using Generator = PoissonSeriesBasisGenerator<BasisSeries, periodic_degree>;
    auto const ω_basis = Generator::Basis(ω.value(), t_min, t_max);
    auto const ω_basis_subspaces = Generator::Subspaces(ω.value());
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
    return std::tuple_size_v<decltype(ω_basis)>;
  }
}

//TODO(phl):comment, parameter names
template<typename BasisSeries,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
Status NormalGramSchmidtStep(
    BasisSeries const& aₘ,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree, Evaluator> const& weight,
    Instant const& t_min,
    Instant const& t_max,
    std::vector<PoissonSeriesSubspace> const& basis_subspaces,
    std::vector<BasisSeries> const& q,
    BasisSeries& qₘ,
    UnboundedVector<double>& rₘ) {
  //TODO(phl):comment
  // This code follows Björk, Numerics of Gram-Schmidt Orthogonalization,
  // Algorithm 6.1.  It processes one column of q and r at a time.

  static constexpr double α = 0.5;
  int const m = q.size();

  BasisSeries q̂ₘ = aₘ;

  // Formal integration works for a single basis element.
  double q̂ₘ_norm =
      Sqrt((PointwiseInnerProduct(q̂ₘ, q̂ₘ) * weight).Integrate(t_min, t_max) /
           (t_max - t_min));

  // Loop on p.
  BasisSeries previous_q̂ₘ = q̂ₘ;
  double previous_q̂ₘ_norm;
  do {
    previous_q̂ₘ = q̂ₘ;
    previous_q̂ₘ_norm = q̂ₘ_norm;
    for (int i = 0; i < m; ++i) {
      if (!PoissonSeriesSubspace::orthogonal(basis_subspaces[i],
                                             basis_subspaces[m])) {
        double const sᵖₘ =
            InnerProduct(q[i], previous_q̂ₘ, weight, t_min, t_max);
        q̂ₘ -= sᵖₘ * q[i];
        rₘ[i] += sᵖₘ;
      }
    }
    q̂ₘ_norm = q̂ₘ.Norm(weight, t_min, t_max);

    if (!IsFinite(q̂ₘ_norm)) {
      return Status(Error::OUT_OF_RANGE, u8"Unable to compute q̂ₘ_norm");
    }
  } while (q̂ₘ_norm < α * previous_q̂ₘ_norm);

  // Fill the result.
  qₘ = q̂ₘ / q̂ₘ_norm;
  rₘ[m] = q̂ₘ_norm;

  return Status::OK;
}

//TODO(phl):comment
template<typename Function, typename BasisSeries, typename Norm,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
Status AugmentedGramSchmidtStep(
    Function const& aₘ,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree, Evaluator> const& weight,
    Instant const& t_min,
    Instant const& t_max,
    std::vector<BasisSeries> const& q,
    UnboundedVector<Norm>& rₘ) {
  //TODO(phl):comment
  // This code follows Björk, Numerics of Gram-Schmidt Orthogonalization,
  // Algorithm 6.1.  It processes one column of q and r at a time.

  static constexpr double α = 0.5;
  int const m = q.size();

  Function q̂ₘ = aₘ;
  Norm q̂ₘ_norm = q̂ₘ.Norm(weight, t_min, t_max);

  // Loop on p.
  Function previous_q̂ₘ = q̂ₘ;
  Norm previous_q̂ₘ_norm;
  do {
    previous_q̂ₘ = q̂ₘ;
    previous_q̂ₘ_norm = q̂ₘ_norm;
    for (int i = 0; i < m; ++i) {
      Norm const sᵖₘ = InnerProduct(q[i], previous_q̂ₘ, weight, t_min, t_max);
      q̂ₘ -= sᵖₘ * q[i];
      rₘ[i] += sᵖₘ;
    }
    q̂ₘ_norm = q̂ₘ.Norm(weight, t_min, t_max);

    if (!IsFinite(q̂ₘ_norm)) {
      return Status(Error::OUT_OF_RANGE, u8"Unable to compute q̂ₘ_norm");
    }
  } while (q̂ₘ_norm < α * previous_q̂ₘ_norm);

  // Fill the result.
  rₘ[m] = q̂ₘ_norm;

  return Status::OK;
}

template<typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
AngularFrequency PreciseMode(
    Interval<AngularFrequency> const& fft_mode,
    Function const& function,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree,
                  Evaluator> const& weight) {
  auto const weighted_function = weight * function;
  auto const weighted_function_spectrum = weighted_function.FourierTransform();

  auto power =
      [&weighted_function_spectrum](AngularFrequency const& ω) {
        return weighted_function_spectrum(ω).Norm²();
      };

  return Brent(power,
               fft_mode.min,
               fft_mode.max,
               std::greater<>());
}

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
Projection(Function const& function,
           AngularFrequency const& ω,
           PoissonSeries<double,
                         aperiodic_wdegree, periodic_wdegree,
                         Evaluator> const& weight,
           Instant const& t_min,
           Instant const& t_max) {
  std::optional<AngularFrequency> optional_ω = ω;

  // A calculator that returns optional_ω once and then stops.
  auto angular_frequency_calculator = [&optional_ω](auto const& residual) {
    auto const result = optional_ω;
    optional_ω = std::nullopt;
    return result;
  };

  return IncrementalProjection<aperiodic_degree, periodic_degree>(
      function,
      angular_frequency_calculator,
      weight,
      t_min, t_max);
}

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         typename AngularFrequencyCalculator,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
IncrementalProjection(Function const& function,
                      AngularFrequencyCalculator const& calculator,
                      PoissonSeries<double,
                                    aperiodic_wdegree, periodic_wdegree,
                                    Evaluator> const& weight,
                      Instant const& t_min,
                      Instant const& t_max) {
  using Value = std::invoke_result_t<Function, Instant>;
  using Norm = typename Hilbert<Value>::NormType;
  using Normalized = typename Hilbert<Value>::NormalizedType;
  using BasisSeries = PoissonSeries<Normalized,
                                    aperiodic_degree, periodic_degree,
                                    Evaluator>;
  using ResultSeries = PoissonSeries<Value,
                                     aperiodic_degree, periodic_degree,
                                     Evaluator>;

  Instant const& t0 = weight.origin();
  auto const basis_zero = static_cast<
      typename BasisSeries::AperiodicPolynomial>(
      typename PoissonSeries<Normalized, 0, 0, Evaluator>::AperiodicPolynomial(
          {Normalized{}}, t0));
  auto const result_zero =
      static_cast<typename ResultSeries::AperiodicPolynomial>(
          typename PoissonSeries<Value, 0, 0, Evaluator>::AperiodicPolynomial(
              {Value{}}, t0));

  std::optional<AngularFrequency> ω = calculator(function);
  CHECK(ω.has_value());

  std::vector<BasisSeries> basis;
  // The Poisson series basis[k] belongs to the subspace basis_subspaces[k];
  // this remains true after orthonormalization, i.e., q[k] belongs to the
  // subspace basis_subspaces[k] below.
  std::vector<PoissonSeriesSubspace> basis_subspaces;

  int basis_size = MakeBasis<aperiodic_degree, periodic_degree>(
      ω, t_min, t_max, basis, basis_subspaces);

  // This is logically Q in the QR decomposition of basis.
  std::vector<BasisSeries> q;

  // This is logically R in the QR decomposition of basis.
  UnboundedUpperTriangularMatrix<double> r(basis_size, uninitialized);

  ResultSeries F(result_zero, {{}});

  // The input function with a degree suitable for the augmented Gram-Schmidt
  // step.
  auto const augmented_function = function - F;

  int m_begin = 0;
  for (;;) {
    for (int m = m_begin; m < basis_size; ++m) {
      BasisSeries qₘ(basis_zero, {{}});
      UnboundedVector<double> rₘ(m + 1);

      auto const status = NormalGramSchmidtStep(/*aₘ=*/basis[m],
                                                weight, t_min, t_max,
                                                basis_subspaces, q,
                                                qₘ, rₘ);
      if (!status.ok()) {
        return F;
      }

      for (int i = 0; i <=m; ++i) {
        r[i][m] = rₘ[i];
      }
      q.push_back(qₘ);
      DCHECK_EQ(m + 1, q.size());
    }

    UnboundedVector<Norm> z_ρ(basis_size + 1);
    auto const status = AugmentedGramSchmidtStep(/*aₘ=*/augmented_function,
                                                 weight, t_min, t_max,
                                                 q,
                                                 /*rₘ=*/z_ρ);
    if (!status.ok()) {
      return F;
    }

    LOG(ERROR) << "ρ: " << z_ρ[z_ρ.size() - 1];
    z_ρ.EraseToEnd(z_ρ.size() - 1);
    auto const x = BackSubstitution(r, z_ρ);

    F = ResultSeries(result_zero, {{}});
    auto f = augmented_function;
    for (int i = 0; i < x.size(); ++i) {
      auto const x_basis = x[i] * basis[i];
      F += x_basis;
      f -= x_basis;
    }

    ω = calculator(f);
    if (!ω.has_value()) {
      return F;
    }

    int const ω_basis_size = MakeBasis<aperiodic_degree, periodic_degree>(
        ω, t_min, t_max, basis, basis_subspaces);
    m_begin = basis_size;
    basis_size += ω_basis_size;
    r.Extend(ω_basis_size, uninitialized);
  }
}

}  // namespace internal_frequency_analysis
}  // namespace frequency_analysis
}  // namespace numerics
}  // namespace principia
<commit_msg>Traces.<commit_after>
#pragma once

#include "numerics/frequency_analysis.hpp"

#include <algorithm>
#include <functional>
#include <vector>

#include "base/status.hpp"
#include "base/tags.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/hilbert.hpp"
#include "mathematica/mathematica.hpp"
#include "numerics/poisson_series_basis.hpp"
#include "numerics/root_finders.hpp"
#include "numerics/unbounded_arrays.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace numerics {
namespace frequency_analysis {
namespace internal_frequency_analysis {

using base::Error;
using base::Status;
using base::uninitialized;
using geometry::Hilbert;
using geometry::Vector;
using quantities::Inverse;
using quantities::IsFinite;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;
using quantities::si::Metre;
using quantities::si::Radian;
using quantities::si::Second;

// Appends basis elements for |ω| to |basis| and |basis_subspaces|.  Returns the
// number of elements that were appended.
template<int aperiodic_degree, int periodic_degree,
         typename BasisSeries>
int MakeBasis(std::optional<AngularFrequency> const& ω,
              Instant const& t_min,
              Instant const& t_max,
              std::vector<BasisSeries>& basis,
              std::vector<PoissonSeriesSubspace>& basis_subspaces) {
  if (ω.value() == AngularFrequency{}) {
    using Generator =
        PoissonSeriesBasisGenerator<BasisSeries, aperiodic_degree>;
    auto const ω_basis = Generator::Basis(t_min, t_max);
    auto const ω_basis_subspaces = Generator::Subspaces();
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
    return std::tuple_size_v<decltype(ω_basis)>;
  } else {
    using Generator = PoissonSeriesBasisGenerator<BasisSeries, periodic_degree>;
    auto const ω_basis = Generator::Basis(ω.value(), t_min, t_max);
    auto const ω_basis_subspaces = Generator::Subspaces(ω.value());
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
    return std::tuple_size_v<decltype(ω_basis)>;
  }
}

//TODO(phl):comment, parameter names
template<typename BasisSeries,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
Status NormalGramSchmidtStep(
    BasisSeries const& aₘ,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree, Evaluator> const& weight,
    Instant const& t_min,
    Instant const& t_max,
    std::vector<PoissonSeriesSubspace> const& basis_subspaces,
    std::vector<BasisSeries> const& q,
    BasisSeries& qₘ,
    UnboundedVector<double>& rₘ) {
  //TODO(phl):comment
  // This code follows Björk, Numerics of Gram-Schmidt Orthogonalization,
  // Algorithm 6.1.  It processes one column of q and r at a time.

  static constexpr double α = 0.5;
  int const m = q.size();

  BasisSeries q̂ₘ = aₘ;

  // Formal integration works for a single basis element.
  double q̂ₘ_norm =
      Sqrt((PointwiseInnerProduct(q̂ₘ, q̂ₘ) * weight).Integrate(t_min, t_max) /
           (t_max - t_min));

  // Loop on p.
  BasisSeries previous_q̂ₘ = q̂ₘ;
  double previous_q̂ₘ_norm;
  do {
    previous_q̂ₘ = q̂ₘ;
    previous_q̂ₘ_norm = q̂ₘ_norm;
    for (int i = 0; i < m; ++i) {
      if (!PoissonSeriesSubspace::orthogonal(basis_subspaces[i],
                                             basis_subspaces[m])) {
        double const sᵖₘ =
            InnerProduct(q[i], previous_q̂ₘ, weight, t_min, t_max);
        q̂ₘ -= sᵖₘ * q[i];
        rₘ[i] += sᵖₘ;
      }
    }
    q̂ₘ_norm = q̂ₘ.Norm(weight, t_min, t_max);

    if (!IsFinite(q̂ₘ_norm)) {
      return Status(Error::OUT_OF_RANGE, u8"Unable to compute q̂ₘ_norm");
    }
    LOG_IF(ERROR, q̂ₘ_norm < α * previous_q̂ₘ_norm)
        << "CGS retrying: " << m << " " << q̂ₘ_norm << " " << previous_q̂ₘ_norm;
  } while (q̂ₘ_norm < α * previous_q̂ₘ_norm);

  // Fill the result.
  qₘ = q̂ₘ / q̂ₘ_norm;
  rₘ[m] = q̂ₘ_norm;

  return Status::OK;
}

//TODO(phl):comment
template<typename Function, typename BasisSeries, typename Norm,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
Status AugmentedGramSchmidtStep(
    Function const& aₘ,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree, Evaluator> const& weight,
    Instant const& t_min,
    Instant const& t_max,
    std::vector<BasisSeries> const& q,
    UnboundedVector<Norm>& rₘ) {
  //TODO(phl):comment
  // This code follows Björk, Numerics of Gram-Schmidt Orthogonalization,
  // Algorithm 6.1.  It processes one column of q and r at a time.

  static constexpr double α = 0.5;
  int const m = q.size();

  Function q̂ₘ = aₘ;
  Norm q̂ₘ_norm = q̂ₘ.Norm(weight, t_min, t_max);

  // Loop on p.
  Function previous_q̂ₘ = q̂ₘ;
  Norm previous_q̂ₘ_norm;
  do {
    previous_q̂ₘ = q̂ₘ;
    previous_q̂ₘ_norm = q̂ₘ_norm;
    for (int i = 0; i < m; ++i) {
      Norm const sᵖₘ = InnerProduct(q[i], previous_q̂ₘ, weight, t_min, t_max);
      q̂ₘ -= sᵖₘ * q[i];
      rₘ[i] += sᵖₘ;
    }
    q̂ₘ_norm = q̂ₘ.Norm(weight, t_min, t_max);

    if (!IsFinite(q̂ₘ_norm)) {
      return Status(Error::OUT_OF_RANGE, u8"Unable to compute q̂ₘ_norm");
    }
  } while (q̂ₘ_norm < α * previous_q̂ₘ_norm);

  // Fill the result.
  rₘ[m] = q̂ₘ_norm;

  return Status::OK;
}

template<typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
AngularFrequency PreciseMode(
    Interval<AngularFrequency> const& fft_mode,
    Function const& function,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree,
                  Evaluator> const& weight) {
  auto const weighted_function = weight * function;
  auto const weighted_function_spectrum = weighted_function.FourierTransform();

  auto power =
      [&weighted_function_spectrum](AngularFrequency const& ω) {
        return weighted_function_spectrum(ω).Norm²();
      };

  return Brent(power,
               fft_mode.min,
               fft_mode.max,
               std::greater<>());
}

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
Projection(Function const& function,
           AngularFrequency const& ω,
           PoissonSeries<double,
                         aperiodic_wdegree, periodic_wdegree,
                         Evaluator> const& weight,
           Instant const& t_min,
           Instant const& t_max) {
  std::optional<AngularFrequency> optional_ω = ω;

  // A calculator that returns optional_ω once and then stops.
  auto angular_frequency_calculator = [&optional_ω](auto const& residual) {
    auto const result = optional_ω;
    optional_ω = std::nullopt;
    return result;
  };

  return IncrementalProjection<aperiodic_degree, periodic_degree>(
      function,
      angular_frequency_calculator,
      weight,
      t_min, t_max);
}

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         typename AngularFrequencyCalculator,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
IncrementalProjection(Function const& function,
                      AngularFrequencyCalculator const& calculator,
                      PoissonSeries<double,
                                    aperiodic_wdegree, periodic_wdegree,
                                    Evaluator> const& weight,
                      Instant const& t_min,
                      Instant const& t_max) {
  using Value = std::invoke_result_t<Function, Instant>;
  using Norm = typename Hilbert<Value>::NormType;
  using Normalized = typename Hilbert<Value>::NormalizedType;
  using BasisSeries = PoissonSeries<Normalized,
                                    aperiodic_degree, periodic_degree,
                                    Evaluator>;
  using ResultSeries = PoissonSeries<Value,
                                     aperiodic_degree, periodic_degree,
                                     Evaluator>;

  Instant const& t0 = weight.origin();
  auto const basis_zero = static_cast<
      typename BasisSeries::AperiodicPolynomial>(
      typename PoissonSeries<Normalized, 0, 0, Evaluator>::AperiodicPolynomial(
          {Normalized{}}, t0));
  auto const result_zero =
      static_cast<typename ResultSeries::AperiodicPolynomial>(
          typename PoissonSeries<Value, 0, 0, Evaluator>::AperiodicPolynomial(
              {Value{}}, t0));

  std::optional<AngularFrequency> ω = calculator(function);
  CHECK(ω.has_value());

  std::vector<BasisSeries> basis;
  // The Poisson series basis[k] belongs to the subspace basis_subspaces[k];
  // this remains true after orthonormalization, i.e., q[k] belongs to the
  // subspace basis_subspaces[k] below.
  std::vector<PoissonSeriesSubspace> basis_subspaces;

  int basis_size = MakeBasis<aperiodic_degree, periodic_degree>(
      ω, t_min, t_max, basis, basis_subspaces);

  // This is logically Q in the QR decomposition of basis.
  std::vector<BasisSeries> q;

  // This is logically R in the QR decomposition of basis.
  UnboundedUpperTriangularMatrix<double> r(basis_size, uninitialized);

  ResultSeries F(result_zero, {{}});

  // The input function with a degree suitable for the augmented Gram-Schmidt
  // step.
  auto const augmented_function = function - F;

  mathematica::Logger logger(TEMP_DIR / "least_square.wl");
  logger.Set(
      "function", function, mathematica::ExpressIn(Metre, Radian, Second));
  logger.Set(
      "tMin", t_min, mathematica::ExpressIn(Metre, Radian, Second));
  logger.Set(
      "tMax", t_max, mathematica::ExpressIn(Metre, Radian, Second));

  int m_begin = 0;
  for (;;) {
    for (int m = m_begin; m < basis_size; ++m) {
      BasisSeries qₘ(basis_zero, {{}});
      UnboundedVector<double> rₘ(m + 1);

      auto const status = NormalGramSchmidtStep(/*aₘ=*/basis[m],
                                                weight, t_min, t_max,
                                                basis_subspaces, q,
                                                qₘ, rₘ);
      if (!status.ok()) {
        return F;
      }

      //LOG_IF(ERROR, m == 0) << qₘ;
      //LOG(ERROR) << "m: " << m << " rₘ[m]: " << rₘ[m]
      //           << " qₘ:" << qₘ.Norm(weight, t_min, t_max);
      for (int i = 0; i <= m; ++i) {
        r[i][m] = rₘ[i];
      }
      q.push_back(qₘ);
      DCHECK_EQ(m + 1, q.size());
    }

    UnboundedVector<Norm> z_ρ(basis_size + 1);
    auto const status = AugmentedGramSchmidtStep(/*aₘ=*/augmented_function,
                                                 weight, t_min, t_max,
                                                 q,
                                                 /*rₘ=*/z_ρ);
    if (!status.ok()) {
      return F;
    }

    logger.Append("q", q, mathematica::ExpressIn(Metre, Radian, Second));
    logger.Append("r", r, mathematica::ExpressIn(Metre, Radian, Second));
    logger.Append("basis", basis, mathematica::ExpressIn(Metre, Radian, Second));
    logger.Append(u8"zρ", z_ρ, mathematica::ExpressIn(Metre, Radian, Second));
    //LOG(ERROR) << "ρ: " << z_ρ[z_ρ.size() - 1];
    //for (int i = 0; i < z_ρ.size() - 1; ++i) {
    //  LOG(ERROR) << "z[" << i << "]: " << z_ρ[i];
    //}
    z_ρ.EraseToEnd(z_ρ.size() - 1);
    auto const x = BackSubstitution(r, z_ρ);

    logger.Append("x", x, mathematica::ExpressIn(Metre, Radian, Second));
    F = ResultSeries(result_zero, {{}});
    auto f = augmented_function;
    for (int i = 0; i < x.size(); ++i) {
      auto const x_basis = x[i] * basis[i];
      F += x_basis;
      f -= x_basis;
    }
    logger.Append(
        "approximation", F, mathematica::ExpressIn(Metre, Radian, Second));

    ω = calculator(f);
    if (!ω.has_value()) {
      return F;
    }

    int const ω_basis_size = MakeBasis<aperiodic_degree, periodic_degree>(
        ω, t_min, t_max, basis, basis_subspaces);
    m_begin = basis_size;
    basis_size += ω_basis_size;
    r.Extend(ω_basis_size, uninitialized);
  }
}

}  // namespace internal_frequency_analysis
}  // namespace frequency_analysis
}  // namespace numerics
}  // namespace principia
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin-server.
 *
 * libbitcoin-server is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/server/interface/transaction_pool.hpp>

#include <cstdint>
#include <cstddef>
#include <bitcoin/server/configuration.hpp>
#include <bitcoin/server/server_node.hpp>
#include "utility.hpp"

namespace libbitcoin {
namespace server {

using namespace chain;
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;

void transaction_pool::fetch_transaction(server_node::ptr node,
    const incoming& request, send_handler handler)
{
    hash_digest hash;
    if (!unwrap_fetch_transaction_args(hash, request))
        return;

    log::debug(LOG_REQUEST)
        << "transaction_pool.fetch_transaction(" << encode_hash(hash)
        << ")";

    node->pool().fetch(hash,
        std::bind(transaction_fetched,
            _1, _2, request, handler));
}

void transaction_pool::validate(server_node::ptr node, const incoming& request,
    send_handler handler)
{
    transaction tx;
    if (!tx.from_data(request.data()))
    {
        handle_validated(error::bad_stream, {}, {}, {}, request, handler);
        return;
    }

    node->pool().validate(tx,
        std::bind(&transaction_pool::handle_validated,
            _1, _2, _3, _4, request, handler));
}

void transaction_pool::handle_validated(const code& ec, const transaction& tx,
    const hash_digest& tx_hash, const index_list& unconfirmed,
    const incoming& request, send_handler handler)
{
    data_chunk result(code_size + unconfirmed.size() * index_size);
    auto serial = make_serializer(result.begin());
    serial.write_error_code(ec);
    BITCOIN_ASSERT(serial.iterator() == result.begin() + code_size);

    for (const auto unconfirmed_index: unconfirmed)
    {
        BITCOIN_ASSERT(unconfirmed_index <= max_uint32);
        const auto index32 = static_cast<uint32_t>(unconfirmed_index);
        serial.write_4_bytes_little_endian(index32);
    }

    BITCOIN_ASSERT(serial.iterator() == result.end());

    log::debug(LOG_REQUEST)
        << "transaction_pool.validate() finished. Sending response: ec="
        << ec.message();

    outgoing response(request, result);
    handler(response);
}

} // namespace server
} // namespace libbitcoin
<commit_msg>Log requests to info (console).<commit_after>/**
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin-server.
 *
 * libbitcoin-server is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/server/interface/transaction_pool.hpp>

#include <cstdint>
#include <cstddef>
#include <bitcoin/server/configuration.hpp>
#include <bitcoin/server/server_node.hpp>
#include "utility.hpp"

namespace libbitcoin {
namespace server {

using namespace chain;
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;

void transaction_pool::fetch_transaction(server_node::ptr node,
    const incoming& request, send_handler handler)
{
    hash_digest hash;
    if (!unwrap_fetch_transaction_args(hash, request))
        return;

    log::debug(LOG_REQUEST)
        << "transaction_pool.fetch_transaction(" << encode_hash(hash)
        << ")";

    node->pool().fetch(hash,
        std::bind(transaction_fetched,
            _1, _2, request, handler));
}

void transaction_pool::validate(server_node::ptr node, const incoming& request,
    send_handler handler)
{
    transaction tx;
    if (!tx.from_data(request.data()))
    {
        handle_validated(error::bad_stream, {}, {}, {}, request, handler);
        return;
    }

    node->pool().validate(tx,
        std::bind(&transaction_pool::handle_validated,
            _1, _2, _3, _4, request, handler));
}

void transaction_pool::handle_validated(const code& ec, const transaction& tx,
    const hash_digest& tx_hash, const index_list& unconfirmed,
    const incoming& request, send_handler handler)
{
    data_chunk result(code_size + unconfirmed.size() * index_size);
    auto serial = make_serializer(result.begin());
    serial.write_error_code(ec);
    BITCOIN_ASSERT(serial.iterator() == result.begin() + code_size);

    for (const auto unconfirmed_index: unconfirmed)
    {
        BITCOIN_ASSERT(unconfirmed_index <= max_uint32);
        const auto index32 = static_cast<uint32_t>(unconfirmed_index);
        serial.write_4_bytes_little_endian(index32);
    }

    BITCOIN_ASSERT(serial.iterator() == result.end());

    log::info(LOG_REQUEST)
        << "transaction_pool.validate() finished. Sending response: ec="
        << ec.message();

    outgoing response(request, result);
    handler(response);
}

} // namespace server
} // namespace libbitcoin
<|endoftext|>


<commit_before><commit_msg>Add headers missing in r18291. Review URL: http://codereview.chromium.org/125072<commit_after><|endoftext|>


<commit_before><commit_msg>Make a DCHECK in Browser::ExecuteCommandWithDisposition more informative.<commit_after><|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
// Licensed to Qualys, Inc. (QUALYS) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// QUALYS licenses this file to You 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.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/// @file
/// @brief IronBee --- Vector Test Functions
///
/// @author Sam Baskinger <sbaskinger@qualys.com>
//////////////////////////////////////////////////////////////////////////////
#include "ironbee_config_auto.h"

#include <ironbee/queue.h>

#include "gtest/gtest.h"

#include <stdexcept>

/* Lots of string constants to push and pop from the queue. */
namespace {
    const char *s01 = "s01";
    const char *s02 = "s02";
    const char *s03 = "s03";
    const char *s04 = "s04";
    const char *s05 = "s05";
    const char *s06 = "s06";
    const char *s07 = "s07";
    const char *s08 = "s08";
    const char *s09 = "s09";
    const char *s10 = "s10";

    const char *s[] = {s01, s02, s03, s04, s05, s06, s07, s08, s09, s10, NULL};
}

class QueueTest : public ::testing::Test {
public:
    virtual void SetUp() {
        ASSERT_EQ(IB_OK, ib_mpool_create(&m_mp, "QueueTest", NULL));
        ASSERT_EQ(IB_OK, ib_queue_create(&m_q, m_mp, IB_QUEUE_NONE));
    }
    virtual void TearDown() {
        ib_mpool_release(m_mp);
    }
    virtual ~QueueTest() {
    }
protected:
    ib_mpool_t *m_mp;
    ib_queue_t *m_q;
};

TEST_F(QueueTest, Init) {
    ASSERT_FALSE(NULL == m_mp);
    ASSERT_FALSE(NULL == m_q);
}

TEST_F(QueueTest, SetGet) {
    void *v;
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s01)));
    ASSERT_EQ(1U, ib_queue_size(m_q));

    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s02)));
    ASSERT_EQ(2U, ib_queue_size(m_q));

    ASSERT_EQ(IB_OK, ib_queue_peek(m_q, &v));
    ASSERT_EQ(s01, v);

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 0, &v));
    ASSERT_EQ(s01, v);

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 1, &v));
    ASSERT_EQ(s02, v);
}

TEST_F(QueueTest, PushBack) {
    ASSERT_EQ(IB_OK, ib_queue_reserve(m_q, 100));

    for (int i = 0; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s[i])));
    }

    for (int i = 0; s[i] != NULL; ++i) {
        void *v;
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_EQ(s[i], v);
    }
}

TEST_F(QueueTest, PushBackResize) {
    ASSERT_EQ(IB_OK, ib_queue_reserve(m_q, 2));

    for (int i = 0; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s[i])));

        void *v;
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }

    for (int i = 0; s[i] != NULL; ++i) {
        void *v;
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }
}

TEST_F(QueueTest, PushBackOffset) {
    ASSERT_EQ(IB_OK, ib_queue_reserve(m_q, 4));

    /* Move the head offset by pop_front. */
    void *v;
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, NULL));
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, NULL));
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s01)));
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s02)));
    ASSERT_EQ(IB_OK, ib_queue_pop_front(m_q, &v));
    ASSERT_EQ(IB_OK, ib_queue_pop_front(m_q, &v));

    /* Head is now at offset 2. */

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 0, &v));
    ASSERT_STREQ(s01, reinterpret_cast<char *>(v));

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 1, &v));
    ASSERT_STREQ(s02, reinterpret_cast<char *>(v));

    /* Push the rest. */
    for (int i = 2; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s[i])));
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }

    for (int i = 0; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }
}

TEST_F(QueueTest, GetEinval) {
    void *v;
    ASSERT_EQ(IB_EINVAL, ib_queue_get(m_q, 100, &v));
}

TEST_F(QueueTest, SetEinval) {
    void *v;
    ASSERT_EQ(IB_EINVAL, ib_queue_set(m_q, 100, &v));
}
<commit_msg>test_util_queue.cpp: Updating to use memory managers.<commit_after>//////////////////////////////////////////////////////////////////////////////
// Licensed to Qualys, Inc. (QUALYS) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// QUALYS licenses this file to You 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.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/// @file
/// @brief IronBee --- Vector Test Functions
///
/// @author Sam Baskinger <sbaskinger@qualys.com>
//////////////////////////////////////////////////////////////////////////////
#include "ironbee_config_auto.h"

#include <ironbee/mm_mpool.h>
#include <ironbee/queue.h>

#include "gtest/gtest.h"

#include <stdexcept>

/* Lots of string constants to push and pop from the queue. */
namespace {
    const char *s01 = "s01";
    const char *s02 = "s02";
    const char *s03 = "s03";
    const char *s04 = "s04";
    const char *s05 = "s05";
    const char *s06 = "s06";
    const char *s07 = "s07";
    const char *s08 = "s08";
    const char *s09 = "s09";
    const char *s10 = "s10";

    const char *s[] = {s01, s02, s03, s04, s05, s06, s07, s08, s09, s10, NULL};
}

class QueueTest : public ::testing::Test {
public:
    virtual void SetUp() {
        ASSERT_EQ(IB_OK, ib_mpool_create(&m_mp, "QueueTest", NULL));
        ASSERT_EQ(IB_OK, ib_queue_create(&m_q, ib_mm_mpool(m_mp), IB_QUEUE_NONE));
    }
    virtual void TearDown() {
        ib_mpool_release(m_mp);
    }
    virtual ~QueueTest() {
    }
protected:
    ib_mpool_t *m_mp;
    ib_queue_t *m_q;
};

TEST_F(QueueTest, Init) {
    ASSERT_FALSE(NULL == m_mp);
    ASSERT_FALSE(NULL == m_q);
}

TEST_F(QueueTest, SetGet) {
    void *v;
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s01)));
    ASSERT_EQ(1U, ib_queue_size(m_q));

    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s02)));
    ASSERT_EQ(2U, ib_queue_size(m_q));

    ASSERT_EQ(IB_OK, ib_queue_peek(m_q, &v));
    ASSERT_EQ(s01, v);

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 0, &v));
    ASSERT_EQ(s01, v);

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 1, &v));
    ASSERT_EQ(s02, v);
}

TEST_F(QueueTest, PushBack) {
    ASSERT_EQ(IB_OK, ib_queue_reserve(m_q, 100));

    for (int i = 0; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s[i])));
    }

    for (int i = 0; s[i] != NULL; ++i) {
        void *v;
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_EQ(s[i], v);
    }
}

TEST_F(QueueTest, PushBackResize) {
    ASSERT_EQ(IB_OK, ib_queue_reserve(m_q, 2));

    for (int i = 0; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s[i])));

        void *v;
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }

    for (int i = 0; s[i] != NULL; ++i) {
        void *v;
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }
}

TEST_F(QueueTest, PushBackOffset) {
    ASSERT_EQ(IB_OK, ib_queue_reserve(m_q, 4));

    /* Move the head offset by pop_front. */
    void *v;
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, NULL));
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, NULL));
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s01)));
    ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s02)));
    ASSERT_EQ(IB_OK, ib_queue_pop_front(m_q, &v));
    ASSERT_EQ(IB_OK, ib_queue_pop_front(m_q, &v));

    /* Head is now at offset 2. */

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 0, &v));
    ASSERT_STREQ(s01, reinterpret_cast<char *>(v));

    ASSERT_EQ(IB_OK, ib_queue_get(m_q, 1, &v));
    ASSERT_STREQ(s02, reinterpret_cast<char *>(v));

    /* Push the rest. */
    for (int i = 2; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_push_back(m_q, const_cast<char *>(s[i])));
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }

    for (int i = 0; s[i] != NULL; ++i) {
        ASSERT_EQ(IB_OK, ib_queue_get(m_q, i, &v));
        ASSERT_STREQ(s[i], reinterpret_cast<char *>(v));
    }
}

TEST_F(QueueTest, GetEinval) {
    void *v;
    ASSERT_EQ(IB_EINVAL, ib_queue_get(m_q, 100, &v));
}

TEST_F(QueueTest, SetEinval) {
    void *v;
    ASSERT_EQ(IB_EINVAL, ib_queue_set(m_q, 100, &v));
}
<|endoftext|>


<commit_before>/*
 * Copyright 2010 Alex Graveley
 *
 * 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 <sstream>
#include <png.h>

#include "EventMachine.hh"

EventMask::EventMask(string mask)
{
   istringstream iss(mask, istringstream::in);
   string word;
   while(iss >> word) {
      fprintf(stderr, "EventMask: mask=%s\n", word.c_str());
      names.push_back(word);
   }
}

bool
EventMask::stateMatch(State *state)
{
   vector<string>::iterator i;
   for (i = names.begin(); i != names.end(); i++) {
      if (!state->getDigitalIn((*i).c_str())) {
         return false;
      }
   }
   return true;
}

EventScript::~EventScript()
{
   for (uint y=0; y < info->height; y++) {
      free(data[y]);
   }
   png_read_destroy(png, info, NULL);
}

uint
EventScript::get_frames()
{
   return info->height;
}

bool
EventScript::load(string script)
{
   /* open file and test for it being a png */
   FILE *fp = fopen(script.c_str(), "rb");
   if (!fp) {
      fprintf(stderr, "EventMachine::loadScript: File %s could not be opened for reading",
              script.c_str());
      return false;
   }

   /* initialize stuff */
   png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   info = png_create_info_struct(png);

   png_init_io(png, fp);
   png_read_info(png, info);
   png_read_update_info(png, info);

   data = (png_bytepp) malloc(sizeof(png_bytep) * info->height);
   for (uint y=0; y < info->height; y++) {
      data[y] = (png_byte*) malloc(info->rowbytes);
   }
   png_read_image(png, data);

   fclose(fp);
   return true;
}

bool
EventScript::update(State *state,
                    uint frame,
                    vector<string> lowerLedNames,
                    vector<string> axonLedNames,
                    vector<string> upperLedNames,
                    vector<string> digitalNames)
{
   // XXX: constify offsets
   uint8_t x=0;
   uint8_t y=frame;

   if (y >= info->height) {
      //fprintf(stderr, "EventScript::update frame %d greater than PNG height %lu\n",
      //        frame, info->height);
      return false;
   }
   png_byte* row = data[y];

   vector<string>::iterator i = axonLedNames.begin();
   x = 0; //axon LED's start at pixel 0
   for (i = axonLedNames.begin(); i != axonLedNames.end(); i++) {
      png_byte* ptr = &(row[x*3]); //get pixel rgb
      //fprintf(stderr, "Pixel [x: %d, y: %d] R:%d G:%d B:%d\n",
      //        x, y, ptr[0], ptr[1], ptr[2]);
      state->setLightOut(i->c_str(), ptr[0], ptr[1], ptr[2]);
      //printf("%d %d %d | ", ptr[0], ptr[1], ptr[2]);
      x++;
   }

   i = upperLedNames.begin();
   x = 10; //upper soma LED's start at pixel 10
   for (i = upperLedNames.begin(); i != upperLedNames.end(); i++) {
      png_byte* ptr = &(row[x*3]); //get pixel rgb
      state->setLightOut(i->c_str(), ptr[0], ptr[1], ptr[2]);
      x++;
   }

   i = lowerLedNames.begin();
   x = 40; //lower soma LED's start at pixel 40
   for (i = lowerLedNames.begin(); i != lowerLedNames.end(); i++) {
      png_byte* ptr = &(row[x*3]); //get pixel rgb
      state->setLightOut(i->c_str(), ptr[0], ptr[1], ptr[2]);
      x++;
   }

   i = digitalNames.begin();
   x = 50; //digital poofers start at pixel 50
   for (i = digitalNames.begin(); i != digitalNames.end(); i++) {
      //png_byte* ptr = &(row[x*3]); //get pixel rgb
      //fprintf(stderr, "Setting Poofer [x: %d, y: %d] %s\n",
      //        x, y, ptr[0] + ptr[1] + ptr[2] == 0 ? "ON" : "OFF");
      // XXX: WARNING trying to set non-existant digital out a8b
      //state->setDigitalOut(i->c_str(), ptr[0] + ptr[1] + ptr[2] == 0);
      x++;
   }

   //fprintf(stderr, "EventScript::update return == %s\n", y < info->height ? "TRUE" : "FALSE");
   return y < info->height;
}

bool
EventMachine::addScript(string mask, string script)
{
   if (scriptData[script] != NULL) {
      fprintf(stderr, "EventMachine::addScript: reusing loaded script '%s'\n",
              script.c_str());
      return true;
   }

   // load script file, add with name to scriptData
   EventScript *es = scriptData[script];
   if (!es) {
      es = new EventScript();
      if (!es->load(script)) {
         delete es;
         fprintf(stderr, "EventMachine::addScript: failed to load script '%s'\n",
                 script.c_str());
         return false;
      }
   }

   scriptMasks.push_back(pair<EventMask, string>(EventMask(mask), script));
   scriptData[script] = es;

   fprintf(stderr, "EventMachine::addScript: mask=%s, script=%s, total scripts: %d\n",
           mask.c_str(), script.c_str(), (int)scriptMasks.size());
   return true;
}

void
EventMachine::update(State *state,
                     vector<string> lowerLedNames,
                     vector<string> axonLedNames,
                     vector<string> upperLedNames,
                     vector<string> digitalNames)
{
   for (vector< pair<EventMask, string> >::iterator i = scriptMasks.begin();
        i != scriptMasks.end(); i++) {
      if (i->first.stateMatch(state)) {
         //fprintf(stderr, "EventMachine::update: state matches for script: %s\n",
         //        i->second.c_str());
         EventScript *data = scriptData[i->second];
         bool isRunning = false;
         for (vector< pair<EventScript*, uint> >::iterator i2 = scriptStates.begin();
              i2 != scriptStates.end(); i2++) {
            if (data == i2->first) {
               //fprintf(stderr, "EventMachine::update: script %p already running.\n",
               //        i2->first);
               isRunning = true;
            }
         }
         if (!isRunning) {
            fprintf(stderr, "EventMachine::update: restarting script '%s'.\n",
                    i->second.c_str());
            scriptStates.push_back(pair<EventScript *, uint>(data, 0));
         }
      }
   }

   vector< pair<EventScript*, uint> > nextStates;
   for (vector< pair<EventScript*, uint> >::iterator i2 = scriptStates.begin();
        i2 != scriptStates.end(); i2++) {
      // Call Ben's event updating for the row at index i2->second.
      EventScript *data = i2->first;
      uint frame = i2->second;

      if (data && data->update(state, frame, lowerLedNames, axonLedNames,
                               upperLedNames, digitalNames)) {
         //fprintf(stderr, "EventMachine::update: advancing to frame %d: %p\n", frame, data);
         nextStates.push_back(pair<EventScript*, uint>(data, frame + 1));
      } else {
         fprintf(stderr, "EventMachine::update: script completed: %p\n", data);
      }
   }
   scriptStates = nextStates;
}
<commit_msg>Whoops, don't cancel the addScript call if the script has already been loaded.  Fixes events for all the buttons.<commit_after>/*
 * Copyright 2010 Alex Graveley
 *
 * 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 <sstream>
#include <png.h>

#include "EventMachine.hh"

EventMask::EventMask(string mask)
{
   istringstream iss(mask, istringstream::in);
   string word;
   while(iss >> word) {
      fprintf(stderr, "EventMask: mask=%s\n", word.c_str());
      names.push_back(word);
   }
}

bool
EventMask::stateMatch(State *state)
{
   vector<string>::iterator i;
   for (i = names.begin(); i != names.end(); i++) {
      if (!state->getDigitalIn((*i).c_str())) {
         return false;
      }
   }
   return true;
}

EventScript::~EventScript()
{
   for (uint y=0; y < info->height; y++) {
      free(data[y]);
   }
   png_read_destroy(png, info, NULL);
}

uint
EventScript::get_frames()
{
   return info->height;
}

bool
EventScript::load(string script)
{
   /* open file and test for it being a png */
   FILE *fp = fopen(script.c_str(), "rb");
   if (!fp) {
      fprintf(stderr, "EventMachine::loadScript: File %s could not be opened for reading",
              script.c_str());
      return false;
   }

   /* initialize stuff */
   png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   info = png_create_info_struct(png);

   png_init_io(png, fp);
   png_read_info(png, info);
   png_read_update_info(png, info);

   data = (png_bytepp) malloc(sizeof(png_bytep) * info->height);
   for (uint y=0; y < info->height; y++) {
      data[y] = (png_byte*) malloc(info->rowbytes);
   }
   png_read_image(png, data);

   fclose(fp);
   return true;
}

bool
EventScript::update(State *state,
                    uint frame,
                    vector<string> lowerLedNames,
                    vector<string> axonLedNames,
                    vector<string> upperLedNames,
                    vector<string> digitalNames)
{
   // XXX: constify offsets
   uint8_t x=0;
   uint8_t y=frame;

   if (y >= info->height) {
      //fprintf(stderr, "EventScript::update frame %d greater than PNG height %lu\n",
      //        frame, info->height);
      return false;
   }
   png_byte* row = data[y];

   vector<string>::iterator i = axonLedNames.begin();
   x = 0; //axon LED's start at pixel 0
   for (i = axonLedNames.begin(); i != axonLedNames.end(); i++) {
      png_byte* ptr = &(row[x*3]); //get pixel rgb
      //fprintf(stderr, "Pixel [x: %d, y: %d] R:%d G:%d B:%d\n",
      //        x, y, ptr[0], ptr[1], ptr[2]);
      state->setLightOut(i->c_str(), ptr[0], ptr[1], ptr[2]);
      //printf("%d %d %d | ", ptr[0], ptr[1], ptr[2]);
      x++;
   }

   i = upperLedNames.begin();
   x = 10; //upper soma LED's start at pixel 10
   for (i = upperLedNames.begin(); i != upperLedNames.end(); i++) {
      png_byte* ptr = &(row[x*3]); //get pixel rgb
      state->setLightOut(i->c_str(), ptr[0], ptr[1], ptr[2]);
      x++;
   }

   i = lowerLedNames.begin();
   x = 40; //lower soma LED's start at pixel 40
   for (i = lowerLedNames.begin(); i != lowerLedNames.end(); i++) {
      png_byte* ptr = &(row[x*3]); //get pixel rgb
      state->setLightOut(i->c_str(), ptr[0], ptr[1], ptr[2]);
      x++;
   }

   i = digitalNames.begin();
   x = 50; //digital poofers start at pixel 50
   for (i = digitalNames.begin(); i != digitalNames.end(); i++) {
      //png_byte* ptr = &(row[x*3]); //get pixel rgb
      //fprintf(stderr, "Setting Poofer [x: %d, y: %d] %s\n",
      //        x, y, ptr[0] + ptr[1] + ptr[2] == 0 ? "ON" : "OFF");
      // XXX: WARNING trying to set non-existant digital out a8b
      //state->setDigitalOut(i->c_str(), ptr[0] + ptr[1] + ptr[2] == 0);
      x++;
   }

   //fprintf(stderr, "EventScript::update return == %s\n", y < info->height ? "TRUE" : "FALSE");
   return y < info->height;
}

bool
EventMachine::addScript(string mask, string script)
{
   EventScript *es = scriptData[script];
   if (!es) {
      // load script file, add with name to scriptData
      es = new EventScript();
      if (!es->load(script)) {
         delete es;
         fprintf(stderr, "EventMachine::addScript: failed to load script '%s'\n",
                 script.c_str());
         return false;
      }
   }

   scriptMasks.push_back(pair<EventMask, string>(EventMask(mask), script));
   scriptData[script] = es;

   fprintf(stderr, "EventMachine::addScript: mask=%s, script=%s, total scripts: %d\n",
           mask.c_str(), script.c_str(), (int)scriptMasks.size());
   return true;
}

void
EventMachine::update(State *state,
                     vector<string> lowerLedNames,
                     vector<string> axonLedNames,
                     vector<string> upperLedNames,
                     vector<string> digitalNames)
{
   for (vector< pair<EventMask, string> >::iterator i = scriptMasks.begin();
        i != scriptMasks.end(); i++) {
      if (i->first.stateMatch(state)) {
         //fprintf(stderr, "EventMachine::update: state matches for script: %s\n",
         //        i->second.c_str());
         EventScript *data = scriptData[i->second];
         bool isRunning = false;
         for (vector< pair<EventScript*, uint> >::iterator i2 = scriptStates.begin();
              i2 != scriptStates.end(); i2++) {
            if (data == i2->first) {
               //fprintf(stderr, "EventMachine::update: script %p already running.\n",
               //        i2->first);
               isRunning = true;
            }
         }
         if (!isRunning) {
            fprintf(stderr, "EventMachine::update: restarting script '%s'.\n",
                    i->second.c_str());
            scriptStates.push_back(pair<EventScript *, uint>(data, 0));
         }
      }
   }

   vector< pair<EventScript*, uint> > nextStates;
   for (vector< pair<EventScript*, uint> >::iterator i2 = scriptStates.begin();
        i2 != scriptStates.end(); i2++) {
      // Call Ben's event updating for the row at index i2->second.
      EventScript *data = i2->first;
      uint frame = i2->second;

      if (data && data->update(state, frame, lowerLedNames, axonLedNames,
                               upperLedNames, digitalNames)) {
         //fprintf(stderr, "EventMachine::update: advancing to frame %d: %p\n", frame, data);
         nextStates.push_back(pair<EventScript*, uint>(data, frame + 1));
      } else {
         fprintf(stderr, "EventMachine::update: script completed: %p\n", data);
      }
   }
   scriptStates = nextStates;
}
<|endoftext|>


<commit_before>// Copyright 2022 The Centipede Authors.
//
// 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
//
//      https://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.

// Fork server, a.k.a. a process Zygote, for the Centipede runner.
//
// Startup:
// * Centipede creates two named FIFO pipes: pipe0 and pipe1.
// * Centipede runs the target in background, and passes the FIFO names to it
//   using two environment variables: CENTIPEDE_FORK_SERVER_FIFO[01].
// * Centipede opens the pipe0 for writing, pipe1 for reading.
//   These would block until the same pipes are open in the runner.
// * Runner, early at startup, checks if it is given the pipe names.
//    If so, it opens pipe0 for reading, pipe1 for writing,
//    and enters the infinite fork-server loop.
// Loop:
// * Centipede writes a byte to pipe0.
// * Runner blocks until it reads a byte from pipe0, then forks and waits.
//   This is where the child process executes and does the work.
//   This works because every execution of the target has the same arguments.
// * Runner receives the child exit status and writes it to pipe1.
// * Centipede blocks until it reads the status from pipe1.
// Exit:
// * Centipede closes the pipes (and then deletes them).
// * Runner (the fork server) fails on the next read from pipe0 and exits.
//
// The fork server code kicks in super-early in the process startup,
// via injecting itself into the .preinit_array.
// Ensure that this code is not dropped from linking (alwayslink=1).
//
// The main benefts of the fork server over plain fork/exec or system() are:
//  * Dynamic linking happens once at the fork-server startup.
//  * fork is cheaper than fork/exec, especially when running multiple threads.
//
// Other than performance, using fork server should be the same as not using it.
//
// Similar ideas:
// * lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html
// * Android Zygote.
//
// We try to avoid any high-level code here, even most of libc because this code
// works too early in the process. E.g. getenv() will not work yet.

#include <fcntl.h>
#include <linux/limits.h>  // ARG_MAX
#include <sys/wait.h>
#include <unistd.h>

#include <cstring>

namespace centipede {

// Writes a C string to stderr when debugging, no-op otherwise.
void Log(const char *str) {
  // Uncomment these lines to debug.
  // (void)write(STDERR_FILENO, str, strlen(str));
  // fsync(STDERR_FILENO);
}

// Maybe writes the `reason` to stderr; then calls _exit.
void Exit(const char *reason) {
  Log(reason);
  _exit(0);  // The exit code does not matter, it won't be checked anyway.
}

// Contents of /proc/self/environ. We avoid malloc, so it's a fixed-size global.
// The fork server will fail to initialize if /proc/self/environ is too large.
char env[ARG_MAX];

// Reads /proc/self/environ into env.
void GetAllEnv() {
  int fd = open("/proc/self/environ", O_RDONLY);
  if (fd < 0) Exit("GetEnv: can't open /proc/self/environ\n");
  if (read(fd, env, sizeof(env)) < 0) Exit("GetEnv: can't read to env\n");
  if (close(fd) != 0) Exit("GetEnv: can't close /proc/self/environ\n");
  env[sizeof(env) - 1] = 0;  // Just in case.
}

// Gets a zero-terminated string matching the environment `key` (ends with '=').
const char *GetOneEnv(const char *key) {
  size_t key_len = strlen(key);
  bool in_the_beginning_of_key = true;
  // env is not a C string.
  // It is an array of bytes, with '\0' between individual key=val pairs.
  for (size_t idx = 0; idx < sizeof(env) - key_len; ++idx) {
    if (env[idx] == 0) {
      in_the_beginning_of_key = true;
      continue;
    }
    if (in_the_beginning_of_key && 0 == memcmp(env + idx, key, key_len))
      return &env[idx + key_len];  // zero-terminated.
    in_the_beginning_of_key = false;
  }
  return nullptr;
}

// Starts the fork server if the pipes are given.
// This function is called from .preinit_array when linked statically,
// or from the DSO constructor when injected via LD_PRELOAD.
__attribute__((constructor)) void ForkServerCallMeVeryEarly() {
  // Guard from calling twice.
  static bool called_already = false;
  if (called_already) return;
  called_already = true;
  // Startup.
  GetAllEnv();
  const char *pipe0_name = GetOneEnv("CENTIPEDE_FORK_SERVER_FIFO0=");
  const char *pipe1_name = GetOneEnv("CENTIPEDE_FORK_SERVER_FIFO1=");
  if (!pipe0_name || !pipe1_name) return;
  Log("###Centipede fork server requested\n");
  int pipe0 = open(pipe0_name, O_RDONLY);
  if (pipe0 < 0) Exit("###open pipe0 failed\n");
  int pipe1 = open(pipe1_name, O_WRONLY);
  if (pipe1 < 0) Exit("###open pipe1 failed\n");
  Log("###Centipede fork server ready\n");

  // Loop.
  while (true) {
    Log("###Centipede fork server blocking on pipe0\n");
    // This read will fail when Centipede shuts down the pipes.
    char ch = 0;
    if (read(pipe0, &ch, 1) != 1) Exit("###read from pipe0 failed\n");
    Log("###Centipede starting fork\n");
    auto pid = fork();
    if (pid < 0) {
      Exit("###fork failed\n");
    } else if (pid == 0) {
      // Child process. Reset stdout/stderr and let it run normally.
      for (int fd = 1; fd <= 2; fd++) {
        lseek(fd, 0, SEEK_SET);
        // NOTE: Allow ftruncate() to fail by ignoring its return; that okay to
        // happen when the stdout/stderr are not redirected to a file.
        (void)ftruncate(fd, 0);
      }
      return;
    } else {
      // Parent process.
      int status = -1;
      if (waitpid(pid, &status, 0) < 0) Exit("###waitpid failed\n");
      Log("###Centipede fork done; writing to pipe1\n");
      if (write(pipe1, &status, sizeof(status)) == -1)
        Exit("###write to pipe1 failed\n");
    }
  }
  // The only way out of the loop is via Exit() or return.
  __builtin_unreachable();
}

__attribute__((section(".preinit_array"))) auto call_very_early =
    ForkServerCallMeVeryEarly;

}  // namespace centipede
<commit_msg>More verbose debug logging in runner_fork_server.cc<commit_after>// Copyright 2022 The Centipede Authors.
//
// 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
//
//      https://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.

// Fork server, a.k.a. a process Zygote, for the Centipede runner.
//
// Startup:
// * Centipede creates two named FIFO pipes: pipe0 and pipe1.
// * Centipede runs the target in background, and passes the FIFO names to it
//   using two environment variables: CENTIPEDE_FORK_SERVER_FIFO[01].
// * Centipede opens the pipe0 for writing, pipe1 for reading.
//   These would block until the same pipes are open in the runner.
// * Runner, early at startup, checks if it is given the pipe names.
//    If so, it opens pipe0 for reading, pipe1 for writing,
//    and enters the infinite fork-server loop.
// Loop:
// * Centipede writes a byte to pipe0.
// * Runner blocks until it reads a byte from pipe0, then forks and waits.
//   This is where the child process executes and does the work.
//   This works because every execution of the target has the same arguments.
// * Runner receives the child exit status and writes it to pipe1.
// * Centipede blocks until it reads the status from pipe1.
// Exit:
// * Centipede closes the pipes (and then deletes them).
// * Runner (the fork server) fails on the next read from pipe0 and exits.
//
// The fork server code kicks in super-early in the process startup,
// via injecting itself into the .preinit_array.
// Ensure that this code is not dropped from linking (alwayslink=1).
//
// The main benefts of the fork server over plain fork/exec or system() are:
//  * Dynamic linking happens once at the fork-server startup.
//  * fork is cheaper than fork/exec, especially when running multiple threads.
//
// Other than performance, using fork server should be the same as not using it.
//
// Similar ideas:
// * lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html
// * Android Zygote.
//
// We try to avoid any high-level code here, even most of libc because this code
// works too early in the process. E.g. getenv() will not work yet.

#include <fcntl.h>
#include <linux/limits.h>  // ARG_MAX
#include <sys/wait.h>
#include <unistd.h>

#include <cstdlib>
#include <cstring>

namespace centipede {

// Writes a C string to stderr when debugging, no-op otherwise.
void Log(const char *str) {
  // Uncomment these lines to debug.
  // (void)write(STDERR_FILENO, str, strlen(str));
  // fsync(STDERR_FILENO);
}

// Maybe writes the `reason` to stderr; then calls _exit.
void Exit(const char *reason) {
  Log(reason);
  _exit(0);  // The exit code does not matter, it won't be checked anyway.
}

// Contents of /proc/self/environ. We avoid malloc, so it's a fixed-size global.
// The fork server will fail to initialize if /proc/self/environ is too large.
char env[ARG_MAX];

// Reads /proc/self/environ into env.
void GetAllEnv() {
  int fd = open("/proc/self/environ", O_RDONLY);
  if (fd < 0) Exit("GetEnv: can't open /proc/self/environ\n");
  if (read(fd, env, sizeof(env)) < 0) Exit("GetEnv: can't read to env\n");
  if (close(fd) != 0) Exit("GetEnv: can't close /proc/self/environ\n");
  env[sizeof(env) - 1] = 0;  // Just in case.
}

// Gets a zero-terminated string matching the environment `key` (ends with '=').
const char *GetOneEnv(const char *key) {
  size_t key_len = strlen(key);
  bool in_the_beginning_of_key = true;
  // env is not a C string.
  // It is an array of bytes, with '\0' between individual key=val pairs.
  for (size_t idx = 0; idx < sizeof(env) - key_len; ++idx) {
    if (env[idx] == 0) {
      in_the_beginning_of_key = true;
      continue;
    }
    if (in_the_beginning_of_key && 0 == memcmp(env + idx, key, key_len))
      return &env[idx + key_len];  // zero-terminated.
    in_the_beginning_of_key = false;
  }
  return nullptr;
}

// Starts the fork server if the pipes are given.
// This function is called from .preinit_array when linked statically,
// or from the DSO constructor when injected via LD_PRELOAD.
__attribute__((constructor)) void ForkServerCallMeVeryEarly() {
  // Guard from calling twice.
  static bool called_already = false;
  if (called_already) return;
  called_already = true;
  // Startup.
  GetAllEnv();
  const char *pipe0_name = GetOneEnv("CENTIPEDE_FORK_SERVER_FIFO0=");
  const char *pipe1_name = GetOneEnv("CENTIPEDE_FORK_SERVER_FIFO1=");
  if (!pipe0_name || !pipe1_name) return;
  Log("###Centipede fork server requested\n");
  int pipe0 = open(pipe0_name, O_RDONLY);
  if (pipe0 < 0) Exit("###open pipe0 failed\n");
  int pipe1 = open(pipe1_name, O_WRONLY);
  if (pipe1 < 0) Exit("###open pipe1 failed\n");
  Log("###Centipede fork server ready\n");

  // Loop.
  while (true) {
    Log("###Centipede fork server blocking on pipe0\n");
    // This read will fail when Centipede shuts down the pipes.
    char ch = 0;
    if (read(pipe0, &ch, 1) != 1) Exit("###read from pipe0 failed\n");
    Log("###Centipede starting fork\n");
    auto pid = fork();
    if (pid < 0) {
      Exit("###fork failed\n");
    } else if (pid == 0) {
      // Child process. Reset stdout/stderr and let it run normally.
      for (int fd = 1; fd <= 2; fd++) {
        lseek(fd, 0, SEEK_SET);
        // NOTE: Allow ftruncate() to fail by ignoring its return; that okay to
        // happen when the stdout/stderr are not redirected to a file.
        (void)ftruncate(fd, 0);
      }
      return;
    } else {
      // Parent process.
      int status = -1;
      if (waitpid(pid, &status, 0) < 0) Exit("###waitpid failed\n");
      if (WIFEXITED(status)) {
        if (WEXITSTATUS(status) == EXIT_SUCCESS)
          Log("###Centipede fork returned EXIT_SUCCESS\n");
        else if (WEXITSTATUS(status) == EXIT_FAILURE)
          Log("###Centipede fork returned EXIT_FAILURE\n");
        else
          Log("###Centipede fork returned unknown failure status\n");
      } else {
        Log("###Centipede fork crashed\n");
      }
      Log("###Centipede fork writing status to pipe1\n");
      if (write(pipe1, &status, sizeof(status)) == -1)
        Exit("###write to pipe1 failed\n");
    }
  }
  // The only way out of the loop is via Exit() or return.
  __builtin_unreachable();
}

__attribute__((section(".preinit_array"))) auto call_very_early =
    ForkServerCallMeVeryEarly;

}  // namespace centipede
<|endoftext|>