Completed
Push — master ( 602dbe...ab6845 )
by Christophe
10:37
created

src/SortedCollection/TreeMap.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * chdemko\SortedCollection\TreeMap class
5
 *
6
 * @author     Christophe Demko <[email protected]>
7
 * @copyright  Copyright (C) 2012-2015 Christophe Demko. All rights reserved.
8
 *
9
 * @license    http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html The CeCILL B license
10
 *
11
 * This file is part of the php-sorted-collections package https://github.com/chdemko/php-sorted-collections
12
 */
13
14
// Declare chdemko\SortedCollection namespace
15
namespace chdemko\SortedCollection;
16
17
/**
18
 * Tree map
19
 *
20
 * @package     SortedCollection
21
 * @subpackage  Map
22
 *
23
 * @since       1.0.0
24
 *
25
 * @property-read  callable   $comparator  The key comparison function
26
 * @property-read  TreeNode   $first       The first element of the map
27
 * @property-read  mixed      $firstKey    The first key of the map
28
 * @property-read  mixed      $firstValue  The first value of the map
29
 * @property-read  TreeNode   $last        The last element of the map
30
 * @property-read  mixed      $lastKey     The last key of the map
31
 * @property-read  mixed      $lastValue   The last value of the map
32
 * @property-read  Iterator   $keys        The keys iterator
33
 * @property-read  Iterator   $values      The values iterator
34
 * @property-read  integer    $count       The number of elements in the map
35
 */
