<commit_before>/**
 * Copyright (c) 2014 Ruben Nijveld, Aaron van Geffen
 * See LICENSE for license.
 */

#include "../config.h"
#include "sqlite.h"
#include <sqlite3.h>
#include <stdio.h>
#include <iostream>

namespace dazeus {
namespace db {

/**
 * Cleanup all prepared queries and close the connection.
 */
SQLiteDatabase::~SQLiteDatabase()
{
  if (conn_) {
    sqlite3_finalize(find_property);
    sqlite3_finalize(remove_property);
    sqlite3_finalize(update_property);
    sqlite3_finalize(properties);
    sqlite3_finalize(add_permission);
    sqlite3_finalize(remove_permission);
    sqlite3_finalize(has_permission);
    int res = sqlite3_close(conn_);
    assert(res == SQLITE_OK);
  }
}

void SQLiteDatabase::open()
{
  // Connect the lot!
  int fail = sqlite3_open_v2(dbc_.filename.c_str(), &conn_,
                             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
                             NULL);
  if (fail) {
    auto error = "Can't open database (code " + std::to_string(fail) + "): " +
                 sqlite3_errmsg(conn_);
    throw exception(error);
  }
  upgradeDB();
  bootstrapDB();
}

/**
 * @brief Try to create a prepared statement on the SQLite3 connection.
 */
void SQLiteDatabase::tryPrepare(const std::string &stmt,
                                sqlite3_stmt **target) const
{
  int result = sqlite3_prepare_v2(conn_, stmt.c_str(), stmt.length(), target,
                                  NULL);
  if (result != SQLITE_OK) {
    throw exception(
        std::string("Failed to prepare SQL statement, got an error: ") +
        sqlite3_errmsg(conn_));
  }
}

/**
 * @brief Try to bind a parameter to a prepared statement.
 */
void SQLiteDatabase::tryBind(sqlite3_stmt *target, int param,
                             const std::string &value) const
{
  int result = sqlite3_bind_text(target, param, value.c_str(), value.length(),
                                 SQLITE_TRANSIENT);
  if (result != SQLITE_OK) {
    throw exception("Failed to bind parameter " + std::to_string(param) +
                    " to query with error: " + sqlite3_errmsg(conn_) +
                    " (errorcode " + std::to_string(result) + ")");
  }
}

/**
 * @brief Prepare all SQL statements used by the database layer
 */
void SQLiteDatabase::bootstrapDB()
{
  tryPrepare(
      "SELECT value FROM dazeus_properties "
      "WHERE key = ?1 "
      "  AND network = ?2 AND receiver = ?3 AND sender = ?4",
      &find_property);

  tryPrepare(
      "DELETE FROM dazeus_properties "
      "WHERE key = ?1 "
      "  AND network = ?2 AND receiver = ?3 AND sender = ?3",
      &remove_property);

  tryPrepare(
      "INSERT OR REPLACE INTO dazeus_properties "
      "(key, value, network, receiver, sender) "
      "VALUES (?1, ?2, ?3, :receiver, :sender) ",
      &update_property);

  tryPrepare(
      "SELECT key FROM dazeus_properties "
      "WHERE key LIKE :key || '%' "
      "  AND network = :network AND receiver = :receiver AND sender = :sender",
      &properties);

  tryPrepare(
      "INSERT OR REPLACE INTO dazeus_permissions "
      "(permission, network, receiver, sender) "
      "VALUES (:permission, :network, :receiver, :sender) ",
      &add_permission);

  tryPrepare(
      "DELETE FROM dazeus_permissions "
      "WHERE permission = :permission "
      "  AND network = :network AND receiver = :receiver AND sender = :sender",
      &remove_permission);

  tryPrepare(
      "SELECT * FROM dazeus_permissions "
      "WHERE permission = :permission "
      "  AND network = :network AND receiver = :receiver AND sender = :sender",
      &has_permission);
}

/**
 * @brief Upgrade the database to the latest version.
 */
void SQLiteDatabase::upgradeDB()
{
  bool found = false;
  int errc = sqlite3_exec(conn_,
      "SELECT name FROM sqlite_master WHERE type = 'table' "
      "AND name = 'dazeus_properties'",
      [](void* found, int, char**, char**)
      {
        *(static_cast<bool*>(found)) = true;
        return 0;
      }, static_cast<void*>(&found), NULL);

  if (errc != SQLITE_OK) {
    throw exception(std::string("Failed to retrieve database version: ") +
        sqlite3_errmsg(conn_));
  }

  int db_version = 0;
  if (errc == SQLITE_OK) {
    if (found) {
      // table exists, query for latest version
      errc = sqlite3_exec(conn_,
          "SELECT value FROM dazeus_properties "
          "WHERE key = 'dazeus_version' LIMIT 1",
          [](void* dbver, int columns, char** values, char**)
          {
            if (columns > 0) {
              *(static_cast<int*>(dbver)) = std::stoi(values[0]);
            }
            return 0;
          }, static_cast<void*>(&db_version), NULL);
    }
  }

  const char *upgrades[] = {
    "CREATE TABLE dazeus_properties( "
      "key VARCHAR(255) NOT NULL, "
      "value TEXT NOT NULL, "
      "network VARCHAR(255) NOT NULL, "
      "receiver VARCHAR(255) NOT NULL, "
      "sender VARCHAR(255) NOT NULL, "
      "created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
      "updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
            "PRIMARY KEY(key, network, receiver, sender) "
    ")"
    ,
    "CREATE TABLE dazeus_permissions( "
      "permission VARCHAR(255) NOT NULL, "
      "network VARCHAR(255) NOT NULL, "
      "receiver VARCHAR(255) NOT NULL, "
      "sender VARCHAR(255) NOT NULL, "
      "created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
      "updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
            "PRIMARY KEY(permission, network, receiver, sender) "
        ")"
  };

  // run any outstanding updates
  static int target_version = std::end(upgrades) - std::begin(upgrades);
  if (db_version < target_version) {
    std::cout << "Will now upgrade database from version " << db_version
              << " to version " << target_version << "." << std::endl;
    for (int i = db_version; i < target_version; ++i) {
      int result = sqlite3_exec(conn_, upgrades[i], NULL, NULL, NULL);
      if (result != SQLITE_OK) {
        throw exception("Could not run upgrade " + std::to_string(i) +
                            ", got error: " + sqlite3_errmsg(conn_));
      }
    }
    std::cout << "Upgrade completed. Will now update dazeus_version to "
              << target_version << std::endl;
    errc = sqlite3_exec(conn_,
        ("INSERT OR REPLACE INTO dazeus_properties "
         "(key, value, network, receiver, sender) "
         "VALUES ('dazeus_version', '" + std::to_string(target_version) + "', "
         "        '', '', '') ").c_str(),
        NULL, NULL, NULL);
    if (errc != SQLITE_OK) {
      throw exception(
          "Could not set dazeus_version to latest version, got error: " +
          std::string(sqlite3_errmsg(conn_)));
    }
  }
}

std::string SQLiteDatabase::property(const std::string &variable,
                                     const std::string &networkScope,
                                     const std::string &receiverScope,
                                     const std::string &senderScope)
{
  tryBind(find_property, 1, variable);
  tryBind(find_property, 2, networkScope);
  tryBind(find_property, 3, receiverScope);
  tryBind(find_property, 4, senderScope);
  int errc = sqlite3_step(find_property);

  if (errc == SQLITE_ROW) {
    std::string value = reinterpret_cast<const char *>(sqlite3_column_text(find_property, 0));
    sqlite3_reset(find_property);
    return value;
  } else if (errc == SQLITE_DONE) {
    // TODO: define behavior when no result is found
    sqlite3_reset(find_property);
    return "";
  } else {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(find_property);
    throw exception(msg);
  }
}

void SQLiteDatabase::setProperty(const std::string &variable,
    const std::string &value, const std::string &networkScope,
    const std::string &receiverScope,
    const std::string &senderScope)
{
  tryBind(update_property, 1, variable);
  tryBind(update_property, 2, value);
  tryBind(update_property, 3, networkScope);
  tryBind(update_property, 4, receiverScope);
  tryBind(update_property, 5, senderScope);
  int errc = sqlite3_step(update_property);

  if (errc != SQLITE_OK && errc != SQLITE_DONE) {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(update_property);
    throw exception(msg);
  }

  sqlite3_reset(update_property);
}

std::vector<std::string> SQLiteDatabase::propertyKeys(const std::string &prefix,
      const std::string &networkScope,
      const std::string &receiverScope,
      const std::string &senderScope)
{
  // Return a vector of all the property keys matching the criteria.
  std::vector<std::string> keys = std::vector<std::string>();

  tryBind(properties, 1, prefix);
  tryBind(properties, 2, networkScope);
  tryBind(properties, 3, receiverScope);
  tryBind(properties, 4, senderScope);

  while (true) {
    int errc = sqlite3_step(properties);

    if (errc == SQLITE_ROW) {
      std::string value = reinterpret_cast<const char *>(sqlite3_column_text(properties, 0));
      keys.push_back(value);
    } else if (errc == SQLITE_DONE) {
      sqlite3_reset(properties);
      break;
    } else {
      std::string msg = "Got an error while executing an SQL query (code " +
                        std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
      sqlite3_reset(properties);
      throw exception(msg);
    }
  }

  return keys;
}

bool SQLiteDatabase::hasPermission(const std::string &perm_name,
      const std::string &network, const std::string &channel,
      const std::string &sender, bool defaultPermission) const
{
  tryBind(has_permission, 1, perm_name);
  tryBind(has_permission, 2, network);
  tryBind(has_permission, 3, channel);
  tryBind(has_permission, 4, sender);

  int errc = sqlite3_step(has_permission);

  if (errc == SQLITE_ROW) {
    sqlite3_reset(has_permission);
    return true;
  } else if (errc == SQLITE_DONE) {
    sqlite3_reset(has_permission);
    return defaultPermission;
  } else {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(has_permission);
    throw exception(msg);
  }
}

void SQLiteDatabase::unsetPermission(const std::string &perm_name,
      const std::string &network, const std::string &receiver,
      const std::string &sender)
{
  tryBind(remove_permission, 1, perm_name);
  tryBind(remove_permission, 2, network);
  tryBind(remove_permission, 3, receiver);
  tryBind(remove_permission, 4, sender);
  int errc = sqlite3_step(remove_permission);

  if (errc != SQLITE_OK && errc != SQLITE_DONE) {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(remove_permission);
    throw exception(msg);
  }

  sqlite3_reset(remove_permission);
}

void SQLiteDatabase::setPermission(bool permission, const std::string &perm_name,
      const std::string &network, const std::string &receiver,
      const std::string &sender)
{
  tryBind(add_permission, 1, perm_name);
  tryBind(add_permission, 2, network);
  tryBind(add_permission, 3, receiver);
  tryBind(add_permission, 4, sender);
  int errc = sqlite3_step(add_permission);

  if (errc != SQLITE_OK && errc != SQLITE_DONE) {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(add_permission);
    throw exception(msg);
  }

  sqlite3_reset(add_permission);
}

}  // namespace db
}  // namespace dazeus
<commit_msg>Sqlite scoping updated<commit_after>/**
 * Copyright (c) 2014 Ruben Nijveld, Aaron van Geffen
 * See LICENSE for license.
 */

#include "../config.h"
#include "sqlite.h"
#include <sqlite3.h>
#include <stdio.h>
#include <iostream>

