MemoryEfficientLongestCommonSubsequenceCalculator.php 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of sebastian/diff.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace SebastianBergmann\Diff;
  11. final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
  12. {
  13. /**
  14. * {@inheritdoc}
  15. */
  16. public function calculate(array $from, array $to): array
  17. {
  18. $cFrom = \count($from);
  19. $cTo = \count($to);
  20. if ($cFrom === 0) {
  21. return [];
  22. }
  23. if ($cFrom === 1) {
  24. if (\in_array($from[0], $to, true)) {
  25. return [$from[0]];
  26. }
  27. return [];
  28. }
  29. $i = (int) ($cFrom / 2);
  30. $fromStart = \array_slice($from, 0, $i);
  31. $fromEnd = \array_slice($from, $i);
  32. $llB = $this->length($fromStart, $to);
  33. $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to));
  34. $jMax = 0;
  35. $max = 0;
  36. for ($j = 0; $j <= $cTo; $j++) {
  37. $m = $llB[$j] + $llE[$cTo - $j];
  38. if ($m >= $max) {
  39. $max = $m;
  40. $jMax = $j;
  41. }
  42. }
  43. $toStart = \array_slice($to, 0, $jMax);
  44. $toEnd = \array_slice($to, $jMax);
  45. return \array_merge(
  46. $this->calculate($fromStart, $toStart),
  47. $this->calculate($fromEnd, $toEnd)
  48. );
  49. }
  50. private function length(array $from, array $to): array
  51. {
  52. $current = \array_fill(0, \count($to) + 1, 0);
  53. $cFrom = \count($from);
  54. $cTo = \count($to);
  55. for ($i = 0; $i < $cFrom; $i++) {
  56. $prev = $current;
  57. for ($j = 0; $j < $cTo; $j++) {
  58. if ($from[$i] === $to[$j]) {
  59. $current[$j + 1] = $prev[$j] + 1;
  60. } else {
  61. $current[$j + 1] = \max($current[$j], $prev[$j + 1]);
  62. }
  63. }
  64. }
  65. return $current;
  66. }
  67. }