diff --git a/sources/BaseRealm/API/Math/BigInt.uc b/sources/BaseRealm/API/Math/BigInt.uc index 66758d6..14bd402 100644 --- a/sources/BaseRealm/API/Math/BigInt.uc +++ b/sources/BaseRealm/API/Math/BigInt.uc @@ -1,7 +1,8 @@ /** - * A simple big integer implementation, mostly to allow Acedia's databases to - * store integers of arbitrary size. - * Copyright 2022 Anton Tarasenko + * Author: dkanus + * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore + * License: GPL + * Copyright 2022-2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -19,41 +20,22 @@ * along with Acedia. If not, see . */ class BigInt extends AcediaObject - dependson(MathAPI); - -/** - * # `BigInt` - * - * A simple big integer implementation, mostly to allow Acedia's databases to - * store integers of arbitrary size. It can be used for long arithmetic - * computations, but it was mainly meant as a players' statistics counter and, - * therefore, not optimized for performing large amount of operations. - * - * ## Usage - * - * `BigInt` can be created from both `int` and decimal `BaseText`/`string` - * representation, preferably by `MathAPI` (`_.math.`) methods - * `ToBigInt()`/`MakeBigInt()`. - * Then it can be combined either directly with other `BigInt` or with - * `int`/`BaseText`/`string` through available arithmetic operations. - * To make use of stored value one can convert it back into either `int` or - * decimal `BaseText`/`string` representation. - * Newly allocated `BigInt` is guaranteed to hold `0` as value. - */ - -/** - * `BigInt` data as a `struct` - meant to be used to store `BigInt`'s values - * inside the local databases. - */ -struct BigIntData -{ - var bool negative; + dependson(MathApi); + +/// A simple big integer implementation. +/// +/// [`BigInt`]'s main purpose is to allow Acedia's databases to store integers of arbitrary size. +/// It can be used for long arithmetic computations, but it was mainly meant as a players' +/// statistics counter and, therefore, not optimized for performing large amount of operations. + +/// [`BigInt`] data as a struct - meant to be used to store [`BigInt`]'s values inside +/// the local databases. +struct BigIntData { + var bool negative; var array digits; }; -/** - * Used to represent a result of comparison for `BigInt`s with each other. - */ +/// Result of comparison for [`BigInt`]s with each other. enum BigIntCompareResult { BICR_Less, @@ -61,60 +43,60 @@ enum BigIntCompareResult BICR_Greater }; -// Does stored `BigInt` has negative sign? +/// Does stored [`BigInt`] have a negative sign? var private bool negative; -// Digits array, from least to most significant. For example, for 13524: -// `digits[0] = 4` -// `digits[1] = 2` -// `digits[2] = 5` -// `digits[3] = 3` -// `digits[4] = 1` -// Valid `BigInt` should not have this array empty: zero should be -// represented by an array with a single `0`-element. -// This isn't a most efficient representation for `BigInt`, but it's easy -// to convert to and from decimal representation. -// INVARIANT: this array must not have leading (in the sense of significance) -// zeroes. That is, last element of the array should not be a `0`. The only -// exception if if stored value is `0`, then `digits` must consist of a single -// `0` element. +/// Digits array, from least to most significant. For example, for 13524: +/// +/// ``` +/// `digits[0] = 4` +/// `digits[1] = 2` +/// `digits[2] = 5` +/// `digits[3] = 3` +/// `digits[4] = 1` +/// ``` +/// +/// Valid [`BigInt`] should not have this array empty: zero should be represented by an array with +/// a single `0`-element. +/// This isn't a most efficient representation for [`BigInt`], but it's easy to convert to and from +/// decimal representation. +/// +/// # Invariants +/// +/// This array must not have leading (in the sense of significance) zeroes. +/// That is, last element of the array should not be a `0`. +/// The only exception if if stored value is `0`, then `digits` must consist of +/// a single `0` element. var private array digits; -// Constants useful for converting `BigInt` back to `int`, while avoiding -// overflow. -// We can add less digits than that without any fear of overflow +/// Constants useful for converting [`BigInt`] back to [`int`], while avoiding overflow. +/// We can add less digits than that without any fear of overflow. const DIGITS_IN_MAX_INT = 10; -// Maximum `int` value is `2147483647`, so in case most significant digit -// is 10th and is `2` (so number has a form of "2xxxxxxxxx"), to check for -// overflow we only need to compare combination of the rest of the digits with -// this constant. -const ALMOST_MAX_INT = 147483647; -// To add last digit we add/subtract that digit multiplied by this value. +/// Maximum [`int`] value is `2147483647`, so in case most significant digit is 10th and is `2` +/// (so number has a form of `2xxxxxxxxx`), to check for overflow we only need to compare +/// combination of the rest of the digits with this constant. +const ALMOST_MAX_INT = 147483647; +/// To add last digit we add/subtract that digit multiplied by this value. const LAST_DIGIT_ORDER = 1000000000; -protected function Constructor() -{ +protected function Constructor() { SetZero(); } -protected function Finalizer() -{ +protected function Finalizer() { negative = false; digits.length = 0; } // Auxiliary method to set current value to zero -private function BigInt SetZero() -{ +private function SetZero() { negative = false; digits.length = 1; digits[0] = 0; - return self; } -// Minimal `int` value `-2,147,483,648` is somewhat of a pain to handle, so -// just use this auxiliary pre-made constructor for it -private function BigInt SetMinimalNegative() -{ +// Minimal [`int`] value `-2,147,483,648` is somewhat of a pain to handle, so just use this +// auxiliary pre-made constructor for it +private function SetMinimalNegative() { negative = true; digits.length = 10; digits[0] = 8; @@ -127,26 +109,23 @@ private function BigInt SetMinimalNegative() digits[7] = 4; digits[8] = 1; digits[9] = 2; - return self; } -// Removes unnecessary zeroes from leading digit positions `digits`. -// Does not change contained value. -private final function TrimLeadingZeroes() -{ +// Removes unnecessary zeroes from leading digit positions `digits`. +// Does not change contained value. +private final function TrimLeadingZeroes() { local int i, zeroesToRemove; - // Find how many leading zeroes there is. - // Since `digits` stores digits from least to most significant, we need - // to check from the end of `digits` array. - for (i = digits.length - 1; i >= 0; i -= 1) - { + // Finds how many leading zeroes there is. + // Since `digits` stores digits from least to most significant, we need to check from the end of + // `digits` array. + for (i = digits.length - 1; i >= 0; i -= 1) { if (digits[i] != 0) { break; } zeroesToRemove += 1; } - // `digits` must not be empty, enforce `0` value in that case + // `digits` must not be empty, enforce `0` value in that case if (zeroesToRemove >= digits.length) { SetZero(); } @@ -155,144 +134,125 @@ private final function TrimLeadingZeroes() } } -/** - * Changes current value of `BigInt` to given `BigInt` value. - * - * @param value New value of the caller `BigInt`. If `none` is given, - * method does nothing. - * @return Self-reference to allow for method chaining. - */ -public final function BigInt Set(BigInt value) +/// Changes current value of [`BigInt`] to given value. +public final function Set(BigInt value) { - if (value == none) { - return self; + if (value != none) { + value.TrimLeadingZeroes(); + digits = value.digits; + negative = value.negative; } - value.TrimLeadingZeroes(); - digits = value.digits; - negative = value.negative; - return self; } -/** - * Changes current value of `BigInt` to given `int` value `value`. - * - * Cannot fail. - * - * @param value New value of the caller `BigInt`. - * @return Self-reference to allow for method chaining. - */ -public final function BigInt SetInt(int value) -{ - local MathAPI.IntegerDivisionResult divisionResult; +/// Changes current value of [`BigInt`] to given value. +public final function SetInt(int value) { + local MathApi.IntegerDivisionResult divisionResult; negative = false; digits.length = 0; - if (value < 0) - { - // Treat special case of minimal `int` value `-2,147,483,648` that - // won't fit into positive `int` as special and use pre-made + if (value < 0) { + // Treat special case of minimal [`int`] value `-2,147,483,648` that + // won't fit into positive [`int`] as special and use pre-made // specialized constructor `CreateMinimalNegative()` - if (value < -MaxInt) { - return SetMinimalNegative(); - } - else - { + if (value < -maxInt) { + SetMinimalNegative(); + return; + } else { negative = true; value *= -1; } } if (value == 0) { digits[0] = 0; - } - else - { - while (value > 0) - { + } else { + while (value > 0) { divisionResult = __().math.IntegerDivision(value, 10); value = divisionResult.quotient; digits[digits.length] = divisionResult.remainder; } } TrimLeadingZeroes(); - return self; } -/** - * Changes current value of `BigInt` to the value, given by decimal - * representation inside `value` argument. - * - * If invalid decimal representation (digits only, possibly with leading sign) - * is given - behavior is undefined. Otherwise cannot fail. - * - * @param value New value of the caller `BigInt`, given by decimal - * its representation. If `none` is given, method does nothing. - * @return Self-reference to allow for method chaining. - */ -public final function BigInt SetDecimal(BaseText value) -{ - local int i; - local byte nextDigit; - local Parser parser; - local Basetext.Character nextCharacter; +/// Changes current value of [`BigInt`] to the value, given by decimal representation. +/// +/// If `none` or invalid decimal representation (digits only, possibly with leading sign) is given +/// as an argument, caller's value won't change. +/// Returns `true` in case of success and `false` otherwise. +public final function bool SetDecimal(BaseText value) { + local int i; + local byte nextDigit; + local bool newNegative; + local array newDigits; + local Parser parser; + local Basetext.Character nextCharacter; if (value == none) { - return none; + return false; } parser = value.Parse(); - negative = parser.Match(P("-")).Ok(); - if (!parser.Ok()) { - parser.R().Match(P("+")).Ok(); - } - // Reset to valid state whether sign was consumed or not + newNegative = ParseSign(parser); + // Reset to valid state whether sign was consumed or not parser.Confirm(); parser.R(); - // Reset current value - digits.length = 0; - digits.length = parser.GetRemainingLength(); - // Parse new one - i = digits.length - 1; - while (!parser.HasFinished()) - { + newDigits.length = parser.GetRemainingLength(); + // Parse new one + i = newDigits.length - 1; + while (!parser.HasFinished()){ // This should not happen, but just in case if (i < 0) { break; } parser.MCharacter(nextCharacter); - nextDigit = Clamp(__().text.CharacterToInt(nextCharacter), 0, 9); - digits[i] = nextDigit; + nextDigit = __().text.CharacterToInt(nextCharacter, 10); + if (nextDigit < 0) { + return false; + } + newDigits[i] = nextDigit; i -= 1; } parser.FreeSelf(); + digits = newDigits; + negative = newNegative; TrimLeadingZeroes(); - return self; + return true; } -/** - * Changes current value of `BigInt` to the value, given by decimal - * representation inside `value` argument. - * - * If invalid decimal representation (digits only, possibly with leading sign) - * is given - behavior is undefined. Otherwise cannot fail. - * - * @param value New value of the caller `BigInt`, given by decimal - * its representation. - * @return Self-reference to allow for method chaining. - */ -public final function BigInt SetDecimal_S(string value) -{ +// Tries to parse either `+` or `-` and returns `true` iff it parsed `-`. +// If neither got parsed, `parser` will enter failed state. +// Assumes `parser` isn't `none`. +private final function bool ParseSign(Parser parser) { + parser.Match(P("-")); + negative = parser.Ok(); + if (parser.Ok()) { + negative = true; + } + else { + parser.R(); + parser.Match(P("+")); + } + return negative; +} + +/// Changes current value of [`BigInt`] to the value, given by decimal representation. +/// +/// If invalid decimal representation (digits only, possibly with leading sign) is given as +/// an argument, caller's value won't change. +/// Returns `true` in case of success and `false` otherwise. +public final function bool SetDecimal_S(string value) { + local bool result; local MutableText wrapper; wrapper = __().text.FromStringM(value); - SetDecimal(wrapper); + result = SetDecimal(wrapper); wrapper.FreeSelf(); - return self; + return result; } -// Auxiliary method for comparing two `BigInt`s by their absolute value. -private function BigIntCompareResult _compareAbsolute(BigInt other) -{ - local int i; - local array otherDigits; +// Auxiliary method for comparing two [`BigInt`]s by their absolute value. +private function BigIntCompareResult _compareAbsolute(BigInt other) { + local int i; + local array otherDigits; otherDigits = other.digits; if (digits.length == otherDigits.length) @@ -314,54 +274,34 @@ private function BigIntCompareResult _compareAbsolute(BigInt other) return BICR_Greater; } -/** - * Compares caller `BigInt` to `other`. - * - * @param other Value to compare the caller `BigInt`. - * If given reference is `none` - behavior is undefined. - * @return `BigIntCompareResult` representing the result of comparison. - * Returned value describes how caller `BigInt` relates to the `other`, - * e.g. if `BICR_Less` was returned - it means that caller `BigInt` is - * smaller that `other`. - */ -public function BigIntCompareResult Compare(BigInt other) -{ +/// Compares caller [`BigInt`] to [`other`]. +/// +/// [`BigIntCompareResult`] representing the result of comparison is returned as a result. +/// It describes how caller [`BigInt`] relates to the `other`, e.g. if `BICR_Less` was returned then +/// it means that caller [`BigInt`] is smaller that `other`. +/// If argument is `none`, then it is considered to be less ([`BICR_Less`]) than caller [`BigInt`]. +public function BigIntCompareResult Compare(BigInt other) { local BigIntCompareResult resultForModulus; - if (other == none) { - return BICR_Less; - } - if (negative && !other.negative) { - return BICR_Less; - } - if (!negative && other.negative) { - return BICR_Greater; - } + if (other == none) return BICR_Less; + if (negative && !other.negative) return BICR_Less; + if (!negative && other.negative) return BICR_Greater; resultForModulus = _compareAbsolute(other); - if (resultForModulus == BICR_Equal) { - return BICR_Equal; - } - if ( (negative && (resultForModulus == BICR_Greater)) - || (!negative && (resultForModulus == BICR_Less)) ) - { - return BICR_Less; - } + if (resultForModulus == BICR_Equal) return BICR_Equal; + if (negative && (resultForModulus == BICR_Greater)) return BICR_Less; + if (!negative && (resultForModulus == BICR_Less)) return BICR_Less; + return BICR_Greater; } -/** - * Compares caller `BigInt` to `other`. - * - * @param other Value to compare the caller `BigInt`. - * @return `BigIntCompareResult` representing the result of comparison. - * Returned value describes how caller `BigInt` relates to the `other`, - * e.g. if `BICR_Less` was returned - it means that caller `BigInt` is - * smaller that `other`. - */ -public function BigIntCompareResult CompareInt(int other) -{ - local BigInt wrapper; - local BigIntCompareResult result; +/// Compares caller [`BigInt`] to [`other`]. +/// +/// [`BigIntCompareResult`] representing the result of comparison is returned as a result. +/// It describes how caller [`BigInt`] relates to the `other`, e.g. if `BICR_Less` was returned then +/// it means that caller [`BigInt`] is smaller that `other`. +public function BigIntCompareResult CompareInt(int other) { + local BigInt wrapper; + local BigIntCompareResult result; wrapper = _.math.ToBigInt(other); result = Compare(wrapper); @@ -369,41 +309,33 @@ public function BigIntCompareResult CompareInt(int other) return result; } -/** - * Compares caller `BigInt` to `other`. - * - * @param other Value to compare the caller `BigInt`. - * If given reference is `none` - behavior is undefined. - * @return `BigIntCompareResult` representing the result of comparison. - * Returned value describes how caller `BigInt` relates to the `other`, - * e.g. if `BICR_Less` was returned - it means that caller `BigInt` is - * smaller that `other`. - */ -public function BigIntCompareResult CompareDecimal(BaseText other) -{ - local BigInt wrapper; - local BigIntCompareResult result; +/// Compares caller [`BigInt`] to a decimal representation of a number. +/// +/// [`BigIntCompareResult`] representing the result of comparison is returned as a result. +/// It describes how caller [`BigInt`] relates to the `other`, e.g. if `BICR_Less` was returned then +/// it means that caller [`BigInt`] is smaller that `other`. +/// If argument is `none` or is an invalid decimal representation (digits only, possibly with +/// leading sign, then it is considered to be less ([`BICR_Less`]) than caller [`BigInt`]. +public function BigIntCompareResult CompareDecimal(BaseText other) { + local BigInt wrapper; + local BigIntCompareResult result; wrapper = _.math.MakeBigInt(other); result = Compare(wrapper); - wrapper.FreeSelf(); + _.memory.Free(wrapper); return result; } -/** - * Compares caller `BigInt` to `other`. - * - * @param other Value to compare the caller `BigInt`. - * If given value contains invalid decimal value - behavior is undefined. - * @return `BigIntCompareResult` representing the result of comparison. - * Returned value describes how caller `BigInt` relates to the `other`, - * e.g. if `BICR_Less` was returned - it means that caller `BigInt` is - * smaller that `other`. - */ -public function BigIntCompareResult CompareDecimal_S(string other) -{ - local BigInt wrapper; - local BigIntCompareResult result; +/// Compares caller [`BigInt`] to a decimal representation of a number. +/// +/// [`BigIntCompareResult`] representing the result of comparison is returned as a result. +/// It describes how caller [`BigInt`] relates to the `other`, e.g. if `BICR_Less` was returned then +/// it means that caller [`BigInt`] is smaller that `other`. +/// If argument is an invalid decimal representation (digits only, possibly with leading sign, then +/// it is considered to be less ([`BICR_Less`]) than caller [`BigInt`]. +public function BigIntCompareResult CompareDecimal_S(string other) { + local BigInt wrapper; + local BigIntCompareResult result; wrapper = _.math.MakeBigInt_S(other); result = Compare(wrapper); @@ -411,13 +343,11 @@ public function BigIntCompareResult CompareDecimal_S(string other) return result; } -// Adds absolute values of caller `BigInt` and `other` with no changes to -// the sign -private function _add(BigInt other) -{ - local int i; - local byte carry, digitSum; - local array otherDigits; +// Adds absolute values of caller [`BigInt`] and [`other`] with no changes to the sign. +private function _add(BigInt other) { + local int i; + local byte carry, digitSum; + local array otherDigits; if (other == none) { return; @@ -425,13 +355,11 @@ private function _add(BigInt other) otherDigits = other.digits; if (digits.length < otherDigits.length) { digits.length = otherDigits.length; - } - else { + } else { otherDigits.length = digits.length; } carry = 0; - for (i = 0; i < digits.length; i += 1) - { + for (i = 0; i < digits.length; i += 1) { digitSum = digits[i] + otherDigits[i] + carry; digits[i] = _.math.Remainder(digitSum, 10); carry = (digitSum - digits[i]) / 10; @@ -442,47 +370,39 @@ private function _add(BigInt other) // No leading zeroes can be created here, so no need to trim } -// Subtracts absolute value of `other` from the caller `BigInt`, flipping -// caller's sign in case `other`'s absolute value is bigger. -private function _sub(BigInt other) -{ - local int i; - local int carry, nextDigit; - local array minuendDigits, subtrahendDigits; - local BigIntCompareResult resultForModulus; +// Subtracts absolute value of [`other`] from the caller [`BigInt`], flipping caller's sign in case +// `other`'s absolute value is bigger. +private function _sub(BigInt other) { + local int i; + local int carry, nextDigit; + local array minuendDigits, subtrahendDigits; + local BigIntCompareResult resultForModulus; if (other == none) { return; } resultForModulus = _compareAbsolute(other); - if (resultForModulus == BICR_Equal) - { + if (resultForModulus == BICR_Equal) { SetZero(); return; } - if (resultForModulus == BICR_Less) - { - negative = !negative; - minuendDigits = other.digits; - subtrahendDigits = digits; + if (resultForModulus == BICR_Less) { + negative = !negative; + minuendDigits = other.digits; + subtrahendDigits = digits; + } else { + minuendDigits = digits; + subtrahendDigits = other.digits; } - else - { - minuendDigits = digits; - subtrahendDigits = other.digits; - } - digits.length = minuendDigits.length; + digits.length = minuendDigits.length; subtrahendDigits.length = minuendDigits.length; carry = 0; - for (i = 0; i < digits.length; i += 1) - { + for (i = 0; i < digits.length; i += 1) { nextDigit = int(minuendDigits[i]) - int(subtrahendDigits[i]) + carry; - if (nextDigit < 0) - { + if (nextDigit < 0) { nextDigit += 10; carry = -1; - } - else { + } else { carry = 0; } digits[i] = nextDigit; @@ -490,170 +410,108 @@ private function _sub(BigInt other) TrimLeadingZeroes(); } -/** - * Adds `other` value to the caller `BigInt`. - * - * @param other Value to add. If `none` is given method does nothing. - * @return Self-reference to allow for method chaining. - */ -public function BigInt Add(BigInt other) -{ +/// Adds another value to the caller [`BigInt`]. +/// +/// If argument is `none`, then given method does nothing. +public function Add(BigInt other) { if (other == none) { - return self; + return; } if (negative == other.negative) { _add(other); - } - else { + } else { _sub(other); } - return self; } -/** - * Adds `other` value to the caller `BigInt`. - * - * Cannot fail. - * - * @param other Value to add. - * @return Self-reference to allow for method chaining. - */ -public function BigInt AddInt(int other) -{ +/// Adds another value to the caller [`BigInt`]. +public function AddInt(int other) { local BigInt otherObject; otherObject = _.math.ToBigInt(other); Add(otherObject); _.memory.Free(otherObject); - return self; } -/** - * Adds `other` value to the caller `BigInt`. - * - * If invalid decimal representation (digits only, possibly with leading sign) - * is given - behavior is undefined. Otherwise cannot fail. - * - * @param other Value to add. If `none` is given, method does nothing. - * @return Self-reference to allow for method chaining. - */ -public function BigInt AddDecimal(BaseText other) -{ +/// Adds decimal representation of the number to the caller [`BigInt`]. +/// +/// If `none` or invalid decimal representation (digits only, possibly with leading sign) is given - +/// does nothing. +public function AddDecimal(BaseText other) { local BigInt otherObject; if (other == none) { - return self; + return; } otherObject = _.math.MakeBigInt(other); Add(otherObject); _.memory.Free(otherObject); - return self; } -/** - * Adds `other` value to the caller `BigInt`. - * - * If invalid decimal representation (digits only, possibly with leading sign) - * is given - behavior is undefined. Otherwise cannot fail. - * - * @param other Value to add. - * @return Self-reference to allow for method chaining. - */ -public function BigInt AddDecimal_S(string other) -{ +/// Adds decimal representation of the number to the caller [`BigInt`]. +/// +/// If invalid decimal representation (digits only, possibly with leading sign) is given - +/// does nothing. +public function AddDecimal_S(string other) { local BigInt otherObject; otherObject = _.math.MakeBigInt_S(other); Add(otherObject); _.memory.Free(otherObject); - return self; } -/** - * Subtracts `other` value to the caller `BigInt`. - * - * @param other Value to subtract. If `none` is given method does nothing. - * @return Self-reference to allow for method chaining. - */ -public function BigInt Subtract(BigInt other) -{ +/// Subtracts another value to the caller [`BigInt`]. +/// +/// If argument is `none`, then given method does nothing. +public function Subtract(BigInt other) { if (negative != other.negative) { _add(other); - } - else { + } else { _sub(other); } - return self; } -/** - * Subtracts `other` value to the caller `BigInt`. - * - * Cannot fail. - * - * @param other Value to subtract. - * @return Self-reference to allow for method chaining. - */ -public function BigInt SubtractInt(int other) -{ +/// Adds another value to the caller [`BigInt`]. +public function SubtractInt(int other) { local BigInt otherObject; otherObject = _.math.ToBigInt(other); Subtract(otherObject); _.memory.Free(otherObject); - return self; } -/** - * Subtracts `other` value to the caller `BigInt`. - * - * If invalid decimal representation (digits only, possibly with leading sign) - * is given - behavior is undefined. Otherwise cannot fail. - * - * @param other Value to subtract. If `none`, method does nothing. - * @return Self-reference to allow for method chaining. - */ -public function BigInt SubtractDecimal(BaseText other) -{ +/// Subtracts decimal representation of the number to the caller [`BigInt`]. +/// +/// If `none` or invalid decimal representation (digits only, possibly with leading sign) is given - +/// does nothing. +public function SubtractDecimal(BaseText other) { local BigInt otherObject; if (other == none) { - return self; + return; } otherObject = _.math.MakeBigInt(other); Subtract(otherObject); _.memory.Free(otherObject); - return self; } -/** - * Subtracts `other` value to the caller `BigInt`. - * - * If invalid decimal representation (digits only, possibly with leading sign) - * is given - behavior is undefined. Otherwise cannot fail. - * - * @param other Value to subtract. - * @return Self-reference to allow for method chaining. - */ -public function BigInt SubtractDecimal_S(string other) -{ +/// Adds decimal representation of the number to the caller [`BigInt`]. +/// +/// If invalid decimal representation (digits only, possibly with leading sign) is given - +/// does nothing. +public function SubtractDecimal_S(string other) { local BigInt otherObject; otherObject = _.math.MakeBigInt_S(other); Subtract(otherObject); _.memory.Free(otherObject); - return self; } -/** - * Checks if caller `BigInt` is negative. Zero is not considered negative - * number. - * - * @return `true` if stored value is negative (`< 0`) and `false` otherwise - * (`>= 0`). - */ -public function bool IsNegative() -{ +/// Checks if caller [`BigInt`] is negative. +/// +/// Returns if stored value is negative and `false` otherwise. +/// Zero is not considered negative number. +public function bool IsNegative() { // Handle special case of zero first (it ignores `negative` flag) if (digits.length == 1 && digits[0] == 0) { return false; @@ -661,19 +519,12 @@ public function bool IsNegative() return negative; } -/** - * Converts caller `BigInt` into `int` representation. - * - * In case stored value is outside `int`'s value range - * (`[-MaxInt-1, MaxInt] == [-2147483648; 2147483647]`), - * method returns either maximal or minimal possible value, depending on - * the `BigInt`'s sign. - * - * @return `int` representation of the caller `BigInt`, clamped into available - * `int` value range. - */ -public function int ToInt() -{ +/// Converts caller [`BigInt`] into [`int`] representation. +/// +/// In case stored value is outside `int`'s value range +/// (`[-maxInt-1, maxInt] == [-2147483648; 2147483647]`), method returns either maximal or minimal +// possible value, depending on the [`BigInt`]'s sign. +public function int ToInt() { local int i; local int accumulator; local int safeDigitsAmount; @@ -681,19 +532,16 @@ public function int ToInt() if (digits.length <= 0) { return 0; } - if (digits.length > DIGITS_IN_MAX_INT) - { + if (digits.length > DIGITS_IN_MAX_INT) { if (negative) { - return (-MaxInt - 1); - } - else { - return MaxInt; + return (-maxInt - 1); + } else { + return maxInt; } } - // At most `DIGITS_IN_MAX_INT - 1` iterations + // At most `DIGITS_IN_MAX_INT - 1` iterations safeDigitsAmount = Min(DIGITS_IN_MAX_INT - 1, digits.length); - for (i = safeDigitsAmount - 1; i >= 0; i -= 1) - { + for (i = safeDigitsAmount - 1; i >= 0; i -= 1) { accumulator *= 10; accumulator += digits[i]; } @@ -704,33 +552,28 @@ public function int ToInt() return accumulator; } -// Adding `DIGITS_IN_MAX_INT - 1` will never lead to an overflow, but -// adding the next digit can, so we need to handle it differently and more -// carefully. -// Assumes `digits.length <= DIGITS_IN_MAX_INT`. -private function int AddUnsafeDigitToInt(int accumulator) -{ - local int unsafeDigit; - local bool noOverflow; +/// Adding `DIGITS_IN_MAX_INT - 1` will never lead to an overflow, but adding the next digit can, +/// so we need to handle it differently and more carefully. +/// Assumes `digits.length <= DIGITS_IN_MAX_INT`. +private function int AddUnsafeDigitToInt(int accumulator) { + local int unsafeDigit; + local bool noOverflow; if (digits.length < DIGITS_IN_MAX_INT) { return accumulator; } unsafeDigit = digits[DIGITS_IN_MAX_INT - 1]; - // `MaxInt` stats with `2`, so if last/unsafe digit is either `0` or `1`, - // there is no overflow, otherwise - check rest of the digits + // `maxInt` stats with `2`, so if last/unsafe digit is either `0` or `1`, there is no overflow, + // otherwise - check rest of the digits noOverflow = (unsafeDigit < 2); - if (unsafeDigit == 2) - { - // Include `MaxInt` and `-MaxInt-1` (minimal possible value) into - // an overflow too - this way we still give a correct result, but do - // not have to worry about `int`-arithmetic error + if (unsafeDigit == 2) { + // Include `maxInt` and `-maxInt-1` (minimal possible value) into an overflow too - this way + // we still give a correct result, but do not have to worry about `int`-arithmetic error noOverflow = noOverflow || (negative && (accumulator > -ALMOST_MAX_INT - 1)) || (!negative && (accumulator < ALMOST_MAX_INT)); } - if (noOverflow) - { + if (noOverflow) { if (negative) { accumulator -= unsafeDigit * LAST_DIGIT_ORDER; } @@ -741,30 +584,20 @@ private function int AddUnsafeDigitToInt(int accumulator) } // Handle overflow if (negative) { - return (-MaxInt - 1); + return (-maxInt - 1); } - return MaxInt; + return maxInt; } -/** - * Converts caller `BigInt` into `Text` representation. - * - * @return `Text` representation of the caller `BigInt`. - */ -public function Text ToText() -{ +/// Converts caller [`BigInt`] into [`Text`] representation. +public function Text ToText() { return ToText_M().IntoText(); } -/** - * Converts caller `BigInt` into `MutableText` representation. - * - * @return `MutableText` representation of the caller `BigInt`. - */ -public function MutableText ToText_M() -{ - local int i; - local MutableText result; +/// Converts caller [`BigInt`] into [`MutableText`] representation. +public function MutableText ToText_M() { + local int i; + local MutableText result; result = _.text.Empty(); if (negative) { @@ -776,15 +609,10 @@ public function MutableText ToText_M() return result; } -/** - * Converts caller `BigInt` into `string` representation. - * - * @return `string` representation of the caller `BigInt`. - */ -public function string ToString() -{ - local int i; - local string result; +/// Converts caller [`BigInt`] into [`string`] representation. +public function string ToString() { + local int i; + local string result; if (negative) { result = "-"; @@ -795,20 +623,14 @@ public function string ToString() return result; } -/** - * Restores `BigInt` from the `BigIntData` value. - * - * This method is created to make an efficient way to store `BigInt` inside - * local databases. - * - * @param data Data to read new caller `BigInt`'s value from. - */ -public function FromData(BigIntData data) -{ +/// Restores [`BigInt`] from the [`BigIntData`] value. +/// +/// This method is created to make an efficient way to store [`BigInt`]. +public function FromData(BigIntData data) { local int i; - negative = data.negative; - digits = data.digits; + negative = data.negative; + digits = data.digits; // Deal with possibly erroneous data for (i = 0; i < digits.length; i += 1) { if (digits[i] > 9) { @@ -817,23 +639,16 @@ public function FromData(BigIntData data) } } -/** - * Converts caller `BigInt`'s value into `BigIntData`. - * - * This method is created to make an efficient way to store `BigInt` inside - * local databases. - * - * @return Value of the caller `BigInt` in the `struct` form. - */ -public function BigIntData ToData() -{ +/// Converts caller [`BigInt`]'s value into [`BigIntData`]. +/// +/// This method is created to make an efficient way to store [`BigInt`]. +public function BigIntData ToData() { local BigIntData result; result.negative = negative; - result.digits = digits; + result.digits = digits; return result; } -defaultproperties -{ +defaultproperties { } \ No newline at end of file diff --git a/sources/BaseRealm/API/Math/MathAPI.uc b/sources/BaseRealm/API/Math/MathAPI.uc index 504f546..1f4f8da 100644 --- a/sources/BaseRealm/API/Math/MathAPI.uc +++ b/sources/BaseRealm/API/Math/MathAPI.uc @@ -37,36 +37,41 @@ public function BigInt ToBigInt(int value) local BigInt result; result = BigInt(_.memory.Allocate(class'BigInt')); - return result.SetInt(value); + result.SetInt(value); + return result; } /// Creates new `BigInt` value, based on the decimal number representation. /// -/// Expects valid decimal representation as input (digits only, possibly with leading sign), -/// otherwise contents of returned value are undefined. -/// If invalid decimal representation is given - contents of returned value are undefined. -/// Otherwise cannot fail and is guaranteed to return non-`none` value. +/// If (and only if) `none` or invalid decimal representation (digits only, possibly with +/// leading sign) is given as an argument, method will return `none`. public function BigInt MakeBigInt(BaseText value) { local BigInt result; result = BigInt(_.memory.Allocate(class'BigInt')); - return result.SetDecimal(value); + if (result.SetDecimal(value)) { + return result; + } + result.FreeSelf(); + return none; } /// Creates new `BigInt` value, based on the decimal number representation. /// -/// Expects valid decimal representation as input (digits only, possibly with leading sign), -/// otherwise contents of returned value are undefined. -/// If invalid decimal representation is given - contents of returned value are undefined. -/// Otherwise cannot fail and is guaranteed to return non-`none` value. +/// If (and only if) invalid decimal representation (digits only, possibly with leading sign) is +/// given as an argument, method will return `none`. public function BigInt MakeBigInt_S(string value) { local BigInt result; result = BigInt(_.memory.Allocate(class'BigInt')); - return result.SetDecimal_S(value); + if (result.SetDecimal_S(value)) { + return result; + } + result.FreeSelf(); + return none; } /// Computes remainder of the integer division of [`number`] by [`divisor`]. @@ -79,7 +84,7 @@ public function int Remainder(int number, int divisor) local int quotient; quotient = number / divisor; - return (number - quotient/// divisor); + return (number - quotient * divisor); } /// Computes quotient and remainder of the integer division of [`number`] by [`divisor`]. diff --git a/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc b/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc index c90785c..d327b26 100644 --- a/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc +++ b/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc @@ -1,6 +1,8 @@ /** - * Set of tests for `BigInt` class. - * Copyright 2022 Anton Tarasenko + * Author: dkanus + * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore + * License: GPL + * Copyright 2022-2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -20,8 +22,7 @@ class TEST_BigInt extends TestCase abstract; -protected static function TESTS() -{ +protected static function TESTS() { // Here we use `ToString()` method to check `BigInt` creation, // therefore also testing it Context("Testing creation of `BigInt`s."); @@ -36,43 +37,33 @@ protected static function TESTS() Test_SubtractingValues(); } -protected static function Test_Creating() -{ +protected static function Test_Creating() { Issue("`ToString()` doesn't return value `BigInt` was initialized with" @ "a positive `int`."); TEST_ExpectTrue(__().math.ToBigInt(13524).ToString() == "13524"); - TEST_ExpectTrue( - __().math.ToBigInt(MaxInt).ToString() == "2147483647"); + TEST_ExpectTrue(__().math.ToBigInt(MaxInt).ToString() == "2147483647"); Issue("`ToString()` doesn't return value `BigInt` was initialized with" @ "a positive integer inside `string`."); - TEST_ExpectTrue( - __().math.MakeBigInt_S("2147483647").ToString() - == "2147483647"); + TEST_ExpectTrue(__().math.MakeBigInt_S("2147483647").ToString() == "2147483647"); TEST_ExpectTrue( __().math.MakeBigInt_S("4238756872643464981264982128742389") .ToString() == "4238756872643464981264982128742389"); - Issue("`ToString()` doesn't return value `BigInt` was initialized with" @ - "a negative `int`."); + Issue("`ToString()` doesn't return value `BigInt` was initialized with a negative `int`."); TEST_ExpectTrue(__().math.ToBigInt(-666).ToString() == "-666"); - TEST_ExpectTrue( - __().math.ToBigInt(-MaxInt).ToString() == "-2147483647"); - TEST_ExpectTrue( - __().math.ToBigInt(-MaxInt - 1).ToString() == "-2147483648"); + TEST_ExpectTrue(__().math.ToBigInt(-MaxInt).ToString() == "-2147483647"); + TEST_ExpectTrue(__().math.ToBigInt(-MaxInt - 1).ToString() == "-2147483648"); Issue("`ToString()` doesn't return value `BigInt` was initialized with" @ "a negative integer inside `string`."); - TEST_ExpectTrue( - __().math.MakeBigInt_S("-2147483648").ToString() - == "-2147483648"); + TEST_ExpectTrue(__().math.MakeBigInt_S("-2147483648").ToString() == "-2147483648"); TEST_ExpectTrue( __().math.MakeBigInt_S("-238473846327894632879097410348127") .ToString() == "-238473846327894632879097410348127"); } -protected static function Test_ToText() -{ +protected static function Test_ToText() { Issue("`ToText()` doesn't return value `BigInt` was initialized with" @ "a positive integer inside `string`."); TEST_ExpectTrue(__().math @@ -96,20 +87,19 @@ 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() -{ +protected static function SubTest_AddingSameSignValues() { local BigInt main, addition; Issue("Two positive `BigInt`s are incorrectly added."); main = __().math.MakeBigInt_S("927641962323462271784269213864"); addition = __().math.MakeBigInt_S("16324234842947239847239239"); main.Add(addition); + Log("UMBRA" @ main.ToString()); TEST_ExpectTrue(main.ToString() == "927658286558305219024116453103"); main = __().math.MakeBigInt_S("16324234842947239847239239"); addition = __().math.MakeBigInt_S("927641962323462271784269213864"); @@ -135,8 +125,7 @@ protected static function SubTest_AddingSameSignValues() TEST_ExpectTrue(main.ToString() == "-1457931745873178552"); } -protected static function SubTest_AddingDifferentSignValues() -{ +protected static function SubTest_AddingDifferentSignValues() { local BigInt main, addition; Issue("Negative `BigInt`s is incorrectly added to positive one."); @@ -168,14 +157,12 @@ protected static function SubTest_AddingDifferentSignValues() TEST_ExpectTrue(main.ToString() == "0"); } -protected static function Test_SubtractingValues() -{ +protected static function Test_SubtractingValues() { SubTest_SubtractingSameSignValues(); SubTest_SubtractingDifferentSignValues(); } -protected static function SubTest_SubtractingSameSignValues() -{ +protected static function SubTest_SubtractingSameSignValues() { local BigInt main, sub; Issue("Two positive `BigInt`s are incorrectly subtracted."); @@ -207,8 +194,7 @@ protected static function SubTest_SubtractingSameSignValues() TEST_ExpectTrue(main.ToString() == "0"); } -protected static function SubTest_SubtractingDifferentSignValues() -{ +protected static function SubTest_SubtractingDifferentSignValues() { local BigInt main, sub; Issue("Negative `BigInt`s is incorrectly subtracted from positive one."); @@ -240,8 +226,7 @@ protected static function SubTest_SubtractingDifferentSignValues() TEST_ExpectTrue(main.ToString() == "-1457931745873178552"); } -protected static function Test_ToInt() -{ +protected static function Test_ToInt() { Issue("Testing conversion for non-overflowing values."); TEST_ExpectTrue(__().math.MakeBigInt_S("0").ToInt() == 0); TEST_ExpectTrue(__().math.MakeBigInt_S("-0").ToInt() == 0); @@ -264,8 +249,7 @@ protected static function Test_ToInt() __().math.MakeBigInt_S("-32545657348437563873").ToInt() == -2147483648); } -defaultproperties -{ - caseGroup = "Math" - caseName = "BigInt" +defaultproperties { + caseGroup = "Math" + caseName = "BigInt" } \ No newline at end of file