namespace dazeus {
namespace db {

/**
 * Cleanup all prepared queries and close the connection.
 */
SQLiteDatabase::~SQLiteDatabase()
{
  if (conn_) {
    sqlite3_finalize(find_property);
    sqlite3_finalize(remove_property);
    sqlite3_finalize(update_property);
    sqlite3_finalize(properties);
    sqlite3_finalize(add_permission);
    sqlite3_finalize(remove_permission);
    sqlite3_finalize(has_permission);
    int res = sqlite3_close(conn_);
    assert(res == SQLITE_OK);
  }
}

void SQLiteDatabase::open()
{
  // Connect the lot!
  int fail = sqlite3_open_v2(dbc_.filename.c_str(), &conn_,
                             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
                             NULL);
  if (fail) {
    auto error = "Can't open database (code " + std::to_string(fail) + "): " +
                 sqlite3_errmsg(conn_);
    throw exception(error);
  }
  upgradeDB();
  bootstrapDB();
}

/**
 * @brief Try to create a prepared statement on the SQLite3 connection.
 */
void SQLiteDatabase::tryPrepare(const std::string &stmt,
                                sqlite3_stmt **target) const
{
  int result = sqlite3_prepare_v2(conn_, stmt.c_str(), stmt.length(), target,
                                  NULL);
  if (result != SQLITE_OK) {
    throw exception(
        std::string("Failed to prepare SQL statement, got an error: ") +
        sqlite3_errmsg(conn_));
  }
}

/**
 * @brief Try to bind a parameter to a prepared statement.
 */
void SQLiteDatabase::tryBind(sqlite3_stmt *target, int param,
                             const std::string &value) const
{
  int result = sqlite3_bind_text(target, param, value.c_str(), value.length(),
                                 SQLITE_TRANSIENT);
  if (result != SQLITE_OK) {
    throw exception("Failed to bind parameter " + std::to_string(param) +
                    " to query with error: " + sqlite3_errmsg(conn_) +
                    " (errorcode " + std::to_string(result) + ")");
  }
}

/**
 * @brief Prepare all SQL statements used by the database layer
 */
void SQLiteDatabase::bootstrapDB()
{
  tryPrepare(
      "SELECT value FROM dazeus_properties "
      "WHERE key = ?1 "
      "  AND (network = ?2 OR network = '') "
      "  AND (receiver = ?3 OR receiver = '') "
      "  AND (sender = ?4 OR sender = '') "
      "ORDER BY network DESC, receiver DESC, sender DESC "
      "LIMIT 1",
      &find_property);

  tryPrepare(
      "DELETE FROM dazeus_properties "
      "WHERE key = ?1 "
      "  AND network = ?3 AND receiver = ?4 AND sender = ?5",
      &remove_property);

  tryPrepare(
      "INSERT OR REPLACE INTO dazeus_properties "
      "(key, value, network, receiver, sender) "
      "VALUES (?1, ?2, ?3, ?4, ?5) ",
      &update_property);

  tryPrepare(
      "SELECT key FROM dazeus_properties "
      "WHERE key LIKE ?1 || '%' "
      "  AND network = ?2 AND receiver = ?3 AND sender = ?4",
      &properties);

  tryPrepare(
      "INSERT OR REPLACE INTO dazeus_permissions "
      "(permission, network, receiver, sender) "
      "VALUES (?1, ?2, ?3, ?4) ",
      &add_permission);

  tryPrepare(
      "DELETE FROM dazeus_permissions "
      "WHERE permission = ?1 "
      "  AND network = ?2 AND receiver = ?3 AND sender = ?4",
      &remove_permission);

  tryPrepare(
      "SELECT * FROM dazeus_permissions "
      "WHERE permission = ?1 "
      "  AND network = ?2 AND receiver = ?3 AND sender = ?4",
      &has_permission);
}

/**
 * @brief Upgrade the database to the latest version.
 */
void SQLiteDatabase::upgradeDB()
{
  bool found = false;
  int errc = sqlite3_exec(conn_,
      "SELECT name FROM sqlite_master WHERE type = 'table' "
      "AND name = 'dazeus_properties'",
      [](void* found, int, char**, char**)
      {
        *(static_cast<bool*>(found)) = true;
        return 0;
      }, static_cast<void*>(&found), NULL);

  if (errc != SQLITE_OK) {
    throw exception(std::string("Failed to retrieve database version: ") +
        sqlite3_errmsg(conn_));
  }

  int db_version = 0;
  if (errc == SQLITE_OK) {
    if (found) {
      // table exists, query for latest version
      errc = sqlite3_exec(conn_,
          "SELECT value FROM dazeus_properties "
          "WHERE key = 'dazeus_version' LIMIT 1",
          [](void* dbver, int columns, char** values, char**)
          {
            if (columns > 0) {
              *(static_cast<int*>(dbver)) = std::stoi(values[0]);
            }
            return 0;
          }, static_cast<void*>(&db_version), NULL);
    }
  }

  const char *upgrades[] = {
    "CREATE TABLE dazeus_properties( "
      "key VARCHAR(255) NOT NULL, "
      "value TEXT NOT NULL, "
      "network VARCHAR(255) NOT NULL, "
      "receiver VARCHAR(255) NOT NULL, "
      "sender VARCHAR(255) NOT NULL, "
      "created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
      "updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
            "PRIMARY KEY(key, network, receiver, sender) "
    ")"
    ,
    "CREATE TABLE dazeus_permissions( "
      "permission VARCHAR(255) NOT NULL, "
      "network VARCHAR(255) NOT NULL, "
      "receiver VARCHAR(255) NOT NULL, "
      "sender VARCHAR(255) NOT NULL, "
      "created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
      "updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
            "PRIMARY KEY(permission, network, receiver, sender) "
        ")"
  };

  // run any outstanding updates
  static int target_version = std::end(upgrades) - std::begin(upgrades);
  if (db_version < target_version) {
    std::cout << "Will now upgrade database from version " << db_version
              << " to version " << target_version << "." << std::endl;
    for (int i = db_version; i < target_version; ++i) {
      int result = sqlite3_exec(conn_, upgrades[i], NULL, NULL, NULL);
      if (result != SQLITE_OK) {
        throw exception("Could not run upgrade " + std::to_string(i) +
                            ", got error: " + sqlite3_errmsg(conn_));
      }
    }
    std::cout << "Upgrade completed. Will now update dazeus_version to "
              << target_version << std::endl;
    errc = sqlite3_exec(conn_,
        ("INSERT OR REPLACE INTO dazeus_properties "
         "(key, value, network, receiver, sender) "
         "VALUES ('dazeus_version', '" + std::to_string(target_version) + "', "
         "        '', '', '') ").c_str(),
        NULL, NULL, NULL);
    if (errc != SQLITE_OK) {
      throw exception(
          "Could not set dazeus_version to latest version, got error: " +
          std::string(sqlite3_errmsg(conn_)));
    }
  }
}

std::string SQLiteDatabase::property(const std::string &variable,
                                     const std::string &networkScope,
                                     const std::string &receiverScope,
                                     const std::string &senderScope)
{
  tryBind(find_property, 1, variable);
  tryBind(find_property, 2, networkScope);
  tryBind(find_property, 3, receiverScope);
  tryBind(find_property, 4, senderScope);
  int errc = sqlite3_step(find_property);

  if (errc == SQLITE_ROW) {
    std::string value = reinterpret_cast<const char *>(sqlite3_column_text(find_property, 0));
    sqlite3_reset(find_property);
    return value;
  } else if (errc == SQLITE_DONE) {
    // TODO: define behavior when no result is found
    sqlite3_reset(find_property);
    return "";
  } else {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(find_property);
    throw exception(msg);
  }
}

void SQLiteDatabase::setProperty(const std::string &variable,
    const std::string &value, const std::string &networkScope,
    const std::string &receiverScope,
    const std::string &senderScope)
{
  sqlite3_stmt *stmt = value == "" ? remove_property : update_property;

  tryBind(stmt, 1, variable);
  tryBind(stmt, 2, value);
  tryBind(stmt, 3, networkScope);
  tryBind(stmt, 4, receiverScope);
  tryBind(stmt, 5, senderScope);
  int errc = sqlite3_step(stmt);

  if (errc != SQLITE_OK && errc != SQLITE_DONE) {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(stmt);
    throw exception(msg);
  }

  sqlite3_reset(stmt);
}

std::vector<std::string> SQLiteDatabase::propertyKeys(const std::string &prefix,
      const std::string &networkScope,
      const std::string &receiverScope,
      const std::string &senderScope)
{
  // Return a vector of all the property keys matching the criteria.
  std::vector<std::string> keys = std::vector<std::string>();

  tryBind(properties, 1, prefix);
  tryBind(properties, 2, networkScope);
  tryBind(properties, 3, receiverScope);
  tryBind(properties, 4, senderScope);

  while (true) {
    int errc = sqlite3_step(properties);

    if (errc == SQLITE_ROW) {
      std::string value = reinterpret_cast<const char *>(sqlite3_column_text(properties, 0));
      keys.push_back(value);
    } else if (errc == SQLITE_DONE) {
      sqlite3_reset(properties);
      break;
    } else {
      std::string msg = "Got an error while executing an SQL query (code " +
                        std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
      sqlite3_reset(properties);
      throw exception(msg);
    }
  }

  return keys;
}

bool SQLiteDatabase::hasPermission(const std::string &perm_name,
      const std::string &network, const std::string &channel,
      const std::string &sender, bool defaultPermission) const
{
  tryBind(has_permission, 1, perm_name);
  tryBind(has_permission, 2, network);
  tryBind(has_permission, 3, channel);
  tryBind(has_permission, 4, sender);

  int errc = sqlite3_step(has_permission);

  if (errc == SQLITE_ROW) {
    sqlite3_reset(has_permission);
    return true;
  } else if (errc == SQLITE_DONE) {
    sqlite3_reset(has_permission);
    return defaultPermission;
  } else {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(has_permission);
    throw exception(msg);
  }
}

void SQLiteDatabase::unsetPermission(const std::string &perm_name,
      const std::string &network, const std::string &receiver,
      const std::string &sender)
{
  tryBind(remove_permission, 1, perm_name);
  tryBind(remove_permission, 2, network);
  tryBind(remove_permission, 3, receiver);
  tryBind(remove_permission, 4, sender);
  int errc = sqlite3_step(remove_permission);

  if (errc != SQLITE_OK && errc != SQLITE_DONE) {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(remove_permission);
    throw exception(msg);
  }

  sqlite3_reset(remove_permission);
}

void SQLiteDatabase::setPermission(bool permission, const std::string &perm_name,
      const std::string &network, const std::string &receiver,
      const std::string &sender)
{
  tryBind(add_permission, 1, perm_name);
  tryBind(add_permission, 2, network);
  tryBind(add_permission, 3, receiver);
  tryBind(add_permission, 4, sender);
  int errc = sqlite3_step(add_permission);

  if (errc != SQLITE_OK && errc != SQLITE_DONE) {
    std::string msg = "Got an error while executing an SQL query (code " +
                      std::to_string(errc) + "): " + sqlite3_errmsg(conn_);
    sqlite3_reset(add_permission);
    throw exception(msg);
  }

  sqlite3_reset(add_permission);
}

}  // namespace db
}  // namespace dazeus
<|endoftext|>


<commit_before>#include "TChain.h"
#include "TTree.h"
#include "TH1F.h"
#include "TF1.h"
#include "TH2F.h"
#include "TCanvas.h"
#include "TObjArray.h"
#include "TTreeStream.h"

#define NPMTs 24

