Text_Diff_Engine_native   F
last analyzed

Complexity

Total Complexity 95

Size/Duplication

Total Lines 426
Duplicated Lines 2.58 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 11
loc 426
rs 2
c 0
b 0
f 0
wmc 95
lcom 1
cbo 4

5 Methods

Rating   Name   Duplication   Size   Complexity  
F diff() 0 98 29
F _diag() 11 79 20
A _lcsPos() 0 28 5
C _compareseq() 0 43 13
F _shiftBoundaries() 0 108 28

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Text_Diff_Engine_native often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Text_Diff_Engine_native, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Text_Diff
5
 *
6
 * General API for generating and formatting diffs - the differences between
7
 * two sequences of strings.
8
 *
9
 * The PHP diff code used in this package was originally written by Geoffrey
10
 * T. Dairiki and is used with his permission.
11
 *
12
 * $Horde: framework/Text_Diff/Diff.php,v 1.13 2005/05/26 20:26:06 selsky Exp $
13
 *
14
 * @package Text_Diff
15
 * @author  Geoffrey T. Dairiki <[email protected]>
16
 */
17
class Text_Diff
18
{
19
20
    /**
21
     * Array of changes.
22
     *
23
     * @var array
24
     */
25
    public $_edits;
26
27
    /**
28
     * Computes diffs between sequences of strings.
29
     *
30
     * @param array $from_lines An array of strings.  Typically these are
31
     *                          lines from a file.
32
     * @param array $to_lines   An array of strings.
33
     */
34
    public function __construct($from_lines, $to_lines)
35
    {
36
        array_walk($from_lines, array($this, '_trimNewlines'));
37
        array_walk($to_lines, array($this, '_trimNewlines'));
38
39
        if (extension_loaded('xdiff')) {
40
            $engine = new Text_Diff_Engine_xdiff();
41
        } else {
42
            $engine = new Text_Diff_Engine_native();
43
        }
44
45
        $this->_edits = $engine->diff($from_lines, $to_lines);
46
    }
47
48
    /**
49
     * Returns the array of differences.
50
     */
51
    public function getDiff()
52
    {
53
        return $this->_edits;
54
    }
55
56
    /**
57
     * Computes a reversed diff.
58
     *
59
     * Example:
60
     * <code>
61
     * $diff = new Text_Diff($lines1, $lines2);
62
     * $rev = $diff->reverse();
63
     * </code>
64
     *
65
     * @return Text_Diff A Diff object representing the inverse of the
66
     *                   original diff.  Note that we purposely don't return a
67
     *                   reference here, since this essentially is a clone()
68
     *                   method.
69
     */
70
    public function reverse()
71
    {
72
        if (version_compare(zend_version(), '2', '>')) {
73
            $rev = clone$obj;
0 ignored issues
show
Bug introduced by
The variable $obj does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
74
        } else {
75
            $rev = $this;
76
        }
77
        $rev->_edits = array();
78
        foreach ($this->_edits as $edit) {
79
            $rev->_edits[] = $edit->reverse();
80
        }
81
82
        return $rev;
83
    }
84
85
    /**
86
     * Checks for an empty diff.
87
     *
88
     * @return boolean True if two sequences were identical.
89
     */
90
    public function isEmpty()
91
    {
92
        foreach ($this->_edits as $edit) {
93
            if (!is_a($edit, 'Text_Diff_Op_copy')) {
94
                return false;
95
            }
96
        }
97
98
        return true;
99
    }
100
101
    /**
102
     * Computes the length of the Longest Common Subsequence (LCS).
103
     *
104
     * This is mostly for diagnostic purposes.
105
     *
106
     * @return integer The length of the LCS.
107
     */
108
    public function lcs()
109
    {
110
        $lcs = 0;
111
        foreach ($this->_edits as $edit) {
112
            if (is_a($edit, 'Text_Diff_Op_copy')) {
113
                $lcs += count($edit->orig);
114
            }
115
        }
116
117
        return $lcs;
118
    }
119
120
    /**
121
     * Gets the original set of lines.
122
     *
123
     * This reconstructs the $from_lines parameter passed to the constructor.
124
     *
125
     * @return array The original sequence of strings.
126
     */
127 View Code Duplication
    public function getOriginal()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
    {
129
        $lines = array();
130
        foreach ($this->_edits as $edit) {
131
            if ($edit->orig) {
132
                array_splice($lines, count($lines), 0, $edit->orig);
133
            }
134
        }
135
136
        return $lines;
137
    }
138
139
    /**
140
     * Gets the final set of lines.
141
     *
142
     * This reconstructs the $to_lines parameter passed to the constructor.
143
     *
144
     * @return array The sequence of strings.
145
     */
146 View Code Duplication
    public function getFinal()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
    {
148
        $lines = array();
149
        foreach ($this->_edits as $edit) {
150
            if ($edit->final) {
151
                array_splice($lines, count($lines), 0, $edit->final);
152
            }
153
        }
154
155
        return $lines;
156
    }
157
158
    /**
159
     * Removes trailing newlines from a line of text. This is meant to be used
160
     * with array_walk().
161
     *
162
     * @param string  $line The line to trim.
163
     * @param integer $key  The index of the line in the array. Not used.
164
     */
165
    public function _trimNewlines(&$line, $key)
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
166
    {
167
        $line = str_replace(array("\n", "\r"), '', $line);
168
    }
169
170
    /**
171
     * Checks a diff for validity.
172
     *
173
     * This is here only for debugging purposes.
174
     * @param $from_lines
175
     * @param $to_lines
176
     * @return bool
177
     */
178
    public function _check($from_lines, $to_lines)
179
    {
180
        if (serialize($from_lines) != serialize($this->getOriginal())) {
181
            trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
182
        }
183
        if (serialize($to_lines) != serialize($this->getFinal())) {
184
            trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
185
        }
186
187
        $rev = $this->reverse();
188
        if (serialize($to_lines) != serialize($rev->getOriginal())) {
189
            trigger_error("Reversed original doesn't match", E_USER_ERROR);
190
        }
191
        if (serialize($from_lines) != serialize($rev->getFinal())) {
192
            trigger_error("Reversed final doesn't match", E_USER_ERROR);
193
        }
194
195
        $prevtype = null;
196
        foreach ($this->_edits as $edit) {
197
            if ($prevtype == get_class($edit)) {
198
                trigger_error('Edit sequence is non-optimal', E_USER_ERROR);
199
            }
200
            $prevtype = get_class($edit);
201
        }
202
203
        return true;
204
    }
205
}
206
207
/**
208
 * $Horde: framework/Text_Diff/Diff.php,v 1.13 2005/05/26 20:26:06 selsky Exp $
209
 *
210
 * @package Text_Diff
211
 * @author  Geoffrey T. Dairiki <[email protected]>
212
 */
