diff-lcs 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/{ChangeLog → History.rdoc} +23 -15
- data/License.rdoc +38 -0
- data/Manifest.txt +27 -0
- data/README.rdoc +72 -0
- data/Rakefile +16 -106
- data/bin/htmldiff +21 -101
- data/bin/ldiff +2 -41
- data/diff-lcs.gemspec +51 -0
- data/docs/COPYING.txt +340 -0
- data/docs/artistic.html +289 -0
- data/lib/diff-lcs.rb +5 -0
- data/lib/diff/lcs.rb +471 -471
- data/lib/diff/lcs/array.rb +1 -1
- data/lib/diff/lcs/block.rb +1 -1
- data/lib/diff/lcs/callbacks.rb +1 -1
- data/lib/diff/lcs/change.rb +1 -1
- data/lib/diff/lcs/htmldiff.rb +151 -0
- data/lib/diff/lcs/hunk.rb +1 -16
- data/lib/diff/lcs/ldiff.rb +37 -53
- data/lib/diff/lcs/string.rb +1 -1
- data/spec/diff_spec.rb +35 -0
- data/spec/lcs_spec.rb +36 -0
- data/spec/patch_spec.rb +390 -0
- data/spec/sdiff_spec.rb +204 -0
- data/spec/spec_helper.rb +284 -0
- data/spec/traverse_balanced_spec.rb +286 -0
- data/spec/traverse_sequences_spec.rb +83 -0
- metadata +136 -58
- data/Install +0 -6
- data/README +0 -76
- data/tests/00test.rb +0 -626
data/lib/diff-lcs.rb
ADDED
data/lib/diff/lcs.rb
CHANGED
@@ -1,211 +1,196 @@
|
|
1
|
-
|
2
|
-
#--
|
3
|
-
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
|
4
|
-
# adapted from:
|
5
|
-
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
|
6
|
-
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
|
7
|
-
# implements McIlroy-Hunt diff algorithm
|
8
|
-
#
|
9
|
-
# This program is free software. It may be redistributed and/or modified
|
10
|
-
# under the terms of the GPL version 2 (or later), the Perl Artistic
|
11
|
-
# licence, or the Ruby licence.
|
12
|
-
#
|
13
|
-
# $Id: lcs.rb,v 1.9 2004/10/17 20:31:10 austin Exp $
|
14
|
-
#++
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
15
2
|
|
16
3
|
module Diff
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
# Common Subsequences</em>, CACM, vol.20, no.5, pp.350-353, May 1977, with
|
145
|
-
# a few minor improvements to improve the speed."
|
4
|
+
# = Diff::LCS 1.1.3
|
5
|
+
# Computes "intelligent" differences between two sequenced Enumerables.
|
6
|
+
# This is an implementation of the McIlroy-Hunt "diff" algorithm for
|
7
|
+
# Enumerable objects that include Diffable.
|
8
|
+
#
|
9
|
+
# Based on Mario I. Wolczko's Smalltalk version (1.2, 1993) and Ned Konz's
|
10
|
+
# Perl version (Algorithm::Diff 1.15).
|
11
|
+
#
|
12
|
+
# == Synopsis
|
13
|
+
# require 'diff/lcs'
|
14
|
+
#
|
15
|
+
# seq1 = %w(a b c e h j l m n p)
|
16
|
+
# seq2 = %w(b c d e f j k l m r s t)
|
17
|
+
#
|
18
|
+
# lcs = Diff::LCS.LCS(seq1, seq2)
|
19
|
+
# diffs = Diff::LCS.diff(seq1, seq2)
|
20
|
+
# sdiff = Diff::LCS.sdiff(seq1, seq2)
|
21
|
+
# seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj)
|
22
|
+
# bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj)
|
23
|
+
# seq2 == Diff::LCS.patch(seq1, diffs)
|
24
|
+
# seq2 == Diff::LCS.patch!(seq1, diffs)
|
25
|
+
# seq1 == Diff::LCS.unpatch(seq2, diffs)
|
26
|
+
# seq1 == Diff::LCS.unpatch!(seq2, diffs)
|
27
|
+
# seq2 == Diff::LCS.patch(seq1, sdiff)
|
28
|
+
# seq2 == Diff::LCS.patch!(seq1, sdiff)
|
29
|
+
# seq1 == Diff::LCS.unpatch(seq2, sdiff)
|
30
|
+
# seq1 == Diff::LCS.unpatch!(seq2, sdiff)
|
31
|
+
#
|
32
|
+
# Alternatively, objects can be extended with Diff::LCS:
|
33
|
+
#
|
34
|
+
# seq1.extend(Diff::LCS)
|
35
|
+
# lcs = seq1.lcs(seq2)
|
36
|
+
# diffs = seq1.diff(seq2)
|
37
|
+
# sdiff = seq1.sdiff(seq2)
|
38
|
+
# seq = seq1.traverse_sequences(seq2, callback_obj)
|
39
|
+
# bal = seq1.traverse_balanced(seq2, callback_obj)
|
40
|
+
# seq2 == seq1.patch(diffs)
|
41
|
+
# seq2 == seq1.patch!(diffs)
|
42
|
+
# seq1 == seq2.unpatch(diffs)
|
43
|
+
# seq1 == seq2.unpatch!(diffs)
|
44
|
+
# seq2 == seq1.patch(sdiff)
|
45
|
+
# seq2 == seq1.patch!(sdiff)
|
46
|
+
# seq1 == seq2.unpatch(sdiff)
|
47
|
+
# seq1 == seq2.unpatch!(sdiff)
|
48
|
+
#
|
49
|
+
# Default extensions are provided for Array and String objects through the
|
50
|
+
# use of 'diff/lcs/array' and 'diff/lcs/string'.
|
51
|
+
#
|
52
|
+
# == Introduction (by Mark-Jason Dominus)
|
53
|
+
#
|
54
|
+
# <em>The following text is from the Perl documentation. The only changes
|
55
|
+
# have been to make the text appear better in Rdoc</em>.
|
56
|
+
#
|
57
|
+
# I once read an article written by the authors of +diff+; they said that
|
58
|
+
# they hard worked very hard on the algorithm until they found the right
|
59
|
+
# one.
|
60
|
+
#
|
61
|
+
# I think what they ended up using (and I hope someone will correct me,
|
62
|
+
# because I am not very confident about this) was the `longest common
|
63
|
+
# subsequence' method. In the LCS problem, you have two sequences of
|
64
|
+
# items:
|
65
|
+
#
|
66
|
+
# a b c d f g h j q z
|
67
|
+
# a b c d e f g i j k r x y z
|
68
|
+
#
|
69
|
+
# and you want to find the longest sequence of items that is present in
|
70
|
+
# both original sequences in the same order. That is, you want to find a
|
71
|
+
# new sequence *S* which can be obtained from the first sequence by
|
72
|
+
# deleting some items, and from the second sequence by deleting other
|
73
|
+
# items. You also want *S* to be as long as possible. In this case *S* is:
|
74
|
+
#
|
75
|
+
# a b c d f g j z
|
76
|
+
#
|
77
|
+
# From there it's only a small step to get diff-like output:
|
78
|
+
#
|
79
|
+
# e h i k q r x y
|
80
|
+
# + - + + - + + +
|
81
|
+
#
|
82
|
+
# This module solves the LCS problem. It also includes a canned function
|
83
|
+
# to generate +diff+-like output.
|
84
|
+
#
|
85
|
+
# It might seem from the example above that the LCS of two sequences is
|
86
|
+
# always pretty obvious, but that's not always the case, especially when
|
87
|
+
# the two sequences have many repeated elements. For example, consider
|
88
|
+
#
|
89
|
+
# a x b y c z p d q
|
90
|
+
# a b c a x b y c z
|
91
|
+
#
|
92
|
+
# A naive approach might start by matching up the +a+ and +b+ that appear
|
93
|
+
# at the beginning of each sequence, like this:
|
94
|
+
#
|
95
|
+
# a x b y c z p d q
|
96
|
+
# a b c a b y c z
|
97
|
+
#
|
98
|
+
# This finds the common subsequence +a b c z+. But actually, the LCS is
|
99
|
+
# +a x b y c z+:
|
100
|
+
#
|
101
|
+
# a x b y c z p d q
|
102
|
+
# a b c a x b y c z
|
103
|
+
#
|
104
|
+
# == Author
|
105
|
+
# This version is by Austin Ziegler <austin@rubyforge.org>.
|
106
|
+
#
|
107
|
+
# It is based on the Perl Algorithm::Diff (1.15) by Ned Konz , copyright
|
108
|
+
# © 2000–2002 and the Smalltalk diff version by Mario I.
|
109
|
+
# Wolczko, copyright © 1993. Documentation includes work by
|
110
|
+
# Mark-Jason Dominus.
|
111
|
+
#
|
112
|
+
# == Licence
|
113
|
+
# Copyright © 2004 Austin Ziegler
|
114
|
+
# This program is free software; you can redistribute it and/or modify it
|
115
|
+
# under the same terms as Ruby, or alternatively under the Perl Artistic
|
116
|
+
# licence.
|
117
|
+
#
|
118
|
+
# == Credits
|
119
|
+
# Much of the documentation is taken directly from the Perl
|
120
|
+
# Algorithm::Diff implementation and was written originally by Mark-Jason
|
121
|
+
# Dominus and later by Ned Konz. The basic Ruby implementation was
|
122
|
+
# re-ported from the Smalltalk implementation, available at
|
123
|
+
# ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
|
124
|
+
#
|
125
|
+
# #sdiff and #traverse_balanced were written for the Perl version by Mike
|
126
|
+
# Schilli <m@perlmeister.com>.
|
127
|
+
#
|
128
|
+
# "The algorithm is described in <em>A Fast Algorithm for Computing
|
129
|
+
# Longest Common Subsequences</em>, CACM, vol.20, no.5, pp.350-353, May
|
130
|
+
# 1977, with a few minor improvements to improve the speed."
|
146
131
|
module LCS
|
147
|
-
VERSION = '1.1.
|
132
|
+
VERSION = '1.1.3'
|
148
133
|
end
|
149
134
|
end
|
150
135
|
|
151
136
|
require 'diff/lcs/callbacks'
|
152
137
|
|
153
138
|
module Diff::LCS
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
139
|
+
# Returns an Array containing the longest common subsequence(s) between
|
140
|
+
# +self+ and +other+. See Diff::LCS#LCS.
|
141
|
+
#
|
142
|
+
# lcs = seq1.lcs(seq2)
|
158
143
|
def lcs(other, &block) #:yields self[ii] if there are matched subsequences:
|
159
144
|
Diff::LCS.LCS(self, other, &block)
|
160
145
|
end
|
161
146
|
|
162
|
-
|
163
|
-
|
147
|
+
# Returns the difference set between +self+ and +other+. See
|
148
|
+
# Diff::LCS#diff.
|
164
149
|
def diff(other, callbacks = nil, &block)
|
165
150
|
Diff::LCS::diff(self, other, callbacks, &block)
|
166
151
|
end
|
167
152
|
|
168
|
-
|
169
|
-
|
153
|
+
# Returns the balanced ("side-by-side") difference set between +self+ and
|
154
|
+
# +other+. See Diff::LCS#sdiff.
|
170
155
|
def sdiff(other, callbacks = nil, &block)
|
171
156
|
Diff::LCS::sdiff(self, other, callbacks, &block)
|
172
157
|
end
|
173
158
|
|
174
|
-
|
175
|
-
|
159
|
+
# Traverses the discovered longest common subsequences between +self+ and
|
160
|
+
# +other+. See Diff::LCS#traverse_sequences.
|
176
161
|
def traverse_sequences(other, callbacks = nil, &block)
|
177
|
-
traverse_sequences(self, other, callbacks ||
|
178
|
-
&block)
|
162
|
+
traverse_sequences(self, other, callbacks ||
|
163
|
+
Diff::LCS::YieldingCallbacks, &block)
|
179
164
|
end
|
180
165
|
|
181
|
-
|
182
|
-
|
183
|
-
|
166
|
+
# Traverses the discovered longest common subsequences between +self+ and
|
167
|
+
# +other+ using the alternate, balanced algorithm. See
|
168
|
+
# Diff::LCS#traverse_balanced.
|
184
169
|
def traverse_balanced(other, callbacks = nil, &block)
|
185
|
-
traverse_balanced(self, other, callbacks ||
|
186
|
-
&block)
|
170
|
+
traverse_balanced(self, other, callbacks ||
|
171
|
+
Diff::LCS::YieldingCallbacks, &block)
|
187
172
|
end
|
188
173
|
|
189
|
-
|
190
|
-
|
174
|
+
# Attempts to patch a copy of +self+ with the provided +patchset+. See
|
175
|
+
# Diff::LCS#patch.
|
191
176
|
def patch(patchset)
|
192
177
|
Diff::LCS::patch(self.dup, patchset)
|
193
178
|
end
|
194
179
|
|
195
|
-
|
196
|
-
|
180
|
+
# Attempts to unpatch a copy of +self+ with the provided +patchset+. See
|
181
|
+
# Diff::LCS#patch.
|
197
182
|
def unpatch(patchset)
|
198
183
|
Diff::LCS::unpatch(self.dup, patchset)
|
199
184
|
end
|
200
185
|
|
201
|
-
|
202
|
-
|
186
|
+
# Attempts to patch +self+ with the provided +patchset+. See
|
187
|
+
# Diff::LCS#patch!. Does no autodiscovery.
|
203
188
|
def patch!(patchset)
|
204
189
|
Diff::LCS::patch!(self, patchset)
|
205
190
|
end
|
206
191
|
|
207
|
-
|
208
|
-
|
192
|
+
# Attempts to unpatch +self+ with the provided +patchset+. See
|
193
|
+
# Diff::LCS#unpatch. Does no autodiscovery.
|
209
194
|
def unpatch!(patchset)
|
210
195
|
Diff::LCS::unpatch!(self, patchset)
|
211
196
|
end
|
@@ -213,20 +198,20 @@ end
|
|
213
198
|
|
214
199
|
module Diff::LCS
|
215
200
|
class << self
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
201
|
+
# Given two sequenced Enumerables, LCS returns an Array containing their
|
202
|
+
# longest common subsequences.
|
203
|
+
#
|
204
|
+
# lcs = Diff::LCS.LCS(seq1, seq2)
|
205
|
+
#
|
206
|
+
# This array whose contents is such that:
|
207
|
+
#
|
208
|
+
# lcs.each_with_index do |ee, ii|
|
209
|
+
# assert(ee.nil? || (seq1[ii] == seq2[ee]))
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# If a block is provided, the matching subsequences will be yielded from
|
213
|
+
# +seq1+ in turn and may be modified before they are placed into the
|
214
|
+
# returned Array of subsequences.
|
230
215
|
def LCS(seq1, seq2, &block) #:yields seq1[ii] for each matched:
|
231
216
|
matches = Diff::LCS.__lcs(seq1, seq2)
|
232
217
|
ret = []
|
@@ -242,15 +227,15 @@ module Diff::LCS
|
|
242
227
|
ret
|
243
228
|
end
|
244
229
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
230
|
+
# Diff::LCS.diff computes the smallest set of additions and deletions
|
231
|
+
# necessary to turn the first sequence into the second, and returns a
|
232
|
+
# description of these changes.
|
233
|
+
#
|
234
|
+
# See Diff::LCS::DiffCallbacks for the default behaviour. An alternate
|
235
|
+
# behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If
|
236
|
+
# a Class argument is provided for +callbacks+, #diff will attempt to
|
237
|
+
# initialise it. If the +callbacks+ object (possibly initialised)
|
238
|
+
# responds to #finish, it will be called.
|
254
239
|
def diff(seq1, seq2, callbacks = nil, &block) # :yields diff changes:
|
255
240
|
callbacks ||= Diff::LCS::DiffCallbacks
|
256
241
|
if callbacks.kind_of?(Class)
|
@@ -263,7 +248,7 @@ module Diff::LCS
|
|
263
248
|
if block_given?
|
264
249
|
res = callbacks.diffs.map do |hunk|
|
265
250
|
if hunk.kind_of?(Array)
|
266
|
-
hunk = hunk.map { |
|
251
|
+
hunk = hunk.map { |hunk_block| yield hunk_block }
|
267
252
|
else
|
268
253
|
yield hunk
|
269
254
|
end
|
@@ -274,20 +259,20 @@ module Diff::LCS
|
|
274
259
|
end
|
275
260
|
end
|
276
261
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
262
|
+
# Diff::LCS.sdiff computes all necessary components to show two sequences
|
263
|
+
# and their minimized differences side by side, just like the Unix
|
264
|
+
# utility <em>sdiff</em> does:
|
265
|
+
#
|
266
|
+
# old < -
|
267
|
+
# same same
|
268
|
+
# before | after
|
269
|
+
# - > new
|
270
|
+
#
|
271
|
+
# See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate
|
272
|
+
# behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If
|
273
|
+
# a Class argument is provided for +callbacks+, #diff will attempt to
|
274
|
+
# initialise it. If the +callbacks+ object (possibly initialised)
|
275
|
+
# responds to #finish, it will be called.
|
291
276
|
def sdiff(seq1, seq2, callbacks = nil, &block) #:yields diff changes:
|
292
277
|
callbacks ||= Diff::LCS::SDiffCallbacks
|
293
278
|
if callbacks.kind_of?(Class)
|
@@ -300,7 +285,7 @@ module Diff::LCS
|
|
300
285
|
if block_given?
|
301
286
|
res = callbacks.diffs.map do |hunk|
|
302
287
|
if hunk.kind_of?(Array)
|
303
|
-
hunk = hunk.map { |
|
288
|
+
hunk = hunk.map { |hunk_block| yield hunk_block }
|
304
289
|
else
|
305
290
|
yield hunk
|
306
291
|
end
|
@@ -311,87 +296,88 @@ module Diff::LCS
|
|
311
296
|
end
|
312
297
|
end
|
313
298
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
299
|
+
# Diff::LCS.traverse_sequences is the most general facility provided by this
|
300
|
+
# module; +diff+ and +LCS+ are implemented as calls to it.
|
301
|
+
#
|
302
|
+
# The arguments to #traverse_sequences are the two sequences to
|
303
|
+
# traverse, and a callback object, like this:
|
304
|
+
#
|
305
|
+
# traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
|
306
|
+
#
|
307
|
+
# #diff is implemented with #traverse_sequences.
|
308
|
+
#
|
309
|
+
# == Callback Methods
|
310
|
+
# Optional callback methods are <em>emphasized</em>.
|
311
|
+
#
|
312
|
+
# callbacks#match:: Called when +a+ and +b+ are pointing
|
313
|
+
# to common elements in +A+ and +B+.
|
314
|
+
# callbacks#discard_a:: Called when +a+ is pointing to an
|
315
|
+
# element not in +B+.
|
316
|
+
# callbacks#discard_b:: Called when +b+ is pointing to an
|
317
|
+
# element not in +A+.
|
318
|
+
# <em>callbacks#finished_a</em>:: Called when +a+ has reached the end of
|
319
|
+
# sequence +A+.
|
320
|
+
# <em>callbacks#finished_b</em>:: Called when +b+ has reached the end of
|
321
|
+
# sequence +B+.
|
322
|
+
#
|
323
|
+
# == Algorithm
|
324
|
+
# a---+
|
325
|
+
# v
|
326
|
+
# A = a b c e h j l m n p
|
327
|
+
# B = b c d e f j k l m r s t
|
328
|
+
# ^
|
329
|
+
# b---+
|
330
|
+
#
|
331
|
+
# If there are two arrows (+a+ and +b+) pointing to elements of
|
332
|
+
# sequences +A+ and +B+, the arrows will initially point to the first
|
333
|
+
# elements of their respective sequences. #traverse_sequences will
|
334
|
+
# advance the arrows through the sequences one element at a time,
|
335
|
+
# calling a method on the user-specified callback object before each
|
336
|
+
# advance. It will advance the arrows in such a way that if there are
|
337
|
+
# elements <tt>A[ii]</tt> and <tt>B[jj]</tt> which are both equal and
|
338
|
+
# part of the longest common subsequence, there will be some moment
|
339
|
+
# during the execution of #traverse_sequences when arrow +a+ is pointing
|
340
|
+
# to <tt>A[ii]</tt> and arrow +b+ is pointing to <tt>B[jj]</tt>. When
|
341
|
+
# this happens, #traverse_sequences will call <tt>callbacks#match</tt>
|
342
|
+
# and then it will advance both arrows.
|
343
|
+
#
|
344
|
+
# Otherwise, one of the arrows is pointing to an element of its sequence
|
345
|
+
# that is not part of the longest common subsequence.
|
346
|
+
# #traverse_sequences will advance that arrow and will call
|
347
|
+
# <tt>callbacks#discard_a</tt> or <tt>callbacks#discard_b</tt>, depending
|
348
|
+
# on which arrow it advanced. If both arrows point to elements that are
|
349
|
+
# not part of the longest common subsequence, then #traverse_sequences
|
350
|
+
# will advance one of them and call the appropriate callback, but it is
|
351
|
+
# not specified which it will call.
|
352
|
+
#
|
353
|
+
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
|
354
|
+
# and <tt>callbacks#discard_b</tt> are invoked with an event comprising
|
355
|
+
# the action ("=", "+", or "-", respectively), the indicies +ii+ and
|
356
|
+
# +jj+, and the elements <tt>A[ii]</tt> and <tt>B[jj]</tt>. Return
|
357
|
+
# values are discarded by #traverse_sequences.
|
358
|
+
#
|
359
|
+
# === End of Sequences
|
360
|
+
# If arrow +a+ reaches the end of its sequence before arrow +b+ does,
|
361
|
+
# #traverse_sequence will try to call <tt>callbacks#finished_a</tt> with
|
362
|
+
# the last index and element of +A+ (<tt>A[-1]</tt>) and the current
|
363
|
+
# index and element of +B+ (<tt>B[jj]</tt>). If
|
364
|
+
# <tt>callbacks#finished_a</tt> does not exist, then
|
365
|
+
# <tt>callbacks#discard_b</tt> will be called on each element of +B+
|
366
|
+
# until the end of the sequence is reached (the call
|
367
|
+
# will be done with <tt>A[-1]</tt> and <tt>B[jj]</tt> for each element).
|
368
|
+
#
|
369
|
+
# If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
|
370
|
+
# <tt>callbacks#finished_b</tt> will be called with the current index
|
371
|
+
# and element of +A+ (<tt>A[ii]</tt>) and the last index and element of
|
372
|
+
# +B+ (<tt>A[-1]</tt>). Again, if <tt>callbacks#finished_b</tt> does not
|
373
|
+
# exist on the callback object, then <tt>callbacks#discard_a</tt> will
|
374
|
+
# be called on each element of +A+ until the end of the sequence is
|
375
|
+
# reached (<tt>A[ii]</tt> and <tt>B[-1]</tt>).
|
376
|
+
#
|
377
|
+
# There is a chance that one additional <tt>callbacks#discard_a</tt> or
|
378
|
+
# <tt>callbacks#discard_b</tt> will be called after the end of the
|
379
|
+
# sequence is reached, if +a+ has not yet reached the end of +A+ or +b+
|
380
|
+
# has not yet reached the end of +B+.
|
395
381
|
def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks, &block) #:yields change events:
|
396
382
|
matches = Diff::LCS.__lcs(seq1, seq2)
|
397
383
|
|
@@ -433,10 +419,10 @@ module Diff::LCS
|
|
433
419
|
end
|
434
420
|
ai += 1
|
435
421
|
|
436
|
-
|
437
|
-
|
422
|
+
# The last entry (if any) processed was a match. +ai+ and +bj+ point
|
423
|
+
# just past the last matching lines in their sequences.
|
438
424
|
while (ai < a_size) or (bj < b_size)
|
439
|
-
|
425
|
+
# last A?
|
440
426
|
if ai == a_size and bj < b_size
|
441
427
|
if callbacks.respond_to?(:finished_a) and not run_finished_a
|
442
428
|
ax = string ? seq1[-1, 1] : seq1[-1]
|
@@ -458,7 +444,7 @@ module Diff::LCS
|
|
458
444
|
end
|
459
445
|
end
|
460
446
|
|
461
|
-
|
447
|
+
# last B?
|
462
448
|
if bj == b_size and ai < a_size
|
463
449
|
if callbacks.respond_to?(:finished_b) and not run_finished_b
|
464
450
|
ax = string ? seq1[ai, 1] : seq1[ai]
|
@@ -500,88 +486,88 @@ module Diff::LCS
|
|
500
486
|
end
|
501
487
|
end
|
502
488
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
489
|
+
# #traverse_balanced is an alternative to #traverse_sequences. It
|
490
|
+
# uses a different algorithm to iterate through the entries in the
|
491
|
+
# computed longest common subsequence. Instead of viewing the changes as
|
492
|
+
# insertions or deletions from one of the sequences, #traverse_balanced
|
493
|
+
# will report <em>changes</em> between the sequences. To represent a
|
494
|
+
#
|
495
|
+
# The arguments to #traverse_balanced are the two sequences to traverse
|
496
|
+
# and a callback object, like this:
|
497
|
+
#
|
498
|
+
# traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
|
499
|
+
#
|
500
|
+
# #sdiff is implemented with #traverse_balanced.
|
501
|
+
#
|
502
|
+
# == Callback Methods
|
503
|
+
# Optional callback methods are <em>emphasized</em>.
|
504
|
+
#
|
505
|
+
# callbacks#match:: Called when +a+ and +b+ are pointing
|
506
|
+
# to common elements in +A+ and +B+.
|
507
|
+
# callbacks#discard_a:: Called when +a+ is pointing to an
|
508
|
+
# element not in +B+.
|
509
|
+
# callbacks#discard_b:: Called when +b+ is pointing to an
|
510
|
+
# element not in +A+.
|
511
|
+
# <em>callbacks#change</em>:: Called when +a+ and +b+ are pointing
|
512
|
+
# to the same relative position, but
|
513
|
+
# <tt>A[a]</tt> and <tt>B[b]</tt> are
|
514
|
+
# not the same; a <em>change</em> has
|
515
|
+
# occurred.
|
516
|
+
#
|
517
|
+
# #traverse_balanced might be a bit slower than #traverse_sequences,
|
518
|
+
# noticable only while processing huge amounts of data.
|
519
|
+
#
|
520
|
+
# The +sdiff+ function of this module is implemented as call to
|
521
|
+
# #traverse_balanced.
|
522
|
+
#
|
523
|
+
# == Algorithm
|
524
|
+
# a---+
|
525
|
+
# v
|
526
|
+
# A = a b c e h j l m n p
|
527
|
+
# B = b c d e f j k l m r s t
|
528
|
+
# ^
|
529
|
+
# b---+
|
530
|
+
#
|
531
|
+
# === Matches
|
532
|
+
# If there are two arrows (+a+ and +b+) pointing to elements of
|
533
|
+
# sequences +A+ and +B+, the arrows will initially point to the first
|
534
|
+
# elements of their respective sequences. #traverse_sequences will
|
535
|
+
# advance the arrows through the sequences one element at a time,
|
536
|
+
# calling a method on the user-specified callback object before each
|
537
|
+
# advance. It will advance the arrows in such a way that if there are
|
538
|
+
# elements <tt>A[ii]</tt> and <tt>B[jj]</tt> which are both equal and
|
539
|
+
# part of the longest common subsequence, there will be some moment
|
540
|
+
# during the execution of #traverse_sequences when arrow +a+ is pointing
|
541
|
+
# to <tt>A[ii]</tt> and arrow +b+ is pointing to <tt>B[jj]</tt>. When
|
542
|
+
# this happens, #traverse_sequences will call <tt>callbacks#match</tt>
|
543
|
+
# and then it will advance both arrows.
|
544
|
+
#
|
545
|
+
# === Discards
|
546
|
+
# Otherwise, one of the arrows is pointing to an element of its sequence
|
547
|
+
# that is not part of the longest common subsequence.
|
548
|
+
# #traverse_sequences will advance that arrow and will call
|
549
|
+
# <tt>callbacks#discard_a</tt> or <tt>callbacks#discard_b</tt>,
|
550
|
+
# depending on which arrow it advanced.
|
551
|
+
#
|
552
|
+
# === Changes
|
553
|
+
# If both +a+ and +b+ point to elements that are not part of the longest
|
554
|
+
# common subsequence, then #traverse_sequences will try to call
|
555
|
+
# <tt>callbacks#change</tt> and advance both arrows. If
|
556
|
+
# <tt>callbacks#change</tt> is not implemented, then
|
557
|
+
# <tt>callbacks#discard_a</tt> and <tt>callbacks#discard_b</tt> will be
|
558
|
+
# called in turn.
|
559
|
+
#
|
560
|
+
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
|
561
|
+
# <tt>callbacks#discard_b</tt>, and <tt>callbacks#change</tt> are
|
562
|
+
# invoked with an event comprising the action ("=", "+", "-", or "!",
|
563
|
+
# respectively), the indicies +ii+ and +jj+, and the elements
|
564
|
+
# <tt>A[ii]</tt> and <tt>B[jj]</tt>. Return values are discarded by
|
565
|
+
# #traverse_balanced.
|
566
|
+
#
|
567
|
+
# === Context
|
568
|
+
# Note that +ii+ and +jj+ may not be the same index position, even if
|
569
|
+
# +a+ and +b+ are considered to be pointing to matching or changed
|
570
|
+
# elements.
|
585
571
|
def traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks)
|
586
572
|
matches = Diff::LCS.__lcs(seq1, seq2)
|
587
573
|
a_size = seq1.size
|
@@ -690,10 +676,10 @@ module Diff::LCS
|
|
690
676
|
:unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }
|
691
677
|
}
|
692
678
|
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
679
|
+
# Given a patchset, convert the current version to the new
|
680
|
+
# version. If +direction+ is not specified (must be
|
681
|
+
# <tt>:patch</tt> or <tt>:unpatch</tt>), then discovery of the
|
682
|
+
# direction of the patch will be attempted.
|
697
683
|
def patch(src, patchset, direction = nil)
|
698
684
|
string = src.kind_of?(String)
|
699
685
|
# Start with a new empty type of the source's class
|
@@ -793,51 +779,54 @@ module Diff::LCS
|
|
793
779
|
res
|
794
780
|
end
|
795
781
|
|
796
|
-
|
797
|
-
|
782
|
+
# Given a set of patchset, convert the current version to the prior
|
783
|
+
# version. Does no auto-discovery.
|
798
784
|
def unpatch!(src, patchset)
|
799
785
|
Diff::LCS.patch(src, patchset, :unpatch)
|
800
786
|
end
|
801
787
|
|
802
|
-
|
803
|
-
|
788
|
+
# Given a set of patchset, convert the current version to the next
|
789
|
+
# version. Does no auto-discovery.
|
804
790
|
def patch!(src, patchset)
|
805
791
|
Diff::LCS.patch(src, patchset, :patch)
|
806
792
|
end
|
807
793
|
|
808
794
|
# private
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
795
|
+
# Compute the longest common subsequence between the sequenced
|
796
|
+
# Enumerables +a+ and +b+. The result is an array whose contents is such
|
797
|
+
# that
|
798
|
+
#
|
799
|
+
# result = Diff::LCS.__lcs(a, b)
|
800
|
+
# result.each_with_index do |e, ii|
|
801
|
+
# assert_equal(a[ii], b[e]) unless e.nil?
|
802
|
+
# end
|
803
|
+
#
|
804
|
+
# Note: This will be deprecated as a public function in a future release.
|
816
805
|
def __lcs(a, b)
|
817
806
|
a_start = b_start = 0
|
818
807
|
a_finish = a.size - 1
|
819
808
|
b_finish = b.size - 1
|
820
809
|
vector = []
|
821
810
|
|
822
|
-
|
811
|
+
# Prune off any common elements at the beginning...
|
823
812
|
while (a_start <= a_finish) and
|
824
|
-
|
825
|
-
|
813
|
+
(b_start <= b_finish) and
|
814
|
+
(a[a_start] == b[b_start])
|
826
815
|
vector[a_start] = b_start
|
827
816
|
a_start += 1
|
828
817
|
b_start += 1
|
829
818
|
end
|
830
819
|
|
831
|
-
|
820
|
+
# Now the end...
|
832
821
|
while (a_start <= a_finish) and
|
833
|
-
|
834
|
-
|
822
|
+
(b_start <= b_finish) and
|
823
|
+
(a[a_finish] == b[b_finish])
|
835
824
|
vector[a_finish] = b_finish
|
836
825
|
a_finish -= 1
|
837
826
|
b_finish -= 1
|
838
827
|
end
|
839
828
|
|
840
|
-
|
829
|
+
# Now, compute the equivalence classes of positions of elements.
|
841
830
|
b_matches = Diff::LCS.__position_hash(b, b_start .. b_finish)
|
842
831
|
|
843
832
|
thresh = []
|
@@ -868,14 +857,16 @@ module Diff::LCS
|
|
868
857
|
vector
|
869
858
|
end
|
870
859
|
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
860
|
+
# Find the place at which +value+ would normally be inserted into the
|
861
|
+
# Enumerable. If that place is already occupied by +value+, do nothing
|
862
|
+
# and return +nil+. If the place does not exist (i.e., it is off the end
|
863
|
+
# of the Enumerable), add it to the end. Otherwise, replace the element
|
864
|
+
# at that point with +value+. It is assumed that the Enumerable's values
|
865
|
+
# are numeric.
|
866
|
+
#
|
867
|
+
# This operation preserves the sort order.
|
868
|
+
#
|
869
|
+
# Note: This will be deprecated as a public function in a future release.
|
879
870
|
def __replace_next_larger(enum, value, last_index = nil)
|
880
871
|
# Off the end?
|
881
872
|
if enum.empty? or (value > enum[-1])
|
@@ -906,9 +897,11 @@ module Diff::LCS
|
|
906
897
|
return first_index
|
907
898
|
end
|
908
899
|
|
909
|
-
|
910
|
-
|
911
|
-
|
900
|
+
# If +vector+ maps the matching elements of another collection onto this
|
901
|
+
# Enumerable, compute the inverse +vector+ that maps this Enumerable
|
902
|
+
# onto the collection. (Currently unused.)
|
903
|
+
#
|
904
|
+
# Note: This will be deprecated as a public function in a future release.
|
912
905
|
def __inverse_vector(a, vector)
|
913
906
|
inverse = a.dup
|
914
907
|
(0 ... vector.size).each do |ii|
|
@@ -917,9 +910,11 @@ module Diff::LCS
|
|
917
910
|
inverse
|
918
911
|
end
|
919
912
|
|
920
|
-
|
921
|
-
|
922
|
-
|
913
|
+
# Returns a hash mapping each element of an Enumerable to the set of
|
914
|
+
# positions it occupies in the Enumerable, optionally restricted to the
|
915
|
+
# elements specified in the range of indexes specified by +interval+.
|
916
|
+
#
|
917
|
+
# Note: This will be deprecated as a public function in a future release.
|
923
918
|
def __position_hash(enum, interval = 0 .. -1)
|
924
919
|
hash = Hash.new { |hh, kk| hh[kk] = [] }
|
925
920
|
interval.each do |ii|
|
@@ -929,13 +924,15 @@ module Diff::LCS
|
|
929
924
|
hash
|
930
925
|
end
|
931
926
|
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
927
|
+
# Examine the patchset and the source to see in which direction the
|
928
|
+
# patch should be applied.
|
929
|
+
#
|
930
|
+
# WARNING: By default, this examines the whole patch, so this could take
|
931
|
+
# some time. This also works better with Diff::LCS::ContextChange or
|
932
|
+
# Diff::LCS::Change as its source, as an array will cause the creation
|
933
|
+
# of one of the above.
|
934
|
+
#
|
935
|
+
# Note: This will be deprecated as a public function in a future release.
|
939
936
|
def __diff_direction(src, patchset, limit = nil)
|
940
937
|
count = left = left_miss = right = right_miss = 0
|
941
938
|
string = src.kind_of?(String)
|
@@ -945,9 +942,9 @@ module Diff::LCS
|
|
945
942
|
|
946
943
|
case change
|
947
944
|
when Diff::LCS::Change
|
948
|
-
|
949
|
-
|
950
|
-
|
945
|
+
# With a simplistic change, we can't tell the difference between
|
946
|
+
# the left and right on '!' actions, so we ignore those. On '='
|
947
|
+
# actions, if there's a miss, we miss both left and right.
|
951
948
|
element = string ? src[change.position, 1] : src[change.position]
|
952
949
|
|
953
950
|
case change.action
|
@@ -1007,7 +1004,7 @@ module Diff::LCS
|
|
1007
1004
|
end
|
1008
1005
|
end
|
1009
1006
|
|
1010
|
-
break if not limit.nil?
|
1007
|
+
break if (not limit.nil?) && (count > limit)
|
1011
1008
|
end
|
1012
1009
|
|
1013
1010
|
no_left = (left == 0) and (left_miss >= 0)
|
@@ -1023,55 +1020,56 @@ module Diff::LCS
|
|
1023
1020
|
end
|
1024
1021
|
end
|
1025
1022
|
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1023
|
+
# Normalize the patchset. A patchset is always a sequence of changes, but
|
1024
|
+
# how those changes are represented may vary, depending on how they were
|
1025
|
+
# generated. In all cases we support, we also support the array
|
1026
|
+
# representation of the changes. The formats are:
|
1027
|
+
#
|
1028
|
+
# [ # patchset <- Diff::LCS.diff(a, b)
|
1029
|
+
# [ # one or more hunks
|
1030
|
+
# Diff::LCS::Change # one or more changes
|
1031
|
+
# ] ]
|
1032
|
+
#
|
1033
|
+
# [ # patchset, equivalent to the above
|
1034
|
+
# [ # one or more hunks
|
1035
|
+
# [ action, line, value ] # one or more changes
|
1036
|
+
# ] ]
|
1037
|
+
#
|
1038
|
+
# [ # patchset <- Diff::LCS.diff(a, b, Diff::LCS::ContextDiffCallbacks)
|
1039
|
+
# # OR <- Diff::LCS.sdiff(a, b, Diff::LCS::ContextDiffCallbacks)
|
1040
|
+
# [ # one or more hunks
|
1041
|
+
# Diff::LCS::ContextChange # one or more changes
|
1042
|
+
# ] ]
|
1043
|
+
#
|
1044
|
+
# [ # patchset, equivalent to the above
|
1045
|
+
# [ # one or more hunks
|
1046
|
+
# [ action, [ old line, old value ], [ new line, new value ] ]
|
1047
|
+
# # one or more changes
|
1048
|
+
# ] ]
|
1049
|
+
#
|
1050
|
+
# [ # patchset <- Diff::LCS.sdiff(a, b)
|
1051
|
+
# # OR <- Diff::LCS.diff(a, b, Diff::LCS::SDiffCallbacks)
|
1052
|
+
# Diff::LCS::ContextChange # one or more changes
|
1053
|
+
# ]
|
1054
|
+
#
|
1055
|
+
# [ # patchset, equivalent to the above
|
1056
|
+
# [ action, [ old line, old value ], [ new line, new value ] ]
|
1057
|
+
# # one or more changes
|
1058
|
+
# ]
|
1059
|
+
#
|
1060
|
+
# The result of this will be either of the following.
|
1061
|
+
#
|
1062
|
+
# [ # patchset
|
1063
|
+
# Diff::LCS::ContextChange # one or more changes
|
1064
|
+
# ]
|
1065
|
+
#
|
1066
|
+
# [ # patchset
|
1067
|
+
# Diff::LCS::Change # one or more changes
|
1068
|
+
# ]
|
1069
|
+
#
|
1070
|
+
# If either of the above is provided, it will be returned as such.
|
1071
|
+
#
|
1072
|
+
# Note: This will be deprecated as a public function in a future release.
|
1075
1073
|
def __normalize_patchset(patchset)
|
1076
1074
|
patchset.map do |hunk|
|
1077
1075
|
case hunk
|
@@ -1103,3 +1101,5 @@ module Diff::LCS
|
|
1103
1101
|
end
|
1104
1102
|
end
|
1105
1103
|
end
|
1104
|
+
|
1105
|
+
# vim: ft=ruby
|