int MakeTrendT0( char *infile, int run) {

  char *outfile = "trending.root";

  if(!infile) return -1;
  if(!outfile) return -1;

  TFile *f = TFile::Open(infile,"read");
  if (!f) {
    printf("File %s not available\n", infile);
    return -1;
  }

  //            LOAD HISTOGRAMS FROM QAresults.root
  TObjArray   *fTzeroObject = (TObjArray*) f->Get("T0_Performance/QAT0chists");

  TH1F* fTzeroORAplusORC =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fTzeroORAplusORC"))->Clone("A");
  TH1F* fResolution      =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fResolution"))->Clone("B");
  TH1F* fTzeroORA        =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fTzeroORA"))->Clone("C");
  TH1F* fTzeroORC        =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fTzeroORC"))->Clone("D");

  TH2F *fTimeVSAmplitude[NPMTs];//counting PMTs from 0
  TH1D *fAmplitude[NPMTs];
  TH1D *fTime[NPMTs];
  //-----> add new histogram here

  // sigma fit of resolution, mean T0A, T0C and T0C&A,  mean amplitude for each PMT
  double resolutionSigma = -9999; //dummy init
  double tzeroOrAPlusOrC = -9999; //dummy init
  double tzeroOrA        = -9999; //dummy init
  double tzeroOrC        = -9999; //dummy init
  double meanAmplitude[NPMTs]; //dummy init
  double meanTime[NPMTs]; //dummy init
  double timeDelayOCDB[NPMTs]; //dummy init

  //.................................................................................

  for(int ipmt=1; ipmt<=NPMTs; ipmt++){ //loop over all PMTs
    fTimeVSAmplitude[ipmt-1] =(TH2F*) ((TH2F*) fTzeroObject->FindObject(Form("fTimeVSAmplitude%d",ipmt)))->Clone(Form("E%d",ipmt));
    int nbinsX = fTimeVSAmplitude[ipmt-1]->GetNbinsX();
    int nbinsY = fTimeVSAmplitude[ipmt-1]->GetNbinsY();
    //Amplitude
    fAmplitude[ipmt-1] = (TH1D*) fTimeVSAmplitude[ipmt-1]->ProjectionX(Form("fAmplitude%d",ipmt), 1, nbinsY);
    meanAmplitude[ipmt-1] = -9999; //dummy init
    if(fAmplitude[ipmt-1]->GetEntries()>0){
      meanAmplitude[ipmt-1] = fAmplitude[ipmt-1]->GetMean();
    }

    //Time
    fTime[ipmt-1] = (TH1D*) fTimeVSAmplitude[ipmt-1]->ProjectionY(Form("fTime%d",ipmt), 1, nbinsX);
    meanTime[ipmt-1] = -9999; //dummy init
    if(fTime[ipmt-1]->GetEntries()>0){
      if(fTime[ipmt-1]->GetEntries()>20){
        meanTime[ipmt-1] = GetParameterGaus((TH1F*) fTime[ipmt-1], 1); // Mean Time
      }else if(fTime[ipmt-1]->GetEntries()>0){
        meanTime[ipmt-1] = fTime[ipmt-1]->GetMean();
      }
    }
  }

  if(fResolution->GetEntries()>20){
    resolutionSigma = GetParameterGaus(fResolution, 2); //gaussian sigma
  }else if(fResolution->GetEntries()>0){
    resolutionSigma = fResolution->GetRMS(); //gaussian sigma
  }

  if(fTzeroORAplusORC->GetEntries()>20){
    tzeroOrAPlusOrC = GetParameterGaus(fTzeroORAplusORC, 1); //gaussian mean
  }else if(fTzeroORAplusORC->GetEntries()>0){
    tzeroOrAPlusOrC = fTzeroORAplusORC->GetMean();
  }

  if(fTzeroORA->GetEntries()>20){
    tzeroOrA = GetParameterGaus(fTzeroORA, 1); //gaussian mean
  }else if(fTzeroORA->GetEntries()>0){
    tzeroOrA = fTzeroORA->GetMean();
  }

  if(fTzeroORC->GetEntries()>20){
    tzeroOrC = GetParameterGaus(fTzeroORC, 1); //gaussian mean
  }else if(fTzeroORC->GetEntries()>0){
    tzeroOrC = fTzeroORC->GetMean(); //gaussian mean
  }

  //-----> analyze the new histogram here and set mean/sigma

  f->Close();

  //-------------------- READ OCDB TIME DELAYS ---------------------------
  // Arguments:
  AliCDBManager* man = AliCDBManager::Instance();
  man->SetDefaultStorage("raw://");
  man->SetRun(run);
  AliCDBEntry *entry = AliCDBManager::Instance()->Get("T0/Calib/TimeDelay");
  AliT0CalibTimeEq *clb = (AliT0CalibTimeEq*)entry->GetObject();
  for (Int_t i=0; i<NPMTs; i++){
    timeDelayOCDB[i] = 0;
    if(clb)
      timeDelayOCDB[i] = clb->GetCFDvalue(i,0);
  }

  //--------------- write walues to the output ------------
  TTreeSRedirector* pcstream = NULL;
  pcstream = new TTreeSRedirector(outfile);
  if (!pcstream) return;

  TFile *x =  pcstream->GetFile();
  x->cd();

  TObjString runType;
  Int_t startTimeGRP=0;
  Int_t stopTimeGRP=0;
  Int_t time=0;
  Int_t duration=0;

  time     = (startTimeGRP+stopTimeGRP)/2;
  duration = (stopTimeGRP-startTimeGRP);

  (*pcstream)<<"t0QA"<<
                  "run="<<run;//<<
                 //"time="<<time<<
            // "startTimeGRP="<<startTimeGRP<<
            //  "stopTimeGRP="<<stopTimeGRP<<
              ///    "duration="<<duration<<
               //   "runType.="<<&runType;

 (*pcstream)<<"t0QA"<<
             "resolution="<< resolutionSigma<<
             "tzeroOrAPlusOrC="<< tzeroOrAPlusOrC<<
             "tzeroOrA="<< tzeroOrA<<
             "tzeroOrC="<< tzeroOrC<<
             "amplPMT1="<<meanAmplitude[0]<<
             "amplPMT2="<<meanAmplitude[1]<<
             "amplPMT3="<<meanAmplitude[2]<<
             "amplPMT4="<<meanAmplitude[3]<<
             "amplPMT5="<<meanAmplitude[4]<<
             "amplPMT6="<<meanAmplitude[5]<<
             "amplPMT7="<<meanAmplitude[6]<<
             "amplPMT8="<<meanAmplitude[7]<<
             "amplPMT9="<<meanAmplitude[8]<<
             "amplPMT10="<<meanAmplitude[9]<<
             "amplPMT11="<<meanAmplitude[10]<<
             "amplPMT12="<<meanAmplitude[11]<<
             "amplPMT13="<<meanAmplitude[12]<<
             "amplPMT14="<<meanAmplitude[13]<<
             "amplPMT15="<<meanAmplitude[14]<<
             "amplPMT16="<<meanAmplitude[15]<<
             "amplPMT17="<<meanAmplitude[16]<<
             "amplPMT18="<<meanAmplitude[17]<<
             "amplPMT19="<<meanAmplitude[18]<<
             "amplPMT20="<<meanAmplitude[19]<<
             "amplPMT21="<<meanAmplitude[20]<<
             "amplPMT22="<<meanAmplitude[21]<<
             "amplPMT23="<<meanAmplitude[22]<<
             "amplPMT24="<<meanAmplitude[23]<<
             "timePMT1="<<meanTime[0]<<
             "timePMT2="<<meanTime[1]<<
             "timePMT3="<<meanTime[2]<<
             "timePMT4="<<meanTime[3]<<
             "timePMT5="<<meanTime[4]<<
             "timePMT6="<<meanTime[5]<<
             "timePMT7="<<meanTime[6]<<
             "timePMT8="<<meanTime[7]<<
             "timePMT9="<<meanTime[8]<<
             "timePMT10="<<meanTime[9]<<
             "timePMT11="<<meanTime[10]<<
             "timePMT12="<<meanTime[11]<<
             "timePMT13="<<meanTime[12]<<
             "timePMT14="<<meanTime[13]<<
             "timePMT15="<<meanTime[14]<<
             "timePMT16="<<meanTime[15]<<
             "timePMT17="<<meanTime[16]<<
             "timePMT18="<<meanTime[17]<<
             "timePMT19="<<meanTime[18]<<
             "timePMT20="<<meanTime[19]<<
             "timePMT21="<<meanTime[20]<<
             "timePMT22="<<meanTime[21]<<
             "timePMT23="<<meanTime[22]<<
             "timePMT24="<<meanTime[23];
(*pcstream)<<"t0QA"<<
             "timeDelayPMT1="<<timeDelayOCDB[0]<<
             "timeDelayPMT2="<<timeDelayOCDB[1]<<
             "timeDelayPMT3="<<timeDelayOCDB[2]<<
             "timeDelayPMT4="<<timeDelayOCDB[3]<<
             "timeDelayPMT5="<<timeDelayOCDB[4]<<
             "timeDelayPMT6="<<timeDelayOCDB[5]<<
             "timeDelayPMT7="<<timeDelayOCDB[6]<<
             "timeDelayPMT8="<<timeDelayOCDB[7]<<
             "timeDelayPMT9="<<timeDelayOCDB[8]<<
             "timeDelayPMT10="<<timeDelayOCDB[9]<<
             "timeDelayPMT11="<<timeDelayOCDB[10]<<
             "timeDelayPMT12="<<timeDelayOCDB[11]<<
             "timeDelayPMT13="<<timeDelayOCDB[12]<<
             "timeDelayPMT14="<<timeDelayOCDB[13]<<
             "timeDelayPMT15="<<timeDelayOCDB[14]<<
             "timeDelayPMT16="<<timeDelayOCDB[15]<<
             "timeDelayPMT17="<<timeDelayOCDB[16]<<
             "timeDelayPMT18="<<timeDelayOCDB[17]<<
             "timeDelayPMT19="<<timeDelayOCDB[18]<<
             "timeDelayPMT20="<<timeDelayOCDB[19]<<
             "timeDelayPMT21="<<timeDelayOCDB[20]<<
             "timeDelayPMT22="<<timeDelayOCDB[21]<<
             "timeDelayPMT23="<<timeDelayOCDB[22]<<
             "timeDelayPMT24="<<timeDelayOCDB[23];

            //-----> add the mean/sigma of the new histogram here

 (*pcstream)<<"t0QA"<<"\n";

  pcstream->Close();

  delete pcstream;

  return;

}

//_____________________________________________________________________________
double GetParameterGaus(TH1F *histo, int whichParameter){

   int    maxBin   =  histo->GetMaximumBin();
   double max      =  (histo->GetBinContent(maxBin-1) + histo->GetBinContent(maxBin) + histo->GetBinContent(maxBin+1))/3;
   double mean     =  histo->GetBinCenter(maxBin); //mean
   double lowfwhm  =  histo->GetBinCenter(histo->FindFirstBinAbove(max/2));
   double highfwhm =  histo->GetBinCenter(histo->FindLastBinAbove(max/2));
   double sigma    = (highfwhm - lowfwhm)/2.35482; //estimate fwhm  FWHM = 2.35482*sigma

   TF1 *gaussfit   = new TF1("gaussfit","gaus", mean - 4*sigma, mean + 4*sigma); // fit in +- 4 sigma window
   gaussfit->SetParameters(max, mean, sigma);

   if(whichParameter==2)
     histo->Fit(gaussfit,"RQNI");
   else
     histo->Fit(gaussfit,"RQN");

   double parValue = gaussfit->GetParameter(whichParameter);

   delete gaussfit;

   return parValue;
}

<commit_msg>make the script working<commit_after>#define NPMTs 24

