Browse Source

Merge branch 'decimal'

* decimal: (31 commits)
  Export bigdecimal.h properly.
  Fixing test printers.
  Fix bug where comparisons for operator==, etc were inverted (e.g. < was >, but == was still ==)
  Fix bug in bigdecimal constructor for >9 digits after the decimal point
  Fix bug with rescaling
  Fixing bug when performing division with a number of the form '0.*'
  Fixing bug where we pushed the numbers forward, causing them to be 1,000,000,000^diff times as large as appropriate.
  More test magic. Fixing bug in rescaling
  Add coverage for those last few hard-to-get places.
  Fix bug in math::biginteger::operator-= Fix comment in math::detail::divide about number of inner loops... (can that be fixed?)
  Breaking up final test in BigDecimalTest
  Renaming test type
  Since the bigdecimal tests for int values are largely identical to the ones for biginteger, combine them as a typed test.
  Some formatting things
  Convert to using GoogleMock for testing
  Adding tests for bigdecimal using integer values
  Removing unused shift10 export.
  Adding other comparison operators. Fixing typo in compare shortcut.
  Fixing operator equals
  Fixing division. Cleaning up multiplication code. Using getter functions where appropriate.
  ...
Sam Jaffe 7 years ago
parent
commit
f942742920

+ 24 - 0
bigdecimal-test/Info.plist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 195 - 147
bigdecimal.xcodeproj/project.pbxproj

@@ -7,47 +7,90 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		CD2EC1BC1F0AF2C300D49DF5 /* bigdecimal.h in Headers */ = {isa = PBXBuildFile; fileRef = CD2EC1BB1F0AF2C300D49DF5 /* bigdecimal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		CD2EC1BF1F0AF3B800D49DF5 /* 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 */; };
-		CD2EC1C61F0BCCBF00D49DF5 /* bignum_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD2EC1C41F0BCCBF00D49DF5 /* bignum_helper.cpp */; };
+		CD47694820AFA150009AA8BB /* libbigdecimal.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5FB2861F06EFEA005A0D61 /* libbigdecimal.dylib */; };
+		CD47698920AFACAE009AA8BB /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD47698020AFAC9C009AA8BB /* GoogleMock.framework */; };
+		CD47698A20AFB48C009AA8BB /* bignumber_integral_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3B82A31F114E1C0081E9FC /* bignumber_integral_test.cpp */; };
+		CD47698B20AFB4FD009AA8BB /* bigdecimal_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD14CA711F0DA9FC0091A168 /* bigdecimal_test.cpp */; };
+		CD47698C20B03C5E009AA8BB /* biginteger_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD47697520AFA6B2009AA8BB /* biginteger_test.cpp */; };
 		CD5FB2911F06EFEF005A0D61 /* biginteger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD5FB2801F06EF7D005A0D61 /* biginteger.cpp */; };
 		CD5FB2921F06EFF2005A0D61 /* biginteger.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5FB27F1F06EF70005A0D61 /* biginteger.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CD5FB2A31F06F03D005A0D61 /* biginteger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD5FB2801F06EF7D005A0D61 /* biginteger.cpp */; };
-		CD5FB2A41F06F048005A0D61 /* libbigdecimal_s.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5FB2981F06F030005A0D61 /* libbigdecimal_s.a */; };
-		CD5FB2A71F06F0D2005A0D61 /* bigdecimal_tc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD5FB2A61F06F0D2005A0D61 /* bigdecimal_tc.cpp */; };
+		CDB7F52F20B19E0D0053645C /* bignumber_test_printers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB7F52E20B19E0D0053645C /* bignumber_test_printers.cpp */; };
 /* End PBXBuildFile section */
 
-/* Begin PBXCopyFilesBuildPhase section */
-		CD5FB26F1F06EEAF005A0D61 /* CopyFiles */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = /usr/share/man/man1/;
-			dstSubfolderSpec = 0;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 1;
+/* Begin PBXContainerItemProxy section */
+		CD47694920AFA150009AA8BB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD5FB2691F06EEAF005A0D61 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = CD5FB2851F06EFEA005A0D61;
+			remoteInfo = bigdecimal;
+		};
+		CD47697F20AFAC9C009AA8BB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F861A685AEA0072A469;
+			remoteInfo = GoogleMock;
+		};
+		CD47698120AFAC9C009AA8BB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96ABD1A68600C00204102;
+			remoteInfo = gmock;
+		};
+		CD47698320AFAC9C009AA8BB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96B1F1A68634900204102;
+			remoteInfo = gtest;
+		};
+		CD47698520AFAC9C009AA8BB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F901A685AEA0072A469;
+			remoteInfo = GoogleMockTests;
+		};
+		CD47698720AFACA8009AA8BB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = 05818F851A685AEA0072A469;
+			remoteInfo = GoogleMock;
 		};
-/* End PBXCopyFilesBuildPhase section */
+/* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		CD14CA711F0DA9FC0091A168 /* bigdecimal_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bigdecimal_test.cpp; 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; };
-		CD5FB27E1F06EF64005A0D61 /* biginteger.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = biginteger.t.h; sourceTree = "<group>"; };
+		CD3B82A31F114E1C0081E9FC /* bignumber_integral_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bignumber_integral_test.cpp; sourceTree = "<group>"; };
+		CD47694320AFA150009AA8BB /* bigdecimal-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "bigdecimal-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD47694720AFA150009AA8BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CD47697520AFA6B2009AA8BB /* biginteger_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = biginteger_test.cpp; sourceTree = "<group>"; };
+		CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CD5FB27F1F06EF70005A0D61 /* biginteger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = biginteger.h; sourceTree = "<group>"; };
 		CD5FB2801F06EF7D005A0D61 /* biginteger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = biginteger.cpp; sourceTree = "<group>"; };
 		CD5FB2861F06EFEA005A0D61 /* libbigdecimal.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libbigdecimal.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
-		CD5FB2981F06F030005A0D61 /* libbigdecimal_s.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbigdecimal_s.a; sourceTree = BUILT_PRODUCTS_DIR; };
-		CD5FB2A61F06F0D2005A0D61 /* bigdecimal_tc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bigdecimal_tc.cpp; sourceTree = "<group>"; };
+		CDB7F52D20B19D3F0053645C /* bignumber_test_printers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bignumber_test_printers.h; sourceTree = "<group>"; };
+		CDB7F52E20B19E0D0053645C /* bignumber_test_printers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bignumber_test_printers.cpp; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
-		CD5FB26E1F06EEAF005A0D61 /* Frameworks */ = {
+		CD47694020AFA150009AA8BB /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CD5FB2A41F06F048005A0D61 /* libbigdecimal_s.a in Frameworks */,
+				CD47698920AFACAE009AA8BB /* GoogleMock.framework in Frameworks */,
+				CD47694820AFA150009AA8BB /* libbigdecimal.dylib in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -58,22 +101,36 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CD5FB2951F06F030005A0D61 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		CD47694420AFA150009AA8BB /* bigdecimal-test */ = {
+			isa = PBXGroup;
+			children = (
+				CD47694720AFA150009AA8BB /* Info.plist */,
+			);
+			path = "bigdecimal-test";
+			sourceTree = "<group>";
+		};
+		CD47697920AFAC9C009AA8BB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD47698020AFAC9C009AA8BB /* GoogleMock.framework */,
+				CD47698220AFAC9C009AA8BB /* gmock.framework */,
+				CD47698420AFAC9C009AA8BB /* gtest.framework */,
+				CD47698620AFAC9C009AA8BB /* GoogleMockTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		CD5FB2681F06EEAF005A0D61 = {
 			isa = PBXGroup;
 			children = (
+				CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */,
 				CD5FB27B1F06EED6005A0D61 /* include */,
 				CD5FB27C1F06EED6005A0D61 /* src */,
 				CD5FB27D1F06EED6005A0D61 /* test */,
+				CD47694420AFA150009AA8BB /* bigdecimal-test */,
 				CD5FB2721F06EEAF005A0D61 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -81,9 +138,8 @@
 		CD5FB2721F06EEAF005A0D61 /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				CD5FB2711F06EEAF005A0D61 /* bigdecimal_tc */,
 				CD5FB2861F06EFEA005A0D61 /* libbigdecimal.dylib */,
-				CD5FB2981F06F030005A0D61 /* libbigdecimal_s.a */,
+				CD47694320AFA150009AA8BB /* bigdecimal-test.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -92,6 +148,7 @@
 			isa = PBXGroup;
 			children = (
 				CD5FB27F1F06EF70005A0D61 /* biginteger.h */,
+				CD2EC1BB1F0AF2C300D49DF5 /* bigdecimal.h */,
 				CD2EC1C11F0BCCA700D49DF5 /* bignum_helper.h */,
 			);
 			path = include;