213
class Text_MappedDiff extends Text_Diff
214
{
215
216
    /**
217
     * Computes a diff between sequences of strings.
218
     *
219
     * This can be used to compute things like case-insensitve diffs, or diffs
220
     * which ignore changes in white-space.
221
     *
222
     * @param array $from_lines        An array of strings.
223
     * @param array $to_lines          An array of strings.
224
     * @param array $mapped_from_lines This array should have the same size
225
     *                                 number of elements as $from_lines.  The
226
     *                                 elements in $mapped_from_lines and
227
     *                                 $mapped_to_lines are what is actually
228
     *                                 compared when computing the diff.
229
     * @param array $mapped_to_lines   This array should have the same number
230
     *                                 of elements as $to_lines.
231
     */
232
    public function __construct($from_lines, $to_lines, $mapped_from_lines, $mapped_to_lines)
233
    {
234
        assert(count($from_lines) == count($mapped_from_lines));
235
        assert(count($to_lines) == count($mapped_to_lines));
236
237
        parent::__construct($mapped_from_lines, $mapped_to_lines);
238
239
        $xi = $yi = 0;
240
        for ($i = 0; $i < count($this->_edits); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
241
            $orig = &$this->_edits[$i]->orig;
242
            if (is_array($orig)) {
243
                $orig = array_slice($from_lines, $xi, count($orig));
244
                $xi += count($orig);
245
            }
246
247
            $final = &$this->_edits[$i]->final;
248
            if (is_array($final)) {
249
                $final = array_slice($to_lines, $yi, count($final));
250
                $yi += count($final);
251
            }
252
        }
253
    }
254
}
255
256
/**
257
 * Class used internally by Diff to actually compute the diffs.  This class
258
 * uses the xdiff PECL package (http://pecl.php.net/package/xdiff) to compute
259
 * the differences between the two input arrays.
260
 *
261
 * @author  Jon Parise <[email protected]>
262
 * @package Text_Diff
263
 *
264
 * @access  private
265
 */
