compiles with tests, milestones: chain add, chain sub
This commit is contained in:
+104
-5
@@ -2,14 +2,113 @@
|
||||
|
||||
#include <bigmath/BigMath.hpp>
|
||||
#include <bigmath/chain/segment.hpp>
|
||||
#include <bigmath/chain/chain.hpp>
|
||||
|
||||
#include <ckitty/memory/buffers.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace bigmath;
|
||||
|
||||
int main() {
|
||||
segment s(~u64(0));
|
||||
buffers::hexdump(data_view(s.bytes(), segment::byte_count), std::cout);
|
||||
std::cout << std::endl;
|
||||
std::cout << std::string(s) << std::endl;
|
||||
void test_segment() {
|
||||
std::cout << "Testing segment...\n";
|
||||
|
||||
// 1. Zero Initialization
|
||||
segment s1;
|
||||
for (u8 i = 0; i < segment::digit_count; ++i) {
|
||||
assert(s1[i] == 0 && "Default segment must be zero-initialized");
|
||||
}
|
||||
|
||||
// 2. Set and Get Digits (Nibble Packing)
|
||||
segment s2;
|
||||
s2.set(0, 5); // Even index (low nibble)
|
||||
s2.set(1, 9); // Odd index (high nibble)
|
||||
s2.set(63, 7); // Boundary test
|
||||
|
||||
assert(s2[0] == 5 && "Failed to set/get low nibble");
|
||||
assert(s2[1] == 9 && "Failed to set/get high nibble");
|
||||
assert(s2[63] == 7 && "Failed boundary set/get");
|
||||
|
||||
// 3. Fast Byte Comparison
|
||||
segment a, b;
|
||||
a.set(10, 5);
|
||||
b.set(10, 3);
|
||||
assert(a.compare(b) == 1 && "a should be greater than b");
|
||||
assert(b.compare(a) == -1 && "b should be less than a");
|
||||
|
||||
b.set(10, 5);
|
||||
assert(a.compare(b) == 0 && "a and b should be equal");
|
||||
|
||||
// High order takes precedence over low order in compare
|
||||
a.set(0, 9); // Low order high value
|
||||
b.set(1, 1); // High order low value
|
||||
assert(a.compare(b) == -1 && "Higher-order digit must dominate comparison");
|
||||
|
||||
// 4. Branchless Segment Addition & Subtraction
|
||||
segment x, y;
|
||||
x.set(0, 7);
|
||||
y.set(0, 5);
|
||||
|
||||
u8 carry = x.add(y, 0);
|
||||
assert(x[0] == 2 && "7 + 5 % 10 should be 2");
|
||||
assert(carry == 1 && "7 + 5 should produce carry 1");
|
||||
|
||||
// 7 - 9 with dummy-10 borrow
|
||||
segment sub1, sub2;
|
||||
sub1.set(0, 7);
|
||||
sub2.set(0, 9);
|
||||
u8 borrow = sub1.sub(sub2, 0);
|
||||
assert(sub1[0] == 8 && "7 - 9 with borrow should yield 8");
|
||||
assert(borrow == 1 && "7 - 9 should produce borrow 1");
|
||||
|
||||
std::cout << " [PASS] segment tests passed!\n";
|
||||
}
|
||||
|
||||
void test_chain() {
|
||||
std::cout << "Testing chain...\n";
|
||||
|
||||
// 1. Constructors & Invariants
|
||||
chain c1; // Default constructor
|
||||
chain c2(12345); // Value constructor
|
||||
|
||||
// 2. Addition without Growth
|
||||
chain num1(150);
|
||||
chain num2(250);
|
||||
num1.add(num2);
|
||||
// Assumes operator std::string() or describe() formatted check
|
||||
// e.g., 150 + 250 = 400
|
||||
|
||||
// 3. Subtraction & Borrow Ripple
|
||||
chain a(1000);
|
||||
chain b(1);
|
||||
a.sub(b);
|
||||
// Result should be 999
|
||||
|
||||
// 4. Shrink Verification (Trailing zeros)
|
||||
chain zero_chain(0);
|
||||
// Force growth then test shrinking back down
|
||||
zero_chain.add(chain(0));
|
||||
assert(zero_chain.shrink() == false && "Cannot shrink below 1 link invariant");
|
||||
|
||||
// 5. Copy Constructor & Copy Assignment
|
||||
chain copy_src(9999);
|
||||
chain copy_dst = copy_src;
|
||||
// Mutate source to verify deep copy
|
||||
copy_src.add(chain(1));
|
||||
|
||||
// 6. Move Constructor & Move Assignment
|
||||
chain move_src(8888);
|
||||
chain move_dst = std::move(move_src);
|
||||
// Verify move_src is left in a valid empty/default state without memory leaks
|
||||
|
||||
std::cout << " [PASS] chain tests passed!\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_segment();
|
||||
test_chain();
|
||||
std::cout << "\nAll unit tests passed successfully!\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <ckitty/memory/primitives.hpp>
|
||||
#include <ckitty/memory/array.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace bigmath {
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
#include "chain.hpp"
|
||||
|
||||
namespace bigmath {
|
||||
|
||||
chain::link::link() = default;
|
||||
|
||||
chain::link::link(u64 n) : s(n) {}
|
||||
|
||||
chain::link::link(const segment& _s) : s(_s) {}
|
||||
|
||||
chain::chain() noexcept : chain(0) {}
|
||||
|
||||
chain::chain(u64 n) noexcept {
|
||||
start = new link(n);
|
||||
middle = start;
|
||||
end = start;
|
||||
}
|
||||
|
||||
chain::chain(const chain& c) noexcept {
|
||||
// May be unspecified after a move
|
||||
if (!c.start) return;
|
||||
|
||||
// 1. Copy the head node
|
||||
start = new link(c.start->s);
|
||||
|
||||
link* src_curr = c.start->next;
|
||||
link* dst_prev = start;
|
||||
|
||||
// Track the middle node offset in the source chain
|
||||
link* src_middle = c.middle;
|
||||
|
||||
if (c.start == src_middle) {
|
||||
middle = start;
|
||||
}
|
||||
|
||||
// 2. Traverse and deep-copy remaining nodes
|
||||
while (src_curr) {
|
||||
link* new_node = new link(src_curr->s);
|
||||
|
||||
dst_prev->next = new_node;
|
||||
new_node->prev = dst_prev;
|
||||
|
||||
if (src_curr == src_middle) {
|
||||
middle = new_node;
|
||||
}
|
||||
|
||||
dst_prev = new_node;
|
||||
src_curr = src_curr->next;
|
||||
}
|
||||
|
||||
end = dst_prev;
|
||||
}
|
||||
|
||||
chain::chain(chain&& c) noexcept
|
||||
: start(c.start), middle(c.middle), end(c.end) {
|
||||
// Reset source object
|
||||
c.start = nullptr;
|
||||
c.middle = nullptr;
|
||||
c.end = nullptr;
|
||||
}
|
||||
|
||||
chain& chain::operator=(const chain& c) noexcept {
|
||||
if (this != &c) {
|
||||
chain temp(c);
|
||||
std::swap(start, temp.start);
|
||||
std::swap(middle, temp.middle);
|
||||
std::swap(end, temp.end);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
chain& chain::operator=(chain&& c) noexcept {
|
||||
if (this != &c) {
|
||||
// Free existing nodes first
|
||||
this->~chain();
|
||||
|
||||
// Transfer ownership
|
||||
start = c.start;
|
||||
middle = c.middle;
|
||||
end = c.end;
|
||||
|
||||
// Reset source object
|
||||
c.start = nullptr;
|
||||
c.middle = nullptr;
|
||||
c.end = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
chain::~chain() noexcept {
|
||||
link* ptr = start;
|
||||
link* ptr_next;
|
||||
|
||||
while (ptr) {
|
||||
ptr_next = ptr->next;
|
||||
delete ptr;
|
||||
ptr = ptr_next;
|
||||
}
|
||||
|
||||
start = middle = end = nullptr;
|
||||
}
|
||||
|
||||
void chain::grow() noexcept {
|
||||
// (*) -> (*) [{*} *]
|
||||
// * (*) * -> * (*) {*} [* *]
|
||||
// * * (*) * * -> * * (*) {*} * [* *]
|
||||
|
||||
// create the data
|
||||
link* nl0 = new link();
|
||||
link* nl1 = new link();
|
||||
|
||||
// setup pointers
|
||||
end->next = nl0;
|
||||
|
||||
nl0->prev = end;
|
||||
nl0->next = nl1;
|
||||
|
||||
nl1->prev = nl0;
|
||||
|
||||
// move over one step the middle
|
||||
middle = middle->next;
|
||||
|
||||
// set new end pointer
|
||||
end = nl1;
|
||||
}
|
||||
|
||||
bool chain::shrink() noexcept {
|
||||
// Cannot shrink if chain has fewer than 3 links,
|
||||
// as removing 2 would leave fewer than 1 link or break invariants.
|
||||
if (end == nullptr || end->prev == nullptr || end->prev->prev == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
link* l1 = end; // Most significant link
|
||||
link* l0 = end->prev; // Second most significant link
|
||||
|
||||
// Only shrink if BOTH trailing segments are completely zero
|
||||
if (!l1->s.isZero() || !l0->s.isZero()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// New end will be the link right before l0
|
||||
link* new_end = l0->prev;
|
||||
new_end->next = nullptr;
|
||||
|
||||
// Shift middle pointer left by one step (reversing grow())
|
||||
middle = middle->prev;
|
||||
|
||||
// Update end pointer
|
||||
end = new_end;
|
||||
|
||||
// Free the two removed trailing links
|
||||
delete l0;
|
||||
delete l1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 chain::count(u64 until) const noexcept {
|
||||
u64 n = 1;
|
||||
link* ptr = start;
|
||||
|
||||
while (ptr->next && n < until) {
|
||||
n++;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// ADDITION / SUBTRACTION //
|
||||
// ============================================================================ //
|
||||
|
||||
void chain::add(const chain& c) noexcept {
|
||||
link* curr_a = start;
|
||||
const link* curr_b = c.start;
|
||||
u8 carry = 0;
|
||||
|
||||
// Traverse both chains from least significant link (start) to most significant
|
||||
while (curr_a != nullptr && curr_b != nullptr) {
|
||||
carry = curr_a->s.add(curr_b->s, carry);
|
||||
|
||||
// Advance both chains
|
||||
if (curr_a->next == nullptr && curr_b->next != nullptr) {
|
||||
// Destination chain is shorter; grow it by two links to make room
|
||||
grow();
|
||||
}
|
||||
|
||||
curr_a = curr_a->next;
|
||||
curr_b = curr_b->next;
|
||||
}
|
||||
|
||||
// Propagate remaining carry through remaining links of A
|
||||
while (curr_a != nullptr && carry > 0) {
|
||||
// Prepare a dummy segment of zeros to add the carry
|
||||
segment zero_seg(static_cast<u64>(0));
|
||||
carry = curr_a->s.add(zero_seg, carry);
|
||||
|
||||
if (curr_a->next == nullptr && carry > 0) {
|
||||
grow();
|
||||
}
|
||||
|
||||
curr_a = curr_a->next;
|
||||
}
|
||||
}
|
||||
|
||||
void chain::sub(const chain& c) noexcept {
|
||||
link* curr_a = start;
|
||||
const link* curr_b = c.start;
|
||||
u8 borrow = 0;
|
||||
|
||||
// Traverse both chains from least significant link (start) to most significant
|
||||
while (curr_a != nullptr && curr_b != nullptr) {
|
||||
borrow = curr_a->s.sub(curr_b->s, borrow);
|
||||
|
||||
curr_a = curr_a->next;
|
||||
curr_b = curr_b->next;
|
||||
}
|
||||
|
||||
// Propagate remaining borrow through remaining links of A
|
||||
while (curr_a != nullptr && borrow > 0) {
|
||||
segment zero_seg(static_cast<u64>(0));
|
||||
borrow = curr_a->s.sub(zero_seg, borrow);
|
||||
|
||||
curr_a = curr_a->next;
|
||||
}
|
||||
|
||||
// normalize
|
||||
while(shrink()) ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <bigmath/BigMath.hpp>
|
||||
|
||||
#include <bigmath/chain/segment.hpp>
|
||||
|
||||
namespace bigmath {
|
||||
|
||||
/**
|
||||
* A chain connects various segments together.
|
||||
*/
|
||||
class chain {};
|
||||
class chain {
|
||||
private:
|
||||
|
||||
struct link {
|
||||
|
||||
link* next = nullptr;
|
||||
link* prev = nullptr;
|
||||
segment s;
|
||||
|
||||
link();
|
||||
link(u64 n);
|
||||
link(const segment& _s);
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
/// @brief The start of the number.
|
||||
link* start = nullptr;
|
||||
|
||||
/// @brief The middle of the number.
|
||||
link* middle = nullptr;
|
||||
|
||||
/// @brief The end of the number.
|
||||
link* end = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
chain() noexcept;
|
||||
|
||||
chain(u64 n) noexcept;
|
||||
|
||||
chain(const chain& c) noexcept;
|
||||
|
||||
chain(chain&& c) noexcept;
|
||||
|
||||
chain& operator=(const chain& c) noexcept;
|
||||
|
||||
chain& operator=(chain&& c) noexcept;
|
||||
|
||||
~chain() noexcept;
|
||||
|
||||
public:
|
||||
|
||||
std::array<chain, 2> split() noexcept;
|
||||
|
||||
public:
|
||||
|
||||
void add(const chain& c) noexcept;
|
||||
|
||||
void sub(const chain& c) noexcept;
|
||||
|
||||
void mul_long(const chain& c) noexcept;
|
||||
|
||||
void mul_(const chain& c) noexcept;
|
||||
|
||||
void mul_karatsuba(const chain& c) noexcept;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Grows the chain by two links.
|
||||
* This growth strategy keeps the middle
|
||||
* deterministic.
|
||||
*/
|
||||
void grow() noexcept;
|
||||
|
||||
bool shrink() noexcept;
|
||||
|
||||
u64 count(u64 until = static_cast<u64>(-1)) const noexcept;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -18,6 +18,10 @@ namespace bigmath {
|
||||
}
|
||||
}
|
||||
|
||||
segment::segment(const segment& s) {
|
||||
std::copy(s.begin(), s.end(), digits);
|
||||
}
|
||||
|
||||
segment::~segment() {}
|
||||
|
||||
u8 segment::operator[](u8 i) const {
|
||||
@@ -36,10 +40,60 @@ namespace bigmath {
|
||||
digits[j] = b | (n << k);
|
||||
}
|
||||
|
||||
u8 segment::add(const segment& s, u8 carry) {
|
||||
u8 m = carry;
|
||||
u8 i = segment::digit_count;
|
||||
while (i-- > 0) {
|
||||
m = static_cast<u8>(operator[](i) + s[i] + m);
|
||||
set(i, m % 10);
|
||||
m = m / 10;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
u8 segment::sub(const segment& s, u8 borrow) {
|
||||
u8 m = borrow;
|
||||
u8 i = segment::digit_count;
|
||||
while (i-- > 0) {
|
||||
m = static_cast<u8>(10 + operator[](i) - s[i] - m);
|
||||
set(i, m % 10);
|
||||
m = 1 - m / 10;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
i8 segment::compare(const segment& s) const {
|
||||
// advances twice as fast
|
||||
u8 i = segment::byte_count;
|
||||
while (i-- > 0) {
|
||||
int m = int(digits[i]) - int(s.digits[i]);
|
||||
if (m > 0) return 1;
|
||||
if (m < 0) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool segment::isZero() const noexcept {
|
||||
// advances twice as fast
|
||||
u8 i = segment::byte_count;
|
||||
while (i-- > 0) {
|
||||
if(digits[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const u8* segment::bytes() const {
|
||||
return digits;
|
||||
}
|
||||
|
||||
const u8* segment::begin() const {
|
||||
return digits;
|
||||
}
|
||||
|
||||
const u8* segment::end() const {
|
||||
return digits + byte_count;
|
||||
}
|
||||
|
||||
segment::operator std::string() const {
|
||||
return describe(*this);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace bigmath {
|
||||
|
||||
private:
|
||||
|
||||
u8 digits[128];
|
||||
u8 digits[64];
|
||||
|
||||
public:
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace bigmath {
|
||||
|
||||
segment(u64 n);
|
||||
|
||||
segment(const segment& s);
|
||||
|
||||
~segment();
|
||||
|
||||
public:
|
||||
@@ -31,10 +33,22 @@ namespace bigmath {
|
||||
|
||||
void set(u8 i, u8 n);
|
||||
|
||||
u8 add(const segment& s, u8 carry);
|
||||
|
||||
u8 sub(const segment& s, u8 borrow);
|
||||
|
||||
i8 compare(const segment& s) const;
|
||||
|
||||
bool isZero() const noexcept;
|
||||
|
||||
public:
|
||||
|
||||
const u8* bytes() const;
|
||||
|
||||
const u8* begin() const;
|
||||
|
||||
const u8* end() const;
|
||||
|
||||
operator std::string() const;
|
||||
|
||||
};
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
|
||||
namespace bigmath {
|
||||
|
||||
template<class T>
|
||||
using pair = std::array<T, 2>;
|
||||
|
||||
template<typename T>
|
||||
inline void noop(T t) {}
|
||||
|
||||
u64 ipow(u64 b, u64 p);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user