int MakeTrendT0( char *infile, int run) {

  gSystem->Load("libANALYSIS");
  gSystem->Load("libANALYSISalice");
  gSystem->Load("libCORRFW");
  gSystem->Load("libTENDER");
  gSystem->Load("libPWGPP.so");

  char *outfile = "trending.root";

  if(!infile) return -1;
  if(!outfile) return -1;

  TFile *f = TFile::Open(infile,"read");
  if (!f) {
    printf("File %s not available\n", infile);
    return -1;
  }

  //            LOAD HISTOGRAMS FROM QAresults.root
  TObjArray   *fTzeroObject = (TObjArray*) f->Get("T0_Performance/QAT0chists");

  TH1F* fTzeroORAplusORC =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fTzeroORAplusORC"))->Clone("A");
  TH1F* fResolution      =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fResolution"))->Clone("B");
  TH1F* fTzeroORA        =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fTzeroORA"))->Clone("C");
  TH1F* fTzeroORC        =(TH1F*) ((TH1F*) fTzeroObject->FindObject("fTzeroORC"))->Clone("D");

  TH2F *fTimeVSAmplitude[NPMTs];//counting PMTs from 0
  TH1D *fAmplitude[NPMTs];
  TH1D *fTime[NPMTs];
  //-----> add new histogram here

  // sigma fit of resolution, mean T0A, T0C and T0C&A,  mean amplitude for each PMT
  double resolutionSigma = -9999; //dummy init
  double tzeroOrAPlusOrC = -9999; //dummy init
  double tzeroOrA        = -9999; //dummy init
  double tzeroOrC        = -9999; //dummy init
  double meanAmplitude[NPMTs]; //dummy init
  double meanTime[NPMTs]; //dummy init
  double timeDelayOCDB[NPMTs]; //dummy init

  //.................................................................................

  for(int ipmt=1; ipmt<=NPMTs; ipmt++){ //loop over all PMTs
    fTimeVSAmplitude[ipmt-1] =(TH2F*) ((TH2F*) fTzeroObject->FindObject(Form("fTimeVSAmplitude%d",ipmt)))->Clone(Form("E%d",ipmt));
    int nbinsX = fTimeVSAmplitude[ipmt-1]->GetNbinsX();
    int nbinsY = fTimeVSAmplitude[ipmt-1]->GetNbinsY();
    //Amplitude
    fAmplitude[ipmt-1] = (TH1D*) fTimeVSAmplitude[ipmt-1]->ProjectionX(Form("fAmplitude%d",ipmt), 1, nbinsY);
    meanAmplitude[ipmt-1] = -9999; //dummy init
    if(fAmplitude[ipmt-1]->GetEntries()>0){
      meanAmplitude[ipmt-1] = fAmplitude[ipmt-1]->GetMean();
    }

    //Time
    fTime[ipmt-1] = (TH1D*) fTimeVSAmplitude[ipmt-1]->ProjectionY(Form("fTime%d",ipmt), 1, nbinsX);
    meanTime[ipmt-1] = -9999; //dummy init
    if(fTime[ipmt-1]->GetEntries()>0){
      if(fTime[ipmt-1]->GetEntries()>20){
        meanTime[ipmt-1] = GetParameterGaus((TH1F*) fTime[ipmt-1], 1); // Mean Time
      }else if(fTime[ipmt-1]->GetEntries()>0){
        meanTime[ipmt-1] = fTime[ipmt-1]->GetMean();
      }
    }
  }

  if(fResolution->GetEntries()>20){
    resolutionSigma = GetParameterGaus(fResolution, 2); //gaussian sigma
  }else if(fResolution->GetEntries()>0){
    resolutionSigma = fResolution->GetRMS(); //gaussian sigma
  }

  if(fTzeroORAplusORC->GetEntries()>20){
    tzeroOrAPlusOrC = GetParameterGaus(fTzeroORAplusORC, 1); //gaussian mean
  }else if(fTzeroORAplusORC->GetEntries()>0){
    tzeroOrAPlusOrC = fTzeroORAplusORC->GetMean();
  }

  if(fTzeroORA->GetEntries()>20){
    tzeroOrA = GetParameterGaus(fTzeroORA, 1); //gaussian mean
  }else if(fTzeroORA->GetEntries()>0){
    tzeroOrA = fTzeroORA->GetMean();
  }

  if(fTzeroORC->GetEntries()>20){
    tzeroOrC = GetParameterGaus(fTzeroORC, 1); //gaussian mean
  }else if(fTzeroORC->GetEntries()>0){
    tzeroOrC = fTzeroORC->GetMean(); //gaussian mean
  }

  //-----> analyze the new histogram here and set mean/sigma

  f->Close();

  //-------------------- READ OCDB TIME DELAYS ---------------------------
  // Arguments:
  AliCDBManager* man = AliCDBManager::Instance();
  man->SetDefaultStorage("raw://");
  man->SetRun(run);
  AliCDBEntry *entry = AliCDBManager::Instance()->Get("T0/Calib/TimeDelay");
  AliT0CalibTimeEq *clb = (AliT0CalibTimeEq*)entry->GetObject();
  for (Int_t i=0; i<NPMTs; i++){
    timeDelayOCDB[i] = 0;
    if(clb)
      timeDelayOCDB[i] = clb->GetCFDvalue(i,0);
  }

  //--------------- write walues to the output ------------
  TTreeSRedirector* pcstream = NULL;
  pcstream = new TTreeSRedirector(outfile);
  if (!pcstream) return;

  TFile *x =  pcstream->GetFile();
  x->cd();

  TObjString runType;
  Int_t startTimeGRP=0;
  Int_t stopTimeGRP=0;
  Int_t time=0;
  Int_t duration=0;

  time     = (startTimeGRP+stopTimeGRP)/2;
  duration = (stopTimeGRP-startTimeGRP);

  (*pcstream)<<"t0QA"<<
                  "run="<<run;//<<
                 //"time="<<time<<
            // "startTimeGRP="<<startTimeGRP<<
            //  "stopTimeGRP="<<stopTimeGRP<<
              ///    "duration="<<duration<<
               //   "runType.="<<&runType;

 (*pcstream)<<"t0QA"<<
             "resolution="<< resolutionSigma<<
             "tzeroOrAPlusOrC="<< tzeroOrAPlusOrC<<
             "tzeroOrA="<< tzeroOrA<<
             "tzeroOrC="<< tzeroOrC<<
             "amplPMT1="<<meanAmplitude[0]<<
             "amplPMT2="<<meanAmplitude[1]<<
             "amplPMT3="<<meanAmplitude[2]<<
             "amplPMT4="<<meanAmplitude[3]<<
             "amplPMT5="<<meanAmplitude[4]<<
             "amplPMT6="<<meanAmplitude[5]<<
             "amplPMT7="<<meanAmplitude[6]<<
             "amplPMT8="<<meanAmplitude[7]<<
             "amplPMT9="<<meanAmplitude[8]<<
             "amplPMT10="<<meanAmplitude[9]<<
             "amplPMT11="<<meanAmplitude[10]<<
             "amplPMT12="<<meanAmplitude[11]<<
             "amplPMT13="<<meanAmplitude[12]<<
             "amplPMT14="<<meanAmplitude[13]<<
             "amplPMT15="<<meanAmplitude[14]<<
             "amplPMT16="<<meanAmplitude[15]<<
             "amplPMT17="<<meanAmplitude[16]<<
             "amplPMT18="<<meanAmplitude[17]<<
             "amplPMT19="<<meanAmplitude[18]<<
             "amplPMT20="<<meanAmplitude[19]<<
             "amplPMT21="<<meanAmplitude[20]<<
             "amplPMT22="<<meanAmplitude[21]<<
             "amplPMT23="<<meanAmplitude[22]<<
             "amplPMT24="<<meanAmplitude[23]<<
             "timePMT1="<<meanTime[0]<<
             "timePMT2="<<meanTime[1]<<
             "timePMT3="<<meanTime[2]<<
             "timePMT4="<<meanTime[3]<<
             "timePMT5="<<meanTime[4]<<
             "timePMT6="<<meanTime[5]<<
             "timePMT7="<<meanTime[6]<<
             "timePMT8="<<meanTime[7]<<
             "timePMT9="<<meanTime[8]<<
             "timePMT10="<<meanTime[9]<<
             "timePMT11="<<meanTime[10]<<
             "timePMT12="<<meanTime[11]<<
             "timePMT13="<<meanTime[12]<<
             "timePMT14="<<meanTime[13]<<
             "timePMT15="<<meanTime[14]<<
             "timePMT16="<<meanTime[15]<<
             "timePMT17="<<meanTime[16]<<
             "timePMT18="<<meanTime[17]<<
             "timePMT19="<<meanTime[18]<<
             "timePMT20="<<meanTime[19]<<
             "timePMT21="<<meanTime[20]<<
             "timePMT22="<<meanTime[21]<<
             "timePMT23="<<meanTime[22]<<
             "timePMT24="<<meanTime[23];
(*pcstream)<<"t0QA"<<
             "timeDelayPMT1="<<timeDelayOCDB[0]<<
             "timeDelayPMT2="<<timeDelayOCDB[1]<<
             "timeDelayPMT3="<<timeDelayOCDB[2]<<
             "timeDelayPMT4="<<timeDelayOCDB[3]<<
             "timeDelayPMT5="<<timeDelayOCDB[4]<<
             "timeDelayPMT6="<<timeDelayOCDB[5]<<
             "timeDelayPMT7="<<timeDelayOCDB[6]<<
             "timeDelayPMT8="<<timeDelayOCDB[7]<<
             "timeDelayPMT9="<<timeDelayOCDB[8]<<
             "timeDelayPMT10="<<timeDelayOCDB[9]<<
             "timeDelayPMT11="<<timeDelayOCDB[10]<<
             "timeDelayPMT12="<<timeDelayOCDB[11]<<
             "timeDelayPMT13="<<timeDelayOCDB[12]<<
             "timeDelayPMT14="<<timeDelayOCDB[13]<<
             "timeDelayPMT15="<<timeDelayOCDB[14]<<
             "timeDelayPMT16="<<timeDelayOCDB[15]<<
             "timeDelayPMT17="<<timeDelayOCDB[16]<<
             "timeDelayPMT18="<<timeDelayOCDB[17]<<
             "timeDelayPMT19="<<timeDelayOCDB[18]<<
             "timeDelayPMT20="<<timeDelayOCDB[19]<<
             "timeDelayPMT21="<<timeDelayOCDB[20]<<
             "timeDelayPMT22="<<timeDelayOCDB[21]<<
             "timeDelayPMT23="<<timeDelayOCDB[22]<<
             "timeDelayPMT24="<<timeDelayOCDB[23];

            //-----> add the mean/sigma of the new histogram here

 (*pcstream)<<"t0QA"<<"\n";

  pcstream->Close();

  delete pcstream;

  return;

}

//_____________________________________________________________________________
double GetParameterGaus(TH1F *histo, int whichParameter){

   int    maxBin   =  histo->GetMaximumBin();
   double max      =  (histo->GetBinContent(maxBin-1) + histo->GetBinContent(maxBin) + histo->GetBinContent(maxBin+1))/3;
   double mean     =  histo->GetBinCenter(maxBin); //mean
   double lowfwhm  =  histo->GetBinCenter(histo->FindFirstBinAbove(max/2));
   double highfwhm =  histo->GetBinCenter(histo->FindLastBinAbove(max/2));
   double sigma    = (highfwhm - lowfwhm)/2.35482; //estimate fwhm  FWHM = 2.35482*sigma

   TF1 *gaussfit   = new TF1("gaussfit","gaus", mean - 4*sigma, mean + 4*sigma); // fit in +- 4 sigma window
   gaussfit->SetParameters(max, mean, sigma);

   if(whichParameter==2)
     histo->Fit(gaussfit,"RQNI");
   else
     histo->Fit(gaussfit,"RQN");

   double parValue = gaussfit->GetParameter(whichParameter);

   delete gaussfit;

   return parValue;
}

<|endoftext|>


<commit_before>#pragma once

#include "core_configuration/core_configuration.hpp"
#include "device_properties.hpp"
#include "event_queue.hpp"
#include "hid_keyboard_caps_lock_led_state_manager.hpp"
#include "iokit_utility.hpp"
#include "pressed_keys_manager.hpp"
#include "types.hpp"
#include <pqrs/osx/iokit_hid_queue_value_monitor.hpp>

namespace krbn {
namespace grabber {
namespace device_grabber_details {
class entry final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  entry(device_id device_id,
        IOHIDDeviceRef device,
        std::weak_ptr<const core_configuration::core_configuration> core_configuration) : dispatcher_client(),
                                                                                device_id_(device_id),
                                                                                core_configuration_(core_configuration),
                                                                                hid_queue_value_monitor_async_start_called_(false),
                                                                                first_value_arrived_(false),
                                                                                grabbed_(false),
                                                                                disabled_(false),
                                                                                grabbed_time_stamp_(0),
                                                                                ungrabbed_time_stamp_(0) {
    device_properties_ = std::make_shared<device_properties>(device_id,
                                                             device);

    pressed_keys_manager_ = std::make_shared<pressed_keys_manager>();
    hid_queue_value_monitor_ = std::make_shared<pqrs::osx::iokit_hid_queue_value_monitor>(pqrs::dispatcher::extra::get_shared_dispatcher(),
                                                                                device);
    caps_lock_led_state_manager_ = std::make_shared<krbn::hid_keyboard_caps_lock_led_state_manager>(device);
    device_name_ = iokit_utility::make_device_name_for_log(device_id,
                                                           device);
    device_short_name_ = iokit_utility::make_device_name(device);
  }

  ~entry(void) {
    detach_from_dispatcher([this] {
      hid_queue_value_monitor_ = nullptr;
      caps_lock_led_state_manager_ = nullptr;
    });
  }

  device_id get_device_id(void) const {
    return device_id_;
  }

  void set_core_configuration(std::weak_ptr<const core_configuration::core_configuration> core_configuration) {
    core_configuration_ = core_configuration;

    control_caps_lock_led_state_manager();
  }

  std::shared_ptr<device_properties> get_device_properties(void) const {
    return device_properties_;
  }

  std::shared_ptr<pressed_keys_manager> get_pressed_keys_manager(void) const {
    return pressed_keys_manager_;
  }

  std::shared_ptr<pqrs::osx::iokit_hid_queue_value_monitor> get_hid_queue_value_monitor(void) const {
    return hid_queue_value_monitor_;
  }

  bool get_first_value_arrived(void) const {
    return first_value_arrived_;
  }

  void set_first_value_arrived(bool value) {
    first_value_arrived_ = value;
  }

  std::shared_ptr<hid_keyboard_caps_lock_led_state_manager> get_caps_lock_led_state_manager(void) const {
    return caps_lock_led_state_manager_;
  }

  const std::string& get_device_name(void) const {
    return device_name_;
  }

  const std::string& get_device_short_name(void) const {
    return device_short_name_;
  }

  bool get_grabbed(void) const {
    return grabbed_;
  }

  void set_grabbed(bool value) {
    grabbed_ = value;

    if (grabbed_) {
      grabbed_time_stamp_ = pqrs::osx::chrono::mach_absolute_time_point();
    } else {
      ungrabbed_time_stamp_ = pqrs::osx::chrono::mach_absolute_time_point();
    }

    control_caps_lock_led_state_manager();
  }

  bool get_disabled(void) const {
    return disabled_;
  }

  void set_disabled(bool value) {
    disabled_ = value;
  }

  bool is_ignored_device(void) const {
    if (device_properties_) {
      if (auto c = core_configuration_.lock()) {
        if (auto device_identifiers = device_properties_->get_device_identifiers()) {
          return c->get_selected_profile().get_device_ignore(
              *device_identifiers);
        }
      }
    }

    return false;
  }

  bool is_disable_built_in_keyboard_if_exists(void) const {
    if (device_properties_) {
      if (auto c = core_configuration_.lock()) {
        if (auto device_identifiers = device_properties_->get_device_identifiers()) {
          return c->get_selected_profile().get_device_disable_built_in_keyboard_if_exists(
              *device_identifiers);
        }
      }
    }
    return false;
  }

  void async_start_queue_value_monitor(void) {
    if (hid_queue_value_monitor_) {
      if (!hid_queue_value_monitor_async_start_called_) {
        first_value_arrived_ = false;
        hid_queue_value_monitor_async_start_called_ = true;
      }

      hid_queue_value_monitor_->async_start(kIOHIDOptionsTypeSeizeDevice,
                                            std::chrono::milliseconds(1000));
    }
  }

  void async_stop_queue_value_monitor(void) {
    if (hid_queue_value_monitor_) {
      hid_queue_value_monitor_async_start_called_ = false;

      hid_queue_value_monitor_->async_stop();
    }
  }

