<commit_before>/** \brief Updates Zeder (via Ingo's SQL database) w/ the last N issues of harvested articles for each journal.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018-2020 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include "Compiler.h"
#include "DbConnection.h"
#include "DnsUtil.h"
#include "IniFile.h"
#include "MARC.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "UBTools.h"
#include "util.h"
#include "Zeder.h"

namespace {

// Please note that Zeder PPN entries are separated by spaces and, unlike what the column names "print_ppn" and
// "online_ppn" imply may in rare cases contain space-separated lists of PPN's.
inline auto SplitZederPPNs(const std::string &ppns) {
    std::vector<std::string> individual_ppns;
    StringUtil::Split(ppns, ' ', &individual_ppns);
    return individual_ppns;
}

struct ZederIdAndType {
    unsigned zeder_id_;
    char type_; // 'p' or 'e' for "print" or "electronic"
public:
    ZederIdAndType(const unsigned zeder_id, const char type)
        : zeder_id_(zeder_id), type_(type) { }
};

std::unordered_map<std::string, ZederIdAndType> GetPPNsToZederIdsAndTypesMap(const std::string &system_type) {
    std::unordered_map<std::string, ZederIdAndType> ppns_to_zeder_ids_and_types_map;

    const Zeder::SimpleZeder zeder(system_type == "ixtheo" ? Zeder::IXTHEO : Zeder::KRIMDOK, { "eppn", "pppn" });
    if (unlikely(zeder.empty()))
        LOG_ERROR("found no Zeder entries matching any of our requested columns!"
                  " (This *should* not happen as we included the column ID!)");

    unsigned included_journal_count(0);
    std::set<std::string> bundle_ppns; // We use a std::set because it is automatically being sorted for us.
    for (const auto &journal : zeder) {
        if (journal.empty())
            continue;
        ++included_journal_count;

        const auto print_ppns(SplitZederPPNs(journal.lookup("pppn")));
        const auto online_ppns(SplitZederPPNs(journal.lookup("eppn")));

        if (print_ppns.empty() and online_ppns.empty()) {
            --included_journal_count;
            LOG_WARNING("Zeder entry #" + std::to_string(journal.getId()) + " is missing print and online PPN's!");
            continue;
        }

        for (const auto &print_ppn : print_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(print_ppn, ZederIdAndType(journal.getId(), 'p'));

        for (const auto &online_ppn : online_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(online_ppn, ZederIdAndType(journal.getId(), 'e'));
    }

    LOG_INFO("downloaded information for " + std::to_string(included_journal_count) + " journal(s) from Zeder.");

    return ppns_to_zeder_ids_and_types_map;
}

bool SplitPageNumbers(const std::string &possibly_combined_pages, unsigned * const start_page, unsigned * const end_page) {
    if (StringUtil::ToUnsigned(possibly_combined_pages, start_page)) {
        *end_page = *start_page;
        return true;
    } else if (possibly_combined_pages.length() >= 2
               and possibly_combined_pages[possibly_combined_pages.length() - 1] == 'f')
    {
        if (not StringUtil::ToUnsigned(possibly_combined_pages.substr(0, possibly_combined_pages.length() - 1), start_page))
            return false;
        *end_page = *start_page + 1;
        return true;
    }

    // Common case, page range with a hypen.
    const auto first_hyphen_pos(possibly_combined_pages.find('-'));
    if (first_hyphen_pos == std::string::npos)
        return false;

    return StringUtil::ToUnsigned(possibly_combined_pages.substr(0, first_hyphen_pos), start_page)
           and StringUtil::ToUnsigned(possibly_combined_pages.substr(first_hyphen_pos + 1), end_page);
}

// \return the year as a small integer or 0 if we could not parse it.
unsigned short YearStringToShort(const std::string &year_as_string) {
    unsigned short year_as_unsigned_short;
    if (not StringUtil::ToUnsignedShort(year_as_string, &year_as_unsigned_short))
        return 0;
    return year_as_unsigned_short;
}

void ProcessRecords(MARC::Reader * const reader, MARC::Writer * const writer, const std::string &system_type,
                    const std::unordered_map<std::string, ZederIdAndType> &ppns_to_zeder_ids_and_types_map,
                    DbConnection * const db_connection)
{
    const auto zeder_flavour(system_type == "krimdok" ? Zeder::KRIMDOK : Zeder::IXTHEO);
    const auto JOB_START_TIME(std::to_string(std::time(nullptr)));
    const auto HOSTNAME(DnsUtil::GetHostname());
    const std::string ZEDER_URL_PREFIX(Zeder::GetFullDumpEndpointPath(zeder_flavour) + "#suche=Z%3D");

    unsigned total_count(0), inserted_count(0);
    while (const auto record = reader->read()) {
        ++total_count;
        writer->write(record); // For the next pipeline phase.

        const std::string superior_control_number(record.getSuperiorControlNumber());
        if (superior_control_number.empty())
            continue;

        const auto zeder_id_and_type(ppns_to_zeder_ids_and_types_map.find(superior_control_number));
        if (zeder_id_and_type == ppns_to_zeder_ids_and_types_map.cend())
            continue;

        const auto _936_field(record.findTag("936"));
        if (_936_field == record.end())
            continue;

        const std::string pages(_936_field->getFirstSubfieldWithCode('h'));
        std::string volume;
        std::string issue(_936_field->getFirstSubfieldWithCode('e'));
        if (issue.empty())
            issue = _936_field->getFirstSubfieldWithCode('d');
        else
            volume = _936_field->getFirstSubfieldWithCode('d');
        const std::string year(_936_field->getFirstSubfieldWithCode('j'));

        std::map<std::string, std::string> column_names_to_values_map{
            { "timestamp",     JOB_START_TIME                                                         },
            { "Quellrechner",  HOSTNAME                                                               },
            { "Systemtyp",     system_type,                                                           },
            { "Zeder_ID",      std::to_string(zeder_id_and_type->second.zeder_id_)                    },
            { "Zeder_URL",     ZEDER_URL_PREFIX + std::to_string(zeder_id_and_type->second.zeder_id_) },
            { "PPN_Typ",       std::string(1, zeder_id_and_type->second.type_)                        },
            { "PPN",           superior_control_number                                                },
            { "Jahr",          std::to_string(YearStringToShort(year))                                },
            { "Band",          volume                                                                 },
            { "Heft",          issue                                                                  },
            { "Seitenbereich", pages                                                                  }
        };

        unsigned start_page, end_page;
        if (SplitPageNumbers(pages, &start_page, &end_page)) {
            column_names_to_values_map["Startseite"] = StringUtil::ToString(start_page);
            column_names_to_values_map["Endseite"]   = StringUtil::ToString(end_page);
        }

        db_connection->insertIntoTableOrDie("zeder.erschliessung", column_names_to_values_map);
        ++inserted_count;
    }

    LOG_INFO("Processed " + std::to_string(total_count) + " records and inserted " + std::to_string(inserted_count)
             + " into Ingo's database.");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 4)
        ::Usage("[--min-log-level=log_level] system_type marc_input marc_output\n"
                "\twhere \"system_type\" must be one of ixtheo|krimdok");

    std::string system_type;
    if (__builtin_strcmp("ixtheo", argv[1]))
        system_type = "ixtheo";
    else if (__builtin_strcmp("krimdok", argv[1]))
        system_type = "krimdok";
    else
        LOG_ERROR("system_type must be one of ixtheo|krimdok|relbib!");

    const auto ppns_to_zeder_ids_and_types_map(GetPPNsToZederIdsAndTypesMap(system_type));

    IniFile ini_file;
    DbConnection db_connection(ini_file);

    const auto marc_reader(MARC::Reader::Factory(argv[2]));
    const auto marc_writer(MARC::Writer::Factory(argv[3]));
    ProcessRecords(marc_reader.get(), marc_writer.get(), system_type, ppns_to_zeder_ids_and_types_map, &db_connection);

    return EXIT_SUCCESS;
}
<commit_msg>Fixed some obvious errors.<commit_after>/** \brief Updates Zeder (via Ingo's SQL database) w/ the last N issues of harvested articles for each journal.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018-2020 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include "Compiler.h"
#include "DbConnection.h"
#include "DnsUtil.h"
#include "IniFile.h"
#include "MARC.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "UBTools.h"
#include "util.h"
#include "Zeder.h"

namespace {

// Please note that Zeder PPN entries are separated by spaces and, unlike what the column names "print_ppn" and
// "online_ppn" imply may in rare cases contain space-separated lists of PPN's.
inline auto SplitZederPPNs(const std::string &ppns) {
    std::vector<std::string> individual_ppns;
    StringUtil::Split(ppns, ' ', &individual_ppns);
    return individual_ppns;
}

struct ZederIdAndType {
    unsigned zeder_id_;
    char type_; // 'p' or 'e' for "print" or "electronic"
public:
    ZederIdAndType(const unsigned zeder_id, const char type)
        : zeder_id_(zeder_id), type_(type) { }
};

std::unordered_map<std::string, ZederIdAndType> GetPPNsToZederIdsAndTypesMap(const std::string &system_type) {
    std::unordered_map<std::string, ZederIdAndType> ppns_to_zeder_ids_and_types_map;

    const Zeder::SimpleZeder zeder(system_type == "ixtheo" ? Zeder::IXTHEO : Zeder::KRIMDOK, { "eppn", "pppn" });
    if (unlikely(zeder.empty()))
        LOG_ERROR("found no Zeder entries matching any of our requested columns!"
                  " (This *should* not happen as we included the column ID!)");

    unsigned included_journal_count(0);
    std::set<std::string> bundle_ppns; // We use a std::set because it is automatically being sorted for us.
    for (const auto &journal : zeder) {
        if (journal.empty())
            continue;
        ++included_journal_count;

        const auto print_ppns(SplitZederPPNs(journal.lookup("pppn")));
        const auto online_ppns(SplitZederPPNs(journal.lookup("eppn")));

        if (print_ppns.empty() and online_ppns.empty()) {
            --included_journal_count;
            LOG_WARNING("Zeder entry #" + std::to_string(journal.getId()) + " is missing print and online PPN's!");
            continue;
        }

        for (const auto &print_ppn : print_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(print_ppn, ZederIdAndType(journal.getId(), 'p'));

        for (const auto &online_ppn : online_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(online_ppn, ZederIdAndType(journal.getId(), 'e'));
    }

    LOG_INFO("downloaded information for " + std::to_string(included_journal_count) + " journal(s) from Zeder.");

    return ppns_to_zeder_ids_and_types_map;
}

bool SplitPageNumbers(const std::string &possibly_combined_pages, unsigned * const start_page, unsigned * const end_page) {
    if (StringUtil::ToUnsigned(possibly_combined_pages, start_page)) {
        *end_page = *start_page;
        return true;
    } else if (possibly_combined_pages.length() >= 2
               and possibly_combined_pages[possibly_combined_pages.length() - 1] == 'f')
    {
        if (not StringUtil::ToUnsigned(possibly_combined_pages.substr(0, possibly_combined_pages.length() - 1), start_page))
            return false;
        *end_page = *start_page + 1;
        return true;
    }

    // Common case, page range with a hypen.
    const auto first_hyphen_pos(possibly_combined_pages.find('-'));
    if (first_hyphen_pos == std::string::npos)
        return false;

    return StringUtil::ToUnsigned(possibly_combined_pages.substr(0, first_hyphen_pos), start_page)
           and StringUtil::ToUnsigned(possibly_combined_pages.substr(first_hyphen_pos + 1), end_page);
}

// \return the year as a small integer or 0 if we could not parse it.
unsigned short YearStringToShort(const std::string &year_as_string) {
    unsigned short year_as_unsigned_short;
    if (not StringUtil::ToUnsignedShort(year_as_string, &year_as_unsigned_short))
        return 0;
    return year_as_unsigned_short;
}

void ProcessRecords(MARC::Reader * const reader, MARC::Writer * const writer, const std::string &system_type,
                    const std::unordered_map<std::string, ZederIdAndType> &ppns_to_zeder_ids_and_types_map,
                    DbConnection * const db_connection)
{
    const auto zeder_flavour(system_type == "krimdok" ? Zeder::KRIMDOK : Zeder::IXTHEO);
    const auto JOB_START_TIME(std::to_string(std::time(nullptr)));
    const auto HOSTNAME(DnsUtil::GetHostname());
    const std::string ZEDER_URL_PREFIX(Zeder::GetFullDumpEndpointPath(zeder_flavour) + "#suche=Z%3D");

    unsigned total_count(0), inserted_count(0);
    while (const auto record = reader->read()) {
        ++total_count;
        writer->write(record); // For the next pipeline phase.

        const std::string superior_control_number(record.getSuperiorControlNumber());
        if (superior_control_number.empty())
            continue;

        const auto zeder_id_and_type(ppns_to_zeder_ids_and_types_map.find(superior_control_number));
        if (zeder_id_and_type == ppns_to_zeder_ids_and_types_map.cend())
            continue;

        const auto _936_field(record.findTag("936"));
        if (_936_field == record.end())
            continue;

        const std::string pages(_936_field->getFirstSubfieldWithCode('h'));
        std::string volume;
        std::string issue(_936_field->getFirstSubfieldWithCode('e'));
        if (issue.empty())
            issue = _936_field->getFirstSubfieldWithCode('d');
        else
            volume = _936_field->getFirstSubfieldWithCode('d');
        const std::string year(_936_field->getFirstSubfieldWithCode('j'));

        std::map<std::string, std::string> column_names_to_values_map{
            { "timestamp",     JOB_START_TIME                                                         },
            { "Quellrechner",  HOSTNAME                                                               },
            { "Systemtyp",     system_type,                                                           },
            { "Zeder_ID",      std::to_string(zeder_id_and_type->second.zeder_id_)                    },
            { "Zeder_URL",     ZEDER_URL_PREFIX + std::to_string(zeder_id_and_type->second.zeder_id_) },
            { "PPN_Typ",       std::string(1, zeder_id_and_type->second.type_)                        },
            { "PPN",           superior_control_number                                                },
            { "Jahr",          std::to_string(YearStringToShort(year))                                },
            { "Band",          volume                                                                 },
            { "Heft",          issue                                                                  },
            { "Seitenbereich", pages                                                                  }
        };

        unsigned start_page, end_page;
        if (SplitPageNumbers(pages, &start_page, &end_page)) {
            column_names_to_values_map["Startseite"] = StringUtil::ToString(start_page);
            column_names_to_values_map["Endseite"]   = StringUtil::ToString(end_page);
        }

        db_connection->insertIntoTableOrDie("zeder.erschliessung", column_names_to_values_map);
        ++inserted_count;
    }

    LOG_INFO("Processed " + std::to_string(total_count) + " records and inserted " + std::to_string(inserted_count)
             + " into Ingo's database.");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 4)
        ::Usage("[--min-log-level=log_level] system_type marc_input marc_output\n"
                "\twhere \"system_type\" must be one of ixtheo|krimdok");

    std::string system_type;
    if (__builtin_strcmp("ixtheo", argv[1]) == 0)
        system_type = "ixtheo";
    else if (__builtin_strcmp("krimdok", argv[1]) == 0)
        system_type = "krimdok";
    else
        LOG_ERROR("system_type must be one of ixtheo|krimdok!");

    const auto ppns_to_zeder_ids_and_types_map(GetPPNsToZederIdsAndTypesMap(system_type));

    IniFile ini_file;
    DbConnection db_connection(ini_file);

    const auto marc_reader(MARC::Reader::Factory(argv[2]));
    const auto marc_writer(MARC::Writer::Factory(argv[3]));
    ProcessRecords(marc_reader.get(), marc_writer.get(), system_type, ppns_to_zeder_ids_and_types_map, &db_connection);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/** \file    populate_in_tuebingen_available.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool that adds a new "SIG" field to a MARC record if there are UB or IFK call numbers in a record.
 */

