<commit_before>//Copyright (c) 2019 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <gtest/gtest.h>

#include "../src/infill.h"

namespace cura
{
    // TODO (in the input file?): Should path be closed?
    bool readTestPolygons(const std::string& filename, std::vector<Polygons>& polygons_out)
    {
        FILE* handle = std::fopen(filename.c_str(), "r");
        if (! handle)
        {
            return false;
        }

        Polygon next_path;
        Polygons next_shape;

        char command = '_';
        int read = 0;
        while (command != '#')
        {
            read = std::fscanf(handle, " %c ", &command);
            if (read == EOF)
            {
                command = '#';
            }
            else if (read <= 0)
            {
                return false;
            }
            switch (command)
            {
            case 'v': // read next coordinate
                coord_t coord_x, coord_y;
                read = std::fscanf(handle, " %lld %lld ", &coord_x, &coord_y);
                if (read == EOF || read <= 0)
                {
                    return false;
                }
                next_path.emplace_back(coord_x, coord_y);
                break;
            case 'x': // close 'next' path
                // fallthrough
            case '&': // finalize 'next' polygon (which may also close a path)
                // fallthrough
            case '#': // end of file
                if (! next_path.empty())
                {
                    next_shape.add(Polygon(next_path)); // copy and add
                    next_path.clear();
                }
                if (command == '&' && ! next_shape.empty())
                {
                    polygons_out.push_back(Polygons(next_shape)); // copy and add
                    next_shape.clear();
                }
                break;
            default:
                return false;
            }
        }

        return true;
    }

    double calculateInfillArea(const coord_t& infill_line_width, Polygons& infill_shapes)
    {
        double total_infill_area = 0;
        for (PolygonRef poly : infill_shapes)
        {
            Point last_line = poly[0];
            for (const Point& line : poly)
            {
                // don't have to skip the 0th line, as it'll have an area of 0 anyway
                const double vector_x = last_line.X - line.X;
                const double vector_y = last_line.Y - line.Y;
                total_infill_area += std::sqrt(vector_x * vector_x + vector_y * vector_y) * infill_line_width;

                last_line = line;
            }
        }
        return total_infill_area;
    }

    template<typename ... Ts>
    std::string makeName(const std::string& format_string, Ts ... args)
    {
        constexpr int buff_size = 1024;
        char buff[buff_size];
        std::snprintf(buff, buff_size, format_string.c_str(), args...);
        return std::string(buff);
    }

    // TODO:
    //   - put read polygons in some cached/singleton struct -> for the whole suite
    //   - put 'generated infill' in  "   "   "  -> for each set of tests that need to be done on that
    //     ...
    //  solve this: put the generation of infill in 'InfillTestParameters'

    struct InfillParameters
    {
    public:
        // Actual infill parameters:
        EFillMethod pattern;
        bool zig_zagify;
        bool connect_polygons;
        coord_t line_distance;

        std::string name;

        InfillParameters(const EFillMethod& pattern, const bool& zig_zagify, const bool& connect_polygons, const coord_t& line_distance) :
            pattern(pattern),
            zig_zagify(zig_zagify),
            connect_polygons(connect_polygons),
            line_distance(line_distance)
        {
            name = makeName("InfillParameters_%d_%d_%d_%lld", (int)pattern, (int)zig_zagify, (int)connect_polygons, line_distance);
        }
    };

    class InfillTestParameters
    {
    public:
        bool valid;  // <-- if the file isn't read (or anything else goes wrong with the setup) we can communicate it to the tests
        std::string fail_reason;

        // Parameters used to generate the infill:
        InfillParameters params;
        Polygons outline_polygons;

        // Resulting infill:
        Polygons result_lines;
        Polygons result_polygons;

        std::string name;

        InfillTestParameters() :
            valid(false),
            fail_reason("Read of file with test polygons failed (see generateInfillTests), can't continue tests."),
            params(InfillParameters(EFillMethod::NONE, false, false, 0)),
            outline_polygons(Polygons()),
            result_lines(Polygons()),
            result_polygons(Polygons()),
            name("UNNAMED")
        {
        }

        InfillTestParameters(const InfillParameters& params, const Polygons& outline_polygons, const Polygons& result_lines, const Polygons& result_polygons) :
            valid(true),
            fail_reason("__"),
            params(params),
            outline_polygons(outline_polygons),
            result_lines(result_lines),
            result_polygons(result_polygons)
        {
            name = makeName("InfillTestParameters_%d_%d_%d_%lld_%d", (int)params.pattern, (int)params.zig_zagify, (int)params.connect_polygons, params.line_distance, (int)valid);
        }
    };

    constexpr coord_t outline_offset = 0;
    constexpr coord_t infill_line_width = 350;
    constexpr coord_t infill_overlap = 0;
    constexpr size_t infill_multiplier = 1;
    const AngleDegrees fill_angle = 0.;
    constexpr coord_t z = 100; // future/TODO: test even / uneven layers, check if they support each other
    constexpr coord_t shift = 0;

    InfillTestParameters generateInfillToTest(const InfillParameters& params, const Polygons& outline_polygons)
    {
        const EFillMethod pattern = params.pattern;
        const bool zig_zagify = params.zig_zagify;
        const bool connect_polygons = params.connect_polygons;
        const coord_t line_distance = params.line_distance;

        Infill infill
        (
            pattern,
            zig_zagify,
            connect_polygons,
            outline_polygons,
            outline_offset,
            infill_line_width,
            line_distance,
            infill_overlap,
            infill_multiplier,
            fill_angle,
            z,
            shift
        ); // There are some optional parameters, but these will do for now.

        Polygons result_polygons;
        Polygons result_lines;
        infill.generate(result_polygons, result_lines, nullptr, nullptr);

        return InfillTestParameters(params, outline_polygons, result_lines, result_polygons);
    }

    std::vector<InfillTestParameters> generateInfillTests()
    {
        std::vector<Polygons> shapes;
        if (! readTestPolygons("../tests/polygons.txt", shapes))
        {
            return { InfillTestParameters() };  // return an invalid class, that'll trip up the 'file read' assertion in the TEST_P's
        }

        std::vector<EFillMethod> methods;
        methods = { EFillMethod::LINES, EFillMethod::GRID, EFillMethod::TRIANGLES, EFillMethod::TRIHEXAGON, EFillMethod::CONCENTRIC };
        //TODO: In the future, this could maybe be populated by:
        //      "for (int i_method = 0; i_method < static_cast<int>(EFillMethod::NONE); ++i_method) { methods.push_back(static_cast<EFillMethod>(i_method)); }"

        std::vector<coord_t> line_distances = { 350, 400, 600, 800, 1200 }; //, 2500, 5000 }; // TODO: Why do these last two fail?

        std::vector<InfillTestParameters> parameters_list;
        for (const Polygons& polygons : shapes)
        {
            for (const EFillMethod& method : methods)
            {
                for (const coord_t& line_distance : line_distances)
                {
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, false, false, line_distance), polygons));
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, false, true, line_distance), polygons));
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, true, false, line_distance), polygons));
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, true, true, line_distance), polygons));
                }
            }
        }

        return parameters_list;
    }

    class InfillTest : public testing::TestWithParam<InfillTestParameters> {};

    INSTANTIATE_TEST_CASE_P(InfillTestcases, InfillTest, testing::ValuesIn(generateInfillTests()), [](testing::TestParamInfo<InfillTestParameters> info){ return info.param.name; });

    TEST_P(InfillTest, TestInfillSanity)
    {
        InfillTestParameters params = GetParam();
        ASSERT_TRUE(params.valid) << params.fail_reason;
        ASSERT_FALSE(params.result_polygons.empty() && params.result_lines.empty()) << "Infill should have been generated.";

        const double available_area = std::abs(params.outline_polygons.offset(-infill_line_width / 2).area());
        const double expected_infill_area = (available_area * infill_line_width) / params.params.line_distance;
        const double total_infill_area = calculateInfillArea(infill_line_width, params.result_polygons) + calculateInfillArea(infill_line_width, params.result_lines);

        ASSERT_GT((coord_t) available_area, (coord_t) total_infill_area) << "Infill area should allways be less than the total area available.";
        ASSERT_NEAR((coord_t) total_infill_area, (coord_t) expected_infill_area, (coord_t) (total_infill_area * 0.5)) << "Infill area should be within 5% of total size.";
        ASSERT_NEAR(params.outline_polygons.intersection(params.result_polygons).area(), params.result_polygons.area(), 100) << "Infill (polys) should not be outside target polygon.";
        ASSERT_NEAR(params.outline_polygons.intersection(params.result_lines).area(), params.result_lines.area(), 100) << "Infill (lines) should not be outside target polygon.";

        // TODO: (Almost) none of this works yet: I have a hunch that the input polygons aren't correct (inside-out and/or missing a closing line segment).
        // TODO: Are all vertices/parts of the infill -- inside of the 'to generate' polygon?
        // TODO (large?): Infill should support last layer.
        // TODO: Split TestInfillSanity into multiple methods
    }

} //namespace cura
<commit_msg>Infill unit-tests:works for 4 types of infill. Concentric still wrong.<commit_after>//Copyright (c) 2019 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <gtest/gtest.h>

#include "../src/infill.h"

namespace cura
{
    // TODO (in the input file?): Should path be closed?
    bool readTestPolygons(const std::string& filename, std::vector<Polygons>& polygons_out)
    {
        FILE* handle = std::fopen(filename.c_str(), "r");
        if (!handle)
        {
            return false;
        }

        Polygon next_path;
        Polygons next_shape;

        char command = '_';
        int read = 0;
        while (command != '#')
        {
            read = std::fscanf(handle, " %c ", &command);
            if (read == EOF)
            {
                command = '#';
            }
            else if (read <= 0)
            {
                return false;
            }
            switch (command)
            {
            case 'v': // read next coordinate
                coord_t coord_x, coord_y;
                read = std::fscanf(handle, " %lld %lld ", &coord_x, &coord_y);
                if (read == EOF || read <= 0)
                {
                    return false;
                }
                next_path.emplace_back(coord_x, coord_y);
                break;
            case 'x': // close 'next' path
                // fallthrough
            case '&': // finalize 'next' polygon (which may also close a path)
                // fallthrough
            case '#': // end of file
                if (!next_path.empty())
                {
                    next_shape.add(Polygon(next_path)); // copy and add
                    next_path.clear();
                }
                if (command != 'x' && !next_shape.empty())
                {
                    polygons_out.push_back(Polygons(next_shape)); // copy and add
                    next_shape.clear();
                }
                break;
            default:
                return false;
            }
        }

        return true;
    }

