Skip to content

Commit abd5acb

Browse files
committed
Smaller fixes, added new tests
1 parent 381cdb9 commit abd5acb

File tree

12 files changed

+423
-44
lines changed

12 files changed

+423
-44
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ PageRank
1515
</p>
1616

1717
<p align="center">
18-
This source code is an OOP implementation of the PageRank algorithm, under MIT licence.
18+
This source code is an OOP implementation of the PageRank algorithm.
1919
<br />The minimum required PHP version is 7.4.
2020
<br />
2121
<br />
@@ -35,7 +35,7 @@ long-running calculation can be scheduled in batches using the Strategy OOP patt
3535
iteration number.
3636
* However, the iteration stops when the ranks are accurate enough even if the max iteration didn't reach its limit.
3737
* The accuracy measured by the float epsilon constant.
38-
* At the end the algorithm normalizes the ranks between 0 and 1 and then scale them between 1 and 10. The scaling range
38+
* At the end, the algorithm normalizes the ranks between 0 and 1 and then scale them between 1 and 10. The scaling range
3939
is configurable.
4040
* Getting, setting, updating the nodes from the resource is a responsibility of the NodeDataSourceStrategyInterface.
4141
* The package provides a simple implementation of the NodeDataSourceStrategyInterface that only keeps the nodes in the

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
"email": "87.bdavid@gmail.com"
1010
}
1111
],
12-
"require": {},
12+
"require": {
13+
"php": "7.4.*"
14+
},
1315
"autoload": {
1416
"psr-4": {
1517
"PhpScience\\PageRank\\": [
1618
"src",
19+
"tests/unit",
1720
"tests/functional"
1821
]
1922
}

src/Service/Normalizer.php

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/Service/PageRankAlgorithm.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpScience\PageRank\Service;
66

77
use PhpScience\PageRank\Data\NodeCollectionInterface;
8+
use PhpScience\PageRank\Service\PageRankAlgorithm\NormalizerInterface;
89
use PhpScience\PageRank\Service\PageRankAlgorithm\RankingInterface;
910
use PhpScience\PageRank\Strategy\NodeDataSourceStrategyInterface;
1011

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpScience\PageRank\Service\PageRankAlgorithm;
6+
7+
use PhpScience\PageRank\Data\NodeCollectionInterface;
8+
9+
class Normalizer implements NormalizerInterface
10+
{
11+
private float $scaleBottom;
12+
private float $scaleTop;
13+
14+
public function __construct(
15+
float $scaleBottom = 1,
16+
float $scaleTop = 10
17+
) {
18+
$this->scaleBottom = $scaleBottom;
19+
$this->scaleTop = $scaleTop;
20+
}
21+
22+
public function normalize(
23+
NodeCollectionInterface $nodeCollection,
24+
float $lowestRank,
25+
float $highestRank
26+
): void {
27+
$divider = $this->getDivider($lowestRank, $highestRank);
28+
29+
foreach ($nodeCollection->getNodes() as $node) {
30+
$rank = $this->getScaledRank(
31+
$node->getRank(),
32+
$lowestRank,
33+
$divider
34+
);
35+
$node->setRank($rank);
36+
}
37+
}
38+
39+
private function getDivider(float $lowestRank, float $highestRank): float
40+
{
41+
$divider = $highestRank - $lowestRank;
42+
43+
if (.0 === $divider) {
44+
$divider = 1;
45+
}
46+
47+
return $divider;
48+
}
49+
50+
private function getScaledRank(
51+
float $value,
52+
float $lowestRank,
53+
float $divider
54+
): float {
55+
$normalized = ($value - $lowestRank) / $divider;
56+
$multiplier = $this->scaleTop - $this->scaleBottom;
57+
58+
return ($normalized * $multiplier) + $this->scaleBottom;
59+
}
60+
}

src/Service/NormalizerInterface.php renamed to src/Service/PageRankAlgorithm/NormalizerInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace PhpScience\PageRank\Service;
5+
namespace PhpScience\PageRank\Service\PageRankAlgorithm;
66

77
use PhpScience\PageRank\Data\NodeCollectionInterface;
88

tests/functional/Service/PageRankAlgorithmTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use PhpScience\PageRank\Builder\NodeBuilder;
88
use PhpScience\PageRank\Builder\NodeCollectionBuilder;
9+
use PhpScience\PageRank\Service\PageRankAlgorithm\Normalizer;
910
use PhpScience\PageRank\Service\PageRankAlgorithm\RankComparator;
1011
use PhpScience\PageRank\Service\PageRankAlgorithm\Ranking;
1112
use PhpScience\PageRank\Strategy\MemorySourceStrategy;

tests/phpunit.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
colors="true">
66

77
<testsuites>
8+
<testsuite name="Unit Tests">
9+
<directory>../tests/unit</directory>
10+
</testsuite>
811
<testsuite name="Functional Tests">
912
<directory>../tests/functional</directory>
1013
</testsuite>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpScience\PageRank\Builder;
6+
7+
use PHPUnit\Framework\TestCase;
8+
9+
class NodeBuilderTest extends TestCase
10+
{
11+
private NodeBuilder $nodeBuilder;
12+
13+
protected function setUp(): void
14+
{
15+
$this->nodeBuilder = new NodeBuilder();
16+
}
17+
18+
public function testBuild(): void
19+
{
20+
$expected = 0.25;
21+
22+
$data = [
23+
'id' => 1,
24+
'rank' => $expected
25+
];
26+
27+
$actual = $this->nodeBuilder->build($data);
28+
29+
static::assertSame($expected, $actual->getRank());
30+
}
31+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpScience\PageRank\Service\PageRankAlgorithm;
6+
7+
use PhpScience\PageRank\Data\NodeCollectionInterface;
8+
use PhpScience\PageRank\Data\NodeInterface;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class NormalizerTest extends TestCase
12+
{
13+
/**
14+
* @dataProvider dataProviderNormalize
15+
*
16+
* @param float $originalRank
17+
* @param float $scaleBottom
18+
* @param float $scaleTop
19+
* @param float $lowestRank
20+
* @param float $highestRank
21+
* @param float $expectedRank
22+
*/
23+
public function testNormalize(
24+
float $originalRank,
25+
float $scaleBottom,
26+
float $scaleTop,
27+
float $lowestRank,
28+
float $highestRank,
29+
float $expectedRank
30+
): void {
31+
$nodeCollection = $this
32+
->createMock(NodeCollectionInterface::class);
33+
$node = $this
34+
->createMock(NodeInterface::class);
35+
36+
$nodeCollection
37+
->expects($this->once())
38+
->method('getNodes')
39+
->willReturn([$node]);
40+
41+
$node
42+
->expects($this->once())
43+
->method('getRank')
44+
->willReturn($originalRank);
45+
46+
$node
47+
->expects($this->once())
48+
->method('setRank')
49+
->with($expectedRank);
50+
51+
$normalizer = new Normalizer(
52+
$scaleBottom,
53+
$scaleTop
54+
);
55+
56+
$normalizer->normalize(
57+
$nodeCollection,
58+
$lowestRank,
59+
$highestRank
60+
);
61+
}
62+
63+
public function dataProviderNormalize(): array
64+
{
65+
return [
66+
'realistic' => [
67+
1.234,
68+
1.0,
69+
10.0,
70+
-5.0,
71+
5.0,
72+
6.6106
73+
],
74+
'division_by_zero' => [
75+
5,
76+
1.0,
77+
10.0,
78+
5.0,
79+
5.0,
80+
1.0
81+
],
82+
'division_by_float_epsilon' => [
83+
5,
84+
1.0,
85+
10.0,
86+
5.0,
87+
5.0 + PHP_FLOAT_EPSILON,
88+
1.0
89+
],
90+
'scale_from_minus' => [
91+
0.12577,
92+
-5,
93+
5,
94+
100,
95+
1000,
96+
-6.109713666666667
97+
]
98+
];
99+
}
100+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpScience\PageRank\Service\PageRankAlgorithm;
6+
7+
use PHPUnit\Framework\TestCase;
8+
9+
class RankComparatorTest extends TestCase
10+
{
11+
private RankComparator $rankComparator;
12+
13+
protected function setUp(): void
14+
{
15+
$this->rankComparator = new RankComparator();
16+
}
17+
18+
/**
19+
* @dataProvider dataProviderIsEqual
20+
*
21+
* @param float $rank1
22+
* @param float $rank2
23+
* @param bool $expected
24+
*/
25+
public function testIsEqual(
26+
float $rank1,
27+
float $rank2,
28+
bool $expected
29+
): void {
30+
$actual = $this->rankComparator->isEqual($rank1, $rank2);
31+
32+
static::assertSame($expected, $actual);
33+
}
34+
35+
public function dataProviderIsEqual(): array
36+
{
37+
return [
38+
'not_equal' => [
39+
.1, .2, false
40+
],
41+
'equal' => [
42+
.1, .1, true
43+
],
44+
'absolute_value_of_minus' => [
45+
.1, .2, false
46+
],
47+
'absolute_of_minus_one_is_bigger_than_float_epsilon' => [
48+
1, 2, false
49+
],
50+
'smallest_representable_difference' => [
51+
1, 1 + PHP_FLOAT_EPSILON, false
52+
],
53+
'non_representable_difference' => [
54+
1, 1 + 2.2204460492503e-17, true
55+
]
56+
];
57+
}
58+
}

0 commit comments

Comments
 (0)