34 array_walk($from_lines, array($this,
'_trimNewlines'));
35 array_walk($to_lines, array($this,
'_trimNewlines'));
37 if (extension_loaded(
'xdiff')) {
43 $this->_edits = $engine->diff($from_lines, $to_lines);
72 $rev->_edits = array();
73 foreach ($this->_edits as $edit) {
74 $rev->_edits[] = $edit->reverse();
86 foreach ($this->_edits as $edit) {
104 foreach ($this->_edits as $edit) {
106 $lcs += count($edit->orig);
122 foreach ($this->_edits as $edit) {
124 array_splice($lines, count($lines), 0, $edit->orig);
140 foreach ($this->_edits as $edit) {
142 array_splice($lines, count($lines), 0, $edit->final);
157 $line = str_replace(array(
"\n",
"\r"),
'', $line);
167 if (serialize($from_lines) != serialize($this->
getOriginal())) {
168 trigger_error(
"Reconstructed original doesn't match", E_USER_ERROR);
170 if (serialize($to_lines) != serialize($this->
getFinal())) {
171 trigger_error(
"Reconstructed final doesn't match", E_USER_ERROR);
175 if (serialize($to_lines) != serialize($rev->getOriginal())) {
176 trigger_error(
"Reversed original doesn't match", E_USER_ERROR);
178 if (serialize($from_lines) != serialize($rev->getFinal())) {
179 trigger_error(
"Reversed final doesn't match", E_USER_ERROR);
183 foreach ($this->_edits as $edit) {
184 if ($prevtype == get_class($edit)) {
185 trigger_error(
"Edit sequence is non-optimal", E_USER_ERROR);
187 $prevtype = get_class($edit);
220 $mapped_from_lines, $mapped_to_lines)
222 assert(count($from_lines) == count($mapped_from_lines));
223 assert(count($to_lines) == count($mapped_to_lines));
228 for ($i = 0; $i < count($this->_edits); $i++) {
229 $orig = &$this->_edits[$i]->orig;
230 if (is_array($orig)) {
231 $orig = array_slice($from_lines, $xi, count($orig));
235 $final = &$this->_edits[$i]->final;
236 if (is_array($final)) {
237 $final = array_slice($to_lines, $yi, count($final));
238 $yi += count($final);
257 function diff($from_lines, $to_lines)
260 $from_string = implode(
"\n", $from_lines);
261 $to_string = implode(
"\n", $to_lines);
264 $diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
265 $diff = explode(
"\n", $diff);
276 foreach ($diff as $line) {
323 function diff($from_lines, $to_lines)
325 $n_from = count($from_lines);
326 $n_to = count($to_lines);
328 $this->xchanged = $this->ychanged = array();
329 $this->xv = $this->yv = array();
330 $this->xind = $this->yind = array();
332 unset($this->in_seq);
336 for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
337 if ($from_lines[$skip] != $to_lines[$skip]) {
340 $this->xchanged[$skip] = $this->ychanged[$skip] =
false;
344 $xi = $n_from; $yi = $n_to;
345 for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
346 if ($from_lines[$xi] != $to_lines[$yi]) {
349 $this->xchanged[$xi] = $this->ychanged[$yi] =
false;
353 for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
354 $xhash[$from_lines[$xi]] = 1;
356 for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
357 $line = $to_lines[$yi];
358 if (($this->ychanged[$yi] = empty($xhash[$line]))) {
365 for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
366 $line = $from_lines[$xi];
367 if (($this->xchanged[$xi] = empty($yhash[$line]))) {
375 $this->
_compareseq(0, count($this->xv), 0, count($this->yv));
384 while ($xi < $n_from || $yi < $n_to) {
385 assert($yi < $n_to || $this->xchanged[$xi]);
386 assert($xi < $n_from || $this->ychanged[$yi]);
390 while ($xi < $n_from && $yi < $n_to
391 && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
392 $copy[] = $from_lines[$xi++];
401 while ($xi < $n_from && $this->xchanged[$xi]) {
402 $delete[] = $from_lines[$xi++];
406 while ($yi < $n_to && $this->ychanged[$yi]) {
407 $add[] = $to_lines[$yi++];
410 if ($delete && $add) {
438 function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
442 if ($xlim - $xoff > $ylim - $yoff) {
446 list ($xoff, $xlim, $yoff, $ylim)
447 = array($yoff, $ylim, $xoff, $xlim);
451 for ($i = $ylim - 1; $i >= $yoff; $i--) {
452 $ymatches[$this->xv[$i]][] = $i;
455 for ($i = $ylim - 1; $i >= $yoff; $i--) {
456 $ymatches[$this->yv[$i]][] = $i;
461 $this->seq[0]= $yoff - 1;
462 $this->in_seq = array();
465 $numer = $xlim - $xoff + $nchunks - 1;
467 for ($chunk = 0; $chunk < $nchunks; $chunk++) {
469 for ($i = 0; $i <= $this->lcs; $i++) {
470 $ymids[$i][$chunk - 1] = $this->seq[$i];
474 $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
475 for (; $x < $x1; $x++) {
476 $line = $flip ? $this->yv[$x] : $this->xv[$x];
477 if (empty($ymatches[$line])) {
480 $matches = $ymatches[$line];
481 foreach ($matches as $y) {
482 if (empty($this->in_seq[$y])) {
485 $ymids[$k] = $ymids[$k - 1];
490 while (list($junk, $y) = each($matches)) {
491 if ($y > $this->seq[$k - 1]) {
492 assert($y < $this->seq[$k]);
495 $this->in_seq[$this->seq[$k]] =
false;
497 $this->in_seq[$y] = 1;
498 } elseif (empty($this->in_seq[$y])) {
501 $ymids[$k] = $ymids[$k - 1];
507 $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
508 $ymid = $ymids[$this->lcs];
509 for (
$n = 0;
$n < $nchunks - 1;
$n++) {
510 $x1 = $xoff + (int)(($numer + ($xlim - $xoff) *
$n) / $nchunks);
512 $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
514 $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
516 return array($this->lcs, $seps);
522 if ($end == 0 || $ypos > $this->seq[$end]) {
523 $this->seq[++$this->lcs] = $ypos;
524 $this->in_seq[$ypos] = 1;
529 while ($beg < $end) {
530 $mid = (int)(($beg + $end) / 2);
531 if ($ypos > $this->seq[$mid]) {
538 assert($ypos != $this->seq[$end]);
540 $this->in_seq[$this->seq[$end]] =
false;
541 $this->seq[$end] = $ypos;
542 $this->in_seq[$ypos] = 1;
561 while ($xoff < $xlim && $yoff < $ylim
562 && $this->xv[$xoff] == $this->yv[$yoff]) {
568 while ($xlim > $xoff && $ylim > $yoff
569 && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
574 if ($xoff == $xlim || $yoff == $ylim) {
580 $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
582 = $this->
_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
588 while ($yoff < $ylim) {
589 $this->ychanged[$this->yind[$yoff++]] = 1;
591 while ($xoff < $xlim) {
592 $this->xchanged[$this->xind[$xoff++]] = 1;
598 while ($pt2 = next($seps)) {
599 $this->
_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
622 assert(
'count($lines) == count($changed)');
623 $len = count($lines);
624 $other_len = count($other_changed);
638 while ($j < $other_len && $other_changed[$j]) {
642 while ($i < $len && !
$changed[$i]) {
643 assert(
'$j < $other_len && ! $other_changed[$j]');
645 while ($j < $other_len && $other_changed[$j]) {
657 while (++$i < $len &&
$changed[$i]) {
664 $runlength = $i - $start;
669 while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
672 while ($start > 0 &&
$changed[$start - 1]) {
676 while ($other_changed[--$j]) {
679 assert(
'$j >= 0 && !$other_changed[$j]');
686 $corresponding = $j < $other_len ? $i : $len;
693 while ($i < $len && $lines[$start] == $lines[$i]) {
700 assert(
'$j < $other_len && ! $other_changed[$j]');
702 if ($j < $other_len && $other_changed[$j]) {
704 while ($j < $other_len && $other_changed[$j]) {
709 }
while ($runlength != $i - $start);
713 while ($corresponding < $i) {
717 while ($other_changed[--$j]) {
720 assert(
'$j >= 0 && !$other_changed[$j]');
740 trigger_error(
'Abstract method', E_USER_ERROR);
745 return $this->orig ? count($this->orig) : 0;
750 return $this->
final ? count($this->
final) : 0;
789 $this->orig = $lines;
790 $this->
final =
false;
810 $this->
final = $lines;