/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/_patiencediff_c.c

  • Committer: Aaron Bentley
  • Date: 2008-10-16 21:47:05 UTC
  • mfrom: (3782 +trunk)
  • mto: (0.14.25 prepare-shelf)
  • mto: This revision was merged to the branch mainline in revision 3820.
  • Revision ID: aaron@aaronbentley.com-20081016214705-9y3mj4era3zw3skh
Merge with bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (C) 2007 Canonical Ltd
 
3
 
 
4
 This program is free software; you can redistribute it and/or modify
 
5
 it under the terms of the GNU General Public License as published by
 
6
 the Free Software Foundation; either version 2 of the License, or
 
7
 (at your option) any later version.
 
8
 
 
9
 This program is distributed in the hope that it will be useful,
 
10
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 GNU General Public License for more details.
 
13
 
 
14
 You should have received a copy of the GNU General Public License
 
15
 along with this program; if not, write to the Free Software
 
16
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
 Function equate_lines based on bdiff.c from Mercurial.
 
19
   Copyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>
 
20
 
 
21
 Functions unique_lcs/recurse_matches based on _patiencediff_py.py.
 
22
   Copyright (C) 2005 Bram Cohen, Copyright (C) 2005, 2006 Canonical Ltd
 
23
*/
 
24
 
 
25
 
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <Python.h>
 
29
 
 
30
#include "python-compat.h"
 
31
 
 
32
 
 
33
#if defined(__GNUC__)
 
34
#   define inline __inline__
 
35
#elif defined(_MSC_VER)
 
36
#   define inline __inline
 
37
#else
 
38
#   define inline
 
39
#endif
 
40
 
 
41
 
 
42
#define MIN(a, b) (((a) > (b)) ? (b) : (a))
 
43
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
 
44
 
 
45
 
 
46
#define SENTINEL -1
 
47
 
 
48
 
 
49
enum {
 
50
    OP_EQUAL = 0,
 
51
    OP_INSERT,
 
52
    OP_DELETE,
 
53
    OP_REPLACE
 
54
};
 
55
 
 
56
 
 
57
/* values from this array need to correspont to the order of the enum above */
 
58
static char *opcode_names[] = {
 
59
    "equal",
 
60
    "insert",
 
61
    "delete",
 
62
    "replace",
 
63
};
 
64
 
 
65
 
 
66
struct line {
 
67
    long hash;         /* hash code of the string/object */
 
68
    Py_ssize_t next;   /* next line from the same equivalence class */
 
69
    Py_ssize_t equiv;  /* equivalence class */
 
70
    PyObject *data;
 
71
};
 
72
 
 
73
 
 
74
struct bucket {
 
75
    Py_ssize_t a_head;  /* first item in `a` from this equivalence class */
 
76
    Py_ssize_t a_count;
 
77
    Py_ssize_t b_head;  /* first item in `b` from this equivalence class */
 
78
    Py_ssize_t b_count;
 
79
    Py_ssize_t a_pos;
 
80
    Py_ssize_t b_pos;
 
81
};
 
82
 
 
83
 
 
84
struct hashtable {
 
85
    Py_ssize_t last_a_pos;
 
86
    Py_ssize_t last_b_pos;
 
87
    Py_ssize_t size;
 
88
    struct bucket *table;
 
89
};
 
90
 
 
91
struct matching_line {
 
92
    Py_ssize_t a;     /* index of the line in `a` */
 
93
    Py_ssize_t b;     /* index of the line in `b` */
 
94
};
 
95
 
 
96
 
 
97
struct matching_block {
 
98
    Py_ssize_t a;     /* index of the first line in `a` */
 
99
    Py_ssize_t b;     /* index of the first line in `b` */
 
100
    Py_ssize_t len;   /* length of the block */
 
101
};
 
102
 
 
103
 
 
104
struct matching_blocks {
 
105
    struct matching_block *matches;
 
106
    Py_ssize_t count;
 
107
};
 
108
 
 
109
 
 
110
struct opcode {
 
111
    int tag;
 
112
    Py_ssize_t i1;
 
113
    Py_ssize_t i2;
 
114
    Py_ssize_t j1;
 
115
    Py_ssize_t j2;
 
116
};
 
117
 
 
118
 
 
119
typedef struct {
 
120
    PyObject_HEAD
 
121
    Py_ssize_t asize;
 
122
    Py_ssize_t bsize;
 
123
    struct line *a;
 
124
    struct line *b;
 
125
    struct hashtable hashtable;
 
126
    Py_ssize_t *backpointers;
 
127
} PatienceSequenceMatcher;
 
128
 
 
129
 
 
130
static inline Py_ssize_t
 
131
bisect_left(Py_ssize_t *list, Py_ssize_t item, Py_ssize_t lo, Py_ssize_t hi)
 
132
{
 
133
    while (lo < hi) {
 
134
        Py_ssize_t mid = lo / 2 + hi / 2 + (lo % 2 + hi % 2) / 2;
 
135
        if (list[mid] < item)
 
136
            lo = mid + 1;
 
137
        else
 
138
            hi = mid;
 
139
    }
 
140
    return lo;
 
141
}
 
142
 
 
143
 
 
144
static inline int
 
145
compare_lines(struct line *a, struct line *b)
 
146
{
 
147
    return ((a->hash != b->hash)
 
148
            || PyObject_Compare(a->data, b->data));
 
149
}
 
150
 
 
151
 
 
152
static inline int
 
153
find_equivalence_class(struct bucket *hashtable, Py_ssize_t hsize,
 
154
                       struct line *lines, struct line *ref_lines,
 
155
                       Py_ssize_t i)
 
156
{
 
157
    Py_ssize_t j;
 
158
    for (j = lines[i].hash & hsize; hashtable[j].b_head != SENTINEL; j = (j + 1) & hsize) {
 
159
        if (!compare_lines(lines + i, ref_lines + hashtable[j].b_head)) {
 
160
            break;
 
161
        }
 
162
    }
 
163
    return j;
 
164
}
 
165
 
 
166
 
 
167
static int
 
168
equate_lines(struct hashtable *result,
 
169
             struct line *lines_a, struct line *lines_b,
 
170
             Py_ssize_t asize, Py_ssize_t bsize)
 