  bool is_grabbed(absolute_time_point time_stamp) {
    if (grabbed_) {
      if (grabbed_time_stamp_ <= time_stamp) {
        return true;
      }
    } else {
      //
      // (grabbed_time_stamp_ <= ungrabbed_time_stamp_) when (grabbed_ == false)
      //

      if (grabbed_time_stamp_ <= time_stamp &&
          time_stamp <= ungrabbed_time_stamp_) {
        return true;
      }
    }

    return false;
  }

private:
  void control_caps_lock_led_state_manager(void) {
    if (caps_lock_led_state_manager_) {
      if (device_properties_) {
        if (auto c = core_configuration_.lock()) {
          if (auto device_identifiers = device_properties_->get_device_identifiers()) {
            if (c->get_selected_profile().get_device_manipulate_caps_lock_led(*device_identifiers)) {
              if (grabbed_) {
                caps_lock_led_state_manager_->async_start();
                return;
              }
            }
          }
        }
      }

      caps_lock_led_state_manager_->async_stop();
    }
  }

  device_id device_id_;
  std::weak_ptr<const core_configuration::core_configuration> core_configuration_;
  std::shared_ptr<device_properties> device_properties_;
  std::shared_ptr<pressed_keys_manager> pressed_keys_manager_;

  std::shared_ptr<pqrs::osx::iokit_hid_queue_value_monitor> hid_queue_value_monitor_;
  bool hid_queue_value_monitor_async_start_called_;
  bool first_value_arrived_;

  std::shared_ptr<hid_keyboard_caps_lock_led_state_manager> caps_lock_led_state_manager_;
  std::string device_name_;
  std::string device_short_name_;

  bool grabbed_;
  bool disabled_;

  absolute_time_point grabbed_time_stamp_;
  absolute_time_point ungrabbed_time_stamp_;
};
} // namespace device_grabber_details
} // namespace grabber
} // namespace krbn
<commit_msg>Return false at is_disable_built_in_keyboard_if_exists if device is built in<commit_after>#pragma once

#include "core_configuration/core_configuration.hpp"
#include "device_properties.hpp"
#include "event_queue.hpp"
#include "hid_keyboard_caps_lock_led_state_manager.hpp"
#include "iokit_utility.hpp"
#include "pressed_keys_manager.hpp"
#include "types.hpp"
#include <pqrs/osx/iokit_hid_queue_value_monitor.hpp>

namespace krbn {
namespace grabber {
namespace device_grabber_details {
class entry final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  entry(device_id device_id,
        IOHIDDeviceRef device,
        std::weak_ptr<const core_configuration::core_configuration> core_configuration) : dispatcher_client(),
                                                                                device_id_(device_id),
                                                                                core_configuration_(core_configuration),
                                                                                hid_queue_value_monitor_async_start_called_(false),
                                                                                first_value_arrived_(false),
                                                                                grabbed_(false),
                                                                                disabled_(false),
                                                                                grabbed_time_stamp_(0),
                                                                                ungrabbed_time_stamp_(0) {
    device_properties_ = std::make_shared<device_properties>(device_id,
                                                             device);

    pressed_keys_manager_ = std::make_shared<pressed_keys_manager>();
    hid_queue_value_monitor_ = std::make_shared<pqrs::osx::iokit_hid_queue_value_monitor>(pqrs::dispatcher::extra::get_shared_dispatcher(),
                                                                                device);
    caps_lock_led_state_manager_ = std::make_shared<krbn::hid_keyboard_caps_lock_led_state_manager>(device);
    device_name_ = iokit_utility::make_device_name_for_log(device_id,
                                                           device);
    device_short_name_ = iokit_utility::make_device_name(device);
  }

  ~entry(void) {
    detach_from_dispatcher([this] {
      hid_queue_value_monitor_ = nullptr;
      caps_lock_led_state_manager_ = nullptr;
    });
  }

  device_id get_device_id(void) const {
    return device_id_;
  }

  void set_core_configuration(std::weak_ptr<const core_configuration::core_configuration> core_configuration) {
    core_configuration_ = core_configuration;

    control_caps_lock_led_state_manager();
  }

  std::shared_ptr<device_properties> get_device_properties(void) const {
    return device_properties_;
  }

  std::shared_ptr<pressed_keys_manager> get_pressed_keys_manager(void) const {
    return pressed_keys_manager_;
  }

  std::shared_ptr<pqrs::osx::iokit_hid_queue_value_monitor> get_hid_queue_value_monitor(void) const {
    return hid_queue_value_monitor_;
  }

  bool get_first_value_arrived(void) const {
    return first_value_arrived_;
  }

  void set_first_value_arrived(bool value) {
    first_value_arrived_ = value;
  }

  std::shared_ptr<hid_keyboard_caps_lock_led_state_manager> get_caps_lock_led_state_manager(void) const {
    return caps_lock_led_state_manager_;
  }

  const std::string& get_device_name(void) const {
    return device_name_;
  }

  const std::string& get_device_short_name(void) const {
    return device_short_name_;
  }

  bool get_grabbed(void) const {
    return grabbed_;
  }

  void set_grabbed(bool value) {
    grabbed_ = value;

    if (grabbed_) {
      grabbed_time_stamp_ = pqrs::osx::chrono::mach_absolute_time_point();
    } else {
      ungrabbed_time_stamp_ = pqrs::osx::chrono::mach_absolute_time_point();
    }

    control_caps_lock_led_state_manager();
  }

  bool get_disabled(void) const {
    return disabled_;
  }

  void set_disabled(bool value) {
    disabled_ = value;
  }

  bool is_ignored_device(void) const {
    if (device_properties_) {
      if (auto c = core_configuration_.lock()) {
        if (auto device_identifiers = device_properties_->get_device_identifiers()) {
          return c->get_selected_profile().get_device_ignore(
              *device_identifiers);
        }
      }
    }

    return false;
  }

  bool is_disable_built_in_keyboard_if_exists(void) const {
    if (device_properties_) {
      if (device_properties_->get_is_built_in_keyboard() ||
          device_properties_->get_is_built_in_pointing_device()) {
        return false;
      }

      if (auto c = core_configuration_.lock()) {
        if (auto device_identifiers = device_properties_->get_device_identifiers()) {
          return c->get_selected_profile().get_device_disable_built_in_keyboard_if_exists(
              *device_identifiers);
        }
      }
    }
    return false;
  }

  void async_start_queue_value_monitor(void) {
    if (hid_queue_value_monitor_) {
      if (!hid_queue_value_monitor_async_start_called_) {
        first_value_arrived_ = false;
        hid_queue_value_monitor_async_start_called_ = true;
      }

      hid_queue_value_monitor_->async_start(kIOHIDOptionsTypeSeizeDevice,
                                            std::chrono::milliseconds(1000));
    }
  }

  void async_stop_queue_value_monitor(void) {
    if (hid_queue_value_monitor_) {
      hid_queue_value_monitor_async_start_called_ = false;

      hid_queue_value_monitor_->async_stop();
    }
  }

  bool is_grabbed(absolute_time_point time_stamp) {
    if (grabbed_) {
      if (grabbed_time_stamp_ <= time_stamp) {
        return true;
      }
    } else {
      //
      // (grabbed_time_stamp_ <= ungrabbed_time_stamp_) when (grabbed_ == false)
      //

      if (grabbed_time_stamp_ <= time_stamp &&
          time_stamp <= ungrabbed_time_stamp_) {
        return true;
      }
    }

    return false;
  }

private:
  void control_caps_lock_led_state_manager(void) {
    if (caps_lock_led_state_manager_) {
      if (device_properties_) {
        if (auto c = core_configuration_.lock()) {
          if (auto device_identifiers = device_properties_->get_device_identifiers()) {
            if (c->get_selected_profile().get_device_manipulate_caps_lock_led(*device_identifiers)) {
              if (grabbed_) {
                caps_lock_led_state_manager_->async_start();
                return;
              }
            }
          }
        }
      }

      caps_lock_led_state_manager_->async_stop();
    }
  }

  device_id device_id_;
  std::weak_ptr<const core_configuration::core_configuration> core_configuration_;
  std::shared_ptr<device_properties> device_properties_;
  std::shared_ptr<pressed_keys_manager> pressed_keys_manager_;

  std::shared_ptr<pqrs::osx::iokit_hid_queue_value_monitor> hid_queue_value_monitor_;
  bool hid_queue_value_monitor_async_start_called_;
  bool first_value_arrived_;

  std::shared_ptr<hid_keyboard_caps_lock_led_state_manager> caps_lock_led_state_manager_;
  std::string device_name_;
  std::string device_short_name_;

  bool grabbed_;
  bool disabled_;

  absolute_time_point grabbed_time_stamp_;
  absolute_time_point ungrabbed_time_stamp_;
};
} // namespace device_grabber_details
} // namespace grabber
} // namespace krbn
<|endoftext|>


<commit_before>///
/// @file  S2_easy.cpp
/// @brief Calculate the contribution of the clustered easy leaves
///        and the sparse easy leaves in parallel using OpenMP
///        (Deleglise-Rivat algorithm).
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <PiTable.hpp>
#include <primecount-internal.hpp>
#include <calculator.hpp>
#include <fast_div.hpp>
#include <generate.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <imath.hpp>
#include <S2Status.hpp>
#include <S2.hpp>
#include <json.hpp>

#include <stdint.h>
#include <vector>
#include <fstream>

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

using namespace std;
using namespace primecount;
using namespace nlohmann;

namespace {

template <typename T>
void backup(T x,
            int64_t y,
            int64_t z,
            int64_t c,
            int64_t start,
            int64_t pi_x13,
            T s2_easy,
            double percent,
            double time)
{
  ifstream ifs("primecount.backup");
  json j;

  if (ifs.is_open())
  {
    ifs >> j;
    ifs.close();
  }

  if (j.find("S2_easy") != j.end())
    j.erase(j.find("S2_easy"));

  j["S2_easy"]["x"] = to_string(x);
  j["S2_easy"]["y"] = y;
  j["S2_easy"]["z"] = z;
  j["S2_easy"]["c"] = c;
  j["S2_easy"]["start"] = start;
  j["S2_easy"]["pi_x13"] = pi_x13;
  j["S2_easy"]["s2_easy"] = to_string(s2_easy);
  j["S2_easy"]["percent"] = percent;
  j["S2_easy"]["seconds"] = get_wtime() - time;

  ofstream ofs("primecount.backup");
  ofs << setw(4) << j << endl;
}

template <typename T>
bool resume(T x,
            int64_t y,
            int64_t z,
            int64_t c,
            int64_t& start,
            int64_t& pi_x13,
            T& s2_easy,
            double& time)
{
  ifstream ifs("primecount.backup");
  json j;

  if (ifs.is_open())
  {
    ifs >> j;
    ifs.close();
  }

  if (j.find("S2_easy") != j.end() &&
      x == calculator::eval<T>(j["S2_easy"]["x"]) &&
      y == j["S2_easy"]["y"] &&
      z == j["S2_easy"]["z"] &&
      c == j["S2_easy"]["c"])
  {
    double percent = j["S2_easy"]["percent"];
    double seconds = j["S2_easy"]["seconds"];

    start = j["S2_easy"]["start"];
    pi_x13 = j["S2_easy"]["pi_x13"];
    s2_easy = calculator::eval<T>(j["S2_easy"]["s2_easy"]);
    time = get_wtime() - seconds;

    if (is_print())
    {
      if (!print_variables())
        cout << endl;

      cout << "=== Resuming from primecount.backup ===" << endl;
      cout << "start = " << start << endl;
      cout << "pi_x13 = " << pi_x13 << endl;
      cout << "s2_easy = " << s2_easy << endl;
      cout << "Seconds: " << seconds << endl << endl;
      cout << "Status: " << fixed << setprecision(get_status_precision(x)) << percent << '%' << flush;
    }

    return true;
  }

  return false;
}

/// Calculate the contribution of the clustered easy leaves
/// and the sparse easy leaves.
/// @param T  either int64_t or uint128_t.
///
template <typename T, typename Primes>
T S2_easy_OpenMP(T x,
                 int64_t y,
                 int64_t z,
                 int64_t c,
                 Primes& primes,
                 int threads,
                 double& time)
{
  T s2_easy = 0;
  int64_t start;
  int64_t pi_x13;
  double backup_time = get_wtime();
  bool is_resume = resume(x, y, z, c, start, pi_x13, s2_easy, time);

  if (is_resume && start >= pi_x13)
    return s2_easy;

  PiTable pi(y);
  S2Status status(x);

  int64_t pi_sqrty = pi[isqrt(y)];
  int64_t x13 = iroot<3>(x);
  int64_t max_dist = 1;
  int64_t thread_threshold = 1000;
  threads = ideal_num_threads(threads, x13, thread_threshold);

  if (!is_resume)
  {
    start = max(c, pi_sqrty) + 1;
    pi_x13 = pi[x13];
  }

  while (start <= pi_x13)
  {
    int64_t stop = min(start + max_dist * threads, pi_x13);

    #pragma omp parallel for schedule(dynamic) num_threads(threads) reduction(+: s2_easy)
    for (int64_t b = start; b <= stop; b++)
    {
      int64_t prime = primes[b];
      T x2 = x / prime;
      int64_t min_trivial = min(x2 / prime, y);
      int64_t min_clustered = (int64_t) isqrt(x2);
      int64_t min_sparse = z / prime;

      min_clustered = in_between(prime, min_clustered, y);
      min_sparse = in_between(prime, min_sparse, y);

      int64_t l = pi[min_trivial];
      int64_t pi_min_clustered = pi[min_clustered];
      int64_t pi_min_sparse = pi[min_sparse];

      // Find all clustered easy leaves:
      // n = primes[b] * primes[l]
      // x / n <= y && phi(x / n, b - 1) == phi(x / m, b - 1)
      // where phi(x / n, b - 1) = pi(x / n) - b + 2
      while (l > pi_min_clustered)
      {
        int64_t xn = (int64_t) fast_div(x2, primes[l]);
        int64_t phi_xn = pi[xn] - b + 2;
        int64_t xm = (int64_t) fast_div(x2, primes[b + phi_xn - 1]);
        int64_t l2 = pi[xm];
        s2_easy += phi_xn * (l - l2);
        l = l2;
      }

      // Find all sparse easy leaves:
      // n = primes[b] * primes[l]
      // x / n <= y && phi(x / n, b - 1) = pi(x / n) - b + 2
      for (; l > pi_min_sparse; l--)
      {
        int64_t xn = (int64_t) fast_div(x2, primes[l]);
        s2_easy += pi[xn] - b + 2;
      }

      if (is_print())
        status.print(b, pi_x13);
    }

    start = stop + 1;

    if (get_wtime() - backup_time < 60)
      max_dist *= 2;
    else
    {
      max_dist = ceil_div(max_dist, 2);
      double percent = status.getPercent(start, pi_x13, start, pi_x13);
      backup(x, y, z, c, start, pi_x13, s2_easy, percent, time);
      backup_time = get_wtime();
    }
  }

  backup(x, y, z, c, pi_x13, pi_x13, s2_easy, 100, time);

  return s2_easy;
}

} // namespace

