浏览代码

Initial commit of big decimal code, supporting +, -, *.

Samuel Jaffe 8 年之前
父节点
当前提交
bb2144baf3
共有 4 个文件被更改,包括 529 次插入2 次删除
  1. 17 2
      bigdecimal.xcodeproj/project.pbxproj
  2. 76 0
      include/bigdecimal.h
  3. 199 0
      src/bigdecimal.cpp
  4. 237 0
      test/bigdecimal.t.h

+ 17 - 2
bigdecimal.xcodeproj/project.pbxproj

@@ -7,6 +7,10 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		CD2EC1BC1F0AF2C300D49DF5 /* bigdecimal.h in Headers */ = {isa = PBXBuildFile; fileRef = CD2EC1BB1F0AF2C300D49DF5 /* bigdecimal.h */; };
+		CD2EC1BD1F0AF2C300D49DF5 /* bigdecimal.h in Headers */ = {isa = PBXBuildFile; fileRef = CD2EC1BB1F0AF2C300D49DF5 /* bigdecimal.h */; };
+		CD2EC1BF1F0AF3B800D49DF5 /* bigdecimal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD2EC1BE1F0AF3B800D49DF5 /* bigdecimal.cpp */; };
+		CD2EC1C01F0AF3B800D49DF5 /* bigdecimal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD2EC1BE1F0AF3B800D49DF5 /* bigdecimal.cpp */; };
 		CD2EC1C21F0BCCA700D49DF5 /* bignum_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = CD2EC1C11F0BCCA700D49DF5 /* bignum_helper.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CD2EC1C31F0BCCA700D49DF5 /* bignum_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = CD2EC1C11F0BCCA700D49DF5 /* bignum_helper.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CD2EC1C51F0BCCBF00D49DF5 /* bignum_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD2EC1C41F0BCCBF00D49DF5 /* bignum_helper.cpp */; };
@@ -31,6 +35,9 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		CD14CA711F0DA9FC0091A168 /* bigdecimal.t.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bigdecimal.t.h; sourceTree = "<group>"; };
+		CD2EC1BB1F0AF2C300D49DF5 /* bigdecimal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bigdecimal.h; sourceTree = "<group>"; };
+		CD2EC1BE1F0AF3B800D49DF5 /* bigdecimal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bigdecimal.cpp; sourceTree = "<group>"; };
 		CD2EC1C11F0BCCA700D49DF5 /* bignum_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bignum_helper.h; sourceTree = "<group>"; };
 		CD2EC1C41F0BCCBF00D49DF5 /* bignum_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bignum_helper.cpp; sourceTree = "<group>"; };
 		CD5FB2711F06EEAF005A0D61 /* bigdecimal_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = bigdecimal_tc; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -92,6 +99,7 @@
 			isa = PBXGroup;
 			children = (
 				CD5FB27F1F06EF70005A0D61 /* biginteger.h */,
+				CD2EC1BB1F0AF2C300D49DF5 /* bigdecimal.h */,
 				CD2EC1C11F0BCCA700D49DF5 /* bignum_helper.h */,
 			);
 			path = include;
@@ -101,6 +109,7 @@
 			isa = PBXGroup;
 			children = (
 				CD5FB2801F06EF7D005A0D61 /* biginteger.cpp */,
+				CD2EC1BE1F0AF3B800D49DF5 /* bigdecimal.cpp */,
 				CD2EC1C41F0BCCBF00D49DF5 /* bignum_helper.cpp */,
 			);
 			path = src;
@@ -110,6 +119,7 @@
 			isa = PBXGroup;
 			children = (
 				CD5FB27E1F06EF64005A0D61 /* biginteger.t.h */,
+				CD14CA711F0DA9FC0091A168 /* bigdecimal.t.h */,
 				CD5FB2A61F06F0D2005A0D61 /* bigdecimal_tc.cpp */,
 			);
 			path = test;
@@ -123,6 +133,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD5FB2921F06EFF2005A0D61 /* biginteger.h in Headers */,
+				CD2EC1BC1F0AF2C300D49DF5 /* bigdecimal.h in Headers */,
 				CD2EC1C21F0BCCA700D49DF5 /* bignum_helper.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -132,6 +143,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD2EC1C31F0BCCA700D49DF5 /* bignum_helper.h in Headers */,