171
{
 
172
    Py_ssize_t i, j, hsize;
 
173
    struct bucket *hashtable;
 
174
 
 
175
    /* check for overflow, we need the table to be at least bsize+1 */
 
176
    if (bsize == PY_SSIZE_T_MAX) {
 
177
        PyErr_SetNone(PyExc_OverflowError);
 
178
        return 0;
 
179
    }
 
180
 
 
181
    /* build a hash table of the next highest power of 2 */
 
182
    hsize = 1;
 
183
    while (hsize < bsize + 1)
 
184
        hsize *= 2;
 
185
 
 
186
    hashtable = (struct bucket *)malloc(sizeof(struct bucket) * hsize);
 
187
    if (hashtable == NULL) {
 
188
        PyErr_NoMemory();
 
189
        return 0;
 
190
    }
 
191
 
 
192
    /* initialise the hashtable */
 
193
    for (i = 0; i < hsize; i++) {
 
194
        hashtable[i].a_count = 0;
 
195
        hashtable[i].b_count = 0;
 
196
        hashtable[i].a_head = SENTINEL;
 
197
        hashtable[i].b_head = SENTINEL;
 
198
    }
 
199
    hsize--;
 
200
 
 
201
    /* add lines from lines_b to the hash table chains. iterating
 
202
       backwards so the matching lines are sorted to the linked list
 
203
       by the line number (because we are adding new lines to the
 
204
       head of the list) */
 
205
    for (i = bsize - 1; i >= 0; i--) {
 
206
        /* find the first hashtable entry, which is either empty or contains
 
207
           the same line as lines_b[i] */
 
208
        j = find_equivalence_class(hashtable, hsize, lines_b, lines_b, i);
 
209
 
 
210
        /* set the equivalence class */
 
211
        lines_b[i].equiv = j;
 
212
 
 
213
        /* add to the head of the equivalence class */
 
214
        lines_b[i].next = hashtable[j].b_head;
 
215
        hashtable[j].b_head = i;
 
216
        hashtable[j].b_count++;
 
217
    }
 
218
 
 
219
    /* match items from lines_a to their equivalence class in lines_b.
 
220
       again, iterating backwards for the right order of the linked lists */
 
221
    for (i = asize - 1; i >= 0; i--) {
 
222
        /* find the first hash entry, which is either empty or contains
 
223
           the same line as lines_a[i] */
 
224
        j = find_equivalence_class(hashtable, hsize, lines_a, lines_b, i);
 
225
 
 
226
        /* set the equivalence class, even if we are not interested in this
 
227
           line, because the values are not pre-filled */
 
228
        lines_a[i].equiv = j;
 
229
 
 
230
        /* we are not interested in lines which are not also in lines_b */
 
231
        if (hashtable[j].b_head == SENTINEL)
 
232
            continue;
 
233
 
 
234
        /* add to the head of the equivalence class */
 
235
        lines_a[i].next = hashtable[j].a_head;
 
236
        hashtable[j].a_head = i;
 
237
        hashtable[j].a_count++;
 
238
    }
 
239
 
 
240
    result->last_a_pos = -1;
 
241
    result->last_b_pos = -1;
 
242
    result->size = hsize + 1;
 
243
    result->table = hashtable;
 
244
 
 
245
    return 1;
 
246
}
 
247
 
 
248
 
 
249
 
 
250
/* Finds longest common subsequence of unique lines in a[alo:ahi] and
 
251
   b[blo:bhi].
 
252
   Parameter backpointers must have allocated memory for at least
 
253
   4 * (bhi - blo) ints. */
 
254
Py_ssize_t
 
255
unique_lcs(struct matching_line *answer,
 
256
           struct hashtable *hashtable, Py_ssize_t *backpointers,
 
257
           struct line *lines_a, struct line *lines_b,
 
258
           Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi)
 