    template<typename ... Ts>
    std::string makeName(const std::string& format_string, Ts ... args)
    {
        constexpr int buff_size = 1024;
        char buff[buff_size];
        std::snprintf(buff, buff_size, format_string.c_str(), args...);
        return std::string(buff);
    }

    coord_t getPatternMultiplier(const EFillMethod& pattern)
    {
        switch (pattern)
        {
        case EFillMethod::GRID: // fallthrough
        case EFillMethod::TETRAHEDRAL: // fallthrough
        case EFillMethod::QUARTER_CUBIC:
            return 2;
        case EFillMethod::TRIANGLES: // fallthrough
        case EFillMethod::TRIHEXAGON: // fallthrough
        case EFillMethod::CUBIC: // fallthrough
        case EFillMethod::CUBICSUBDIV:
            return 3;
        default:
            return 1;
        }
    }

    // TODO:
    //   - put read polygons in some cached/singleton struct -> for the whole suite
    //   - put 'generated infill' in  "   "   "  -> for each set of tests that need to be done on that
    //     ...
    //  solve this: put the generation of infill in 'InfillTestParameters'

    struct InfillParameters
    {
    public:
        // Actual infill parameters:
        EFillMethod pattern;
        bool zig_zagify;
        bool connect_polygons;
        coord_t line_distance;

        std::string name;

        InfillParameters(const EFillMethod& pattern, const bool& zig_zagify, const bool& connect_polygons, const coord_t& line_distance) :
            pattern(pattern),
            zig_zagify(zig_zagify),
            connect_polygons(connect_polygons),
            line_distance(line_distance)
        {
            name = makeName("InfillParameters_%d_%d_%d_%lld", (int)pattern, (int)zig_zagify, (int)connect_polygons, line_distance);
        }
    };

    class InfillTestParameters
    {
    public:
        bool valid;  // <-- if the file isn't read (or anything else goes wrong with the setup) we can communicate it to the tests
        std::string fail_reason;
        size_t test_polygon_id;

        // Parameters used to generate the infill:
        InfillParameters params;
        Polygons outline_polygons;

        // Resulting infill:
        Polygons result_lines;
        Polygons result_polygons;

        std::string name;

        InfillTestParameters() :
            valid(false),
            fail_reason("Read of file with test polygons failed (see generateInfillTests), can't continue tests."),
            test_polygon_id(-1),
            params(InfillParameters(EFillMethod::NONE, false, false, 0)),
            outline_polygons(Polygons()),
            result_lines(Polygons()),
            result_polygons(Polygons()),
            name("UNNAMED")
        {
        }

        InfillTestParameters(const InfillParameters& params, const size_t& test_polygon_id, const Polygons& outline_polygons, const Polygons& result_lines, const Polygons& result_polygons) :
            valid(true),
            fail_reason("__"),
            test_polygon_id(test_polygon_id),
            params(params),
            outline_polygons(outline_polygons),
            result_lines(result_lines),
            result_polygons(result_polygons)
        {
            name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%lld", (int)params.pattern, (int)params.zig_zagify, (int)params.connect_polygons, params.line_distance, test_polygon_id);
        }

        friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params)
        {
            return os << params.name << "(" << (params.valid ? std::string("input OK") : params.fail_reason) << ")";
        }
    };

    constexpr coord_t outline_offset = 0;
    constexpr coord_t infill_line_width = 350;
    constexpr coord_t infill_overlap = 0;
    constexpr size_t infill_multiplier = 1;
    const AngleDegrees fill_angle = 0.;
    constexpr coord_t z = 100; // future/TODO: test even / uneven layers, check if they support each other
    constexpr coord_t shift = 0;

    InfillTestParameters generateInfillToTest(const InfillParameters& params, const size_t& test_polygon_id, const Polygons& outline_polygons)
    {
        const EFillMethod pattern = params.pattern;
        const bool zig_zagify = params.zig_zagify;
        const bool connect_polygons = params.connect_polygons;
        const coord_t line_distance = params.line_distance;

        Infill infill
        (
            pattern,
            zig_zagify,
            connect_polygons,
            outline_polygons,
            outline_offset,
            infill_line_width,
            line_distance,
            infill_overlap,
            infill_multiplier,
            fill_angle,
            z,
            shift
        ); // There are some optional parameters, but these will do for now.

        Polygons result_polygons;
        Polygons result_lines;
        infill.generate(result_polygons, result_lines, nullptr, nullptr);

        auto result = InfillTestParameters(params, test_polygon_id, outline_polygons, result_lines, result_polygons);
        return result;
    }

    std::vector<InfillTestParameters> generateInfillTests()
    {
        std::vector<Polygons> shapes;
        if (!readTestPolygons("../tests/polygons.txt", shapes))
        {
            return { InfillTestParameters() };  // return an invalid class, that'll trip up the 'file read' assertion in the TEST_P's
        }

        std::vector<EFillMethod> methods;
        methods = { EFillMethod::LINES, EFillMethod::GRID, EFillMethod::TRIANGLES, EFillMethod::TRIHEXAGON }; //, EFillMethod::CONCENTRIC };
        //TODO: In the future, this could maybe be populated by:
        //      "for (int i_method = 0; i_method < static_cast<int>(EFillMethod::NONE); ++i_method) { methods.push_back(static_cast<EFillMethod>(i_method)); }"

        std::vector<coord_t> line_distances = { 400, 600, 800, 1200, 2400 };

        std::vector<InfillTestParameters> parameters_list;
        size_t test_polygon_id = 0;
        for (const Polygons& polygons : shapes)
        {
            for (const EFillMethod& method : methods)
            {
                for (const coord_t& line_distance : line_distances)
                {
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, false, false, line_distance), test_polygon_id, polygons));
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, false, true, line_distance), test_polygon_id, polygons));
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, true, false, line_distance), test_polygon_id, polygons));
                    parameters_list.push_back(generateInfillToTest(InfillParameters(method, true, true, line_distance), test_polygon_id, polygons));
                }
            }
            ++test_polygon_id;
        }

        return parameters_list;
    }

    class InfillTest : public testing::TestWithParam<InfillTestParameters> {};

    INSTANTIATE_TEST_CASE_P(InfillTestcases, InfillTest, testing::ValuesIn(generateInfillTests()), [](testing::TestParamInfo<InfillTestParameters> info) { return info.param.name; });

    TEST_P(InfillTest, TestInfillSanity)
    {
        InfillTestParameters params = GetParam();
        ASSERT_TRUE(params.valid) << params.fail_reason;
        ASSERT_FALSE(params.result_polygons.empty() && params.result_lines.empty()) << "Infill should have been generated.";

        const double available_area = std::abs(params.outline_polygons.area());
        const double expected_infill_area = (available_area * infill_line_width) / params.params.line_distance;
        const double total_infill_area = (params.result_polygons.polygonLength() + params.result_lines.polyLineLength()) * infill_line_width / getPatternMultiplier(params.params.pattern);

        ASSERT_GT((coord_t)available_area, (coord_t)total_infill_area) << "Infill area should allways be less than the total area available.";
        ASSERT_NEAR((coord_t)total_infill_area, (coord_t)expected_infill_area, (coord_t)(total_infill_area * 0.25)) << "Infill area should be within 25% of expected size.";
        ASSERT_NEAR(params.outline_polygons.intersection(params.result_polygons).area(), params.result_polygons.area(), 100) << "Infill (polys) should not be outside target polygon.";
        ASSERT_NEAR(params.outline_polygons.intersection(params.result_lines).area(), params.result_lines.area(), 100) << "Infill (lines) should not be outside target polygon.";

        // TODO: (Almost) none of this works yet: I have a hunch that the input polygons aren't correct (inside-out and/or missing a closing line segment).
        // TODO: Are all vertices/parts of the infill -- inside of the 'to generate' polygon?
        // TODO (large?): Infill should support last layer.
        // TODO: Split TestInfillSanity into multiple methods
    }

} //namespace cura
<|endoftext|>


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

#include "SurgSim/Devices/NullDevice/NullDevice.h"

#include <iostream>
#include <iomanip>

#include <HD/hd.h>

#include <SurgSim/Math/Vector.h>
#include <SurgSim/Math/Matrix.h>
#include <SurgSim/Math/RigidTransform.h>
#include <SurgSim/Framework/Log.h>
#include <SurgSim/Input/DataGroup.h>
#include <SurgSim/Input/DataGroupBuilder.h>

using SurgSim::Math::Vector3d;
using SurgSim::Math::Matrix44d;
using SurgSim::Math::Matrix33d;
using SurgSim::Math::RigidTransform3d;

using SurgSim::Input::DataGroup;
using SurgSim::Input::DataGroupBuilder;

namespace SurgSim
{
namespace Device
{

NullDevice::NullDevice(const std::string& uniqueName) :
	SurgSim::Input::CommonInputDevice(uniqueName, buildInputData())
{
}

bool NullDevice::initialize()
{
	return true;
}

bool NullDevice::finalize()
{
	return true;
}

DataGroup NullDevice::buildInputData()
{
	DataGroupBuilder builder;
	builder.addPose("pose");
	builder.addBoolean("button0");
	builder.addBoolean("button1");
	builder.addBoolean("button2");
	builder.addBoolean("button3");
	return builder.createData();
}

bool NullDevice::addListener(std::shared_ptr<SurgSim::Input::InputDeviceListenerInterface> listener)
{
	bool status = CommonInputDevice::addListener(std::move(listener));

	// The NullDevice doesn't have any input events; it just sits there.
	// So we push the output to all the listeners, including the new one, right away after we add it.
	getInputData().poses().put("pose", RigidTransform3d::Identity());
	getInputData().booleans().put("button0", false);
	pushInput();

	return status;
}

bool NullDevice::addInputListener(std::shared_ptr<SurgSim::Input::InputDeviceListenerInterface> listener)
{
	bool status = CommonInputDevice::addInputListener(std::move(listener));

	// The NullDevice doesn't have any input events; it just sits there.
	// So we push the output to all the listeners, including the new one, right away after we add it.
	getInputData().poses().put("pose", RigidTransform3d::Identity());
	getInputData().booleans().put("button0", false);
	pushInput();

	return status;
}

};  // namespace Device
};  // namespace SurgSim
<commit_msg>Add a couple of comments.<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "SurgSim/Devices/NullDevice/NullDevice.h"

