<commit_before>/**
 * @file
 * @brief Implementation of unit system
 *
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "unit.h"

#include <algorithm>
#include <cmath>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>

#include "text.h"

using namespace allpix;

std::map<std::string, allpix::Units::UnitType> Units::unit_map_;

/**
 * @throws std::invalid_argument If the unit already exists
 *
 * Units should consist of only alphabetical characters. Units are converted to lowercase internally. All the defined units
 * should have unique values and it is not possible to redefine them.
 *
 * @note No explicit check is done for alphabetical units
 */
void Units::add(std::string str, UnitType value) {
    // Do not distinguish between different case for units
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);

    // Find the unit
    if(unit_map_.find(str) != unit_map_.end()) {
        throw std::invalid_argument("unit " + str + " already defined");
    }
    unit_map_.emplace(str, value);
}

/**
 * @throws std::invalid_argument If the requested unit does not exist
 *
 * All units are converted to lowercase before finding their value in the internal database.
 */
allpix::Units::UnitType Units::getSingle(std::string str) {
    if(allpix::trim(str).empty()) {
        // An empty unit equals a multiplication with one:
        return 1.;
    }
    // Do not distinguish between different case for units
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);

    // Find the unit
    auto iter = unit_map_.find(str);
    if(iter == unit_map_.end()) {
        throw std::invalid_argument("unit " + str + " not found");
    }
    return iter->second;
}

/**
 * @throws invalid_argument If one tries to get the value of an empty unit
 *
 * Units are combined by applying the multiplication operator * and the division operator / linearly. The first unit is
 * always multiplied, following common sense. Grouping units together within brackets or parentheses is not supported. Thus
 * any other character then a name of a unit, * or \ should lead to an error
 */
allpix::Units::UnitType Units::get(std::string str) {
    UnitType ret_value = 1;
    if(allpix::trim(str).empty()) {
        throw std::invalid_argument("empty unit is not defined");
    }

    // Go through all units
    char lst = '*';
    std::string unit;
    for(char ch : str) {
        if(ch == '*' || ch == '/') {
            if(lst == '*') {
                ret_value = getSingle(ret_value, unit);
                unit.clear();
            } else if(lst == '/') {
                ret_value = getSingleInverse(ret_value, unit);
                unit.clear();
            }
            lst = ch;
        } else {
            unit += ch;
        }
    }

    // Apply last unit
    if(lst == '*') {
        ret_value = getSingle(ret_value, unit);
    } else if(lst == '/') {
        ret_value = getSingleInverse(ret_value, unit);
    }
    return ret_value;
}

allpix::Units::UnitType Units::convert(UnitType input, std::string str) {
    // Do not distinguish between different case for units
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);

    // Go through all units
    char lst = '*';
    std::string unit;
    for(char ch : str) {
        if(ch == '*' || ch == '/') {
            if(lst == '*') {
                input = getSingleInverse(input, unit);
                unit.clear();
            } else if(lst == '/') {
                input = getSingle(input, unit);
                unit.clear();
            }
            lst = ch;
        } else {
            unit += ch;
        }
    }
    // Handle last unit
    if(lst == '*') {
        input = getSingleInverse(input, unit);
    } else if(lst == '/') {
        input = getSingle(input, unit);
    }
    return input;
}

/**
 * @throws std::invalid_argument If the list of units is empty
 *
 * The best unit is determined using the two rules below:
 * - If there exists at least one unit for which the value is larger than one, the unit with value nearest to one is chosen
 *   from all units with values larger than one
 * - Otherwise the unit is chosen that has a value as close as possible to one (from below)
 */
std::string Units::display(UnitType input, std::initializer_list<std::string> units) {
    if(units.size() == 0) {
        throw std::invalid_argument("list of possible units cannot be empty");
    }

    std::ostringstream stream;
    if(std::fabs(input) < std::numeric_limits<Units::UnitType>::epsilon()) {
        // Zero needs no unit
        stream << input;
    } else {
        // Find best unit
        int best_exponent = std::numeric_limits<int>::min();
        std::string best_unit;
        for(auto& unit : units) {
            Units::UnitType value = convert(input, unit);
            int exponent = 0;
            std::frexp(value, &exponent);
            if((best_exponent <= 0 && exponent > best_exponent) || (exponent > 0 && exponent < best_exponent)) {
                best_exponent = exponent;
                best_unit = unit;
            }
        }

        // Write unit
        stream << convert(input, best_unit);
        stream << best_unit;
    }
    return stream.str();
}
std::string Units::display(UnitType input, std::string unit) {
    return display(input, {std::move(unit)});
}
<commit_msg>Allow empty unit: just returns 1 as multiplication factor<commit_after>/**
 * @file
 * @brief Implementation of unit system
 *
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "unit.h"

#include <algorithm>
#include <cmath>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>

#include "text.h"

using namespace allpix;

std::map<std::string, allpix::Units::UnitType> Units::unit_map_;

/**
 * @throws std::invalid_argument If the unit already exists
 *
 * Units should consist of only alphabetical characters. Units are converted to lowercase internally. All the defined units
 * should have unique values and it is not possible to redefine them.
 *
 * @note No explicit check is done for alphabetical units
 */
void Units::add(std::string str, UnitType value) {
    // Do not distinguish between different case for units
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);

    // Find the unit
    if(unit_map_.find(str) != unit_map_.end()) {
        throw std::invalid_argument("unit " + str + " already defined");
    }
    unit_map_.emplace(str, value);
}

/**
 * @throws std::invalid_argument If the requested unit does not exist
 *
 * All units are converted to lowercase before finding their value in the internal database.
 */
allpix::Units::UnitType Units::getSingle(std::string str) {
    if(allpix::trim(str).empty()) {
        // An empty unit equals a multiplication with one:
        return 1.;
    }
    // Do not distinguish between different case for units
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);

    // Find the unit
    auto iter = unit_map_.find(str);
    if(iter == unit_map_.end()) {
        throw std::invalid_argument("unit " + str + " not found");
    }
    return iter->second;
}

/**
 * @throws invalid_argument If one tries to get the value of an empty unit
 *
 * Units are combined by applying the multiplication operator * and the division operator / linearly. The first unit is
 * always multiplied, following common sense. Grouping units together within brackets or parentheses is not supported. Thus
 * any other character then a name of a unit, * or \ should lead to an error
 */
allpix::Units::UnitType Units::get(std::string str) {
    UnitType ret_value = 1;
    if(allpix::trim(str).empty()) {
        return ret_value;
    }

    // Go through all units
    char lst = '*';
    std::string unit;
    for(char ch : str) {
        if(ch == '*' || ch == '/') {
            if(lst == '*') {
                ret_value = getSingle(ret_value, unit);
                unit.clear();
            } else if(lst == '/') {
                ret_value = getSingleInverse(ret_value, unit);
                unit.clear();
            }
            lst = ch;
        } else {
            unit += ch;
        }
    }

    // Apply last unit
    if(lst == '*') {
        ret_value = getSingle(ret_value, unit);
    } else if(lst == '/') {
        ret_value = getSingleInverse(ret_value, unit);
    }
    return ret_value;
}

allpix::Units::UnitType Units::convert(UnitType input, std::string str) {
    // Do not distinguish between different case for units
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);

    // Go through all units
    char lst = '*';
    std::string unit;
    for(char ch : str) {
        if(ch == '*' || ch == '/') {
            if(lst == '*') {
                input = getSingleInverse(input, unit);
                unit.clear();
            } else if(lst == '/') {
                input = getSingle(input, unit);
                unit.clear();
            }
            lst = ch;
        } else {
            unit += ch;
        }
    }
    // Handle last unit
    if(lst == '*') {
        input = getSingleInverse(input, unit);
    } else if(lst == '/') {
        input = getSingle(input, unit);
    }
    return input;
}

/**
 * @throws std::invalid_argument If the list of units is empty
 *
 * The best unit is determined using the two rules below:
 * - If there exists at least one unit for which the value is larger than one, the unit with value nearest to one is chosen
 *   from all units with values larger than one
 * - Otherwise the unit is chosen that has a value as close as possible to one (from below)
 */
std::string Units::display(UnitType input, std::initializer_list<std::string> units) {
    if(units.size() == 0) {
        throw std::invalid_argument("list of possible units cannot be empty");
    }

    std::ostringstream stream;
    if(std::fabs(input) < std::numeric_limits<Units::UnitType>::epsilon()) {
        // Zero needs no unit
        stream << input;
    } else {
        // Find best unit
        int best_exponent = std::numeric_limits<int>::min();
        std::string best_unit;
        for(auto& unit : units) {
            Units::UnitType value = convert(input, unit);
            int exponent = 0;
            std::frexp(value, &exponent);
            if((best_exponent <= 0 && exponent > best_exponent) || (exponent > 0 && exponent < best_exponent)) {
                best_exponent = exponent;
                best_unit = unit;
            }
        }

        // Write unit
        stream << convert(input, best_unit);
        stream << best_unit;
    }
    return stream.str();
}
std::string Units::display(UnitType input, std::string unit) {
    return display(input, {std::move(unit)});
}
<|endoftext|>


<commit_before><commit_msg>Remove common.cpp<commit_after><|endoftext|>


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

// Implementation of the Equihash Proof-of-Work algorithm.
//
// Reference
// =========
// Alex Biryukov and Dmitry Khovratovich
// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem
// NDSS ’16, 21-24 February 2016, San Diego, CA, USA
// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf

#include "crypto/equihash.h"
#include "util.h"

#include <algorithm>
#include <iostream>
#include <stdexcept>

void validate_params(int n, int k)
{
    if (k>=n) {
        std::cerr << "n must be larger than k\n";
        throw invalid_params();
    }
    if (n % 8 != 0) {
        std::cerr << "Parameters must satisfy n = 0 mod 8\n";
        throw invalid_params();
    }
    if ((n/(k+1)) % 8 != 0) {
        std::cerr << "Parameters must satisfy n/(k+1) = 0 mod 8\n";
        throw invalid_params();
    }
}

int Equihash::InitialiseState(eh_HashState& base_state)
{
    unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
    memcpy(personalization, "ZcashPOW", 8);
    memcpy(personalization+8,  &n, 4);
    memcpy(personalization+12, &k, 4);
    return crypto_generichash_blake2b_init_salt_personal(&base_state,
                                                         NULL, 0, // No key.
                                                         n/8,
                                                         NULL,    // No salt.
                                                         personalization);
}

StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
        hash {new unsigned char[n/8]},
        len {n/8}
{
    eh_HashState state;
    state = base_state;
    crypto_generichash_blake2b_update(&state, (unsigned char*) &i, sizeof(eh_index));
    crypto_generichash_blake2b_final(&state, hash, n/8);
}

StepRow::~StepRow()
{
    delete[] hash;
}

StepRow::StepRow(const StepRow& a) :
        hash {new unsigned char[a.len]},
        len {a.len}
{
    for (int i = 0; i < len; i++)
        hash[i] = a.hash[i];
}

FullStepRow::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
        StepRow {n, base_state, i},
        indices {i}
{
    assert(indices.size() == 1);
}

FullStepRow& FullStepRow::operator=(const FullStepRow& a)
{
    unsigned char* p = new unsigned char[a.len];
    for (int i = 0; i < a.len; i++)
        p[i] = a.hash[i];
    delete[] hash;
    hash = p;
    len = a.len;
    indices = a.indices;
    return *this;
}

FullStepRow& FullStepRow::operator^=(const FullStepRow& a)
{
    if (a.len != len) {
        throw std::invalid_argument("Hash length differs");
    }
    if (a.indices.size() != indices.size()) {
        throw std::invalid_argument("Number of indices differs");
    }
    unsigned char* p = new unsigned char[len];
    for (int i = 0; i < len; i++)
        p[i] = hash[i] ^ a.hash[i];
    delete[] hash;
    hash = p;
    indices.reserve(indices.size() + a.indices.size());
    indices.insert(indices.end(), a.indices.begin(), a.indices.end());
    return *this;
}

void FullStepRow::TrimHash(int l)
{
    unsigned char* p = new unsigned char[len-l];
    for (int i = 0; i < len-l; i++)
        p[i] = hash[i+l];
    delete[] hash;
    hash = p;
    len -= l;
}

bool StepRow::IsZero()
{
    char res = 0;
    for (int i = 0; i < len; i++)
        res |= hash[i];
    return res == 0;
}

bool HasCollision(StepRow& a, StepRow& b, int l)
{
    bool res = true;
    for (int j = 0; j < l; j++)
        res &= a.hash[j] == b.hash[j];
    return res;
}

// Checks if the intersection of a.indices and b.indices is empty
bool DistinctIndices(const FullStepRow& a, const FullStepRow& b)
{
    std::vector<eh_index> aSrt(a.indices);
    std::vector<eh_index> bSrt(b.indices);

    std::sort(aSrt.begin(), aSrt.end());
    std::sort(bSrt.begin(), bSrt.end());

    unsigned int i = 0;
    for (unsigned int j = 0; j < bSrt.size(); j++) {
        while (aSrt[i] < bSrt[j]) {
            i++;
            if (i == aSrt.size()) { return true; }
        }
        assert(aSrt[i] >= bSrt[j]);
        if (aSrt[i] == bSrt[j]) { return false; }
    }
    return true;
}

Equihash::Equihash(unsigned int n, unsigned int k) :
        n(n), k(k)
{
    validate_params(n, k);
}

std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_state)
{
    assert(CollisionBitLength() + 1 < 8*sizeof(eh_index));
    eh_index init_size { ((eh_index) 1) << (CollisionBitLength() + 1) };

    // 1) Generate first list
    LogPrint("pow", "Generating first list\n");
    std::vector<FullStepRow> X;
    X.reserve(init_size);
    for (eh_index i = 0; i < init_size; i++) {
        X.emplace_back(n, base_state, i);
    }

    // 3) Repeat step 2 until 2n/(k+1) bits remain
    for (int r = 1; r < k && X.size() > 0; r++) {
        LogPrint("pow", "Round %d:\n", r);
        // 2a) Sort the list
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());

        LogPrint("pow", "- Finding collisions\n");
        int i = 0;
        int posFree = 0;
        std::vector<FullStepRow> Xc;
        while (i < X.size() - 1) {
            // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
            int j = 1;
            while (i+j < X.size() &&
                    HasCollision(X[i], X[i+j], CollisionByteLength())) {
                j++;
            }

            // 2c) Calculate tuples (X_i ^ X_j, (i, j))
            for (int l = 0; l < j - 1; l++) {
                for (int m = l + 1; m < j; m++) {
                    if (DistinctIndices(X[i+l], X[i+m])) {
                        Xc.push_back(X[i+l] ^ X[i+m]);
                        Xc.back().TrimHash(CollisionByteLength());
                    }
                }
            }

            // 2d) Store tuples on the table in-place if possible
            while (posFree < i+j && Xc.size() > 0) {
                X[posFree++] = Xc.back();
                Xc.pop_back();
            }

            i += j;
        }

        // 2e) Handle edge case where final table entry has no collision
        while (posFree < X.size() && Xc.size() > 0) {
            X[posFree++] = Xc.back();
            Xc.pop_back();
        }

        if (Xc.size() > 0) {
            // 2f) Add overflow to end of table
            X.insert(X.end(), Xc.begin(), Xc.end());
        } else if (posFree < X.size()) {
            // 2g) Remove empty space at the end
            X.erase(X.begin()+posFree, X.end());
            X.shrink_to_fit();
        }
    }

    // k+1) Find a collision on last 2n(k+1) bits
    LogPrint("pow", "Final round:\n");
    std::set<std::vector<eh_index>> solns;
    if (X.size() > 1) {
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());
        LogPrint("pow", "- Finding collisions\n");
        for (int i = 0; i < X.size() - 1; i++) {
            FullStepRow res = X[i] ^ X[i+1];
            if (res.IsZero() && DistinctIndices(X[i], X[i+1])) {
                solns.insert(res.GetSolution());
            }
        }
    } else
        LogPrint("pow", "- List is empty\n");

    return solns;
}

bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
{
    eh_index soln_size { 1u << k };
    if (soln.size() != soln_size) {
        LogPrint("pow", "Invalid solution size: %d\n", soln.size());
        return false;
    }

    std::vector<FullStepRow> X;
    X.reserve(soln_size);
    for (eh_index i : soln) {
        X.emplace_back(n, base_state, i);
    }

    while (X.size() > 1) {
        std::vector<FullStepRow> Xc;
        for (int i = 0; i < X.size(); i += 2) {
            if (!HasCollision(X[i], X[i+1], CollisionByteLength())) {
                LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
                LogPrint("pow", "X[i]   = %s\n", X[i].GetHex());
                LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex());
                return false;
            }
            if (X[i+1].IndicesBefore(X[i])) {
                return false;
                LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
            }
            if (!DistinctIndices(X[i], X[i+1])) {
                LogPrint("pow", "Invalid solution: duplicate indices\n");
                return false;
            }
            Xc.push_back(X[i] ^ X[i+1]);
            Xc.back().TrimHash(CollisionByteLength());
        }
        X = Xc;
    }

    assert(X.size() == 1);
    return X[0].IsZero();
}
<commit_msg>Cleanups<commit_after>// Copyright (c) 2016 Jack Grigg
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// Implementation of the Equihash Proof-of-Work algorithm.
//
// Reference
// =========
// Alex Biryukov and Dmitry Khovratovich
// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem
// NDSS ’16, 21-24 February 2016, San Diego, CA, USA
// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf

#include "crypto/equihash.h"
#include "util.h"

#include <algorithm>
#include <iostream>
#include <stdexcept>

void validate_params(int n, int k)
{
    if (k>=n) {
        std::cerr << "n must be larger than k\n";
        throw invalid_params();
    }
    if (n % 8 != 0) {
        std::cerr << "Parameters must satisfy n = 0 mod 8\n";
        throw invalid_params();
    }
    if ((n/(k+1)) % 8 != 0) {
        std::cerr << "Parameters must satisfy n/(k+1) = 0 mod 8\n";
        throw invalid_params();
    }
}

int Equihash::InitialiseState(eh_HashState& base_state)
{
    unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
    memcpy(personalization, "ZcashPOW", 8);
    memcpy(personalization+8,  &n, 4);
    memcpy(personalization+12, &k, 4);
    return crypto_generichash_blake2b_init_salt_personal(&base_state,
                                                         NULL, 0, // No key.
                                                         n/8,
                                                         NULL,    // No salt.
                                                         personalization);
}

StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
        hash {new unsigned char[n/8]},
        len {n/8}
{
    eh_HashState state;
    state = base_state;
    crypto_generichash_blake2b_update(&state, (unsigned char*) &i, sizeof(eh_index));
    crypto_generichash_blake2b_final(&state, hash, n/8);
}

StepRow::~StepRow()
{
    delete[] hash;
}

StepRow::StepRow(const StepRow& a) :
        hash {new unsigned char[a.len]},
        len {a.len}
{
    std::copy(a.hash, a.hash+a.len, hash);
}

FullStepRow::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
        StepRow {n, base_state, i},
        indices {i}
{
    assert(indices.size() == 1);
}

FullStepRow& FullStepRow::operator=(const FullStepRow& a)
{
    unsigned char* p = new unsigned char[a.len];
    std::copy(a.hash, a.hash+a.len, p);
    delete[] hash;
    hash = p;
    len = a.len;
    indices = a.indices;
    return *this;
}

FullStepRow& FullStepRow::operator^=(const FullStepRow& a)
{
    if (a.len != len) {
        throw std::invalid_argument("Hash length differs");
    }
    if (a.indices.size() != indices.size()) {
        throw std::invalid_argument("Number of indices differs");
    }
    unsigned char* p = new unsigned char[len];
    for (int i = 0; i < len; i++)
        p[i] = hash[i] ^ a.hash[i];
    delete[] hash;
    hash = p;
    indices.reserve(indices.size() + a.indices.size());
    indices.insert(indices.end(), a.indices.begin(), a.indices.end());
    return *this;
}

void FullStepRow::TrimHash(int l)
{
    unsigned char* p = new unsigned char[len-l];
    std::copy(hash+l, hash+len, p);
    delete[] hash;
    hash = p;
    len -= l;
}

bool StepRow::IsZero()
{
    char res = 0;
    for (int i = 0; i < len; i++)
        res |= hash[i];
    return res == 0;
}

bool HasCollision(StepRow& a, StepRow& b, int l)
{
    bool res = true;
    for (int j = 0; j < l; j++)
        res &= a.hash[j] == b.hash[j];
    return res;
}