259
{
 
260
    Py_ssize_t i, k, equiv, apos, bpos, norm_apos, norm_bpos, bsize, stacksize;
 
261
    Py_ssize_t *stacks, *lasts, *btoa;
 
262
    struct bucket *h;
 
263
 
 
264
    k = 0;
 
265
    stacksize = 0;
 
266
    bsize = bhi - blo;
 
267
    h = hashtable->table;
 
268
 
 
269
    /* "unpack" the allocated memory */
 
270
    stacks = backpointers + bsize;
 
271
    lasts = stacks + bsize;
 
272
    btoa = lasts + bsize;
 
273
 
 
274
    /* initialise the backpointers */
 
275
    for (i = 0; i < bsize; i++)
 
276
        backpointers[i] = SENTINEL;
 
277
 
 
278
    if (hashtable->last_a_pos == -1 || hashtable->last_a_pos > alo)
 
279
        for (i = 0; i < hashtable->size; i++)
 
280
            h[i].a_pos = h[i].a_head;
 
281
    hashtable->last_a_pos = alo;
 
282
 
 
283
    if (hashtable->last_b_pos == -1 || hashtable->last_b_pos > blo)
 
284
        for (i = 0; i < hashtable->size; i++)
 
285
            h[i].b_pos = h[i].b_head;
 
286
    hashtable->last_b_pos = blo;
 
287
 
 
288
    for (bpos = blo; bpos < bhi; bpos++) {
 
289
        equiv = lines_b[bpos].equiv;
 
290
 
 
291
        /* no lines in a or b  */
 
292
        if (h[equiv].a_count == 0 || h[equiv].b_count == 0)
 
293
            continue;
 
294
 
 
295
        /* find an unique line in lines_a that matches lines_b[bpos]
 
296
           if we find more than one line within the range alo:ahi,
 
297
           jump to the next line from lines_b immediately */
 
298
        apos = SENTINEL;
 
299
        /* loop through all lines in the linked list */
 
300
        for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {
 
301
            /* the index is lower than alo, the the next line */
 
302
            if (i < alo) {
 
303
                h[equiv].a_pos = i;
 
304
                continue;
 
305
            }
 
306
            /* the index is higher than ahi, stop searching */
 
307
            if (i >= ahi)
 
308
                break;
 
309
            /* if the line is within our range, check if it's a duplicate */
 
310
            if (apos != SENTINEL)
 
311
                goto nextb;
 
312
            /* save index to the line */
 
313
            apos = i;
 
314
        }
 
315
        /* this line has no equivalent in lines_a[alo:ahi] */
 
316
        if (apos == SENTINEL)
 
317
            goto nextb;
 
318
 
 
319
        /* check for duplicates of this line in lines_b[blo:bhi] */
 
320
        /* loop through all lines in the linked list */
 
321
        for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {
 
322
            /* the index is lower than blo, the the next line */
 
323
            if (i < blo) {
 
324
                h[equiv].b_pos = i;
 
325
                continue;
 
326
            }
 
327
            /* the index is higher than bhi, stop searching */
 
328
            if (i >= bhi)
 
329
                break;
 
330
            /* if this isn't the line with started with and it's within
 
331
               our range, it's a duplicate */
 
332
            if (i != bpos)
 
333
                goto nextb;
 
334
        }
 
335
 
 
336
        /* use normalised indexes ([0,ahi-alo) instead of [alo,ahi))
 
337
           for the patience sorting algorithm */
 
338
        norm_bpos = bpos - blo;
 
339
        norm_apos = apos - alo;
 
340
        btoa[norm_bpos] = norm_apos;
 
341
 
 
342
        /*
 
343
        Ok, how does this work...
 
344
 
 
345
        We have a list of matching lines from two lists, a and b. These
 
346
        matches are stored in variable `btoa`. As we are iterating over this
 
347
        table by bpos, the lines from b already form an increasing sequence.
 
348
        We need to "sort" also the lines from a using the patience sorting
 
349
        algorithm, ignoring the lines which would need to be swapped.
 
350
 
 
351
          http://en.wikipedia.org/wiki/Patience_sorting
 
352
 
 
353
        For each pair of lines, we need to place the line from a on either
 
354
        an existing pile that has higher value on the top or create a new
 
355
        pile. Variable `stacks` represents the tops of these piles and in
 
356
        variable `lasts` we store the lines from b, that correspond to the
 
357
        lines from a in `stacks`.
 
358
 
 
359
        Whenever we place a new line on top of a pile, we store a
 
360
        backpointer to the line (b) from top of the previous pile. This means
 
361
        that after the loop, variable `backpointers` will contain an index
 
362
        to the previous matching lines that forms an increasing sequence
 
363
        (over both indexes a and b) with the current matching lines. If
 
364
        either index a or b of the previous matching lines would be higher
 
365
        than indexes of the current one or if the indexes of the current
 
366
        one are 0, it will contain SENTINEL.
 
367
 
 
368
        To construct the LCS, we will just need to follow these backpointers
 
369
        from the top of the last pile and stop when we reach SENTINEL.
 
370
        */
 
371
 
 
372
        /* as an optimization, check if the next line comes at the end,
 
373
           because it usually does */
 
374
        if (stacksize && stacks[stacksize - 1] < norm_apos)
 
375
            k = stacksize;
 
376
        /* as an optimization, check if the next line comes right after
 
377
           the previous line, because usually it does */
 
378
        else if (stacksize && (stacks[k] < norm_apos) &&
 
379
                 (k == stacksize - 1 || stacks[k + 1] > norm_apos))
 
380
            k += 1;
 
381
        else
 
382
            k = bisect_left(stacks, norm_apos, 0, stacksize);
 
383
 
 
384
        if (k > 0)
 
385
            backpointers[norm_bpos] = lasts[k - 1];
 
386
 
 
387
        if (k < stacksize) {
 
388
            stacks[k] = norm_apos;
 
389
            lasts[k] = norm_bpos;
 
390
        }
 
391
        else {
 
392
            stacks[stacksize] = norm_apos;
 
393
            lasts[stacksize] = norm_bpos;
 
394
            stacksize += 1;
 
395
        }
 
396
 
 
397
 
 
398
nextb:
 
399
        ;
 
400
    }
 
401
 
 
402
    if (stacksize == 0)
 
403
        return 0;
 
404
 
 
405
    /* backtrace the structures to find the LCS */
 
406
    i = 0;
 
407
    k = lasts[stacksize - 1];
 
408
    while (k != SENTINEL) {
 
409
        answer[i].a = btoa[k];
 
410
        answer[i].b = k;
 
411
        k = backpointers[k];
 
412
        i++;
 
413
    }
 
414
 
 
415
    return i;
 
416
}
 
417
 
 
418
/* Adds a new line to the list of matching blocks, either extending the
 
419
   current block or adding a new one. */
 
420
static inline void
 
421
add_matching_line(struct matching_blocks *answer, Py_ssize_t a, Py_ssize_t b)
 
422
{
 
423
    Py_ssize_t last_index = answer->count - 1;
 
424
    if ((last_index >= 0) &&
 
425
        (a == answer->matches[last_index].a +
 
426
              answer->matches[last_index].len) &&
 
427
        (b == answer->matches[last_index].b +
 
428
              answer->matches[last_index].len)) {
 
429
        /* enlarge the last block */
 
430
        answer->matches[last_index].len++;
 
431
    }
 
432
    else {
 
433
        /* create a new block */
 
434
        last_index++;
 
435
        answer->matches[last_index].a = a;
 
436
        answer->matches[last_index].b = b;
 
437
        answer->matches[last_index].len = 1;
 
438
        answer->count++;
 
439
    }
 
440
}
 
441
 
 
442
 
 
443
static int
 
444
recurse_matches(struct matching_blocks *answer, struct hashtable *hashtable,
 
445
                Py_ssize_t *backpointers, struct line *a, struct line *b,
 
446
                Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi,
 
447
                int maxrecursion)
 
448
{
 
449
    int res;
 
450
    Py_ssize_t new, last_a_pos, last_b_pos, lcs_size, nahi, nbhi, i, apos, bpos;
 
451
    struct matching_line *lcs;
 
452
 
 
453
    if (maxrecursion < 0)
 
454
        return 1;
 
455
 
 
456
    if (alo == ahi || blo == bhi)
 
457
        return 1;
 
458
 
 
459
    new = 0;
 
460
    last_a_pos = alo - 1;
 
461
    last_b_pos = blo - 1;
 
462
 
 
463
    lcs = (struct matching_line *)malloc(sizeof(struct matching_line) * (bhi - blo));
 
464
    if (lcs == NULL)
 
465
        return 0;
 
466
 
 
467
    lcs_size = unique_lcs(lcs, hashtable, backpointers, a, b, alo, blo, ahi, bhi);
 
468
 
 
469
    /* recurse between lines which are unique in each file and match */
 
470
    for (i = lcs_size - 1; i >= 0; i--) {
 
471
        apos = alo + lcs[i].a;
 
472
        bpos = blo + lcs[i].b;
 
473
        if (last_a_pos + 1 != apos || last_b_pos + 1 != bpos) {
 
474
            res = recurse_matches(answer, hashtable,
 
475
                                  backpointers, a, b,
 
476
                                  last_a_pos + 1, last_b_pos + 1,
 
477
                                  apos, bpos, maxrecursion - 1);
 
478
            if (!res)
 
479
                goto error;
 
480
        }
 
481
        last_a_pos = apos;
 
482
        last_b_pos = bpos;
 
483
        add_matching_line(answer, apos, bpos);
 
484
        new = 1;
 
485
    }
 
486
 
 
487
    free(lcs);
 
488
    lcs = NULL;
 
489
 
 
490
    /* find matches between the last match and the end */
 
491
    if (new > 0) {
 
492
        res = recurse_matches(answer, hashtable,
 
493
                              backpointers, a, b,
 
494
                              last_a_pos + 1, last_b_pos + 1,
 
495
                              ahi, bhi, maxrecursion - 1);
 
496
        if (!res)
 
497
            goto error;
 
498
    }
 
499
 
 
500
 
 
501
    /* find matching lines at the very beginning */
 
502
    else if (a[alo].equiv == b[blo].equiv) {
 
503
        while (alo < ahi && blo < bhi && a[alo].equiv == b[blo].equiv)
 
504
            add_matching_line(answer, alo++, blo++);
 
505
        res = recurse_matches(answer, hashtable,
 
506
                              backpointers, a, b,
 
507
                              alo, blo, ahi, bhi, maxrecursion - 1);
 
508
        if (!res)
 
509
            goto error;
 
510
    }
 
511
 
 
512
    /* find matching lines at the very end */
 
513
    else if (a[ahi - 1].equiv == b[bhi - 1].equiv) {
 
514
        nahi = ahi - 1;
 
515
        nbhi = bhi - 1;
 
516
        while (nahi > alo && nbhi > blo && a[nahi - 1].equiv == b[nbhi - 1].equiv) {
 
517
            nahi--;
 
518
            nbhi--;
 
519
        }
 
520
        res = recurse_matches(answer, hashtable,
 
521
                              backpointers, a, b,
 
522
                              last_a_pos + 1, last_b_pos + 1,
 
523
                              nahi, nbhi, maxrecursion - 1);
 
524
        if (!res)
 
525
            goto error;
 
526
        for (i = 0; i < ahi - nahi; i++)
 
527
            add_matching_line(answer, nahi + i, nbhi + i);
 
528
    }
 
529
 
 
530
    return 1;
 
531
 
 
532
error:
 
533
    free(lcs);
 
534
    return 0;
 
535
}
 