/*
    Copyright (C) 2015, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <cstdlib>
#include "DirectoryEntry.h"
#include "Leader.h"
#include "MarcUtil.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << progname << " [--verbose] marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

static FILE *output_ptr;
static unsigned modified_record_count;
static unsigned add_sig_count;

bool ProcessRecord(std::shared_ptr <Leader> &leader, std::vector <DirectoryEntry> *const dir_entries,
                   std::vector <std::string> *const field_data, std::string *const /*err_msg*/) {
    std::vector <std::pair<size_t, size_t>> local_block_boundaries;
    MarcUtil::FindAllLocalDataBlocks(*dir_entries, *field_data, &local_block_boundaries);

    bool modified_record(false);
    for (const auto &block_start_and_end : local_block_boundaries) {
        std::vector <size_t> _852_field_indices;
        if (MarcUtil::FindFieldsInLocalBlock("852", "??", block_start_and_end, *field_data, &_852_field_indices) == 0)
            continue;
        for (const size_t _852_index : _852_field_indices) {
            const Subfields subfields1((*field_data)[_852_index]);
            const std::string isil_subfield(subfields1.getFirstSubfieldValue('a'));

            if (isil_subfield != "DE-21" and isil_subfield != "DE-21-110")
                continue;

            const std::string institution(isil_subfield == "DE-21" ? "UB: " : "IFK: ");
            if (_852_index + 1 < block_start_and_end.second) {
                const Subfields subfields2((*field_data)[_852_index + 1]);
                const std::string c_subfield(subfields2.getFirstSubfieldValue('c'));
                if (not c_subfield.empty()) {
                    const std::string institution_and_call_number(institution + c_subfield);
                    ++add_sig_count;
                    modified_record = true;
                    MarcUtil::InsertField(institution_and_call_number, "SIG", leader, dir_entries, field_data);
                }
            }
            break;
        }
    }

    if (modified_record)
        ++modified_record_count;

    MarcUtil::ComposeAndWriteRecord(output_ptr, *dir_entries, *field_data, leader);
    return true;
}

void PopulateTheInTuebingenAvailableField(const bool verbose, FILE *const input, FILE *const output) {
    output_ptr = output;

    std::string err_msg;
    if (not MarcUtil::ProcessRecords(input, ProcessRecord, &err_msg))
        Error("error while processing records: " + err_msg);

    if (verbose) {
        std::cout << "Modified " << modified_record_count << " records.\n";
        std::cout << "Added " << add_sig_count << " signature fields.\n";
    }
}

int main(int argc, char **argv) {
    progname = argv[0];

    if (argc != 3 and argc != 4)
        Usage();

    bool verbose;
    if (argc == 3)
        verbose = false;
    else { // argc == 4
        if (std::strcmp(argv[1], "--verbose") != 0)
            Usage();
        verbose = true;
    }

    const std::string marc_input_filename(argv[argc == 3 ? 1 : 2]);
    FILE *const marc_input(std::fopen(marc_input_filename.c_str(), "rbm"));
    if (marc_input == nullptr)
        Error("can't open \"" + marc_input_filename + "\" for reading!");

    const std::string marc_output_filename(argv[argc == 3 ? 2 : 3]);
    FILE *const marc_output(std::fopen(marc_output_filename.c_str(), "wb"));
    if (marc_output == nullptr)
        Error("can't open \"" + marc_output_filename + "\" for writing!");

    PopulateTheInTuebingenAvailableField(verbose, marc_input, marc_output);

    std::fclose(marc_input);
    std::fclose(marc_output);
}
<commit_msg>Changed signatures-string to multi value field<commit_after>/** \file    populate_in_tuebingen_available.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool that adds a new "SIG" field to a MARC record if there are UB or IFK call numbers in a record.
 */

/*
    Copyright (C) 2015, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <cstdlib>
#include "DirectoryEntry.h"
#include "Leader.h"
#include "MarcUtil.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << progname << " [--verbose] marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

static FILE *output_ptr;
static unsigned modified_record_count;
static unsigned add_sig_count;

bool ProcessRecord(std::shared_ptr <Leader> &leader, std::vector <DirectoryEntry> * const dir_entries,
                   std::vector <std::string> * const field_data, std::string * const /*err_msg*/) {
    std::vector <std::pair<size_t, size_t>> local_block_boundaries;
    MarcUtil::FindAllLocalDataBlocks(*dir_entries, *field_data, &local_block_boundaries);

    bool modified_record(false);
    for (const auto &block_start_and_end : local_block_boundaries) {
        std::vector <size_t> _852_field_indices;
        if (MarcUtil::FindFieldsInLocalBlock("852", "??", block_start_and_end, *field_data, &_852_field_indices) == 0)
            continue;
        for (const size_t _852_index : _852_field_indices) {
            const Subfields subfields1((*field_data)[_852_index]);
            const std::string isil_subfield(subfields1.getFirstSubfieldValue('a'));

            if (isil_subfield != "DE-21" and isil_subfield != "DE-21-110")
                continue;

            const std::string institution(isil_subfield == "DE-21" ? "UB: " : "IFK: ");
            if (_852_index + 1 < block_start_and_end.second) {
                const Subfields subfields2((*field_data)[_852_index + 1]);
                const std::string c_subfield(subfields2.getFirstSubfieldValue('c'));
                if (not c_subfield.empty()) {
                    const std::string institution_and_call_number(institution + c_subfield);
                    ++add_sig_count;
                    modified_record = true;
                    MarcUtil::InsertField(institution_and_call_number, "SIG", leader, dir_entries, field_data);
                }
            }
            break;
        }
    }

    if (modified_record)
        ++modified_record_count;

    MarcUtil::ComposeAndWriteRecord(output_ptr, *dir_entries, *field_data, leader);
    return true;
}