// Checks if the intersection of a.indices and b.indices is empty
bool DistinctIndices(const FullStepRow& a, const FullStepRow& b)
{
    std::vector<eh_index> aSrt(a.indices);
    std::vector<eh_index> bSrt(b.indices);

    std::sort(aSrt.begin(), aSrt.end());
    std::sort(bSrt.begin(), bSrt.end());

    unsigned int i = 0;
    for (unsigned int j = 0; j < bSrt.size(); j++) {
        while (aSrt[i] < bSrt[j]) {
            i++;
            if (i == aSrt.size()) { return true; }
        }
        assert(aSrt[i] >= bSrt[j]);
        if (aSrt[i] == bSrt[j]) { return false; }
    }
    return true;
}

Equihash::Equihash(unsigned int n, unsigned int k) :
        n(n), k(k)
{
    validate_params(n, k);
}

std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_state)
{
    assert(CollisionBitLength() + 1 < 8*sizeof(eh_index));
    eh_index init_size { 1 << (CollisionBitLength() + 1) };

    // 1) Generate first list
    LogPrint("pow", "Generating first list\n");
    std::vector<FullStepRow> X;
    X.reserve(init_size);
    for (eh_index i = 0; i < init_size; i++) {
        X.emplace_back(n, base_state, i);
    }

    // 3) Repeat step 2 until 2n/(k+1) bits remain
    for (int r = 1; r < k && X.size() > 0; r++) {
        LogPrint("pow", "Round %d:\n", r);
        // 2a) Sort the list
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());

        LogPrint("pow", "- Finding collisions\n");
        int i = 0;
        int posFree = 0;
        std::vector<FullStepRow> Xc;
        while (i < X.size() - 1) {
            // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
            int j = 1;
            while (i+j < X.size() &&
                    HasCollision(X[i], X[i+j], CollisionByteLength())) {
                j++;
            }

            // 2c) Calculate tuples (X_i ^ X_j, (i, j))
            for (int l = 0; l < j - 1; l++) {
                for (int m = l + 1; m < j; m++) {
                    if (DistinctIndices(X[i+l], X[i+m])) {
                        Xc.push_back(X[i+l] ^ X[i+m]);
                        Xc.back().TrimHash(CollisionByteLength());
                    }
                }
            }

            // 2d) Store tuples on the table in-place if possible
            while (posFree < i+j && Xc.size() > 0) {
                X[posFree++] = Xc.back();
                Xc.pop_back();
            }

            i += j;
        }

        // 2e) Handle edge case where final table entry has no collision
        while (posFree < X.size() && Xc.size() > 0) {
            X[posFree++] = Xc.back();
            Xc.pop_back();
        }

        if (Xc.size() > 0) {
            // 2f) Add overflow to end of table
            X.insert(X.end(), Xc.begin(), Xc.end());
        } else if (posFree < X.size()) {
            // 2g) Remove empty space at the end
            X.erase(X.begin()+posFree, X.end());
            X.shrink_to_fit();
        }
    }

    // k+1) Find a collision on last 2n(k+1) bits
    LogPrint("pow", "Final round:\n");
    std::set<std::vector<eh_index>> solns;
    if (X.size() > 1) {
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());
        LogPrint("pow", "- Finding collisions\n");
        for (int i = 0; i < X.size() - 1; i++) {
            FullStepRow res = X[i] ^ X[i+1];
            if (res.IsZero() && DistinctIndices(X[i], X[i+1])) {
                solns.insert(res.GetSolution());
            }
        }
    } else
        LogPrint("pow", "- List is empty\n");

    return solns;
}

bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
{
    eh_index soln_size { 1u << k };
    if (soln.size() != soln_size) {
        LogPrint("pow", "Invalid solution size: %d\n", soln.size());
        return false;
    }

    std::vector<FullStepRow> X;
    X.reserve(soln_size);
    for (eh_index i : soln) {
        X.emplace_back(n, base_state, i);
    }

    while (X.size() > 1) {
        std::vector<FullStepRow> Xc;
        for (int i = 0; i < X.size(); i += 2) {
            if (!HasCollision(X[i], X[i+1], CollisionByteLength())) {
                LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
                LogPrint("pow", "X[i]   = %s\n", X[i].GetHex());
                LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex());
                return false;
            }
            if (X[i+1].IndicesBefore(X[i])) {
                return false;
                LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
            }
            if (!DistinctIndices(X[i], X[i+1])) {
                LogPrint("pow", "Invalid solution: duplicate indices\n");
                return false;
            }
            Xc.push_back(X[i] ^ X[i+1]);
            Xc.back().TrimHash(CollisionByteLength());
        }
        X = Xc;
    }

    assert(X.size() == 1);
    return X[0].IsZero();
}
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 2007-2009, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id$*/

///////////////////////////////////////////////////////////////////////////////
///
/// This class provides access to ITS SDD digits in raw data.
///
///////////////////////////////////////////////////////////////////////////////

#include "AliITSRawStreamSDD.h"
#include "AliRawReader.h"
#include "AliLog.h"

ClassImp(AliITSRawStreamSDD)

const UInt_t AliITSRawStreamSDD::fgkCodeLength[8] =  {8, 18, 2, 3, 4, 5, 6, 7};

//______________________________________________________________________
AliITSRawStreamSDD::AliITSRawStreamSDD(AliRawReader* rawReader) :
  AliITSRawStream(rawReader),
fDDLModuleMap(0),
fData(0),
fEventId(0),
fCarlosId(-1),
fChannel(0),
fJitter(0),
fDDL(0),
fResetSkip(0)
{
// create an object to read ITS SDD raw digits
  fDDLModuleMap=new AliITSDDLModuleMapSDD();
  fDDLModuleMap->SetDefaultMap();
  Reset();
  for(Int_t im=0;im<kSDDModules;im++){
    fLowThresholdArray[im][0]=0;
    fLowThresholdArray[im][1]=0;
  }
  for(Int_t i=0;i<kFifoWords;i++) fNfifo[i]=0;
  for(Int_t i=0;i<kDDLsNumber;i++) fSkip[i]=0;
  fRawReader->Reset();
  fRawReader->Select("ITSSDD");

  for(Short_t i=0; i<kCarlosWords; i++) fICarlosWord[i]=0x30000000 + i; // 805306368+i;
  for(Short_t i=0; i<kFifoWords; i++) fIFifoWord[i]=0x30000010 + i;  // 805306384+i;
}

//______________________________________________________________________
AliITSRawStreamSDD::AliITSRawStreamSDD(const AliITSRawStreamSDD& rs) :
AliITSRawStream(rs.fRawReader),
fDDLModuleMap(rs.fDDLModuleMap),
fData(0),
fEventId(0),
fCarlosId(-1),
fChannel(0),
fJitter(0),
fDDL(0),
fResetSkip(0)
{
  // copy constructor
  AliError("Copy constructor should not be used.");
}
//__________________________________________________________________________
AliITSRawStreamSDD& AliITSRawStreamSDD::operator=(const AliITSRawStreamSDD& rs) {
  // assignment operator
  if (this!=&rs) {}
  AliError("Assignment opertator should not be used.");
  return *this;
}

//______________________________________________________________________
AliITSRawStreamSDD::~AliITSRawStreamSDD(){
  if(fDDLModuleMap) delete fDDLModuleMap;
}
//______________________________________________________________________
UInt_t AliITSRawStreamSDD::ReadBits()
{
// read bits from the given channel
  UInt_t result = (fChannelData[fCarlosId][fChannel] & ((1<<fReadBits[fCarlosId][fChannel]) - 1));
  fChannelData[fCarlosId][fChannel] >>= fReadBits[fCarlosId][fChannel];
  fLastBit[fCarlosId][fChannel] -= fReadBits[fCarlosId][fChannel];
  return result;
}

//______________________________________________________________________
Int_t AliITSRawStreamSDD::DecompAmbra(Int_t value) const
{
  // AMBRA decompression (from 8 to 10 bit)

  if ((value & 0x80) == 0) {
    return value & 0x7f;
  } else if ((value & 0x40) == 0) {
    return 0x081 + ((value & 0x3f) << 1);
  } else if ((value & 0x20) == 0) {
    return 0x104 + ((value & 0x1f) << 3);
  } else {
    return 0x208 + ((value & 0x1f) << 4);
  }

}

