<commit_before>#include <fstream>
#include "Simbody.h"
#include "Exception.h"
#include "FileAdapter.h"
#include "TimeSeriesTable.h"
#include "APDMDataReader.h"

namespace OpenSim {

const std::vector<std::string> APDMDataReader::acceleration_labels{
        "/Acceleration/X", "/Acceleration/Y", "/Acceleration/Z"
};
const std::vector<std::string> APDMDataReader::angular_velocity_labels{
    "/Angular Velocity/X", "/Angular Velocity/Y","/Angular Velocity/Z"
};
const std::vector<std::string> APDMDataReader::magnetic_heading_labels{
    "/Magnetic Field/X", "/Magnetic Field/Y","/Magnetic Field/Z"
};
const std::vector<std::string> APDMDataReader::orientation_labels{
    "/Orientation/Scalar", "/Orientation/X", "/Orientation/Y","/Orientation/Z"
};

const std::string APDMDataReader::TimeLabel{ "Time" };

APDMDataReader* APDMDataReader::clone() const {
    return new APDMDataReader{*this};
}

DataAdapter::OutputTables
APDMDataReader::extendRead(const std::string& fileName) const {

    OPENSIM_THROW_IF(fileName.empty(),
        EmptyFileName);

    std::ifstream in_stream{ fileName };
    OPENSIM_THROW_IF(!in_stream.good(),
        FileDoesNotExist,
        fileName);

    OPENSIM_THROW_IF(in_stream.peek() == std::ifstream::traits_type::eof(),
        FileIsEmpty,
        fileName);

    std::vector<std::string> labels; // will be written to output tables

    double dataRate = SimTK::NaN;
    std::vector<int> accIndex;
    std::vector<int>  gyroIndex;
    std::vector<int>  magIndex;
    std::vector<int>  orientationsIndex;

    int n_imus = _settings.getProperty_ExperimentalSensors().size();
    int last_size = 1024;
    // Will read data into pre-allocated Matrices in-memory rather than appendRow
    // on the fly which copies the whole table on every call.
    SimTK::Matrix_<SimTK::Quaternion> rotationsData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> linearAccelerationData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> magneticHeadingData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> angularVelocityData{ last_size, n_imus };
    std::vector<double> times;
    times.resize(last_size);
    // We support two formats, they contain similar data but headers are different
    std::string line;
    // Line 1
    std::getline(in_stream, line);
    std::vector<std::string> tokens = FileAdapter::tokenize(line, ",");
    bool newFormat = false;
    if (tokens[0] == "Format=7") {
        newFormat = true;
        dataRate = 128; // Will fix after reading computing it from time column
        // Header Line 1:Format=7, [I1,,,$IMU1,,,,,,,,,,,]*
        // Header Line 2: Time,[Accelerometer,,,Gyroscope,,,Magnetometer,,,Barometer,Orientation,,,]*
        // Header Line 3: ,[X,Y,Z,X,Y,Z,X,Y,Z,,S,X,Y,Z]*
        std::vector<std::string> emptyLabels;
        emptyLabels.push_back("");
        // In this format there's no dataRate, either assumed or computed from Time column
        for (int imu_index = 0; imu_index < n_imus; ++imu_index) {
            std::string sensorName = _settings.get_ExperimentalSensors(imu_index).getName();
            labels.push_back(_settings.get_ExperimentalSensors(imu_index).get_name_in_model());
            find_start_column(tokens, emptyLabels, sensorName, accIndex, newFormat);
            if (accIndex[imu_index] != -1) {
                gyroIndex.push_back(accIndex[imu_index] + 3);
                magIndex.push_back(accIndex[imu_index] + 6);
                orientationsIndex.push_back(accIndex[imu_index] + 10);
            }
            else
                OPENSIM_THROW(Exception, "Data for sensor:" +sensorName + "was not found in data file "+ fileName+".");
        }
        // Line 2 unused
        std::getline(in_stream, line);
    }
    else {
        // Older Format looks like this:
        // Header Line 1: Test Name:, $String,,,,,..
        // Header Line 2: Sample Rate:, $Value, Hz,,,,,
        // Labels Line 3: Time {SensorName/Acceleration/X,SensorName/Acceleration/Y,SensorName/Acceleration/Z,....} repeated per sensor
        // Units Line 4: s,{m/s^2,m/s^2,m/s^2....} repeated
        int header_lines = 4;

        std::string trialName = tokens[1]; // May contain spaces
        // Line 2
        std::getline(in_stream, line);
        tokens = FileAdapter::tokenize(line, ",");
        dataRate = std::stod(tokens[1]);
        // Line 3, find columns for IMUs
        std::getline(in_stream, line);
        tokens = FileAdapter::tokenize(line, ",");
        OPENSIM_THROW_IF((tokens[0] != TimeLabel), UnexpectedColumnLabel,
            fileName,
            TimeLabel,
            tokens[0]);

        for (int imu_index = 0; imu_index < n_imus; ++imu_index) {
            std::string sensorName = _settings.get_ExperimentalSensors(imu_index).getName();
            labels.push_back(_settings.get_ExperimentalSensors(imu_index).get_name_in_model());
            find_start_column(tokens, APDMDataReader::acceleration_labels, sensorName, accIndex);
            find_start_column(tokens, APDMDataReader::angular_velocity_labels, sensorName, gyroIndex);
            find_start_column(tokens, APDMDataReader::magnetic_heading_labels, sensorName, magIndex);
            find_start_column(tokens, APDMDataReader::orientation_labels, sensorName, orientationsIndex);
        }
    }
    // Will create a table to map
    // internally keep track of what data was found in input files
    bool foundLinearAccelerationData = accIndex.size()>0;
    bool foundMagneticHeadingData = magIndex.size()>0;
    bool foundAngularVelocityData = gyroIndex.size()>0;

    // If no Orientation data is available we'll abort
    OPENSIM_THROW_IF((orientationsIndex.size() == 0),
        TableMissingHeader);
    // Line 4, Units unused
    std::getline(in_stream, line);

    // For all tables, will create row, stitch values from different sensors then append
    bool done = false;
    double time = 0.0;
    double timeIncrement = 1 / dataRate;
    int rowNumber = 0;
    while (!done){
        // Make vectors one per table
        TimeSeriesTableQuaternion::RowVector
            orientation_row_vector{ n_imus, SimTK::Quaternion() };
        TimeSeriesTableVec3::RowVector
            accel_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            magneto_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            gyro_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        std::vector<std::string> nextRow = FileAdapter::getNextLine(in_stream, ",");
        if (nextRow.empty()) {
            done = true;
            break;
        }
        // Cycle through the imus collating values
        for (int imu_index = 0; imu_index < n_imus; ++imu_index) {
            // parse gyro info from in_stream
           if (foundLinearAccelerationData)
                accel_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[accIndex[imu_index]]),
                    std::stod(nextRow[accIndex[imu_index] + 1]), std::stod(nextRow[accIndex[imu_index] + 2]));
            if (foundMagneticHeadingData)
                magneto_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[magIndex[imu_index]]),
                    std::stod(nextRow[magIndex[imu_index] + 1]), std::stod(nextRow[magIndex[imu_index] + 2]));
            if (foundAngularVelocityData)
                gyro_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[gyroIndex[imu_index]]),
                    std::stod(nextRow[gyroIndex[imu_index] + 1]), std::stod(nextRow[gyroIndex[imu_index] + 2]));
            // Create Quaternion from values in file, assume order in file W, X, Y, Z
            orientation_row_vector[imu_index] =
                SimTK::Quaternion(std::stod(nextRow[orientationsIndex[imu_index]]),
                    std::stod(nextRow[orientationsIndex[imu_index] + 1]),
                    std::stod(nextRow[orientationsIndex[imu_index] + 2]),
                    std::stod(nextRow[orientationsIndex[imu_index] + 3]));
        }
        // append to the tables
        times[rowNumber] = time;
        if (foundLinearAccelerationData)
            linearAccelerationData[rowNumber] =  accel_row_vector;
        if (foundMagneticHeadingData)
            magneticHeadingData[rowNumber] = magneto_row_vector;
        if (foundAngularVelocityData)
            angularVelocityData[rowNumber] = gyro_row_vector;
        rotationsData[rowNumber] = orientation_row_vector;
        // We could get some indication of time from file or generate time based on rate
        // Here we use the latter mechanism.
        time += timeIncrement;
        rowNumber++;
        if (std::remainder(rowNumber, last_size) == 0) {
            // resize all Data/Matrices, double the size  while keeping data
            int newSize = last_size*2;
            times.resize(newSize);
            // Repeat for Data matrices in use
            if (foundLinearAccelerationData) linearAccelerationData.resizeKeep(newSize, n_imus);
            if (foundMagneticHeadingData) magneticHeadingData.resizeKeep(newSize, n_imus);
            if (foundAngularVelocityData) angularVelocityData.resizeKeep(newSize, n_imus);
            rotationsData.resizeKeep(newSize, n_imus);
            last_size = newSize;
        }
    }
    // Trim Matrices in use to actual data and move into tables
    times.resize(rowNumber);
    // Repeat for Data matrices in use and create Tables from them or size 0 for empty
    linearAccelerationData.resizeKeep(foundLinearAccelerationData? rowNumber : 0,
        n_imus);
    magneticHeadingData.resizeKeep(foundMagneticHeadingData? rowNumber : 0,
            n_imus);
    angularVelocityData.resizeKeep(foundAngularVelocityData? rowNumber :0,
        n_imus);
    rotationsData.resizeKeep(rowNumber, n_imus);
    // Now create the tables from matrices
    // Create 4 tables for Rotations, LinearAccelerations, AngularVelocity, MagneticHeading
    // Tables could be empty if data is not present in file(s)
    DataAdapter::OutputTables tables = createTablesFromMatrices(dataRate, labels, times,
        rotationsData, linearAccelerationData, magneticHeadingData, angularVelocityData);
    return tables;
}

void APDMDataReader::find_start_column(std::vector<std::string> tokens,
    std::vector<std::string> search_labels,
    const std::string& sensorName,
    std::vector<int>& indices, bool newFromat) const {
    // Search for "sensorName/{search_labels} in tokens, append result to indices if found"
    std::string firstLabel = sensorName + search_labels[0];
    // look for first label, when found check/confirm the rest. Out of order is not supported
    int found_index = -1;
    std::vector<std::string>::iterator it = std::find(tokens.begin(), tokens.end(), firstLabel);
    if (it != tokens.end()) {
        found_index = static_cast<int>(std::distance(tokens.begin(), it));
        // now check the following indices for match with remaining search_labels
        bool match = true;
        for (int remaining = 1; remaining < (int)search_labels.size() && match;
                remaining++) {
            match = tokens[found_index + remaining].
                compare(sensorName + search_labels[remaining]) == 0;
        }
        if (match) {
            if (newFromat)
                indices.push_back(found_index - 3); // Three extra  comma separated fields in header before imu name
            else
                indices.push_back(found_index);
                return;
            }
            else { // first label found but the remaining didn't. Throw
                throw Exception{ "Expected labels for sensor " + sensorName +
                    " were not found." };
            }
        }
    return; // not found
    }
}

<commit_msg>Fix warning in APDMDataReader.cpp.<commit_after>#include <fstream>
#include "Simbody.h"
#include "Exception.h"
#include "FileAdapter.h"
#include "TimeSeriesTable.h"
#include "APDMDataReader.h"