@@ -101,6 +158,7 @@
 			isa = PBXGroup;
 			children = (
 				CD5FB2801F06EF7D005A0D61 /* biginteger.cpp */,
+				CD2EC1BE1F0AF3B800D49DF5 /* bigdecimal.cpp */,
 				CD2EC1C41F0BCCBF00D49DF5 /* bignum_helper.cpp */,
 			);
 			path = src;
@@ -109,8 +167,11 @@
 		CD5FB27D1F06EED6005A0D61 /* test */ = {
 			isa = PBXGroup;
 			children = (
-				CD5FB27E1F06EF64005A0D61 /* biginteger.t.h */,
-				CD5FB2A61F06F0D2005A0D61 /* bigdecimal_tc.cpp */,
+				CD47697520AFA6B2009AA8BB /* biginteger_test.cpp */,
+				CDB7F52D20B19D3F0053645C /* bignumber_test_printers.h */,
+				CDB7F52E20B19E0D0053645C /* bignumber_test_printers.cpp */,
+				CD3B82A31F114E1C0081E9FC /* bignumber_integral_test.cpp */,
+				CD14CA711F0DA9FC0091A168 /* bigdecimal_test.cpp */,
 			);
 			path = test;
 			sourceTree = "<group>";
@@ -123,38 +184,32 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD5FB2921F06EFF2005A0D61 /* biginteger.h in Headers */,
+				CD2EC1BC1F0AF2C300D49DF5 /* bigdecimal.h in Headers */,
 				CD2EC1C21F0BCCA700D49DF5 /* bignum_helper.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CD5FB2961F06F030005A0D61 /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				CD2EC1C31F0BCCA700D49DF5 /* bignum_helper.h in Headers */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
-		CD5FB2701F06EEAF005A0D61 /* bigdecimal_tc */ = {
+		CD47694220AFA150009AA8BB /* bigdecimal-test */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = CD5FB2781F06EEAF005A0D61 /* Build configuration list for PBXNativeTarget "bigdecimal_tc" */;
+			buildConfigurationList = CD47694B20AFA150009AA8BB /* Build configuration list for PBXNativeTarget "bigdecimal-test" */;
 			buildPhases = (
-				CD5FB26D1F06EEAF005A0D61 /* Sources */,
-				CD5FB26E1F06EEAF005A0D61 /* Frameworks */,
-				CD5FB26F1F06EEAF005A0D61 /* CopyFiles */,
-				CD5FB2A51F06F04C005A0D61 /* ShellScript */,
+				CD47693F20AFA150009AA8BB /* Sources */,
+				CD47694020AFA150009AA8BB /* Frameworks */,
+				CD47694120AFA150009AA8BB /* Resources */,
 			);
 			buildRules = (
 			);
 			dependencies = (
+				CD47698820AFACA8009AA8BB /* PBXTargetDependency */,
+				CD47694A20AFA150009AA8BB /* PBXTargetDependency */,
 			);
-			name = bigdecimal_tc;
-			productName = bigdecimal;
-			productReference = CD5FB2711F06EEAF005A0D61 /* bigdecimal_tc */;
-			productType = "com.apple.product-type.tool";
+			name = "bigdecimal-test";
+			productName = "bigdecimal-test";
+			productReference = CD47694320AFA150009AA8BB /* bigdecimal-test.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
 		};
 		CD5FB2851F06EFEA005A0D61 /* bigdecimal */ = {
 			isa = PBXNativeTarget;
@@ -173,23 +228,6 @@
 			productReference = CD5FB2861F06EFEA005A0D61 /* libbigdecimal.dylib */;
 			productType = "com.apple.product-type.library.dynamic";
 		};
-		CD5FB2971F06F030005A0D61 /* bigdecimal_s */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = CD5FB2A01F06F030005A0D61 /* Build configuration list for PBXNativeTarget "bigdecimal_s" */;
-			buildPhases = (
-				CD5FB2941F06F030005A0D61 /* Sources */,
-				CD5FB2951F06F030005A0D61 /* Frameworks */,
-				CD5FB2961F06F030005A0D61 /* Headers */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = bigdecimal_s;
-			productName = bigdecimal_s;
-			productReference = CD5FB2981F06F030005A0D61 /* libbigdecimal_s.a */;
-			productType = "com.apple.product-type.library.static";
-		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -199,15 +237,12 @@
 				LastUpgradeCheck = 0720;
 				ORGANIZATIONNAME = "Sam Jaffe";
 				TargetAttributes = {
-					CD5FB2701F06EEAF005A0D61 = {
+					CD47694220AFA150009AA8BB = {
 						CreatedOnToolsVersion = 7.2.1;
 					};
 					CD5FB2851F06EFEA005A0D61 = {
 						CreatedOnToolsVersion = 7.2.1;
 					};
-					CD5FB2971F06F030005A0D61 = {
-						CreatedOnToolsVersion = 7.2.1;
-					};
 				};
 			};
 			buildConfigurationList = CD5FB26C1F06EEAF005A0D61 /* Build configuration list for PBXProject "bigdecimal" */;
@@ -220,39 +255,69 @@
 			mainGroup = CD5FB2681F06EEAF005A0D61;
 			productRefGroup = CD5FB2721F06EEAF005A0D61 /* Products */;
 			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = CD47697920AFAC9C009AA8BB /* Products */;
+					ProjectRef = CD47697820AFAC9C009AA8BB /* GoogleMock.xcodeproj */;
+				},
+			);
 			projectRoot = "";
 			targets = (
-				CD5FB2701F06EEAF005A0D61 /* bigdecimal_tc */,
 				CD5FB2851F06EFEA005A0D61 /* bigdecimal */,
-				CD5FB2971F06F030005A0D61 /* bigdecimal_s */,
+				CD47694220AFA150009AA8BB /* bigdecimal-test */,
 			);
 		};
 /* End PBXProject section */
 