void PopulateTheInTuebingenAvailableField(const bool verbose, FILE * const input, FILE * const output) {
    output_ptr = output;

    std::string err_msg;
    if (not MarcUtil::ProcessRecords(input, ProcessRecord, &err_msg))
        Error("error while processing records: " + err_msg);

    if (verbose) {
        std::cout << "Modified " << modified_record_count << " records.\n";
        std::cout << "Added " << add_sig_count << " signature fields.\n";
    }
}

int main(int argc, char **argv) {
    progname = argv[0];

    if (argc != 3 and argc != 4)
        Usage();

    bool verbose;
    if (argc == 3)
        verbose = false;
    else { // argc == 4
        if (std::strcmp(argv[1], "--verbose") != 0)
            Usage();
        verbose = true;
    }

    const std::string marc_input_filename(argv[argc == 3 ? 1 : 2]);
    FILE * const marc_input(std::fopen(marc_input_filename.c_str(), "rbm"));
    if (marc_input == nullptr)
        Error("can't open \"" + marc_input_filename + "\" for reading!");

    const std::string marc_output_filename(argv[argc == 3 ? 2 : 3]);
    FILE * const marc_output(std::fopen(marc_output_filename.c_str(), "wb"));
    if (marc_output == nullptr)
        Error("can't open \"" + marc_output_filename + "\" for writing!");

    PopulateTheInTuebingenAvailableField(verbose, marc_input, marc_output);

    std::fclose(marc_input);
    std::fclose(marc_output);
}
<|endoftext|>


<commit_before>/* Copyright 2018 Iakov Kirilenko, CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include <QProcess>
#include <QsLog.h>
#include <QFileInfo>
#include <QVector>

#include <trikNetwork/mailboxInterface.h>
#include <trikKernel/paths.h>
#include <trikKernel/exceptions/internalErrorException.h>
#include <trikScriptRunnerInterface.h>

#include "pythonEngineWorker.h"
#include <Python.h>
#include "PythonQtConversion.h"

using namespace trikScriptRunner;

QAtomicInt PythonEngineWorker::initCounter = 0;

static int quitFromPython(void*) {
	PyErr_SetInterrupt();
	return 0;
}

static void abortPythonInterpreter() {
	if(!Py_IsInitialized()) {
		return;
	}
	PythonQtGILScope _;
	Py_AddPendingCall(&quitFromPython, nullptr);
}

PythonEngineWorker::PythonEngineWorker(trikControl::BrickInterface &brick
		, trikNetwork::MailboxInterface * const mailbox
		)
	: mBrick(brick)
	, mScriptExecutionControl(new ScriptExecutionControl())
	, mMailbox(mailbox)
	, mState(ready)
{}

PythonEngineWorker::~PythonEngineWorker()
{
	stopScript();
	{
		// In python at least before 3.7 (3.5,3.6)
		// we need to make all pending calls before the context
		// is destroyed, otherwise python crashes
		PythonQtGILScope _;
		Py_MakePendingCalls();
		mMainContext = nullptr;
		if (mPyInterpreter) {
			Py_EndInterpreter(mPyInterpreter);
			mPyInterpreter = nullptr;
		}
	}

	if (--initCounter == 0) {
		Py_Finalize();
		PyMem_RawFree(mProgramName);
		PyMem_RawFree(mPythonPath);
		if (PythonQt::self()) {
			PythonQt::cleanup();
		}
	}
}

void PythonEngineWorker::init()
{
	if (initCounter++ == 0) {
		mProgramName = Py_DecodeLocale("trikPythonRuntime", nullptr);
		Py_SetProgramName(mProgramName);
		constexpr auto varName = "TRIK_PYTHONPATH";
		auto const &path = QProcessEnvironment::systemEnvironment().value(varName);
		if (path.isEmpty()) {
			auto const &e = QString("%1 must be set to correct value").arg(varName);
			QLOG_FATAL() << e;
			throw trikKernel::InternalErrorException(e);
		} else {
			QLOG_INFO() << varName << ":" << path;
		}

		/// TODO: Must point to local .zip file
		mPythonPath = Py_DecodeLocale(path.toStdString().data(), nullptr);
		Py_SetPath(mPythonPath);

/* uncomment for verbosity
		Py_VerboseFlag = 3;
		Py_InspectFlag = 1;
		Py_DebugFlag = 2;
// */
		Py_IsolatedFlag = 1;
		Py_BytesWarningFlag = 3;
		Py_DontWriteBytecodeFlag = 1;
		Py_NoSiteFlag = 1;
		Py_NoUserSiteDirectory = 1;

		Py_Initialize();
		PyEval_InitThreads(); // For Python < 3.7
	}

	if (!mPyInterpreter) {
//		mPyInterpreter = Py_NewInterpreter();
	}

	if (!PythonQt::self()) {
		PythonQt::setEnableThreadSupport(true);
		PythonQtGILScope _;
		PythonQt::init(PythonQt::RedirectStdOut | PythonQt::PythonAlreadyInitialized);
		connect(PythonQt::self(), &PythonQt::pythonStdErr, this, &PythonEngineWorker::updateErrorMessage);
		connect(PythonQt::self(), &PythonQt::pythonStdOut, this, &PythonEngineWorker::sendStdOutMessage);
		PythonQtRegisterListTemplateConverter(QVector, uint8_t)
		PythonQt_QtAll::init();
	}
	if (!mMainContext) {
		mMainContext = PythonQt::self()->getMainModule();
		recreateContext();
	}
	emit inited();
}

bool PythonEngineWorker::recreateContext()
{
	{
		PythonQtGILScope _;
		Py_MakePendingCalls();
		PyErr_CheckSignals();
		PyErr_Clear();
	}
	PythonQt::self()->clearError();
	return initTrik();
}

bool PythonEngineWorker::evalSystemPy()
{
	const QString systemPyPath = trikKernel::Paths::systemScriptsPath() + "system.py";

	if (!QFileInfo::exists(systemPyPath)) {
		QLOG_ERROR() << "system.py not found, path:" << systemPyPath;
		return false;
	}
	mMainContext.evalFile(systemPyPath);
	if (PythonQt::self()->hadError()) {
		QLOG_ERROR() << "Failed to eval system.py";
		return false;
	}
	return true;
}

bool PythonEngineWorker::initTrik()
{
	PythonQt_init_PyTrikControl(mMainContext);
	mMainContext.addObject("brick", &mBrick);
	mMainContext.addObject("script", mScriptExecutionControl.data());

	//return true;
	return evalSystemPy();
}

void PythonEngineWorker::resetBrick()
{
	QLOG_INFO() << "Stopping robot";

	if (mMailbox) {
		mMailbox->stopWaiting();
		mMailbox->clearQueue();
	}

	mBrick.reset();
}

void PythonEngineWorker::brickBeep()
{
	mBrick.playSound(trikKernel::Paths::mediaPath() + "media/beep_soft.wav");
}

void PythonEngineWorker::sendStdOutMessage(const QString &text)
{
	emit sendMessage(QString("print: %1").arg(text));
}

void PythonEngineWorker::stopScript()
{
	QMutexLocker locker(&mScriptStateMutex);

	if (mState == stopping) {
		// Already stopping, so we can do nothing.
		return;
	}

	if (mState == ready) {
		// Engine is ready for execution.
		return;
	}

	QLOG_INFO() << "PythonEngineWorker: stopping script";

	mState = stopping;

	if (QThread::currentThread() != thread()) {
		abortPythonInterpreter();
	} else {
		QLOG_FATAL() << "Attempt to abort Python from main thread.";
	}

	if (mMailbox) {
		mMailbox->stopWaiting();
	}

	mState = ready;

	/// @todo: is it actually stopped?

	QLOG_INFO() << "PythonEngineWorker: stopping complete";
}

QStringList PythonEngineWorker::knownNames() const
{
	QSet<QString> result = {"brick", "script", "threading"};
	TrikScriptRunnerInterface::Helper::collectMethodNames(result, &trikControl::BrickInterface::staticMetaObject);
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mScriptControl.metaObject());
	if (mMailbox) {
		result.insert("mailbox");
		TrikScriptRunnerInterface::Helper::collectMethodNames(result, mMailbox->metaObject());
	}
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mThreading.metaObject());
	return result.toList();
}

void PythonEngineWorker::run(const QString &script)
{
	QMutexLocker locker(&mScriptStateMutex);
	mState = starting;
	QMetaObject::invokeMethod(this, "doRun", Q_ARG(QString, script));
}

void PythonEngineWorker::doRun(const QString &script)
{
	emit startedScript("", 0);
	mErrorMessage.clear();
	/// When starting script execution (by any means), clear button states.
	mBrick.keys()->reset();
	mState = running;
	auto ok = recreateContext();
	if (!ok) {
		emit completed(mErrorMessage,0);
		return;
	}

	mMainContext.evalScript(script);

	QLOG_INFO() << "PythonEngineWorker: evaluation ended";

	auto wasError = mState != ready && PythonQt::self()->hadError();
	mState = ready;
	if (wasError) {
		emit completed(mErrorMessage, 0);
	} else {
		emit completed("", 0);
	}
}

void PythonEngineWorker::runDirect(const QString &command)
{
	QMutexLocker locker(&mScriptStateMutex);
	QMetaObject::invokeMethod(this, "doRunDirect", Q_ARG(QString, command));
}

void PythonEngineWorker::doRunDirect(const QString &command)
{
	emit startedDirectScript(0);
	if (PythonQt::self()->hadError()) {
		PythonQt::self()->clearError();
		mErrorMessage.clear();
		recreateContext();
	}
	mMainContext.evalScript(command);
	emit completed(mErrorMessage, 0);
}

void PythonEngineWorker::updateErrorMessage(const QString &err)
{
	mErrorMessage += err;
}

