Skip to content

Commit 2ea4e22

Browse files
authored
Merge pull request #95 from saxbophone/josh/math-support
Move pow to math support library and added ilog
2 parents 9860a0c + ba8ddd2 commit 2ea4e22

File tree

17 files changed

+223
-54
lines changed

17 files changed

+223
-54
lines changed

Doxyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1471,7 +1471,7 @@ FORMULA_TRANSPARENT = YES
14711471
# The default value is: NO.
14721472
# This tag requires that the tag GENERATE_HTML is set to YES.
14731473

1474-
USE_MATHJAX = NO
1474+
USE_MATHJAX = YES
14751475

14761476
# When MathJax is enabled you can set the default output format to be used for
14771477
# the MathJax output. See the MathJax site (see:

arby/include/arby/Nat.hpp

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* @file
3-
* @brief This file forms part of arby
3+
* @brief Nat class supporting arbitrary-size unsigned (Natural) integers
4+
* @note This file forms part of arby
45
* @details arby is a C++ library providing arbitrary-precision integer types
56
* @warning arby is alpha-quality software
67
*
@@ -123,7 +124,8 @@ namespace com::saxbophone::arby {
123124

124125
/**
125126
* @brief Arbitrary-precision unsigned integer type
126-
* @details This is named after 𝐍, the set of Natural numbers, which this type models
127+
* @details This is named after \f$\mathbb{N}\f$, the set of Natural numbers,
128+
* which this type models
127129
* @note `std::numeric_limits<Nat>` is specialised such that most of the
128130
* members of that type are implemented to describe the traits of this type.
129131
* @note Exceptions include any members of std::numeric_limits<> which
@@ -598,38 +600,6 @@ namespace com::saxbophone::arby {
598600
std::tie(std::ignore, remainder) = Nat::divmod(lhs, rhs);
599601
return remainder;
600602
}
601-
/**
602-
* @returns base raised to the power of exponent
603-
* @param base,exponent parameters for the base and exponent
604-
* @todo This currently uses a divide-and-conquer approach that divides
605-
* exponent by 2 each time, for a binary-recursion on the order of
606-
* \f$\mathcal{O}(n\log{}n)\f$. This is fine, but it would be nice to
607-
* see if we can make incremental improvements to the optimisation by
608-
* using factors, logarithms or something else to divide the exponent
609-
* into more than 2 chunks at each level of recursion.
610-
*/
611-
static constexpr Nat pow(const Nat& base, const Nat& exponent) {
612-
// use divide-and-conquer recursion to break up huge powers into products of smaller powers
613-
// exponent = 0 is our base case to terminate the recursion
614-
if (exponent == 0) {
615-
return 1;
616-
} else if (exponent == 1) {
617-
// exponent = 1 is an additional base case mainly to prevent a redundant level of recursion to 0
618-
return base;
619-
} else if (exponent == 2) {
620-
// exponent = 2 is our final base case, as it seems a waste to leave it to the catch-all case below
621-
return base * base;
622-
}
623-
auto [quotient, remainder] = Nat::divmod(exponent, 2);
624-
// instead of calculating x^n, do x^(n/2)
625-
Nat power = Nat::pow(base, quotient);
626-
power *= power;
627-
// and multiply by base again if n was odd
628-
if (remainder == 1) {
629-
power *= base;
630-
}
631-
return power;
632-
}
633603
// XXX: unimplemented shift operators commented out until implemented
634604
// // left-shift-assignment
635605
// constexpr Nat& operator<<=(const Nat& n) {

arby/include/arby/math.hpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @file
3+
* @brief cmath-like math support functions
4+
* @note This file forms part of arby
5+
* @details arby is a C++ library providing arbitrary-precision integer types
6+
* @warning arby is alpha-quality software
7+
*
8+
* @author Joshua Saxby <[email protected]>
9+
* @date May 2022
10+
*
11+
* @copyright Copyright Joshua Saxby <[email protected]> 2022
12+
*
13+
* @copyright
14+
* This Source Code Form is subject to the terms of the Mozilla Public
15+
* License, v. 2.0. If a copy of the MPL was not distributed with this
16+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
17+
*/
18+
19+
#ifndef COM_SAXBOPHONE_ARBY_MATH_HPP
20+
#define COM_SAXBOPHONE_ARBY_MATH_HPP
21+
22+
#include <stdexcept>
23+
#include <utility>
24+
25+
#include <arby/Nat.hpp>
26+
27+
28+
namespace com::saxbophone::arby {
29+
/**
30+
* @returns base raised to the power of exponent
31+
* @param base,exponent parameters for the base and exponent
32+
* @todo This currently uses a divide-and-conquer approach that divides
33+
* exponent by 2 each time, for a binary-recursion on the order of
34+
* \f$\mathcal{O}(n\log{}n)\f$. This is fine, but it would be nice to
35+
* see if we can make incremental improvements to the optimisation by
36+
* using factors, logarithms or something else to divide the exponent
37+
* into more than 2 chunks at each level of recursion.
38+
* @relatedalso Nat
39+
*/
40+
constexpr Nat pow(const Nat& base, const Nat& exponent) {
41+
// use divide-and-conquer recursion to break up huge powers into products of smaller powers
42+
// exponent = 0 is our base case to terminate the recursion
43+
if (exponent == 0) {
44+
return 1;
45+
} else if (exponent == 1) {
46+
// exponent = 1 is an additional base case mainly to prevent a redundant level of recursion to 0
47+
return base;
48+
} else if (exponent == 2) {
49+
// exponent = 2 is our final base case, as it seems a waste to leave it to the catch-all case below
50+
return base * base;
51+
}
52+
auto [quotient, remainder] = Nat::divmod(exponent, 2);
53+
// instead of calculating x^n, do x^(n/2)
54+
Nat power = pow(base, quotient);
55+
power *= power;
56+
// and multiply by base again if n was odd
57+
if (remainder == 1) {
58+
power *= base;
59+
}
60+
return power;
61+
}
62+
/**
63+
* @brief Calculates integer log of `x` in `base`
64+
* @returns pair of \f$floor(log_b(x)),\ ceil(log_b(x))\f$ for the given
65+
* base \f$b\f$ and \f$x\f$
66+
* @param base base to use for \f$b\f$
67+
* @param x value to use for \f$x\f$
68+
* @throws std::domain_error when \f$b<2\f$
69+
* @throws std::domain_error when \f$x<1\f$
70+
*/
71+
constexpr std::pair<Nat, Nat> ilog(const Nat& base, const Nat& x) {
72+
if (base < 2) {
73+
throw std::domain_error("ilog: base cannot be < 2");
74+
} else if (x < 1) {
75+
throw std::domain_error("ilog: x cannot be < 1");
76+
}
77+
// find the smallest power of base that is just >= than x
78+
Nat power = 1;
79+
Nat floor = 0;
80+
Nat exponent = 0;
81+
while (power < x) {
82+
power *= base;
83+
floor = exponent++; // increment and store old value in floor
84+
}
85+
// floor = ceil when power = x
86+
return {power == x ? exponent : floor, exponent};
87+
}
88+
}
89+
90+
#endif // include guard

tests/CMakeLists.txt

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,40 @@ CPMFindPackage(
55
EXCLUDE_FROM_ALL YES
66
)
77

8+
# common option-propagating target to use for test suite sub-targets
9+
add_library(tests-config INTERFACE)
10+
target_link_libraries(
11+
tests-config
12+
INTERFACE
13+
arby-compiler-options # tests use same compiler options as main project
14+
arby
15+
Catch2::Catch2 # unit testing framework
16+
)
17+
18+
# every sub-part of the test suite
19+
add_subdirectory(math_support)
20+
add_subdirectory(Nat)
21+
22+
# test executable wraps everything together
823
add_executable(tests)
924
target_sources(
1025
tests PRIVATE
1126
main.cpp
12-
basic_arithmetic.cpp
13-
casting.cpp
14-
divmod.cpp
15-
misc.cpp
16-
multiplication.cpp
17-
namespaces.cpp
18-
pow.cpp
19-
self_assignment.cpp
20-
stringification.cpp
21-
user_defined_literals.cpp
27+
$<TARGET_OBJECTS:math-support>
28+
$<TARGET_OBJECTS:Nat>
2229
)
2330
target_link_libraries(
24-
tests
25-
PRIVATE
31+
tests PRIVATE
2632
arby-compiler-options # tests use same compiler options as main project
2733
arby
2834
Catch2::Catch2 # unit testing framework
2935
)
30-
target_precompile_headers(tests PRIVATE <arby/Nat.hpp>)
3136

3237
enable_testing()
3338

3439
# auto-discover and add Catch2 tests from unit tests program
3540
include(CTest)
3641
include(Catch)
3742

43+
# TODO: we might consider having one separate test executable per part, but not for now
3844
catch_discover_tests(tests WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")

tests/Nat/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
add_library(
2+
Nat OBJECT
3+
basic_arithmetic.cpp
4+
casting.cpp
5+
divmod.cpp
6+
misc.cpp
7+
multiplication.cpp
8+
namespaces.cpp
9+
self_assignment.cpp
10+
stringification.cpp
11+
user_defined_literals.cpp
12+
)
13+
target_link_libraries(Nat PRIVATE tests-config)
14+
target_precompile_headers(Nat PRIVATE <arby/Nat.hpp>)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)