/**
 * Compares two files based initially on lines and then on words within the lines that
 * differ.
 * @param array $lines1 Array of ouwiki_line
 * @param array $lines2 Array of ouwiki_line
 * @return array (deleted,added); deleted and added are arrays of ouwiki_word with
 *   position numbers from $lines1 and $lines2 respectively 
 */
function ouwiki_diff_words($lines1, $lines2)
{
    // Prepare arrays
    $deleted = array();
    $added = array();
    // Get line difference
    $linediff = ouwiki_diff(ouwiki_line::get_as_strings($lines1), ouwiki_line::get_as_strings($lines2));
    // Handle lines that were entirely deleted
    foreach ($linediff->deletes as $deletedline) {
        $deleted = array_merge($deleted, $lines1[$deletedline]->words);
    }
    // And ones that were entirely added
    foreach ($linediff->adds as $addedline) {
        $added = array_merge($added, $lines2[$addedline]->words);
    }
    // Changes get diffed at the individual-word level
    foreach ($linediff->changes as $changerange) {
        // Build list of all words in each side of the range
        $file1words = array();
        for ($index = $changerange->file1start; $index < $changerange->file1start + $changerange->file1count; $index++) {
            foreach ($lines1[$index]->words as $word) {
                $file1words[] = $word;
            }
        }
        $file2words = array();
        for ($index = $changerange->file2start; $index < $changerange->file2start + $changerange->file2count; $index++) {
            foreach ($lines2[$index]->words as $word) {
                $file2words[] = $word;
            }
        }
        // Make arrays 1-based
        array_unshift($file1words, 'dummy');
        unset($file1words[0]);
        array_unshift($file2words, 'dummy');
        unset($file2words[0]);
        // Convert word lists into plain strings
        $file1strings = array();
        foreach ($file1words as $index => $word) {
            $file1strings[$index] = $word->word;
        }
        $file2strings = array();
        foreach ($file2words as $index => $word) {
            $file2strings[$index] = $word->word;
        }
        // Run diff on strings
        $worddiff = ouwiki_diff($file1strings, $file2strings);
        foreach ($worddiff->adds as $index) {
            $added[] = $file2words[$index];
        }
        foreach ($worddiff->deletes as $index) {
            $deleted[] = $file1words[$index];
        }
        foreach ($worddiff->changes as $changerange) {
            for ($index = $changerange->file1start; $index < $changerange->file1start + $changerange->file1count; $index++) {
                $deleted[] = $file1words[$index];
            }
            for ($index = $changerange->file2start; $index < $changerange->file2start + $changerange->file2count; $index++) {
                $added[] = $file2words[$index];
            }
        }
    }
    return array($deleted, $added);
}
 function test_diff_changes()
 {
     // Initial file for comparison (same for all examples)
     $file1 = array(1 => 'a', 'b', 'c', 'd', 'e', 'f', 'g');
     // Add text at beginning
     $file2 = array(1 => '0', '1', 'a', 'b', 'c', 'd', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual($result->changes, array());
     $this->assertEqual($result->adds, array(1, 2));
     // Add text at end
     $file2 = array(1 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', '0', '1');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual($result->changes, array());
     $this->assertEqual($result->adds, array(8, 9));
     // Add text in middle
     $file2 = array(1 => 'a', 'b', 'c', '0', '1', 'd', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual($result->changes, array());
     $this->assertEqual($result->adds, array(4, 5));
     // Delete text at beginning
     $file2 = array(1 => 'c', 'd', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array(1, 2));
     $this->assertEqual($result->changes, array());
     $this->assertEqual($result->adds, array());
     // Delete text at end
     $file2 = array(1 => 'a', 'b', 'c', 'd', 'e');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array(6, 7));
     $this->assertEqual($result->changes, array());
     $this->assertEqual($result->adds, array());
     // Delete text in middle
     $file2 = array(1 => 'a', 'b', 'c', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array(4, 5));
     $this->assertEqual($result->changes, array());
     $this->assertEqual($result->adds, array());
     // Change text in middle (one line)
     $file2 = array(1 => 'a', 'b', 'frog', 'd', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual(count($result->changes), 1);
     $this->assertEqual(array_values((array) $result->changes[0]), array(3, 1, 3, 1));
     $this->assertEqual($result->adds, array());
     // Change text in middle (two lines)
     $file2 = array(1 => 'a', 'b', 'frog', 'toad', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual(count($result->changes), 1);
     $this->assertEqual(array_values((array) $result->changes[0]), array(3, 2, 3, 2));
     $this->assertEqual($result->adds, array());
     // Change text in middle (one line -> two)
     $file2 = array(1 => 'a', 'b', 'frog', 'toad', 'd', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual(count($result->changes), 1);
     $this->assertEqual(array_values((array) $result->changes[0]), array(3, 1, 3, 2));
     $this->assertEqual($result->adds, array());
     // Change text in middle (two lines -> one)
     $file2 = array(1 => 'a', 'b', 'frog', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual(count($result->changes), 1);
     $this->assertEqual(array_values((array) $result->changes[0]), array(3, 2, 3, 1));
     $this->assertEqual($result->adds, array());
     // Two changes
     $file2 = array(1 => 'a', 'frog', 'toad', 'c', 'd', 'zombie', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual(count($result->changes), 2);
     $this->assertEqual(array_values((array) $result->changes[0]), array(2, 1, 2, 2));
     $this->assertEqual(array_values((array) $result->changes[1]), array(5, 2, 6, 1));
     $this->assertEqual($result->adds, array());
     // Changes at ends
     $file2 = array(1 => 'ant', 'frog', 'toad', 'c', 'd', 'zombie');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array());
     $this->assertEqual(count($result->changes), 2);
     $this->assertEqual(array_values((array) $result->changes[0]), array(1, 2, 1, 3));
     $this->assertEqual(array_values((array) $result->changes[1]), array(5, 3, 6, 1));
     $this->assertEqual($result->adds, array());
     // A change, a delete, an add
     $file2 = array(1 => 'ant', 'b', 'd', 'zombie', 'e', 'f', 'g');
     $result = ouwiki_diff($file1, $file2);
     $this->assertEqual($result->deletes, array(3));
     $this->assertEqual(count($result->changes), 1);
     $this->assertEqual(array_values((array) $result->changes[0]), array(1, 1, 1, 1));
     $this->assertEqual($result->adds, array(4));
 }