//______________________________________________________________________
Bool_t AliITSRawStreamSDD::Next()
{
// read the next raw digit
// returns kFALSE if there is no digit left
// returns kTRUE and fCompletedModule=kFALSE when a digit is found
// returns kTRUE and fCompletedModule=kTRUE  when a module is completed (=3x3FFFFFFF footer words)

  fPrevModuleID = fModuleID;
  fDDL=fRawReader->GetDDLID();
  fCompletedModule=kFALSE;

  while (kTRUE) {
    if(fResetSkip==0){
      Bool_t kSkip = SkipHeaderWord();
      fResetSkip=1;
      if(!kSkip){
	fRawReader->AddMajorErrorLog(kDataError,"Bad CarlosRX header");
	AliWarning(Form("Invalid data: bad CarlosRX header %08x\n", fData));
	return kFALSE;
      }
    }

    if ((fChannel < 0) || (fCarlosId < 0) || (fChannel >= 2) || (fCarlosId >= kModulesPerDDL) || (fLastBit[fCarlosId][fChannel] < fReadBits[fCarlosId][fChannel]) ) {
      if (!fRawReader->ReadNextInt(fData)) return kFALSE;  // read next word

      Int_t ddln = fRawReader->GetDDLID();
      if(ddln!=fDDL) {
	Reset();
	fDDL=fRawReader->GetDDLID();
      }

      fChannel = -1;
      if((fData >> 16) == 0x7F00){ // jitter word
	fResetSkip=0;
	continue;
      }

      UInt_t nData28= fData >> 28;
      UInt_t nData30= fData >> 30;

      if (nData28== 0x02) {           // header
	fEventId = (fData >> 3) & 0x07FF;
      } else if (nData28== 0x03) {    // Carlos and FIFO words or Footers
	if(fData>=fICarlosWord[0]&&fData<=fICarlosWord[11]) { // Carlos Word
	  fCarlosId = fData-fICarlosWord[0];
	  Int_t iFifoIdx = fCarlosId/3;
	  fNfifo[iFifoIdx] = fCarlosId;
	} else if (fData>=fIFifoWord[0]&&fData<=fIFifoWord[3]){ // FIFO word
	  fCarlosId = fNfifo[fData-fIFifoWord[0]];
	} else if(fData==0x3FFFFFFF){ // Carlos footer
	  fICountFoot[fCarlosId]++; // stop before the last word (last word=jitter)
	  if(fICountFoot[fCarlosId]==3){
	    fCompletedModule=kTRUE;
	    //	      printf("Completed module %d DDL %d\n",fCarlosId,fDDL);
	    return kTRUE;
	  }
	} else if(fData==0x3F1F1F1F){ // CarlosRX footer
	  // CARLOSRX footer -- do nothing
	}else{
	  fRawReader->AddMajorErrorLog(kDataError,"Bad footer");
	  AliWarning(Form("Invalid data: bad footer %08X\n", fData));
	  return kFALSE;
	}
      } else if (nData30 == 0x02 || nData30 == 0x03) {
	fChannel = nData30-2;
	fChannelData[fCarlosId][fChannel] +=
	  (ULong64_t(fData & 0x3FFFFFFF) << fLastBit[fCarlosId][fChannel]);
	fLastBit[fCarlosId][fChannel] += 30;
      } else if (nData28== 0x04) {
	// JTAG word -- do nothing
      } else {                               // unknown data format
	fRawReader->AddMajorErrorLog(kDataFormatErr,Form("Invalid data %8.8x",fData));
	AliWarning(Form("Invalid data: %8.8x\n", fData));
	return kFALSE;
      }

      if(fCarlosId>=0 && fCarlosId <kModulesPerDDL){
	 fModuleID = GetModuleNumber(fDDL,fCarlosId);
      }
    } else {  // decode data
      if (fReadCode[fCarlosId][fChannel]) {// read the next code word
	fChannelCode[fCarlosId][fChannel] = ReadBits();
	fReadCode[fCarlosId][fChannel] = kFALSE;
	fReadBits[fCarlosId][fChannel] = fgkCodeLength[fChannelCode[fCarlosId][fChannel]];
      } else {                      // read the next data word
	UInt_t data = ReadBits();
	fReadCode[fCarlosId][fChannel] = kTRUE;
	fReadBits[fCarlosId][fChannel] = 3;
	if (fChannelCode[fCarlosId][fChannel] == 0) {         // set the time bin
	  fTimeBin[fCarlosId][fChannel] = data;
	} else if (fChannelCode[fCarlosId][fChannel] == 1) {  // next anode
	  fTimeBin[fCarlosId][fChannel] = 0;
	  fAnode[fCarlosId][fChannel]++;
	} else {                                   // ADC signal data
	  fSignal = DecompAmbra(data + (1 << fChannelCode[fCarlosId][fChannel]) + fLowThresholdArray[fModuleID-kSPDModules][fChannel]);
	  fCoord1 = fAnode[fCarlosId][fChannel];
	  fCoord2 = fTimeBin[fCarlosId][fChannel];
	  fTimeBin[fCarlosId][fChannel]++;
	  //printf("Data read, Module=%d , Anode=%d , Time=%d , Charge=%d\n",fModuleID,fCoord1,fCoord2,fSignal);
	  return kTRUE;
	}
      }
    }
  }
  return kFALSE;
}

//______________________________________________________________________
void AliITSRawStreamSDD::Reset(){

  //reset data member for a new ddl
  for(Int_t i=0;i<2;i++){
    for(Int_t ic=0;ic<kModulesPerDDL;ic++){
      fChannelData[ic][i]=0;
      fLastBit[ic][i]=0;
      fChannelCode[ic][i]=0;
      fReadCode[ic][i]=kTRUE;
      fReadBits[ic][i]=3;
      fTimeBin[ic][i]=0;
      fAnode[ic][i]=0;
    }
  }
  for(Int_t i=0;i<kModulesPerDDL;i++) fICountFoot[i]=0;
}

//______________________________________________________________________
Bool_t AliITSRawStreamSDD::SkipHeaderWord(){
  // skip the 1 DDL header word = 0xffffffff
  while (kTRUE) {
    if (!fRawReader->ReadNextInt(fData)) return kFALSE;
    if ((fData >> 30) == 0x01) continue;  // JTAG word
    if(fData==0xFFFFFFFF) return kTRUE;
  }
}

<commit_msg>Fix unwanted error message<commit_after>/**************************************************************************
 * Copyright(c) 2007-2009, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id$*/

///////////////////////////////////////////////////////////////////////////////
///
/// This class provides access to ITS SDD digits in raw data.
///
///////////////////////////////////////////////////////////////////////////////

#include "AliITSRawStreamSDD.h"
#include "AliRawReader.h"
#include "AliLog.h"

ClassImp(AliITSRawStreamSDD)

const UInt_t AliITSRawStreamSDD::fgkCodeLength[8] =  {8, 18, 2, 3, 4, 5, 6, 7};

//______________________________________________________________________
AliITSRawStreamSDD::AliITSRawStreamSDD(AliRawReader* rawReader) :
  AliITSRawStream(rawReader),
fDDLModuleMap(0),
fData(0),
fEventId(0),
fCarlosId(-1),
fChannel(0),
fJitter(0),
fDDL(0),
fResetSkip(0)
{
// create an object to read ITS SDD raw digits
  fDDLModuleMap=new AliITSDDLModuleMapSDD();
  fDDLModuleMap->SetDefaultMap();
  Reset();
  for(Int_t im=0;im<kSDDModules;im++){
    fLowThresholdArray[im][0]=0;
    fLowThresholdArray[im][1]=0;
  }
  for(Int_t i=0;i<kFifoWords;i++) fNfifo[i]=0;
  for(Int_t i=0;i<kDDLsNumber;i++) fSkip[i]=0;
  fRawReader->Reset();
  fRawReader->Select("ITSSDD");

  for(Short_t i=0; i<kCarlosWords; i++) fICarlosWord[i]=0x30000000 + i; // 805306368+i;
  for(Short_t i=0; i<kFifoWords; i++) fIFifoWord[i]=0x30000010 + i;  // 805306384+i;
}

//______________________________________________________________________
AliITSRawStreamSDD::AliITSRawStreamSDD(const AliITSRawStreamSDD& rs) :
AliITSRawStream(rs.fRawReader),
fDDLModuleMap(rs.fDDLModuleMap),
fData(0),
fEventId(0),
fCarlosId(-1),
fChannel(0),
fJitter(0),
fDDL(0),
fResetSkip(0)
{
  // copy constructor
  AliError("Copy constructor should not be used.");
}
//__________________________________________________________________________
AliITSRawStreamSDD& AliITSRawStreamSDD::operator=(const AliITSRawStreamSDD& rs) {
  // assignment operator
  if (this!=&rs) {}
  AliError("Assignment opertator should not be used.");
  return *this;
}

//______________________________________________________________________
AliITSRawStreamSDD::~AliITSRawStreamSDD(){
  if(fDDLModuleMap) delete fDDLModuleMap;
}
//______________________________________________________________________
UInt_t AliITSRawStreamSDD::ReadBits()
{
// read bits from the given channel
  UInt_t result = (fChannelData[fCarlosId][fChannel] & ((1<<fReadBits[fCarlosId][fChannel]) - 1));
  fChannelData[fCarlosId][fChannel] >>= fReadBits[fCarlosId][fChannel];
  fLastBit[fCarlosId][fChannel] -= fReadBits[fCarlosId][fChannel];
  return result;
}

//______________________________________________________________________
Int_t AliITSRawStreamSDD::DecompAmbra(Int_t value) const
{
  // AMBRA decompression (from 8 to 10 bit)

  if ((value & 0x80) == 0) {
    return value & 0x7f;
  } else if ((value & 0x40) == 0) {
    return 0x081 + ((value & 0x3f) << 1);
  } else if ((value & 0x20) == 0) {
    return 0x104 + ((value & 0x1f) << 3);
  } else {
    return 0x208 + ((value & 0x1f) << 4);
  }

}

//______________________________________________________________________
Bool_t AliITSRawStreamSDD::Next()
{
// read the next raw digit
// returns kFALSE if there is no digit left
// returns kTRUE and fCompletedModule=kFALSE when a digit is found
// returns kTRUE and fCompletedModule=kTRUE  when a module is completed (=3x3FFFFFFF footer words)

  fPrevModuleID = fModuleID;
  fDDL=fRawReader->GetDDLID();
  fCompletedModule=kFALSE;

  while (kTRUE) {
    if(fResetSkip==0){
      Bool_t kSkip = SkipHeaderWord();
      fResetSkip=1;
      if(!kSkip) return kSkip;
    }

    if ((fChannel < 0) || (fCarlosId < 0) || (fChannel >= 2) || (fCarlosId >= kModulesPerDDL) || (fLastBit[fCarlosId][fChannel] < fReadBits[fCarlosId][fChannel]) ) {
      if (!fRawReader->ReadNextInt(fData)) return kFALSE;  // read next word

      Int_t ddln = fRawReader->GetDDLID();
      if(ddln!=fDDL) {
	Reset();
	fDDL=fRawReader->GetDDLID();
      }

      fChannel = -1;
      if((fData >> 16) == 0x7F00){ // jitter word
	fResetSkip=0;
	continue;
      }

      UInt_t nData28= fData >> 28;
      UInt_t nData30= fData >> 30;

      if (nData28== 0x02) {           // header
	fEventId = (fData >> 3) & 0x07FF;
      } else if (nData28== 0x03) {    // Carlos and FIFO words or Footers
	if(fData>=fICarlosWord[0]&&fData<=fICarlosWord[11]) { // Carlos Word
	  fCarlosId = fData-fICarlosWord[0];
	  Int_t iFifoIdx = fCarlosId/3;
	  fNfifo[iFifoIdx] = fCarlosId;
	} else if (fData>=fIFifoWord[0]&&fData<=fIFifoWord[3]){ // FIFO word
	  fCarlosId = fNfifo[fData-fIFifoWord[0]];
	} else if(fData==0x3FFFFFFF){ // Carlos footer
	  fICountFoot[fCarlosId]++; // stop before the last word (last word=jitter)
	  if(fICountFoot[fCarlosId]==3){
	    fCompletedModule=kTRUE;
	    //	      printf("Completed module %d DDL %d\n",fCarlosId,fDDL);
	    return kTRUE;
	  }
	} else if(fData==0x3F1F1F1F){ // CarlosRX footer
	  // CARLOSRX footer -- do nothing
	}else{
	  fRawReader->AddMajorErrorLog(kDataError,"Bad footer");
	  AliWarning(Form("Invalid data: bad footer %08X\n", fData));
	  return kFALSE;
	}
      } else if (nData30 == 0x02 || nData30 == 0x03) {
	fChannel = nData30-2;
	fChannelData[fCarlosId][fChannel] +=
	  (ULong64_t(fData & 0x3FFFFFFF) << fLastBit[fCarlosId][fChannel]);
	fLastBit[fCarlosId][fChannel] += 30;
      } else if (nData28== 0x04) {
	// JTAG word -- do nothing
      } else {                               // unknown data format
	fRawReader->AddMajorErrorLog(kDataFormatErr,Form("Invalid data %8.8x",fData));
	AliWarning(Form("Invalid data: %8.8x\n", fData));
	return kFALSE;
      }

      if(fCarlosId>=0 && fCarlosId <kModulesPerDDL){
	 fModuleID = GetModuleNumber(fDDL,fCarlosId);
      }
    } else {  // decode data
      if (fReadCode[fCarlosId][fChannel]) {// read the next code word
	fChannelCode[fCarlosId][fChannel] = ReadBits();
	fReadCode[fCarlosId][fChannel] = kFALSE;
	fReadBits[fCarlosId][fChannel] = fgkCodeLength[fChannelCode[fCarlosId][fChannel]];
      } else {                      // read the next data word
	UInt_t data = ReadBits();
	fReadCode[fCarlosId][fChannel] = kTRUE;
	fReadBits[fCarlosId][fChannel] = 3;
	if (fChannelCode[fCarlosId][fChannel] == 0) {         // set the time bin
	  fTimeBin[fCarlosId][fChannel] = data;
	} else if (fChannelCode[fCarlosId][fChannel] == 1) {  // next anode
	  fTimeBin[fCarlosId][fChannel] = 0;
	  fAnode[fCarlosId][fChannel]++;
	} else {                                   // ADC signal data
	  fSignal = DecompAmbra(data + (1 << fChannelCode[fCarlosId][fChannel]) + fLowThresholdArray[fModuleID-kSPDModules][fChannel]);
	  fCoord1 = fAnode[fCarlosId][fChannel];
	  fCoord2 = fTimeBin[fCarlosId][fChannel];
	  fTimeBin[fCarlosId][fChannel]++;
	  //printf("Data read, Module=%d , Anode=%d , Time=%d , Charge=%d\n",fModuleID,fCoord1,fCoord2,fSignal);
	  return kTRUE;
	}
      }
    }
  }
  return kFALSE;
}

