Browse Source

Adding A*B operations.
Making operator-() delegate to operator*.
Adding test cases for multiplication.
Adding one test for subtraction to increase code coverage.

Samuel Jaffe 8 years ago
parent
commit
c0445f468e
2 changed files with 83 additions and 10 deletions
  1. 45 10
      src/biginteger.cpp
  2. 38 0
      test/biginteger.t.h

+ 45 - 10
src/biginteger.cpp

@@ -15,6 +15,7 @@ namespace detail {
   int compare(data_type const & rhs, data_type const & lhs);
   data_type add(data_type const & rhs, data_type const & lhs);
   data_type subtract_nounderflow(data_type const & rhs, data_type const & lhs);
+  data_type multiply(data_type const & rhs, data_type const & lhs);
 }
 
 static void swap(biginteger & rhs, biginteger && lhs) {
@@ -58,20 +59,18 @@ biginteger & operator-=(biginteger & rhs, biginteger const & lhs) {
   return rhs;
 }
 
-//biginteger & operator*=(biginteger & rhs, biginteger const & lhs) {
-//  swap(rhs, rhs * lhs);
-//  return rhs;
-//}
-//
+biginteger & operator*=(biginteger & rhs, biginteger const & lhs) {
+  swap(rhs, rhs * lhs);
+  return rhs;
+}
+
 //biginteger & operator/=(biginteger & rhs, biginteger const & lhs) {
 //  swap(rhs, rhs / lhs);
 //  return rhs;
 //}
 
 biginteger biginteger::operator-() const {
-  biginteger tmp{*this};
-  tmp.is_negative = !tmp.is_negative;
-  return tmp;
+  return (*this) * NEGATIVE_ONE;
 }
 
 biginteger math::operator+(biginteger const & rhs, biginteger const & lhs) {
@@ -82,7 +81,7 @@ biginteger math::operator+(biginteger const & rhs, biginteger const & lhs) {
   } else {
     auto cmp = detail::compare(rhs.data, lhs.data);
     if (cmp == 0) {
-      return 0;
+      return biginteger::ZERO;
     } else if (cmp > 0) {
       return {rhs.is_negative, detail::subtract_nounderflow(rhs.data, lhs.data)};
     } else {
@@ -99,7 +98,7 @@ biginteger math::operator-(biginteger const & rhs, biginteger const & lhs) {
   } else {
     auto cmp = detail::compare(rhs.data, lhs.data);
     if (cmp == 0) {
-      return 0;
+      return biginteger::ZERO;
     } else if (cmp > 0) {
       return {rhs.is_negative, detail::subtract_nounderflow(rhs.data, lhs.data)};
     } else {
@@ -108,6 +107,13 @@ biginteger math::operator-(biginteger const & rhs, biginteger const & lhs) {
   }
 }
 
+biginteger math::operator*(biginteger const & rhs, biginteger const & lhs) {
+  if (rhs == biginteger::ZERO || lhs == biginteger::ZERO) {
+    return biginteger::ZERO;
+  }
+  return {rhs.is_negative != lhs.is_negative, detail::multiply(rhs.data, lhs.data)};
+}
+
 namespace detail {
 #define IMPL_COMPARE(expr) \
   if (rhs expr < lhs expr) return -1; \
@@ -161,6 +167,35 @@ namespace detail {
     if (rval[rbnd-1] == 0 && rbnd > 1) { rval.pop_back(); }
     return rval;
   }
+  
+  data_type multiply(data_type const & rhs, data_type const & lhs) {
+    if (compare(rhs, {1}) == 0) { return lhs; }
+    else if (compare(lhs, {1}) == 0) { return rhs; }
+    data_type::size_type const rbnd = rhs.size(), lbnd = lhs.size();
+    data_type::size_type const ubnd = rbnd + lbnd;
+    data_type rval(ubnd + 1);
+    // Multiply
+    for (data_type::size_type i = 0; i < rbnd; ++i) {
+      for (data_type::size_type j = 0; j < lbnd; ++j) {
+        // Max input              999,999,999
+        // Max output 999,999,998,000,000,001
+        int64_t product = static_cast<int64_t>(rhs[i]) * static_cast<int64_t>(lhs[j]);
+        int64_t overflow = product / biginteger::OVER_SEG;
+        rval[i+j] += static_cast<int32_t>(product - (overflow * biginteger::OVER_SEG));
+        rval[i+j+1] += static_cast<int32_t>(overflow);
+      }
+    }
+    // Carry
+    for (data_type::size_type i = 0; i < ubnd; ++i) {
+      if (rval[i] > biginteger::MAX_SEG) {
+        int32_t overflow = rval[i] / biginteger::OVER_SEG;
+        rval[i] -= (overflow * biginteger::OVER_SEG);
+        rval[i+1] += overflow;
+      }
+    }
+    if (rval[ubnd] == 0) { rval.pop_back(); }
+    return rval;
+  }
 }
 
 bool math::operator==(biginteger const & rhs, biginteger const & lhs) {

+ 38 - 0
test/biginteger.t.h

@@ -52,4 +52,42 @@ public:
     math::biginteger bi{1000};
     TS_ASSERT_EQUALS((bi-999).to_string(), "1");
   }
+
+  void testSubUnderflowBorrows() {
+    math::biginteger bi{1000000000ULL};
+    TS_ASSERT_EQUALS((bi-1).to_string(), "999999999");
+  }
+
+  void testMultiplyZeroReturnsZero() {
+    auto &ZERO =  math::biginteger::ZERO;
+    math::biginteger bi{999999999ULL};
+    TS_ASSERT_EQUALS((bi*ZERO).to_string(), "0");
+    TS_ASSERT_EQUALS((ZERO*bi).to_string(), "0");
+  }
+  
+  void testMultiplyOneReturnsValue() {
+    auto &ONE =  math::biginteger::ONE;
+    math::biginteger bi{999999999ULL};
+    TS_ASSERT_EQUALS((bi*ONE).to_string(), "999999999");
+    TS_ASSERT_EQUALS((ONE*bi).to_string(), "999999999");
+  }
+
+  void testMultiplyNegativeOneReturnsInverse() {
+    auto &NEGATIVE_ONE =  math::biginteger::NEGATIVE_ONE;
+    math::biginteger bi{999999999ULL};
+    TS_ASSERT_EQUALS((bi*NEGATIVE_ONE).to_string(), "-999999999");
+    TS_ASSERT_EQUALS((NEGATIVE_ONE*bi).to_string(), "-999999999");
+  }
+  
+  void testMultiplyOverflowsIntoNextCell() {
+    math::biginteger bi{999999999ULL};
+    TS_ASSERT_EQUALS((bi*bi).to_string(), "999999998000000001");
+  }
+  
+  void testMultiplyCarryIntoNextCell() {
+    math::biginteger bi{999999999ULL};
+    math::biginteger big{bi*bi};
+    TS_ASSERT_EQUALS((big*big).to_string(),
+                     "999999996000000005999999996000000001");
+  }
 };