536
 
 
537
 
 
538
static void
 
539
delete_lines(struct line *lines, Py_ssize_t size)
 
540
{
 
541
    struct line *line = lines;
 
542
    while (size-- > 0) {
 
543
        Py_XDECREF(line->data);
 
544
        line++;
 
545
    }
 
546
    free(lines);
 
547
}
 
548
 
 
549
 
 
550
static Py_ssize_t
 
551
load_lines(PyObject *orig, struct line **lines)
 
552
{
 
553
    Py_ssize_t size, i;
 
554
    struct line *line;
 
555
    PyObject *seq, *item;
 
556
 
 
557
    seq = PySequence_Fast(orig, "sequence expected");
 
558
    if (seq == NULL) {
 
559
        return -1;
 
560
    }
 
561
 
 
562
    size = PySequence_Fast_GET_SIZE(seq);
 
563
    if (size == 0) {
 
564
        Py_DECREF(seq);
 
565
        return 0;
 
566
    }
 
567
 
 
568
    /* Allocate a memory block for line data, initialized to 0 */
 
569
    line = *lines = (struct line *)calloc(size, sizeof(struct line));
 
570
    if (line == NULL) {
 
571
        PyErr_NoMemory();
 
572
        Py_DECREF(seq);
 
573
        return -1;
 
574
    }
 
575
 
 
576
    for (i = 0; i < size; i++) {
 
577
        item = PySequence_Fast_GET_ITEM(seq, i);
 
578
        Py_INCREF(item);
 
579
        line->data = item;
 
580
        line->hash = PyObject_Hash(item);
 
581
        if (line->hash == (-1)) {
 
582
            /* Propogate the hash exception */
 
583
            size = -1;
 
584
            goto cleanup;
 
585
        }
 
586
        line->next = SENTINEL;
 
587
        line++;
 
588
    }
 
589
 
 
590
    cleanup:
 
591
    Py_DECREF(seq);
 
592
    if (size == -1) {
 
593
        /* Error -- cleanup unused object references */
 
594
        delete_lines(*lines, i);
 
595
        *lines = NULL;
 
596
    }
 
597
    return size;
 
598
}
 
599
 
 
600
 
 
601
static PyObject *
 
602
py_unique_lcs(PyObject *self, PyObject *args)
 
603
{
 
604
    PyObject *aseq, *bseq, *res, *item;
 
605
    Py_ssize_t asize, bsize, i, nmatches, *backpointers = NULL;
 
606
    struct line *a = NULL, *b = NULL;
 
607
    struct matching_line *matches = NULL;
 
608
    struct hashtable hashtable;
 
609
 
 
610
    if (!PyArg_ParseTuple(args, "OO", &aseq, &bseq))
 
611
        return NULL;
 
612
 
 
613
    hashtable.table = NULL;
 
614
 
 
615
    asize = load_lines(aseq, &a);
 
616
    bsize = load_lines(bseq, &b);
 
617
    if (asize == -1 || bsize == -1)
 
618
        goto error;
 
619
 
 
620
    if (!equate_lines(&hashtable, a, b, asize, bsize))
 
621
        goto error;
 
622
 
 
623
    matches = (struct matching_line *)malloc(sizeof(struct matching_line) * bsize);
 
624
    if (matches == NULL)
 
625
        goto error;
 
626
 
 
627
    backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * bsize * 4);
 
628
    if (backpointers == NULL)
 
629
        goto error;
 
630
 
 
631
    nmatches = unique_lcs(matches, &hashtable, backpointers, a, b, 0, 0, asize, bsize);
 
632
 
 
633
    res = PyList_New(nmatches);
 
634
    for (i = 0; i < nmatches; i++) {
 
635
#if PY_VERSION_HEX < 0x02050000
 
636
        item = Py_BuildValue("ii", matches[nmatches - i - 1].a, matches[nmatches - i - 1].b);
 
637
#else
 
638
        item = Py_BuildValue("nn", matches[nmatches - i - 1].a, matches[nmatches - i - 1].b);
 
639
#endif
 
640
        if (item == NULL)
 
641
            goto error;
 
642
        if (PyList_SetItem(res, i, item) != 0)
 
643
            goto error;
 
644
    }
 
645
 
 
646
    free(backpointers);
 
647
    free(matches);
 
648
    free(hashtable.table);
 
649
    delete_lines(b, bsize);
 
650
    delete_lines(a, asize);
 
651
    return res;
 
652
 
 
653
error:
 
654
    free(backpointers);
 
655
    free(matches);
 
656
    free(hashtable.table);
 
657
    delete_lines(b, bsize);
 
658
    delete_lines(a, asize);
 
659
    return NULL;
 
660
}
 
661
 
 
662
 
 
663
static PyObject *
 
664
py_recurse_matches(PyObject *self, PyObject *args)
 