266
class Text_Diff_Engine_xdiff
267
{
268
269
    /**
270
     * @param $from_lines
271
     * @param $to_lines
272
     * @return array
273
     */
274
    public function diff($from_lines, $to_lines)
275
    {
276
        /* Convert the two input arrays into strings for xdiff processing. */
277
        $from_string = implode("\n", $from_lines);
278
        $to_string   = implode("\n", $to_lines);
279
280
        /* Diff the two strings and convert the result to an array. */
281
        $diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
282
        $diff = explode("\n", $diff);
283
284
        /* Walk through the diff one line at a time.  We build the $edits
285
         * array of diff operations by reading the first character of the
286
         * xdiff output (which is in the "unified diff" format).
287
         *
288
         * Note that we don't have enough information to detect "changed"
289
         * lines using this approach, so we can't add Text_Diff_Op_changed
290
         * instances to the $edits array.  The result is still perfectly
291
         * valid, albeit a little less descriptive and efficient. */
292
        $edits = array();
293
        foreach ($diff as $line) {
294
            switch ($line[0]) {
295
                case ' ':
296
                    $edits[] = new Text_Diff_Op_copy(array(substr($line, 1)));
297
                    break;
298
299
                case '+':
300
                    $edits[] = new Text_Diff_Op_add(array(substr($line, 1)));
301
                    break;
302
303
                case '-':
304
                    $edits[] = new Text_Diff_Op_delete(array(substr($line, 1)));
305
                    break;
306
            }
307
        }
308
309
        return $edits;
310
    }
311
}
312
313
/**
314
 * Class used internally by Diff to actually compute the diffs.  This class is
315
 * implemented using native PHP code.
316
 *
317
 * The algorithm used here is mostly lifted from the perl module
318
 * Algorithm::Diff (version 1.06) by Ned Konz, which is available at:
319
 * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip
320
 *
321
 * More ideas are taken from:
322
 * http://www.ics.uci.edu/~eppstein/161/960229.html
323
 *
324
 * Some ideas (and a bit of code) are taken from analyze.c, of GNU
325
 * diffutils-2.7, which can be found at:
326
 * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
327
 *
328
 * Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from
329
 * Geoffrey T. Dairiki <[email protected]>. The original PHP version of this
330
 * code was written by him, and is used/adapted with his permission.
331
 *
332
 * @author  Geoffrey T. Dairiki <[email protected]>
333
 * @package Text_Diff
334
 *
335
 * @access  private
336
 */
337
class Text_Diff_Engine_native
338
{
339
340
    /**
341
     * @param $from_lines
342
     * @param $to_lines
343
     * @return array
344
     */
345
    public function diff($from_lines, $to_lines)
346
    {
347
        $n_from = count($from_lines);
348
        $n_to   = count($to_lines);
349
350
        $this->xchanged = $this->ychanged = array();
0 ignored issues
show
Bug introduced by
The property xchanged does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property ychanged does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
351
        $this->xv       = $this->yv = array();
0 ignored issues
show
Bug introduced by
The property xv does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property yv does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
352
        $this->xind     = $this->yind = array();
0 ignored issues
show
Bug introduced by
The property xind does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property yind does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
353
        unset($this->seq);
354
        unset($this->in_seq);
355
        unset($this->lcs);
356
357
        // Skip leading common lines.
358
        for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
359
            if ($from_lines[$skip] != $to_lines[$skip]) {
360
                break;
361
            }
362
            $this->xchanged[$skip] = $this->ychanged[$skip] = false;
363
        }
364
365
        // Skip trailing common lines.
366
        $xi = $n_from;
367
        $yi = $n_to;
368
        for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
369
            if ($from_lines[$xi] != $to_lines[$yi]) {
370
                break;
371
            }
372
            $this->xchanged[$xi] = $this->ychanged[$yi] = false;
373
        }
374
375
        // Ignore lines which do not exist in both files.
376
        for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
377
            $xhash[$from_lines[$xi]] = 1;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$xhash was never initialized. Although not strictly required by PHP, it is generally a good practice to add $xhash = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
378
        }
379
        for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
380
            $line = $to_lines[$yi];
381
            if ($this->ychanged[$yi] = empty($xhash[$line])) {
382
                continue;
383
            }
384
            $yhash[$line] = 1;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$yhash was never initialized. Although not strictly required by PHP, it is generally a good practice to add $yhash = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
