Passed
Push — master ( 45bf8d...c799c1 )
by Nico
26:28 queued 15:48
created

StuRandom   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 85
Duplicated Lines 0 %

Test Coverage

Coverage 32.5%

Importance

Changes 0
Metric Value
eloc 37
dl 0
loc 85
ccs 13
cts 40
cp 0.325
rs 10
c 0
b 0
f 0
wmc 16

6 Methods

Rating   Name   Duplication   Size   Complexity  
A array_rand() 0 3 1
A generateRandomValueStandardNormalDistribution() 0 14 3
A rand() 0 17 4
A generateRandomValueAsymmetricDistribution() 0 20 4
A randomKeyOfProbabilities() 0 15 3
A uniqid() 0 3 1
1
<?php
2
3
namespace Stu\Module\Control;
4
5
use RuntimeException;
6
7
/**
8
 * This class adds the possibility to inject a random generator
9
 */
10
class StuRandom
11
{
12 1
    public function rand(
13
        int $min,
14
        int $max,
15
        bool $useStandardNormalDistribution = false,
16
        ?int $mean = null,
17
        float $skewness = 0
18
    ): int {
19 1
        if ($skewness != 0 && $useStandardNormalDistribution) {
20
            return $this->generateRandomValueAsymmetricDistribution($min, $max, $mean, $skewness);
21
        }
22
23 1
        if ($useStandardNormalDistribution) {
24 1
            return $this->generateRandomValueStandardNormalDistribution($min, $max, $mean);
25
        }
26
27
28
        return random_int($min, $max);
29
    }
30
31
    public function array_rand(array $array): string|int
32
    {
33
        return array_rand($array);
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_rand($array) could return the type array which is incompatible with the type-hinted return integer|string. Consider adding an additional type-check to rule them out.
Loading history...
34
    }
35
36
    /** @param array<int, int> $probabilities */
37
    public function randomKeyOfProbabilities(array $probabilities): int
38
    {
39
        $totalProbability = array_sum($probabilities);
40
41
        $randomNumber = random_int(1, $totalProbability);
0 ignored issues
show
Bug introduced by
It seems like $totalProbability can also be of type double; however, parameter $max of random_int() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

41
        $randomNumber = random_int(1, /** @scrutinizer ignore-type */ $totalProbability);
Loading history...
42
        $cumulativeProbability = 0;
43
44
        foreach ($probabilities as $key => $prob) {
45
            $cumulativeProbability += $prob;
46
            if ($randomNumber <= $cumulativeProbability) {
47
                return $key;
48
            }
49
        }
50
51
        throw new RuntimeException('this should not happen');
52
    }
53
54
    public function uniqid(): string
55
    {
56
        return uniqid();
57
    }
58
59 1
    private function generateRandomValueStandardNormalDistribution(int $min, int $max, ?int $mean): int
60
    {
61 1
        $usedMean = $mean ?? ($min + $max) / 2; // MW
62 1
        $stdDeviation = $usedMean / 2.5; // FWHM
63
64
        do {
65 1
            $value = random_int($min, $max);
66 1
            $probability = exp(-0.5 * (($value - $usedMean) / $stdDeviation) ** 2); // normal distribution
67 1
            $randomProbability = random_int(0, mt_getrandmax()) / mt_getrandmax();
68
69 1
            if ($randomProbability <= $probability) {
70 1
                return $value;
71
            }
72 1
        } while (true);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
73
    }
74
75
    private function generateRandomValueAsymmetricDistribution(int $min, int $max, ?int $mean = null, float $skewness = 0): int
76
    {
77
        $usedMean = $mean ?? ($min + $max) / 2;
78
        $stdDeviation = ($max - $min) / 6;
79
80
        do {
81
            $value = random_int($min, $max);
82
            $probability = exp(-0.5 * (($value - $usedMean) / $stdDeviation) ** 2);
83
84
            if ($skewness) {
85
                $skewFactor = 1 + $skewness * (($value - $usedMean) / ($max - $min));
86
                $probability *= max(0, $skewFactor);
87
            }
88
89
            $randomProbability = random_int(0, mt_getrandmax()) / mt_getrandmax();
90
91
            if ($randomProbability <= $probability) {
92
                return $value;
93
            }
94
        } while (true);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
95
    }
96
}