From fcc914109e1eec59db7f6b405521c9ac034469a9 Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Mon, 17 Feb 2014 16:03:41 +1000 Subject: [PATCH 1/6] Repository maintenance. --- .travis.env | 2 +- .travis.install | 45 +- .travis.yml | 27 +- CHANGELOG.md | 12 +- CONTRIBUTING.md | 25 +- LICENSE | 2 +- README.md | 101 ++- composer.json | 12 +- composer.lock | 860 +++++++++++++++++++++- src/Ezzatron/LCS/LCSSolver.php | 112 --- src/LcsSolver.php | 125 ++++ src/LcsSolverInterface.php | 34 + test/suite/Ezzatron/LCS/LCSSolverTest.php | 105 --- test/suite/LcsSolverTest.php | 130 ++++ 14 files changed, 1234 insertions(+), 358 deletions(-) delete mode 100644 src/Ezzatron/LCS/LCSSolver.php create mode 100644 src/LcsSolver.php create mode 100644 src/LcsSolverInterface.php delete mode 100644 test/suite/Ezzatron/LCS/LCSSolverTest.php create mode 100644 test/suite/LcsSolverTest.php diff --git a/.travis.env b/.travis.env index 2ca4449..826c9b1 100644 --- a/.travis.env +++ b/.travis.env @@ -1 +1 @@ -3G+wU0c9r9fKxnWsHSpwKNAJMUVBAyqG3pkflpHJ8grfe/qJb5Od0HyW/IJHRcm8FoUtmz46AK3u/Xup34cah+6+8H7IR4TrW8/5YNLv7k8mAAN3LqQgP8K0kcdUgr9DibWRbhNLAHHZxMD0FXELWfxotj92XJiT5aNV+JjyQug= \ No newline at end of file +QYAsj0sNlPKY/QuRAkupRb4pdQZBNTQ66kcHWrGs04eRrjAVwhBIN5gvpJTnJuLbxGNJePIDu7TjbcnaDyAXoQ5QVWlLZOHOUPinbzrFdMUjDWOPlJjHc00f2o9eDlGENKWKEI3NCLphqaKepOJ6oAvwooCGYkuftUeudEhNQ94= \ No newline at end of file diff --git a/.travis.install b/.travis.install index 60d38bd..b86b925 100755 --- a/.travis.install +++ b/.travis.install @@ -1,26 +1,37 @@ #!/usr/bin/env php array( - 'github-oauth' => array('github.com' => $token) - ) - ); +// Update composer to the latest version ... +passthru('composer self-update --no-interaction'); - $file = '~/.composer/config.json'; - $dir = dirname($file); - if (!is_dir($dir)) { - mkdir($dir, 0755, true); - } - file_put_contents($file, json_encode($config)); +// Build a composer config that uses the GitHub OAuth token if it is available ... +$config = array( + 'config' => array( + 'notify-on-install' => false + ) +); +if ($token = getenv('ARCHER_TOKEN')) { + $config['config']['github-oauth'] = array( + 'github.com' => $token + ); $composerFlags = '--prefer-dist'; } else { $composerFlags = '--prefer-source'; } -passthru('composer install --dev --no-progress --no-interaction --ansi ' . $composerFlags); +$file = '~/.composer/config.json'; +$dir = dirname($file); +if (!is_dir($dir)) { + mkdir($dir, 0755, true); +} +file_put_contents($file, json_encode($config)); + +// Display some information about GitHub rate limiting ... +if ($token) { + passthru('curl -s -i -H "Authorization: token $ARCHER_TOKEN" https://api.github.com | grep "^X-RateLimit"'); +} + +// Install composer dependencies ... +$exitCode = 0; +passthru('composer install --dev --no-progress --no-interaction --ansi ' . $composerFlags, $exitCode); +exit($exitCode); diff --git a/.travis.yml b/.travis.yml index 378cd82..20545f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,18 @@ -# -# This is the default Travis CI configuration. -# -# It uses a GitHub OAuth token when fetching composer dependencies -# to avoid IP-based API throttling. -# -# It also allows publication of artifacts via an additional build. -# language: php -php: - - 5.3 - - 5.4 - - 5.5 +php: ["5.3", "5.4", "5.5", "hhvm"] + +matrix: + allow_failures: + - php: hhvm env: global: - - ARCHER_PUBLISH_VERSION=5.4 - - secure: "3G+wU0c9r9fKxnWsHSpwKNAJMUVBAyqG3pkflpHJ8grfe/qJb5Od0HyW/IJHRcm8FoUtmz46AK3u/Xup34cah+6+8H7IR4TrW8/5YNLv7k8mAAN3LqQgP8K0kcdUgr9DibWRbhNLAHHZxMD0FXELWfxotj92XJiT5aNV+JjyQug=" + - ARCHER_PUBLISH_VERSION=5.5 + - secure: "QYAsj0sNlPKY/QuRAkupRb4pdQZBNTQ66kcHWrGs04eRrjAVwhBIN5gvpJTnJuLbxGNJePIDu7TjbcnaDyAXoQ5QVWlLZOHOUPinbzrFdMUjDWOPlJjHc00f2o9eDlGENKWKEI3NCLphqaKepOJ6oAvwooCGYkuftUeudEhNQ94=" install: - ./.travis.install + script: - ./vendor/bin/archer travis:build - -matrix: - # PHP 5.5 is still in alpha, so ignore build failures. - allow_failures: - - php: 5.5 diff --git a/CHANGELOG.md b/CHANGELOG.md index fb733d8..426cec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # PHP-LCS changelog -### 1.0.3 +## 2.0.0 (unrelease) -* [Archer](https://github.com/IcecaveStudios/archer) integration -* Implemented changelog +- **[BC BREAK]** Moved to Eloquent +- **[NEW]** Customizable comparator +- **[NEW]** API documentation + +## 1.0.3 (2013-03-04) + +- **[NEW]** [Archer](https://github.com/IcecaveStudios/archer) integration +- **[NEW]** Implemented changelog diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc35e05..d7b3c00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,24 +6,27 @@ changes. ### Code style -All PHP code must adhere to the -[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) -standards. +All PHP code must adhere to the [PSR-2] standards. ### Branching and pull requests As a guideline, please follow this process: - 1. [Fork the repository](https://help.github.com/articles/fork-a-repo). + 1. [Fork the repository]. 2. Create a topic branch for the change: - * New features should branch from **develop**. - * Bug fixes to existing versions should branch from **master**. - * Please ensure the branch is clearly labelled as a feature or fix. + - New features should branch from **develop**. + - Bug fixes to existing versions should branch from **master**. + - Please ensure the branch is clearly labelled as a feature or fix. 3. Make the relevant changes. - 4. [Squash](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) - commits if necessary. + 4. [Squash] commits if necessary. 4. Submit a pull request to the **develop** branch. Please note this is a general guideline only. For more information on the -branching structure please see the -[git-flow cheatsheet](http://danielkummer.github.com/git-flow-cheatsheet/). +branching structure please see the [git-flow cheatsheet]. + + + +[Fork the repository](https://help.github.com/articles/fork-a-repo) +[git-flow cheatsheet](http://danielkummer.github.com/git-flow-cheatsheet/) +[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) +[Squash](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) diff --git a/LICENSE b/LICENSE index f23d006..54584b1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2013 Erin Millard +Copyright © 2014 Erin Millard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index dbd3baa..ef8c779 100644 --- a/README.md +++ b/README.md @@ -2,73 +2,72 @@ *An implementation of the 'longest common subsequence' algorithm for PHP.* -[![Build Status]](http://travis-ci.org/ezzatron/php-lcs) -[![Test Coverage]](http://ezzatron.com/php-lcs/artifacts/tests/coverage/) +[![The most recent stable version is 1.0.3][version-image]][Semantic versioning] +[![Current build status image][build-image]][Current build status] +[![Current coverage status image][coverage-image]][Current coverage status] -## Installation +## Installation and documentation -Available as [Composer](http://getcomposer.org/) package -[ezzatron/php-lcs](https://packagist.org/packages/ezzatron/php-lcs). +- Available as [Composer] package [eloquent/php-lcs]. +- [API documentation] available. ## What is PHP-LCS? -PHP-LCS is a PHP implementation of an algorithm to solve the 'longest common +*PHP-LCS* is a PHP implementation of an algorithm to solve the 'longest common subsequence' problem. -From [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem): +From [Wikipedia - longest common subsequence problem]: > The **longest common subsequence (LCS) problem** is to find the longest -> [subsequence](http://en.wikipedia.org/wiki/Subsequence) common to all -> sequences in a set of sequences (often just two). Note that subsequence is -> different from a substring, see -> [substring vs. subsequence](http://en.wikipedia.org/wiki/Subsequence#Substring_vs._subsequence). -> It is a classic [computer science](http://en.wikipedia.org/wiki/Computer_science) -> problem, the basis of [file comparison](http://en.wikipedia.org/wiki/File_comparison) -> programs such as [diff](http://en.wikipedia.org/wiki/Diff), and has -> applications in [bioinformatics](http://en.wikipedia.org/wiki/Bioinformatics). +> [subsequence] common to all sequences in a set of sequences (often just two). +> Note that subsequence is different from a substring, see [substring vs. +> subsequence]. It is a classic [computer science] problem, the basis of [file +> comparison] programs such as [diff], and has applications in [bioinformatics]. ## Usage ```php -use Ezzatron\LCS\LCSSolver; +use Eloquent\Lcs\LcsSolver; -$solver = new LCSSolver; +$solver = new LcsSolver; -$left = array( - 'B', - 'A', - 'N', - 'A', - 'N', - 'A', -); -$right = array( - 'A', - 'T', - 'A', - 'N', - 'A', -); -$expectedLCS = array( - 'A', - 'A', - 'N', - 'A', -); +$sequenceA = array('B', 'A', 'N', 'A', 'N', 'A'); +$sequenceB = array('A', 'T', 'A', 'N', 'A'); -$LCS = $solver->longestCommonSubsequence( - $left, - $right -); +// calculates correct LCS of array('A', 'A', 'N', 'A') +$lcs = $solver->longestCommonSubsequence($sequenceA, $sequenceB); +``` + +Elements in sequences can be anything. By default, sequence members are compared +using the `===` operator. To customize this comparison, simply construct the +solver with a custom comparator, like so: -if ($LCS === $expectedLCS) { - echo 'LCS solver is working.'; -} else { - echo 'LCS solver is not working.'; -} -// the above outputs 'LCS solver is working.' ``` +use Eloquent\Lcs\LcsSolver; + +$solver = new LcsSolver( + function ($left, $right) { + // return true if $left and $right are equal + } +); +``` + + + +[bioinformatics]: http://en.wikipedia.org/wiki/Bioinformatics +[computer science]: http://en.wikipedia.org/wiki/Computer_science +[diff]: http://en.wikipedia.org/wiki/Diff +[file comparison]: http://en.wikipedia.org/wiki/File_comparison +[subsequence]: http://en.wikipedia.org/wiki/Subsequence +[substring vs. subsequence]: http://en.wikipedia.org/wiki/Subsequence#Substring_vs._subsequence +[Wikipedia - longest common subsequence problem]: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem - -[Build Status]: https://raw.github.com/ezzatron/php-lcs/gh-pages/artifacts/images/icecave/regular/build-status.png -[Test Coverage]: https://raw.github.com/ezzatron/php-lcs/gh-pages/artifacts/images/icecave/regular/coverage.png +[API documentation]: http://lqnt.co/php-lcs/artifacts/documentation/api/ +[Composer]: http://getcomposer.org/ +[build-image]: http://img.shields.io/travis/eloquent/php-lcs/develop.svg "Current build status for the develop branch" +[Current build status]: https://travis-ci.org/eloquent/php-lcs +[coverage-image]: http://img.shields.io/coveralls/eloquent/php-lcs/develop.svg "Current test coverage for the develop branch" +[Current coverage status]: https://coveralls.io/r/eloquent/php-lcs +[eloquent/php-lcs]: https://packagist.org/packages/eloquent/php-lcs +[Semantic versioning]: http://semver.org/ +[version-image]: http://img.shields.io/:semver-1.0.3-brightgreen.svg "This project uses semantic versioning" diff --git a/composer.json b/composer.json index 3d8ce89..6c9dff8 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { - "name": "ezzatron/php-lcs", + "name": "eloquent/php-lcs", "description": "An implementation of the 'longest common subsequence' algorithm for PHP.", - "keywords": ["longest","common","subsequence","sequence","diff","algorithm"], + "keywords": ["longest", "common", "subsequence", "sequence", "diff", "algorithm"], "homepage": "https://github.com/ezzatron/php-lcs", "license": "MIT", "authors": [ @@ -12,14 +12,14 @@ } ], "require": { - "php": ">=5" + "php": ">=5.3" }, "require-dev": { - "icecave/archer": "~0.2" + "icecave/archer": "~1" }, "autoload": { - "psr-0": { - "Ezzatron\\LCS": "src" + "psr-4": { + "Eloquent\\Lcs\\": "src" } } } diff --git a/composer.lock b/composer.lock index 0c4b6f6..7c0588a 100644 --- a/composer.lock +++ b/composer.lock @@ -1,28 +1,189 @@ { - "hash": "d0c54150060ff40d6c29bf125acf82c2", + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "5158cd4a9f66298ec16dd8e50af8b55d", "packages": [ ], "packages-dev": [ + { + "name": "dflydev/markdown", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-markdown.git", + "reference": "6baed9b50f29c980795b6656d43722aadb126f7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-markdown/zipball/6baed9b50f29c980795b6656d43722aadb126f7e", + "reference": "6baed9b50f29c980795b6656d43722aadb126f7e", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "dflydev\\markdown": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Michel Fortin", + "homepage": "http://michelf.com" + }, + { + "name": "John Gruber", + "homepage": "http://daringfireball.net" + } + ], + "description": "PHP Markdown & Extra", + "homepage": "http://github.com/dflydev/dflydev-markdown", + "keywords": [ + "markdown" + ], + "time": "2013-09-23 12:00:18" + }, + { + "name": "guzzle/guzzle", + "version": "v3.8.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4de0618a01b34aa1c8c33a3f13f396dcd3882eba", + "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": ">=2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "*", + "monolog/monolog": "1.*", + "phpunit/phpunit": "3.7.*", + "psr/log": "1.0.*", + "symfony/class-loader": "*", + "zendframework/zend-cache": "<2.3", + "zendframework/zend-log": "<2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.8-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2014-01-28 22:29:15" + }, { "name": "icecave/archer", - "version": "0.2.1", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/IcecaveStudios/archer.git", - "reference": "0.2.1" + "reference": "bd29aecd559962e0f8cf9753fce449ae78f2a913" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/IcecaveStudios/archer/zipball/0.2.1", - "reference": "0.2.1", + "url": "https://api.github.com/repos/IcecaveStudios/archer/zipball/bd29aecd559962e0f8cf9753fce449ae78f2a913", + "reference": "bd29aecd559962e0f8cf9753fce449ae78f2a913", "shasum": "" }, "require": { - "phake/phake": ">=1.0,<2.0", + "phake/phake": "~1", "php": ">=5.3", - "symfony/console": ">=2.0,<3.0", - "symfony/process": ">=2.0,<3.0" + "sami/sami": "~1.1.0", + "satooshi/php-coveralls": "~0.6", + "symfony/console": "~2", + "symfony/process": "~2" + }, + "require-dev": { + "eloquent/liberator": "~1", + "symfony/event-dispatcher": "~2.1" }, "suggest": { "ext-openssl": "OpenSSL is required to encrypt GitHub OAuth tokens for artifact publication." @@ -33,8 +194,8 @@ ], "type": "library", "autoload": { - "psr-0": { - "Icecave\\Archer": "lib" + "psr-4": { + "Icecave\\Archer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -49,34 +210,83 @@ }, { "name": "James Harris", - "email": "james.harris@icecave.com.au" + "email": "james.harris@icecave.com.au", + "homepage": "https://github.com/jmalloc" } ], - "description": "PHP testing and continuous integration by convention.", + "description": "Testing, CI and documentation of PHP projects by convention.", "homepage": "https://github.com/IcecaveStudios/archer", "keywords": [ + "api", "artifacts", + "convention", "coverage", + "documentation", "phake", "phpunit", + "project", "test", "testing", "unit" ], - "time": "2013-02-27 04:42:20" + "time": "2014-02-10 03:46:44" + }, + { + "name": "nikic/php-parser", + "version": "v0.9.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "1e5e280ae88a27effa2ae4aa2bd088494ed8594f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1e5e280ae88a27effa2ae4aa2bd088494ed8594f", + "reference": "1e5e280ae88a27effa2ae4aa2bd088494ed8594f", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + } + }, + "autoload": { + "psr-0": { + "PHPParser": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2013-08-25 17:11:40" }, { "name": "phake/phake", "version": "v1.0.3", "source": { "type": "git", - "url": "git://github.com/mlively/Phake.git", - "reference": "v1.0.3" + "url": "https://github.com/mlively/Phake.git", + "reference": "4d822246b401f4dc609500d47e42dac8df645f4f" }, "dist": { "type": "zip", - "url": "https://github.com/mlively/Phake/zipball/v1.0.3", - "reference": "v1.0.3", + "url": "https://api.github.com/repos/mlively/Phake/zipball/4d822246b401f4dc609500d47e42dac8df645f4f", + "reference": "4d822246b401f4dc609500d47e42dac8df645f4f", "shasum": "" }, "require": { @@ -107,30 +317,297 @@ "mock", "testing" ], - "time": "2012-05-14 08:06:51" + "time": "2012-05-14 15:06:51" + }, + { + "name": "pimple/pimple", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Pimple.git", + "reference": "ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Pimple/zipball/ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94", + "reference": "ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2013-03-08 08:21:40" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "sami/sami", + "version": "v1.1", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Sami.git", + "reference": "46c58957ce4823c613e8535626b81e2fd80a9865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Sami/zipball/46c58957ce4823c613e8535626b81e2fd80a9865", + "reference": "46c58957ce4823c613e8535626b81e2fd80a9865", + "shasum": "" + }, + "require": { + "dflydev/markdown": "1.0.*", + "nikic/php-parser": "0.9.*", + "php": ">=5.3.0", + "pimple/pimple": "1.0.*", + "symfony/console": "~2.1", + "symfony/filesystem": "~2.1", + "symfony/finder": "~2.1", + "symfony/process": "~2.1", + "symfony/yaml": "~2.1", + "twig/twig": "1.*" + }, + "bin": [ + "sami.php" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Sami": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Sami, an API documentation generator", + "homepage": "http://sami.sensiolabs.org", + "keywords": [ + "phpdoc" + ], + "time": "2013-08-04 13:56:41" + }, + { + "name": "satooshi/php-coveralls", + "version": "v0.6.1", + "source": { + "type": "git", + "url": "https://github.com/satooshi/php-coveralls.git", + "reference": "dd0df95bd37a7cf5c5c50304dfe260ffe4b50760" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/dd0df95bd37a7cf5c5c50304dfe260ffe4b50760", + "reference": "dd0df95bd37a7cf5c5c50304dfe260ffe4b50760", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-simplexml": "*", + "guzzle/guzzle": ">=3.0", + "php": ">=5.3", + "psr/log": "1.0.0", + "symfony/config": ">=2.0", + "symfony/console": ">=2.0", + "symfony/stopwatch": ">=2.2", + "symfony/yaml": ">=2.0" + }, + "require-dev": { + "apigen/apigen": "2.8.*@stable", + "pdepend/pdepend": "dev-master", + "phpmd/phpmd": "dev-master", + "phpunit/php-invoker": ">=1.1.0,<1.2.0", + "phpunit/phpunit": "3.7.*@stable", + "sebastian/finder-facade": "dev-master", + "sebastian/phpcpd": "1.4.*@stable", + "squizlabs/php_codesniffer": "1.4.*@stable", + "theseer/fdomdocument": "dev-master" + }, + "bin": [ + "composer/bin/coveralls" + ], + "type": "library", + "autoload": { + "psr-0": { + "Contrib\\Component": "src/", + "Contrib\\Bundle": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kitamura Satoshi", + "email": "with.no.parachute@gmail.com", + "homepage": "https://www.facebook.com/satooshi.jp" + } + ], + "description": "PHP client library for Coveralls API", + "homepage": "https://github.com/satooshi/php-coveralls", + "keywords": [ + "ci", + "coverage", + "github", + "test" + ], + "time": "2013-05-04 08:07:33" + }, + { + "name": "symfony/config", + "version": "v2.4.2", + "target-dir": "Symfony/Component/Config", + "source": { + "type": "git", + "url": "https://github.com/symfony/Config.git", + "reference": "d81bd01eac1514c10dcb3b11eaa9048d6b87dd1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Config/zipball/d81bd01eac1514c10dcb3b11eaa9048d6b87dd1f", + "reference": "d81bd01eac1514c10dcb3b11eaa9048d6b87dd1f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/filesystem": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Config\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "http://symfony.com", + "time": "2014-01-07 13:28:54" }, { "name": "symfony/console", - "version": "v2.2.0", + "version": "v2.4.2", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "v2.2.0" + "reference": "940f217cbc3c8a33e5403e7c595495c4884400fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/v2.2.0", - "reference": "v2.2.0", + "url": "https://api.github.com/repos/symfony/Console/zipball/940f217cbc3c8a33e5403e7c595495c4884400fe", + "reference": "940f217cbc3c8a33e5403e7c595495c4884400fe", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "require-dev": { + "symfony/event-dispatcher": "~2.1" + }, + "suggest": { + "symfony/event-dispatcher": "" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -145,7 +622,9 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { "name": "Symfony Community", @@ -154,21 +633,175 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2013-03-01 06:43:14" + "time": "2014-02-11 13:52:09" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.4.2", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "4708b8cd41984a5ba29fe7dd40716f7f761ac501" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/4708b8cd41984a5ba29fe7dd40716f7f761ac501", + "reference": "4708b8cd41984a5ba29fe7dd40716f7f761ac501", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2014-02-11 13:52:09" + }, + { + "name": "symfony/filesystem", + "version": "v2.4.2", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "7e65abb06d3b38f4be89266fe3fb4a759544e713" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/7e65abb06d3b38f4be89266fe3fb4a759544e713", + "reference": "7e65abb06d3b38f4be89266fe3fb4a759544e713", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2014-01-07 13:28:54" + }, + { + "name": "symfony/finder", + "version": "v2.4.2", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/b6735d1fc16da13c4c7dddfe78366a4a098cf011", + "reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2014-01-07 13:28:54" }, { "name": "symfony/process", - "version": "v2.2.0", + "version": "v2.4.2", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "v2.2.0-RC3" + "reference": "c175448bac997556f8ab972908a4e14c7291fb03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/v2.2.0-RC3", - "reference": "v2.2.0-RC3", + "url": "https://api.github.com/repos/symfony/Process/zipball/c175448bac997556f8ab972908a4e14c7291fb03", + "reference": "c175448bac997556f8ab972908a4e14c7291fb03", "shasum": "" }, "require": { @@ -177,7 +810,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -192,7 +825,9 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { "name": "Symfony Community", @@ -201,7 +836,162 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2013-02-18 21:28:10" + "time": "2014-02-11 13:52:09" + }, + { + "name": "symfony/stopwatch", + "version": "v2.4.2", + "target-dir": "Symfony/Component/Stopwatch", + "source": { + "type": "git", + "url": "https://github.com/symfony/Stopwatch.git", + "reference": "bffad325e36a3e71fba6d5dcce6e2f4b4637b91a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/bffad325e36a3e71fba6d5dcce6e2f4b4637b91a", + "reference": "bffad325e36a3e71fba6d5dcce6e2f4b4637b91a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Stopwatch\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "http://symfony.com", + "time": "2014-02-11 13:52:09" + }, + { + "name": "symfony/yaml", + "version": "v2.4.2", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/bb6ddaf8956139d1b8c360b4b713ed0138e876b3", + "reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-01-07 13:28:54" + }, + { + "name": "twig/twig", + "version": "v1.15.1", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig.git", + "reference": "1fb5784662f438d7d96a541e305e28b812e2eeed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/1fb5784662f438d7d96a541e305e28b812e2eeed", + "reference": "1fb5784662f438d7d96a541e305e28b812e2eeed", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "https://github.com/fabpot/Twig/graphs/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2014-02-13 10:19:29" } ], "aliases": [ @@ -210,5 +1000,11 @@ "minimum-stability": "stable", "stability-flags": [ + ], + "platform": { + "php": ">=5.3" + }, + "platform-dev": [ + ] } diff --git a/src/Ezzatron/LCS/LCSSolver.php b/src/Ezzatron/LCS/LCSSolver.php deleted file mode 100644 index 45048cd..0000000 --- a/src/Ezzatron/LCS/LCSSolver.php +++ /dev/null @@ -1,112 +0,0 @@ - 2) { - $arguments = func_get_args(); - array_splice( - $arguments, - 0, - 2, - array( - $this->longestCommonSubsequence($left, $right) - ) - ); - - return call_user_func_array( - array($this, 'longestCommonSubsequence'), - $arguments - ); - } - - $m = count($left); - $n = count($right); - - // $a[$i][$j] = length of LCS of $left[$i..$m] and $right[$j..$n] - $a = array(); - - // compute length of LCS and all subproblems via dynamic programming - for ($i = $m - 1; $i >= 0; $i--) { - for ($j = $n - 1; $j >= 0; $j--) { - if ($left[$i] === $right[$j]) { - $a[$i][$j] = - ( - isset($a[$i + 1][$j + 1]) ? - $a[$i + 1][$j + 1] : - 0 - ) + - 1 - ; - } else { - $a[$i][$j] = max( - ( - isset($a[$i + 1][$j]) ? - $a[$i + 1][$j] : - 0 - ), - ( - isset($a[$i][$j + 1]) ? - $a[$i][$j + 1] : - 0 - ) - ); - } - } - } - - // recover LCS itself - $i = 0; - $j = 0; - $lcs = array(); - - while($i < $m && $j < $n) { - if ($left[$i] === $right[$j]) { - $lcs[] = $left[$i]; - - $i++; - $j++; - } elseif ( - ( - isset($a[$i + 1][$j]) ? - $a[$i + 1][$j] : - 0 - ) >= - ( - isset($a[$i][$j + 1]) ? - $a[$i][$j + 1] : - 0 - ) - ) { - $i++; - } else { - $j++; - } - } - - return $lcs; - } -} diff --git a/src/LcsSolver.php b/src/LcsSolver.php new file mode 100644 index 0000000..bec1c68 --- /dev/null +++ b/src/LcsSolver.php @@ -0,0 +1,125 @@ +comparator = $comparator; + } + + /** + * Get the comparator. + * + * @return callable The comparator. + */ + public function comparator() + { + return $this->comparator; + } + + /** + * Returns the longest common subsequence of the given sequences. + * + * @link http://en.wikipedia.org/wiki/Longest_common_subsequence_problem + * + * @param array $sequenceA The first sequence. + * @param array $sequenceB The second sequence. + * @param array $additional,... Any number of additional sequences. + * + * @return array The longest common subsequence. + */ + public function longestCommonSubsequence(array $sequenceA, array $sequenceB) + { + if (func_num_args() > 2) { + $arguments = func_get_args(); + array_splice( + $arguments, + 0, + 2, + array($this->longestCommonSubsequence($sequenceA, $sequenceB)) + ); + + return call_user_func_array( + array($this, 'longestCommonSubsequence'), + $arguments + ); + } + + $comparator = $this->comparator(); + + $m = count($sequenceA); + $n = count($sequenceB); + + // $a[$i][$j] = length of LCS of $sequenceA[$i..$m] and $sequenceB[$j..$n] + $a = array(); + + // compute length of LCS and all subproblems via dynamic programming + for ($i = $m - 1; $i >= 0; $i--) { + for ($j = $n - 1; $j >= 0; $j--) { + if ($comparator($sequenceA[$i], $sequenceB[$j])) { + $a[$i][$j] = + (isset($a[$i + 1][$j + 1]) ? $a[$i + 1][$j + 1] : 0) + + 1; + } else { + $a[$i][$j] = max( + (isset($a[$i + 1][$j]) ? $a[$i + 1][$j] : 0), + (isset($a[$i][$j + 1]) ? $a[$i][$j + 1] : 0) + ); + } + } + } + + // recover LCS itself + $i = 0; + $j = 0; + $lcs = array(); + + while ($i < $m && $j < $n) { + if ($comparator($sequenceA[$i], $sequenceB[$j])) { + $lcs[] = $sequenceA[$i]; + + $i++; + $j++; + } elseif ( + (isset($a[$i + 1][$j]) ? $a[$i + 1][$j] : 0) >= + (isset($a[$i][$j + 1]) ? $a[$i][$j + 1] : 0) + ) { + $i++; + } else { + $j++; + } + } + + return $lcs; + } + + private $comparator; +} diff --git a/src/LcsSolverInterface.php b/src/LcsSolverInterface.php new file mode 100644 index 0000000..9fab8e2 --- /dev/null +++ b/src/LcsSolverInterface.php @@ -0,0 +1,34 @@ + $sequenceA The first sequence. + * @param array $sequenceB The second sequence. + * @param array $additional,... Any number of additional sequences. + * + * @return array The longest common subsequence. + */ + public function longestCommonSubsequence( + array $sequenceA, + array $sequenceB + ); +} diff --git a/test/suite/Ezzatron/LCS/LCSSolverTest.php b/test/suite/Ezzatron/LCS/LCSSolverTest.php deleted file mode 100644 index 678f1c8..0000000 --- a/test/suite/Ezzatron/LCS/LCSSolverTest.php +++ /dev/null @@ -1,105 +0,0 @@ -_solver = new LCSSolver; - } - - public function longestCommonSubsequenceData() - { - $data = array(); - - $left = array('A', 'B', 'C'); - $right = array('A', 'B', 'C'); - $expected = array('A', 'B', 'C'); - $data['All elements equal'] = array($expected, $left, $right); - - $left = array('A', 'B', 'C', 'D', 'E', 'F'); - $right = array('B', 'C', 'D', 'E'); - $expected = array('B', 'C', 'D', 'E'); - $data['Second sequence is a subsequence of first'] = array($expected, $left, $right); - - $left = array('A', 'B', 'D', 'E'); - $right = array('A', 'C', 'D', 'F'); - $expected = array('A', 'D',); - $data['Basic common subsequence'] = array($expected, $left, $right); - - $left = array('A', 'B', 'C', 'D', 'E', 'F'); - $right = array('J', 'A', 'D', 'F', 'A', 'F', 'K', 'D', 'F', 'B', 'C', 'D', 'E', 'H', 'J', 'D', 'F', 'G'); - $expected = array('A', 'B', 'C', 'D', 'E', 'F'); - $data['Common subsequence of larger data set'] = array($expected, $left, $right); - - $left = array('A', 'B', 'C', 'D', 'E', 'F'); - $right = array('A'); - $expected = array('A'); - $data[] = array($expected, $left, $right); - - $left = array('A', 'B', 'C', 'D', 'E', 'F'); - $right = array('D'); - $expected = array('D'); - $data['Single element subsequence'] = array($expected, $left, $right); - - $left = array('A', 'B', 'C', 'D', 'E', 'F'); - $right = array('F'); - $expected = array('F'); - $data['Single element subsequence at end of other sequence'] = array($expected, $left, $right); - - $left = array('J', 'A', 'F', 'A', 'F', 'K', 'D', 'B', 'C', 'E', 'H', 'J', 'D', 'F'); - $right = array('J', 'D', 'F', 'A', 'K', 'D', 'F', 'C', 'D', 'E', 'J', 'D', 'F', 'G'); - $expected = array('J', 'F', 'A', 'K', 'D', 'C', 'E', 'J', 'D', 'F'); - $data['Elements after end of first sequence'] = array($expected, $left, $right); - - $left = array('A', 'B', 'C'); - $right = array('D', 'E', 'F'); - $expected = array(); - $data['No common elements'] = array($expected, $left, $right); - - $left = array('B', 'A', 'N', 'A', 'N', 'A'); - $right = array('A', 'T', 'A', 'N', 'A'); - $expected = array('A', 'A', 'N', 'A'); - $data['README example'] = array($expected, $left, $right); - - $first = array('A', 'B', 'D', 'E', 'G', 'H'); - $second = array('A', 'D', 'G', 'J'); - $third = array('B', 'C', 'D', 'E', 'F', 'G'); - $expected = array('D', 'G'); - $data['Three-way subsequence'] = array($expected, $first, $second, $third); - - return $data; - } - - /** - * @dataProvider longestCommonSubsequenceData - */ - public function testLongestCommonSubsequence(array $expected, array $left, array $right) - { - $arguments = func_get_args(); - array_shift($arguments); - - $this->assertSame($expected, call_user_func_array( - array($this->_solver, 'longestCommonSubsequence'), - $arguments - )); - $this->assertSame($expected, call_user_func_array( - array($this->_solver, 'longestCommonSubsequence'), - array_reverse($arguments) - )); - } -} diff --git a/test/suite/LcsSolverTest.php b/test/suite/LcsSolverTest.php new file mode 100644 index 0000000..f2662ad --- /dev/null +++ b/test/suite/LcsSolverTest.php @@ -0,0 +1,130 @@ +solver = new LcsSolver; + } + + public function testConstructor() + { + $comparator = function () {}; + $this->solver = new LcsSolver($comparator); + + $this->assertSame($comparator, $this->solver->comparator()); + } + + public function testConstructorDefaults() + { + $this->assertTrue(is_callable($this->solver->comparator())); + } + + public function longestCommonSubsequenceData() + { + return array( + 'All elements equal' => array( + array('A', 'B', 'C'), + array('A', 'B', 'C'), + array('A', 'B', 'C'), + ), + 'Second sequence is a subsequence of first' => array( + array('A', 'B', 'C', 'D', 'E', 'F'), + array('B', 'C', 'D', 'E'), + array('B', 'C', 'D', 'E'), + ), + 'Basic common subsequence' => array( + array('A', 'B', 'D', 'E'), + array('A', 'C', 'D', 'F'), + array('A', 'D'), + ), + 'Common subsequence of larger data set' => array( + array('A', 'B', 'C', 'D', 'E', 'F'), + array('J', 'A', 'D', 'F', 'A', 'F', 'K', 'D', 'F', 'B', 'C', 'D', 'E', 'H', 'J', 'D', 'F', 'G'), + array('A', 'B', 'C', 'D', 'E', 'F'), + ), + 'Single element subsequence at start' => array( + array('A', 'B', 'C', 'D', 'E', 'F'), + array('A'), + array('A'), + ), + 'Single element subsequence at middle' => array( + array('A', 'B', 'C', 'D', 'E', 'F'), + array('D'), + array('D'), + ), + 'Single element subsequence at end' => array( + array('A', 'B', 'C', 'D', 'E', 'F'), + array('F'), + array('F'), + ), + 'Elements after end of first sequence' => array( + array('J', 'A', 'F', 'A', 'F', 'K', 'D', 'B', 'C', 'E', 'H', 'J', 'D', 'F'), + array('J', 'D', 'F', 'A', 'K', 'D', 'F', 'C', 'D', 'E', 'J', 'D', 'F', 'G'), + array('J', 'F', 'A', 'K', 'D', 'C', 'E', 'J', 'D', 'F'), + ), + 'No common elements' => array( + array('A', 'B', 'C'), + array('D', 'E', 'F'), + array(), + ), + 'README example' => array( + array('B', 'A', 'N', 'A', 'N', 'A'), + array('A', 'T', 'A', 'N', 'A'), + array('A', 'A', 'N', 'A'), + ), + ); + } + + /** + * @dataProvider longestCommonSubsequenceData + */ + public function testLongestCommonSubsequence($sequenceA, $sequenceB, $expected) + { + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceA, $sequenceB)); + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceB, $sequenceA)); + } + + public function testLongestCommonSubsequence3Way() + { + $sequenceA = array('A', 'B', 'D', 'E', 'G', 'H'); + $sequenceB = array('A', 'D', 'G', 'J'); + $sequenceC = array('B', 'C', 'D', 'E', 'F', 'G'); + $expected = array('D', 'G'); + + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceA, $sequenceB, $sequenceC)); + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceA, $sequenceC, $sequenceB)); + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceB, $sequenceA, $sequenceC)); + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceB, $sequenceC, $sequenceA)); + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceC, $sequenceA, $sequenceB)); + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceC, $sequenceB, $sequenceA)); + } + + public function testLongestCommonSubsequenceCustomComparator() + { + $comparator = function ($left, $right) { + return strtolower($left) === strtolower($right); + }; + $this->solver = new LcsSolver($comparator); + $sequenceA = array('B', 'A', 'N', 'A', 'N', 'A'); + $sequenceB = array('a', 't', 'a', 'n', 'a'); + $expected = array('A', 'A', 'N', 'A'); + + $this->assertSame($expected, $this->solver->longestCommonSubsequence($sequenceA, $sequenceB)); + } +} From b01327802d0fbdd0dbccd2235286f8be75334477 Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Mon, 17 Feb 2014 16:09:31 +1000 Subject: [PATCH 2/6] Minor documentation update. --- .travis.env | 2 +- .travis.yml | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.env b/.travis.env index 826c9b1..8081c85 100644 --- a/.travis.env +++ b/.travis.env @@ -1 +1 @@ -QYAsj0sNlPKY/QuRAkupRb4pdQZBNTQ66kcHWrGs04eRrjAVwhBIN5gvpJTnJuLbxGNJePIDu7TjbcnaDyAXoQ5QVWlLZOHOUPinbzrFdMUjDWOPlJjHc00f2o9eDlGENKWKEI3NCLphqaKepOJ6oAvwooCGYkuftUeudEhNQ94= \ No newline at end of file +ZZocT3FEPwogN5Qsw69w6hqI/xfUeaP1LqKmFVujuMFDaX8xuNrYWHtyRCXemGfcefFqHGlVyGwORSivKI4TLeE1yDNGcJ1+AAAslC4eIAXi96Mm/RKgSR2Umul+JK7afI8XqxEnZLqmNs/sUCZvnr/93BhA9UTgcmj9LSgqOYM= \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 20545f6..2da9c04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ matrix: env: global: - ARCHER_PUBLISH_VERSION=5.5 - - secure: "QYAsj0sNlPKY/QuRAkupRb4pdQZBNTQ66kcHWrGs04eRrjAVwhBIN5gvpJTnJuLbxGNJePIDu7TjbcnaDyAXoQ5QVWlLZOHOUPinbzrFdMUjDWOPlJjHc00f2o9eDlGENKWKEI3NCLphqaKepOJ6oAvwooCGYkuftUeudEhNQ94=" + - secure: "ZZocT3FEPwogN5Qsw69w6hqI/xfUeaP1LqKmFVujuMFDaX8xuNrYWHtyRCXemGfcefFqHGlVyGwORSivKI4TLeE1yDNGcJ1+AAAslC4eIAXi96Mm/RKgSR2Umul+JK7afI8XqxEnZLqmNs/sUCZvnr/93BhA9UTgcmj9LSgqOYM=" install: - ./.travis.install diff --git a/README.md b/README.md index ef8c779..55c7675 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ $solver = new LcsSolver; $sequenceA = array('B', 'A', 'N', 'A', 'N', 'A'); $sequenceB = array('A', 'T', 'A', 'N', 'A'); -// calculates correct LCS of array('A', 'A', 'N', 'A') +// calculates the LCS to be array('A', 'A', 'N', 'A') $lcs = $solver->longestCommonSubsequence($sequenceA, $sequenceB); ``` From 8316b1ee86b60543e6e557e0c4b55cadbdf3e38d Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Mon, 17 Feb 2014 16:16:45 +1000 Subject: [PATCH 3/6] Fixed dodgy Composer config. --- composer.json | 4 ++-- composer.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 6c9dff8..6d94f29 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { - "name": "eloquent/php-lcs", + "name": "eloquent/lcs", "description": "An implementation of the 'longest common subsequence' algorithm for PHP.", "keywords": ["longest", "common", "subsequence", "sequence", "diff", "algorithm"], - "homepage": "https://github.com/ezzatron/php-lcs", + "homepage": "https://github.com/eloquent/php-lcs", "license": "MIT", "authors": [ { diff --git a/composer.lock b/composer.lock index 7c0588a..beccbe6 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "5158cd4a9f66298ec16dd8e50af8b55d", + "hash": "10623d2774ac4a7a1caecfcc33ec1f73", "packages": [ ], From a014b26724198395535d5ea2b69764331950d6b2 Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Mon, 17 Feb 2014 16:19:12 +1000 Subject: [PATCH 4/6] PHP syntax highlighting in readme. [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55c7675..6265514 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Elements in sequences can be anything. By default, sequence members are compared using the `===` operator. To customize this comparison, simply construct the solver with a custom comparator, like so: -``` +```php use Eloquent\Lcs\LcsSolver; $solver = new LcsSolver( From dc8de26ea51ce1a88423af8b7eb61389026c7c9b Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Mon, 17 Feb 2014 16:22:18 +1000 Subject: [PATCH 5/6] Updated Composer link. [ck skip] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6265514..ad58711 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## Installation and documentation -- Available as [Composer] package [eloquent/php-lcs]. +- Available as [Composer] package [eloquent/lcs]. - [API documentation] available. ## What is PHP-LCS? @@ -68,6 +68,6 @@ $solver = new LcsSolver( [Current build status]: https://travis-ci.org/eloquent/php-lcs [coverage-image]: http://img.shields.io/coveralls/eloquent/php-lcs/develop.svg "Current test coverage for the develop branch" [Current coverage status]: https://coveralls.io/r/eloquent/php-lcs -[eloquent/php-lcs]: https://packagist.org/packages/eloquent/php-lcs +[eloquent/lcs]: https://packagist.org/packages/eloquent/lcs [Semantic versioning]: http://semver.org/ [version-image]: http://img.shields.io/:semver-1.0.3-brightgreen.svg "This project uses semantic versioning" From 558a7d902f428cfdfeaf70c846a277315ce5e38c Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Mon, 17 Feb 2014 16:24:07 +1000 Subject: [PATCH 6/6] Updated changelog, readme. --- CHANGELOG.md | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 426cec9..9bf769b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # PHP-LCS changelog -## 2.0.0 (unrelease) +## 2.0.0 (2014-02-17) - **[BC BREAK]** Moved to Eloquent - **[NEW]** Customizable comparator diff --git a/README.md b/README.md index ad58711..8e8844d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ *An implementation of the 'longest common subsequence' algorithm for PHP.* -[![The most recent stable version is 1.0.3][version-image]][Semantic versioning] +[![The most recent stable version is 2.0.0][version-image]][Semantic versioning] [![Current build status image][build-image]][Current build status] [![Current coverage status image][coverage-image]][Current coverage status] @@ -70,4 +70,4 @@ $solver = new LcsSolver( [Current coverage status]: https://coveralls.io/r/eloquent/php-lcs [eloquent/lcs]: https://packagist.org/packages/eloquent/lcs [Semantic versioning]: http://semver.org/ -[version-image]: http://img.shields.io/:semver-1.0.3-brightgreen.svg "This project uses semantic versioning" +[version-image]: http://img.shields.io/:semver-2.0.0-brightgreen.svg "This project uses semantic versioning"