namespace OpenSim {

const std::vector<std::string> APDMDataReader::acceleration_labels{
        "/Acceleration/X", "/Acceleration/Y", "/Acceleration/Z"
};
const std::vector<std::string> APDMDataReader::angular_velocity_labels{
    "/Angular Velocity/X", "/Angular Velocity/Y","/Angular Velocity/Z"
};
const std::vector<std::string> APDMDataReader::magnetic_heading_labels{
    "/Magnetic Field/X", "/Magnetic Field/Y","/Magnetic Field/Z"
};
const std::vector<std::string> APDMDataReader::orientation_labels{
    "/Orientation/Scalar", "/Orientation/X", "/Orientation/Y","/Orientation/Z"
};

const std::string APDMDataReader::TimeLabel{ "Time" };

APDMDataReader* APDMDataReader::clone() const {
    return new APDMDataReader{*this};
}

DataAdapter::OutputTables
APDMDataReader::extendRead(const std::string& fileName) const {

    OPENSIM_THROW_IF(fileName.empty(),
        EmptyFileName);

    std::ifstream in_stream{ fileName };
    OPENSIM_THROW_IF(!in_stream.good(),
        FileDoesNotExist,
        fileName);

    OPENSIM_THROW_IF(in_stream.peek() == std::ifstream::traits_type::eof(),
        FileIsEmpty,
        fileName);

    std::vector<std::string> labels; // will be written to output tables

    double dataRate = SimTK::NaN;
    std::vector<int> accIndex;
    std::vector<int>  gyroIndex;
    std::vector<int>  magIndex;
    std::vector<int>  orientationsIndex;

    int n_imus = _settings.getProperty_ExperimentalSensors().size();
    int last_size = 1024;
    // Will read data into pre-allocated Matrices in-memory rather than appendRow
    // on the fly which copies the whole table on every call.
    SimTK::Matrix_<SimTK::Quaternion> rotationsData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> linearAccelerationData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> magneticHeadingData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> angularVelocityData{ last_size, n_imus };
    std::vector<double> times;
    times.resize(last_size);
    // We support two formats, they contain similar data but headers are different
    std::string line;
    // Line 1
    std::getline(in_stream, line);
    std::vector<std::string> tokens = FileAdapter::tokenize(line, ",");
    bool newFormat = false;
    if (tokens[0] == "Format=7") {
        newFormat = true;
        dataRate = 128; // Will fix after reading computing it from time column
        // Header Line 1:Format=7, [I1,,,$IMU1,,,,,,,,,,,]*
        // Header Line 2: Time,[Accelerometer,,,Gyroscope,,,Magnetometer,,,Barometer,Orientation,,,]*
        // Header Line 3: ,[X,Y,Z,X,Y,Z,X,Y,Z,,S,X,Y,Z]*
        std::vector<std::string> emptyLabels;
        emptyLabels.push_back("");
        // In this format there's no dataRate, either assumed or computed from Time column
        for (int imu_index = 0; imu_index < n_imus; ++imu_index) {
            std::string sensorName = _settings.get_ExperimentalSensors(imu_index).getName();
            labels.push_back(_settings.get_ExperimentalSensors(imu_index).get_name_in_model());
            find_start_column(tokens, emptyLabels, sensorName, accIndex, newFormat);
            if (accIndex[imu_index] != -1) {
                gyroIndex.push_back(accIndex[imu_index] + 3);
                magIndex.push_back(accIndex[imu_index] + 6);
                orientationsIndex.push_back(accIndex[imu_index] + 10);
            }
            else
                OPENSIM_THROW(Exception, "Data for sensor:" +sensorName + "was not found in data file "+ fileName+".");
        }
        // Line 2 unused
        std::getline(in_stream, line);
    }
    else {
        // Older Format looks like this:
        // Header Line 1: Test Name:, $String,,,,,..
        // Header Line 2: Sample Rate:, $Value, Hz,,,,,
        // Labels Line 3: Time {SensorName/Acceleration/X,SensorName/Acceleration/Y,SensorName/Acceleration/Z,....} repeated per sensor
        // Units Line 4: s,{m/s^2,m/s^2,m/s^2....} repeated
        int header_lines = 4;

        std::string trialName = tokens[1]; // May contain spaces
        // Line 2
        std::getline(in_stream, line);
        tokens = FileAdapter::tokenize(line, ",");
        dataRate = std::stod(tokens[1]);
        // Line 3, find columns for IMUs
        std::getline(in_stream, line);
        tokens = FileAdapter::tokenize(line, ",");
        OPENSIM_THROW_IF((tokens[0] != TimeLabel), UnexpectedColumnLabel,
            fileName,
            TimeLabel,
            tokens[0]);

        for (int imu_index = 0; imu_index < n_imus; ++imu_index) {
            std::string sensorName = _settings.get_ExperimentalSensors(imu_index).getName();
            labels.push_back(_settings.get_ExperimentalSensors(imu_index).get_name_in_model());
            find_start_column(tokens, APDMDataReader::acceleration_labels, sensorName, accIndex);
            find_start_column(tokens, APDMDataReader::angular_velocity_labels, sensorName, gyroIndex);
            find_start_column(tokens, APDMDataReader::magnetic_heading_labels, sensorName, magIndex);
            find_start_column(tokens, APDMDataReader::orientation_labels, sensorName, orientationsIndex);
        }
    }
    // Will create a table to map
    // internally keep track of what data was found in input files
    bool foundLinearAccelerationData = accIndex.size()>0;
    bool foundMagneticHeadingData = magIndex.size()>0;
    bool foundAngularVelocityData = gyroIndex.size()>0;

    // If no Orientation data is available we'll abort
    OPENSIM_THROW_IF((orientationsIndex.size() == 0),
        TableMissingHeader);
    // Line 4, Units unused
    std::getline(in_stream, line);

    // For all tables, will create row, stitch values from different sensors then append
    bool done = false;
    double time = 0.0;
    double timeIncrement = 1 / dataRate;
    int rowNumber = 0;
    while (!done){
        // Make vectors one per table
        TimeSeriesTableQuaternion::RowVector
            orientation_row_vector{ n_imus, SimTK::Quaternion() };
        TimeSeriesTableVec3::RowVector
            accel_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            magneto_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            gyro_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        std::vector<std::string> nextRow = FileAdapter::getNextLine(in_stream, ",");
        if (nextRow.empty()) {
            done = true;
            break;
        }
        // Cycle through the imus collating values
        for (int imu_index = 0; imu_index < n_imus; ++imu_index) {
            // parse gyro info from in_stream
           if (foundLinearAccelerationData)
                accel_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[accIndex[imu_index]]),
                    std::stod(nextRow[accIndex[imu_index] + 1]), std::stod(nextRow[accIndex[imu_index] + 2]));
            if (foundMagneticHeadingData)
                magneto_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[magIndex[imu_index]]),
                    std::stod(nextRow[magIndex[imu_index] + 1]), std::stod(nextRow[magIndex[imu_index] + 2]));
            if (foundAngularVelocityData)
                gyro_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[gyroIndex[imu_index]]),
                    std::stod(nextRow[gyroIndex[imu_index] + 1]), std::stod(nextRow[gyroIndex[imu_index] + 2]));
            // Create Quaternion from values in file, assume order in file W, X, Y, Z
            orientation_row_vector[imu_index] =
                SimTK::Quaternion(std::stod(nextRow[orientationsIndex[imu_index]]),
                    std::stod(nextRow[orientationsIndex[imu_index] + 1]),
                    std::stod(nextRow[orientationsIndex[imu_index] + 2]),
                    std::stod(nextRow[orientationsIndex[imu_index] + 3]));
        }
        // append to the tables
        times[rowNumber] = time;
        if (foundLinearAccelerationData)
            linearAccelerationData[rowNumber] =  accel_row_vector;
        if (foundMagneticHeadingData)
            magneticHeadingData[rowNumber] = magneto_row_vector;
        if (foundAngularVelocityData)
            angularVelocityData[rowNumber] = gyro_row_vector;
        rotationsData[rowNumber] = orientation_row_vector;
        // We could get some indication of time from file or generate time based on rate
        // Here we use the latter mechanism.
        time += timeIncrement;
        rowNumber++;
        if (std::remainder(rowNumber, last_size) == 0) {
            // resize all Data/Matrices, double the size  while keeping data
            int newSize = last_size*2;
            times.resize(newSize);
            // Repeat for Data matrices in use
            if (foundLinearAccelerationData) linearAccelerationData.resizeKeep(newSize, n_imus);
            if (foundMagneticHeadingData) magneticHeadingData.resizeKeep(newSize, n_imus);
            if (foundAngularVelocityData) angularVelocityData.resizeKeep(newSize, n_imus);
            rotationsData.resizeKeep(newSize, n_imus);
            last_size = newSize;
        }
    }
    // Trim Matrices in use to actual data and move into tables
    times.resize(rowNumber);
    // Repeat for Data matrices in use and create Tables from them or size 0 for empty
    linearAccelerationData.resizeKeep(foundLinearAccelerationData? rowNumber : 0,
        n_imus);
    magneticHeadingData.resizeKeep(foundMagneticHeadingData? rowNumber : 0,
            n_imus);
    angularVelocityData.resizeKeep(foundAngularVelocityData? rowNumber :0,
        n_imus);
    rotationsData.resizeKeep(rowNumber, n_imus);
    // Now create the tables from matrices
    // Create 4 tables for Rotations, LinearAccelerations, AngularVelocity, MagneticHeading
    // Tables could be empty if data is not present in file(s)
    DataAdapter::OutputTables tables = createTablesFromMatrices(dataRate, labels, times,
        rotationsData, linearAccelerationData, magneticHeadingData, angularVelocityData);
    return tables;
}

void APDMDataReader::find_start_column(std::vector<std::string> tokens,
                                       std::vector<std::string> search_labels, const std::string& sensorName,
                                       std::vector<int>& indices, bool newFormat) const {
    // Search for "sensorName/{search_labels} in tokens, append result to indices if found"
    std::string firstLabel = sensorName + search_labels[0];
    // look for first label, when found check/confirm the rest. Out of order is not supported
    int found_index = -1;
    std::vector<std::string>::iterator it =
            std::find(tokens.begin(), tokens.end(), firstLabel);
    if (it != tokens.end()) {
        found_index = static_cast<int>(std::distance(tokens.begin(), it));
        // now check the following indices for match with remaining search_labels
        bool match = true;
        for (int remaining = 1; remaining < (int)search_labels.size() && match;
             remaining++) {
            match = tokens[found_index + remaining].compare(
                    sensorName + search_labels[remaining]) == 0;
        }
        if (match) {
            if (newFormat) {
                // Three extra comma separated fields in header before imu name
                indices.push_back(found_index - 3);
            } else {
                indices.push_back(found_index);
            }
        } else { // first label found but the remaining didn't. Throw
            throw Exception{"Expected labels for sensor " + sensorName +
                            " were not found."};
        }
    }
}

} // namespace OpenSim
<|endoftext|>


<commit_before>/*
 * This file is part of the libefgy project.
*/

/*
 * Copyright (c) 2012-2013, ef.gy Project Members
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
*/

#include <iostream>

#include <ef.gy/fractions.h>
#include <ef.gy/continued-fractions.h>

using namespace efgy::math;
using namespace std;

