Skip to content

Commit 1f6bb96

Browse files
authored
Minor improvements to performance and Logic of Addition (#3)
* Add l-value and r-value versions of negate and abs * Fix negate bug not flipping sign of negative * Switch to loop based add instead of transform * Add a negation test * Split performance tests into individual tests * add default value to is_neg * Add guards to trim and no longer use a temp * Swap to subtraction instead of division * Remove unnecessary trim * Move zero and sign logic around * add comment to random
1 parent c0e598a commit 1f6bb96

2 files changed

Lines changed: 187 additions & 99 deletions

File tree

bigint.h

Lines changed: 90 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ namespace BigInt {
157157

158158
bigint operator+=(const bigint &rhs)
159159
{
160-
*this = add(*this, rhs);
160+
if (*this == 0 && rhs == 0) return *this;
161+
if (*this == 0) {*this = rhs; return *this;}
162+
if (rhs != 0) {
163+
*this = add(*this, rhs);
164+
}
161165
return *this;
162166
}
163167

@@ -246,6 +250,21 @@ namespace BigInt {
246250
return tmp;
247251
}
248252

253+
bigint operator-() const &
254+
{
255+
bigint temp = *this;
256+
if (temp == 0) return temp;
257+
temp.is_neg = !this->is_neg;
258+
return temp;
259+
}
260+
261+
bigint operator-() &&
262+
{
263+
if (*this == 0) return *this;
264+
this->is_neg = !this->is_neg;
265+
return *this;
266+
}
267+
249268
friend bool operator==(const bigint &l, const bigint &r)
250269
{
251270
if (l.is_neg != r.is_neg)
@@ -300,14 +319,17 @@ namespace BigInt {
300319

301320
static bigint abs(const bigint &s)
302321
{
303-
if (is_negative(s))
304-
{
305-
bigint temp = s;
306-
temp.is_neg = false;
322+
if (!is_negative(s)) return s;
307323

308-
return temp;
309-
}
324+
bigint temp = s;
325+
temp.is_neg = false;
326+
327+
return temp;
328+
}
310329

330+
static bigint abs(bigint&& s)
331+
{
332+
s.is_neg = false;
311333
return s;
312334
}
313335

@@ -355,20 +377,32 @@ namespace BigInt {
355377
return sum;
356378
}
357379

380+
/**
381+
* @brief Generates a random positive bigint of a specified length.
382+
*
383+
* This method ensures the resulting bigint is valid by using a random device
384+
* and engine for non-deterministic seeding.
385+
*
386+
* @param length The number of digits the generated bigint should have.
387+
* @return A bigint object representing the randomly generated number.
388+
*/
358389
static bigint random(size_t length);
359390

360391
private:
361392
std::vector<long long> vec{};
362-
bool is_neg{};
393+
bool is_neg{false};
363394

364395
// Function Definitions for Internal Uses
365396

366-
static bigint trim(const bigint& input) {
367-
auto temp = input;
368-
while (temp.vec.front() == 0){
369-
temp.vec.erase(temp.vec.begin());
397+
static bigint trim(bigint input) {
398+
while (input.vec.size() > 1 && input.vec.front() == 0) {
399+
input.vec.erase(input.vec.begin());
370400
}
371-
return temp;
401+
if (input.vec.empty()) {
402+
input.vec.push_back(0);
403+
input.is_neg = false;
404+
}
405+
return input;
372406
}
373407

374408
static std::vector<long long> string_to_vector(std::string input);
@@ -418,10 +452,16 @@ namespace BigInt {
418452
static bigint negate(const bigint& input)
419453
{
420454
bigint temp = input;
421-
temp.is_neg = true;
455+
temp.is_neg = !temp.is_neg;
422456
return temp;
423457
}
424458

459+
static bigint negate(bigint&& input)
460+
{
461+
input.is_neg = !input.is_neg;
462+
return input;
463+
}
464+
425465
static bool less_than(const bigint& lhs, const bigint& rhs)
426466
{
427467
if (is_negative(lhs) && is_negative(rhs))
@@ -455,61 +495,50 @@ namespace BigInt {
455495
return s.find_first_not_of("0123456789", 0) == std::string::npos;
456496
}
457497

458-
inline std::pair<int, long long> add_with_carry(const long long lhs, const long long rhs)
498+
inline bigint bigint::add(const bigint &lhs, const bigint &rhs)
459499
{
460-
auto sum = lhs + rhs;
500+
bool negate_answer = false;
501+
// Ensure both are positive
502+
if (is_negative(lhs) && is_negative(rhs)) negate_answer = true;
503+
else if (is_negative(lhs)) return subtract(rhs, abs(lhs));
504+
else if (is_negative(rhs)) return subtract(lhs, abs(rhs));
461505

462-
if (sum >= bigint::MAX_SIZE)
463-
{
464-
// Carry needs to happen
465-
auto carry = sum / bigint::MAX_SIZE;
466-
auto result = sum % bigint::MAX_SIZE;
467-
return {carry, result};
468-
}
506+
// Ensure LHS is larger than RHS
507+
if (lhs.vec.size() < rhs.vec.size()) return add(rhs, lhs);
469508

470-
return {0, sum};
471-
}
509+
// Prepare result vector with enough space (max size + 1 for potential carry)
510+
std::vector<long long> result;
511+
result.reserve(lhs.vec.size() + 1);
472512

473-
inline bigint bigint::add(const bigint &lhs, const bigint &rhs)
474-
{
475-
// Ensure LHS is larger than RHS, and both are positive
476-
if (lhs == 0) return rhs;
477-
if (rhs == 0) return lhs;
478-
if (is_negative(lhs) && is_negative(rhs))
479-
{
480-
return negate(add(abs(lhs) ,abs(rhs)));
481-
}
482-
if (is_negative(lhs))
483-
{
484-
return rhs - abs(lhs);
485-
}
486-
if (is_negative(rhs))
487-
{
488-
return lhs - abs(rhs);
489-
}
490-
if (lhs < rhs)
491-
{
492-
return add(rhs, lhs);
493-
}
513+
long long carry = 0;
514+
auto it_l = lhs.vec.rbegin();
515+
auto it_r = rhs.vec.rbegin();
494516

495-
bigint full_rhs = rhs;
496-
std::vector<std::pair<int, long long>> carry_result(lhs.vec.size() + 1);
517+
while (it_l != lhs.vec.rend()) {
518+
long long sum = *it_l + carry;
519+
if (it_r != rhs.vec.rend()) {
520+
sum += *it_r;
521+
++it_r;
522+
}
497523

498-
// Fill the smaller to match the larger size
499-
while (lhs.vec.size() > full_rhs.vec.size())
500-
{
501-
full_rhs.vec.insert(full_rhs.vec.begin(), 0);
502-
}
524+
if (sum >= MAX_SIZE) {
525+
sum -= MAX_SIZE;
526+
carry = 1;
527+
} else {
528+
carry = 0;
529+
}
503530

504-
std::transform(lhs.vec.rbegin(), lhs.vec.rend(), full_rhs.vec.rbegin(), carry_result.rbegin(), add_with_carry);
531+
result.push_back(sum);
532+
++it_l;
533+
}
505534

506-
std::vector<long long> final(lhs.vec.size() + 1);
507-
for (int i = carry_result.size() - 1; i > 0; --i) {
508-
final[i] += carry_result[i].second;
509-
final[i - 1] += carry_result[i].first;
535+
if (carry > 0) {
536+
result.push_back(carry);
510537
}
511538

512-
return trim(bigint(final));
539+
std::reverse(result.begin(), result.end());
540+
bigint result_bigint {std::move(result)};
541+
return negate_answer ? negate(result_bigint) : result_bigint;
513542
}
514543

515544
inline std::pair<int, long long> subtract_with_borrow(const long long lhs, const long long rhs)
@@ -843,8 +872,8 @@ namespace BigInt {
843872
return true;
844873
}
845874

846-
inline bigint bigint::random(size_t length) {
847-
const char charset[] = "0123456789";
875+
inline bigint bigint::random(const size_t length) {
876+
constexpr char charset[] = "0123456789";
848877
std::default_random_engine rng(std::random_device{}());
849878

850879
// Distribution for the first digit (1-9)

0 commit comments

Comments
 (0)