+				CD2EC1BD1F0AF2C300D49DF5 /* bigdecimal.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -142,10 +154,10 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = CD5FB2781F06EEAF005A0D61 /* Build configuration list for PBXNativeTarget "bigdecimal_tc" */;
 			buildPhases = (
+				CD5FB2A51F06F04C005A0D61 /* ShellScript */,
 				CD5FB26D1F06EEAF005A0D61 /* Sources */,
 				CD5FB26E1F06EEAF005A0D61 /* Frameworks */,
 				CD5FB26F1F06EEAF005A0D61 /* CopyFiles */,
-				CD5FB2A51F06F04C005A0D61 /* ShellScript */,
 			);
 			buildRules = (
 			);
@@ -237,13 +249,14 @@
 			);
 			inputPaths = (
 				"$(SRCROOT)/test/biginteger.t.h",
+				"$(SRCROOT)/test/bigdecimal.t.h",
 			);
 			outputPaths = (
 				"$(SRCROOT)/test/bigdecimal_tc.cpp",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "cd test; cxxtestgen --error-printer -o bigdecimal_tc.cpp biginteger.t.h";
+			shellScript = "cd test; cxxtestgen --error-printer -o bigdecimal_tc.cpp biginteger.t.h bigdecimal.t.h";
 		};
 /* End PBXShellScriptBuildPhase section */
 
@@ -260,6 +273,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD2EC1BF1F0AF3B800D49DF5 /* bigdecimal.cpp in Sources */,
 				CD5FB2911F06EFEF005A0D61 /* biginteger.cpp in Sources */,
 				CD2EC1C51F0BCCBF00D49DF5 /* bignum_helper.cpp in Sources */,
 			);
@@ -269,6 +283,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD2EC1C01F0AF3B800D49DF5 /* bigdecimal.cpp in Sources */,
 				CD5FB2A31F06F03D005A0D61 /* biginteger.cpp in Sources */,
 				CD2EC1C61F0BCCBF00D49DF5 /* bignum_helper.cpp in Sources */,
 			);

+ 76 - 0
include/bigdecimal.h

@@ -0,0 +1,76 @@
+//
+//  bigdecimal.h
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 7/3/17.
+//
+
+#pragma once
+
+#include "biginteger.h"
+
+namespace math {
+  class bigdecimal {
+  private:
+    template <typename Int>
+    using is_signed_t = typename std::enable_if<std::numeric_limits<Int>::is_integer && std::numeric_limits<Int>::is_signed, void*>::type;
+    template <typename Int>
+    using is_unsigned_t = typename std::enable_if<std::numeric_limits<Int>::is_integer && !std::numeric_limits<Int>::is_signed, void*>::type;
+  public:
+    using data_type = std::vector<int32_t>;
+    static bigdecimal const ZERO, ONE, NEGATIVE_ONE;
+    static constexpr int32_t const MAX_SEG   { 999999999};
+    static constexpr int32_t const OVER_SEG  {1000000000};
+    static constexpr int32_t const SEG_DIGITS{         9};
+  public:
+    bigdecimal();
+
+    template <typename Value>
+    bigdecimal(Value && value, int32_t scale)
+    : bigdecimal(value) {
+      rescale(scale);
+    }
+    
+    template <typename Int>
+    bigdecimal(Int value, is_signed_t<Int> = nullptr)
+    : bigdecimal(value < 0, static_cast<uint64_t>(value < 0 ? -value : value)) {}
+    
+    template <typename Int>
+    bigdecimal(Int value, is_unsigned_t<Int> = nullptr)
+    : bigdecimal(false, static_cast<uint64_t>(value)) {}
+
+    bigdecimal(long double);
+    bigdecimal(char const *);
+    
+    int32_t scale() const { return scale_; }
+    void rescale(int32_t);
+    void set_value(bigdecimal const &);
+    
+    bigdecimal operator-() const;
+    
+    friend bigdecimal operator+(bigdecimal, bigdecimal const &);
+    friend bigdecimal operator-(bigdecimal, bigdecimal const &);
+    friend bigdecimal operator*(bigdecimal, bigdecimal const &);
+    friend bigdecimal & operator+=(bigdecimal &, bigdecimal const &);
+    friend bigdecimal & operator-=(bigdecimal &, bigdecimal const &);
+    friend bigdecimal & operator*=(bigdecimal &, bigdecimal const &);
+    friend bigdecimal & operator/=(bigdecimal &, bigdecimal const &);
+    friend bool operator==(bigdecimal const &, bigdecimal const &);
+    
+    std::string to_string() const;
+  private:
+    bigdecimal(bool, uint64_t);
+    int32_t get_impl_scale() const;
+    void subtract_impl(bigdecimal const &, bool);
+    friend void swap(bigdecimal & rhs, bigdecimal & lhs) {
+      using std::swap;
+      swap(rhs.is_negative, lhs.is_negative);
+      swap(rhs.scale_, lhs.scale_);
+      swap(rhs.data, lhs.data);
+    }
+    
+    bool is_negative;
+    int32_t scale_{0};
+    data_type data{};
+  };
+}