int main (int argc, char **argv)
{
    try
    {
        numeric::fractional<number> af(6,11), bf(4,5), rf;
        numeric::continuedFractional<number> a(af), b(bf), r;

        rf = af + bf;
        cerr << string(af) << " + " << string(bf) << " = " << string(rf) << "\n";

        r = a + b;
        rf = r;
        cerr << string(a)  << " + " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 1;
        }

        rf = af - bf;
        cerr << string(af) << " - " << string(bf) << " = " << string(rf) << "\n";

        r = a - b;
        rf = r;
        cerr << string(a)  << " - " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 2;
        }

        rf = af * bf;
        cerr << string(af) << " * " << string(bf) << " = " << string(rf) << "\n";

        r = a * b;
        rf = r;
        cerr << string(a)  << " * " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 3;
        }

        rf = af / bf;
        cerr << string(af) << " / " << string(bf) << " = " << string(rf) << "\n";

        r = a + b;
        rf = r;
        cerr << string(a)  << " / " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 4;
        }
    }
    catch (exception &e)
    {
        cerr << "Exception: " << e.what() << "\n";
        return -1;
    }
    catch (...)
    {
        cerr << "Unknown Exception\n";
        return -1;
    }

    return 0;
}
<commit_msg>fixed test case<commit_after>/*
 * This file is part of the libefgy project.
*/

/*
 * Copyright (c) 2012-2013, ef.gy Project Members
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
*/

#include <iostream>

#include <ef.gy/fractions.h>
#include <ef.gy/continued-fractions.h>

using namespace efgy::math;
using namespace std;

int main (int argc, char **argv)
{
    try
    {
        numeric::fractional<number> af(6,11), bf(4,5), rf;
        numeric::continuedFractional<number> a(af), b(bf), r;

        rf = af + bf;
        cerr << string(af) << " + " << string(bf) << " = " << string(rf) << "\n";

        r = a + b;
        rf = r;
        cerr << string(a)  << " + " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 1;
        }

        rf = af - bf;
        cerr << string(af) << " - " << string(bf) << " = " << string(rf) << "\n";

        r = a - b;
        rf = r;
        cerr << string(a)  << " - " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 2;
        }

        rf = af * bf;
        cerr << string(af) << " * " << string(bf) << " = " << string(rf) << "\n";

        r = a * b;
        rf = r;
        cerr << string(a)  << " * " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 3;
        }

        rf = af / bf;
        cerr << string(af) << " / " << string(bf) << " = " << string(rf) << "\n";

        r = a / b;
        rf = r;
        cerr << string(a)  << " / " << string(b)  << " = " << string(r)  << " = " << string(rf) << "\n";

        if (rf != r)
        {
            return 4;
        }
    }
    catch (exception &e)
    {
        cerr << "Exception: " << e.what() << "\n";
        return -1;
    }
    catch (...)
    {
        cerr << "Unknown Exception\n";
        return -1;
    }

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 <thrift/thrift-config.h>

#include <algorithm>
#include <iostream>
#include <random>

#include <thrift/transport/TSocketPool.h>

using std::pair;
using std::string;
using std::vector;

namespace apache {
namespace thrift {
namespace transport {

using stdcxx::shared_ptr;

/**
 * TSocketPoolServer implementation
 *
 */
TSocketPoolServer::TSocketPoolServer()
  : host_(""), port_(0), socket_(THRIFT_INVALID_SOCKET), lastFailTime_(0), consecutiveFailures_(0) {
}

/**
 * Constructor for TSocketPool server
 */
TSocketPoolServer::TSocketPoolServer(const string& host, int port)
  : host_(host),
    port_(port),
    socket_(THRIFT_INVALID_SOCKET),
    lastFailTime_(0),
    consecutiveFailures_(0) {
}

/**
 * TSocketPool implementation.
 *
 */

TSocketPool::TSocketPool()
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
}

TSocketPool::TSocketPool(const vector<string>& hosts, const vector<int>& ports)
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
  if (hosts.size() != ports.size()) {
    GlobalOutput("TSocketPool::TSocketPool: hosts.size != ports.size");
    throw TTransportException(TTransportException::BAD_ARGS);
  }

  for (unsigned int i = 0; i < hosts.size(); ++i) {
    addServer(hosts[i], ports[i]);
  }
}

TSocketPool::TSocketPool(const vector<pair<string, int> >& servers)
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
  for (unsigned i = 0; i < servers.size(); ++i) {
    addServer(servers[i].first, servers[i].second);
  }
}

TSocketPool::TSocketPool(const vector<shared_ptr<TSocketPoolServer> >& servers)
  : TSocket(),
    servers_(servers),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
}

TSocketPool::TSocketPool(const string& host, int port)
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
  addServer(host, port);
}

TSocketPool::~TSocketPool() {
  vector<shared_ptr<TSocketPoolServer> >::const_iterator iter = servers_.begin();
  vector<shared_ptr<TSocketPoolServer> >::const_iterator iterEnd = servers_.end();
  for (; iter != iterEnd; ++iter) {
    setCurrentServer(*iter);
    TSocketPool::close();
  }
}

void TSocketPool::addServer(const string& host, int port) {
  servers_.push_back(shared_ptr<TSocketPoolServer>(new TSocketPoolServer(host, port)));
}

void TSocketPool::addServer(shared_ptr<TSocketPoolServer>& server) {
  if (server) {
    servers_.push_back(server);
  }
}

void TSocketPool::setServers(const vector<shared_ptr<TSocketPoolServer> >& servers) {
  servers_ = servers;
}

void TSocketPool::getServers(vector<shared_ptr<TSocketPoolServer> >& servers) {
  servers = servers_;
}

void TSocketPool::setNumRetries(int numRetries) {
  numRetries_ = numRetries;
}

void TSocketPool::setRetryInterval(int retryInterval) {
  retryInterval_ = retryInterval;
}

void TSocketPool::setMaxConsecutiveFailures(int maxConsecutiveFailures) {
  maxConsecutiveFailures_ = maxConsecutiveFailures;
}

void TSocketPool::setRandomize(bool randomize) {
  randomize_ = randomize;
}

void TSocketPool::setAlwaysTryLast(bool alwaysTryLast) {
  alwaysTryLast_ = alwaysTryLast;
}

void TSocketPool::setCurrentServer(const shared_ptr<TSocketPoolServer>& server) {
  currentServer_ = server;
  host_ = server->host_;
  port_ = server->port_;
  socket_ = server->socket_;
}

/**
 * This function throws an exception if socket open fails. When socket
 * opens fails, the socket in the current server is reset.
 */
/* TODO: without apc we ignore a lot of functionality from the php version */
void TSocketPool::open() {

  size_t numServers = servers_.size();
  if (numServers == 0) {
    socket_ = THRIFT_INVALID_SOCKET;
    throw TTransportException(TTransportException::NOT_OPEN);
  }

  if (isOpen()) {
    return;
  }

  if (randomize_ && numServers > 1) {
    std::random_device rng;
    std::mt19937 urng(rng());
    std::shuffle(servers_.begin(), servers_.end(), urng);
  }

  for (size_t i = 0; i < numServers; ++i) {

    shared_ptr<TSocketPoolServer>& server = servers_[i];
    // Impersonate the server socket
    setCurrentServer(server);

    if (isOpen()) {
      // already open means we're done
      return;
    }

    bool retryIntervalPassed = (server->lastFailTime_ == 0);
    bool isLastServer = alwaysTryLast_ ? (i == (numServers - 1)) : false;

    if (server->lastFailTime_ > 0) {
      // The server was marked as down, so check if enough time has elapsed to retry
      time_t elapsedTime = time(NULL) - server->lastFailTime_;
      if (elapsedTime > retryInterval_) {
        retryIntervalPassed = true;
      }
    }

    if (retryIntervalPassed || isLastServer) {
      for (int j = 0; j < numRetries_; ++j) {
        try {
          TSocket::open();
        } catch (const TException &e) {
          string errStr = "TSocketPool::open failed " + getSocketInfo() + ": " + e.what();
          GlobalOutput(errStr.c_str());
          socket_ = THRIFT_INVALID_SOCKET;
          continue;
        }

        // Copy over the opened socket so that we can keep it persistent
        server->socket_ = socket_;
        // reset lastFailTime_ is required
        server->lastFailTime_ = 0;
        // success
        return;
      }

      ++server->consecutiveFailures_;
      if (server->consecutiveFailures_ > maxConsecutiveFailures_) {
        // Mark server as down
        server->consecutiveFailures_ = 0;
        server->lastFailTime_ = time(NULL);
      }
    }
  }

  GlobalOutput("TSocketPool::open: all connections failed");
  throw TTransportException(TTransportException::NOT_OPEN);
}

void TSocketPool::close() {
  TSocket::close();
  if (currentServer_) {
    currentServer_->socket_ = THRIFT_INVALID_SOCKET;
  }
}
}
}
} // apache::thrift::transport
<commit_msg>Fix build with old c++<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 <thrift/thrift-config.h>

#include <algorithm>
#include <iostream>
#include <random>

#include <thrift/transport/TSocketPool.h>

using std::pair;
using std::string;
using std::vector;

namespace apache {
namespace thrift {
namespace transport {

using stdcxx::shared_ptr;

/**
 * TSocketPoolServer implementation
 *
 */
TSocketPoolServer::TSocketPoolServer()
  : host_(""), port_(0), socket_(THRIFT_INVALID_SOCKET), lastFailTime_(0), consecutiveFailures_(0) {
}

/**
 * Constructor for TSocketPool server
 */
TSocketPoolServer::TSocketPoolServer(const string& host, int port)
  : host_(host),
    port_(port),
    socket_(THRIFT_INVALID_SOCKET),
    lastFailTime_(0),
    consecutiveFailures_(0) {
}

/**
 * TSocketPool implementation.
 *
 */

TSocketPool::TSocketPool()
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
}

TSocketPool::TSocketPool(const vector<string>& hosts, const vector<int>& ports)
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
  if (hosts.size() != ports.size()) {
    GlobalOutput("TSocketPool::TSocketPool: hosts.size != ports.size");
    throw TTransportException(TTransportException::BAD_ARGS);
  }

  for (unsigned int i = 0; i < hosts.size(); ++i) {
    addServer(hosts[i], ports[i]);
  }
}

TSocketPool::TSocketPool(const vector<pair<string, int> >& servers)
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
  for (unsigned i = 0; i < servers.size(); ++i) {
    addServer(servers[i].first, servers[i].second);
  }
}

TSocketPool::TSocketPool(const vector<shared_ptr<TSocketPoolServer> >& servers)
  : TSocket(),
    servers_(servers),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
}

TSocketPool::TSocketPool(const string& host, int port)
  : TSocket(),
    numRetries_(1),
    retryInterval_(60),
    maxConsecutiveFailures_(1),
    randomize_(true),
    alwaysTryLast_(true) {
  addServer(host, port);
}

TSocketPool::~TSocketPool() {
  vector<shared_ptr<TSocketPoolServer> >::const_iterator iter = servers_.begin();
  vector<shared_ptr<TSocketPoolServer> >::const_iterator iterEnd = servers_.end();
  for (; iter != iterEnd; ++iter) {
    setCurrentServer(*iter);
    TSocketPool::close();
  }
}

void TSocketPool::addServer(const string& host, int port) {
  servers_.push_back(shared_ptr<TSocketPoolServer>(new TSocketPoolServer(host, port)));
}

void TSocketPool::addServer(shared_ptr<TSocketPoolServer>& server) {
  if (server) {
    servers_.push_back(server);
  }
}

