Skip to content

Commit

Permalink
Use boost::hash with Integer
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Feb 5, 2024
1 parent 623b648 commit e611031
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 91 deletions.
6 changes: 5 additions & 1 deletion include/fintamath/functions/powers/Root.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class Rational;
class Real;

class Root final : public IFunctionCRTP<INumber, Root, INumber, INumber> {
using RootToFactorMap = std::map<Integer, Integer>;

public:
Root() = default;

Expand All @@ -41,9 +43,11 @@ class Root final : public IFunctionCRTP<INumber, Root, INumber, INumber> {

static std::unique_ptr<IMathObject> rootSimplify(const Real &lhs, const Integer &rhs);

static std::map<Integer, Integer> roots(const Integer &lhs, const Integer &rhs);
static RootToFactorMap roots(const Integer &lhs, const Integer &rhs);

static std::unique_ptr<IMathObject> perfectRoot(const Integer &lhs, const Integer &rhs);

static Integer extractFirstFactor(RootToFactorMap &rootToFactorMap);
};

FINTAMATH_FUNCTION_EXPRESSION(Root, rootExpr);
Expand Down
6 changes: 6 additions & 0 deletions include/fintamath/numbers/Integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

#include <compare>
#include <concepts>
#include <cstddef>
#include <cstdint>
#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,4 +89,8 @@ class Integer final : public IIntegerCRTP<Integer> {
Backend backend;
};

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

}
10 changes: 7 additions & 3 deletions include/fintamath/numbers/IntegerFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

#include <concepts>
#include <cstddef>
#include <map>
#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>>;

