Passed
Pull Request — master (#2310)
by
unknown
17:04 queued 11:52
created

StuRandom::rand()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4.5923

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 5
dl 0
loc 17
ccs 4
cts 6
cp 0.6667
crap 4.5923
rs 10
c 0
b 0
f 0
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
    /**
32
     * Return a random key from the given array.
33
     *
34
     * @template TKey of int|string
35
     * @param array<TKey, mixed> $array The array to pick a key from.
36
     * @return TKey A randomly selected key from the array.
0 ignored issues
show
Bug introduced by
The type Stu\Module\Control\TKey was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
     */
38
    public function array_rand(array $array): string|int
39
    {
40
        if ($array === []) {
41
            throw new RuntimeException('Cannot pick a random key from an empty array');
42
        }
43
        
44
        /** @var TKey of int|string */
45
        $result = array_rand($array);
46
47
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result 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...
48
    }
49
50
    /** @param array<int, int> $probabilities */
51
    public function randomKeyOfProbabilities(array $probabilities): int
52
    {
53
        $totalProbability = array_sum($probabilities);
54
55
        $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

55
        $randomNumber = random_int(1, /** @scrutinizer ignore-type */ $totalProbability);
Loading history...
56
        $cumulativeProbability = 0;
57
58
        foreach ($probabilities as $key => $prob) {
59
            $cumulativeProbability += $prob;
60
            if ($randomNumber <= $cumulativeProbability) {
61
                return $key;
62
            }
63
        }
64
65
        throw new RuntimeException('this should not happen');
66
    }
67
68
    public function uniqid(): string
69
    {
70
        return uniqid();
71
    }
72
73 1
    private function generateRandomValueStandardNormalDistribution(int $min, int $max, ?int $mean): int
74
    {
75 1
        $usedMean = $mean ?? ($min + $max) / 2; // MW
76 1
        $stdDeviation = $usedMean / 2.5; // FWHM
77
78
        do {
79 1
            $value = random_int($min, $max);
80 1
            $probability = exp(-0.5 * (($value - $usedMean) / $stdDeviation) ** 2); // normal distribution
81 1
            $randomProbability = random_int(0, mt_getrandmax()) / mt_getrandmax();
82
83 1
            if ($randomProbability <= $probability) {
84 1
                return $value;
85
            }
86 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...
87
    }
88
89
    private function generateRandomValueAsymmetricDistribution(int $min, int $max, ?int $mean = null, float $skewness = 0): int
90
    {
91
        $usedMean = $mean ?? ($min + $max) / 2;
92
        $stdDeviation = ($max - $min) / 6;
93
94
        do {
95
            $value = random_int($min, $max);
96
            $probability = exp(-0.5 * (($value - $usedMean) / $stdDeviation) ** 2);
97
98
            if ($skewness !== 0.0) {
99
                $skewFactor = 1 + $skewness * (($value - $usedMean) / ($max - $min));
100
                $probability *= max(0, $skewFactor);
101
            }
102
103
            $randomProbability = random_int(0, mt_getrandmax()) / mt_getrandmax();
104
105
            if ($randomProbability <= $probability) {
106
                return $value;
107
            }
108
        } 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...
109
    }
110
}
111