<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QIcon>
#include <QDebug>
#include <QMessageBox>
#include <QFileInfo>

#define MESSAGE_DISPLAY_LENGTH 4000

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowIcon(QIcon(":/imgs/money_management.gif"));
    setWindowTitle("Money Management");
    setFixedSize(width(), height());
    ui->statusBar->showMessage("", MESSAGE_DISPLAY_LENGTH);
    ui->dateEdit->setDate(QDate::currentDate());

    ui->comboBoxMode->addItem("Deposit");
    ui->comboBoxMode->addItem("Withdraw");

    ui->comboBoxMode->setCurrentIndex(-1);
    ui->lineEditDepWithdr->setValidator(new QDoubleValidator(1, 10000000, 2, ui->lineEditDepWithdr));
    db_path = QDir::currentPath() + "/transaction_db.db";

    setup_database();

    open_database();
    QSqlQuery qry = transaction_db.exec("SELECT amount FROM transactions ORDER BY id DESC LIMIT 1;");
    if(qry.next()){
      ui->labelTotal->setText("Total: " + format.toCurrencyString(qry.value(0).toDouble()));
    }else{
      ui->labelTotal->setText("Total: " + format.toCurrencyString(0.00));
    }
    close_database();
}

MainWindow::~MainWindow()
{
//    QDir d;
//    d.remove(db_path);
    delete ui;
}
void MainWindow::setup_database(){
  transaction_db = QSqlDatabase::addDatabase("QSQLITE");
  transaction_db.setDatabaseName(db_path);
  QFileInfo db_info(db_path);
  bool exists = db_info.exists();
  bool status = transaction_db.open();
  if(!status){
      //add in message boxes showing errors...
      ui->statusBar->showMessage("Error connecting to database", MESSAGE_DISPLAY_LENGTH);
      return;
  }
  ui->statusBar->showMessage("Connected", MESSAGE_DISPLAY_LENGTH);
  if(!exists){
      qDebug() << "Creating transaction table";
      QSqlQuery create_transaction_table_qry = transaction_db.exec("CREATE TABLE transactions(id INTEGER PRIMARY KEY AUTOINCREMENT, description TEXT, mode TEXT , amount DOUBLE, date_added DATE);");
      qDebug() << create_transaction_table_qry.lastError();
  }
  close_database();
}

bool MainWindow::open_database(){
  bool status = transaction_db.open();
  if(status){
      ui->statusBar->showMessage("Connected...", MESSAGE_DISPLAY_LENGTH);
  }else{
      ui->statusBar->showMessage("Error connecting to database", MESSAGE_DISPLAY_LENGTH);
    }
  return status;
}

void MainWindow::close_database(){
//  bool status = transaction_db.commit();
//  qDebug() << "Closing database: " << status;
  transaction_db.close();
}

void MainWindow::on_actionAbout_triggered()
{
}

void MainWindow::on_pushButtonSubmit_clicked()
{
  if(!open_database()){
      //error opening db.
      //notify user transaction was not saved.

      qDebug() << "Error connecting to database (submit button)";
      return;
    }
  QString description = ui->lineEditDescription->text();
  QString mode = ui->comboBoxMode->currentText();
  double amount = ui->lineEditDepWithdr->text().toDouble();

  if(amount == 0 || description == "" || mode == ""){
      QMessageBox::critical(this, "Invalid Input", "Please provide a description, amount and if this transaction is a deposit or withdrawal.");
      qDebug() << "Invalid Input";
      return;
  }
  QSqlQuery last_trans = transaction_db.exec("SELECT amount FROM transactions ORDER BY id DESC LIMIT 1;");
  double last_balance = 0;
  if(last_trans.next()){
      qDebug() << "this should print once";
      last_balance = last_trans.value(0).toDouble();
  }
  qDebug() << "balance: " << last_balance;
  if(mode == "Deposit"){
      qDebug() << "Depositing " << amount << " to " << last_balance;
      last_balance += amount;
      ui->labelTotal->setText("Total: " + format.toCurrencyString(last_balance));
  }else if(mode == "Withdraw"){
      qDebug() << "Withdrawing " << amount << " from " << last_balance;
      if(last_balance - amount < 0){
          QMessageBox::information(this, "Insufficient Funds", "There is not enough money to withdraw " + format.toCurrencyString(amount));
          return;
      }
      last_balance -= amount;
      ui->labelTotal->setText("Total: " + format.toCurrencyString(last_balance));
  }
  QSqlQuery add_transaction_qry;
  add_transaction_qry.prepare("INSERT INTO transactions (description, amount, mode, date_added) VALUES (:desc, :amount, :mode, :date);");
  add_transaction_qry.bindValue(":desc", description);
  add_transaction_qry.bindValue(":amount", last_balance);
  add_transaction_qry.bindValue(":mode", mode);
  add_transaction_qry.bindValue(":date", ui->dateEdit->date());
  bool result = add_transaction_qry.exec();
  qDebug() << add_transaction_qry.lastError();
  if(result){
      ui->statusBar->showMessage("Transaction saved", MESSAGE_DISPLAY_LENGTH);
      ui->lineEditDepWithdr->setText("");
      ui->lineEditDescription->setText("");
      ui->comboBoxMode->setCurrentIndex(-1);
      ui->pushButtonSubmit->setEnabled(false);
      QTimer::singleShot(1750, this, SLOT(reenable_submit_btn()));
    }else{
      ui->statusBar->showMessage("Error saving transaction", MESSAGE_DISPLAY_LENGTH);
    }
  close_database();
}

void MainWindow::reenable_submit_btn(){
  ui->pushButtonSubmit->setEnabled(true);
}
<commit_msg>added new column to db that holds the remaining balance. made some other small adjustments for error checking.<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QIcon>
#include <QDebug>
#include <QMessageBox>
#include <QFileInfo>

#define MESSAGE_DISPLAY_LENGTH 4000

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowIcon(QIcon(":/imgs/money_management.gif"));
    setWindowTitle("Money Management");
    setFixedSize(width(), height());
    ui->dateEdit->setDate(QDate::currentDate());
    ui->comboBoxMode->addItem("Deposit");
    ui->comboBoxMode->addItem("Withdraw");
    ui->comboBoxMode->setCurrentIndex(-1);
    ui->lineEditDepWithdr->setValidator(new QDoubleValidator(1, 10000000, 2, ui->lineEditDepWithdr));
    db_path = QDir::currentPath() + "/transaction_db.db";

    setup_database();

    open_database();
    QSqlQuery qry = transaction_db.exec("SELECT balance FROM transactions ORDER BY id DESC LIMIT 1;");
    if(qry.next()){
      ui->labelTotal->setText("Total: " + format.toCurrencyString(qry.value(0).toDouble()));
    }else{
      ui->labelTotal->setText("Total: " + format.toCurrencyString(0.00));
    }
    close_database();
}

MainWindow::~MainWindow()
{
//    QDir d;
//    d.remove(db_path);
    delete ui;
}
void MainWindow::setup_database(){
  transaction_db = QSqlDatabase::addDatabase("QSQLITE");
  transaction_db.setDatabaseName(db_path);
  QFileInfo db_info(db_path);
  bool exists = db_info.exists();
  bool status = transaction_db.open();
  if(!status){
      ui->statusBar->showMessage("Error connecting to database", MESSAGE_DISPLAY_LENGTH);
      QMessageBox::critical(this, "Error Connecting To Database", "Error opening database, please restart the program");
      ui->lineEditDepWithdr->setEnabled(false);
      ui->lineEditDescription->setEnabled(false);
      ui->pushButtonSubmit->setEnabled(false);
      ui->comboBoxMode->setEnabled(false);
      ui->dateEdit->setEnabled(false);
      ui->menuBar->setEnabled(false);
      return;
  }
  ui->statusBar->showMessage("Connected...", MESSAGE_DISPLAY_LENGTH);
  if(!exists){
      qDebug() << "Creating transaction table";
      QSqlQuery create_transaction_table_qry = transaction_db.exec("CREATE TABLE transactions(id INTEGER PRIMARY KEY AUTOINCREMENT, description TEXT, mode TEXT, trans_amount DOUBLE, balance DOUBLE, date_added DATE);");
      qDebug() << create_transaction_table_qry.lastError();
  }
  close_database();
}

bool MainWindow::open_database(){
  bool status = transaction_db.open();
  if(status){
      ui->statusBar->showMessage("Connected...", MESSAGE_DISPLAY_LENGTH);
  }else{
      ui->statusBar->showMessage("Error connecting to database", MESSAGE_DISPLAY_LENGTH);
      QMessageBox::critical(this, "Error Connecting To Database", "Error opening database, please restart the program");
      ui->lineEditDepWithdr->setEnabled(false);
      ui->lineEditDescription->setEnabled(false);
      ui->pushButtonSubmit->setEnabled(false);
      ui->comboBoxMode->setEnabled(false);
      ui->dateEdit->setEnabled(false);
      ui->menuBar->setEnabled(false);
    }
  return status;
}

void MainWindow::close_database(){
//  bool status = transaction_db.commit();
//  qDebug() << "Closing database: " << status;
  transaction_db.close();
}

void MainWindow::on_actionAbout_triggered()
{
}

void MainWindow::on_pushButtonSubmit_clicked()
{
  if(!open_database()){
      //error opening db.
      //notify user transaction was not saved.
      QMessageBox::critical(this, "Error Saving Transaction", "Error saving the transaction, please try again");
      qDebug() << "Error connecting to database (submit button)";
      return;
    }
  QString description = ui->lineEditDescription->text();
  QString mode = ui->comboBoxMode->currentText();
  double amount = ui->lineEditDepWithdr->text().toDouble();

  if(amount == 0 || description == "" || mode == ""){
      QMessageBox::critical(this, "Invalid Input", "Please provide a description, amount and if this transaction is a deposit or withdrawal.");
      qDebug() << "Invalid Input";
      return;
  }
  QSqlQuery last_trans = transaction_db.exec("SELECT balance FROM transactions ORDER BY id DESC LIMIT 1;");
  double last_balance = 0;
  if(last_trans.next()){
      qDebug() << "this should print once";
      last_balance = last_trans.value(0).toDouble();
  }
  qDebug() << "balance: " << last_balance;
  if(mode == "Deposit"){
      qDebug() << "Depositing " << amount << " to " << last_balance;
      last_balance += amount;
      ui->labelTotal->setText("Total: " + format.toCurrencyString(last_balance));
  }else if(mode == "Withdraw"){
      qDebug() << "Withdrawing " << amount << " from " << last_balance;
      if(last_balance - amount < 0){
          QMessageBox::information(this, "Insufficient Funds", "There is not enough money to withdraw " + format.toCurrencyString(amount));
          return;
      }
      last_balance -= amount;
      ui->labelTotal->setText("Total: " + format.toCurrencyString(last_balance));
  }
  QSqlQuery add_transaction_qry;
  add_transaction_qry.prepare("INSERT INTO transactions (description, mode, trans_amount, balance, date_added) VALUES (:desc, :mode, :trans_amount, :balance, :date);");
  add_transaction_qry.bindValue(":desc", description);
  add_transaction_qry.bindValue(":mode", mode);
  add_transaction_qry.bindValue(":trans_amount", amount);
  add_transaction_qry.bindValue(":balance", last_balance);
  add_transaction_qry.bindValue(":date", ui->dateEdit->date());
  bool result = add_transaction_qry.exec();
  qDebug() << add_transaction_qry.lastError();
  if(result){
      ui->statusBar->showMessage("Transaction saved", MESSAGE_DISPLAY_LENGTH);
      ui->lineEditDepWithdr->setText("");
      ui->lineEditDescription->setText("");
      ui->comboBoxMode->setCurrentIndex(-1);
      ui->pushButtonSubmit->setEnabled(false);
      QTimer::singleShot(1750, this, SLOT(reenable_submit_btn()));
    }else{
      ui->statusBar->showMessage("Error saving transaction", MESSAGE_DISPLAY_LENGTH);
    }
  close_database();
}

void MainWindow::reenable_submit_btn(){
  ui->pushButtonSubmit->setEnabled(true);
}
<|endoftext|>


<commit_before>#include "pci_handler.hpp"

#include <cstdint>
#include <vector>

namespace blobs
{

std::vector<std::uint8_t> PciDataHandler::copyFrom(std::uint32_t length)
{
    /* TODO: implement this. */
    return {};
}

bool PciDataHandler::write(const std::vector<std::uint8_t>& configuration)
{
    /* PCI handler doesn't require configuration write, only read. */
    return false;
}

std::vector<std::uint8_t> PciDataHandler::read()
{
    /* PCI handler does require returning a configuration from read. */
    struct PciConfigResponse reply;
    reply.address = 0;

    std::vector<std::uint8_t> bytes;
    bytes.resize(sizeof(reply));

    return bytes;
}

} // namespace blobs
<commit_msg>pci: continued implementation in P2A bridge handler<commit_after>#include "pci_handler.hpp"