//______________________________________________________________________
void AliITSRawStreamSDD::Reset(){

  //reset data member for a new ddl
  for(Int_t i=0;i<2;i++){
    for(Int_t ic=0;ic<kModulesPerDDL;ic++){
      fChannelData[ic][i]=0;
      fLastBit[ic][i]=0;
      fChannelCode[ic][i]=0;
      fReadCode[ic][i]=kTRUE;
      fReadBits[ic][i]=3;
      fTimeBin[ic][i]=0;
      fAnode[ic][i]=0;
    }
  }
  for(Int_t i=0;i<kModulesPerDDL;i++) fICountFoot[i]=0;
}

//______________________________________________________________________
Bool_t AliITSRawStreamSDD::SkipHeaderWord(){
  // skip the 1 DDL header word = 0xffffffff
  while (kTRUE) {
    if (!fRawReader->ReadNextInt(fData)) return kFALSE;
    if ((fData >> 30) == 0x01) continue;  // JTAG word
    if(fData==0xFFFFFFFF) return kTRUE;
  }
}

<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2016, Computer Science Institute VI, University of Bonn
 *  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 University of Bonn, Computer Science Institute
 *     VI 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 <scan_assembler_nodelet.h>

#include <pluginlib/class_list_macros.h>

namespace mrs_laser_mapping
{
ScanAssemblerNodelet::ScanAssemblerNodelet()
  : frame_id_("/base_link")
  , wait_duration_(0.1)
  , scan_line_buffer_(40)

  , is_running_(true)
  , is_first_scan_line_(true)
  , is_first_scan_(true)

  , cloud_for_assembler_(new pcl::PointCloud<OutputPointType>())

  , last_laser_yaw_angle_(0.0)

  , half_rotation_(false)
  , add_invalid_points_(false)
  , scan_line_number_by_stamp_(false)
{
  NODELET_INFO("Initializing scan assembler nodelet.. ");
}

ScanAssemblerNodelet::~ScanAssemblerNodelet()
{
  is_running_ = false;
  process_scan_thread_.join();
  NODELET_INFO("Scan assembler nodelet exiting normal.. ");
}

void ScanAssemblerNodelet::onInit()
{
  NODELET_DEBUG("ScanAssemblerNodelet onInit()");

  ros::NodeHandle& ph = getMTPrivateNodeHandle();

  ph.param<std::string>("frame_id", frame_id_, "/base_link");
  ph.param<bool>("add_invalid_points", add_invalid_points_, false);
  ph.param<bool>("half_rotation", half_rotation_, true);;
  ph.param<bool>("scan_line_number_by_stamp", scan_line_number_by_stamp_, false);

  double transform_wait_duration;
  ph.param<double>("transform_wait_duration", transform_wait_duration, 0.1);
  wait_duration_ = ros::Duration(transform_wait_duration);

  // subscribe laserscanlines
  sub_laser_scan_.subscribe(ph, "input", 10);

  message_filter_laser_scan_.reset(
      new tf::MessageFilter<sensor_msgs::LaserScan>(sub_laser_scan_, tf_listener_, frame_id_, 100, ph));
  message_filter_laser_scan_->registerCallback(boost::bind(&ScanAssemblerNodelet::receivedLaserScan, this, _1));

  scan_publisher_ = ph.advertise<pcl::PointCloud<OutputPointType>>("output", 1);

  process_scan_thread_ = boost::thread(&ScanAssemblerNodelet::processScans, this);
}

void ScanAssemblerNodelet::receivedLaserScan(const sensor_msgs::LaserScanConstPtr& msg)
{
  scan_line_buffer_.push_front(*msg);
}

void ScanAssemblerNodelet::processScans()
{
  unsigned int scan_line_number = 0;
  unsigned int point_number = 0;

  while (is_running_)
  {
    if (scan_line_buffer_.size() == 0)
    {
      boost::this_thread::sleep(boost::posix_time::milliseconds(1));
      continue;
    }
    NODELET_DEBUG("processing scans.");

    // get scan line from buffer
    sensor_msgs::LaserScan scan;
    scan_line_buffer_.pop_back(&scan);

    // save frame id from one specific scanner to know when a rotation is complete
    if (is_first_scan_line_ && is_first_scan_)
    {
      scan_header_frame_id_ = scan.header.frame_id;
    }

    // save stamp of first scan line of a scan
    if (is_first_scan_line_)
    {
      first_stamp_ = scan.header.stamp;
      is_first_scan_line_ = false;
    }

    sensor_msgs::PointCloud2 cloud;
    pcl::PointCloud<PointT>::Ptr cloud_transformed(new pcl::PointCloud<PointT>());

    if (!tf_listener_.waitForTransform(frame_id_, scan.header.frame_id, scan.header.stamp +
		ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment), wait_duration_))
    {
      NODELET_ERROR_THROTTLE(10.0, "scan assembler: could not wait for transform... %f %f", scan.header.stamp.toSec(),
	  (scan.header.stamp + ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment)).toSec());

      // clear buffer if we cannot get the transform. Otherwise, we will never catch up...
      scan_line_buffer_.clear();
      continue;
    }

    // transform 2D scan line to 3D point cloud
    try
    {
      // keep points with max range measurements
      if (add_invalid_points_)
      {
        for (unsigned int i = 0; i < scan.ranges.size(); i++)
        {
          if (scan.ranges[i] >= scan.range_max)
          {
            scan.ranges[i] = scan.range_max - 0.1;
          }
        }
        scan_projector_.transformLaserScanToPointCloud(frame_id_, scan, cloud, tf_listener_, -1.0,
        laser_geometry::channel_option::Default);
      }
      else
      {
        scan_projector_.transformLaserScanToPointCloud(frame_id_, scan, cloud, tf_listener_, 35.f,
        laser_geometry::channel_option::Default);
      }

      // fix fields.count member
      for (unsigned int i = 0; i < cloud.fields.size(); i++)
        cloud.fields[i].count = 1;

      pcl::fromROSMsg(cloud, *cloud_transformed);
    }
    catch (tf::TransformException& exc)
    {
      NODELET_ERROR_THROTTLE(10.0, "Scan Assembler: No transform found");
      NODELET_ERROR_THROTTLE(10.0, "message: '%s'", exc.what());
    }

    std::vector<int> indices;
    pcl::removeNaNFromPointCloud(*cloud_transformed, *cloud_transformed, indices);

    // calculate the scan line number from the scan's timestamp (e.g. when having two lasers)
    if (scan_line_number_by_stamp_)
    {
      scan_line_number = static_cast<unsigned int>(ros::Duration(scan.header.stamp - first_stamp_).toNSec()*1e-6);
    }

    // add meta information
    for (size_t i = 0; i < cloud_transformed->size(); ++i)
    {
      OutputPointType p;
      p.x = cloud_transformed->points[i].x;
      p.y = cloud_transformed->points[i].y;
      p.z = cloud_transformed->points[i].z;

      p.scanlineNr = scan_line_number;
      p.pointNr = point_number++;

      cloud_for_assembler_->push_back(p);
    }
    scan_line_number++;

    // get rotation to check if a scan is complete
    tf::StampedTransform laser_rotation_transform;
    if (!tf_listener_.waitForTransform("/base_link", scan_header_frame_id_,
	    scan.header.stamp + ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment), wait_duration_))
      NODELET_ERROR_THROTTLE(10.0, "scan assembler: could not wait for transform... %f %f", scan.header.stamp.toSec(),
	  (scan.header.stamp + ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment)).toSec());

    try
    {
      tf_listener_.lookupTransform("/base_link", scan_header_frame_id_, scan.header.stamp +
					ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment), laser_rotation_transform);
    }
    catch (tf::TransformException& exc)
    {
      NODELET_ERROR_THROTTLE(10.0, "Scan Assembler: No transform found");
      NODELET_ERROR_THROTTLE(10.0, "message: '%s'", exc.what());
    }

    double laser_yaw = tf::getYaw(laser_rotation_transform.getRotation()); //add M_PI_2 to have "black spots" on sides
    if (laser_yaw > M_PI)
      laser_yaw = -M_PI + (laser_yaw - M_PI);

    // check for one scanner specified by scan_header_frame_id_ if a rotation is complete
    if (scan.header.frame_id == scan_header_frame_id_ && isScanComplete(laser_yaw))
    {
      pcl::PointCloud<OutputPointType>::Ptr output_cloud(new pcl::PointCloud<OutputPointType>());

      cloud_for_assembler_->swap(*output_cloud);
      output_cloud->header.frame_id = frame_id_;
      // adjust stamp to the "mean" stamp
      output_cloud->header.stamp = pcl_conversions::toPCL(first_stamp_ + ((scan.header.stamp - first_stamp_) * 0.5));
      output_cloud->header.seq = cloud_for_assembler_->header.seq;

      is_first_scan_line_ = true;

      // discard first point cloud. it tends to be incomplete
      if (!is_first_scan_)
      {
        scan_publisher_.publish(output_cloud);
        NODELET_DEBUG("published cloud");
      }
      else
        is_first_scan_ = false;

      scan_line_number = 0;
    }

    if (scan.header.frame_id == scan_header_frame_id_)
      last_laser_yaw_angle_ = laser_yaw;
  }
}