#include <iostream>
#include <iomanip>

#include <HD/hd.h>

#include <SurgSim/Math/Vector.h>
#include <SurgSim/Math/Matrix.h>
#include <SurgSim/Math/RigidTransform.h>
#include <SurgSim/Framework/Log.h>
#include <SurgSim/Input/DataGroup.h>
#include <SurgSim/Input/DataGroupBuilder.h>

using SurgSim::Math::Vector3d;
using SurgSim::Math::Matrix44d;
using SurgSim::Math::Matrix33d;
using SurgSim::Math::RigidTransform3d;

using SurgSim::Input::DataGroup;
using SurgSim::Input::DataGroupBuilder;

namespace SurgSim
{
namespace Device
{

NullDevice::NullDevice(const std::string& uniqueName) :
	SurgSim::Input::CommonInputDevice(uniqueName, buildInputData())
{
}

bool NullDevice::initialize()
{
	// required by the InputDeviceInterface API
	return true;
}

bool NullDevice::finalize()
{
	// required by the InputDeviceInterface API
	return true;
}

DataGroup NullDevice::buildInputData()
{
	DataGroupBuilder builder;
	builder.addPose("pose");
	builder.addBoolean("button0");
	builder.addBoolean("button1");
	builder.addBoolean("button2");
	builder.addBoolean("button3");
	return builder.createData();
}

bool NullDevice::addListener(std::shared_ptr<SurgSim::Input::InputDeviceListenerInterface> listener)
{
	bool status = CommonInputDevice::addListener(std::move(listener));

	// The NullDevice doesn't have any input events; it just sits there.
	// So we push the output to all the listeners, including the new one, right away after we add it.
	getInputData().poses().put("pose", RigidTransform3d::Identity());
	getInputData().booleans().put("button0", false);
	pushInput();

	return status;
}

bool NullDevice::addInputListener(std::shared_ptr<SurgSim::Input::InputDeviceListenerInterface> listener)
{
	bool status = CommonInputDevice::addInputListener(std::move(listener));

	// The NullDevice doesn't have any input events; it just sits there.
	// So we push the output to all the listeners, including the new one, right away after we add it.
	getInputData().poses().put("pose", RigidTransform3d::Identity());
	getInputData().booleans().put("button0", false);
	pushInput();

	return status;
}

};  // namespace Device
};  // namespace SurgSim
<|endoftext|>


<commit_before>#include "mult.h"
#include "hll.h"
#include "hk.h"
#include "ccm.h"
using namespace sketch;
using namespace cws;
using PairHasher = XXH3PairHasher;

using H = HeavyKeeper<7,9>;
using C = cm::ccmbase_t<>;
using S = wj::WeightedSketcher<hll::hll_t, H>;
using S2 = wj::WeightedSketcher<hll::hll_t, C>;
using S3 = wj::WeightedSketcher<hll::hll_t, wj::ExactCountingAdapter>;

int main (int argc, char *argv[]) {
    common::DefaultRNGType gen;
    int tbsz = argc == 1 ? 1 << 20: std::atoi(argv[1]);
    int ntbls = argc <= 2 ? 6: std::atoi(argv[2]);
    size_t nitems = 1 << (20 - 6);
#if !defined(NO_BLAZE) && (VECTOR_WIDTH <= 32 || AVX512_REDUCE_OPERATIONS_ENABLED)
    CWSamples<> zomg(100, 1000);
#endif
    realccm_t<> rc(0.999, 10, 20, 8);
    nt::VecCard<uint16_t> vc(13, 10), vc2(13, 10);
    for(size_t i = 0; i < 100000; ++i)
        vc.addh(gen()), vc2.addh(gen());
    auto vc3 = vc + vc2;
    auto zomg2 = vc.report();
    auto zomg3 = vc3.report();
    {
        S ws(H(tbsz / 2, ntbls), hll::hll_t(10));
        S ws2(H(tbsz /2, ntbls), hll::hll_t(10));
        hll::hll_t cmp1(10), cmp2(10);
        gen.seed(0);
        for(size_t i =0; i < nitems; ++i) {
            auto v = gen(), t = gen(), c = t % 16, c2 = (t >> 6) % 16;
            for(unsigned i = 0; i < c; ++i) {
                ws.addh(v);
                ws2.addh(v);
            }
            uint64_t v1 = v;
            for(unsigned i = 0; i < c2; ++i) {
                wy::wyhash64_stateless(&v1);
                ws.addh(v1);
                wy::wyhash64_stateless(&v1);
                ws2.addh(v1);
            }
        }
        hll::hll_t v1 = ws.finalize(), v2 = ws2.finalize();
        v1.sum(); v2.sum();
        std::fprintf(stderr, "HeavyKeeper:v1 wji with v2 %lf\n", v1.jaccard_index(v2));
        assert(std::abs(v1.jaccard_index(v2) - 0.333333) < 0.03);
        std::fprintf(stderr, "HeavyKeeper:v1.str: %s. ws1 cardinality %lf\n", v1.to_string().data(), ws.sketch_.report());
    }
    {
        S3 ws(H(tbsz / 2, ntbls), hll::hll_t(10));
        S3 ws2(H(tbsz /2, ntbls), hll::hll_t(10));
        hll::hll_t cmp1(10), cmp2(10);
        gen.seed(0);
        for(size_t i =0; i < nitems; ++i) {
            auto v = gen(), t = gen(), c = t % 16, c2 = (t >> 6) % 16;
            for(unsigned i = 0; i < c; ++i) {
                ws.addh(v);
                ws2.addh(v);
            }
            uint64_t v1 = v;
            for(unsigned i = 0; i < c2; ++i) {
                wy::wyhash64_stateless(&v1);
                ws.addh(v1);
                wy::wyhash64_stateless(&v1);
                ws2.addh(v1);
            }
        }
        hll::hll_t v1 = ws.finalize(), v2 = ws2.finalize();
        v1.sum(); v2.sum();
        std::fprintf(stderr, "ExactCounter:v1 wji with v2 %lf\n", v1.jaccard_index(v2));
        assert(std::abs(v1.jaccard_index(v2) - 0.333333) < 0.03);
        std::fprintf(stderr, "ExactCounter:v1.str: %s. ws1 cardinality %lf\n", v1.to_string().data(), ws.sketch_.report());
    }
    {
        int nbits = 32;
        int l2sz = ilog2(tbsz);
        int nhashes = ntbls;
        S2 ws(C(nbits, l2sz, nhashes), hll::hll_t(10));
        S2 ws2(C(nbits, l2sz, nhashes), hll::hll_t(10));
        hll::hll_t cmp1(10), cmp2(10);
        size_t shared = 0, unshared = 0;
        gen.seed(0);
        for(size_t i =0; i < nitems; ++i) {
            auto v = gen(), t = gen(), c = t % 16, c2 = (t >> 6) % 16;
            shared += c;
            for(size_t j = 0; j < c; ++j) {
                auto hv = PairHasher()(v, j);
                ws.addh(v), ws2.addh(v), cmp1.add(hv), cmp2.add(hv);
            }
            unshared += c2;
            for(size_t j = 0; j < c2; ++j) {
                uint64_t v1 = v, v2;
                wy::wyhash64_stateless(&v1);
                v2 = v1;
                wy::wyhash64_stateless(&v2);
                ws.addh(v1), ws2.addh(v2);
                auto hv1 = PairHasher()(v1, j);
                auto hv2 = PairHasher()(v2, j);
                cmp1.add(hv1), cmp2.add(hv2);
            }
        }
        hll::hll_t v1 = ws.finalize(), v2 = ws2.finalize();
        v1.sum(); v2.sum();
        std::fprintf(stderr, "WJ without HK or CM by HLL: %lf\n", cmp1.jaccard_index(cmp2));
        assert(std::abs(v1.jaccard_index(v2) - 0.333333) < 0.03);
        assert(std::abs(cmp1.jaccard_index(cmp2) - 0.333333) < 0.05);
        std::fprintf(stderr, "CM:v1 wji with v2 %lf and true ji %lf\n", v1.jaccard_index(v2), double(shared) / (shared + unshared * 2));
        std::fprintf(stderr, "CM:v1.str: %s. ws1 cardinality %lf\n", v1.to_string().data(), ws.sketch_.report());
    }
}
<commit_msg>HK is much more accurate than count-min for the adapter.<commit_after>#include "mult.h"
#include "hll.h"
#include "hk.h"
#include "ccm.h"
using namespace sketch;
using namespace cws;
using PairHasher = XXH3PairHasher;

using H = HeavyKeeper<7,9>;
using C = cm::ccmbase_t<>;
using S = wj::WeightedSketcher<hll::hll_t, H>;
using S2 = wj::WeightedSketcher<hll::hll_t, C>;
using S3 = wj::WeightedSketcher<hll::hll_t, wj::ExactCountingAdapter>;

