/**
     * Calculates the difference between two objects or arrays and renders them to a html
     * based page in human-readable format. Currently only works reliably on arrays.
     *
     * @version 1.0
     * @since 1.0
     * @author http://ca2.php.net/manual/en/function.var-dump.php#92594
     *
     * @param array/object &$from | From object
     * @param array/object &$to | To object
     */
    public static function diff(&$from_ref, &$to_ref, $granularity = 2)
    {
        $from = self::string_dump($from_ref, '$');
        $to = self::string_dump($to_ref, '$');
        $pre = "<pre style='margin: 0px 0px 10px 0px; display: block; background: white; color: black; font-family:";
        $pre .= " Verdana; border: 1px solid #cccccc; padding: 5px; font-size: 10px; line-height: 13px; white-space:pre;'>";
        echo $pre;
        $start_time = gettimeofday(true);
        $granularityStacks = array(FOX_fineDiff::$paragraphGranularity, FOX_fineDiff::$sentenceGranularity, FOX_fineDiff::$wordGranularity, FOX_fineDiff::$characterGranularity);
        $diff = new FOX_fineDiff($from, $to, $granularityStacks[$granularity]);
        $rendered_diff = $diff->renderDiffToHTML();
        ?>
		    <div class="panecontainer" style="white-space:nowrap;">
			    <div id="htmldiff">
				    <div class="pane" style="white-space:nowrap;"><?php 
        echo $rendered_diff;
        ?>
</div>
			    </div>
		    </div>

		<?php 
    }
 /**
  * This is the core algorithm which actually perform the diff itself,
  * fragmenting the strings as per specified delimiters.
  *
  * This function is naturally recursive, however for performance purpose
  * a local job queue is used instead of outright recursivity.
  */
 private static function doFragmentDiff($from_text, $to_text, $delimiters)
 {
     // Empty delimiter means character-level diffing.
     // In such case, use code path optimized for character-level diffing.
     if (empty($delimiters)) {
         return FOX_fineDiff::doCharDiff($from_text, $to_text);
     }
     $result = array();
     // fragment-level diffing
     $from_text_len = strlen($from_text);
     $to_text_len = strlen($to_text);
     $from_fragments = FOX_fineDiff::extractFragments($from_text, $delimiters);
     $to_fragments = FOX_fineDiff::extractFragments($to_text, $delimiters);
     $jobs = array(array(0, $from_text_len, 0, $to_text_len));
     $cached_array_keys = array();
     while ($job = array_pop($jobs)) {
         // get the segments which must be diff'ed
         list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job;
         // catch easy cases first
         $from_segment_length = $from_segment_end - $from_segment_start;
         $to_segment_length = $to_segment_end - $to_segment_start;
         if (!$from_segment_length || !$to_segment_length) {
             if ($from_segment_length) {
                 $result[$from_segment_start * 4] = new FOX_fineDiffDeleteOp($from_segment_length);
             } elseif ($to_segment_length) {
                 $result[$from_segment_start * 4 + 1] = new FOX_fineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_length));
             }
             continue;
         }
         // find longest copy operation for the current segments
         $best_copy_length = 0;
         $from_base_fragment_index = $from_segment_start;
         $cached_array_keys_for_current_segment = array();
         while ($from_base_fragment_index < $from_segment_end) {
             $from_base_fragment = $from_fragments[$from_base_fragment_index];
             $from_base_fragment_length = strlen($from_base_fragment);
             // performance boost: cache array keys
             if (!isset($cached_array_keys_for_current_segment[$from_base_fragment])) {
                 if (!isset($cached_array_keys[$from_base_fragment])) {
                     $to_all_fragment_indices = $cached_array_keys[$from_base_fragment] = array_keys($to_fragments, $from_base_fragment, true);
                 } else {
                     $to_all_fragment_indices = $cached_array_keys[$from_base_fragment];
                 }
                 // get only indices which falls within current segment
                 if ($to_segment_start > 0 || $to_segment_end < $to_text_len) {
                     $to_fragment_indices = array();
                     foreach ($to_all_fragment_indices as $to_fragment_index) {
                         if ($to_fragment_index < $to_segment_start) {
                             continue;
                         }
                         if ($to_fragment_index >= $to_segment_end) {
                             break;
                         }
                         $to_fragment_indices[] = $to_fragment_index;
                     }
                     $cached_array_keys_for_current_segment[$from_base_fragment] = $to_fragment_indices;
                 } else {
                     $to_fragment_indices = $to_all_fragment_indices;
                 }
             } else {
                 $to_fragment_indices = $cached_array_keys_for_current_segment[$from_base_fragment];
             }
             // iterate through collected indices
             foreach ($to_fragment_indices as $to_base_fragment_index) {
                 $fragment_index_offset = $from_base_fragment_length;
                 // iterate until no more match
                 for (;;) {
                     $fragment_from_index = $from_base_fragment_index + $fragment_index_offset;
                     if ($fragment_from_index >= $from_segment_end) {
                         break;
                     }
                     $fragment_to_index = $to_base_fragment_index + $fragment_index_offset;
                     if ($fragment_to_index >= $to_segment_end) {
                         break;
                     }
                     if ($from_fragments[$fragment_from_index] !== $to_fragments[$fragment_to_index]) {
                         break;
                     }
                     $fragment_length = strlen($from_fragments[$fragment_from_index]);
                     $fragment_index_offset += $fragment_length;
                 }
                 if ($fragment_index_offset > $best_copy_length) {
                     $best_copy_length = $fragment_index_offset;
                     $best_from_start = $from_base_fragment_index;
                     $best_to_start = $to_base_fragment_index;
                 }
             }
             $from_base_fragment_index += strlen($from_base_fragment);
             // If match is larger than half segment size, no point trying to find better
             // TODO: Really?
             if ($best_copy_length >= $from_segment_length / 2) {
                 break;
             }
             // no point to keep looking if what is left is less than
             // current best match
             if ($from_base_fragment_index + $best_copy_length >= $from_segment_end) {
                 break;
             }
         }
         if ($best_copy_length) {
             $jobs[] = array($from_segment_start, $best_from_start, $to_segment_start, $best_to_start);
             $result[$best_from_start * 4 + 2] = new FOX_fineDiffCopyOp($best_copy_length);
             $jobs[] = array($best_from_start + $best_copy_length, $from_segment_end, $best_to_start + $best_copy_length, $to_segment_end);
         } else {
             $result[$from_segment_start * 4] = new FOX_fineDiffReplaceOp($from_segment_length, substr($to_text, $to_segment_start, $to_segment_length));
         }
     }
     ksort($result, SORT_NUMERIC);
     return array_values($result);
 }