$a * @param array $b * * @return array{0:non-empty-list>, 1: int, 2: int, 3: array} */ protected static function calculateTrace( Closure $is_equal, array $a, array $b, string $a_code, string $b_code ): array { $n = count($a); $m = count($b); $max = $n + $m; $v = [1 => 0]; $bc = []; $trace = []; for ($d = 0; $d <= $max; ++$d) { $trace[] = $v; for ($k = -$d; $k <= $d; $k += 2) { if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) { $x = $v[$k + 1]; } else { $x = $v[$k - 1] + 1; } $y = $x - $k; $body_change = false; while ($x < $n && $y < $m && ($is_equal)($a[$x], $b[$y], $a_code, $b_code, $body_change)) { $bc[$x] = $body_change; ++$x; ++$y; $body_change = false; } $v[$k] = $x; if ($x >= $n && $y >= $m) { return [$trace, $x, $y, $bc]; } } } throw new Exception('Should not happen'); } /** * @param array> $trace * @param array $a * @param array $b * @param array $bc * * @return list * * @psalm-pure */ protected static function extractDiff(array $trace, int $x, int $y, array $a, array $b, array $bc): array { $result = []; for ($d = count($trace) - 1; $d >= 0; --$d) { $v = $trace[$d]; $k = $x - $y; if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) { $prevK = $k + 1; } else { $prevK = $k - 1; } $prevX = $v[$prevK]; $prevY = $prevX - $prevK; while ($x > $prevX && $y > $prevY) { $result[] = new DiffElem( $bc[$x - 1] ? DiffElem::TYPE_KEEP_SIGNATURE : DiffElem::TYPE_KEEP, $a[$x - 1], $b[$y - 1] ); --$x; --$y; } if ($d === 0) { break; } while ($x > $prevX) { $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x - 1], null); --$x; } while ($y > $prevY) { $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y - 1]); --$y; } } return array_reverse($result); } }