int main (int argc, char *argv[]) {
    common::DefaultRNGType gen;
    int tbsz = argc == 1 ? 1 << 10: std::atoi(argv[1]);
    int ntbls = argc <= 2 ? 6: std::atoi(argv[2]);
    size_t nitems = 1 << (20 - 6);
#if !defined(NO_BLAZE) && (VECTOR_WIDTH <= 32 || AVX512_REDUCE_OPERATIONS_ENABLED)
    CWSamples<> zomg(100, 1000);
#endif
    realccm_t<> rc(0.999, 10, 20, 8);
    nt::VecCard<uint16_t> vc(13, 10), vc2(13, 10);
    for(size_t i = 0; i < 100000; ++i)
        vc.addh(gen()), vc2.addh(gen());
    auto vc3 = vc + vc2;
    auto zomg2 = vc.report();
    auto zomg3 = vc3.report();
    {
        S ws(H(tbsz / 2, ntbls), hll::hll_t(10));
        S ws2(H(tbsz /2, ntbls), hll::hll_t(10));
        hll::hll_t cmp1(10), cmp2(10);
        gen.seed(0);
        for(size_t i =0; i < nitems; ++i) {
            auto v = gen(), t = gen(), c = t % 16, c2 = (t >> 6) % 16;
            for(unsigned i = 0; i < c; ++i) {
                ws.addh(v);
                ws2.addh(v);
            }
            uint64_t v1 = v;
            for(unsigned i = 0; i < c2; ++i) {
                wy::wyhash64_stateless(&v1);
                ws.addh(v1);
                wy::wyhash64_stateless(&v1);
                ws2.addh(v1);
            }
        }
        hll::hll_t v1 = ws.finalize(), v2 = ws2.finalize();
        v1.sum(); v2.sum();
        std::fprintf(stderr, "HeavyKeeper:v1 wji with v2 %lf\n", v1.jaccard_index(v2));
        assert(std::abs(v1.jaccard_index(v2) - 0.333333) < 0.1);
        std::fprintf(stderr, "HeavyKeeper:v1.str: %s. ws1 cardinality %lf\n", v1.to_string().data(), ws.sketch_.report());
    }
    {
        S3 ws(H(tbsz / 2, ntbls), hll::hll_t(10));
        S3 ws2(H(tbsz /2, ntbls), hll::hll_t(10));
        hll::hll_t cmp1(10), cmp2(10);
        gen.seed(0);
        for(size_t i =0; i < nitems; ++i) {
            auto v = gen(), t = gen(), c = t % 16, c2 = (t >> 6) % 16;
            for(unsigned i = 0; i < c; ++i) {
                ws.addh(v);
                ws2.addh(v);
            }
            uint64_t v1 = v;
            for(unsigned i = 0; i < c2; ++i) {
                wy::wyhash64_stateless(&v1);
                ws.addh(v1);
                wy::wyhash64_stateless(&v1);
                ws2.addh(v1);
            }
        }
        hll::hll_t v1 = ws.finalize(), v2 = ws2.finalize();
        v1.sum(); v2.sum();
        std::fprintf(stderr, "ExactCounter:v1 wji with v2 %lf\n", v1.jaccard_index(v2));
        assert(std::abs(v1.jaccard_index(v2) - 0.333333) < 0.03);
        std::fprintf(stderr, "ExactCounter:v1.str: %s. ws1 cardinality %lf\n", v1.to_string().data(), ws.sketch_.report());
    }
    {
        int nbits = 32;
        int l2sz = ilog2(tbsz);
        int nhashes = ntbls;
        S2 ws(C(nbits, l2sz, nhashes), hll::hll_t(10));
        S2 ws2(C(nbits, l2sz, nhashes), hll::hll_t(10));
        hll::hll_t cmp1(10), cmp2(10);
        size_t shared = 0, unshared = 0;
        gen.seed(0);
        for(size_t i =0; i < nitems; ++i) {
            auto v = gen(), t = gen(), c = t % 16, c2 = (t >> 6) % 16;
            shared += c;
            for(size_t j = 0; j < c; ++j) {
                auto hv = PairHasher()(v, j);
                ws.addh(v), ws2.addh(v), cmp1.add(hv), cmp2.add(hv);
            }
            unshared += c2;
            for(size_t j = 0; j < c2; ++j) {
                uint64_t v1 = v, v2;
                wy::wyhash64_stateless(&v1);
                v2 = v1;
                wy::wyhash64_stateless(&v2);
                ws.addh(v1), ws2.addh(v2);
                auto hv1 = PairHasher()(v1, j);
                auto hv2 = PairHasher()(v2, j);
                cmp1.add(hv1), cmp2.add(hv2);
            }
        }
        hll::hll_t v1 = ws.finalize(), v2 = ws2.finalize();
        v1.sum(); v2.sum();
        std::fprintf(stderr, "WJ without HK or CM by HLL: %lf\n", cmp1.jaccard_index(cmp2));
        std::fprintf(stderr, "CM:v1 wji with v2 %lf and true ji %lf\n", v1.jaccard_index(v2), double(shared) / (shared + unshared * 2));
        //assert(std::abs(v1.jaccard_index(v2) - 0.333333) < 0.1);
        assert(std::abs(cmp1.jaccard_index(cmp2) - 0.333333) < 0.05);
        std::fprintf(stderr, "CM:v1.str: %s. ws1 cardinality %lf\n", v1.to_string().data(), ws.sketch_.report());
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 ScyllaDB
 *
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <unordered_map>
#include <iosfwd>
#include <string_view>

#include <boost/program_options.hpp>

#include <seastar/core/sstring.hh>
#include <seastar/core/future.hh>
#include <seastar/util/defer.hh>

#include "utils/updateable_value.hh"

#include "seastarx.hh"

namespace seastar { class file; }
namespace seastar::json { class json_return_type; }
namespace YAML { class Node; }

namespace utils {

namespace bpo = boost::program_options;

class config_type {
    std::string_view _name;
    std::function<json::json_return_type (const void*)> _to_json;
private:
    template <typename NativeType>
    std::function<json::json_return_type (const void*)> make_to_json(json::json_return_type (*func)(const NativeType&)) {
        return [func] (const void* value) {
            return func(*static_cast<const NativeType*>(value));
        };
    }
public:
    template <typename NativeType>
    config_type(std::string_view name, json::json_return_type (*to_json)(const NativeType&)) : _name(name), _to_json(make_to_json(to_json)) {}
    std::string_view name() const { return _name; }
    json::json_return_type to_json(const void* value) const;
};

template <typename T>
extern const config_type config_type_for;

class config_file {
    static thread_local unsigned s_shard_id;
    struct any_value {
        virtual ~any_value() = default;
        virtual std::unique_ptr<any_value> clone() const = 0;
        virtual void update_from(const any_value* source) = 0;
    };
    std::vector<std::vector<std::unique_ptr<any_value>>> _per_shard_values { 1 };
public:
    typedef std::unordered_map<sstring, sstring> string_map;
    typedef std::vector<sstring> string_list;

    enum class value_status {
        Used,
        Unused,
        Invalid,
    };

    enum class liveness {
        LiveUpdate,
        MustRestart,
    };

    enum class config_source : uint8_t {
        None,
        SettingsFile,
        CommandLine
    };

    struct config_src {
        config_file* _cf;
        std::string_view _name, _desc;
        const config_type* _type;
        size_t _per_shard_values_offset;
    protected:
        virtual const void* current_value() const = 0;
    public:
        config_src(config_file* cf, std::string_view name, const config_type* type, std::string_view desc)
            : _cf(cf)
            , _name(name)
            , _desc(desc)
            , _type(type)
        {}
        virtual ~config_src() {}

        const std::string_view & name() const {
            return _name;
        }
        const std::string_view & desc() const {
            return _desc;
        }
        std::string_view type_name() const {
            return _type->name();
        }
        config_file * get_config_file() const {
            return _cf;
        }
        virtual void add_command_line_option(
                        bpo::options_description_easy_init&, const std::string_view&,
                        const std::string_view&) = 0;
        virtual void set_value(const YAML::Node&) = 0;
        virtual value_status status() const = 0;
        virtual config_source source() const = 0;
        json::json_return_type value_as_json() const;
    };

    template<typename T>
    struct named_value : public config_src {
    private:
        friend class config;
        std::string_view _name, _desc;
        config_source _source = config_source::None;
        value_status _value_status;
        struct the_value_type final : any_value {
            the_value_type(T value) : value(std::move(value)) {}
            utils::updateable_value_source<T> value;
            virtual std::unique_ptr<any_value> clone() const override {
                return std::make_unique<the_value_type>(value());
            }
            virtual void update_from(const any_value* source) override {
                auto typed_source = static_cast<const the_value_type*>(source);
                value.set(typed_source->value());
            }
        };
        liveness _liveness;
    protected:
        updateable_value_source<T>& the_value() {
            any_value* av =_cf->_per_shard_values[_cf->s_shard_id][_per_shard_values_offset].get();
            return static_cast<the_value_type*>(av)->value;
        }
        const updateable_value_source<T>& the_value() const {
            return const_cast<named_value*>(this)->the_value();
        }
        virtual const void* current_value() const override {
            return &the_value().get();
        }
    public:
        typedef T type;
        typedef named_value<T> MyType;

        named_value(config_file* file, std::string_view name, liveness liveness_, value_status vs, const T& t = T(), std::string_view desc = {},
                std::initializer_list<T> allowed_values = {})
            : config_src(file, name, &config_type_for<T>, desc)
            , _value_status(vs)
            , _liveness(liveness_)
        {
            file->add(*this, std::make_unique<the_value_type>(std::move(t)));
        }
        named_value(config_file* file, std::string_view name, value_status vs, const T& t = T(), std::string_view desc = {},
                std::initializer_list<T> allowed_values = {})
                : named_value(file, name, liveness::MustRestart, vs, t, desc, allowed_values) {
        }
        value_status status() const override {
            return _value_status;
        }
        config_source source() const override {
            return _source;
        }
        bool is_set() const {
            return _source > config_source::None;
        }
        MyType & operator()(const T& t) {
            the_value().set(t);
            return *this;
        }
        MyType & operator()(T&& t, config_source src = config_source::None) {
            the_value().set(std::move(t));
            if (src > config_source::None) {
                _source = src;
            }
            return *this;
        }
        void set(T&& t, config_source src = config_source::None) {
            operator()(std::move(t), src);
        }
        const T& operator()() const {
            return the_value().get();
        }

        operator updateable_value<T>() const & {
            return updateable_value<T>(the_value());
        }

        void add_command_line_option(bpo::options_description_easy_init&,
                        const std::string_view&, const std::string_view&) override;
        void set_value(const YAML::Node&) override;
    };

    typedef std::reference_wrapper<config_src> cfg_ref;

    config_file(std::initializer_list<cfg_ref> = {});
    config_file(const config_file&) = delete;

    void add(cfg_ref, std::unique_ptr<any_value> value);
    void add(std::initializer_list<cfg_ref>);
    void add(const std::vector<cfg_ref> &);

    boost::program_options::options_description get_options_description();
    boost::program_options::options_description get_options_description(boost::program_options::options_description);

    boost::program_options::options_description_easy_init&
    add_options(boost::program_options::options_description_easy_init&);

    /**
     * Default behaviour for yaml parser is to throw on
     * unknown stuff, invalid opts or conversion errors.
     *
     * Error handling function allows overriding this.
     *
     * error: <option name>, <message>, <optional value_status>
     *
     * The last arg, opt value_status will tell you the type of
     * error occurred. If not set, the option found does not exist.
     * If invalid, it is invalid. Otherwise, a parse error.
     *
     */
    using error_handler = std::function<void(const sstring&, const sstring&, std::optional<value_status>)>;

    void read_from_yaml(const sstring&, error_handler = {});
    void read_from_yaml(const char *, error_handler = {});
    future<> read_from_file(const sstring&, error_handler = {});
    future<> read_from_file(file, error_handler = {});

    using configs = std::vector<cfg_ref>;

    configs set_values() const;
    configs unset_values() const;
    const configs& values() const {
        return _cfgs;
    }
    future<> broadcast_to_all_shards();
private:
    configs
        _cfgs;
};

extern template struct config_file::named_value<seastar::log_level>;

}

<commit_msg>utils: config_file: Introduce named_value::observe()<commit_after>/*
 * Copyright (C) 2017 ScyllaDB
 *
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <unordered_map>
#include <iosfwd>
#include <string_view>

#include <boost/program_options.hpp>

#include <seastar/core/sstring.hh>
#include <seastar/core/future.hh>
#include <seastar/util/defer.hh>

#include "utils/updateable_value.hh"

#include "seastarx.hh"

namespace seastar { class file; }
namespace seastar::json { class json_return_type; }
namespace YAML { class Node; }

namespace utils {

namespace bpo = boost::program_options;

class config_type {
    std::string_view _name;
    std::function<json::json_return_type (const void*)> _to_json;
private:
    template <typename NativeType>
    std::function<json::json_return_type (const void*)> make_to_json(json::json_return_type (*func)(const NativeType&)) {
        return [func] (const void* value) {
            return func(*static_cast<const NativeType*>(value));
        };
    }
public:
    template <typename NativeType>
    config_type(std::string_view name, json::json_return_type (*to_json)(const NativeType&)) : _name(name), _to_json(make_to_json(to_json)) {}
    std::string_view name() const { return _name; }
    json::json_return_type to_json(const void* value) const;
};

template <typename T>
extern const config_type config_type_for;

class config_file {
    static thread_local unsigned s_shard_id;
    struct any_value {
        virtual ~any_value() = default;
        virtual std::unique_ptr<any_value> clone() const = 0;
        virtual void update_from(const any_value* source) = 0;
    };
    std::vector<std::vector<std::unique_ptr<any_value>>> _per_shard_values { 1 };
public:
    typedef std::unordered_map<sstring, sstring> string_map;
    typedef std::vector<sstring> string_list;

    enum class value_status {
        Used,
        Unused,
        Invalid,
    };

    enum class liveness {
        LiveUpdate,
        MustRestart,
    };

    enum class config_source : uint8_t {
        None,
        SettingsFile,
        CommandLine
    };

    struct config_src {
        config_file* _cf;
        std::string_view _name, _desc;
        const config_type* _type;
        size_t _per_shard_values_offset;
    protected:
        virtual const void* current_value() const = 0;
    public:
        config_src(config_file* cf, std::string_view name, const config_type* type, std::string_view desc)
            : _cf(cf)
            , _name(name)
            , _desc(desc)
            , _type(type)
        {}
        virtual ~config_src() {}

        const std::string_view & name() const {
            return _name;
        }
        const std::string_view & desc() const {
            return _desc;
        }
        std::string_view type_name() const {
            return _type->name();
        }
        config_file * get_config_file() const {
            return _cf;
        }
        virtual void add_command_line_option(
                        bpo::options_description_easy_init&, const std::string_view&,
                        const std::string_view&) = 0;
        virtual void set_value(const YAML::Node&) = 0;
        virtual value_status status() const = 0;
        virtual config_source source() const = 0;
        json::json_return_type value_as_json() const;
    };

    template<typename T>
    struct named_value : public config_src {
    private:
        friend class config;
        std::string_view _name, _desc;
        config_source _source = config_source::None;
        value_status _value_status;
        struct the_value_type final : any_value {
            the_value_type(T value) : value(std::move(value)) {}
            utils::updateable_value_source<T> value;
            virtual std::unique_ptr<any_value> clone() const override {
                return std::make_unique<the_value_type>(value());
            }
            virtual void update_from(const any_value* source) override {
                auto typed_source = static_cast<const the_value_type*>(source);
                value.set(typed_source->value());
            }
        };
        liveness _liveness;
    protected:
        updateable_value_source<T>& the_value() {
            any_value* av =_cf->_per_shard_values[_cf->s_shard_id][_per_shard_values_offset].get();
            return static_cast<the_value_type*>(av)->value;
        }
        const updateable_value_source<T>& the_value() const {
            return const_cast<named_value*>(this)->the_value();
        }
        virtual const void* current_value() const override {
            return &the_value().get();
        }
    public:
        typedef T type;
        typedef named_value<T> MyType;

        named_value(config_file* file, std::string_view name, liveness liveness_, value_status vs, const T& t = T(), std::string_view desc = {},
                std::initializer_list<T> allowed_values = {})
            : config_src(file, name, &config_type_for<T>, desc)
            , _value_status(vs)
            , _liveness(liveness_)
        {
            file->add(*this, std::make_unique<the_value_type>(std::move(t)));
        }
        named_value(config_file* file, std::string_view name, value_status vs, const T& t = T(), std::string_view desc = {},
                std::initializer_list<T> allowed_values = {})
                : named_value(file, name, liveness::MustRestart, vs, t, desc, allowed_values) {
        }
        value_status status() const override {
            return _value_status;
        }
        config_source source() const override {
            return _source;
        }
        bool is_set() const {
            return _source > config_source::None;
        }
        MyType & operator()(const T& t) {
            the_value().set(t);
            return *this;
        }
        MyType & operator()(T&& t, config_source src = config_source::None) {
            the_value().set(std::move(t));
            if (src > config_source::None) {
                _source = src;
            }
            return *this;
        }
        void set(T&& t, config_source src = config_source::None) {
            operator()(std::move(t), src);
        }
        const T& operator()() const {
            return the_value().get();
        }

        operator updateable_value<T>() const & {
            return updateable_value<T>(the_value());
        }

        observer<T> observe(std::function<void (const T&)> callback) const {
            return the_value().observe(std::move(callback));
        }

        void add_command_line_option(bpo::options_description_easy_init&,
                        const std::string_view&, const std::string_view&) override;
        void set_value(const YAML::Node&) override;
    };

    typedef std::reference_wrapper<config_src> cfg_ref;

    config_file(std::initializer_list<cfg_ref> = {});
    config_file(const config_file&) = delete;

    void add(cfg_ref, std::unique_ptr<any_value> value);
    void add(std::initializer_list<cfg_ref>);
    void add(const std::vector<cfg_ref> &);

    boost::program_options::options_description get_options_description();
    boost::program_options::options_description get_options_description(boost::program_options::options_description);

    boost::program_options::options_description_easy_init&
    add_options(boost::program_options::options_description_easy_init&);

    /**
     * Default behaviour for yaml parser is to throw on
     * unknown stuff, invalid opts or conversion errors.
     *
     * Error handling function allows overriding this.
     *
     * error: <option name>, <message>, <optional value_status>
     *
     * The last arg, opt value_status will tell you the type of
     * error occurred. If not set, the option found does not exist.
     * If invalid, it is invalid. Otherwise, a parse error.
     *
     */
    using error_handler = std::function<void(const sstring&, const sstring&, std::optional<value_status>)>;

    void read_from_yaml(const sstring&, error_handler = {});
    void read_from_yaml(const char *, error_handler = {});
    future<> read_from_file(const sstring&, error_handler = {});
    future<> read_from_file(file, error_handler = {});

    using configs = std::vector<cfg_ref>;

    configs set_values() const;
    configs unset_values() const;
    const configs& values() const {
        return _cfgs;
    }
    future<> broadcast_to_all_shards();
private:
    configs
        _cfgs;
};

extern template struct config_file::named_value<seastar::log_level>;

}

