diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87e6bb6..98e35e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [[v0.2.2](https://github.com/The-FireHub-Project/Runtime/compare/v0.2.1...v0.2.2)] – 2026-08-31
+
+### Added
+* Improved `mod` method for `DecimalEngine` by @dieselxxx
+
## [[v0.2.1](https://github.com/The-FireHub-Project/Runtime/compare/v0.2.0...v0.2.1)] – 2026-08-31
### Added
diff --git a/README.md b/README.md
index 6552d30..b7d6b4e 100644
--- a/README.md
+++ b/README.md
@@ -7,12 +7,12 @@
PHP Runtime Foundation
@@ -22,25 +22,46 @@
/>
-[](https://github.com/The-FireHub-Project/Runtime/actions/workflows/PHPStan.yml)
-[](https://github.com/The-FireHub-Project/Runtime/actions/workflows/PHPUnit.yml)
-[](https://app.codecov.io/gh/The-FireHub-Project/Runtime/tree/develop)
-
-
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
* The divisor. *
- * @param positive-int $scale [optional]+ * @param non-negative-int $scale [optional]
* The number of decimal places to calculate. *
* diff --git a/src/Math/Decimal/Mod.php b/src/Math/Decimal/Mod.php new file mode 100644 index 0000000..4416384 --- /dev/null +++ b/src/Math/Decimal/Mod.php @@ -0,0 +1,66 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=7.4 + * @package Runtime + */ + +namespace FireHub\Runtime\Math\Decimal; + +use DivisionByZeroError; + +/** + * ### Decimal Number Modulo + * @since 1.0.0 + */ +trait Mod { + + /** + * ### Calculates the remainder of a decimal division + * + * The remainder follows truncation-toward-zero semantics. Its sign is the same as the dividend. + * @since 1.0.0 + * + * @uses \FireHub\Runtime\Math\DecimalEngine::normalize() To normalize the decimal values. + * @uses \FireHub\Runtime\Math\DecimalEngine::divide() To calculate the truncated quotient. + * @uses \FireHub\Runtime\Math\DecimalEngine::multiply() To calculate the product of the quotient and divisor. + * @uses \FireHub\Runtime\Math\DecimalEngine::subtract() To calculate the remainder. + * + * @param non-empty-string $left+ * The dividend. + *
+ * @param non-empty-string $right+ * The divisor. + *
+ * + * @throws \FireHub\Runtime\Exception\InvalidDecimalNumberException If either decimal value is invalid. + * @throws \DivisionByZeroError If the divisor is zero. + * + * @return numeric-string The remainder of the division. + */ + public static function mod (string $left, string $right):string { + + [, $right_integer, $right_fraction] = self::normalize($right); + + if ($right_integer === '0' && $right_fraction === '') + throw new DivisionByZeroError('Modulo by zero.'); + + [, $left_integer, $left_fraction] = self::normalize($left); + + if ($left_integer === '0' && $left_fraction === '') return '0'; + + $quotient = self::divide($left, $right, 0); + $product = self::multiply($quotient, $right); + + /** @var numeric-string */ + return self::subtract($left, $product); + + } + +} \ No newline at end of file diff --git a/src/Math/DecimalEngine.php b/src/Math/DecimalEngine.php index dbd13fb..ca72296 100644 --- a/src/Math/DecimalEngine.php +++ b/src/Math/DecimalEngine.php @@ -16,7 +16,7 @@ use FireHub\Core\Boundary\Runtime\NativeRuntime; use FireHub\Core\Meta\Enum\Side; use FireHub\Runtime\Math\Decimal\ { - Arithmetic, Divide, Multiply, Round + Arithmetic, Divide, Mod, Multiply, Round }; use FireHub\Runtime; use FireHub\Runtime\Exception\InvalidDecimalNumberException; @@ -37,7 +37,7 @@ final class DecimalEngine extends NativeRuntime { * ### Implements operations * @since 1.0.0 */ - use Arithmetic, Divide, Multiply, Round; + use Arithmetic, Divide, Mod, Multiply, Round; /** * ### Compares two positive decimal values diff --git a/tests/Unit/Math/DecimalEngineTest.php b/tests/Unit/Math/DecimalEngineTest.php index 176e9b4..2e498aa 100644 --- a/tests/Unit/Math/DecimalEngineTest.php +++ b/tests/Unit/Math/DecimalEngineTest.php @@ -523,4 +523,60 @@ public function testPositiveInfinity (string $expected, string $value, int $prec } + /** + * @since 1.0.0 + * + * @param string $expected + * @param non-empty-string $left + * @param non-empty-string $right + * + * @throws \FireHub\Runtime\Exception\InvalidDecimalNumberException + * @throws \DivisionByZeroError + * + * @return void + */ + #[TestWith(['1', '10', '3'])] + #[TestWith(['2', '11', '3'])] + #[TestWith(['0', '12', '3'])] + #[TestWith(['1', '13', '3'])] + #[TestWith(['2', '14', '3'])] + #[TestWith(['0', '0', '3'])] + #[TestWith(['0', '3', '3'])] + #[TestWith(['1', '4', '3'])] + #[TestWith(['2', '5', '3'])] + #[TestWith(['-1', '-10', '3'])] + #[TestWith(['-2', '-11', '3'])] + #[TestWith(['0', '-12', '3'])] + #[TestWith(['-1', '-13', '3'])] + #[TestWith(['1', '10', '-3'])] + #[TestWith(['2', '11', '-3'])] + #[TestWith(['0', '12', '-3'])] + #[TestWith(['-1', '-10', '-3'])] + #[TestWith(['-2', '-11', '-3'])] + #[TestWith(['0', '-12', '-3'])] + #[TestWith(['0.34', '12.34', '4'])] + #[TestWith(['0.34', '12.34', '4.0'])] + #[TestWith(['1.34', '13.34', '4'])] + #[TestWith(['0.5', '10.5', '5'])] + #[TestWith(['0.25', '10.25', '5'])] + #[TestWith(['0.01', '10.01', '5'])] + #[TestWith(['2.23', '12.23', '2.5'])] + #[TestWith(['0.1', '1.1', '0.5'])] + #[TestWith(['0', '1.5', '0.5'])] + #[TestWith(['0.34', '12.34', '4.0'])] + #[TestWith(['1.34', '13.34', '4.0'])] + #[TestWith(['0.02', '1.02', '0.5'])] + #[TestWith(['0.25', '1.25', '0.5'])] + #[TestWith(['1', '100000000000000000000000000000000000001', '100000000000000000000000000000000000000'])] + #[TestWith(['999999999999999999', '1999999999999999999', '1000000000000000000'])] + #[TestWith(['0', '100000000000000000000000000000000000000', '100000000000000000000000000000000000000'])] + #[TestWith(['1', '0000010', '0000003'])] + #[TestWith(['0.5', '0010.5', '5'])] + #[TestWith(['0.5', '10.5000', '5.0000'])] + public function testMod (string $expected, string $left, string $right):void { + + self::assertSame($expected, DecimalEngine::mod($left, $right)); + + } + } \ No newline at end of file