void PythonEngineWorker::onScriptRequestingToQuit()
{
	throw std::logic_error("Not implemented");
}
<commit_msg>Fix Travis issue<commit_after>/* Copyright 2018 Iakov Kirilenko, CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include <QProcess>
#include <QsLog.h>
#include <QFileInfo>
#include <QVector>

#include <trikNetwork/mailboxInterface.h>
#include <trikKernel/paths.h>
#include <trikKernel/exceptions/internalErrorException.h>
#include <trikScriptRunnerInterface.h>

#include "pythonEngineWorker.h"
#include <Python.h>
#include "PythonQtConversion.h"

using namespace trikScriptRunner;

QAtomicInt PythonEngineWorker::initCounter = 0;

static int quitFromPython(void*) {
	PyErr_SetInterrupt();
	return 0;
}

static void abortPythonInterpreter() {
	if(!Py_IsInitialized()) {
		return;
	}
	PythonQtGILScope _;
	Py_AddPendingCall(&quitFromPython, nullptr);
}

PythonEngineWorker::PythonEngineWorker(trikControl::BrickInterface &brick
		, trikNetwork::MailboxInterface * const mailbox
		)
	: mBrick(brick)
	, mScriptExecutionControl(new ScriptExecutionControl())
	, mMailbox(mailbox)
	, mState(ready)
{}

PythonEngineWorker::~PythonEngineWorker()
{
	stopScript();
	{
		// In python at least before 3.7 (3.5,3.6)
		// we need to make all pending calls before the context
		// is destroyed, otherwise python crashes
		PythonQtGILScope _;
		Py_MakePendingCalls();
		mMainContext = nullptr;
		if (mPyInterpreter) {
			Py_EndInterpreter(mPyInterpreter);
			mPyInterpreter = nullptr;
		}
	}

	if (--initCounter == 0) {
		Py_Finalize();
		PyMem_RawFree(mProgramName);
		PyMem_RawFree(mPythonPath);
		if (PythonQt::self()) {
			PythonQt::cleanup();
		}
	}
}

void PythonEngineWorker::init()
{
	if (initCounter++ == 0) {
		mProgramName = Py_DecodeLocale("trikPythonRuntime", nullptr);
		Py_SetProgramName(mProgramName);
		constexpr auto varName = "TRIK_PYTHONPATH";
		auto const &path = QProcessEnvironment::systemEnvironment().value(varName);
		if (path.isEmpty()) {
			auto const &e = QString("%1 must be set to correct value").arg(varName);
			QLOG_FATAL() << e;
			throw trikKernel::InternalErrorException(e);
		} else {
			QLOG_INFO() << varName << ":" << path;
		}

		/// TODO: Must point to local .zip file
		mPythonPath = Py_DecodeLocale(path.toStdString().data(), nullptr);
		Py_SetPath(mPythonPath);

/* uncomment for verbosity
		Py_VerboseFlag = 3;
		Py_InspectFlag = 1;
		Py_DebugFlag = 2;
// */
		Py_IsolatedFlag = 1;
		Py_BytesWarningFlag = 3;
		Py_DontWriteBytecodeFlag = 1;
		Py_NoSiteFlag = 1;
		Py_NoUserSiteDirectory = 1;

		Py_Initialize();
		PyEval_InitThreads(); // For Python < 3.7
	}

	if (!mPyInterpreter) {
//		mPyInterpreter = Py_NewInterpreter();
	}

	if (!PythonQt::self()) {
		PythonQt::setEnableThreadSupport(true);
		PythonQtGILScope _;
		PythonQt::init(PythonQt::RedirectStdOut | PythonQt::PythonAlreadyInitialized);
		connect(PythonQt::self(), &PythonQt::pythonStdErr, this, &PythonEngineWorker::updateErrorMessage);
		connect(PythonQt::self(), &PythonQt::pythonStdOut, this, &PythonEngineWorker::sendStdOutMessage);
		PythonQtRegisterListTemplateConverter(QVector, uint8_t)
		PythonQt_QtAll::init();
	}
	if (!mMainContext) {
		mMainContext = PythonQt::self()->getMainModule();
		recreateContext();
	}
	emit inited();
}

bool PythonEngineWorker::recreateContext()
{
	{
		PythonQtGILScope _;
		Py_MakePendingCalls();
		PyErr_CheckSignals();
		PyErr_Clear();
	}
	PythonQt::self()->clearError();
	return initTrik();
}

bool PythonEngineWorker::evalSystemPy()
{
	const QString systemPyPath = trikKernel::Paths::systemScriptsPath() + "system.py";

	if (!QFileInfo::exists(systemPyPath)) {
		QLOG_ERROR() << "system.py not found, path:" << systemPyPath;
		return false;
	}
	mMainContext.evalFile(systemPyPath);
	if (PythonQt::self()->hadError()) {
		QLOG_ERROR() << "Failed to eval system.py";
		return false;
	}
	return true;
}

bool PythonEngineWorker::initTrik()
{
	PythonQt_init_PyTrikControl(mMainContext);
	mMainContext.addObject("brick", &mBrick);
	mMainContext.addObject("script", mScriptExecutionControl.data());

	return evalSystemPy();
}

void PythonEngineWorker::resetBrick()
{
	QLOG_INFO() << "Stopping robot";

	if (mMailbox) {
		mMailbox->stopWaiting();
		mMailbox->clearQueue();
	}

	mBrick.reset();
}

void PythonEngineWorker::brickBeep()
{
	mBrick.playSound(trikKernel::Paths::mediaPath() + "media/beep_soft.wav");
}

void PythonEngineWorker::sendStdOutMessage(const QString &text)
{
	emit sendMessage(QString("print: %1").arg(text));
}

void PythonEngineWorker::stopScript()
{
	QMutexLocker locker(&mScriptStateMutex);

	if (mState == stopping) {
		// Already stopping, so we can do nothing.
		return;
	}

	if (mState == ready) {
		// Engine is ready for execution.
		return;
	}

	QLOG_INFO() << "PythonEngineWorker: stopping script";

	mState = stopping;

	if (QThread::currentThread() != thread()) {
		abortPythonInterpreter();
	} else {
		QLOG_FATAL() << "Attempt to abort Python from main thread.";
	}

	if (mMailbox) {
		mMailbox->stopWaiting();
	}

	mState = ready;

	/// @todo: is it actually stopped?

	QLOG_INFO() << "PythonEngineWorker: stopping complete";
}

QStringList PythonEngineWorker::knownNames() const
{
	QSet<QString> result = {"brick", "script", "threading"};
	TrikScriptRunnerInterface::Helper::collectMethodNames(result, &trikControl::BrickInterface::staticMetaObject);
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mScriptControl.metaObject());
	if (mMailbox) {
		result.insert("mailbox");
		TrikScriptRunnerInterface::Helper::collectMethodNames(result, mMailbox->metaObject());
	}
/// TODO:	TrikScriptRunnerInterface::Helper::collectMethodNames(result, mThreading.metaObject());
	return result.toList();
}

void PythonEngineWorker::run(const QString &script)
{
	QMutexLocker locker(&mScriptStateMutex);
	mState = starting;
	QMetaObject::invokeMethod(this, "doRun", Q_ARG(QString, script));
}

void PythonEngineWorker::doRun(const QString &script)
{
	emit startedScript("", 0);
	mErrorMessage.clear();
	/// When starting script execution (by any means), clear button states.
	mBrick.keys()->reset();
	mState = running;
	auto ok = recreateContext();
	if (!ok) {
		emit completed(mErrorMessage,0);
		return;
	}

	mMainContext.evalScript(script);

	QLOG_INFO() << "PythonEngineWorker: evaluation ended";

	auto wasError = mState != ready && PythonQt::self()->hadError();
	mState = ready;
	if (wasError) {
		emit completed(mErrorMessage, 0);
	} else {
		emit completed("", 0);
	}
}

void PythonEngineWorker::runDirect(const QString &command)
{
	QMutexLocker locker(&mScriptStateMutex);
	QMetaObject::invokeMethod(this, "doRunDirect", Q_ARG(QString, command));
}

void PythonEngineWorker::doRunDirect(const QString &command)
{
	emit startedDirectScript(0);
	if (PythonQt::self()->hadError()) {
		PythonQt::self()->clearError();
		mErrorMessage.clear();
		recreateContext();
	}
	mMainContext.evalScript(command);
	emit completed(mErrorMessage, 0);
}

void PythonEngineWorker::updateErrorMessage(const QString &err)
{
	mErrorMessage += err;
}

void PythonEngineWorker::onScriptRequestingToQuit()
{
	throw std::logic_error("Not implemented");
}
<|endoftext|>


<commit_before><commit_msg>ODBC: binary data is not null-terminated<commit_after><|endoftext|>


<commit_before>/*
*   Copyright (c) 2010-2016, MIT Probabilistic Computing Project
*
*   Lead Developers: Dan Lovell and Jay Baxter
*   Authors: Dan Lovell, Baxter Eaves, Jay Baxter, Vikash Mansinghka
*   Research Leads: Vikash Mansinghka, Patrick Shafto
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*/

#define __STDC_CONSTANT_MACROS  // Make <stdint.h> define UINT64_C &c.

#include <algorithm>
#include <cassert>
#include <cmath>
#include <stdint.h>

#include "RandomNumberGenerator.h"

static inline unsigned bitcount64(uint64_t x) {
    // Count two-bit groups.
    x -= ((x >> 1) & UINT64_C(0x5555555555555555));

    // Add to four-bit groups, carefully masking carries and garbage.
    x = ((x >> 2) & UINT64_C(0x3333333333333333)) +
        (x & UINT64_C(0x3333333333333333));

    // Add to eight-bit groups.
    x = (((x >> 4) + x) & UINT64_C(0x0f0f0f0f0f0f0f0f));

    // Add all eight-bit groups in the leftmost column of a multiply.
    // Bit counts are small enough no other columns will carry.
    return (x * UINT64_C(0x0101010101010101)) >> 56;
}

static inline unsigned clz64(uint64_t x) {
    // Round up to a power of two minus one.
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    x |= x >> 32;

    // Count the bits thus set, and subtract from 64 to count leading
    // zeros.
    return 64 - bitcount64(x);
}

//////////////////////////////////
// return a random real between
// 0 and 1 with uniform dist
double RandomNumberGenerator::next() {
    int e = -64;
    uint64_t s;
    unsigned d;

    // Draw a significand from an infinite stream of bits.
    while ((s = crypto_weakprng_64(&_weakprng)) == 0) {
        e -= 64;
        // Just return zero if the exponent is so small there are no
        // floating-point numbers that tiny.
        if (e < -1074)
            return 0;
    }

    // Shift leading zeros into the exponent and fill trailing bits of
    // significand uniformly at random.
    if ((d = clz64(s)) != 0) {
        e -= d;
        s <<= d;
        s |= crypto_weakprng_64(&_weakprng) >> (64 - d);
    }

    // Set sticky bit, since there is (almost surely) another 1 in the
    // bit stream, to avoid a bias toward `even'.
    s |= 1;

    // Return s * 2^e.
    return ldexp(static_cast<double>(s), e);
}

//////////////////////////////
// return a random int bewteen
// zero and max - 1 with uniform
// dist if called with same max
int RandomNumberGenerator::nexti(int bound) {
    assert(0 < bound);
    return crypto_weakprng_below(&_weakprng, bound);
}

/////////////////////////////
// standard normal samples
// via Box-Muller transform
double RandomNumberGenerator::stdnormal() {
    double u, v;

    u = next();
    v = next();

    return sqrt(-2*log(u)) * sin(2*M_PI*v);
}

/////////////////////////////
// standard Gamma samples
//
//       George Marsaglia & Wai Wan Tsang, `A simple method for
//       generating gamma variables', ACM Transactions on Mathematical
//       Software 26(3), September 2000.  DOI: 10.1145/358407.358414
//       URI: https://dl.acm.org/citation.cfm?doid=358407.358414
double RandomNumberGenerator::stdgamma(double alpha) {
    const double d = alpha - (double)1/3;
    double x, u, v;

    assert(1 <= alpha);

    for (;;) {
        x = stdnormal();
        v = 1 + x/sqrt(9*d);
        v = v*v*v;

        u = next();
        if (u < 1 - 0.0331*((x*x)*(x*x)) ||
            log(u) < x*x/2 + d - d*v + d*log(v))
            return d*v;
    }
}