-/* Begin PBXShellScriptBuildPhase section */
-		CD5FB2A51F06F04C005A0D61 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
+/* Begin PBXReferenceProxy section */
+		CD47698020AFAC9C009AA8BB /* GoogleMock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = GoogleMock.framework;
+			remoteRef = CD47697F20AFAC9C009AA8BB /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD47698220AFAC9C009AA8BB /* gmock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gmock.framework;
+			remoteRef = CD47698120AFAC9C009AA8BB /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD47698420AFAC9C009AA8BB /* gtest.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gtest.framework;
+			remoteRef = CD47698320AFAC9C009AA8BB /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD47698620AFAC9C009AA8BB /* GoogleMockTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = GoogleMockTests.xctest;
+			remoteRef = CD47698520AFAC9C009AA8BB /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+		CD47694120AFA150009AA8BB /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
-			inputPaths = (
-				"$(SRCROOT)/test/biginteger.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";
 		};
-/* End PBXShellScriptBuildPhase section */
+/* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
-		CD5FB26D1F06EEAF005A0D61 /* Sources */ = {
+		CD47693F20AFA150009AA8BB /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CD5FB2A71F06F0D2005A0D61 /* bigdecimal_tc.cpp in Sources */,
+				CD47698B20AFB4FD009AA8BB /* bigdecimal_test.cpp in Sources */,
+				CD47698A20AFB48C009AA8BB /* bignumber_integral_test.cpp in Sources */,
+				CD47698C20B03C5E009AA8BB /* biginteger_test.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -260,23 +325,51 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CDB7F52F20B19E0D0053645C /* bignumber_test_printers.cpp in Sources */,
+				CD2EC1BF1F0AF3B800D49DF5 /* bigdecimal.cpp in Sources */,
 				CD5FB2911F06EFEF005A0D61 /* biginteger.cpp in Sources */,
 				CD2EC1C51F0BCCBF00D49DF5 /* bignum_helper.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CD5FB2941F06F030005A0D61 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				CD5FB2A31F06F03D005A0D61 /* biginteger.cpp in Sources */,
-				CD2EC1C61F0BCCBF00D49DF5 /* bignum_helper.cpp in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXSourcesBuildPhase section */
 
+/* Begin PBXTargetDependency section */
+		CD47694A20AFA150009AA8BB /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = CD5FB2851F06EFEA005A0D61 /* bigdecimal */;
+			targetProxy = CD47694920AFA150009AA8BB /* PBXContainerItemProxy */;
+		};
+		CD47698820AFACA8009AA8BB /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = GoogleMock;
+			targetProxy = CD47698720AFACA8009AA8BB /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin XCBuildConfiguration section */
+		CD47694C20AFA150009AA8BB /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = "bigdecimal-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.bigdecimal-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CD47694D20AFA150009AA8BB /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = "bigdecimal-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.bigdecimal-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 		CD5FB2761F06EEAF005A0D61 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -360,22 +453,6 @@
 			};
 			name = Release;
 		};
-		CD5FB2791F06EEAF005A0D61 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				HEADER_SEARCH_PATHS = /usr/local/include;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Debug;
-		};
-		CD5FB27A1F06EEAF005A0D61 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				HEADER_SEARCH_PATHS = /usr/local/include;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Release;
-		};
 		CD5FB28F1F06EFEA005A0D61 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -402,43 +479,23 @@
 			};
 			name = Release;
 		};
-		CD5FB2A11F06F030005A0D61 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Debug;
-		};
-		CD5FB2A21F06F030005A0D61 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Release;
-		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		CD5FB26C1F06EEAF005A0D61 /* Build configuration list for PBXProject "bigdecimal" */ = {
+		CD47694B20AFA150009AA8BB /* Build configuration list for PBXNativeTarget "bigdecimal-test" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CD5FB2761F06EEAF005A0D61 /* Debug */,
-				CD5FB2771F06EEAF005A0D61 /* Release */,
+				CD47694C20AFA150009AA8BB /* Debug */,
+				CD47694D20AFA150009AA8BB /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CD5FB2781F06EEAF005A0D61 /* Build configuration list for PBXNativeTarget "bigdecimal_tc" */ = {
+		CD5FB26C1F06EEAF005A0D61 /* Build configuration list for PBXProject "bigdecimal" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CD5FB2791F06EEAF005A0D61 /* Debug */,
-				CD5FB27A1F06EEAF005A0D61 /* Release */,
+				CD5FB2761F06EEAF005A0D61 /* Debug */,
+				CD5FB2771F06EEAF005A0D61 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
@@ -452,15 +509,6 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CD5FB2A01F06F030005A0D61 /* Build configuration list for PBXNativeTarget "bigdecimal_s" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				CD5FB2A11F06F030005A0D61 /* Debug */,
-				CD5FB2A21F06F030005A0D61 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
 /* End XCConfigurationList section */
 	};
 	rootObject = CD5FB2691F06EEAF005A0D61 /* Project object */;

+ 86 - 0
include/bigdecimal.h

@@ -0,0 +1,86 @@
+//
+//  bigdecimal.h
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 7/3/17.
+//
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+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 bigdecimal & operator/=(bigdecimal &, bigdecimal const &);
+    
+    friend bool operator==(bigdecimal const &, bigdecimal const &);
+    friend bool operator!=(bigdecimal const &, bigdecimal const &);
+    friend bool operator<=(bigdecimal const &, bigdecimal const &);
+    friend bool operator< (bigdecimal const &, bigdecimal const &);
+    friend bool operator>=(bigdecimal const &, bigdecimal const &);
+    friend bool operator> (bigdecimal const &, bigdecimal const &);
+    
+    std::string to_string() const;
+  private:
+    bigdecimal(bool, uint64_t);
+    void set_scale(int32_t);
+    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.steps_, lhs.steps_);
+      swap(rhs.data, lhs.data);
+    }
+    
+    bool is_negative;
+    int32_t scale_{0}, steps_{0};
+    data_type data{};
+  };
+}

+ 2 - 3
include/bignum_helper.h

