From 5616bc817e1d48dc949fbe68cfd6564fc091f82e Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Fri, 5 Aug 2022 05:18:48 +0700 Subject: [PATCH] Add addition and subtraction to `BigInt` --- sources/Data/Database/BigInt.uc | 115 ++++++++++++++++++++- sources/Data/Database/Tests/TEST_BigInt.uc | 84 +++++++++++++-- 2 files changed, 190 insertions(+), 9 deletions(-) diff --git a/sources/Data/Database/BigInt.uc b/sources/Data/Database/BigInt.uc index 3a41954..9bf2d9f 100644 --- a/sources/Data/Database/BigInt.uc +++ b/sources/Data/Database/BigInt.uc @@ -23,6 +23,13 @@ class BigInt extends AcediaObject dependson(MathAPI); +enum BigIntCompareResult +{ + BICR_Less, + BICR_Equal, + BICR_Greater +}; + var private bool negative; // Digits array, from least to most significant: // For example, for 13524: @@ -70,7 +77,7 @@ private static function BigInt CreateMinimalNegative() // Removes unnecessary zeroes from leading digit positions `digits`. // Does not change contained value. -private final static function TrimLeadingZeroes() +private final function TrimLeadingZeroes() { local int i, zeroesToRemove; @@ -188,6 +195,53 @@ public final static function BigInt FromDecimal_S(string value) return result; } +public function BigIntCompareResult _compareModulus(BigInt other) +{ + local int i; + local array otherDigits; + + otherDigits = other.digits; + if (digits.length == otherDigits.length) + { + for (i = digits.length - 1; i >= 0; i -= 1) + { + if (digits[i] < otherDigits[i]) { + return BICR_Less; + } + if (digits[i] > otherDigits[i]) { + return BICR_Greater; + } + } + return BICR_Equal; + } + if (digits.length < otherDigits.length) { + return BICR_Less; + } + return BICR_Greater; +} + +public function BigIntCompareResult Compare(BigInt other) +{ + local BigIntCompareResult resultForModulus; + + if (negative && !other.negative) { + return BICR_Less; + } + if (!negative && other.negative) { + return BICR_Greater; + } + resultForModulus = _compareModulus(other); + if (resultForModulus == BICR_Equal) { + return BICR_Equal; + } + if ( (negative && (resultForModulus == BICR_Greater)) + || (!negative && (resultForModulus == BICR_Less)) ) + { + return BICR_Less; + } + return BICR_Greater; +} + private function _add(BigInt other) { local int i; @@ -201,6 +255,9 @@ private function _add(BigInt other) if (digits.length < otherDigits.length) { digits.length = otherDigits.length; } + else { + otherDigits.length = digits.length; + } carry = 0; for (i = 0; i < digits.length; i += 1) { @@ -211,11 +268,65 @@ private function _add(BigInt other) if (carry > 0) { digits[digits.length] = carry; } + // No leading zeroes can be created here, so no need to trim +} + +private function _sub(BigInt other) +{ + local int i; + local int carry, nextDigit; + local array minuendDigits, subtrahendDigits; + local BigIntCompareResult resultForModulus; + + if (other == none) { + return; + } + resultForModulus = _compareModulus(other); + if (resultForModulus == BICR_Equal) + { + negative = false; + digits.length = 1; + digits[0] = 0; + return; + } + if (resultForModulus == BICR_Less) + { + negative = !negative; + minuendDigits = other.digits; + subtrahendDigits = digits; + } + else + { + minuendDigits = digits; + subtrahendDigits = other.digits; + } + digits.length = minuendDigits.length; + subtrahendDigits.length = minuendDigits.length; + carry = 0; + for (i = 0; i < digits.length; i += 1) + { + nextDigit = int(minuendDigits[i]) - int(subtrahendDigits[i]) + carry; + if (nextDigit < 0) + { + nextDigit += 10; + carry = -1; + } + else { + carry = 0; + } + digits[i] = nextDigit; + } + TrimLeadingZeroes(); } public function BigInt Add(BigInt other) { - _add(other); + if (negative == other.negative) { + _add(other); + } + else { + _sub(other); + } return self; } diff --git a/sources/Data/Database/Tests/TEST_BigInt.uc b/sources/Data/Database/Tests/TEST_BigInt.uc index b5f8845..9e23360 100644 --- a/sources/Data/Database/Tests/TEST_BigInt.uc +++ b/sources/Data/Database/Tests/TEST_BigInt.uc @@ -27,10 +27,10 @@ protected static function TESTS() Test_Creating(); // So here we nee to test `ToText()` methods separately Test_ToText(); - Test_LeadingZeroes(); - //Test_AddingValues(); + Context("Testing basic arithmetic operations on `BigInt`s."); + Test_AddingValues(); } -// TODO: leading zeroes + protected static function Test_Creating() { Context("Testing creation of `BigInt`s."); @@ -93,11 +93,81 @@ protected static function Test_ToText() .ToString() == "-9827657892365923510176386357863078603212901078175829"); } -/*protected static function Test_AddingValues() +protected static function Test_AddingValues() +{ + SubTest_AddingSameSignValues(); + SubTest_AddingDifferentSignValues(); +} + +protected static function SubTest_AddingSameSignValues() +{ + local BigInt main, addition; + + Issue("Two positive `BigInt`s are incorrectly added."); + main = class'BigInt'.static.FromDecimal_S("927641962323462271784269213864"); + addition = class'BigInt'.static.FromDecimal_S("16324234842947239847239239"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "927658286558305219024116453103"); + main = class'BigInt'.static.FromDecimal_S("16324234842947239847239239"); + addition = class'BigInt'.static + .FromDecimal_S("927641962323462271784269213864"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "927658286558305219024116453103"); + main = class'BigInt'.static.FromDecimal_S("728965872936589276"); + addition = class'BigInt'.static.FromDecimal_S("728965872936589276"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "1457931745873178552"); + + Issue("Two negative `BigInt`s are incorrectly added."); + main = class'BigInt'.static.FromDecimal_S("-27641962323462271784269213864"); + addition = class'BigInt'.static.FromDecimal_S("-6324234842947239847239239"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "-27648286558305219024116453103"); + main = class'BigInt'.static.FromDecimal_S("-16324234842947239847239239"); + addition = class'BigInt'.static + .FromDecimal_S("-927641962323462271784269213864"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "-927658286558305219024116453103"); + main = class'BigInt'.static.FromDecimal_S("-728965872936589276"); + addition = class'BigInt'.static.FromDecimal_S("-728965872936589276"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "-1457931745873178552"); +} + +protected static function SubTest_AddingDifferentSignValues() { - Context("Testing adding values to `BigInt`"); - Issue("`JSONPointer` is incorrectly extracted."); -}*/ + local BigInt main, addition; + + Issue("Negative `BigInt`s is incorrectly added to positive one."); + main = class'BigInt'.static.FromDecimal_S("927641962323462271784269213864"); + addition = class'BigInt'.static.FromDecimal_S("-1632423484294239847239239"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "927640329899977977544421974625"); + main = class'BigInt'.static.FromDecimal_S("16324234842947239847239239"); + addition = class'BigInt'.static + .FromDecimal_S("-927641962323462271784269213864"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "-927625638088619324544421974625"); + main = class'BigInt'.static.FromDecimal_S("728965872936589276"); + addition = class'BigInt'.static.FromDecimal_S("-728965872936589276"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "0"); + + Issue("Positive `BigInt`s is incorrectly added to negative one."); + main = class'BigInt'.static.FromDecimal_S("-27641962323462271784269213864"); + addition = class'BigInt'.static.FromDecimal_S("6324234842947239847239239"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "-27635638088619324544421974625"); + main = class'BigInt'.static.FromDecimal_S("-16324234842947239847239239"); + addition = class'BigInt'.static + .FromDecimal_S("927641962323462271784269213864"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "927625638088619324544421974625"); + main = class'BigInt'.static.FromDecimal_S("-728965872936589276"); + addition = class'BigInt'.static.FromDecimal_S("728965872936589276"); + main.Add(addition); + TEST_ExpectTrue(main.ToString() == "0"); +} defaultproperties {