Skip to content

Commit

Permalink
Remove boost::hash and implement our own hash
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Mar 27, 2024
1 parent 27c3bdf commit 0cde547
Show file tree
Hide file tree
Showing 25 changed files with 345 additions and 195 deletions.
9 changes: 4 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,11 @@ target_include_directories(

target_link_libraries(
${PROJECT_NAME}
PUBLIC MPFR
PUBLIC fmt::fmt
cppcoro
MPFR
Boost::multiprecision
Boost::math
Boost::container_hash
fmt::fmt
cppcoro)
Boost::math)

include(cmake/CompilerWarnings.cmake)
include(cmake/Coverage.cmake)
Expand Down
25 changes: 25 additions & 0 deletions include/fintamath/core/CoreUtils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <concepts>
#include <functional>
#include <ranges>

namespace fintamath {

namespace stdr = std::ranges;
namespace stdv = std::views;

template <typename From, typename To>
concept ConvertibleToAndNotSameAs = std::convertible_to<From, To> && !std::same_as<From, To>;

template <class T>
concept TupleLike = requires {
std::tuple_size<T>::value;
};

template <class T>
concept Hashable = requires(const T &v) {
std::hash<T>{}(v);
};

}
45 changes: 45 additions & 0 deletions include/fintamath/core/Hash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <functional>
#include <tuple>

#include "fintamath/core/CoreUtils.hpp"