385
            $this->yv[]   = $line;
386
            $this->yind[] = $yi;
387
        }
388
        for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
389
            $line = $from_lines[$xi];
390
            if ($this->xchanged[$xi] = empty($yhash[$line])) {
391
                continue;
392
            }
393
            $this->xv[]   = $line;
394
            $this->xind[] = $xi;
395
        }
396
397
        // Find the LCS.
398
        $this->_compareseq(0, count($this->xv), 0, count($this->yv));
399
400
        // Merge edits when possible.
401
        $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
402
        $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
403
404
        // Compute the edit operations.
405
        $edits = array();
406
        $xi    = $yi = 0;
407
        while ($xi < $n_from || $yi < $n_to) {
408
            assert($yi < $n_to || $this->xchanged[$xi]);
409
            assert($xi < $n_from || $this->ychanged[$yi]);
410
411
            // Skip matching "snake".
412
            $copy = array();
413
            while ($xi < $n_from && $yi < $n_to && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
414
                $copy[] = $from_lines[$xi++];
415
                ++$yi;
416
            }
417
            if ($copy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $copy of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
418
                $edits[] = new Text_Diff_Op_copy($copy);
419
            }
420
421
            // Find deletes & adds.
422
            $delete = array();
423
            while ($xi < $n_from && $this->xchanged[$xi]) {
424
                $delete[] = $from_lines[$xi++];
425
            }
426
427
            $add = array();
428
            while ($yi < $n_to && $this->ychanged[$yi]) {
429
                $add[] = $to_lines[$yi++];
430
            }
431
432
            if ($delete && $add) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $delete of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $add of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
433
                $edits[] = new Text_Diff_Op_change($delete, $add);
434
            } elseif ($delete) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $delete of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
435
                $edits[] = new Text_Diff_Op_delete($delete);
436
            } elseif ($add) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $add of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
437
                $edits[] = new Text_Diff_Op_add($add);
438
            }
439
        }
440
441
        return $edits;
442
    }
443
444
    /**
445
     * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF,
446
     * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized
447
     * segments.
448
     *
449
     * Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an array of
450
     * NCHUNKS+1 (X, Y) indexes giving the diving points between sub
451
     * sequences.  The first sub-sequence is contained in (X0, X1), (Y0, Y1),
452
     * the second in (X1, X2), (Y1, Y2) and so on.  Note that (X0, Y0) ==
453
     * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
454
     *
455
     * This function assumes that the first lines of the specified portions of
456
     * the two files do not match, and likewise that the last lines do not
457
     * match.  The caller must trim matching lines from the beginning and end
458
     * of the portions it is going to specify.
459
     * @param $xoff
460
     * @param $xlim
461
     * @param $yoff
462
     * @param $ylim
463
     * @param $nchunks
464
     * @return array
465
     */
466
    public function _diag($xoff, $xlim, $yoff, $ylim, $nchunks)
467
    {
468
        $flip = false;
469
470
        if ($xlim - $xoff > $ylim - $yoff) {
471
            /* Things seems faster (I'm not sure I understand why) when the
472
             * shortest sequence is in X. */
473
            $flip = true;
474
            list($xoff, $xlim, $yoff, $ylim) = array($yoff, $ylim, $xoff, $xlim);
475
        }
476
477
        if ($flip) {
478
            for ($i = $ylim - 1; $i >= $yoff; $i--) {
479
                $ymatches[$this->xv[$i]][] = $i;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ymatches was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ymatches = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
480
            }
481
        } else {
482
            for ($i = $ylim - 1; $i >= $yoff; $i--) {
483
                $ymatches[$this->yv[$i]][] = $i;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ymatches was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ymatches = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
484
            }
485
        }
486
487
        $this->lcs    = 0;
0 ignored issues
show
Bug introduced by
The property lcs does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
488
        $this->seq[0] = $yoff - 1;
0 ignored issues
show
Bug introduced by
The property seq does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
489
        $this->in_seq = array();
0 ignored issues
show
Bug introduced by
The property in_seq does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
490
        $ymids[0]     = array();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ymids was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ymids = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
491
492
        $numer = $xlim - $xoff + $nchunks - 1;
493
        $x     = $xoff;
494
        for ($chunk = 0; $chunk < $nchunks; $chunk++) {
495
            if ($chunk > 0) {
496
                for ($i = 0; $i <= $this->lcs; $i++) {
497
                    $ymids[$i][$chunk - 1] = $this->seq[$i];
498
                }
499
            }
500
501
            $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks);
502
            for (; $x < $x1; $x++) {
503
                $line = $flip ? $this->yv[$x] : $this->xv[$x];
504
                if (empty($ymatches[$line])) {
505
                    continue;
506
                }
507
                $matches = $ymatches[$line];
508
                foreach ($matches as $y) {
509 View Code Duplication
                    if (empty($this->in_seq[$y])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
510
                        $k = $this->_lcsPos($y);
511
                        assert($k > 0);
512
                        $ymids[$k] = $ymids[$k - 1];
513
                        break;
514
                    }
515
                }
516
517
                while (list($junk, $y) = each($matches)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $junk is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
518
                    if ($y > $this->seq[$k - 1]) {
519
                        assert($y < $this->seq[$k]);
0 ignored issues
show
Bug introduced by
The variable $k does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
520
                        /* Optimization: this is a common case: next match is
521
                         * just replacing previous match. */
522
                        $this->in_seq[$this->seq[$k]] = false;
523
                        $this->seq[$k]                = $y;
524
                        $this->in_seq[$y]             = 1;
525 View Code Duplication
                    } elseif (empty($this->in_seq[$y])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
526
                        $k = $this->_lcsPos($y);
527
                        assert($k > 0);
528
                        $ymids[$k] = $ymids[$k - 1];
529
                    }
530
                }
531
            }
532
        }