+ 199 - 0
src/bigdecimal.cpp

@@ -0,0 +1,199 @@
+//
+//  bigdecimal.cpp
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 7/3/17.
+//
+
+#include "bigdecimal.h"
+#include "bignum_helper.h"
+
+#include <cstdlib>
+
+using namespace math;
+
+static size_t mul_scale(int32_t scale) {
+  return scale < 0 ? 0 : size_t(scale / bigdecimal::SEG_DIGITS);
+}
+
+static int32_t add_scale(int32_t scale) {
+  return scale / bigdecimal::SEG_DIGITS + (scale > 0 ? 1 : 0);
+}
+
+static void read(int32_t & dst, char const * & str, size_t len) {
+  char seg[bigdecimal::SEG_DIGITS+1] = "";
+  strncpy(seg, str, len);
+  dst = atoi(seg);
+  str += len;
+}
+
+static void read_segment(bigdecimal::data_type & data, char const * number, size_t len) {
+  auto elems = len/bigdecimal::SEG_DIGITS;
+  data.resize(elems);
+  if (auto small = len-(elems*bigdecimal::SEG_DIGITS)) {
+    read(*data.emplace(data.end()), number, small);
+  }
+  for (size_t idx = elems; idx > 0; --idx) {
+    read(data[idx-1], number, bigdecimal::SEG_DIGITS);
+  }
+}
+
+bigdecimal::bigdecimal(bool neg, uint64_t value) :
+is_negative(neg) {
+  uint64_t next{0};
+  do {
+    next = value / OVER_SEG;
+    data.push_back(static_cast<int32_t>(value - (OVER_SEG * next)));
+  } while ((value = next) > 0);
+}
+
+bigdecimal::bigdecimal(char const * number) :
+is_negative(number[0] == '-') {
+  if (is_negative) { ++number; }
+  if (auto p = strchr(number, '.')) {
+    read_segment(data, number, size_t(p - number));
+    number = p + 1;
+    scale_ = int32_t(strlen(number));
+    size_t elems = size_t(scale_/SEG_DIGITS);
+    data.resize(elems+data.size());
+    for (size_t idx = elems; idx > 0; --idx) {
+      read(data[idx-1], number, SEG_DIGITS);
+    }
+    if (auto l = strlen(number)) {
+      data.insert(data.begin(), atoi(number) * detail::powers[SEG_DIGITS-l]);
+    }
+  } else {
+    read_segment(data, number, strlen(number));
+  }
+}
+
+void bigdecimal::set_value(bigdecimal const & other) {
+  int32_t scale = scale_;
+  operator=(other);
+  rescale(scale);
+}
+
+int32_t bigdecimal::get_impl_scale() const {
+  return add_scale(scale_);
+}
+
+void bigdecimal::rescale(int32_t nscale) {
+  int32_t steps = add_scale(scale_);
+  int32_t const nsteps = add_scale(nscale);
+  if (steps > nsteps) {
+    data.erase(data.begin(), data.begin() + steps - nsteps);
+  } else if (steps < nsteps) {
+    data.insert(data.begin(), size_t(nsteps-steps), 0);
+  }
+  scale_ = nscale;
+}
+
+void bigdecimal::subtract_impl(bigdecimal const & lhs, bool is_sub) {
+  size_t const offset = size_t(get_impl_scale() - lhs.get_impl_scale());
+  auto cmp = detail::compare(data, lhs.data, offset);
+  if (cmp == 0) {
+    set_value(bigdecimal::ZERO);
+  } else if (cmp > 0) {
+    detail::subtract_nounderflow(data, lhs.data, offset);
+  } else {
+    bigdecimal tmp{lhs};
+    tmp.is_negative = lhs.is_negative ^ is_sub;
+    swap(*this, tmp);
+    rescale(tmp.scale());
+    detail::subtract_nounderflow(data, tmp.data);
+  }
+}
+
+bigdecimal bigdecimal::operator-() const {
+  return *this * NEGATIVE_ONE;
+}
+
+bigdecimal & math::operator+=(bigdecimal & rhs, bigdecimal const & lhs) {
+  int32_t const new_scale = std::max(rhs.scale_, lhs.scale_);
+  rhs.rescale(new_scale);
+  size_t const offset = size_t(rhs.get_impl_scale() - lhs.get_impl_scale());
+  if (lhs == bigdecimal::ZERO) { return rhs; }
+  else if (rhs == bigdecimal::ZERO) { rhs.set_value(lhs); }
+  else if (rhs.is_negative == lhs.is_negative) {
+    detail::add(rhs.data, lhs.data, offset);
+  } else {
+    rhs.subtract_impl(lhs, false);
+  }
+  return rhs;
+}
+
+bigdecimal & math::operator-=(bigdecimal & rhs, bigdecimal const & lhs) {
+  int32_t const new_scale = std::max(rhs.scale_, lhs.scale_);
+  rhs.rescale(new_scale);
+  size_t const offset = size_t(rhs.get_impl_scale() - lhs.get_impl_scale());
+  if (lhs == bigdecimal::ZERO) { return rhs; }
+  else if (rhs == bigdecimal::ZERO) { rhs = -lhs; }
+  else if (rhs.is_negative != lhs.is_negative) {
+    detail::add(rhs.data, lhs.data, offset);
+  } else {
+    rhs.subtract_impl(lhs, true);
+  }
+  return rhs;
+}
+
+bigdecimal & math::operator*=(bigdecimal & rhs, bigdecimal const & lhs) {
+  int32_t const new_scale = rhs.scale_ + lhs.scale_;
+  size_t const offset = mul_scale(rhs.scale_) + mul_scale(lhs.scale_) - mul_scale(new_scale);
+  bool is_neg = rhs.is_negative != lhs.is_negative;
+  if (rhs == bigdecimal::ZERO || lhs == bigdecimal::ZERO) {
+    rhs.set_value(bigdecimal::ZERO);
+    rhs.rescale(new_scale);
+  } else if (detail::compare(lhs.data, bigdecimal::ONE.data) == 0) {
+    rhs.is_negative = is_neg;
+    rhs.rescale(new_scale);
+  } else if (detail::compare(rhs.data, bigdecimal::ONE.data) == 0) {
+    rhs.data = lhs.data;
+    rhs.is_negative = is_neg;
+    rhs.rescale(new_scale);
+  } else {
+    detail::multiply(rhs.data, lhs.data, offset);
+    rhs.scale_ = new_scale; // TODO: more steps in certain cases
+  }
+  return rhs;
+}
+
+bigdecimal math::operator+(bigdecimal rhs, bigdecimal const & lhs) {
+  return rhs += lhs;
+}
+
+bigdecimal math::operator-(bigdecimal rhs, bigdecimal const & lhs) {
+  return rhs -= lhs;
+}
+
+bigdecimal math::operator*(bigdecimal rhs, bigdecimal const & lhs) {
+  return rhs *= lhs;
+}
+
+bool math::operator==(bigdecimal const & lhs, bigdecimal const & rhs) {
+  return lhs.is_negative == rhs.is_negative && detail::compare(lhs.data, rhs.data) == 0; // TODO
+}
+
+bigdecimal const bigdecimal::ZERO{0}, bigdecimal::ONE{1}, bigdecimal::NEGATIVE_ONE{-1};
+
+std::string bigdecimal::to_string() const {
+  std::vector<char> output(biginteger::SEG_DIGITS * data.size() + 1, '\0');
+  std::ptrdiff_t idx = 0;
+  if (is_negative) { output[0] = '-'; ++idx; }
+  idx += sprintf(output.data() + idx, "%d", data[data.size()-1]);
+  size_t i = data.size()-1;
+  for (; i > size_t(std::max(0, add_scale(scale_))); --i, idx+=SEG_DIGITS) {
+    sprintf(output.data() + idx, "%0.*d", SEG_DIGITS, data[i-1]);
+  }
+  if (scale_ > 0) { // TODO: scale > size?
+    sprintf(output.data() + idx++, ".");
+    for (; i > 1; --i, idx+=SEG_DIGITS) {
+      sprintf(output.data() + idx, "%0.*d", SEG_DIGITS, data[i-1]);
+    }
+    int32_t const val = scale_ % SEG_DIGITS;
+    sprintf(output.data() + idx, "%0.*d", val, data[0]/detail::powers[SEG_DIGITS-val]);
+  } else if (scale_ < 0) {
+    
+  }
+  return output.data();
+}
+