/////////////////////////////
// chi^2 samples
double RandomNumberGenerator::chisquare(double nu) {
    double shape = nu/2;
    double scale = 2;

    return scale*stdgamma(shape);
}

/////////////////////////////
// Student's t-distribution samples
double RandomNumberGenerator::student_t(double nu) {
    return stdnormal() * sqrt(nu/chisquare(nu));
}

/////////////////////////////
// control the seed
void RandomNumberGenerator::set_seed(std::time_t seed) {
    uint8_t seedbuf[crypto_weakprng_SEEDBYTES] = {0};
    size_t i;

    for (i = 0; i < std::min(sizeof seedbuf, sizeof seed); i++)
        seedbuf[i] = seed >> (8*i);
    crypto_weakprng_seed(&_weakprng, seedbuf);
}
<commit_msg>Precompute 1/sqrt(9*d).  Reject nonpositive v sooner.<commit_after>/*
*   Copyright (c) 2010-2016, MIT Probabilistic Computing Project
*
*   Lead Developers: Dan Lovell and Jay Baxter
*   Authors: Dan Lovell, Baxter Eaves, Jay Baxter, Vikash Mansinghka
*   Research Leads: Vikash Mansinghka, Patrick Shafto
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*/

#define __STDC_CONSTANT_MACROS  // Make <stdint.h> define UINT64_C &c.

#include <algorithm>
#include <cassert>
#include <cmath>
#include <stdint.h>

#include "RandomNumberGenerator.h"

static inline unsigned bitcount64(uint64_t x) {
    // Count two-bit groups.
    x -= ((x >> 1) & UINT64_C(0x5555555555555555));

    // Add to four-bit groups, carefully masking carries and garbage.
    x = ((x >> 2) & UINT64_C(0x3333333333333333)) +
        (x & UINT64_C(0x3333333333333333));

    // Add to eight-bit groups.
    x = (((x >> 4) + x) & UINT64_C(0x0f0f0f0f0f0f0f0f));

    // Add all eight-bit groups in the leftmost column of a multiply.
    // Bit counts are small enough no other columns will carry.
    return (x * UINT64_C(0x0101010101010101)) >> 56;
}

static inline unsigned clz64(uint64_t x) {
    // Round up to a power of two minus one.
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    x |= x >> 32;

    // Count the bits thus set, and subtract from 64 to count leading
    // zeros.
    return 64 - bitcount64(x);
}

//////////////////////////////////
// return a random real between
// 0 and 1 with uniform dist
double RandomNumberGenerator::next() {
    int e = -64;
    uint64_t s;
    unsigned d;

    // Draw a significand from an infinite stream of bits.
    while ((s = crypto_weakprng_64(&_weakprng)) == 0) {
        e -= 64;
        // Just return zero if the exponent is so small there are no
        // floating-point numbers that tiny.
        if (e < -1074)
            return 0;
    }

    // Shift leading zeros into the exponent and fill trailing bits of
    // significand uniformly at random.
    if ((d = clz64(s)) != 0) {
        e -= d;
        s <<= d;
        s |= crypto_weakprng_64(&_weakprng) >> (64 - d);
    }

    // Set sticky bit, since there is (almost surely) another 1 in the
    // bit stream, to avoid a bias toward `even'.
    s |= 1;

    // Return s * 2^e.
    return ldexp(static_cast<double>(s), e);
}

//////////////////////////////
// return a random int bewteen
// zero and max - 1 with uniform
// dist if called with same max
int RandomNumberGenerator::nexti(int bound) {
    assert(0 < bound);
    return crypto_weakprng_below(&_weakprng, bound);
}

/////////////////////////////
// standard normal samples
// via Box-Muller transform
double RandomNumberGenerator::stdnormal() {
    double u, v;

    u = next();
    v = next();

    return sqrt(-2*log(u)) * sin(2*M_PI*v);
}

/////////////////////////////
// standard Gamma samples
//
//       George Marsaglia & Wai Wan Tsang, `A simple method for
//       generating gamma variables', ACM Transactions on Mathematical
//       Software 26(3), September 2000.  DOI: 10.1145/358407.358414
//       URI: https://dl.acm.org/citation.cfm?doid=358407.358414
double RandomNumberGenerator::stdgamma(double alpha) {
    const double d = alpha - (double)1/3;
    const double c = 1/sqrt(9*d);
    double x, u, v;

    assert(1 <= alpha);

    for (;;) {
        x = stdnormal();
        v = 1 + x*c;
        if (v <= 0)
            continue;
        v = v*v*v;

        u = next();
        if (u < 1 - 0.0331*((x*x)*(x*x)) ||
            log(u) < x*x/2 + d - d*v + d*log(v))
            return d*v;
    }
}

/////////////////////////////
// chi^2 samples
double RandomNumberGenerator::chisquare(double nu) {
    double shape = nu/2;
    double scale = 2;

    return scale*stdgamma(shape);
}

/////////////////////////////
// Student's t-distribution samples
double RandomNumberGenerator::student_t(double nu) {
    return stdnormal() * sqrt(nu/chisquare(nu));
}

