Skip to content

Commit

Permalink
Allow for null values
Browse files Browse the repository at this point in the history
  • Loading branch information
deleugpn committed Jul 14, 2020
1 parent 768be1f commit b6719f4
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 50 deletions.
4 changes: 2 additions & 2 deletions src/Decimal.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ final class Decimal

private $decimal;

public function __construct($key, float $decimal)
public function __construct($key, ?float $decimal)
{
$this->key = $key;
$this->decimal = $decimal;
Expand All @@ -19,7 +19,7 @@ public function key()
return $this->key;
}

public function value(): float
public function value(): ?float
{
return $this->decimal;
}
Expand Down
28 changes: 28 additions & 0 deletions src/Definition/NullNumber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types=1);

namespace CustomerGauge\Math\Remainder\Definition;

use LogicException;

final class NullNumber implements Number
{
public function integer(): ?int
{
return null;
}

public function value(): ?float
{
return null;
}

public function increment(): void
{
throw new LogicException("A Null Value cannot be incremented.");
}

public function decimal(): ?float
{
return null;
}
}
14 changes: 14 additions & 0 deletions src/Definition/Number.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace CustomerGauge\Math\Remainder\Definition;

interface Number
{
public function integer(): ?int;

public function value(): ?float;

public function increment(): void;

public function decimal(): ?float;
}
16 changes: 16 additions & 0 deletions src/Definition/NumberFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace CustomerGauge\Math\Remainder\Definition;

final class NumberFactory
{

public static function make($key, ?int $number, int $sum, int $precision)
{
if ($sum === 0 || $number === null) {
return new NullNumber();
}

return new RegularNumber($key, $number, $sum, $precision);
}
}
83 changes: 83 additions & 0 deletions src/Definition/RegularNumber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php declare(strict_types=1);

namespace CustomerGauge\Math\Remainder\Definition;

final class RegularNumber implements Number
{
/**
* Original array key that identifies the number's position. Useful to return
* back an array in the exact same order that it was received.
*
* @var int|string
*/
private $key;

/**
* Result of the division between $part and $total, multiplied by $precision.
*
* @var float|int
*/
private $quotient;

/**
* $quotient rounded down, disregarding any decimal places.
*
* @var int
*/
private $integer;

/**
* Final value after applying the Largest Remainder Method. It starts as $integer, but
* can be incremented when deemed necessary.
*
* @var int
*/
private $value;

/**
* All of the decimal places of quotient. It is the difference between $quotient - $integer.
* It is used to determine which numbers receive an increment by sorting on largest
* decimal places.
*
* @var float
*/
private $decimal;

/**
* Amount of decimal places that will be taken into account before incrementing.
*
* @var int
*/
private $precision;

public function __construct($key, ?int $part, ?int $total, int $precision)
{
$this->key = $key;
$this->quotient = ($part / $total) * $precision;
$this->integer = (int) $this->quotient;
$this->value = $this->integer;
$this->decimal = $this->quotient - $this->integer;
$this->precision = $precision;
}

public function integer(): int
{
return $this->integer;
}

public function value(): float
{
// Since the quotient was multiplied by the precision, we need to divide it back.
return $this->value / $this->precision;
}

public function increment(): void
{
$this->value++;
}

public function decimal(): float
{
return $this->decimal;
}
}
40 changes: 0 additions & 40 deletions src/Number.php

This file was deleted.

25 changes: 20 additions & 5 deletions src/Remainder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace CustomerGauge\Math\Remainder;

use CustomerGauge\Math\Remainder\Definition\NumberFactory;

final class Remainder
{
private $numbers;
Expand All @@ -24,7 +26,7 @@ public function round(int $precision): array
$decimals = [];

foreach ($this->numbers as $key => $number) {
$numberObject = new Number($key, $number, $this->sum, $precision);
$numberObject = NumberFactory::make($key, $number, $this->sum, $precision);

$numbers[$key] = $numberObject;

Expand All @@ -34,21 +36,34 @@ public function round(int $precision): array
}

usort($decimals, function (Decimal $a, Decimal $b) {
return $b->value() <=> $a->value();
if ($b->value() > $a->value()) {
return 1;
}

// If both values are equal, let's try to preserve the exact same order of their original keys.
if ($a->value() == $b->value()) {
return $a->key() > $b->key();
}

return -1;
});

$remaining = $precision - $this->accumulatedSumWithoutDecimals;
if ($this->accumulatedSumWithoutDecimals) {
$remaining = $precision - $this->accumulatedSumWithoutDecimals;
} else {
$remaining = 0;
}

for ($i = 0; $i < $remaining; $i++) {
$key = $decimals[$i]->key();

$numbers[$key]->increase();
$numbers[$key]->increment();
}

$result = [];

foreach ($numbers as $key => $number) {
$result[$key] = $number->integer() / $precision;
$result[$key] = $number->value();
}

return $result;
Expand Down
24 changes: 21 additions & 3 deletions tests/RemainderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function test_basic_division()

$result = $remainder->round(4);

self::assertSame([1, 0, 0], $result);
self::assertEquals([1, 0, 0], $result);
}

public function test_division_with_two_numbers()
Expand All @@ -22,7 +22,7 @@ public function test_division_with_two_numbers()

$result = $remainder->round(4);

self::assertSame([0.5, 0.5, 0], $result);
self::assertEquals([0.5, 0.5, 0], $result);
}

public function test_array_order_is_kept()
Expand All @@ -31,7 +31,7 @@ public function test_array_order_is_kept()

$result = $remainder->round(4);

self::assertSame([0, 0.5, 0.5], $result);
self::assertEquals([0, 0.5, 0.5], $result);
}

public function test_basic_remainder_decision()
Expand All @@ -55,4 +55,22 @@ public function test_irrational_numbers()
0.4211 // 0.42105263157894736842105263157895
], $result);
}

public function test_null_values_will_always_return_null()
{
$remainder = new Remainder([null, null, null]);

$result = $remainder->round(4);

self::assertSame([null, null, null], $result);
}

public function test_null_will_not_receive_increment()
{
$remainder = new Remainder([null, 1, 1, 1]);

$result = $remainder->round(4);

self::assertSame([null, 0.3334, 0.3333, 0.3333], $result);
}
}

0 comments on commit b6719f4

Please sign in to comment.