+ 237 - 0
test/bigdecimal.t.h

@@ -0,0 +1,237 @@
+//
+//  bigdecimal.t.h
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 7/5/17.
+//
+
+#pragma once
+
+#include "bigdecimal.h"
+
+#include <cxxtest/TestSuite.h>
+
+class bigdecimal_TestSuite : public CxxTest::TestSuite {
+public:
+//  void testConstructFromStringIsSameValueAsFromInt() {
+//    using bi = math::bigdecimal;
+//    TS_ASSERT_EQUALS(bi("1000000"), bi(1000000));
+//  }
+//
+  
+  void testConstructIntegerAsDecimal() {
+    using bd = math::bigdecimal;
+    TS_ASSERT_EQUALS(bd("1000.00").to_string(), "1000.00");
+  }
+
+  void testConstructDecimal() {
+    using bd = math::bigdecimal;
+    TS_ASSERT_EQUALS(bd("1000.10").to_string(), "1000.10");
+    TS_ASSERT_EQUALS(bd("1000.01").to_string(), "1000.01");
+  }
+
+  void testRescaleHigherAddsDigits() {
+    math::bigdecimal dec(100);
+    TS_ASSERT_EQUALS(dec.scale(), 0);
+    dec.rescale(2);
+    TS_ASSERT_EQUALS(dec.to_string(), "100.00");
+  }
+  
+  void testRescaleLowerCanBeLossy() {
+    math::bigdecimal dec("100.10");
+    TS_ASSERT_EQUALS(dec.scale(), 2);
+    dec.rescale(0);
+    dec.rescale(2);
+    TS_ASSERT_EQUALS(dec.to_string(), "100.00");
+  }
+  
+  void testConstructWithScaleEqualsWithout() {
+    math::bigdecimal scl(100, -2);
+    TS_ASSERT_EQUALS(scl.to_string(), "100");
+  }
+  
+  void testAddTwoDecimalsSameScale() {
+    using bd = math::bigdecimal;
+    bd a("1000.10");
+    bd b("1000.01");
+    TS_ASSERT_EQUALS(a.scale(), b.scale());
+    TS_ASSERT_EQUALS((a+b).to_string(), "2000.11");
+  }
+  
+  void testAddTwoDecimalsDifferentScalesUsesHigherScale() {
+    using bd = math::bigdecimal;
+    bd a("1000.10");
+    bd b("1000");
+    TS_ASSERT(a.scale() > b.scale());
+    TS_ASSERT_EQUALS((a+b).to_string(), "2000.10");
+  }
+  
+  void testAddNumberWithInversePreservesScale() {
+    math::bigdecimal a("1.001");
+    TS_ASSERT_EQUALS((a+(-a)).to_string(), "0.000");
+  }
+  
+  void testSubtractTwoDecimalsDifferentScalesUsesHigherScale() {
+    using bd = math::bigdecimal;
+    bd a("900.10");
+    bd b("1000");
+    TS_ASSERT(a.scale() > b.scale());
+    TS_ASSERT_EQUALS((a-b).to_string(), "-99.90");
+    TS_ASSERT_EQUALS((b-a).to_string(), "99.90");
+  }
+
+//  void testAddPastBounds() {
+//    math::bigdecimal bi{999999999ULL};
+//    TS_ASSERT_EQUALS((bi+1).to_string(), "1000000000");
+//  }
+//  
+//  void testAddReciprocalIsZero() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi+(-bi)).to_string(), "0");
+//  }
+//  
+//  void testAddNegativeLargerGivesNegative() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi+(-1001)).to_string(), "-1");
+//  }
+//  
+//  void testAddNegativeSmallerGivesPositive() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi+(-999)).to_string(), "1");
+//  }
+//  
+//  void testSubSelfIsZero() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi-bi).to_string(), "0");
+//  }
+//  
+//  void testNegativeMinusNegativeIncreateAbs() {
+//    math::bigdecimal bi{-1000};
+//    TS_ASSERT_EQUALS((bi-100).to_string(), "-1100");
+//  }
+//  
+//  void testSubLargerGivesNegative() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi-1001).to_string(), "-1");
+//  }
+//  
+//  void testSubSmallerGivesPositive() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi-999).to_string(), "1");
+//  }
+//  
+//  void testSubUnderflowBorrows() {
+//    math::bigdecimal bi{1000000000ULL};
+//    TS_ASSERT_EQUALS((bi-1).to_string(), "999999999");
+//  }
+//  
+//  void testMultiplyZeroReturnsZero() {
+//    auto &ZERO =  math::bigdecimal::ZERO;
+//    math::bigdecimal bi{999999999ULL};
+//    TS_ASSERT_EQUALS(bi*ZERO, ZERO);
+//    TS_ASSERT_EQUALS(ZERO*bi, ZERO);
+//  }
+//  
+//  void testMultiplyOneReturnsValue() {
+//    auto &ONE =  math::bigdecimal::ONE;
+//    math::bigdecimal bi{999999999ULL};
+//    TS_ASSERT_EQUALS((bi*ONE).to_string(), "999999999");
+//    TS_ASSERT_EQUALS((ONE*bi).to_string(), "999999999");
+//  }
+//  
+//  void testMultiplyNegativeOneReturnsInverse() {
+//    auto &NEGATIVE_ONE =  math::bigdecimal::NEGATIVE_ONE;
+//    math::bigdecimal bi{999999999ULL};
+//    TS_ASSERT_EQUALS((bi*NEGATIVE_ONE).to_string(), "-999999999");
+//    TS_ASSERT_EQUALS((NEGATIVE_ONE*bi).to_string(), "-999999999");
+//  }
+//  
+//  void testMultiplyOverflowsIntoNextCell() {
+//    math::bigdecimal bi{999999999ULL};
+//    TS_ASSERT_EQUALS((bi*bi).to_string(), "999999998000000001");
+//  }
+//  
+//  void testMultiplyCarryIntoNextCell() {
+//    math::bigdecimal bi{999999999ULL};
+//    math::bigdecimal big{bi*bi};
+//    TS_ASSERT_EQUALS((big*big).to_string(),
+//                     "999999996000000005999999996000000001");
+//  }
+//  
+//  void testMultiplyNoOverflow() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi*bi).to_string(), "1000000");
+//  }
+//
+//  void testDivideByZeroThrows() {
+//    auto &ZERO =  math::bigdecimal::ZERO;
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_THROWS(bi/ZERO, std::domain_error);
+//  }
+//  
+//  void testDivideByOneReturnsValue() {
+//    auto &ONE =  math::bigdecimal::ONE;
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS(bi/ONE, bi);
+//  }
+//  
+//  void testDivideByNegativeOneReturnsInverse() {
+//    auto &NEGATIVE_ONE =  math::bigdecimal::NEGATIVE_ONE;
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS((bi/NEGATIVE_ONE).to_string(), "-1000");
+//  }
+//  
+//  void testDivisionWithMultipleMultSubSteps() {
+//    math::bigdecimal bi{1112};
+//    TS_ASSERT_EQUALS((bi/2).to_string(), "556");
+//  }
+//  
+//  void testDivisionDroppingNumberOfCells() {
+//    math::bigdecimal bi{1000000000ULL};
+//    TS_ASSERT_EQUALS((bi/2).to_string(), "500000000");
+//  }
+//  
+//  void testDivisionByBiggerNumberIsZero() {
+//    math::bigdecimal bi{1000ULL};
+//    TS_ASSERT_EQUALS((bi/1001).to_string(), "0");
+//  }
+//  
+//  void testDivisionWithLargeNumbers() {
+//    math::bigdecimal big{"999999998000000001"};
+//    TS_ASSERT_EQUALS(big.to_string(), "999999998000000001");
+//    TS_ASSERT_EQUALS((big/999999999ULL).to_string(),
+//                     "999999999");
+//  }
+//  
+//  void testModuloZeroThrows() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_THROWS(bi%0, std::domain_error);
+//  }
+//  
+//  void testModuloBiggerIsSameValue() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS(bi%2000, bi);
+//  }
+//  
+//  void testModuloSameNumberIsZero() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS(bi%1000, 0);
+//  }
+//  
+//  void testModuloDivisorIsZero() {
+//    math::bigdecimal bi{1000};
+//    TS_ASSERT_EQUALS(bi%100, 0);
+//  }
+//  
+//  void testModuloDiffSignIsInverseElement() {
+//    math::bigdecimal bi{1000};
+//    math::bigdecimal mod{13};
+//    TS_ASSERT_EQUALS((bi%mod)+((-bi)%mod), mod);
+//  }
+//  
+//  void testModuloNegativesIsNegative() {
+//    math::bigdecimal bi{1000};
+//    math::bigdecimal mod{13};
+//    TS_ASSERT_EQUALS((bi%mod), -((-bi)%(-mod)));
+//  }
+};