/////////////////////////////
// control the seed
void RandomNumberGenerator::set_seed(std::time_t seed) {
    uint8_t seedbuf[crypto_weakprng_SEEDBYTES] = {0};
    size_t i;

    for (i = 0; i < std::min(sizeof seedbuf, sizeof seed); i++)
        seedbuf[i] = seed >> (8*i);
    crypto_weakprng_seed(&_weakprng, seedbuf);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2020 The TensorFlow Runtime Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Tests for tfrt::ThreadLocal implementation.

#include "tfrt/support/thread_local.h"

#include <thread>

#include "gtest/gtest.h"
#include "tfrt/support/latch.h"

namespace tfrt {
namespace {

// Make WorkerId non-default-constructible and move-only to check that
// TheadLocal storage can properly handle such types.
struct WorkerId {
  explicit WorkerId(int id) : id(id) {}

  // Workaround for an apparent MSVC bug which results in a build failure
  // if WorkerId is move-only
#ifdef _MSC_VER
  WorkerId(const WorkerId&) = default;
  WorkerId& operator=(const WorkerId&) = default;
#else
  WorkerId(const WorkerId&) = delete;
  WorkerId& operator=(const WorkerId&) = delete;
#endif // _MSC_VER

  WorkerId(WorkerId&&) = default;
  WorkerId& operator=(WorkerId&&) = default;
  int id;
};

struct WorkerIdGenerator {
  explicit WorkerIdGenerator(int init) : counter(init) {}
  WorkerId Construct() { return WorkerId(counter.fetch_add(1)); }
  std::atomic<int> counter;
};

using CurrentWorkerId = ThreadLocal<WorkerId, WorkerIdGenerator>;

TEST(ThreadLocalTest, WorkerIdTest) {
  static constexpr int kInitWorkerId = 123;

  auto test = [](int num_threads, int capacity) {
    CurrentWorkerId worker_id(capacity, kInitWorkerId);

    // Wait until all threads are started.
    latch ready(num_threads);

    // Spin in the worker threads to force contention on the initial worker id
    // assignment (thread local element creation).
    std::atomic<bool> run{false};

    // Count the number of times when worker id changed for a thread. This
    // should never happen, once worker id assigned to a thread, it should
    // always stay the same.
    std::atomic<int> errors{0};

    std::vector<std::thread> threads;
    for (int i = 0; i < num_threads; ++i) {
      threads.emplace_back([&]() {
        ready.count_down();
        while (!run.load()) {
        }

        // Get a unique worker id.
        WorkerId& id = worker_id.Local();

        // Check that every other call returns the same worker id.
        for (int i = 0; i < 1000; ++i) {
          if (id.id != worker_id.Local().id) errors.fetch_add(1);
        }
      });
    }

    ready.wait();     // wait for threads to arrive at run spin loop
    run.store(true);  // allow threads to run the test

    for (auto& thread : threads) thread.join();

    ASSERT_EQ(errors, 0);
    worker_id.ForEach([&](std::thread::id, WorkerId& id) {
      ASSERT_GE(id.id, kInitWorkerId);
      ASSERT_LT(id.id, kInitWorkerId + num_threads);
    });
  };

  for (int num_threads : {2, 4, 8, 16}) {
    for (int capacity : {0, 2, 4, 8, 16, 32, 64}) {
      test(num_threads, capacity);
    }
  }
}

}  // namespace
}  // namespace tfrt
<commit_msg>A better fix for WorkerId on Windows<commit_after>/*
 * Copyright 2020 The TensorFlow Runtime Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Tests for tfrt::ThreadLocal implementation.

#include "tfrt/support/thread_local.h"

#include <thread>

#include "gtest/gtest.h"
#include "tfrt/support/latch.h"

namespace tfrt {
namespace {

// Make WorkerId non-default-constructible and move-only to check that
// TheadLocal storage can properly handle such types.
struct WorkerId {
  explicit WorkerId(int id) : id(id) {}
  WorkerId(const WorkerId&) = default;
  WorkerId& operator=(const WorkerId&) = default;
  WorkerId(WorkerId&&) noexcept = default;
  WorkerId& operator=(WorkerId&&) noexcept = default;
  int id;
};

struct WorkerIdGenerator {
  explicit WorkerIdGenerator(int init) : counter(init) {}
  WorkerId Construct() { return WorkerId(counter.fetch_add(1)); }
  std::atomic<int> counter;
};

using CurrentWorkerId = ThreadLocal<WorkerId, WorkerIdGenerator>;

TEST(ThreadLocalTest, WorkerIdTest) {
  static constexpr int kInitWorkerId = 123;

  auto test = [](int num_threads, int capacity) {
    CurrentWorkerId worker_id(capacity, kInitWorkerId);

    // Wait until all threads are started.
    latch ready(num_threads);

    // Spin in the worker threads to force contention on the initial worker id
    // assignment (thread local element creation).
    std::atomic<bool> run{false};

    // Count the number of times when worker id changed for a thread. This
    // should never happen, once worker id assigned to a thread, it should
    // always stay the same.
    std::atomic<int> errors{0};

    std::vector<std::thread> threads;
    for (int i = 0; i < num_threads; ++i) {
      threads.emplace_back([&]() {
        ready.count_down();
        while (!run.load()) {
        }

        // Get a unique worker id.
        WorkerId& id = worker_id.Local();

        // Check that every other call returns the same worker id.
        for (int i = 0; i < 1000; ++i) {
          if (id.id != worker_id.Local().id) errors.fetch_add(1);
        }
      });
    }

    ready.wait();     // wait for threads to arrive at run spin loop
    run.store(true);  // allow threads to run the test

    for (auto& thread : threads) thread.join();

    ASSERT_EQ(errors, 0);
    worker_id.ForEach([&](std::thread::id, WorkerId& id) {
      ASSERT_GE(id.id, kInitWorkerId);
      ASSERT_LT(id.id, kInitWorkerId + num_threads);
    });
  };

  for (int num_threads : {2, 4, 8, 16}) {
    for (int capacity : {0, 2, 4, 8, 16, 32, 64}) {
      test(num_threads, capacity);
    }
  }
}

}  // namespace
}  // namespace tfrt
<|endoftext|>


<commit_before>#include <Renderer.h>
#include <DefaultFramebuffer.h>
#include <DebugTools/ForceRenderer.h>
#include <DebugTools/ResourceManager.h>
#include <DebugTools/ShapeRenderer.h>
#ifndef CORRADE_TARGET_NACL
#include <Platform/Sdl2Application.h>
#else
#include <Platform/NaClApplication.h>
#endif
#include <SceneGraph/Camera2D.h>
#include <SceneGraph/DualComplexTransformation.h>
#include <SceneGraph/Object.h>
#include <SceneGraph/Scene.h>
#include <Physics/ObjectShape.h>
#include <Physics/ObjectShapeGroup.h>
#include <Physics/Box.h>
#include <Physics/Point.h>
#include <Physics/ShapeGroup.h>
#include <Physics/Sphere.h>

#ifdef MAGNUM_BUILD_STATIC
#include <Shaders/magnumShadersResourceImport.hpp>
#endif

#include "Kotel.h"

namespace Kotel { namespace Prototype {

class Forces2D: public Platform::Application {
    public:
        explicit Forces2D(const Arguments& arguments);

        void viewportEvent(const Vector2i& size) override;
        void drawEvent() override;
        void keyPressEvent(KeyEvent& event) override;
        void keyReleaseEvent(KeyEvent& event) override;

    private:
        DebugTools::ResourceManager debugResourceManager;

        Scene2D scene;
        Object2D cameraObject;
        SceneGraph::Camera2D<> *camera;
        SceneGraph::DrawableGroup<2> drawables;
        Physics::ObjectShapeGroup2D shapes;

        Object2D *tube, *vehicle;

        Vector2 gravity;

        Deg armAngle;

        struct {
            Vector2 weightBody,
                weightLeftArm, weightRightArm,
                engineLeftArm, engineRightArm,
                totalNormalLeftArm, totalNormalRightArm,
                totalTangentLeftArm, totalTangentRightArm;
        } forces;

        struct {
            Float massBody,
                massArm,
                powerArm;
        } parameters;

        struct {
            Float currentPowerLeftArm,
                currentPowerRightArm;
        } engine;

        DualComplex baseLeftArmTransformation, baseRightArmTransformation;
};

Forces2D::Forces2D(const Arguments& arguments): Platform::Application(arguments, (new Configuration())
    #ifndef CORRADE_TARGET_NACL
    ->setTitle("Kotel::Prototype::Forces2D")
    ->setSampleCount(16)
    #endif
), engine{{}, {}} {
    Renderer::setClearColor(Color3<>(0.125f));
    Renderer::setFeature(Renderer::Feature::Blending, true);
    Renderer::setBlendFunction(Renderer::BlendFunction::SourceAlpha, Renderer::BlendFunction::OneMinusSourceAlpha);

    /* Parameters */
    gravity = Vector2::yAxis(-9.81);
    armAngle = Deg(110.0f);
    parameters.massBody = 260.0f;
    parameters.massArm = 80.0f;
    parameters.powerArm = 1000.0f;

    /* Camera setup */
    cameraObject.setParent(&scene);
    (camera = new SceneGraph::Camera2D<>(&cameraObject))
        ->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend)
        ->setProjection(Vector2(3.0f));

    /* Debug draw setup */
    debugResourceManager.set("gravity", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(190.0f), 0.75f, 0.9f, 0.5f)));
    debugResourceManager.set("engines", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(50.0f), 0.75f, 0.9f, 0.5f)));
    debugResourceManager.set("tangent", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(245.0f), 0.75f, 0.9f, 0.75f)));
    debugResourceManager.set("normal", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(5.0f), 0.75f, 0.9f, 0.75f)));
    debugResourceManager.set("tube", (new DebugTools::ShapeRendererOptions())
        ->setColor(Color3<>(0.2f)));
    debugResourceManager.set("vehicle", (new DebugTools::ShapeRendererOptions())
        ->setColor(Color3<>(0.5f)));
    debugResourceManager.set("parameters", (new DebugTools::ShapeRendererOptions())
        ->setPointSize(0.1f)->setColor(Color3<>::fromHSV(Deg(25.0f), 0.75f, 0.9f)));

    /* Tube */
    tube = new Object2D(&scene);
    auto tubeShape = new Physics::ObjectShape2D(tube, &shapes);
    tubeShape->setShape(
        Physics::Sphere2D({}, 1.05f) ||
        Physics::Sphere2D({}, 1.15f)
    );
    new DebugTools::ShapeRenderer2D(tubeShape, "tube", &drawables);

    /* Vehicle parameters */
    baseLeftArmTransformation = DualComplex::rotation(Deg(-armAngle/2))*DualComplex::translation(Vector2::yAxis(-1.0f));
    baseRightArmTransformation = DualComplex::rotation(Deg(armAngle/2))*DualComplex::translation(Vector2::yAxis(-1.0f));
    Matrix3 baseArmTransformation = Matrix3::translation(Vector2::yAxis(-0.585f))*Matrix3::scaling({0.02f, 0.385f});
    Vector2 vehicleCenterOfMass = ((baseLeftArmTransformation.translation() + baseRightArmTransformation.translation())*
        parameters.massArm + Vector2()*parameters.massBody)/(parameters.massArm*2 + parameters.massBody);

    /* Vehicle */
    vehicle = new Object2D(&scene);
    auto vehicleShape = new Physics::ObjectShape2D(vehicle, &shapes);
    vehicleShape->setShape(
        Physics::Sphere2D({}, .2f) ||
        Physics::Box2D(Matrix3::rotation(Deg(-armAngle/2))*baseArmTransformation) ||
        Physics::Box2D(Matrix3::rotation(Deg(armAngle/2))*baseArmTransformation) ||
        Physics::Box2D(baseLeftArmTransformation.toMatrix()*Matrix3::scaling({0.1f, 0.03f})) ||
        Physics::Box2D(baseRightArmTransformation.toMatrix()*Matrix3::scaling({0.1f, 0.03f}))
    );
    new DebugTools::ShapeRenderer2D(vehicleShape, "vehicle", &drawables);

    /* Vehicle center-of-mass */
    auto vehicleCenterOfMassShape = new Physics::ObjectShape2D(vehicle, &shapes);
    vehicleCenterOfMassShape->setShape(Physics::Point2D(vehicleCenterOfMass));
    new DebugTools::ShapeRenderer2D(vehicleCenterOfMassShape, "parameters", &drawables);

    /* Gravity forces */
    forces.weightBody = gravity*parameters.massBody;
    forces.weightLeftArm = gravity*parameters.massArm;
    forces.weightRightArm = gravity*parameters.massArm;
    new DebugTools::ForceRenderer2D(vehicle, {},
        &forces.weightBody, "gravity", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, baseLeftArmTransformation.translation(),
        &forces.weightLeftArm, "gravity", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, baseRightArmTransformation.translation(),
        &forces.weightRightArm, "gravity", &drawables);

    /* Engine forces */
    new DebugTools::ForceRenderer2D(vehicle, baseLeftArmTransformation.translation(),
        &forces.engineLeftArm, "engines", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, baseRightArmTransformation.translation(),
        &forces.engineRightArm, "engines", &drawables);

    /* Tangent and normal forces */
    new DebugTools::ForceRenderer2D(vehicle, baseLeftArmTransformation.translation(),
        &forces.totalTangentLeftArm, "tangent", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, baseRightArmTransformation.translation(),
        &forces.totalTangentRightArm, "tangent", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, baseLeftArmTransformation.translation(),
        &forces.totalNormalLeftArm, "normal", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, baseRightArmTransformation.translation(),
        &forces.totalNormalRightArm, "normal", &drawables);
}

void Forces2D::viewportEvent(const Vector2i& size) {
    defaultFramebuffer.setViewport({{}, size});

    camera->setViewport(size);
}

void Forces2D::drawEvent() {
    defaultFramebuffer.bind(DefaultFramebuffer::Target::Draw);
    defaultFramebuffer.clear(DefaultFramebuffer::Clear::Color);

    /* Compute tangent and normal vectors */
    const Vector2 tangentLeftArm = (vehicle->transformation().rotation()*
        baseLeftArmTransformation.rotation()).transformVector(Vector2::xAxis());
    const Vector2 tangentRightArm = (vehicle->transformation().rotation()*
        baseRightArmTransformation.rotation()).transformVector(Vector2::xAxis());
    const Vector2 normalLeftArm = (vehicle->transformation().rotation()*
        baseLeftArmTransformation.rotation()).transformVector(-Vector2::yAxis());
    const Vector2 normalRightArm = (vehicle->transformation().rotation()*
        baseRightArmTransformation.rotation()).transformVector(-Vector2::yAxis());

    /* Reset */
    forces.totalNormalLeftArm = forces.totalNormalRightArm =
        forces.totalTangentLeftArm = forces.totalTangentRightArm = {};

    /* Propagate body weight to arms */
    forces.totalNormalLeftArm += forces.weightBody.projectedOntoNormalized(normalLeftArm);
    forces.totalNormalRightArm += forces.weightBody.projectedOntoNormalized(normalRightArm);

    /* Decompose arm weight to normal and tangent */
    forces.totalNormalLeftArm += forces.weightLeftArm.projectedOntoNormalized(normalLeftArm);
    forces.totalNormalRightArm += forces.weightRightArm.projectedOntoNormalized(normalRightArm);
    forces.totalTangentLeftArm += forces.weightLeftArm.projectedOntoNormalized(tangentLeftArm);
    forces.totalTangentRightArm += forces.weightRightArm.projectedOntoNormalized(tangentRightArm);

    /* Engine forces to tangent */
    forces.engineLeftArm = tangentLeftArm*engine.currentPowerLeftArm;
    forces.engineRightArm = tangentRightArm*engine.currentPowerRightArm;
    forces.totalTangentLeftArm += forces.engineLeftArm;
    forces.totalTangentRightArm += forces.engineRightArm;

    shapes.setClean();
    camera->draw(drawables);

    swapBuffers();
}

void Forces2D::keyPressEvent(KeyEvent& event) {
    if(event.key() == KeyEvent::Key::Left) {
        engine.currentPowerLeftArm = parameters.powerArm;
        engine.currentPowerRightArm = parameters.powerArm;
    } else if (event.key() == KeyEvent::Key::Right) {
        engine.currentPowerLeftArm = -parameters.powerArm;
        engine.currentPowerRightArm = -parameters.powerArm;
    } else return;

    event.setAccepted();
    redraw();
}

