Completed
Branch master (eb975e)
by Basarab
06:37 queued 04:53
created

KDTree   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 3
dl 0
loc 179
ccs 76
cts 76
cp 1
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 39 7
A getItemCount() 0 4 1
A getRoot() 0 4 1
A getMinBoundary() 0 4 1
A getMaxBoundary() 0 4 1
A getDimensionCount() 0 4 1
A buildSubTree() 0 18 3
A exch() 0 6 1
A select() 0 14 4
B partition() 0 27 6
1
<?php
2
3
namespace Hexogen\KDTree;
4
5
class KDTree implements KDTreeInterface
6
{
7
    /**
8
     * @var NodeInterface
9
     */
10
    private $root;
11
12
    /**
13
     * @var ItemInterface[]|null array of items or null after tree has been built
14
     */
15
    private $items;
16
17
    /**
18
     * @var array
19
     */
20
    private $maxBoundary;
21
22
    /**
23
     * @var array
24
     */
25
    private $minBoundary;
26
27
    /**
28
     * @var int number of items in the tree
29
     */
30
    private $length;
31
32
    /**
33
     * @var int
34
     */
35
    private $dimensions;
36
37 42
    public function __construct(ItemList $itemList)
38
    {
39 42
        $this->dimensions = $itemList->getDimensionCount();
40 42
        $this->items = $itemList->getItems();
41 42
        $this->length = count($this->items);
42
43 42
        $this->maxBoundary = [];
44 42
        $this->minBoundary = [];
45
46 42
        for ($i = 0; $i < $this->dimensions; $i++) {
47 42
            $this->maxBoundary[$i] = -INF;
48 42
            $this->minBoundary[$i] = INF;
49
        }
50
51 42
        foreach ($this->items as $item) {
52 39
            for ($i = 0; $i < $this->dimensions; $i++) {
53 39
                $this->maxBoundary[$i] = max($this->maxBoundary[$i], $item->getNthDimension($i));
54 39
                $this->minBoundary[$i] = min($this->minBoundary[$i], $item->getNthDimension($i));
55
            }
56
        }
57
58 42
        $this->root = null;
59 42
        if ($this->length > 0) {
60 39
            $hi = $this->length - 1;
61 39
            $mid = (int)($hi / 2);
62 39
            $item = $this->select($mid, 0, $hi, 0);
63
64 39
            $this->root = new Node($item);
65
66 39
            $nextDimension = 1 % $this->dimensions;
67 39
            if ($mid > 0) {
68 36
                $this->root->setLeft($this->buildSubTree(0, $mid - 1, $nextDimension));
69
            }
70 39
            if ($mid < $hi) {
71 36
                $this->root->setRight($this->buildSubTree($mid + 1, $hi, $nextDimension));
72
            }
73
        }
74 42
        $this->items = null;
75 42
    }
76
77 3
    public function getItemCount(): int
78
    {
79 3
        return $this->length;
80
    }
81
82 27
    public function getRoot(): NodeInterface
83
    {
84 27
        return $this->root;
85
    }
86
87 3
    public function getMinBoundary(): array
88
    {
89 3
        return $this->minBoundary;
90
    }
91
92 3
    public function getMaxBoundary(): array
93
    {
94 3
        return $this->maxBoundary;
95
    }
96
97 3
    public function getDimensionCount(): int
98
    {
99 3
        return $this->dimensions;
100
    }
101
102 36
    private function buildSubTree(int $lo, int $hi, int $d): Node
103
    {
104 36
        $mid = (int)(($hi - $lo) / 2) + $lo;
105
106 36
        $item = $this->select($mid, $lo, $hi, $d);
107 36
        $node = new Node($item);
108
109 36
        $d++;
110 36
        $d = $d % $this->dimensions;
111
112 36
        if ($mid > $lo) {
113 30
            $node->setLeft($this->buildSubTree($lo, $mid - 1, $d));
114
        }
115 36
        if ($mid < $hi) {
116 36
            $node->setRight($this->buildSubTree($mid + 1, $hi, $d));
117
        }
118 36
        return $node;
119
    }
120
121 36
    private function exch(int $i, int $j)
122
    {
123 36
        $tmp = $this->items[$i];
124 36
        $this->items[$i] = $this->items[$j];
125 36
        $this->items[$j] = $tmp;
126 36
    }
127
128
    /**
129
     * @param int $k
130
     * @param int $lo
131
     * @param int $hi
132
     * @param int $d
133
     * @return ItemInterface
134
     */
135 39
    private function select(int $k, int $lo, int $hi, int $d)
136
    {
137 39
        while ($hi > $lo) {
138 36
            $j = $this->partition($lo, $hi, $d);
139 36
            if ($j > $k) {
140 33
                $hi = $j - 1;
141 36
            } elseif ($j < $k) {
142 36
                $lo = $j + 1;
143
            } else {
144 36
                return $this->items[$k];
145
            }
146
        }
147 39
        return $this->items[$k];
148
    }
149
150
    /**
151
     * @param int $lo
152
     * @param int $hi
153
     * @param int $d
154
     * @return int
155
     */
156 36
    private function partition(int $lo, int $hi, int $d)
157
    {
158 36
        $i = $lo;
159 36
        $j = $hi + 1;
160 36
        $v = $this->items[$lo];
161 36
        $val = $v->getNthDimension($d);
162 36
        while (true) {
163 36
            while ($this->items[++$i]->getNthDimension($d) < $val) {
164 33
                if ($i == $hi) {
165 33
                    break;
166
                }
167
            }
168
169 36
            while ($this->items[--$j]->getNthDimension($d) > $val) {
170
            }
171
172 36
            if ($i >= $j) {
173 36
                break;
174
            }
175
176 30
            $this->exch($i, $j);
177
        }
178
179 36
        $this->exch($lo, $j);
180
181 36
        return $j;
182
    }
183
}
184