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

#include "cmdoptions.hpp"

#include <primecount.hpp>
#include <primecount-internal.hpp>
#include <imath.hpp>
#include <int128_t.hpp>
#include <PhiTiny.hpp>
#include <S1.hpp>
#include <S2.hpp>
#include <json.hpp>

#include <stdint.h>
#include <stdio.h>
#include <time.h>

#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <string>

#ifdef HAVE_MPI
  #include <mpi.h>
#endif

using namespace std;
using namespace primecount;

namespace {

// Get current date & time
// http://www.cplusplus.com/reference/ctime/strftime
const string dateTime()
{
  time_t rawtime;
  struct tm* timeinfo;
  char buffer[80];

  time(&rawtime);
  timeinfo = localtime(&rawtime);
  strftime(buffer, sizeof(buffer), "%Y-%m-%d %X", timeinfo);

  return buffer;
}

string basename(string path)
{
  while (path.back() == '/' ||
         path.back() == '\\')
  {
    path.pop_back();
  }

  size_t pos = path.find_last_of("/\\");

  if (pos != string::npos)
    return path.substr(pos + 1);

  return path;
}

void result_txt(int argc,
                char* argv[],
                maxint_t res,
                int threads,
                double seconds)
{
  ofstream resfile("results.txt", ofstream::out | ofstream::app);

  if (resfile.is_open())
  {
    resfile << basename(argv[0]);

    for (int i = 1; i < argc; i++)
      resfile << " " << argv[i];

    resfile << endl;
    resfile << "Result: " << res << endl;
    resfile << "Threads: " << threads << endl;
    resfile << "Seconds: " << fixed << setprecision(3) << seconds << endl;
    resfile << "Date: " << dateTime() << endl << endl;
  }
}

void result_log(maxint_t res, double seconds)
{
  ofstream logfile("primecount.log", ofstream::out | ofstream::app);

  if (logfile.is_open())
  {
    logfile << endl << res << endl;
    logfile << "Seconds: " << fixed << setprecision(3) << seconds << endl;
  }
}

} // namespace