bool ScanAssemblerNodelet::isScanComplete(float laser_angle)
{
  return (last_laser_yaw_angle_ < 0 && laser_angle > 0) || (half_rotation_ && (last_laser_yaw_angle_ > 0 && laser_angle < 0));
}
}

PLUGINLIB_EXPORT_CLASS(mrs_laser_mapping::ScanAssemblerNodelet, nodelet::Nodelet)
<commit_msg>Fix laser transform max range. Previous value of 35.0 would not work if max range in LaserScan message lower<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2016, Computer Science Institute VI, University of Bonn
 *  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 University of Bonn, Computer Science Institute
 *     VI 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 <scan_assembler_nodelet.h>

#include <pluginlib/class_list_macros.h>

namespace mrs_laser_mapping
{
ScanAssemblerNodelet::ScanAssemblerNodelet()
  : frame_id_("/base_link")
  , wait_duration_(0.1)
  , scan_line_buffer_(40)

  , is_running_(true)
  , is_first_scan_line_(true)
  , is_first_scan_(true)

  , cloud_for_assembler_(new pcl::PointCloud<OutputPointType>())

  , last_laser_yaw_angle_(0.0)

  , half_rotation_(false)
  , add_invalid_points_(false)
  , scan_line_number_by_stamp_(false)
{
  NODELET_INFO("Initializing scan assembler nodelet.. ");
}

ScanAssemblerNodelet::~ScanAssemblerNodelet()
{
  is_running_ = false;
  process_scan_thread_.join();
  NODELET_INFO("Scan assembler nodelet exiting normal.. ");
}

void ScanAssemblerNodelet::onInit()
{
  NODELET_DEBUG("ScanAssemblerNodelet onInit()");

  ros::NodeHandle& ph = getMTPrivateNodeHandle();

  ph.param<std::string>("frame_id", frame_id_, "/base_link");
  ph.param<bool>("add_invalid_points", add_invalid_points_, false);
  ph.param<bool>("half_rotation", half_rotation_, true);;
  ph.param<bool>("scan_line_number_by_stamp", scan_line_number_by_stamp_, false);

  double transform_wait_duration;
  ph.param<double>("transform_wait_duration", transform_wait_duration, 0.1);
  wait_duration_ = ros::Duration(transform_wait_duration);

  // subscribe laserscanlines
  sub_laser_scan_.subscribe(ph, "input", 10);

  message_filter_laser_scan_.reset(
      new tf::MessageFilter<sensor_msgs::LaserScan>(sub_laser_scan_, tf_listener_, frame_id_, 100, ph));
  message_filter_laser_scan_->registerCallback(boost::bind(&ScanAssemblerNodelet::receivedLaserScan, this, _1));

  scan_publisher_ = ph.advertise<pcl::PointCloud<OutputPointType>>("output", 1);

  process_scan_thread_ = boost::thread(&ScanAssemblerNodelet::processScans, this);
}

void ScanAssemblerNodelet::receivedLaserScan(const sensor_msgs::LaserScanConstPtr& msg)
{
  scan_line_buffer_.push_front(*msg);
}

void ScanAssemblerNodelet::processScans()
{
  unsigned int scan_line_number = 0;
  unsigned int point_number = 0;

  while (is_running_)
  {
    if (scan_line_buffer_.size() == 0)
    {
      boost::this_thread::sleep(boost::posix_time::milliseconds(1));
      continue;
    }
    NODELET_DEBUG("processing scans.");

    // get scan line from buffer
    sensor_msgs::LaserScan scan;
    scan_line_buffer_.pop_back(&scan);

    // save frame id from one specific scanner to know when a rotation is complete
    if (is_first_scan_line_ && is_first_scan_)
    {
      scan_header_frame_id_ = scan.header.frame_id;
    }

    // save stamp of first scan line of a scan
    if (is_first_scan_line_)
    {
      first_stamp_ = scan.header.stamp;
      is_first_scan_line_ = false;
    }

    sensor_msgs::PointCloud2 cloud;
    pcl::PointCloud<PointT>::Ptr cloud_transformed(new pcl::PointCloud<PointT>());

    if (!tf_listener_.waitForTransform(frame_id_, scan.header.frame_id, scan.header.stamp +
		ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment), wait_duration_))
    {
      NODELET_ERROR_THROTTLE(10.0, "scan assembler: could not wait for transform... %f %f", scan.header.stamp.toSec(),
	  (scan.header.stamp + ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment)).toSec());

      // clear buffer if we cannot get the transform. Otherwise, we will never catch up...
      scan_line_buffer_.clear();
      continue;
    }

    // transform 2D scan line to 3D point cloud
    try
    {
      // keep points with max range measurements
      if (add_invalid_points_)
      {
        for (unsigned int i = 0; i < scan.ranges.size(); i++)
        {
          if (scan.ranges[i] >= scan.range_max)
          {
            scan.ranges[i] = scan.range_max - 0.1;
          }
        }
        scan_projector_.transformLaserScanToPointCloud(frame_id_, scan, cloud, tf_listener_, -1.0,
        laser_geometry::channel_option::Default);
      }
      else
      {
        scan_projector_.transformLaserScanToPointCloud(frame_id_, scan, cloud, tf_listener_, scan.range_max - 0.01,
        laser_geometry::channel_option::Default);
      }

      // fix fields.count member
      for (unsigned int i = 0; i < cloud.fields.size(); i++)
        cloud.fields[i].count = 1;

      pcl::fromROSMsg(cloud, *cloud_transformed);
    }
    catch (tf::TransformException& exc)
    {
      NODELET_ERROR_THROTTLE(10.0, "Scan Assembler: No transform found");
      NODELET_ERROR_THROTTLE(10.0, "message: '%s'", exc.what());
    }

    std::vector<int> indices;
    pcl::removeNaNFromPointCloud(*cloud_transformed, *cloud_transformed, indices);

    // calculate the scan line number from the scan's timestamp (e.g. when having two lasers)
    if (scan_line_number_by_stamp_)
    {
      scan_line_number = static_cast<unsigned int>(ros::Duration(scan.header.stamp - first_stamp_).toNSec()*1e-6);
    }

    // add meta information
    for (size_t i = 0; i < cloud_transformed->size(); ++i)
    {
      OutputPointType p;
      p.x = cloud_transformed->points[i].x;
      p.y = cloud_transformed->points[i].y;
      p.z = cloud_transformed->points[i].z;

      p.scanlineNr = scan_line_number;
      p.pointNr = point_number++;

      cloud_for_assembler_->push_back(p);
    }
    scan_line_number++;

    // get rotation to check if a scan is complete
    tf::StampedTransform laser_rotation_transform;
    if (!tf_listener_.waitForTransform("/base_link", scan_header_frame_id_,
	    scan.header.stamp + ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment), wait_duration_))
      NODELET_ERROR_THROTTLE(10.0, "scan assembler: could not wait for transform... %f %f", scan.header.stamp.toSec(),
	  (scan.header.stamp + ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment)).toSec());

    try
    {
      tf_listener_.lookupTransform("/base_link", scan_header_frame_id_, scan.header.stamp +
					ros::Duration().fromSec((scan.ranges.size()) * scan.time_increment), laser_rotation_transform);
    }
    catch (tf::TransformException& exc)
    {
      NODELET_ERROR_THROTTLE(10.0, "Scan Assembler: No transform found");
      NODELET_ERROR_THROTTLE(10.0, "message: '%s'", exc.what());
    }

    double laser_yaw = tf::getYaw(laser_rotation_transform.getRotation()); //add M_PI_2 to have "black spots" on sides
    if (laser_yaw > M_PI)
      laser_yaw = -M_PI + (laser_yaw - M_PI);

    // check for one scanner specified by scan_header_frame_id_ if a rotation is complete
    if (scan.header.frame_id == scan_header_frame_id_ && isScanComplete(laser_yaw))
    {
      pcl::PointCloud<OutputPointType>::Ptr output_cloud(new pcl::PointCloud<OutputPointType>());

      cloud_for_assembler_->swap(*output_cloud);
      output_cloud->header.frame_id = frame_id_;
      // adjust stamp to the "mean" stamp
      output_cloud->header.stamp = pcl_conversions::toPCL(first_stamp_ + ((scan.header.stamp - first_stamp_) * 0.5));
      output_cloud->header.seq = cloud_for_assembler_->header.seq;

      is_first_scan_line_ = true;

      // discard first point cloud. it tends to be incomplete
      if (!is_first_scan_)
      {
        scan_publisher_.publish(output_cloud);
        NODELET_DEBUG("published cloud");
      }
      else
        is_first_scan_ = false;

      scan_line_number = 0;
    }

    if (scan.header.frame_id == scan_header_frame_id_)
      last_laser_yaw_angle_ = laser_yaw;
  }
}

bool ScanAssemblerNodelet::isScanComplete(float laser_angle)
{
  return (last_laser_yaw_angle_ < 0 && laser_angle > 0) || (half_rotation_ && (last_laser_yaw_angle_ > 0 && laser_angle < 0));
}
}

PLUGINLIB_EXPORT_CLASS(mrs_laser_mapping::ScanAssemblerNodelet, nodelet::Nodelet)
<|endoftext|>


<commit_before>#include <event/event_callback.h>
#include <event/event_main.h>
#include <event/event_system.h>

#define	TIMER_MS	1000

static uint8_t zbuf[8192];