void TSocketPool::setServers(const vector<shared_ptr<TSocketPoolServer> >& servers) {
  servers_ = servers;
}

void TSocketPool::getServers(vector<shared_ptr<TSocketPoolServer> >& servers) {
  servers = servers_;
}

void TSocketPool::setNumRetries(int numRetries) {
  numRetries_ = numRetries;
}

void TSocketPool::setRetryInterval(int retryInterval) {
  retryInterval_ = retryInterval;
}

void TSocketPool::setMaxConsecutiveFailures(int maxConsecutiveFailures) {
  maxConsecutiveFailures_ = maxConsecutiveFailures;
}

void TSocketPool::setRandomize(bool randomize) {
  randomize_ = randomize;
}

void TSocketPool::setAlwaysTryLast(bool alwaysTryLast) {
  alwaysTryLast_ = alwaysTryLast;
}

void TSocketPool::setCurrentServer(const shared_ptr<TSocketPoolServer>& server) {
  currentServer_ = server;
  host_ = server->host_;
  port_ = server->port_;
  socket_ = server->socket_;
}

/**
 * This function throws an exception if socket open fails. When socket
 * opens fails, the socket in the current server is reset.
 */
/* TODO: without apc we ignore a lot of functionality from the php version */
void TSocketPool::open() {

  size_t numServers = servers_.size();
  if (numServers == 0) {
    socket_ = THRIFT_INVALID_SOCKET;
    throw TTransportException(TTransportException::NOT_OPEN);
  }

  if (isOpen()) {
    return;
  }

  if (randomize_ && numServers > 1) {
#if __cplusplus >= 201500L // c++17
    std::random_device rng;
    std::mt19937 urng(rng());
    std::shuffle(servers_.begin(), servers_.end(), urng);
#else
    std::random_shuffle(servers_.begin(), servers_.end());
#endif
  }

  for (size_t i = 0; i < numServers; ++i) {

    shared_ptr<TSocketPoolServer>& server = servers_[i];
    // Impersonate the server socket
    setCurrentServer(server);

    if (isOpen()) {
      // already open means we're done
      return;
    }

    bool retryIntervalPassed = (server->lastFailTime_ == 0);
    bool isLastServer = alwaysTryLast_ ? (i == (numServers - 1)) : false;

    if (server->lastFailTime_ > 0) {
      // The server was marked as down, so check if enough time has elapsed to retry
      time_t elapsedTime = time(NULL) - server->lastFailTime_;
      if (elapsedTime > retryInterval_) {
        retryIntervalPassed = true;
      }
    }

    if (retryIntervalPassed || isLastServer) {
      for (int j = 0; j < numRetries_; ++j) {
        try {
          TSocket::open();
        } catch (const TException &e) {
          string errStr = "TSocketPool::open failed " + getSocketInfo() + ": " + e.what();
          GlobalOutput(errStr.c_str());
          socket_ = THRIFT_INVALID_SOCKET;
          continue;
        }

        // Copy over the opened socket so that we can keep it persistent
        server->socket_ = socket_;
        // reset lastFailTime_ is required
        server->lastFailTime_ = 0;
        // success
        return;
      }

      ++server->consecutiveFailures_;
      if (server->consecutiveFailures_ > maxConsecutiveFailures_) {
        // Mark server as down
        server->consecutiveFailures_ = 0;
        server->lastFailTime_ = time(NULL);
      }
    }
  }

  GlobalOutput("TSocketPool::open: all connections failed");
  throw TTransportException(TTransportException::NOT_OPEN);
}

void TSocketPool::close() {
  TSocket::close();
  if (currentServer_) {
    currentServer_->socket_ = THRIFT_INVALID_SOCKET;
  }
}
}
}
} // apache::thrift::transport
<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright (C) 2016 ScyllaDB.
 */

#include "metrics.hh"
#include "metrics_api.hh"
#include <boost/range/algorithm.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/range/algorithm_ext/erase.hpp>

namespace seastar {
namespace metrics {

metric_groups::metric_groups() noexcept : _impl(impl::create_metric_groups()) {
}

void metric_groups::clear() {
    _impl = impl::create_metric_groups();
}

metric_groups::metric_groups(std::initializer_list<metric_group_definition> mg) : _impl(impl::create_metric_groups()) {
    for (auto&& i : mg) {
        add_group(i.name, i.metrics);
    }
}
metric_groups& metric_groups::add_group(const group_name_type& name, const std::initializer_list<metric_definition>& l) {
    _impl->add_group(name, l);
    return *this;
}
metric_group::metric_group() noexcept = default;
metric_group::~metric_group() = default;
metric_group::metric_group(const group_name_type& name, std::initializer_list<metric_definition> l) {
    add_group(name, l);
}

metric_group_definition::metric_group_definition(const group_name_type& name, std::initializer_list<metric_definition> l) : name(name), metrics(l) {
}

metric_group_definition::~metric_group_definition() = default;

metric_groups::~metric_groups() = default;
metric_definition::metric_definition(metric_definition&& m) noexcept : _impl(std::move(m._impl)) {
}

metric_definition::~metric_definition()  = default;

metric_definition::metric_definition(impl::metric_definition_impl const& m) noexcept :
    _impl(std::make_unique<impl::metric_definition_impl>(m)) {
}

bool label_instance::operator<(const label_instance& id2) const {
    auto& id1 = *this;
    return std::tie(id1.key(), id1.value())
                < std::tie(id2.key(), id2.value());
}

bool label_instance::operator==(const label_instance& id2) const {
    auto& id1 = *this;
    return std::tie(id1.key(), id1.value())
                    == std::tie(id2.key(), id2.value());
}

static std::string get_hostname() {
    char hostname[PATH_MAX];
    gethostname(hostname, sizeof(hostname));
    hostname[PATH_MAX-1] = '\0';
    return hostname;
}

boost::program_options::options_description get_options_description() {
    namespace bpo = boost::program_options;
    bpo::options_description opts("Metrics options");
    opts.add_options()(
            "metrics-hostname",
            bpo::value<std::string>()->default_value(get_hostname()),
            "set the hostname used by the metrics, if not set, the local hostname will be used");
    return opts;
}

future<> configure(const boost::program_options::variables_map & opts) {
    impl::config c;
    c.hostname = opts["metrics-hostname"].as<std::string>();
    return smp::invoke_on_all([c] {
        impl::get_local_impl()->set_config(c);
    });
}

bool label_instance::operator!=(const label_instance& id2) const {
    auto& id1 = *this;
    return !(id1 == id2);
}

label shard_label("shard");
label type_label("type");
namespace impl {

registered_metric::registered_metric(metric_id id, metric_function f, bool enabled) :
        _f(f), _impl(get_local_impl()) {
    _info.enabled = enabled;
    _info.id = id;
}

metric_value metric_value::operator+(const metric_value& c) {
    metric_value res(*this);
    switch (_type) {
    case data_type::HISTOGRAM:
        boost::get<histogram>(res.u) += boost::get<histogram>(c.u);
    default:
        boost::get<double>(res.u) += boost::get<double>(c.u);
        break;
    }
    return res;
}

metric_definition_impl::metric_definition_impl(
        metric_name_type name,
        metric_type type,
        metric_function f,
        description d,
        std::vector<label_instance> _labels)
        : name(name), type(type), f(f)
        , d(d), enabled(true) {
    for (auto i: _labels) {
        labels[i.key()] = i.value();
    }
    if (labels.find(shard_label.name()) == labels.end()) {
        labels[shard_label.name()] = shard();
    }
    if (labels.find(type_label.name()) == labels.end()) {
        labels[type_label.name()] = type.type_name;
    }
}

metric_definition_impl& metric_definition_impl::operator ()(bool _enabled) {
    enabled = _enabled;
    return *this;
}

metric_definition_impl& metric_definition_impl::operator ()(const label_instance& label) {
    labels[label.key()] = label.value();
    return *this;
}

std::unique_ptr<metric_groups_def> create_metric_groups() {
    return  std::make_unique<metric_groups_impl>();
}

metric_groups_impl::~metric_groups_impl() {
    for (auto i : _registration) {
        unregister_metric(i);
    }
}

metric_groups_impl& metric_groups_impl::add_metric(group_name_type name, const metric_definition& md)  {

    metric_id id(name, md._impl->name, md._impl->labels);

    get_local_impl()->add_registration(id, md._impl->type.base_type, md._impl->f, md._impl->d, md._impl->enabled);

    _registration.push_back(id);
    return *this;
}

metric_groups_impl& metric_groups_impl::add_group(group_name_type name, const std::vector<metric_definition>& l) {
    for (auto i = l.begin(); i != l.end(); ++i) {
        add_metric(name, *(i->_impl.get()));
    }
    return *this;
}

metric_groups_impl& metric_groups_impl::add_group(group_name_type name, const std::initializer_list<metric_definition>& l) {
    for (auto i = l.begin(); i != l.end(); ++i) {
        add_metric(name, *i);
    }
    return *this;
}

bool metric_id::operator<(
        const metric_id& id2) const {
    return as_tuple() < id2.as_tuple();
}

static std::string safe_name(const sstring& name) {
    auto rep = boost::replace_all_copy(boost::replace_all_copy(name, "-", "_"), " ", "_");
    boost::remove_erase_if(rep, boost::is_any_of("+()"));
    return rep;
}

sstring metric_id::full_name() const {
    return safe_name(_group + "_" + _name);
}

bool metric_id::operator==(
        const metric_id & id2) const {
    return as_tuple() == id2.as_tuple();
}

// Unfortunately, metrics_impl can not be shared because it
// need to be available before the first users (reactor) will call it

shared_ptr<impl>  get_local_impl() {
    static thread_local auto the_impl = ::seastar::make_shared<impl>();
    return the_impl;
}
void impl::remove_registration(const metric_id& id) {
    auto i = get_value_map().find(id.full_name());
    if (i != get_value_map().end()) {
        auto j = i->second.find(id.labels());
        if (j != i->second.end()) {
            j->second = nullptr;
            i->second.erase(j);
        }
        if (i->second.empty()) {
            get_value_map().erase(i);
        }
        dirty();
    }
}

void unregister_metric(const metric_id & id) {
    get_local_impl()->remove_registration(id);
}

const value_map& get_value_map() {
    return get_local_impl()->get_value_map();
}

foreign_ptr<values_reference> get_values() {
    shared_ptr<values_copy> res_ref = ::seastar::make_shared<values_copy>();
    auto& res = *(res_ref.get());
    auto& mv = res.values;
    res.metadata = get_local_impl()->metadata();
    auto & functions = get_local_impl()->functions();
    mv.reserve(functions.size());
    for (auto&& i : functions) {
        value_vector values;
        values.reserve(i.size());
        for (auto&& v : i) {
            values.emplace_back(v());
        }
        mv.emplace_back(std::move(values));
    }
    return res_ref;
}

instance_id_type shard() {
    if (engine_is_ready()) {
        return to_sstring(engine().cpu_id());
    }

    return sstring("0");
}

void impl::update_metrics_if_needed() {
    if (_dirty) {
        // Forcing the metadata to an empty initialization
        // Will prevent using corrupted data if an exception is thrown
        _metadata = ::seastar::make_shared<metric_metadata>();

        auto mt_ref = ::seastar::make_shared<metric_metadata>();
        auto &mt = *(mt_ref.get());
        mt.reserve(_value_map.size());
        _current_metrics.resize(_value_map.size());
        size_t i = 0;
        for (auto&& mf : _value_map) {
            metric_metadata_vector metrics;
            _current_metrics[i].clear();
            for (auto&& m : mf.second) {
                if (m.second && m.second->is_enabled()) {
                    metrics.emplace_back(m.second->info());
                    _current_metrics[i].emplace_back(m.second->get_function());
                }
            }
            if (!metrics.empty()) {
                // If nothing was added, no need to add the metric_family
                // and no need to progress
                mt.emplace_back(metric_family_metadata{mf.second.info(), std::move(metrics)});
                i++;
            }
        }
        // Maybe we didn't use all the original size
        _current_metrics.resize(i);
        _metadata = mt_ref;
        _dirty = false;
    }
}

shared_ptr<metric_metadata> impl::metadata() {
    update_metrics_if_needed();
    return _metadata;
}

std::vector<std::vector<metric_function>>& impl::functions() {
    update_metrics_if_needed();
    return _current_metrics;
}

void impl::add_registration(const metric_id& id, data_type type, metric_function f, const description& d, bool enabled) {
    auto rm = ::seastar::make_shared<registered_metric>(id, f, enabled);
    sstring name = id.full_name();
    if (_value_map.find(name) != _value_map.end()) {
        auto& metric = _value_map[name];
        if (metric.find(id.labels()) != metric.end()) {
            throw std::runtime_error("registering metrics twice for metrics: " + name);
        }
        if (metric.info().type != type) {
            throw std::runtime_error("registering metrics " + name + " registered with different type.");
        }
        metric[id.labels()] = rm;
    } else {
        _value_map[name].info().type = type;
        _value_map[name].info().d = d;
        _value_map[name].info().name = id.full_name();
        _value_map[name][id.labels()] = rm;
    }
    dirty();
}

}

const bool metric_disabled = false;

histogram& histogram::operator+=(const histogram& c) {
    for (size_t i = 0; i < c.buckets.size(); i++) {
        if (buckets.size() <= i) {
            buckets.push_back(c.buckets[i]);
        } else {
            if (buckets[i].upper_bound != c.buckets[i].upper_bound) {
                throw std::out_of_range("Trying to add histogram with different bucket limits");
            }
            buckets[i].count += c.buckets[i].count;
        }
    }
    return *this;
}

histogram histogram::operator+(const histogram& c) const {
    histogram res = *this;
    res += c;
    return res;
}

histogram histogram::operator+(histogram&& c) const {
    c += *this;
    return std::move(c);
}

}
}
<commit_msg>metrics: Add missing `break` to metric_value::operator+()<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright (C) 2016 ScyllaDB.
 */

