From e1951a0a6d276a613324a6040fd3a46dc2959a77 Mon Sep 17 00:00:00 2001 From: Giovanny Piovesan Date: Wed, 8 Jan 2025 10:10:19 -0300 Subject: [PATCH 1/4] add workaround for double.infinity --- lib/big_decimal.dart | 1 + lib/src/big_decimal.dart | 92 ++++++++++------------ lib/src/big_decimal_infinity.dart | 5 ++ test/big_decimal_test.dart | 126 +++++++++--------------------- 4 files changed, 87 insertions(+), 137 deletions(-) create mode 100644 lib/src/big_decimal_infinity.dart diff --git a/lib/big_decimal.dart b/lib/big_decimal.dart index e02b799..1382e3b 100644 --- a/lib/big_decimal.dart +++ b/lib/big_decimal.dart @@ -1 +1,2 @@ export 'src/big_decimal.dart'; +export 'src/big_decimal_infinity.dart'; diff --git a/lib/src/big_decimal.dart b/lib/src/big_decimal.dart index 07c3df5..562e09e 100644 --- a/lib/src/big_decimal.dart +++ b/lib/src/big_decimal.dart @@ -1,5 +1,7 @@ // ignore_for_file: constant_identifier_names +import 'package:big_decimal/src/big_decimal_infinity.dart'; + enum RoundingMode { UP, DOWN, @@ -20,13 +22,17 @@ const zeroCode = 48; const nineCode = 57; class BigDecimal implements Comparable { - BigDecimal._({ + BigDecimal({ required this.intVal, required this.scale, }); + factory BigDecimal.fromInfinity() { + return BigDecimalInfinity(); + } + factory BigDecimal.fromBigInt(BigInt value) { - return BigDecimal._( + return BigDecimal( intVal: value, scale: 0, ); @@ -56,6 +62,10 @@ class BigDecimal implements Comparable { } factory BigDecimal.parse(String value) { + if (value == double.infinity.toString()) { + return BigDecimal.fromInfinity(); + } + var sign = ''; var index = 0; var nextIndex = 0; @@ -88,7 +98,7 @@ class BigDecimal implements Comparable { index = nextIndex; if (index >= value.length) { - return BigDecimal._( + return BigDecimal( intVal: BigInt.parse('$integerPart$decimalPart'), scale: decimalPart.length, ); @@ -100,7 +110,7 @@ class BigDecimal implements Comparable { case capitalECode: index++; final exponent = int.parse(value.substring(index)); - return BigDecimal._( + return BigDecimal( intVal: BigInt.parse('$integerPart$decimalPart'), scale: decimalPart.length - exponent, ); @@ -117,20 +127,15 @@ class BigDecimal implements Comparable { final int scale; @override - bool operator ==(dynamic other) => - other is BigDecimal && compareTo(other) == 0; + bool operator ==(dynamic other) => other is BigDecimal && compareTo(other) == 0; - bool exactlyEquals(dynamic other) => - other is BigDecimal && intVal == other.intVal && scale == other.scale; + bool exactlyEquals(dynamic other) => other is BigDecimal && intVal == other.intVal && scale == other.scale; - BigDecimal operator +(BigDecimal other) => - _add(intVal, other.intVal, scale, other.scale); + BigDecimal operator +(BigDecimal other) => _add(intVal, other.intVal, scale, other.scale); - BigDecimal operator *(BigDecimal other) => - BigDecimal._(intVal: intVal * other.intVal, scale: scale + other.scale); + BigDecimal operator *(BigDecimal other) => BigDecimal(intVal: intVal * other.intVal, scale: scale + other.scale); - BigDecimal operator -(BigDecimal other) => - _add(intVal, -other.intVal, scale, other.scale); + BigDecimal operator -(BigDecimal other) => _add(intVal, -other.intVal, scale, other.scale); bool operator <(BigDecimal other) => compareTo(other) < 0; @@ -140,34 +145,30 @@ class BigDecimal implements Comparable { bool operator >=(BigDecimal other) => compareTo(other) >= 0; - BigDecimal operator -() => BigDecimal._(intVal: -intVal, scale: scale); + BigDecimal operator -() => BigDecimal(intVal: -intVal, scale: scale); - BigDecimal abs() => BigDecimal._(intVal: intVal.abs(), scale: scale); + BigDecimal abs() => BigDecimal(intVal: intVal.abs(), scale: scale); BigDecimal divide( BigDecimal divisor, { RoundingMode roundingMode = RoundingMode.UNNECESSARY, int? scale, }) => - _divide(intVal, this.scale, divisor.intVal, divisor.scale, - scale ?? this.scale, roundingMode); + _divide(intVal, this.scale, divisor.intVal, divisor.scale, scale ?? this.scale, roundingMode); BigDecimal pow(int n) { if (n >= 0 && n <= 999999999) { // TODO: Check scale of this multiplication final newScale = scale * n; - return BigDecimal._(intVal: intVal.pow(n), scale: newScale); + return BigDecimal(intVal: intVal.pow(n), scale: newScale); } - throw Exception( - 'Invalid operation: Exponent should be between 0 and 999999999'); + throw Exception('Invalid operation: Exponent should be between 0 and 999999999'); } - double toDouble() => - intVal.toDouble() / BigInt.from(10).pow(scale).toDouble(); + double toDouble() => intVal.toDouble() / BigInt.from(10).pow(scale).toDouble(); BigInt toBigInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) => withScale(0, roundingMode: roundingMode).intVal; - int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) => - toBigInt(roundingMode: roundingMode).toInt(); + int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) => toBigInt(roundingMode: roundingMode).toInt(); BigDecimal withScale( int newScale, { @@ -176,16 +177,15 @@ class BigDecimal implements Comparable { if (scale == newScale) { return this; } else if (intVal.sign == 0) { - return BigDecimal._(intVal: BigInt.zero, scale: newScale); + return BigDecimal(intVal: BigInt.zero, scale: newScale); } else { if (newScale > scale) { final drop = sumScale(newScale, -scale); final intResult = intVal * BigInt.from(10).pow(drop); - return BigDecimal._(intVal: intResult, scale: newScale); + return BigDecimal(intVal: intResult, scale: newScale); } else { final drop = sumScale(scale, -newScale); - return _divideAndRound(intVal, BigInt.from(10).pow(drop), newScale, - roundingMode, newScale); + return _divideAndRound(intVal, BigInt.from(10).pow(drop), newScale, roundingMode, newScale); } } } @@ -198,17 +198,16 @@ class BigDecimal implements Comparable { return intVal.abs().compareTo(BigInt.from(10).pow(r)) < 0 ? r : r + 1; } - static BigDecimal _add( - BigInt intValA, BigInt intValB, int scaleA, int scaleB) { + static BigDecimal _add(BigInt intValA, BigInt intValB, int scaleA, int scaleB) { final scaleDiff = scaleA - scaleB; if (scaleDiff == 0) { - return BigDecimal._(intVal: intValA + intValB, scale: scaleA); + return BigDecimal(intVal: intValA + intValB, scale: scaleA); } else if (scaleDiff < 0) { final scaledX = intValA * BigInt.from(10).pow(-scaleDiff); - return BigDecimal._(intVal: scaledX + intValB, scale: scaleB); + return BigDecimal(intVal: scaledX + intValB, scale: scaleB); } else { final scaledY = intValB * BigInt.from(10).pow(scaleDiff); - return BigDecimal._(intVal: intValA + scaledY, scale: scaleA); + return BigDecimal(intVal: intValA + scaledY, scale: scaleA); } } @@ -221,20 +220,18 @@ class BigDecimal implements Comparable { RoundingMode roundingMode, ) { if (dividend == BigInt.zero) { - return BigDecimal._(intVal: BigInt.zero, scale: scale); + return BigDecimal(intVal: BigInt.zero, scale: scale); } if (sumScale(scale, divisorScale) > dividendScale) { final newScale = scale + divisorScale; final raise = newScale - dividendScale; final scaledDividend = dividend * BigInt.from(10).pow(raise); - return _divideAndRound( - scaledDividend, divisor, scale, roundingMode, scale); + return _divideAndRound(scaledDividend, divisor, scale, roundingMode, scale); } else { final newScale = sumScale(dividendScale, -scale); final raise = newScale - divisorScale; final scaledDivisor = divisor * BigInt.from(10).pow(raise); - return _divideAndRound( - dividend, scaledDivisor, scale, roundingMode, scale); + return _divideAndRound(dividend, scaledDivisor, scale, roundingMode, scale); } } @@ -249,18 +246,16 @@ class BigDecimal implements Comparable { final remainder = dividend.remainder(divisor).abs(); final quotientPositive = dividend.sign == divisor.sign; if (remainder != BigInt.zero) { - if (_needIncrement( - divisor, roundingMode, quotientPositive, quotient, remainder)) { - final intResult = - quotient + (quotientPositive ? BigInt.one : -BigInt.one); - return BigDecimal._(intVal: intResult, scale: scale); + if (_needIncrement(divisor, roundingMode, quotientPositive, quotient, remainder)) { + final intResult = quotient + (quotientPositive ? BigInt.one : -BigInt.one); + return BigDecimal(intVal: intResult, scale: scale); } - return BigDecimal._(intVal: quotient, scale: scale); + return BigDecimal(intVal: quotient, scale: scale); } else { if (preferredScale != scale) { return createAndStripZerosForScale(quotient, scale, preferredScale); } else { - return BigDecimal._(intVal: quotient, scale: scale); + return BigDecimal(intVal: quotient, scale: scale); } } } @@ -287,7 +282,7 @@ class BigDecimal implements Comparable { scaleMut = sumScale(scaleMut, -1); } - return BigDecimal._(intVal: intValMut, scale: scaleMut); + return BigDecimal(intVal: intValMut, scale: scaleMut); } static bool _needIncrement( @@ -297,8 +292,7 @@ class BigDecimal implements Comparable { BigInt quotient, BigInt remainder, ) { - final remainderComparisonToHalfDivisor = - (remainder * BigInt.from(2)).compareTo(divisor); + final remainderComparisonToHalfDivisor = (remainder * BigInt.from(2)).compareTo(divisor); switch (roundingMode) { case RoundingMode.UNNECESSARY: throw Exception('Rounding necessary'); diff --git a/lib/src/big_decimal_infinity.dart b/lib/src/big_decimal_infinity.dart new file mode 100644 index 0000000..f1adf03 --- /dev/null +++ b/lib/src/big_decimal_infinity.dart @@ -0,0 +1,5 @@ +import 'big_decimal.dart'; + +class BigDecimalInfinity extends BigDecimal { + BigDecimalInfinity() : super(intVal: BigInt.from(0), scale: 0); +} diff --git a/test/big_decimal_test.dart b/test/big_decimal_test.dart index 3090c1e..dbb4be6 100644 --- a/test/big_decimal_test.dart +++ b/test/big_decimal_test.dart @@ -1,4 +1,5 @@ import 'package:big_decimal/src/big_decimal.dart'; +import 'package:big_decimal/src/big_decimal_infinity.dart'; import 'package:test/test.dart'; import 'helpers/coerce.dart'; @@ -7,6 +8,19 @@ import 'helpers/tabular.dart'; void main() { group('parse', () { + group('infinity parse', () { + test('Should return BigDecimalInfinity', () { + final bigdec = BigDecimal.parse('Infinity'); + + expect(true, bigdec is BigDecimalInfinity); + }); + + test('Should return BigDecimal', () { + final bigdec = BigDecimal.parse('0'); + + expect(false, bigdec is BigDecimalInfinity); + }); + }); group( 'parses correctly', tabular((String s, int intVal, int scale, int precision) { @@ -22,14 +36,11 @@ void main() { tabCase(['2', 2, 0, 1], 'positive integer with no decimal places'), tabCase(['-2', -2, 0, 1], 'negative integer with no decimal places'), tabCase(['2.000', 2000, 3, 4], 'positive integer with decimal places'), - tabCase( - ['-2.000', -2000, 3, 4], 'negative integer with decimal places'), + tabCase(['-2.000', -2000, 3, 4], 'negative integer with decimal places'), tabCase(['2.01', 201, 2, 3], 'positive number with decimal places'), tabCase(['-2.01', -201, 2, 3], 'negative number with decimal places'), - tabCase( - ['-.2e1', -2, 0, 1], 'negative with decimal places and exponent'), - tabCase(['-.2e-1', -2, 2, 1], - 'negative with decimal places and negative exponent'), + tabCase(['-.2e1', -2, 0, 1], 'negative with decimal places and exponent'), + tabCase(['-.2e-1', -2, 2, 1], 'negative with decimal places and negative exponent'), tabCase(['10.00e2', 1000, 0, 4], 'with exponential'), tabCase(['10e2', 10, -2, 2], 'with exponential and negative scale'), tabCase(['10.e2', 10, -2, 2], 'with exponential and negative scale'), @@ -114,8 +125,7 @@ void main() { group('division', () { group( 'divide', - tabular((Object a, Object b, Object result, - [RoundingMode roundingMode = RoundingMode.UNNECESSARY, int? scale]) { + tabular((Object a, Object b, Object result, [RoundingMode roundingMode = RoundingMode.UNNECESSARY, int? scale]) { expect( a.dec.divide(b.dec, roundingMode: roundingMode, scale: scale), exactly(result.dec), @@ -138,8 +148,7 @@ void main() { ]), ); - test('unable to divide to repeating decimals without proper RoundingMode', - () { + test('unable to divide to repeating decimals without proper RoundingMode', () { // 0.3333333... expect(() => '10'.dec.divide('3'.dec), throwsException); // 7.3828282... @@ -267,8 +276,7 @@ void main() { group('successfully changing the scale', () { group( 'simple cases', - tabular((Object a, int newScale, Object result, - [RoundingMode roundingMode = RoundingMode.UNNECESSARY]) { + tabular((Object a, int newScale, Object result, [RoundingMode roundingMode = RoundingMode.UNNECESSARY]) { expect( a.dec.withScale(newScale, roundingMode: roundingMode), exactly(result.dec), @@ -292,26 +300,18 @@ void main() { String half_even, Object unnecessary, ) { - BigDecimal round(RoundingMode mode) => - input.dec.withScale(0, roundingMode: mode); + BigDecimal round(RoundingMode mode) => input.dec.withScale(0, roundingMode: mode); expect(round(RoundingMode.UP), exactly(up.dec), reason: 'UP'); expect(round(RoundingMode.DOWN), exactly(down.dec), reason: 'DOWN'); - expect(round(RoundingMode.CEILING), exactly(ceiling.dec), - reason: 'CEILING'); - expect(round(RoundingMode.FLOOR), exactly(floor.dec), - reason: 'FLOOR'); - expect(round(RoundingMode.HALF_UP), exactly(half_up.dec), - reason: 'HALF_UP'); - expect(round(RoundingMode.HALF_DOWN), exactly(half_down.dec), - reason: 'HALF_DOWN'); - expect(round(RoundingMode.HALF_EVEN), exactly(half_even.dec), - reason: 'HALF_EVEN'); + expect(round(RoundingMode.CEILING), exactly(ceiling.dec), reason: 'CEILING'); + expect(round(RoundingMode.FLOOR), exactly(floor.dec), reason: 'FLOOR'); + expect(round(RoundingMode.HALF_UP), exactly(half_up.dec), reason: 'HALF_UP'); + expect(round(RoundingMode.HALF_DOWN), exactly(half_down.dec), reason: 'HALF_DOWN'); + expect(round(RoundingMode.HALF_EVEN), exactly(half_even.dec), reason: 'HALF_EVEN'); if (unnecessary is String) { - expect(round(RoundingMode.UNNECESSARY), exactly(unnecessary.dec), - reason: 'UNNECESSARY'); + expect(round(RoundingMode.UNNECESSARY), exactly(unnecessary.dec), reason: 'UNNECESSARY'); } else { - expect(() => round(RoundingMode.UNNECESSARY), unnecessary, - reason: 'UNNECESSARY'); + expect(() => round(RoundingMode.UNNECESSARY), unnecessary, reason: 'UNNECESSARY'); } }, [ // Input UP DOWN CEILING FLOOR HALF_UP HALF_DOWN HALF_EVEN UNNECESSARY @@ -321,50 +321,10 @@ void main() { tabCase(['1.1', '2', '1', '2', '1', '1', '1', '1', throwsException]), tabCase(['1.0', '1', '1', '1', '1', '1', '1', '1', '1']), tabCase(['-1.0', '-1', '-1', '-1', '-1', '-1', '-1', '-1', '-1']), - tabCase([ - '-1.1', - '-2', - '-1', - '-1', - '-2', - '-1', - '-1', - '-1', - throwsException - ]), - tabCase([ - '-1.6', - '-2', - '-1', - '-1', - '-2', - '-2', - '-2', - '-2', - throwsException - ]), - tabCase([ - '-2.5', - '-3', - '-2', - '-2', - '-3', - '-3', - '-2', - '-2', - throwsException - ]), - tabCase([ - '-5.5', - '-6', - '-5', - '-5', - '-6', - '-6', - '-5', - '-6', - throwsException - ]), + tabCase(['-1.1', '-2', '-1', '-1', '-2', '-1', '-1', '-1', throwsException]), + tabCase(['-1.6', '-2', '-1', '-1', '-2', '-2', '-2', '-2', throwsException]), + tabCase(['-2.5', '-3', '-2', '-2', '-3', '-3', '-2', '-2', throwsException]), + tabCase(['-5.5', '-6', '-5', '-5', '-6', '-6', '-5', '-6', throwsException]), ]), ); }); @@ -394,8 +354,7 @@ void main() { group( 'toBigInt', - tabular((BigDecimal bd, BigInt bint, - [RoundingMode roundingMode = RoundingMode.UNNECESSARY]) { + tabular((BigDecimal bd, BigInt bint, [RoundingMode roundingMode = RoundingMode.UNNECESSARY]) { expect(bd.toBigInt(roundingMode: roundingMode), bint); }, [ tabCase(['1.5'.dec, BigInt.from(1), RoundingMode.DOWN]), @@ -412,21 +371,14 @@ void main() { tabCase(['-1.5'.dec, BigInt.from(-2), RoundingMode.HALF_EVEN]), tabCase(['-1.5'.dec, BigInt.from(-2), RoundingMode.HALF_UP]), tabCase(['-1.5'.dec, BigInt.from(-1), RoundingMode.HALF_DOWN]), - tabCase([ - '92233720368547758089999'.dec, - BigInt.parse('92233720368547758089999') - ], 'very large integer'), - tabCase([ - '-92233720368547758089999'.dec, - BigInt.parse('-92233720368547758089999') - ], 'very small integer'), + tabCase(['92233720368547758089999'.dec, BigInt.parse('92233720368547758089999')], 'very large integer'), + tabCase(['-92233720368547758089999'.dec, BigInt.parse('-92233720368547758089999')], 'very small integer'), ]), ); group( 'toInt', - tabular((BigDecimal bd, int i, - [RoundingMode roundingMode = RoundingMode.UNNECESSARY]) { + tabular((BigDecimal bd, int i, [RoundingMode roundingMode = RoundingMode.UNNECESSARY]) { expect(bd.toInt(roundingMode: roundingMode), i); }, [ tabCase(['1.5'.dec, 1, RoundingMode.DOWN]), @@ -443,10 +395,8 @@ void main() { tabCase(['-1.5'.dec, -2, RoundingMode.HALF_EVEN]), tabCase(['-1.5'.dec, -2, RoundingMode.HALF_UP]), tabCase(['-1.5'.dec, -1, RoundingMode.HALF_DOWN]), - tabCase(['92233720368547758089999'.dec, 9223372036854775807], - 'very large integer'), - tabCase(['-92233720368547758089999'.dec, -9223372036854775808], - 'very small integer'), + tabCase(['92233720368547758089999'.dec, 9223372036854775807], 'very large integer'), + tabCase(['-92233720368547758089999'.dec, -9223372036854775808], 'very small integer'), ]), ); From 08c57c7b3489f1a9f500bb8c783ac98461f6fa59 Mon Sep 17 00:00:00 2001 From: Giovanny Piovesan Date: Wed, 8 Jan 2025 10:11:07 -0300 Subject: [PATCH 2/4] fix import --- lib/src/big_decimal.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/big_decimal.dart b/lib/src/big_decimal.dart index 562e09e..01bef8e 100644 --- a/lib/src/big_decimal.dart +++ b/lib/src/big_decimal.dart @@ -1,6 +1,6 @@ // ignore_for_file: constant_identifier_names -import 'package:big_decimal/src/big_decimal_infinity.dart'; +import 'big_decimal_infinity.dart'; enum RoundingMode { UP, From 756ad3c936f6ec5bdcfaa22eb36a98a556a20985 Mon Sep 17 00:00:00 2001 From: Giovanny Piovesan Date: Thu, 9 Jan 2025 12:07:25 -0300 Subject: [PATCH 3/4] adds overrides --- lib/src/big_decimal.dart | 9 +- lib/src/big_decimal_infinity.dart | 156 +++++++++++++++++++++++++++++- test/big_decimal_test.dart | 6 ++ 3 files changed, 169 insertions(+), 2 deletions(-) diff --git a/lib/src/big_decimal.dart b/lib/src/big_decimal.dart index 01bef8e..cabdc00 100644 --- a/lib/src/big_decimal.dart +++ b/lib/src/big_decimal.dart @@ -27,7 +27,10 @@ class BigDecimal implements Comparable { required this.scale, }); - factory BigDecimal.fromInfinity() { + factory BigDecimal.fromInfinity({bool? negative}) { + if (negative == true) { + return BigDecimalNegativeInfinity(); + } return BigDecimalInfinity(); } @@ -66,6 +69,10 @@ class BigDecimal implements Comparable { return BigDecimal.fromInfinity(); } + if (value == double.negativeInfinity.toString()) { + return BigDecimal.fromInfinity(negative: true); + } + var sign = ''; var index = 0; var nextIndex = 0; diff --git a/lib/src/big_decimal_infinity.dart b/lib/src/big_decimal_infinity.dart index f1adf03..22fde3b 100644 --- a/lib/src/big_decimal_infinity.dart +++ b/lib/src/big_decimal_infinity.dart @@ -1,5 +1,159 @@ import 'big_decimal.dart'; class BigDecimalInfinity extends BigDecimal { - BigDecimalInfinity() : super(intVal: BigInt.from(0), scale: 0); + BigDecimalInfinity() : super(intVal: BigInt.from(double.maxFinite), scale: 1); + + @override + BigDecimal operator +(BigDecimal other) => BigDecimal.fromInfinity(); + + @override + BigDecimal operator *(BigDecimal other) => BigDecimal.fromInfinity(); + + @override + BigDecimal operator -(BigDecimal other) => BigDecimal.fromInfinity(); + + @override + BigDecimal operator -() => BigDecimal.fromInfinity(negative: true); + + @override + BigDecimal abs() { + return BigDecimal.fromInfinity(); + } + + @override + BigDecimal divide( + BigDecimal divisor, { + RoundingMode roundingMode = RoundingMode.UNNECESSARY, + int? scale, + }) { + return BigDecimal.fromInfinity(); + } + + @override + BigDecimal pow(int n) { + throw Exception('Invalid operation: Exponent should be between 0 and 999999999'); + } + + @override + double toDouble() { + return double.infinity; + } + + @override + BigInt toBigInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { + throw UnsupportedError('Value must be finite: Infinity'); + } + + @override + int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { + throw UnsupportedError('Value must be finite: Infinity'); + } + + @override + BigDecimal withScale(int newScale, {RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { + throw UnsupportedError('Value must be finite: Infinity'); + } + + @override + int compareTo(BigDecimal other) { + if (other is BigDecimalInfinity) { + return 0; + } + + return 1; + } + + @override + bool exactlyEquals(other) { + if (other is BigDecimalInfinity) { + return true; + } + + return false; + } + + @override + String toString() => "Infinity"; + + @override + String toPlainString() => "Infinity"; +} + +class BigDecimalNegativeInfinity extends BigDecimal { + BigDecimalNegativeInfinity() : super(intVal: BigInt.from(-double.maxFinite), scale: 1); + + @override + BigDecimal operator +(BigDecimal other) => BigDecimal.fromInfinity(); + + @override + BigDecimal operator *(BigDecimal other) => BigDecimal.fromInfinity(); + + @override + BigDecimal operator -(BigDecimal other) => BigDecimal.fromInfinity(); + + @override + BigDecimal operator -() => BigDecimal.fromInfinity(); + + @override + BigDecimal abs() { + return BigDecimal.fromInfinity(); + } + + @override + BigDecimal divide( + BigDecimal divisor, { + RoundingMode roundingMode = RoundingMode.UNNECESSARY, + int? scale, + }) { + return BigDecimal.fromInfinity(); + } + + @override + BigDecimal pow(int n) { + throw Exception('Invalid operation: Exponent should be between 0 and 999999999'); + } + + @override + double toDouble() { + return double.negativeInfinity; + } + + @override + BigInt toBigInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { + throw UnsupportedError('Value must be finite: -Infinity'); + } + + @override + int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { + throw UnsupportedError('Value must be finite: -Infinity'); + } + + @override + BigDecimal withScale(int newScale, {RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { + throw UnsupportedError('Value must be finite: -Infinity'); + } + + @override + int compareTo(BigDecimal other) { + if (other is BigDecimalInfinity) { + return 0; + } + + return 1; + } + + @override + bool exactlyEquals(other) { + if (other is BigDecimalInfinity) { + return true; + } + + return false; + } + + @override + String toString() => "-Infinity"; + + @override + String toPlainString() => "-Infinity"; } diff --git a/test/big_decimal_test.dart b/test/big_decimal_test.dart index dbb4be6..06e76f1 100644 --- a/test/big_decimal_test.dart +++ b/test/big_decimal_test.dart @@ -15,6 +15,12 @@ void main() { expect(true, bigdec is BigDecimalInfinity); }); + test('Should return BigDecimalNegativeInfinity', () { + final bigdec = BigDecimal.parse('-Infinity'); + + expect(true, bigdec is BigDecimalNegativeInfinity); + }); + test('Should return BigDecimal', () { final bigdec = BigDecimal.parse('0'); From 012f84121ffe0cbe48a540771dd60077d58485fa Mon Sep 17 00:00:00 2001 From: Giovanny Piovesan Date: Mon, 13 Jan 2025 14:38:11 -0300 Subject: [PATCH 4/4] refactor operators and removed NegativeInfinity class --- lib/src/big_decimal.dart | 13 +-- lib/src/big_decimal_infinity.dart | 157 +++++++++++++++--------------- test/big_decimal_test.dart | 4 +- 3 files changed, 85 insertions(+), 89 deletions(-) diff --git a/lib/src/big_decimal.dart b/lib/src/big_decimal.dart index cabdc00..437333e 100644 --- a/lib/src/big_decimal.dart +++ b/lib/src/big_decimal.dart @@ -27,13 +27,6 @@ class BigDecimal implements Comparable { required this.scale, }); - factory BigDecimal.fromInfinity({bool? negative}) { - if (negative == true) { - return BigDecimalNegativeInfinity(); - } - return BigDecimalInfinity(); - } - factory BigDecimal.fromBigInt(BigInt value) { return BigDecimal( intVal: value, @@ -44,6 +37,8 @@ class BigDecimal implements Comparable { static BigDecimal zero = BigDecimal.fromBigInt(BigInt.zero); static BigDecimal one = BigDecimal.fromBigInt(BigInt.one); static BigDecimal two = BigDecimal.fromBigInt(BigInt.two); + static BigDecimal infinity = BigDecimalInfinity(); + static BigDecimal negativeInifinity = BigDecimalInfinity(isNegative: true); static int nextNonDigit(String value, [int start = 0]) { var index = start; @@ -66,11 +61,11 @@ class BigDecimal implements Comparable { factory BigDecimal.parse(String value) { if (value == double.infinity.toString()) { - return BigDecimal.fromInfinity(); + return BigDecimal.infinity; } if (value == double.negativeInfinity.toString()) { - return BigDecimal.fromInfinity(negative: true); + return BigDecimal.negativeInifinity; } var sign = ''; diff --git a/lib/src/big_decimal_infinity.dart b/lib/src/big_decimal_infinity.dart index 22fde3b..790c8a4 100644 --- a/lib/src/big_decimal_infinity.dart +++ b/lib/src/big_decimal_infinity.dart @@ -1,102 +1,69 @@ import 'big_decimal.dart'; class BigDecimalInfinity extends BigDecimal { - BigDecimalInfinity() : super(intVal: BigInt.from(double.maxFinite), scale: 1); + final bool isNegative; - @override - BigDecimal operator +(BigDecimal other) => BigDecimal.fromInfinity(); - - @override - BigDecimal operator *(BigDecimal other) => BigDecimal.fromInfinity(); - - @override - BigDecimal operator -(BigDecimal other) => BigDecimal.fromInfinity(); + BigDecimalInfinity({this.isNegative = false}) : super(intVal: BigInt.from(double.maxFinite), scale: 1); @override - BigDecimal operator -() => BigDecimal.fromInfinity(negative: true); + BigDecimal operator +(BigDecimal other) { + if (other is BigDecimalInfinity) { + if (isNegative && !other.isNegative) { + throw Exception('Invalid operation: NaN'); + } - @override - BigDecimal abs() { - return BigDecimal.fromInfinity(); - } + if (!isNegative && other.isNegative) { + throw Exception('Invalid operation: NaN'); + } + } - @override - BigDecimal divide( - BigDecimal divisor, { - RoundingMode roundingMode = RoundingMode.UNNECESSARY, - int? scale, - }) { - return BigDecimal.fromInfinity(); - } + if (isNegative) return BigDecimal.negativeInifinity; - @override - BigDecimal pow(int n) { - throw Exception('Invalid operation: Exponent should be between 0 and 999999999'); + return BigDecimal.infinity; } @override - double toDouble() { - return double.infinity; - } + BigDecimal operator *(BigDecimal other) { + if (other is BigDecimalInfinity) { + if (other.isNegative) { + return BigDecimal.negativeInifinity; + } - @override - BigInt toBigInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { - throw UnsupportedError('Value must be finite: Infinity'); - } + return BigDecimal.infinity; + } - @override - int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { - throw UnsupportedError('Value must be finite: Infinity'); - } + if (other == BigDecimal.zero) { + throw Exception('Invalid operation: NaN'); + } - @override - BigDecimal withScale(int newScale, {RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { - throw UnsupportedError('Value must be finite: Infinity'); + return BigDecimal.infinity; } @override - int compareTo(BigDecimal other) { + BigDecimal operator -(BigDecimal other) { if (other is BigDecimalInfinity) { - return 0; - } + if (isNegative && other.isNegative) { + throw Exception('Invalid operation: NaN'); + } - return 1; - } + if (!isNegative && !other.isNegative) { + throw Exception('Invalid operation: NaN'); + } + } - @override - bool exactlyEquals(other) { - if (other is BigDecimalInfinity) { - return true; + if (other.intVal < BigInt.zero) { + return BigDecimal.negativeInifinity; } - return false; + return BigDecimal.infinity; } @override - String toString() => "Infinity"; - - @override - String toPlainString() => "Infinity"; -} - -class BigDecimalNegativeInfinity extends BigDecimal { - BigDecimalNegativeInfinity() : super(intVal: BigInt.from(-double.maxFinite), scale: 1); - - @override - BigDecimal operator +(BigDecimal other) => BigDecimal.fromInfinity(); - - @override - BigDecimal operator *(BigDecimal other) => BigDecimal.fromInfinity(); - - @override - BigDecimal operator -(BigDecimal other) => BigDecimal.fromInfinity(); - - @override - BigDecimal operator -() => BigDecimal.fromInfinity(); + BigDecimal operator -() => isNegative ? BigDecimal.infinity : BigDecimal.negativeInifinity; @override BigDecimal abs() { - return BigDecimal.fromInfinity(); + return BigDecimal.infinity; } @override @@ -105,32 +72,66 @@ class BigDecimalNegativeInfinity extends BigDecimal { RoundingMode roundingMode = RoundingMode.UNNECESSARY, int? scale, }) { - return BigDecimal.fromInfinity(); + if (divisor is BigDecimalInfinity) { + throw Exception('Invalid operation: NaN'); + } + + return BigDecimal.infinity; } @override BigDecimal pow(int n) { - throw Exception('Invalid operation: Exponent should be between 0 and 999999999'); + if (isNegative) { + switch (n) { + case 0: + return BigDecimal.fromBigInt(BigInt.one); + case -1: + return BigDecimal.fromBigInt(BigInt.zero); + case 1: + return BigDecimal.negativeInifinity; + } + + if (n < -1) { + return BigDecimal.fromBigInt(BigInt.zero); + } + + return BigDecimal.infinity; + } + + switch (n) { + case 0: + return BigDecimal.fromBigInt(BigInt.one); + case -1: + return BigDecimal.fromBigInt(BigInt.zero); + case 1: + return BigDecimal.negativeInifinity; + } + + if (n < -1) { + return BigDecimal.fromBigInt(BigInt.zero); + } + + return BigDecimal.infinity; } @override double toDouble() { - return double.negativeInfinity; + return isNegative ? double.negativeInfinity : double.infinity; } @override BigInt toBigInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { - throw UnsupportedError('Value must be finite: -Infinity'); + throw UnsupportedError('Value must be finite: ${isNegative ? "-Infinity" : "Infinity"}'); } @override int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { - throw UnsupportedError('Value must be finite: -Infinity'); + throw UnsupportedError('Value must be finite: ${isNegative ? "-Infinity" : "Infinity"}'); } @override BigDecimal withScale(int newScale, {RoundingMode roundingMode = RoundingMode.UNNECESSARY}) { - throw UnsupportedError('Value must be finite: -Infinity'); + throw UnsupportedError('Value must be finite: ${isNegative ? "-Infinity" : "Infinity"}'); } @override @@ -152,8 +153,8 @@ class BigDecimalNegativeInfinity extends BigDecimal { } @override - String toString() => "-Infinity"; + String toString() => isNegative ? "-Infinity" : "Infinity"; @override - String toPlainString() => "-Infinity"; + String toPlainString() => isNegative ? "-Infinity" : "Infinity"; } diff --git a/test/big_decimal_test.dart b/test/big_decimal_test.dart index 06e76f1..404379a 100644 --- a/test/big_decimal_test.dart +++ b/test/big_decimal_test.dart @@ -15,10 +15,10 @@ void main() { expect(true, bigdec is BigDecimalInfinity); }); - test('Should return BigDecimalNegativeInfinity', () { + test('Should return BigDecimalInfinity', () { final bigdec = BigDecimal.parse('-Infinity'); - expect(true, bigdec is BigDecimalNegativeInfinity); + expect(true, bigdec is BigDecimalInfinity); }); test('Should return BigDecimal', () {