|
1 | 1 | // swiftlint:disable line_length |
2 | 2 | // swiftlint:disable file_length |
3 | | -// swiftlint:disable cyclomatic_complexity |
4 | 3 | // |
5 | 4 | // DiffArray.swift |
6 | 5 | // DBDB |
@@ -233,162 +232,191 @@ private func diff_computeDiffsBetweenArrays<T: Equatable>(arrayA: [T], arrayB: [ |
233 | 232 | return diff_bisectOfArrays(arrayA: arrayA, arrayB: arrayB) |
234 | 233 | } |
235 | 234 |
|
236 | | -// yes this method is way too long. Pull requests welcome! |
| 235 | +private class Kay { |
| 236 | + var start: Int = 0 |
| 237 | + var end: Int = 0 |
| 238 | + var index: Int = 0 |
| 239 | +} |
237 | 240 |
|
238 | | -// swiftlint:disable function_body_length |
239 | | -func diff_bisectOfArrays<T: Equatable>(arrayA inArrayA: [T], arrayB inArrayB: [T]) -> [Diff<T>] { |
240 | | - let arrayALength = inArrayA.count |
241 | | - let arrayBLength = inArrayB.count |
242 | | - var haveFoundDiffs = false |
243 | | - var diffs: [Diff<T>] = [] |
| 241 | +private struct CommonPathParameters<T: Equatable> { |
| 242 | + let vOffset: Int |
| 243 | + let inArrayA: [T] |
| 244 | + let inArrayB: [T] |
| 245 | + let front: Bool |
| 246 | + let delta: Int |
| 247 | + let vLength: Int |
| 248 | +} |
244 | 249 |
|
245 | | - let maxD = (arrayALength + arrayBLength + 1) / 2 |
246 | | - let vOffset = maxD |
247 | | - var vLength = 2 * maxD |
| 250 | +private func walkFrontPath<T: Equatable>( |
| 251 | + commonParameters: CommonPathParameters<T>, |
| 252 | + currentD: Int, |
| 253 | + kay1: Kay, |
| 254 | + vectors1: inout [Int], |
| 255 | + vectors2: inout [Int]) -> [Diff<T>] { |
248 | 256 |
|
249 | | - if vLength <= vOffset + 2 { |
250 | | - vLength = vOffset + 2 |
251 | | - } |
252 | | - |
253 | | - var v1: [Int] = [] |
254 | | - var v2: [Int] = [] |
| 257 | + while kay1.index <= currentD - kay1.end { |
| 258 | + defer { |
| 259 | + kay1.index += 2 |
| 260 | + } |
| 261 | + let k1Offset = commonParameters.vOffset + kay1.index |
| 262 | + var x1 = 0 |
255 | 263 |
|
256 | | - for _ in 0..<vLength { |
257 | | - v1.append(-1) |
258 | | - v2.append(-1) |
259 | | - } |
| 264 | + if kay1.index == -currentD || (kay1.index != currentD && vectors1[k1Offset - 1] < vectors1[k1Offset + 1]) { |
| 265 | + x1 = vectors1[k1Offset + 1] |
| 266 | + } else { |
| 267 | + x1 = vectors1[k1Offset - 1] + 1 |
| 268 | + } |
260 | 269 |
|
261 | | - v1[vOffset + 1] = 0 |
262 | | - v2[vOffset + 1] = 0 |
| 270 | + var y1 = x1 - kay1.index |
263 | 271 |
|
264 | | - let delta = arrayALength - arrayBLength |
| 272 | + // follow the snake! |
| 273 | + while x1 < commonParameters.inArrayA.count && y1 < commonParameters.inArrayB.count && commonParameters.inArrayA[x1] == commonParameters.inArrayB[y1] { |
| 274 | + x1 += 1 |
| 275 | + y1 += 1 |
| 276 | + } |
265 | 277 |
|
266 | | - // If the total number of characters is odd, then the front path will collide with the reverse path. |
267 | | - let front = delta % 2 != 0 |
268 | | - // BOOL front = (delta % 2 != 0); |
| 278 | + vectors1[k1Offset] = x1 |
| 279 | + |
| 280 | + if x1 > commonParameters.inArrayA.count { |
| 281 | + // Ran off the right of the graph. |
| 282 | + kay1.end += 2 |
| 283 | + } else if y1 > commonParameters.inArrayB.count { |
| 284 | + // Ran off the bottom of the graph. |
| 285 | + kay1.start += 2 |
| 286 | + } else if commonParameters.front { |
| 287 | + let k2Offset = commonParameters.vOffset + commonParameters.delta - kay1.index |
| 288 | + |
| 289 | + if k2Offset >= 0 && k2Offset < commonParameters.vLength && vectors2[k2Offset] != -1 { |
| 290 | + // Mirror x2 onto top-left coordinate system. |
| 291 | + let x2 = commonParameters.inArrayA.count - vectors2[k2Offset] |
| 292 | + if x1 >= x2 { |
| 293 | + // diffs = diff_bisectSplitOfStrings(text1, text2, x1, y1, properties); |
| 294 | + return diff_bisectSplitOfArrays(arrayA: commonParameters.inArrayA, arrayB: commonParameters.inArrayB, x: x1, y: y1) |
| 295 | + } |
| 296 | + } |
| 297 | + } |
| 298 | + } |
| 299 | + return [] |
| 300 | +} |
269 | 301 |
|
270 | | - // Offsets for start and end of k loop. Prevents mapping of space beyond the grid. |
271 | | - var k1start = 0 |
272 | | - var k1end = 0 |
273 | | - var k2start = 0 |
274 | | - var k2end = 0 |
| 302 | +private func walkReversePath<T: Equatable>( |
| 303 | + commonParameters: CommonPathParameters<T>, |
| 304 | + currentD: Int, |
| 305 | + kay2: Kay, |
| 306 | + vectors1: inout [Int], |
| 307 | + vectors2: inout [Int]) -> [Diff<T>] { |
275 | 308 |
|
276 | | - // |
277 | | - for currentD in 0..<maxD { |
| 309 | + while kay2.index <= currentD - kay2.end { |
278 | 310 |
|
279 | | - // Walk the front path one step. |
280 | | - var k1 = -currentD + k1start |
281 | | - while k1 <= currentD - k1end { |
282 | | - defer { |
283 | | - k1 += 2 |
284 | | - } |
285 | | - let k1Offset = vOffset + k1 |
286 | | - var x1 = 0 |
| 311 | + defer { |
| 312 | + kay2.index += 2 |
| 313 | + } |
| 314 | + let k2Offset = commonParameters.vOffset + kay2.index |
| 315 | + var x2 = 0 |
287 | 316 |
|
288 | | - if k1 == -currentD || (k1 != currentD && v1[k1Offset - 1] < v1[k1Offset + 1]) { |
289 | | - x1 = v1[k1Offset + 1] |
290 | | - } else { |
291 | | - x1 = v1[k1Offset - 1] + 1 |
292 | | - } |
| 317 | + if kay2.index == -currentD || (kay2.index != currentD && vectors2[k2Offset - 1] < vectors2[k2Offset + 1]) { |
| 318 | + x2 = vectors2[k2Offset + 1] |
| 319 | + } else { |
| 320 | + x2 = vectors2[k2Offset - 1] + 1 |
| 321 | + } |
293 | 322 |
|
294 | | - var y1 = x1 - k1 |
| 323 | + var y2 = x2 - kay2.index |
295 | 324 |
|
296 | | - // follow the snake! |
297 | | - while x1 < arrayALength && y1 < arrayBLength && inArrayA[x1] == inArrayB[y1] { |
298 | | - x1 += 1 |
299 | | - y1 += 1 |
300 | | - } |
| 325 | + while x2 < commonParameters.inArrayA.count && y2 < commonParameters.inArrayB.count && (commonParameters.inArrayA[commonParameters.inArrayA.count - x2 - 1] == commonParameters.inArrayB[commonParameters.inArrayB.count - y2 - 1]) { |
| 326 | + x2 += 1 |
| 327 | + y2 += 1 |
| 328 | + } |
301 | 329 |
|
302 | | - v1[k1Offset] = x1 |
303 | | - |
304 | | - if x1 > arrayALength { |
305 | | - // Ran off the right of the graph. |
306 | | - k1end += 2 |
307 | | - } else if y1 > arrayBLength { |
308 | | - // Ran off the bottom of the graph. |
309 | | - k1start += 2 |
310 | | - } else if front { |
311 | | - let k2Offset = vOffset + delta - k1 |
312 | | - |
313 | | - if k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] != -1 { |
314 | | - // Mirror x2 onto top-left coordinate system. |
315 | | - let x2 = arrayALength - v2[k2Offset] |
316 | | - if x1 >= x2 { |
317 | | - // diffs = diff_bisectSplitOfStrings(text1, text2, x1, y1, properties); |
318 | | - diffs = diff_bisectSplitOfArrays(arrayA: inArrayA, arrayB: inArrayB, x: x1, y: y1) |
319 | | - haveFoundDiffs = true |
320 | | - break |
321 | | - } |
| 330 | + vectors2[k2Offset] = x2 |
| 331 | + |
| 332 | + if x2 > commonParameters.inArrayA.count { |
| 333 | + // Ran off the left of the graph. |
| 334 | + kay2.end += 2 |
| 335 | + } else if y2 > commonParameters.inArrayB.count { |
| 336 | + // Ran off the top of the graph. |
| 337 | + kay2.start += 2 |
| 338 | + } else if !commonParameters.front { |
| 339 | + let k1Offset = commonParameters.vOffset + commonParameters.delta - kay2.index |
| 340 | + |
| 341 | + if k1Offset >= 0 && k1Offset < commonParameters.vLength && vectors1[k1Offset] != -1 { |
| 342 | + let x1 = vectors1[k1Offset] |
| 343 | + let y1 = commonParameters.vOffset + x1 - k1Offset |
| 344 | + // Mirror x2 onto top-left coordinate system. |
| 345 | + x2 = commonParameters.inArrayA.count - x2 |
| 346 | + |
| 347 | + if x1 >= x2 { |
| 348 | + // Overlap detected. |
| 349 | + return diff_bisectSplitOfArrays(arrayA: commonParameters.inArrayA, arrayB: commonParameters.inArrayB, x: x1, y: y1) |
322 | 350 | } |
323 | 351 | } |
324 | 352 | } |
| 353 | + } |
| 354 | + return [] |
| 355 | +} |
325 | 356 |
|
326 | | - if haveFoundDiffs { |
327 | | - break |
328 | | - } |
| 357 | +// yes this method is way too long. Pull requests welcome! |
| 358 | +func diff_bisectOfArrays<T: Equatable>(arrayA inArrayA: [T], arrayB inArrayB: [T]) -> [Diff<T>] { |
| 359 | + let maxD = (inArrayA.count + inArrayB.count + 1) / 2 |
| 360 | + let vOffset = maxD |
| 361 | + var vLength = 2 * maxD |
329 | 362 |
|
330 | | - // Walk the reverse path one step. |
331 | | - var k2 = -currentD + k2start |
332 | | - while k2 <= currentD - k2end { |
| 363 | + if vLength <= vOffset + 2 { |
| 364 | + vLength = vOffset + 2 |
| 365 | + } |
333 | 366 |
|
334 | | - defer { |
335 | | - k2 += 2 |
336 | | - } |
337 | | - let k2Offset = vOffset + k2 |
338 | | - var x2 = 0 |
| 367 | + var vectors1: [Int] = [] |
| 368 | + var vectors2: [Int] = [] |
339 | 369 |
|
340 | | - if k2 == -currentD || (k2 != currentD && v2[k2Offset - 1] < v2[k2Offset + 1]) { |
341 | | - x2 = v2[k2Offset + 1] |
342 | | - } else { |
343 | | - x2 = v2[k2Offset - 1] + 1 |
344 | | - } |
| 370 | + for _ in 0..<vLength { |
| 371 | + vectors1.append(-1) |
| 372 | + vectors2.append(-1) |
| 373 | + } |
| 374 | + vectors1[vOffset + 1] = 0 |
| 375 | + vectors2[vOffset + 1] = 0 |
345 | 376 |
|
346 | | - var y2 = x2 - k2 |
| 377 | + let delta = inArrayA.count - inArrayB.count |
347 | 378 |
|
348 | | - while x2 < arrayALength && y2 < arrayBLength && (inArrayA[arrayALength - x2 - 1] == inArrayB[arrayBLength - y2 - 1]) { |
349 | | - x2 += 1 |
350 | | - y2 += 1 |
351 | | - } |
| 379 | + // If the total number of characters is odd, then the front path will collide with the reverse path. |
| 380 | + let front = delta % 2 != 0 |
352 | 381 |
|
353 | | - v2[k2Offset] = x2 |
354 | | - |
355 | | - if x2 > arrayALength { |
356 | | - // Ran off the left of the graph. |
357 | | - k2end += 2 |
358 | | - } else if y2 > arrayBLength { |
359 | | - // Ran off the top of the graph. |
360 | | - k2start += 2 |
361 | | - } else if !front { |
362 | | - let k1Offset = vOffset + delta - k2 |
363 | | - |
364 | | - if k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] != -1 { |
365 | | - let x1 = v1[k1Offset] |
366 | | - let y1 = vOffset + x1 - k1Offset |
367 | | - // Mirror x2 onto top-left coordinate system. |
368 | | - x2 = arrayALength - x2 |
369 | | - |
370 | | - if x1 >= x2 { |
371 | | - // Overlap detected. |
372 | | - diffs = diff_bisectSplitOfArrays(arrayA: inArrayA, arrayB: inArrayB, x: x1, y: y1) |
373 | | - haveFoundDiffs = true |
374 | | - break |
375 | | - } |
376 | | - } |
377 | | - } |
378 | | - } |
| 382 | + // Offsets for start and end of k loop. Prevents mapping of space beyond the grid. |
| 383 | + let kay1 = Kay() |
| 384 | + let kay2 = Kay() |
379 | 385 |
|
380 | | - if haveFoundDiffs { |
381 | | - break |
| 386 | + let commonParameters = CommonPathParameters(vOffset: vOffset, inArrayA: inArrayA, inArrayB: inArrayB, front: front, delta: delta, vLength: vLength) |
| 387 | + |
| 388 | + for currentD in 0..<maxD { |
| 389 | + |
| 390 | + // Walk the front path one step. |
| 391 | + kay1.index = -currentD + kay1.start |
| 392 | + let frontDiffs = walkFrontPath( |
| 393 | + commonParameters: commonParameters, |
| 394 | + currentD: currentD, |
| 395 | + kay1: kay1, |
| 396 | + vectors1: &vectors1, |
| 397 | + vectors2: &vectors2) |
| 398 | + |
| 399 | + if !frontDiffs.isEmpty { |
| 400 | + return frontDiffs |
382 | 401 | } |
383 | | - } |
384 | 402 |
|
385 | | - if !haveFoundDiffs { |
386 | | - // we haven't found a snake at all so we couldn't cut the problem in half. |
387 | | - // This means we have no common element. Just add the diffs straight away. |
388 | | - diffs = [Diff(operation: .delete, array: inArrayA), Diff(operation: .insert, array: inArrayB)] |
| 403 | + // Walk the reverse path one step. |
| 404 | + kay2.index = -currentD + kay2.start |
| 405 | + let reverseDiffs = walkReversePath( |
| 406 | + commonParameters: commonParameters, |
| 407 | + currentD: currentD, |
| 408 | + kay2: kay2, |
| 409 | + vectors1: &vectors1, |
| 410 | + vectors2: &vectors2) |
| 411 | + |
| 412 | + if !reverseDiffs.isEmpty { |
| 413 | + return reverseDiffs |
| 414 | + } |
389 | 415 | } |
390 | 416 |
|
391 | | - return diffs |
| 417 | + // we haven't found a snake at all so we couldn't cut the problem in half. |
| 418 | + // This means we have no common element. Just add the diffs straight away. |
| 419 | + return [Diff(operation: .delete, array: inArrayA), Diff(operation: .insert, array: inArrayB)] |
392 | 420 | } |
393 | 421 | // swiftlint:enable function_body_length |
394 | 422 |
|
|
0 commit comments