533
534
        $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$seps was never initialized. Although not strictly required by PHP, it is generally a good practice to add $seps = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
535
        $ymid   = $ymids[$this->lcs];
536
        for ($n = 0; $n < $nchunks - 1; $n++) {
537
            $x1     = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
538
            $y1     = $ymid[$n] + 1;
539
            $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
540
        }
541
        $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
542
543
        return array($this->lcs, $seps);
544
    }
545
546
    /**
547
     * @param $ypos
548
     * @return int
549
     */
550
    public function _lcsPos($ypos)
551
    {
552
        $end = $this->lcs;
553
        if ($end == 0 || $ypos > $this->seq[$end]) {
554
            $this->seq[++$this->lcs] = $ypos;
555
            $this->in_seq[$ypos]     = 1;
556
557
            return $this->lcs;
558
        }
559
560
        $beg = 1;
561
        while ($beg < $end) {
562
            $mid = (int)(($beg + $end) / 2);
563
            if ($ypos > $this->seq[$mid]) {
564
                $beg = $mid + 1;
565
            } else {
566
                $end = $mid;
567
            }
568
        }
569
570
        assert($ypos != $this->seq[$end]);
571
572
        $this->in_seq[$this->seq[$end]] = false;
573
        $this->seq[$end]                = $ypos;
574
        $this->in_seq[$ypos]            = 1;
575
576
        return $end;
577
    }
578
579
    /**
580
     * Finds LCS of two sequences.
581
     *
582
     * The results are recorded in the vectors $this->{x,y}changed[], by
583
     * storing a 1 in the element for each line that is an insertion or
584
     * deletion (ie. is not in the LCS).
585
     *
586
     * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1.
587
     *
588
     * Note that XLIM, YLIM are exclusive bounds.  All line numbers are
589
     * origin-0 and discarded lines are not counted.
590
     * @param $xoff
591
     * @param $xlim
592
     * @param $yoff
593
     * @param $ylim
594
     */
595
    public function _compareseq($xoff, $xlim, $yoff, $ylim)