<|endoftext|>


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

#include "ViewItemManager.h"

#include "items/ViewItem.h"
#include "Scene.h"

#include "ModelBase/src/model/TreeManager.h"
#include "ModelBase/src/model/AllTreeManagers.h"

namespace Visualization {

ViewItemManager::ViewItemManager(Scene* scene)
	: scene_{scene}
{
	viewItems_.resize(VIEW_ITEM_COLUMNS);
	currentViewItem_ = newViewItem("ProjectView");
	currentViewItem_->show();
}

ViewItemManager::~ViewItemManager()
{
	for (auto vector : viewItems_)
		for (auto view : vector)
			if (view)
			{
				scene_->removeTopLevelItem(view);
				SAFE_DELETE_ITEM(view);
			}
	currentViewItem_ = nullptr;
	scene_ = nullptr;
}

ViewItem* ViewItemManager::currentViewItem()
{
	if (std::all_of(viewItems_.begin(), viewItems_.end(),
					[](QVector<ViewItem*> v) { return v.isEmpty(); }))
	{
		currentViewItem_ = newViewItem("ProjectView");
		currentViewItem_->show();
	}
	Q_ASSERT(currentViewItem_);
	return currentViewItem_;
}

ViewItem* ViewItemManager::viewItem(const QString name)
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			if (item && item->name() == name)
				return item;
	return nullptr;
}

void ViewItemManager::addViewItem(ViewItem *view, QPoint position)
{
	Q_ASSERT(!scene_->inAnUpdate_);
	if (position.x() < 0 || position.y() < 0)
		position = nextEmptyPosition();
	Q_ASSERT(position.x() >= 0 && position.x() < VIEW_ITEM_COLUMNS
			 && position.y() >= 0);
	if (viewItems_[position.x()].size() <= position.y())
		viewItems_[position.x()].resize(position.y() + 1);
	//If there already is an item -> use insert
	if (viewItems_[position.x()][position.y()])
		viewItems_[position.x()].insert(position.y(), view);
	else
	{
		//Else just overwrite the nullptr
		viewItems_[position.x()][position.y()] = view;
	}
	scene_->addTopLevelItem(view, false);
}