@@ -11,12 +11,11 @@
 
 namespace math { namespace detail {
   using data_type = std::vector<int32_t>;
-  constexpr const int32_t powers[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+  constexpr const int32_t powers[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1 };
   // 1 => GREATER, 0 => EQUAL, -1 => LESS
   int compare(data_type const & rhs, data_type const & lhs, size_t offset = 0);
   void add(data_type & rhs, data_type const & lhs, size_t offset = 0);
   void subtract_nounderflow(data_type & rhs, data_type const & lhs, size_t offset = 0);
-  void multiply(data_type & rhs, data_type const & lhs, size_t offset = 0);
+  void multiply(data_type & rhs, data_type const & lhs);
   data_type divide(data_type & remainder, data_type const & divisor);
-  data_type shift10(data_type const & data, int32_t places);
 } }

+ 289 - 0
src/bigdecimal.cpp

@@ -0,0 +1,289 @@
+//
+//  bigdecimal.cpp
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 7/3/17.
+//
+
+#include "bigdecimal.h"
+#include "bignum_helper.h"
+
+#include <cstdlib>
+
+using namespace math;
+
+static int32_t add_scale(int32_t scale) {
+  return scale / bigdecimal::SEG_DIGITS + (scale > 0 ? 1 : 0);
+}
+
+static size_t mul_scale(int32_t scale) {
+  return scale < 0 ? 0 : size_t(add_scale(scale));
+}
+
+static bool all_zero(bigdecimal::data_type const & data, size_t from) {
+  for (size_t i = from; i > 0; --i) {
+    if (data[i-1] != 0) return false;
+  }
+  return true;
+}
+
+static int compare(bigdecimal::data_type const & ldata, bigdecimal::data_type const & rdata, size_t offset) {
+  auto cmp = detail::compare(ldata, rdata, offset);
+  if (cmp == 0) { return !all_zero(ldata, offset); }
+  return cmp;
+}
+
+static int compare(bigdecimal::data_type const & ldata, int32_t lsteps, bigdecimal::data_type const & rdata, int32_t rsteps) {
+  return lsteps < rsteps ?
+  -compare(rdata, ldata, size_t(rsteps - lsteps))
+  : compare(ldata, rdata, size_t(lsteps - rsteps));
+}
+
+static bool is_one(bigdecimal::data_type const & data, int32_t scale) {
+  static bigdecimal::data_type ONE = {1};
+  return compare(data, add_scale(scale), ONE, 0) == 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() : is_negative(false), data({0}) {}
+
+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;
+    set_scale(int32_t(strlen(number)));
+    size_t elems = size_t(scale()/SEG_DIGITS);
+    data.insert(data.begin(), elems, 0);
+    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 oscale = scale();
+  operator=(other);
+  rescale(oscale);
+}
+
+void bigdecimal::set_scale(int32_t nscale) {
+  scale_ = nscale;
+  steps_ = add_scale(nscale);
+}
+
+void bigdecimal::rescale(int32_t nscale) {
+  auto const diff = steps_ - add_scale(nscale);
+  if (diff > 0) {
+    data.erase(data.begin(), data.begin() + diff);
+    if (data.empty()) { data.push_back(0); }
+  } else if (diff < 0) {
+    data.insert(data.begin(), size_t(-diff), 0);
+  }
+  auto const idx = -nscale % SEG_DIGITS;
+  if (scale() > nscale && nscale < 0 && idx) {
+    data.front() /= detail::powers[idx];
+    data.front() *= detail::powers[idx];
+  }
+  set_scale(nscale);
+}
+
+void bigdecimal::subtract_impl(bigdecimal const & lhs, bool is_sub) {
+  size_t const offset = size_t(steps_ - lhs.steps_);
+  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.steps_ - lhs.steps_);
+  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.steps_ - lhs.steps_);
+  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, true);
+  }
+  return rhs;
+}
+
+bigdecimal & math::operator*=(bigdecimal & rhs, bigdecimal const & lhs) {
+  int32_t const new_scale = rhs.scale() + lhs.scale();
+  bool is_neg = rhs.is_negative != lhs.is_negative;
+  if (rhs == bigdecimal::ZERO || lhs == bigdecimal::ZERO) {
+    rhs = bigdecimal::ZERO;
+  } else if (is_one(lhs.data, lhs.scale())) {
+    rhs.is_negative = is_neg;
+  } else if (is_one(rhs.data, rhs.scale())) {
+    rhs = lhs;
+    rhs.is_negative = is_neg;
+  } else {
+    detail::multiply(rhs.data, lhs.data);
+    auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
+    if (diff < 0) {
+      rhs.data.erase(rhs.data.begin(), rhs.data.begin() - diff);
+    }
+    rhs.set_scale(new_scale);
+    return rhs;
+  }
+  rhs.rescale(new_scale);
+  return rhs;
+}
+
+bigdecimal & math::operator/=(bigdecimal & rhs, bigdecimal const & lhs) {
+  int32_t const new_scale = rhs.scale() - lhs.scale();
+  rhs.is_negative ^= lhs.is_negative;
+  if (lhs == bigdecimal::ZERO) { throw std::domain_error("cannot divide by 0"); }
+  else if (rhs == bigdecimal::ZERO) {
+    rhs = bigdecimal::ZERO;
+  } else if (!is_one(lhs.data, lhs.scale())) {
+    if (rhs.scale() < new_scale) rhs.rescale(new_scale);
+    auto cmp = detail::compare(rhs.data, lhs.data);
+    if (cmp < 0) { rhs = bigdecimal::ZERO; }
+    else if (cmp == 0) { return rhs = {1, new_scale}; }
+    else {
+      rhs.data = detail::divide(rhs.data, lhs.data);
+      auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
+      if (diff > 0) {
+        rhs.data.erase(rhs.data.begin(), rhs.data.begin() + diff);
+      }
+      if (rhs.data.size() <= mul_scale(new_scale)) {
+        rhs.data.push_back(0);
+      }
+      rhs.set_scale(new_scale);
+      return rhs;
+    }
+  }
+  rhs.rescale(new_scale);
+  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;
+}
+
+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 && compare(lhs.data, lhs.steps_, rhs.data, rhs.steps_) == 0;
+}
+
+bool math::operator!=(bigdecimal const & rhs, bigdecimal const & lhs) {
+  return !(rhs == lhs);
+}
+
+bool math::operator<=(bigdecimal const & rhs, bigdecimal const & lhs) {
+  return !(rhs > lhs);
+}
+
+bool math::operator< (bigdecimal const & rhs, bigdecimal const & lhs) {
+  if (rhs.is_negative != lhs.is_negative) { return rhs.is_negative; }
+  else if (rhs.is_negative) { return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) > 0; }
+  else { return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) < 0; }
+}
+
+bool math::operator>=(bigdecimal const & rhs, bigdecimal const & lhs) {
+  return !(rhs < lhs);
+}
+
+bool math::operator> (bigdecimal const & rhs, bigdecimal const & lhs) {
+  return lhs < rhs;
+}
+
+bigdecimal const bigdecimal::ZERO{0}, bigdecimal::ONE{1}, bigdecimal::NEGATIVE_ONE{-1};
+
+std::string bigdecimal::to_string() const {
+  size_t const decimal_split = size_t(std::max(0, steps_));
+  int32_t const hidden = std::max(0, SEG_DIGITS * (-scale()/SEG_DIGITS));
+  size_t const chars = SEG_DIGITS * data.size() + size_t(hidden);
+  std::vector<char> output(chars + 3, '\0');
+  char * ptr = output.data();
+  if (is_negative) { *ptr++ = '-'; }
+  ptr += sprintf(ptr, "%d", data.back());
+  for (size_t i = data.size()-1; i > decimal_split; --i) {
+    ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i-1]);
+  }
+  if (scale() > 0) {
+    *ptr++ = '.';
+    for (size_t i = decimal_split; i > 1; --i) {
+      ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i-1]);
+    }
+    int32_t const val = scale() % SEG_DIGITS;
+    sprintf(ptr, "%0.*d", val, data[0]/detail::powers[SEG_DIGITS-val]);
+  } else if (data.back()) {
+    sprintf(ptr, "%0.*d", hidden, 0);
+  }
+  return output.data();
+}
+