namespace primecount {

int64_t to_int64(maxint_t x)
{
  if (x > numeric_limits<int64_t>::max())
    throw primecount_error("x must be < 2^63");
  return (int64_t) x;
}

maxint_t P2(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("P2(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);

  if (x <= numeric_limits<int64_t>::max())
    return P2((int64_t) x, y, threads);
  else
    return P2(x, y, threads);
}

maxint_t S1(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S1(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S1((int64_t) x, y, c, threads);
  else
    return S1(x, y, c, threads);
}

maxint_t S2_trivial(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S2_trivial(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t z = (int64_t) (x / y);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S2_trivial((int64_t) x, y, z, c, threads);
  else
    return S2_trivial(x, y, z, c, threads);
}

maxint_t S2_easy(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S2_easy(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t z = (int64_t) (x / y);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S2_easy((int64_t) x, y, z, c, threads);
  else
    return S2_easy(x, y, z, c, threads);
}

maxint_t S2_hard(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S2_hard(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t z = (int64_t) (x / y);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S2_hard((int64_t) x, y, z, c, (int64_t) Ri(x), threads);
  else
    return S2_hard(x, y, z, c, Ri(x), threads);
}

} // namespace

int main (int argc, char* argv[])
{
#ifdef HAVE_MPI
  MPI_Init(&argc, &argv);
#endif

  try
  {
    CmdOptions opt = parseOptions(argc, argv);

    if (!opt.is_resume())
      backup_command(argc, argv);
    else
    {
      vector<string> args = get_backup_command();
      vector<char*> cargs;

      for (string& s : args)
        cargs.push_back((char*) s.c_str());

      opt = parseOptions((int) cargs.size(), cargs.data());
    }

    double time = get_wtime();

    auto x = opt.x;
    auto a = opt.a;
    auto threads = opt.threads;
    maxint_t res = 0;

    switch (opt.option)
    {
      case OPTION_DELEGLISE_RIVAT:
      {
#ifdef HAVE_INT128_T
        if (x > numeric_limits<int64_t>::max())
          res = pi_deleglise_rivat_parallel2(x, threads, &time);
        else
#endif
        res = pi_deleglise_rivat_parallel1(to_int64(x), threads, &time);
        break;
      }
      case OPTION_DELEGLISE_RIVAT1:
        res = pi_deleglise_rivat1(to_int64(x)); break;
      case OPTION_DELEGLISE_RIVAT2:
        res = pi_deleglise_rivat2(to_int64(x)); break;
      case OPTION_DELEGLISE_RIVAT_PARALLEL1:
        res = pi_deleglise_rivat_parallel1(to_int64(x), threads, &time); break;
      case OPTION_LEGENDRE:
        res = pi_legendre(to_int64(x), threads); break;
      case OPTION_LEHMER:
        res = pi_lehmer(to_int64(x), threads); break;
      case OPTION_LMO:
        res = pi_lmo(to_int64(x), threads); break;
      case OPTION_LMO1:
        res = pi_lmo1(to_int64(x)); break;
      case OPTION_LMO2:
        res = pi_lmo2(to_int64(x)); break;
      case OPTION_LMO3:
        res = pi_lmo3(to_int64(x)); break;
      case OPTION_LMO4:
        res = pi_lmo4(to_int64(x)); break;
      case OPTION_LMO5:
        res = pi_lmo5(to_int64(x)); break;
      case OPTION_LMO_PARALLEL:
        res = pi_lmo_parallel(to_int64(x), threads); break;
      case OPTION_MEISSEL:
        res = pi_meissel(to_int64(x), threads); break;
      case OPTION_PRIMESIEVE:
        res = pi_primesieve(to_int64(x), threads); break;
      case OPTION_P2:
        res = P2(x, threads); break;
      case OPTION_PHI:
        res = phi(to_int64(x), a, threads); break;
      case OPTION_PI:
        res = pi(x, threads); break;
      case OPTION_LI:
        res = Li(x); break;
      case OPTION_LIINV:
        res = Li_inverse(x); break;
      case OPTION_RI:
        res = Ri(x); break;
      case OPTION_RIINV:
        res = Ri_inverse(x); break;
      case OPTION_NTHPRIME:
        res = nth_prime(to_int64(x), threads); break;
      case OPTION_S1:
        res = S1(x, threads); break;
      case OPTION_S2_EASY:
        res = S2_easy(x, threads); break;
      case OPTION_S2_HARD:
        res = S2_hard(x, threads); break;
      case OPTION_S2_TRIVIAL:
        res = S2_trivial(x, threads); break;
#ifdef HAVE_INT128_T
      case OPTION_DELEGLISE_RIVAT_PARALLEL2:
        res = pi_deleglise_rivat_parallel2(x, threads, &time); break;
#endif
    }

    double seconds = get_wtime() - time;
    result_txt(argc, argv, res, threads, seconds);

    if (print_result())
    {
      if (is_print())
        cout << endl;

      cout << res << endl;

      if (opt.time)
        cout << "Seconds: " << fixed << setprecision(3) << seconds << endl;

      result_log(res, seconds);
    }
  }
  catch (exception& e)
  {
#ifdef HAVE_MPI
    MPI_Finalize();
#endif
    cerr << "primecount: " << e.what() << endl
         << "Try 'primecount --help' for more information." << endl;
    return 1;
  }

#ifdef HAVE_MPI
    MPI_Finalize();
#endif

  return 0;
}
<commit_msg>Add log_footer()<commit_after>///
/// @file   main.cpp
/// @brief  primecount console application
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include "cmdoptions.hpp"

#include <primecount.hpp>
#include <primecount-internal.hpp>
#include <imath.hpp>
#include <int128_t.hpp>
#include <PhiTiny.hpp>
#include <S1.hpp>
#include <S2.hpp>
#include <json.hpp>

#include <stdint.h>
#include <stdio.h>
#include <time.h>

#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <string>

#ifdef HAVE_MPI
  #include <mpi.h>
#endif

using namespace std;
using namespace primecount;

namespace {

// Get current date & time
// http://www.cplusplus.com/reference/ctime/strftime
const string dateTime()
{
  time_t rawtime;
  struct tm* timeinfo;
  char buffer[80];

  time(&rawtime);
  timeinfo = localtime(&rawtime);
  strftime(buffer, sizeof(buffer), "%Y-%m-%d %X", timeinfo);

  return buffer;
}

string basename(string path)
{
  while (path.back() == '/' ||
         path.back() == '\\')
  {
    path.pop_back();
  }

  size_t pos = path.find_last_of("/\\");

  if (pos != string::npos)
    return path.substr(pos + 1);

  return path;
}

void result_txt(int argc,
                char* argv[],
                maxint_t res,
                int threads,
                double seconds)
{
  ofstream resfile("results.txt", ofstream::out | ofstream::app);

  if (resfile.is_open())
  {
    resfile << basename(argv[0]);

    for (int i = 1; i < argc; i++)
      resfile << " " << argv[i];

    resfile << endl;
    resfile << "Result: " << res << endl;
    resfile << "Threads: " << threads << endl;
    resfile << "Seconds: " << fixed << setprecision(3) << seconds << endl;
    resfile << "Date: " << dateTime() << endl << endl;
  }
}

void log_footer()
{
  ofstream logfile("primecount.log", ofstream::out | ofstream::app);

  if (logfile.is_open())
    logfile << endl << string(60, '=') << endl;
}

void log_result(maxint_t res, double seconds)
{
  ofstream logfile("primecount.log", ofstream::out | ofstream::app);

  if (logfile.is_open())
  {
    logfile << endl << res << endl;
    logfile << "Seconds: " << fixed << setprecision(3) << seconds << endl;
  }
}

} // namespace

namespace primecount {

int64_t to_int64(maxint_t x)
{
  if (x > numeric_limits<int64_t>::max())
    throw primecount_error("x must be < 2^63");
  return (int64_t) x;
}

maxint_t P2(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("P2(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);

  if (x <= numeric_limits<int64_t>::max())
    return P2((int64_t) x, y, threads);
  else
    return P2(x, y, threads);
}

maxint_t S1(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S1(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S1((int64_t) x, y, c, threads);
  else
    return S1(x, y, c, threads);
}

maxint_t S2_trivial(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S2_trivial(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t z = (int64_t) (x / y);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S2_trivial((int64_t) x, y, z, c, threads);
  else
    return S2_trivial(x, y, z, c, threads);
}

maxint_t S2_easy(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S2_easy(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t z = (int64_t) (x / y);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S2_easy((int64_t) x, y, z, c, threads);
  else
    return S2_easy(x, y, z, c, threads);
}

maxint_t S2_hard(maxint_t x, int threads)
{
  if (x < 1)
    return 0;

  double alpha = get_alpha_deleglise_rivat(x);
  string limit = get_max_x(alpha);

  if (x > to_maxint(limit))
    throw primecount_error("S2_hard(x): x must be <= " + limit);

  if (is_print())
    set_print_variables(true);

  int64_t y = (int64_t) (iroot<3>(x) * alpha);
  int64_t z = (int64_t) (x / y);
  int64_t c = PhiTiny::get_c(y);

  if (x <= numeric_limits<int64_t>::max())
    return S2_hard((int64_t) x, y, z, c, (int64_t) Ri(x), threads);
  else
    return S2_hard(x, y, z, c, Ri(x), threads);
}

} // namespace

int main (int argc, char* argv[])
{
#ifdef HAVE_MPI
  MPI_Init(&argc, &argv);
#endif

  try
  {
    CmdOptions opt = parseOptions(argc, argv);

    if (!opt.is_resume())
      backup_command(argc, argv);
    else
    {
      vector<string> args = get_backup_command();
      vector<char*> cargs;

      for (string& s : args)
        cargs.push_back((char*) s.c_str());

      opt = parseOptions((int) cargs.size(), cargs.data());
    }

    double time = get_wtime();

    auto x = opt.x;
    auto a = opt.a;
    auto threads = opt.threads;
    maxint_t res = 0;

    switch (opt.option)
    {
      case OPTION_DELEGLISE_RIVAT:
      {
#ifdef HAVE_INT128_T
        if (x > numeric_limits<int64_t>::max())
          res = pi_deleglise_rivat_parallel2(x, threads, &time);
        else
#endif
        res = pi_deleglise_rivat_parallel1(to_int64(x), threads, &time);
        break;
      }
      case OPTION_DELEGLISE_RIVAT1:
        res = pi_deleglise_rivat1(to_int64(x)); break;
      case OPTION_DELEGLISE_RIVAT2:
        res = pi_deleglise_rivat2(to_int64(x)); break;
      case OPTION_DELEGLISE_RIVAT_PARALLEL1:
        res = pi_deleglise_rivat_parallel1(to_int64(x), threads, &time); break;
      case OPTION_LEGENDRE:
        res = pi_legendre(to_int64(x), threads); break;
      case OPTION_LEHMER:
        res = pi_lehmer(to_int64(x), threads); break;
      case OPTION_LMO:
        res = pi_lmo(to_int64(x), threads); break;
      case OPTION_LMO1:
        res = pi_lmo1(to_int64(x)); break;
      case OPTION_LMO2:
        res = pi_lmo2(to_int64(x)); break;
      case OPTION_LMO3:
        res = pi_lmo3(to_int64(x)); break;
      case OPTION_LMO4:
        res = pi_lmo4(to_int64(x)); break;
      case OPTION_LMO5:
        res = pi_lmo5(to_int64(x)); break;
      case OPTION_LMO_PARALLEL:
        res = pi_lmo_parallel(to_int64(x), threads); break;
      case OPTION_MEISSEL:
        res = pi_meissel(to_int64(x), threads); break;
      case OPTION_PRIMESIEVE:
        res = pi_primesieve(to_int64(x), threads); break;
      case OPTION_P2:
        res = P2(x, threads); break;
      case OPTION_PHI:
        res = phi(to_int64(x), a, threads); break;
      case OPTION_PI:
        res = pi(x, threads); break;
      case OPTION_LI:
        res = Li(x); break;
      case OPTION_LIINV:
        res = Li_inverse(x); break;
      case OPTION_RI:
        res = Ri(x); break;
      case OPTION_RIINV:
        res = Ri_inverse(x); break;
      case OPTION_NTHPRIME:
        res = nth_prime(to_int64(x), threads); break;
      case OPTION_S1:
        res = S1(x, threads); break;
      case OPTION_S2_EASY:
        res = S2_easy(x, threads); break;
      case OPTION_S2_HARD:
        res = S2_hard(x, threads); break;
      case OPTION_S2_TRIVIAL:
        res = S2_trivial(x, threads); break;
#ifdef HAVE_INT128_T
      case OPTION_DELEGLISE_RIVAT_PARALLEL2:
        res = pi_deleglise_rivat_parallel2(x, threads, &time); break;
#endif
    }

    double seconds = get_wtime() - time;
    result_txt(argc, argv, res, threads, seconds);

    if (print_result())
    {
      log_result(res, seconds);

      if (is_print())
        cout << endl;

      cout << res << endl;

      if (opt.time)
        cout << "Seconds: " << fixed << setprecision(3) << seconds << endl;
    }

    log_footer();
  }
  catch (exception& e)
  {
#ifdef HAVE_MPI
    MPI_Finalize();
#endif
    cerr << "primecount: " << e.what() << endl
         << "Try 'primecount --help' for more information." << endl;
    return 1;
  }

#ifdef HAVE_MPI
    MPI_Finalize();
#endif

  return 0;
}
<|endoftext|>


<commit_before><commit_msg>Macro to generate J/psi from B events (to be used with MCGen lego trains)<commit_after><|endoftext|>


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

#include "EratBig.h"
#include "SieveOfEratosthenes.h"
#include "WheelFactorization.h"
#include "defs.h"
#include "bithacks.h"

#include <stdexcept>
#include <cstdlib>
#include <list>

EratBig::EratBig(const SieveOfEratosthenes& soe) :
  Modulo210Wheel(soe),
  lists_(NULL),
  stock_(NULL),
  index_(0),
  log2SieveSize_(floorLog2(soe.getSieveSize()))
{
  // EratBig uses bitwise operations that require a power of 2 sieve size
  if (!isPowerOf2(soe.getSieveSize()))
    throw std::invalid_argument(
        "EratBig: sieveSize must be a power of 2 (2^n).");
  this->setSize(soe);
  this->initBucketLists();
}

EratBig::~EratBig() {
  delete[] lists_;
  for (std::list<Bucket_t*>::iterator it = pointers_.begin();
      it != pointers_.end(); ++it)
    delete[] *it;
}

/**
 * Set the size of the lists_ array.
 * @remark The size is a power of 2 value which allows use of fast
 *         bitwise operators in sieve(uint8_t*).
 */
void EratBig::setSize(const SieveOfEratosthenes& soe) {
  // MAX values in sieve(uint8_t*)
  uint32_t maxSievingPrime = soe.getSquareRoot() / (SieveOfEratosthenes::NUMBERS_PER_BYTE / 2);
  uint32_t maxWheelFactor  = wheel_[1].nextMultipleFactor;
  uint32_t maxSieveIndex   = (soe.getSieveSize() - 1) + maxSievingPrime * maxWheelFactor;
  uint32_t maxSegmentCount = maxSieveIndex / soe.getSieveSize();
  // 'maxSegmentCount + 1' is the smallest possible
  // size for the lists_ array
  size_ = nextHighestPowerOf2(maxSegmentCount + 1);
}

/**
 * Allocate the lists_ array and initialize each list
 * with an empty bucket.
 */
void EratBig::initBucketLists() {
  lists_ = new Bucket_t*[size_];
  for (uint32_t i = 0; i < size_; i++) {
    lists_[i] = NULL;
    this->pushBucket(i);
  }
}

/**
 * Add a prime number <= sqrt(n) for sieving to EratBig.
 */
void EratBig::addSievingPrime(uint32_t prime) {
  uint32_t sieveIndex;
  uint32_t wheelIndex;
  if (this->getWheelPrimeData(&prime, &sieveIndex, &wheelIndex) == true) {
    // indicates in how many segments the next multiple
    // of prime needs to be crossed-off
    uint32_t segmentCount = sieveIndex >> log2SieveSize_;
    sieveIndex &= (1U << log2SieveSize_) - 1;
    // calculate the list index related to the next multiple of prime
    uint32_t next = (index_ + segmentCount) & (size_ - 1);
    // add prime to the bucket list related to
    // its next multiple occurrence
    if (!lists_[next]->addWheelPrime(prime, sieveIndex, wheelIndex))
      this->pushBucket(next);
  }
}

/**
 * Add an empty bucket from the bucket stock_ to the
 * front of lists_[index], if the bucket stock_ is
 * empty new buckets are allocated first.
 */
void EratBig::pushBucket(uint32_t index) {
  if (stock_ == NULL) {
    Bucket_t* more = new Bucket_t[BUCKETS_PER_ALLOC];
    stock_ = &more[0];
    pointers_.push_back(more);
    for(int i = 0; i < BUCKETS_PER_ALLOC - 1; i++)
      more[i].setNext(&more[i + 1]);
    more[BUCKETS_PER_ALLOC - 1].setNext(NULL);
  }
  Bucket_t* bucket = stock_;
  stock_ = stock_->next();
  bucket->setNext(lists_[index]);
  lists_[index] = bucket;
}

/**
 * Implementation of Tomas Oliveira e Silva's cache-friendly segmented
 * sieve of Eratosthenes algorithm, see:
 * http://www.ieeta.pt/~tos/software/prime_sieve.html
 * My implementation uses a sieve array with 30 numbers per byte,
 * 8 bytes per sieving prime and a modulo 210 wheel that skips
 * multiples of 2, 3, 5 and 7.
 *
 * Removes the multiples of big sieving primes (i.e. with very few
 * multiple occurrences per segment) from the sieve array.
 * @see SieveOfEratosthenes::crossOffMultiples()
 */
void EratBig::sieve(uint8_t* sieve) {
  // get the bucket list related to the current segment,
  // the list contains the sieving primes with multiple occurence(s)
  // in the current segment
  Bucket_t*& list = lists_[index_];

  // process the buckets within list until all multiples of
  // its sieving primes have been crossed-off
  while (!list->isEmpty() || list->hasNext()) {
    Bucket_t* bucket = list;
    list = NULL;
    this->pushBucket(index_);

    // each loop iteration processes a bucket i.e. removes the
    // next multiple of its sieving primes
    do {
      const uint32_t      count  = bucket->getCount();
      const WheelPrime_t* wPrime = bucket->getWheelPrimes();
      const WheelPrime_t* end    = &wPrime[count - count % 2];

      // iterate over the sieving primes within the current bucket
      // loop unrolled 2 times, optimized for x64 CPUs
      for (; wPrime < end; wPrime += 2) {
        uint32_t sieveIndex0   = wPrime[0].getSieveIndex();
        uint32_t wheelIndex0   = wPrime[0].getWheelIndex();
        uint32_t sievingPrime0 = wPrime[0].getSievingPrime();
        uint32_t sieveIndex1   = wPrime[1].getSieveIndex();
        uint32_t wheelIndex1   = wPrime[1].getWheelIndex();
        uint32_t sievingPrime1 = wPrime[1].getSievingPrime();

        // cross-off the next multiple (unset corresponding bit) of the
        // current sieving primes within the sieve array
        sieve[sieveIndex0] &= wheel_[wheelIndex0].unsetBit;
        sieveIndex0        += wheel_[wheelIndex0].nextMultipleFactor * sievingPrime0;
        sieveIndex0        += wheel_[wheelIndex0].correct;
        wheelIndex0        += wheel_[wheelIndex0].next;
        sieve[sieveIndex1] &= wheel_[wheelIndex1].unsetBit;
        sieveIndex1        += wheel_[wheelIndex1].nextMultipleFactor * sievingPrime1;
        sieveIndex1        += wheel_[wheelIndex1].correct;
        wheelIndex1        += wheel_[wheelIndex1].next;

        uint32_t next0 = (index_ + (sieveIndex0 >> log2SieveSize_)) & (size_ - 1);
        sieveIndex0 &= (1U << log2SieveSize_) - 1;
        uint32_t next1 = (index_ + (sieveIndex1 >> log2SieveSize_)) & (size_ - 1);
        sieveIndex1 &= (1U << log2SieveSize_) - 1;

        // move the current sieving primes to the bucket list
        // related to their next multiple occurrence
        if (!lists_[next0]->addWheelPrime(sievingPrime0, sieveIndex0, wheelIndex0))
          this->pushBucket(next0);
        if (!lists_[next1]->addWheelPrime(sievingPrime1, sieveIndex1, wheelIndex1))
          this->pushBucket(next1);
      }

      // process the remaining sieving primes
      for (; wPrime < &end[count % 2]; wPrime++) {
        uint32_t sieveIndex   = wPrime->getSieveIndex();
        uint32_t wheelIndex   = wPrime->getWheelIndex();
        uint32_t sievingPrime = wPrime->getSievingPrime();
        sieve[sieveIndex] &= wheel_[wheelIndex].unsetBit;
        sieveIndex += wheel_[wheelIndex].nextMultipleFactor * sievingPrime;
        sieveIndex += wheel_[wheelIndex].correct;
        wheelIndex += wheel_[wheelIndex].next;
        uint32_t next = (index_ + (sieveIndex >> log2SieveSize_)) & (size_ - 1);
        sieveIndex &= (1U << log2SieveSize_) - 1;
        if (!lists_[next]->addWheelPrime(sievingPrime, sieveIndex, wheelIndex))
          this->pushBucket(next);
      }

      // reset the processed bucket and move it to the bucket stock_
      Bucket_t* old = bucket;
      bucket = bucket->next();
      old->reset();
      old->setNext(stock_);
      stock_ = old;
    }
    while (bucket != NULL);
  }

  // increase the list index_ for the next segment
  index_ = (index_ + 1) & (size_ - 1);
}
<commit_msg>added/modified Bucket::begin(), Bucket::end() functions<commit_after>//
// Copyright (c) 2011 Kim Walisch, <kim.walisch@gmail.com>.
// All rights reserved.
//
// This file is part of primesieve.
// Visit: http://primesieve.googlecode.com
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above
//     copyright notice, this list of conditions and the following
//     disclaimer in the documentation and/or other materials provided
//     with the distribution.
//   * Neither the name of the author nor the names of its
//     contributors may be used to endorse or promote products derived
//     from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#include "EratBig.h"
#include "SieveOfEratosthenes.h"
#include "WheelFactorization.h"
#include "defs.h"
#include "bithacks.h"

#include <stdexcept>
#include <cstdlib>
#include <list>

EratBig::EratBig(const SieveOfEratosthenes& soe) :
  Modulo210Wheel(soe),
  lists_(NULL),
  stock_(NULL),
  index_(0),
  log2SieveSize_(floorLog2(soe.getSieveSize()))
{
  // EratBig uses bitwise operations that require a power of 2 sieve size
  if (!isPowerOf2(soe.getSieveSize()))
    throw std::invalid_argument(
        "EratBig: sieveSize must be a power of 2 (2^n).");
  this->setSize(soe);
  this->initBucketLists();
}

EratBig::~EratBig() {
  delete[] lists_;
  for (std::list<Bucket_t*>::iterator it = pointers_.begin();
      it != pointers_.end(); ++it)
    delete[] *it;
}

/**
 * Set the size of the lists_ array.
 * @remark The size is a power of 2 value which allows use of fast
 *         bitwise operators in sieve(uint8_t*).
 */
void EratBig::setSize(const SieveOfEratosthenes& soe) {
  // MAX values in sieve(uint8_t*)
  uint32_t maxSievingPrime = soe.getSquareRoot() / (SieveOfEratosthenes::NUMBERS_PER_BYTE / 2);
  uint32_t maxWheelFactor  = wheel_[1].nextMultipleFactor;
  uint32_t maxSieveIndex   = (soe.getSieveSize() - 1) + maxSievingPrime * maxWheelFactor;
  uint32_t maxSegmentCount = maxSieveIndex / soe.getSieveSize();
  // 'maxSegmentCount + 1' is the smallest possible
  // size for the lists_ array
  size_ = nextHighestPowerOf2(maxSegmentCount + 1);
}

/**
 * Allocate the lists_ array and initialize each list
 * with an empty bucket.
 */
void EratBig::initBucketLists() {
  lists_ = new Bucket_t*[size_];
  for (uint32_t i = 0; i < size_; i++) {
    lists_[i] = NULL;
    this->pushBucket(i);
  }
}

/**
 * Add a prime number <= sqrt(n) for sieving to EratBig.
 */
void EratBig::addSievingPrime(uint32_t prime) {
  uint32_t sieveIndex;
  uint32_t wheelIndex;
  if (this->getWheelPrimeData(&prime, &sieveIndex, &wheelIndex) == true) {
    // indicates in how many segments the next multiple
    // of prime needs to be crossed-off
    uint32_t segmentCount = sieveIndex >> log2SieveSize_;
    sieveIndex &= (1U << log2SieveSize_) - 1;
    // calculate the list index related to the next multiple of prime
    uint32_t next = (index_ + segmentCount) & (size_ - 1);
    // add prime to the bucket list related to
    // its next multiple occurrence
    if (!lists_[next]->addWheelPrime(prime, sieveIndex, wheelIndex))
      this->pushBucket(next);
  }
}

/**
 * Add an empty bucket from the bucket stock_ to the
 * front of lists_[index], if the bucket stock_ is
 * empty new buckets are allocated first.
 */
void EratBig::pushBucket(uint32_t index) {
  if (stock_ == NULL) {
    Bucket_t* more = new Bucket_t[BUCKETS_PER_ALLOC];
    stock_ = &more[0];
    pointers_.push_back(more);
    for(int i = 0; i < BUCKETS_PER_ALLOC - 1; i++)
      more[i].setNext(&more[i + 1]);
    more[BUCKETS_PER_ALLOC - 1].setNext(NULL);
  }
  Bucket_t* bucket = stock_;
  stock_ = stock_->next();
  bucket->setNext(lists_[index]);
  lists_[index] = bucket;
}

/**
 * Implementation of Tomas Oliveira e Silva's cache-friendly segmented
 * sieve of Eratosthenes algorithm, see:
 * http://www.ieeta.pt/~tos/software/prime_sieve.html
 * My implementation uses a sieve array with 30 numbers per byte,
 * 8 bytes per sieving prime and a modulo 210 wheel that skips
 * multiples of 2, 3, 5 and 7.
 *
 * Removes the multiples of big sieving primes (i.e. with very few
 * multiple occurrences per segment) from the sieve array.
 * @see SieveOfEratosthenes::crossOffMultiples()
 */
void EratBig::sieve(uint8_t* sieve) {
  // get the bucket list related to the current segment,
  // the list contains the sieving primes with multiple occurence(s)
  // in the current segment
  Bucket_t*& list = lists_[index_];

  // process the buckets within list until all multiples of
  // its sieving primes have been crossed-off
  while (!list->isEmpty() || list->hasNext()) {
    Bucket_t* bucket = list;
    list = NULL;
    this->pushBucket(index_);

    // each loop iteration processes a bucket i.e. removes the
    // next multiple of its sieving primes
    do {
      const WheelPrime_t* wPrime = bucket->begin();
      const WheelPrime_t* end    = bucket->end();

      // iterate over the sieving primes within the current bucket
      // loop unrolled 2 times, optimized for x64 CPUs
      for (; &wPrime[2] <= end; wPrime += 2) {
        uint32_t sieveIndex0   = wPrime[0].getSieveIndex();
        uint32_t wheelIndex0   = wPrime[0].getWheelIndex();
        uint32_t sievingPrime0 = wPrime[0].getSievingPrime();
        uint32_t sieveIndex1   = wPrime[1].getSieveIndex();
        uint32_t wheelIndex1   = wPrime[1].getWheelIndex();
        uint32_t sievingPrime1 = wPrime[1].getSievingPrime();

        // cross-off the next multiple (unset corresponding bit) of the
        // current sieving primes within the sieve array
        sieve[sieveIndex0] &= wheel_[wheelIndex0].unsetBit;
        sieveIndex0        += wheel_[wheelIndex0].nextMultipleFactor * sievingPrime0;
        sieveIndex0        += wheel_[wheelIndex0].correct;
        wheelIndex0        += wheel_[wheelIndex0].next;
        sieve[sieveIndex1] &= wheel_[wheelIndex1].unsetBit;
        sieveIndex1        += wheel_[wheelIndex1].nextMultipleFactor * sievingPrime1;
        sieveIndex1        += wheel_[wheelIndex1].correct;
        wheelIndex1        += wheel_[wheelIndex1].next;

        uint32_t next0 = (index_ + (sieveIndex0 >> log2SieveSize_)) & (size_ - 1);
        sieveIndex0 &= (1U << log2SieveSize_) - 1;
        uint32_t next1 = (index_ + (sieveIndex1 >> log2SieveSize_)) & (size_ - 1);
        sieveIndex1 &= (1U << log2SieveSize_) - 1;

        // move the current sieving primes to the bucket list
        // related to their next multiple occurrence
        if (!lists_[next0]->addWheelPrime(sievingPrime0, sieveIndex0, wheelIndex0))
          this->pushBucket(next0);
        if (!lists_[next1]->addWheelPrime(sievingPrime1, sieveIndex1, wheelIndex1))
          this->pushBucket(next1);
      }

      // process the remaining sieving primes
      for (; wPrime < end; wPrime++) {
        uint32_t sieveIndex   = wPrime->getSieveIndex();
        uint32_t wheelIndex   = wPrime->getWheelIndex();
        uint32_t sievingPrime = wPrime->getSievingPrime();
        sieve[sieveIndex] &= wheel_[wheelIndex].unsetBit;
        sieveIndex += wheel_[wheelIndex].nextMultipleFactor * sievingPrime;
        sieveIndex += wheel_[wheelIndex].correct;
        wheelIndex += wheel_[wheelIndex].next;
        uint32_t next = (index_ + (sieveIndex >> log2SieveSize_)) & (size_ - 1);
        sieveIndex &= (1U << log2SieveSize_) - 1;
        if (!lists_[next]->addWheelPrime(sievingPrime, sieveIndex, wheelIndex))
          this->pushBucket(next);
      }

      // reset the processed bucket and move it to the bucket stock_
      Bucket_t* old = bucket;
      bucket = bucket->next();
      old->reset();
      old->setNext(stock_);
      stock_ = old;
    }
    while (bucket != NULL);
  }

  // increase the list index_ for the next segment
  index_ = (index_ + 1) & (size_ - 1);
}
<|endoftext|>


<commit_before>/*
 * FogLAMP PI Server north plugin.
 *
 * Copyright (c) 2018 Dianomic Systems
 *
 * Released under the Apache 2.0 Licence
 *
 * Author: Massimiliano Pinto
 */
#include <plugin_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string>
#include <logger.h>
#include <plugin_exception.h>
#include <iostream>
#include <omf.h>
#include <simple_https.h>
#include <config_category.h>
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"

using namespace std;
using namespace rapidjson;

#define PLUGIN_NAME "PI_Server_V2"
#define TYPE_ID_KEY "type-id"
#define TYPE_ID_DEFAULT "1"

/**
 * Plugin specific default configuration
 */
#define PLUGIN_DEFAULT_CONFIG \
			"\"URL\": { " \
				"\"description\": \"The URL of the PI Connector to send data to\", " \
				"\"type\": \"string\", " \
				"\"default\": \"https://pi-server:5460/ingress/messages\", " \
				"\"order\": \"1\" }, " \
			"\"producerToken\": { " \
				"\"description\": \"The producer token that represents this FogLAMP stream\", " \
				"\"type\": \"string\", \"default\": \"omf_north_0001\", " \
				"\"order\": \"2\" }, " \
			"\"source\": {" \
				"\"description\": \"Defines the source of the data to be sent on the stream, " \
				"this may be one of either readings, statistics or audit.\", \"type\": \"enumeration\", " \
				"\"default\": \"readings\", "\
				"\"options\": [\"readings\", \"statistics\"], " \
				"\"order\": \"3\"  }, " \
			"\"StaticData\": { " \
				"\"description\": \"Static data to include in each sensor reading sent to the PI Server.\", " \
				"\"type\": \"string\", \"default\": \"Location: Palo Alto, Company: Dianomic\", " \
				"\"order\": \"4\" }, " \
			"\"OMFRetrySleepTime\": { " \
        			"\"description\": \"Seconds between each retry for the communication with the OMF PI Connector Relay, " \
                       		"NOTE : the time is doubled at each attempt.\", \"type\": \"integer\", \"default\": \"1\", " \
				"\"order\": \"9\" }, " \
			"\"OMFMaxRetry\": { " \
				"\"description\": \"Max number of retries for the communication with the OMF PI Connector Relay\", " \
				"\"type\": \"integer\", \"default\": \"3\", " \
				"\"order\": \"10\" }, " \
			"\"OMFHttpTimeout\": { " \
				"\"description\": \"Timeout in seconds for the HTTP operations with the OMF PI Connector Relay\", " \
				"\"type\": \"integer\", \"default\": \"10\", " \
				"\"order\": \"13\" }, " \
			"\"formatInteger\": { " \
        			"\"description\": \"OMF format property to apply to the type Integer\", " \
				"\"type\": \"string\", \"default\": \"int64\", " \
				"\"order\": \"14\" }, " \
			"\"formatNumber\": { " \
        			"\"description\": \"OMF format property to apply to the type Number\", " \
				"\"type\": \"string\", \"default\": \"float64\", " \
				"\"order\": \"15\" }, " \
			"\"compression\": { " \
        			"\"description\": \"Compress readings data before sending to PI server\", " \
				"\"type\": \"boolean\", \"default\": \"True\", " \
				"\"order\": \"16\" }, " \
			"\"streamId\": {" \
				"\"description\": \"Identifies the specific stream to handle and the related information," \
				" among them the ID of the last object streamed.\", " \
				"\"type\": \"integer\", \"default\": \"0\", " \
				"\"readonly\": \"true\" }, " \
			"\"notBlockingErrors\": {" \
				"\"description\": \"These errors are considered not blocking in the communication with the PI Server, " \
				" the sending operation will proceed with the next block of data if one of these is encountered\" ," \
				"\"type\": \"string\", " \
				"\"default\": \"Redefinition of the type with the same ID is not allowed" \
				              "-Invalid value type for the property\", " \
				"\"order\": \"17\" ,"  \
				"\"readonly\": \"true\" " \
			"} "

#define OMF_PLUGIN_DESC "\"plugin\": {\"description\": \"PI Server North C Plugin\", \"type\": \"string\", \"default\": \"" PLUGIN_NAME "\", \"readonly\": \"true\"}"

#define PLUGIN_DEFAULT_CONFIG_INFO "{" OMF_PLUGIN_DESC ", " PLUGIN_DEFAULT_CONFIG "}"

/**
 * The PI Server plugin interface
 */
extern "C" {

/**
 * The C API plugin information structure
 */
static PLUGIN_INFORMATION info = {
	PLUGIN_NAME,		        // Name
	"1.0.0",			// Version
	SP_PERSIST_DATA,		// Flags
	PLUGIN_TYPE_NORTH,		// Type
	"1.0.0",			// Interface version
	PLUGIN_DEFAULT_CONFIG_INFO      // Configuration
};

/**
 * Historian PI Server connector info
 */
typedef struct
{
	SimpleHttps	*sender;	// HTTPS connection
	OMF 		*omf;		// OMF data protocol
	bool		compression;	// whether to compress readings' data
	string		hostAndPort;	// hostname:port for SimpleHttps
	unsigned int	timeout;	// connect and operation timeout
	string		path;		// PI Server application path
	string		typeId;		// OMF protocol type-id prefix
	string		producerToken;	// PI Server connector token
	string		formatNumber;	// OMF protocol Number format
	string		formatInteger;	// OMF protocol Integer format
	// FIXME:
        string		notBlockingErrors;
} CONNECTOR_INFO;

/**
 * Return the information about this plugin
 */
PLUGIN_INFORMATION *plugin_info()
{
	return &info;
}

/**
 * Initialise the plugin with configuration.
 *
 * This function is called to get the plugin handle.
 */
PLUGIN_HANDLE plugin_init(ConfigCategory* configData)
{
	/**
	 * Handle the PI Server parameters here
	 */
	string url = configData->getValue("URL");
	unsigned int timeout = atoi(configData->getValue("OMFHttpTimeout").c_str());
	string producerToken = configData->getValue("producerToken");

	string formatNumber = configData->getValue("formatNumber");
	string formatInteger = configData->getValue("formatInteger");

	/**
	 * Extract host, port, path from URL
	 */
	size_t findProtocol = url.find_first_of(":");
	string protocol = url.substr(0,findProtocol);

	string tmpUrl = url.substr(findProtocol + 3);
	size_t findPort = tmpUrl.find_first_of(":");
	string hostName = tmpUrl.substr(0, findPort);

	size_t findPath = tmpUrl.find_first_of("/");
	string port = tmpUrl.substr(findPort + 1 , findPath - findPort -1);
	string path = tmpUrl.substr(findPath);

	string hostAndPort(hostName + ":" + port);

	// Allocate connector struct
	CONNECTOR_INFO* connInfo = new CONNECTOR_INFO;
	// Set configuration felds
	connInfo->hostAndPort = hostAndPort;
	connInfo->path = path;
	connInfo->timeout = timeout;
	connInfo->typeId = TYPE_ID_DEFAULT;
	connInfo->producerToken = producerToken;
	connInfo->formatNumber = formatNumber;
	connInfo->formatInteger = formatInteger;

	// Use compression ?
	string compr = configData->getValue("compression");
	if (compr == "True" || compr == "true" || compr == "TRUE")
		connInfo->compression = true;
	else
		connInfo->compression = false;

	// FIXME:
	connInfo->notBlockingErrors = configData->getValue("notBlockingErrors");

	// Log plugin configuration
	Logger::getLogger()->info("%s plugin configured: URL=%s, "
				  "producerToken=%s, compression=%s",
				  PLUGIN_NAME,
				  url.c_str(),
				  producerToken.c_str(),
				  connInfo->compression ? "True" : "False");

	return (PLUGIN_HANDLE)connInfo;
}

/**
 * Plugin start with sored plugin_data
 *
 * @param handle	The plugin handle
 * @param storedData	The stored plugin_data
 */
void plugin_start(const PLUGIN_HANDLE handle,
		  const string& storedData)
{
	Logger* logger = Logger::getLogger();
	CONNECTOR_INFO* connInfo = (CONNECTOR_INFO *)handle;

	// Parse JSON plugin_data
	Document JSONData;
	JSONData.Parse(storedData.c_str());
	if (JSONData.HasParseError())
	{
		logger->error("%s plugin error: failure parsing "
			      "plugin data JSON object '%s'",
			      PLUGIN_NAME,
			      storedData.c_str());
	}
	else if(JSONData.HasMember(TYPE_ID_KEY) &&
		JSONData[TYPE_ID_KEY].IsString())
	{
		// Update type-id in PLUGIN_HANDLE object
		connInfo->typeId = JSONData[TYPE_ID_KEY].GetString();
	}
	else
	{
		logger->error("%s plugin error: key " TYPE_ID_KEY " not found "
			      " or not valid in plugin data JSON object'%s'",
			      PLUGIN_NAME,
			      storedData.c_str());
	}
	// Log plugin configuration
	Logger::getLogger()->info("%s plugin is using OMF %s=%s",
				  PLUGIN_NAME,
				  TYPE_ID_KEY,
				  connInfo->typeId.c_str());
}

/**
 * Send Readings data to historian server
 */
uint32_t plugin_send(const PLUGIN_HANDLE handle,
		     const vector<Reading *>& readings)
{
	CONNECTOR_INFO* connInfo = (CONNECTOR_INFO *)handle;

	/**
	 * Allocate the HTTPS handler for "Hostname : port"
	 * connect_timeout and request_timeout.
	 * Default is no timeout at all
	 */
	connInfo->sender = new SimpleHttps(connInfo->hostAndPort,
					   connInfo->timeout,
					   connInfo->timeout);

	// Allocate the PI Server data protocol
	connInfo->omf = new OMF(*connInfo->sender,
				connInfo->path,
				connInfo->typeId,
				connInfo->producerToken);

	// Set OMF FormatTypes
	connInfo->omf->setFormatType(OMF_TYPE_FLOAT,
				     connInfo->formatNumber);
	connInfo->omf->setFormatType(OMF_TYPE_INTEGER,
				     connInfo->formatInteger);

	// FIXME:
	connInfo->omf->setNotBlockingErrors(connInfo->notBlockingErrors);

	// Send data
	uint32_t ret = connInfo->omf->sendToServer(readings,
						   connInfo->compression);

	// Detect typeId change in OMF class
	if (connInfo->omf->getTypeId().compare(connInfo->typeId) != 0)
	{
		// Update typeId in plugin handle
		connInfo->typeId = connInfo->omf->getTypeId();
		// Log change
		Logger::getLogger()->info("%s plugin: a new OMF %s (%s) has been created.",
					  PLUGIN_NAME,
					  TYPE_ID_KEY,
					  connInfo->typeId.c_str());
	}
	// Delete objects
	delete connInfo->sender;
	delete connInfo->omf;

	// Return sent data ret code
	return ret;
}

/**
 * Shutdown the plugin
 *
 * Delete allocated data
 *
 * @param handle    The plugin handle
 * @return	    A string with JSON plugin data
 *		    the caller will persist
 */
string plugin_shutdown(PLUGIN_HANDLE handle)
{
	// Delete the handle
	CONNECTOR_INFO* connInfo = (CONNECTOR_INFO *) handle;

	// Create save data
	string saveData("{\"" TYPE_ID_KEY "\": \"" + connInfo->typeId + "\"}");

        // Log saving the plugin configuration
        Logger::getLogger()->info("%s plugin: saving plugin_data '%s'",
                                  PLUGIN_NAME,
                                  saveData.c_str());

	// Delete plugin handle
	delete connInfo;

	// Return current plugin data to save
	return saveData;
}

// End of extern "C"
};
<commit_msg>FOGL-2031: working stage - notBlockingErrors dynamic handling<commit_after>/*
 * FogLAMP PI Server north plugin.
 *
 * Copyright (c) 2018 Dianomic Systems
 *
 * Released under the Apache 2.0 Licence
 *
 * Author: Massimiliano Pinto
 */
#include <plugin_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string>
#include <logger.h>
#include <plugin_exception.h>
#include <iostream>
#include <omf.h>
#include <simple_https.h>
#include <config_category.h>
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"

using namespace std;
using namespace rapidjson;

#define PLUGIN_NAME "PI_Server_V2"
#define TYPE_ID_KEY "type-id"
#define TYPE_ID_DEFAULT "1"

/**
 * Plugin specific default configuration
 */
#define PLUGIN_DEFAULT_CONFIG \
			"\"URL\": { " \
				"\"description\": \"The URL of the PI Connector to send data to\", " \
				"\"type\": \"string\", " \
				"\"default\": \"https://pi-server:5460/ingress/messages\", " \
				"\"order\": \"1\" }, " \
			"\"producerToken\": { " \
				"\"description\": \"The producer token that represents this FogLAMP stream\", " \
				"\"type\": \"string\", \"default\": \"omf_north_0001\", " \
				"\"order\": \"2\" }, " \
			"\"source\": {" \
				"\"description\": \"Defines the source of the data to be sent on the stream, " \
				"this may be one of either readings, statistics or audit.\", \"type\": \"enumeration\", " \
				"\"default\": \"readings\", "\
				"\"options\": [\"readings\", \"statistics\"], " \
				"\"order\": \"3\"  }, " \
			"\"StaticData\": { " \
				"\"description\": \"Static data to include in each sensor reading sent to the PI Server.\", " \
				"\"type\": \"string\", \"default\": \"Location: Palo Alto, Company: Dianomic\", " \
				"\"order\": \"4\" }, " \
			"\"OMFRetrySleepTime\": { " \
        			"\"description\": \"Seconds between each retry for the communication with the OMF PI Connector Relay, " \
                       		"NOTE : the time is doubled at each attempt.\", \"type\": \"integer\", \"default\": \"1\", " \
				"\"order\": \"9\" }, " \
			"\"OMFMaxRetry\": { " \
				"\"description\": \"Max number of retries for the communication with the OMF PI Connector Relay\", " \
				"\"type\": \"integer\", \"default\": \"3\", " \
				"\"order\": \"10\" }, " \
			"\"OMFHttpTimeout\": { " \
				"\"description\": \"Timeout in seconds for the HTTP operations with the OMF PI Connector Relay\", " \
				"\"type\": \"integer\", \"default\": \"10\", " \
				"\"order\": \"13\" }, " \
			"\"formatInteger\": { " \
        			"\"description\": \"OMF format property to apply to the type Integer\", " \
				"\"type\": \"string\", \"default\": \"int64\", " \
				"\"order\": \"14\" }, " \
			"\"formatNumber\": { " \
        			"\"description\": \"OMF format property to apply to the type Number\", " \
				"\"type\": \"string\", \"default\": \"float64\", " \
				"\"order\": \"15\" }, " \
			"\"compression\": { " \
        			"\"description\": \"Compress readings data before sending to PI server\", " \
				"\"type\": \"boolean\", \"default\": \"True\", " \
				"\"order\": \"16\" }, " \
			"\"streamId\": {" \
				"\"description\": \"Identifies the specific stream to handle and the related information," \
				" among them the ID of the last object streamed.\", " \
				"\"type\": \"integer\", \"default\": \"0\", " \
				"\"readonly\": \"true\" }, " \
			"\"notBlockingErrors\": {" \
				"\"description\": "\
					"\"These errors are considered not blocking in the communication with the PI Server, " \
					  " the sending operation will proceed with the next block of data if one of these is encountered\" ," \
				"\"type\": \"JSON\", " \
				"\"default\": \"{\\\"errors400\\\": "\
		                        "["\
			                        "\\\"Redefinition of the type with the same ID is not allowed\\\", "\
						"\\\"Invalid value type for the property\\\" "\
		                        "]"\
                                "}\", " \
				"\"order\": \"17\" ,"  \
				"\"readonly\": \"true\" " \
			"} "

// "default": "{\"pipeline\": [\"DeltaFilter\"]}"

#define OMF_PLUGIN_DESC "\"plugin\": {\"description\": \"PI Server North C Plugin\", \"type\": \"string\", \"default\": \"" PLUGIN_NAME "\", \"readonly\": \"true\"}"

#define PLUGIN_DEFAULT_CONFIG_INFO "{" OMF_PLUGIN_DESC ", " PLUGIN_DEFAULT_CONFIG "}"

/**
 * The PI Server plugin interface
 */
extern "C" {

/**
 * The C API plugin information structure
 */
static PLUGIN_INFORMATION info = {
	PLUGIN_NAME,		        // Name
	"1.0.0",			// Version
	SP_PERSIST_DATA,		// Flags
	PLUGIN_TYPE_NORTH,		// Type
	"1.0.0",			// Interface version
	PLUGIN_DEFAULT_CONFIG_INFO      // Configuration
};

/**
 * Historian PI Server connector info
 */
typedef struct
{
	SimpleHttps	*sender;	// HTTPS connection
	OMF 		*omf;		// OMF data protocol
	bool		compression;	// whether to compress readings' data
	string		hostAndPort;	// hostname:port for SimpleHttps
	unsigned int	timeout;	// connect and operation timeout
	string		path;		// PI Server application path
	string		typeId;		// OMF protocol type-id prefix
	string		producerToken;	// PI Server connector token
	string		formatNumber;	// OMF protocol Number format
	string		formatInteger;	// OMF protocol Integer format
	// FIXME:
        string		notBlockingErrors;
} CONNECTOR_INFO;

/**
 * Return the information about this plugin
 */
PLUGIN_INFORMATION *plugin_info()
{
	return &info;
}

/**
 * Initialise the plugin with configuration.
 *
 * This function is called to get the plugin handle.
 */
PLUGIN_HANDLE plugin_init(ConfigCategory* configData)
{
	/**
	 * Handle the PI Server parameters here
	 */
	string url = configData->getValue("URL");
	unsigned int timeout = atoi(configData->getValue("OMFHttpTimeout").c_str());
	string producerToken = configData->getValue("producerToken");

	string formatNumber = configData->getValue("formatNumber");
	string formatInteger = configData->getValue("formatInteger");

	/**
	 * Extract host, port, path from URL
	 */
	size_t findProtocol = url.find_first_of(":");
	string protocol = url.substr(0,findProtocol);

	string tmpUrl = url.substr(findProtocol + 3);
	size_t findPort = tmpUrl.find_first_of(":");
	string hostName = tmpUrl.substr(0, findPort);

	size_t findPath = tmpUrl.find_first_of("/");
	string port = tmpUrl.substr(findPort + 1 , findPath - findPort -1);
	string path = tmpUrl.substr(findPath);

	string hostAndPort(hostName + ":" + port);

	// Allocate connector struct
	CONNECTOR_INFO* connInfo = new CONNECTOR_INFO;
	// Set configuration felds
	connInfo->hostAndPort = hostAndPort;
	connInfo->path = path;
	connInfo->timeout = timeout;
	connInfo->typeId = TYPE_ID_DEFAULT;
	connInfo->producerToken = producerToken;
	connInfo->formatNumber = formatNumber;
	connInfo->formatInteger = formatInteger;

	// Use compression ?
	string compr = configData->getValue("compression");
	if (compr == "True" || compr == "true" || compr == "TRUE")
		connInfo->compression = true;
	else
		connInfo->compression = false;

	// FIXME:
	connInfo->notBlockingErrors = configData->getValue("notBlockingErrors");

	// Log plugin configuration
	Logger::getLogger()->info("%s plugin configured: URL=%s, "
				  "producerToken=%s, compression=%s",
				  PLUGIN_NAME,
				  url.c_str(),
				  producerToken.c_str(),
				  connInfo->compression ? "True" : "False");

	return (PLUGIN_HANDLE)connInfo;
}

/**
 * Plugin start with sored plugin_data
 *
 * @param handle	The plugin handle
 * @param storedData	The stored plugin_data
 */
void plugin_start(const PLUGIN_HANDLE handle,
		  const string& storedData)
{
	Logger* logger = Logger::getLogger();
	CONNECTOR_INFO* connInfo = (CONNECTOR_INFO *)handle;

	// Parse JSON plugin_data
	Document JSONData;
	JSONData.Parse(storedData.c_str());
	if (JSONData.HasParseError())
	{
		logger->error("%s plugin error: failure parsing "
			      "plugin data JSON object '%s'",
			      PLUGIN_NAME,
			      storedData.c_str());
	}
	else if(JSONData.HasMember(TYPE_ID_KEY) &&
		JSONData[TYPE_ID_KEY].IsString())
	{
		// Update type-id in PLUGIN_HANDLE object
		connInfo->typeId = JSONData[TYPE_ID_KEY].GetString();
	}
	else
	{
		logger->error("%s plugin error: key " TYPE_ID_KEY " not found "
			      " or not valid in plugin data JSON object'%s'",
			      PLUGIN_NAME,
			      storedData.c_str());
	}
	// Log plugin configuration
	Logger::getLogger()->info("%s plugin is using OMF %s=%s",
				  PLUGIN_NAME,
				  TYPE_ID_KEY,
				  connInfo->typeId.c_str());
}

/**
 * Send Readings data to historian server
 */
uint32_t plugin_send(const PLUGIN_HANDLE handle,
		     const vector<Reading *>& readings)
{
	CONNECTOR_INFO* connInfo = (CONNECTOR_INFO *)handle;

	/**
	 * Allocate the HTTPS handler for "Hostname : port"
	 * connect_timeout and request_timeout.
	 * Default is no timeout at all
	 */
	connInfo->sender = new SimpleHttps(connInfo->hostAndPort,
					   connInfo->timeout,
					   connInfo->timeout);

	// Allocate the PI Server data protocol
	connInfo->omf = new OMF(*connInfo->sender,
				connInfo->path,
				connInfo->typeId,
				connInfo->producerToken);

	// Set OMF FormatTypes
	connInfo->omf->setFormatType(OMF_TYPE_FLOAT,
				     connInfo->formatNumber);
	connInfo->omf->setFormatType(OMF_TYPE_INTEGER,
				     connInfo->formatInteger);

	// FIXME:
	connInfo->omf->setNotBlockingErrors(connInfo->notBlockingErrors);

	// Send data
	uint32_t ret = connInfo->omf->sendToServer(readings,
						   connInfo->compression);

	// Detect typeId change in OMF class
	if (connInfo->omf->getTypeId().compare(connInfo->typeId) != 0)
	{
		// Update typeId in plugin handle
		connInfo->typeId = connInfo->omf->getTypeId();
		// Log change
		Logger::getLogger()->info("%s plugin: a new OMF %s (%s) has been created.",
					  PLUGIN_NAME,
					  TYPE_ID_KEY,
					  connInfo->typeId.c_str());
	}
	// Delete objects
	delete connInfo->sender;
	delete connInfo->omf;

	// Return sent data ret code
	return ret;
}

/**
 * Shutdown the plugin
 *
 * Delete allocated data
 *
 * @param handle    The plugin handle
 * @return	    A string with JSON plugin data
 *		    the caller will persist
 */
string plugin_shutdown(PLUGIN_HANDLE handle)
{
	// Delete the handle
	CONNECTOR_INFO* connInfo = (CONNECTOR_INFO *) handle;

	// Create save data
	string saveData("{\"" TYPE_ID_KEY "\": \"" + connInfo->typeId + "\"}");

        // Log saving the plugin configuration
        Logger::getLogger()->info("%s plugin: saving plugin_data '%s'",
                                  PLUGIN_NAME,
                                  saveData.c_str());

	// Delete plugin handle
	delete connInfo;

	// Return current plugin data to save
	return saveData;
}

// End of extern "C"
};
<|endoftext|>


<commit_before>#include "joblistwidgetitem.h"

WARNINGS_DISABLE
#include <QAction>
#include <QDateTime>
#include <QEvent>
#include <QKeySequence>
#include <QSize>
#include <QWidget>
#include <Qt>

#include "ui_joblistwidgetitem.h"
WARNINGS_ENABLE

#include "TElidedLabel.h"

#include "persistentmodel/archive.h"
#include "persistentmodel/job.h"
#include "utils.h"

JobListWidgetItem::JobListWidgetItem(JobPtr job)
    : _ui(new Ui::JobListWidgetItem), _widget(new QWidget)
{
    _widget->installEventFilter(this);
    _ui->setupUi(_widget);
    updateUi();
    // Set a sensible size.
    setSizeHint(QSize(_widget->minimumWidth(), _widget->minimumHeight()));

    connect(_ui->backupButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestBackup);
    connect(_ui->inspectButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestInspect);
    connect(_ui->restoreButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestRestore);
    connect(_ui->deleteButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestDelete);

    setJob(job);
}

JobListWidgetItem::~JobListWidgetItem()
{
    delete _ui;
}

QWidget *JobListWidgetItem::widget()
{
    return _widget;
}
JobPtr JobListWidgetItem::job() const
{
    return _job;
}

void JobListWidgetItem::setJob(const JobPtr &job)
{
    if(_job)
        disconnect(_job.data(), &Job::changed, this,
                   &JobListWidgetItem::update);

    _job = job;

    connect(_job.data(), &Job::changed, this, &JobListWidgetItem::update,
            QUEUED);

    _ui->nameLabel->setText(_job->name());
    if(_job->archives().isEmpty())
        _ui->lastBackupLabel->setText(tr("No backups"));
    else
    {
        QDateTime timestamp = _job->archives().first()->timestamp();
        _ui->lastBackupLabel->setText(
            timestamp.toString(Qt::DefaultLocaleShortDate));
    }

    int     count  = _job->archives().count();
    QString detail = tr("%1 %2 totaling ")
                         .arg(count)
                         .arg(count == 1 ? tr("archive") : tr("archives"));
    quint64 totalSize = 0;
    for(const ArchivePtr &archive : _job->archives())
    {
        totalSize += archive->sizeTotal();
    }
    detail.append(Utils::humanBytes(totalSize));

    _ui->detailLabel->setText(detail);
}

void JobListWidgetItem::update()
{
    setJob(_job);
}

bool JobListWidgetItem::eventFilter(QObject *obj, QEvent *event)
{
    if((obj == _widget) && (event->type() == QEvent::LanguageChange))
    {
        _ui->retranslateUi(_widget);
        updateUi();
        update();
        return true;
    }
    return false;
}

void JobListWidgetItem::updateUi()
{
    _ui->inspectButton->setToolTip(_ui->inspectButton->toolTip().arg(
        _ui->actionJobInspect->shortcut().toString(QKeySequence::NativeText)));
    _ui->restoreButton->setToolTip(_ui->restoreButton->toolTip().arg(
        _ui->actionJobRestore->shortcut().toString(QKeySequence::NativeText)));
    _ui->backupButton->setToolTip(_ui->backupButton->toolTip().arg(
        _ui->actionJobBackup->shortcut().toString(QKeySequence::NativeText)));
    _ui->deleteButton->setToolTip(_ui->deleteButton->toolTip().arg(
        _ui->actionJobDelete->shortcut().toString(QKeySequence::NativeText)));
}
<commit_msg>JobListWidgetItem: comments<commit_after>#include "joblistwidgetitem.h"

WARNINGS_DISABLE
#include <QAction>
#include <QDateTime>
#include <QEvent>
#include <QKeySequence>
#include <QSize>
#include <QWidget>
#include <Qt>

#include "ui_joblistwidgetitem.h"
WARNINGS_ENABLE

#include "TElidedLabel.h"

#include "persistentmodel/archive.h"
#include "persistentmodel/job.h"
#include "utils.h"

JobListWidgetItem::JobListWidgetItem(JobPtr job)
    : _ui(new Ui::JobListWidgetItem), _widget(new QWidget)
{
    _widget->installEventFilter(this);
    _ui->setupUi(_widget);
    updateUi();
    // Set a sensible size.
    setSizeHint(QSize(_widget->minimumWidth(), _widget->minimumHeight()));

    // Connections for buttons.
    connect(_ui->backupButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestBackup);
    connect(_ui->inspectButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestInspect);
    connect(_ui->restoreButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestRestore);
    connect(_ui->deleteButton, &QToolButton::clicked, this,
            &JobListWidgetItem::requestDelete);

    // Display info about the Job.
    setJob(job);
}

JobListWidgetItem::~JobListWidgetItem()
{
    delete _ui;
}

QWidget *JobListWidgetItem::widget()
{
    return _widget;
}

JobPtr JobListWidgetItem::job() const
{
    return _job;
}

void JobListWidgetItem::setJob(const JobPtr &job)
{
    // Disconnect previous Job.
    if(_job)
        disconnect(_job.data(), &Job::changed, this,
                   &JobListWidgetItem::update);

    // Set pointer.
    _job = job;

    // Connection for any modifications: the list of Archives
    // belonging to this Job has been updated.
    connect(_job.data(), &Job::changed, this, &JobListWidgetItem::update,
            QUEUED);

    // Display the Job name.
    _ui->nameLabel->setText(_job->name());

    // Display the datetime of the most recent archive, or "No backups".
    if(_job->archives().isEmpty())
        _ui->lastBackupLabel->setText(tr("No backups"));
    else
    {
        QDateTime timestamp = _job->archives().first()->timestamp();
        _ui->lastBackupLabel->setText(
            timestamp.toString(Qt::DefaultLocaleShortDate));
    }

    // Display the number and total size of Archives.
    int     count  = _job->archives().count();
    QString detail = tr("%1 %2 totaling ")
                         .arg(count)
                         .arg(count == 1 ? tr("archive") : tr("archives"));
    quint64 totalSize = 0;
    for(const ArchivePtr &archive : _job->archives())
    {
        totalSize += archive->sizeTotal();
    }
    detail.append(Utils::humanBytes(totalSize));

    _ui->detailLabel->setText(detail);
}

void JobListWidgetItem::update()
{
    setJob(_job);
}

bool JobListWidgetItem::eventFilter(QObject *obj, QEvent *event)
{
    if((obj == _widget) && (event->type() == QEvent::LanguageChange))
    {
        _ui->retranslateUi(_widget);
        updateUi();
        update();
        return true;
    }
    return false;
}

void JobListWidgetItem::updateUi()
{
    _ui->inspectButton->setToolTip(_ui->inspectButton->toolTip().arg(
        _ui->actionJobInspect->shortcut().toString(QKeySequence::NativeText)));
    _ui->restoreButton->setToolTip(_ui->restoreButton->toolTip().arg(
        _ui->actionJobRestore->shortcut().toString(QKeySequence::NativeText)));
    _ui->backupButton->setToolTip(_ui->backupButton->toolTip().arg(
        _ui->actionJobBackup->shortcut().toString(QKeySequence::NativeText)));
    _ui->deleteButton->setToolTip(_ui->deleteButton->toolTip().arg(
        _ui->actionJobDelete->shortcut().toString(QKeySequence::NativeText)));
}
<|endoftext|>


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

#ifndef __BASE_TRIE_HH__
#define __BASE_TRIE_HH__

#include "base/cprintf.hh"
#include "base/misc.hh"
#include "base/types.hh"

// Key has to be an integral type.
template <class Key, class Value>
class Trie
{
  protected:
    struct Node
    {
        Key key;
        Key mask;

        bool
        matches(Key test)
        {
            return (test & mask) == key;
        }

        Value *value;

        Node *parent;
        Node *kids[2];

        Node(Key _key, Key _mask, Value *_val) :
            key(_key & _mask), mask(_mask), value(_val),
            parent(NULL)
        {
            kids[0] = NULL;
            kids[1] = NULL;
        }

        void
        clear()
        {
            if (kids[1]) {
                kids[1]->clear();
                delete kids[1];
                kids[1] = NULL;
            }
            if (kids[0]) {
                kids[0]->clear();
                delete kids[0];
                kids[0] = NULL;
            }
        }

        void
        dump(int level)
        {
            for (int i = 1; i < level; i++) {
                cprintf("|");
            }
            if (level == 0)
                cprintf("Root ");
            else
                cprintf("+ ");
            cprintf("(%p, %p, %#X, %#X, %p)\n", parent, this, key, mask, value);
            if (kids[0])
                kids[0]->dump(level + 1);
            if (kids[1])
                kids[1]->dump(level + 1);
        }
    };

  protected:
    Node head;

  public:
    typedef Node *Handle;

    Trie() : head(0, 0, NULL)
    {}

    static const unsigned MaxBits = sizeof(Key) * 8;

  private:
    /**
     * A utility method which checks whether the key being looked up lies
     * beyond the Node being examined. If so, it returns true and advances the
     * node being examined.
     * @param parent The node we're currently "at", which can be updated.
     * @param kid The node we may want to move to.
     * @param key The key we're looking for.
     * @param new_mask The mask to use when matching against the key.
     * @return Whether the current Node was advanced.
     */
    bool
    goesAfter(Node **parent, Node *kid, Key key, Key new_mask)
    {
        if (kid && kid->matches(key) && (kid->mask & new_mask) == kid->mask) {
            *parent = kid;
            return true;
        } else {
            return false;
        }
    }

    /**
     * A utility method which extends a mask value one more bit towards the
     * lsb. This is almost just a signed right shift, except that the shifted
     * in bits are technically undefined. This is also slightly complicated by
     * the zero case.
     * @param orig The original mask to extend.
     * @return The extended mask.
     */
    Key
    extendMask(Key orig)
    {
        // Just in case orig was 0.
        const Key msb = ULL(1) << (MaxBits - 1);
        return orig | (orig >> 1) | msb;
    }

    /**
     * Method which looks up the Handle corresponding to a particular key. This
     * is useful if you want to delete the Node corresponding to a key since
     * the "remove" function takes a Node as its argument.
     * @param key The key to look up.
     * @return The first Node matching this key, or NULL if none was found.
     */
    Handle
    lookupHandle(Key key)
    {
        Node *node = &head;
        while (node) {
            if (node->value)
                return node;

            if (node->kids[0] && node->kids[0]->matches(key))
                node = node->kids[0];
            else if (node->kids[1] && node->kids[1]->matches(key))
                node = node->kids[1];
            else
                node = NULL;
        }

        return NULL;
    }

  public:
    /**
     * Method which inserts a key/value pair into the trie.
     * @param key The key which can later be used to look up this value.
     * @param width How many bits of the key (from msb) should be used.
     * @param val A pointer to the value to store in the trie.
     * @return A pointer to the Node which holds this value.
     */
    Handle
    insert(Key key, unsigned width, Value *val)
    {
        // Build a mask which masks off all the bits we don't care about.
        Key new_mask = ~(Key)0;
        if (width < MaxBits)
            new_mask <<= (MaxBits - width);
        // Use it to tidy up the key.
        key &= new_mask;

        // Walk past all the nodes this new node will be inserted after. They
        // can be ignored for the purposes of this function.
        Node *node = &head;
        while (goesAfter(&node, node->kids[0], key, new_mask) ||
               goesAfter(&node, node->kids[1], key, new_mask))
        {}
        assert(node);

        Key cur_mask = node->mask;
        // If we're already where the value needs to be...
        if (cur_mask == new_mask) {
            assert(!node->value);
            node->value = val;
            return node;
        }

        for (unsigned int i = 0; i < 2; i++) {
            Node *&kid = node->kids[i];
            Node *new_node;
            if (!kid) {
                // No kid. Add a new one.
                new_node = new Node(key, new_mask, val);
                new_node->parent = node;
                kid = new_node;
                return new_node;
            }

            // Walk down the leg until something doesn't match or we run out
            // of bits.
            Key last_mask;
            bool done;
            do {
                last_mask = cur_mask;
                cur_mask = extendMask(cur_mask);
                done = ((key & cur_mask) != (kid->key & cur_mask)) ||
                    last_mask == new_mask;
            } while (!done);
            cur_mask = last_mask;

            // If this isn't the right leg to go down at all, skip it.
            if (cur_mask == node->mask)
                continue;

            // At the point we walked to above, add a new node.
            new_node = new Node(key, cur_mask, NULL);
            new_node->parent = node;
            kid->parent = new_node;
            new_node->kids[0] = kid;
            kid = new_node;

            // If we ran out of bits, the value goes right here.
            if (cur_mask == new_mask) {
                new_node->value = val;
                return new_node;
            }

            // Still more bits to deal with, so add a new node for that path.
            new_node = new Node(key, new_mask, val);
            new_node->parent = kid;
            kid->kids[1] = new_node;
            return new_node;
        }

        panic("Reached the end of the Trie insert function!\n");
        return NULL;
    }

    /**
     * Method which looks up the Value corresponding to a particular key.
     * @param key The key to look up.
     * @return The first Value matching this key, or NULL if none was found.
     */
    Value *
    lookup(Key key)
    {
        Node *node = lookupHandle(key);
        if (node)
            return node->value;
        else
            return NULL;
    }

    /**
     * Method to delete a value from the trie.
     * @param node A pointer to the Node to remove.
     * @return The Value pointer from the removed entry.
     */
    Value *
    remove(Handle handle)
    {
        Node *node = handle;
        Value *val = node->value;
        if (node->kids[1]) {
            assert(node->value);
            node->value = NULL;
            return val;
        }
        if (!node->parent)
            panic("Trie: Can't remove root node.\n");

        Node *parent = node->parent;

        // If there's a kid, fix up it's parent pointer.
        if (node->kids[0])
            node->kids[0]->parent = parent;
        // Figure out which kid we are, and update our parent's pointers.
        if (parent->kids[0] == node)
            parent->kids[0] = node->kids[0];
        else if (parent->kids[1] == node)
            parent->kids[1] = node->kids[0];
        else
            panic("Trie: Inconsistent parent/kid relationship.\n");
        // Make sure if the parent only has one kid, it's kid[0].
        if (parent->kids[1] && !parent->kids[0]) {
            parent->kids[0] = parent->kids[1];
            parent->kids[1] = NULL;
        }

        // If the parent has less than two kids and no cargo and isn't the
        // root, delete it too.
        if (!parent->kids[1] && !parent->value && parent->parent)
            remove(parent);
        delete node;
        return val;
    }

    /**
     * Method to lookup a value from the trie and then delete it.
     * @param key The key to look up and then remove.
     * @return The Value pointer from the removed entry, if any.
     */
    Value *
    remove(Key key)
    {
        Handle handle = lookupHandle(key);
        if (!handle)
            return NULL;
        return remove(handle);
    }

    /**
     * A method which removes all key/value pairs from the trie. This is more
     * efficient than trying to remove elements individually.
     */
    void
    clear()
    {
        head.clear();
    }

    /**
     * A debugging method which prints the contents of this trie.
     * @param title An identifying title to put in the dump header.
     */
    void
    dump(const char *title)
    {
        cprintf("**************************************************\n");
        cprintf("*** Start of Trie: %s\n", title);
        cprintf("*** (parent, me, key, mask, value pointer)\n");
        cprintf("**************************************************\n");
        head.dump(0);
    }
};

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

#ifndef __BASE_TRIE_HH__
#define __BASE_TRIE_HH__

#include "base/cprintf.hh"
#include "base/misc.hh"
#include "base/types.hh"

// Key has to be an integral type.
template <class Key, class Value>
class Trie
{
  protected:
    struct Node
    {
        Key key;
        Key mask;

        bool
        matches(Key test)
        {
            return (test & mask) == key;
        }

        Value *value;

        Node *parent;
        Node *kids[2];

        Node(Key _key, Key _mask, Value *_val) :
            key(_key & _mask), mask(_mask), value(_val),
            parent(NULL)
        {
            kids[0] = NULL;
            kids[1] = NULL;
        }

        void
        clear()
        {
            if (kids[1]) {
                kids[1]->clear();
                delete kids[1];
                kids[1] = NULL;
            }
            if (kids[0]) {
                kids[0]->clear();
                delete kids[0];
                kids[0] = NULL;
            }
        }

        void
        dump(int level)
        {
            for (int i = 1; i < level; i++) {
                cprintf("|");
            }
            if (level == 0)
                cprintf("Root ");
            else
                cprintf("+ ");
            cprintf("(%p, %p, %#X, %#X, %p)\n", parent, this, key, mask, value);
            if (kids[0])
                kids[0]->dump(level + 1);
            if (kids[1])
                kids[1]->dump(level + 1);
        }
    };

  protected:
    Node head;

  public:
    typedef Node *Handle;

    Trie() : head(0, 0, NULL)
    {}

    static const unsigned MaxBits = sizeof(Key) * 8;

  private:
    /**
     * A utility method which checks whether the key being looked up lies
     * beyond the Node being examined. If so, it returns true and advances the
     * node being examined.
     * @param parent The node we're currently "at", which can be updated.
     * @param kid The node we may want to move to.
     * @param key The key we're looking for.
     * @param new_mask The mask to use when matching against the key.
     * @return Whether the current Node was advanced.
     */
    bool
    goesAfter(Node **parent, Node *kid, Key key, Key new_mask)
    {
        if (kid && kid->matches(key) && (kid->mask & new_mask) == kid->mask) {
            *parent = kid;
            return true;
        } else {
            return false;
        }
    }

    /**
     * A utility method which extends a mask value one more bit towards the
     * lsb. This is almost just a signed right shift, except that the shifted
     * in bits are technically undefined. This is also slightly complicated by
     * the zero case.
     * @param orig The original mask to extend.
     * @return The extended mask.
     */
    Key
    extendMask(Key orig)
    {
        // Just in case orig was 0.
        const Key msb = ULL(1) << (MaxBits - 1);
        return orig | (orig >> 1) | msb;
    }

    /**
     * Method which looks up the Handle corresponding to a particular key. This
     * is useful if you want to delete the Handle corresponding to a key since
     * the "remove" function takes a Handle as its argument.
     * @param key The key to look up.
     * @return The first Handle matching this key, or NULL if none was found.
     */
    Handle
    lookupHandle(Key key)
    {
        Node *node = &head;
        while (node) {
            if (node->value)
                return node;

            if (node->kids[0] && node->kids[0]->matches(key))
                node = node->kids[0];
            else if (node->kids[1] && node->kids[1]->matches(key))
                node = node->kids[1];
            else
                node = NULL;
        }

        return NULL;
    }

  public:
    /**
     * Method which inserts a key/value pair into the trie.
     * @param key The key which can later be used to look up this value.
     * @param width How many bits of the key (from msb) should be used.
     * @param val A pointer to the value to store in the trie.
     * @return A Handle corresponding to this value.
     */
    Handle
    insert(Key key, unsigned width, Value *val)
    {
        // Build a mask which masks off all the bits we don't care about.
        Key new_mask = ~(Key)0;
        if (width < MaxBits)
            new_mask <<= (MaxBits - width);
        // Use it to tidy up the key.
        key &= new_mask;

        // Walk past all the nodes this new node will be inserted after. They
        // can be ignored for the purposes of this function.
        Node *node = &head;
        while (goesAfter(&node, node->kids[0], key, new_mask) ||
               goesAfter(&node, node->kids[1], key, new_mask))
        {}
        assert(node);

        Key cur_mask = node->mask;
        // If we're already where the value needs to be...
        if (cur_mask == new_mask) {
            assert(!node->value);
            node->value = val;
            return node;
        }

        for (unsigned int i = 0; i < 2; i++) {
            Node *&kid = node->kids[i];
            Node *new_node;
            if (!kid) {
                // No kid. Add a new one.
                new_node = new Node(key, new_mask, val);
                new_node->parent = node;
                kid = new_node;
                return new_node;
            }

            // Walk down the leg until something doesn't match or we run out
            // of bits.
            Key last_mask;
            bool done;
            do {
                last_mask = cur_mask;
                cur_mask = extendMask(cur_mask);
                done = ((key & cur_mask) != (kid->key & cur_mask)) ||
                    last_mask == new_mask;
            } while (!done);
            cur_mask = last_mask;

            // If this isn't the right leg to go down at all, skip it.
            if (cur_mask == node->mask)
                continue;

            // At the point we walked to above, add a new node.
            new_node = new Node(key, cur_mask, NULL);
            new_node->parent = node;
            kid->parent = new_node;
            new_node->kids[0] = kid;
            kid = new_node;

            // If we ran out of bits, the value goes right here.
            if (cur_mask == new_mask) {
                new_node->value = val;
                return new_node;
            }

            // Still more bits to deal with, so add a new node for that path.
            new_node = new Node(key, new_mask, val);
            new_node->parent = kid;
            kid->kids[1] = new_node;
            return new_node;
        }

        panic("Reached the end of the Trie insert function!\n");
        return NULL;
    }

    /**
     * Method which looks up the Value corresponding to a particular key.
     * @param key The key to look up.
     * @return The first Value matching this key, or NULL if none was found.
     */
    Value *
    lookup(Key key)
    {
        Node *node = lookupHandle(key);
        if (node)
            return node->value;
        else
            return NULL;
    }

    /**
     * Method to delete a value from the trie.
     * @param node A Handle to remove.
     * @return The Value pointer from the removed entry.
     */
    Value *
    remove(Handle handle)
    {
        Node *node = handle;
        Value *val = node->value;
        if (node->kids[1]) {
            assert(node->value);
            node->value = NULL;
            return val;
        }
        if (!node->parent)
            panic("Trie: Can't remove root node.\n");

        Node *parent = node->parent;

        // If there's a kid, fix up it's parent pointer.
        if (node->kids[0])
            node->kids[0]->parent = parent;
        // Figure out which kid we are, and update our parent's pointers.
        if (parent->kids[0] == node)
            parent->kids[0] = node->kids[0];
        else if (parent->kids[1] == node)
            parent->kids[1] = node->kids[0];
        else
            panic("Trie: Inconsistent parent/kid relationship.\n");
        // Make sure if the parent only has one kid, it's kid[0].
        if (parent->kids[1] && !parent->kids[0]) {
            parent->kids[0] = parent->kids[1];
            parent->kids[1] = NULL;
        }

        // If the parent has less than two kids and no cargo and isn't the
        // root, delete it too.
        if (!parent->kids[1] && !parent->value && parent->parent)
            remove(parent);
        delete node;
        return val;
    }

    /**
     * Method to lookup a value from the trie and then delete it.
     * @param key The key to look up and then remove.
     * @return The Value pointer from the removed entry, if any.
     */
    Value *
    remove(Key key)
    {
        Handle handle = lookupHandle(key);
        if (!handle)
            return NULL;
        return remove(handle);
    }

    /**
     * A method which removes all key/value pairs from the trie. This is more
     * efficient than trying to remove elements individually.
     */
    void
    clear()
    {
        head.clear();
    }

    /**
     * A debugging method which prints the contents of this trie.
     * @param title An identifying title to put in the dump header.
     */
    void
    dump(const char *title)
    {
        cprintf("**************************************************\n");
        cprintf("*** Start of Trie: %s\n", title);
        cprintf("*** (parent, me, key, mask, value pointer)\n");
        cprintf("**************************************************\n");
        head.dump(0);
    }
};

#endif
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <memory>

#include <boost/variant.hpp>

#include "tac/Printer.hpp"
#include "tac/Program.hpp"

#include "VisitorUtils.hpp"

#include "Utils.hpp"

using namespace eddic;

namespace {

struct ArgumentToString : public boost::static_visitor<std::string> {
   std::string operator()(std::shared_ptr<Variable>& variable) const {
        return variable->name();
   }

   std::string operator()(int& integer) const {
        return toString(integer);
   }

   std::string operator()(std::string& str) const {
        return str;
   }
};

std::string printArgument(tac::Argument& arg){
    return boost::apply_visitor(ArgumentToString(), arg);
}

struct DebugVisitor : public boost::static_visitor<> {
    void operator()(tac::Program& program){
        std::cout << "TAC Program " << std::endl << std::endl;

        visit_each_non_variant(*this, program.functions);
    }

    void operator()(std::shared_ptr<tac::Function> function){
        std::cout << "Function " << function->getName() << std::endl;

        visit_each(*this, function->getStatements());
        visit_each_non_variant(*this, function->getBasicBlocks());

        std::cout << std::endl;
    }

    void operator()(std::shared_ptr<tac::BasicBlock>& block){
        std::cout << "-> Basic block" << std::endl;

        visit_each(*this, block->statements);
    }

    void operator()(tac::Statement& statement){
        visit(*this, statement);
    }

    void operator()(tac::Quadruple& quadruple){
        if(!quadruple.op){
            std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << std::endl;
        } else {
            tac::Operator op = *quadruple.op;

            if(op == tac::Operator::ADD){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " + " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::SUB){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " - " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::MUL){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " * " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::DIV){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " / " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::MOD){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " % " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::DOT){
                std::cout << "\t" << quadruple.result->name() << " = (" << printArgument(quadruple.arg1) << ")" << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::DOT_ASSIGN){
                std::cout << "\t(" << quadruple.result->name() << ")" << printArgument(quadruple.arg1) << " = " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::ARRAY){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " [" << printArgument(*quadruple.arg2) << "]" << std::endl;
            } else if(op == tac::Operator::ARRAY_ASSIGN){
                std::cout << "\t" << quadruple.result->name() << "[" << printArgument(quadruple.arg1) << "] = " << printArgument(*quadruple.arg2) << std::endl;
            }
        }
    }

    void operator()(tac::IfFalse& ifFalse){
        if(ifFalse.op == tac::BinaryOperator::EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " == " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::NOT_EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " != " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::LESS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " < " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::LESS_EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " <= " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::GREATER){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " > " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::GREATER_EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " >= " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        }
    }

    void operator()(tac::Goto& goto_){
        std::cout << "\tgoto " << goto_.label << std::endl;
    }

    void operator()(tac::Param& param){
        std::cout << "\tparam ";

        std::cout << printArgument(param.arg);

        std::cout << std::endl;
    }

    void operator()(tac::Return& return_){
        std::cout << "\treturn";

        if(return_.arg1){
            std::cout << " " << printArgument(*return_.arg1);
        }

        if(return_.arg2){
            std::cout << ", " << printArgument(*return_.arg2);
        }

        std::cout << std::endl;
    }

    void operator()(tac::Call& call){
        std::cout << "\t";

        if(call.return_){
            std::cout << call.return_->name();
        }

        if(call.return2_){
            std::cout << ", " << call.return2_->name();
        }

        if(call.return_ || call.return2_){
            std::cout << " = ";
        }

        std::cout << "call " << call.function << std::endl;
    }

    void operator()(std::string& label){
        std::cout << "\t" << label << ":" << std::endl;
    }
};

} //end of anonymous namespace

void tac::Printer::print(tac::Program& program) const {
   DebugVisitor visitor;
   visitor(program);
}
<commit_msg>Print the index of the block in the debug output<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <memory>

#include <boost/variant.hpp>

#include "tac/Printer.hpp"
#include "tac/Program.hpp"

#include "VisitorUtils.hpp"

#include "Utils.hpp"

using namespace eddic;

namespace {

struct ArgumentToString : public boost::static_visitor<std::string> {
   std::string operator()(std::shared_ptr<Variable>& variable) const {
        return variable->name();
   }

   std::string operator()(int& integer) const {
        return toString(integer);
   }

   std::string operator()(std::string& str) const {
        return str;
   }
};

std::string printArgument(tac::Argument& arg){
    return boost::apply_visitor(ArgumentToString(), arg);
}

struct DebugVisitor : public boost::static_visitor<> {
    void operator()(tac::Program& program){
        std::cout << "TAC Program " << std::endl << std::endl;

        visit_each_non_variant(*this, program.functions);
    }

    void operator()(std::shared_ptr<tac::Function> function){
        std::cout << "Function " << function->getName() << std::endl;

        visit_each(*this, function->getStatements());
        visit_each_non_variant(*this, function->getBasicBlocks());

        std::cout << std::endl;
    }

    void operator()(std::shared_ptr<tac::BasicBlock>& block){
        std::cout << "B" << block->index << "->" << std::endl;

        visit_each(*this, block->statements);
    }

    void operator()(tac::Statement& statement){
        visit(*this, statement);
    }

    void operator()(tac::Quadruple& quadruple){
        if(!quadruple.op){
            std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << std::endl;
        } else {
            tac::Operator op = *quadruple.op;

            if(op == tac::Operator::ADD){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " + " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::SUB){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " - " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::MUL){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " * " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::DIV){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " / " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::MOD){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " % " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::DOT){
                std::cout << "\t" << quadruple.result->name() << " = (" << printArgument(quadruple.arg1) << ")" << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::DOT_ASSIGN){
                std::cout << "\t(" << quadruple.result->name() << ")" << printArgument(quadruple.arg1) << " = " << printArgument(*quadruple.arg2) << std::endl;
            } else if(op == tac::Operator::ARRAY){
                std::cout << "\t" << quadruple.result->name() << " = " << printArgument(quadruple.arg1) << " [" << printArgument(*quadruple.arg2) << "]" << std::endl;
            } else if(op == tac::Operator::ARRAY_ASSIGN){
                std::cout << "\t" << quadruple.result->name() << "[" << printArgument(quadruple.arg1) << "] = " << printArgument(*quadruple.arg2) << std::endl;
            }
        }
    }

    void operator()(tac::IfFalse& ifFalse){
        if(ifFalse.op == tac::BinaryOperator::EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " == " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::NOT_EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " != " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::LESS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " < " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::LESS_EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " <= " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::GREATER){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " > " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        } else if(ifFalse.op == tac::BinaryOperator::GREATER_EQUALS){
            std::cout << "\tifFalse " << printArgument(ifFalse.arg1) << " >= " << printArgument(ifFalse.arg2) << " goto " << ifFalse.label << std::endl;
        }
    }

    void operator()(tac::Goto& goto_){
        std::cout << "\tgoto " << goto_.label << std::endl;
    }

    void operator()(tac::Param& param){
        std::cout << "\tparam ";

        std::cout << printArgument(param.arg);

        std::cout << std::endl;
    }

    void operator()(tac::Return& return_){
        std::cout << "\treturn";

        if(return_.arg1){
            std::cout << " " << printArgument(*return_.arg1);
        }

        if(return_.arg2){
            std::cout << ", " << printArgument(*return_.arg2);
        }

        std::cout << std::endl;
    }

    void operator()(tac::Call& call){
        std::cout << "\t";

        if(call.return_){
            std::cout << call.return_->name();
        }

        if(call.return2_){
            std::cout << ", " << call.return2_->name();
        }

        if(call.return_ || call.return2_){
            std::cout << " = ";
        }

        std::cout << "call " << call.function << std::endl;
    }

    void operator()(std::string& label){
        std::cout << "\t" << label << ":" << std::endl;
    }
};

} //end of anonymous namespace

void tac::Printer::print(tac::Program& program) const {
   DebugVisitor visitor;
   visitor(program);
}
<|endoftext|>