#include <cstdint>
#include <cstring>
#include <vector>

namespace blobs
{

std::vector<std::uint8_t> PciDataHandler::copyFrom(std::uint32_t length)
{
    /* TODO: implement this. */
    return {};
}

bool PciDataHandler::write(const std::vector<std::uint8_t>& configuration)
{
    /* PCI handler doesn't require configuration write, only read. */
    return false;
}

std::vector<std::uint8_t> PciDataHandler::read()
{
    /* PCI handler does require returning a configuration from read. */
    struct PciConfigResponse reply;
    reply.address = 0;

    std::vector<std::uint8_t> bytes;
    bytes.resize(sizeof(reply));
    std::memcpy(bytes.data(), &reply, sizeof(reply));

    return bytes;
}

} // namespace blobs
<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE "test_clementi_dihedral_potential"

#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif

#include <test/mjolnir/traits.hpp>
#include <mjolnir/potential/local/ClementiDihedralPotential.hpp>
#include <mjolnir/math/constants.hpp>
#include <mjolnir/util/make_unique.hpp>

BOOST_AUTO_TEST_CASE(ClementiDihedral_double)
{
    using real_type = double;
    constexpr static std::size_t       N   = 1000;
    constexpr static real_type h   = 1e-6;
    constexpr static real_type pi  = mjolnir::math::constants<real_type>::pi;

    const real_type k1 = 1.0;
    const real_type k3 = 2.0;
    const real_type r0 = pi * 2. / 3.;

    mjolnir::ClementiDihedralPotential<real_type> c(k1, k3, r0);

    const real_type x_min = 0.;
    const real_type x_max = 2. * pi;
    const real_type dx = (x_max - x_min) / N;

    real_type x = x_min;
    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x_up = (x+h < 2. * pi) ? x+h : x+h - 2. * pi;
        const real_type x_bt = (x-h > 0) ? x-h : x-h + 2. * pi;
        const real_type pot1 = c.potential(x_up);
        const real_type pot2 = c.potential(x_bt);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = c.derivative(x);

        if(std::abs(deri) > h)
            BOOST_CHECK_CLOSE_FRACTION(dpot, deri, h);
        else
            BOOST_CHECK_SMALL(deri, h);

        x += dx;
    }
}

BOOST_AUTO_TEST_CASE(ClementiDihedral_float)
{
    using real_type = float;
    constexpr static std::size_t       N    = 100;
    constexpr static real_type h    = 1e-3;
    constexpr static real_type pi = mjolnir::math::constants<real_type>::pi;

    const real_type k1 = 1.0;
    const real_type k3 = 2.0;
    const real_type r0 = pi * 2. / 3.;

    mjolnir::ClementiDihedralPotential<real_type> c(k1, k3, r0);

    const real_type x_min = 0.;
    const real_type x_max = 2. * pi;
    const real_type dx = (x_max - x_min) / N;

    real_type x = x_min;
    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x_up = (x+h < 2. * pi) ? x+h : x+h - 2. * pi;
        const real_type x_bt = (x-h > 0) ? x-h : x-h + 2. * pi;
        const real_type pot1 = c.potential(x_up);
        const real_type pot2 = c.potential(x_bt);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = c.derivative(x);

        if(std::abs(deri) > h)
            BOOST_CHECK_CLOSE_FRACTION(dpot, deri, h);
        else
            BOOST_CHECK_SMALL(deri, h);

        x += dx;
    }
}

<commit_msg>change delta for numerical derivative<commit_after>#define BOOST_TEST_MODULE "test_clementi_dihedral_potential"

#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif

#include <test/mjolnir/traits.hpp>
#include <mjolnir/potential/local/ClementiDihedralPotential.hpp>
#include <mjolnir/math/constants.hpp>
#include <mjolnir/util/make_unique.hpp>

BOOST_AUTO_TEST_CASE(ClementiDihedral_double)
{
    using real_type = double;
    constexpr static std::size_t N  = 1000;
    constexpr static real_type   h  = 1e-6;
    constexpr static real_type   pi = mjolnir::math::constants<real_type>::pi;

    const real_type k1 = 1.0;
    const real_type k3 = 2.0;
    const real_type r0 = pi * 2. / 3.;

    mjolnir::ClementiDihedralPotential<real_type> c(k1, k3, r0);

    const real_type x_min = 0.;
    const real_type x_max = 2. * pi;
    const real_type dx = (x_max - x_min) / N;

    real_type x = x_min;
    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x_up = (x+h < 2. * pi) ? x+h : x+h - 2. * pi;
        const real_type x_bt = (x-h > 0) ? x-h : x-h + 2. * pi;
        const real_type pot1 = c.potential(x_up);
        const real_type pot2 = c.potential(x_bt);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = c.derivative(x);

        if(std::abs(deri) > h)
            BOOST_CHECK_CLOSE_FRACTION(dpot, deri, h);
        else
            BOOST_CHECK_SMALL(deri, h);

        x += dx;
    }
}

BOOST_AUTO_TEST_CASE(ClementiDihedral_float)
{
    using real_type = float;
    constexpr static std::size_t N  = 100;
    constexpr static real_type   h  = 1e-2f;
    constexpr static real_type   pi = mjolnir::math::constants<real_type>::pi;

    const real_type k1 = 1.0f;
    const real_type k3 = 2.0f;
    const real_type r0 = pi * 2. / 3.;

    mjolnir::ClementiDihedralPotential<real_type> c(k1, k3, r0);

    const real_type x_min = 0.f;
    const real_type x_max = 2.f * pi;
    const real_type dx = (x_max - x_min) / N;

    real_type x = x_min;
    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x_up = (x+h < 2.0f * pi) ? x+h : x+h - 2.0f * pi;
        const real_type x_bt = (x-h > 0.0f) ? x-h : x-h + 2.0f * pi;
        const real_type pot1 = c.potential(x_up);
        const real_type pot2 = c.potential(x_bt);
        const real_type dpot = (pot1 - pot2) / (2.0f * h);
        const real_type deri = c.derivative(x);

        if(std::abs(deri) > h)
        {
            BOOST_CHECK_CLOSE_FRACTION(dpot, deri, h);
        }
        else
        {
            BOOST_CHECK_SMALL(deri, h);
        }

        x += dx;
    }
}

<|endoftext|>


<commit_before>#include "../test_jsonserializer.hpp"
#include "nnlib/serialization/jsonserializer.hpp"
#include "nnlib/serialization/serialized.hpp"
#include "nnlib/nn/linear.hpp"
#include "nnlib/nn/sequential.hpp"
#include "nnlib/nn/tanh.hpp"
using namespace nnlib;