665
{
 
666
    PyObject *aseq, *bseq, *item, *answer;
 
667
    int maxrecursion, res;
 
668
    Py_ssize_t i, j, asize, bsize, alo, blo, ahi, bhi;
 
669
    Py_ssize_t *backpointers = NULL;
 
670
    struct line *a = NULL, *b = NULL;
 
671
    struct hashtable hashtable;
 
672
    struct matching_blocks matches;
 
673
 
 
674
#if PY_VERSION_HEX < 0x02050000
 
675
    if (!PyArg_ParseTuple(args, "OOiiiiOi", &aseq, &bseq, &alo, &blo,
 
676
                          &ahi, &bhi, &answer, &maxrecursion))
 
677
#else
 
678
    if (!PyArg_ParseTuple(args, "OOnnnnOi", &aseq, &bseq, &alo, &blo,
 
679
                          &ahi, &bhi, &answer, &maxrecursion))
 
680
#endif
 
681
        return NULL;
 
682
 
 
683
    hashtable.table = NULL;
 
684
    matches.matches = NULL;
 
685
 
 
686
    asize = load_lines(aseq, &a);
 
687
    bsize = load_lines(bseq, &b);
 
688
    if (asize == -1 || bsize == -1)
 
689
        goto error;
 
690
 
 
691
    if (!equate_lines(&hashtable, a, b, asize, bsize))
 
692
        goto error;
 
693
 
 
694
    matches.count = 0;
 
695
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * bsize);
 
696
    if (matches.matches == NULL)
 
697
        goto error;
 
698
 
 
699
    backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * bsize * 4);
 
700
    if (backpointers == NULL)
 
701
        goto error;
 
702
 
 
703
    res = recurse_matches(&matches, &hashtable, backpointers,
 
704
                          a, b, alo, blo, ahi, bhi, maxrecursion);
 
705
    if (!res)
 
706
        goto error;
 
707
 
 
708
    for (i = 0; i < matches.count; i++) {
 
709
        for (j = 0; j < matches.matches[i].len; j++) {
 
710
#if PY_VERSION_HEX < 0x02050000
 
711
            item = Py_BuildValue("ii", matches.matches[i].a + j,
 
712
                                 matches.matches[i].b + j);
 
713
#else
 
714
            item = Py_BuildValue("nn", matches.matches[i].a + j,
 
715
                                 matches.matches[i].b + j);
 
716
#endif
 
717
            if (item == NULL)
 
718
                goto error;
 
719
            if (PyList_Append(answer, item) != 0)
 
720
                goto error;
 
721
        }
 
722
    }
 
723
 
 
724
    free(backpointers);
 
725
    free(matches.matches);
 
726
    free(hashtable.table);
 
727
    delete_lines(b, bsize);
 
728
    delete_lines(a, asize);
 
729
    Py_RETURN_NONE;
 
730
 
 
731
error:
 
732
    free(backpointers);
 
733
    free(matches.matches);
 
734
    free(hashtable.table);
 
735
    delete_lines(b, bsize);
 
736
    delete_lines(a, asize);
 
737
    return NULL;
 
738
}
 
739
 
 
740
 
 
741
static PyObject *
 
742
PatienceSequenceMatcher_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
743
{
 
744
    PyObject *junk, *a, *b;
 
745
    PatienceSequenceMatcher *self;
 
746
 
 
747
    self = (PatienceSequenceMatcher *)type->tp_alloc(type, 0);
 
748
    if (self != NULL) {
 
749
 
 
750
        if (!PyArg_ParseTuple(args, "OOO", &junk, &a, &b)) {
 
751
            Py_DECREF(self);
 
752
            return NULL;
 
753
        }
 
754
 
 
755
        self->asize = load_lines(a, &(self->a));
 
756
        self->bsize = load_lines(b, &(self->b));
 
757
 
 
758
        if (self->asize == -1 || self->bsize == -1) {
 
759
            Py_DECREF(self);
 
760
            return NULL;
 
761
        }
 
762
 
 
763
        if (!equate_lines(&self->hashtable, self->a, self->b, self->asize, self->bsize)) {
 
764
            Py_DECREF(self);
 
765
            return NULL;
 
766
        }
 
767
 
 
768
        self->backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * self->bsize * 4);
 
769
        if (self->backpointers == NULL) {
 
770
            Py_DECREF(self);
 
771
            return NULL;
 
772
        }
 
773
 
 
774
    }
 
775
 
 
776
    return (PyObject *)self;
 
777
}
 
778
 
 
779
 
 
780
static void
 
781
PatienceSequenceMatcher_dealloc(PatienceSequenceMatcher* self)
 
782
{
 
783
    free(self->backpointers);
 
784
    free(self->hashtable.table);
 
785
    delete_lines(self->b, self->bsize);
 
786
    delete_lines(self->a, self->asize);
 
787
    self->ob_type->tp_free((PyObject *)self);
 
788
}
 
789
 
 
790
 
 
791
static char PatienceSequenceMatcher_get_matching_blocks_doc[] =
 
792
    "Return list of triples describing matching subsequences.\n"
 
793
    "\n"
 
794
    "Each triple is of the form (i, j, n), and means that\n"
 
795
    "a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in\n"
 
796
    "i and in j.\n"
 
797
    "\n"
 
798
    "The last triple is a dummy, (len(a), len(b), 0), and is the only\n"
 
799
    "triple with n==0.\n"
 
800
    "\n"
 
801
    ">>> s = PatienceSequenceMatcher(None, \"abxcd\", \"abcd\")\n"
 
802
    ">>> s.get_matching_blocks()\n"
 
803
    "[(0, 0, 2), (3, 2, 2), (5, 4, 0)]\n";
 
804
 
 
805
static PyObject *
 
806
PatienceSequenceMatcher_get_matching_blocks(PatienceSequenceMatcher* self)
 
807
{
 
808
    PyObject *answer, *item;
 
809
    int res;
 
810
    Py_ssize_t i;
 
811
    struct matching_blocks matches;
 
812
 
 
813
    matches.count = 0;
 
814
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * self->bsize);
 
815
    if (matches.matches == NULL)
 
816
        return PyErr_NoMemory();
 
817
 
 
818
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
819
                          self->a, self->b, 0, 0,
 
820
                          self->asize, self->bsize, 10);
 
821
    if (!res) {
 
822
        free(matches.matches);
 
823
        return PyErr_NoMemory();
 
824
    }
 
825
 
 
826
    answer = PyList_New(matches.count + 1);
 
827
    if (answer == NULL) {
 
828
        free(matches.matches);
 
829
        return NULL;
 
830
    }
 