36
class TreeMap extends AbstractMap
37
{
38
	/**
39
	 * @var     TreeNode  Root of the tree
40
	 *
41
	 * @since   1.0.0
42
	 */
43
	private $root;
44
45
	/**
46
	 * @var     Callable  Comparator function
47
	 *
48
	 * @param   mixed  $key1  First key
49
	 * @param   mixed  $key2  Second key
50
	 *
51
	 * @return  integer  negative if $key1 is lesser than $key2,
52
	 *                   0 if $key1 is equal to $key2,
53
	 *                   positive if $key1 is greater than $key2
54
	 *
55
	 * @since   1.0.0
56
	 */
57
	private $comparator;
58
59
	/**
60
	 * Constructor
61
	 *
62
	 * @param   Callable  $comparator  Comparison function
63
	 *
64
	 * @since   1.0.0
65
	 */
66
	protected function __construct($comparator = null)
67
	{
68
		if ($comparator == null)
69
		{
70
			$this->comparator = function ($key1, $key2)
71
			{
72
				return $key1 - $key2;
73
			};
74
		}
75
		else
76
		{
77
			$this->comparator = $comparator;
78
		}
79
	}
80
81
	/**
82
	 * Create
83
	 *
84
	 * @param   Callable  $comparator  Comparison function
85
	 *
86
	 * @return  TreeMap  A new TreeMap
87
	 *
88
	 * @since   1.0.0
89
	 */
90
	static public function create($comparator = null)
0 ignored issues
show
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
91
	{
92
		return new static($comparator);
93
	}
94
95
	/**
96
	 * Get the comparator
97
	 *
98
	 * @return  callable  The comparator
99
	 *
100
	 * @since   1.0.0
101
	 */
102
	public function comparator()
103
	{
104
		return $this->comparator;
105
	}
106
107
	/**
108
	 * Get the first element or throw an exception if there is no such element
109
	 *
110
	 * @return  mixed  The first element
111
	 *
112
	 * @throws  \OutOfBoundsException  If there is no element
113
	 *
114
	 * @since   1.0.0
115
	 */
116
	public function first()
117
	{
118
		if ($this->root)
119
		{
120
			return $this->root->first;
121
		}
122
		else
123
		{
124
			throw new \OutOfBoundsException('First element unexisting');
125
		}
126
	}
127
128
	/**
129
	 * Get the last element or throw an exception if there is no such element
130
	 *
131
	 * @return  mixed  The last element
132
	 *
133
	 * @throws  \OutOfBoundsException  If there is no element
134
	 *
135
	 * @since   1.0.0
136
	 */
137
	public function last()
138
	{
139
		if ($this->root)
140
		{
141
			return $this->root->last;
142
		}
143
		else
144
		{
145
			throw new \OutOfBoundsException('Last element unexisting');
146
		}
147
	}
148
149
	/**
150
	 * Get the predecessor element or throw an exception if there is no such element
151
	 *
152
	 * @param   TreeNode  $element  A tree node member of the underlying TreeMap
153
	 *
154
	 * @return  mixed  The predecessor element
155
	 *
156
	 * @throws  \OutOfBoundsException  If there is no predecessor
157
	 *
158
	 * @since   1.0.0
159
	 */
160
	public function predecessor($element)
161
	{
162
		$predecessor = $element->predecessor;
163
164
		if ($predecessor)
165
		{
166
			return $predecessor;
167
		}
168
		else
169
		{
170
			throw new \OutOfBoundsException('Predecessor element unexisting');
171
		}
172
	}
173
174
	/**
175
	 * Get the successor element or throw an exception if there is no such element
176
	 *
177
	 * @param   TreeNode  $element  A tree node member of the underlying TreeMap
178
	 *
179
	 * @return  mixed  The successor element
180
	 *
181
	 * @throws  \OutOfBoundsException  If there is no successor
182
	 *
183
	 * @since   1.0.0
184
	 */
185
	public function successor($element)
186
	{
187
		$successor = $element->successor;
188
189
		if ($successor)
190
		{
191
			return $successor;
192
		}
193
		else
194
		{
195
			throw new \OutOfBoundsException('Successor element unexisting');
196
		}
197
	}
198
199
	/**
200
	 * Returns the element whose key is the greatest key lesser than the given key or throw an exception if there is no such element
201
	 *
202
	 * @param   mixed  $key  The searched key
203
	 *
204
	 * @return  mixed  The found element
205
	 *
206
	 * @throws  \OutOfBoundsException  If there is no lower element
207
	 *
208
	 * @since   1.0.0
209
	 */
210 View Code Duplication
	public function lower($key)
211
	{
212
		if ($this->root)
213
		{
214
			$lower = $this->root->find($key, $this->comparator, -2);
215
		}
216
		else
217
		{
218
			$lower = null;
219
		}
220
221
		if ($lower)
222
		{
223
			return $lower;
224
		}
225
		else
226
		{
227
			throw new \OutOfBoundsException('Lower element unexisting');
228
		}
229
	}
230
231
	/**
232
	 * Returns the element whose key is the greatest key lesser than or equal to the given key or throw an exception if there is no such element
233
	 *
234
	 * @param   mixed  $key  The searched key
235
	 *
236
	 * @return  mixed  The found element
237
	 *
238
	 * @throws  \OutOfBoundsException  If there is no floor element
239
	 *
240
	 * @since   1.0.0
241
	 */
242 View Code Duplication
	public function floor($key)
243
	{
244
		if ($this->root)
245
		{
246
			$floor = $this->root->find($key, $this->comparator, -1);
247
		}
248
		else
249
		{
250
			$floor = null;
251
		}
252
253
		if ($floor)
254
		{
255
			return $floor;
256
		}
257
		else
258
		{
259
			throw new \OutOfBoundsException('Floor element unexisting');
260
		}
261
	}
262
263
	/**
264
	 * Returns the element whose key is equal to the given key or throw an exception if there is no such element
265
	 *
266
	 * @param   mixed  $key  The searched key
267
	 *
268
	 * @return  mixed  The found element
269
	 *
270
	 * @throws  \OutOfBoundsException  If there is no such element
271
	 *
272
	 * @since   1.0.0
273
	 */
274 View Code Duplication
	public function find($key)
275
	{
276
		if ($this->root)
277
		{
278
			$find = $this->root->find($key, $this->comparator, 0);
279
		}
280
		else
281
		{
282
			$find = null;
283
		}
284
285
		if ($find)
286
		{
287
			return $find;
288
		}
289
		else
290
		{
291
			throw new \OutOfBoundsException('Element unexisting');
292
		}
293
	}
294
295
	/**
296
	 * Returns the element whose key is the lowest key greater than or equal to the given key or throw an exception if there is no such element
297
	 *
298
	 * @param   mixed  $key  The searched key
299
	 *
300
	 * @return  mixed  The found element
301
	 *
302
	 * @throws  \OutOfBoundsException  If there is no ceiling element
303
	 *
304
	 * @since   1.0.0
305
	 */
306 View Code Duplication
	public function ceiling($key)
307
	{
308
		if ($this->root)
309
		{
310
			$ceiling = $this->root->find($key, $this->comparator, 1);
311
		}
312
		else
313
		{
314
			$ceiling = null;
315
		}
316
317
		if ($ceiling)
318
		{
319
			return $ceiling;
320
		}
321
		else
322
		{
323
			throw new \OutOfBoundsException('Ceiling element unexisting');
324
		}
325
	}
326
327
	/**
328
	 * Returns the element whose key is the lowest key greater than to the given key or throw an exception if there is no such element
329
	 *
330
	 * @param   mixed  $key  The searched key
331
	 *
332
	 * @return  mixed  The found element
333
	 *
334
	 * @throws  \OutOfBoundsException  If there is no higher element
335
	 *
336
	 * @since   1.0.0
337
	 */
338 View Code Duplication
	public function higher($key)
339
	{
340
		if ($this->root)
341
		{
342
			$higher = $this->root->find($key, $this->comparator, 2);
343
		}
344
		else
345
		{
346
			$higher = null;
347
		}
348
349
		if ($higher)
350
		{
351
			return $higher;
352
		}
353
		else
354
		{
355
			throw new \OutOfBoundsException('Higher element unexisting');
356
		}
357
	}
358
359
	/**
360
	 * Put values in the map
361
	 *
362
	 * @param   \Traversable  $traversable  Values to put in the map
363
	 *
364
	 * @return  TreeMap  $this for chaining
365
	 *
366
	 * @since   1.0.0
367
	 */
368
	public function put($traversable = [])
369
	{
370
		foreach ($traversable as $key => $value)
371
		{
372
			$this[$key] = $value;
373
		}
374
375
		return $this;
376
	}
377
378
	/**
379
	 * Clear the map
380
	 *
381
	 * @return  TreeMap  $this for chaining
382
	 *
383
	 * @since   1.0.0
384
	 */
385
	public function clear()
386
	{
387
		$this->root = null;
388
389
		return $this;
390
	}
391
392
	/**
393
	 * Initialise the map
394
	 *
395
	 * @param   \Traversable  $traversable  Values to initialise the map
396
	 *
397
	 * @return  TreeMap  $this for chaining
398
	 *
399
	 * @since   1.0.0
400
	 */
401
	public function initialise($traversable = [])
402
	{
403
		return $this->clear()->put($traversable);
404
	}
405
406
	/**
407
	 * Clone the map
408
	 *
409
	 * @return  void
410
	 *
411
	 * @since   1.0.0
412
	 */
413
	public function __clone()
414
	{
415
		if ($this->root != null)
416
		{
417
			$root = $this->root;
418
			$this->root = null;
419
			$node = $root->first;
420
421
			while ($node != null)
422
			{
423
				$this[$node->key] = $node->value;
424
				$node = $node->successor;
425
			}
426
		}
427
	}
428
429
	/**
430
	 * Serialize the object
431
	 *
432
	 * @return  array  Array of values
433
	 *
434
	 * @since   1.0.0
435
	 */
436
	public function jsonSerialize()
437
	{
438
		$array = [];
439
440
		foreach ($this as $key => $value)
441
		{
442
			$array[$key] = $value;
443
		}
444
445
		return ['TreeMap' => $array];
446
	}
447
448
	/**
449
	 * Set the value for a key
450
	 *
451
	 * @param   mixed  $key    The key
452
	 * @param   mixed  $value  The value
453
	 *
454
	 * @return  void
455
	 *
456
	 * @since   1.0.0
457
	 */
458
	public function offsetSet($key, $value)
459
	{
460
		if ($this->root)
461
		{
462
			$this->root = $this->root->insert($key, $value, $this->comparator);
463
		}
464
		else
465
		{
466
			$this->root = TreeNode::create($key, $value);
467
		}
468
	}
469
470
	/**
471
	 * Unset the existence of a key
472
	 *
473
	 * @param   mixed  $key  The key
474
	 *
475
	 * @return  void
476
	 *
477
	 * @since   1.0.0
478
	 */
479
	public function offsetUnset($key)
480
	{
481
		if ($this->root)
482
		{
483
			$this->root = $this->root->remove($key, $this->comparator);
484
		}
485
	}
486
487
	/**
488
	 * Count the number of key/value pairs
489
	 *
490
	 * @return  integer
491
	 *
492
	 * @since   1.0.0
493
	 */
494
	public function count()
495
	{
496
		if ($this->root)
497
		{
498
			return count($this->root);
499
		}
500
		else
501
		{
502
			return 0;
503
		}
504
	}
505
}
506