void Forces2D::keyReleaseEvent(KeyEvent& event) {
    engine.currentPowerLeftArm = engine.currentPowerRightArm = {};

    event.setAccepted();
    redraw();
}

}}

MAGNUM_APPLICATION_MAIN(Kotel::Prototype::Forces2D)
<commit_msg>prototypes: minor refactoring, adapted to Magnum changes.<commit_after>#include <Renderer.h>
#include <DefaultFramebuffer.h>
#include <DebugTools/ForceRenderer.h>
#include <DebugTools/ResourceManager.h>
#include <DebugTools/ShapeRenderer.h>
#ifndef CORRADE_TARGET_NACL
#include <Platform/Sdl2Application.h>
#else
#include <Platform/NaClApplication.h>
#endif
#include <SceneGraph/Camera2D.h>
#include <SceneGraph/DualComplexTransformation.h>
#include <SceneGraph/Object.h>
#include <SceneGraph/Scene.h>
#include <Physics/ObjectShape.h>
#include <Physics/ObjectShapeGroup.h>
#include <Physics/Box.h>
#include <Physics/Point.h>
#include <Physics/ShapeGroup.h>
#include <Physics/Sphere.h>

#ifdef MAGNUM_BUILD_STATIC
#include <Shaders/magnumShadersResourceImport.hpp>
#endif

#include "Kotel.h"

namespace Kotel { namespace Prototype {

class Forces2D: public Platform::Application {
    public:
        explicit Forces2D(const Arguments& arguments);

        void viewportEvent(const Vector2i& size) override;
        void drawEvent() override;
        void keyPressEvent(KeyEvent& event) override;
        void keyReleaseEvent(KeyEvent& event) override;

    private:
        DebugTools::ResourceManager debugResourceManager;

        Scene2D scene;
        Object2D cameraObject;
        SceneGraph::Camera2D<> *camera;
        SceneGraph::DrawableGroup<2> drawables;
        Physics::ObjectShapeGroup2D shapes;

        Object2D *tube, *vehicle;

        struct {
            Vector2 weightBody,
                weightLeftArm, weightRightArm,
                engineLeftArm, engineRightArm,
                totalNormalLeftArm, totalNormalRightArm,
                totalTangentLeftArm, totalTangentRightArm;
        } forces;

        struct {
            Float massBody,
                massArm,
                powerArm;
            Vector2 gravity,
                centerOfMass;

            Deg armAngle;
            DualComplex baseLeftArmTransformation,
                baseRightArmTransformation;
        } parameters;

        struct {
            Float currentPowerLeftArm,
                currentPowerRightArm;
        } engine;

};

Forces2D::Forces2D(const Arguments& arguments): Platform::Application(arguments, (new Configuration())
    #ifndef CORRADE_TARGET_NACL
    ->setTitle("Kotel::Prototype::Forces2D")
    ->setSampleCount(16)
    #endif
), engine{{}, {}} {
    Renderer::setClearColor(Color3<>(0.125f));
    Renderer::setFeature(Renderer::Feature::Blending, true);
    Renderer::setBlendFunction(Renderer::BlendFunction::SourceAlpha, Renderer::BlendFunction::OneMinusSourceAlpha);

    /* Parameters */
    parameters.gravity = Vector2::yAxis(-9.81);
    parameters.armAngle = Deg(110.0f);
    parameters.massBody = 260.0f;
    parameters.massArm = 80.0f;
    parameters.powerArm = 1000.0f;

    /* Camera setup */
    cameraObject.setParent(&scene);
    (camera = new SceneGraph::Camera2D<>(&cameraObject))
        ->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend)
        ->setProjection(Vector2(3.0f));

    /* Debug draw setup */
    debugResourceManager.set("gravity", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(190.0f), 0.75f, 0.9f, 0.5f)));
    debugResourceManager.set("engines", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(50.0f), 0.75f, 0.9f, 0.5f)));
    debugResourceManager.set("tangent", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(245.0f), 0.75f, 0.9f, 0.75f)));
    debugResourceManager.set("normal", (new DebugTools::ForceRendererOptions())
        ->setSize(0.0005f)->setColor(Color4<>::fromHSV(Deg(5.0f), 0.75f, 0.9f, 0.75f)));
    debugResourceManager.set("tube", (new DebugTools::ShapeRendererOptions())
        ->setColor(Color3<>(0.2f)));
    debugResourceManager.set("vehicle", (new DebugTools::ShapeRendererOptions())
        ->setColor(Color3<>(0.5f)));
    debugResourceManager.set("parameters", (new DebugTools::ShapeRendererOptions())
        ->setPointSize(0.1f)->setColor(Color3<>::fromHSV(Deg(25.0f), 0.75f, 0.9f)));

    /* Tube */
    tube = new Object2D(&scene);
    auto tubeShape = new Physics::ObjectShape2D(tube, &shapes);
    tubeShape->setShape(
        Physics::Sphere2D({}, 1.05f) ||
        Physics::Sphere2D({}, 1.15f)
    );
    new DebugTools::ShapeRenderer2D(tubeShape, "tube", &drawables);

    /* Vehicle parameters */
    parameters.baseLeftArmTransformation = DualComplex::rotation(-parameters.armAngle/2)*DualComplex::translation(Vector2::yAxis(-1.0f));
    parameters.baseRightArmTransformation = DualComplex::rotation(parameters.armAngle/2)*DualComplex::translation(Vector2::yAxis(-1.0f));
    parameters.centerOfMass = ((parameters.baseLeftArmTransformation.translation() +
        parameters.baseRightArmTransformation.translation())*
        parameters.massArm + Vector2()*parameters.massBody)/(parameters.massArm*2 + parameters.massBody);

    /* Vehicle */
    vehicle = new Object2D(&scene);
    Matrix3 baseArmTransformation = Matrix3::translation(Vector2::yAxis(-0.585f))*Matrix3::scaling({0.02f, 0.385f});
    auto vehicleShape = new Physics::ObjectShape2D(vehicle, &shapes);
    vehicleShape->setShape(
        Physics::Sphere2D({}, .2f) ||
        Physics::Box2D(Matrix3::rotation(-parameters.armAngle/2)*baseArmTransformation) ||
        Physics::Box2D(Matrix3::rotation(parameters.armAngle/2)*baseArmTransformation) ||
        Physics::Box2D(parameters.baseLeftArmTransformation.toMatrix()*Matrix3::scaling({0.1f, 0.03f})) ||
        Physics::Box2D(parameters.baseRightArmTransformation.toMatrix()*Matrix3::scaling({0.1f, 0.03f}))
    );
    new DebugTools::ShapeRenderer2D(vehicleShape, "vehicle", &drawables);

    /* Vehicle center-of-mass */
    auto vehicleCenterOfMassShape = new Physics::ObjectShape2D(vehicle, &shapes);
    vehicleCenterOfMassShape->setShape(Physics::Point2D(parameters.centerOfMass));
    new DebugTools::ShapeRenderer2D(vehicleCenterOfMassShape, "parameters", &drawables);

    /* Gravity forces */
    forces.weightBody = parameters.gravity*parameters.massBody;
    forces.weightLeftArm = parameters.gravity*parameters.massArm;
    forces.weightRightArm = parameters.gravity*parameters.massArm;
    new DebugTools::ForceRenderer2D(vehicle, {},
        &forces.weightBody, "gravity", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseLeftArmTransformation.translation(),
        &forces.weightLeftArm, "gravity", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseRightArmTransformation.translation(),
        &forces.weightRightArm, "gravity", &drawables);

    /* Engine forces */
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseLeftArmTransformation.translation(),
        &forces.engineLeftArm, "engines", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseRightArmTransformation.translation(),
        &forces.engineRightArm, "engines", &drawables);

    /* Tangent and normal forces */
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseLeftArmTransformation.translation(),
        &forces.totalTangentLeftArm, "tangent", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseRightArmTransformation.translation(),
        &forces.totalTangentRightArm, "tangent", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseLeftArmTransformation.translation(),
        &forces.totalNormalLeftArm, "normal", &drawables);
    new DebugTools::ForceRenderer2D(vehicle, parameters.baseRightArmTransformation.translation(),
        &forces.totalNormalRightArm, "normal", &drawables);
}

void Forces2D::viewportEvent(const Vector2i& size) {
    defaultFramebuffer.setViewport({{}, size});

    camera->setViewport(size);
}

void Forces2D::drawEvent() {
    defaultFramebuffer.bind(DefaultFramebuffer::Target::Draw);
    defaultFramebuffer.clear(DefaultFramebuffer::Clear::Color);

    /* Compute tangent and normal vectors */
    const Vector2 tangentLeftArm = (vehicle->transformation().rotation()*
        parameters.baseLeftArmTransformation.rotation()).transformVector(Vector2::xAxis());
    const Vector2 tangentRightArm = (vehicle->transformation().rotation()*
        parameters.baseRightArmTransformation.rotation()).transformVector(Vector2::xAxis());
    const Vector2 normalLeftArm = (vehicle->transformation().rotation()*
        parameters.baseLeftArmTransformation.rotation()).transformVector(-Vector2::yAxis());
    const Vector2 normalRightArm = (vehicle->transformation().rotation()*
        parameters.baseRightArmTransformation.rotation()).transformVector(-Vector2::yAxis());

    /* Reset */
    forces.totalNormalLeftArm = forces.totalNormalRightArm =
        forces.totalTangentLeftArm = forces.totalTangentRightArm = {};

    /* Propagate body weight to arms */
    forces.totalNormalLeftArm += forces.weightBody.projectedOntoNormalized(normalLeftArm);
    forces.totalNormalRightArm += forces.weightBody.projectedOntoNormalized(normalRightArm);

    /* Decompose arm weight to normal and tangent */
    forces.totalNormalLeftArm += forces.weightLeftArm.projectedOntoNormalized(normalLeftArm);
    forces.totalNormalRightArm += forces.weightRightArm.projectedOntoNormalized(normalRightArm);
    forces.totalTangentLeftArm += forces.weightLeftArm.projectedOntoNormalized(tangentLeftArm);
    forces.totalTangentRightArm += forces.weightRightArm.projectedOntoNormalized(tangentRightArm);

    /* Engine forces to tangent */
    forces.engineLeftArm = tangentLeftArm*engine.currentPowerLeftArm;
    forces.engineRightArm = tangentRightArm*engine.currentPowerRightArm;
    forces.totalTangentLeftArm += forces.engineLeftArm;
    forces.totalTangentRightArm += forces.engineRightArm;

    shapes.setClean();
    camera->draw(drawables);

    swapBuffers();
}

void Forces2D::keyPressEvent(KeyEvent& event) {
    if(event.key() == KeyEvent::Key::Left) {
        engine.currentPowerLeftArm = parameters.powerArm;
        engine.currentPowerRightArm = parameters.powerArm;
    } else if (event.key() == KeyEvent::Key::Right) {
        engine.currentPowerLeftArm = -parameters.powerArm;
        engine.currentPowerRightArm = -parameters.powerArm;
    } else return;

    event.setAccepted();
    redraw();
}