namespace primecount {

int64_t S2_easy(int64_t x,
                int64_t y,
                int64_t z,
                int64_t c,
                int threads)
{
#ifdef HAVE_MPI
  if (mpi_num_procs() > 1)
    return S2_easy_mpi(x, y, z, c, threads);
#endif

  print("");
  print("=== S2_easy(x, y) ===");
  print("Computation of the easy special leaves");
  print(x, y, c, threads);

  double time = get_wtime();
  auto primes = generate_primes<int32_t>(y);
  int64_t s2_easy = S2_easy_OpenMP((intfast64_t) x, y, z, c, primes, threads, time);

  print("S2_easy", s2_easy, time);
  return s2_easy;
}

#ifdef HAVE_INT128_T

int128_t S2_easy(int128_t x,
                 int64_t y,
                 int64_t z,
                 int64_t c,
                 int threads)
{
#ifdef HAVE_MPI
  if (mpi_num_procs() > 1)
    return S2_easy_mpi(x, y, z, c, threads);
#endif

  print("");
  print("=== S2_easy(x, y) ===");
  print("Computation of the easy special leaves");
  print(x, y, c, threads);

  int128_t s2_easy;
  double time = get_wtime();

  // uses less memory
  if (y <= numeric_limits<uint32_t>::max())
  {
    auto primes = generate_primes<uint32_t>(y);
    s2_easy = S2_easy_OpenMP((intfast128_t) x, y, z, c, primes, threads, time);
  }
  else
  {
    auto primes = generate_primes<int64_t>(y);
    s2_easy = S2_easy_OpenMP((intfast128_t) x, y, z, c, primes, threads, time);
  }

  print("S2_easy", s2_easy, time);
  return s2_easy;
}

#endif

} // namespace
<commit_msg>Backup every 5 minutes<commit_after>///
/// @file  S2_easy.cpp
/// @brief Calculate the contribution of the clustered easy leaves
///        and the sparse easy leaves in parallel using OpenMP
///        (Deleglise-Rivat algorithm).
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <PiTable.hpp>
#include <primecount-internal.hpp>
#include <calculator.hpp>
#include <fast_div.hpp>
#include <generate.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <imath.hpp>
#include <S2Status.hpp>
#include <S2.hpp>
#include <json.hpp>

#include <stdint.h>
#include <vector>
#include <fstream>

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

using namespace std;
using namespace primecount;
using namespace nlohmann;

namespace {

template <typename T>
void backup(T x,
            int64_t y,
            int64_t z,
            int64_t c,
            int64_t start,
            int64_t pi_x13,
            T s2_easy,
            double percent,
            double time)
{
  ifstream ifs("primecount.backup");
  json j;

  if (ifs.is_open())
  {
    ifs >> j;
    ifs.close();
  }

  if (j.find("S2_easy") != j.end())
    j.erase(j.find("S2_easy"));

  j["S2_easy"]["x"] = to_string(x);
  j["S2_easy"]["y"] = y;
  j["S2_easy"]["z"] = z;
  j["S2_easy"]["c"] = c;
  j["S2_easy"]["start"] = start;
  j["S2_easy"]["pi_x13"] = pi_x13;
  j["S2_easy"]["s2_easy"] = to_string(s2_easy);
  j["S2_easy"]["percent"] = percent;
  j["S2_easy"]["seconds"] = get_wtime() - time;

  ofstream ofs("primecount.backup");
  ofs << setw(4) << j << endl;
}

template <typename T>
bool resume(T x,
            int64_t y,
            int64_t z,
            int64_t c,
            int64_t& start,
            int64_t& pi_x13,
            T& s2_easy,
            double& time)
{
  ifstream ifs("primecount.backup");
  json j;

  if (ifs.is_open())
  {
    ifs >> j;
    ifs.close();
  }

  if (j.find("S2_easy") != j.end() &&
      x == calculator::eval<T>(j["S2_easy"]["x"]) &&
      y == j["S2_easy"]["y"] &&
      z == j["S2_easy"]["z"] &&
      c == j["S2_easy"]["c"])
  {
    double percent = j["S2_easy"]["percent"];
    double seconds = j["S2_easy"]["seconds"];

    start = j["S2_easy"]["start"];
    pi_x13 = j["S2_easy"]["pi_x13"];
    s2_easy = calculator::eval<T>(j["S2_easy"]["s2_easy"]);
    time = get_wtime() - seconds;

    if (is_print())
    {
      if (!print_variables())
        cout << endl;

      cout << "=== Resuming from primecount.backup ===" << endl;
      cout << "start = " << start << endl;
      cout << "pi_x13 = " << pi_x13 << endl;
      cout << "s2_easy = " << s2_easy << endl;
      cout << "Seconds: " << seconds << endl << endl;
      cout << "Status: " << fixed << setprecision(get_status_precision(x)) << percent << '%' << flush;
    }

    return true;
  }

  return false;
}

/// Calculate the contribution of the clustered easy leaves
/// and the sparse easy leaves.
/// @param T  either int64_t or uint128_t.
///
template <typename T, typename Primes>
T S2_easy_OpenMP(T x,
                 int64_t y,
                 int64_t z,
                 int64_t c,
                 Primes& primes,
                 int threads,
                 double& time)
{
  T s2_easy = 0;
  int64_t start;
  int64_t pi_x13;
  double backup_time = get_wtime();
  bool is_resume = resume(x, y, z, c, start, pi_x13, s2_easy, time);

  if (is_resume && start >= pi_x13)
    return s2_easy;

  PiTable pi(y);
  S2Status status(x);

  int64_t pi_sqrty = pi[isqrt(y)];
  int64_t x13 = iroot<3>(x);
  int64_t max_dist = 1;
  int64_t thread_threshold = 1000;
  threads = ideal_num_threads(threads, x13, thread_threshold);

  if (!is_resume)
  {
    start = max(c, pi_sqrty) + 1;
    pi_x13 = pi[x13];
  }

  while (start <= pi_x13)
  {
    int64_t stop = min(start + max_dist * threads, pi_x13);

    #pragma omp parallel for schedule(dynamic) num_threads(threads) reduction(+: s2_easy)
    for (int64_t b = start; b <= stop; b++)
    {
      int64_t prime = primes[b];
      T x2 = x / prime;
      int64_t min_trivial = min(x2 / prime, y);
      int64_t min_clustered = (int64_t) isqrt(x2);
      int64_t min_sparse = z / prime;

      min_clustered = in_between(prime, min_clustered, y);
      min_sparse = in_between(prime, min_sparse, y);

      int64_t l = pi[min_trivial];
      int64_t pi_min_clustered = pi[min_clustered];
      int64_t pi_min_sparse = pi[min_sparse];

      // Find all clustered easy leaves:
      // n = primes[b] * primes[l]
      // x / n <= y && phi(x / n, b - 1) == phi(x / m, b - 1)
      // where phi(x / n, b - 1) = pi(x / n) - b + 2
      while (l > pi_min_clustered)
      {
        int64_t xn = (int64_t) fast_div(x2, primes[l]);
        int64_t phi_xn = pi[xn] - b + 2;
        int64_t xm = (int64_t) fast_div(x2, primes[b + phi_xn - 1]);
        int64_t l2 = pi[xm];
        s2_easy += phi_xn * (l - l2);
        l = l2;
      }

      // Find all sparse easy leaves:
      // n = primes[b] * primes[l]
      // x / n <= y && phi(x / n, b - 1) = pi(x / n) - b + 2
      for (; l > pi_min_sparse; l--)
      {
        int64_t xn = (int64_t) fast_div(x2, primes[l]);
        s2_easy += pi[xn] - b + 2;
      }

      if (is_print())
        status.print(b, pi_x13);
    }

    start = stop + 1;

    if (get_wtime() - backup_time < 300)
      max_dist *= 2;
    else
    {
      max_dist = ceil_div(max_dist, 2);
      double percent = status.getPercent(start, pi_x13, start, pi_x13);
      backup(x, y, z, c, start, pi_x13, s2_easy, percent, time);
      backup_time = get_wtime();
    }
  }

  backup(x, y, z, c, pi_x13, pi_x13, s2_easy, 100, time);

  return s2_easy;
}

} // namespace

namespace primecount {

int64_t S2_easy(int64_t x,
                int64_t y,
                int64_t z,
                int64_t c,
                int threads)
{
#ifdef HAVE_MPI
  if (mpi_num_procs() > 1)
    return S2_easy_mpi(x, y, z, c, threads);
#endif

  print("");
  print("=== S2_easy(x, y) ===");
  print("Computation of the easy special leaves");
  print(x, y, c, threads);

  double time = get_wtime();
  auto primes = generate_primes<int32_t>(y);
  int64_t s2_easy = S2_easy_OpenMP((intfast64_t) x, y, z, c, primes, threads, time);

  print("S2_easy", s2_easy, time);
  return s2_easy;
}

#ifdef HAVE_INT128_T

int128_t S2_easy(int128_t x,
                 int64_t y,
                 int64_t z,
                 int64_t c,
                 int threads)
{
#ifdef HAVE_MPI
  if (mpi_num_procs() > 1)
    return S2_easy_mpi(x, y, z, c, threads);
#endif

  print("");
  print("=== S2_easy(x, y) ===");
  print("Computation of the easy special leaves");
  print(x, y, c, threads);

  int128_t s2_easy;
  double time = get_wtime();

  // uses less memory
  if (y <= numeric_limits<uint32_t>::max())
  {
    auto primes = generate_primes<uint32_t>(y);
    s2_easy = S2_easy_OpenMP((intfast128_t) x, y, z, c, primes, threads, time);
  }
  else
  {
    auto primes = generate_primes<int64_t>(y);
    s2_easy = S2_easy_OpenMP((intfast128_t) x, y, z, c, primes, threads, time);
  }

  print("S2_easy", s2_easy, time);
  return s2_easy;
}

#endif

} // namespace
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011-2013 Yule Fox. All rights reserved.
 * http://www.yulefox.com/
 */

#include <elf/log.h>
#include <elf/memory.h>
#include <elf/time.h>
#include <elf/timer.h>
#include <algorithm>
#include <list>

