Skip to content

Commit 5ca0ac4

Browse files
authored
Merge pull request #101 from saxbophone/josh/docs-update-v0.4
Docs update v0.4 + efficiency increases
2 parents 02a611d + 5eaf2bb commit 5ca0ac4

File tree

6 files changed

+79
-19
lines changed

6 files changed

+79
-19
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ else() # GCC/Clang warning option
111111
enable_cxx_compiler_flag_if_supported("-Wextra")
112112
# enable sign conversion warnings
113113
enable_cxx_compiler_flag_if_supported("-Wsign-conversion")
114+
# disable unknown-pragma warnings --we have pragmas for some compilers that aren't known by others
115+
enable_cxx_compiler_flag_if_supported("-Wno-unknown-pragmas")
114116
# if tests are enabled, enable converting all warnings to errors too
115117
if (ENABLE_TESTS)
116118
enable_cxx_compiler_flag_if_supported("-Werror")

Doxyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,7 @@ MATHJAX_FORMAT = HTML-CSS
14941494
# The default value is: http://cdn.mathjax.org/mathjax/latest.
14951495
# This tag requires that the tag USE_MATHJAX is set to YES.
14961496

1497-
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.1/es5/startup.js
1497+
MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest
14981498

14991499
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
15001500
# extension names that should be enabled during MathJax rendering. For example

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ using namespace com::saxbophone;
9494
int main() {
9595
// powers of 11
9696
for (unsigned int power = 0; power < 20; power++) {
97-
std::cout << arby::Nat::pow(11, power) << std::endl;
97+
std::cout << arby::pow(11, power) << std::endl;
9898
}
99-
std::cout << arby::Nat::pow(2, 64) << std::endl;
99+
std::cout << arby::pow(2, 64) << std::endl;
100100
// demo of custom literals
101101
using namespace com::saxbophone::arby::literals;
102102
arby::Nat foo = 1234567891011121314151617181920_nat * 77378921_nat;

arby/include/arby/Nat.hpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,14 @@ namespace com::saxbophone::arby {
156156
* @brief Defaulted equality operator for Nat objects
157157
* @param rhs other Nat object to compare against
158158
* @returns `true` if objects are equal, otherwise `false`
159+
* @note Worst-case complexity: @f$ \mathcal{O(n)} @f$
159160
*/
160161
constexpr bool operator==(const Nat& rhs) const = default;
161162
/**
162163
* @brief three-way-comparison operator defines all relational operators
163164
* @param rhs other Nat object to compare against
164165
* @returns std::strong_ordering object for comparison
166+
* @note Complexity: @f$ \mathcal{O(n)} @f$
165167
*/
166168
constexpr auto operator<=>(const Nat& rhs) const {
167169
// use size to indicate ordering if they differ
@@ -297,6 +299,7 @@ namespace com::saxbophone::arby {
297299
/**
298300
* @brief custom ostream operator that allows class Nat to be printed
299301
* with std::cout and friends
302+
* @note Complexity is @f$ \mathcal{O(terrible)} @f$
300303
*/
301304
friend std::ostream& operator<<(std::ostream& os, const Nat& object);
302305
/**
@@ -306,6 +309,8 @@ namespace com::saxbophone::arby {
306309
/**
307310
* @brief prefix increment
308311
* @returns new value of Nat object after incrementing
312+
* @note Best-case complexity: @f$ \mathcal{O(1)} @f$
313+
* @note Worst-case complexity: @f$ \mathcal{O(n)} @f$
309314
*/
310315
constexpr Nat& operator++() {
311316
// increment least significant digit then rollover remaining digits as needed
@@ -325,6 +330,8 @@ namespace com::saxbophone::arby {
325330
/**
326331
* @brief postfix increment
327332
* @returns old value of Nat object before incrementing
333+
* @note Best-case complexity: @f$ \mathcal{O(1)} @f$
334+
* @note Worst-case complexity: @f$ \mathcal{O(n)} @f$
328335
*/
329336
constexpr Nat operator++(int) {
330337
Nat old = *this; // copy old value
@@ -335,10 +342,12 @@ namespace com::saxbophone::arby {
335342
* @brief prefix decrement
336343
* @returns new value of Nat object after decrementing
337344
* @throws std::underflow_error when value of Nat is `0`
345+
* @note Best-case complexity: @f$ \mathcal{O(1)} @f$
346+
* @note Worst-case complexity: @f$ \mathcal{O(n)} @f$
338347
*/
339348
constexpr Nat& operator--() {
340349
// empty digits vector (means value is zero) is a special case
341-
if (_digits.size() == 0) {
350+
if (_digits.empty()) {
342351
throw std::underflow_error("arithmetic underflow: can't decrement unsigned zero");
343352
} else {
344353
// decrement least significant digit then borrow from remaining digits as needed
@@ -360,6 +369,8 @@ namespace com::saxbophone::arby {
360369
* @brief postfix decrement
361370
* @returns old value of Nat object before decrementing
362371
* @throws std::underflow_error when value of Nat is `0`
372+
* @note Best-case complexity: @f$ \mathcal{O(1)} @f$
373+
* @note Worst-case complexity: @f$ \mathcal{O(n)} @f$
363374
*/
364375
constexpr Nat operator--(int) {
365376
Nat old = *this; // copy old value
@@ -371,10 +382,11 @@ namespace com::saxbophone::arby {
371382
* @details Adds other value to this Nat and assigns the result to self
372383
* @param rhs value to add to this Nat
373384
* @returns resulting object after addition-assignment
385+
* @note Complexity: @f$ \mathcal{O(n)} @f$
374386
*/
375387
constexpr Nat& operator+=(Nat rhs) {
376-
// either arg being a zero is a no-op, guard against this
377-
if (_digits.size() != 0 or rhs._digits.size() != 0) {
388+
// both args being zero is a no-op, guard against this
389+
if (not (_digits.empty() and rhs._digits.empty())) {
378390
// make sure this and rhs are the same size, fill with leading zeroes if needed
379391
if (rhs._digits.size() > _digits.size()) {
380392
_digits.push_front(rhs._digits.size() - _digits.size(), 0);
@@ -404,6 +416,7 @@ namespace com::saxbophone::arby {
404416
* @brief Addition operator for Nat
405417
* @param lhs,rhs operands for the addition
406418
* @returns sum of lhs + rhs
419+
* @note Complexity: @f$ \mathcal{O(n)} @f$
407420
*/
408421
friend constexpr Nat operator+(Nat lhs, const Nat& rhs) {
409422
lhs += rhs; // reuse compound assignment
@@ -415,11 +428,12 @@ namespace com::saxbophone::arby {
415428
* @param rhs value to subtract from this Nat
416429
* @returns resulting object after subtraction-assignment
417430
* @throws std::underflow_error when rhs is bigger than this
431+
* @note Complexity: @f$ \mathcal{O(n)} @f$
418432
*/
419433
constexpr Nat& operator-=(Nat rhs) {
420434
// TODO: detect underflow early?
421435
// rhs being a zero is a no-op, guard against this
422-
if (rhs._digits.size() != 0) {
436+
if (not rhs._digits.empty()) {
423437
// make sure this and rhs are the same size, fill with leading zeroes if needed
424438
if (rhs._digits.size() > _digits.size()) {
425439
_digits.push_front(rhs._digits.size() - _digits.size(), 0);
@@ -444,7 +458,7 @@ namespace com::saxbophone::arby {
444458
}
445459
}
446460
// remove any leading zeroes
447-
while (_digits.size() > 0 and _digits.front() == 0) {
461+
while (not _digits.empty() and _digits.front() == 0) {
448462
_digits.pop_front();
449463
}
450464
return *this; // return the result by reference
@@ -454,6 +468,7 @@ namespace com::saxbophone::arby {
454468
* @param lhs,rhs operands for the subtraction
455469
* @returns result of lhs - rhs
456470
* @throws std::underflow_error when rhs is bigger than lhs
471+
* @note Complexity: @f$ \mathcal{O(n)} @f$
457472
*/
458473
friend constexpr Nat operator-(Nat lhs, const Nat& rhs) {
459474
lhs -= rhs; // reuse compound assignment
@@ -464,6 +479,7 @@ namespace com::saxbophone::arby {
464479
* @details Multiplies this Nat by other value and assigns the result to self
465480
* @param rhs value to multiply this Nat by
466481
* @returns resulting object after multiplication-assignment
482+
* @note Complexity: @f$ \mathcal{O(n^2)} @f$
467483
*/
468484
constexpr Nat& operator*=(const Nat& rhs) {
469485
Nat product = *this * rhs; // uses friend *operator
@@ -475,12 +491,13 @@ namespace com::saxbophone::arby {
475491
* @brief Multiplication operator for Nat
476492
* @param lhs,rhs operands for the multiplication
477493
* @returns product of lhs * rhs
494+
* @note Complexity: @f$ \mathcal{O(n^2)} @f$
478495
*/
479496
friend constexpr Nat operator*(const Nat& lhs, const Nat& rhs) {
480497
// init product to zero
481498
Nat product;
482499
// either operand being zero always results in zero, so only run the algorithm if they're both non-zero
483-
if (lhs._digits.size() != 0 and rhs._digits.size() != 0) {
500+
if (not (lhs._digits.empty() or rhs._digits.empty())) {
484501
// multiply each digit from lhs with each digit from rhs
485502
std::size_t l = 0; // manual indices to track which digit we are on,
486503
std::size_t r = 0; // as codlili's iterators are not random-access
@@ -547,10 +564,11 @@ namespace com::saxbophone::arby {
547564
* @param lhs,rhs operands for the division/modulo operation
548565
* @returns pair of {quotient, remainder}
549566
* @throws std::domain_error when rhs is zero
567+
* @todo Work out time-complexity
550568
*/
551569
static constexpr std::pair<Nat, Nat> divmod(const Nat& lhs, const Nat& rhs) {
552570
// division by zero is undefined
553-
if (rhs._digits.size() == 0) {
571+
if (rhs._digits.empty()) {
554572
throw std::domain_error("division by zero");
555573
}
556574
// this will gradually accumulate the calculated quotient
@@ -588,6 +606,7 @@ namespace com::saxbophone::arby {
588606
* @param rhs value to divide this Nat by
589607
* @returns resulting object after division-assignment
590608
* @throws std::domain_error when rhs is zero
609+
* @todo Work out time-complexity
591610
*/
592611
constexpr Nat& operator/=(const Nat& rhs) {
593612
Nat quotient = *this / rhs; // uses friend /operator
@@ -600,6 +619,7 @@ namespace com::saxbophone::arby {
600619
* @note This implements floor-division, returning the quotient only
601620
* @param lhs,rhs operands for the division
602621
* @returns quotient of lhs / rhs
622+
* @todo Work out time-complexity
603623
*/
604624
friend constexpr Nat operator/(Nat lhs, const Nat& rhs) {
605625
Nat quotient;
@@ -613,6 +633,7 @@ namespace com::saxbophone::arby {
613633
* @param rhs value to modulo-divide this Nat by
614634
* @returns resulting object after modulo-assignment
615635
* @throws std::domain_error when rhs is zero
636+
* @todo Work out time-complexity
616637
*/
617638
constexpr Nat& operator%=(const Nat& rhs) {
618639
Nat remainder = *this % rhs; // uses friend %operator
@@ -626,6 +647,7 @@ namespace com::saxbophone::arby {
626647
* @param lhs,rhs operands for the division
627648
* @returns remainder of lhs / rhs
628649
* @throws std::domain_error when rhs is zero
650+
* @todo Work out time-complexity
629651
*/
630652
friend constexpr Nat operator%(Nat lhs, const Nat& rhs) {
631653
Nat remainder;
@@ -634,6 +656,7 @@ namespace com::saxbophone::arby {
634656
}
635657
/**
636658
* @brief bitwise OR-assignment
659+
* @note Complexity: @f$ \mathcal{O(n)} @f$
637660
*/
638661
constexpr Nat& operator|=(const Nat& rhs) {
639662
// add additional digits to this if fewer than rhs
@@ -655,22 +678,26 @@ namespace com::saxbophone::arby {
655678
}
656679
/**
657680
* @brief bitwise OR operator for Nat
681+
* @note Complexity: @f$ \mathcal{O(n)} @f$
658682
*/
659683
friend constexpr Nat operator|(Nat lhs, const Nat& rhs) {
660684
lhs |= rhs; // reuse member operator
661685
return lhs;
662686
}
663687
/**
664688
* @brief bitwise AND-assignment
689+
* @note Complexity: @f$ \mathcal{O(n)} @f$
665690
*/
666691
constexpr Nat& operator&=(const Nat& rhs) {
667692
/*
668693
* if rhs has fewer digits than this, we can remove this' leading
669694
* digits because they would be AND'ed with implicit zero which is
670695
* always zero
671696
*/
672-
if (rhs._digits.size() < _digits.size()) {
673-
while (_digits.size() > rhs._digits.size()) {
697+
std::size_t lhs_size = _digits.size();
698+
std::size_t rhs_size = rhs._digits.size();
699+
if (lhs_size > rhs_size) {
700+
for (std::size_t i = 0; i < lhs_size - rhs_size; i++) {
674701
_digits.pop_front();
675702
}
676703
}
@@ -686,20 +713,22 @@ namespace com::saxbophone::arby {
686713
*it &= *rhs_it;
687714
}
688715
// remove any leading zeroes
689-
while (_digits.size() > 0 and _digits.front() == 0) {
716+
while (not _digits.empty() and _digits.front() == 0) {
690717
_digits.pop_front();
691718
}
692719
return *this;
693720
}
694721
/**
695722
* @brief bitwise AND operator for Nat
723+
* @note Complexity: @f$ \mathcal{O(n)} @f$
696724
*/
697725
friend constexpr Nat operator&(Nat lhs, const Nat& rhs) {
698726
lhs &= rhs; // reuse member operator
699727
return lhs;
700728
}
701729
/**
702730
* @brief bitwise XOR-assignment
731+
* @note Complexity: @f$ \mathcal{O(n)} @f$
703732
*/
704733
constexpr Nat& operator^=(const Nat& rhs) {
705734
Nat result = *this ^ rhs; // reuse friend function
@@ -709,6 +738,7 @@ namespace com::saxbophone::arby {
709738
}
710739
/**
711740
* @brief bitwise XOR operator for Nat
741+
* @note Complexity: @f$ \mathcal{O(n)} @f$
712742
*/
713743
friend constexpr Nat operator^(Nat lhs, const Nat& rhs) {
714744
Nat result;
@@ -764,6 +794,7 @@ namespace com::saxbophone::arby {
764794
/**
765795
* @brief contextual conversion to bool (behaves same way as int)
766796
* @returns `false` when value is `0`, otherwise `true`
797+
* @note Complexity is @f$ \mathcal{O(n)} @f$ but should be @f$ \mathcal{O(1)} @f$
767798
*/
768799
explicit constexpr operator bool() const {
769800
// zero is false --all other values are true

arby/include/arby/math.hpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ namespace com::saxbophone::arby {
2929
/**
3030
* @returns base raised to the power of exponent
3131
* @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.
32+
* @todo Work out time-complexity
3833
* @relatedalso Nat
3934
*/
4035
constexpr Nat pow(const Nat& base, const Nat& exponent) {
@@ -67,6 +62,8 @@ namespace com::saxbophone::arby {
6762
* @param x value to use for \f$x\f$
6863
* @throws std::domain_error when \f$b<2\f$
6964
* @throws std::domain_error when \f$x<1\f$
65+
* @todo Work out time-complexity
66+
* @relatedalso Nat
7067
*/
7168
constexpr std::pair<Nat, Nat> ilog(const Nat& base, const Nat& x) {
7269
if (base < 2) {

tests/Nat/basic_arithmetic.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,21 @@ TEST_CASE("Addition of arby::Nat and much smaller arby::Nat", "[basic-arithmetic
287287
CHECK((uintmax_t)result == addition);
288288
}
289289

290+
TEST_CASE("arby::Nat + 0") {
291+
arby::Nat value = GENERATE((uintmax_t)0, std::numeric_limits<uintmax_t>::max());
292+
293+
CHECK((value + 0) == value);
294+
}
295+
296+
TEST_CASE("arby::Nat += 0") {
297+
uintmax_t value = GENERATE((uintmax_t)0, std::numeric_limits<uintmax_t>::max());
298+
arby::Nat object = value;
299+
300+
object += 0;
301+
302+
CHECK((uintmax_t)object == value);
303+
}
304+
290305
TEST_CASE("Assignment-subtraction of arby::Nat from arby::Nat", "[basic-arithmetic]") {
291306
auto minuend = GENERATE(take(100, random((uintmax_t)1, std::numeric_limits<uintmax_t>::max())));
292307
// ensure subtrahend is never bigger than minuend so we don't underflow
@@ -384,3 +399,18 @@ TEST_CASE("Subtraction of arby::Nat(0) from arby::Nat(0)", "[basic-arithmetic]")
384399

385400
CHECK((uintmax_t)(lhs - rhs) == 0);
386401
}
402+
403+
TEST_CASE("arby::Nat - 0") {
404+
arby::Nat value = GENERATE((uintmax_t)0, std::numeric_limits<uintmax_t>::max());
405+
406+
CHECK((value - 0) == value);
407+
}
408+
409+
TEST_CASE("arby::Nat -= 0") {
410+
uintmax_t value = GENERATE((uintmax_t)0, std::numeric_limits<uintmax_t>::max());
411+
arby::Nat object = value;
412+
413+
object -= 0;
414+
415+
CHECK((uintmax_t)object == value);
416+
}

0 commit comments

Comments
 (0)