void Forces2D::keyReleaseEvent(KeyEvent& event) {
    engine.currentPowerLeftArm = engine.currentPowerRightArm = {};

    event.setAccepted();
    redraw();
}

}}

MAGNUM_APPLICATION_MAIN(Kotel::Prototype::Forces2D)
<|endoftext|>


<commit_before>#define DEFINE_DEFAULT_ITERATOR_CTOR
#include "helpers.hpp"
#undef DEFINE_DEFAULT_ITERATOR_CTOR

#include <combinations.hpp>

#include <set>
#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::combinations;
using itertest::BasicIterable;
using itertest::SolidInt;
using CharCombSet = std::vector<std::vector<char>>;

TEST_CASE("combinations: Simple combination of 4", "[combinations]") {
  std::string s{"ABCD"};
  CharCombSet sc;
  for (auto v : combinations(s, 2)) {
    sc.emplace_back(std::begin(v), std::end(v));
  }

  CharCombSet ans = {
      {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}};
  REQUIRE(ans == sc);
}

TEST_CASE("combinations: iterators can be compared", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it == std::begin(c));
  REQUIRE_FALSE(it != std::begin(c));
  ++it;
  REQUIRE(it != std::begin(c));
  REQUIRE_FALSE(it == std::begin(c));
}

TEST_CASE("combinations: operator->", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it->size() == 2);
}

TEST_CASE("combinations: size too large gives no results", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 5);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE("combinations: size 0 gives nothing", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 0);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE(
    "combinations: iterable without operator*() const", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  auto c = combinations(bi, 1);
  auto it = std::begin(c);
  ++it;
  (*it)[0];
}

TEST_CASE("combinations: binds to lvalues, moves rvalues", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  SECTION("binds to lvalues") {
    combinations(bi, 1);
    REQUIRE_FALSE(bi.was_moved_from());
  }
  SECTION("moves rvalues") {
    combinations(std::move(bi), 1);
    REQUIRE(bi.was_moved_from());
  }
}

TEST_CASE("combinations: doesn't move or copy elements of iterable",
    "[combinations]") {
  constexpr SolidInt arr[] = {{6}, {7}, {8}};
  for (auto&& i : combinations(arr, 1)) {
    (void)i;
  }
}

TEST_CASE("combinations: iterator meets requirements", "[combinations]") {
  std::string s{"abc"};
  auto c = combinations(s, 1);
  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
  auto&& row = *std::begin(c);
  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
}

template <typename T>
using ImpT = decltype(combinations(std::declval<T>(), 1));
TEST_CASE("combinations: has correct ctor and assign ops", "[combinations]") {
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
}
<commit_msg>Tests combinations with pipe<commit_after>#define DEFINE_DEFAULT_ITERATOR_CTOR
#include "helpers.hpp"
#undef DEFINE_DEFAULT_ITERATOR_CTOR

#include <combinations.hpp>

#include <set>
#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::combinations;
using itertest::BasicIterable;
using itertest::SolidInt;
using CharCombSet = std::vector<std::vector<char>>;

TEST_CASE("combinations: Simple combination of 4", "[combinations]") {
  std::string s{"ABCD"};
  CharCombSet sc;
  SECTION("Normal call") {
    for (auto&& v : combinations(s, 2)) {
      sc.emplace_back(std::begin(v), std::end(v));
    }
  }
  SECTION("Pipe") {
    for (auto&& v : s | combinations(2)) {
      sc.emplace_back(std::begin(v), std::end(v));
    }
  }

  CharCombSet ans = {
      {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}};
  REQUIRE(ans == sc);
}

TEST_CASE("combinations: iterators can be compared", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it == std::begin(c));
  REQUIRE_FALSE(it != std::begin(c));
  ++it;
  REQUIRE(it != std::begin(c));
  REQUIRE_FALSE(it == std::begin(c));
}

TEST_CASE("combinations: operator->", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it->size() == 2);
}

TEST_CASE("combinations: size too large gives no results", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 5);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE("combinations: size 0 gives nothing", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 0);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE(
    "combinations: iterable without operator*() const", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  auto c = combinations(bi, 1);
  auto it = std::begin(c);
  ++it;
  (*it)[0];
}

TEST_CASE("combinations: binds to lvalues, moves rvalues", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  SECTION("binds to lvalues") {
    combinations(bi, 1);
    REQUIRE_FALSE(bi.was_moved_from());
  }
  SECTION("moves rvalues") {
    combinations(std::move(bi), 1);
    REQUIRE(bi.was_moved_from());
  }
}

TEST_CASE("combinations: doesn't move or copy elements of iterable",
    "[combinations]") {
  constexpr SolidInt arr[] = {{6}, {7}, {8}};
  for (auto&& i : combinations(arr, 1)) {
    (void)i;
  }
}

TEST_CASE("combinations: iterator meets requirements", "[combinations]") {
  std::string s{"abc"};
  auto c = combinations(s, 1);
  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
  auto&& row = *std::begin(c);
  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
}

template <typename T>
using ImpT = decltype(combinations(std::declval<T>(), 1));
TEST_CASE("combinations: has correct ctor and assign ops", "[combinations]") {
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
}
<|endoftext|>


<commit_before>#define DEFINE_DEFAULT_ITERATOR_CTOR
#include "helpers.hpp"
#undef DEFINE_DEFAULT_ITERATOR_CTOR

#include <combinations.hpp>

#include <set>
#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::combinations;
using itertest::BasicIterable;
using itertest::SolidInt;
using CharCombSet = std::vector<std::vector<char>>;

TEST_CASE("combinations: Simple combination of 4", "[combinations]") {
  std::string s{"ABCD"};
  CharCombSet sc;
  SECTION("Normal call") {
    for (auto&& v : combinations(s, 2)) {
      sc.emplace_back(std::begin(v), std::end(v));
    }
  }
  SECTION("Pipe") {
    for (auto&& v : s | combinations(2)) {
      sc.emplace_back(std::begin(v), std::end(v));
    }
  }

  CharCombSet ans = {
      {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}};
  REQUIRE(ans == sc);
}

TEST_CASE("combinations: iterators can be compared", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it == std::begin(c));
  REQUIRE_FALSE(it != std::begin(c));
  ++it;
  REQUIRE(it != std::begin(c));
  REQUIRE_FALSE(it == std::begin(c));
}

TEST_CASE("combinations: operator->", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it->size() == 2);
}

TEST_CASE("combinations: size too large gives no results", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 5);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE("combinations: size 0 gives nothing", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 0);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE(
    "combinations: iterable without operator*() const", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  auto c = combinations(bi, 1);
  auto it = std::begin(c);
  ++it;
  (*it)[0];
}

TEST_CASE("combinations: binds to lvalues, moves rvalues", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  SECTION("binds to lvalues") {
    combinations(bi, 1);
    REQUIRE_FALSE(bi.was_moved_from());
  }
  SECTION("moves rvalues") {
    combinations(std::move(bi), 1);
    REQUIRE(bi.was_moved_from());
  }
}

TEST_CASE("combinations: doesn't move or copy elements of iterable",
    "[combinations]") {
  constexpr SolidInt arr[] = {{6}, {7}, {8}};
  for (auto&& i : combinations(arr, 1)) {
    (void)i;
  }
}

TEST_CASE("combinations: iterator meets requirements", "[combinations]") {
  std::string s{"abc"};
  auto c = combinations(s, 1);
  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
  auto&& row = *std::begin(c);
  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
}

template <typename T>
using ImpT = decltype(combinations(std::declval<T>(), 1));
TEST_CASE("combinations: has correct ctor and assign ops", "[combinations]") {
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
}
<commit_msg>Tests combinations with different begin and end<commit_after>#define DEFINE_DEFAULT_ITERATOR_CTOR
#define CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
#include "helpers.hpp"
#undef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
#undef DEFINE_DEFAULT_ITERATOR_CTOR

#include <combinations.hpp>

#include <set>
#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::combinations;
using itertest::BasicIterable;
using itertest::SolidInt;
using CharCombSet = std::vector<std::vector<char>>;

TEST_CASE("combinations: Simple combination of 4", "[combinations]") {
  std::string s{"ABCD"};
  CharCombSet sc;
  SECTION("Normal call") {
    for (auto&& v : combinations(s, 2)) {
      sc.emplace_back(std::begin(v), std::end(v));
    }
  }
  SECTION("Pipe") {
    for (auto&& v : s | combinations(2)) {
      sc.emplace_back(std::begin(v), std::end(v));
    }
  }

  CharCombSet ans = {
      {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}};
  REQUIRE(ans == sc);
}

TEST_CASE("combinations: Works with different begin and end types",
    "[combinations]") {
  CharRange cr{'e'};
  CharCombSet sc;
  for (auto&& v : combinations(cr, 2)) {
    sc.emplace_back(std::begin(v), std::end(v));
  }
  CharCombSet ans = {
      {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'b', 'c'}, {'b', 'd'}, {'c', 'd'}};
  REQUIRE(ans == sc);
}

TEST_CASE("combinations: iterators can be compared", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it == std::begin(c));
  REQUIRE_FALSE(it != std::begin(c));
  ++it;
  REQUIRE(it != std::begin(c));
  REQUIRE_FALSE(it == std::begin(c));
}

TEST_CASE("combinations: operator->", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 2);
  auto it = std::begin(c);
  REQUIRE(it->size() == 2);
}

TEST_CASE("combinations: size too large gives no results", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 5);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE("combinations: size 0 gives nothing", "[combinations]") {
  std::string s{"ABCD"};
  auto c = combinations(s, 0);
  REQUIRE(std::begin(c) == std::end(c));
}

TEST_CASE(
    "combinations: iterable without operator*() const", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  auto c = combinations(bi, 1);
  auto it = std::begin(c);
  ++it;
  (*it)[0];
}

TEST_CASE("combinations: binds to lvalues, moves rvalues", "[combinations]") {
  BasicIterable<char> bi{'x', 'y', 'z'};
  SECTION("binds to lvalues") {
    combinations(bi, 1);
    REQUIRE_FALSE(bi.was_moved_from());
  }
  SECTION("moves rvalues") {
    combinations(std::move(bi), 1);
    REQUIRE(bi.was_moved_from());
  }
}

TEST_CASE("combinations: doesn't move or copy elements of iterable",
    "[combinations]") {
  constexpr SolidInt arr[] = {{6}, {7}, {8}};
  for (auto&& i : combinations(arr, 1)) {
    (void)i;
  }
}

TEST_CASE("combinations: iterator meets requirements", "[combinations]") {
  std::string s{"abc"};
  auto c = combinations(s, 1);
  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
  auto&& row = *std::begin(c);
  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
}

template <typename T>
using ImpT = decltype(combinations(std::declval<T>(), 1));
TEST_CASE("combinations: has correct ctor and assign ops", "[combinations]") {
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
}
<|endoftext|>