831
 
 
832
    for (i = 0; i < matches.count; i++) {
 
833
#if PY_VERSION_HEX < 0x02050000
 
834
        item = Py_BuildValue("iii", matches.matches[i].a,
 
835
                             matches.matches[i].b, matches.matches[i].len);
 
836
#else
 
837
        item = Py_BuildValue("nnn", matches.matches[i].a,
 
838
                             matches.matches[i].b, matches.matches[i].len);
 
839
#endif
 
840
        if (item == NULL)
 
841
            goto error;
 
842
        if (PyList_SetItem(answer, i, item) != 0)
 
843
            goto error;
 
844
    }
 
845
#if PY_VERSION_HEX < 0x02050000
 
846
    item = Py_BuildValue("iii", self->asize, self->bsize, 0);
 
847
#else
 
848
    item = Py_BuildValue("nnn", self->asize, self->bsize, 0);
 
849
#endif
 
850
    if (item == NULL)
 
851
        goto error;
 
852
    if (PyList_SetItem(answer, i, item) != 0)
 
853
        goto error;
 
854
 
 
855
    free(matches.matches);
 
856
    return answer;
 
857
 
 
858
error:
 
859
    free(matches.matches);
 
860
    Py_DECREF(answer);
 
861
    return NULL;
 
862
}
 
863
 
 
864
 
 
865
static char PatienceSequenceMatcher_get_opcodes_doc[] =
 
866
    "Return list of 5-tuples describing how to turn a into b.\n"
 
867
    "\n"
 
868
    "Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple\n"
 
869
    "has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the\n"
 
870
    "tuple preceding it, and likewise for j1 == the previous j2.\n"
 
871
    "\n"
 
872
    "The tags are strings, with these meanings:\n"
 
873
    "\n"
 
874
    "'replace':  a[i1:i2] should be replaced by b[j1:j2]\n"
 
875
    "'delete':   a[i1:i2] should be deleted.\n"
 
876
    "               Note that j1==j2 in this case.\n"
 
877
    "'insert':   b[j1:j2] should be inserted at a[i1:i1].\n"
 
878
    "               Note that i1==i2 in this case.\n"
 
879
    "'equal':    a[i1:i2] == b[j1:j2]\n"
 
880
    "\n"
 
881
    ">>> a = \"qabxcd\"\n"
 
882
    ">>> b = \"abycdf\"\n"
 
883
    ">>> s = PatienceSequenceMatcher(None, a, b)\n"
 
884
    ">>> for tag, i1, i2, j1, j2 in s.get_opcodes():\n"
 
885
    "...    print (\"%7s a[%d:%d] (%s) b[%d:%d] (%s)\" %\n"
 
886
    "...           (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))\n"
 
887
    " delete a[0:1] (q) b[0:0] ()\n"
 
888
    "  equal a[1:3] (ab) b[0:2] (ab)\n"
 
889
    "replace a[3:4] (x) b[2:3] (y)\n"
 
890
    "  equal a[4:6] (cd) b[3:5] (cd)\n"
 
891
    " insert a[6:6] () b[5:6] (f)\n";
 
892
 
 
893
static PyObject *
 
894
PatienceSequenceMatcher_get_opcodes(PatienceSequenceMatcher* self)
 
895
{
 
896
    PyObject *answer, *item;
 
897
    Py_ssize_t i, j, k, ai, bj;
 
898
    int tag, res;
 
899
    struct matching_blocks matches;
 
900
 
 
901
    matches.count = 0;
 
902
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * (self->bsize + 1));
 
903
    if (matches.matches == NULL)
 
904
        return PyErr_NoMemory();
 
905
 
 
906
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
907
                          self->a, self->b, 0, 0,
 
908
                          self->asize, self->bsize, 10);
 
909
    if (!res) {
 
910
        free(matches.matches);
 
911
        return PyErr_NoMemory();
 
912
    }
 
913
 
 
914
    matches.matches[matches.count].a = self->asize;
 
915
    matches.matches[matches.count].b = self->bsize;
 
916
    matches.matches[matches.count].len = 0;
 
917
    matches.count++;
 
918
 
 
919
    answer = PyList_New(0);
 
920
    if (answer == NULL) {
 
921
        free(matches.matches);
 
922
        return NULL;
 
923
    }
 
924
 
 
925
    i = j = 0;
 
926
    for (k = 0; k < matches.count; k++) {
 
927
        ai = matches.matches[k].a;
 
928
        bj = matches.matches[k].b;
 
929
 
 
930
        tag = -1;
 
931
        if (i < ai && j < bj)
 
932
            tag = OP_REPLACE;
 
933
        else if (i < ai)
 
934
            tag = OP_DELETE;
 
935
        else if (j < bj)
 
936
            tag = OP_INSERT;
 
937
 
 
938
        if (tag != -1) {
 
939
#if PY_VERSION_HEX < 0x02050000
 
940
            item = Py_BuildValue("siiii", opcode_names[tag], i, ai, j, bj);
 
941
#else
 
942
            item = Py_BuildValue("snnnn", opcode_names[tag], i, ai, j, bj);
 
943
#endif
 
944
            if (item == NULL)
 
945
                goto error;
 
946
            if (PyList_Append(answer, item) != 0)
 
947
                goto error;
 
948
        }
 
949
 
 
950
        i = ai + matches.matches[k].len;
 
951
        j = bj + matches.matches[k].len;
 
952
 
 
953
        if (matches.matches[k].len > 0) {
 
954
#if PY_VERSION_HEX < 0x02050000
 
955
            item = Py_BuildValue("siiii", opcode_names[OP_EQUAL], ai, i, bj, j);
 
956
#else
 
957
            item = Py_BuildValue("snnnn", opcode_names[OP_EQUAL], ai, i, bj, j);
 
958
#endif
 
959
            if (item == NULL)
 
960
                goto error;
 
961
            if (PyList_Append(answer, item) != 0)
 
962
                goto error;
 
963
        }
 
964
    }
 
965
 
 
966
    free(matches.matches);
 
967
    return answer;
 
968
 
 
969
error:
 
970
    free(matches.matches);
 
971
    Py_DECREF(answer);
 
972
    return NULL;
 
973
}
 
974
 
 
975
 
 
976
static char PatienceSequenceMatcher_get_grouped_opcodes_doc[] =
 
977
    "Isolate change clusters by eliminating ranges with no changes.\n"
 
978
    "\n"
 
979
    "Return a list of groups with upto n lines of context.\n"
 
980
    "Each group is in the same format as returned by get_opcodes().\n"
 
981
    "\n"
 
982
    ">>> from pprint import pprint\n"
 