namespace elf {
#define WHEEL_SET_BIT0                  8
#define WHEEL_SET_BITS                  (WHEEL_SET_BIT0 * 4)
#define WHEEL_SET_BIT(l)                (WHEEL_SET_BIT0 * (l))
#define WHEEL_SET_SIZE0                 (1 << WHEEL_SET_BIT0)
#define WHEEL_SET_SIZE(l)               WHEEL_SET_SIZE0
#define MAX_WHEEL_SET_SIZE              (WHEEL_SET_SIZE0 * 4)
#define MAX_CURSOR                      (1LL << WHEEL_SET_BITS)
#define MAX_LIFE                        (MAX_CURSOR * s_mgr.interval)
#define FRAME_DIFFER(l, r)              (int)(l - r)

union cursor_t {
    struct wheel_t {
        unsigned char w0;
        unsigned char w1;
        unsigned char w2;
        unsigned char w3;
    };
    wheel_t w;
    int a;
};

struct timer_t {
    oid_t id; // identification
    time64_t life; // life from now
    cursor_t cursor; // expired frame
    bool script;
    union cb_t {
        char script[1024]; // script function name
        callback func; // callback function
    };
    cb_t cb; // callback function
    void *args; // callback arguments
    timer_t *next; // the next node of the tail node is NULL
    timer_t *prev; // the previous node of the head node is the tail
};

struct mgr_t {
    time64_t start_time; // start time
    time64_t end_time; // end time
    time64_t cur_time; // current frame time
    time64_t last_time; // last pause time
    time64_t interval; // frame interval time
    cursor_t cursor; // current cursor (number of escaped frames)
    int round; // current round
    int last_cursor; // last pause cursor
    int timer_total; // total number of timers
    int timer_remain; // remain timers
    int timer_passed; // total number of passed timers
    int timer_cancelled; // total number of cancelled timers
    bool pause; // suspend all timers
    timer_t *timers[MAX_WHEEL_SET_SIZE];
};

typedef std::list<callback> handler_list;

static handler_list s_handlers;

static const time64_t TIMER_FRAME_INTERVAL_DEFAULT = 50; // (ms)
static const time64_t TIMER_FRAME_INTERVAL_MIN = 1; // (frame)
static const time64_t TIMER_FRAME_INTERVAL_MAX = 1000; // (frame)
static mgr_t s_mgr;

/// calculate the number of life frames
#define FRAME_CALC(t) ((time64_t)(t) / s_mgr.interval)

// @{
/// get current bucket cursor at wheel 0
#define CURRENT_WHEEL_CURSOR    s_mgr.cursor.w.w0

/// compare given cursor with current bucket cursor at wheel @param l
#define WHEEL_CMP(cur, l)                                                   \
    ((int)cur.w.w##l - (int)s_mgr.cursor.w.w##l)

/// map cursor at wheel @param l
#define BUCKET_MAP(cur, l)                                                  \
    (cur.w.w##l + WHEEL_SET_SIZE0 * l)

/// calculate the number of frames elapsed from last cursor
#define FRAME_BINGO (s_mgr.last_cursor +                                    \
        FRAME_CALC(s_mgr.cur_time - s_mgr.last_time) - s_mgr.cursor.a)
// @}

static void reset(void);
static void push(timer_t *t, int bucket);
static timer_t *get(const oid_t &tid, int *bucket);
static void destroy(timer_t *t);
static int schedule(timer_t *t);
static void expire(timer_t *t);
static void bingo(void);
static void hash(int bucket);
static void timer_min(void *args);

int timer_init(void)
{
    MODULE_IMPORT_SWITCH;
    s_mgr.interval = TIMER_FRAME_INTERVAL_DEFAULT;
    s_mgr.start_time = s_mgr.last_time = time_ms();
    s_mgr.pause = true;
    s_mgr.cursor.a = 0;
    s_mgr.round = 0;
    memset(s_mgr.timers, 0, sizeof(s_mgr.timers[0]) * MAX_WHEEL_SET_SIZE);
    timer_min(NULL);
    return 0;
}

int timer_fini(void)
{
    MODULE_IMPORT_SWITCH;
    s_mgr.end_time = time_ms();
    for (int i = 0; i < MAX_WHEEL_SET_SIZE; ++i) {
        timer_t *head = s_mgr.timers[i];
        timer_t *t = head;

        while (t) {
            timer_t *n = t->next;

            destroy(t);
            if (n == head) break;
            t = n;
        }
    }
    return 0;
}

void timer_run(void)
{
    if (s_mgr.pause || s_mgr.timer_remain <= 0) {
        return;
    }

    // frame may greater than 1 while CPU busying
    s_mgr.cur_time = time_ms();
    int frame = FRAME_BINGO;

    // time anomaly
    if (frame < 0) {
        LOG_WARN("timer", "(%u)%08X: %llu - %llu(%d).",
                s_mgr.round, s_mgr.cursor.a,
                s_mgr.cur_time, s_mgr.last_time,
                frame);
        assert(frame >= 0);
        return;
    }
    // while processors is busy
    if (frame > 1) {
        LOG_WARN("timer", "(%u)%08X: %llu - %llu(%d).",
                s_mgr.round, s_mgr.cursor.a,
                s_mgr.cur_time, s_mgr.last_time,
                frame);
    }
    for (int i = 1; i <= frame; ++i) { // BINGO!
        unsigned char bucket = CURRENT_WHEEL_CURSOR;
        timer_t *head = s_mgr.timers[bucket];
        timer_t *t = head;

        while (t) {
            timer_t *n = t->next;

            expire(t);
            ++s_mgr.timer_passed;
            --s_mgr.timer_remain;
            if (n == head) {
                s_mgr.timers[bucket] = NULL;
                break;
            }
            t = n;
        }
        bingo();
    }
}

void timer_stat(void)
{
    LOG_INFO("timer",
            "ST: %lld RND: %u FRM: %u TMT: %u TMP: %u TMC: %u.",
            s_mgr.start_time, s_mgr.round, s_mgr.cursor.a,
            s_mgr.timer_total, s_mgr.timer_passed, s_mgr.timer_cancelled);
}

int timer_size(void)
{
    return s_mgr.timer_remain;
}

const oid_t &timer_add(time64_t life, const char *func)
{
    if (life < 0 || life >= MAX_LIFE) {
        LOG_WARN("timer",
                "Timer added FAILED: invalid timer life(%lld) [0, %lld).",
                life, MAX_LIFE);
        return OID_NIL;
    }

    timer_t *t = E_NEW timer_t;

    memset(t, 0, sizeof(*t));
    t->id = oid_gen();
    t->life = std::max(life, s_mgr.interval);
    t->cursor.a = (FRAME_CALC(t->life) + s_mgr.cursor.a) % MAX_CURSOR;
    t->script = true;
    strcpy(t->cb.script, func);
    t->args = NULL;
    schedule(t);
    ++s_mgr.timer_total;
    ++s_mgr.timer_remain;
    return t->id;
}

const oid_t &timer_add(time64_t life, callback func, void *args)
{
    if (life < 0 || life >= MAX_LIFE) {
        LOG_WARN("timer",
                "Timer added FAILED: invalid timer life(%lld) [0, %lld).",
                life, MAX_LIFE);
        return OID_NIL;
    }

    timer_t *t = E_NEW timer_t;

    memset(t, 0, sizeof(*t));
    t->id = oid_gen();
    t->life = std::max(life, s_mgr.interval);
    t->cursor.a = (FRAME_CALC(t->life) + s_mgr.cursor.a) % MAX_CURSOR;
    t->script = false;
    t->cb.func = func;
    t->args = args;
    schedule(t);
    ++s_mgr.timer_total;
    ++s_mgr.timer_remain;
    return t->id;
}

void timer_cycle(callback func)
{
    s_handlers.push_back(func);
}

void timer_remove(const oid_t &tid)
{
    int bucket;
    timer_t *t = get(tid, &bucket);

    if (t) {
        timer_t *n = t->next;

        if (n != t) {
            t->prev->next = n;
            n->prev = t->prev;
        } else {
            n = NULL;
        }
        if (t == s_mgr.timers[bucket]) {
            s_mgr.timers[bucket] = n;
        }
        S_FREE(t->args);
        destroy(t);
        ++s_mgr.timer_cancelled;
        --s_mgr.timer_remain;
    }
}

void timer_pause(void)
{
    if (!s_mgr.pause) {
        s_mgr.pause = true;
    }
}

void timer_resume(void)
{
    if (s_mgr.pause) {
        s_mgr.pause = false;
        reset();
    }
}

void timer_pause(const oid_t &tid)
{
}

void timer_resume(const oid_t &tid)
{
}

void timer_interval(time64_t t)
{
    ELF_ASSERT(t >= TIMER_FRAME_INTERVAL_MIN && t <= TIMER_FRAME_INTERVAL_MAX);
    s_mgr.interval = t;
    reset();
}

void timer_bucket(unsigned char no, int l)
{
    switch (l) {
        case 0:
            s_mgr.cursor.w.w0 = no;
            break;
        case 1:
            s_mgr.cursor.w.w1 = no;
            break;
        case 2:
            s_mgr.cursor.w.w2 = no;
            break;
        case 3:
            s_mgr.cursor.w.w3 = no;
            break;
    }
    reset();
}

static void reset(void)
{
    s_mgr.last_time = time_ms();
    s_mgr.last_cursor = s_mgr.cursor.a;
}

static void push(timer_t *t, int bucket)
{
    timer_t *head = s_mgr.timers[bucket];

    if (head) {
        head->prev->next = t;
        t->prev = head->prev;
    } else {
        head = s_mgr.timers[bucket] = t;
    }
    head->prev = t;
    t->next = head;
}

static timer_t *get(const oid_t &tid, int *bucket)
{
    timer_t *head = NULL;
    timer_t *t = NULL;

    for (int i = 0; i < MAX_WHEEL_SET_SIZE; ++i) {
        head = t = s_mgr.timers[i];
        while (t) {
            if (tid == t->id) {
                *bucket = i;
                return t;
            }
            if ((t = t->next) == head) break;
        }
    }
    *bucket = 0;
    return NULL;
}

static void destroy(timer_t *t)
{
    if (t) {
        S_DELETE(t);
    }
}

static int schedule(timer_t *t)
{
    int frame = FRAME_DIFFER(t->cursor.a, s_mgr.cursor.a);
    int bucket = 0;

    if (frame < WHEEL_SET_SIZE0) { // hash into wheel 0
        bucket = BUCKET_MAP(t->cursor, 0);
    } else if (WHEEL_CMP(t->cursor, 3)) { // hash into wheel 3
        bucket = BUCKET_MAP(t->cursor, 3);
    } else if (WHEEL_CMP(t->cursor, 2)) { // hash into wheel 2
        bucket = BUCKET_MAP(t->cursor, 2);
    } else if (WHEEL_CMP(t->cursor, 1)) { // hash into wheel 1
        bucket = BUCKET_MAP(t->cursor, 1);
    }
    push(t, bucket);
    return bucket;
}

static void expire(timer_t *t)
{
    ELF_ASSERT(t);
    if (t->script) {
        // script_func_exec(t->cb.script, 0);
    } else {
        t->cb.func(t->args);
    }
    destroy(t);
}

static void bingo(void)
{
    int bucket = 0;
    cursor_t next_cursor = s_mgr.cursor;

    ++next_cursor.a;
    if (WHEEL_CMP(next_cursor, 3)) {
        bucket = BUCKET_MAP(next_cursor, 3);
    } else if (WHEEL_CMP(next_cursor, 2)) {
        bucket = BUCKET_MAP(next_cursor, 2);
    } else if (WHEEL_CMP(next_cursor, 1)) {
        bucket = BUCKET_MAP(next_cursor, 1);
    }
    if (++s_mgr.cursor.a == 0) {
        ++s_mgr.round;
    }
    if (bucket > 0) { // hash to more precisely wheel
        hash(bucket);
    }
}

static void hash(int bucket)
{
    timer_t *head = s_mgr.timers[bucket];
    timer_t *t = head;

    while (t) {
        timer_t *n = t->next;

        schedule(t);
        if (n == head) {
            s_mgr.timers[bucket] = NULL;
            break;
        }
        t = n;
    }
}
static void timer_min(void *args)
{
    LOG_TRACE("timer", "%s", "BINGO: MIN.");

    time_t cur = elf::time_s();
    elf::time64_t tm_timer;
    struct tm tm_cur;

    localtime_r(&cur, &tm_cur);
    tm_timer = (60 - tm_cur.tm_sec) * 1000llu;
    timer_add(tm_timer, timer_min, NULL);

    handler_list::const_iterator itr = s_handlers.begin();

    for (; itr != s_handlers.end(); ++itr) {
        (*itr)(&cur);
    }
}
} // namespace elf

<commit_msg>[FIX] mimute timer.<commit_after>/*
 * Copyright (C) 2011-2013 Yule Fox. All rights reserved.
 * http://www.yulefox.com/
 */

#include <elf/log.h>
#include <elf/memory.h>
#include <elf/time.h>
#include <elf/timer.h>
#include <algorithm>
#include <list>

namespace elf {
#define WHEEL_SET_BIT0                  8
#define WHEEL_SET_BITS                  (WHEEL_SET_BIT0 * 4)
#define WHEEL_SET_BIT(l)                (WHEEL_SET_BIT0 * (l))
#define WHEEL_SET_SIZE0                 (1 << WHEEL_SET_BIT0)
#define WHEEL_SET_SIZE(l)               WHEEL_SET_SIZE0
#define MAX_WHEEL_SET_SIZE              (WHEEL_SET_SIZE0 * 4)
#define MAX_CURSOR                      (1LL << WHEEL_SET_BITS)
#define MAX_LIFE                        (MAX_CURSOR * s_mgr.interval)
#define FRAME_DIFFER(l, r)              (int)(l - r)

union cursor_t {
    struct wheel_t {
        unsigned char w0;
        unsigned char w1;
        unsigned char w2;
        unsigned char w3;
    };
    wheel_t w;
    int a;
};

struct timer_t {
    oid_t id; // identification
    time64_t life; // life from now
    cursor_t cursor; // expired frame
    bool script;
    union cb_t {
        char script[1024]; // script function name
        callback func; // callback function
    };
    cb_t cb; // callback function
    void *args; // callback arguments
    timer_t *next; // the next node of the tail node is NULL
    timer_t *prev; // the previous node of the head node is the tail
};

struct mgr_t {
    time64_t start_time; // start time
    time64_t end_time; // end time
    time64_t cur_time; // current frame time
    time64_t last_time; // last pause time
    time64_t interval; // frame interval time
    cursor_t cursor; // current cursor (number of escaped frames)
    int round; // current round
    int last_cursor; // last pause cursor
    int timer_total; // total number of timers
    int timer_remain; // remain timers
    int timer_passed; // total number of passed timers
    int timer_cancelled; // total number of cancelled timers
    bool pause; // suspend all timers
    timer_t *timers[MAX_WHEEL_SET_SIZE];
};

typedef std::list<callback> handler_list;

static handler_list s_handlers;

static const time64_t TIMER_FRAME_INTERVAL_DEFAULT = 50; // (ms)
static const time64_t TIMER_FRAME_INTERVAL_MIN = 1; // (frame)
static const time64_t TIMER_FRAME_INTERVAL_MAX = 1000; // (frame)
static mgr_t s_mgr;

/// calculate the number of life frames
#define FRAME_CALC(t) ((time64_t)(t) / s_mgr.interval)

// @{
/// get current bucket cursor at wheel 0
#define CURRENT_WHEEL_CURSOR    s_mgr.cursor.w.w0

/// compare given cursor with current bucket cursor at wheel @param l
#define WHEEL_CMP(cur, l)                                                   \
    ((int)cur.w.w##l - (int)s_mgr.cursor.w.w##l)

/// map cursor at wheel @param l
#define BUCKET_MAP(cur, l)                                                  \
    (cur.w.w##l + WHEEL_SET_SIZE0 * l)

/// calculate the number of frames elapsed from last cursor
#define FRAME_BINGO (s_mgr.last_cursor +                                    \
        FRAME_CALC(s_mgr.cur_time - s_mgr.last_time) - s_mgr.cursor.a)
// @}

static void reset(void);
static void push(timer_t *t, int bucket);
static timer_t *get(const oid_t &tid, int *bucket);
static void destroy(timer_t *t);
static int schedule(timer_t *t);
static void expire(timer_t *t);
static void bingo(void);
static void hash(int bucket);
static void timer_min(void *args);

int timer_init(void)
{
    MODULE_IMPORT_SWITCH;
    s_mgr.interval = TIMER_FRAME_INTERVAL_DEFAULT;
    s_mgr.start_time = s_mgr.last_time = time_ms();
    s_mgr.pause = true;
    s_mgr.cursor.a = 0;
    s_mgr.round = 0;
    memset(s_mgr.timers, 0, sizeof(s_mgr.timers[0]) * MAX_WHEEL_SET_SIZE);

    time_t ms = elf::time_ms() % 1000;
    time_t cur = elf::time_s();
    elf::time64_t tm_timer;
    struct tm tm_cur;

    localtime_r(&cur, &tm_cur);
    tm_timer = (60 - tm_cur.tm_sec) * 1000llu - ms;

    timer_add(tm_timer, timer_min, NULL);
    return 0;
}

int timer_fini(void)
{
    MODULE_IMPORT_SWITCH;
    s_mgr.end_time = time_ms();
    for (int i = 0; i < MAX_WHEEL_SET_SIZE; ++i) {
        timer_t *head = s_mgr.timers[i];
        timer_t *t = head;

        while (t) {
            timer_t *n = t->next;

            destroy(t);
            if (n == head) break;
            t = n;
        }
    }
    return 0;
}

void timer_run(void)
{
    if (s_mgr.pause || s_mgr.timer_remain <= 0) {
        return;
    }

    // frame may greater than 1 while CPU busying
    s_mgr.cur_time = time_ms();
    int frame = FRAME_BINGO;

    // time anomaly
    if (frame < 0) {
        LOG_WARN("timer", "(%u)%08X: %llu - %llu(%d).",
                s_mgr.round, s_mgr.cursor.a,
                s_mgr.cur_time, s_mgr.last_time,
                frame);
        assert(frame >= 0);
        return;
    }
    // while processors is busy
    if (frame > 1) {
        LOG_WARN("timer", "(%u)%08X: %llu - %llu(%d).",
                s_mgr.round, s_mgr.cursor.a,
                s_mgr.cur_time, s_mgr.last_time,
                frame);
    }
    for (int i = 1; i <= frame; ++i) { // BINGO!
        unsigned char bucket = CURRENT_WHEEL_CURSOR;
        timer_t *head = s_mgr.timers[bucket];
        timer_t *t = head;

        while (t) {
            timer_t *n = t->next;

            expire(t);
            ++s_mgr.timer_passed;
            --s_mgr.timer_remain;
            if (n == head) {
                s_mgr.timers[bucket] = NULL;
                break;
            }
            t = n;
        }
        bingo();
    }
}

void timer_stat(void)
{
    LOG_INFO("timer",
            "ST: %lld RND: %u FRM: %u TMT: %u TMP: %u TMC: %u.",
            s_mgr.start_time, s_mgr.round, s_mgr.cursor.a,
            s_mgr.timer_total, s_mgr.timer_passed, s_mgr.timer_cancelled);
}

int timer_size(void)
{
    return s_mgr.timer_remain;
}

const oid_t &timer_add(time64_t life, const char *func)
{
    if (life < 0 || life >= MAX_LIFE) {
        LOG_WARN("timer",
                "Timer added FAILED: invalid timer life(%lld) [0, %lld).",
                life, MAX_LIFE);
        return OID_NIL;
    }

    timer_t *t = E_NEW timer_t;

    memset(t, 0, sizeof(*t));
    t->id = oid_gen();
    t->life = std::max(life, s_mgr.interval);
    t->cursor.a = (FRAME_CALC(t->life) + s_mgr.cursor.a) % MAX_CURSOR;
    t->script = true;
    strcpy(t->cb.script, func);
    t->args = NULL;
    schedule(t);
    ++s_mgr.timer_total;
    ++s_mgr.timer_remain;
    return t->id;
}

const oid_t &timer_add(time64_t life, callback func, void *args)
{
    if (life < 0 || life >= MAX_LIFE) {
        LOG_WARN("timer",
                "Timer added FAILED: invalid timer life(%lld) [0, %lld).",
                life, MAX_LIFE);
        return OID_NIL;
    }

    timer_t *t = E_NEW timer_t;

    memset(t, 0, sizeof(*t));
    t->id = oid_gen();
    t->life = std::max(life, s_mgr.interval);
    t->cursor.a = (FRAME_CALC(t->life) + s_mgr.cursor.a) % MAX_CURSOR;
    t->script = false;
    t->cb.func = func;
    t->args = args;
    schedule(t);
    ++s_mgr.timer_total;
    ++s_mgr.timer_remain;
    return t->id;
}

void timer_cycle(callback func)
{
    s_handlers.push_back(func);
}

void timer_remove(const oid_t &tid)
{
    int bucket;
    timer_t *t = get(tid, &bucket);

    if (t) {
        timer_t *n = t->next;

        if (n != t) {
            t->prev->next = n;
            n->prev = t->prev;
        } else {
            n = NULL;
        }
        if (t == s_mgr.timers[bucket]) {
            s_mgr.timers[bucket] = n;
        }
        S_FREE(t->args);
        destroy(t);
        ++s_mgr.timer_cancelled;
        --s_mgr.timer_remain;
    }
}

void timer_pause(void)
{
    if (!s_mgr.pause) {
        s_mgr.pause = true;
    }
}

void timer_resume(void)
{
    if (s_mgr.pause) {
        s_mgr.pause = false;
        reset();
    }
}

void timer_pause(const oid_t &tid)
{
}

void timer_resume(const oid_t &tid)
{
}

void timer_interval(time64_t t)
{
    ELF_ASSERT(t >= TIMER_FRAME_INTERVAL_MIN && t <= TIMER_FRAME_INTERVAL_MAX);
    s_mgr.interval = t;
    reset();
}

void timer_bucket(unsigned char no, int l)
{
    switch (l) {
        case 0:
            s_mgr.cursor.w.w0 = no;
            break;
        case 1:
            s_mgr.cursor.w.w1 = no;
            break;
        case 2:
            s_mgr.cursor.w.w2 = no;
            break;
        case 3:
            s_mgr.cursor.w.w3 = no;
            break;
    }
    reset();
}

static void reset(void)
{
    s_mgr.last_time = time_ms();
    s_mgr.last_cursor = s_mgr.cursor.a;
}

static void push(timer_t *t, int bucket)
{
    timer_t *head = s_mgr.timers[bucket];

    if (head) {
        head->prev->next = t;
        t->prev = head->prev;
    } else {
        head = s_mgr.timers[bucket] = t;
    }
    head->prev = t;
    t->next = head;
}

static timer_t *get(const oid_t &tid, int *bucket)
{
    timer_t *head = NULL;
    timer_t *t = NULL;

    for (int i = 0; i < MAX_WHEEL_SET_SIZE; ++i) {
        head = t = s_mgr.timers[i];
        while (t) {
            if (tid == t->id) {
                *bucket = i;
                return t;
            }
            if ((t = t->next) == head) break;
        }
    }
    *bucket = 0;
    return NULL;
}

static void destroy(timer_t *t)
{
    if (t) {
        S_DELETE(t);
    }
}

static int schedule(timer_t *t)
{
    int frame = FRAME_DIFFER(t->cursor.a, s_mgr.cursor.a);
    int bucket = 0;

    if (frame < WHEEL_SET_SIZE0) { // hash into wheel 0
        bucket = BUCKET_MAP(t->cursor, 0);
    } else if (WHEEL_CMP(t->cursor, 3)) { // hash into wheel 3
        bucket = BUCKET_MAP(t->cursor, 3);
    } else if (WHEEL_CMP(t->cursor, 2)) { // hash into wheel 2
        bucket = BUCKET_MAP(t->cursor, 2);
    } else if (WHEEL_CMP(t->cursor, 1)) { // hash into wheel 1
        bucket = BUCKET_MAP(t->cursor, 1);
    }
    push(t, bucket);
    return bucket;
}

static void expire(timer_t *t)
{
    ELF_ASSERT(t);
    if (t->script) {
        // script_func_exec(t->cb.script, 0);
    } else {
        t->cb.func(t->args);
    }
    destroy(t);
}

static void bingo(void)
{
    int bucket = 0;
    cursor_t next_cursor = s_mgr.cursor;

    ++next_cursor.a;
    if (WHEEL_CMP(next_cursor, 3)) {
        bucket = BUCKET_MAP(next_cursor, 3);
    } else if (WHEEL_CMP(next_cursor, 2)) {
        bucket = BUCKET_MAP(next_cursor, 2);
    } else if (WHEEL_CMP(next_cursor, 1)) {
        bucket = BUCKET_MAP(next_cursor, 1);
    }
    if (++s_mgr.cursor.a == 0) {
        ++s_mgr.round;
    }
    if (bucket > 0) { // hash to more precisely wheel
        hash(bucket);
    }
}

static void hash(int bucket)
{
    timer_t *head = s_mgr.timers[bucket];
    timer_t *t = head;

    while (t) {
        timer_t *n = t->next;

        schedule(t);
        if (n == head) {
            s_mgr.timers[bucket] = NULL;
            break;
        }
        t = n;
    }
}
static void timer_min(void *args)
{
    LOG_TRACE("timer", "%s", "BINGO: MIN.");

    time_t cur = elf::time_s();
    elf::time64_t tm_timer;
    struct tm tm_cur;

    localtime_r(&cur, &tm_cur);
    tm_timer = (60 - tm_cur.tm_sec) * 1000llu;
    timer_add(tm_timer, timer_min, NULL);

    handler_list::const_iterator itr = s_handlers.begin();

    for (; itr != s_handlers.end(); ++itr) {
        (*itr)(&cur);
    }
}
} // namespace elf

<|endoftext|>