annotate lib/diff.rb @ 1628:9c5f8e24dadc live tip

Quieten this cron script
author Chris Cannam
date Tue, 25 Aug 2020 11:38:49 +0100
parents 433d4f72a19b
children
rev   line source
Chris@0 1 module RedmineDiff
Chris@0 2 class Diff
Chris@0 3
Chris@0 4 VERSION = 0.3
Chris@0 5
Chris@0 6 def Diff.lcs(a, b)
Chris@0 7 astart = 0
Chris@0 8 bstart = 0
Chris@0 9 afinish = a.length-1
Chris@0 10 bfinish = b.length-1
Chris@0 11 mvector = []
Chris@909 12
Chris@0 13 # First we prune off any common elements at the beginning
Chris@0 14 while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
Chris@0 15 mvector[astart] = bstart
Chris@0 16 astart += 1
Chris@0 17 bstart += 1
Chris@0 18 end
Chris@909 19
Chris@0 20 # now the end
Chris@0 21 while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
Chris@0 22 mvector[afinish] = bfinish
Chris@0 23 afinish -= 1
Chris@0 24 bfinish -= 1
Chris@0 25 end
Chris@0 26
Chris@0 27 bmatches = b.reverse_hash(bstart..bfinish)
Chris@0 28 thresh = []
Chris@0 29 links = []
Chris@909 30
Chris@0 31 (astart..afinish).each { |aindex|
Chris@0 32 aelem = a[aindex]
Chris@0 33 next unless bmatches.has_key? aelem
Chris@0 34 k = nil
Chris@0 35 bmatches[aelem].reverse.each { |bindex|
Chris@1115 36 if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
Chris@1115 37 thresh[k] = bindex
Chris@1115 38 else
Chris@1115 39 k = thresh.replacenextlarger(bindex, k)
Chris@1115 40 end
Chris@1115 41 links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
Chris@0 42 }
Chris@0 43 }
Chris@0 44
Chris@0 45 if !thresh.empty?
Chris@0 46 link = links[thresh.length-1]
Chris@0 47 while link
Chris@1115 48 mvector[link[1]] = link[2]
Chris@1115 49 link = link[0]
Chris@0 50 end
Chris@0 51 end
Chris@0 52
Chris@0 53 return mvector
Chris@0 54 end
Chris@0 55
Chris@0 56 def makediff(a, b)
Chris@0 57 mvector = Diff.lcs(a, b)
Chris@0 58 ai = bi = 0
Chris@0 59 while ai < mvector.length
Chris@0 60 bline = mvector[ai]
Chris@0 61 if bline
Chris@1115 62 while bi < bline
Chris@1115 63 discardb(bi, b[bi])
Chris@1115 64 bi += 1
Chris@1115 65 end
Chris@1115 66 match(ai, bi)
Chris@1115 67 bi += 1
Chris@0 68 else
Chris@1115 69 discarda(ai, a[ai])
Chris@0 70 end
Chris@0 71 ai += 1
Chris@0 72 end
Chris@0 73 while ai < a.length
Chris@0 74 discarda(ai, a[ai])
Chris@0 75 ai += 1
Chris@0 76 end
Chris@0 77 while bi < b.length
Chris@0 78 discardb(bi, b[bi])
Chris@0 79 bi += 1
Chris@0 80 end
Chris@0 81 match(ai, bi)
Chris@0 82 1
Chris@0 83 end
Chris@0 84
Chris@0 85 def compactdiffs
Chris@0 86 diffs = []
Chris@0 87 @diffs.each { |df|
Chris@0 88 i = 0
Chris@0 89 curdiff = []
Chris@0 90 while i < df.length
Chris@1115 91 whot = df[i][0]
Chris@1115 92 s = @isstring ? df[i][2].chr : [df[i][2]]
Chris@1115 93 p = df[i][1]
Chris@1115 94 last = df[i][1]
Chris@1115 95 i += 1
Chris@1115 96 while df[i] && df[i][0] == whot && df[i][1] == last+1
Chris@1115 97 s << df[i][2]
Chris@1115 98 last = df[i][1]
Chris@1115 99 i += 1
Chris@1115 100 end
Chris@1115 101 curdiff.push [whot, p, s]
Chris@0 102 end
Chris@0 103 diffs.push curdiff
Chris@0 104 }
Chris@0 105 return diffs
Chris@0 106 end
Chris@0 107
Chris@0 108 attr_reader :diffs, :difftype
Chris@0 109
Chris@0 110 def initialize(diffs_or_a, b = nil, isstring = nil)
Chris@0 111 if b.nil?
Chris@0 112 @diffs = diffs_or_a
Chris@0 113 @isstring = isstring
Chris@0 114 else
Chris@0 115 @diffs = []
Chris@0 116 @curdiffs = []
Chris@0 117 makediff(diffs_or_a, b)
Chris@0 118 @difftype = diffs_or_a.class
Chris@0 119 end
Chris@0 120 end
Chris@909 121
Chris@0 122 def match(ai, bi)
Chris@0 123 @diffs.push @curdiffs unless @curdiffs.empty?
Chris@0 124 @curdiffs = []
Chris@0 125 end
Chris@0 126
Chris@0 127 def discarda(i, elem)
Chris@0 128 @curdiffs.push ['-', i, elem]
Chris@0 129 end
Chris@0 130
Chris@0 131 def discardb(i, elem)
Chris@0 132 @curdiffs.push ['+', i, elem]
Chris@0 133 end
Chris@0 134
Chris@0 135 def compact
Chris@0 136 return Diff.new(compactdiffs)
Chris@0 137 end
Chris@0 138
Chris@0 139 def compact!
Chris@0 140 @diffs = compactdiffs
Chris@0 141 end
Chris@0 142
Chris@0 143 def inspect
Chris@0 144 @diffs.inspect
Chris@0 145 end
Chris@0 146
Chris@0 147 end
Chris@0 148 end
Chris@0 149
Chris@0 150 module Diffable
Chris@0 151 def diff(b)
Chris@0 152 RedmineDiff::Diff.new(self, b)
Chris@0 153 end
Chris@0 154
Chris@0 155 # Create a hash that maps elements of the array to arrays of indices
Chris@0 156 # where the elements are found.
Chris@0 157
Chris@0 158 def reverse_hash(range = (0...self.length))
Chris@0 159 revmap = {}
Chris@0 160 range.each { |i|
Chris@0 161 elem = self[i]
Chris@0 162 if revmap.has_key? elem
Chris@1115 163 revmap[elem].push i
Chris@0 164 else
Chris@1115 165 revmap[elem] = [i]
Chris@0 166 end
Chris@0 167 }
Chris@0 168 return revmap
Chris@0 169 end
Chris@0 170
Chris@0 171 def replacenextlarger(value, high = nil)
Chris@0 172 high ||= self.length
Chris@0 173 if self.empty? || value > self[-1]
Chris@0 174 push value
Chris@0 175 return high
Chris@0 176 end
Chris@0 177 # binary search for replacement point
Chris@0 178 low = 0
Chris@0 179 while low < high
Chris@0 180 index = (high+low)/2
Chris@0 181 found = self[index]
Chris@0 182 return nil if value == found
Chris@0 183 if value > found
Chris@1115 184 low = index + 1
Chris@0 185 else
Chris@1115 186 high = index
Chris@0 187 end
Chris@0 188 end
Chris@0 189
Chris@0 190 self[low] = value
Chris@0 191 # $stderr << "replace #{value} : 0/#{low}/#{init_high} (#{steps} steps) (#{init_high-low} off )\n"
Chris@0 192 # $stderr.puts self.inspect
Chris@0 193 #gets
Chris@0 194 #p length - low
Chris@0 195 return low
Chris@0 196 end
Chris@0 197
Chris@0 198 def patch(diff)
Chris@0 199 newary = nil
Chris@0 200 if diff.difftype == String
Chris@0 201 newary = diff.difftype.new('')
Chris@0 202 else
Chris@0 203 newary = diff.difftype.new
Chris@0 204 end
Chris@0 205 ai = 0
Chris@0 206 bi = 0
Chris@0 207 diff.diffs.each { |d|
Chris@0 208 d.each { |mod|
Chris@1115 209 case mod[0]
Chris@1115 210 when '-'
Chris@1115 211 while ai < mod[1]
Chris@1115 212 newary << self[ai]
Chris@1115 213 ai += 1
Chris@1115 214 bi += 1
Chris@1115 215 end
Chris@1115 216 ai += 1
Chris@1115 217 when '+'
Chris@1115 218 while bi < mod[1]
Chris@1115 219 newary << self[ai]
Chris@1115 220 ai += 1
Chris@1115 221 bi += 1
Chris@1115 222 end
Chris@1115 223 newary << mod[2]
Chris@1115 224 bi += 1
Chris@1115 225 else
Chris@1115 226 raise "Unknown diff action"
Chris@1115 227 end
Chris@0 228 }
Chris@0 229 }
Chris@0 230 while ai < self.length
Chris@0 231 newary << self[ai]
Chris@0 232 ai += 1
Chris@0 233 bi += 1
Chris@0 234 end
Chris@0 235 return newary
Chris@0 236 end
Chris@0 237 end
Chris@0 238
Chris@0 239 class Array
Chris@0 240 include Diffable
Chris@0 241 end
Chris@0 242
Chris@0 243 class String
Chris@0 244 include Diffable
Chris@0 245 end
Chris@0 246
Chris@0 247 =begin
Chris@0 248 = Diff
Chris@0 249 (({diff.rb})) - computes the differences between two arrays or
Chris@0 250 strings. Copyright (C) 2001 Lars Christensen
Chris@0 251
Chris@0 252 == Synopsis
Chris@0 253
Chris@0 254 diff = Diff.new(a, b)
Chris@0 255 b = a.patch(diff)
Chris@0 256
Chris@0 257 == Class Diff
Chris@0 258 === Class Methods
Chris@0 259 --- Diff.new(a, b)
Chris@0 260 --- a.diff(b)
Chris@0 261 Creates a Diff object which represent the differences between
Chris@0 262 ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
Chris@0 263 of any objects, strings, or object of any class that include
Chris@0 264 module ((|Diffable|))
Chris@0 265
Chris@0 266 == Module Diffable
Chris@0 267 The module ((|Diffable|)) is intended to be included in any class for
Chris@0 268 which differences are to be computed. Diffable is included into String
Chris@0 269 and Array when (({diff.rb})) is (({require}))'d.
Chris@0 270
Chris@0 271 Classes including Diffable should implement (({[]})) to get element at
Chris@0 272 integer indices, (({<<})) to append elements to the object and
Chris@0 273 (({ClassName#new})) should accept 0 arguments to create a new empty
Chris@0 274 object.
Chris@0 275
Chris@0 276 === Instance Methods
Chris@0 277 --- Diffable#patch(diff)
Chris@0 278 Applies the differences from ((|diff|)) to the object ((|obj|))
Chris@0 279 and return the result. ((|obj|)) is not changed. ((|obj|)) and
Chris@0 280 can be either an array or a string, but must match the object
Chris@0 281 from which the ((|diff|)) was created.
Chris@0 282 =end