// Use exponentiation by squaring with constant auxiliary memory (iterative version).
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring#With_constant_auxiliary_memory.
template <std::derived_from<INumber> Lhs>
Expand All @@ -27,7 +31,7 @@ Lhs pow(const Lhs &lhs, Integer rhs) {
Lhs sqr = lhs;

while (rhs != 0) {
if ((rhs.toString().back() - '0') % 2 == 0) {
if (rhs % 2 == 0) {
rhs /= 2;
sqr = sqr * sqr;
}
Expand All @@ -54,7 +58,7 @@ Integer factorial(const Integer &rhs);

Integer factorial(const Integer &rhs, size_t order);

std::map<Integer, Integer> factors(Integer rhs, Integer limit = -1);
FactorToCountMap factors(Integer rhs, Integer limit = -1);

Integer combinations(const Integer &totalNumber, const Integer &choosedNumber);

Expand Down
1 change: 1 addition & 0 deletions src/fintamath/expressions/unary/InvTrigExpression.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "fintamath/expressions/unary/InvTrigExpression.hpp"

#include <functional>
#include <map>
#include <string>
#include <unordered_map>
#include <utility>
Expand Down
75 changes: 35 additions & 40 deletions src/fintamath/functions/powers/Root.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "fintamath/functions/powers/Root.hpp"

#include <map>
#include <memory>

#include "fintamath/core/CoreUtils.hpp"
Expand Down Expand Up @@ -77,17 +76,12 @@ std::unique_ptr<IMathObject> Root::rootSimplify(const Integer &lhs, const Intege

ArgumentPtrVector mulChildren;

std::map<Integer, Integer> rootFactors = roots(lhs, rhs);

if (rootFactors.begin()->first == 1) {
if (rootFactors.begin()->second != 1) {
mulChildren.emplace_back(rootFactors.begin()->second.clone());
}

rootFactors.erase(rootFactors.begin());
RootToFactorMap rootToFactorMap = roots(lhs, rhs);
if (const Integer firstFactor = extractFirstFactor(rootToFactorMap); firstFactor != 1) {
mulChildren.emplace_back(firstFactor.clone());
}

for (const auto &[root, factor] : rootFactors) {
for (const auto &[root, factor] : rootToFactorMap) {
if (factor != 1) {
mulChildren.emplace_back(rootExpr(factor, root));
}
Expand All @@ -100,35 +94,35 @@ std::unique_ptr<IMathObject> Root::rootSimplify(const Integer &lhs, const Intege
return mulExpr(mulChildren);
}

std::map<Integer, Integer> Root::roots(const Integer &lhs, const Integer &rhs) {
static Integer factorLimit = pow(Integer(2), 15);
Root::RootToFactorMap Root::roots(const Integer &lhs, const Integer &rhs) {
static const Integer factorLimit = pow(Integer(2), 15);

std::map<Integer, Integer> rootFactors{{1, 1}};
const std::map<Integer, Integer> factorRates = factors(lhs, factorLimit);
const FactorToCountMap factorToCountMap = factors(lhs, factorLimit);
RootToFactorMap rootToFactorMap{{1, 1}};

for (const auto &[factor, rate] : factorRates) {
for (const auto &[factor, rate] : factorToCountMap) {
const Rational power(rate, rhs);

if (power.denominator() == 1) {
rootFactors[1] *= pow(factor, power.numerator());
rootToFactorMap[1] *= pow(factor, power.numerator());
continue;
}

if (power.numerator() > power.denominator()) {
rootFactors[1] *= pow(factor, power.numerator() / power.denominator());
rootToFactorMap[1] *= pow(factor, power.numerator() / power.denominator());
}

const Integer factorMultiplier = pow(factor, power.numerator() % power.denominator());

if (auto rootIter = rootFactors.find(power.denominator()); rootIter != rootFactors.end()) {
if (auto rootIter = rootToFactorMap.find(power.denominator()); rootIter != rootToFactorMap.end()) {
rootIter->second *= factorMultiplier;
}
else {
rootFactors.try_emplace(power.denominator(), factorMultiplier);
rootToFactorMap.try_emplace(power.denominator(), factorMultiplier);
}
}

return rootFactors;
return rootToFactorMap;
}

std::unique_ptr<IMathObject> Root::perfectRoot(const Integer &lhs, const Integer &rhs) {
Expand Down Expand Up @@ -171,32 +165,23 @@ std::unique_ptr<IMathObject> Root::rootSimplify(const Rational &lhs, const Integ
ArgumentPtrVector numeratorChildren;
Rational denominator = 1;

std::map<Integer, Integer> numeratorRootFactors = roots(lhs.numerator(), rhs);
std::map<Integer, Integer> denominatorRootFactors = roots(lhs.denominator(), rhs);

if (numeratorRootFactors.begin()->first == 1) {
if (numeratorRootFactors.begin()->second != 1) {
numeratorChildren.emplace_back(numeratorRootFactors.begin()->second.clone());
}

numeratorRootFactors.erase(numeratorRootFactors.begin());
RootToFactorMap numeratorRootToFactorMap = roots(lhs.numerator(), rhs);
if (const Integer firstFactor = extractFirstFactor(numeratorRootToFactorMap); firstFactor != 1) {
numeratorChildren.emplace_back(firstFactor.clone());
}

if (denominatorRootFactors.begin()->first == 1) {
if (denominatorRootFactors.begin()->second != 1) {
denominator *= denominatorRootFactors.begin()->second;
}

denominatorRootFactors.erase(denominatorRootFactors.begin());
RootToFactorMap denominatorRootToFactorMap = roots(lhs.denominator(), rhs);
if (const Integer firstFactor = extractFirstFactor(denominatorRootToFactorMap); firstFactor != 1) {
denominator *= firstFactor;
}

for (const auto &[root, numeratorFactor] : numeratorRootFactors) {
if (const auto denominatorFactorIter = denominatorRootFactors.find(root);
denominatorFactorIter != denominatorRootFactors.end()) {
for (const auto &[root, numeratorFactor] : numeratorRootToFactorMap) {
if (const auto denominatorFactorIter = denominatorRootToFactorMap.find(root);
denominatorFactorIter != denominatorRootToFactorMap.end()) {

Integer denominatorFactor = denominatorFactorIter->second;

denominatorRootFactors.erase(denominatorFactorIter);
denominatorRootToFactorMap.erase(denominatorFactorIter);

if (denominatorFactor != 1) {
denominator *= denominatorFactor;
Expand All @@ -213,7 +198,7 @@ std::unique_ptr<IMathObject> Root::rootSimplify(const Rational &lhs, const Integ
}
}

for (const auto &[root, denominatorFactor] : denominatorRootFactors) {
for (const auto &[root, denominatorFactor] : denominatorRootToFactorMap) {
if (denominatorFactor != 1) {
denominator *= denominatorFactor;

Expand All @@ -237,4 +222,14 @@ std::unique_ptr<IMathObject> Root::rootSimplify(const Real &lhs, const Integer &
return Pow{}(lhs, Rational(1, rhs));
}

Integer Root::extractFirstFactor(RootToFactorMap &rootToFactorMap) {
if (const auto iter = rootToFactorMap.find(1); iter != rootToFactorMap.end()) {
Integer res = iter->second;
rootToFactorMap.erase(iter);
return res;
}

return 1;
}

}
12 changes: 6 additions & 6 deletions src/fintamath/numbers/IntegerFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ Integer factorial(const Integer &rhs, const size_t order) {
return res;
}

std::map<Integer, Integer> factors(Integer rhs, Integer limit) {
FactorToCountMap factors(Integer rhs, Integer limit) {
if (rhs < 2) {
throw UndefinedFunctionException("factors", {rhs.toString()});
}

std::map<Integer, Integer> factorRates;
FactorToCountMap factorToCountMap;

while (rhs % 2 == 0) {
++factorRates[2];
++factorToCountMap[2];
rhs /= 2;
}

Expand All @@ -115,16 +115,16 @@ std::map<Integer, Integer> factors(Integer rhs, Integer limit) {

for (Integer i = 3; i <= limit; i += 2) {
while (rhs % i == 0) {
++factorRates[i];
++factorToCountMap[i];
rhs = rhs / i;
}
}

if (rhs > 1) {
++factorRates[rhs];
++factorToCountMap[rhs];
}

return factorRates;
return factorToCountMap;
}

// Use number of combinations formula.
Expand Down
82 changes: 41 additions & 41 deletions tests/src/numbers/IntegerFunctionsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,47 +201,47 @@ TEST(IntegerFunctionsTests, nthFactorialTest) {
}

TEST(IntegerFunctionsTests, factorsTest) {
std::map<Integer, Integer> factorRates;

factorRates = factors(2);
EXPECT_EQ(factorRates.size(), 1);
EXPECT_EQ(factorRates[2], 1);

factorRates = factors(32);
EXPECT_EQ(factorRates.size(), 1);
EXPECT_EQ(factorRates[2], 5);

factorRates = factors(144);
EXPECT_EQ(factorRates.size(), 2);
EXPECT_EQ(factorRates[2], 4);
EXPECT_EQ(factorRates[3], 2);

factorRates = factors(123);
EXPECT_EQ(factorRates.size(), 2);
EXPECT_EQ(factorRates[3], 1);
EXPECT_EQ(factorRates[41], 1);

factorRates = factors(Integer("139826427468275632"), 1000);
EXPECT_EQ(factorRates.size(), 5);
EXPECT_EQ(factorRates[2], 4);
EXPECT_EQ(factorRates[7], 1);
EXPECT_EQ(factorRates[17], 1);
EXPECT_EQ(factorRates[31], 1);
EXPECT_EQ(factorRates[Integer("2368975797443")], 1);

factorRates = factors(Integer("139826427468275632"), 10000);
EXPECT_EQ(factorRates[2], 4);
EXPECT_EQ(factorRates[7], 1);
EXPECT_EQ(factorRates[17], 1);
EXPECT_EQ(factorRates[31], 1);
EXPECT_EQ(factorRates[1093], 1);
EXPECT_EQ(factorRates[Integer("2167406951")], 1);

factorRates = factors(Integer("13982642746827562949728"), 1000);
EXPECT_EQ(factorRates.size(), 3);
EXPECT_EQ(factorRates[2], 5);
EXPECT_EQ(factorRates[59], 1);
EXPECT_EQ(factorRates[Integer("7406060776921378681")], 1);
FactorToCountMap factorToCountMap;

factorToCountMap = factors(2);
EXPECT_EQ(factorToCountMap.size(), 1);
EXPECT_EQ(factorToCountMap[2], 1);

factorToCountMap = factors(32);
EXPECT_EQ(factorToCountMap.size(), 1);
EXPECT_EQ(factorToCountMap[2], 5);

factorToCountMap = factors(144);
EXPECT_EQ(factorToCountMap.size(), 2);
EXPECT_EQ(factorToCountMap[2], 4);
EXPECT_EQ(factorToCountMap[3], 2);

factorToCountMap = factors(123);
EXPECT_EQ(factorToCountMap.size(), 2);
EXPECT_EQ(factorToCountMap[3], 1);
EXPECT_EQ(factorToCountMap[41], 1);

factorToCountMap = factors(Integer("139826427468275632"), 1000);
EXPECT_EQ(factorToCountMap.size(), 5);
EXPECT_EQ(factorToCountMap[2], 4);
EXPECT_EQ(factorToCountMap[7], 1);
EXPECT_EQ(factorToCountMap[17], 1);
EXPECT_EQ(factorToCountMap[31], 1);
EXPECT_EQ(factorToCountMap[Integer("2368975797443")], 1);

factorToCountMap = factors(Integer("139826427468275632"), 10000);
EXPECT_EQ(factorToCountMap[2], 4);
EXPECT_EQ(factorToCountMap[7], 1);
EXPECT_EQ(factorToCountMap[17], 1);
EXPECT_EQ(factorToCountMap[31], 1);
EXPECT_EQ(factorToCountMap[1093], 1);
EXPECT_EQ(factorToCountMap[Integer("2167406951")], 1);

factorToCountMap = factors(Integer("13982642746827562949728"), 1000);
EXPECT_EQ(factorToCountMap.size(), 3);
EXPECT_EQ(factorToCountMap[2], 5);
EXPECT_EQ(factorToCountMap[59], 1);
EXPECT_EQ(factorToCountMap[Integer("7406060776921378681")], 1);

EXPECT_THROW(factors(-1), UndefinedFunctionException);
EXPECT_THROW(factors(0), UndefinedFunctionException);
Expand Down

0 comments on commit e611031

Please sign in to comment.