namespace fintamath::detail {

template <class T>
struct Hash;

template <class T>
void hashCombine(size_t &seed, const T &v) noexcept {
Hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

template <Hashable T>
struct Hash<T> {
size_t operator()(const T &v) const noexcept {
return std::hash<T>{}(v);
}
};

template <TupleLike T>
struct Hash<T> {
size_t operator()(const T &v) const noexcept {
std::size_t seed = 0;
hashCombineTuple<0>(seed, v);
return seed;
}

private:
template <std::size_t i>
static void hashCombineTuple(std::size_t &seed, const T &v) noexcept {
if constexpr (i < std::tuple_size_v<T>) {
Hash<std::tuple_element_t<i, T>> hasher;
hashCombine(seed, hasher(std::get<i>(v)));
hashCombineTuple<i + 1>(seed, v);
}
}
};

}
13 changes: 4 additions & 9 deletions include/fintamath/core/MathObjectClass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@

#include <compare>
#include <cstddef>
#include <optional>
#include <string_view>
#include <unordered_map>
#include <unordered_set>

#include <boost/container_hash/hash.hpp>

#include "fintamath/config/Config.hpp"

namespace fintamath {

class MathObjectClass;

size_t hash_value(MathObjectClass rhs) noexcept;

}

template <>
struct std::hash<fintamath::MathObjectClass> {
size_t operator()(const fintamath::MathObjectClass &rhs) const noexcept {
return boost::hash<fintamath::MathObjectClass>{}(rhs);
}
size_t operator()(const fintamath::MathObjectClass &rhs) const noexcept;
};

namespace fintamath {
Expand Down Expand Up @@ -100,8 +95,8 @@ void MathObjectClass::bindTypes() {
}
}

inline size_t hash_value(const MathObjectClass rhs) noexcept {
return boost::hash<MathObjectClass::Name>{}(rhs.getName());
}

inline size_t std::hash<fintamath::MathObjectClass>::operator()(const fintamath::MathObjectClass &rhs) const noexcept {
return std::hash<fintamath::MathObjectClass::Name>{}(rhs.getName());
}
7 changes: 0 additions & 7 deletions include/fintamath/core/MathObjectUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <concepts>
#include <functional>
#include <memory>
#include <ranges>
#include <type_traits>

#include "fintamath/core/MathObjectClass.hpp"
Expand All @@ -12,12 +11,6 @@ namespace fintamath {

class IMathObject;

namespace stdr = std::ranges;
namespace stdv = std::views;

template <typename From, typename To>
concept ConvertibleToAndNotSameAs = std::convertible_to<From, To> && !std::same_as<From, To>;

inline bool is(const MathObjectClass to, const MathObjectClass from) {
return to == from || to.getChildren(true).contains(from);
}
Expand Down
5 changes: 2 additions & 3 deletions include/fintamath/core/MultiMethod.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
#include <tuple>
#include <unordered_map>

#include <boost/container_hash/hash.hpp>

#include "fintamath/core/Hash.hpp"
#include "fintamath/core/MathObjectClass.hpp"
#include "fintamath/core/MathObjectUtils.hpp"

Expand All @@ -23,7 +22,7 @@ class MultiMethod<Res(ArgsBase...)> final {

using Callback = std::function<Res(ArgsBase...)>;

using IdToCallbackMap = std::unordered_map<CallbackId, Callback, boost::hash<CallbackId>>;
using IdToCallbackMap = std::unordered_map<CallbackId, Callback, Hash<CallbackId>>;

public:
template <typename... Args>
Expand Down
12 changes: 8 additions & 4 deletions include/fintamath/numbers/Integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <memory>
#include <string>

#include <boost/container_hash/hash.hpp>
#include <boost/multiprecision/fwd.hpp>
#include <boost/multiprecision/gmp.hpp>

Expand Down Expand Up @@ -87,8 +86,13 @@ class Integer final : public IIntegerCRTP<Integer> {
Backend backend;
};

inline size_t hash_value(const Integer &rhs) noexcept {
return boost::hash<Integer::Backend>{}(rhs.getBackend());
}

}
template <>
struct std::hash<fintamath::Integer> {
size_t operator()(const fintamath::Integer &rhs) const noexcept {
using fintamath::detail::Hash;

return Hash<fintamath::Integer::Backend>{}(rhs.getBackend());
}
};
4 changes: 1 addition & 3 deletions include/fintamath/numbers/IntegerFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
#include <unordered_map>
#include <vector>

#include <boost/container_hash/hash.hpp>

#include "fintamath/exceptions/UndefinedException.hpp"
#include "fintamath/numbers/INumber.hpp"
#include "fintamath/numbers/Integer.hpp"

namespace fintamath {

using FactorToCountMap = std::unordered_map<Integer, Integer, boost::hash<Integer>>;
using FactorToCountMap = std::unordered_map<Integer, Integer>;

// Use exponentiation by squaring with constant auxiliary memory (iterative version).
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring#With_constant_auxiliary_memory.
Expand Down
20 changes: 12 additions & 8 deletions include/fintamath/numbers/Rational.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include <memory>
#include <string>

#include <boost/container_hash/hash.hpp>

#include "fintamath/core/IArithmetic.hpp"
#include "fintamath/core/IMathObject.hpp"
#include "fintamath/core/MathObjectClass.hpp"
Expand Down Expand Up @@ -68,11 +66,17 @@ class Rational final : public INumberCRTP<Rational> {
Integer denom = 1;
};

inline size_t hash_value(const Rational &rhs) noexcept {
size_t seed = 0;
boost::hash_combine(seed, boost::hash<Integer>{}(rhs.numerator()));
boost::hash_combine(seed, boost::hash<Integer>{}(rhs.denominator()));
return seed;
}

}
template <>
struct std::hash<fintamath::Rational> {
size_t operator()(const fintamath::Rational &rhs) const noexcept {
using fintamath::detail::Hash;
using fintamath::detail::hashCombine;

size_t seed = 0;
hashCombine(seed, Hash<fintamath::Integer>{}(rhs.numerator()));
hashCombine(seed, Hash<fintamath::Integer>{}(rhs.denominator()));
return seed;
}
};
25 changes: 15 additions & 10 deletions include/fintamath/numbers/Real.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <memory>
#include <string>

#include <boost/container_hash/hash.hpp>
#include <boost/multiprecision/fwd.hpp>
#include <boost/multiprecision/mpfr.hpp>

Expand Down Expand Up @@ -110,14 +109,20 @@ class Real final : public INumberCRTP<Real> {
bool isNegative = false;
};

inline size_t hash_value(const Real &rhs) noexcept {
if (rhs.isZero()) {
size_t seed = 0;
boost::hash_combine(seed, boost::hash<int>{}(rhs.sign()));
return seed;
}

return boost::hash<Real::Backend>{}(rhs.getBackend());
}

}
template <>
struct std::hash<fintamath::Real> {
size_t operator()(const fintamath::Real &rhs) const noexcept {
using fintamath::detail::Hash;
using fintamath::detail::hashCombine;

if (rhs.isZero()) {
size_t seed = 0;
hashCombine(seed, Hash<int>{}(rhs.sign()));
return seed;
}

return Hash<fintamath::Real::Backend>{}(rhs.getBackend());
}
};
2 changes: 1 addition & 1 deletion src/fintamath/core/Tokenizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <regex>
#include <string>

#include "fintamath/core/MathObjectUtils.hpp"
#include "fintamath/core/CoreUtils.hpp"

namespace fintamath::detail {

Expand Down
4 changes: 1 addition & 3 deletions src/fintamath/expressions/unary/InvTrigExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#include <unordered_map>
#include <utility>

#include <boost/container_hash/hash.hpp>

#include "fintamath/core/Converter.hpp"
#include "fintamath/core/MathObjectUtils.hpp"
#include "fintamath/expressions/IExpression.hpp"
Expand All @@ -30,7 +28,7 @@ namespace fintamath {

using NameToSimplifyFunctionMap = std::unordered_map<std::string, std::function<ArgumentPtr(const Rational &)>>;

using TrigTable = std::unordered_map<Rational, ArgumentPtr, boost::hash<Rational>>;
using TrigTable = std::unordered_map<Rational, ArgumentPtr>;

ArgumentPtr findValue(const TrigTable &trigTable, const Rational &key);

Expand Down
4 changes: 1 addition & 3 deletions src/fintamath/expressions/unary/TrigExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
#include <unordered_map>
#include <utility>

#include <boost/container_hash/hash.hpp>

#include "fintamath/core/Converter.hpp"
#include "fintamath/core/MathObjectUtils.hpp"
#include "fintamath/expressions/ExpressionUtils.hpp"
Expand Down Expand Up @@ -42,7 +40,7 @@ using NameToSimplifyFunctionMap = std::unordered_map<std::string, std::function<

using NameToTrigFunctionMap = std::unordered_map<std::string, std::function<ArgumentPtr(const Rational &)>>;

using TrigTable = std::unordered_map<Rational, ArgumentPtr, boost::hash<Rational>>;
using TrigTable = std::unordered_map<Rational, ArgumentPtr>;

ArgumentPtr findValue(const TrigTable &trigTable, const Rational &key, bool isNegated);

Expand Down
58 changes: 2 additions & 56 deletions tests/src/core/CoreUtilsTests.cpp
Original file line number Diff line number Diff line change
@@ -1,59 +1,5 @@
#include <gtest/gtest.h>

#include "fintamath/core/MathObjectUtils.hpp"
#include "fintamath/core/CoreUtils.hpp"

#include "fintamath/literals/constants/E.hpp"
#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/Rational.hpp"

using namespace fintamath;

E c;
Integer i;

TEST(MathObjectUtilsTests, isTest) {
EXPECT_TRUE(is<IArithmetic>(i));
EXPECT_FALSE(is<IArithmetic>(c));

EXPECT_TRUE(is<IArithmetic>(i.clone().get()));
EXPECT_FALSE(is<IArithmetic>(E().clone().get()));
EXPECT_FALSE(is<IArithmetic>(std::unique_ptr<IArithmetic>().get()));

EXPECT_TRUE(is<IArithmetic>(i.clone()));
EXPECT_FALSE(is<IArithmetic>(E().clone()));

EXPECT_TRUE(is<IArithmetic>(std::shared_ptr(i.clone())));
EXPECT_FALSE(is<IArithmetic>(std::shared_ptr(E().clone())));

EXPECT_TRUE(is<IArithmetic>(std::const_pointer_cast<const IMathObject>(std::shared_ptr(i.clone()))));
EXPECT_FALSE(is<IArithmetic>(std::const_pointer_cast<const IMathObject>(std::shared_ptr(E().clone()))));

EXPECT_TRUE(is<IArithmetic>(std::reference_wrapper<IMathObject>(i)));
EXPECT_FALSE(is<IArithmetic>(std::reference_wrapper<IMathObject>(c)));

EXPECT_TRUE(is<IArithmetic>(std::reference_wrapper<const IMathObject>(i)));
EXPECT_FALSE(is<IArithmetic>(std::reference_wrapper<const IMathObject>(c)));
}

TEST(MathObjectUtilsTests, castTest) {
EXPECT_NO_THROW(cast<IArithmetic>(i));
EXPECT_THROW(cast<IArithmetic>(cast<IMathObject>(c)), std::bad_cast);

EXPECT_NO_THROW(cast<IArithmetic>(Integer(1)));
EXPECT_THROW(cast<IArithmetic>(cast<IMathObject>(E())), std::bad_cast);

EXPECT_TRUE(cast<IArithmetic>(i.clone().get()));
EXPECT_FALSE(cast<IArithmetic>(E().clone().get()));

EXPECT_TRUE(cast<IArithmetic>(const_cast<const IMathObject *>(i.clone().get())));
EXPECT_FALSE(cast<IArithmetic>(const_cast<const IMathObject *>(E().clone().get())));

EXPECT_TRUE(cast<IArithmetic>(i.clone()));
EXPECT_FALSE(cast<IArithmetic>(E().clone()));

EXPECT_TRUE(cast<IArithmetic>(std::shared_ptr(i.clone())));
EXPECT_FALSE(cast<IArithmetic>(std::shared_ptr(E().clone())));

EXPECT_TRUE(cast<IArithmetic>(std::const_pointer_cast<const IMathObject>(std::shared_ptr(i.clone()))));
EXPECT_FALSE(cast<IArithmetic>(std::const_pointer_cast<const IMathObject>(std::shared_ptr(E().clone()))));
}
// Nothing to test yet
Loading

0 comments on commit 0cde547

Please sign in to comment.