596
    {
597
        /* Slide down the bottom initial diagonal. */
598
        while ($xoff < $xlim && $yoff < $ylim && $this->xv[$xoff] == $this->yv[$yoff]) {
599
            ++$xoff;
600
            ++$yoff;
601
        }
602
603
        /* Slide up the top initial diagonal. */
604
        while ($xlim > $xoff && $ylim > $yoff && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
605
            --$xlim;
606
            --$ylim;
607
        }
608
609
        if ($xoff == $xlim || $yoff == $ylim) {
610
            $lcs = 0;
611
        } else {
612
            /* This is ad hoc but seems to work well.  $nchunks =
613
             * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks =
614
             * max(2,min(8,(int)$nchunks)); */
615
            $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
616
            list($lcs, $seps) = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
617
        }
618
619
        if ($lcs == 0) {
620
            /* X and Y sequences have no common subsequence: mark all
621
             * changed. */
622
            while ($yoff < $ylim) {
623
                $this->ychanged[$this->yind[$yoff++]] = 1;
624
            }
625
            while ($xoff < $xlim) {
626
                $this->xchanged[$this->xind[$xoff++]] = 1;
627
            }
628
        } else {
629
            /* Use the partitions to split this problem into subproblems. */
630
            reset($seps);
631
            $pt1 = $seps[0];
632
            while ($pt2 = next($seps)) {
633
                $this->_compareseq($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
634
                $pt1 = $pt2;
635
            }
636
        }
637
    }
638
639
    /**
640
     * Adjusts inserts/deletes of identical lines to join changes as much as
641
     * possible.
642
     *
643
     * We do something when a run of changed lines include a line at one end
644
     * and has an excluded, identical line at the other.  We are free to
645
     * choose which identical line is included.  `compareseq' usually chooses
646
     * the one at the beginning, but usually it is cleaner to consider the
647
     * following identical line to be the "change".
648
     *
649
     * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
650
     * @param $lines
651
     * @param $changed
652
     * @param $other_changed
653
     */
654
    public function _shiftBoundaries($lines, &$changed, $other_changed)
655
    {
656
        $i = 0;
657
        $j = 0;
658
659
        assert('count($lines) == count($changed)');
660
        $len       = count($lines);
661
        $other_len = count($other_changed);
662
663
        while (1) {
664
            /* Scan forward to find the beginning of another run of
665
             * changes. Also keep track of the corresponding point in the
666
             * other file.
667
             *
668
             * Throughout this code, $i and $j are adjusted together so that
669
             * the first $i elements of $changed and the first $j elements of
670
             * $other_changed both contain the same number of zeros (unchanged
671
             * lines).
672
             *
673
             * Furthermore, $j is always kept so that $j == $other_len or
674
             * $other_changed[$j] == false. */
675
            while ($j < $other_len && $other_changed[$j]) {
676
                $j++;
677
            }
678
679
            while ($i < $len && !$changed[$i]) {
680
                assert('$j < $other_len && ! $other_changed[$j]');
681
                $i++;
682
                $j++;
683
                while ($j < $other_len && $other_changed[$j]) {
684
                    $j++;
685
                }
686
            }
687
688
            if ($i == $len) {
689
                break;
690
            }
691
692
            $start = $i;
693
694
            /* Find the end of this run of changes. */
695
            while (++$i < $len && $changed[$i]) {
696
                continue;
697
            }
698
699
            do {
700
                /* Record the length of this run of changes, so that we can
701
                 * later determine whether the run has grown. */
702
                $runlength = $i - $start;
703
704
                /* Move the changed region back, so long as the previous
705
                 * unchanged line matches the last changed one.  This merges
706
                 * with previous changed regions. */
707
                while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
708
                    $changed[--$start] = 1;
709
                    $changed[--$i]     = false;
710
                    while ($start > 0 && $changed[$start - 1]) {
711
                        $start--;
712
                    }
713
                    assert('$j > 0');
714
                    while ($other_changed[--$j]) {
715
                        continue;
716
                    }
717
                    assert('$j >= 0 && !$other_changed[$j]');
718
                }
719
720
                /* Set CORRESPONDING to the end of the changed run, at the
721
                 * last point where it corresponds to a changed run in the
722
                 * other file. CORRESPONDING == LEN means no such point has
723
                 * been found. */
724
                $corresponding = $j < $other_len ? $i : $len;
725
726
                /* Move the changed region forward, so long as the first
727
                 * changed line matches the following unchanged one.  This
728
                 * merges with following changed regions.  Do this second, so
729
                 * that if there are no merges, the changed region is moved
730
                 * forward as far as possible. */
731
                while ($i < $len && $lines[$start] == $lines[$i]) {
732
                    $changed[$start++] = false;
733
                    $changed[$i++]     = 1;
734
                    while ($i < $len && $changed[$i]) {
735
                        $i++;
736
                    }
737
738
                    assert('$j < $other_len && ! $other_changed[$j]');
739
                    $j++;
740
                    if ($j < $other_len && $other_changed[$j]) {
741
                        $corresponding = $i;
742
                        while ($j < $other_len && $other_changed[$j]) {
743
                            $j++;
744
                        }
745
                    }
746
                }
747
            } while ($runlength != $i - $start);
748
749
            /* If possible, move the fully-merged run of changes back to a
750
             * corresponding run in the other file. */
751
            while ($corresponding < $i) {
752
                $changed[--$start] = 1;
753
                $changed[--$i]     = 0;
754
                assert('$j > 0');
755
                while ($other_changed[--$j]) {
756
                    continue;
757
                }
758
                assert('$j >= 0 && !$other_changed[$j]');
759
            }
760
        }
761
    }
