Newer
Older
dotfiles / .config / lite-xl / libraries / coro_diff / myers_midpoint.lua
local myers_midpoint = {}

function myers_midpoint.get_midpoint_resumable(a, b, x1, y1, x2, y2)
  local a_len, b_len = #a, #b
  local width = x2 - x1
  local height = y2 - y1
  if width + height == 0 then return end
  local delta = width - height
  local max = math.ceil((width + height) / 2)
  local vf = { x1 }
  local vb = { y2 }

  return function(iterations)
    local function forwards(d, k_init, k_fin)
      for k=k_init,k_fin,-2 do
        local c = k - delta
        local x, y, px, py
        if k == -d or (k ~= d and vf[k - 1] < vf[k + 1]) then
          px = vf[k + 1]
          x = px
        else
          px = vf[k - 1]
          x = px + 1
        end

        y = y1 + (x - x1) - k
        py = (d == 0 or x ~= px) and y or y - 1

        while x < x2 and y < y2 and a[x + 1] == b[y + 1] do
          x, y = x + 1, y + 1
        end

        vf[k] = x
        if delta % 2 ~= 0 and (c >= -(d - 1) and c <= d - 1) and y >= vb[c] then
          return px, py, x, y
        end
      end
    end

    local function backwards(d, c_init, c_fin)
      for c=c_init,c_fin,-2 do
        local k = c + delta
        local x, y, px, py
        if c == -d or (c ~= d and vb[c - 1] > vb[c + 1]) then
          py = vb[c + 1]
          y = py
        else
          py = vb[c - 1]
          y = py - 1
        end

        x = x1 + (y - y1) + k
        px = (d == 0 or y ~= py) and x or x + 1

        while x > x1 and y > y1 and a[x - 1 + 1] == b[y - 1 + 1] do
          x, y = x - 1, y - 1
        end

        vb[c] = y
        if delta % 2 == 0 and (k >= -d and k <= d) and x <= vf[k] then
          return x, y, px, py
        end
      end
    end

    local iterations_count = 0
    local m_x1, m_y1, m_x2, m_y2
    for d=0,max do
      -- Optimization from https://blog.robertelder.org/diff-algorithm/ (myers_diff_length_half_memory)
      local k_init = d - (2 * math.max(0, d - a_len))
      local k_fin = -(d - 2 * math.max(0, d - b_len))
      if k_init >= k_fin then
        m_x1, m_y1, m_x2, m_y2 = forwards(d, k_init, k_fin)
        iterations_count = iterations_count + (k_init - k_fin) // 2 + 1
        if m_x1 then break end
        if iterations_count >= iterations then
          iterations = coroutine.yield(false, iterations_count) or iterations
          iterations_count = 0
        end
      end

      local c_init = d - (2 * math.max(0, d - b_len))
      local c_fin = -(d - 2 * math.max(0, d - a_len))
      if c_init >= c_fin then
        m_x1, m_y1, m_x2, m_y2 = backwards(d, c_init, c_fin)
        iterations_count = iterations_count + (k_init - k_fin) // 2 + 1
        if m_x1 then break end
        if iterations_count >= iterations then
          iterations = coroutine.yield(false, iterations_count) or iterations
          iterations_count = 0
        end
      end
    end

    return true, iterations_count, m_x1, m_y1, m_x2, m_y2
  end
end

function myers_midpoint.get_string_cache(a)
  return a
end

return myers_midpoint