983
    ">>> a = map(str, range(1,40))\n"
 
984
    ">>> b = a[:]\n"
 
985
    ">>> b[8:8] = ['i']     # Make an insertion\n"
 
986
    ">>> b[20] += 'x'       # Make a replacement\n"
 
987
    ">>> b[23:28] = []      # Make a deletion\n"
 
988
    ">>> b[30] += 'y'       # Make another replacement\n"
 
989
    ">>> pprint(PatienceSequenceMatcher(None,a,b).get_grouped_opcodes())\n"
 
990
    "[[('equal', 5, 8, 5, 8), ('insert', 8, 8, 8, 9), ('equal', 8, 11, 9, 12)],\n"
 
991
    " [('equal', 16, 19, 17, 20),\n"
 
992
    "  ('replace', 19, 20, 20, 21),\n"
 
993
    "  ('equal', 20, 22, 21, 23),\n"
 
994
    "  ('delete', 22, 27, 23, 23),\n"
 
995
    "  ('equal', 27, 30, 23, 26)],\n"
 
996
    " [('equal', 31, 34, 27, 30),\n"
 
997
    "  ('replace', 34, 35, 30, 31),\n"
 
998
    "  ('equal', 35, 38, 31, 34)]]\n";
 
999
 
 
1000
static PyObject *
 
1001
PatienceSequenceMatcher_get_grouped_opcodes(PatienceSequenceMatcher* self,
 
1002
                                            PyObject *args)
 
1003
{
 
1004
    PyObject *answer, *group, *item;
 
1005
    Py_ssize_t i, j, k, ai, bj, size, ncodes, tag;
 
1006
    Py_ssize_t i1, i2, j1, j2;
 
1007
    int n = 3, nn, res;
 
1008
    struct matching_blocks matches;
 
1009
    struct opcode *codes;
 
1010
 
 
1011
    if (!PyArg_ParseTuple(args, "|i", &n))
 
1012
        return NULL;
 
1013
 
 
1014
    matches.count = 0;
 
1015
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * (self->bsize + 1));
 
1016
    if (matches.matches == NULL)
 
1017
        return PyErr_NoMemory();
 
1018
 
 
1019
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
1020
                          self->a, self->b, 0, 0,
 
1021
                          self->asize, self->bsize, 10);
 
1022
    if (!res) {
 
1023
        free(matches.matches);
 
1024
        return PyErr_NoMemory();
 
1025
    }
 
1026
 
 
1027
    matches.matches[matches.count].a = self->asize;
 
1028
    matches.matches[matches.count].b = self->bsize;
 
1029
    matches.matches[matches.count].len = 0;
 
1030
    matches.count++;
 
1031
 
 
1032
    ncodes = 0;
 
1033
    codes = (struct opcode *)malloc(sizeof(struct opcode) * matches.count * 2);
 
1034
    if (codes == NULL) {
 
1035
        free(matches.matches);
 
1036
        return PyErr_NoMemory();
 
1037
    }
 
1038
 
 
1039
    i = j = 0;
 
1040
    for (k = 0; k < matches.count; k++) {
 
1041
        ai = matches.matches[k].a;
 
1042
        bj = matches.matches[k].b;
 
1043
 
 
1044
        tag = -1;
 
1045
        if (i < ai && j < bj)
 
1046
            tag = OP_REPLACE;
 
1047
        else if (i < ai)
 
1048
            tag = OP_DELETE;
 
1049
        else if (j < bj)
 
1050
            tag = OP_INSERT;
 
1051
 
 
1052
        if (tag != -1) {
 
1053
            codes[ncodes].tag = tag;
 
1054
            codes[ncodes].i1 = i;
 
1055
            codes[ncodes].i2 = ai;
 
1056
            codes[ncodes].j1 = j;
 
1057
            codes[ncodes].j2 = bj;
 
1058
            ncodes++;
 
1059
        }
 
1060
 
 
1061
        i = ai + matches.matches[k].len;
 
1062
        j = bj + matches.matches[k].len;
 
1063
 
 
1064
        if (matches.matches[k].len > 0) {
 
1065
            codes[ncodes].tag = OP_EQUAL;
 
1066
            codes[ncodes].i1 = ai;
 
1067
            codes[ncodes].i2 = i;
 
1068
            codes[ncodes].j1 = bj;
 
1069
            codes[ncodes].j2 = j;
 
1070
            ncodes++;
 
1071
        }
 
1072
    }
 
1073
 
 
1074
    if (ncodes == 0) {
 
1075
        codes[ncodes].tag = OP_EQUAL;
 
1076
        codes[ncodes].i1 = 0;
 
1077
        codes[ncodes].i2 = 1;
 
1078
        codes[ncodes].j1 = 0;
 
1079
        codes[ncodes].j2 = 1;
 
1080
        ncodes++;
 
1081
    }
 
1082
 
 
1083
    /* fixup leading and trailing groups if they show no changes. */
 
1084
    if (codes[0].tag == OP_EQUAL) {
 
1085
        codes[0].i1 = MAX(codes[0].i1, codes[0].i2 - n);
 
1086
        codes[0].j1 = MAX(codes[0].j1, codes[0].j2 - n);
 
1087
    }
 
1088
    if (codes[ncodes - 1].tag == OP_EQUAL) {
 
1089
        codes[ncodes - 1].i2 = MIN(codes[ncodes - 1].i2,
 
1090
                                   codes[ncodes - 1].i1 + n);
 
1091
        codes[ncodes - 1].j2 = MIN(codes[ncodes - 1].j2,
 
1092
                                   codes[ncodes - 1].j1 + n);
 
1093
    }
 
1094
 
 
1095
    group = NULL;
 
1096
 
 
1097
    answer = PyList_New(0);
 
1098
    if (answer == NULL)
 
1099
        goto error;
 
1100
 
 
1101
    group = PyList_New(0);
 
1102
    if (group == NULL)
 
1103
        goto error;
 
1104
 
 
1105
    nn = n + n;
 
1106
    tag = -1;
 
