diff --git a/README.md b/README.md index 2a1ceb4..7004d24 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # PHP Interval tree -Is an implementation of interval binary search tree according to Thomas H. Cormen book "Introduction to Algorithms". +Implementation of interval binary search tree. ## Usage @@ -10,27 +10,30 @@ require_once 'vendor/autoload.php'; use Danon\IntervalTree\IntervalTree; $tree = new IntervalTree(); -$intervals = [[6,8],[1,4],[2,3],[5,12],[1,1],[3,5],[5,7]]; - -// Insert interval as a key and string "val0", "val1" etc. as a value +$intervals = [[6, 8], [1, 4], [2, 3], [5, 12], [1, 1], [3, 5], [5, 7]]; + +// Insert interval as a key and interval index as a value for ($i=0; $i < count($intervals); $i++) { - $tree->insert($intervals[$i], "val" . $i); + $tree->insert($intervals[$i], $i); +} + +// Iterate nodes which keys intersect with given interval +$nodesInRange = $tree->iterateIntersections([2, 3]); +$intersectedIntervalIndexes = []; +foreach ($nodesInRange as $node) { + $intersectedIntervalIndexes[] = $node->getValue(); } +// Expected array: [1, 2, 5] + +// Check that interval has at least one intersection +$tree->hasIntersection([2, 3]); +// Expected value: true + +// Count intervals that has intersections +$tree->countIntersections([2, 3]); +// Expected value: 3 // Get array of keys sorted in ascendant order -$sorted_intervals = $tree->getKeys(); // expected array [[1,1],[1,4],[5,7],[5,12],[6,8]] - -// Search items which keys intersect with given interval, and return array of values -$valuesInRange = $tree->search([2,3], function($value, $key) { - return $value; -}); - -print_r($valuesInRange); - -// Array -// ( -// [0] => val1 -// [1] => val2 -// [2] => val5 -// ) -``` +$sortedIntervals = $tree->getKeys(); +// Expected array: [[1, 1], [1, 4], [2, 3], [3, 5], [5, 7], [5, 12], [6, 8]] +``` \ No newline at end of file diff --git a/composer.json b/composer.json index 0dedbbb..3b6cb39 100644 --- a/composer.json +++ b/composer.json @@ -16,5 +16,8 @@ }, "require": { "php": "^5.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..96c4fcd --- /dev/null +++ b/composer.lock @@ -0,0 +1,1601 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "bf95be58ba1825fe47b62fb2abfb052b", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2019-10-21T16:45:58+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2020-01-17T21:11:47+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2018-08-07T13:53:10+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "shasum": "" + }, + "require": { + "ext-filter": "^7.1", + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0", + "phpdocumentor/type-resolver": "^1.0", + "webmozart/assert": "^1" + }, + "require-dev": { + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2020-02-22T12:28:44+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "shasum": "" + }, + "require": { + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-02-18T18:59:58+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.10.3", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "451c3cd1418cf640de218914901e51b064abb093" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5 || ^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2020-03-05T15:02:03+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "8.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "31e94ccc084025d6abee0585df533eb3a792b96a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/31e94ccc084025d6abee0585df533eb3a792b96a", + "reference": "31e94ccc084025d6abee0585df533eb3a792b96a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.3", + "phpunit/php-file-iterator": "^3.0", + "phpunit/php-text-template": "^2.0", + "phpunit/php-token-stream": "^4.0", + "sebastian/code-unit-reverse-lookup": "^2.0", + "sebastian/environment": "^5.0", + "sebastian/version": "^3.0", + "theseer/tokenizer": "^1.1.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2020-02-19T13:41:19+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "354d4a5faa7449a377a18b94a2026ca3415e3d7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/354d4a5faa7449a377a18b94a2026ca3415e3d7a", + "reference": "354d4a5faa7449a377a18b94a2026ca3415e3d7a", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2020-02-07T06:05:22+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "7579d5a1ba7f3ac11c80004d205877911315ae7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/7579d5a1ba7f3ac11c80004d205877911315ae7a", + "reference": "7579d5a1ba7f3ac11c80004d205877911315ae7a", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "time": "2020-02-07T06:06:11+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "526dc996cc0ebdfa428cd2dfccd79b7b53fee346" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/526dc996cc0ebdfa428cd2dfccd79b7b53fee346", + "reference": "526dc996cc0ebdfa428cd2dfccd79b7b53fee346", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2020-02-01T07:43:44+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "4118013a4d0f97356eae8e7fb2f6c6472575d1df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/4118013a4d0f97356eae8e7fb2f6c6472575d1df", + "reference": "4118013a4d0f97356eae8e7fb2f6c6472575d1df", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2020-02-07T06:08:11+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "b2560a0c33f7710e4d7f8780964193e8e8f8effe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/b2560a0c33f7710e4d7f8780964193e8e8f8effe", + "reference": "b2560a0c33f7710e4d7f8780964193e8e8f8effe", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2020-02-07T06:19:00+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "c0ecbfb898ab8b24d8a59a23520f7b2a73e27b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c0ecbfb898ab8b24d8a59a23520f7b2a73e27b5b", + "reference": "c0ecbfb898ab8b24d8a59a23520f7b2a73e27b5b", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.9.1", + "phar-io/manifest": "^1.0.3", + "phar-io/version": "^2.0.1", + "php": "^7.3", + "phpspec/prophecy": "^1.8.1", + "phpunit/php-code-coverage": "^8.0.1", + "phpunit/php-file-iterator": "^3.0", + "phpunit/php-invoker": "^3.0", + "phpunit/php-text-template": "^2.0", + "phpunit/php-timer": "^3.0", + "sebastian/comparator": "^4.0", + "sebastian/diff": "^4.0", + "sebastian/environment": "^5.0.1", + "sebastian/exporter": "^4.0", + "sebastian/global-state": "^4.0", + "sebastian/object-enumerator": "^4.0", + "sebastian/resource-operations": "^3.0", + "sebastian/type": "^2.0", + "sebastian/version": "^3.0" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2020-03-31T08:57:51+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5b5dbe0044085ac41df47e79d34911a15b96d82e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5b5dbe0044085ac41df47e79d34911a15b96d82e", + "reference": "5b5dbe0044085ac41df47e79d34911a15b96d82e", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2020-02-07T06:20:13+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85b3435da967696ed618ff745f32be3ff4a2b8e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85b3435da967696ed618ff745f32be3ff4a2b8e8", + "reference": "85b3435da967696ed618ff745f32be3ff4a2b8e8", + "shasum": "" + }, + "require": { + "php": "^7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2020-02-07T06:08:51+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c0c26c9188b538bfa985ae10c9f05d278f12060d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c0c26c9188b538bfa985ae10c9f05d278f12060d", + "reference": "c0c26c9188b538bfa985ae10c9f05d278f12060d", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "symfony/process": "^4 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2020-02-07T06:09:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "c39c1db0a5cffc98173f3de5a17d489d1043fd7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/c39c1db0a5cffc98173f3de5a17d489d1043fd7b", + "reference": "c39c1db0a5cffc98173f3de5a17d489d1043fd7b", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2020-03-31T12:14:15+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "80c26562e964016538f832f305b2286e1ec29566" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/80c26562e964016538f832f305b2286e1ec29566", + "reference": "80c26562e964016538f832f305b2286e1ec29566", + "shasum": "" + }, + "require": { + "php": "^7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2020-02-07T06:10:52+00:00" + }, + { + "name": "sebastian/global-state", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bdb1e7c79e592b8c82cb1699be3c8743119b8a72", + "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72", + "shasum": "" + }, + "require": { + "php": "^7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2020-02-07T06:11:37+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "e67516b175550abad905dc952f43285957ef4363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67516b175550abad905dc952f43285957ef4363", + "reference": "e67516b175550abad905dc952f43285957ef4363", + "shasum": "" + }, + "require": { + "php": "^7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2020-02-07T06:12:23+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "f4fd0835cabb0d4a6546d9fe291e5740037aa1e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/f4fd0835cabb0d4a6546d9fe291e5740037aa1e7", + "reference": "f4fd0835cabb0d4a6546d9fe291e5740037aa1e7", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2020-02-07T06:19:40+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cdd86616411fc3062368b720b0425de10bd3d579" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cdd86616411fc3062368b720b0425de10bd3d579", + "reference": "cdd86616411fc3062368b720b0425de10bd3d579", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2020-02-07T06:18:20+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98", + "reference": "8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2020-02-07T06:13:02+00:00" + }, + { + "name": "sebastian/type", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "9e8f42f740afdea51f5f4e8cec2035580e797ee1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/9e8f42f740afdea51f5f4e8cec2035580e797ee1", + "reference": "9e8f42f740afdea51f5f4e8cec2035580e797ee1", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "time": "2020-02-07T06:13:43+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "0411bde656dce64202b39c2f4473993a9081d39e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/0411bde656dce64202b39c2f4473993a9081d39e", + "reference": "0411bde656dce64202b39c2f4473993a9081d39e", + "shasum": "" + }, + "require": { + "php": "^7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2020-01-21T06:36:37+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2020-02-27T09:26:54+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2019-06-13T22:48:21+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "vimeo/psalm": "<3.6.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36 || ^7.5.13" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2020-02-14T12:15:55+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^5.3 || ^7.0" + }, + "platform-dev": [] +} diff --git a/examples/index.php b/examples/index.php deleted file mode 100644 index 5e4666b..0000000 --- a/examples/index.php +++ /dev/null @@ -1,29 +0,0 @@ -insert($intervals[$i], "val" . $i); -} - -// Get array of keys sorted in ascendant order -$sorted_intervals = $tree->getKeys(); // expected array [[1,1],[1,4],[5,7],[5,12],[6,8]] - -// Search items which keys intersect with given interval, and return array of values -$valuesInRange = $tree->search([2,3], function($value, $key) { - return $value; -}); - -print_r($valuesInRange); - -// Array -// ( -// [0] => val1 -// [1] => val2 -// [2] => val5 -// ) \ No newline at end of file diff --git a/src/Interval.php b/src/Interval.php index 4d8a8ec..e7467f3 100644 --- a/src/Interval.php +++ b/src/Interval.php @@ -1,13 +1,18 @@ $high) { + throw new InvalidArgumentException('Low interval cannot be greater than high'); + } + $this->low = $low; $this->high = $high; } diff --git a/src/IntervalTree.php b/src/IntervalTree.php index 4e18eaa..f3d2248 100644 --- a/src/IntervalTree.php +++ b/src/IntervalTree.php @@ -1,26 +1,29 @@ root = null; $this->nilNode = new Node(); } /** * Returns number of items stored in the interval tree - * @returns {number} + * + * @return int */ - public function getSize() { + public function getSize(): int + { $count = 0; - $this->treeWalk($this->root, function() use(&$count) { + $this->treeWalk($this->root, function () use (&$count) { $count++; }); return $count; @@ -28,9 +31,11 @@ public function getSize() { /** * Returns array of sorted keys in the ascending order - * @returns {Array} + * + * @return void */ - public function getKeys() { + public function getKeys(): array + { $res = []; $this->treeWalk($this->root, function ($node) use (&$res) { @@ -43,7 +48,8 @@ public function getKeys() { * Return array of values in the ascending keys order * @returns {Array} */ - public function getValues() { + public function getValues() + { $res = []; $this->treeWalk($this->root, function ($node) use (&$res) { $res[] = $node->item->value; @@ -55,12 +61,13 @@ public function getValues() { * Returns array of items ( pairs) in the ascended keys order * @returns {Array} */ - public function getItems() { + public function getItems() + { $res = []; $this->treeWalk($this->root, function ($node) use (&$res) { - $res[] = (object)[ + $res[] = (object) [ 'key' => $node->item->key ? $node->item->key->output() : $node->item->key, - 'value' => $node->item->value + 'value' => $node->item->value, ]; }); return $res; @@ -68,21 +75,67 @@ public function getItems() { /** * Returns true if tree is empty - * @returns {boolean} + * + * @return boolean */ - public function isEmpty() { + public function isEmpty() + { return ($this->root == null || $this->root == $this->nilNode); } + /** + * Iterator of nodes which keys intersect with given interval + * If no values stored in the tree, returns array of keys which intersect given interval + * @param array $interval + * @return iterable + */ + public function iterateIntersections(array $interval): iterable + { + $searchNode = new Node($interval); + yield from $this->treeSearchInterval($this->root, $searchNode); + } + + /** + * Check that interval has intersections + * + * @param array $interval + * @return boolean + */ + public function hasIntersection(array $interval): bool + { + $nodesIterator = $this->iterateIntersections($interval); + return $nodesIterator->current() !== null; + } + + /** + * Count intervals that has intersections + * + * @param array $interval + * @return boolean + */ + public function countIntersections($interval): int + { + $nodesIterator = $this->iterateIntersections($interval); + return iterator_count($nodesIterator); + } + /** * Insert new item into interval tree - * @param key - interval object or array of two numbers [low, high] - * @param value - value representing any object (optional) - * @returns {Node} - returns reference to inserted node as an object {key:interval, value: value} + * + * @param array $key - array of two numbers [low, high] + * @param mixed $value - value representing any object (optional) + * @return Node - returns reference to inserted node */ - public function insert($key, $value = null) { - if ($key === null) return; - if ($value === null) $value = $key; + public function insert(array $key, $value = null) + { + if ($key === null) { + return; + } + + if ($value === null) { + $value = $key; + } + $insertNode = new Node($key, $value, $this->nilNode, $this->nilNode, null, Node::COLOR_RED); $this->treeInsert($insertNode); $this->recalcMax($insertNode); @@ -95,7 +148,8 @@ public function insert($key, $value = null) { * @param value - value object to be checked * @returns {boolean} - true if item {key, value} exist in the tree, false otherwise */ - public function exist($key, $value) { + public function exist($key, $value) + { $searchNode = new Node($key, $value); return $this->treeSearch($this->root, $searchNode) ? true : false; } @@ -106,7 +160,8 @@ public function exist($key, $value) { * @param value - - value object * @returns {boolean} - true if item {key, value} deleted, false if not found */ - public function remove($key, $value) { + public function remove($key, $value) + { $searchNode = new Node($key, $value); $deleteNode = $this->treeSearch($this->root, $searchNode); if ($deleteNode) { @@ -115,46 +170,31 @@ public function remove($key, $value) { return $deleteNode; } - /** - * Returns array of entry values which keys intersect with given interval
- * If no values stored in the tree, returns array of keys which intersect given interval - * @param interval - search interval, or array [low, high] - * @param outputMapperFn(value,key) - optional function that maps (value, key) to custom output - * @returns {Array} - */ - public function search($interval, $outputMapperFn) { - $searchNode = new Node($interval); - $respNodes = []; - $this->treeSearchInterval($this->root, $searchNode, $respNodes); - - return array_map(function($node) use ($outputMapperFn) { - return $outputMapperFn($node->item->value, $node->item->key); - }, $respNodes); - } - /** * Tree visitor. For each node implement a callback function.
* Method calls a callback function with two parameters (key, value) * @param visitor(key,value) - function to be called for each tree item */ - public function forEach($visitor) { + function foreach ($visitor) { $this->treeWalk($this->root, function ($node) { return $visitor($node->item->key, $node->item->value); }); } /** Value Mapper. Walk through every node and map node value to another value - * @param callback(value, key) - function to be called for each tree item - */ - public function map($callback) { + * @param callback(value, key) - function to be called for each tree item + */ + public function map($callback) + { $tree = new IntervalTree(); - $this->treeWalk($this->root, function($node) use (&$tree, $callback) { + $this->treeWalk($this->root, function ($node) use (&$tree, $callback) { return $tree->insert($node->item->key, $callback($node->item->value, $node->item->key)); }); return $tree; } - public function recalcMax($node) { + public function recalcMax($node) + { $nodeCurrent = $node; while ($nodeCurrent->parent != null) { $nodeCurrent->parent->updateMax(); @@ -162,7 +202,8 @@ public function recalcMax($node) { } } - public function treeInsert($insertNode) { + public function treeInsert($insertNode) + { $currentNode = $this->root; $parentNode = null; @@ -173,8 +214,7 @@ public function treeInsert($insertNode) { $parentNode = $currentNode; if ($insertNode->lessThan($currentNode)) { $currentNode = $currentNode->left; - } - else { + } else { $currentNode = $currentNode->right; } } @@ -183,8 +223,7 @@ public function treeInsert($insertNode) { if ($insertNode->lessThan($parentNode)) { $parentNode->left = $insertNode; - } - else { + } else { $parentNode->right = $insertNode; } } @@ -192,51 +231,49 @@ public function treeInsert($insertNode) { $this->insertFixup($insertNode); } -// After insertion insert_node may have red-colored parent, and this is a single possible violation -// Go upwords to the root and re-color until violation will be resolved - public function insertFixup($insertNode) { + // After insertion insert_node may have red-colored parent, and this is a single possible violation + // Go upwords to the root and re-color until violation will be resolved + public function insertFixup($insertNode) + { $currentNode; $uncleNode; $currentNode = $insertNode; while ($currentNode != $this->root && $currentNode->parent->color == Node::COLOR_RED) { - if ($currentNode->parent == $currentNode->parent->parent->left) { // parent is left child of grandfather - $uncleNode = $currentNode->parent->parent->right; // right brother of parent - if ($uncleNode->color == Node::COLOR_RED) { // Case 1. Uncle is red + if ($currentNode->parent == $currentNode->parent->parent->left) { // parent is left child of grandfather + $uncleNode = $currentNode->parent->parent->right; // right brother of parent + if ($uncleNode->color == Node::COLOR_RED) { // Case 1. Uncle is red // re-color father and uncle into black $currentNode->parent->color = Node::COLOR_BLACK; $uncleNode->color = Node::COLOR_BLACK; $currentNode->parent->parent->color = Node::COLOR_RED; $currentNode = $currentNode->parent->parent; - } - else { // Case 2 & 3. Uncle is black - if ($currentNode == $currentNode->parent->right) { // Case 2. Current if right child + } else { // Case 2 & 3. Uncle is black + if ($currentNode == $currentNode->parent->right) { // Case 2. Current if right child // This case is transformed into Case 3. $currentNode = $currentNode->parent; $this->rotateLeft($currentNode); } - $currentNode->parent->color = Node::COLOR_BLACK; // Case 3. Current is left child. + $currentNode->parent->color = Node::COLOR_BLACK; // Case 3. Current is left child. // Re-color father and grandfather, rotate grandfather right $currentNode->parent->parent->color = Node::COLOR_RED; $this->rotateRight($currentNode->parent->parent); } - } - else { // parent is right child of grandfather - $uncleNode = $currentNode->parent->parent->left; // left brother of parent - if ($uncleNode->color == Node::COLOR_RED) { // Case 4. Uncle is red + } else { // parent is right child of grandfather + $uncleNode = $currentNode->parent->parent->left; // left brother of parent + if ($uncleNode->color == Node::COLOR_RED) { // Case 4. Uncle is red // re-color father and uncle into black $currentNode->parent->color = Node::COLOR_BLACK; $uncleNode->color = Node::COLOR_BLACK; $currentNode->parent->parent->color = Node::COLOR_RED; $currentNode = $currentNode->parent->parent; - } - else { - if ($currentNode == $currentNode->parent->left) { // Case 5. Current is left child + } else { + if ($currentNode == $currentNode->parent->left) { // Case 5. Current is left child // Transform into case 6 $currentNode = $currentNode->parent; $this->rotateRight($currentNode); } - $currentNode->parent->color = Node::COLOR_BLACK; // Case 6. Current is right child. + $currentNode->parent->color = Node::COLOR_BLACK; // Case 6. Current is right child. // Re-color father and grandfather, rotate grandfather left $currentNode->parent->parent->color = Node::COLOR_RED; $this->rotateLeft($currentNode->parent->parent); @@ -247,22 +284,21 @@ public function insertFixup($insertNode) { $this->root->color = Node::COLOR_BLACK; } - public function treeDelete($deleteNode) { - $cutNode; // node to be cut - either delete_node or successor_node ("y" from 14.4) - $fixNode; // node to fix rb tree property ("x" from 14.4) + public function treeDelete($deleteNode) + { + $cutNode; // node to be cut - either delete_node or successor_node ("y" from 14.4) + $fixNode; // node to fix rb tree property ("x" from 14.4) - if ($deleteNode->left == $this->nilNode || $deleteNode->right == $this->nilNode) { // delete_node has less then 2 children + if ($deleteNode->left == $this->nilNode || $deleteNode->right == $this->nilNode) { // delete_node has less then 2 children $cutNode = $deleteNode; - } - else { // delete_node has 2 children + } else { // delete_node has 2 children $cutNode = $this->treeSuccessor($deleteNode); } // fix_node if single child of cut_node if ($cutNode->left != $this->nilNode) { $fixNode = $cutNode->left; - } - else { + } else { $fixNode = $cutNode->right; } @@ -273,58 +309,56 @@ public function treeDelete($deleteNode) { if ($cutNode == $this->root) { $this->root = $fixNode; - } - else { + } else { if ($cutNode == $cutNode->parent->left) { $cutNode->parent->left = $fixNode; - } - else { + } else { $cutNode->parent->right = $fixNode; } - $cutNode->parent->updateMax(); // update max property of the parent + $cutNode->parent->updateMax(); // update max property of the parent } - $this->recalcMax($fixNode); // update max property upward from fix_node to root + $this->recalcMax($fixNode); // update max property upward from fix_node to root // COPY DATA !!! // Delete_node becomes cut_node, it means that we cannot hold reference // to node in outer structure and we will have to delete by key, additional search need if ($cutNode != $deleteNode) { $deleteNode->copyData($cutNode); - $deleteNode->updateMax(); // update max property of the cut node at the new place - $this->recalcMax($deleteNode); // update max property upward from delete_node to root + $deleteNode->updateMax(); // update max property of the cut node at the new place + $this->recalcMax($deleteNode); // update max property upward from delete_node to root } - if (/*fix_node != this.nil_node && */$cutNode->color == Node::COLOR_BLACK) { + if ( /*fix_node != this.nil_node && */$cutNode->color == Node::COLOR_BLACK) { $this->deleteFixup($fixNode); } } - public function deleteFixup($fixNode) { + public function deleteFixup($fixNode) + { $currentNode = $fixNode; $brotherNode; while ($currentNode != $this->root && $currentNode->parent != null && $currentNode->color == Node::COLOR_BLACK) { - if ($currentNode == $currentNode->parent->left) { // fix node is left child + if ($currentNode == $currentNode->parent->left) { // fix node is left child $brotherNode = $currentNode->parent->right; - if ($brotherNode->color == Node::COLOR_RED) { // Case 1. Brother is red - $brotherNode->color = Node::COLOR_BLACK; // re-color brother - $currentNode->parent->color = Node::COLOR_RED; // re-color father + if ($brotherNode->color == Node::COLOR_RED) { // Case 1. Brother is red + $brotherNode->color = Node::COLOR_BLACK; // re-color brother + $currentNode->parent->color = Node::COLOR_RED; // re-color father $this->rotateLeft($currentNode->parent); - $brotherNode = $currentNode->parent->right; // update brother + $brotherNode = $currentNode->parent->right; // update brother } // Derive to cases 2..4: brother is black if ($brotherNode->left->color == Node::COLOR_BLACK && - $brotherNode->right->color == Node::COLOR_BLACK) { // case 2: both nephews black - $brotherNode->color = Node::COLOR_RED; // re-color brother - $currentNode = $currentNode->parent; // continue iteration - } - else { - if ($brotherNode->right->color == Node::COLOR_BLACK) { // case 3: left nephew red, right nephew black - $brotherNode->color = Node::COLOR_RED; // re-color brother - $brotherNode->left->color = Node::COLOR_BLACK; // re-color nephew + $brotherNode->right->color == Node::COLOR_BLACK) { // case 2: both nephews black + $brotherNode->color = Node::COLOR_RED; // re-color brother + $currentNode = $currentNode->parent; // continue iteration + } else { + if ($brotherNode->right->color == Node::COLOR_BLACK) { // case 3: left nephew red, right nephew black + $brotherNode->color = Node::COLOR_RED; // re-color brother + $brotherNode->left->color = Node::COLOR_BLACK; // re-color nephew $this->rotateRight($brotherNode); - $brotherNode = $currentNode->parent->right; // update brother + $brotherNode = $currentNode->parent->right; // update brother // Derive to case 4: left nephew black, right nephew red } // case 4: left nephew black, right nephew red @@ -332,29 +366,27 @@ public function deleteFixup($fixNode) { $currentNode->parent->color = Node::COLOR_BLACK; $brotherNode->right->color = Node::COLOR_BLACK; $this->rotateLeft($currentNode->parent); - $currentNode = $this->root; // exit from loop + $currentNode = $this->root; // exit from loop } - } - else { // fix node is right child + } else { // fix node is right child $brotherNode = $currentNode->parent->left; - if ($brotherNode->color == Node::COLOR_RED) { // Case 1. Brother is red - $brotherNode->color = Node::COLOR_BLACK; // re-color brother - $currentNode->parent->color = Node::COLOR_RED; // re-color father + if ($brotherNode->color == Node::COLOR_RED) { // Case 1. Brother is red + $brotherNode->color = Node::COLOR_BLACK; // re-color brother + $currentNode->parent->color = Node::COLOR_RED; // re-color father $this->rotateRight($currentNode->parent); - $brotherNode = $currentNode->parent->left; // update brother + $brotherNode = $currentNode->parent->left; // update brother } // Go to cases 2..4 if ($brotherNode->left->color == Node::COLOR_BLACK && - $brotherNode->right->color == Node::COLOR_BLACK) { // case 2 - $brotherNode->color = Node::COLOR_RED; // re-color brother - $currentNode = $currentNode->parent; // continue iteration - } - else { - if ($brotherNode->left->color == Node::COLOR_BLACK) { // case 3: right nephew red, left nephew black - $brotherNode->color = Node::COLOR_RED; // re-color brother - $brotherNode->right->color = Node::COLOR_BLACK; // re-color nephew + $brotherNode->right->color == Node::COLOR_BLACK) { // case 2 + $brotherNode->color = Node::COLOR_RED; // re-color brother + $currentNode = $currentNode->parent; // continue iteration + } else { + if ($brotherNode->left->color == Node::COLOR_BLACK) { // case 3: right nephew red, left nephew black + $brotherNode->color = Node::COLOR_RED; // re-color brother + $brotherNode->right->color = Node::COLOR_BLACK; // re-color nephew $this->rotateLeft($brotherNode); - $brotherNode = $currentNode->parent->left; // update brother + $brotherNode = $currentNode->parent->left; // update brother // Derive to case 4: right nephew black, left nephew red } // case 4: right nephew black, left nephew red @@ -362,7 +394,7 @@ public function deleteFixup($fixNode) { $currentNode->parent->color = Node::COLOR_BLACK; $brotherNode->left->color = Node::COLOR_BLACK; $this->rotateRight($currentNode->parent); - $currentNode = $this->root; // force exit from loop + $currentNode = $this->root; // force exit from loop } } } @@ -370,41 +402,45 @@ public function deleteFixup($fixNode) { $currentNode->color = Node::COLOR_BLACK; } - public function treeSearch($node, $searchNode) { - if ($node == null || $node == $this->nilNode) + public function treeSearch($node, $searchNode) + { + if ($node == null || $node == $this->nilNode) { return null; + } if ($searchNode->equalTo($node)) { return $node; } if ($searchNode->lessThan($node)) { return $this->treeSearch($node->left, $searchNode); - } - else { + } else { return $this->treeSearch($node->right, $searchNode); } } // Original search_interval method; container res support push() insertion // Search all intervals intersecting given one - public function treeSearchInterval($node, $searchNode, &$res) { + public function treeSearchInterval($node, $searchNode, &$res = []) + { if ($node != null && $node != $this->nilNode) { // if (node->left != this.nil_node && node->left->max >= low) { if ($node->left != $this->nilNode && !$node->notIntersectLeftSubtree($searchNode)) { - $this->treeSearchInterval($node->left, $searchNode, $res); + yield from $this->treeSearchInterval($node->left, $searchNode, $res); } // if (low <= node->high && node->low <= high) { if ($node->intersect($searchNode)) { $res[] = $node; + yield $node; } // if (node->right != this.nil_node && node->low <= high) { if ($node->right != $this->nilNode && !$node->notIntersectRightSubtree($searchNode)) { - $this->treeSearchInterval($node->right, $searchNode, $res); + yield from $this->treeSearchInterval($node->right, $searchNode, $res); } } } - public function localMinimum($node) { + public function localMinimum($node) + { $nodeMin = $node; while ($nodeMin->left != null && $nodeMin->left != $this->nilNode) { $nodeMin = $nodeMin->left; @@ -413,7 +449,8 @@ public function localMinimum($node) { } // not in use - public function localMaximum($node) { + public function localMaximum($node) + { $nodeMax = $node; while ($nodeMax->right != null && $nodeMax->right != $this->nilNode) { $nodeMax = $nodeMax->right; @@ -421,15 +458,15 @@ public function localMaximum($node) { return $nodeMax; } - public function treeSuccessor($node) { + public function treeSuccessor($node) + { $nodeSuccessor; $currentNode; $parentNode; if ($node->right != $this->nilNode) { $nodeSuccessor = $this->localMinimum($node->right); - } - else { + } else { $currentNode = $node; $parentNode = $node->parent; while ($parentNode != null && $parentNode->right == $currentNode) { @@ -448,29 +485,28 @@ public function treeSuccessor($node) { // / \ <--------------- / \ // a b b c - public function rotateLeft($x) { + public function rotateLeft($x) + { $y = $x->right; - $x->right = $y->left; // b goes to x.right + $x->right = $y->left; // b goes to x.right if ($y->left != $this->nilNode) { - $y->left->parent = $x; // x becomes parent of b + $y->left->parent = $x; // x becomes parent of b } - $y->parent = $x->parent; // move parent + $y->parent = $x->parent; // move parent if ($x == $this->root) { - $this->root = $y; // y becomes root - } - else { // y becomes child of x.parent + $this->root = $y; // y becomes root + } else { // y becomes child of x.parent if ($x == $x->parent->left) { $x->parent->left = $y; - } - else { + } else { $x->parent->right = $y; } } - $y->left = $x; // x becomes left child of y - $x->parent = $y; // and y becomes parent of x + $y->left = $x; // x becomes left child of y + $x->parent = $y; // and y becomes parent of x if ($x != null && $x != $this->nilNode) { $x->updateMax(); @@ -482,29 +518,28 @@ public function rotateLeft($x) { } } - public function rotateRight($y) { + public function rotateRight($y) + { $x = $y->left; - $y->left = $x->right; // b goes to y.left + $y->left = $x->right; // b goes to y.left if ($x->right != $this->nilNode) { - $x->right->parent = $y; // y becomes parent of b + $x->right->parent = $y; // y becomes parent of b } - $x->parent = $y->parent; // move parent + $x->parent = $y->parent; // move parent - if ($y == $this->root) { // x becomes root + if ($y == $this->root) { // x becomes root $this->root = $x; - } - else { // y becomes child of x.parent + } else { // y becomes child of x.parent if ($y == $y->parent->left) { $y->parent->left = $x; - } - else { + } else { $y->parent->right = $x; } } - $x->right = $y; // y becomes right child of x - $y->parent = $x; // and x becomes parent of y + $x->right = $y; // y becomes right child of x + $y->parent = $x; // and x becomes parent of y if ($y != null && $y != $this->nilNode) { $y->updateMax(); @@ -516,7 +551,8 @@ public function rotateRight($y) { } } - public function treeWalk($node, $action) { + public function treeWalk($node, $action) + { if ($node != null && $node != $this->nilNode) { $this->treeWalk($node->left, $action); // arr.push(node.toArray()); @@ -526,7 +562,8 @@ public function treeWalk($node, $action) { } /* Return true if all red nodes have exactly two black child nodes */ - public function testRedBlackProperty() { + public function testRedBlackProperty() + { $res = true; $this->treeWalk($this->root, function ($node) use (&$res) { if ($node->color == Node::COLOR_RED) { @@ -539,7 +576,8 @@ public function testRedBlackProperty() { } /* Throw error if not every path from root to bottom has same black height */ - public function testBlackHeightProperty($node) { + public function testBlackHeightProperty($node) + { $height = 0; $heightLeft = 0; $heightRight = 0; @@ -548,14 +586,12 @@ public function testBlackHeightProperty($node) { } if ($node->left != $this->nilNode) { $heightLeft = $this->testBlackHeightProperty($node->left); - } - else { + } else { $heightLeft = 1; } if ($node->right != $this->nilNode) { $heightRight = $this->testBlackHeightProperty($node->right); - } - else { + } else { $heightRight = 1; } if ($heightLeft != $heightRight) { @@ -564,4 +600,4 @@ public function testBlackHeightProperty($node) { $height += $heightLeft; return $height; } -}; \ No newline at end of file +}; diff --git a/src/Node.php b/src/Node.php index 01237e0..a21aed9 100644 --- a/src/Node.php +++ b/src/Node.php @@ -1,7 +1,8 @@ left = $left; $this->right = $right; $this->parent = $parent; $this->color = $color; - $this->item = (object)compact('key', 'value'); // key is supposed to be instance of Interval + $this->item = (object) compact('key', 'value'); // key is supposed to be instance of Interval /* If not, this should by an array of two numbers */ if ($key && is_array($key) && count($key) === 2) { @@ -47,38 +49,54 @@ public function __construct($key = null, $value = null, $left = null, $right = n $this->max = $this->item->key ? clone $this->item->key : null; } - public function isNil() { + public function getValue() + { + return $this->item->value; + } + + public function getKey() + { + return $this->item->key; + } + + public function isNil() + { return ($this->item->key === null && $this->item->value === null && $this->left === null && $this->right === null && $this->color === Node::COLOR_BLACK); } - public function lessThan($otherNode) { + public function lessThan($otherNode) + { return $this->item->key->lessThan($otherNode->item->key); } - public function equalTo($otherNode) { + public function equalTo($otherNode) + { $valueEqual = true; if ($this->item->value && $otherNode->item->value) { $valueEqual = $this->item->value ? $this->item->value->equalTo($otherNode->item->value) : - $this->item->value == $otherNode->item->value; + $this->item->value == $otherNode->item->value; } return $this->item->key->equalTo($otherNode->item->key) && $valueEqual; } - public function intersect($otherNode) { + public function intersect($otherNode) + { return $this->item->key->intersect($otherNode->item->key); } - public function copyData($otherNode) { + public function copyData($otherNode) + { $this->item->key = clone $otherNode->item->key; $this->item->value = $otherNode->item->value; } - public function updateMax() { + public function updateMax() + { // use key (Interval) max property instead of key.high $this->max = $this->item->key ? $this->item->key->getMax() : null; if ($this->right && $this->right->max) { - $this->max = Interval::comparableMax($this->max, $this->right->max); // static method + $this->max = Interval::comparableMax($this->max, $this->right->max); // static method } if ($this->left && $this->left->max) { $this->max = Interval::comparableMax($this->max, $this->left->max); @@ -86,16 +104,18 @@ public function updateMax() { } // Other_node does not intersect any node of left subtree, if this.left.max < other_node.item.key.low - public function notIntersectLeftSubtree($searchNode) { + public function notIntersectLeftSubtree($searchNode) + { //const comparable_less_than = this.item.key.constructor.comparable_less_than; // static method $high = $this->left->max->high !== null ? $this->left->max->high : $this->left->max; return Interval::comparableLessThan($high, $searchNode->item->key->low); } // Other_node does not intersect right subtree if other_node.item.key.high < this.right.key.low - public function notIntersectRightSubtree($searchNode) { + public function notIntersectRightSubtree($searchNode) + { //const comparable_less_than = this.item.key.constructor.comparable_less_than; // static method $low = $this->right->max->low !== null ? $this->right->max->low : $this->right->item->key->low; return Interval::comparableLessThan($searchNode->item->key->high, $low); } -} \ No newline at end of file +} diff --git a/tests/IntervalTest.php b/tests/IntervalTest.php new file mode 100644 index 0000000..b8fce87 --- /dev/null +++ b/tests/IntervalTest.php @@ -0,0 +1,21 @@ +assertInstanceOf( + Interval::class, + new Interval(1, 2) + ); + } + + public function testCannotBeCreatedFromIncorrectInterval(): void + { + $this->expectException(InvalidArgumentException::class); + + new Interval(2, 1); + } +} \ No newline at end of file diff --git a/tests/IntervalTreeTest.php b/tests/IntervalTreeTest.php new file mode 100644 index 0000000..7e690cc --- /dev/null +++ b/tests/IntervalTreeTest.php @@ -0,0 +1,70 @@ +assertInstanceOf( + IntervalTree::class, + new IntervalTree + ); + } + + public function testFindAllIntervalsIntersection(): void + { + $intervals = [[6, 8], [1, 4], [2, 3], [5, 12], [1, 1], [3, 5], [5, 7]]; + $tree = new IntervalTree(); + for ($i = 0; $i < count($intervals); $i++) { + $tree->insert($intervals[$i], $i); + } + + $nodesInRange = $tree->iterateIntersections([2, 3]); + $intersectedIntervalIndexes = []; + foreach ($nodesInRange as $node) { + $intersectedIntervalIndexes[] = $node->getValue(); + } + + $this->assertEquals($intersectedIntervalIndexes, [1, 2, 5]); + } + + public function testHasIntersection(): void + { + $intervals = [[6, 8], [1, 4], [2, 3], [5, 12], [1, 1], [3, 5], [5, 7]]; + $tree = new IntervalTree(); + for ($i = 0; $i < count($intervals); $i++) { + $tree->insert($intervals[$i], $i); + } + + $this->assertTrue($tree->hasIntersection([2, 3])); + $this->assertTrue($tree->hasIntersection([0, 1])); + $this->assertTrue($tree->hasIntersection([0, 12])); + $this->assertFalse($tree->hasIntersection([0, 0])); + $this->assertFalse($tree->hasIntersection([13, 14])); + } + + public function testCountIntersections(): void + { + $intervals = [[6, 8], [1, 4], [2, 3], [5, 12], [1, 1], [3, 5], [5, 7]]; + $tree = new IntervalTree(); + for ($i = 0; $i < count($intervals); $i++) { + $tree->insert($intervals[$i], $i); + } + + $this->assertEquals($tree->countIntersections([2, 3]), 3); + $this->assertEquals($tree->countIntersections([13, 14]), 0); + $this->assertEquals($tree->countIntersections([0, 1]), 2); + } + + public function testGetKeys(): void + { + $intervals = [[6, 8], [1, 4], [2, 3], [5, 12], [1, 1], [3, 5], [5, 7]]; + $tree = new IntervalTree(); + for ($i = 0; $i < count($intervals); $i++) { + $tree->insert($intervals[$i], $i); + } + + $this->assertEquals($tree->getKeys(), [[1, 1], [1, 4], [2, 3], [3, 5], [5, 7], [5, 12], [6, 8]]); + } +}