#include "metrics.hh"
#include "metrics_api.hh"
#include <boost/range/algorithm.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/range/algorithm_ext/erase.hpp>

namespace seastar {
namespace metrics {

metric_groups::metric_groups() noexcept : _impl(impl::create_metric_groups()) {
}

void metric_groups::clear() {
    _impl = impl::create_metric_groups();
}

metric_groups::metric_groups(std::initializer_list<metric_group_definition> mg) : _impl(impl::create_metric_groups()) {
    for (auto&& i : mg) {
        add_group(i.name, i.metrics);
    }
}
metric_groups& metric_groups::add_group(const group_name_type& name, const std::initializer_list<metric_definition>& l) {
    _impl->add_group(name, l);
    return *this;
}
metric_group::metric_group() noexcept = default;
metric_group::~metric_group() = default;
metric_group::metric_group(const group_name_type& name, std::initializer_list<metric_definition> l) {
    add_group(name, l);
}

metric_group_definition::metric_group_definition(const group_name_type& name, std::initializer_list<metric_definition> l) : name(name), metrics(l) {
}

metric_group_definition::~metric_group_definition() = default;

metric_groups::~metric_groups() = default;
metric_definition::metric_definition(metric_definition&& m) noexcept : _impl(std::move(m._impl)) {
}

metric_definition::~metric_definition()  = default;

metric_definition::metric_definition(impl::metric_definition_impl const& m) noexcept :
    _impl(std::make_unique<impl::metric_definition_impl>(m)) {
}

bool label_instance::operator<(const label_instance& id2) const {
    auto& id1 = *this;
    return std::tie(id1.key(), id1.value())
                < std::tie(id2.key(), id2.value());
}

bool label_instance::operator==(const label_instance& id2) const {
    auto& id1 = *this;
    return std::tie(id1.key(), id1.value())
                    == std::tie(id2.key(), id2.value());
}

static std::string get_hostname() {
    char hostname[PATH_MAX];
    gethostname(hostname, sizeof(hostname));
    hostname[PATH_MAX-1] = '\0';
    return hostname;
}

boost::program_options::options_description get_options_description() {
    namespace bpo = boost::program_options;
    bpo::options_description opts("Metrics options");
    opts.add_options()(
            "metrics-hostname",
            bpo::value<std::string>()->default_value(get_hostname()),
            "set the hostname used by the metrics, if not set, the local hostname will be used");
    return opts;
}

future<> configure(const boost::program_options::variables_map & opts) {
    impl::config c;
    c.hostname = opts["metrics-hostname"].as<std::string>();
    return smp::invoke_on_all([c] {
        impl::get_local_impl()->set_config(c);
    });
}

bool label_instance::operator!=(const label_instance& id2) const {
    auto& id1 = *this;
    return !(id1 == id2);
}

label shard_label("shard");
label type_label("type");
namespace impl {

registered_metric::registered_metric(metric_id id, metric_function f, bool enabled) :
        _f(f), _impl(get_local_impl()) {
    _info.enabled = enabled;
    _info.id = id;
}

metric_value metric_value::operator+(const metric_value& c) {
    metric_value res(*this);
    switch (_type) {
    case data_type::HISTOGRAM:
        boost::get<histogram>(res.u) += boost::get<histogram>(c.u);
        break;
    default:
        boost::get<double>(res.u) += boost::get<double>(c.u);
        break;
    }
    return res;
}

metric_definition_impl::metric_definition_impl(
        metric_name_type name,
        metric_type type,
        metric_function f,
        description d,
        std::vector<label_instance> _labels)
        : name(name), type(type), f(f)
        , d(d), enabled(true) {
    for (auto i: _labels) {
        labels[i.key()] = i.value();
    }
    if (labels.find(shard_label.name()) == labels.end()) {
        labels[shard_label.name()] = shard();
    }
    if (labels.find(type_label.name()) == labels.end()) {
        labels[type_label.name()] = type.type_name;
    }
}

metric_definition_impl& metric_definition_impl::operator ()(bool _enabled) {
    enabled = _enabled;
    return *this;
}

metric_definition_impl& metric_definition_impl::operator ()(const label_instance& label) {
    labels[label.key()] = label.value();
    return *this;
}

std::unique_ptr<metric_groups_def> create_metric_groups() {
    return  std::make_unique<metric_groups_impl>();
}

metric_groups_impl::~metric_groups_impl() {
    for (auto i : _registration) {
        unregister_metric(i);
    }
}

metric_groups_impl& metric_groups_impl::add_metric(group_name_type name, const metric_definition& md)  {

    metric_id id(name, md._impl->name, md._impl->labels);

    get_local_impl()->add_registration(id, md._impl->type.base_type, md._impl->f, md._impl->d, md._impl->enabled);

    _registration.push_back(id);
    return *this;
}

metric_groups_impl& metric_groups_impl::add_group(group_name_type name, const std::vector<metric_definition>& l) {
    for (auto i = l.begin(); i != l.end(); ++i) {
        add_metric(name, *(i->_impl.get()));
    }
    return *this;
}

metric_groups_impl& metric_groups_impl::add_group(group_name_type name, const std::initializer_list<metric_definition>& l) {
    for (auto i = l.begin(); i != l.end(); ++i) {
        add_metric(name, *i);
    }
    return *this;
}

bool metric_id::operator<(
        const metric_id& id2) const {
    return as_tuple() < id2.as_tuple();
}

static std::string safe_name(const sstring& name) {
    auto rep = boost::replace_all_copy(boost::replace_all_copy(name, "-", "_"), " ", "_");
    boost::remove_erase_if(rep, boost::is_any_of("+()"));
    return rep;
}

sstring metric_id::full_name() const {
    return safe_name(_group + "_" + _name);
}

bool metric_id::operator==(
        const metric_id & id2) const {
    return as_tuple() == id2.as_tuple();
}

// Unfortunately, metrics_impl can not be shared because it
// need to be available before the first users (reactor) will call it

shared_ptr<impl>  get_local_impl() {
    static thread_local auto the_impl = ::seastar::make_shared<impl>();
    return the_impl;
}
void impl::remove_registration(const metric_id& id) {
    auto i = get_value_map().find(id.full_name());
    if (i != get_value_map().end()) {
        auto j = i->second.find(id.labels());
        if (j != i->second.end()) {
            j->second = nullptr;
            i->second.erase(j);
        }
        if (i->second.empty()) {
            get_value_map().erase(i);
        }
        dirty();
    }
}

void unregister_metric(const metric_id & id) {
    get_local_impl()->remove_registration(id);
}

const value_map& get_value_map() {
    return get_local_impl()->get_value_map();
}

foreign_ptr<values_reference> get_values() {
    shared_ptr<values_copy> res_ref = ::seastar::make_shared<values_copy>();
    auto& res = *(res_ref.get());
    auto& mv = res.values;
    res.metadata = get_local_impl()->metadata();
    auto & functions = get_local_impl()->functions();
    mv.reserve(functions.size());
    for (auto&& i : functions) {
        value_vector values;
        values.reserve(i.size());
        for (auto&& v : i) {
            values.emplace_back(v());
        }
        mv.emplace_back(std::move(values));
    }
    return res_ref;
}

instance_id_type shard() {
    if (engine_is_ready()) {
        return to_sstring(engine().cpu_id());
    }

    return sstring("0");
}

void impl::update_metrics_if_needed() {
    if (_dirty) {
        // Forcing the metadata to an empty initialization
        // Will prevent using corrupted data if an exception is thrown
        _metadata = ::seastar::make_shared<metric_metadata>();

        auto mt_ref = ::seastar::make_shared<metric_metadata>();
        auto &mt = *(mt_ref.get());
        mt.reserve(_value_map.size());
        _current_metrics.resize(_value_map.size());
        size_t i = 0;
        for (auto&& mf : _value_map) {
            metric_metadata_vector metrics;
            _current_metrics[i].clear();
            for (auto&& m : mf.second) {
                if (m.second && m.second->is_enabled()) {
                    metrics.emplace_back(m.second->info());
                    _current_metrics[i].emplace_back(m.second->get_function());
                }
            }
            if (!metrics.empty()) {
                // If nothing was added, no need to add the metric_family
                // and no need to progress
                mt.emplace_back(metric_family_metadata{mf.second.info(), std::move(metrics)});
                i++;
            }
        }
        // Maybe we didn't use all the original size
        _current_metrics.resize(i);
        _metadata = mt_ref;
        _dirty = false;
    }
}

shared_ptr<metric_metadata> impl::metadata() {
    update_metrics_if_needed();
    return _metadata;
}

std::vector<std::vector<metric_function>>& impl::functions() {
    update_metrics_if_needed();
    return _current_metrics;
}

void impl::add_registration(const metric_id& id, data_type type, metric_function f, const description& d, bool enabled) {
    auto rm = ::seastar::make_shared<registered_metric>(id, f, enabled);
    sstring name = id.full_name();
    if (_value_map.find(name) != _value_map.end()) {
        auto& metric = _value_map[name];
        if (metric.find(id.labels()) != metric.end()) {
            throw std::runtime_error("registering metrics twice for metrics: " + name);
        }
        if (metric.info().type != type) {
            throw std::runtime_error("registering metrics " + name + " registered with different type.");
        }
        metric[id.labels()] = rm;
    } else {
        _value_map[name].info().type = type;
        _value_map[name].info().d = d;
        _value_map[name].info().name = id.full_name();
        _value_map[name][id.labels()] = rm;
    }
    dirty();
}

}

const bool metric_disabled = false;

histogram& histogram::operator+=(const histogram& c) {
    for (size_t i = 0; i < c.buckets.size(); i++) {
        if (buckets.size() <= i) {
            buckets.push_back(c.buckets[i]);
        } else {
            if (buckets[i].upper_bound != c.buckets[i].upper_bound) {
                throw std::out_of_range("Trying to add histogram with different bucket limits");
            }
            buckets[i].count += c.buckets[i].count;
        }
    }
    return *this;
}

histogram histogram::operator+(const histogram& c) const {
    histogram res = *this;
    res += c;
    return res;
}

histogram histogram::operator+(histogram&& c) const {
    c += *this;
    return std::move(c);
}

}
}
<|endoftext|>