+ 3 - 3
src/biginteger.cpp

@@ -22,7 +22,7 @@ static void read(int32_t & dst, char const * & str, size_t len) {
 }
 
 biginteger::biginteger()
-: biginteger(false, 0) {}
+: is_negative(false), data({0}) {}
 
 biginteger::biginteger(char const * number)
 : is_negative(number[0] == '-') {
@@ -75,7 +75,7 @@ biginteger & math::operator+=(biginteger & rhs, biginteger const & lhs) {
 biginteger & math::operator-=(biginteger & rhs, biginteger const & lhs) {
   if (lhs == biginteger::ZERO) { return rhs; }
   else if (rhs == biginteger::ZERO) { rhs = -lhs; }
-  if (rhs.is_negative != lhs.is_negative) {
+  else if (rhs.is_negative != lhs.is_negative) {
     detail::add(rhs.data, lhs.data);
   } else {
     rhs.subtract_impl(lhs, true);
@@ -133,7 +133,7 @@ biginteger math::operator/(biginteger rhs, biginteger const & lhs) {
 
 biginteger math::operator%(biginteger rhs, biginteger const & lhs) {
   if (lhs == biginteger::ZERO) { throw std::domain_error("cannot divide by 0"); }
-  else if (detail::compare(lhs.data, biginteger::ONE.data) == 0 ||
+  else if (lhs == biginteger::ONE ||
            rhs == biginteger::ZERO) { return biginteger::ZERO; }
   else {
     auto cmp = detail::compare(rhs.data, lhs.data);

+ 8 - 9
src/bignum_helper.cpp

@@ -72,7 +72,7 @@ namespace math { namespace detail {
     if (rhs[rbnd-1] == 0 && rbnd > 1) { rhs.pop_back(); }
   }
   
-  void multiply(data_type & rhs, data_type const & lhs, size_t offset) {
+  void multiply(data_type & rhs, data_type const & lhs) {
     size_t const rbnd = rhs.size(), lbnd = lhs.size();
     size_t const ubnd = rbnd + lbnd;
     rhs.resize(ubnd);
@@ -99,12 +99,10 @@ namespace math { namespace detail {
       }
     }
     while (rhs.back() == 0) { rhs.pop_back(); }
-    rhs.erase(rhs.begin(), rhs.begin() + ptrdiff_t(offset));
   }
     
   data_type shift10(data_type const & data, int32_t places) {
     int32_t shift = places / SEG_DIGITS;
-    if (places < 0) { --shift; }
     int64_t const pow = powers[places - (shift * SEG_DIGITS)];
     size_t const bnd = data.size();
     data_type rval(size_t((int32_t)bnd + shift) + 1);
@@ -115,10 +113,6 @@ namespace math { namespace detail {
       rval[o] += int32_t(product - (overflow * OVER_SEG));
       rval[o+1] += int32_t(overflow);
     }
-    if (shift < 0) {
-      // TODO rounding
-      rval.erase(rval.begin(), rval.begin()-shift);
-    }
     if (rval.back() == 0 && rval.size() > 1) { rval.pop_back(); }
     return rval;
   }
@@ -128,7 +122,12 @@ namespace math { namespace detail {
   }
   
   size_t digits(data_type const & data) {
-    return SEG_DIGITS * (data.size()-1) + digits(data.back());
+    size_t segDig = SEG_DIGITS * (data.size()-1);
+    if (data.back() == 0) {
+      return segDig - SEG_DIGITS + digits(data[data.size()-2]);
+    } else {
+      return segDig + digits(data.back());
+    }
   }
   
   data_type divide(data_type & remainder, data_type const & divisor) {
@@ -142,7 +141,7 @@ namespace math { namespace detail {
       do {
         subtract_nounderflow(remainder, value, shift);
         add(accum, powers[ipow], shift);
-      } while (compare(remainder, value, shift) >= 0); // Up to 9 times
+      } while (compare(remainder, value, shift) >= 0); // Up to 10 times
     } while (compare(remainder, divisor) >= 0);
     return accum;
   }

+ 167 - 0
test/bigdecimal_test.cpp

@@ -0,0 +1,167 @@
+//
+//  bigdecimal_test.cpp
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 5/18/18.
+//
+
+#include <gmock/gmock.h>
+
+#include "bigdecimal.h"
+#include "bignumber_test_printers.h"
+
+TEST(BigDecimalTest, ConstructIntegerAsDecimal) {
+  EXPECT_THAT(math::bigdecimal(1000, 2).to_string(), "1000.00");
+}
+
+TEST(BigDecimalTest, ConstructDecimal) {
+  EXPECT_THAT(math::bigdecimal("1000.10").to_string(), "1000.10");
+  EXPECT_THAT(math::bigdecimal("1000.0000000001").to_string(), "1000.0000000001");
+}
+
+TEST(BigDecimalTest, ConstructIntWithScaleAndStep) {
+  EXPECT_THAT(math::bigdecimal(1000000000, -2).to_string(), "1000000000");
+}
+
+TEST(BigDecimalTest, RescalePositiveAddsDecimalPlaces) {
+  math::bigdecimal dec(100);
+  EXPECT_THAT(dec.scale(), 0);
+  dec.rescale(2);
+  EXPECT_THAT(dec.to_string(), "100.00");
+}
+
+TEST(BigDecimalTest, RescaleNegativeDropsLowerOrderElements) {
+  math::bigdecimal dec(111);
+  EXPECT_THAT(dec.scale(), 0);
+  dec.rescale(-2);
+  EXPECT_THAT(dec.to_string(), "100");
+}
+
+TEST(BigDecimalTest, RescaleDownPermanantlyDropsLowOrderElements) {
+  math::bigdecimal dec("100.10");
+  EXPECT_THAT(dec.scale(), 2);
+  dec.rescale(0);
+  dec.rescale(2);
+  EXPECT_THAT(dec.to_string(), "100.00");
+}
+
+TEST(BigDecimalTest, ScaleCtorCanBeLossy) {
+  math::bigdecimal dec("123", -2);
+  EXPECT_THAT(dec.to_string(), "100");
+}
+
+TEST(BigDecimalTest, NonLossyScaleCtorEqualsNoScaleCtor) {
+  math::bigdecimal scl(100, -2);
+  math::bigdecimal nc(100);
+  EXPECT_THAT(scl, nc);
+}
+
+TEST(BigDecimalTest, ScaleBelowNegSegDoesntLoseAnything) {
+  math::bigdecimal dec("1000000000", -9);
+  EXPECT_THAT(dec.to_string(), "1000000000");
+}
+
+TEST(BigDecimalTest, AddingEqualScalesDoesNotAddTrailingDigitsOrLoseData) {
+  math::bigdecimal a("1000.10");
+  math::bigdecimal b("1000.01");
+  EXPECT_THAT(a.scale(), b.scale());
+  EXPECT_THAT((a+b).to_string(), "2000.11");
+}
+
+TEST(BigDecimalTest, AdditionNormalizeScaleToHighest) {
+  math::bigdecimal a("1000.10");
+  math::bigdecimal b("1000");
+  EXPECT_THAT(a.scale(), testing::Gt(b.scale()));
+  EXPECT_THAT((a+b).scale(), a.scale());
+}
+
+TEST(BigDecimalTest, InverseOfNumberCarriesSameScale) {
+  math::bigdecimal a("1.001");
+  EXPECT_THAT(a.scale(), (-a).scale());
+}
+
+TEST(BigDecimalTest, SubtractionNormalizeScaleToHighest) {
+  math::bigdecimal a("900.10");
+  math::bigdecimal b("1000");
+  EXPECT_THAT(a.scale(), testing::Gt(b.scale()));
+  EXPECT_THAT((a-b).scale(), a.scale());
+  EXPECT_THAT((b-a).scale(), a.scale());
+}
+
+class MultiplicationScaleTest : public testing::TestWithParam<ArithTuple> {};
+
+TEST_P(MultiplicationScaleTest, ScaleIsSumOfOperAndMultipScales) {
+  auto tup = GetParam();
+  auto out = tup.lhs * tup.rhs;
+  EXPECT_THAT(out.scale(), tup.lhs.scale() + tup.rhs.scale());
+  EXPECT_THAT(out.to_string(), tup.expected);
+}
+
+class DivisionScaleTest : public testing::TestWithParam<ArithTuple> {};
+
+TEST_P(DivisionScaleTest, ScaleIsDifferenceOfNumAndDenomScales) {
+  auto tup = GetParam();
+  auto out = tup.lhs / tup.rhs;
+  EXPECT_THAT(out.scale(), tup.lhs.scale() - tup.rhs.scale());
+  EXPECT_THAT(out.to_string(), tup.expected);
+}
+
+class EqTest : public testing::TestWithParam<BigDecPair> {};
+
+TEST_P(EqTest, DecimalsAreEqualEvenIfScalesAreNot) {
+  auto pair = GetParam();
+  EXPECT_THAT(pair.lhs, pair.rhs);
+}
+
+class DecimalLtTest : public testing::TestWithParam<BigDecPair> {};
+
+TEST_P(DecimalLtTest, IsLessThanEvenWithDifferentScales) {
+  auto pair = GetParam();
+  EXPECT_THAT(pair.lhs, testing::Lt(pair.rhs));
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+INSTANTIATE_TEST_CASE_P(BigDecimal, MultiplicationScaleTest,
+                        testing::Values(ArithTuple{{1, 1}, { 1,  0},  "1.0"},
+                                        ArithTuple{{1, 0}, { 1,  1},  "1.0"},
+                                        ArithTuple{{1, 0}, {10,  0}, "10"  },
+                                        ArithTuple{{1, 1}, {10,  0}, "10.0"},
+                                        ArithTuple{{1, 1}, {10, -1}, "10", },
+                                        ArithTuple{{"1.1"}, {"1.1"}, "1.21"},
+                                        ArithTuple{{"0.01"}, {100, -2}, "1"},
+                                        ArithTuple{{1, 5}, {1,  5}, "1.0000000000"},
+                                        ArithTuple{{1, 5}, {1000000000, -9}, "1000000000"},
+                                        ArithTuple{{100, -2}, {1000000000, -9}, "100000000000"},
+                                        ArithTuple{{10000, -4}, {100000, -5}, "1000000000"}));
+
+INSTANTIATE_TEST_CASE_P(BigDecimal, DivisionScaleTest,
+                        testing::Values(ArithTuple{{1, 1}, { 1,  0}, "1.0" },
+                                        ArithTuple{{1, 0}, { 1,  1}, "0"   },
+                                        ArithTuple{{1, 0}, {10,  0}, "0"   },
+                                        ArithTuple{{1, 1}, {10,  0}, "0.1" },
+                                        ArithTuple{{1, 1}, {10, -1}, "0.10"},
+                                        ArithTuple{{"1.1"}, {"1.1"}, "1"},
+                                        ArithTuple{{"0.01"}, {100, -2}, "0.0001"},
+                                        ArithTuple{{1, 5}, {1,  5}, "1"},
+                                        ArithTuple{{1, 5}, {1000000000, -9}, "0.00000000100000"},
+                                        ArithTuple{{100, -2}, {1000000000, -9}, "0.0000001"},
+                                        ArithTuple{{10000, -4}, {100000, -5}, "0.1"},
+                                        ArithTuple{{"11.0"}, {"1.10"}, "10"}));
+
+INSTANTIATE_TEST_CASE_P(BigDecimal, EqTest,
+                        testing::Values(BigDecPair{{  0}, {  0,  4}},
+                                        BigDecPair{{100}, {100, -2}},
+                                        BigDecPair{{1000000000, -4}, {1000000000, -9}},
+                                        BigDecPair{{"0.1", 1}, {"0.10", 2}}));
+
+INSTANTIATE_TEST_CASE_P(BigDecimal, DecimalLtTest,
+                        testing::Values(BigDecPair{{ 0, 0}, { 1, 4}},
+                                        BigDecPair{{ 0, 4}, { 1, 0}},
+                                        BigDecPair{{ 0, 4}, { 1, 4}},
+                                        BigDecPair{{-1, 4}, { 1, 4}},
+                                        BigDecPair{{-2, 0}, {-1, 4}},
+                                        BigDecPair{{-2, 4}, {-1, 0}},
+                                        BigDecPair{{-2, 4}, {-1, 4}}));
+
+#pragma clang diagnostic pop

+ 0 - 175
test/biginteger.t.h

@@ -1,175 +0,0 @@
-//
-//  biginteger.t.h
-//  bigdecimal
-//
-//  Created by Sam Jaffe on 6/30/17.
-//
-
-#pragma once
-
-#include "biginteger.h"
-
-#include <cxxtest/TestSuite.h>
-
-class biginteger_TestSuite : public CxxTest::TestSuite {
-public:
-  void testConstructFromStringIsSameValueAsFromInt() {
-    using bi = math::biginteger;
-    TS_ASSERT_EQUALS(bi("1000000"), bi(1000000));
-  }
-  
-  void testAddPastBounds() {
-    math::biginteger bi{999999999ULL};
-    TS_ASSERT_EQUALS((bi+1).to_string(), "1000000000");
-  }
-  
-  void testAddReciprocalIsZero() {
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi+(-bi)).to_string(), "0");
-  }
-
-  void testAddNegativeLargerGivesNegative() {
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi+(-1001)).to_string(), "-1");
-  }
-
-  void testAddNegativeSmallerGivesPositive() {
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi+(-999)).to_string(), "1");
-  }
-  
-  void testSubSelfIsZero() {
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi-bi).to_string(), "0");
-  }
-  
-  void testNegativeMinusNegativeIncreateAbs() {
-    math::biginteger bi{-1000};
-    TS_ASSERT_EQUALS((bi-100).to_string(), "-1100");
-  }
-  
-  void testSubLargerGivesNegative() {
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi-1001).to_string(), "-1");
-  }
-  
-  void testSubSmallerGivesPositive() {
-    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, ZERO);
-    TS_ASSERT_EQUALS(ZERO*bi, ZERO);
-  }
-  
-  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");
-  }
-  
-  void testMultiplyNoOverflow() {
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi*bi).to_string(), "1000000");
-  }
-  
-  void testDivideByZeroThrows() {
-    auto &ZERO =  math::biginteger::ZERO;
-    math::biginteger bi{1000};
-    TS_ASSERT_THROWS(bi/ZERO, std::domain_error);
-  }
-  
-  void testDivideByOneReturnsValue() {
-    auto &ONE =  math::biginteger::ONE;
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS(bi/ONE, bi);
-  }
-
-  void testDivideByNegativeOneReturnsInverse() {
-    auto &NEGATIVE_ONE =  math::biginteger::NEGATIVE_ONE;
-    math::biginteger bi{1000};
-    TS_ASSERT_EQUALS((bi/NEGATIVE_ONE).to_string(), "-1000");
-  }
-
-  void testDivisionWithMultipleMultSubSteps() {
-    math::biginteger bi{1112};
-    TS_ASSERT_EQUALS((bi/2).to_string(), "556");
-  }
-  
-  void testDivisionDroppingNumberOfCells() {
-    math::biginteger bi{1000000000ULL};
-    TS_ASSERT_EQUALS((bi/2).to_string(), "500000000");
-  }
-  
-  void testDivisionByBiggerNumberIsZero() {
-    math::biginteger bi{1000ULL};
-    TS_ASSERT_EQUALS((bi/1001).to_string(), "0");
-  }
-  
-  void testDivisionWithLargeNumbers() {
-    math::biginteger big{"999999998000000001"};
-    TS_ASSERT_EQUALS(big.to_string(), "999999998000000001");
-    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)));
-  }
-};

