From 58bf0c2a4449288e98d74d47e8f2f886a1fdde3c Mon Sep 17 00:00:00 2001 From: "Elliot J. Reed" Date: Sun, 3 Dec 2023 15:14:30 +0000 Subject: [PATCH] Adds percentage increase and decrease calculation methods --- src/ElliotJReed/Maths/Number.php | 40 ++++ src/ElliotJReed/Maths/NumberFormat.php | 4 +- src/ElliotJReed/Maths/NumberImmutable.php | 40 ++++ .../ElliotJReed/Maths/NumberImmutableTest.php | 192 ++++++++++++++++++ tests/ElliotJReed/Maths/NumberTest.php | 132 ++++++++++++ 5 files changed, 406 insertions(+), 2 deletions(-) diff --git a/src/ElliotJReed/Maths/Number.php b/src/ElliotJReed/Maths/Number.php index 9d14176..9eb3ed1 100644 --- a/src/ElliotJReed/Maths/Number.php +++ b/src/ElliotJReed/Maths/Number.php @@ -164,4 +164,44 @@ public function raiseToPowerReduceByModulus( return $this; } + + /** + * @param \ElliotJReed\Maths\Number|int|float|string $percent the percentage to increase the "base" number by + * + * @return $this + */ + public function increaseByPercentage(self | int | float | string $percent): self + { + $percentAsString = $this->castNumberToString($percent); + + $increase = \bcmul( + $this->number, + \bcdiv($percentAsString, '100', $this->precision), + $this->precision + ); + + $this->number = \bcadd($this->number, $increase, $this->precision); + + return $this; + } + + /** + * @param \ElliotJReed\Maths\Number|int|float|string $percent the percentage to decrease the "base" number by + * + * @return $this + */ + public function decreaseByPercentage(self | int | float | string $percent): self + { + $percentAsString = $this->castNumberToString($percent); + + $increase = \bcmul( + $this->number, + \bcmul(\bcdiv($percentAsString, '100', $this->precision), '-1', $this->precision), + $this->precision + ); + + $this->number = \bcadd($this->number, $increase, $this->precision); + + return $this; + } } diff --git a/src/ElliotJReed/Maths/NumberFormat.php b/src/ElliotJReed/Maths/NumberFormat.php index 262a273..ed08c3d 100644 --- a/src/ElliotJReed/Maths/NumberFormat.php +++ b/src/ElliotJReed/Maths/NumberFormat.php @@ -12,9 +12,9 @@ abstract class NumberFormat /** * @param \ElliotJReed\Maths\Number|int|float|string $number (Optional) The "base" number. Default: 0 - * @param int $precision (Optional) The number of digits after the decimal place in the result. Default: 64 + * @param int $precision (Optional) The number of digits after the decimal place in the result. Default: 256 */ - public function __construct(self | int | float | string $number = 0, protected readonly int $precision = 64) + public function __construct(self | int | float | string $number = 0, protected readonly int $precision = 256) { $this->number = $this->castNumberToString($number); } diff --git a/src/ElliotJReed/Maths/NumberImmutable.php b/src/ElliotJReed/Maths/NumberImmutable.php index fc034b2..48aa2b8 100644 --- a/src/ElliotJReed/Maths/NumberImmutable.php +++ b/src/ElliotJReed/Maths/NumberImmutable.php @@ -166,4 +166,44 @@ public function raiseToPowerReduceByModulus( return new self($newNumber, $this->precision); } + + /** + * @param \ElliotJReed\Maths\Number|int|float|string $percent the percentage to increase the "base" number by + * + * @return $this Returns a new instance of \ElliotJReed\Maths\Number + */ + public function increaseByPercentage(self | int | float | string $percent): self + { + $percentAsString = $this->castNumberToString($percent); + + $increase = \bcmul( + $this->number, + \bcdiv($percentAsString, '100', $this->precision), + $this->precision + ); + + $newNumber = \bcadd($this->number, $increase, $this->precision); + + return new self($newNumber, $this->precision); + } + + /** + * @param \ElliotJReed\Maths\Number|int|float|string $percent the percentage to decrease the "base" number by + * + * @return $this Returns a new instance of \ElliotJReed\Maths\Number + */ + public function decreaseByPercentage(self | int | float | string $percent): self + { + $percentAsString = $this->castNumberToString($percent); + + $increase = \bcmul( + $this->number, + \bcmul(\bcdiv($percentAsString, '100', $this->precision), '-1', $this->precision), + $this->precision + ); + + $newNumber = \bcadd($this->number, $increase, $this->precision); + + return new self($newNumber, $this->precision); + } } diff --git a/tests/ElliotJReed/Maths/NumberImmutableTest.php b/tests/ElliotJReed/Maths/NumberImmutableTest.php index 2f85817..6a83227 100644 --- a/tests/ElliotJReed/Maths/NumberImmutableTest.php +++ b/tests/ElliotJReed/Maths/NumberImmutableTest.php @@ -1316,4 +1316,196 @@ public function testItThrowsExceptionWhenDecimalPlacesArgumentIsLessThanZeroWhen $number->roundToDecimalPlaces(-2); } + + public function testItIncreasesBaseNumberByPercentageWhenNumberIsAnInteger(): void + { + $number = new NumberImmutable(100); + $newNumber = $number->increaseByPercentage(10); + + $this->assertSame('110', $newNumber->asString()); + $this->assertSame(110.0, $newNumber->asFloat()); + $this->assertSame(110, $newNumber->asInteger()); + $this->assertSame(110, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100', $number->asString()); + $this->assertSame(100.0, $number->asFloat()); + $this->assertSame(100, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByPercentageWhenNumberIsAFloat(): void + { + $number = new NumberImmutable(100.50); + $newNumber = $number->increaseByPercentage(10.125); + + $this->assertSame('110.675625', $newNumber->asString()); + $this->assertSame(110.675625, $newNumber->asFloat()); + $this->assertSame(111, $newNumber->asInteger()); + $this->assertSame(111, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100.5', $number->asString()); + $this->assertSame(100.5, $number->asFloat()); + $this->assertSame(101, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByPercentageWhenNumberIsAString(): void + { + $number = new NumberImmutable('100.50'); + $newNumber = $number->increaseByPercentage('10.125'); + + $this->assertSame('110.675625', $newNumber->asString()); + $this->assertSame(110.675625, $newNumber->asFloat()); + $this->assertSame(111, $newNumber->asInteger()); + $this->assertSame(111, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100.5', $number->asString()); + $this->assertSame(100.5, $number->asFloat()); + $this->assertSame(101, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByNegativePercentageWhenNumberIsAnInteger(): void + { + $number = new NumberImmutable(100); + $newNumber = $number->increaseByPercentage(-10); + + $this->assertSame('90', $newNumber->asString()); + $this->assertSame(90.0, $newNumber->asFloat()); + $this->assertSame(90, $newNumber->asInteger()); + $this->assertSame(90, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100', $number->asString()); + $this->assertSame(100.0, $number->asFloat()); + $this->assertSame(100, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByNegativePercentageWhenNumberIsAFloat(): void + { + $number = new NumberImmutable(-100.25); + $newNumber = $number->increaseByPercentage(-10.55); + + $this->assertSame('-89.673625', $newNumber->asString()); + $this->assertSame(-89.673625, $newNumber->asFloat()); + $this->assertSame(-90, $newNumber->asInteger()); + $this->assertSame(-90, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('-100.25', $number->asString()); + $this->assertSame(-100.25, $number->asFloat()); + $this->assertSame(-100, $number->asInteger()); + $this->assertSame(-100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByNegativePercentageWhenNumberIsAString(): void + { + $number = new NumberImmutable('100.33'); + $newNumber = $number->increaseByPercentage('-10.66'); + + $this->assertSame('89.634822', $newNumber->asString()); + $this->assertSame(89.634822, $newNumber->asFloat()); + $this->assertSame(90, $newNumber->asInteger()); + $this->assertSame(90, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100.33', $number->asString()); + $this->assertSame(100.33, $number->asFloat()); + $this->assertSame(100, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByPercentageWhenNumberIsAnInteger(): void + { + $number = new NumberImmutable(100); + $newNumber = $number->decreaseByPercentage(10); + + $this->assertSame('90', $newNumber->asString()); + $this->assertSame(90.0, $newNumber->asFloat()); + $this->assertSame(90, $newNumber->asInteger()); + $this->assertSame(90, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100', $number->asString()); + $this->assertSame(100.0, $number->asFloat()); + $this->assertSame(100, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByPercentageWhenNumberIsAFloat(): void + { + $number = new NumberImmutable(10.99); + $newNumber = $number->decreaseByPercentage(5.123); + + $this->assertSame('10.4269823', $newNumber->asString()); + $this->assertSame(10.4269823, $newNumber->asFloat()); + $this->assertSame(10, $newNumber->asInteger()); + $this->assertSame(10, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('10.99', $number->asString()); + $this->assertSame(10.99, $number->asFloat()); + $this->assertSame(11, $number->asInteger()); + $this->assertSame(11, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByPercentageWhenNumberIsAString(): void + { + $number = new NumberImmutable('10.99'); + $newNumber = $number->decreaseByPercentage('5.123'); + + $this->assertSame('10.4269823', $newNumber->asString()); + $this->assertSame(10.4269823, $newNumber->asFloat()); + $this->assertSame(10, $newNumber->asInteger()); + $this->assertSame(10, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('10.99', $number->asString()); + $this->assertSame(10.99, $number->asFloat()); + $this->assertSame(11, $number->asInteger()); + $this->assertSame(11, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByNegativePercentageWhenNumberIsAnInteger(): void + { + $number = new NumberImmutable(100); + $newNumber = $number->decreaseByPercentage(-10); + + $this->assertSame('110', $newNumber->asString()); + $this->assertSame(110.0, $newNumber->asFloat()); + $this->assertSame(110, $newNumber->asInteger()); + $this->assertSame(110, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('100', $number->asString()); + $this->assertSame(100.0, $number->asFloat()); + $this->assertSame(100, $number->asInteger()); + $this->assertSame(100, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByNegativePercentageWhenNumberIsAFloat(): void + { + $number = new NumberImmutable(25.5); + $newNumber = $number->decreaseByPercentage(15.25); + + $this->assertSame('21.61125', $newNumber->asString()); + $this->assertSame(21.61125, $newNumber->asFloat()); + $this->assertSame(22, $newNumber->asInteger()); + $this->assertSame(22, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('25.5', $number->asString()); + $this->assertSame(25.5, $number->asFloat()); + $this->assertSame(26, $number->asInteger()); + $this->assertSame(25, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByNegativePercentageWhenNumberIsAString(): void + { + $number = new NumberImmutable('25.5'); + $newNumber = $number->decreaseByPercentage('15.25'); + + $this->assertSame('21.61125', $newNumber->asString()); + $this->assertSame(21.61125, $newNumber->asFloat()); + $this->assertSame(22, $newNumber->asInteger()); + $this->assertSame(22, $newNumber->asInteger(\PHP_ROUND_HALF_DOWN)); + + $this->assertSame('25.5', $number->asString()); + $this->assertSame(25.5, $number->asFloat()); + $this->assertSame(26, $number->asInteger()); + $this->assertSame(25, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } } diff --git a/tests/ElliotJReed/Maths/NumberTest.php b/tests/ElliotJReed/Maths/NumberTest.php index 4360e60..3421608 100644 --- a/tests/ElliotJReed/Maths/NumberTest.php +++ b/tests/ElliotJReed/Maths/NumberTest.php @@ -1001,4 +1001,136 @@ public function testItThrowsExceptionWhenDecimalPlacesArgumentIsLessThanZeroWhen $number->roundToDecimalPlaces(-2); } + + public function testItIncreasesBaseNumberByPercentageWhenNumberIsAnInteger(): void + { + $number = new Number(100); + $number->increaseByPercentage(10); + + $this->assertSame('110', $number->asString()); + $this->assertSame(110.0, $number->asFloat()); + $this->assertSame(110, $number->asInteger()); + $this->assertSame(110, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByPercentageWhenNumberIsAFloat(): void + { + $number = new Number(100.50); + $number->increaseByPercentage(10.125); + + $this->assertSame('110.675625', $number->asString()); + $this->assertSame(110.675625, $number->asFloat()); + $this->assertSame(111, $number->asInteger()); + $this->assertSame(111, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByPercentageWhenNumberIsAString(): void + { + $number = new Number('100.50'); + $number->increaseByPercentage('10.125'); + + $this->assertSame('110.675625', $number->asString()); + $this->assertSame(110.675625, $number->asFloat()); + $this->assertSame(111, $number->asInteger()); + $this->assertSame(111, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByNegativePercentageWhenNumberIsAnInteger(): void + { + $number = new Number(100); + $number->increaseByPercentage(-10); + + $this->assertSame('90', $number->asString()); + $this->assertSame(90.0, $number->asFloat()); + $this->assertSame(90, $number->asInteger()); + $this->assertSame(90, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByNegativePercentageWhenNumberIsAFloat(): void + { + $number = new Number(-100.25); + $number->increaseByPercentage(-10.55); + + $this->assertSame('-89.673625', $number->asString()); + $this->assertSame(-89.673625, $number->asFloat()); + $this->assertSame(-90, $number->asInteger()); + $this->assertSame(-90, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItIncreasesBaseNumberByNegativePercentageWhenNumberIsAString(): void + { + $number = new Number('100.33'); + $number->increaseByPercentage('-10.66'); + + $this->assertSame('89.634822', $number->asString()); + $this->assertSame(89.634822, $number->asFloat()); + $this->assertSame(90, $number->asInteger()); + $this->assertSame(90, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByPercentageWhenNumberIsAnInteger(): void + { + $number = new Number(100); + $number->decreaseByPercentage(10); + + $this->assertSame('90', $number->asString()); + $this->assertSame(90.0, $number->asFloat()); + $this->assertSame(90, $number->asInteger()); + $this->assertSame(90, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByPercentageWhenNumberIsAFloat(): void + { + $number = new Number(10.99); + $number->decreaseByPercentage(5.123); + + $this->assertSame('10.4269823', $number->asString()); + $this->assertSame(10.4269823, $number->asFloat()); + $this->assertSame(10, $number->asInteger()); + $this->assertSame(10, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByPercentageWhenNumberIsAString(): void + { + $number = new Number('10.99'); + $number->decreaseByPercentage('5.123'); + + $this->assertSame('10.4269823', $number->asString()); + $this->assertSame(10.4269823, $number->asFloat()); + $this->assertSame(10, $number->asInteger()); + $this->assertSame(10, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByNegativePercentageWhenNumberIsAnInteger(): void + { + $number = new Number(100); + $number->decreaseByPercentage(-10); + + $this->assertSame('110', $number->asString()); + $this->assertSame(110.0, $number->asFloat()); + $this->assertSame(110, $number->asInteger()); + $this->assertSame(110, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByNegativePercentageWhenNumberIsAFloat(): void + { + $number = new Number(25.5); + $number->decreaseByPercentage(15.25); + + $this->assertSame('21.61125', $number->asString()); + $this->assertSame(21.61125, $number->asFloat()); + $this->assertSame(22, $number->asInteger()); + $this->assertSame(22, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } + + public function testItDecreasesBaseNumberByNegativePercentageWhenNumberIsAString(): void + { + $number = new Number('25.5'); + $number->decreaseByPercentage('15.25'); + + $this->assertSame('21.61125', $number->asString()); + $this->assertSame(21.61125, $number->asFloat()); + $this->assertSame(22, $number->asInteger()); + $this->assertSame(22, $number->asInteger(\PHP_ROUND_HALF_DOWN)); + } }