浏览代码

Adding modulo operator.
Fixing bug with division operator underflowing the digits offset.

Samuel Jaffe 8 年之前
父节点
当前提交
b3139e0768
共有 3 个文件被更改,包括 60 次插入7 次删除
  1. 1 0
      include/biginteger.h
  2. 24 4
      src/biginteger.cpp
  3. 35 3
      test/biginteger.t.h

+ 1 - 0
include/biginteger.h

@@ -33,6 +33,7 @@ namespace math {
     friend biginteger operator-(biginteger const &, biginteger const &);
     friend biginteger operator*(biginteger const &, biginteger const &);
     friend biginteger operator/(biginteger const &, biginteger const &);
+    friend biginteger operator%(biginteger const &, biginteger const &);
     friend biginteger & operator+=(biginteger &, biginteger const &);
     friend biginteger & operator-=(biginteger &, biginteger const &);
     friend biginteger & operator*=(biginteger &, biginteger const &);

+ 24 - 4
src/biginteger.cpp

@@ -153,7 +153,27 @@ biginteger math::operator/(biginteger const & rhs, biginteger const & lhs) {
     bool is_neg = rhs.is_negative != lhs.is_negative;
     if (cmp < 0) { return biginteger::ZERO; }
     else if (cmp == 0) { return {is_neg, {1}}; }
-    else { return {is_neg, detail::divide(rhs.data, lhs.data)}; }
+    else { return {is_neg, detail::divide(rhs.data, lhs.data).first}; }
+  }
+}
+
+biginteger math::operator%(biginteger const & rhs, biginteger const & lhs) {
+  if (lhs == biginteger::ZERO) { throw std::domain_error("cannot divide by 0"); }
+  else if (rhs == biginteger::ZERO || lhs == biginteger::ONE ||
+           lhs == biginteger::NEGATIVE_ONE) { return biginteger::ZERO; }
+  else {
+    auto cmp = detail::compare(rhs.data, lhs.data);
+    if (cmp < 0) { return rhs; }
+    else if (cmp == 0) { return biginteger::ZERO; }
+    else {
+      auto data = detail::divide(rhs.data, lhs.data).second;
+      if (detail::compare(data, {0}) == 0) { return biginteger::ZERO; }
+      else if (rhs.is_negative != lhs.is_negative) {
+        return {lhs.is_negative, detail::subtract_nounderflow(lhs.data, data)};
+      } else {
+        return {lhs.is_negative, std::move(data)};
+      }
+    }
   }
 }
 
@@ -266,11 +286,11 @@ namespace detail {
   
   int32_t powers[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
   
-  data_type divide(data_type remainder, data_type const & divisor) {
+  std::pair<data_type, data_type> divide(data_type remainder, data_type const & divisor) {
     data_type accum{0};
     auto const dig = digits(divisor);
     do {
-      auto const diff = digits(remainder) - dig - 1;
+      auto const diff = std::max(1UL, digits(remainder) - dig) - 1;
       auto const shift = diff / biginteger::SEG_DIGITS;
       auto const ipow = diff - (shift * biginteger::SEG_DIGITS);
       data_type step{shift10({1}, powers[ipow], shift)};
@@ -280,7 +300,7 @@ namespace detail {
         add_into(accum, step);
       } while (detail::compare(remainder, value) >= 0);
     } while (detail::compare(remainder, divisor) >= 0);
-    return accum;
+    return { std::move(accum), std::move(remainder) };
   }
 }
 

+ 35 - 3
test/biginteger.t.h

@@ -61,8 +61,8 @@ public:
   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");
+    TS_ASSERT_EQUALS(bi*ZERO, ZERO);
+    TS_ASSERT_EQUALS(ZERO*bi, ZERO);
   }
   
   void testMultiplyOneReturnsValue() {
@@ -105,7 +105,7 @@ public:
   void testDivideByOneReturnsValue() {
     auto &ONE =  math::biginteger::ONE;
     math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi/ONE).to_string(), "1000");
+    TS_ASSERT_EQUALS(bi/ONE, bi);
   }
 
   void testDivideByNegativeOneReturnsInverse() {
@@ -135,4 +135,36 @@ public:
     TS_ASSERT_EQUALS((big/999999999ULL).to_string(),
                      "999999999");
   }
+  
+  void testModuloZeroThrows() {
+    math::biginteger bi{1000};
+    TS_ASSERT_THROWS(bi%0, std::domain_error);
+  }
+  
+  void testModuloBiggerIsSameValue() {
+    math::biginteger bi{1000};
+    TS_ASSERT_EQUALS(bi%2000, bi);
+  }
+  
+  void testModuloSameNumberIsZero() {
+    math::biginteger bi{1000};
+    TS_ASSERT_EQUALS(bi%1000, 0);
+  }
+
+  void testModuloDivisorIsZero() {
+    math::biginteger bi{1000};
+    TS_ASSERT_EQUALS(bi%100, 0);
+  }
+
+  void testModuloDiffSignIsInverseElement() {
+    math::biginteger bi{1000};
+    math::biginteger mod{13};
+    TS_ASSERT_EQUALS((bi%mod)+((-bi)%mod), mod);
+  }
+
+  void testModuloNegativesIsNegative() {
+    math::biginteger bi{1000};
+    math::biginteger mod{13};
+    TS_ASSERT_EQUALS((bi%mod), -((-bi)%(-mod)));
+  }
 };