+ 76 - 0
test/biginteger_test.cpp

@@ -0,0 +1,76 @@
+//
+//  biginteger_test.cpp
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 5/18/18.
+//
+
+#include "bignumber_test_printers.h"
+#include <gmock/gmock.h>
+
+#include "biginteger.h"
+
+TEST(BigIntegerTest, ModuloZeroThrows) {
+  math::biginteger bi{1000};
+  EXPECT_THROW(bi%0, std::domain_error);
+}
+
+TEST(BigIntegerTest, ModuloBiggerIsSameValue) {
+  math::biginteger bi{1000};
+  EXPECT_THAT(bi%2000, bi);
+}
+
+TEST(BigIntegerTest, ModuloSameNumberIsZero) {
+  math::biginteger bi{1000};
+  EXPECT_THAT(bi%1000, 0);
+}
+
+TEST(BigIntegerTest, ModuloDivisorIsZero) {
+  math::biginteger bi{1000};
+  EXPECT_THAT(bi%100, 0);
+}
+
+TEST(BigIntegerTest, ModuloDiffSignIsInverseElement) {
+  math::biginteger bi{1000};
+  math::biginteger mod{13};
+  EXPECT_THAT((bi%mod)+((-bi)%mod), mod);
+}
+
+TEST(BigIntegerTest, ModuloNegativesIsNegative) {
+  math::biginteger bi{1000};
+  math::biginteger mod{13};
+  EXPECT_THAT((bi%mod), -((-bi)%(-mod)));
+}
+
+class BigIntModuloTest : public testing::TestWithParam<BigIntPair> {};
+
+TEST_P(BigIntModuloTest, IsZero) {
+  auto & ZERO = math::biginteger::ZERO;
+  auto pair = GetParam();
+  EXPECT_THAT(std::get<0>(pair)%std::get<1>(pair), ZERO);
+}
+
+class LtTest : public testing::TestWithParam<BigIntPair> {};
+
+TEST_P(LtTest, IsLessThan) {
+  auto pair = GetParam();
+  EXPECT_THAT(std::get<0>(pair), testing::Lt(std::get<1>(pair)));
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+INSTANTIATE_TEST_CASE_P(BigInteger, LtTest,
+                        testing::Values(BigIntPair{-1,  1},
+                                        BigIntPair{ 0,  1},
+                                        BigIntPair{-2, -1},
+                                        BigIntPair{ 1000000000,  1000000001},
+                                        BigIntPair{-1000000001, -1000000000}));
+
+INSTANTIATE_TEST_CASE_P(ZeroModAny, BigIntModuloTest,
+                        testing::Combine(testing::Values(0),
+                        testing::Values(1, 2, 10, 100, -4, 1000000000, -1000000000)));
+
+INSTANTIATE_TEST_CASE_P(AnyModOne, BigIntModuloTest,
+                        testing::Combine(testing::Values(1, 2, 10, 100, -4, 1000000000, -1000000000),
+                                         testing::Values(1)));
+#pragma clang diagnostic pop

+ 186 - 0
test/bignumber_integral_test.cpp

@@ -0,0 +1,186 @@
+//
+//  bigdecimal_integral_test.cpp
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 5/18/18.
+//
+
+#include <gmock/gmock.h>
+
+#include "biginteger.h"
+#include "bigdecimal.h"
+#include "bignumber_test_printers.h"
+
+template <typename T>
+class BigIntLikeTest : public testing::Test {
+public:
+  T typeget_;
+};
+#define BigNumber decltype(this->typeget_)
+
+typedef testing::Types<math::biginteger, math::bigdecimal> IntLikeBigNumbers;
+TYPED_TEST_CASE(BigIntLikeTest, IntLikeBigNumbers);
+
+TYPED_TEST(BigIntLikeTest, DefaultConstructorCreatesZero) {
+  EXPECT_THAT(BigNumber(), 0);
+  EXPECT_THAT(BigNumber().to_string(), "0");
+}
+
+TYPED_TEST(BigIntLikeTest, StringAndNumberConstructorsEqual) {
+  EXPECT_THAT(BigNumber( "1000000"), BigNumber( 1000000));
+  EXPECT_THAT(BigNumber("-1000000"), BigNumber(-1000000));
+}
+
+TYPED_TEST(BigIntLikeTest, AddPastBounds) {
+  BigNumber bi{999999999ULL};
+  EXPECT_THAT((bi+1).to_string(), "1000000000");
+}
+
+TYPED_TEST(BigIntLikeTest, AddReciprocalIsZero) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi+(-bi)).to_string(), "0");
+}
+
+TYPED_TEST(BigIntLikeTest, AddNegativeLargerGivesNegative) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi+(-1001)).to_string(), "-1");
+}
+
+TYPED_TEST(BigIntLikeTest, AddNegativeSmallerGivesPositive) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi+(-999)).to_string(), "1");
+}
+
+TYPED_TEST(BigIntLikeTest, AddNumberZeroGivesOriginal) {
+  BigNumber bi{1000};
+  EXPECT_THAT(bi+0, bi);
+}
+
+TYPED_TEST(BigIntLikeTest, AddZeroNumberGivesOriginal) {
+  BigNumber bi{1000};
+  EXPECT_THAT(0+bi, bi);
+}
+
+TYPED_TEST(BigIntLikeTest, SubSelfIsZero) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi-bi).to_string(), "0");
+}
+
+TYPED_TEST(BigIntLikeTest, SubtractZeroGivesOriginal) {
+  BigNumber bi{1000};
+  EXPECT_THAT(bi-0, bi);
+}
+
+TYPED_TEST(BigIntLikeTest, ZeroMinusNumberEqualsReciprocal) {
+  BigNumber bi{1000};
+  EXPECT_THAT((0-bi).to_string(), (-bi).to_string());
+}
+
+TYPED_TEST(BigIntLikeTest, NegativeMinusNegativeIncreateAbs) {
+  BigNumber bi{-1000};
+  EXPECT_THAT((bi-100).to_string(), "-1100");
+}
+
+TYPED_TEST(BigIntLikeTest, SubLargerGivesNegative) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi-1001).to_string(), "-1");
+}
+
+TYPED_TEST(BigIntLikeTest, SubSmallerGivesPositive) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi-999).to_string(), "1");
+}
+
+TYPED_TEST(BigIntLikeTest, SubUnderflowBorrows) {
+  BigNumber bi{1000000000ULL};
+  EXPECT_THAT((bi-1).to_string(), "999999999");
+}
+
+TYPED_TEST(BigIntLikeTest, MultiplyZeroReturnsZero) {
+  auto &ZERO =  BigNumber::ZERO;
+  BigNumber bi{999999999ULL};
+  EXPECT_THAT(bi*ZERO, ZERO);
+  EXPECT_THAT(ZERO*bi, ZERO);
+}
+
+TYPED_TEST(BigIntLikeTest, MultiplyOneReturnsValue) {
+  auto &ONE =  BigNumber::ONE;
+  BigNumber bi{999999999ULL};
+  EXPECT_THAT((bi*ONE).to_string(), "999999999");
+  EXPECT_THAT((ONE*bi).to_string(), "999999999");
+}
+
+TYPED_TEST(BigIntLikeTest, MultiplyNegativeOneReturnsInverse) {
+  auto &NEGATIVE_ONE =  BigNumber::NEGATIVE_ONE;
+  BigNumber bi{999999999ULL};
+  EXPECT_THAT((bi*NEGATIVE_ONE).to_string(), "-999999999");
+  EXPECT_THAT((NEGATIVE_ONE*bi).to_string(), "-999999999");
+}
+
+TYPED_TEST(BigIntLikeTest, MultiplyOverflowsIntoNextCell) {
+  BigNumber bi{999999999ULL};
+  EXPECT_THAT((bi*bi).to_string(), "999999998000000001");
+}
+
+TYPED_TEST(BigIntLikeTest, MultiplyCarryIntoNextCell) {
+  BigNumber bi{999999999ULL};
+  BigNumber big{bi*bi};
+  EXPECT_THAT((big*big).to_string(),
+                   "999999996000000005999999996000000001");
+}
+
+TYPED_TEST(BigIntLikeTest, MultiplyNoOverflow) {
+  BigNumber bi{1000};
+  EXPECT_THAT((bi*bi).to_string(), "1000000");
+}
+
+TYPED_TEST(BigIntLikeTest, DivideByZeroThrows) {
+  auto &ZERO =  BigNumber::ZERO;
+  BigNumber bi{1000};
+  EXPECT_THROW(bi/ZERO, std::domain_error);
+}
+
+TYPED_TEST(BigIntLikeTest, DivideByOneReturnsValue) {
+  auto &ONE =  BigNumber::ONE;
+  BigNumber bi{1000};
+  EXPECT_THAT(bi/ONE, bi);
+}
+
+TYPED_TEST(BigIntLikeTest, DivideBySelfReturnsOne) {
+  auto &ONE =  BigNumber::ONE;
+  BigNumber bi{1000};
+  EXPECT_THAT(bi/bi, ONE);
+}
+
+TYPED_TEST(BigIntLikeTest, DivideByNegativeOneReturnsInverse) {
+  auto &NEGATIVE_ONE =  BigNumber::NEGATIVE_ONE;
+  BigNumber bi{1000};
+  EXPECT_THAT((bi/NEGATIVE_ONE).to_string(), "-1000");
+}
+
+TYPED_TEST(BigIntLikeTest, DivisionWithMultipleMultSubSteps) {
+  BigNumber bi{1112};
+  EXPECT_THAT((bi/2).to_string(), "556");
+}
+
+TYPED_TEST(BigIntLikeTest, DivisionDroppingNumberOfCells) {
+  BigNumber bi{1000000000ULL};
+  EXPECT_THAT((bi/2).to_string(), "500000000");
+}
+
+TYPED_TEST(BigIntLikeTest, DivisionByBiggerNumberIsZero) {
+  BigNumber bi{1000ULL};
+  EXPECT_THAT((bi/1001).to_string(), "0");
+}
+
+TYPED_TEST(BigIntLikeTest, DivisionWithLargeNumbers) {
+  BigNumber big{"999999998000000001"};
+  EXPECT_THAT(big.to_string(), "999999998000000001");
+  EXPECT_THAT((big/999999999ULL).to_string(),
+                   "999999999");
+}
+
+TYPED_TEST(BigIntLikeTest, ZeroDividedByAnyReturnsZero) {
+  auto & ZERO = BigNumber::ZERO;
+  EXPECT_THAT(ZERO/25, ZERO);
+}