class BufferSpeed {
	uintmax_t bytes_;
	Action *callback_action_;
	Action *timeout_action_;
public:
	BufferSpeed(void)
	: callback_action_(NULL),
	  timeout_action_(NULL)
	{
		callback_action_ = callback(this, &BufferSpeed::callback_complete)->schedule();

		INFO("/example/buffer/append/speed1") << "Arming timer.";
		timeout_action_ = EventSystem::instance()->timeout(TIMER_MS, callback(this, &BufferSpeed::timer));
	}

	~BufferSpeed()
	{
		ASSERT(timeout_action_ == NULL);
	}

private:
	void callback_complete(void)
	{
		callback_action_->cancel();
		callback_action_ = NULL;

		Buffer tmp;
		tmp.append(zbuf, sizeof zbuf);
		bytes_ += tmp.length();

		callback_action_ = callback(this, &BufferSpeed::callback_complete)->schedule();
	}

	void timer(void)
	{
		timeout_action_->cancel();
		timeout_action_ = NULL;

		ASSERT(callback_action_ != NULL);
		callback_action_->cancel();
		callback_action_ = NULL;

		INFO("/example/buffer/append/speed1") << "Timer expired; " << bytes_ << " bytes appended.";
	}
};

int
main(void)
{
	INFO("/example/buffer/append/speed1") << "Timer delay: " << TIMER_MS << "ms";

	memset(zbuf, 0, sizeof zbuf);

	BufferSpeed *cs = new BufferSpeed();

	event_main();

	delete cs;
}
<commit_msg>Initialize the byte counter.<commit_after>#include <event/event_callback.h>
#include <event/event_main.h>
#include <event/event_system.h>

#define	TIMER_MS	1000

static uint8_t zbuf[8192];

class BufferSpeed {
	uintmax_t bytes_;
	Action *callback_action_;
	Action *timeout_action_;
public:
	BufferSpeed(void)
	: bytes_(0),
	  callback_action_(NULL),
	  timeout_action_(NULL)
	{
		callback_action_ = callback(this, &BufferSpeed::callback_complete)->schedule();

		INFO("/example/buffer/append/speed1") << "Arming timer.";
		timeout_action_ = EventSystem::instance()->timeout(TIMER_MS, callback(this, &BufferSpeed::timer));
	}

	~BufferSpeed()
	{
		ASSERT(timeout_action_ == NULL);
	}

private:
	void callback_complete(void)
	{
		callback_action_->cancel();
		callback_action_ = NULL;

		Buffer tmp;
		tmp.append(zbuf, sizeof zbuf);
		bytes_ += tmp.length();

		callback_action_ = callback(this, &BufferSpeed::callback_complete)->schedule();
	}

	void timer(void)
	{
		timeout_action_->cancel();
		timeout_action_ = NULL;

		ASSERT(callback_action_ != NULL);
		callback_action_->cancel();
		callback_action_ = NULL;

		INFO("/example/buffer/append/speed1") << "Timer expired; " << bytes_ << " bytes appended.";
	}
};

int
main(void)
{
	INFO("/example/buffer/append/speed1") << "Timer delay: " << TIMER_MS << "ms";

	memset(zbuf, 0, sizeof zbuf);

	BufferSpeed *cs = new BufferSpeed();

	event_main();

	delete cs;
}
<|endoftext|>


<commit_before>/**
  http://stackoverflow.com/questions/21821948/concept-based-polymorphism
  http://thinkthencode.blogspot.ru/2013/02/concept-based-polymorphism.html

*/

#include <gtest/gtest.h>

#include <vector>
#include <iostream>
#include <string>
#include <memory>
#include <algorithm>

using std::ostream;
using std::string;
using std::endl;
using std::cout;
using std::vector;
using std::move;

namespace ps_sample_shared_extend {
class object_t {
public:
  template<typename T>
  object_t(const T& x) : self_(std::make_shared<model<T>>(move(x)))
  { }

  friend void draw(const object_t &x, ostream &out, size_t position)
  { x.self_->draw_(out, position); }

private:
  class concept_t {
  public:
    virtual ~concept_t() = default;
    virtual void draw_(ostream& out, size_t position) const = 0;
  };

  template<typename T>
  struct model : concept_t {
    model(const T& x) : data_(move(x)) { }
    void draw_(ostream& out, size_t position) const {
      draw(data_, out, position);
    }

    T data_;  // главный вопрос в куче ли? Да - см в Мейсере 35
  };

  std::shared_ptr<const concept_t> self_;  // ссылки на immutable
};

using document_t = vector<object_t>;  // полиморфизм только через shared_ptrs

template<typename T>
void draw(const T& x, ostream& out, size_t position)
{ out << string(position, ' ') << x << endl; }

void draw(const document_t&x, ostream &out, size_t position)
{
  out << string(position, ' ') << "<document>" << endl;
  for (const auto& e : x) draw(e, out, position + 2);
  out << string(position, ' ') << "</document>" << endl;
}

/// Ps
using history_t = vector<document_t>;
void commit(history_t& x) {
  assert(x.size());
  x.push_back(x.back());
}

void undo(history_t& x) { assert(x.size()); x.pop_back(); }

document_t& current(history_t& x) {
  assert(x.size());
  return x.back();
}
}

namespace external_space  {
// не нужно ничего наследовать.
class my_class_t {
public:
  my_class_t() {}
  my_class_t(const my_class_t&) { cout << "copy mc\n"; }
  int arr[10];

private:
  // FIXME: What the hell?
  static void *operator new (size_t size);
  static void operator delete(void *ptr);
};

void draw(const my_class_t&, ostream& out, size_t position)
{ out << string(position, ' ') << "my_class_t()" << endl; }
}

TEST(EvelPsExtend, App) {
  using namespace ps_sample_shared_extend;
  using namespace external_space;
  history_t h(1);

  // Работаем с верхним элементом
  //current(h).emplace_back(0);
  //current(h).emplace_back(string("Hello!"));
  current(h).emplace_back(my_class_t());
  current(h).emplace_back(my_class_t());
  current(h).emplace_back(my_class_t());

  draw(current(h), cout, 0);
  cout << "-------------" << endl;
  std::reverse(current(h).begin(), current(h).end());

  commit(h);  // сохраняем текущую и ее копируем на верх.
  cout << "-------------" << endl;

  //current(h).emplace_back(current(h));
  current(h).emplace_back(my_class_t());
  current(h)[4] = my_class_t();

  draw(current(h), cout, 0);
  cout << "-------------" << endl;

  undo(h);
  draw(current(h), cout, 0);
}

namespace database {
/**
  \fixme const troubles

  \attention how reduce 2 hier.?
*/
namespace detail {
struct pq_tag { };
struct sqlite_tab { };

template <typename T> struct holder_traits;
template <> struct holder_traits<string> {
  typedef sqlite_tab category;
};

template <> struct holder_traits<int> {
  typedef pq_tag category;
};
}

namespace real_objs {
// no inh.!
struct sqlite {
  sqlite(string) { }
  void drop() { }
};

struct sqlite_builder {
  //sqlite_builder()
};

struct postgresql {
  postgresql(int) { }
  void drop() { }
};
}  // space

// Dropable
class one_table_concept_t {
public:
  template<typename T>
  object_t(const T& x) : self_(std::make_shared<model<T>>(move(x)))
  { }

  // FIXME: it's bad. must be friend?
  void drop()
  { self_->drop_(); }

  // generate
  //template <typename T

private:
  class concept_t {
  public:
    virtual ~concept_t() = default;
    virtual void drop_() = 0;
  };

  template<typename T>
  struct model : concept_t {
    model(const T& x) : data_(move(x)) { }
    void drop_() override
    { data_.drop(); }

    T data_;
  };

  std::shared_ptr<
    //const  // can't
    concept_t> self_;  // ссылки на immutable
};

// Fabric:
template<typename T>
one_table_concept_t create(std::weak_ptr<T> p) {
  return one_table_concept_t(0);
}

// by value, not by type
enum db_vars { DB_SQLITE, DB_POSTGRES };

//if (selector == DB_POSTGRES)
one_table_concept_t build_data_base(const int selector) {
  using namespace real_objs;
  if (selector == DB_SQLITE)
    return one_table_concept_t(sqlite(""));
  else
    return one_table_concept_t(postgresql(0));
}
}

TEST(DB, Test) {
  using namespace database;
  using namespace database::real_objs;

  // db.registerBeanClass<Obj>()
  auto a = one_table_concept_t(sqlite(""));
  auto b = one_table_concept_t(postgresql(0));
  a.drop();

  // FIXME: how connect multy DB drivers?

  int selector = 0;
  auto db = build_data_base(selector);

  // 1. Put handler - db specific - to queries builder
  // 2. Builder store in some high level object
  // 3. Want make queries on base getted handler
}
<commit_msg>begin add concepts<commit_after>/**
  http://stackoverflow.com/questions/21821948/concept-based-polymorphism
  http://thinkthencode.blogspot.ru/2013/02/concept-based-polymorphism.html

*/

#include <typeinfo>

#include <gtest/gtest.h>

#include <vector>
#include <iostream>
#include <string>
#include <memory>
#include <algorithm>

using std::ostream;
using std::string;
using std::endl;
using std::cout;
using std::vector;
using std::move;

namespace ps_sample_shared_extend {
class object_t {
public:
  template<typename T>
  object_t(const T& x) : self_(std::make_shared<model<T>>(move(x)))
  { }

  friend void draw(const object_t &x, ostream &out, size_t position)
  {
    x.self_->draw_(out, position);
    //cout << x
  }

private:
  class concept_t {
  public:
    virtual ~concept_t() = default;
    virtual void draw_(ostream& out, size_t position) const = 0;
  };

  template<typename T>
  struct model : concept_t {
    model(const T& x) : data_(move(x)) { }
    void draw_(ostream& out, size_t position) const {
      //draw(data_, out, position);
    }

    T data_;  // главный вопрос в куче ли? Да - см в Мейсере 35
  };

  std::shared_ptr<const concept_t> self_;  // ссылки на immutable
};

using document_t = vector<object_t>;  // полиморфизм только через shared_ptrs

template<typename T>
void draw(const T& x, ostream& out, size_t position)
{ out << string(position, ' ') << x << endl; }

void draw(const document_t&x, ostream &out, size_t position)
{
  out << string(position, ' ') << "<document>" << endl;
  for (const auto& e : x) draw(e, out, position + 2);
  out << string(position, ' ') << "</document>" << endl;
}

/// Ps
using history_t = vector<document_t>;
void commit(history_t& x) {
  assert(x.size());
  x.push_back(x.back());
}

void undo(history_t& x) { assert(x.size()); x.pop_back(); }

document_t& current(history_t& x) {
  assert(x.size());
  return x.back();
}
}