<commit_before>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "HistogramManager.h"

#include <vtkFloatArray.h>
#include <vtkImageData.h>
#include <vtkPointData.h>
#include <vtkTable.h>
#include <vtkUnsignedLongLongArray.h>

#include "ComputeHistogram.h"

#include <QCoreApplication>
#include <QThread>

Q_DECLARE_METATYPE(vtkSmartPointer<vtkImageData>)
Q_DECLARE_METATYPE(vtkSmartPointer<vtkTable>)

namespace {

// This is just here for now - quick and dirty historgram calculations...
void PopulateHistogram(vtkImageData* input, vtkTable* output)
{
  // The output table will have the twice the number of columns, they will be
  // the x and y for input column. This is the bin centers, and the population.
  double minmax[2] = { 0.0, 0.0 };

  // This number of bins in the 2D histogram will also be used as the number of
  // bins in the 2D transfer function for X (scalar value) and Y (gradient mag.)
  const int numberOfBins = 256;

  // Keep the array we are working on around even if the user shallow copies
  // over the input image data by incrementing the reference count here.
  vtkSmartPointer<vtkDataArray> arrayPtr = input->GetPointData()->GetScalars();
  if (!arrayPtr) {
    return;
  }

  // The bin values are the centers, extending +/- half an inc either side
  arrayPtr->GetFiniteRange(minmax, -1);
  if (minmax[0] == minmax[1]) {
    minmax[1] = minmax[0] + 1.0;
  }

  double inc = (minmax[1] - minmax[0]) / (numberOfBins - 1);
  double halfInc = inc / 2.0;
  vtkSmartPointer<vtkFloatArray> extents =
    vtkFloatArray::SafeDownCast(output->GetColumnByName("image_extents"));
  if (!extents) {
    extents = vtkSmartPointer<vtkFloatArray>::New();
    extents->SetName("image_extents");
  }
  extents->SetNumberOfTuples(numberOfBins);
  double min = minmax[0] + halfInc;
  for (int j = 0; j < numberOfBins; ++j) {
    extents->SetValue(j, min + j * inc);
  }
  vtkSmartPointer<vtkUnsignedLongLongArray> populations =
    vtkUnsignedLongLongArray::SafeDownCast(
      output->GetColumnByName("image_pops"));
  if (!populations) {
    populations = vtkSmartPointer<vtkUnsignedLongLongArray>::New();
    populations->SetName("image_pops");
  }
  populations->SetNumberOfTuples(numberOfBins);
  auto pops = static_cast<uint64_t*>(populations->GetVoidPointer(0));
  for (int k = 0; k < numberOfBins; ++k) {
    pops[k] = 0;
  }
  int invalid = 0;

  switch (arrayPtr->GetDataType()) {
    vtkTemplateMacro(tomviz::CalculateHistogram(
      reinterpret_cast<VTK_TT*>(arrayPtr->GetVoidPointer(0)),
      arrayPtr->GetNumberOfTuples(), arrayPtr->GetNumberOfComponents(),
      minmax[0], minmax[1], pops, 1.0 / inc, invalid));
    default:
      cout << "UpdateFromFile: Unknown data type" << endl;
  }

#ifndef NDEBUG
  vtkIdType total = invalid;
  for (int i = 0; i < numberOfBins; ++i)
    total += pops[i];
  assert(total == arrayPtr->GetNumberOfTuples());
#endif
  if (invalid) {
    cout << "Warning: NaN or infinite value in dataset" << endl;
  }

  output->AddColumn(extents);
  output->AddColumn(populations);
}

void Populate2DHistogram(vtkImageData* input, vtkImageData* output)
{
  double minmax[2] = { 0.0, 0.0 };
  const int numberOfBins = 256;

  // Keep the array we are working on around even if the user shallow copies
  // over the input image data by incrementing the reference count here.
  vtkSmartPointer<vtkDataArray> arrayPtr = input->GetPointData()->GetScalars();
  if (!arrayPtr) {
    return;
  }

  // The bin values are the centers, extending +/- half an inc either side
  arrayPtr->GetFiniteRange(minmax, -1);
  if (minmax[0] == minmax[1]) {
    minmax[1] = minmax[0] + 1.0;
  }

  // vtkPlotHistogram2D expects the histogram array to be VTK_DOUBLE
  output->SetDimensions(numberOfBins, numberOfBins, 1);
  output->AllocateScalars(VTK_DOUBLE, 1);

  // Get input parameters
  int dim[3];
  input->GetDimensions(dim);
  int numComp = arrayPtr->GetNumberOfComponents();
  double spacing[3];
  input->GetSpacing(spacing);

  switch (arrayPtr->GetDataType()) {
    vtkTemplateMacro(tomviz::Calculate2DHistogram(
      reinterpret_cast<VTK_TT*>(arrayPtr->GetVoidPointer(0)), dim, numComp,
      minmax, output, spacing));
    default:
      cout << "UpdateFromFile: Unknown data type" << endl;
  }
}

} // namespace

namespace tomviz {

// This is a QObject that will be owned by the background thread
// and use signals/slots to create histograms
class HistogramMaker : public QObject
{
  Q_OBJECT

  void run();

public:
  HistogramMaker(QObject* p = nullptr) : QObject(p) {}

public slots:
  void makeHistogram(vtkSmartPointer<vtkImageData> input,
                     vtkSmartPointer<vtkTable> output);

  void makeHistogram2D(vtkSmartPointer<vtkImageData> input,
                       vtkSmartPointer<vtkImageData> output);

signals:
  void histogramDone(vtkSmartPointer<vtkImageData> image,
                     vtkSmartPointer<vtkTable> output);

  void histogram2DDone(vtkSmartPointer<vtkImageData> image,
                       vtkSmartPointer<vtkImageData> output);
};

void HistogramMaker::makeHistogram(vtkSmartPointer<vtkImageData> input,
                                   vtkSmartPointer<vtkTable> output)
{
  // make the histogram and notify observers (the main thread) that it
  // is done.
  if (input && output) {
    PopulateHistogram(input, output);
  }
  emit histogramDone(input, output);
}

void HistogramMaker::makeHistogram2D(vtkSmartPointer<vtkImageData> input,
                                     vtkSmartPointer<vtkImageData> output)
{
  if (input && output) {
    Populate2DHistogram(input, output);
  }
  emit histogram2DDone(input, output);
}

HistogramManager::HistogramManager()
  : m_histogramGen(new HistogramMaker), m_worker(new QThread(this))
{
  qRegisterMetaType<vtkSmartPointer<vtkImageData>>();
  qRegisterMetaType<vtkSmartPointer<vtkTable>>();

  // Start the worker thread and give it ownership of the HistogramMaker
  // object. Also connect the HistogramMaker's signal to the histogramReady
  // slot on this object. This slot will be called on the GUI thread when the
  // histogram has been finished on the background thread.
  m_worker->start();
  m_histogramGen->moveToThread(m_worker);
  connect(m_histogramGen, SIGNAL(histogramDone(vtkSmartPointer<vtkImageData>,
                                               vtkSmartPointer<vtkTable>)),
          SLOT(histogramReadyInternal(vtkSmartPointer<vtkImageData>,
                                      vtkSmartPointer<vtkTable>)));
  connect(m_histogramGen,
          SIGNAL(histogram2DDone(vtkSmartPointer<vtkImageData>,
                                 vtkSmartPointer<vtkImageData>)),
          SLOT(histogram2DReadyInternal(vtkSmartPointer<vtkImageData>,
                                        vtkSmartPointer<vtkImageData>)));
}

HistogramManager::~HistogramManager()
{
  if (m_worker) {
    finalize();
  }
}

void HistogramManager::finalize()
{
  // disconnect all signals/slots
  disconnect(m_histogramGen, nullptr, nullptr, nullptr);
  // when the HistogramMaker is deleted, kill the background thread
  connect(m_histogramGen, SIGNAL(destroyed()), m_worker, SLOT(quit()));
  // I can't remember if deleteLater must be called on the owning thread
  // play it safe and let the owning thread call it.
  QMetaObject::invokeMethod(m_histogramGen, "deleteLater");
  // Wait for the background thread to clean up the object and quit
  while (m_worker->isRunning()) {
    QCoreApplication::processEvents();
  }
  m_worker = nullptr;
  m_histogramCache.clear();
  m_histogram2DCache.clear();
}

HistogramManager& HistogramManager::instance()
{
  static HistogramManager theInstance;
  return theInstance;
}

vtkSmartPointer<vtkTable> HistogramManager::getHistogram(
  vtkSmartPointer<vtkImageData> image)
{
  if (m_histogramCache.contains(image)) {
    auto cachedTable = m_histogramCache[image];
    if (cachedTable->GetMTime() > image->GetMTime()) {
      return cachedTable;
    } else {
      // Need to recalculate, clear the plots, and remove the cached data.
      m_histogramCache.remove(image);
    }
  }
  if (m_histogramsInProgress.contains(image)) {
    // it is in progress, don't start a new one
    return nullptr;
  }
  auto table = vtkSmartPointer<vtkTable>::New();
  m_histogramsInProgress.append(image);
  vtkSmartPointer<vtkImageData> const imageSP = image;

  // This fakes a Qt signal to the background thread (without exposing the
  // class internals as a signal).  The background thread will then call
  // makeHistogram on the HistogramMaker object with the parameters we
  // gave here.
  QMetaObject::invokeMethod(m_histogramGen, "makeHistogram",
                            Q_ARG(vtkSmartPointer<vtkImageData>, imageSP),
                            Q_ARG(vtkSmartPointer<vtkTable>, table));

  // The histogram cannot be returned for use while the background thread is
  // populating it.
  return nullptr;
}

vtkSmartPointer<vtkImageData> HistogramManager::getHistogram2D(
  vtkSmartPointer<vtkImageData> image)
{
  if (m_histogram2DCache.contains(image)) {
    auto cachedHistogram = m_histogram2DCache[image];
    if (cachedHistogram->GetMTime() > image->GetMTime()) {
      return cachedHistogram;
    } else {
      // Need to recalculate, clear the plots, and remove the cached data.
      m_histogram2DCache.remove(image);
    }
  }
  if (m_histogram2DsInProgress.contains(image)) {
    // it is in progress, don't start a new one
    return nullptr;
  }
  auto histogram = vtkSmartPointer<vtkImageData>::New();
  m_histogram2DsInProgress.append(image);
  vtkSmartPointer<vtkImageData> const imageSP = image;

  // This fakes a Qt signal to the background thread (without exposing the
  // class internals as a signal).  The background thread will then call
  // makeHistogram on the HistogramMaker object with the parameters we
  // gave here.
  QMetaObject::invokeMethod(m_histogramGen, "makeHistogram2D",
                            Q_ARG(vtkSmartPointer<vtkImageData>, imageSP),
                            Q_ARG(vtkSmartPointer<vtkImageData>, histogram));
  // The histogram cannot be returned for use while the background thread is
  // populating it.
  return nullptr;
}

void HistogramManager::histogramReadyInternal(
  vtkSmartPointer<vtkImageData> image, vtkSmartPointer<vtkTable> histogram)
{
  m_histogramCache[image] = histogram;
  m_histogramsInProgress.removeAll(image);
  emit this->histogramReady(image, histogram);
}

void HistogramManager::histogram2DReadyInternal(
  vtkSmartPointer<vtkImageData> image, vtkSmartPointer<vtkImageData> histogram)
{
  m_histogram2DCache[image] = histogram;
  m_histogram2DsInProgress.removeAll(image);
  emit this->histogram2DReady(image, histogram);
}

} // namespace tomviz