+ 42 - 0
test/bignumber_test_printers.cpp

@@ -0,0 +1,42 @@
+//
+//  bignumber_test_printers.cpp
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 5/20/18.
+//
+
+#include "bignumber_test_printers.h"
+
+#include <iostream>
+
+void math::PrintTo(math::bigdecimal const & dec, std::ostream * out) {
+  (*out) << dec.to_string() << "(" << dec.scale() << ")";
+}
+
+void math::PrintTo(math::biginteger const & dec, std::ostream * out) {
+  (*out) << dec.to_string();
+}
+
+void PrintTo(BigDecPair const & tup, std::ostream * out) {
+  (*out) << "{ lhs = ";
+  math::PrintTo(tup.lhs, out);
+  (*out) << ", rhs = ";
+  math::PrintTo(tup.rhs, out);
+  (*out) << " }";
+}
+
+void std::PrintTo(BigIntPair const & tup, std::ostream * out) {
+  (*out) << "{ lhs = ";
+  math::PrintTo(std::get<0>(tup), out);
+  (*out) << ", rhs = ";
+  math::PrintTo(std::get<1>(tup), out);
+  (*out) << " }";
+}
+
+void PrintTo(ArithTuple const & tup, std::ostream * out) {
+  (*out) << "{ lhs = ";
+  math::PrintTo(tup.lhs, out);
+  (*out) << ", rhs = ";
+  math::PrintTo(tup.rhs, out);
+  (*out) << " } -> " << tup.expected;
+}

+ 29 - 0
test/bignumber_test_printers.h

@@ -0,0 +1,29 @@
+//
+//  bignumber_test_printers.h
+//  bigdecimal
+//
+//  Created by Sam Jaffe on 5/20/18.
+//
+
+#pragma once
+
+#include "biginteger.h"
+#include "bigdecimal.h"
+
+#include <string>
+#include <iosfwd>
+#include <tuple>
+
+struct ArithTuple { math::bigdecimal lhs, rhs; std::string expected; };
+struct BigDecPair { math::bigdecimal lhs, rhs; };
+using BigIntPair = std::tuple<math::biginteger, math::biginteger>;
+
+namespace math {
+  void PrintTo(math::bigdecimal const & dec, std::ostream * out);
+  void PrintTo(math::biginteger const & dec, std::ostream * out);
+}
+void PrintTo(BigDecPair const & tup, std::ostream * out);
+void PrintTo(ArithTuple const & tup, std::ostream * out);
+namespace std {
+  void PrintTo(BigIntPair const & tup, std::ostream * out);
+}