void TestJSONSerializer()
{
	// basic serialization

	{
		Serialized s;
		s.set("library", "nnlib");
		s.set("awesome", true);
		s.set("notAwesome", false);
		s.set("number", 32);
		s.set("number", 42);
		s.set("negative", -42);
		s.set("nothing", Serialized::Null);
		s.set("emptyArray", Serialized::Array);
		s.set("emptyObject", Serialized::Object);
		s.set("nested", Serialized::Object);

		s.get("nested")->set("indented", true);
		s.get("nested")->set("powerLevel", "> 9000");

		// minified
		{
			std::stringstream ss;
			JSONSerializer::write(s, ss);

			Serialized d = JSONSerializer::read(ss);

			NNAssertEquals(d.get<std::string>("library"), "nnlib", "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("awesome"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("notAwesome"), false, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("number"), 42, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("negative"), -42, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<float>("number"), 42.0, 1e-12, "JSONSerializer failed!");
			NNAssertEquals(d.type("nothing"), Serialized::Null, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyArray"), 0, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyObject"), 0, "JSONSerializer failed!");

			NNAssertEquals(d.get("nested")->get<bool>("indented"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get("nested")->get<std::string>("powerLevel"), "> 9000", "JSONSerializer failed!");
		}

		// pretty
		{
			std::stringstream ss;
			JSONSerializer::write(s, ss, true);

			Serialized d = JSONSerializer::read(ss);

			NNAssertEquals(d.get<std::string>("library"), "nnlib", "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("awesome"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("notAwesome"), false, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("number"), 42, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("negative"), -42, "JSONSerializer failed!");
			NNAssertEquals(d.type("nothing"), Serialized::Null, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyArray"), 0, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyObject"), 0, "JSONSerializer failed!");

			NNAssertEquals(d.get("nested")->get<bool>("indented"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get("nested")->get<std::string>("powerLevel"), "> 9000", "JSONSerializer failed!");
		}

		// from string
		{
			std::string jsonString = "[ \"hello, my name is \\\"bingo\\\"\", 1.22e1, 512e-2, -3.14 ]";
			std::stringstream ss(jsonString);

			Serialized d = JSONSerializer::read(ss);

			NNAssertEquals(d.get<std::string>(0), "hello, my name is \"bingo\"", "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(1), 12.2, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(2), 5.12, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(3), -3.14, 1e-12, "JSONSerializer failed!");

			std::stringstream ss2;
			JSONSerializer::write(d, ss2);
			d = JSONSerializer::read(ss2);

			NNAssertEquals(d.get<std::string>(0), "hello, my name is \"bingo\"", "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(1), 12.2, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(2), 5.12, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(3), -3.14, 1e-12, "JSONSerializer failed!");
		}

		// incompatibility

		bool ok = false;
		try
		{
			std::stringstream ss("[ this is not json ]");
			JSONSerializer::read(ss);
		}
		catch(const Error &)
		{
			ok = true;
		}
		NNAssert(ok, "JSONSerializer failed! Allowed invalid JSON values!");
	}

	// neural network serialization

	{
		Sequential<NN_REAL_T> nn(
			new Linear<NN_REAL_T>(10, 5),
			new TanH<NN_REAL_T>(),
			new Linear<NN_REAL_T>(5, 10),
			new TanH<NN_REAL_T>()
		);

		std::stringstream ss;
		JSONSerializer::write(nn, ss);

		Sequential<NN_REAL_T> *deserialized = JSONSerializer::read(ss).get<Sequential<NN_REAL_T> *>();

		auto &p1 = nn.params();
		auto &p2 = deserialized->params();

		for(auto i = p1.begin(), j = p2.begin(), end = p1.end(); i != end; ++i, ++j)
		{
			NNAssertAlmostEquals(*i, *j, 1e-12, "JSONSerializer failed!");
		}

		delete deserialized;
	}
}
<commit_msg>Formatting change in JSON test.<commit_after>#include "../test_jsonserializer.hpp"
#include "nnlib/serialization/jsonserializer.hpp"
#include "nnlib/serialization/serialized.hpp"
#include "nnlib/nn/linear.hpp"
#include "nnlib/nn/sequential.hpp"
#include "nnlib/nn/tanh.hpp"
using namespace nnlib;

void TestJSONSerializer()
{
	// basic serialization

	{
		Serialized s;
		s.set("library", "nnlib");
		s.set("awesome", true);
		s.set("notAwesome", false);
		s.set("number", 32);
		s.set("number", 42);
		s.set("negative", -42);
		s.set("nothing", Serialized::Null);
		s.set("emptyArray", Serialized::Array);
		s.set("emptyObject", Serialized::Object);
		s.set("nested", Serialized::Object);
		s.get("nested")->set("indented", true);
		s.get("nested")->set("powerLevel", "> 9000");

		// minified
		{
			std::stringstream ss;
			JSONSerializer::write(s, ss);

			Serialized d = JSONSerializer::read(ss);

			NNAssertEquals(d.get<std::string>("library"), "nnlib", "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("awesome"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("notAwesome"), false, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("number"), 42, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("negative"), -42, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<float>("number"), 42.0, 1e-12, "JSONSerializer failed!");
			NNAssertEquals(d.type("nothing"), Serialized::Null, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyArray"), 0, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyObject"), 0, "JSONSerializer failed!");
			NNAssertEquals(d.get("nested")->get<bool>("indented"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get("nested")->get<std::string>("powerLevel"), "> 9000", "JSONSerializer failed!");
		}

		// pretty
		{
			std::stringstream ss;
			JSONSerializer::write(s, ss, true);

			Serialized d = JSONSerializer::read(ss);

			NNAssertEquals(d.get<std::string>("library"), "nnlib", "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("awesome"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get<bool>("notAwesome"), false, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("number"), 42, "JSONSerializer failed!");
			NNAssertEquals(d.get<size_t>("negative"), -42, "JSONSerializer failed!");
			NNAssertEquals(d.type("nothing"), Serialized::Null, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyArray"), 0, "JSONSerializer failed!");
			NNAssertEquals(d.size("emptyObject"), 0, "JSONSerializer failed!");
			NNAssertEquals(d.get("nested")->get<bool>("indented"), true, "JSONSerializer failed!");
			NNAssertEquals(d.get("nested")->get<std::string>("powerLevel"), "> 9000", "JSONSerializer failed!");
		}

		// from string
		{
			std::string jsonString = "[ \"hello, my name is \\\"bingo\\\"\", 1.22e1, 512e-2, -3.14 ]";
			std::stringstream ss(jsonString);

			Serialized d = JSONSerializer::read(ss);

			NNAssertEquals(d.get<std::string>(0), "hello, my name is \"bingo\"", "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(1), 12.2, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(2), 5.12, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(3), -3.14, 1e-12, "JSONSerializer failed!");

			std::stringstream ss2;
			JSONSerializer::write(d, ss2);
			d = JSONSerializer::read(ss2);

			NNAssertEquals(d.get<std::string>(0), "hello, my name is \"bingo\"", "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(1), 12.2, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(2), 5.12, 1e-12, "JSONSerializer failed!");
			NNAssertAlmostEquals(d.get<double>(3), -3.14, 1e-12, "JSONSerializer failed!");
		}

		// incompatibility

		bool ok = false;
		try
		{
			std::stringstream ss("[ this is not json ]");
			JSONSerializer::read(ss);
		}
		catch(const Error &)
		{
			ok = true;
		}
		NNAssert(ok, "JSONSerializer failed! Allowed invalid JSON values!");
	}

	// neural network serialization

	{
		Sequential<NN_REAL_T> nn(
			new Linear<NN_REAL_T>(10, 5),
			new TanH<NN_REAL_T>(),
			new Linear<NN_REAL_T>(5, 10),
			new TanH<NN_REAL_T>()
		);

		std::stringstream ss;
		JSONSerializer::write(nn, ss);

		Sequential<NN_REAL_T> *deserialized = JSONSerializer::read(ss).get<Sequential<NN_REAL_T> *>();

		auto &p1 = nn.params();
		auto &p2 = deserialized->params();

		for(auto i = p1.begin(), j = p2.begin(), end = p1.end(); i != end; ++i, ++j)
		{
			NNAssertAlmostEquals(*i, *j, 1e-12, "JSONSerializer failed!");
		}

		delete deserialized;
	}
}
<|endoftext|>


<commit_before>#include <cstdio>

int main(){
    double n,  d[] = {100.0, 50.0, 20.0, 10.0, 5.0, 2.0, 1.0, 0.5, 0.25, 0.10, 0.05, 0.01};
    int t = 0, c;

    scanf("%lf", &n);
    printf("NOTAS:\n");

    t = 0;
    while (n >= 0.01){
        c = 0;
        while (n >= d[t]){
            n -= d[t];
            c++;
        }
        if (d[t] == 1.0)
            printf("MOEDAS:\n");
        if (d[t] >= 2.0 )
            printf("%d nota(s) de R$ %.2f\n", c, d[t]);
        else
            printf("%d moeda(s) de R$ %.2f\n", c, d[t]);
        t++;
    }
    return 0;
}
<commit_msg>Corrige 1021.<commit_after>#include <cstdio>

int main(){
    double n,  d[] = {100.0, 50.0, 20.0, 10.0, 5.0, 2.0, 1.0, 0.5, 0.25, 0.10, 0.05, 0.01};
    int t = 0, c, i;

    scanf("%lf", &n);
    printf("NOTAS:\n");

    t = 0;
    for (i = 0; i < 12; i++) {
        c = 0;
        while (n >= d[t]){
            n -= d[t];
            c++;
        }
        if (d[t] == 1.0)
            printf("MOEDAS:\n");
        if (d[t] >= 2.0 )
            printf("%d nota(s) de R$ %.2f\n", c, d[t]);
        else
            printf("%d moeda(s) de R$ %.2f\n", c, d[t]);
        t++;
    }
    return 0;
}
<|endoftext|>


<commit_before>#include "IOHandler.hh"

#include <stdio.h>
#include <sys/event.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include <chrono>

#include "ZMQ.hh"
#include "GPIO.hh"
#include "Config.hh"

#define KE_LEN 32
// the oneshot offset for kqueue ident
#define ID_OS_OFFSET 100
// the pulse offset for kqueue ident
#define ID_PULSE_OFFSET 1000

namespace aegir {

  IOHandler::IOHandler(GPIO &_gpio, SPI &_spi): c_gpio(_gpio), c_spi(_spi),
    c_mq_pub(ZMQ::SocketType::PUB),
    c_mq_iocmd(ZMQ::SocketType::SUB) {
    auto cfg = Config::getInstance();

    // first initialize the sensors
    for (int i=0; i<4; ++i) {
      c_tcs.push_back(std::make_unique<MAX31856>(c_spi, i));
    }
    // now set them up
    {
      auto nf = cfg->getMAX31856NoiseFilter();
      auto tctype = cfg->getMAX31856TCType();
      for (auto &it: c_tcs) {
	it->setAvgMode(MAX31856::AvgMode::S8); // averages of 4 samples
	it->set50Hz(nf == NoiseFilters::HZ50);
	it->setTCType(tctype);
	it->setConversionMode(true);
      }
    }
    // fetch the sensor mapping from the config
    cfg->getThermocouples(c_tcmap);
    // and the reading interval
    c_thermoival = cfg->getTCival();
    // and the pin polling ival
    c_pinival = cfg->getPINival();

    auto thrmgr = ThreadManager::getInstance();

    c_mq_pub.bind("inproc://iopub");
    c_mq_iocmd.bind("inproc://iocmd").subscribe("");

    // later we might need to handle unused pins for multiple configs here
    for ( auto &it: g_pinconfig ) {
      if ( it.second.mode == PinMode::IN ) {
#ifdef AEGIR_DEBUG
	printf("IOHandle: Loading PIN %s\n", it.first.c_str());
#endif
	c_inpins[it.first] = PINState::Unknown;
      } else if ( it.second.mode == PinMode::OUT ) {
	c_outpins[it.first].state = PINState::Off;
	c_outpins[it.first].name = it.first;
	c_gpio[it.first].low();
      }
    }

    c_kq = kqueue();

    thrmgr->addThread("IOHandler", *this);
  }

  IOHandler::~IOHandler() {
    close(c_kq);
    for ( auto &it: c_outpins ) {
      c_gpio[it.first].low();
    }
  }

  void IOHandler::readTCs() {
    struct timeval tv;
    gettimeofday(&tv, 0);
#ifdef AEGIR_DEBUG
    printf("\nIOHandler::readTCs():\n");
    auto start = std::chrono::high_resolution_clock::now();
#endif
    for (auto &it: c_tcmap) {
      float temp = c_tcs[it.second]->readTCTemp();
      float cjtemp = c_tcs[it.second]->readCJTemp();
#ifdef AEGIR_DEBUG
      float cjto = c_tcs[it.second]->getCJOffset();
      printf("Sensor %s/%i temp: %f C Time: %li CJ:%.2f CJTO:%.4f\n", it.first.c_str(), it.second, temp,
	     tv.tv_sec, cjtemp, cjto);
#endif
      c_mq_pub.send(ThermoReadingMessage(it.first, temp, tv.tv_sec));
    }
#ifdef AEGIR_DEBUG
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> diff = end-start;
    printf("IOHandler::readTCs(): sensor reading took %.4f ms\n", diff.count()*1000);
#endif
  }

  void IOHandler::handlePins() {
    PINState newval;
    std::shared_ptr<Message> msg;

    // read the input pins
    for (auto &it: c_inpins) {
      newval = c_gpio[it.first].get();
      if ( newval != it.second ) {
#ifdef AEGIR_DEBUG
	printf("IOHandle: Pin %s %hhu -> %hhu\n ", it.first.c_str(),
	       (uint8_t)it.second, (uint8_t)newval);
#endif
	it.second = newval;
	auto msg = PinStateMessage(it.first, newval);
	c_mq_pub.send(msg);
      }
    }

    // check our input queue
    while ( (msg = c_mq_iocmd.recv()) != nullptr ) {
      if ( msg->type() == MessageType::PINSTATE ) {
	auto psmsg = std::static_pointer_cast<PinStateMessage>(msg);
	auto it = c_outpins.find(psmsg->getName());
	if ( it != c_outpins.end() ) {
#ifdef AEGIR_DEBUG
	  printf("IOHandler: setting %s to %hhu\n", psmsg->getName().c_str(), (uint8_t)psmsg->getState());
#endif
	  // always clear the pulsate state, it's either not needed,
	  // or has to be replaced by new parameters
	  if ( c_outpins[psmsg->getName()].state == PINState::Pulsate ) {
	    clearPulsate(c_gpio[psmsg->getName()].getID());
	    c_gpio[psmsg->getName()].low();
	  }

	  if ( psmsg->getState() == PINState::On ) {
	    c_gpio[psmsg->getName()].high();
	    c_outpins[psmsg->getName()].state = PINState::On;

	  } else if ( psmsg->getState() == PINState::Off )  {
	    c_gpio[psmsg->getName()].low();
	    c_outpins[psmsg->getName()].state = PINState::Off;

	  } else if ( psmsg->getState() == PINState::Pulsate ) {
	    // for pulsating, we have to start with an On state, then
	    // create a oneshot for adding the repeating Off
	    struct kevent ke[2];
	    // cycle time in milliseconds
	    int ctms = 1000*psmsg->getCycletime();
	    // offset in milliseconds
	    int offsetms = ctms*psmsg->getOnratio();
	    // the gpio pin id
	    int id = c_gpio[psmsg->getName()].getID();
	    // the passed udata
	    outpindata *opd = &(c_outpins[psmsg->getName()]);
	    void *udata = (void*)opd;
	    opd->cycletime = ctms;
	    opd->onratio   = psmsg->getOnratio();

#ifdef AEGIR_DEBUG
	    printf("IOHandler starting pulsate: %s/%i C:%i OR:%.2f\n", psmsg->getName().c_str(), id,
		   opd->cycletime, opd->onratio);
#endif

	    // EV_SET(kev, ident, filter, flags, fflags, data, udata);
#ifdef AEGIR_DEBUG
	    printf("IOHandler EV_SET(%i, TIMER, ADD|ENABLE, MSECONDS, %i, udata)\n",
		   ID_PULSE_OFFSET+2*id+1, ctms);
	    printf("IOHandler EV_SET(%i, TIMER, ADD|ENABLE|ONESHOT, MSECONDS, %i, udata)\n",
		   ID_OS_OFFSET+id, offsetms);
#endif
	    EV_SET(&ke[0], ID_PULSE_OFFSET+2*id+1, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_MSECONDS, ctms, udata);
	    EV_SET(&ke[1], ID_OS_OFFSET+id, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_MSECONDS, offsetms, udata);
	    if ( kevent(c_kq, ke, 2, 0, 0, 0) < 0 ) {
	      printf("kevent failed: %i/%s\n", errno, strerror(errno));
	    }
	    c_gpio[psmsg->getName()].high();
	    c_outpins[psmsg->getName()].state = PINState::Pulsate;
	  } else {
	    printf("IOHandler:%i: Unhandled pinstate %hhu\n", __LINE__, psmsg->getState());
	  }
	} else {
	  printf("IOHandler: can't set %s to %hhu: no such pin\n", psmsg->getName().c_str(), psmsg->getState());
	}
      }
    }
  }

  void IOHandler::clearPulsate(int id) {
    struct kevent ke[3];

    // EV_SET(kev, ident, filter, flags, fflags, data, udata);
    EV_SET(&ke[0], ID_PULSE_OFFSET+2*id+0, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
    EV_SET(&ke[1], ID_PULSE_OFFSET+2*id+1, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
    EV_SET(&ke[2], ID_OS_OFFSET+id, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
    kevent(c_kq, ke, 3, 0, 0, 0);
  }

  void IOHandler::run() {
    printf("IOHandler started\n");

    // set our kqueue up
    struct kevent ke[KE_LEN];
    struct kevent oske;
    int nchanges = KE_LEN;
    int ident;
    int filter;
    // EV_SET(kev, ident, filter, flags, fflags, data, udata);
    EV_SET(&ke[0], 0, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_SECONDS, c_thermoival, 0);
    EV_SET(&ke[1], 1, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_MSECONDS, c_pinival, 0);
    if ( kevent(c_kq, ke, 2, 0, 0, 0) < 0 ) {
      printf("kevent failed: %i/%s\n", errno, strerror(errno));
    }

    while ( c_run ) {
      // start with kevent
      if ( (nchanges = kevent(c_kq, 0, 0, ke, KE_LEN, 0)) > 0 ) {
	for (int i=0; i<nchanges; ++i) {
	  ident  = ke[i].ident;
	  filter = ke[i].filter;
	  // EVFILT_TIMER with ident=0 is our sensor timer
	  // we only ready the sensors, when a brew process is active
	  if ( filter == EVFILT_TIMER && ident == 0 ) {
	    // TC reading
	    readTCs();
	  } else if ( filter == EVFILT_TIMER && ident == 1 ) {
	    // general PIN handling
	    handlePins();
	  } else if ( filter == EVFILT_TIMER && ident >= ID_OS_OFFSET && ident < ID_PULSE_OFFSET ) {
	    // offset timer installation
	    int pindent = ident - ID_OS_OFFSET;
	    outpindata *opd = (outpindata*)ke[i].udata;

	    EV_SET(&oske, ID_PULSE_OFFSET+2*pindent+0, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_MSECONDS, opd->cycletime, (void*)opd);
	    c_gpio[opd->name].low();
	    if ( kevent(c_kq, &oske, 1, 0, 0, 0) < 0 ) {
	      printf("kevent failed: %i/%s\n", errno, strerror(errno));
	    }

	  } else if ( filter == EVFILT_TIMER && ident >= ID_PULSE_OFFSET ) {
	    int pindent = ((ident - ID_PULSE_OFFSET) & 0x0ffe)/2;
	    int up = ident & 1;
	    outpindata *opd = (outpindata*)ke[i].udata;

#ifdef AEGIR_DEBUG
	    printf("IOHandler timer ident %i -> %i up:%i\n", ident, pindent, up);
#endif

	    if ( up ) {
	      c_gpio[opd->name].high();
	    } else {
	      c_gpio[opd->name].low();
	    }
	  }
	}
      }

    }

    printf("IOHandler stopped\n");
  }
}
<commit_msg>[brewd] Moved CJTemp reading inside the debug ifdef, where it belongs to<commit_after>#include "IOHandler.hh"

#include <stdio.h>
#include <sys/event.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include <chrono>

#include "ZMQ.hh"
#include "GPIO.hh"
#include "Config.hh"

#define KE_LEN 32
// the oneshot offset for kqueue ident
#define ID_OS_OFFSET 100
// the pulse offset for kqueue ident
#define ID_PULSE_OFFSET 1000

namespace aegir {

  IOHandler::IOHandler(GPIO &_gpio, SPI &_spi): c_gpio(_gpio), c_spi(_spi),
    c_mq_pub(ZMQ::SocketType::PUB),
    c_mq_iocmd(ZMQ::SocketType::SUB) {
    auto cfg = Config::getInstance();

    // first initialize the sensors
    for (int i=0; i<4; ++i) {
      c_tcs.push_back(std::make_unique<MAX31856>(c_spi, i));
    }
    // now set them up
    {
      auto nf = cfg->getMAX31856NoiseFilter();
      auto tctype = cfg->getMAX31856TCType();
      for (auto &it: c_tcs) {
	it->setAvgMode(MAX31856::AvgMode::S8); // averages of 4 samples
	it->set50Hz(nf == NoiseFilters::HZ50);
	it->setTCType(tctype);
	it->setConversionMode(true);
      }
    }
    // fetch the sensor mapping from the config
    cfg->getThermocouples(c_tcmap);
    // and the reading interval
    c_thermoival = cfg->getTCival();
    // and the pin polling ival
    c_pinival = cfg->getPINival();

    auto thrmgr = ThreadManager::getInstance();

    c_mq_pub.bind("inproc://iopub");
    c_mq_iocmd.bind("inproc://iocmd").subscribe("");

    // later we might need to handle unused pins for multiple configs here
    for ( auto &it: g_pinconfig ) {
      if ( it.second.mode == PinMode::IN ) {
#ifdef AEGIR_DEBUG
	printf("IOHandle: Loading PIN %s\n", it.first.c_str());
#endif
	c_inpins[it.first] = PINState::Unknown;
      } else if ( it.second.mode == PinMode::OUT ) {
	c_outpins[it.first].state = PINState::Off;
	c_outpins[it.first].name = it.first;
	c_gpio[it.first].low();
      }
    }

    c_kq = kqueue();

    thrmgr->addThread("IOHandler", *this);
  }

  IOHandler::~IOHandler() {
    close(c_kq);
    for ( auto &it: c_outpins ) {
      c_gpio[it.first].low();
    }
  }

  void IOHandler::readTCs() {
    struct timeval tv;
    gettimeofday(&tv, 0);
#ifdef AEGIR_DEBUG
    printf("\nIOHandler::readTCs():\n");
    auto start = std::chrono::high_resolution_clock::now();
#endif
    for (auto &it: c_tcmap) {
      float temp = c_tcs[it.second]->readTCTemp();
#ifdef AEGIR_DEBUG
      float cjtemp = c_tcs[it.second]->readCJTemp();
      float cjto = c_tcs[it.second]->getCJOffset();
      printf("Sensor %s/%i temp: %f C Time: %li CJ:%.2f CJTO:%.4f\n", it.first.c_str(), it.second, temp,
	     tv.tv_sec, cjtemp, cjto);
#endif
      c_mq_pub.send(ThermoReadingMessage(it.first, temp, tv.tv_sec));
    }
#ifdef AEGIR_DEBUG
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> diff = end-start;
    printf("IOHandler::readTCs(): sensor reading took %.4f ms\n", diff.count()*1000);
#endif
  }

  void IOHandler::handlePins() {
    PINState newval;
    std::shared_ptr<Message> msg;

    // read the input pins
    for (auto &it: c_inpins) {
      newval = c_gpio[it.first].get();
      if ( newval != it.second ) {
#ifdef AEGIR_DEBUG
	printf("IOHandle: Pin %s %hhu -> %hhu\n ", it.first.c_str(),
	       (uint8_t)it.second, (uint8_t)newval);
#endif
	it.second = newval;
	auto msg = PinStateMessage(it.first, newval);
	c_mq_pub.send(msg);
      }
    }

    // check our input queue
    while ( (msg = c_mq_iocmd.recv()) != nullptr ) {
      if ( msg->type() == MessageType::PINSTATE ) {
	auto psmsg = std::static_pointer_cast<PinStateMessage>(msg);
	auto it = c_outpins.find(psmsg->getName());
	if ( it != c_outpins.end() ) {
#ifdef AEGIR_DEBUG
	  printf("IOHandler: setting %s to %hhu\n", psmsg->getName().c_str(), (uint8_t)psmsg->getState());
#endif
	  // always clear the pulsate state, it's either not needed,
	  // or has to be replaced by new parameters
	  if ( c_outpins[psmsg->getName()].state == PINState::Pulsate ) {
	    clearPulsate(c_gpio[psmsg->getName()].getID());
	    c_gpio[psmsg->getName()].low();
	  }

	  if ( psmsg->getState() == PINState::On ) {
	    c_gpio[psmsg->getName()].high();
	    c_outpins[psmsg->getName()].state = PINState::On;

	  } else if ( psmsg->getState() == PINState::Off )  {
	    c_gpio[psmsg->getName()].low();
	    c_outpins[psmsg->getName()].state = PINState::Off;

	  } else if ( psmsg->getState() == PINState::Pulsate ) {
	    // for pulsating, we have to start with an On state, then
	    // create a oneshot for adding the repeating Off
	    struct kevent ke[2];
	    // cycle time in milliseconds
	    int ctms = 1000*psmsg->getCycletime();
	    // offset in milliseconds
	    int offsetms = ctms*psmsg->getOnratio();
	    // the gpio pin id
	    int id = c_gpio[psmsg->getName()].getID();
	    // the passed udata
	    outpindata *opd = &(c_outpins[psmsg->getName()]);
	    void *udata = (void*)opd;
	    opd->cycletime = ctms;
	    opd->onratio   = psmsg->getOnratio();

#ifdef AEGIR_DEBUG
	    printf("IOHandler starting pulsate: %s/%i C:%i OR:%.2f\n", psmsg->getName().c_str(), id,
		   opd->cycletime, opd->onratio);
#endif

	    // EV_SET(kev, ident, filter, flags, fflags, data, udata);
#ifdef AEGIR_DEBUG
	    printf("IOHandler EV_SET(%i, TIMER, ADD|ENABLE, MSECONDS, %i, udata)\n",
		   ID_PULSE_OFFSET+2*id+1, ctms);
	    printf("IOHandler EV_SET(%i, TIMER, ADD|ENABLE|ONESHOT, MSECONDS, %i, udata)\n",
		   ID_OS_OFFSET+id, offsetms);
#endif
	    EV_SET(&ke[0], ID_PULSE_OFFSET+2*id+1, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_MSECONDS, ctms, udata);
	    EV_SET(&ke[1], ID_OS_OFFSET+id, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_MSECONDS, offsetms, udata);
	    if ( kevent(c_kq, ke, 2, 0, 0, 0) < 0 ) {
	      printf("kevent failed: %i/%s\n", errno, strerror(errno));
	    }
	    c_gpio[psmsg->getName()].high();
	    c_outpins[psmsg->getName()].state = PINState::Pulsate;
	  } else {
	    printf("IOHandler:%i: Unhandled pinstate %hhu\n", __LINE__, psmsg->getState());
	  }
	} else {
	  printf("IOHandler: can't set %s to %hhu: no such pin\n", psmsg->getName().c_str(), psmsg->getState());
	}
      }
    }
  }

  void IOHandler::clearPulsate(int id) {
    struct kevent ke[3];

    // EV_SET(kev, ident, filter, flags, fflags, data, udata);
    EV_SET(&ke[0], ID_PULSE_OFFSET+2*id+0, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
    EV_SET(&ke[1], ID_PULSE_OFFSET+2*id+1, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
    EV_SET(&ke[2], ID_OS_OFFSET+id, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
    kevent(c_kq, ke, 3, 0, 0, 0);
  }

  void IOHandler::run() {
    printf("IOHandler started\n");

    // set our kqueue up
    struct kevent ke[KE_LEN];
    struct kevent oske;
    int nchanges = KE_LEN;
    int ident;
    int filter;
    // EV_SET(kev, ident, filter, flags, fflags, data, udata);
    EV_SET(&ke[0], 0, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_SECONDS, c_thermoival, 0);
    EV_SET(&ke[1], 1, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_MSECONDS, c_pinival, 0);
    if ( kevent(c_kq, ke, 2, 0, 0, 0) < 0 ) {
      printf("kevent failed: %i/%s\n", errno, strerror(errno));
    }

    while ( c_run ) {
      // start with kevent
      if ( (nchanges = kevent(c_kq, 0, 0, ke, KE_LEN, 0)) > 0 ) {
	for (int i=0; i<nchanges; ++i) {
	  ident  = ke[i].ident;
	  filter = ke[i].filter;
	  // EVFILT_TIMER with ident=0 is our sensor timer
	  // we only ready the sensors, when a brew process is active
	  if ( filter == EVFILT_TIMER && ident == 0 ) {
	    // TC reading
	    readTCs();
	  } else if ( filter == EVFILT_TIMER && ident == 1 ) {
	    // general PIN handling
	    handlePins();
	  } else if ( filter == EVFILT_TIMER && ident >= ID_OS_OFFSET && ident < ID_PULSE_OFFSET ) {
	    // offset timer installation
	    int pindent = ident - ID_OS_OFFSET;
	    outpindata *opd = (outpindata*)ke[i].udata;

	    EV_SET(&oske, ID_PULSE_OFFSET+2*pindent+0, EVFILT_TIMER, EV_ADD|EV_ENABLE, NOTE_MSECONDS, opd->cycletime, (void*)opd);
	    c_gpio[opd->name].low();
	    if ( kevent(c_kq, &oske, 1, 0, 0, 0) < 0 ) {
	      printf("kevent failed: %i/%s\n", errno, strerror(errno));
	    }

	  } else if ( filter == EVFILT_TIMER && ident >= ID_PULSE_OFFSET ) {
	    int pindent = ((ident - ID_PULSE_OFFSET) & 0x0ffe)/2;
	    int up = ident & 1;
	    outpindata *opd = (outpindata*)ke[i].udata;

#ifdef AEGIR_DEBUG
	    printf("IOHandler timer ident %i -> %i up:%i\n", ident, pindent, up);
#endif

	    if ( up ) {
	      c_gpio[opd->name].high();
	    } else {
	      c_gpio[opd->name].low();
	    }
	  }
	}
      }

    }

    printf("IOHandler stopped\n");
  }
}
<|endoftext|>


<commit_before>/*
    This file is part of Akregator.
    Copyright (c) 2008 Frank Osterfeld <osterfeld@kde.org>

    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "akregator_config_general.h"
#include "akregatorconfig.h"

#include "ui_settings_general.h"

#include <KAboutData>
#include <KConfigDialogManager>
#include <KGenericFactory>
#include <KLocalizedString>
#include <kdemacros.h>

#include <QVBoxLayout>

using namespace Akregator;

K_PLUGIN_FACTORY(KCMAkregatorGeneralConfigFactory, registerPlugin<KCMAkregatorGeneralConfig>();)
K_EXPORT_PLUGIN(KCMAkregatorGeneralConfigFactory( "kcmakrgeneralconfig" ))

KCMAkregatorGeneralConfig::KCMAkregatorGeneralConfig( QWidget* parent, const QVariantList& args )
    : KCModule( KCMAkregatorGeneralConfigFactory::componentData(), parent, args ), m_widget( new QWidget )
{
    Ui::SettingsGeneral ui;
    ui.setupUi( m_widget );

    QVBoxLayout* layout = new QVBoxLayout( this );
    layout->addWidget( m_widget );

    ui.kcfg_AutoFetchInterval->setSuffix(ki18np(" minute", "minutes"));

    connect( ui.kcfg_UseIntervalFetch, SIGNAL( toggled( bool ) ),
             ui.kcfg_AutoFetchInterval, SLOT( setEnabled( bool ) ) );
    connect( ui.kcfg_UseIntervalFetch, SIGNAL( toggled( bool ) ),
             ui.autoFetchIntervalLabel, SLOT( setEnabled( bool ) ) );

    KAboutData *about = new KAboutData( I18N_NOOP( "kcmakrgeneralconfig" ), 0,
                                        ki18n( "Configure Feeds" ),
                                        0, KLocalizedString(), KAboutData::License_GPL,
                                        ki18n( "(c), 2004 - 2008 Frank Osterfeld" ) );

    about->addAuthor( ki18n( "Frank Osterfeld" ), KLocalizedString(), "osterfeld@kde.org" );
    setAboutData( about );

    addConfig( Settings::self(), m_widget );
}

#include "akregator_config_general.moc"
<commit_msg>Fix missing whitespace.<commit_after>/*
    This file is part of Akregator.
    Copyright (c) 2008 Frank Osterfeld <osterfeld@kde.org>

    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "akregator_config_general.h"
#include "akregatorconfig.h"

#include "ui_settings_general.h"

#include <KAboutData>
#include <KConfigDialogManager>
#include <KGenericFactory>
#include <KLocalizedString>
#include <kdemacros.h>

#include <QVBoxLayout>

using namespace Akregator;

K_PLUGIN_FACTORY(KCMAkregatorGeneralConfigFactory, registerPlugin<KCMAkregatorGeneralConfig>();)
K_EXPORT_PLUGIN(KCMAkregatorGeneralConfigFactory( "kcmakrgeneralconfig" ))

KCMAkregatorGeneralConfig::KCMAkregatorGeneralConfig( QWidget* parent, const QVariantList& args )
    : KCModule( KCMAkregatorGeneralConfigFactory::componentData(), parent, args ), m_widget( new QWidget )
{
    Ui::SettingsGeneral ui;
    ui.setupUi( m_widget );

    QVBoxLayout* layout = new QVBoxLayout( this );
    layout->addWidget( m_widget );

    ui.kcfg_AutoFetchInterval->setSuffix(ki18np(" minute", " minutes"));

    connect( ui.kcfg_UseIntervalFetch, SIGNAL( toggled( bool ) ),
             ui.kcfg_AutoFetchInterval, SLOT( setEnabled( bool ) ) );
    connect( ui.kcfg_UseIntervalFetch, SIGNAL( toggled( bool ) ),
             ui.autoFetchIntervalLabel, SLOT( setEnabled( bool ) ) );

    KAboutData *about = new KAboutData( I18N_NOOP( "kcmakrgeneralconfig" ), 0,
                                        ki18n( "Configure Feeds" ),
                                        0, KLocalizedString(), KAboutData::License_GPL,
                                        ki18n( "(c), 2004 - 2008 Frank Osterfeld" ) );

    about->addAuthor( ki18n( "Frank Osterfeld" ), KLocalizedString(), "osterfeld@kde.org" );
    setAboutData( about );

    addConfig( Settings::self(), m_widget );
}

#include "akregator_config_general.moc"
<|endoftext|>


<commit_before>#ifndef BNI_LEARNING_STEPWISE_STRUCTURE_HC_HPP
#define BNI_LEARNING_STEPWISE_STRUCTURE_HC_HPP

#include <array>
#include <string>
#include <random>
#include <bayesian/graph.hpp>
#include <bayesian/sampler.hpp>
#include <bayesian/hash.hpp>
#include <bayesian/evaluation/transinformation.hpp>
#include <bayesian/learning/utility.hpp>

namespace bn {
namespace learning {

class mutual_information_holder {
public:
    using pair_dictionary_type = std::unordered_map<std::pair<vertex_type, vertex_type>, double, bn::hash<std::pair<vertex_type, vertex_type>>>;

    mutual_information_holder(bn::sampler const& sampling)
        : sampling_(sampling), entropy_machine_(), mutual_machine_()
    {
    }

    double calculate_entropy(vertex_type const& node)
    {
        // 存在しているか
        auto it = entropy_dic_.find(node);
        if(it != entropy_dic_.end()) return it->second;

        // 新たに計算して返す
        auto value = entropy_machine_(sampling_, node);
        entropy_dic_.emplace(node, value);
        return value;
    }

    double calculate_joint_entropy(vertex_type const& lhs, vertex_type const& rhs)
    {
        auto jointed = make_vertex_pair(lhs, rhs);

        // 存在しているか
        auto it = joint_entropy_dic_.find(jointed);
        if(it != joint_entropy_dic_.end()) return it->second;

        // 新たに計算して返す
        auto value = entropy_machine_(sampling_, {jointed.first, jointed.second});
        joint_entropy_dic_.emplace(std::move(jointed), value);
        return value;
    }

    double calculate_similarity(vertex_type const& lhs, vertex_type const& rhs)
    {
        auto jointed = make_vertex_pair(lhs, rhs);

        // 存在しているか
        auto it = similarity_dic_.find(jointed);
        if(it != similarity_dic_.end()) return it->second;

        // 新たに計算して返す
        auto value = mutual_machine_(
            calculate_entropy(lhs),
            calculate_entropy(rhs),
            calculate_joint_entropy(lhs, rhs)
            );
        similarity_dic_.emplace(std::move(jointed), value);
        return value;
    }

    void delete_entropy(vertex_type const& node)
    {
        entropy_dic_.erase(node);
    }

    void delete_joint_entropy(vertex_type const& lhs, vertex_type const& rhs)
    {
        joint_entropy_dic_.erase(make_vertex_pair(lhs, rhs));
    }

    void delete_similarity(vertex_type const& lhs, vertex_type const& rhs)
    {
        similarity_dic_.erase(make_vertex_pair(lhs, rhs));
    }

private:
    std::pair<vertex_type, vertex_type> make_vertex_pair(vertex_type const& lhs, vertex_type const& rhs)
    {
        return (lhs < rhs) ? std::make_pair(lhs, rhs)
                           : std::make_pair(rhs, lhs);
    }

    sampler const& sampling_;
    bn::evaluation::entropy const entropy_machine_;
    bn::evaluation::mutual_information const mutual_machine_;
    std::unordered_map<vertex_type, double> entropy_dic_;
    pair_dictionary_type joint_entropy_dic_;
    pair_dictionary_type similarity_dic_;
};

template<class Eval, template<class> class BetweenLearning>
class stepwise_structure_hc {
public:
    using cluster_type = std::vector<vertex_type>;
    using similarity_type = std::tuple<const cluster_type*, const cluster_type*, double>;
    using Similarity = bn::evaluation::mutual_information;

    stepwise_structure_hc(bn::sampler const& sampling)
        : sampling_(sampling), eval_(sampling), learning_machine_(sampling_), engine_(make_engine<std::mt19937>()), info_holder_(sampling)
    {
    }

    double operator()(graph_t& graph)
    {
        // graphの初期化
        graph.erase_all_edge();

        // 初期クラスタと初期類似度を得る
        auto clusters = initial_clustering(graph.vertex_list()); // 初期クラスタ
        auto similarities = initial_similarity(clusters);        // 初期類似度

        // クラスタ間学習(結合)
        auto const score = learning_between_clusters(graph, clusters, similarities);

        return score;
    }

private:
    // 初期クラスタリングを行い，クラスタリング結果を返す
    // 第1引数: クラスタリング対象のノード集合
    std::vector<cluster_type> initial_clustering(std::vector<vertex_type> const& nodes)
    {
        // クラスタ集合
        std::vector<cluster_type> clusters(nodes.size());

        // 1ノード1クラスタ
        for(std::size_t i = 0; i < nodes.size(); ++i)
        {
            clusters[i].push_back(nodes[i]);
        }

        return clusters;
    }

    std::vector<similarity_type> initial_similarity(std::vector<cluster_type> const& clusters)
    {
        std::vector<similarity_type> similarities;
        for(std::size_t i = 0; i < clusters.size(); ++i)
        {
            for(std::size_t j = i + 1; j < clusters.size(); ++j)
            {
                auto const& i_cluster = clusters[i];
                auto const& j_cluster = clusters[j];
                similarities.push_back(make_similarity_tuple(i_cluster, j_cluster));
            }
        }
        return similarities;
    }

    similarity_type make_similarity_tuple(cluster_type const& lhs, cluster_type const& rhs)
    {
        // 2クラスタ間のノードのそれぞれの組み合わせ数
        auto const combination_num = lhs.size() * rhs.size();

        // 類似度計算
        double value = 0;
        for(auto const& lhs_nodes : lhs)
        {
            for(auto const& rhs_nodes : rhs)
            {
                // 数で割って足す(平均)
                value += info_holder_.calculate_similarity(lhs_nodes, rhs_nodes) / combination_num;
            }
        }

        // アドレスが小さいクラスタを先にして返す
        return (&lhs < &rhs) ? std::make_tuple(&lhs, &rhs, value)
                             : std::make_tuple(&rhs, &lhs, value);
    }

    // クラスタ間学習を行う
    double learning_between_clusters(
        graph_t& graph,
        std::vector<cluster_type>& clusters,
        std::vector<similarity_type>& similarities
        )
    {
        double score = std::numeric_limits<double>::max();

        while(clusters.size() != 1)
        {
            //
            // 階層的構造学習
            //

            // 最も似ているクラスタ間
            auto const most_similar = std::max_element(
                similarities.begin(), similarities.end(),
                [](similarity_type const& lhs, similarity_type const& rhs){ return std::get<2>(lhs) < std::get<2>(rhs); }
                );

            // most_similarのどちらが親か
            std::uniform_int_distribution<std::size_t> binary_dist(0, 1);
            const cluster_type *parent, *child;
            if(binary_dist(engine_)) std::tie(parent, child, std::ignore) = *most_similar;
            else                     std::tie(child, parent, std::ignore) = *most_similar;

            // parentとchildのindex
            std::size_t const parent_index =
                std::distance(clusters.begin(), std::find(clusters.begin(), clusters.end(), *parent));
            std::size_t const child_index  =
                std::distance(clusters.begin(), std::find(clusters.begin(), clusters.end(), *child));

            // learning
            score = learning_machine_.learn_with_hint(graph, *parent, *child);

            // 合成クラスタ
            cluster_type new_cluster;
            new_cluster.reserve(parent->size() + child->size());
            new_cluster.insert(new_cluster.end(), parent->cbegin(), parent->cend());
            new_cluster.insert(new_cluster.end(), child->cbegin(), child->cend());

            // 前のクラスタに関する類似度を削除
            similarities.erase(
                std::remove_if(
                    similarities.begin(), similarities.end(),
                    [parent, child](similarity_type const& similarity)
                    {
                        // 多分設計ミスってる
                        return
                            std::get<0>(similarity) == parent || std::get<0>(similarity) == child  ||
                            std::get<1>(similarity) == parent || std::get<1>(similarity) == child;
                    }),
                similarities.end()
                );

            // 前のクラスタを消して，挿入
            clusters.erase(clusters.begin() + parent_index);
            clusters.erase(clusters.begin() + child_index + (parent_index < child_index ? -1 : 0));
            clusters.push_back(std::move(new_cluster));

            //
            // 確率的枝刈り (Yet)
            //

            // 合成クラスタと各クラスタ間の類似度を更新
            cluster_type const& inserted_cluster = clusters.back(); // 挿入後のnew_clusterの参照
            std::for_each(
                clusters.begin(), clusters.end() - 1,
                [this, &inserted_cluster, &similarities](cluster_type const& cluster)
                {
                    similarities.push_back(make_similarity_tuple(inserted_cluster, cluster));
                });
        }

        return score;
    }

    double hierarchical_clustering(
        graph_t& graph,
        std::vector<cluster_type>& clusters,
        std::vector<similarity_type>& similarities
        )
    {
        // 最も似ているクラスタ間
        auto const most_similar = std::max_element(
            similarities.begin(), similarities.end(),
            [](similarity_type const& lhs, similarity_type const& rhs){ return std::get<2>(lhs) < std::get<2>(rhs); }
            );

        // most_similarのどちらが親か
        std::uniform_int_distribution<std::size_t> binary_dist(0, 1);
        const cluster_type *parent, *child;
        if(binary_dist(engine_)) std::tie(parent, child, std::ignore) = *most_similar;
        else                     std::tie(child, parent, std::ignore) = *most_similar;

        // parentとchildのindex
        std::size_t const parent_index =
            std::distance(clusters.begin(), std::find(clusters.begin(), clusters.end(), *parent));
        std::size_t const child_index  =
            std::distance(clusters.begin(), std::find(clusters.begin(), clusters.end(), *child));

        // learning
        return learning_machine_.learn_with_hint(graph, *parent, *child);
    }

    sampler sampling_;
    Eval eval_;
    BetweenLearning<Eval> learning_machine_;
    std::mt19937 engine_;
    mutual_information_holder info_holder_;
};

} // namespace learning
} // namespace bn

#endif // BNI_LEARNING_STEPWISE_STRUCTURE_HC_HPP
<commit_msg>Implemented a part of Stochastic Pruning in stepwise_structure_hc.hpp. And write some comment line in Japanese.<commit_after>#ifndef BNI_LEARNING_STEPWISE_STRUCTURE_HC_HPP
#define BNI_LEARNING_STEPWISE_STRUCTURE_HC_HPP

#include <array>
#include <string>
#include <random>
#include <bayesian/graph.hpp>
#include <bayesian/sampler.hpp>
#include <bayesian/hash.hpp>
#include <bayesian/evaluation/transinformation.hpp>
#include <bayesian/learning/utility.hpp>

namespace bn {
namespace learning {

// (同時)エントロピーを複数回計算する無駄を省くための辞書ホルダ
// 相互情報量規準
class mutual_information_holder {
public:
    using pair_dictionary_type = std::unordered_map<std::pair<vertex_type, vertex_type>, double, bn::hash<std::pair<vertex_type, vertex_type>>>;

    mutual_information_holder(bn::sampler const& sampling)
        : sampling_(sampling), entropy_machine_(), mutual_machine_()
    {
    }

    // エントロピーを計算して返す
    // node: 調べるノード
    double calculate_entropy(vertex_type const& node)
    {
        // 存在しているか
        auto it = entropy_dic_.find(node);
        if(it != entropy_dic_.end()) return it->second;

        // 新たに計算して返す
        auto value = entropy_machine_(sampling_, node);
        entropy_dic_.emplace(node, value);
        return value;
    }

    // 同時エントロピーを計算して返す
    // lhs, rhs: 調べるノード(順不同)
    double calculate_joint_entropy(vertex_type const& lhs, vertex_type const& rhs)
    {
        auto jointed = make_vertex_pair(lhs, rhs);

        // 存在しているか
        auto it = joint_entropy_dic_.find(jointed);
        if(it != joint_entropy_dic_.end()) return it->second;

        // 新たに計算して返す
        auto value = entropy_machine_(sampling_, {jointed.first, jointed.second});
        joint_entropy_dic_.emplace(std::move(jointed), value);
        return value;
    }

    // 類似度を計算して返す
    // 必要ならエントロピーを内部で計算する
    // lhs, rhs: 調べるノード(順不同)
    double calculate_similarity(vertex_type const& lhs, vertex_type const& rhs)
    {
        auto jointed = make_vertex_pair(lhs, rhs);

        // 存在しているか
        auto it = similarity_dic_.find(jointed);
        if(it != similarity_dic_.end()) return it->second;

        // 新たに計算して返す
        auto value = mutual_machine_(
            calculate_entropy(lhs),
            calculate_entropy(rhs),
            calculate_joint_entropy(lhs, rhs)
            );
        similarity_dic_.emplace(std::move(jointed), value);
        return value;
    }

    // 登録済みのエントロピーを消す
    void delete_entropy(vertex_type const& node)
    {
        entropy_dic_.erase(node);
    }

    // 登録済みの同時エントロピーを消す
    void delete_joint_entropy(vertex_type const& lhs, vertex_type const& rhs)
    {
        joint_entropy_dic_.erase(make_vertex_pair(lhs, rhs));
    }

    // 登録済みの類似度を消す
    void delete_similarity(vertex_type const& lhs, vertex_type const& rhs)
    {
        similarity_dic_.erase(make_vertex_pair(lhs, rhs));
    }

private:
    // pairの前半がアドレスの小さいノードになるように正規化する
    // (順不同性を保証する)
    std::pair<vertex_type, vertex_type> make_vertex_pair(vertex_type const& lhs, vertex_type const& rhs)
    {
        return (lhs < rhs) ? std::make_pair(lhs, rhs)
                           : std::make_pair(rhs, lhs);
    }

    // 学習器
    sampler const& sampling_;
    bn::evaluation::entropy const entropy_machine_;
    bn::evaluation::mutual_information const mutual_machine_;

    // 辞書
    std::unordered_map<vertex_type, double> entropy_dic_;
    pair_dictionary_type joint_entropy_dic_;
    pair_dictionary_type similarity_dic_;
};

template<class Eval, template<class> class BetweenLearning>
class stepwise_structure_hc {
public:
    using cluster_type = std::shared_ptr<std::vector<vertex_type>>;
    using similarity_type = std::tuple<cluster_type, cluster_type, double>;
    using Similarity = bn::evaluation::mutual_information;

    stepwise_structure_hc(bn::sampler const& sampling)
        : sampling_(sampling), eval_(sampling), learning_machine_(sampling_), engine_(make_engine<std::mt19937>()), info_holder_(sampling)
    {
    }

    // 階層的クラスタリング及び確率的枝刈りを用いた段階的構造学習法の実行
    // graph: グラフ構造(どんな構造が入っていたとしても，クリアされる)
    // alpha: 枝刈りが実行される確率に関する係数
    double operator()(graph_t& graph, double const alpha)
    {
        // graphの初期化
        graph.erase_all_edge();

        // 初期クラスタと初期類似度を初期化
        initial_clustering(graph.vertex_list()); // 初期クラスタ
        initial_similarities();                  // 初期類似度

        // クラスタ間学習(結合)
        auto const score = learning_between_clusters(graph, alpha);
        return score;
    }

private:
    // 初期クラスタリングを行い，クラスタリング結果をメンバ変数clusters_に格納する
    // 第1引数: クラスタリング対象のノード集合
    void initial_clustering(std::vector<vertex_type> const& nodes)
    {
        // クラスタ集合初期化
        clusters_.clear();
        clusters_.reserve(nodes.size());

        // 1ノード1クラスタ
        for(auto const& node : nodes)
        {
            auto cluster = std::make_shared<cluster_type::element_type>();
            cluster->push_back(node);
            clusters_.push_back(std::move(cluster));
        }
    }

    // 初期類似度計算を行い，類似度をメンバ変数similarities_に格納する
    void initial_similarities()
    {
        // 類似度集合初期化
        similarities_.clear();
        similarities_.reserve(clusters_.size() * clusters_.size());

        // 全クラスタペアについて，類似度計算
        for(std::size_t i = 0; i < clusters_.size(); ++i)
        {
            for(std::size_t j = i + 1; j < clusters_.size(); ++j)
            {
                auto const& i_cluster = clusters_[i];
                auto const& j_cluster = clusters_[j];
                similarities_.push_back(make_similarity_tuple(i_cluster, j_cluster));
            }
        }
    }

    // 指定したsimilarityにclusterが関与しているか(clusterに関する類似度か)どうかを返す
    bool is_related(similarity_type const& similarity, cluster_type const& cluster)
    {
        return std::get<0>(similarity) == cluster || std::get<1>(similarity) == cluster;
    }

    // 指定したsimilarityがlhsとrhsに関する類似度かどうかを返す
    bool is_connected(similarity_type const& similarity, cluster_type const& lhs, cluster_type const& rhs)
    {
        return std::get<0>(similarity) == std::min(lhs, rhs) && std::get<1>(similarity) == std::max(lhs, rhs);
    }

    // 引数の2つのクラスタを1つのクラスタに結合する
    // clusters_に追加されていれば，clusters_から削除する(副作用)
    cluster_type combine_clusters(cluster_type const& lhs, cluster_type const& rhs)
    {
        // 合成クラスタ
        auto new_cluster = std::make_shared<cluster_type::element_type>();
        new_cluster->reserve(lhs->size() + rhs->size());
        new_cluster->insert(new_cluster->end(), lhs->cbegin(), lhs->cend());
        new_cluster->insert(new_cluster->end(), rhs->cbegin(), rhs->cend());

        // 前のクラスタを消す
        clusters_.erase(std::find(clusters_.begin(), clusters_.end(), lhs));
        clusters_.erase(std::find(clusters_.begin(), clusters_.end(), rhs));

        return new_cluster;
    }

    // メンバ変数similarities_から最も順位の高いものを取り出し，無作為に親子を決定する
    std::tuple<cluster_type, cluster_type, double> most_similarity()
    {
        // 最も似ているクラスタ間
        auto const most_similar = std::max_element(
            similarities_.begin(), similarities_.end(),
            [](similarity_type const& lhs, similarity_type const& rhs){ return std::get<2>(lhs) < std::get<2>(rhs); }
            );

        // コピーして元のクラスタ間を消す
        auto result = *most_similar;
        similarities_.erase(most_similar);

        // most_similarのどちらが親か(一定条件で入れ替え)
        std::uniform_int_distribution<std::size_t> binary_dist(0, 1);
        if(binary_dist(engine_)) std::swap(std::get<0>(result), std::get<1>(result));

        return result;
    }

    // 与えられた2つのクラスタ間の類似度を求め，similarity_typeとして返す
    similarity_type make_similarity_tuple(cluster_type const& lhs, cluster_type const& rhs)
    {
        // 2クラスタ間のノードのそれぞれの組み合わせ数
        auto const combination_num = lhs->size() * rhs->size();

        // 類似度計算
        double value = 0;
        for(auto const& lhs_nodes : *lhs)
        {
            for(auto const& rhs_nodes : *rhs)
            {
                // 数で割って足す(平均)
                value += info_holder_.calculate_similarity(lhs_nodes, rhs_nodes) / combination_num;
            }
        }

        // アドレスが小さいクラスタを先にして返す
        return (lhs < rhs) ? std::make_tuple(lhs, rhs, value)
                           : std::make_tuple(rhs, lhs, value);
    }

    // クラスタ間学習を行う
    // graph, alpha: operator()参照
    double learning_between_clusters(graph_t& graph, double const alpha)
    {
        double score = std::numeric_limits<double>::max();

        while(clusters_.size() != 1 && !similarities_.empty())
        {
            //
            // 階層的構造学習 部分
            //

            // 結合対象を得る
            similarity_type const combine_target = most_similarity();
            auto const parent = std::get<0>(combine_target);
            auto const child  = std::get<1>(combine_target);

            // learning
            score = learning_machine_.learn_with_hint(graph, *parent, *child);

            // クラスタ合成
            clusters_.push_back(combine_clusters(parent, child));
            auto const& inserted_cluster = clusters_.back();      // 挿入後のnew_clusterの参照

            //
            // 確率的枝刈り 部分
            //
            stochastic_pruning(alpha, inserted_cluster, combine_target);
        }

        return score;
    }

    // 確率的枝刈りを行う
    // alpha: operator()に準ずる
    // new_cluster: 結合後のクラスタを示す
    // old_connection: 結合前の2クラスタ間のsimilarity_typeを示す
    void stochastic_pruning(
        double const alpha,
        cluster_type const& new_cluster, similarity_type const& old_connection
        )
    {
        // 全クラスタと検索
        for(auto const cluster : clusters_)
        {
            // 自分自身及び結合後のクラスタならばパスする
            if(cluster == new_cluster) continue;

            // 探索中クラスタが元のクラスタと幾つ接続されていたか調べる
            std::vector<similarity_type> connection;
            for(auto it = similarities_.begin(); it != similarities_.end(); )
            {
                if(is_connected(*it, cluster, std::get<0>(old_connection)) || is_connected(*it, cluster, std::get<1>(old_connection)))
                {
                    connection.push_back(*it);
                    it = similarities_.erase(it);
                }
                else ++it;
            }

            // 類似度を計算しておく
            auto new_similarity = make_similarity_tuple(new_cluster, cluster);

            // 枝刈り条件
            double probability;
            if(connection.size() == 2)
            {
                // 類似度の平均を求める
                double const average_similar = std::accumulate(
                    similarities_.begin(), similarities_.end(), 0.0,
                    [this](double val, similarity_type const& similarity) -> double
                    {
                        return val + std::get<2>(similarity) / similarities_.size();
                    });

                probability = std::pow(alpha, std::get<2>(new_similarity) / average_similar);
            }
            else if(connection.size() == 1)
            {
                probability = std::pow(alpha, std::get<2>(old_connection) / std::get<2>(connection[0]));
            }
            else if(connection.size() == 0)
            {
                // 張らない
                continue;
            }
            else throw std::runtime_error("too connection");

            // 確率により，probabilityの確率で枝刈り
            std::uniform_real_distribution<double> probability_dist(0.0, 1.0);
            if(probability_dist(engine_) < probability) continue; // 枝刈り

            // 刈らない
            similarities_.push_back(std::move(new_similarity));
        }

        // 消えたノードに関する類似度をすべて消す(ゴリ押し)
        for(auto it = similarities_.begin(); it != similarities_.end(); )
        {
            // 消えたノードに関係しているかどうか
            if(is_related(*it, std::get<0>(old_connection)) || is_related(*it, std::get<1>(old_connection)))
            {
                it = similarities_.erase(it);
            }
            else ++it;
        }
    }

    sampler sampling_;
    Eval eval_;
    BetweenLearning<Eval> learning_machine_;
    std::mt19937 engine_;
    mutual_information_holder info_holder_;

    std::vector<cluster_type> clusters_;
    std::vector<similarity_type> similarities_;
};

} // namespace learning
} // namespace bn

#endif // BNI_LEARNING_STEPWISE_STRUCTURE_HC_HPP
<|endoftext|>


<commit_before>/*
* Copyright (c) 2006, Ondrej Danek (www.ondrej-danek.net)
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Ondrej Danek nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "Sound.h"
#include "BonusList.h"
#include "Weapon.h"
#include "Util.h"
#include "ElevatorList.h"
#include "World.h"
#include "InfoMessageQueue.h"
#include "Explosion.h"
#include "Render.h"
#include "Fire.h"
#include "Video.h"
#include "Game.h"
#include "GameException.h"

namespace Duel6
{
	Game::Game(AppService& appService)
		: appService(appService), video(appService.getVideo()), messageQueue(D6_INFO_DURATION),
		world(D6_ANM_SPEED, D6_WAVE_HEIGHT, appService.getConsole()),
		renderer(*this, appService.getFont(), video, spriteList, messageQueue),
		ammoRange(15, 15), playedRounds(0), maxRounds(0),
		gameService(appService, spriteList, messageQueue, world, players)
	{}

	void Game::initialize()
	{
		appService.getConsole().printLine(D6_L("\n===Game initialization==="));
		Sound& sound = appService.getSound();
		waterSet = Water::createWaterSet(sound, appService.getTextureManager());
		roundStartSound = sound.loadSample("sound/game/round-start.wav");
		gameOverSound = sound.loadSample("sound/game/game-over.wav");
		renderer.initialize(appService.getTextureManager());
		world.initialize(D6_FILE_BLOCK_META);
	}

	void Game::splitScreenView(Player& player, Int32 x, Int32 y)
	{
		PlayerView view(x, y, video.getScreen().getClientWidth() / 2 - 4, video.getScreen().getClientHeight() / 2 - 4);
		player.setView(view);
		player.setInfoBarPosition(x + view.getWidth() / 2 - 76, y + 35);
	}

	void Game::setPlayerViews()
	{
		for (Player& player : players)
		{
			player.prepareCam(video, getScreenMode(), getScreenZoom(), world.getSizeX(), world.getSizeY());
		}

		if (screenMode == ScreenMode::FullScreen)
		{
			Size index = 0;
			Size partition = video.getScreen().getClientWidth() / players.size();

			for (Player& player : players)
			{
				player.setView(PlayerView(0, 40, video.getScreen().getClientWidth(), video.getScreen().getClientHeight() - 40));
				player.setInfoBarPosition(partition * index + partition / 2 - 80, 35);
				index++;
			}

			return;
		}
		else
		{
			if (players.size() == 2)
			{
				splitScreenView(players[0], video.getScreen().getClientWidth() / 4 + 2, 2);
				splitScreenView(players[1], video.getScreen().getClientWidth() / 4 + 2, video.getScreen().getClientHeight() / 2 + 2);
			}

			if (players.size() == 3)
			{
				splitScreenView(players[0], 2, 2);
				splitScreenView(players[1], video.getScreen().getClientWidth() / 2 + 2, 2);
				splitScreenView(players[2], video.getScreen().getClientWidth() / 4 + 2, video.getScreen().getClientHeight() / 2 + 2);
			}

			if (players.size() == 4)
			{
				splitScreenView(players[0], 2, 2);
				splitScreenView(players[1], video.getScreen().getClientWidth() / 2 + 2, 2);
				splitScreenView(players[2], 2, video.getScreen().getClientHeight() / 2 + 2);
				splitScreenView(players[3], video.getScreen().getClientWidth() / 2 + 2, video.getScreen().getClientHeight() / 2 + 2);
			}
		}
	}

	bool Game::isPossibleStartingPosition(Int32 x, Int32 y)
	{
		if (world.isWall(x, y, true) || world.isWater(x, y))
		{
			return false;
		}

		while (y-- > 0)
		{
			if (world.isWater(x, y))
			{
				return false;
			}
			if (world.isWall(x, y, true))
			{
				return true;
			}
		}

		return false;
	}

	void Game::findStartingPositions(std::queue<std::pair<Int32, Int32>>& startingPositions)
	{
		std::vector<std::pair<Int32, Int32>> possibleStartingPositions;

		for (Int32 y = 1; y < world.getSizeY(); y++)
		{
			for (Int32 x = 0; x < world.getSizeX(); x++)
			{
				if (isPossibleStartingPosition(x, y))
				{
					possibleStartingPositions.push_back(std::pair<Int32, Int32>(x, y));
				}
			}
		}

		if (possibleStartingPositions.empty())
		{
			D6_THROW(GameException, "No acceptable starting positions found in this level");
		}

		for (Size i = 0; i < players.size(); ++i)
		{
			Int32 arbitraryPosition = rand() % possibleStartingPositions.size();
			startingPositions.push(possibleStartingPositions[arbitraryPosition]);
		}
	}

	void Game::preparePlayers()
	{
		std::queue<std::pair<Int32, Int32>> startingPositions;
		findStartingPositions(startingPositions);

		for (Player& player : players)
		{
			Int32 ammo = ammoRange.first + rand() % (ammoRange.second - ammoRange.first + 1);
			std::pair<Int32, Int32>& position = startingPositions.front();
			player.startGame(position.first, position.second, ammo);
			startingPositions.pop();

			player.setBonus(D6_BONUS_INVUL, 2);
		}

		setPlayerViews();
	}

	void Game::checkWinner()
	{
		int numAlive = 0;
		Player* lastAlive = nullptr;

		for (Player& player : players)
		{
			if (!player.isDead())
			{
				numAlive++;
				lastAlive = &player;
			}
		}

		if (numAlive < 2)
		{
			winner = 1;
			gameOverWait = D6_GAME_OVER_WAIT;

			if (numAlive == 1)
			{
				messageQueue.add(*lastAlive, D6_L("You have won - press ESC to quit or F1 for another round"));
				lastAlive->getPerson().addWins(1);
			}
			else
			{
				for (const Player& player : players)
				{
					messageQueue.add(player, D6_L("End of round - no winner"));
				}
			}
		}
	}

	void Game::beforeStart(Context* prevContext)
	{
		SDL_ShowCursor(SDL_DISABLE);
	}

	void Game::beforeClose(Context* nextContext)
	{
	}

	void Game::update(Float32 elapsedTime)
	{
		for (Player& player : players)
		{
			player.update(world, getScreenMode(), elapsedTime);
		}

		world.update(elapsedTime);
		spriteList.update(elapsedTime * D6_SPRITE_SPEED_COEF);
		WPN_MoveShots(gameService, elapsedTime);
		EXPL_MoveAll(elapsedTime);
		ELEV_MoveAll(elapsedTime);
		messageQueue.update(elapsedTime);

		// Add new bonuses
		if (rand() % int(3.0f / elapsedTime) == 0)
		{
			BONUS_AddNew(world);
		}

		// Check if there's a winner
		if (winner == -1)
		{
			checkWinner();
		}
		else if (winner == 1)
		{
			gameOverWait -= elapsedTime;
			if (gameOverWait <= 0)
			{
				gameOverWait = 0;
				winner = 2;
				gameOverSound.play();
			}
		}
		updateNotifications(elapsedTime);
	}

	void Game::updateNotifications(Float32 elapsedTime)
	{
		if (showYouAreHere > 0)
		{
			showYouAreHere -= elapsedTime * 3;
		}
		else
		{
			showYouAreHere = 0;
		}
	}

	void Game::render() const
	{
		renderer.render();
	}

	void Game::keyEvent(SDL_Keycode keyCode, Uint16 keyModifiers)
	{
		if (keyCode == SDLK_ESCAPE)
		{
			close();
		}

		// Restart game
		bool roundLimit = (maxRounds > 0) && (playedRounds >= maxRounds);
		if (keyCode == SDLK_F1 && !roundLimit)
		{
			nextRound((keyModifiers & KMOD_SHIFT) != 0);
			return;
		}

		// Switch between fullscreen and split screen mode
		if (keyCode == SDLK_F2 && players.size() < 5)
		{
			switchScreenMode();
		}

		// Turn on/off player statistics
		if (keyCode == SDLK_F4)
		{
			renderer.setShowRanking(!renderer.getShowRanking());
		}

		// Save screenshot
		if (keyCode == SDLK_F10)
		{
			std::string name = Util::saveScreenTga(video);
			appService.getConsole().printLine(Format(D6_L("Screenshot saved to {0}")) << name);
		}
	}

	void Game::textInputEvent(const char* text)
	{
	}

	void Game::switchScreenMode()
	{
		screenMode = (screenMode == ScreenMode::FullScreen) ? ScreenMode::SplitScreen : ScreenMode::FullScreen;
		setPlayerViews();
		renderer.initScreen();
	}

	void Game::start(const std::vector<PlayerDefinition>& playerDefinitions, const std::vector<std::string>& levels, const std::vector<Size>& backgrounds, ScreenMode screenMode, Int32 screenZoom)
	{
		appService.getConsole().printLine("\n=== Starting new game ===");
		appService.getConsole().printLine(Format("...Rounds: {0}") << maxRounds);
		TextureManager& textureManager = appService.getTextureManager();
		players.clear();
		skins.clear();

		for (const PlayerDefinition& playerDef : playerDefinitions)
		{
			appService.getConsole().printLine(Format("...Generating player for person: {0}") << playerDef.getPerson().getName());
			skins.push_back(std::make_unique<PlayerSkin>(D6_TEXTURE_MAN_PATH, playerDef.getColors(), textureManager));
			players.push_back(Player(playerDef.getPerson(), *skins.back(), playerDef.getSounds(), playerDef.getControls(), spriteList, messageQueue, waterSet));
		}

		this->levels = levels;
		this->backgrounds = backgrounds;
		this->screenMode = screenMode;
		this->screenZoom = screenZoom;

		playedRounds = 0;
		nextRound(false);
	}

	void Game::nextRound(bool sameLevel)
	{
		if (!sameLevel)
		{
			lastLevel = rand() % levels.size();
		}

		const std::string levelPath = levels[lastLevel];
		Console& console = appService.getConsole();
		console.printLine(Format(D6_L("\n===Loading level {0}===")) << levelPath);
		bool mirror = rand() % 2 == 0;
		world.loadLevel(levelPath, backgrounds[rand() % backgrounds.size()], mirror);
		world.prepareFaces();
		console.printLine(Format(D6_L("...Width   : {0}")) << world.getSizeX());
		console.printLine(Format(D6_L("...Height  : {0}")) << world.getSizeY());
		console.printLine(Format(D6_L("...Walls   : {0}")) << world.getWalls().getFaces().size());
		console.printLine(Format(D6_L("...Sprites : {0}")) << world.getSprites().getFaces().size());
		console.printLine(Format(D6_L("...Water   : {0}")) << world.getWater().getFaces().size());

		spriteList.clear();

		console.printLine(D6_L("...Preparing players"));
		preparePlayers();

		console.printLine(D6_L("...Level initialization"));
		WPN_LevelInit();
		EXPL_Clear();
		BONUS_Clear();
		FIRE_Find(world.getSprites());
		renderer.initScreen();
		winner = -1;
		playedRounds++;
		showYouAreHere = D6_YOU_ARE_HERE_DURATION;

		roundStartSound.play();
	}

	std::vector<const Player*> Game::getLadder() const
	{
		std::vector<const Player*> ladder;

		for (const Player& player : players)
		{
			ladder.push_back(&player);
		}

		std::sort(ladder.begin(), ladder.end(), [](const Player* pl1, const Player* pl2) {
			return pl2->getPerson().getTotalPoints() < pl1->getPerson().getTotalPoints();
		});
		return ladder;
	}

	Color Game::getGameOverOverlay() const
	{
		Uint8 overlay = Uint8(255.0f * gameOverWait / D6_GAME_OVER_WAIT);
		if (isOver())
		{
			overlay = std::min(overlay + 51, 255);
			return Color(overlay);
		}

		return Color(255, overlay, overlay);
	}

	World& Game::getWorld()
	{
		return world;
	}

	const World& Game::getWorld() const
	{
		return world;
	}
}<commit_msg>Stop round immediatelly after last kill<commit_after>/*
* Copyright (c) 2006, Ondrej Danek (www.ondrej-danek.net)
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Ondrej Danek nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "Sound.h"
#include "BonusList.h"
#include "Weapon.h"
#include "Util.h"
#include "ElevatorList.h"
#include "World.h"
#include "InfoMessageQueue.h"
#include "Explosion.h"
#include "Render.h"
#include "Fire.h"
#include "Video.h"
#include "Game.h"
#include "GameException.h"

namespace Duel6
{
	Game::Game(AppService& appService)
		: appService(appService), video(appService.getVideo()), messageQueue(D6_INFO_DURATION),
		world(D6_ANM_SPEED, D6_WAVE_HEIGHT, appService.getConsole()),
		renderer(*this, appService.getFont(), video, spriteList, messageQueue),
		ammoRange(15, 15), playedRounds(0), maxRounds(0),
		gameService(appService, spriteList, messageQueue, world, players)
	{}

	void Game::initialize()
	{
		appService.getConsole().printLine(D6_L("\n===Game initialization==="));
		Sound& sound = appService.getSound();
		waterSet = Water::createWaterSet(sound, appService.getTextureManager());
		roundStartSound = sound.loadSample("sound/game/round-start.wav");
		gameOverSound = sound.loadSample("sound/game/game-over.wav");
		renderer.initialize(appService.getTextureManager());
		world.initialize(D6_FILE_BLOCK_META);
	}

	void Game::splitScreenView(Player& player, Int32 x, Int32 y)
	{
		PlayerView view(x, y, video.getScreen().getClientWidth() / 2 - 4, video.getScreen().getClientHeight() / 2 - 4);
		player.setView(view);
		player.setInfoBarPosition(x + view.getWidth() / 2 - 76, y + 35);
	}

	void Game::setPlayerViews()
	{
		for (Player& player : players)
		{
			player.prepareCam(video, getScreenMode(), getScreenZoom(), world.getSizeX(), world.getSizeY());
		}

		if (screenMode == ScreenMode::FullScreen)
		{
			Size index = 0;
			Size partition = video.getScreen().getClientWidth() / players.size();

			for (Player& player : players)
			{
				player.setView(PlayerView(0, 40, video.getScreen().getClientWidth(), video.getScreen().getClientHeight() - 40));
				player.setInfoBarPosition(partition * index + partition / 2 - 80, 35);
				index++;
			}

			return;
		}
		else
		{
			if (players.size() == 2)
			{
				splitScreenView(players[0], video.getScreen().getClientWidth() / 4 + 2, 2);
				splitScreenView(players[1], video.getScreen().getClientWidth() / 4 + 2, video.getScreen().getClientHeight() / 2 + 2);
			}

			if (players.size() == 3)
			{
				splitScreenView(players[0], 2, 2);
				splitScreenView(players[1], video.getScreen().getClientWidth() / 2 + 2, 2);
				splitScreenView(players[2], video.getScreen().getClientWidth() / 4 + 2, video.getScreen().getClientHeight() / 2 + 2);
			}

			if (players.size() == 4)
			{
				splitScreenView(players[0], 2, 2);
				splitScreenView(players[1], video.getScreen().getClientWidth() / 2 + 2, 2);
				splitScreenView(players[2], 2, video.getScreen().getClientHeight() / 2 + 2);
				splitScreenView(players[3], video.getScreen().getClientWidth() / 2 + 2, video.getScreen().getClientHeight() / 2 + 2);
			}
		}
	}

	bool Game::isPossibleStartingPosition(Int32 x, Int32 y)
	{
		if (world.isWall(x, y, true) || world.isWater(x, y))
		{
			return false;
		}

		while (y-- > 0)
		{
			if (world.isWater(x, y))
			{
				return false;
			}
			if (world.isWall(x, y, true))
			{
				return true;
			}
		}

		return false;
	}

	void Game::findStartingPositions(std::queue<std::pair<Int32, Int32>>& startingPositions)
	{
		std::vector<std::pair<Int32, Int32>> possibleStartingPositions;

		for (Int32 y = 1; y < world.getSizeY(); y++)
		{
			for (Int32 x = 0; x < world.getSizeX(); x++)
			{
				if (isPossibleStartingPosition(x, y))
				{
					possibleStartingPositions.push_back(std::pair<Int32, Int32>(x, y));
				}
			}
		}

		if (possibleStartingPositions.empty())
		{
			D6_THROW(GameException, "No acceptable starting positions found in this level");
		}

		for (Size i = 0; i < players.size(); ++i)
		{
			Int32 arbitraryPosition = rand() % possibleStartingPositions.size();
			startingPositions.push(possibleStartingPositions[arbitraryPosition]);
		}
	}

	void Game::preparePlayers()
	{
		std::queue<std::pair<Int32, Int32>> startingPositions;
		findStartingPositions(startingPositions);

		for (Player& player : players)
		{
			Int32 ammo = ammoRange.first + rand() % (ammoRange.second - ammoRange.first + 1);
			std::pair<Int32, Int32>& position = startingPositions.front();
			player.startGame(position.first, position.second, ammo);
			startingPositions.pop();

			player.setBonus(D6_BONUS_INVUL, 2);
		}

		setPlayerViews();
	}

	void Game::checkWinner()
	{
		int numAlive = 0;
		Player* lastAlive = nullptr;

		for (Player& player : players)
		{
			if (!player.isDead())
			{
				numAlive++;
				lastAlive = &player;
			}
		}

		if (numAlive < 2)
		{
			winner = 1;
			gameOverWait = D6_GAME_OVER_WAIT;

			if (numAlive == 1)
			{
				messageQueue.add(*lastAlive, D6_L("You have won - press ESC to quit or F1 for another round"));
				lastAlive->getPerson().addWins(1);
			}
			else
			{
				for (const Player& player : players)
				{
					messageQueue.add(player, D6_L("End of round - no winner"));
				}
			}
		}
	}

	void Game::beforeStart(Context* prevContext)
	{
		SDL_ShowCursor(SDL_DISABLE);
	}

	void Game::beforeClose(Context* nextContext)
	{
	}

	void Game::update(Float32 elapsedTime)
	{
        // Check if there's a winner
        if (winner == -1)
        {
            checkWinner();
        }

        if (hasWinner())
        {
            gameOverWait -= elapsedTime;
            if (gameOverWait <= 0)
            {
                gameOverWait = 0;
                winner = 2;
                gameOverSound.play();
            }
            return;
        }

		for (Player& player : players)
		{
			player.update(world, getScreenMode(), elapsedTime);
		}

		world.update(elapsedTime);
		spriteList.update(elapsedTime * D6_SPRITE_SPEED_COEF);
		WPN_MoveShots(gameService, elapsedTime);
		EXPL_MoveAll(elapsedTime);
		ELEV_MoveAll(elapsedTime);
		messageQueue.update(elapsedTime);

		// Add new bonuses
		if (rand() % int(3.0f / elapsedTime) == 0)
		{
			BONUS_AddNew(world);
		}

		updateNotifications(elapsedTime);
	}

	void Game::updateNotifications(Float32 elapsedTime)
	{
		if (showYouAreHere > 0)
		{
			showYouAreHere -= elapsedTime * 3;
		}
		else
		{
			showYouAreHere = 0;
		}
	}

	void Game::render() const
	{
		renderer.render();
	}

	void Game::keyEvent(SDL_Keycode keyCode, Uint16 keyModifiers)
	{
		if (keyCode == SDLK_ESCAPE)
		{
			close();
		}

		// Restart game
		bool roundLimit = (maxRounds > 0) && (playedRounds >= maxRounds);
		if (keyCode == SDLK_F1 && !roundLimit)
		{
			nextRound((keyModifiers & KMOD_SHIFT) != 0);
			return;
		}

		// Switch between fullscreen and split screen mode
		if (keyCode == SDLK_F2 && players.size() < 5)
		{
			switchScreenMode();
		}

		// Turn on/off player statistics
		if (keyCode == SDLK_F4)
		{
			renderer.setShowRanking(!renderer.getShowRanking());
		}

		// Save screenshot
		if (keyCode == SDLK_F10)
		{
			std::string name = Util::saveScreenTga(video);
			appService.getConsole().printLine(Format(D6_L("Screenshot saved to {0}")) << name);
		}
	}

	void Game::textInputEvent(const char* text)
	{
	}

	void Game::switchScreenMode()
	{
		screenMode = (screenMode == ScreenMode::FullScreen) ? ScreenMode::SplitScreen : ScreenMode::FullScreen;
		setPlayerViews();
		renderer.initScreen();
	}

	void Game::start(const std::vector<PlayerDefinition>& playerDefinitions, const std::vector<std::string>& levels, const std::vector<Size>& backgrounds, ScreenMode screenMode, Int32 screenZoom)
	{
		appService.getConsole().printLine("\n=== Starting new game ===");
		appService.getConsole().printLine(Format("...Rounds: {0}") << maxRounds);
		TextureManager& textureManager = appService.getTextureManager();
		players.clear();
		skins.clear();

		for (const PlayerDefinition& playerDef : playerDefinitions)
		{
			appService.getConsole().printLine(Format("...Generating player for person: {0}") << playerDef.getPerson().getName());
			skins.push_back(std::make_unique<PlayerSkin>(D6_TEXTURE_MAN_PATH, playerDef.getColors(), textureManager));
			players.push_back(Player(playerDef.getPerson(), *skins.back(), playerDef.getSounds(), playerDef.getControls(), spriteList, messageQueue, waterSet));
		}

		this->levels = levels;
		this->backgrounds = backgrounds;
		this->screenMode = screenMode;
		this->screenZoom = screenZoom;

		playedRounds = 0;
		nextRound(false);
	}

	void Game::nextRound(bool sameLevel)
	{
		if (!sameLevel)
		{
			lastLevel = rand() % levels.size();
		}

		const std::string levelPath = levels[lastLevel];
		Console& console = appService.getConsole();
		console.printLine(Format(D6_L("\n===Loading level {0}===")) << levelPath);
		bool mirror = rand() % 2 == 0;
		world.loadLevel(levelPath, backgrounds[rand() % backgrounds.size()], mirror);
		world.prepareFaces();
		console.printLine(Format(D6_L("...Width   : {0}")) << world.getSizeX());
		console.printLine(Format(D6_L("...Height  : {0}")) << world.getSizeY());
		console.printLine(Format(D6_L("...Walls   : {0}")) << world.getWalls().getFaces().size());
		console.printLine(Format(D6_L("...Sprites : {0}")) << world.getSprites().getFaces().size());
		console.printLine(Format(D6_L("...Water   : {0}")) << world.getWater().getFaces().size());

		spriteList.clear();

		console.printLine(D6_L("...Preparing players"));
		preparePlayers();

		console.printLine(D6_L("...Level initialization"));
		WPN_LevelInit();
		EXPL_Clear();
		BONUS_Clear();
		FIRE_Find(world.getSprites());
		renderer.initScreen();
		winner = -1;
		playedRounds++;
		showYouAreHere = D6_YOU_ARE_HERE_DURATION;

		roundStartSound.play();
	}

	std::vector<const Player*> Game::getLadder() const
	{
		std::vector<const Player*> ladder;

		for (const Player& player : players)
		{
			ladder.push_back(&player);
		}

		std::sort(ladder.begin(), ladder.end(), [](const Player* pl1, const Player* pl2) {
			return pl2->getPerson().getTotalPoints() < pl1->getPerson().getTotalPoints();
		});
		return ladder;
	}

	Color Game::getGameOverOverlay() const
	{
		Uint8 overlay = Uint8(255.0f * gameOverWait / D6_GAME_OVER_WAIT);
		if (isOver())
		{
			overlay = std::min(overlay + 51, 255);
			return Color(overlay);
		}

		return Color(255, overlay, overlay);
	}

	World& Game::getWorld()
	{
		return world;
	}

	const World& Game::getWorld() const
	{
		return world;
	}
}<|endoftext|>