#include "HistogramManager.moc"
<commit_msg>Get actual range of all components for histogram<commit_after>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "HistogramManager.h"

#include <vtkFloatArray.h>
#include <vtkImageData.h>
#include <vtkPointData.h>
#include <vtkTable.h>
#include <vtkUnsignedLongLongArray.h>

#include "ComputeHistogram.h"

#include <QCoreApplication>
#include <QThread>

Q_DECLARE_METATYPE(vtkSmartPointer<vtkImageData>)
Q_DECLARE_METATYPE(vtkSmartPointer<vtkTable>)

namespace {

// This is just here for now - quick and dirty historgram calculations...
void PopulateHistogram(vtkImageData* input, vtkTable* output)
{
  // The output table will have the twice the number of columns, they will be
  // the x and y for input column. This is the bin centers, and the population.
  double minmax[2] = { 0.0, 0.0 };

  // This number of bins in the 2D histogram will also be used as the number of
  // bins in the 2D transfer function for X (scalar value) and Y (gradient mag.)
  const int numberOfBins = 256;

  // Keep the array we are working on around even if the user shallow copies
  // over the input image data by incrementing the reference count here.
  vtkSmartPointer<vtkDataArray> arrayPtr = input->GetPointData()->GetScalars();
  if (!arrayPtr) {
    return;
  }

  // The bin values are the centers, extending +/- half an inc either side
  arrayPtr->GetFiniteRange(minmax, -1);
  if (minmax[0] == minmax[1]) {
    minmax[1] = minmax[0] + 1.0;
  }

  double inc = (minmax[1] - minmax[0]) / (numberOfBins - 1);
  double halfInc = inc / 2.0;
  vtkSmartPointer<vtkFloatArray> extents =
    vtkFloatArray::SafeDownCast(output->GetColumnByName("image_extents"));
  if (!extents) {
    extents = vtkSmartPointer<vtkFloatArray>::New();
    extents->SetName("image_extents");
  }
  extents->SetNumberOfTuples(numberOfBins);
  double min = minmax[0] + halfInc;
  for (int j = 0; j < numberOfBins; ++j) {
    extents->SetValue(j, min + j * inc);
  }
  vtkSmartPointer<vtkUnsignedLongLongArray> populations =
    vtkUnsignedLongLongArray::SafeDownCast(
      output->GetColumnByName("image_pops"));
  if (!populations) {
    populations = vtkSmartPointer<vtkUnsignedLongLongArray>::New();
    populations->SetName("image_pops");
  }
  populations->SetNumberOfTuples(numberOfBins);
  auto pops = static_cast<uint64_t*>(populations->GetVoidPointer(0));
  for (int k = 0; k < numberOfBins; ++k) {
    pops[k] = 0;
  }
  int invalid = 0;

  switch (arrayPtr->GetDataType()) {
    vtkTemplateMacro(tomviz::CalculateHistogram(
      reinterpret_cast<VTK_TT*>(arrayPtr->GetVoidPointer(0)),
      arrayPtr->GetNumberOfTuples(), arrayPtr->GetNumberOfComponents(),
      minmax[0], minmax[1], pops, 1.0 / inc, invalid));
    default:
      cout << "UpdateFromFile: Unknown data type" << endl;
  }

#ifndef NDEBUG
  vtkIdType total = invalid;
  for (int i = 0; i < numberOfBins; ++i)
    total += pops[i];
  assert(total == arrayPtr->GetNumberOfTuples());
#endif
  if (invalid) {
    cout << "Warning: NaN or infinite value in dataset" << endl;
  }

  output->AddColumn(extents);
  output->AddColumn(populations);
}

void Populate2DHistogram(vtkImageData* input, vtkImageData* output)
{
  double minmax[2] = { DBL_MAX, -DBL_MAX };
  const int numberOfBins = 256;

  // Keep the array we are working on around even if the user shallow copies
  // over the input image data by incrementing the reference count here.
  vtkSmartPointer<vtkDataArray> arrayPtr = input->GetPointData()->GetScalars();
  if (!arrayPtr) {
    return;
  }

  // The bin values are the centers, extending +/- half an inc either side
  for (int i = 0; i < arrayPtr->GetNumberOfComponents(); ++i) {
    double* tmp = arrayPtr->GetFiniteRange(i);
    minmax[0] = std::min(minmax[0], tmp[0]);
    minmax[1] = std::max(minmax[1], tmp[1]);
  }

  if (minmax[0] == minmax[1]) {
    minmax[1] = minmax[0] + 1.0;
  }

  // vtkPlotHistogram2D expects the histogram array to be VTK_DOUBLE
  output->SetDimensions(numberOfBins, numberOfBins, 1);
  output->AllocateScalars(VTK_DOUBLE, 1);

  // Get input parameters
  int dim[3];
  input->GetDimensions(dim);
  int numComp = arrayPtr->GetNumberOfComponents();
  double spacing[3];
  input->GetSpacing(spacing);

  switch (arrayPtr->GetDataType()) {
    vtkTemplateMacro(tomviz::Calculate2DHistogram(
      reinterpret_cast<VTK_TT*>(arrayPtr->GetVoidPointer(0)), dim, numComp,
      minmax, output, spacing));
    default:
      cout << "UpdateFromFile: Unknown data type" << endl;
  }
}

} // namespace

namespace tomviz {

// This is a QObject that will be owned by the background thread
// and use signals/slots to create histograms
class HistogramMaker : public QObject
{
  Q_OBJECT

  void run();

public:
  HistogramMaker(QObject* p = nullptr) : QObject(p) {}

public slots:
  void makeHistogram(vtkSmartPointer<vtkImageData> input,
                     vtkSmartPointer<vtkTable> output);

  void makeHistogram2D(vtkSmartPointer<vtkImageData> input,
                       vtkSmartPointer<vtkImageData> output);

signals:
  void histogramDone(vtkSmartPointer<vtkImageData> image,
                     vtkSmartPointer<vtkTable> output);

  void histogram2DDone(vtkSmartPointer<vtkImageData> image,
                       vtkSmartPointer<vtkImageData> output);
};

void HistogramMaker::makeHistogram(vtkSmartPointer<vtkImageData> input,
                                   vtkSmartPointer<vtkTable> output)
{
  // make the histogram and notify observers (the main thread) that it
  // is done.
  if (input && output) {
    PopulateHistogram(input, output);
  }
  emit histogramDone(input, output);
}

void HistogramMaker::makeHistogram2D(vtkSmartPointer<vtkImageData> input,
                                     vtkSmartPointer<vtkImageData> output)
{
  if (input && output) {
    Populate2DHistogram(input, output);
  }
  emit histogram2DDone(input, output);
}

HistogramManager::HistogramManager()
  : m_histogramGen(new HistogramMaker), m_worker(new QThread(this))
{
  qRegisterMetaType<vtkSmartPointer<vtkImageData>>();
  qRegisterMetaType<vtkSmartPointer<vtkTable>>();

  // Start the worker thread and give it ownership of the HistogramMaker
  // object. Also connect the HistogramMaker's signal to the histogramReady
  // slot on this object. This slot will be called on the GUI thread when the
  // histogram has been finished on the background thread.
  m_worker->start();
  m_histogramGen->moveToThread(m_worker);
  connect(m_histogramGen, SIGNAL(histogramDone(vtkSmartPointer<vtkImageData>,
                                               vtkSmartPointer<vtkTable>)),
          SLOT(histogramReadyInternal(vtkSmartPointer<vtkImageData>,
                                      vtkSmartPointer<vtkTable>)));
  connect(m_histogramGen,
          SIGNAL(histogram2DDone(vtkSmartPointer<vtkImageData>,
                                 vtkSmartPointer<vtkImageData>)),
          SLOT(histogram2DReadyInternal(vtkSmartPointer<vtkImageData>,
                                        vtkSmartPointer<vtkImageData>)));
}

HistogramManager::~HistogramManager()
{
  if (m_worker) {
    finalize();
  }
}

void HistogramManager::finalize()
{
  // disconnect all signals/slots
  disconnect(m_histogramGen, nullptr, nullptr, nullptr);
  // when the HistogramMaker is deleted, kill the background thread
  connect(m_histogramGen, SIGNAL(destroyed()), m_worker, SLOT(quit()));
  // I can't remember if deleteLater must be called on the owning thread
  // play it safe and let the owning thread call it.
  QMetaObject::invokeMethod(m_histogramGen, "deleteLater");
  // Wait for the background thread to clean up the object and quit
  while (m_worker->isRunning()) {
    QCoreApplication::processEvents();
  }
  m_worker = nullptr;
  m_histogramCache.clear();
  m_histogram2DCache.clear();
}

HistogramManager& HistogramManager::instance()
{
  static HistogramManager theInstance;
  return theInstance;
}

vtkSmartPointer<vtkTable> HistogramManager::getHistogram(
  vtkSmartPointer<vtkImageData> image)
{
  if (m_histogramCache.contains(image)) {
    auto cachedTable = m_histogramCache[image];
    if (cachedTable->GetMTime() > image->GetMTime()) {
      return cachedTable;
    } else {
      // Need to recalculate, clear the plots, and remove the cached data.
      m_histogramCache.remove(image);
    }
  }
  if (m_histogramsInProgress.contains(image)) {
    // it is in progress, don't start a new one
    return nullptr;
  }
  auto table = vtkSmartPointer<vtkTable>::New();
  m_histogramsInProgress.append(image);
  vtkSmartPointer<vtkImageData> const imageSP = image;

  // This fakes a Qt signal to the background thread (without exposing the
  // class internals as a signal).  The background thread will then call
  // makeHistogram on the HistogramMaker object with the parameters we
  // gave here.
  QMetaObject::invokeMethod(m_histogramGen, "makeHistogram",
                            Q_ARG(vtkSmartPointer<vtkImageData>, imageSP),
                            Q_ARG(vtkSmartPointer<vtkTable>, table));

  // The histogram cannot be returned for use while the background thread is
  // populating it.
  return nullptr;
}

vtkSmartPointer<vtkImageData> HistogramManager::getHistogram2D(
  vtkSmartPointer<vtkImageData> image)
{
  if (m_histogram2DCache.contains(image)) {
    auto cachedHistogram = m_histogram2DCache[image];
    if (cachedHistogram->GetMTime() > image->GetMTime()) {
      return cachedHistogram;
    } else {
      // Need to recalculate, clear the plots, and remove the cached data.
      m_histogram2DCache.remove(image);
    }
  }
  if (m_histogram2DsInProgress.contains(image)) {
    // it is in progress, don't start a new one
    return nullptr;
  }
  auto histogram = vtkSmartPointer<vtkImageData>::New();
  m_histogram2DsInProgress.append(image);
  vtkSmartPointer<vtkImageData> const imageSP = image;

  // This fakes a Qt signal to the background thread (without exposing the
  // class internals as a signal).  The background thread will then call
  // makeHistogram on the HistogramMaker object with the parameters we
  // gave here.
  QMetaObject::invokeMethod(m_histogramGen, "makeHistogram2D",
                            Q_ARG(vtkSmartPointer<vtkImageData>, imageSP),
                            Q_ARG(vtkSmartPointer<vtkImageData>, histogram));
  // The histogram cannot be returned for use while the background thread is
  // populating it.
  return nullptr;
}

void HistogramManager::histogramReadyInternal(
  vtkSmartPointer<vtkImageData> image, vtkSmartPointer<vtkTable> histogram)
{
  m_histogramCache[image] = histogram;
  m_histogramsInProgress.removeAll(image);
  emit this->histogramReady(image, histogram);
}

void HistogramManager::histogram2DReadyInternal(
  vtkSmartPointer<vtkImageData> image, vtkSmartPointer<vtkImageData> histogram)
{
  m_histogram2DCache[image] = histogram;
  m_histogram2DsInProgress.removeAll(image);
  emit this->histogram2DReady(image, histogram);
}

} // namespace tomviz

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