void ViewItemManager::switchToView(ViewItem *view)
{
	Q_ASSERT(!scene_->inAnUpdate_);
	Q_ASSERT(std::any_of(viewItems_.begin(), viewItems_.end(),
						 [view](QVector<ViewItem*> v) { return v.contains(view); }));
	currentViewItem_->hide();
	currentViewItem_ = view;
	currentViewItem_->show();
	currentViewItem_->setUpdateNeeded(Item::StandardUpdate);
	scene_->scheduleUpdate();
}

bool ViewItemManager::switchToView(const QString viewName)
{
	auto view = viewItem(viewName);
	if (view) switchToView(view);
	return view != nullptr;
}
ViewItem* ViewItemManager::newViewItem(const QString name, QPoint position)
{
	//If the name is invalid, don't create a new view
	if (!ViewItem::isValidName(name)) return nullptr;
	auto result = new ViewItem{nullptr, name};
	addViewItem(result, position);
	return result;
}

void ViewItemManager::removeAllViewItems()
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			scene_->removeTopLevelItem(item);

	viewItems_.clear();
	currentViewItem_ = nullptr;
}

void ViewItemManager::saveView(ViewItem* view, Model::TreeManager* manager) const
{
	auto json = view->toJson().toJson();
	if (!QDir{DIRECTORY_NAME}.exists())
		QDir().mkdir(DIRECTORY_NAME);
	QFile file{fileName(view->name(), manager->name())};
	file.open(QIODevice::WriteOnly);
	QTextStream write{&file};
	write << json;
}

ViewItem* ViewItemManager::loadView(QString name, QPoint position)
{
	for (auto manager : Model::AllTreeManagers::instance().loadedManagers())
		if (auto view = loadView(name, manager))
		{
			addViewItem(view, position);
			return view;
		}
	return nullptr;
}

ViewItem* ViewItemManager::loadView(QString name, Model::TreeManager* manager)
{
	QFile file{fileName(name, manager->name())};
	if (!file.exists()) return nullptr;
	file.open(QIODevice::ReadOnly);
	QTextStream read{&file};
	QString json;
	while (!read.atEnd()) json = json + read.readLine();
	ViewItem* view = new ViewItem{nullptr};
	view->fromJson(QJsonDocument::fromJson(json.toUtf8()));
	return view;
}

void ViewItemManager::cleanupRemovedItem(Item *removedItem)
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			if (item) item->cleanupRemovedItem(removedItem);
}

void ViewItemManager::cleanupRemovedNode(Model::Node* removedNode)
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			if (item) item->cleanupRemovedNode(removedNode);
}

QPoint ViewItemManager::nextEmptyPosition() const
{
	//Take the first empty position, if one exists
	for (int col = 0; col < viewItems_.size(); col++)
		for (int row = 0; row < viewItems_[col].size(); row++)
			if (!viewItems_[col][row]) return QPoint{col, row};
	//Else just use the column with the least rows
	int colToInsert = 0;
	for (int col = 1; col < viewItems_.size(); col++)
		if (viewItems_[col].size() < viewItems_[colToInsert].size())
			colToInsert = col;
	return QPoint{colToInsert, viewItems_[colToInsert].size()};
}

QString ViewItemManager::fileName(QString viewName, QString managerName) const
{
	return QDir::toNativeSeparators(DIRECTORY_NAME + "/" + managerName + "__" + viewName + ".json");
}

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

#include "ViewItemManager.h"

#include "items/ViewItem.h"
#include "Scene.h"

#include "ModelBase/src/model/TreeManager.h"
#include "ModelBase/src/model/AllTreeManagers.h"

namespace Visualization {

ViewItemManager::ViewItemManager(Scene* scene)
	: scene_{scene}
{
	viewItems_.resize(VIEW_ITEM_COLUMNS);
	currentViewItem_ = newViewItem("ProjectView");
	currentViewItem_->show();
}

ViewItemManager::~ViewItemManager()
{
	auto viewItems = viewItems_;
	viewItems_.clear();
	for (auto vector : viewItems)
		for (auto view : vector)
			if (view)
			{
				scene_->removeTopLevelItem(view);
				SAFE_DELETE_ITEM(view);
			}
	currentViewItem_ = nullptr;
	scene_ = nullptr;
}

ViewItem* ViewItemManager::currentViewItem()
{
	if (std::all_of(viewItems_.begin(), viewItems_.end(),
					[](QVector<ViewItem*> v) { return v.isEmpty(); }))
	{
		currentViewItem_ = newViewItem("ProjectView");
		currentViewItem_->show();
	}
	Q_ASSERT(currentViewItem_);
	return currentViewItem_;
}

ViewItem* ViewItemManager::viewItem(const QString name)
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			if (item && item->name() == name)
				return item;
	return nullptr;
}

void ViewItemManager::addViewItem(ViewItem *view, QPoint position)
{
	Q_ASSERT(!scene_->inAnUpdate_);
	if (position.x() < 0 || position.y() < 0)
		position = nextEmptyPosition();
	Q_ASSERT(position.x() >= 0 && position.x() < VIEW_ITEM_COLUMNS
			 && position.y() >= 0);
	if (viewItems_[position.x()].size() <= position.y())
		viewItems_[position.x()].resize(position.y() + 1);
	//If there already is an item -> use insert
	if (viewItems_[position.x()][position.y()])
		viewItems_[position.x()].insert(position.y(), view);
	else
	{
		//Else just overwrite the nullptr
		viewItems_[position.x()][position.y()] = view;
	}
	scene_->addTopLevelItem(view, false);
}

void ViewItemManager::switchToView(ViewItem *view)
{
	Q_ASSERT(!scene_->inAnUpdate_);
	Q_ASSERT(std::any_of(viewItems_.begin(), viewItems_.end(),
						 [view](QVector<ViewItem*> v) { return v.contains(view); }));
	currentViewItem_->hide();
	currentViewItem_ = view;
	currentViewItem_->show();
	currentViewItem_->setUpdateNeeded(Item::StandardUpdate);
	scene_->scheduleUpdate();
}

bool ViewItemManager::switchToView(const QString viewName)
{
	auto view = viewItem(viewName);
	if (view) switchToView(view);
	return view != nullptr;
}
ViewItem* ViewItemManager::newViewItem(const QString name, QPoint position)
{
	//If the name is invalid, don't create a new view
	if (!ViewItem::isValidName(name)) return nullptr;
	auto result = new ViewItem{nullptr, name};
	addViewItem(result, position);
	return result;
}

void ViewItemManager::removeAllViewItems()
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			scene_->removeTopLevelItem(item);

	viewItems_.clear();
	currentViewItem_ = nullptr;
}

void ViewItemManager::saveView(ViewItem* view, Model::TreeManager* manager) const
{
	auto json = view->toJson().toJson();
	if (!QDir{DIRECTORY_NAME}.exists())
		QDir().mkdir(DIRECTORY_NAME);
	QFile file{fileName(view->name(), manager->name())};
	file.open(QIODevice::WriteOnly);
	QTextStream write{&file};
	write << json;
}

ViewItem* ViewItemManager::loadView(QString name, QPoint position)
{
	for (auto manager : Model::AllTreeManagers::instance().loadedManagers())
		if (auto view = loadView(name, manager))
		{
			addViewItem(view, position);
			return view;
		}
	return nullptr;
}

ViewItem* ViewItemManager::loadView(QString name, Model::TreeManager* manager)
{
	QFile file{fileName(name, manager->name())};
	if (!file.exists()) return nullptr;
	file.open(QIODevice::ReadOnly);
	QTextStream read{&file};
	QString json;
	while (!read.atEnd()) json = json + read.readLine();
	ViewItem* view = new ViewItem{nullptr};
	view->fromJson(QJsonDocument::fromJson(json.toUtf8()));
	return view;
}

void ViewItemManager::cleanupRemovedItem(Item *removedItem)
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			if (item) item->cleanupRemovedItem(removedItem);
}

void ViewItemManager::cleanupRemovedNode(Model::Node* removedNode)
{
	for (auto vector : viewItems_)
		for (auto item : vector)
			if (item) item->cleanupRemovedNode(removedNode);
}

QPoint ViewItemManager::nextEmptyPosition() const
{
	//Take the first empty position, if one exists
	for (int col = 0; col < viewItems_.size(); col++)
		for (int row = 0; row < viewItems_[col].size(); row++)
			if (!viewItems_[col][row]) return QPoint{col, row};
	//Else just use the column with the least rows
	int colToInsert = 0;
	for (int col = 1; col < viewItems_.size(); col++)
		if (viewItems_[col].size() < viewItems_[colToInsert].size())
			colToInsert = col;
	return QPoint{colToInsert, viewItems_[colToInsert].size()};
}

QString ViewItemManager::fileName(QString viewName, QString managerName) const
{
	return QDir::toNativeSeparators(DIRECTORY_NAME + "/" + managerName + "__" + viewName + ".json");
}

}
<|endoftext|>