762
}
763
764
/**
765
 * @package Text_Diff
766
 * @author  Geoffrey T. Dairiki <[email protected]>
767
 *
768
 * @access  private
769
 */
770
class Text_Diff_Op
771
{
772
773
    public $orig;
774
    public $final;
775
776
    public function reverse()
777
    {
778
        trigger_error('Abstract method', E_USER_ERROR);
779
    }
780
781
    /**
782
     * @return int
783
     */
784
    public function norig()
785
    {
786
        return $this->orig ? count($this->orig) : 0;
787
    }
788
789
    /**
790
     * @return int
791
     */
792
    public function nfinal()
793
    {
794
        return $this->final ? count($this->final) : 0;
795
    }
796
}
797
798
/**
799
 * @package Text_Diff
800
 * @author  Geoffrey T. Dairiki <[email protected]>
801
 *
802
 * @access  private
803
 */
804
class Text_Diff_Op_copy extends Text_Diff_Op
805
{
806
807
    /**
808
     * Text_Diff_Op_copy constructor.
809
     * @param      $orig
810
     * @param bool $final
811
     */
812
    public function __construct($orig, $final = false)
813
    {
814
        if (!is_array($final)) {
815
            $final = $orig;
816
        }
817
        $this->orig  = $orig;
818
        $this->final = $final;
819
    }
820
821
    /**
822
     * @return Text_Diff_Op_copy
823
     */
824
    public function &reverse()
825
    {
826
        return $reverse = new Text_Diff_Op_copy($this->final, $this->orig);
0 ignored issues
show
Unused Code introduced by
$reverse is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
827
    }
828
}
829
830
/**
831
 * @package Text_Diff
832
 * @author  Geoffrey T. Dairiki <[email protected]>
833
 *
834
 * @access  private
835
 */
836 View Code Duplication
class Text_Diff_Op_delete extends Text_Diff_Op
0 ignored issues
show
Duplication introduced by
This class seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
837
{
838
839
    /**
840
     * Text_Diff_Op_delete constructor.
841
     * @param $lines
842
     */
843
    public function __construct($lines)
844
    {
845
        $this->orig  = $lines;
846
        $this->final = false;
847
    }
848
849
    /**
850
     * @return Text_Diff_Op_add
851
     */
852
    public function &reverse()
853
    {
854
        return $reverse = new Text_Diff_Op_add($this->orig);
0 ignored issues
show
Unused Code introduced by
$reverse is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
855
    }
856
}
857
858
/**
859
 * @package Text_Diff
860
 * @author  Geoffrey T. Dairiki <[email protected]>
861
 *
862
 * @access  private
863
 */
864 View Code Duplication
class Text_Diff_Op_add extends Text_Diff_Op
0 ignored issues
show
Duplication introduced by
This class seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
865
{
866
867
    /**
868
     * Text_Diff_Op_add constructor.
869
     * @param $lines
870
     */
871
    public function __construct($lines)
872
    {
873
        $this->final = $lines;
874
        $this->orig  = false;
875
    }
876
877
    /**
878
     * @return Text_Diff_Op_delete
879
     */
880
    public function &reverse()
881
    {
882
        return $reverse = new Text_Diff_Op_delete($this->final);
0 ignored issues
show
Unused Code introduced by
$reverse is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
883
    }
884
}
885
886
/**
887
 * @package Text_Diff
888
 * @author  Geoffrey T. Dairiki <[email protected]>
889
 *
890
 * @access  private
891
 */
892
class Text_Diff_Op_change extends Text_Diff_Op
893
{
894
895
    /**
896
     * Text_Diff_Op_change constructor.
897
     * @param $orig
898
     * @param $final
899
     */
900
    public function __construct($orig, $final)
901
    {
902
        $this->orig  = $orig;
903
        $this->final = $final;
904
    }
905
906
    /**
907
     * @return Text_Diff_Op_change
908
     */
909
    public function &reverse()
910
    {
911
        return $reverse = new Text_Diff_Op_change($this->final, $this->orig);
0 ignored issues
show
Unused Code introduced by
$reverse is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
912
    }
913
}
914