1107
    for (i = 0; i < ncodes; i++) {
 
1108
        tag = codes[i].tag;
 
1109
        i1 = codes[i].i1;
 
1110
        i2 = codes[i].i2;
 
1111
        j1 = codes[i].j1;
 
1112
        j2 = codes[i].j2;
 
1113
        /* end the current group and start a new one whenever
 
1114
           there is a large range with no changes. */
 
1115
        if (tag == OP_EQUAL && i2 - i1 > nn) {
 
1116
#if PY_VERSION_HEX < 0x02050000
 
1117
            item = Py_BuildValue("siiii", opcode_names[tag],
 
1118
                                  i1, MIN(i2, i1 + n), j1, MIN(j2, j1 + n));
 
1119
#else
 
1120
            item = Py_BuildValue("snnnn", opcode_names[tag],
 
1121
                                  i1, MIN(i2, i1 + n), j1, MIN(j2, j1 + n));
 
1122
#endif
 
1123
            if (item == NULL)
 
1124
                goto error;
 
1125
            if (PyList_Append(group, item) != 0)
 
1126
                goto error;
 
1127
            if (PyList_Append(answer, group) != 0)
 
1128
                goto error;
 
1129
            group = PyList_New(0);
 
1130
            if (group == NULL)
 
1131
                goto error;
 
1132
            i1 = MAX(i1, i2 - n);
 
1133
            j1 = MAX(j1, j2 - n);
 
1134
        }
 
1135
#if PY_VERSION_HEX < 0x02050000
 
1136
        item = Py_BuildValue("siiii", opcode_names[tag], i1, i2, j1 ,j2);
 
1137
#else
 
1138
        item = Py_BuildValue("snnnn", opcode_names[tag], i1, i2, j1 ,j2);
 
1139
#endif
 
1140
        if (item == NULL)
 
1141
            goto error;
 
1142
        if (PyList_Append(group, item) != 0)
 
1143
            goto error;
 
1144
    }
 
1145
    size = PyList_Size(group);
 
1146
    if (size > 0 && !(size == 1 && tag == OP_EQUAL)) {
 
1147
        if (PyList_Append(answer, group) != 0)
 
1148
            goto error;
 
1149
    }
 
1150
    else
 
1151
        Py_DECREF(group);
 
1152
 
 
1153
    free(codes);
 
1154
    free(matches.matches);
 
1155
    return answer;
 
1156
 
 
1157
error:
 
1158
    free(codes);
 
1159
    free(matches.matches);
 
1160
    Py_DECREF(group);
 
1161
    Py_DECREF(answer);
 
1162
    return NULL;
 
1163
}
 
1164
 
 
1165
 
 
1166
static PyMethodDef PatienceSequenceMatcher_methods[] = {
 
1167
    {"get_matching_blocks",
 
1168
     (PyCFunction)PatienceSequenceMatcher_get_matching_blocks,
 
1169
     METH_NOARGS,
 
1170
     PatienceSequenceMatcher_get_matching_blocks_doc},
 
1171
    {"get_opcodes",
 
1172
     (PyCFunction)PatienceSequenceMatcher_get_opcodes,
 
1173
     METH_NOARGS,
 
1174
     PatienceSequenceMatcher_get_opcodes_doc},
 
1175
    {"get_grouped_opcodes",
 
1176
     (PyCFunction)PatienceSequenceMatcher_get_grouped_opcodes,
 
1177
     METH_VARARGS,
 
1178
     PatienceSequenceMatcher_get_grouped_opcodes_doc},
 
1179
    {NULL}
 
1180
};
 
1181
 
 
1182
 
 
1183
static char PatienceSequenceMatcher_doc[] =
 
1184
    "C implementation of PatienceSequenceMatcher";
 
1185
 
 
1186
 
 
1187
static PyTypeObject PatienceSequenceMatcherType = {
 
1188
    PyObject_HEAD_INIT(NULL)
 
1189
    0,                                           /* ob_size */
 
1190
    "PatienceSequenceMatcher",                   /* tp_name */
 
1191
    sizeof(PatienceSequenceMatcher),             /* tp_basicsize */
 
1192
    0,                                           /* tp_itemsize */
 
1193
    (destructor)PatienceSequenceMatcher_dealloc, /* tp_dealloc */
 
1194
    0,                                           /* tp_print */
 
1195
    0,                                           /* tp_getattr */
 
1196
    0,                                           /* tp_setattr */
 
1197
    0,                                           /* tp_compare */
 
1198
    0,                                           /* tp_repr */
 
1199
    0,                                           /* tp_as_number */
 
1200
    0,                                           /* tp_as_sequence */
 
1201
    0,                                           /* tp_as_mapping */
 
1202
    0,                                           /* tp_hash */
 
1203
    0,                                           /* tp_call */
 
1204
    0,                                           /* tp_str */
 
1205
    0,                                           /* tp_getattro */
 
1206
    0,                                           /* tp_setattro */
 
1207
    0,                                           /* tp_as_buffer */
 
1208
    Py_TPFLAGS_DEFAULT,                          /* tp_flags*/
 
1209
    PatienceSequenceMatcher_doc,                 /* tp_doc */
 
1210
    0,                                           /* tp_traverse */
 
1211
    0,                                           /* tp_clear */
 
1212
    0,                                           /* tp_richcompare */
 
1213
    0,                                           /* tp_weaklistoffset */
 
1214
    0,                                           /* tp_iter */
 
1215
    0,                                           /* tp_iternext */
 
1216
    PatienceSequenceMatcher_methods,             /* tp_methods */
 
1217
    0,                                           /* tp_members */
 
1218
    0,                                           /* tp_getset */
 
1219
    0,                                           /* tp_base */
 
1220
    0,                                           /* tp_dict */
 
1221
    0,                                           /* tp_descr_get */
 
1222
    0,                                           /* tp_descr_set */
 
1223
    0,                                           /* tp_dictoffset */
 
1224
    0,                                           /* tp_init */
 
1225
    0,                                           /* tp_alloc */
 
1226
    PatienceSequenceMatcher_new,                 /* tp_new */
 
1227
};
 
1228
 
 
1229
 
 
1230
static PyMethodDef cpatiencediff_methods[] = {
 
1231
    {"unique_lcs_c", py_unique_lcs, METH_VARARGS},
 
1232
    {"recurse_matches_c", py_recurse_matches, METH_VARARGS},
 
1233
    {NULL, NULL}
 
1234
};
 
1235
 
 
1236
 
 
1237
PyMODINIT_FUNC
 
1238
init_patiencediff_c(void)
 
1239
{
 
1240
    PyObject* m;
 
1241
 
 
1242
    if (PyType_Ready(&PatienceSequenceMatcherType) < 0)
 
1243
        return;
 
1244
 
 
1245
    m = Py_InitModule3("_patiencediff_c", cpatiencediff_methods,
 
1246
                       "C implementation of PatienceSequenceMatcher");
 
1247
    if (m == NULL)
 
1248
      return;
 
1249
 
 
1250
    Py_INCREF(&PatienceSequenceMatcherType);
 
1251
    PyModule_AddObject(m, "PatienceSequenceMatcher_c",
 
1252
                       (PyObject *)&PatienceSequenceMatcherType);
 
1253
}