namespace external_space  {
// не нужно ничего наследовать.
class my_class_t {
public:
  my_class_t() {}
  my_class_t(const my_class_t&) { cout << "copy mc\n"; }
  int arr[10];

private:
  // FIXME: What the hell?
  static void *operator new (size_t size);
  static void operator delete(void *ptr);
};

void draw(const my_class_t&, ostream& out, size_t position)
{ out << string(position, ' ') << "my_class_t()" << endl; }
}

TEST(EvelPsExtend, App) {
  using namespace ps_sample_shared_extend;
  using namespace external_space;
  history_t h(1);

  // Работаем с верхним элементом
  //current(h).emplace_back(0);
  //current(h).emplace_back(string("Hello!"));
  current(h).emplace_back(my_class_t());
  current(h).emplace_back(my_class_t());
  current(h).emplace_back(my_class_t());

  draw(current(h), cout, 0);
  cout << "-------------" << endl;
  std::reverse(current(h).begin(), current(h).end());

  commit(h);  // сохраняем текущую и ее копируем на верх.
  cout << "-------------" << endl;

  //current(h).emplace_back(current(h));
  current(h).emplace_back(my_class_t());
  current(h)[4] = my_class_t();

  draw(current(h), cout, 0);
  cout << "-------------" << endl;

  undo(h);
  draw(current(h), cout, 0);
}

namespace database {
/**
  \fixme const troubles

  \attention how reduce 2 hier.?
*/
namespace detail {
struct pq_tag { };
struct sqlite_tab { };

template <typename T> struct holder_traits;
template <> struct holder_traits<string> {
  typedef sqlite_tab category;
};

template <> struct holder_traits<int> {
  typedef pq_tag category;
};
}

namespace real_objs {
// no inh.!
struct sqlite {
  sqlite(string _s) : s(_s) { }
  void drop() { }
  string build() {
    return s;
  }

  string s;
};

struct sqlite_builder {
  //sqlite_builder()
};

struct postgresql {
  postgresql(int _s) : s(_s) { }
  void drop() { }

  int build() {
    return s;
  }

  int s;
};
}  // space

// Dropable
class object_t {
public:
  template<typename T>
  object_t(const T& x) : self_(std::make_shared<model<T>>(move(x)))
  { }

  // FIXME: it's bad. must be friend?
  void drop()
  {
    // no way to know dyn. type
    self_->drop_();
  }

  // generate
  //template <typename T

private:
  class concept_t {
  public:
    virtual ~concept_t() = default;
    virtual void drop_() = 0;
    virtual ps_sample_shared_extend::object_t build_() = 0;
  };

  template<typename T>
  struct model : concept_t {
    model(const T& x) : data_(move(x)) { }
    void drop_() override
    { data_.drop(); }

    ps_sample_shared_extend::object_t build_() override {
      return ps_sample_shared_extend::object_t(data_.build());
    }

    T data_;
  };

  std::shared_ptr<
    //const  // can't
    concept_t> self_;  // ссылки на immutable
};

// Fabric:
template<typename T>
object_t create(std::weak_ptr<T> p) {
  return object_t(0);
}

// by value, not by type
enum db_vars { DB_SQLITE, DB_POSTGRES };

//if (selector == DB_POSTGRES)
object_t build_data_base(const int selector) {
  using namespace real_objs;
  if (selector == DB_SQLITE)
    return object_t(sqlite(""));
  else
    return object_t(postgresql(0));
}
}

TEST(DB, Test) {
  using namespace database;
  using namespace database::real_objs;

  // db.registerBeanClass<Obj>()
  auto a = object_t(sqlite(""));
  auto b = object_t(postgresql(0));
  a.drop();

  // FIXME: how connect multy DB drivers?

  int selector = 0;
  auto db = build_data_base(selector);

  // 1. Put handler - db specific - to queries builder
  // 2. Builder store in some high level object
  // 3. Want make queries on base getted handler
}
<|endoftext|>


<commit_before>/*!
  \copyright (c) RDO-Team, 2012
  \file      generate.cpp
  \authors     (rdo@rk9.bmstu.ru)
  \authors     (dluschan@rk9.bmstu.ru)
  \date      12.02.2012
  \brief       GENERATE
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
#include "simulator/runtime/pch.h"
// ----------------------------------------------------------------------- INCLUDES
// ----------------------------------------------------------------------- SYNOPSIS
#include "utils/rdotypes.h"
#include "utils/rdomacros.h"
#include "simulator/runtime/process/generate.h"
#include "simulator/runtime/calc/calc_base.h"
// --------------------------------------------------------------------------------

OPEN_RDO_RUNTIME_NAMESPACE

// --------------------------------------------------------------------------------
// -------------------- RDOPROCGenerate
// --------------------------------------------------------------------------------
void RDOPROCGenerate::onStart( CREF(LPRDORuntime) pRuntime )
{
	calcNextTimeInterval(pRuntime);
}

rbool RDOPROCGenerate::onCheckCondition( CREF(LPRDORuntime) pRuntime )
{
	if (m_maxTransCount > 0)
	{
		if (m_TransCount < m_maxTransCount)
		{
			return pRuntime->getCurrentTime() >= timeNext ? true : false;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return pRuntime->getCurrentTime() >= timeNext ? true : false;
	}
}

IBaseOperation::BOResult RDOPROCGenerate::onDoOperation(CREF(LPRDORuntime) pRuntime)
{
	++m_TransCount;

	m_pStatistics->setTransCount(m_TransCount);

	RDOValue pValue = m_pCreateAndGoOnTransactCalc->calcValue(pRuntime);

	LPRDOPROCTransact pTransact = pValue.getPointerSafety<RDOResourceTypeTransact>();
	ASSERT(pTransact);

	pTransact->setBlock(this);
	pTransact->next();

	PTR(RDOTrace) tracer = pRuntime->getTracer();
	if (!tracer->isNull())
	{
		tracer->getOStream() << pTransact->traceResourceState('\0', pRuntime) << tracer->getEOL();
	}

	calcNextTimeInterval(pRuntime);
	return IBaseOperation::BOR_done;
}

void RDOPROCGenerate::calcNextTimeInterval( CREF(LPRDORuntime) pRuntime )
{
	pRuntime->addTimePoint( timeNext = m_pTimeCalc->calcValue(pRuntime).getDouble() + pRuntime->getCurrentTime(), m_process, this );
}

void RDOPROCGenerate::onStop(CREF(LPRDORuntime) pRuntime)
{
	UNUSED(pRuntime);
}

void RDOPROCGenerate::onMakePlaned(CREF(LPRDORuntime) pRuntime, PTR(void) pParam)
{
	UNUSED(pRuntime);
	UNUSED(pParam  );
}

IBaseOperation::BOResult RDOPROCGenerate::onContinue(CREF(LPRDORuntime) pRuntime)
{
	UNUSED(pRuntime);
	return IBaseOperation::BOR_cant_run;
}

void RDOPROCGenerate::setStatistics(CREF(LPIInternalStatistics) pStatistics)
{
	m_pStatistics = pStatistics;
}

CLOSE_RDO_RUNTIME_NAMESPACE
<commit_msg>* merged from trunk, rev 7699<commit_after>/*!
  \copyright (c) RDO-Team, 2012
  \file      generate.cpp
  \authors     (rdo@rk9.bmstu.ru)
  \authors     (dluschan@rk9.bmstu.ru)
  \date      12.02.2012
  \brief       GENERATE
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
#include "simulator/runtime/pch.h"
// ----------------------------------------------------------------------- INCLUDES
// ----------------------------------------------------------------------- SYNOPSIS
#include "utils/rdotypes.h"
#include "utils/rdomacros.h"
#include "simulator/runtime/process/generate.h"
#include "simulator/runtime/calc/calc_base.h"
// --------------------------------------------------------------------------------

OPEN_RDO_RUNTIME_NAMESPACE

// --------------------------------------------------------------------------------
// -------------------- RDOPROCGenerate
// --------------------------------------------------------------------------------
void RDOPROCGenerate::onStart( CREF(LPRDORuntime) pRuntime )
{
	calcNextTimeInterval(pRuntime);
}

rbool RDOPROCGenerate::onCheckCondition( CREF(LPRDORuntime) pRuntime )
{
	if (m_maxTransCount > 0)
	{
		if (m_TransCount < m_maxTransCount)
		{
			return pRuntime->getCurrentTime() >= timeNext ? true : false;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return pRuntime->getCurrentTime() >= timeNext ? true : false;
	}
}

IBaseOperation::BOResult RDOPROCGenerate::onDoOperation(CREF(LPRDORuntime) pRuntime)
{
	++m_TransCount;

	if (m_pStatistics)
		m_pStatistics->setTransCount(m_TransCount);

	RDOValue pValue = m_pCreateAndGoOnTransactCalc->calcValue(pRuntime);

	LPRDOPROCTransact pTransact = pValue.getPointerSafety<RDOResourceTypeTransact>();
	ASSERT(pTransact);

	pTransact->setBlock(this);
	pTransact->next();

	PTR(RDOTrace) tracer = pRuntime->getTracer();
	if (!tracer->isNull())
	{
		tracer->getOStream() << pTransact->traceResourceState('\0', pRuntime) << tracer->getEOL();
	}

	calcNextTimeInterval(pRuntime);
	return IBaseOperation::BOR_done;
}

void RDOPROCGenerate::calcNextTimeInterval( CREF(LPRDORuntime) pRuntime )
{
	pRuntime->addTimePoint( timeNext = m_pTimeCalc->calcValue(pRuntime).getDouble() + pRuntime->getCurrentTime(), m_process, this );
}

void RDOPROCGenerate::onStop(CREF(LPRDORuntime) pRuntime)
{
	UNUSED(pRuntime);
}

void RDOPROCGenerate::onMakePlaned(CREF(LPRDORuntime) pRuntime, PTR(void) pParam)
{
	UNUSED(pRuntime);
	UNUSED(pParam  );
}

IBaseOperation::BOResult RDOPROCGenerate::onContinue(CREF(LPRDORuntime) pRuntime)
{
	UNUSED(pRuntime);
	return IBaseOperation::BOR_cant_run;
}

void RDOPROCGenerate::setStatistics(CREF(LPIInternalStatistics) pStatistics)
{
	m_pStatistics = pStatistics;
}

CLOSE_RDO_RUNTIME_NAMESPACE
<|endoftext|>