From ac39c0e7f8a0b2f97b65b81e5f7cf8d2e2afeba8 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 17:58:48 +0200 Subject: [PATCH 01/15] Remove obsolete files. --- grumphp.yml.dist | 4 ---- test.php | 52 ------------------------------------------------ 2 files changed, 56 deletions(-) delete mode 100644 grumphp.yml.dist delete mode 100644 test.php diff --git a/grumphp.yml.dist b/grumphp.yml.dist deleted file mode 100644 index 3d98c90..0000000 --- a/grumphp.yml.dist +++ /dev/null @@ -1,4 +0,0 @@ -parameters: - git_dir: . - bin_dir: vendor/bin - tasks: { } diff --git a/test.php b/test.php deleted file mode 100644 index c21b3e4..0000000 --- a/test.php +++ /dev/null @@ -1,52 +0,0 @@ -setSize(3); - $algo->setData($input); - - $sums = []; - foreach ($algo->getResult() as $subset) { - sort($subset); - $sum = array_sum($subset); - $sums[] = $sum; - echo "[" . implode(',', $subset) . "] = " . $sum . "\n"; - } - - $stdDev = \Oefenweb\Statistics\Statistics::standardDeviation($sums); - echo "Variance: " . round($stdDev, 1) . "\n"; - - $end = microtime(true) - $start_time; - - echo "\nTime elapsed: " . $end . "\n"; -} From 7cdeb1133bf79b3fa4f6b63be2e8454617d4dadd Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 17:55:47 +0200 Subject: [PATCH 02/15] Remove tests. --- tests/phpunit.xml | 18 ----- tests/src/BruteForceCustomATest.php | 107 ---------------------------- tests/src/BruteForceTest.php | 96 ------------------------- tests/src/GreedyTest.php | 89 ----------------------- tests/src/PhpPartitionTestBase.php | 22 ------ tests/src/SimpleTest.php | 89 ----------------------- 6 files changed, 421 deletions(-) delete mode 100644 tests/phpunit.xml delete mode 100644 tests/src/BruteForceCustomATest.php delete mode 100644 tests/src/BruteForceTest.php delete mode 100644 tests/src/GreedyTest.php delete mode 100644 tests/src/PhpPartitionTestBase.php delete mode 100644 tests/src/SimpleTest.php diff --git a/tests/phpunit.xml b/tests/phpunit.xml deleted file mode 100644 index 63d0fbf..0000000 --- a/tests/phpunit.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ./src/ - - - - - - ./vendor - - - ../src - - - diff --git a/tests/src/BruteForceCustomATest.php b/tests/src/BruteForceCustomATest.php deleted file mode 100644 index d0e4eef..0000000 --- a/tests/src/BruteForceCustomATest.php +++ /dev/null @@ -1,107 +0,0 @@ -setSize($input['partition']); - $this->assertEquals($input['partition'], $algo->getSize()); - - $algo->setData($input['data']); - $this->assertEquals($input['data'], $algo->getData()); - - if (isset($input['callback'])) { - $algo->setItemAccessCallback($input['callback']); - } - - $this->assertEquals($output, $algo->getResult()); - } - - /** - * Value provider. - */ - public function simpleValueProvider() - { - $a = new \StdClass(); - $a->id = 'a'; - - $b = new \StdClass(); - $b->id = 'b'; - - return [ - [ - 'input' => [ - 'data' => [1, new PartitionItem($a, 2), new PartitionItem($b, 3), 4], - 'partition' => 2, - ], - 'output' => [ - [1, 4], - [$a, $b], - ], - ], - [ - 'input' => [ - 'data' => [1, 2, 3, 4], - 'partition' => 4, - ], - 'output' => [ - [1], [4], [2], [3], - ], - ], - [ - 'input' => [ - 'data' => [1, 2, 3, 4], - 'partition' => 5, - ], - 'output' => [ - [3], [4], [1], [2], - ], - ], - [ - 'input' => [ - 'data' => [1, 2, 3, 4], - 'partition' => 2, - ], - 'output' => [ - [1, 4], - [2, 3], - ], - ], - [ - 'input' => [ - 'data' => [ - ['key' => 'item1', 'weight' => 1], - ['key' => 'item2', 'weight' => 2], - ['key' => 'item3', 'weight' => 3], - ['key' => 'item4', 'weight' => 4], - ], - 'partition' => 2, - 'callback' => function ($item) { - return $item['weight']; - }, - ], - 'output' => [ - [['key' => 'item1', 'weight' => 1], ['key' => 'item4', 'weight' => 4]], - [['key' => 'item2', 'weight' => 2], ['key' => 'item3', 'weight' => 3]], - ], - ], - ]; - } -} diff --git a/tests/src/BruteForceTest.php b/tests/src/BruteForceTest.php deleted file mode 100644 index 9830ef7..0000000 --- a/tests/src/BruteForceTest.php +++ /dev/null @@ -1,96 +0,0 @@ -setSize($input['partition']); - $this->assertEquals($input['partition'], $algo->getSize()); - - $algo->setData($input['data']); - $this->assertEquals($input['data'], $algo->getData()); - - if (isset($input['callback'])) { - $algo->setItemAccessCallback($input['callback']); - } - - $this->assertEquals( - $output, - $algo->getResult(), - "\$canonicalize = true", - $delta = 0.0, - $maxDepth = 10, - $canonicalize = true - ); - } - - /** - * Value provider. - */ - public function simpleValueProvider() - { - $a = new \StdClass(); - $a->id = 'a'; - - $b = new \StdClass(); - $b->id = 'b'; - - return [ - [ - 'input' => [ - 'data' => [1, new PartitionItem($a, 2), new PartitionItem($b, 3), 4], - 'partition' => 2, - ], - 'output' => [ - [1, 4], - [$a, $b], - ], - ], - [ - 'input' => [ - 'data' => [1, 2, 3, 4], - 'partition' => 2, - ], - 'output' => [ - [1, 4], - [2, 3], - ], - ], - [ - 'input' => [ - 'data' => [ - ['key' => 'item1', 'weight' => 1], - ['key' => 'item2', 'weight' => 2], - ['key' => 'item3', 'weight' => 3], - ['key' => 'item4', 'weight' => 4], - ], - 'partition' => 2, - 'callback' => function ($item) { - return $item['weight']; - }, - ], - 'output' => [ - [['key' => 'item1', 'weight' => 1], ['key' => 'item4', 'weight' => 4]], - [['key' => 'item2', 'weight' => 2], ['key' => 'item3', 'weight' => 3]], - ], - ], - ]; - } -} diff --git a/tests/src/GreedyTest.php b/tests/src/GreedyTest.php deleted file mode 100644 index 3dad2b8..0000000 --- a/tests/src/GreedyTest.php +++ /dev/null @@ -1,89 +0,0 @@ -setSize($input['partition']); - $this->assertEquals($input['partition'], $algo->getSize()); - - $algo->setData($input['data']); - $this->assertEquals($input['data'], $algo->getData()); - - if (isset($input['callback'])) { - $algo->setItemAccessCallback($input['callback']); - } - - $this->assertEquals($output, $algo->getResult()); - } - - /** - * Value provider. - */ - public function simpleValueProvider() - { - $a = new \StdClass(); - $a->id = 'a'; - - $b = new \StdClass(); - $b->id = 'b'; - - return [ - [ - 'input' => [ - 'data' => [1, new PartitionItem($a, 2), new PartitionItem($b, 3), 4], - 'partition' => 2, - ], - 'output' => [ - [$b, $a], - [4, 1], - ], - ], - [ - 'input' => [ - 'data' => [1, 2, 3, 4], - 'partition' => 2, - ], - 'output' => [ - [3, 2], - [4, 1], - ], - ], - [ - 'input' => [ - 'data' => [ - ['key' => 'item1', 'weight' => 1], - ['key' => 'item2', 'weight' => 2], - ['key' => 'item3', 'weight' => 3], - ['key' => 'item4', 'weight' => 4], - ], - 'partition' => 2, - 'callback' => function ($item) { - return $item['weight']; - }, - ], - 'output' => [ - [['key' => 'item3', 'weight' => 3], ['key' => 'item2', 'weight' => 2]], - [['key' => 'item4', 'weight' => 4], ['key' => 'item1', 'weight' => 1]], - ], - ], - ]; - } -} diff --git a/tests/src/PhpPartitionTestBase.php b/tests/src/PhpPartitionTestBase.php deleted file mode 100644 index eb2cc89..0000000 --- a/tests/src/PhpPartitionTestBase.php +++ /dev/null @@ -1,22 +0,0 @@ -setSize($input['partition']); - $this->assertEquals($input['partition'], $algo->getSize()); - - $algo->setData($input['data']); - $this->assertEquals($input['data'], $algo->getData()); - - if (isset($input['callback'])) { - $algo->setItemAccessCallback($input['callback']); - } - - $this->assertEquals($output, $algo->getResult()); - } - - /** - * Value provider. - */ - public function simpleValueProvider() - { - $a = new \StdClass(); - $a->id = 'a'; - - $b = new \StdClass(); - $b->id = 'b'; - - return [ - [ - 'input' => [ - 'data' => [1, new PartitionItem($a, 2), new PartitionItem($b, 3), 4], - 'partition' => 2, - ], - 'output' => [ - [1, $b], - [$a, 4], - ], - ], - [ - 'input' => [ - 'data' => [1, 2, 3, 4], - 'partition' => 2, - ], - 'output' => [ - [1, 3], - [2, 4], - ], - ], - [ - 'input' => [ - 'data' => [ - ['key' => 'item1', 'weight' => 1], - ['key' => 'item2', 'weight' => 2], - ['key' => 'item3', 'weight' => 3], - ['key' => 'item4', 'weight' => 4], - ], - 'partition' => 2, - 'callback' => function ($item) { - return $item['weight']; - }, - ], - 'output' => [ - [['key' => 'item1', 'weight' => 1], ['key' => 'item3', 'weight' => 3]], - [['key' => 'item2', 'weight' => 2], ['key' => 'item4', 'weight' => 4]], - ], - ], - ]; - } -} From c24092df3dbaf45b5d441f495925a86d7bd8448a Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 8 May 2019 14:01:09 +0200 Subject: [PATCH 03/15] Add .editorconfig file. --- .editorconfig | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..173228f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +max_line_length = 80 +indent_style = space +indent_size = 4 +insert_final_newline = true From 6abcf291930d9f8e96bb45cb379f76aec8041d7d Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 17:58:20 +0200 Subject: [PATCH 04/15] Update .gitignore file. --- .gitignore | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 524e5d8..baaec8b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,11 @@ com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties -test.php -.gitignore \ No newline at end of file +.gitignore +/composer.lock +/.php_cs.cache +/test.php +/test2.php +/tests/_output/ From ad818af622975fead4fc5d65a433f5ced2b5cded Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 17:59:02 +0200 Subject: [PATCH 05/15] Update composer.json. --- composer.json | 59 +++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index 19ce00e..5d95fa5 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,18 @@ { "name": "drupol/phpartition", - "description": "Partition problem for balanced arrays splitting made easy.", "type": "library", + "description": "Partition problem for balanced arrays splitting made easy.", + "keywords": [ + "math", + "numbers", + "statistics", + "partition", + "greedy", + "karmarkar", + "karp" + ], "homepage": "https://github.com/drupol/phpartition", - "keywords": ["math", "numbers", "statistics", "partition", "greedy", "karmarkar", "karp"], - "license": "GPL-2.0+", - "support": { - "issues": "https://github.com/drupol/phpartition/issues", - "source": "https://github.com/drupol/phpartition" - }, - "minimum-stability": "stable", + "license": "MIT", "authors": [ { "name": "Pol Dellaiera", @@ -17,32 +20,28 @@ "role": "Author" } ], - "require": { - "oefenweb/statistics": "^1.1", - "drupol/phpermutations": "^1.2", - "phootwork/collection": "^1.4" - }, "require-dev": { - "phpunit/phpunit": "^5.7", - "mockery/mockery": "^0.9", - "squizlabs/php_codesniffer": "^2.0", - "satooshi/php-coveralls": "^1.0", - "phpunit/php-code-coverage": "^4.0", - "scrutinizer/ocular": "^1.3", - "phpro/grumphp": "^0.11" - }, - "scripts": { - "phpcs": "./vendor/bin/phpcs --standard=PSR2 --ignore=vendor .", - "phpcbf": "./vendor/bin/phpcbf --standard=PSR2 --ignore=vendor .", - "phpunit": "./vendor/bin/phpunit --coverage-clover build/logs/clover.xml -c tests/phpunit.xml tests", - "grumphp": "./vendor/bin/grumphp run", - "coveralls": "./vendor/bin/coveralls", - "scrutinizer": "./vendor/bin/ocular code-coverage:upload --format=php-clover build/logs/clover.xml" + "codeception/codeception": "^3", + "drupol/php-conventions": "^1.3", + "drupol/phpermutations": "^1.3" }, "autoload": { "psr-4": { - "drupol\\phpartition\\": "src/", - "drupol\\phpartition\\Tests\\": "tests/src/" + "drupol\\phpartition\\": "src/" } + }, + "autoload-dev": { + "psr-4": { + "drupol\\phpartition\\tests\\": "tests/" + } + }, + "scripts": { + "codecept": "./vendor/bin/codecept run", + "codecept-coverage": "./vendor/bin/codecept run --coverage --coverage-xml --env travis", + "grumphp": "./vendor/bin/grumphp run" + }, + "support": { + "issues": "https://github.com/drupol/phpartition/issues", + "source": "https://github.com/drupol/phpartition" } } From e78bc031da3687cd0e7815d1127268e2cc1fab28 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 21:38:35 +0200 Subject: [PATCH 06/15] Add a grumphp.yml.dist configuration file. --- grumphp.yml.dist | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 grumphp.yml.dist diff --git a/grumphp.yml.dist b/grumphp.yml.dist new file mode 100644 index 0000000..95c7e14 --- /dev/null +++ b/grumphp.yml.dist @@ -0,0 +1,14 @@ +imports: + - { resource: vendor/drupol/php-conventions/config/php7/grumphp.yml } + +parameters: + tasks.phpstan.ignore_patterns: + - vendor/ + - spec/ + - tests/ + tasks.phpcs.ignore_patterns: + - vendor/ + - spec/ + - tests/ + extensions: + - drupol\PhpConventions\GrumphpTasksExtension From afe473a62b931d877f1e5106df3010ede1ca3409 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 21:48:58 +0200 Subject: [PATCH 07/15] Update .travis.yml configuration file. --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75ca950..bdee833 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,15 +9,17 @@ cache: - $HOME/.composer/cache php: - - 5.6 - 7.1 + - 7.2 + - 7.3 install: - composer install script: - composer grumphp - - composer phpunit + - composer codecept-coverage after_success: - - composer scrutinizer \ No newline at end of file + - phpenv config-rm xdebug.ini + - bash <(curl -s https://codecov.io/bash) -f tests/_output/coverage.xml From f32649c3fffcc7e9f0ce74da4b3d18bc1f6f2d71 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 21:43:29 +0200 Subject: [PATCH 08/15] Update README.md, add License, remove scrutinizer. --- .scrutinizer.yml | 61 --------------- LICENSE | 21 +++++ README.md | 194 ++++++++++++----------------------------------- 3 files changed, 71 insertions(+), 205 deletions(-) delete mode 100644 .scrutinizer.yml create mode 100644 LICENSE diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index b39596c..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,61 +0,0 @@ -build: - environment: - php: '5.6' - -filter: - paths: - - 'src/*' - -tools: - external_code_coverage: - timeout: 600 - php_mess_detector: - config: - code_size_rules: { cyclomatic_complexity: true, npath_complexity: true, excessive_method_length: true, excessive_class_length: true, excessive_parameter_list: true, excessive_public_count: true, too_many_fields: true, too_many_methods: true, excessive_class_complexity: true } - design_rules: { number_of_class_children: true, depth_of_inheritance: true, coupling_between_objects: true } - unused_code_rules: { unused_local_variable: true, unused_private_method: true, unused_formal_parameter: true } - naming_rules: { short_variable: true, long_variable: true, short_method: true, boolean_method_name: true } - controversial_rules: { camel_case_class_name: true, camel_case_property_name: true, camel_case_method_name: true, camel_case_parameter_name: true, camel_case_variable_name: true } - php_cs_fixer: - config: - level: all - fixers: { unused_use: true, phpdoc_params: true, braces: true, php_closing_tag: true } - php_analyzer: - config: - suspicious_code: { enabled: true, overriding_parameter: true, overriding_closure_use: true, parameter_closure_use_conflict: true, parameter_multiple_times: true, non_existent_class_in_instanceof_check: true, non_existent_class_in_catch_clause: true, assignment_of_null_return: true, non_commented_switch_fallthrough: true, non_commented_empty_catch_block: true, overriding_private_members: true, use_statement_alias_conflict: true, precedence_in_condition_assignment: true } - verify_php_doc_comments: { enabled: true, parameters: true, return: true, suggest_more_specific_types: true, ask_for_return_if_not_inferrable: true, ask_for_param_type_annotation: true } - loops_must_use_braces: { enabled: true } - simplify_boolean_return: { enabled: true } - phpunit_checks: { enabled: true } - reflection_fixes: { enabled: true } - use_statement_fixes: { enabled: true, order_alphabetically: true, remove_unused: true, preserve_multiple: false, preserve_blanklines: false } - parameter_reference_check: { enabled: false } - checkstyle: { enabled: false, no_trailing_whitespace: true, naming: { enabled: true, local_variable: '^[a-z][a-zA-Z0-9]*$', abstract_class_name: ^Abstract|Factory$, utility_class_name: 'Utils?$', constant_name: '^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$', property_name: '^[a-z][a-zA-Z0-9]*$', method_name: '^(?:[a-z]|__)[a-zA-Z0-9]*$', parameter_name: '^[a-z][a-zA-Z0-9]*$', interface_name: '^[A-Z][a-zA-Z0-9]*Interface$', type_name: '^[A-Z][a-zA-Z0-9]*$', exception_name: '^[A-Z][a-zA-Z0-9]*Exception$', isser_method_name: '^(?:is|has|should|may|supports)' } } - unreachable_code: { enabled: false } - check_access_control: { enabled: false } - typo_checks: { enabled: false } - check_variables: { enabled: false } - check_calls: { enabled: true, too_many_arguments: true, missing_argument: true, argument_type_checks: lenient } - dead_assignments: { enabled: false } - check_usage_context: { enabled: true, foreach: { value_as_reference: true, traversable: true } } - reflection_checks: { enabled: false } - precedence_checks: { enabled: true, assignment_in_condition: true, comparison_of_bit_result: true } - basic_semantic_checks: { enabled: false } - unused_code: { enabled: false } - deprecation_checks: { enabled: false } - useless_function_calls: { enabled: false } - metrics_lack_of_cohesion_methods: { enabled: false } - metrics_coupling: { enabled: true, stable_code: { namespace_prefixes: { }, classes: { } } } - doctrine_parameter_binding: { enabled: false } - doctrine_entity_manager_injection: { enabled: false } - symfony_request_injection: { enabled: false } - doc_comment_fixes: { enabled: false } - php_code_sniffer: - config: - standard: PSR2 - sniffs: { psr2: { classes: { property_declaration_sniff: true }, methods: { method_declaration_sniff: true } } } - sensiolabs_security_checker: true - php_loc: true - php_pdepend: true - php_sim: true - php_changetracking: true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c946b00 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Pol Dellaiera + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index cd607c8..b8f015f 100644 --- a/README.md +++ b/README.md @@ -1,162 +1,68 @@ -## PHPartition -[![Build Status](https://travis-ci.org/drupol/phpartition.svg?branch=master)](https://travis-ci.org/drupol/phpartition) [![Code Coverage](https://scrutinizer-ci.com/g/drupol/phpartition/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/drupol/phpartition/?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/drupol/phpartition/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/drupol/phpartition/?branch=master) [![Dependency Status](https://www.versioneye.com/user/projects/58551f4d4d6466004c28cc8f/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/58551f4d4d6466004c28cc8f) +[![Latest Stable Version](https://img.shields.io/packagist/v/drupol/phpartition.svg?style=flat-square)](https://packagist.org/packages/drupol/phpartition) + [![GitHub stars](https://img.shields.io/github/stars/drupol/phpartition.svg?style=flat-square)](https://packagist.org/packages/drupol/phpartition) + [![Total Downloads](https://img.shields.io/packagist/dt/drupol/phpartition.svg?style=flat-square)](https://packagist.org/packages/drupol/phpartition) + [![Build Status](https://img.shields.io/travis/drupol/phpartition/master.svg?style=flat-square)](https://travis-ci.org/drupol/phpartition) + [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/drupol/phpartition/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/drupol/phpartition/?branch=master) + [![License](https://img.shields.io/packagist/l/drupol/phpartition.svg?style=flat-square)](https://packagist.org/packages/drupol/phpartition) -In number theory and computer science, the partition problem is the task of deciding whether a given multiset of items can be partitioned into multiple balanced subsets. +# PHPartition + +## Description + +In number theory and computer science, the partition problem ([wikipedia](https://en.wikipedia.org/wiki/Partition_problem)) +is the task of splitting a set of items into multiple balanced subsets. + +This library not only handles dividing a partition in two, but it handles as many numbers of them if needed, where the +goal is to divide a set n objects into a given number subsets, minimizing the difference between the smallest and the +largest subset sums (multi-way). + +## Documentation + +Implemented algorithms: +* Greedy +* Linear +* Combinations ([custom anytime algorithm](https://en.wikipedia.org/wiki/Anytime_algorithm)) + +Blog post: [https://not-a-number.io/2019/TODO](https://not-a-number.io/2019/TODO) ## Requirements -* PHP >= 5.6, -* (optional) [PHPUnit](https://phpunit.de/) to run tests. +* PHP >= 7.1 + +## Installation + +```composer require drupol/phpartition``` + +## Optional packages + +* [drupol/phpermutations](https://github.com/drupol/phpermutations): To use the Anytime algorithm. ## Examples ```php setData($data); -$greedy->setSize(3); -$result = $greedy->getResult(); - -// $result is: -/* - * Array - ( - [0] => Array - ( - [0] => 9 - [1] => 5 - [2] => 1 - ) - - [1] => Array - ( - [0] => 7 - [1] => 6 - [2] => 3 - ) - - [2] => Array - ( - [0] => 11 - [1] => 5 - ) - ) - */ - -$simple = new \drupol\phpartition\Algorithm\Simple(); -$simple->setData($data); -$simple->setSize(3); -$result = $simple->getResult(); - -// $result is: -/* - * Array -( - [0] => Array - ( - [0] => 5 - [1] => 11 - ) - - [1] => Array - ( - [0] => 1 - [1] => 7 - [2] => 3 - ) - - [2] => Array - ( - [0] => 5 - [1] => 6 - [2] => 9 - ) -) - */ -``` -You may also pass objects or array but then, you'll have to define how to access their value. +include './vendor/autoload.php'; -```php - 'anything A', - 'weight' => 1, - ), - array( - 'item' => 'anything B', - 'weight' => 2, - ), - array( - 'item' => 'anything C', - 'weight' => 3, - ), - array( - 'item' => 'anything D', - 'weight' => 4, - ), -); - -$greedy = new \drupol\phpartition\Algorithm\Greedy(); -$greedy->setData($data); -$greedy->setSize(2); -$greedy->setItemAccessCallback(function($item) { - return $item['weight']; -}); -$result = $greedy->getResult(); - -// $result is -/* - * Array - ( - [0] => Array - ( - [0] => Array - ( - [item] => anything C - [weight] => 3 - ) - - [1] => Array - ( - [item] => anything B - [weight] => 2 - ) - - ) - - [1] => Array - ( - [0] => Array - ( - [item] => anything D - [weight] => 4 - ) - - [1] => Array - ( - [item] => anything A - [weight] => 1 - ) - - ) - - ) - */ ``` -It's also possible to mix the type of object to partition. +## Code quality, tests and benchmarks + +Every time changes are introduced into the library, [Travis CI](https://travis-ci.org/drupol/phpartition/builds) run the +tests and the benchmarks. + +The library has unit tests written using [PHPUnit](http://www.phpunit.de/) through [Codeception](https://codeception.com/). +Feel free to check them out in the `tests` directory. Run `composer codecept` to trigger the tests. + +Before each commit some inspections are executed with [GrumPHP](https://github.com/phpro/grumphp), +run `./vendor/bin/grumphp run` to check manually. + +## Contributing + +Feel free to contribute to this library by sending Github pull requests. I'm quite reactive :-) ## TODO - Implement Complete Karmarkar-Karp (CKK) algorithm, -- Documentation. - +- More documentation. From 82ecdcf8735773e0d20eec933f8c4abfdf06753c Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 7 May 2019 21:17:48 +0200 Subject: [PATCH 09/15] Refactor the tests. --- codeception.dist.yml | 19 + tests/_data/.gitkeep | 0 tests/_support/Helper/Unit.php | 12 + tests/_support/UnitTester.php | 26 + .../_support/_generated/UnitTesterActions.php | 920 ++++++++++++++++++ tests/unit.suite.yml | 9 + tests/unit/CombinationsTest.php | 83 ++ tests/unit/GreedyAltAltTest.php | 77 ++ tests/unit/GreedyAltTest.php | 77 ++ tests/unit/GreedyTest.php | 83 ++ tests/unit/LinearTest.php | 83 ++ tests/unit/Partition/PartitionTest.php | 52 + tests/unit/_bootstrap.php | 3 + 13 files changed, 1444 insertions(+) create mode 100644 codeception.dist.yml create mode 100644 tests/_data/.gitkeep create mode 100644 tests/_support/Helper/Unit.php create mode 100644 tests/_support/UnitTester.php create mode 100644 tests/_support/_generated/UnitTesterActions.php create mode 100644 tests/unit.suite.yml create mode 100644 tests/unit/CombinationsTest.php create mode 100644 tests/unit/GreedyAltAltTest.php create mode 100644 tests/unit/GreedyAltTest.php create mode 100644 tests/unit/GreedyTest.php create mode 100644 tests/unit/LinearTest.php create mode 100644 tests/unit/Partition/PartitionTest.php create mode 100644 tests/unit/_bootstrap.php diff --git a/codeception.dist.yml b/codeception.dist.yml new file mode 100644 index 0000000..8f9db53 --- /dev/null +++ b/codeception.dist.yml @@ -0,0 +1,19 @@ +actor_suffix: Tester +paths: + tests: tests + data: tests/_data + log: tests/_output + support: tests/_support + envs: tests/_envs +settings: + bootstrap: _bootstrap.php + colors: true + memory_limit: 1024M + lint: true +extensions: + enabled: + - Codeception\Extension\RunFailed +coverage: + enabled: true + include: + - src/* diff --git a/tests/_data/.gitkeep b/tests/_data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/_support/Helper/Unit.php b/tests/_support/Helper/Unit.php new file mode 100644 index 0000000..4577483 --- /dev/null +++ b/tests/_support/Helper/Unit.php @@ -0,0 +1,12 @@ +getScenario()->runStep(new \Codeception\Step\Action('assertArrayHasKey', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $key + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertArrayNotHasKey() + */ + public function assertArrayNotHasKey($key, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertArrayNotHasKey', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that haystack contains needle + * + * @param $needle + * @param $haystack + * @param string $message + * + * @see \Codeception\Module\Asserts::assertContains() + */ + public function assertContains($needle, $haystack, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContains', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $expectedCount + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertCount() + */ + public function assertCount($expectedCount, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertCount', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that variable is empty. + * + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertEmpty() + */ + public function assertEmpty($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEmpty', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that two variables are equal. + * + * @param $expected + * @param $actual + * @param string $message + * @param float $delta + * + * @see \Codeception\Module\Asserts::assertEquals() + */ + public function assertEquals($expected, $actual, $message = null, $delta = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEquals', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that condition is negative. + * + * @param $condition + * @param string $message + * + * @see \Codeception\Module\Asserts::assertFalse() + */ + public function assertFalse($condition, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFalse', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks if file exists + * + * @param string $filename + * @param string $message + * + * @see \Codeception\Module\Asserts::assertFileExists() + */ + public function assertFileExists($filename, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileExists', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks if file doesn't exist + * + * @param string $filename + * @param string $message + * + * @see \Codeception\Module\Asserts::assertFileNotExists() + */ + public function assertFileNotExists($filename, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotExists', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $expected + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertGreaterOrEquals() + */ + public function assertGreaterOrEquals($expected, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterOrEquals', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that actual is greater than expected + * + * @param $expected + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertGreaterThan() + */ + public function assertGreaterThan($expected, $actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThan', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that actual is greater or equal than expected + * + * @param $expected + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertGreaterThanOrEqual() + */ + public function assertGreaterThanOrEqual($expected, $actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThanOrEqual', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $class + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertInstanceOf() + */ + public function assertInstanceOf($class, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertInstanceOf', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $type + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertInternalType() + */ + public function assertInternalType($type, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertInternalType', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsArray() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsArray($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsArray', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsBool() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsBool($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsBool', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsCallable() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsCallable($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsCallable', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertIsEmpty() + */ + public function assertIsEmpty($actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsEmpty', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsFloat() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsFloat($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsFloat', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsInt() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsInt($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsInt', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotArray() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotArray($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotArray', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotBool() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotBool($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotBool', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotCallable() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotCallable($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotCallable', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotFloat() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotFloat($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotFloat', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotInt() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotInt($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotInt', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotNumeric() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotNumeric($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotNumeric', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotObject() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotObject($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotObject', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotResource() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotResource($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotResource', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotScalar() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotScalar($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotScalar', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNotString() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNotString($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotString', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsNumeric() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsNumeric($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNumeric', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsObject() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsObject($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsObject', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsResource() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsResource($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsResource', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsScalar() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsScalar($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsScalar', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertIsString() + * + * @param mixed $actual + * @param null|mixed $message + */ + public function assertIsString($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsString', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $expected + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertLessOrEquals() + */ + public function assertLessOrEquals($expected, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessOrEquals', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that actual is less than expected + * + * @param $expected + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertLessThan() + */ + public function assertLessThan($expected, $actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThan', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that actual is less or equal than expected + * + * @param $expected + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertLessThanOrEqual() + */ + public function assertLessThanOrEqual($expected, $actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThanOrEqual', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that haystack doesn't contain needle. + * + * @param $needle + * @param $haystack + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotContains() + */ + public function assertNotContains($needle, $haystack, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContains', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that variable is not empty. + * + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotEmpty() + */ + public function assertNotEmpty($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEmpty', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that two variables are not equal + * + * @param $expected + * @param $actual + * @param string $message + * @param float $delta + * + * @see \Codeception\Module\Asserts::assertNotEquals() + */ + public function assertNotEquals($expected, $actual, $message = null, $delta = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEquals', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the condition is NOT false (everything but false) + * + * @param $condition + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotFalse() + */ + public function assertNotFalse($condition, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotFalse', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @param $class + * @param $actual + * @param $description + * + * @see \Codeception\Module\Asserts::assertNotInstanceOf() + */ + public function assertNotInstanceOf($class, $actual, $description = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotInstanceOf', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that variable is not NULL + * + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotNull() + */ + public function assertNotNull($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotNull', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that string not match with pattern + * + * @param string $pattern + * @param string $string + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotRegExp() + */ + public function assertNotRegExp($pattern, $string, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotRegExp', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that two variables are not same + * + * @param $expected + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotSame() + */ + public function assertNotSame($expected, $actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotSame', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the condition is NOT true (everything but true) + * + * @param $condition + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNotTrue() + */ + public function assertNotTrue($condition, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotTrue', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that variable is NULL + * + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertNull() + */ + public function assertNull($actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNull', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that string match with pattern + * + * @param string $pattern + * @param string $string + * @param string $message + * + * @see \Codeception\Module\Asserts::assertRegExp() + */ + public function assertRegExp($pattern, $string, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertRegExp', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that two variables are same + * + * @param $expected + * @param $actual + * @param string $message + * + * @see \Codeception\Module\Asserts::assertSame() + */ + public function assertSame($expected, $actual, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertSame', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertStringContainsString() + * + * @param mixed $needle + * @param mixed $haystack + * @param null|mixed $message + */ + public function assertStringContainsString($needle, $haystack, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringContainsString', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertStringContainsStringIgnoringCase() + * + * @param mixed $needle + * @param mixed $haystack + * @param null|mixed $message + */ + public function assertStringContainsStringIgnoringCase($needle, $haystack, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringContainsStringIgnoringCase', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertStringNotContainsString() + * + * @param mixed $needle + * @param mixed $haystack + * @param null|mixed $message + */ + public function assertStringNotContainsString($needle, $haystack, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotContainsString', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * @see \Codeception\Module\Asserts::assertStringNotContainsStringIgnoringCase() + * + * @param mixed $needle + * @param mixed $haystack + * @param null|mixed $message + */ + public function assertStringNotContainsStringIgnoringCase($needle, $haystack, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotContainsStringIgnoringCase', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that a string doesn't start with the given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * + * @see \Codeception\Module\Asserts::assertStringStartsNotWith() + */ + public function assertStringStartsNotWith($prefix, $string, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringStartsNotWith', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that a string starts with the given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * + * @see \Codeception\Module\Asserts::assertStringStartsWith() + */ + public function assertStringStartsWith($prefix, $string, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringStartsWith', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that condition is positive. + * + * @param $condition + * @param string $message + * + * @see \Codeception\Module\Asserts::assertTrue() + */ + public function assertTrue($condition, $message = null) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('assertTrue', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Handles and checks exception called inside callback function. + * Either exception class name or exception instance should be provided. + * + * ```php + * expectException(MyException::class, function() { + * $this->doSomethingBad(); + * }); + * + * $I->expectException(new MyException(), function() { + * $this->doSomethingBad(); + * }); + * ``` + * If you want to check message or exception code, you can pass them with exception instance: + * ```php + * expectException(new MyException("Don't do bad things"), function() { + * $this->doSomethingBad(); + * }); + * ``` + * + * @param $exception string or \Exception + * @param $callback + * + * @see \Codeception\Module\Asserts::expectException() + */ + public function expectException($exception, $callback) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('expectException', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Handles and checks throwables (Exceptions/Errors) called inside the callback function. + * Either throwable class name or throwable instance should be provided. + * + * ```php + * expectThrowable(MyThrowable::class, function() { + * $this->doSomethingBad(); + * }); + * + * $I->expectThrowable(new MyException(), function() { + * $this->doSomethingBad(); + * }); + * ``` + * If you want to check message or throwable code, you can pass them with throwable instance: + * ```php + * expectThrowable(new MyError("Don't do bad things"), function() { + * $this->doSomethingBad(); + * }); + * ``` + * + * @param $throwable string or \Throwable + * @param $callback + * + * @see \Codeception\Module\Asserts::expectThrowable() + */ + public function expectThrowable($throwable, $callback) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('expectThrowable', \func_get_args())); + } + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Fails the test with message. + * + * @param $message + * + * @see \Codeception\Module\Asserts::fail() + */ + public function fail($message) + { + return $this->getScenario()->runStep(new \Codeception\Step\Action('fail', \func_get_args())); + } + /** + * @return \Codeception\Scenario + */ + abstract protected function getScenario(); +} diff --git a/tests/unit.suite.yml b/tests/unit.suite.yml new file mode 100644 index 0000000..026c91d --- /dev/null +++ b/tests/unit.suite.yml @@ -0,0 +1,9 @@ +# Codeception Test Suite Configuration +# +# Suite for unit or integration tests. + +actor: UnitTester +modules: + enabled: + - Asserts + - \Helper\Unit \ No newline at end of file diff --git a/tests/unit/CombinationsTest.php b/tests/unit/CombinationsTest.php new file mode 100644 index 0000000..cd4b9fe --- /dev/null +++ b/tests/unit/CombinationsTest.php @@ -0,0 +1,83 @@ +setDataset($dataset); + + static::assertSame($expected, $anytime->export($chunks)); + } +} diff --git a/tests/unit/GreedyAltAltTest.php b/tests/unit/GreedyAltAltTest.php new file mode 100644 index 0000000..62f22f2 --- /dev/null +++ b/tests/unit/GreedyAltAltTest.php @@ -0,0 +1,77 @@ +setDataset($dataset); + + static::assertSame($expected, $greedy->export($chunks)); + } +} diff --git a/tests/unit/GreedyAltTest.php b/tests/unit/GreedyAltTest.php new file mode 100644 index 0000000..dc209ab --- /dev/null +++ b/tests/unit/GreedyAltTest.php @@ -0,0 +1,77 @@ +setDataset($dataset); + + static::assertSame($expected, $greedy->export($chunks)); + } +} diff --git a/tests/unit/GreedyTest.php b/tests/unit/GreedyTest.php new file mode 100644 index 0000000..45ae263 --- /dev/null +++ b/tests/unit/GreedyTest.php @@ -0,0 +1,83 @@ +setDataset($dataset); + + static::assertSame($expected, $greedy->export($chunks)); + } +} diff --git a/tests/unit/LinearTest.php b/tests/unit/LinearTest.php new file mode 100644 index 0000000..be8f5b9 --- /dev/null +++ b/tests/unit/LinearTest.php @@ -0,0 +1,83 @@ +setDataset($dataset); + + static::assertSame($expected, $greedy->export($chunks)); + } +} diff --git a/tests/unit/Partition/PartitionTest.php b/tests/unit/Partition/PartitionTest.php new file mode 100644 index 0000000..2907270 --- /dev/null +++ b/tests/unit/Partition/PartitionTest.php @@ -0,0 +1,52 @@ +getBounds()); + } + + public function testGetBoundsProvider() + { + yield [ + [4, 5, 6, 7, 8], + [0, 4], + ]; + + yield [ + [8, 5, 6, 7, 4], + [4, 0], + ]; + + yield [ + [5, 4, 6, 8, 7], + [1, 3], + ]; + } +} diff --git a/tests/unit/_bootstrap.php b/tests/unit/_bootstrap.php new file mode 100644 index 0000000..bebc37d --- /dev/null +++ b/tests/unit/_bootstrap.php @@ -0,0 +1,3 @@ + Date: Tue, 7 May 2019 17:56:08 +0200 Subject: [PATCH 10/15] Refactoring. --- src/Algorithm/BruteForce.php | 55 -------- src/Algorithm/BruteForceCustomA.php | 75 ---------- src/Algorithm/Greedy.php | 34 ----- src/Algorithm/Simple.php | 16 --- src/BasePartitionAlgorithm.php | 170 ----------------------- src/Combinations.php | 137 ++++++++++++++++++ src/Contract/Partition.php | 9 ++ src/Contract/PartitionItem.php | 9 ++ src/Contract/PartitionerInterface.php | 44 ++++++ src/Contract/Valuable.php | 13 ++ src/Contract/Weightable.php | 13 ++ src/Greedy.php | 37 +++++ src/GreedyAlt.php | 90 ++++++++++++ src/GreedyAltAlt.php | 72 ++++++++++ src/GreedyAltAltAlt.php | 60 ++++++++ src/Linear.php | 110 +++++++++++++++ src/Partition.php | 167 ---------------------- src/Partition/Partition.php | 75 ++++++++++ src/Partition/PartitionFactory.php | 16 +++ src/Partition/PartitionItem.php | 44 ++++++ src/Partition/PartitionItemFactory.php | 22 +++ src/PartitionAlgorithmInterface.php | 122 ----------------- src/PartitionContainer.php | 160 --------------------- src/PartitionItem.php | 53 ------- src/PartitionItemInterface.php | 28 ---- src/Partitioner.php | 157 +++++++++++++++++++++ src/Partitions/Partitions.php | 76 ++++++++++ src/Partitions/PartitionsFactory.php | 13 ++ src/Subset.php | 183 ------------------------- src/SubsetContainer.php | 109 --------------- src/SubsetItem.php | 54 -------- src/Utils/Statistics.php | 60 ++++++++ 32 files changed, 1057 insertions(+), 1226 deletions(-) delete mode 100644 src/Algorithm/BruteForce.php delete mode 100644 src/Algorithm/BruteForceCustomA.php delete mode 100644 src/Algorithm/Greedy.php delete mode 100644 src/Algorithm/Simple.php delete mode 100644 src/BasePartitionAlgorithm.php create mode 100644 src/Combinations.php create mode 100644 src/Contract/Partition.php create mode 100644 src/Contract/PartitionItem.php create mode 100644 src/Contract/PartitionerInterface.php create mode 100644 src/Contract/Valuable.php create mode 100644 src/Contract/Weightable.php create mode 100644 src/Greedy.php create mode 100644 src/GreedyAlt.php create mode 100644 src/GreedyAltAlt.php create mode 100644 src/GreedyAltAltAlt.php create mode 100644 src/Linear.php delete mode 100644 src/Partition.php create mode 100644 src/Partition/Partition.php create mode 100644 src/Partition/PartitionFactory.php create mode 100644 src/Partition/PartitionItem.php create mode 100644 src/Partition/PartitionItemFactory.php delete mode 100644 src/PartitionAlgorithmInterface.php delete mode 100644 src/PartitionContainer.php delete mode 100644 src/PartitionItem.php delete mode 100644 src/PartitionItemInterface.php create mode 100644 src/Partitioner.php create mode 100644 src/Partitions/Partitions.php create mode 100644 src/Partitions/PartitionsFactory.php delete mode 100644 src/Subset.php delete mode 100644 src/SubsetContainer.php delete mode 100644 src/SubsetItem.php create mode 100644 src/Utils/Statistics.php diff --git a/src/Algorithm/BruteForce.php b/src/Algorithm/BruteForce.php deleted file mode 100644 index 4d72b5c..0000000 --- a/src/Algorithm/BruteForce.php +++ /dev/null @@ -1,55 +0,0 @@ -getDataPartition()->sortByValue('ASC'); - $partitionSize = ($this->getSize() > $this->getDataPartition()->size()) ? - $this->getDataPartition()->size() : - $this->getSize(); - - for ($p = $partitionSize; $p > 1; $p--) { - $best = $this->getDataPartition()->getWeight(); - $target = ($best) / $p; - $goodSubset = array(); - $maxSize = floor($this->getDataPartition()->size() / $p); - - for ($i = 1; $i <= $maxSize; $i++) { - $permutations = new Permutations($this->getDataPartition()->toArray(), $i); - foreach ($permutations->generator() as $subset) { - $x = 0; - foreach ($subset as $item) { - $x += $item->getValue(); - if (abs($x - $target) - abs($best - $target) < 0) { - $best = $x; - $goodSubset = $subset; - } - } - } - } - - $this->getPartitionContainer()->insert($this->getPartition()->addItems($goodSubset)); - $this->getDataPartition()->deleteItems($goodSubset); - } - - $this->getPartitionContainer()->insert($this->getPartition()->addItems($this->getDataPartition()->toArray())); - - return $this->getPartitionContainer()->getPartitionsItemsArray(); - } -} diff --git a/src/Algorithm/BruteForceCustomA.php b/src/Algorithm/BruteForceCustomA.php deleted file mode 100644 index d3f8e03..0000000 --- a/src/Algorithm/BruteForceCustomA.php +++ /dev/null @@ -1,75 +0,0 @@ -getDataPartition()->sortByValue('ASC'); - // Compute the maximum value of the variance. - $variance = pow(2, $this->getDataPartition()->size()); - // Set a default value for the variable that will contain the solution. - $solution = null; - // Get the number of elements in the input dataset. - $count = $this->getDataPartition()->size(); - // Compute the size of a chunk. Ceiling the value because it cannot be zero. - $chunkSize = ceil($count / $this->getSize()); - // Get the rest of the division of the element count by the number of - // partitions. - $rest = $count % $this->getSize(); - - $permutations = new Permutations($this->getDataPartition()->toArray()); - - // Loop through each permutation and - // compute the variance of each subsetchunks. - $i = 0; - foreach ($permutations->generator() as $subset) { - $i++; - // Get the variance of the sums array. - $varianceCandidate = Statistics::variance( - array_map(function ($items) { - $partition = new Partition(); - $partition->addItems($items); - return $partition->getWeight(); - }, array_chunk($subset, $chunkSize)) - ); - - // If we've found a better variance with this subset, store it. - if ($varianceCandidate < $variance) { - $variance = $varianceCandidate; - $solution = $subset; - - // If the variance is equal to the size of the set module the number of - // partition that we want, that means that's the best candidate, - // store the value and exit the loop prematurely. - if ($rest == $varianceCandidate) { - break; - } - } - } - - // Store each chunks into a subset in the SubsetContainer. - foreach (array_chunk($solution, $chunkSize) as $subsetChunks) { - $this->getPartitionContainer()->insert($this->getPartition()->addItems($subsetChunks)); - } - - return $this->getPartitionContainer()->getPartitionsItemsArray(); - } -} diff --git a/src/Algorithm/Greedy.php b/src/Algorithm/Greedy.php deleted file mode 100644 index ef77c92..0000000 --- a/src/Algorithm/Greedy.php +++ /dev/null @@ -1,34 +0,0 @@ -getDataPartition()->sortByValue('DESC'); - return parent::getResult(); - } - - /** - * {@inheritdoc} - */ - public function getPartitionWeight(Partition $partition) - { - return $partition->getWeight(); - } -} diff --git a/src/Algorithm/Simple.php b/src/Algorithm/Simple.php deleted file mode 100644 index 39da01a..0000000 --- a/src/Algorithm/Simple.php +++ /dev/null @@ -1,16 +0,0 @@ -partitionContainer = new PartitionContainer(); - $this->partitionContainer->setAlgo($this); - } - - /** - * {@inheritdoc} - */ - public function setData(array $data = array()) - { - $this->data = $data; - $this->setDataPartition($this->generateDataPartition($this->getData())); - } - - /** - * {@inheritdoc} - */ - public function getData() - { - return $this->data; - } - - /** - * {@inheritdoc} - */ - public function setDataPartition(Partition $partition) - { - $this->dataPartition = $partition; - } - - /** - * {@inheritdoc} - */ - public function getDataPartition() - { - return $this->dataPartition; - } - - /** - * {@inheritdoc} - */ - public function generateDataPartition(array $items = array()) - { - return $this->getPartition()->addItems( - array_map(function ($item) { - if ($item instanceof PartitionItemInterface) { - return $item; - } - return new PartitionItem( - $item, - $this->getItemAccessCallback() - ); - }, $items) - ); - } - - /** - * {@inheritdoc} - */ - public function setSize($size) - { - $this->getPartitionContainer()->setSize($size); - } - - /** - * {@inheritdoc} - */ - public function getSize() - { - return $this->getPartitionContainer()->getSize(); - } - - /** - * {@inheritdoc} - */ - public function setItemAccessCallback(callable $callable = null) - { - $this->itemAccessCallback = $callable; - $this->setData($this->getData()); - } - - /** - * {@inheritdoc} - */ - public function getItemAccessCallback() - { - return $this->itemAccessCallback; - } - - /** - * {@inheritdoc} - */ - public function getPartitionContainer() - { - return $this->partitionContainer; - } - - /** - * {@inheritdoc} - */ - public function getResult() - { - $this->getPartitionContainer()->addItemsToPartition($this->getDataPartition()->toArray()); - - return $this->getPartitionContainer()->getPartitionsItemsArray(); - } - - /** - * {@inheritdoc} - */ - public function getPartitionWeight(Partition $partition) - { - return $partition->size(); - } - - /** - * {@inheritdoc} - */ - public function getPartition() - { - $partition = new Partition(); - $partition->setAlgo($this); - - return $partition; - } -} diff --git a/src/Combinations.php b/src/Combinations.php new file mode 100644 index 0000000..526a0e0 --- /dev/null +++ b/src/Combinations.php @@ -0,0 +1,137 @@ +timeout = $timeout; + } + + /** + * @return int + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * @param int $timeout + * + * @return $this + */ + public function setTimeout(int $timeout) + { + $this->timeout = $timeout; + + return $this; + } + + /** + * {@inheritdoc} + */ + protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void + { + $partition = new Partition($dataset); + $partition->uasort(function ($left, $right) { + return $left->getWeight() <=> $right->getWeight(); + }); + $dataset = $partition->getArrayCopy(); + + for ($p = $chunks; 1 < $p; --$p) { + $partition = new Partition($dataset); + + $best = $partition->getWeight(); + $target = $best / $p; + $maxSize = \floor($partition->count() / $p); + + $goodSubset = $this->getSubsets($maxSize, $dataset, $target, $best); + + // We cannot use array_udiff() to compare objects because we only need + // to remove one object at a time. + foreach ($goodSubset as $item) { + if (false !== $key = \array_search($item, $dataset, true)) { + unset($dataset[$key]); + } + } + + if (!empty($goodSubset)) { + $partitionsArray[] = $goodSubset; + } + } + + $partitionsArray[] = $dataset; + + foreach ($partitionsArray as $key => $subset) { + $partitions->partition($key)->exchangeArray($subset); + } + } + + /** + * @param float $maxSize + * @param array $dataset + * @param float $target + * @param float $best + * + * @return array + */ + private function getSubsets($maxSize, $dataset, $target, $best) + { + $start_time = \time(); + $goodSubsets = $dataset; + $timeout = $this->getTimeout(); + + for ($i = 1; $i <= $maxSize; ++$i) { + $permutations = new CombinationsGenerator($dataset, $i); + foreach ($permutations->generator() as $subset) { + if (\time() - $start_time > $timeout) { + return $goodSubsets; + } + + $x = 0; + foreach ($subset as $item) { + $x += $item->getWeight(); + if (\abs($x - $target) < \abs($best - $target)) { + $best = $x; + $goodSubsets = $subset; + } + } + } + } + + return $goodSubsets; + } +} diff --git a/src/Contract/Partition.php b/src/Contract/Partition.php new file mode 100644 index 0000000..d9174d9 --- /dev/null +++ b/src/Contract/Partition.php @@ -0,0 +1,9 @@ +uasort( + static function (PartitionItem $left, PartitionItem $right) { + return $right->getWeight() <=> $left->getWeight(); + } + ); + + foreach ($dataset as $data) { + $partitions + ->sort() + ->partition(0) + ->append($data); + } + } +} diff --git a/src/GreedyAlt.php b/src/GreedyAlt.php new file mode 100644 index 0000000..9784c6f --- /dev/null +++ b/src/GreedyAlt.php @@ -0,0 +1,90 @@ +count() === $chunks) { + foreach ($dataset as $key => $item) { + $partitions->partition((int) $key)->exchangeArray([$item]); + } + + return; + } + + // Edge case handling. + if (0 === $chunks) { + throw new \InvalidArgumentException('Chunks must be different from 0.'); + } + + // Greedy needs a dataset sorted DESC. + $dataset->uasort( + static function (PartitionItem $left, PartitionItem $right) { + return $right->getWeight() <=> $left->getWeight(); + } + ); + + $best = $dataset->getWeight() / $chunks; + + for ($p = 1; $p < $chunks; ++$p) { + $partition = $partitions->partition($p - 1); + + while ($partition->getWeight() < $best && 1 < $dataset->count()) { + $key = $this->findOptimalItemKey($partition, $dataset, $best); + $partition->append($dataset[$key]); + unset($dataset[$key]); + } + } + + $partitions->partition($p - 1)->exchangeArray($dataset); + } + + /** + * @param \drupol\phpartition\Partition\Partition $partition + * @param \drupol\phpartition\Partition\Partition $dataset + * @param float $best + * + * @return null|false|int|string + */ + private function findOptimalItemKey(Partition $partition, Partition $dataset, $best) + { + // If the current partition is empty, then use the highest value of the + // dataset. + if (0 === $partition->count()) { + // As the dataset is sorted DESC, return the highest value. + return \key($dataset); + } + + // Find which number in the $dataset would be the best fit by checking + // which one is closer to the best solution. + // Best solution is the sum of the dataset values divided by the number + // of chunks we want to have. + $partitionWeightMinusBest = $partition->getWeight() - $best; + + $solutions = \array_map( + static function (PartitionItem $item) use ($partitionWeightMinusBest) { + return \abs($partitionWeightMinusBest + $item->getWeight()); + }, + \iterator_to_array($dataset) + ); + + \asort($solutions); + + return \key($solutions); + } +} diff --git a/src/GreedyAltAlt.php b/src/GreedyAltAlt.php new file mode 100644 index 0000000..f34edd3 --- /dev/null +++ b/src/GreedyAltAlt.php @@ -0,0 +1,72 @@ +count() === $chunks) { + foreach ($dataset as $key => $item) { + $partitions->partition((int) $key)->exchangeArray([$item]); + } + + return; + } + + // Edge case handling. + if (0 === $chunks) { + throw new \InvalidArgumentException('Chunks must be different from 0.'); + } + + // Greedy needs a dataset sorted DESC. + $dataset->uasort( + static function (PartitionItem $left, PartitionItem $right) { + return $left->getWeight() <=> $right->getWeight(); + } + ); + + $partitionsArray = []; + $best = $dataset->getWeight() / $chunks; + + for ($p = 1; $p < $chunks; ++$p) { + $partition = $this->getPartitionFactory()::create(); + + while ($partition->getWeight() < $best && 1 < $dataset->count()) { + $bounds = $dataset->getBounds(); + // Get the key of the item with the lowest value. + $key = $bounds[0]; + + if (0 === $partition->count()) { + // Get the key of the item with the highest value. + $key = $bounds[1]; + } + + $partition->append($dataset[$key]); + unset($dataset[$key]); + $dataset->exchangeArray(\array_values($dataset->getArrayCopy())); + } + + $partitionsArray[] = $partition; + } + + $partitionsArray[] = $dataset; + + foreach ($partitionsArray as $key => $subset) { + $partitions->partition($key)->exchangeArray($subset); + } + } +} diff --git a/src/GreedyAltAltAlt.php b/src/GreedyAltAltAlt.php new file mode 100644 index 0000000..712be92 --- /dev/null +++ b/src/GreedyAltAltAlt.php @@ -0,0 +1,60 @@ +uasort( + static function (PartitionItem $left, PartitionItem $right) { + return $right->getWeight() <=> $left->getWeight(); + } + ); + + $partitionsArray = []; + $best = $dataset->getWeight() / $chunks; + + for ($p = 1; $p < $chunks; ++$p) { + $partition = $this->getPartitionFactory()::create(); + + \reset($dataset); + $key = \key($dataset); + $partition->append($dataset[$key]); + unset($dataset[$key]); + $dataset->exchangeArray(\array_values(\array_reverse($dataset->getArrayCopy()))); + + while ($partition->getWeight() < $best) { + \reset($dataset); + $key = \key($dataset); + $partition->append($dataset[$key]); + unset($dataset[$key]); + } + + $partitionsArray[] = $partition; + + $dataset->exchangeArray(\array_values(\array_reverse($dataset->getArrayCopy()))); + } + + $partitionsArray[] = $dataset; + + foreach ($partitionsArray as $key => $subset) { + $partitions->partition($key)->exchangeArray($subset); + } + } +} diff --git a/src/Linear.php b/src/Linear.php new file mode 100644 index 0000000..9aae12b --- /dev/null +++ b/src/Linear.php @@ -0,0 +1,110 @@ +getArrayCopy(); + + // See https://github.com/technically-php/linear-partitioning for + // original version of this algorithm. + + // An array S of non-negative numbers {s1, ... ,sn} + $s = \array_merge([null], $dataset); // adapt indices here: [0..n-1] => [1..n] + + // Integer K - number of ranges to split items into + $k = $chunks; + $n = \count($dataset); + + // Let D[n,k] be the position of K-th divider + // which produces the minimum possible cost partitioning of N elements to K ranges + $d = []; + + // Let p be the sum of first i elements (cost calculation optimization) + $p = []; + + // 1) Init prefix sums array + // pi = sum of {s1, ..., si} + $p[0] = $this->getPartitionItemFactory()::create(0); + for ($i = 1; $i <= $n; ++$i) { + $p[$i] = $this->getPartitionItemFactory()::create($p[$i - 1]->getWeight() + $s[$i]->getWeight()); + } + + // Let M[n,k] be the minimum possible cost over all partitionings of N elements to K ranges + $m = []; + + // 2) Init boundaries + for ($i = 1; $i <= $n; ++$i) { + // The only possible partitioning of i elements to 1 range is a single all-elements range + // The cost of that partitioning is the sum of those i elements + $m[$i][1] = $p[$i]; // sum of {s1, ..., si} -- optimized using pi + } + + for ($j = 1; $j <= $k; ++$j) { + // The only possible partitioning of 1 element into j ranges is a single one-element range + // The cost of that partitioning is the value of first element + $m[1][$j] = $s[1]; + } + // 3) Main recurrence (fill the rest of values in table M) + for ($i = 2; $i <= $n; ++$i) { + for ($j = 2; $j <= $k; ++$j) { + $solutions = []; + for ($x = 1; ($i - 1) >= $x; ++$x) { + $solutions[] = [ + 0 => $this->getPartitionItemFactory()::create( + \max( + $m[$x][$j - 1]->getWeight(), + $p[$i]->getWeight() - $p[$x]->getWeight() + ) + ), + 1 => $x, + ]; + } + + \usort( + $solutions, + static function (array $x, array $y) { + return $x[0] <=> $y[0]; + } + ); + + $best_solution = $solutions[0]; + $m[$i][$j] = $best_solution[0]; + $d[$i][$j] = $best_solution[1]; + } + } + + // 4) Reconstruct partitioning + $i = $n; + $j = $k; + $partition = []; + while (0 < $j) { + // delimiter position + $dp = $d[$i][$j] ?? 0; + // Add elements after delimiter {sdp, ..., si} to resulting $partition. + $partition[] = \array_slice($s, $dp + 1, $i - $dp); + // Step forward: look for delimiter position for partitioning M[$dp, $j-1] + $i = $dp; + --$j; + } + + foreach ($partition as $i => $p) { + $partitions->partition($i)->exchangeArray($p); + } + } +} diff --git a/src/Partition.php b/src/Partition.php deleted file mode 100644 index 1778b71..0000000 --- a/src/Partition.php +++ /dev/null @@ -1,167 +0,0 @@ -addItems($elements); - } - - /** - * Add an item to the partition. - * - * @param PartitionItem $item - * The item to add to the partition. - * - * @return $this - * Return itself. - */ - public function addItem(PartitionItem $item) - { - $this->set(spl_object_hash($item), $item); - - return $this; - } - - /** - * Add items to the partition. - * - * @param PartitionItem[] $items - * The items to add to the partition. - * - * @return $this - * Return itself. - */ - public function addItems(array $items = array()) - { - foreach ($items as $item) { - $this->addItem($item); - } - - return $this; - } - - /** - * Get an array of original items. - * - * @return array - * The original items. - */ - public function getRawItems() - { - return array_values( - array_map(function ($item) { - return $item->getItem(); - }, $this->toArray()) - ); - } - - /** - * Get the weight of the partition. - * - * @return int - * The partition's weight. - */ - public function getWeight() - { - return array_reduce($this->toArray(), function ($sum, $item) { - $sum += $item->getValue(); - return $sum; - }); - } - - /** - * Set the algorithm to use. - * - * @param BasePartitionAlgorithm $algo - * The algorithm to use. - */ - public function setAlgo(BasePartitionAlgorithm $algo) - { - $this->algo = $algo; - } - - /** - * Get the algorithm in use. - * - * @return PartitionAlgorithmInterface - * The algorithm in use. - */ - public function getAlgo() - { - return $this->algo; - } - - /** - * Delete items from the partition. - * - * @param PartitionItem[] $items - * The items to delete. - */ - public function deleteItems(array $items = array()) - { - foreach ($items as $item) { - $this->delete($item); - } - } - - /** - * Delete an item from the partition. - * - * @param PartitionItem $item - * The item to delete. - */ - public function delete(PartitionItem $item) - { - $this->remove(spl_object_hash($item)); - } - - /** - * Sort the items of the partition in a particular order. - * - * @param string $order - * ASC for ascending, DESC for descending. - * - * @return $this - */ - public function sortByValue($order = 'ASC') - { - if ('ASC' == $order) { - $this->sort(function ($itemA, $itemB) { - return $itemA->getValue() > $itemB->getValue(); - }); - } - - if ('DESC' == $order) { - $this->sort(function ($itemA, $itemB) { - return $itemA->getValue() < $itemB->getValue(); - }); - } - - return $this; - } -} diff --git a/src/Partition/Partition.php b/src/Partition/Partition.php new file mode 100644 index 0000000..4c98aea --- /dev/null +++ b/src/Partition/Partition.php @@ -0,0 +1,75 @@ +getValue(); + }, + $this->getArrayCopy() + ); + } + + /** + * {@inheritdoc} + */ + public function getBounds(): array + { + $max = PHP_INT_MAX; + $min = PHP_INT_MIN; + + $bounds = []; + + foreach ($this as $index => $item) { + if ($item instanceof PartitionItemInterface) { + $item = $item->getWeight(); + } + + if ($item < $max) { + $max = $item; + $bounds[0] = $index; + } + + if ($item > $min) { + $min = $item; + $bounds[1] = $index; + } + } + + return $bounds; + } + + /** + * {@inheritdoc} + */ + public function getWeight(): float + { + $weight = 0; + + foreach ($this as $item) { + if ($item instanceof PartitionItemInterface) { + $item = $item->getWeight(); + } + + $weight += $item; + } + + return $weight; + } +} diff --git a/src/Partition/PartitionFactory.php b/src/Partition/PartitionFactory.php new file mode 100644 index 0000000..febdc68 --- /dev/null +++ b/src/Partition/PartitionFactory.php @@ -0,0 +1,16 @@ +value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritdoc} + */ + public function getWeight(): float + { + return $this->value; + } +} diff --git a/src/Partition/PartitionItemFactory.php b/src/Partition/PartitionItemFactory.php new file mode 100644 index 0000000..70fe623 --- /dev/null +++ b/src/Partition/PartitionItemFactory.php @@ -0,0 +1,22 @@ +getAlgo()->getPartitionWeight($partitionA); - $partitionBWeight = $partitionB->getAlgo()->getPartitionWeight($partitionB); - - if ($partitionAWeight == $partitionBWeight) { - return 0; - } - - return ($partitionAWeight > $partitionBWeight) ? -1 : +1; - } - - /** - * Set the size of the container. - * - * @param int $size - * The size. - */ - public function setSize($size) - { - $this->size = $size; - - for ($i = 0; $i < $size; $i++) { - $subset = new Partition(); - $subset->setAlgo($this->getAlgo()); - $this->insert($subset); - } - } - - /** - * Return the size of the container. - * - * @return int - * The number of partitions the container has. - */ - public function getSize() - { - return $this->size; - } - - /** - * Add items to the partition. - * - * @param PartitionItem[] $items - * The items to add. - */ - public function addItemsToPartition(array $items = array()) - { - foreach ($items as $item) { - $this->addItemToPartition($item); - } - } - - /** - * Add an item to the first partition. - * - * @param \drupol\phpartition\PartitionItem $item - * The item to add. - */ - public function addItemToPartition(PartitionItem $item) - { - $this->top(); - $subset = $this->extract(); - $subset->addItem($item); - $this->insert($subset); - } - - /** - * Get the partition in the container. - * - * @return Partition[] - * An array of partitions. - */ - public function getPartitions() - { - $data = array(); - $clone = clone $this; - - for ($clone->top(); $clone->valid(); $clone->next()) { - $data[] = $clone->current(); - } - - return $data; - } - - /** - * Return the items from each partitions in the container. - * - * @return mixed[] - * The items. - */ - public function getPartitionsItemsArray() - { - $data = array(); - - foreach ($this->getPartitions() as $subset) { - $data[] = $subset->getRawItems(); - } - - return array_values(array_filter($data)); - } - - /** - * Set the algorithm to use. - * - * @param \drupol\phpartition\BasePartitionAlgorithm $algo - * The algorithm. - */ - public function setAlgo(BasePartitionAlgorithm $algo) - { - $this->algo = $algo; - } - - /** - * Get the algorithm. - * - * @return BasePartitionAlgorithm - * The algorithm. - */ - public function getAlgo() - { - return $this->algo; - } -} diff --git a/src/PartitionItem.php b/src/PartitionItem.php deleted file mode 100644 index d6ecfbd..0000000 --- a/src/PartitionItem.php +++ /dev/null @@ -1,53 +0,0 @@ -item = $item; - $this->valueOrCallable = $valueOrCallable; - } - - /** - * {@inheritdoc} - */ - public function getValue() - { - if (is_callable($this->valueOrCallable)) { - return call_user_func($this->valueOrCallable, $this->item); - } - - if (!is_null($this->valueOrCallable)) { - return $this->valueOrCallable; - } - - return is_numeric($this->item) ? $this->item : 0; - } - - /** - * {@inheritdoc} - */ - public function getItem() - { - return $this->item; - } -} diff --git a/src/PartitionItemInterface.php b/src/PartitionItemInterface.php deleted file mode 100644 index 4d960c6..0000000 --- a/src/PartitionItemInterface.php +++ /dev/null @@ -1,28 +0,0 @@ -exportArrayCopy()); + }, + $this + ->run($chunks) + ->getLastRun() + ->partitions() + ); + } + + /** + * {@inheritdoc} + */ + public function getDataset() + { + return $this->dataset; + } + + /** + * @return \drupol\phpartition\Partition\Partition + */ + final public function getDatasetPartition() + { + $partition = $this->getPartitionFactory()::create(); + + foreach ($this->getDataset() as $data) { + $partition->append($this->toPartitionItem($data)); + } + + return $partition; + } + + /** + * @return \drupol\phpartition\Partitions\Partitions + */ + final public function getLastRun() + { + return $this->lastRun; + } + + /** + * @return \drupol\phpartition\Partition\PartitionFactory + */ + public function getPartitionFactory() + { + return $this->partitionFactory ?? new PartitionFactory(); + } + + /** + * @return \drupol\phpartition\Partition\PartitionItemFactory + */ + public function getPartitionItemFactory() + { + return $this->partitionItemFactory ?? new PartitionItemFactory(); + } + + /** + * @return \drupol\phpartition\Partitions\PartitionsFactory + */ + public function getPartitionsFactory() + { + return $this->partitionsFactory ?? new PartitionsFactory(); + } + + /** + * {@inheritdoc} + */ + final public function run(int $chunks = 1) + { + $partitions = $this->getPartitionsFactory()::create($chunks); + + $dataPartition = $this->getDatasetPartition(); + + $this->fillPartitions($partitions, $dataPartition, $chunks); + + $this->lastRun = $partitions; + + return $this; + } + + /** + * {@inheritdoc} + */ + final public function setDataset(iterable $dataset) + { + $this->dataset = $dataset; + + return $this; + } + + /** + * @param mixed $originalItem + * + * @return \drupol\phpartition\Contract\Weightable + */ + public function toPartitionItem($originalItem): Weightable + { + return $this->getPartitionItemFactory()::create($originalItem); + } + + /** + * @param \drupol\phpartition\Partitions\Partitions $partitions + * @param Partition $dataset + * @param int $chunks + */ + abstract protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void; +} diff --git a/src/Partitions/Partitions.php b/src/Partitions/Partitions.php new file mode 100644 index 0000000..6335917 --- /dev/null +++ b/src/Partitions/Partitions.php @@ -0,0 +1,76 @@ +storage['size'] = $size; + $partitionFactory = $partitionFactory ?? new PartitionFactory(); + + for ($i = 0; $i < $size; ++$i) { + $this->storage['partitions'][$i] = $partitionFactory::create(); + } + } + + /** + * {@inheritdoc} + */ + public function count() + { + return \count($this->storage['partitions']); + } + + /** + * @param int $index + * + * @return \drupol\phpartition\Partition\Partition + */ + public function partition(int $index) + { + return $this->storage['partitions'][$index]; + } + + /** + * @return \drupol\phpartition\Partition\Partition[] + */ + public function partitions() + { + return $this->storage['partitions']; + } + + /** + * @param null|callable $compareCallable + * + * @return \drupol\phpartition\Partitions\Partitions + */ + public function sort(callable $compareCallable = null) + { + if (null === $compareCallable) { + $compareCallable = static function (Partition $item1, Partition $item2) { + return $item1->getWeight() <=> $item2->getWeight(); + }; + } + + \usort($this->storage['partitions'], $compareCallable); + + return $this; + } +} diff --git a/src/Partitions/PartitionsFactory.php b/src/Partitions/PartitionsFactory.php new file mode 100644 index 0000000..3a5d83a --- /dev/null +++ b/src/Partitions/PartitionsFactory.php @@ -0,0 +1,13 @@ +getItems()); - } - - /** - * @param \drupol\phpartition\SubsetItem $item - */ - public function addItem(SubsetItem $item) - { - $this->items[$item->getKey()] = $item; - } - - /** - * @param SubsetItem[] $items - */ - public function addItems(array $items = array()) - { - foreach ($items as $item) { - $this->addItem($item); - } - } - - /** - * @return SubsetItem[] - */ - public function getItems() - { - return (array) $this->items; - } - - /** - * @param SubsetItem[] $items - */ - public function setItems(array $items = array()) - { - $this->items = $items; - } - - /** - * @return array - */ - public function getItemsValues() - { - $data = array(); - - foreach ($this->getItems() as $item) { - $data[$item->getKey()] = $item->getValue(); - } - - return $data; - } - - /** - * @return array - */ - public function getRawItems() - { - $data = array(); - - foreach ($this->getItems() as $item) { - $data[] = $item->getItem(); - } - - return $data; - } - - /** - * @return int - */ - public function getWeight() - { - $sum = 0; - - foreach ((array) $this->items as $item) { - $sum += $item->getValue(); - } - - return $sum; - } - - /** - * @param \drupol\phpartition\BasePartitionAlgorithm $algo - */ - public function setAlgo(BasePartitionAlgorithm $algo) - { - $this->algo = $algo; - } - - /** - * @return BasePartitionAlgorithm - */ - public function getAlgo() - { - return $this->algo; - } - - /** - * Clear the subset. - */ - public function clear() - { - $this->setItems(); - } - - /** - * @param $key - * - * @return \drupol\phpartition\SubsetItem - */ - public function getItem($key) - { - return $this->items[$key]; - } - - /** - * @param SubsetItem[] $items - */ - public function deleteItems(array $items = array()) - { - foreach ($items as $item) { - $this->delete($item); - } - } - - /** - * @param \drupol\phpartition\SubsetItem $item - */ - public function delete(SubsetItem $item) - { - foreach ($this->items as $key => $value) { - if ($value->getKey() == $item->getKey()) { - unset($this->items[$key]); - } - } - } - - /** - * @param string $order - * - * @return $this - */ - public function sortByValue($order = 'ASC') - { - $data = $this->getItems(); - - if ('ASC' == $order) { - usort($data, function ($itemA, $itemB) { - return $itemA->getValue() > $itemB->getValue(); - }); - } - - if ('DESC' == $order) { - usort($data, function ($itemA, $itemB) { - return $itemA->getValue() < $itemB->getValue(); - }); - } - - $this->setItems($data); - - return $this; - } -} diff --git a/src/SubsetContainer.php b/src/SubsetContainer.php deleted file mode 100644 index 84ed753..0000000 --- a/src/SubsetContainer.php +++ /dev/null @@ -1,109 +0,0 @@ -getAlgo()->getSubsetWeight($a); - $bl = $b->getAlgo()->getSubsetWeight($b); - - if ($al == $bl) { - return 0; - } - return ($al > $bl) ? -1 : +1; - } - - /** - * @param $partition - */ - public function setPartition($partition) - { - for ($i=0; $i<$partition; $i++) { - $subset = new Subset(); - $subset->setAlgo($this->getAlgo()); - $this->insert($subset); - } - } - - /** - * @param SubsetItem[] $items - */ - public function addItemsToSubset(array $items = array()) - { - foreach ($items as $item) { - $this->addItemToSubset($item); - } - } - - /** - * @param \drupol\phpartition\SubsetItem $item - */ - public function addItemToSubset(SubsetItem $item) - { - $this->top(); - $subset = $this->extract(); - $subset->addItem($item); - $this->insert($subset); - } - - /** - * @return Subset[] - */ - public function getSubsets() - { - $data = array(); - $clone = clone $this; - - for ($clone->top(); $clone->valid(); $clone->next()) { - $data[] = $clone->current(); - } - - return $data; - } - - /** - * @return array - */ - public function getSubsetsAndItemsAsArray() - { - $data = array(); - - foreach ($this->getSubsets() as $subset) { - $data[] = $subset->getRawItems(); - } - - return array_values(array_filter($data)); - } - - /** - * @param \drupol\phpartition\BasePartitionAlgorithm $algo - */ - public function setAlgo(BasePartitionAlgorithm $algo) - { - $this->algo = $algo; - } - - /** - * @return BasePartitionAlgorithm - */ - public function getAlgo() - { - return $this->algo; - } -} diff --git a/src/SubsetItem.php b/src/SubsetItem.php deleted file mode 100644 index 19b8ed8..0000000 --- a/src/SubsetItem.php +++ /dev/null @@ -1,54 +0,0 @@ -key = $key; - $this->value = $value; - $this->item = $item; - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - /** - * @return mixed - */ - public function getKey() - { - return $this->key; - } - - /** - * @return mixed - */ - public function getItem() - { - return $this->item; - } -} diff --git a/src/Utils/Statistics.php b/src/Utils/Statistics.php new file mode 100644 index 0000000..fa6356d --- /dev/null +++ b/src/Utils/Statistics.php @@ -0,0 +1,60 @@ +getWeight() / $partition->count(); + } + /** + * @param \drupol\phpartition\Partition\Partition $partition + * + * @return float + */ + public static function standardDeviationPartition(Partition $partition) + { + $mean = self::meanPartition($partition); + + $sumSquareDiff = \array_sum(\array_map( + static function ($sum) use ($mean) { + return ($sum - $mean) ** 2; + }, + $partition->getArrayCopy() + )); + + return ($sumSquareDiff / $partition->count()) ** .5; + } + /** + * @param \drupol\phpartition\Partitions\Partitions $partitions + * + * @return float + */ + public static function standardDeviationPartitions(Partitions $partitions) + { + $partition = new Partition( + \array_map( + static function (Partition $partition) { + return $partition->getWeight(); + }, + $partitions->partitions() + ) + ); + + return self::standardDeviationPartition($partition); + } +} From d911d19ad20fb66ae1e92b182f310b52615787bb Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 5 Jun 2019 16:25:15 +0200 Subject: [PATCH 11/15] Update .gitignore file. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index baaec8b..2dc8d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ fabric.properties /test.php /test2.php /tests/_output/ +/CHANGELOG.md +/taskman.yml From f9149dd64da4bea44d562f5650241ba13612afa7 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 9 Jul 2019 20:09:37 +0200 Subject: [PATCH 12/15] Add Github files. --- .github/CODEOWNERS | 1 + .github/CODE_OF_CONDUCT.md | 46 ++++++++++++++++++++++++++++++++ .github/CONTRIBUTING.md | 29 ++++++++++++++++++++ .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE.md | 13 +++++++++ .github/PULL_REQUEST_TEMPLATE.md | 9 +++++++ .github/stale.yml | 10 +++++++ 7 files changed, 109 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/stale.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..529c034 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @drupol diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d9159b2 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at am@localheinz.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..5ebc01b --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# CONTRIBUTING + +We're using [Travis CI](https://travis-ci.com) as a continuous integration system. + +For details, see [`.travis.yml`](../.travis.yml). + +## Tests + +We're using [`grumphp/grumphp`](https://github.com/phpro/grumphp) to drive the development. + +Run + +```bash +./vendor/bin/grumphp run +``` + +to run all the tests. + +## Coding Standards + +We are using [`drupol/php-conventions`](https://github.com/drupol/php-conventions) to enforce coding standards. + +Run + +```bash +./vendor/bin/grumphp run +``` + +to automatically detect/fix coding standard violations. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b4c89d9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: drupol diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..3421bc3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +## Steps required to reproduce the problem + +1. +2. +3. + +## Expected Result + +* + +## Actual Result + +* diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..e448c72 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +This PR + +* [x] +* [ ] +* [ ] + +Follows #. +Related to #. +Fixes #. diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..8eb151c --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,10 @@ +daysUntilStale: 60 + +daysUntilClose: 7 + +staleLabel: stale + +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. From 152c1c57ded4899ef52fb984dfdd5b616e9b8ddc Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 24 Jul 2019 08:29:07 +0200 Subject: [PATCH 13/15] Update composer.json and use drupol/php-conventions ^1.5. --- composer.json | 5 ++++- grumphp.yml.dist | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 5d95fa5..72d444b 100644 --- a/composer.json +++ b/composer.json @@ -20,9 +20,12 @@ "role": "Author" } ], + "require": { + "php": ">= 7.1.3" + }, "require-dev": { "codeception/codeception": "^3", - "drupol/php-conventions": "^1.3", + "drupol/php-conventions": "^1.5", "drupol/phpermutations": "^1.3" }, "autoload": { diff --git a/grumphp.yml.dist b/grumphp.yml.dist index 95c7e14..60e8038 100644 --- a/grumphp.yml.dist +++ b/grumphp.yml.dist @@ -1,5 +1,5 @@ imports: - - { resource: vendor/drupol/php-conventions/config/php7/grumphp.yml } + - { resource: vendor/drupol/php-conventions/config/php71/grumphp.yml } parameters: tasks.phpstan.ignore_patterns: @@ -10,5 +10,5 @@ parameters: - vendor/ - spec/ - tests/ - extensions: - - drupol\PhpConventions\GrumphpTasksExtension + extra_tasks: + codeception: ~ From d29161a4b4cb9592b32f2a30a5e58a8586bb4bb7 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 24 Jul 2019 08:29:23 +0200 Subject: [PATCH 14/15] Auto-fix code style. --- src/Combinations.php | 19 +++++++++++-------- src/GreedyAlt.php | 12 ++++++------ src/GreedyAltAlt.php | 2 +- src/GreedyAltAltAlt.php | 12 ++++++------ src/Linear.php | 9 ++++++--- src/Partition/Partition.php | 6 +++--- src/Partitioner.php | 4 ++-- src/Partitions/Partitions.php | 2 +- src/Utils/Statistics.php | 6 ++++-- .../_support/_generated/UnitTesterActions.php | 1 + tests/unit/CombinationsTest.php | 2 +- tests/unit/GreedyAltAltTest.php | 2 +- tests/unit/GreedyAltTest.php | 2 +- tests/unit/GreedyTest.php | 2 +- tests/unit/LinearTest.php | 2 +- tests/unit/Partition/PartitionTest.php | 2 +- 16 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/Combinations.php b/src/Combinations.php index 526a0e0..32596d3 100644 --- a/src/Combinations.php +++ b/src/Combinations.php @@ -27,9 +27,9 @@ class Combinations extends Partitioner */ public function __construct(int $timeout = 10) { - if (!\class_exists(CombinationsGenerator::class)) { + if (!class_exists(CombinationsGenerator::class)) { throw new \Exception( - \sprintf( + sprintf( 'The class %s is not available, run "%s" to get it.', '\drupol\phpermutations\Generators\Combinations', 'composer require drupol/phpermutations' @@ -66,7 +66,7 @@ public function setTimeout(int $timeout) protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void { $partition = new Partition($dataset); - $partition->uasort(function ($left, $right) { + $partition->uasort(static function ($left, $right) { return $left->getWeight() <=> $right->getWeight(); }); $dataset = $partition->getArrayCopy(); @@ -76,14 +76,14 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in $best = $partition->getWeight(); $target = $best / $p; - $maxSize = \floor($partition->count() / $p); + $maxSize = floor($partition->count() / $p); $goodSubset = $this->getSubsets($maxSize, $dataset, $target, $best); // We cannot use array_udiff() to compare objects because we only need // to remove one object at a time. foreach ($goodSubset as $item) { - if (false !== $key = \array_search($item, $dataset, true)) { + if (false !== $key = array_search($item, $dataset, true)) { unset($dataset[$key]); } } @@ -110,21 +110,24 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in */ private function getSubsets($maxSize, $dataset, $target, $best) { - $start_time = \time(); + $start_time = time(); $goodSubsets = $dataset; $timeout = $this->getTimeout(); for ($i = 1; $i <= $maxSize; ++$i) { $permutations = new CombinationsGenerator($dataset, $i); + foreach ($permutations->generator() as $subset) { - if (\time() - $start_time > $timeout) { + if (time() - $start_time > $timeout) { return $goodSubsets; } $x = 0; + foreach ($subset as $item) { $x += $item->getWeight(); - if (\abs($x - $target) < \abs($best - $target)) { + + if (abs($x - $target) < abs($best - $target)) { $best = $x; $goodSubsets = $subset; } diff --git a/src/GreedyAlt.php b/src/GreedyAlt.php index 9784c6f..e8c4377 100644 --- a/src/GreedyAlt.php +++ b/src/GreedyAlt.php @@ -67,7 +67,7 @@ private function findOptimalItemKey(Partition $partition, Partition $dataset, $b // dataset. if (0 === $partition->count()) { // As the dataset is sorted DESC, return the highest value. - return \key($dataset); + return key($dataset); } // Find which number in the $dataset would be the best fit by checking @@ -76,15 +76,15 @@ private function findOptimalItemKey(Partition $partition, Partition $dataset, $b // of chunks we want to have. $partitionWeightMinusBest = $partition->getWeight() - $best; - $solutions = \array_map( + $solutions = array_map( static function (PartitionItem $item) use ($partitionWeightMinusBest) { - return \abs($partitionWeightMinusBest + $item->getWeight()); + return abs($partitionWeightMinusBest + $item->getWeight()); }, - \iterator_to_array($dataset) + iterator_to_array($dataset) ); - \asort($solutions); + asort($solutions); - return \key($solutions); + return key($solutions); } } diff --git a/src/GreedyAltAlt.php b/src/GreedyAltAlt.php index f34edd3..4f6f174 100644 --- a/src/GreedyAltAlt.php +++ b/src/GreedyAltAlt.php @@ -57,7 +57,7 @@ static function (PartitionItem $left, PartitionItem $right) { $partition->append($dataset[$key]); unset($dataset[$key]); - $dataset->exchangeArray(\array_values($dataset->getArrayCopy())); + $dataset->exchangeArray(array_values($dataset->getArrayCopy())); } $partitionsArray[] = $partition; diff --git a/src/GreedyAltAltAlt.php b/src/GreedyAltAltAlt.php index 712be92..0ccd0c8 100644 --- a/src/GreedyAltAltAlt.php +++ b/src/GreedyAltAltAlt.php @@ -33,22 +33,22 @@ static function (PartitionItem $left, PartitionItem $right) { for ($p = 1; $p < $chunks; ++$p) { $partition = $this->getPartitionFactory()::create(); - \reset($dataset); - $key = \key($dataset); + reset($dataset); + $key = key($dataset); $partition->append($dataset[$key]); unset($dataset[$key]); - $dataset->exchangeArray(\array_values(\array_reverse($dataset->getArrayCopy()))); + $dataset->exchangeArray(array_values(array_reverse($dataset->getArrayCopy()))); while ($partition->getWeight() < $best) { - \reset($dataset); - $key = \key($dataset); + reset($dataset); + $key = key($dataset); $partition->append($dataset[$key]); unset($dataset[$key]); } $partitionsArray[] = $partition; - $dataset->exchangeArray(\array_values(\array_reverse($dataset->getArrayCopy()))); + $dataset->exchangeArray(array_values(array_reverse($dataset->getArrayCopy()))); } $partitionsArray[] = $dataset; diff --git a/src/Linear.php b/src/Linear.php index 9aae12b..aaf7335 100644 --- a/src/Linear.php +++ b/src/Linear.php @@ -25,7 +25,7 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in // original version of this algorithm. // An array S of non-negative numbers {s1, ... ,sn} - $s = \array_merge([null], $dataset); // adapt indices here: [0..n-1] => [1..n] + $s = array_merge([null], $dataset); // adapt indices here: [0..n-1] => [1..n] // Integer K - number of ranges to split items into $k = $chunks; @@ -41,6 +41,7 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in // 1) Init prefix sums array // pi = sum of {s1, ..., si} $p[0] = $this->getPartitionItemFactory()::create(0); + for ($i = 1; $i <= $n; ++$i) { $p[$i] = $this->getPartitionItemFactory()::create($p[$i - 1]->getWeight() + $s[$i]->getWeight()); } @@ -64,10 +65,11 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in for ($i = 2; $i <= $n; ++$i) { for ($j = 2; $j <= $k; ++$j) { $solutions = []; + for ($x = 1; ($i - 1) >= $x; ++$x) { $solutions[] = [ 0 => $this->getPartitionItemFactory()::create( - \max( + max( $m[$x][$j - 1]->getWeight(), $p[$i]->getWeight() - $p[$x]->getWeight() ) @@ -76,7 +78,7 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in ]; } - \usort( + usort( $solutions, static function (array $x, array $y) { return $x[0] <=> $y[0]; @@ -93,6 +95,7 @@ static function (array $x, array $y) { $i = $n; $j = $k; $partition = []; + while (0 < $j) { // delimiter position $dp = $d[$i][$j] ?? 0; diff --git a/src/Partition/Partition.php b/src/Partition/Partition.php index 4c98aea..b220406 100644 --- a/src/Partition/Partition.php +++ b/src/Partition/Partition.php @@ -4,9 +4,9 @@ namespace drupol\phpartition\Partition; -use drupol\phpartition\Contract\Valuable; -use drupol\phpartition\Contract\PartitionItem as PartitionItemInterface; use drupol\phpartition\Contract\Partition as PartitionInterface; +use drupol\phpartition\Contract\PartitionItem as PartitionItemInterface; +use drupol\phpartition\Contract\Valuable; /** * Class Partition. @@ -18,7 +18,7 @@ class Partition extends \ArrayObject implements PartitionInterface */ public function exportArrayCopy() { - return \array_map( + return array_map( static function (Valuable $item) { return $item->getValue(); }, diff --git a/src/Partitioner.php b/src/Partitioner.php index b2b79ae..d63939b 100644 --- a/src/Partitioner.php +++ b/src/Partitioner.php @@ -47,9 +47,9 @@ abstract class Partitioner implements PartitionerInterface */ final public function export(int $chunks = 1) { - return \array_map( + return array_map( static function (Partition $partition) { - return \array_values($partition->exportArrayCopy()); + return array_values($partition->exportArrayCopy()); }, $this ->run($chunks) diff --git a/src/Partitions/Partitions.php b/src/Partitions/Partitions.php index 6335917..77d402f 100644 --- a/src/Partitions/Partitions.php +++ b/src/Partitions/Partitions.php @@ -69,7 +69,7 @@ public function sort(callable $compareCallable = null) }; } - \usort($this->storage['partitions'], $compareCallable); + usort($this->storage['partitions'], $compareCallable); return $this; } diff --git a/src/Utils/Statistics.php b/src/Utils/Statistics.php index fa6356d..021e8f3 100644 --- a/src/Utils/Statistics.php +++ b/src/Utils/Statistics.php @@ -21,6 +21,7 @@ public static function meanPartition(Partition $partition) { return $partition->getWeight() / $partition->count(); } + /** * @param \drupol\phpartition\Partition\Partition $partition * @@ -30,7 +31,7 @@ public static function standardDeviationPartition(Partition $partition) { $mean = self::meanPartition($partition); - $sumSquareDiff = \array_sum(\array_map( + $sumSquareDiff = array_sum(array_map( static function ($sum) use ($mean) { return ($sum - $mean) ** 2; }, @@ -39,6 +40,7 @@ static function ($sum) use ($mean) { return ($sumSquareDiff / $partition->count()) ** .5; } + /** * @param \drupol\phpartition\Partitions\Partitions $partitions * @@ -47,7 +49,7 @@ static function ($sum) use ($mean) { public static function standardDeviationPartitions(Partitions $partitions) { $partition = new Partition( - \array_map( + array_map( static function (Partition $partition) { return $partition->getWeight(); }, diff --git a/tests/_support/_generated/UnitTesterActions.php b/tests/_support/_generated/UnitTesterActions.php index 4a8a13e..56a62f3 100644 --- a/tests/_support/_generated/UnitTesterActions.php +++ b/tests/_support/_generated/UnitTesterActions.php @@ -913,6 +913,7 @@ public function fail($message) { return $this->getScenario()->runStep(new \Codeception\Step\Action('fail', \func_get_args())); } + /** * @return \Codeception\Scenario */ diff --git a/tests/unit/CombinationsTest.php b/tests/unit/CombinationsTest.php index cd4b9fe..4ce028b 100644 --- a/tests/unit/CombinationsTest.php +++ b/tests/unit/CombinationsTest.php @@ -72,7 +72,7 @@ public function combinationsTestProvider() * * @dataProvider combinationsTestProvider */ - public function testCombinations($dataset, $chunks, $expected) + public function testCombinations($dataset, $chunks, $expected): void { $anytime = new Combinations(); diff --git a/tests/unit/GreedyAltAltTest.php b/tests/unit/GreedyAltAltTest.php index 62f22f2..d7e7aa4 100644 --- a/tests/unit/GreedyAltAltTest.php +++ b/tests/unit/GreedyAltAltTest.php @@ -66,7 +66,7 @@ public function greedyAltAltTestProvider() * * @dataProvider greedyAltAltTestProvider */ - public function testGreedyAltAlt($dataset, $chunks, $expected) + public function testGreedyAltAlt($dataset, $chunks, $expected): void { $greedy = new GreedyAltAlt(); diff --git a/tests/unit/GreedyAltTest.php b/tests/unit/GreedyAltTest.php index dc209ab..091c243 100644 --- a/tests/unit/GreedyAltTest.php +++ b/tests/unit/GreedyAltTest.php @@ -66,7 +66,7 @@ public function greedyAltTestProvider() * * @dataProvider greedyAltTestProvider */ - public function testGreedyAlt($dataset, $chunks, $expected) + public function testGreedyAlt($dataset, $chunks, $expected): void { $greedy = new GreedyAlt(); diff --git a/tests/unit/GreedyTest.php b/tests/unit/GreedyTest.php index 45ae263..4297e42 100644 --- a/tests/unit/GreedyTest.php +++ b/tests/unit/GreedyTest.php @@ -72,7 +72,7 @@ public function greedyTestProvider() * * @dataProvider greedyTestProvider */ - public function testGreedy($dataset, $chunks, $expected) + public function testGreedy($dataset, $chunks, $expected): void { $greedy = new Greedy(); diff --git a/tests/unit/LinearTest.php b/tests/unit/LinearTest.php index be8f5b9..590dd67 100644 --- a/tests/unit/LinearTest.php +++ b/tests/unit/LinearTest.php @@ -72,7 +72,7 @@ public function linearTestProvider() * * @dataProvider linearTestProvider */ - public function testLinear($dataset, $chunks, $expected) + public function testLinear($dataset, $chunks, $expected): void { $greedy = new Linear(); diff --git a/tests/unit/Partition/PartitionTest.php b/tests/unit/Partition/PartitionTest.php index 2907270..1ba3c88 100644 --- a/tests/unit/Partition/PartitionTest.php +++ b/tests/unit/Partition/PartitionTest.php @@ -25,7 +25,7 @@ final class PartitionTest extends Unit * @param mixed $input * @param mixed $expected */ - public function testGetBounds($input, $expected) + public function testGetBounds($input, $expected): void { $partition = new Partition($input); From 0a97084b41af596ebb3e283b3737655fdc97197b Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 11 Sep 2019 11:14:00 +0200 Subject: [PATCH 15/15] Update CS. --- src/Combinations.php | 16 ++++++++-------- src/Contract/Partition.php | 2 +- src/Contract/PartitionItem.php | 2 +- src/Contract/PartitionerInterface.php | 2 +- src/Contract/Valuable.php | 2 +- src/Contract/Weightable.php | 2 +- src/Greedy.php | 2 +- src/GreedyAlt.php | 14 +++++++------- src/GreedyAltAlt.php | 4 ++-- src/GreedyAltAltAlt.php | 14 +++++++------- src/Linear.php | 8 ++++---- src/Partition/Partition.php | 8 ++++---- src/Partition/PartitionFactory.php | 2 +- src/Partition/PartitionItem.php | 2 +- src/Partition/PartitionItemFactory.php | 2 +- src/Partitioner.php | 6 +++--- src/Partitions/Partitions.php | 4 ++-- src/Partitions/PartitionsFactory.php | 2 +- src/Utils/Statistics.php | 6 +++--- tests/_support/Helper/Unit.php | 2 +- tests/_support/UnitTester.php | 2 +- tests/_support/_generated/UnitTesterActions.php | 2 +- tests/unit/CombinationsTest.php | 4 ++-- tests/unit/GreedyAltAltTest.php | 4 ++-- tests/unit/GreedyAltTest.php | 4 ++-- tests/unit/GreedyTest.php | 4 ++-- tests/unit/LinearTest.php | 4 ++-- tests/unit/Partition/PartitionTest.php | 4 ++-- tests/unit/_bootstrap.php | 2 +- 29 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/Combinations.php b/src/Combinations.php index 32596d3..84c88d9 100644 --- a/src/Combinations.php +++ b/src/Combinations.php @@ -1,6 +1,6 @@ getWeight(); $target = $best / $p; - $maxSize = floor($partition->count() / $p); + $maxSize = \floor($partition->count() / $p); $goodSubset = $this->getSubsets($maxSize, $dataset, $target, $best); // We cannot use array_udiff() to compare objects because we only need // to remove one object at a time. foreach ($goodSubset as $item) { - if (false !== $key = array_search($item, $dataset, true)) { + if (false !== $key = \array_search($item, $dataset, true)) { unset($dataset[$key]); } } @@ -110,7 +110,7 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in */ private function getSubsets($maxSize, $dataset, $target, $best) { - $start_time = time(); + $start_time = \time(); $goodSubsets = $dataset; $timeout = $this->getTimeout(); @@ -118,7 +118,7 @@ private function getSubsets($maxSize, $dataset, $target, $best) $permutations = new CombinationsGenerator($dataset, $i); foreach ($permutations->generator() as $subset) { - if (time() - $start_time > $timeout) { + if (\time() - $start_time > $timeout) { return $goodSubsets; } @@ -127,7 +127,7 @@ private function getSubsets($maxSize, $dataset, $target, $best) foreach ($subset as $item) { $x += $item->getWeight(); - if (abs($x - $target) < abs($best - $target)) { + if (\abs($x - $target) < \abs($best - $target)) { $best = $x; $goodSubsets = $subset; } diff --git a/src/Contract/Partition.php b/src/Contract/Partition.php index d9174d9..79549ab 100644 --- a/src/Contract/Partition.php +++ b/src/Contract/Partition.php @@ -1,6 +1,6 @@ count()) { // As the dataset is sorted DESC, return the highest value. - return key($dataset); + return \key($dataset); } // Find which number in the $dataset would be the best fit by checking @@ -76,15 +76,15 @@ private function findOptimalItemKey(Partition $partition, Partition $dataset, $b // of chunks we want to have. $partitionWeightMinusBest = $partition->getWeight() - $best; - $solutions = array_map( + $solutions = \array_map( static function (PartitionItem $item) use ($partitionWeightMinusBest) { - return abs($partitionWeightMinusBest + $item->getWeight()); + return \abs($partitionWeightMinusBest + $item->getWeight()); }, - iterator_to_array($dataset) + \iterator_to_array($dataset) ); - asort($solutions); + \asort($solutions); - return key($solutions); + return \key($solutions); } } diff --git a/src/GreedyAltAlt.php b/src/GreedyAltAlt.php index 4f6f174..24701cd 100644 --- a/src/GreedyAltAlt.php +++ b/src/GreedyAltAlt.php @@ -1,6 +1,6 @@ append($dataset[$key]); unset($dataset[$key]); - $dataset->exchangeArray(array_values($dataset->getArrayCopy())); + $dataset->exchangeArray(\array_values($dataset->getArrayCopy())); } $partitionsArray[] = $partition; diff --git a/src/GreedyAltAltAlt.php b/src/GreedyAltAltAlt.php index 0ccd0c8..1cc68f5 100644 --- a/src/GreedyAltAltAlt.php +++ b/src/GreedyAltAltAlt.php @@ -1,6 +1,6 @@ getPartitionFactory()::create(); - reset($dataset); - $key = key($dataset); + \reset($dataset); + $key = \key($dataset); $partition->append($dataset[$key]); unset($dataset[$key]); - $dataset->exchangeArray(array_values(array_reverse($dataset->getArrayCopy()))); + $dataset->exchangeArray(\array_values(\array_reverse($dataset->getArrayCopy()))); while ($partition->getWeight() < $best) { - reset($dataset); - $key = key($dataset); + \reset($dataset); + $key = \key($dataset); $partition->append($dataset[$key]); unset($dataset[$key]); } $partitionsArray[] = $partition; - $dataset->exchangeArray(array_values(array_reverse($dataset->getArrayCopy()))); + $dataset->exchangeArray(\array_values(\array_reverse($dataset->getArrayCopy()))); } $partitionsArray[] = $dataset; diff --git a/src/Linear.php b/src/Linear.php index aaf7335..067f8ee 100644 --- a/src/Linear.php +++ b/src/Linear.php @@ -1,6 +1,6 @@ [1..n] + $s = \array_merge([null], $dataset); // adapt indices here: [0..n-1] => [1..n] // Integer K - number of ranges to split items into $k = $chunks; @@ -69,7 +69,7 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in for ($x = 1; ($i - 1) >= $x; ++$x) { $solutions[] = [ 0 => $this->getPartitionItemFactory()::create( - max( + \max( $m[$x][$j - 1]->getWeight(), $p[$i]->getWeight() - $p[$x]->getWeight() ) @@ -78,7 +78,7 @@ protected function fillPartitions(Partitions $partitions, Partition $dataset, in ]; } - usort( + \usort( $solutions, static function (array $x, array $y) { return $x[0] <=> $y[0]; diff --git a/src/Partition/Partition.php b/src/Partition/Partition.php index b220406..6b269e7 100644 --- a/src/Partition/Partition.php +++ b/src/Partition/Partition.php @@ -1,6 +1,6 @@ getValue(); }, @@ -31,8 +31,8 @@ static function (Valuable $item) { */ public function getBounds(): array { - $max = PHP_INT_MAX; - $min = PHP_INT_MIN; + $max = \PHP_INT_MAX; + $min = \PHP_INT_MIN; $bounds = []; diff --git a/src/Partition/PartitionFactory.php b/src/Partition/PartitionFactory.php index febdc68..1469fe6 100644 --- a/src/Partition/PartitionFactory.php +++ b/src/Partition/PartitionFactory.php @@ -1,6 +1,6 @@ exportArrayCopy()); + return \array_values($partition->exportArrayCopy()); }, $this ->run($chunks) diff --git a/src/Partitions/Partitions.php b/src/Partitions/Partitions.php index 77d402f..70d1471 100644 --- a/src/Partitions/Partitions.php +++ b/src/Partitions/Partitions.php @@ -1,6 +1,6 @@ storage['partitions'], $compareCallable); + \usort($this->storage['partitions'], $compareCallable); return $this; } diff --git a/src/Partitions/PartitionsFactory.php b/src/Partitions/PartitionsFactory.php index 3a5d83a..ed12a04 100644 --- a/src/Partitions/PartitionsFactory.php +++ b/src/Partitions/PartitionsFactory.php @@ -1,6 +1,6 @@ getWeight(); }, diff --git a/tests/_support/Helper/Unit.php b/tests/_support/Helper/Unit.php index 4577483..7a222b3 100644 --- a/tests/_support/Helper/Unit.php +++ b/tests/_support/Helper/Unit.php @@ -1,6 +1,6 @@ setDataset($dataset); - static::assertSame($expected, $anytime->export($chunks)); + self::assertSame($expected, $anytime->export($chunks)); } } diff --git a/tests/unit/GreedyAltAltTest.php b/tests/unit/GreedyAltAltTest.php index d7e7aa4..57c6429 100644 --- a/tests/unit/GreedyAltAltTest.php +++ b/tests/unit/GreedyAltAltTest.php @@ -1,6 +1,6 @@ setDataset($dataset); - static::assertSame($expected, $greedy->export($chunks)); + self::assertSame($expected, $greedy->export($chunks)); } } diff --git a/tests/unit/GreedyAltTest.php b/tests/unit/GreedyAltTest.php index 091c243..2ba4306 100644 --- a/tests/unit/GreedyAltTest.php +++ b/tests/unit/GreedyAltTest.php @@ -1,6 +1,6 @@ setDataset($dataset); - static::assertSame($expected, $greedy->export($chunks)); + self::assertSame($expected, $greedy->export($chunks)); } } diff --git a/tests/unit/GreedyTest.php b/tests/unit/GreedyTest.php index 4297e42..cc24fde 100644 --- a/tests/unit/GreedyTest.php +++ b/tests/unit/GreedyTest.php @@ -1,6 +1,6 @@ setDataset($dataset); - static::assertSame($expected, $greedy->export($chunks)); + self::assertSame($expected, $greedy->export($chunks)); } } diff --git a/tests/unit/LinearTest.php b/tests/unit/LinearTest.php index 590dd67..797dcd3 100644 --- a/tests/unit/LinearTest.php +++ b/tests/unit/LinearTest.php @@ -1,6 +1,6 @@ setDataset($dataset); - static::assertSame($expected, $greedy->export($chunks)); + self::assertSame($expected, $greedy->export($chunks)); } } diff --git a/tests/unit/Partition/PartitionTest.php b/tests/unit/Partition/PartitionTest.php index 1ba3c88..7c9d9b5 100644 --- a/tests/unit/Partition/PartitionTest.php +++ b/tests/unit/Partition/PartitionTest.php @@ -1,6 +1,6 @@ getBounds()); + self::assertSame($expected, $partition->getBounds()); } public function testGetBoundsProvider() diff --git a/tests/unit/_bootstrap.php b/tests/unit/_bootstrap.php index bebc37d..174d7fd 100644 --- a/tests/unit/_bootstrap.php +++ b/tests/unit/_bootstrap.php @@ -1,3 +1,3 @@