Passed
Push — master ( 51b511...9aadcc )
by Victor
02:29
created

Flattener::setAddStep()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Vctls\IntervalGraph;
4
5
use Closure;
6
7
/**
8
 * Transforms an array of overlapping intervals into another array of adjacent intervals.
9
 *
10
 * Each new interval holds the keys of the corresponding original intervals.
11
 *
12
 * @package Vctls\IntervalGraph
13
 */
14
class Flattener
15
{
16
    /** @var Closure Substract one step from a bound value. */
17
    protected $substractStep;
18
19
    /** @var Closure Add one step to the bound value. */
20
    protected $addStep;
21
22
    /**
23
     * Set the Closure for decrementing bound values when dealing with
24
     * discontinuous sets.
25
     *
26
     * @param Closure $substractStep
27
     * @return Flattener
28
     */
29
    public function setSubstractStep(Closure $substractStep): Flattener
30
    {
31
        $this->substractStep = $substractStep;
32
        return $this;
33
    }
34
35
    /**
36
     * Set the Closure for incrementing bound values when dealing with
37
     * discontinuous sets.
38
     *
39
     * @param Closure $addStep
40
     * @return Flattener
41
     */
42
    public function setAddStep(Closure $addStep): Flattener
43
    {
44
        $this->addStep = $addStep;
45
        return $this;
46
    }
47
48
    /**
49
     * Make an array of bounds from an array of intervals.
50
     *
51
     * Assign the value of the interval to each bound.
52
     *
53
     * Assign and a '+' sign if it is a low bound, and a '-' if it is an high bound.
54
     * ```
55
     * bound = [
56
     *   bound value,
57
     *   bound type,
58
     *   TODO included,
59
     *   interval key,
60
     *   interval value
61
     * ]
62
     * ```
63
     *
64
     * @param array[] $intervals
65
     * @return array[]
66
     */
67
    public static function intervalsToSignedBounds($intervals): array
68
    {
69
        $bounds = [];
70
        foreach ($intervals as $key => $interval) {
71
            // TODO Get included boolean from interval bound.
72
            $bounds[] = [$interval[1], '-', true, $key, $interval[2] ?? null];
73
            $bounds[] = [$interval[0], '+', true, $key, $interval[2] ?? null];
74
        }
75
        // Order the bounds.
76
        usort($bounds, static function (array $d1, array $d2) {
77
            return ($d1[0] < $d2[0]) ? -1 : 1;
78
        });
79
        return $bounds;
80
    }
81
82
    /**
83
     * Create each new interval and calculate its value based on the active intervals on each bound.
84
     *
85
     * @param array[] $intervals
86
     * @return array[]
87
     */
88
    public function calcAdjacentIntervals(array $intervals): array
89
    {
90
91
        $bounds = self::intervalsToSignedBounds($intervals);
92
        $newIntervals = [];
93
        $activeIntervals = [];
94
95
        $boundsCount = count($bounds);
96
97
        // Create new intervals for each set of two consecutive bounds,
98
        // and calculate its total value.
99
        for ($i = 1; $i < $boundsCount; $i++) {
100
101
            // Set the current bound.
102
            [$curBoundValue, $curBoundType, $curBoundIncluded, $curBoundIntervalKey] = $bounds[$i - 1];
103
            [$nextBoundValue, $nextBoundType, $nextBoundIncluded] = $bounds[$i];
104
105
            if ($curBoundType === '+') {
106
                // If this is a low bound,
107
                // add the key of the interval to the array of active intervals.
108
                $activeIntervals[$curBoundIntervalKey] = true;
109
            } else {
110
                // If this is an high bound, remove the key.
111
                unset($activeIntervals[$curBoundIntervalKey]);
112
            }
113
114
            if (
115
                isset($this->addStep, $this->substractStep) && (
116
                    ($nextBoundIncluded && $nextBoundType === '+')
117
                    || (!$nextBoundIncluded && $nextBoundType === '+')
118
                )
119
            ) {
120
                $newHighBound = ($this->substractStep)($nextBoundValue);
121
            } else {
122
                $newHighBound = $nextBoundValue;
123
            }
124
125
            if (
126
                isset($this->addStep, $this->substractStep) && $curBoundType === '-' && $curBoundIncluded
127
            ) {
128
                $newLowBound = ($this->addStep)($curBoundValue);
129
            } else {
130
                $newLowBound = $curBoundValue;
131
            }
132
133
            $newIntervals[] = [
134
                $newLowBound,
135
                $newHighBound,
136
                $activeIntervals
137
            ];
138
        }
139
140
        return $newIntervals;
141
    }
142
}
143