<commit_before>//===----------- BreakCriticalMachineEdges - Break critical edges ---------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Fernando Pereira and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// Break all of the critical edges in the CFG by inserting a dummy basic block.
// This pass may be "required" by passes that cannot deal with critical edges.
// Notice that this pass invalidates the CFG, because the same BasicBlock is
// used as parameter for the src MachineBasicBlock and the new dummy
// MachineBasicBlock.
//
//===---------------------------------------------------------------------===//

#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Compiler.h"

using namespace llvm;

namespace {
  struct VISIBILITY_HIDDEN BreakCriticalMachineEdges :
                           public MachineFunctionPass {
    static char ID; // Pass identification
    BreakCriticalMachineEdges() : MachineFunctionPass((intptr_t)&ID) {}

    bool runOnMachineFunction(MachineFunction& Fn);
    void splitCriticalEdge(MachineBasicBlock* A, MachineBasicBlock* B);
  };

  char BreakCriticalMachineEdges::ID = 0;
  RegisterPass<BreakCriticalMachineEdges> X("critical-machine-edges",
                                            "Break critical machine code edges");
}

const PassInfo *llvm::BreakCriticalMachineEdgesID = X.getPassInfo();

void BreakCriticalMachineEdges::splitCriticalEdge(MachineBasicBlock* src,
                                                  MachineBasicBlock* dst) {
  const BasicBlock* srcBB = src->getBasicBlock();

  MachineBasicBlock* crit_mbb = new MachineBasicBlock(srcBB);

  // modify the llvm control flow graph
  src->removeSuccessor(dst);
  src->addSuccessor(crit_mbb);
  crit_mbb->addSuccessor(dst);

  // insert the new block into the machine function.
  src->getParent()->getBasicBlockList().insert(src->getParent()->end(),
                                               crit_mbb);

  // insert a unconditional branch linking the new block to dst
  const TargetMachine& TM = src->getParent()->getTarget();
  const TargetInstrInfo* TII = TM.getInstrInfo();
  std::vector<MachineOperand> emptyConditions;
  TII->InsertBranch(*crit_mbb, dst, (MachineBasicBlock*)0, emptyConditions);

  // modify every branch in src that points to dst to point to the new
  // machine basic block instead:
  MachineBasicBlock::iterator mii = src->end();
  bool found_branch = false;
  while (mii != src->begin()) {
    mii--;
    // if there are no more branches, finish the loop
    if (!TII->isTerminatorInstr(mii->getOpcode())) {
      break;
    }

    // Scan the operands of this branch, replacing any uses of dst with
    // crit_mbb.
    for (unsigned i = 0, e = mii->getNumOperands(); i != e; ++i) {
      MachineOperand & mo = mii->getOperand(i);
      if (mo.isMachineBasicBlock() &&
          mo.getMachineBasicBlock() == dst) {
        found_branch = true;
        mo.setMachineBasicBlock(crit_mbb);
      }
    }
  }

  // TODO: This is tentative. It may be necessary to fix this code. Maybe
  // I am inserting too many gotos, but I am trusting that the asm printer
  // will optimize the unnecessary gotos.
  if(!found_branch) {
    TII->InsertBranch(*src, crit_mbb, (MachineBasicBlock*)0, emptyConditions);
  }

  /// Change all the phi functions in dst, so that the incoming block be
  /// crit_mbb, instead of src
  for(mii = dst->begin(); mii != dst->end(); mii++) {
    /// the first instructions are always phi functions.
    if(mii->getOpcode() != TargetInstrInfo::PHI)
      break;

    for (unsigned u = 0; u != mii->getNumOperands(); ++u)
      if (mii->getOperand(u).isMachineBasicBlock() &&
          mii->getOperand(u).getMachineBasicBlock() == src)
        mii->getOperand(u).setMachineBasicBlock(crit_mbb);
  }
}

bool BreakCriticalMachineEdges::runOnMachineFunction(MachineFunction& F) {
  std::vector<MachineBasicBlock *> SourceBlocks;
  std::vector<MachineBasicBlock *> DestBlocks;

  for(MachineFunction::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) {
    for(MachineBasicBlock::succ_iterator SI = FI->succ_begin(),
        SE = FI->succ_end(); SI != SE; ++SI) {
      // predecessor with multiple successors, successor with multiple
      // predecessors.
      if (FI->succ_size() > 1 && (*SI)->pred_size() > 1) {
        SourceBlocks.push_back(FI);
        DestBlocks.push_back(*SI);
      }
    }
  }

  for(unsigned u = 0; u < SourceBlocks.size() > 0; u++)
    splitCriticalEdge(SourceBlocks[u], DestBlocks[u]);

  return false;
}
<commit_msg>Fixed a strange construct. Please review.<commit_after>//===----------- BreakCriticalMachineEdges - Break critical edges ---------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Fernando Pereira and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// Break all of the critical edges in the CFG by inserting a dummy basic block.
// This pass may be "required" by passes that cannot deal with critical edges.
// Notice that this pass invalidates the CFG, because the same BasicBlock is
// used as parameter for the src MachineBasicBlock and the new dummy
// MachineBasicBlock.
//
//===---------------------------------------------------------------------===//

#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Compiler.h"

using namespace llvm;

namespace {
  struct VISIBILITY_HIDDEN BreakCriticalMachineEdges :
                           public MachineFunctionPass {
    static char ID; // Pass identification
    BreakCriticalMachineEdges() : MachineFunctionPass((intptr_t)&ID) {}

    bool runOnMachineFunction(MachineFunction& Fn);
    void splitCriticalEdge(MachineBasicBlock* A, MachineBasicBlock* B);
  };

  char BreakCriticalMachineEdges::ID = 0;
  RegisterPass<BreakCriticalMachineEdges> X("critical-machine-edges",
                                            "Break critical machine code edges");
}

const PassInfo *llvm::BreakCriticalMachineEdgesID = X.getPassInfo();

void BreakCriticalMachineEdges::splitCriticalEdge(MachineBasicBlock* src,
                                                  MachineBasicBlock* dst) {
  const BasicBlock* srcBB = src->getBasicBlock();

  MachineBasicBlock* crit_mbb = new MachineBasicBlock(srcBB);

  // modify the llvm control flow graph
  src->removeSuccessor(dst);
  src->addSuccessor(crit_mbb);
  crit_mbb->addSuccessor(dst);

  // insert the new block into the machine function.
  src->getParent()->getBasicBlockList().insert(src->getParent()->end(),
                                               crit_mbb);

  // insert a unconditional branch linking the new block to dst
  const TargetMachine& TM = src->getParent()->getTarget();
  const TargetInstrInfo* TII = TM.getInstrInfo();
  std::vector<MachineOperand> emptyConditions;
  TII->InsertBranch(*crit_mbb, dst, (MachineBasicBlock*)0, emptyConditions);

  // modify every branch in src that points to dst to point to the new
  // machine basic block instead:
  MachineBasicBlock::iterator mii = src->end();
  bool found_branch = false;
  while (mii != src->begin()) {
    mii--;
    // if there are no more branches, finish the loop
    if (!TII->isTerminatorInstr(mii->getOpcode())) {
      break;
    }

    // Scan the operands of this branch, replacing any uses of dst with
    // crit_mbb.
    for (unsigned i = 0, e = mii->getNumOperands(); i != e; ++i) {
      MachineOperand & mo = mii->getOperand(i);
      if (mo.isMachineBasicBlock() &&
          mo.getMachineBasicBlock() == dst) {
        found_branch = true;
        mo.setMachineBasicBlock(crit_mbb);
      }
    }
  }

  // TODO: This is tentative. It may be necessary to fix this code. Maybe
  // I am inserting too many gotos, but I am trusting that the asm printer
  // will optimize the unnecessary gotos.
  if(!found_branch) {
    TII->InsertBranch(*src, crit_mbb, (MachineBasicBlock*)0, emptyConditions);
  }

  /// Change all the phi functions in dst, so that the incoming block be
  /// crit_mbb, instead of src
  for(mii = dst->begin(); mii != dst->end(); mii++) {
    /// the first instructions are always phi functions.
    if(mii->getOpcode() != TargetInstrInfo::PHI)
      break;

    for (unsigned u = 0; u != mii->getNumOperands(); ++u)
      if (mii->getOperand(u).isMachineBasicBlock() &&
          mii->getOperand(u).getMachineBasicBlock() == src)
        mii->getOperand(u).setMachineBasicBlock(crit_mbb);
  }
}

bool BreakCriticalMachineEdges::runOnMachineFunction(MachineFunction& F) {
  std::vector<MachineBasicBlock *> SourceBlocks;
  std::vector<MachineBasicBlock *> DestBlocks;

  for(MachineFunction::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) {
    for(MachineBasicBlock::succ_iterator SI = FI->succ_begin(),
        SE = FI->succ_end(); SI != SE; ++SI) {
      // predecessor with multiple successors, successor with multiple
      // predecessors.
      if (FI->succ_size() > 1 && (*SI)->pred_size() > 1) {
        SourceBlocks.push_back(FI);
        DestBlocks.push_back(*SI);
      }
    }
  }

  for(unsigned u = 0; u < SourceBlocks.size(); u++)
    splitCriticalEdge(SourceBlocks[u], DestBlocks[u]);

  return false;
}
<|endoftext|>