<commit_before>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://www.visualizationlibrary.com                                               */
/*                                                                                */
/*  Copyright (c) 2005-2010, Michele Bosi                                             */
/*  All rights reserved.                                                              */
/*                                                                                */
/*  Redistribution and use in source and binary forms, with or without modification,  */
/*  are permitted provided that the following conditions are met:                     */
/*                                                                                */
/*  - Redistributions of source code must retain the above copyright notice, this     */
/*  list of conditions and the following disclaimer.                                  */
/*                                                                                */
/*  - Redistributions in binary form must reproduce the above copyright notice, this  */
/*  list of conditions and the following disclaimer in the documentation and/or       */
/*  other materials provided with the distribution.                                   */
/*                                                                                */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
/*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
/*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
/*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
/*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
/*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
/*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
/*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef HalfFloat_INCLUDE_ONCE
#define HalfFloat_INCLUDE_ONCE

#include "vl/Vector4.hpp"

namespace vl
{
  // -15 stored using a single precision bias of 127
  const unsigned int  HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP = 0x38000000;

  // max exponent value in single precision that will be converted
  // to Inf or Nan when stored as a half-float
  const unsigned int  HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP = 0x47800000;

  // 255 is the max exponent biased value
  const unsigned int  FLOAT_MAX_BIASED_EXP = (0xFF << 23) ;
  const unsigned int  HALF_FLOAT_MAX_BIASED_EXP = (0x1F << 10) ;

  //! Represents an half-precision floating point value
  class half
  {
  public:
    half(): bits(0) {}
    half(const half& hf): bits(hf.bits) {}
    half(int i): bits(convertFloatToHalf((float)i).bits) {}
    half(long long i): bits(convertFloatToHalf((float)i).bits) {}
    half(float f): bits(convertFloatToHalf(f).bits) {}
    half(double d): bits(convertFloatToHalf((float)d).bits) {}

    operator float() const
    {
      return convertHalfToFloat(*this);
    }

    operator double() const
    {
      return (double)convertHalfToFloat(*this);
    }

    half operator+(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) + convertHalfToFloat(other) );
    }

    half operator-(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) - convertHalfToFloat(other) );
    }

    half operator*(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) * convertHalfToFloat(other) );
    }

    half operator/(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) / convertHalfToFloat(other) );
    }

    bool operator==(const half& other) const
    {
      return bits == other.bits;
    }

    bool operator!=(const half& other) const
    {
      return bits != other.bits;
    }

    bool operator<(const half& other) const
    {
      return convertHalfToFloat(*this) < convertHalfToFloat(other);
    }

    bool operator>(const half& other) const
    {
      return convertHalfToFloat(*this) < convertHalfToFloat(other);
    }

    //---------------------------------------------------------------------------
    static void convertDoubleToHalf(const double* d, half* h, int count)
    {
      for(int i=0; i<count; ++i)
        h[i] = convertFloatToHalf((float)d[i]);
    }
    //---------------------------------------------------------------------------
    static void convertHalfToDouble(const half* h, double* d, int count)
    {
      for(int i=0; i<count; ++i)
        d[i] = (double)convertHalfToFloat(h[i]);
    }
    //---------------------------------------------------------------------------
    bool isNan()
    {
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa != 0;
    }
    //---------------------------------------------------------------------------
    bool isinf()
    {
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0;
    }
    //---------------------------------------------------------------------------
    bool isinf_pos()
    {
      unsigned int sign = (unsigned int) ( bits >> 15) ;
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0 && sign == 0;
    }
    //---------------------------------------------------------------------------
    bool isinf_neg()
    {
      unsigned int sign = (unsigned int) ( bits >> 15) ;
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0 && sign == 1;
    }
    //---------------------------------------------------------------------------
    bool isdenorm()
    {
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == 0 && mantissa != 0;
    }
    //---------------------------------------------------------------------------
    static half convertFloatToHalf(float f)
    {
      unsigned int x = *(unsigned int*)&f;
      unsigned int sign = (unsigned short)(x>>31);
      unsigned int mantissa = x & ((1 << 23)-1);
      unsigned int exp = x & FLOAT_MAX_BIASED_EXP;
      typedef unsigned short hfloat;
      half hf;

      if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
      {
        // check if the original single precision float number is a NaN
        if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
        {
          // we have a single precision NaN
          mantissa = (1<<23) - 1;
        }
        else
        {
          // 16-bit half-float representation stores number as Inf
          mantissa = 0;
        }
        hf.bits = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
      }
      // check if exponent is <= -15
      else
      if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
      {
        // store a denorm half-float value or zero
        exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
        mantissa >>= (14 + exp);
        hf.bits = (((hfloat)sign) << 15) | (hfloat)(mantissa);
      }
      else
      {
        hf.bits = (((hfloat)sign)<<15) |
                  (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) |
                  (hfloat)(mantissa >> 13);
      }
      return hf;
    }
    //---------------------------------------------------------------------------
    static float convertHalfToFloat(const half& h)
    {
      unsigned short hf = h.bits;
      unsigned int sign = (unsigned int) ( hf >> 15) ;
      unsigned int mantissa = (unsigned int) (hf & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (hf & HALF_FLOAT_MAX_BIASED_EXP) ;
      unsigned int f;

      if (exp == HALF_FLOAT_MAX_BIASED_EXP)
      {
        // we have a half-float NaN or Inf
        // half-float NaNs will be converted to a single precision NaN
        // half-float Infs will be converted to a single precision Inf
        exp = FLOAT_MAX_BIASED_EXP;
        if ( mantissa)
          mantissa = (1 << 23 ) - 1;   // set all bits to indicate a NaN
      }
      else if (exp == 0x0)
      {
        // convert half-float zero/denorm to single precision value
        if ( mantissa)
        {
          mantissa <<= 1;
          exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
          // check for leading 1 in denorm mantissa
          while ((mantissa & (1 << 10) ) == 0)
          {
            // for every leading 0, decrement single precision exponent by 1
            // and shift half-float mantissa value to the left
            mantissa <<= 1;
            exp -= (1 << 23 ) ;
          }
          // clamp the mantissa to 10-bits
          mantissa &= (( 1 << 10) - 1);
          // shift left to generate single-precision mantissa of 23-bits
          mantissa <<= 13;
        }
      }
      else
      {
        // shift left to generate single-precision mantissa of 23-bits
        mantissa <<= 13;
        // generate single precision biased exponent value
        exp = ( exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
      }
      f = ( sign << 31) | exp | mantissa;
      return *((float *) &f) ;
    }
    //---------------------------------------------------------------------------
    static void convertFloatToHalf(const float* f, half* h, int count)
    {
      for(int i=0; i<count; ++i)
      {
        unsigned int x = *(unsigned int*)(f+i);
        unsigned int sign = (unsigned short)(x>>31);
        unsigned int mantissa = x & ((1 << 23)-1);
        unsigned int exp = x & FLOAT_MAX_BIASED_EXP;
        typedef unsigned short hfloat;
        hfloat& hf = h[i].bits;

        if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
        {
          // check if the original single precision float number is a NaN
          if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
          {
            // we have a single precision NaN
            mantissa = (1<<23) - 1;
          }
          else
          {
            // 16-bit half-float representation stores number as Inf
            mantissa = 0;
          }
          hf = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
        }
        // check if exponent is <= -15
        else
        if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
        {
          // store a denorm half-float value or zero
          exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
          mantissa >>= (14 + exp);
          hf = (((hfloat)sign) << 15) | (hfloat)(mantissa);
        }
        else
        {
          hf = (((hfloat)sign)<<15) |
               (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) |
               (hfloat)(mantissa >> 13);
        }
      }
    }
    //---------------------------------------------------------------------------
    static void convertHalfToFloat(const half* h, float *f, int count)
    {
      for(int i=0; i<count; ++i)
      {
        const unsigned short& hf = h[i].bits;
        unsigned int sign = (unsigned int) ( hf >> 15) ;
        unsigned int mantissa = (unsigned int) (hf & (( 1 << 10) - 1) ) ;
        unsigned int exp = (unsigned int) (hf & HALF_FLOAT_MAX_BIASED_EXP) ;
        unsigned int uif;

        if (exp == HALF_FLOAT_MAX_BIASED_EXP)
        {
          // we have a half-float NaN or Inf
          // half-float NaNs will be converted to a single precision NaN
          // half-float Infs will be converted to a single precision Inf
          exp = FLOAT_MAX_BIASED_EXP;
          if ( mantissa)
            mantissa = (1 << 23 ) - 1;   // set all bits to indicate a NaN
        }
        else if (exp == 0x0)
        {
          // convert half-float zero/denorm to single precision value
          if ( mantissa)
          {
            mantissa <<= 1;
            exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
            // check for leading 1 in denorm mantissa
            while ((mantissa & (1 << 10) ) == 0)
            {
              // for every leading 0, decrement single precision exponent by 1
              // and shift half-float mantissa value to the left
              mantissa <<= 1;
              exp -= (1 << 23 ) ;
            }
            // clamp the mantissa to 10-bits
            mantissa &= (( 1 << 10) - 1);
            // shift left to generate single-precision mantissa of 23-bits
            mantissa <<= 13;
          }
        }
        else
        {
          // shift left to generate single-precision mantissa of 23-bits
          mantissa <<= 13;
          // generate single precision biased exponent value
          exp = ( exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
        }
        uif = ( sign << 31) | exp | mantissa;
        f[i] =  *((float *) &uif) ;
      }
    }
    //---------------------------------------------------------------------------
  public:
    unsigned short bits;
  };
  //-----------------------------------------------------------------------------
  typedef Vector4<half> hvec4;
  typedef Vector3<half> hvec3;
  typedef Vector2<half> hvec2;
  //-----------------------------------------------------------------------------
}

#endif
<commit_msg>added operator-(), infinity(), NaN()<commit_after>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://www.visualizationlibrary.com                                               */
/*                                                                                */
/*  Copyright (c) 2005-2010, Michele Bosi                                             */
/*  All rights reserved.                                                              */
/*                                                                                */
/*  Redistribution and use in source and binary forms, with or without modification,  */
/*  are permitted provided that the following conditions are met:                     */
/*                                                                                */
/*  - Redistributions of source code must retain the above copyright notice, this     */
/*  list of conditions and the following disclaimer.                                  */
/*                                                                                */
/*  - Redistributions in binary form must reproduce the above copyright notice, this  */
/*  list of conditions and the following disclaimer in the documentation and/or       */
/*  other materials provided with the distribution.                                   */
/*                                                                                */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
/*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
/*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
/*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
/*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
/*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
/*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
/*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef HalfFloat_INCLUDE_ONCE
#define HalfFloat_INCLUDE_ONCE

#include "vl/Vector4.hpp"

namespace vl
{
  // -15 stored using a single precision bias of 127
  const unsigned int  HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP = 0x38000000;

  // max exponent value in single precision that will be converted
  // to Inf or Nan when stored as a half-float
  const unsigned int  HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP = 0x47800000;

  // 255 is the max exponent biased value
  const unsigned int  FLOAT_MAX_BIASED_EXP = (0xFF << 23) ;
  const unsigned int  HALF_FLOAT_MAX_BIASED_EXP = (0x1F << 10) ;

  //! Represents an half-precision floating point value
  class half
  {
  public:
    half(): bits(0) {}
    half(const half& hf): bits(hf.bits) {}
    half(int i): bits(convertFloatToHalf((float)i).bits) {}
    half(long long i): bits(convertFloatToHalf((float)i).bits) {}
    half(float f): bits(convertFloatToHalf(f).bits) {}
    half(double d): bits(convertFloatToHalf((float)d).bits) {}

    operator float() const
    {
      return convertHalfToFloat(*this);
    }

    operator double() const
    {
      return (double)convertHalfToFloat(*this);
    }

    half operator+(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) + convertHalfToFloat(other) );
    }

    half operator-(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) - convertHalfToFloat(other) );
    }

    half operator*(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) * convertHalfToFloat(other) );
    }

    half operator/(const half& other) const
    {
      return convertFloatToHalf( convertHalfToFloat(*this) / convertHalfToFloat(other) );
    }

    bool operator==(const half& other) const
    {
      return bits == other.bits;
    }

    bool operator!=(const half& other) const
    {
      return bits != other.bits;
    }

    bool operator<(const half& other) const
    {
      return convertHalfToFloat(*this) < convertHalfToFloat(other);
    }

    bool operator>(const half& other) const
    {
      return convertHalfToFloat(*this) < convertHalfToFloat(other);
    }

    //---------------------------------------------------------------------------
    static void convertDoubleToHalf(const double* d, half* h, int count)
    {
      for(int i=0; i<count; ++i)
        h[i] = convertFloatToHalf((float)d[i]);
    }
    //---------------------------------------------------------------------------
    static void convertHalfToDouble(const half* h, double* d, int count)
    {
      for(int i=0; i<count; ++i)
        d[i] = (double)convertHalfToFloat(h[i]);
    }
    //---------------------------------------------------------------------------
    bool isNan()
    {
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa != 0;
    }
    //---------------------------------------------------------------------------
    bool isinf()
    {
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0;
    }
    //---------------------------------------------------------------------------
    bool isinf_pos()
    {
      unsigned int sign = (unsigned int) ( bits >> 15) ;
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0 && sign == 0;
    }
    //---------------------------------------------------------------------------
    bool isinf_neg()
    {
      unsigned int sign = (unsigned int) ( bits >> 15) ;
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0 && sign == 1;
    }
    //---------------------------------------------------------------------------
    bool isdenorm()
    {
      unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP) ;
      return exp == 0 && mantissa != 0;
    }
    //---------------------------------------------------------------------------
    half operator-()
    {
      half h = *this;
      h.bits ^= 1 << 15;
      return h;
    }
    //---------------------------------------------------------------------------
    static half infinity()
    {
      half h;
      h.bits = HALF_FLOAT_MAX_BIASED_EXP;
      return h;
    }
    //---------------------------------------------------------------------------
    static half NaN()
    {
      half h;
      h.bits = HALF_FLOAT_MAX_BIASED_EXP | (( 1 << 10) - 1);
      return h;
    }
    //---------------------------------------------------------------------------
    static half convertFloatToHalf(float f)
    {
      unsigned int x = *(unsigned int*)&f;
      unsigned int sign = (unsigned short)(x>>31);
      unsigned int mantissa = x & ((1 << 23)-1);
      unsigned int exp = x & FLOAT_MAX_BIASED_EXP;
      typedef unsigned short hfloat;
      half hf;

      if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
      {
        // check if the original single precision float number is a NaN
        if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
        {
          // we have a single precision NaN
          mantissa = (1<<23) - 1;
        }
        else
        {
          // 16-bit half-float representation stores number as Inf
          mantissa = 0;
        }
        hf.bits = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
      }
      // check if exponent is <= -15
      else
      if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
      {
        // store a denorm half-float value or zero
        exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
        mantissa >>= (14 + exp);
        hf.bits = (((hfloat)sign) << 15) | (hfloat)(mantissa);
      }
      else
      {
        hf.bits = (((hfloat)sign)<<15) |
                  (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) |
                  (hfloat)(mantissa >> 13);
      }
      return hf;
    }
    //---------------------------------------------------------------------------
    static float convertHalfToFloat(const half& h)
    {
      unsigned short hf = h.bits;
      unsigned int sign = (unsigned int) ( hf >> 15) ;
      unsigned int mantissa = (unsigned int) (hf & (( 1 << 10) - 1) ) ;
      unsigned int exp = (unsigned int) (hf & HALF_FLOAT_MAX_BIASED_EXP) ;
      unsigned int f;

      if (exp == HALF_FLOAT_MAX_BIASED_EXP)
      {
        // we have a half-float NaN or Inf
        // half-float NaNs will be converted to a single precision NaN
        // half-float Infs will be converted to a single precision Inf
        exp = FLOAT_MAX_BIASED_EXP;
        if ( mantissa)
          mantissa = (1 << 23 ) - 1;   // set all bits to indicate a NaN
      }
      else if (exp == 0x0)
      {
        // convert half-float zero/denorm to single precision value
        if ( mantissa)
        {
          mantissa <<= 1;
          exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
          // check for leading 1 in denorm mantissa
          while ((mantissa & (1 << 10) ) == 0)
          {
            // for every leading 0, decrement single precision exponent by 1
            // and shift half-float mantissa value to the left
            mantissa <<= 1;
            exp -= (1 << 23 ) ;
          }
          // clamp the mantissa to 10-bits
          mantissa &= (( 1 << 10) - 1);
          // shift left to generate single-precision mantissa of 23-bits
          mantissa <<= 13;
        }
      }
      else
      {
        // shift left to generate single-precision mantissa of 23-bits
        mantissa <<= 13;
        // generate single precision biased exponent value
        exp = ( exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
      }
      f = ( sign << 31) | exp | mantissa;
      return *((float *) &f) ;
    }
    //---------------------------------------------------------------------------
    static void convertFloatToHalf(const float* f, half* h, int count)
    {
      for(int i=0; i<count; ++i)
      {
        unsigned int x = *(unsigned int*)(f+i);
        unsigned int sign = (unsigned short)(x>>31);
        unsigned int mantissa = x & ((1 << 23)-1);
        unsigned int exp = x & FLOAT_MAX_BIASED_EXP;
        typedef unsigned short hfloat;
        hfloat& hf = h[i].bits;

        if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
        {
          // check if the original single precision float number is a NaN
          if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
          {
            // we have a single precision NaN
            mantissa = (1<<23) - 1;
          }
          else
          {
            // 16-bit half-float representation stores number as Inf
            mantissa = 0;
          }
          hf = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
        }
        // check if exponent is <= -15
        else
        if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
        {
          // store a denorm half-float value or zero
          exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
          mantissa >>= (14 + exp);
          hf = (((hfloat)sign) << 15) | (hfloat)(mantissa);
        }
        else
        {
          hf = (((hfloat)sign)<<15) |
               (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) |
               (hfloat)(mantissa >> 13);
        }
      }
    }
    //---------------------------------------------------------------------------
    static void convertHalfToFloat(const half* h, float *f, int count)
    {
      for(int i=0; i<count; ++i)
      {
        const unsigned short& hf = h[i].bits;
        unsigned int sign = (unsigned int) ( hf >> 15) ;
        unsigned int mantissa = (unsigned int) (hf & (( 1 << 10) - 1) ) ;
        unsigned int exp = (unsigned int) (hf & HALF_FLOAT_MAX_BIASED_EXP) ;
        unsigned int uif;

        if (exp == HALF_FLOAT_MAX_BIASED_EXP)
        {
          // we have a half-float NaN or Inf
          // half-float NaNs will be converted to a single precision NaN
          // half-float Infs will be converted to a single precision Inf
          exp = FLOAT_MAX_BIASED_EXP;
          if ( mantissa)
            mantissa = (1 << 23 ) - 1;   // set all bits to indicate a NaN
        }
        else if (exp == 0x0)
        {
          // convert half-float zero/denorm to single precision value
          if ( mantissa)
          {
            mantissa <<= 1;
            exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
            // check for leading 1 in denorm mantissa
            while ((mantissa & (1 << 10) ) == 0)
            {
              // for every leading 0, decrement single precision exponent by 1
              // and shift half-float mantissa value to the left
              mantissa <<= 1;
              exp -= (1 << 23 ) ;
            }
            // clamp the mantissa to 10-bits
            mantissa &= (( 1 << 10) - 1);
            // shift left to generate single-precision mantissa of 23-bits
            mantissa <<= 13;
          }
        }
        else
        {
          // shift left to generate single-precision mantissa of 23-bits
          mantissa <<= 13;
          // generate single precision biased exponent value
          exp = ( exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
        }
        uif = ( sign << 31) | exp | mantissa;
        f[i] =  *((float *) &uif) ;
      }
    }
    //---------------------------------------------------------------------------
  public:
    unsigned short bits;
  };
  //-----------------------------------------------------------------------------
  typedef Vector4<half> hvec4;
  typedef Vector3<half> hvec3;
  typedef Vector2<half> hvec2;
  //-----------------------------------------------------------------------------
}

#endif
<|endoftext|>


<commit_before>#ifndef _SDD_SDD_HH_
#define _SDD_SDD_HH_

#include "sdd/conf/default_configurations.hh"
#include "sdd/dd/context.hh"
#include "sdd/dd/definition.hh"

/// @brief The main namespace for the library.
namespace sdd
{

/// @brief Contain all stuff related to SDD: definition and operations.
namespace dd {}

/// @brief Contain definition of values to be stored on arcs.
namespace values {}

/// @cond INTERNAL_DOC

/// @brief Contain internal stuff necessary for the library.
namespace internal {}

/// @brief Contain stuff necessary for the memory management.
namespace mem {}

/// @endcond

}

#endif // _SDD_SDD_HH_
<commit_msg>Comments.<commit_after>#ifndef _SDD_SDD_HH_
#define _SDD_SDD_HH_

#include "sdd/conf/default_configurations.hh"
#include "sdd/dd/context.hh"
#include "sdd/dd/definition.hh"

/// @brief The main namespace for the library.
namespace sdd
{

/// @brief Stuff related to SDD: definition and operations.
namespace dd {}

/// @brief Definition of values to be stored on arcs.
namespace values {}

/// @cond INTERNAL_DOC

/// @brief Contain internal stuff necessary for the library.
namespace internal {}

/// @brief Contain stuff necessary for the memory management.
namespace mem {}

/// @endcond

}

#endif // _SDD_SDD_HH_
<|endoftext|>