Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   https://anggtwu.net/LUA/ZHA4.lua.html
--   https://anggtwu.net/LUA/ZHA4.lua
--           (find-angg "LUA/ZHA4.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
--
-- (defun e  () (interactive) (find-angg "LUA/ZHA4.lua"))
-- (defun o  () (interactive) (find-angg "LUA/ZHA1.lua"))
-- (defun oo () (interactive) (find-dn6 "zhas.lua"))

-- This is a rewritten version of
--   (find-angg "LUA/ZHA1.lua")
--   (find-dn6 "zhas.lua")
--   
-- that uses Pict4.lua to generate LaTeX code. See:
--   (find-angg "LUA/Pict4.lua")
--   (find-angg "LUA/PictRect1.lua")


-- From:
-- (find-dn6 "picture.lua")
-- «.V»				(to "V")
-- «.V-tests»			(to "V-tests")
-- «.BoundingBox»		(to "BoundingBox")
-- «.BoundingBox-tests»		(to "BoundingBox-tests")
-- «.AsciiPicture»		(to "AsciiPicture")
-- «.AsciiPicture-tests»	(to "AsciiPicture-tests")
-- «.metaopts»			(to "metaopts")
-- «.copyopts»			(to "copyopts")
-- «.copyopts-tests»		(to "copyopts-tests")
-- «.makepicture»		(to "makepicture")
-- «.makepicture-tests»		(to "makepicture-tests")
-- «.texarrow»			(to "texarrow")
-- «.LPicture»			(to "LPicture")
-- «.LPicture-tests»		(to "LPicture-tests")
--
-- «.TArrow»			(to "TArrow")
-- «.TArrow-tests»		(to "TArrow-tests")
-- «.Squig»			(to "Squig")
-- «.Squig-tests»		(to "Squig-tests")
--
-- «.ZHA»			(to "ZHA")
-- «.ZHA-connectives»		(to "ZHA-connectives")
-- «.ZHA-getcuts»		(to "ZHA-getcuts")
-- «.ZHA-walls»			(to "ZHA-walls")
-- «.ZHA-shrinktop»		(to "ZHA-shrinktop")
-- «.ZHA-tests»			(to "ZHA-tests")
-- «.ZHA-tests-walls»		(to "ZHA-tests-walls")
-- «.ZHA-test-connectives»	(to "ZHA-test-connectives")
-- «.ZHA-test-generators»	(to "ZHA-test-generators")
-- «.ZHA-test-shrinktop»	(to "ZHA-test-shrinktop")
-- «.shortoperators»		(to "shortoperators")
-- «.shortoperators-tests»	(to "shortoperators-tests")
--
-- «.LR»			(to "LR")
-- «.LR-tests»			(to "LR-tests")
-- «.LR-putxy-tests»		(to "LR-putxy-tests")
-- «.LR-twocolgraph-tests»	(to "LR-twocolgraph-tests")
-- «.LR-fromtcgspec-tests»	(to "LR-fromtcgspec-tests")
-- «.LR-shrinktop-tests»	(to "LR-shrinktop-tests")
--
-- «.Cuts»			(to "Cuts")
-- «.Cuts-tests»		(to "Cuts-tests")
--
-- Untested:
-- «.MixedPicture»		(to "MixedPicture")
-- «.mpnew»			(to "mpnew")
-- «.mpnewJ»			(to "mpnewJ")
-- «.MixedPicture-cuts»		(to "MixedPicture-cuts")
-- «.MixedPicture-zha»		(to "MixedPicture-zha")
-- «.MixedPicture-tests»	(to "MixedPicture-tests")
-- «.MixedPicture-zhalrf-tests»	(to "MixedPicture-zhalrf-tests")
-- «.MixedPicture-arch-tests»	(to "MixedPicture-arch-tests")
-- «.MixedPicture-zset-tests»	(to "MixedPicture-zset-tests")
-- «.MixedPicture-J-tests»	(to "MixedPicture-J-tests")
-- «.drawtwocolgraph»		(to "drawtwocolgraph")
-- «.drawtwocolgraph-tests»	(to "drawtwocolgraph-tests")
-- «.VCuts»			(to "VCuts")
-- «.VCuts-tests»		(to "VCuts-tests")
-- «.TCG»			(to "TCG")
-- «.TGC-tests»			(to "TGC-tests")
-- «.asciirectpoints»		(to "asciirectpoints")
-- «.asciirectpoints-tests»	(to "asciirectpoints-tests")



require "Pict4"    -- (find-angg "LUA/Pict4.lua")

checkrange = function(a, b, c, err)
    if a <= b and b <= c then return true end
    if err then error(format("%s", err, a, b, c)) end
  end
isint  = function (x) return math.floor(x) == x end
ishalf = function (x) return isint(x + 0.5) end

pad = function (spaces, str)
    return ((str or "")..spaces):sub(1, #spaces)
  end




--  ____  ____                   _                 
-- |___ \|  _ \  __   _____  ___| |_ ___  _ __ ___ 
--   __) | | | | \ \ / / _ \/ __| __/ _ \| '__/ __|
--  / __/| |_| |  \ V /  __/ (__| || (_) | |  \__ \
-- |_____|____/    \_/ \___|\___|\__\___/|_|  |___/
--                                                 

-- This class supports the usual operations on 2D vectors and also
-- some of the logical operations on elements of a ZHA - even the
-- implication, IIRC...
-- Adapted from:
--   (find-dn6 "picture.lua" "V")
--
-- «V» (to ".V")
V = Class {
  type    = "V",
  __tostring = function (v) return pformat("(%s,%s)", v[1], v[2]) end,
  __add      = function (v, w) return V{v[1]+w[1], v[2]+w[2]} end,
  __sub      = function (v, w) return V{v[1]-w[1], v[2]-w[2]} end,
  __unm      = function (v) return v*-1 end,
  __mul      = function (v, w)
      local ktimesv   = function (k, v) return V{k*v[1], k*v[2]} end
      local innerprod = function (v, w) return v[1]*w[1] + v[2]*w[2] end
      if     type(v) == "number" and type(w) == "table" then return ktimesv(v, w)
      elseif type(v) == "table" and type(w) == "number" then return ktimesv(w, v)
      elseif type(v) == "table" and type(w) == "table"  then return innerprod(v, w)
      else error("Can't multiply "..tostring(v).."*"..tostring(w))
      end
    end,
  --
  fromxy    = function (x, y) return V{x, y} end,
  fromlr    = function (l, r) return V{l, r}:lrtoxy() end,
  fromlrdir = function (l, r, ensw) return V.fromlr(l, r):walk(ensw) end,
  fromab    = function (a, b)
      local ton = tonumber
      if     type(a) == "table"  then return a
      elseif type(a) == "number" then return V.fromxy(a, b)
      elseif type(a) == "string" then
        local x, y = a:match("^%((.-),(.-)%)$")
        if    x then return V.fromxy(ton(x), ton(y)) end
        local l, r = a:match("^(%d)(%d)$")
        if    l then return V.fromlr(ton(l), ton(r)) end
        local l, r, ensw = a:match("^(%d)(%d)([ensw])$")
        if    l then return V.fromlrdir(ton(l), ton(r), ensw) end
        error("V.fromab() received a bad string: "..a)
      end
    end,
  __index = {
    xytolr = function (v)
        local x, y = v[1], v[2]
        local l = toint((y - x) / 2)
        local r = toint((y + x) / 2)
        return V{l, r}
      end,
    lrtoxy = function (v)
        local l, r = v[1], v[2]
        local x = r - l
        local y = r + l
        return V{x, y}
      end,
    --
    todd   = function (v) return v[1]..v[2] end,
    to12   = function (v) return v[1], v[2] end,
    to_x_y = function (v) return v:to12() end,
    to_l_r = function (v) return v:xytolr():to12() end,
    xy     = function (v) return "("..v[1]..","..v[2]..")" end,
    lr     = function (v) local l, r = v:to_l_r(); return l..r end,
    --
    naiveprod = function (v, w)
        return V{v[1]*w[1], v[2]*w[2]}
      end,
    naivemin = function (v, w)
        return V{min(v[1], w[1]), min(v[2], w[2])}
      end,
    naivemax = function (v, w)
        return V{max(v[1], w[1]), max(v[2], w[2])}
      end,
    --
    -- For V.fromlrdir() and from zha.ne / zha.nw
    s  = function (v) return v+V{ 0, -1} end,
    n  = function (v) return v+V{ 0,  1} end,
    w  = function (v) return v+V{-1,  0} end,
    e  = function (v) return v+V{ 1,  0} end,
    se = function (v) return v+V{ 1, -1} end,
    sw = function (v) return v+V{-1, -1} end,
    ne = function (v) return v+V{ 1,  1} end,
    nw = function (v) return v+V{-1,  1} end,
    walk = function (v, ensw) return v[ensw](v) end,
    --
    -- For: (ph1p 11 "prop-calc-ZHA")
    --      (ph1a    "prop-calc-ZHA")
    And = function (P, Q)
        local Pl, Pr = P:to_l_r()
        local Ql, Qr = Q:to_l_r()
        return V{min(Pl, Ql), min(Pr, Qr)}:lrtoxy()
      end,
    Or = function (P, Q)
        local Pl, Pr = P:to_l_r()
        local Ql, Qr = Q:to_l_r()
        return V{max(Pl, Ql), max(Pr, Qr)}:lrtoxy()
      end,
    above = function (P, Q)
        local Pl, Pr = P:to_l_r()
        local Ql, Qr = Q:to_l_r()
        return Pl >= Ql and Pr >= Qr
      end,
    below = function (P, Q)
        local Pl, Pr = P:to_l_r()
        local Ql, Qr = Q:to_l_r()
        return Pl <= Ql and Pr <= Qr
      end,
    leftof = function (P, Q)
        local Pl, Pr = P:to_l_r()
        local Ql, Qr = Q:to_l_r()
        return Pl >= Ql and Pr <= Qr
      end,
    rightof = function (P, Q)
        local Pl, Pr = P:to_l_r()
        local Ql, Qr = Q:to_l_r()
        return Pl <= Ql and Pr >= Qr
      end,
  },
}

v = V.fromab
lr = function (l, r) return V.fromlr(l, r) end


-- «V-tests» (to ".V-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
= V{3,4}             --> (3,4)    
= V{3,4} - V{2,1}    --> (1,3)    
= V{3,4} + V{2,1}    --> (5,5)    
= V{3,4} * V{2,1}    --> 10	     
= V{3,4} * -10	     --> (-30,-40)
= -10 * V{3,4}	     --> (-30,-40)
= V{-3,3}:xytolr()   --> (3,0)    
= V{3,3}:xytolr()    --> (0,3)    
= V{2,4}:xytolr()    --> (1,3)    

= v(-3,3)            --> (-3,3)
= v(-3,3):to12()     --> -3  3
= v(-3,3):lr()       --> 30
= v"30"              --> (-3,3)
= v"30":lr()         --> 30
= v"20"              --> (-2,2)
= v"02"              --> (2,2)
= v"22"              --> (0,4)

--]]






--  ____                        _ _             ____            
-- | __ )  ___  _   _ _ __   __| (_)_ __   __ _| __ )  _____  __
-- |  _ \ / _ \| | | | '_ \ / _` | | '_ \ / _` |  _ \ / _ \ \/ /
-- | |_) | (_) | |_| | | | | (_| | | | | | (_| | |_) | (_) >  < 
-- |____/ \___/ \__,_|_| |_|\__,_|_|_| |_|\__, |____/ \___/_/\_\
--                                        |___/                 
-- A BoundingBox object contains:
--   a field "x0x0" with a V object (the lower left corner),
--   a field "x1x1" with a V object (the upper right corner).
--
-- «BoundingBox» (to ".BoundingBox")

BoundingBox = Class {
  type    = "BoundingBox",
  new     = function () return BoundingBox {} end,
  __tostring = function (bb) return bb:tostring() end,
  __index = {
    nonempty = function (bb) return     bb.x0y0 end,
    empty    = function (bb) return not bb.x0y0 end,
    tostring = function (bb, h)
        if bb:empty() then return "BoundingBox: empty" end
        if h then
          return pformat("BoundingBox: (x0,y0)=%s (x1,y1)=%s", bb.x0y0, bb.x1y1)
        end
        return pformat("BoundingBox: \n (x1,y1)=%s\n (x0,y0)=%s", bb.x1y1, bb.x0y0)
      end,
    --
    addpoint = function (bb, v)
        if bb.x0y0 then bb.x0y0 = bb.x0y0:naivemin(v) else bb.x0y0 = v end
        if bb.x1y1 then bb.x1y1 = bb.x1y1:naivemax(v) else bb.x1y1 = v end
        return bb
      end,
    addbox = function (bb, v, delta0, delta1)
        bb:addpoint(v+delta0)
        return bb:addpoint(v+(delta1 or -delta0))
      end,
    x0y0x1y1 = function (bb)
        local x0, y0 = bb.x0y0:to_x_y()
        local x1, y1 = bb.x1y1:to_x_y()
        return x0, y0, x1, y1
      end,
    x0x1y0y1 = function (bb)
        local x0, y0 = bb.x0y0:to_x_y()
        local x1, y1 = bb.x1y1:to_x_y()
        return x0, x1, y0, y1
      end,
    --
    merge = function (bb1, bb2)
        if bb2.x0y0 then bb1:addpoint(bb2.x0y0) end
        if bb2.x1y1 then bb1:addpoint(bb2.x1y1) end
        return bb1
      end,
  },
}

-- «BoundingBox-tests» (to ".BoundingBox-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
bb = BoundingBox.new()
PP(bb)
= bb
= bb:addpoint(v(2, 4))
PP(bb)
= bb:addbox(v(6, 7), v(.5, .5))
= bb:addbox(v(1, 2), v(.5, .5))
= bb:x0x1y0y1()
= bb
= bb:tostring()
= bb:tostring("h")
PP(bb)

--]]


--     _             _ _ ____  _      _                  
--    / \   ___  ___(_|_)  _ \(_) ___| |_ _   _ _ __ ___ 
--   / _ \ / __|/ __| | | |_) | |/ __| __| | | | '__/ _ \
--  / ___ \\__ \ (__| | |  __/| | (__| |_| |_| | | |  __/
-- /_/   \_\___/\___|_|_|_|   |_|\___|\__|\__,_|_|  \___|
--                                                       
-- «AsciiPicture»  (to ".AsciiPicture")

AsciiPicture = Class {
  type = "AsciiPicture",
  new  = function (padstr)
      return AsciiPicture {
        padstr   = padstr or "  ",
        intbbox  = BoundingBox.new(),
        halfbbox = BoundingBox.new(),
        grid     = VTable {},
      }
    end,
  __tostring = function (ap) return ap:tostring() end,
  __index = {
    get  = function (ap, x, y) return ap.grid[y] and ap.grid[y][x] end,
    pget = function (ap, x, y)
        local contents = ap:get(x, y)
        local padstr   = isint(x) and ap.padstr or " "
        return pad(padstr, contents or "")
      end,
    put  = function (ap, x, y, str)
        if     isint(x)  and isint(y)  then ap.intbbox :addpoint(v(x, y))
        elseif ishalf(x) and ishalf(y) then ap.halfbbox:addpoint(v(x, y))
        else error(pformat("Not intint and not halfhalf %s %s", x, y))
        end
        ap.grid[y]    = ap.grid[y] or HTable {}
        ap.grid[y][x] = str
        return ap
      end,
    putv  = function (ap, v, str) return ap:put(v[1], v[2], str) end,
    putlr = function (ap, v, str) return ap:put(v[1], v[2], str or v:lr()) end,
    --
    linestr = function (ap, xs, y)
        local f = function (x) return ap:pget(x, y) end
        return mapconcat(f, xs, "")
      end,
    gridstr = function (ap, xs, ys)
        local g = function (y) return ap:linestr(xs, y) end
        return mapconcat(g, ys, "\n")
      end,
    ix0 = function (ap) return ap.intbbox.x0y0[1] end,
    iy0 = function (ap) return ap.intbbox.x0y0[2] end,
    ix1 = function (ap) return ap.intbbox.x1y1[1] end,
    iy1 = function (ap) return ap.intbbox.x1y1[2] end,
    hx0 = function (ap) return ap.halfbbox.x0y0[1] end,
    hy0 = function (ap) return ap.halfbbox.x0y0[2] end,
    hx1 = function (ap) return ap.halfbbox.x1y1[1] end,
    hy1 = function (ap) return ap.halfbbox.x1y1[2] end,
    bx0 = function (ap) return min(ap:ix0(), ap:hx0()) end,
    by0 = function (ap) return min(ap:iy0(), ap:hy0()) end,
    bx1 = function (ap) return max(ap:ix1(), ap:hx1()) end,
    by1 = function (ap) return max(ap:iy1(), ap:hy1()) end,
    ixs = function (ap) return seq(ap:ix0(), ap:ix1(),  1) end,
    iys = function (ap) return seq(ap:iy1(), ap:iy0(), -1) end,
    hxs = function (ap) return seq(ap:hx0(), ap:hx1(),  1) end,
    hys = function (ap) return seq(ap:hy1(), ap:hy0(), -1) end,
    bxs = function (ap) return seq(ap:bx0(), ap:bx1(),  0.5) end,
    bys = function (ap) return seq(ap:by1(), ap:by0(), -0.5) end,
    igridstr = function (ap) return ap:gridstr(ap:ixs(), ap:iys()) end,
    hgridstr = function (ap) return ap:gridstr(ap:hxs(), ap:hys()) end,
    bgridstr = function (ap) return ap:gridstr(ap:bxs(), ap:bys()) end,
    --
    hasintpoints  = function (ap) return ap.intbbox:nonempty()  end,
    hashalfpoints = function (ap) return ap.halfbbox:nonempty() end,
    tostring = function (ap)
      if ap:hasintpoints() then
        if ap:hashalfpoints() then
          return ap:bgridstr()
        else
          return ap:igridstr()
        end
      else
        if ap:hashalfpoints() then
          return ap:hgridstr()
        else
          return "(empty)"
        end
      end
    end,
    --
    tostring0 = function (ap)
        return pformat("intbbox:  %s\nhalfbbox: %s\npadstr:   \"%s\"",
          ap.intbbox :tostring("h"),
          ap.halfbbox:tostring("h"),
          ap.padstr)
      end,
    --
    setflags = function (ap, flags) ap.flags = flags; return ap end,
    flag     = function (ap, flag)  return ap.flags:match(flag) end,
    --
    drawline = function (ap, v0, v1)
        local Dx, Dy = v1[1]-v0[1], v1[2]-v0[2]
        if Dx == 0 and Dy == 0 then return end
	local char
        if Dx ==  Dy then char = "/"  end
        if Dx == -Dy then char = "\\" end
        if not char then error("Bad slope") end
        local Sx, Sy = (Dx > 0 and 1 or -1), (Dy > 0 and 1 or -1)
        local vunit = v(Sx, Sy)
        local n = Dx / Sx
        for t=0.5,n-0.5 do ap:putv(v0 + t*vunit, char) end
        return ap
      end,
    drawlines = function (ap, points, poly)
        if type(points) == "string" then points = split(points) end
        for i=1,#points-1 do ap:drawline(v(points[i]), v(points[i+1])) end
        if poly then ap:drawline(v(points[#points]), v(points[1])) end
        return ap
      end,
  },
}

-- «AsciiPicture-tests»  (to ".AsciiPicture-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
ap = AsciiPicture.new()
= ap:tostring0()
= ap
ap:put(0, 0, "foo")
= ap
= ap:pget(0, 0)
= ap:pget(1, 1)
= ap
for l=0,3 do
  for r=0,4 do
    ap:putlr(lr(l, r))
  end
end
= ap
ap:put(0.5, 0.5, "\\")
= ap
= v"10s"
ap:drawline(v"10s", v"14e")
= ap
= ap:hgridstr()
= ap:hgridstr()
= v(v(2,3))
= ap:drawlines("00s 30w 34n 04e", "poly")

--]==]

--  (find-dn6grep "grep --color=auto -nH --null -e AsciiPicture *.lua")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
ap = AsciiPicture.new("  ")
ap = AsciiPicture.new("     ")
ap = AsciiPicture.new("      ")
for l=0,2 do
  for r=0,3 do
    local pos=lr(l, r)
    ap:putv(pos, "..")
    ap:putv(pos, pos:lr())
    ap:putv(pos, pos:xy())
  end
end
= ap
PPV(ap)

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
ap = AsciiPicture.new("  ")
PP(ap)
PP(ap.bb)
= ap
= ap.bb
ap = AsciiPicture.new("  "):putv(v(1,1),"  ")   -- broken!
= ap
= ap:putv(v(1,1), "..")
= ap
= ap.bb
PP(ap)

--]]




--                                    _       
--   ___ ___  _ __  _   _  ___  _ __ | |_ ___ 
--  / __/ _ \| '_ \| | | |/ _ \| '_ \| __/ __|
-- | (_| (_) | |_) | |_| | (_) | |_) | |_\__ \
--  \___\___/| .__/ \__, |\___/| .__/ \__|___/
--           |_|    |___/      |_|            
--
-- An _UGLY_ hack to let me specify options for makepicture in a compact way.
-- A call to copyopts(A, B) copies the options in the table A to the table B.
-- If there is a field "meta" in A it is treated in a special way:
--
--   copyopts({foo=2, bar=3, meta="s ()"}, B)
--
-- works as this, but in an unspecified order:
--
--   copyopts({foo=2, bar=3}, B)
--   copyopts(metaopts["s"],  B)
--   copyopts(metaopts["()"], B)
--
-- Used by: (find-dn6 "zhas.lua" "MixedPicture" "LPicture.new(options)")
--          (find-dn6 "picture.lua" "LPicture" "new" "copyopts(opts, lp)")
--
-- «copyopts» (to ".copyopts")
-- From: (find-dn6 "picture.lua" "copyopts")
--          
copyopts = function (A, B)
    if type(A) == "string" then
      for _,name in ipairs(split(A)) do
        local tbl = metaopts[name] or error("No metaopt[\""..A.."\"]")
        copyopts(tbl, B)
      end
      return B
      -- Old:
      -- if A:match(" ") then
      --   for _,str in ipairs(split(A)) do copyopts(str, B) end
      --   return B
      -- else
      --   local mopts = metaopts[A] or error("No metaopt[\""..A.."\"]")
      --   return copyopts(mopts, B)
      -- end
    end
    for key,val in pairs(A) do
      if key == "meta" then
        copyopts(val, B)
      else
        B[key] = val
      end
    end
    return B
  end

-- «metaopts» (to ".metaopts")
--
metaopts = {}
metaopts["b"]   = {bhbox = 1}
metaopts["p"]   = {paren = 1}
metaopts["()"]  = {paren = 1}
metaopts["{}"]  = {curly = 1}
metaopts["s"]    = {cellfont="\\scriptsize",       celllower="2pt"}
metaopts["ss"]   = {cellfont="\\scriptscriptsize", celllower="1.5pt"}  -- ?
metaopts["t"]    = {cellfont="\\tiny",             celllower="1.5pt"}  -- ?
metaopts["t"]    = {cellfont="\\tiny",             celllower="1.25pt"}  -- ?
metaopts["10pt"] = {scale="10pt"} 
metaopts["8pt"]  = {scale="8pt", meta="s"} 
--
-- metaopts that are mainly for TCGs:
metaopts["1pt"]  = {scale="1pt"}

-- «copyopts-tests» (to ".copyopts-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
testcopyopts = function (A) PP(copyopts(A, {})) end
testcopyopts  "8pt"
testcopyopts  "8pt ()"
testcopyopts {foo=2, bar=3}
testcopyopts {foo=2, bar=3, meta="8pt"}
testcopyopts {foo=2, bar=3, meta="8pt ()"}
testcopyopts {foo=2, bar=3}

--]]



--                  _              _      _                  
--  _ __ ___   __ _| | _____ _ __ (_) ___| |_ _   _ _ __ ___ 
-- | '_ ` _ \ / _` | |/ / _ \ '_ \| |/ __| __| | | | '__/ _ \
-- | | | | | | (_| |   <  __/ |_) | | (__| |_| |_| | | |  __/
-- |_| |_| |_|\__,_|_|\_\___| .__/|_|\___|\__|\__,_|_|  \___|
--                          |_|                              
--
-- «makepicture» (to ".makepicture")
-- From: (find-dn6 "picture.lua" "makepicture")
--
makepicture = function (options, bb, body)
    local x0, y0, x1, y1 = bb:x0y0x1y1()
    local a = {}
    a.xdimen = x1 - x0
    a.ydimen = y1 - y0
    -- a.xoffset = (x0 + x1)/2
    -- a.yoffset = (y0 + y1)/2
    a.xoffset = x0
    a.yoffset = y0
    a.setscale = "\\unitlength="..(options.scale or "10pt").."%\n"
    a.setlower = "\\celllower="..(options.celllower or "2.5pt").."%\n"
    a.setfont  = "\\def\\cellfont{"..(options.cellfont or "").."}%\n"
    a.body = body
    -- local fmt = "!setscale!setlower!setfont"..
    --             "\\begin{picture}(!xdimen,!ydimen)(!xoffset,!yoffset)\n"..
    --             "!body"..
    --             "\\end{picture}"
    -- 2017nov28:
    a.xydimen  = tostring(v(a.xdimen,  a.ydimen))
    a.xyoffset = tostring(v(a.xoffset, a.yoffset))
    local fmt = "!setscale!setlower!setfont"..
                "\\begin{picture}!xydimen!xyoffset\n"..
                "!body"..
                "\\end{picture}"
    local latex = (fmt:gsub("!([a-z]+)", a))
    --
    -- 2021mar02:
    if not options.novcenter then latex = "\\vcenter{\\hbox{"..latex.."}}" end
    --
    if options.bhbox then latex = "\\bhbox{$"..latex.."$}" end
    if options.paren then latex = "\\left("..latex.."\\right)" end
    if options.curly then latex = "\\left\\{"..latex.."\\right\\}" end
    if options.def   then latex = "\\def\\"..options.def.."{"..latex.."}" end
    --
    -- 2019apr29:
    -- (find-es "dednat" "defzha-and-deftcg")
    if options.tdef  then latex = "\\deftcg{"..options.tdef.."}{"..latex.."}" end
    if options.zdef  then latex = "\\defzha{"..options.zdef.."}{"..latex.."}" end
    return latex
  end

-- «makepicture-tests»  (to ".makepicture-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
bb = BoundingBox.new()
bb:addpoint(v(2,5))
bb:addpoint(v(4,12))
= bb
opts = {}
body = "  hello\n"
= makepicture({},           bb, body)
= makepicture({def ="foo"}, bb, body)
= makepicture({tdef="foo"}, bb, body)
= makepicture({zdef="foo"}, bb, body)
= makepicture({meta="s"},   bb, body)

--]]




--  _                                        
-- | |_ _____  ____ _ _ __ _ __ _____      __
-- | __/ _ \ \/ / _` | '__| '__/ _ \ \ /\ / /
-- | ||  __/>  < (_| | |  | | | (_) \ V  V / 
--  \__\___/_/\_\__,_|_|  |_|  \___/ \_/\_/  
--                                           
-- «texarrow» (to ".texarrow")
-- Based on: (find-dn6 "picture.lua" "texarrow")
--
texarrow = {
  nw="\\nwarrow",  n="\\uparrow",   ne="\\nearrow",
   w="\\leftarrow",                  e="\\rightarrow",
  sw="\\swarrow",  s="\\downarrow", se="\\searrow",
}
texarrow_inv = {
  nw="\\searrow",   n="\\downarrow", ne="\\swarrow",
   w="\\rightarrow",                  e="\\leftarrow",
  sw="\\nearrow",   s="\\uparrow",   se="\\nwarrow", 
}
texarrow_smart = function (usewhitemoves)
    if usewhitemoves then return texarrow_inv end
    return texarrow
  end





--  _     ____  _      _                  
-- | |   |  _ \(_) ___| |_ _   _ _ __ ___ 
-- | |   | |_) | |/ __| __| | | | '__/ _ \
-- | |___|  __/| | (__| |_| |_| | | |  __/
-- |_____|_|   |_|\___|\__|\__,_|_|  \___|
--                                        
-- «LPicture» (to ".LPicture")
-- Adapted from: (find-dn6 "picture.lua" "LPicture")
--
-- This is a minimalistic, and V-based, reimplementation of the
-- LaTeX side of the "Picture" class from:
--   (find-dn6 "picture.lua" "Picture")
-- This is used by:
--   (find-dn6 "zhas.lua" "MixedPicture")
--   (find-dn6 "zhas.lua" "MixedPicture" "both an ascii representation and a LaTeX")

LPicture = Class {
  type = "LPicture",
  new  = function (opts)
      local lp = {latex="", bb=BoundingBox.new()}    -- start empty
      -- for k,v in pairs(opts or {}) do lp[k] = v end  -- copy options
      copyopts(opts, lp)
      return LPicture(lp)                            -- set metatable
    end,
  __tostring = function (lp) return lp:tolatex() end,
  __index = {
    addpoint = function (lp, v) lp.bb:addpoint(v); return lp end,
    put = function (lp, v, tex)
        local x, y = v:to_x_y()
        lp.latex = lp.latex .. "  \\put("..x..","..y.."){\\cell{"..tex.."}}\n"
        lp:addpoint(v-V{.5,.5})
        lp:addpoint(v+V{.5,.5})
        return lp
      end,
    putarrow = function (lp, v, dx, dy, tex)
        lp:put(v+V{dx,dy}*0.5, tex)
      end,
    --
    addlineorvector = function (lp, src, tgt, cmd) 
        lp:addpoint(src)
        lp:addpoint(tgt)
        local x0, y0 = src:to_x_y()
        local x1, y1 = tgt:to_x_y()
        local tex = (cmd == "vector") and pict2evector(x0,y0, x1,y1)
                                      or  pict2eline  (x0,y0, x1,y1)
        lp.latex = lp.latex.."  "..tex.."\n"
      end,
    --
    tolatex = function (lp)
        return makepicture(lp, lp.bb, lp.latex)
      end,
    --
    -- 2016dec08:
    addtex = function (lp, tex) lp.latex = lp.latex.."  "..tex.."\n"; return lp; end,
    addt   = function (lp, ...) return lp:addtex(formatt(...)) end,
    rawput = function (lp, v, tex) return lp:addt("\\put%s{%s}", v, tex) end,
    print  = function (lp) print(lp); return lp end,
    lprint = function (lp) print(lp:tolatex()); return lp end,
    output = function (lp) output(lp:tolatex()); return lp end,
    --
    -- 2019apr28:
    -- (find-es "dednat" "LPicture.addrect")
    addrect4 = function (lp, x0, y0, x1, y1)
        return lp:addt("\\polygon(%s,%s)(%s,%s)(%s,%s)(%s,%s)",
                       x0,y0, x1,y0, x1,y1, x0,y1)
      end,
    addrect2 = function (lp, x0y0, x1y1)
        local x0,y0 = x0y0:to_x_y()
        local x1,y1 = x1y1:to_x_y()
        return lp:addt("\\polygon(%s,%s)(%s,%s)(%s,%s)(%s,%s)",
                       x0,y0, x1,y0, x1,y1, x0,y1)
      end,
    addrectr = function (lp, cxcy, rxry)
        return lp:addrect2(cxcy-rxry, cxcy+rxry)
      end,
    --
    -- 2019apr28. Calls this:
    --     (find-LATEX "edrxtikz.lua" "Line")
    --     (find-LATEX "edrxtikz.lua" "Line" "pictv =")
    -- or: (find-dn6 "tcgs.lua" "Line")
    --     (find-dn6 "tcgs.lua" "Line" "pictv =")
    addarrow = function (lp, A, B, t0, t1)
        lp:addtex(Line.newAB(A, B, t0, t1):pictv())
      end,
  },
}

-- «LPicture-tests» (to ".LPicture-tests")
-- See: (find-dn6 "picture.lua" "LPicture-tests")
--      (find-angg "LUA/Pict4.lua" "PictBounds")
--      (find-angg "LUA/Pict4.lua" "PictBounds-methods" "pgat =")
--      (find-angg "LUA/Pict4.lua" "PictBounds-methods" "bep       =")
--      (find-angg "LUA/Pict4.lua" "PictBounds" "beginpicture =")
--      (find-dn6 "picture.lua" "BoundingBox")
--      (find-dn6 "picture.lua" "makepicture")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
-- dofile "picture.lua"
dofile "ZHA4.lua"
lp = LPicture.new {cellfont="\\scriptsize"}
for l=0,2 do
  for r=0,3 do
    local pos=lr(l, r)
    lp:put(pos, pos:xy())
  end
end
= lp

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
-- (find-angg "LUA/lua50init.lua" "pformat")
V.__tostring = function (v) return format("(%.3f,%.3f)", v[1], v[2]) end
V.__tostring = function (v) return format("(%s,%s)", myntos(v[1]), myntos(v[2])) end
V.__tostring = function (v) return "("..myntos(v[1])..","..myntos(v[2])..")" end

= v(1/3, 2/3)
= tostring(v(1/3, 2/3))
LPicture.__index.addLine = function (lp, src, tgt)
    lp:addpoint(src)
    lp:addpoint(tgt)
    local Line = "  \\Line"..tostring(src)..tostring(tgt)
    lp.latex = lp.latex..Line.."\n"
  end
LPicture.__index.setthickness = function (lp, src, tgt)
    lp.latex = lp.latex.."  \linethickness{"..thickness.."}\n"
  end
lp = LPicture.new {}
x0, x1 = 0, 10
lp:addLine(v(x0, 1/3), v(x0, 2/3))
= lp

--]]





--  _____  _                           
-- |_   _|/ \   _ __ _ __ _____      __
--   | | / _ \ | '__| '__/ _ \ \ /\ / /
--   | |/ ___ \| |  | | | (_) \ V  V / 
--   |_/_/   \_\_|  |_|  \___/ \_/\_/  
--                                     
-- This is a tarrow, or a tarrows: "46; 32, 15 26"
-- The "tarrow" above corresponds to the 2CG that has top elements 4_
-- and _6, and inter-column arrows 3_->_2, 1_<-_5 and 2_<-_6.
-- It corresponds to this squig: "123RRL43R21".
-- See: (ph1p 28 "2CGs")
--      (ph1a    "2CGs")
--      (ph1p 33 "converting-ZHAs-2CAGs")
--      (ph1a    "converting-ZHAs-2CAGs")

-- «TArrow»  (to ".TArrow")
TArrow = Class {
  type  = "TArrow",
  from0 = function (str)
      local lr,LR,RL = str:match("^(.*);(.*),(.*)$")
      lr = lr:gsub("[^0-9]", "")
      local maxl = tonumber(lr:sub(1,1))
      local maxr = tonumber(lr:sub(2,2))
      local ap = AsciiPicture.new("  ")
      return TArrow { maxl=maxl, maxr=maxr, LR=LR, RL=RL, ap=ap }
    end,
  from  = function (str)
      return TArrow.from0(str):drawrect():erasearrows()
    end,
  --
  __tostring = function (ta) return ta.ap:tostring() end,
  __index = {
    maxy = function (ta) return ta.maxl + ta.maxr end,
    minx = function (ta) return -ta.maxl end,
    maxx = function (ta) return ta.maxr end,
    --
    drawrect = function (ta)
        for l=0,ta.maxl do
          for r=0,ta.maxr do
            ta.ap:putlr(lr(l, r))
          end
        end
        return ta
      end,
    eraserect = function (ta, l0,r0, l1,r1)
        for l=min(l0,l1),max(l0,l1) do
          for r=min(r0,r1),max(r0,r1) do
            ta.ap:putv(lr(l, r), nil)
          end
        end
        return ta
      end,
    eraselrarrow = function (ta, l,r)
        ta:eraserect(ta.maxl,0, l,r-1)
        return ta
      end,
    eraserlarrow = function (ta, l,r)
        ta:eraserect(0,ta.maxr, l-1,r)
        return ta
      end,
    erasearrows = function (ta)
        local lr_to_l_r = function (lr)
            return tonumber(lr:sub(1,1)), tonumber(lr:sub(2,2))
          end
        for _,lr in ipairs(split(ta.LR)) do ta:eraselrarrow(lr_to_l_r(lr)) end
        for _,lr in ipairs(split(ta.RL)) do ta:eraserlarrow(lr_to_l_r(lr)) end
        return ta
      end,
    --
    newtop = function (ta, l, r)
        ta:eraserect(l+1, 0, ta.maxl, ta.maxr)
        ta:eraserect(0, r+1, ta.maxl, ta.maxr)
        ta.maxl = l
        ta.maxr = r
        return ta
      end,
    --
    validxy = function (ta, x, y)
        return ta.ap:get(x, y)
      end,
    calcminx = function (ta, y)
        for x=ta:minx(),ta:maxx() do if ta:validxy(x, y) then return x end end
      end,
    calcmaxx = function (ta, y)
        for x=ta:maxx(),ta:minx(),-1 do if ta:validxy(x, y) then return x end end
      end,
    validxs = function (ta, y)
        return ta:calcminx(y), ta:calcmaxx(y)
      end,
    --
    digit = function (ta, y)
        local a,b = ta:validxs(y)
        local c,d = ta:validxs(y - 1)
        if b-a == d-c - 2 then return (b-a)/2+1 end
        if b-a == d-c + 2 then return (b-a)/2+1 end
        if b-a == d-c and a == c-1 then return "L" end
        if b-a == d-c and a == c+1 then return "R" end
        error("Bad digit")
      end,
    getsquig = function (ta)
        local sqg = "1"
        for y=1,ta:maxy() do sqg = sqg..ta:digit(y) end
        return sqg
      end,
    --
    gettarrows = function (ta)
        local tolr = function (x, y) return " "..v(x,y):lr() end
        local lrs, rls = "", ""
        for y = 2,ta:maxy() do
          local a,b = ta:validxs(y)
          local c,d = ta:validxs(y - 1)
          local e,f = ta:validxs(y - 2)
          if a == c-1 and c == e+1 then lrs = lrs..tolr(a, y) end
          if b == d+1 and d == f-1 then rls = rls..tolr(b, y) end
        end
        return ta.maxl..ta.maxr..";"..lrs..","..rls
      end,
    --
    validlr = function (ta, l, r)
        return ta.ap:get(lr(l, r):to_x_y())
      end,
    calcminl = function (ta, r)
        for l=0,ta.maxl do if ta:validlr(l, r) then return l end end
      end,
    calcminr = function (ta, l)
        for r=0,ta.maxr do if ta:validlr(l, r) then return r end end
      end,
    calcmaxl = function (ta, r)
        for l=ta.maxl,0,-1 do if ta:validlr(l, r) then return l end end
      end,
    calcmaxr = function (ta, l)
        for r=ta.maxr,0,-1 do if ta:validlr(l, r) then return r end end
      end,
    --
    ne = function (ta, l, r) return lr(l, ta:calcmaxr(l)) end,
    nw = function (ta, l, r) return lr(ta:calcmaxl(r), r) end,
    --
    cutunderl_ = function (ta, l)
        local minr, maxr = ta:calcminr(l), ta:calcmaxr(l-1)
        return format("%d%ds-%d%dn", l, minr, l-1, maxr)
      end,
    cutunder_r = function (ta, r)
        local minl, maxl = ta:calcminl(r), ta:calcmaxl(r-1)
        return format("%d%ds-%d%dn", minl, r, maxl, r-1)
      end,
    cutsunder = function (ta, str)
        local cutsstr = ""
        local addcut = function (str) cutsstr = cutsstr.." ".. str end
        for l,r in str:gmatch("([0-9_])([0-9_])") do
          if r == "_" and l+0 <= ta.maxl then addcut(ta:cutunderl_(l+0)) end
          if l == "_" and r+0 <= ta.maxr then addcut(ta:cutunder_r(r+0)) end
        end
        return cutsstr
      end,
    --
    contour = function (ta)
        local minx,maxx = {}, {}
        for y=0,ta:maxy() do minx[y] = ta:calcminx(y) end
        for y=0,ta:maxy() do maxx[y] = ta:calcmaxx(y) end
        local flipl = function (y) return minx[y+1] == minx[y-1] end
        local flipr = function (y) return maxx[y+1] == maxx[y-1] end
        local contourstr = "00s"
        local addc = function (x, y, dir)
            contourstr = contourstr .. "-" .. v(x,y):lr()..dir
          end
        for y=1,ta:maxy()-1    do if flipl(y) then addc(minx[y], y, "w") end end
        contourstr = contourstr .. "-" .. ta.maxl..ta.maxr .. "n"
        for y=ta:maxy()-1,1,-1 do if flipr(y) then addc(maxx[y], y, "e") end end
        contourstr = contourstr .. "-00s"
        return contourstr
      end,
  },
}

-- «TArrow-tests»  (to ".TArrow-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
ta = TArrow.from("4,6; 32, 15 26")
= ta
= ta:getsquig()          --> "123RRL43R21"
= ta:gettarrows()        --> "46; 32, 15 26"
= ta:contour()           --> "00s-20w-22w-42w-46n-26e-25e-15e-14e-04e-00s"
= ta:cutsunder("4_,_2")  --> " 42s-36n 02s-21n"
= ta:newtop(3, 4)
= ta:getsquig()          --> "123RRL21"
= ta:gettarrows()        --> "34; 32,"
= ta:contour()           --> "00s-20w-22w-32w-34n-04e-00s"
= ta:cutsunder("4_,_2")  --> " 02s-21n"

ta = TArrow.from0("4,6; 32, 15 26")
= ta:drawrect().ap
= ta:erasearrows().ap
= ta:getsquig()
= ta:gettarrows()

-- (ph1p 29 "topologies-on-2CGs")
-- (ph1a    "topologies-on-2CGs")
-- (ph1p 32 "topologies-on-2CGs")
-- (ph1a    "topologies-on-2CGs" "disconnected")
ta = TArrow.from("34; 23, 22")
= ta


= ta:ne(1, 3):lr()
= ta:nw(0, 1):lr()

= ta:drawrect().ap
= ta:eraselrarrow(3,2).ap
= ta:eraserlarrow(2,4).ap
= ta:getsquig()
= ta:digit(2)
= ta:digit(1)

= ta:eraserect(3,1, 2,3).ap

--]]




--  ____              _       
-- / ___|  __ _ _   _(_) __ _ 
-- \___ \ / _` | | | | |/ _` |
--  ___) | (_| | |_| | | (_| |
-- |____/ \__, |\__,_|_|\__, |
--           |_|        |___/ 
--
-- «Squig»  (to ".Squig")
Squig = Class {
  type   = "Squig",
  newlrw = function (L, R, W, char)
      local l, r, w = L[#L], R[#R], W[#W]
      if char   == "L" then return l-1, r-1, w   end
      if char   == "R" then return l+1, r+1, w   end
      if char+0 == w+1 then return l-1, r+1, w+1 end
      if char+0 == w-1 then return l+1, r-1, w-1 end
      PP("l r w char =", l, r, w, char)
      error("Bad char!")
    end,
  getLRW = function (squig)
      local L, R, W = {[0]=0}, {[0]=0}, {[0]=1}
      for pos = 2,#squig do
        local char = squig:sub(pos, pos)
        local l,r,w = Squig.newlrw(L, R, W, char)
        table.insert(L, l)
        table.insert(R, r)
        table.insert(W, w)
      end
      return L, R, W
    end,
  from = function (squig)
      local L, R, W = Squig.getLRW(squig)
      local minx = L[0]
      local maxx = R[0]
      for i=1,#L do minx = min(minx, L[i]) end
      for i=1,#L do maxx = max(maxx, R[i]) end
      local maxy = #squig - 1
      local maxl,maxr = v(L[maxy], maxy):to_l_r()
      return Squig {L=L, R=R, W=W, squig=squig,
                    minx=minx, maxx=maxx, maxy=maxy,
                    maxl=maxl, maxr=maxr}
    end,
  __tostring = function (sq) return sq:tostring() end,
  __index = {
    tostring = function (sq) return sq:draw():tostring() end,
    draw = function (sq, ap)
        ap = ap or AsciiPicture.new("  ")
        for y=0,sq.maxy do
          for x=sq.L[y],sq.R[y],2 do ap:putlr(v(x, y)) end
        end
        return ap
      end,
    gettarrows = function (sq)
        local ta = TArrow { maxl=sq.maxl, maxr=sq.maxr, ap=sq:draw() }
        return ta:gettarrows()
      end,
  },
}

-- «Squig-tests»  (to ".Squig-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
PP (Squig.getLRW("12R1"))
PPV(Squig.from  ("12R1"))
=   Squig.from  ("12R1"):draw()

= TArrow.from("46; 32, 15 26")
= TArrow.from("46; 32, 15 26"):getsquig()
= Squig .from("123RRL43R21")
= Squig .from("123RRL43R21")  :gettarrows()

--]]





-- (p2ap 1 "0._front-matter")
-- (p2aa   "0._front-matter")






--  ______   _    _
-- |__  / | | |  / \
--   / /| |_| | / _ \
--  / /_|  _  |/ ___ \
-- /____|_| |_/_/   \_\
--
-- «ZHA» (to ".ZHA")
-- Adapted from: (find-dn6 "zhas.lua" "ZHA")

specwidths = function (spec)
    local copydigit = function (s) return s:sub(1, 1):rep(#s) end
    return (spec:gsub("[1-9][LRV]+", copydigit))
  end
specx0 = function (spec) return -(ZHA.fromspec(spec).minx) end

ZHA = Class {
  type    = "ZHA",
  fromspec = function (spec)
      return ZHA.fromspec0(spec):calcminmaxlr()
    end,
  fromspec0 = function (spec, x0)
      x0 = x0 or 0
      local widths = specwidths(spec)
      local z = ZHA {spec=spec, widths=widths,
                     maxy=#spec-1, minx=x0, maxx=x0,
                     L={[0]=x0}, R={[0]=x0}}
      local getc = function (y) return spec  :sub(y+1, y+1)   end
      local getw = function (y) return widths:sub(y+1, y+1)+0 end
      local w, LR = 1, V{x0, x0}
      local getdeltaLRw = function (y)
          local c = getc(y)
          if c == "L"       then return V{-1, -1} end           -- move left
          if c == "R"       then return V{ 1,  1} end           -- move right
          if c == (w+1).."" then w = w+1; return V{-1,  1} end  -- become wider
          if c == (w-1).."" then w = w-1; return V{ 1, -1} end  -- become thinner
          error("Bad char: "..c.." in spec: "..z.spec)
        end
      for y=1,z.maxy do
        -- PP(y, getc(y), w)
        -- LR = LR + PP(getdeltaLRw(y))
        LR = LR + getdeltaLRw(y)
        local xL, xR = LR[1], LR[2]
        z.L[y], z.R[y] = xL, xR
        z.minx = min(z.minx, xL)
        z.maxx = max(z.maxx, xR)
      end
      return z
    end,
  --
  -- A high-level method that uses the class LR in zhaspecs.lua.
  -- See: (find-dn6 "zhaspecs.lua" "LR")
  --      (find-dn6 "zhaspecs.lua" "LR" "fromtcgspec =")
  --      (find-dn6 "zhaspecs.lua" "LR-fromtcgspec-tests")
  fromtcgspec = function (spec)
      return LR.fromtcgspec(spec):zha()
    end,
  --
  __tostring = function (z) return z:tostring() end,
  --
  __index = {
    -- PP = function (z) PP(z); return z end,
    PP = function (z) print(mytabletostring(z)); return z end,
    print = function (z) print(z); return z end,
    hasxy = function (z, x,y)
        local x, y = v(x, y):to12()
        local L, R = z.L[y], z.R[y]
        if not L then return false end
        if not checkrange(L, x, R) then return false end
        if not isint((x-L)/2) then return false end
        return true
      end,
    xycontents = function (z, x,y)
        local xy = v(x, y)
        if z:hasxy(xy) then return xy:xytolr():todd() end
      end,
    tolines = function (z)
        local lines = {}
        for y=z.maxy,0,-1 do
          local f = function (x) return z:xycontents(x, y) or "  " end
          table.insert(lines, mapconcat(f, seq(z.minx, z.maxx)))
        end
        return lines
      end,
    tostring = function (z) return table.concat(z:tolines(), "\n") end,
    --
    -- Compute minl[], maxl[], minr[], maxr[], lrtop
    -- (find-dn6file "zha.lua" "calclrminmax =")
    leftpoint  = function (z, y) return v(z.L[y], y) end,
    rightpoint = function (z, y) return v(z.R[y], y) end,
    setminmaxlr0 = function (z, v)
        local l, r = v:to_l_r()
        z.minl[r], z.maxl[r] = minmax(z.minl[r], l, z.maxl[r])
        z.minr[l], z.maxr[l] = minmax(z.minr[l], r, z.maxr[l])
      end,
    calcminmaxlr = function (z)
        z.minl, z.minr, z.maxl, z.maxr = {}, {}, {}, {}
        for y=0,z.maxy do
          z:setminmaxlr0(z:leftpoint(y))
          z:setminmaxlr0(z:rightpoint(y))
        end
        z.top = z:leftpoint(z.maxy)
        z.topl, z.topr = z.top:to_l_r()
        return z
      end,
    -- z:Imp(P,Q) uses ne and nw
    sw = function (z, v) local l,r = v:to_l_r(); return lr(l, z.minr[l]) end,
    ne = function (z, v) local l,r = v:to_l_r(); return lr(l, z.maxr[l]) end,
    se = function (z, v) local l,r = v:to_l_r(); return lr(z.minl[r], r) end,
    nw = function (z, v) local l,r = v:to_l_r(); return lr(z.maxl[r], r) end,
    --
    -- Generate all points and all arrows of the ZHA
    points = function (z)
        return cow(function ()
            for l=z.topl,0,-1 do
              for r=z.minr[l],z.maxr[l] do
                coy(lr(l, r))    -- coy returns: lr(l,r)
              end
            end
          end)
      end,
    arrows = function (z, usewhitemoves)
        local tar = texarrow_smart(usewhitemoves)
        return cow(function ()
            for y=z.maxy,1,-1 do
              for x=z.L[y],z.R[y],2 do
		if z:hasxy(x-1, y-1) then coy(v(x, y), -1, -1, tar.sw) end
		if z:hasxy(x+1, y-1) then coy(v(x, y),  1, -1, tar.se) end
                -- coy returns: v(x,y), dx, dy, tex
              end
            end
          end)
      end,
    --
    -- For left generators, genp returns "L"; for right generators, "R"
    genp = function (z, P)
        local x,y = P:to_x_y()
        if z.L[y] and z.L[y-1] then
          local la,ma,ra = z.L[y],   x, z.R[y]    -- above
          local lb,mb,rb = z.L[y-1], x, z.R[y-1]  -- below
          if la == ma and la + 1 == lb then return "L" end  -- left gen
          if ma == ra and rb + 1 == ra then return "R" end  -- right gen
        end
      end,
    pointslrg = function (z)
        return cow(function ()
            for P in z:points() do
              local gen, l, r = z:genp(P), P:to_l_r()
              coy(P, l, r, gen)
            end
          end)
      end,
    --
    -- «ZHA-connectives» (to ".ZHA-connectives")
    -- (to "ZHA-test-connectives")
    -- Logical operations: <=, T, F, &, v, ->, <->, not
    Le  = function (z, P, Q) return P:below(Q) end,
    T   = function (z)       return z.top end,
    F   = function (z)       return V{0, 0} end,
    And = function (z, P, Q) return P:And(Q) end,
    Or  = function (z, P, Q) return P:Or(Q) end,
    Imp = function (z, P, Q)
        if     P:below(Q)   then return z.top
        elseif P:leftof(Q)  then return z:ne(P:And(Q))
        elseif P:rightof(Q) then return z:nw(P:And(Q))
        else                     return Q
        end
      end,
    Bic = function (z, P, Q) return z:And(z:Imp(P, Q), z:Imp(Q, P)) end,
    Not = function (z, P)    return z:Imp(P, z:F()) end,
    --
    -- «ZHA-getcuts» (to ".ZHA-getcuts")
    -- See: (find-angg "LUA/lua50init.lua" "eval-and-L")
    -- For a J-operator J, test the points of the upper-left and
    -- upper-right walls to determine where are the cuts.
    getcuts = function (z, J)
        if type(J) == "string" then J = L(J) end
        local cuts = ""
        local add = function (c) cuts = cuts..c end
        local ltoP = function (l) return lr(l, z.maxr[l]) end
        local rtoP = function (r) return lr(z.maxl[r], r) end
        local isstable = function (P) return J(P):lr() == P:lr() end
        add(z.topl)
        for l=z.topl-1,0,-1 do
          if isstable(ltoP(l)) then add("/") end
          add(l)
        end
        add(" ")
        for r=0,z.topr-1 do
          add(r)
          if isstable(rtoP(r)) then add("|") end
        end
        add(z.topr)
        return cuts
      end,
    --
    -- «ZHA-walls» (to ".ZHA-walls")
    -- (to "ZHA-tests-walls")
    rightwallgenerators = function (z, all)
        local A = {}
        for r=1,z.topr do
          if all or z.minl[r-1] < z.minl[r] then
            table.insert(A, lr(z.minl[r], r))
          end
        end
        return A
      end,
    leftwallgenerators = function (z, all)
        local A = {}
        for l=1,z.topl do
          if all or z.minr[l-1] < z.minr[l] then
            table.insert(A, lr(l, z.minr[l]))
          end
        end
        return A
      end,
    totcgspec = function (z)
        local maxl = z.topl
        local maxr = z.topr
        local tolr = function (v) return v:lr() end
        local leftgens  = mapconcat(tolr, z:leftwallgenerators(),  " ")
        local rightgens = mapconcat(tolr, z:rightwallgenerators(), " ")
        return format("%s, %s; %s, %s", maxl, maxr, leftgens, rightgens)
      end,
    --
    rightwallcorners = function (z)
        local l, r = 0, 0
        local A = {}
        -- local dbg = function (str) PP(str, l, r) end
        local push = function () table.insert(A, lr(l, r)) end
        local nextoutercorner = function () r = z.maxr[l]; push() end
        local nextinnercorner = function ()
            if r < z.topr then l = z.minl[r+1]; push(); return true end
          end
        push()
        nextoutercorner()
        while nextinnercorner() do nextoutercorner() end
        l, r = z.topl, z.topr
        push()
        return A
      end,
    leftwallcorners = function (z)
        local l, r = 0, 0
        local A = {}
        -- local dbg = function (str) PP(str, l, r) end
        local push = function () table.insert(A, lr(l, r)) end
        local nextoutercorner = function () l = z.maxl[r]; push() end
        local nextinnercorner = function ()
            if l < z.topl then r = z.minr[l+1]; push(); return true end
          end
        push()
        nextoutercorner()
        while nextinnercorner() do nextoutercorner() end
        l, r = z.topl, z.topr
        push()
        return A
      end,
    --
    -- «ZHA-shrinktop» (to ".ZHA-shrinktop")
    -- These methods use the class LR in zhaspecs.lua.
    -- See: (find-dn6 "zhaspecs.lua" "LR")
    toLR = function (z)
        return LR.fromtcgspec(z:totcgspec())
      end,
    shrinktop = function (z, P)
        return z:toLR():shrinktop(P):zha()
      end,
  },
}


-- «ZHA-tests» (to ".ZHA-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
ZHA.fromspec("12L1RRR2RL1"):PP():print()
ZHA.fromspec("123LLR432L1"):PP():print()
ZHA.fromspec("123RRL432R1"):PP():print()

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
dofile "ZHASpecs4.lua"
= ZHA.fromspec("123RRL432R1")
= ZHA.fromspec("123RRL432R1"):totcgspec()
= ZHA.fromtcgspec("4, 6; 32, 15 36")

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
z = ZHA.fromspec("123RRL432R1"):PP()
= z

for y=z.maxy-1,0,-1 do
  for x=z.minx,z.maxx do
    printf(z:xycontents(x, y) or "  ")
  end
  print()
end
= z
z:PP()

PPV(z:tolines())
PP(z:tostring())

= z:hasxy(0,0)
= z



-- «ZHA-tests-walls» (to ".ZHA-tests-walls")
-- (to "ZHA-walls")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
-- (find-dn6 "zha.lua" "ZHA")
dofile "ZHA4.lua"
pris = function (o) printf("%s ", tostring(o)) end
z = ZHA.fromspec("12RRL1LLRR")
= z
for y=z.maxy,0,-1 do pris(z:leftpoint(y):lr())  end; print()
for y=z.maxy,0,-1 do pris(z:rightpoint(y):lr()) end; print()
for _,P in ipairs(z:rightwallgenerators("all")) do pris(P:lr()) end; print()
for _,P in ipairs(z:rightwallgenerators())      do pris(P:lr()) end; print()
for _,P in ipairs(z:leftwallgenerators("all"))  do pris(P:lr()) end; print()
for _,P in ipairs(z:leftwallgenerators())       do pris(P:lr()) end; print()
for _,P in ipairs(z:rightwallcorners())         do pris(P:lr()) end; print()
for _,P in ipairs(z:leftwallcorners())          do pris(P:lr()) end; print()

--      45
--    44
--  43
--    33
--      23
--    22  13
--      12  03
--    11  02
--  10  01
--    00

-- «ZHA-test-connectives» (to ".ZHA-test-connectives")
-- (to "ZHA-connectives")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
-- (find-dn6 "zha.lua" "ZHA")
dofile "ZHA4.lua"
z = ZHA.fromspec("12RRL1LLRR"):print()
= z:Imp(v"11", v"02"):lr()   --> 03
= z:Imp(v"03", v"12"):lr()   --> 22
= z:Imp(v"23", v"12"):lr()   --> 12
= z:Imp(v"12", v"23"):lr()   --> 45
= z:ne(v"10"):lr()           --> 13
= z:nw(v"02"):lr()           --> 22
= v"12":And(v"03"):lr()      --> 02

= v"11":And(v"02"):lr()
= z:ne(v"11":And(v"02")):lr()  --> 03
= v("11"):leftof(v"02")        
= v("11"):above(v"02")
= v("11"):below(v"02")
= v("23"):below(v"12")
= v("23"):above(v"12")

--      45
--    44
--  43
--    33
--      23
--    22  13
--      12  03
--    11  02
--  10  01
--    00

-- «ZHA-test-generators» (to ".ZHA-test-generators")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
z = ZHA.fromspec("123RRL432R1"):print()
for v in z:points() do printf("%s ", v:lr()) end; print()

ap = AsciiPicture.new("  ")
for v in z:points() do ap:putv(v, v:lr()) end
= ap
ap = AsciiPicture.new("  ")
for P in z:points() do ap:putv(P, P:And(v"23"):lr()) end
= ap

z = ZHA.fromspec("121L"):print()
for P,dx,dy,tex in z:arrows() do print(P:lr().."->"..(P+v(dx,dy)):lr(), tex) end

-- «ZHA-test-shrinktop» (to ".ZHA-test-shrinktop")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
dofile "ZHASpecs4.lua"
z = ZHA.fromtcgspec("4, 5; 32, 14 25"):print()
z = ZHA.fromtcgspec("4, 5; 32, 14 25"):shrinktop("34"):print()

--]]



--      _                _                             _
--  ___| |__   ___  _ __| |_ ___  _ __   ___ _ __ __ _| |_ ___  _ __ ___
-- / __| '_ \ / _ \| '__| __/ _ \| '_ \ / _ \ '__/ _` | __/ _ \| '__/ __|
-- \__ \ | | | (_) | |  | || (_) | |_) |  __/ | | (_| | || (_) | |  \__ \
-- |___/_| |_|\___/|_|   \__\___/| .__/ \___|_|  \__,_|\__\___/|_|  |___/
--                               |_|
-- «shortoperators» (to ".shortoperators")
function shortoperators ()
    True  = function () return z:T() end
    False = function () return z:F() end
    And   = function (P, Q) return P:And(Q) end
    Or    = function (P, Q) return P:Or(Q) end
    Imp   = function (P, Q) return z:Imp(P, Q) end
    Not   = function (P) return Imp(P, False()) end
    Bic   = function (P, Q) return z:Bic(P, Q) end
    --
    Cloq  = function (A)    return function (P) return Or(A, P) end end
    Opnq  = function (A)    return function (P) return Imp(A, P) end end
    Booq  = function (A)    return function (P) return Imp(Imp(P, A), A) end end
    Forq  = function (A, B) return function (P) return And(Or(A, P), Imp(B, P)) end end
    Jand  = function (J, K) return function (P) return And(J(P), K(P)) end end
    --
    Mixq  = function (A)    return function (P) return Imp(Imp(P, A), P) end end
    Mixq2 = function (A)    return function (P) return Jand(Booq(A), Opnq(A))(P) end end
    Truq  = function ()     return function (P) return True() end end
    Falq  = function ()     return function (P) return P end end
  end

-- «shortoperators-tests» (to ".shortoperators-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
shortoperators()
-- Broken:
= mpnewJ({}, "1234567654321", Opnq(v"23")):zhaPs("23")
= mpnewJ({}, "1234567654321", Cloq(v"23")):zhaPs("23")
= mpnewJ({}, "1234567654321", Booq(v"23")):zhaPs("23")
= mpnewJ({}, "1234567654321", Forq(v"42", v"24")):zhaPs("42 24")
= mpnewJ({}, "1234567654321", Jand(Booq(v"21"), Booq(v"12"))):zhaPs("21 12")
= mpnewJ({}, "1234567654321", Mixq (v"33")):zhaPs("33")
= mpnewJ({}, "1234567654321", Mixq2(v"33")):zhaPs("33")
= mpnewJ({}, "1234567654321", Truq()):zhaPs("")
= mpnewJ({}, "1234567654321", Falq()):zhaPs("")

--]]






--  _     ____  
-- | |   |  _ \ 
-- | |   | |_) |
-- | |___|  _ < 
-- |_____|_| \_\
--              
-- «LR» (to ".LR")
-- Adapted from: (find-dn6 "zhaspecs.lua" "LR")
-- See: (find-angg "LUA/ZHASpecs4.lua")

LR = Class {
  type = "LR",
  empty = function (maxy, ax)
      return LR {L={}, R={}, maxy=maxy, ax=ax}
    end,
  from = function (L, R, maxy, ax)
      return LR {L=L, R=R, maxy=maxy, ax=ax}
    end,
  fromspec = function (spec, x0, ax)
      local z = ZHA.fromspec0(spec, x0)
      return LR {L=z.L, R=z.R, ax=ax}
    end,
  fromtriples = function (A, maxy, ax)
      local L, R = {}, {}
      for _,triple in ipairs(A) do
        local y, l, r = triple[1], triple[2], triple[3]
        L[y], R[y] = l, (r or l)
      end
      return LR {L=L, R=R, maxy=maxy, ax=ax}
    end,
  --
  -- 2-column graphs
  -- See: (find-dn6 "tcgs.lua" "TCGSpec")
  fromtcgspec = function (spec, ax)
      -- local maxl,maxr,leftgens,rightgens = unpack(split(spec, "[ %d]+"))
      local pat = "^ *(%d)[ ,]*(%d) *;([ %d]*),([ %d]*)$"
      local maxl,maxr,leftgens,rightgens = spec:match(pat)
      maxl,maxr = tonumber(maxl),tonumber(maxr)
      return LR.fromtwocolgraph(maxl+0, maxr+0, leftgens, rightgens, ax)
    end,
  fromtwocolgraph = function (maxl, maxr, leftgens, rightgens, ax)
      return LR.lozenge(maxl, maxr, ax):addgenerators(leftgens, rightgens)
    end,
  lozenge = function (maxl, maxr, ax)
      local o = LR {L={}, R={}, ax=ax, maxl=maxl, maxr=maxr, maxy=maxl+maxr}
      for y=0,maxl+maxr do
        o.L[y] = o:nesex(-maxl, maxl, y)
        o.R[y] = o:nwswx( maxr, maxr, y)
      end
      return o
    end,
  --
  __tostring = function (o) return o:asciipicture():tostring() end,
  __index = {
    PP = function (o) print(mytabletostring(o)); return o end,
    ap = function (o) return o:asciipicture() end,
    asciipicture = function (o)
        local ap = AsciiPicture.new("  ")
        local put = function (x, y, n) ap:put(V{x, y}, n.."") end
        for y=0,(o.maxy or #o.L) do
          if o.L[y] then put(o.L[y], y, o.L[y]) end
          if o.R[y] then put(o.R[y], y, o.R[y]) end
          if o.ax then put(o.ax, y, y..":") end
        end
        return ap
      end,
    triples = function (o)
        local A = {}
        for y=(o.maxy or #o.L),0,-1 do
          local l, r = o.L[y], o.R[y]
          if l then table.insert(A, {y, l, r}) end
        end
        return A
      end,
    spec = function (o)
        local W = {}
        for y=0,#o.L do W[y] = toint((o.R[y] - o.L[y])/2) + 1 end
        local spec = "1"
        for y=1,#o.L do
          if W[y] == W[y-1]
          then spec = spec..((o.L[y]<o.L[y-1]) and "L" or "R")
          else spec = spec..W[y]
          end
        end  
        return spec
      end,
    zha = function (o) return ZHA.fromspec(o:spec()) end,
    --
    -- putxy - see: (find-dn6 "luarects.lua" "ZHAFromPoints")
    putxy = function (o, x, y)
        o.L[y], o.R[y] = minmax(o.L[y], x, o.R[y])
        return o
      end,
    --
    -- addgenerators (for 2-column graphs)
    swx = function (o, x0, y0, y1) return x0+(y1-y0) end,
    nex = function (o, x0, y0, y1) return x0+(y1-y0) end,
    sex = function (o, x0, y0, y1) return x0-(y1-y0) end,
    nwx = function (o, x0, y0, y1) return x0-(y1-y0) end,
    nesex = function (o, x0, y0, y1)
        return y1>y0 and o:nex(x0, y0, y1) or o:sex(x0, y0, y1)
      end,
    nwswx = function (o, x0, y0, y1)
        return y1>y0 and o:nwx(x0, y0, y1) or o:swx(x0, y0, y1)
      end,
    addleftpoint = function (o, x0, y0)
        for y=0,o.maxy do o.L[y] = max(o.L[y], o:nwswx(x0, y0, y)) end
        return o
      end,
    addrightpoint = function (o, x0, y0)
        for y=0,o.maxy do o.R[y] = min(o.R[y], o:nesex(x0, y0, y)) end
        return o
      end,
    addgenerators = function (o, leftstr, rightstr)
        for leftgen in leftstr:gmatch"%d%d" do
          local leftpoint = v(leftgen) + V{1,-1}
          o:addleftpoint(leftpoint:to_x_y())
        end
        for rightgen in rightstr:gmatch"%d%d" do
          local rightpoint = v(rightgen) + V{-1,-1}
          o:addrightpoint(rightpoint:to_x_y())
        end
        return o
      end,
    --
    shrinktop = function (o, newtop)
        newtop = v(newtop)
        local x1, y1 = newtop[1], newtop[2]
        if not (y1 <= o.maxy) then return o end
        if not (o.L[y1] <= x1 and x1 <= o.R[y1]) then return o end
        for y=o.maxy,y1+1,-1 do
          o.L[y] = nil
          o.R[y] = nil
        end
        o.maxy = y1
        for dy=0,y1 do
          local y, xl, xr = y1-dy, x1-dy, x1+dy
          o.L[y] = max(o.L[y], xl)
          o.R[y] = min(o.R[y], xr)
        end
        return o
      end,
  },
}

-- «LR-tests» (to ".LR-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
A = {{5,  1    },
     {4,   2   },
     {3,  1,3  },
     {2, 0,  4 },
     {1,  1,3  },
     {0,   2   }}
= LR.fromtriples(A, nil)      -- Broken
= LR.fromtriples(A, nil, -2)
= LR.fromtriples(A, nil, -4)

o = LR.fromtriples(A, nil, -2)
= o
= o:PP()
= o:spec()
= o:zha():PP()

= LR.fromspec("123RR21RL")
= LR.fromspec("123RR21RL", nil, -4)
= LR.fromspec("123RR21RL",   0, -4)
= LR.fromspec("123RR21RL",   1, -4)
= LR.fromspec("123RR21RL",   2, -4)
PPV(LR.fromspec("123RR21RL", 2, -4):triples())



-- «LR-putxy-tests» (to ".LR-putxy-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
-- require "picture"
-- require "zhas"
f = function (y, str)
    for x,c in str:gmatch"()(%S)" do o:putxy(x, y) end
    return o
  end
o = LR.from({}, {}, 5, -2)
= o
= f(5, " o   ")
= f(4, "  o  ")
= f(3, " o o ")
= f(2, "o o o")
= f(1, " o o ")
= f(0, "  o  ")
= o:zha()


-- «LR-twocolgraph-tests» (to ".LR-twocolgraph-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhaspecs.lua"
require "picture"
require "zhas"
= LR.lozenge(2, 4)
= LR.lozenge(4, 6)
= LR.lozenge(4, 6, -6)
= LR.lozenge(4, 6):zha()
= LR.lozenge(4, 6):addgenerators("32", ""):zha()
= LR.lozenge(4, 6):addgenerators("32", "15"):zha()
= LR.lozenge(4, 6):addgenerators("32", "15 26"):zha()
= LR.fromtwocolgraph(4, 6, "",   ""     ):zha()
= LR.fromtwocolgraph(4, 6, "32", ""     ):zha()
= LR.fromtwocolgraph(4, 6, "32", "15"   ):zha()
= LR.fromtwocolgraph(4, 6, "32", "15 26"):zha()
= LR.fromtcgspec("   4, 6;    ,        "):zha()
= LR.fromtcgspec("   4, 6;  32,        "):zha()
= LR.fromtcgspec("   4, 6;  32,   15   "):zha()
= LR.fromtcgspec("   4, 6;  32,   15 26"):zha()
= LR.fromtcgspec("   4, 6;  32,   15 26"):zha().spec
= LR.fromtcgspec("   4, 6;  32,   15 26"):zha():totcgspec()
= LR.fromtcgspec(     "46;  32,   15 26"):zha():totcgspec()

-- «LR-fromtcgspec-tests» (to ".LR-fromtcgspec-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
-- dofile "zhaspecs.lua"
require "picture"
-- require "zhas"
= LR.fromtcgspec("4, 6; 11 22 34 45, 25"):zha()
= LR.fromtcgspec("4, 6; 11 22 34 45, 25"):zha().spec
= LR.fromtcgspec("4, 6; 11 22 34 45, 25"):zha():totcgspec()
= LR.fromtcgspec("4, 6; 11 22 34 45, 25 06"):zha()
= LR.fromtcgspec("4, 6; 11 22 34 45, 25 06"):zha():totcgspec()


-- «LR-shrinktop-tests» (to ".LR-shrinktop-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
o = LR.fromtcgspec("4, 6; 11 22 34 45, 25 06")
= o:zha()
= o:shrinktop(v"47"):zha()
= o:shrinktop( "35"):zha()

--]]




--                                    _       
--   ___ ___  _ __  _   _  ___  _ __ | |_ ___ 
--  / __/ _ \| '_ \| | | |/ _ \| '_ \| __/ __|
-- | (_| (_) | |_) | |_| | (_) | |_) | |_\__ \
--  \___\___/| .__/ \__, |\___/| .__/ \__|___/
--           |_|    |___/      |_|            
--
-- An _UGLY_ hack to let me specify options for makepicture in a compact way.
-- A call to copyopts(A, B) copies the options in the table A to the table B.
-- If there is a field "meta" in A it is treated in a special way:
--
--   copyopts({foo=2, bar=3, meta="s ()"}, B)
--
-- works as this, but in an unspecified order:
--
--   copyopts({foo=2, bar=3}, B)
--   copyopts(metaopts["s"],  B)
--   copyopts(metaopts["()"], B)
--
-- Used by: (find-dn6 "zhas.lua" "MixedPicture" "LPicture.new(options)")
--          (find-dn6 "picture.lua" "LPicture" "new" "copyopts(opts, lp)")
--
-- «copyopts» (to ".copyopts")
--          
copyopts = function (A, B)
    if type(A) == "string" then
      for _,name in ipairs(split(A)) do
        local tbl = metaopts[name] or error("No metaopt[\""..A.."\"]")
        copyopts(tbl, B)
      end
      return B
    end
    for key,val in pairs(A) do
      if key == "meta" then
        copyopts(val, B)
      else
        B[key] = val
      end
    end
    return B
  end

-- «metaopts» (to ".metaopts")
--
metaopts = {}
metaopts["b"]   = {bhbox = 1}
metaopts["p"]   = {paren = 1}
metaopts["()"]  = {paren = 1}
metaopts["{}"]  = {curly = 1}
metaopts["s"]    = {cellfont="\\scriptsize",       celllower="2pt"}
metaopts["ss"]   = {cellfont="\\scriptscriptsize", celllower="1.5pt"}  -- ?
metaopts["t"]    = {cellfont="\\tiny",             celllower="1.5pt"}  -- ?
metaopts["t"]    = {cellfont="\\tiny",             celllower="1.25pt"}  -- ?
metaopts["10pt"] = {scale="10pt"} 
metaopts["8pt"]  = {scale="8pt", meta="s"} 
--
-- metaopts that are mainly for TCGs:
metaopts["1pt"]  = {scale="1pt"}

-- «copyopts-tests» (to ".copyopts-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
testcopyopts = function (A) PP(copyopts(A, {})) end
testcopyopts  "8pt"
testcopyopts  "8pt ()"
testcopyopts {foo=2, bar=3}
testcopyopts {foo=2, bar=3, meta="8pt"}
testcopyopts {foo=2, bar=3, meta="8pt ()"}
testcopyopts {foo=2, bar=3}

--]]









--   ____      _
--  / ___|   _| |_ ___
-- | |  | | | | __/ __|
-- | |__| |_| | |_\__ \
--  \____\__,_|\__|___/
--
-- «Cuts» (to ".Cuts")

Cuts = Class {
  type = "Cuts",
  new = function ()
      return Cuts {minicuts = VerticalTable {},
                             asciibb=BoundingBox.new(),
                   latex="", latexbb=BoundingBox.new()}
    end,
  __tostring = function (c) return c:tostring() end,
  __index = {
    get = function (c, v)
        return (c.minicuts[v[2]] or {})[v[1]] or " "
      end,
    tolines = function (c)
        local x0, x1, y0, y1 = c.asciibb:x0x1y0y1()
        local lines = {}
        for y=y1,y0,-1 do
          local s = ""
          for x=x0,x1 do
            local char = (c.minicuts[y] or {})[x] or " "
            s = s..char
          end
          table.insert(lines, s)
        end
        return lines
      end,
    tostring = function (c) return table.concat(c:tolines(), "\n") end,
    print = function (c) print(c); return c end,
    --
    -- Low-level functions for adding cuts
    addminicut = function (c, x, y, slash)
        c.minicuts[y] = c.minicuts[y] or {}
        c.minicuts[y][x] = slash
        c.asciibb:addpoint(v(x, y))
      end,
    addcut0 = function (c, src, tgt)
        c.latexbb:addpoint(src)
        c.latexbb:addpoint(tgt)
        --
        local x0, y0 = src:to_x_y()
        local x1, y1 = tgt:to_x_y()
        if y1 < y0 then x0, y0, x1, y1 = x1, y1, x0, y0 end
        local dx, dy = x1-x0, y1-y0
        if     dy == dx then  -- northeast, with "/"s
          for a=0.5,dy-0.5 do
            c:addminicut(x0+a, y0+a, "/")
          end
        elseif dy == -dx then -- northwest, with "\"s
          for a=0.5,dy-0.5 do
            c:addminicut(x0-a, y0+a, "\\")
          end
        else PP("dx=", dx, "dy=", dy); error()
        end
        c:addlatexcut(src, tgt) -- defined below
        return c
      end,
    addcuts0 = function (c, list)
        for i=1,#list-1 do c:addcut0(list[i], list[i+1]) end
        return c
      end,
    --
    -- Medium-level functions for adding cuts
    addcontour = function (c, zha)
        local leftwall  = zha:leftwallcorners()
        local rightwall = zha:rightwallcorners()
        local corners = {}
        table.insert(corners, leftwall[1]:s())
        for i=2,#leftwall-1 do table.insert(corners, leftwall[i]:w()) end
        table.insert(corners, leftwall[#leftwall]:n())
        for i=#rightwall-1,2,-1 do table.insert(corners, rightwall[i]:e()) end
        table.insert(corners, rightwall[1]:s())
        c:addcuts0(corners)
        return c
      end,
    addlcut = function (c, zha, l)
        if l+1 > zha.topl then return c end
        local westcell = lr(l+1, zha.minr[l+1])
        local eastcell = lr(l,   zha.maxr[l])
        c:addcut0(westcell:s(), eastcell:n())
        return c
      end,
    addrcut = function (c, zha, r)
        if r+1 > zha.topr then return c end
        local eastcell = lr(zha.minl[r+1], r+1)
        local westcell = lr(zha.maxl[r],   r)
        c:addcut0(westcell:n(), eastcell:s())
        return c
      end,
    --
    -- A high-level function for adding (all kinds of) cuts.
    addcuts = function (c, zha, str)
        if str:match"c" then c:addcontour(zha) end
        for l in str:gmatch"/(%d)" do c:addlcut(zha, l+0) end
        for r in str:gmatch"(%d)|" do c:addrcut(zha, r+0) end
        --
        local f = function (lr, dir) local P = v(lr); return P[dir](P) end
        local pat = "(%d%d)([ensw])-(%d%d)([ensw])"
        for src,sdir,tgt,tdir in str:gmatch(pat) do
          c:addcut0(f(src, sdir), f(tgt, tdir))
        end
        return c
      end,
    --
    -- Communication with Picture objects
    -- Similar to: (find-dn6 "zhas.lua" "LPicture" "addlineorvector =")
    addlatexcut = function (c, src, tgt)
        local x0, y0 = src:to_x_y()
        local x1, y1 = tgt:to_x_y()
        local dx, dy = x1-x0, y1-y0
        local len = math.abs(dx)
        local udx, udy = dx/len, dy/len
        -- (find-pict2epage 9 "\\Line( X1,Y1 )( X2,Y2 )")
        -- local put = "  \\put("..x0..","..y0..")"..
        --             "{\\line("..udx..","..udy.."){"..len.."}}"
        local put = "  \\Line("..x0..","..y0..")("..x1..","..y1..")"
        c.latex = c.latex..put.."\n"
      end,
    -- copylatexcuts = function (c, pic)
    --   end,
  },
}


--[[
-- «Cuts-tests» (to ".Cuts-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
c = Cuts.new()
z = ZHA.fromspec("12RRL1LLRR")
= z
= c:addcuts0{v"00":w(), v"01":n(), v"01":e()}
= mytabletostring(c)
= c:addcontour(z)
= c.latex

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
c = Cuts.new()
z = ZHA.fromspec("12RRL1LLRR")
= c:addcontour(z)
= c:addlcut(z, 0)
= c:addlcut(z, 1)
= c:addlcut(z, 2)

= c:addrcut(z, 0)
= c:addrcut(z, 1)
= c:addrcut(z, 2)

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
c = Cuts.new()
z = ZHA.fromspec("12RRL1LLRR"):print()
= c:addcuts(z, "c 432/1/0 0|1|2|345")

z = ZHA.fromspec("1234567654321"):print()
c = Cuts.new()
= c:addcuts(z, "c 01e-41n 40w-44n 14n-64n 11n-16n")

--]]





--  __  __ _              _ ____  _      _
-- |  \/  (_)_  _____  __| |  _ \(_) ___| |_ _   _ _ __ ___
-- | |\/| | \ \/ / _ \/ _` | |_) | |/ __| __| | | | '__/ _ \
-- | |  | | |>  <  __/ (_| |  __/| | (__| |_| |_| | | |  __/
-- |_|  |_|_/_/\_\___|\__,_|_|   |_|\___|\__|\__,_|_|  \___|
--
-- «MixedPicture» (to ".MixedPicture")
-- A MixedPicture object has both an ascii representation and a LaTeX
-- representation. Most MixedPicture objects I use have a ZHA, and sometimes
-- a Cuts.

strtotex = function (str)  -- Used by MixedPicture:put(...)
    if str:match"^%.+$" then return "\\cdot" end
    if str:match"^%*+$" then return "\\bullet" end
    if str:match"^%o+$" then return "\\circ" end
  end
celltotex = function (str, f)  -- used by MixedPicture:addcells(...)
    return (type(f)=="table" and f[str])
        or (type(f)=="function" and f(str))
        or (str:gsub("!", "\\"))
  end

MixedPicture = Class {
  type    = "MixedPicture",
  new     = function (options, zha, J, asciirect)
      return MixedPicture {
        ap   = AsciiPicture.new(options.s or "  "),
        lp   = LPicture.new(options),
        cuts = Cuts.new(),
        zha  = zha,
        J    = J,
        ar   = asciirect,
      }
    end,
  __tostring = function (mp) return mp:tostring() end,
  __index = {
    tolines_mixed = function (mp)
        local  x0,  x1,  y0,  y1 = mp.ap       .bb:x0x1y0y1()
        local cx0, cx1, cy0, cy1 = mp.cuts.asciibb:x0x1y0y1()
        local lines = {}
        for y=max(y1, cy1),min(y0, cy0),-.5 do
          local line = ""
          for x=min(x0, cx0),max(x1, cx1),.5 do
            if isint(x)
            then line = line..mp.ap:get(v(x, y))
            else line = line..mp.cuts:get(v(x, y))
            end
          end
          table.insert(lines, line)
        end
        return lines
      end,
    tolines = function (mp)
        local hascuts  = mp.cuts.asciibb.x0y0
        local hasnodes = mp.ap.bb.x0y0
        if hasnodes and hascuts then return mp:tolines_mixed() end
        if hasnodes then return mp.ap:tolines() end
        if hascuts  then return mp.cuts:tolines() end
        return {"empty"}
      end,
    tostring = function (mp) return table.concat(mp:tolines(), "\n") end,
    tolatex = function (mp)
        local body = mp.cuts.latex .. mp.lp.latex
        local bb = BoundingBox.new():merge(mp.lp.bb):merge(mp.cuts.latexbb)
        return makepicture(mp.lp, bb, body)
      end,
    print  = function (mp) print(mp); return mp end,
    lprint = function (mp) print(mp:tolatex()); return mp end,
    output = function (mp) output(mp:tolatex()); return mp end,
    --
    put = function (mp, v, str, tex)
        mp.ap:putv(v, str)
        mp.lp:put (v, tex or strtotex(str) or str)
        return mp
      end,
    --
    -- «MixedPicture-cuts» (to ".MixedPicture-cuts")
    addcontour = function (mp) mp.cuts:addcontour(mp.zha); return mp end,
    addlcut = function (mp, ...) mp.cuts:addlcut(mp.zha, ...); return mp end,
    addrcut = function (mp, ...) mp.cuts:addrcut(mp.zha, ...); return mp end,
    addcuts = function (mp, ...) mp.cuts:addcuts(mp.zha, ...); return mp end,
    --
    addcutssub = function (mp, newtop, ...)
        mp.cuts:addcuts(mp.zha:shrinktop(newtop), ...)
        return mp
      end,
    --
    -- OBSOLETE.
    -- zfunction and zsetbullets were replaced by methods using 
    -- the ar field (which holds an AsciiRect)
    zfunction = function (mp, asciirect)
        for x,y,c in asciirectpoints(asciirect) do
          if c:match"%d" then
            mp:put(v(x, y), "#"..c)
            mp.lp.def = mp.lp.def.."#"..c
          end
        end
        return mp
      end,
    zsetbullets = function (mp, asciirect)
        for x,y,c in asciirectpoints(asciirect) do
          if c:match"%d" then mp:put(v(x, y), "**") end
        end
        return mp
      end,
    --
    -- New methods (2015sep06)
    -- points = function (mp, ...) return mp.zha:points() end,
    points = function (mp) return (mp.zha or mp.ar):points() end,
    arrows = function (mp, w) return (mp.zha or mp.ar):arrows(w) end,
    addarrows = function (mp, w)
        for v,dx,dy,tex in mp:arrows(w) do mp.lp:putarrow(v, dx, dy, tex) end
        return mp
      end,
    addarrowsexcept = function (mp, w, omit)
        if type(omit) == "string" then omit = Set.from(split(omit)) end
        if type(omit) == "table" then
          local o = omit
          omit = function (vdx) return o:has(vdx) end
        end
        for v,dx,dy,tex in mp:arrows(w) do
          if not omit(v:xy()..dx, v, dx) then mp.lp:putarrow(v, dx, dy, tex) end
        end
        return mp
      end,
    addbullets = function (mp)
        for v in mp:points() do mp:put(v, "**") end
        return mp
      end,
    adddots = function (mp)
        for v in mp:points() do mp:put(v, "..") end
        return mp
      end,
    addlrs = function (mp) -- for zhas
        for v in mp.zha:points() do mp:put(v, v:lr()) end
        return mp
      end,
    addgens = function (mp) -- for zhas
        for P,l,r,gen in mp.zha:pointslrg() do
          if     gen == "L" then mp:put(P, l.."_", l.."\\_")
          elseif gen == "R" then mp:put(P, "_"..r, "\\_"..r)
          else                   mp:put(P, "..")
          end
        end
        return mp
      end,
    addcells = function (mp, f) -- for asciirects
        for v,str in mp.ar:points() do mp:put(v, celltotex(str, f)) end
        return mp
      end,
    addxys = function (mp)
        for v,str in mp:points() do mp:put(v, v:xy()) end
        return mp
      end,
    --
    -- «MixedPicture-zha» (to ".MixedPicture-zha")
    zhabullets = function (mp)
        for v in mp.zha:points() do mp:put(v, "**") end
        return mp
      end,
    zhadots = function (mp)
        for v in mp.zha:points() do mp:put(v, "..") end
        return mp
      end,
    zhalr = function (mp)
        for v in mp.zha:points() do mp:put(v, v:lr()) end
        return mp
      end,
    zhaPs = function (mp, str)
        for _,w in ipairs(split(str)) do mp:put(v(w), w) end
        return mp
      end,
    --
    setz = function (mp) z = mp.zha; return mp end,  -- set the global z
    zhalrf0 = function (mp, f)
        if type(f) == "string" then f = L(f) end
        for v in mp.zha:points() do mp:put(v, tostring(f(v))) end
        return mp
      end,
    zhalrf = function (mp, f)
        if type(f) == "string" then f = L(f) end
        for v in mp.zha:points() do mp:put(v, f(v):lr()) end
        return mp
      end,
    zhaJ     = function (mp) return mp:zhalrf(mp.J) end,
    zhaJcuts = function (mp) return mp:addcuts(mp.zha:getcuts(mp.J)) end,
    --
    putpiledef = function (mp, v)
        local L, R = mp.zha.topl, mp.zha.topr
        local l, r = v:to_l_r()
        local pile = function (h, n) return ("0"):rep(h-n)..("1"):rep(n) end
        local piledef = function (A, B, a, b)
            return "\\foo{"..a..b.."}{"..pile(A, a).." "..pile(B, b).."}"
          end
        mp:put(v, v:lr(), piledef(L, R, l, r))
        return mp
      end,
    zhapiledefs = function (mp)
        for v in mp.zha:points() do mp:putpiledef(v) end
        return mp
      end,
    --
    -- (find-angg "LUA/lua50init.lua" "eval-and-L")
  },
}


--                                      
--  _ __ ___  _ __  _ __   _____      __
-- | '_ ` _ \| '_ \| '_ \ / _ \ \ /\ / /
-- | | | | | | |_) | | | |  __/\ V  V / 
-- |_| |_| |_| .__/|_| |_|\___| \_/\_/  
--           |_|                        
--
-- «mpnew» (to ".mpnew")
-- «mpnewJ» (to ".mpnewJ")
-- (to "MixedPicture-J-tests")
-- See: (find-dn6 "luarects.lua" "AsciiRect")
--      (find-dn6 "luarects.lua" "AsciiRect-tests")
--
-- Mpnew and mpnewJ are shorthands that create MixedPicture objects
-- with or without a J-operator...

mpnew = function (opts, spec, J, asciirect)
    local z  = ZHA.fromspec(spec)
    local mp = MixedPicture.new(opts, z, J, asciirect)
    return mp, z
  end

mpnewJ = function (opts, spec, J)
    return mpnew(opts, spec, J):setz():zhaJcuts():addcontour()
  end



-- «MixedPicture-tests» (to ".MixedPicture-tests")
--  (find-dn6grep "grep --color=auto -nH --null -e LPicture *.lua")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "ZHA4.lua"
z = ZHA.fromspec("12RRL1LLRR"):print()
mp = MixedPicture.new({}, z)
mp = MixedPicture.new({bhbox=1, paren=1, scale="10pt", def="foo"}, z)
= mp:put(v"02", "02")
= mp:tolatex()
for v in z:points() do mp:put(v, v:lr()) end
= mp:addcontour()
= mp:addlcut(0):addrcut(2)
latex = mp:tolatex()
= latex
writefile("/tmp/o.tex", latex)  -- (find-fline "/tmp/o.tex")
-- (find-ist "-handouts.tex" "mixedpicture-tests")



* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
z = ZHA.fromspec("1234R3L21L"):print()
mp = MixedPicture.new({bhbox=1, paren=1, scale="10pt", def="foo"}, z)
mp = MixedPicture.new({def="foo"}, z)
  for v in z:points() do mp:put(v, v:lr()) end    -- optional
= mp:addcontour()
= mp:addrcut(0):addrcut(2)
= mp:addlcut(1)
= mp:put(v"20", "P")
= mp:put(v"30", "P*", "P^*")
= mp:put(v"11", "Q")
= mp:put(v"12", "Q*", "Q^*")
= mp:put(v"03", "R")
= mp:put(v"14", "R*", "R^*")
latex = mp:tolatex()
= latex
writefile("/tmp/o.tex", latex)  -- (find-fline "/tmp/o.tex")
-- (find-ist "-handouts.tex" "mixedpicture-tests")


* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
z = ZHA.fromspec("121L"):print()
mp = MixedPicture.new({bhbox=1, paren=1, scale="20pt", def="foo"}, z)
mp = MixedPicture.new({bhbox=1, scale="14pt", cellfont="\\scriptsize", def="foo"}, z)
-- for v in z:points() do mp:put(v, v:lr()) end
for v in z:points() do mp:put(v, v:xy()) end
= mp
for v,dx,dy in mp.zha:arrows() do print(v, dx, dy) end
for v,dx,dy in mp.zha:arrows() do
  local tex = (dx==-1) and "\\swarrow" or "\\searrow"
  mp.lp:putarrow(v, dx, dy, tex)
end
= mp.lp
latex = mp:tolatex()
latex = "\\def\\foo{"..latex.."}"
= latex
writefile("/tmp/o.tex", latex)  -- (find-fline "/tmp/o.tex")
-- (find-ist "-handouts.tex" "mixedpicture-tests")


-- «MixedPicture-zhalrf-tests» (to ".MixedPicture-zhalrf-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
z = ZHA.fromspec("123454321"):print()
mp = MixedPicture.new({}, z)
mp = mpnew({}, "123454321")
= mp:zhalrf0("lr -> 23")
= mp:zhalrf0("lr -> '23'")
= mp:zhalrf0("lr -> v'23'")
= mp:zhalrf0("lr -> v'23':lr()")
= mp:zhalrf0("lr -> otype(lr)")
= mp:zhalrf ("lr -> lr")
= mp:zhalrf0("lr -> lr:And(v'12'):lr()")
= mp:zhalrf ("lr -> lr:And(v'12')")
= mp:zhalrf0("P -> P:below(v'12') and 1 or 0")
= mp:zhalrf0("P -> P:And(v'22')")
= mp:zhalrf0("P -> P:And(v'22'):lr()")
= v"31":And(v"12"):lr()




-- «MixedPicture-arch-tests» (to ".MixedPicture-arch-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"

-- The (v*) cube
mp = mpnew({def="orStarCubeArchetypal"}, "12321L"):addcuts("c 21/0 0|12")
mp:put(v"10", "P"):put(v"20", "P*", "P^*")
mp:put(v"01", "Q"):put(v"02", "Q*", "Q^*"):print()

-- The (&*) cube
mp = mpnew({def="andStarCubeArchetypal"}, "12321"):addcuts("c 2/10 01|2")
mp:put(v"20", "P"):put(v"21", "P*", "P^*")
mp:put(v"02", "Q"):put(v"12", "Q*", "Q^*"):print()

-- The (->*) cube
mp = mpnew({def="impStarCubeArchetypal"}, "12321"):addcuts("c 2/10 01|2")
mp:put(v"10", "P")
mp:put(v"00", "Q"):print()

-- (&R) is *-functorial
mp = mpnew({def="andRIsStarFunctorial"}, "1234R321"):addcuts("c 32/10 01|23|4")
mp:put(v"30", "P"):put(v"31", "P*", "P^*")
mp:put(v"22", "Q"):put(v"33", "Q*", "Q^*")
mp:put(v"04", "R"):put(v"14", "R*", "R^*"):print()

-- (Pv) is *-functorial
mp = mpnew({def="PvIsStarFunctorial"}, "1234R3L21L"):addcuts("c 5432/10 0|12|34")
mp:put(v"20", "P"):put(v"30", "P*", "P^*")
mp:put(v"11", "Q"):put(v"12", "Q*", "Q^*")
mp:put(v"03", "R"):put(v"14", "R*", "R^*"):print()



-- «MixedPicture-zset-tests» (to ".MixedPicture-zset-tests")
-- (find-dn6file "zhas.lua" "zfunction =")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
z = ZHA.fromspec("12321L"):print()
house = ".1.|2.3|4.5"
mp = MixedPicture.new({def="dagHouse"}):zfunction(house):print():lprint()
mp = MixedPicture.new({def="dagHouse", meta="t", scale="5pt"}, z):zfunction(house):lprint()
mp = MixedPicture.new({def="House"}):zsetbullets(house):print():lprint()
mp = MixedPicture.new({def="Ten"}, z):zhabullets():print()
mp = MixedPicture.new({def="Ten"}, z):zhadots():print()
mp = MixedPicture.new({def="Ten"}, z):zhalr():print()

mp = mpnew({scale="15pt"}, "121L"):zhapiledefs():print():lprint()

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
threefour = "..1|2.3|4.5|6.7"
MixedPicture.new({def="dagThreeFour", meta="s"}):zfunction(threefour):print():lprint()


-- «MixedPicture-J-tests» (to ".MixedPicture-J-tests")
-- (to "mpnewJ")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
= mpnewJ({}, "1234RR321", "P -> z:Or(P, v'12')")
= mpnewJ({}, "1234RR321", "P -> z:Imp(v'12', P)")

mp = mpnewJ({}, "1234RR321", "P -> z:Imp(v'12', P)")
= mp.zha:getcuts(mp.J)

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"

--]]




--      _                    ____           _                       _     
--   __| |_ __ __ ___      _|___ \ ___ ___ | | __ _ _ __ __ _ _ __ | |__  
--  / _` | '__/ _` \ \ /\ / / __) / __/ _ \| |/ _` | '__/ _` | '_ \| '_ \ 
-- | (_| | | | (_| |\ V  V / / __/ (_| (_) | | (_| | | | (_| | |_) | | | |
--  \__,_|_|  \__,_| \_/\_/ |_____\___\___/|_|\__, |_|  \__,_| .__/|_| |_|
--                                            |___/          |_|          
--
-- «drawtwocolgraph» (to ".drawtwocolgraph")

dxyrunf = function (...) print("%D "..format(...)); dxyrun(format(...)) end

drawtwocolgraph = function (dx, dy, maxl, maxr, leftgens, rightgens, ltexs, rtexs)
    local D = dxyrunf
    local y = function (i) return 100+dy*(i-1) end
    local ltex = function (i) return ltexs and split(ltexs)[i] or i.."\\_" end
    local rtex = function (i) return rtexs and split(rtexs)[i] or "\\_"..i end
    D "(("
    for i=1,maxl do D("node: %d,%d L%d .tex= %s", 100,    y(i), i, ltex(i)) end
    for i=1,maxr do D("node: %d,%d R%d .tex= %s", 100+dx, y(i), i, rtex(i)) end
    for i=maxl,2,-1 do D("L%d L%d ->", i, i-1) end
    for i=maxr,2,-1 do D("R%d R%d ->", i, i-1) end
    for l,r in leftgens :gmatch "(%d)(%d)" do D("L%d R%d ->", l, r) end
    for l,r in rightgens:gmatch "(%d)(%d)" do D("L%d R%d <-", l, r) end
    D "))"
  end

-- «drawtwocolgraph-tests» (to ".drawtwocolgraph-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
dxyrunf = function (...) print("%D "..format(...)) end
drawtwocolgraph(40, 20, 4, 6, "24", "24 35")

--]==]






-- __     ______      _       
-- \ \   / / ___|   _| |_ ___ 
--  \ \ / / |  | | | | __/ __|
--   \ V /| |__| |_| | |_\__ \
--    \_/  \____\__,_|\__|___/
--                            
-- «VCuts» (to ".VCuts")
-- For drawing slashings as V-shaped diagrams.
-- (phap 27 "piccs-and-slashings")
-- (pha     "piccs-and-slashings")

VCuts = Class {
  type = "VCuts",
  new  = function (options, l, r)
      local vc = VCuts {mp=MixedPicture.new(options)}
      if l then vc:putls(l) end
      if r then vc:putrs(r) end
      return vc
    end,
  __tostring = function (vc) return tostring(vc.mp) end,
  __index = {
    tostring = function (vc) return tostring(vc.mp) end,
    tolatex  = function (vc) return vc.mp:tolatex() end,
    output   = function (vc) output(vc:tolatex()); return vc end,
    print = function (vc) print(tostring(vc)); return vc end,
    putl = function (vc, n) vc.mp:put(v((n+1).."0"), n.."", n..""); return vc  end,
    putr = function (vc, n) vc.mp:put(v("0"..(n+1)), n.."", n..""); return vc  end,
    cutl = function (vc, n) vc.mp:addcuts(format("%d0w-%d0n", n+1, n+1)); return vc end,
    cutr = function (vc, n) vc.mp:addcuts(format("0%dn-0%de", n+1, n+1)); return vc end,
    putls = function (vc, n) for i=0,n+0 do vc:putl(i) end; return vc end,
    putrs = function (vc, n) for i=0,n+0 do vc:putr(i) end; return vc end,
  },
}

-- «VCuts-tests» (to ".VCuts-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"

vc = VCuts.new({scale="7pt", def="foo"}, 4, 6)
= vc
vc:cutl(0)
vc:cutr(3):cutr(5)
= vc
= vc:tolatex()

--]]





--[==[

--  _____ ____ ____ 
-- |_   _/ ___/ ___|
--   | || |  | |  _ 
--   | || |__| |_| |
--   |_| \____\____|
--                  
-- «TCG» (to ".TCG")
-- Draw two-column graphs.
-- Code commented out! Too messy!
-- Moved to:
--   (find-LATEX "edrxpict.lua" "TCG")
-- Sort of superseded by this, that has a different interface:
--   (find-dn6file "tcgs.lua")


TCG = Class {
  type = "TCG",
  new  = function (opts, def, l, r, lrarrows, rlarrows)
      local dims = opts                   -- was opts.dims
      local lp = LPicture.new(opts)
      lp.def = def
      local tcg = {lp=lp,   dh=dims.dh, dv=dims.dv, eh=dims.eh, ev=dims.ev,
                   l=l+0, r=r+0, lrarrows=lrarrows, rlarrows=rlarrows}
      return TCG(tcg)
    end,
  __tostring = function (tcg) return tcg:tostring() end,
  __index = {
    tostring = function (tcg)
        return format("(%s, %s, %q, %q)", tcg.l, tcg.r, tcg.lrarrows, tcg.rlarrows)
      end,
    tolatex = function (tcg) return tcg.lp:tolatex() end,
    L = function (tcg, y) return v(0,      tcg.dv*y) end,
    R = function (tcg, y) return v(tcg.dh, tcg.dv*y) end,
    arrow = function (tcg, A, B, e)
        tcg.lp:addtex(Line.newAB(A, B, e, 1-e):pictv())
      end,
    lrs = function (tcg)
        for y=1,tcg.l do tcg.lp:put(tcg:L(y), y.."\\_") end
        for y=1,tcg.r do tcg.lp:put(tcg:R(y), "\\_"..y) end
        return tcg
      end,
    bus = function (tcg)
        for y=1,tcg.l do tcg.lp:put(tcg:L(y), "\\bullet") end
        for y=1,tcg.r do tcg.lp:put(tcg:R(y), "\\bullet") end
        return tcg
      end,
    strs = function (tcg, strsl, strsr)
        if type(strsl) == "string" then strsl = split(strsl) end
        if type(strsr) == "string" then strsr = split(strsr) end
        for y,str in ipairs(strsl) do tcg.lp:put(tcg:L(y), str) end
        for y,str in ipairs(strsr) do tcg.lp:put(tcg:R(y), str) end
        return tcg
      end,
    cs = function (tcg, charsl, charsr)
        return tcg:strs(split(charsl, "."), split(charsr, "."))
      end,
    cq = function (tcg, charsl, charsr)
        local lc   = function (y) return charsl:sub(y, y) end
        local rc   = function (y) return charsr:sub(y, y) end
        local lstr = function (y) return lc(y)=="L" and y.."\\_" or lc(y) end
        local rstr = function (y) return rc(y)=="R" and "\\_"..y or rc(y) end
        for y=1,#charsl do tcg.lp:put(tcg:L(y), lstr(y)) end
        for y=1,#charsr do tcg.lp:put(tcg:R(y), rstr(y)) end
        return tcg
      end,
    vs = function (tcg)
        for y=1,tcg.l-1 do tcg:arrow(tcg:L(y+1), tcg:L(y), tcg.ev) end
        for y=1,tcg.r-1 do tcg:arrow(tcg:R(y+1), tcg:R(y), tcg.ev) end
        return tcg
      end,
    hs = function (tcg)
        for l,r in tcg.lrarrows:gmatch("(%d)(%d)") do
          tcg:arrow(tcg:L(l), tcg:R(r), tcg.eh)
        end
        for l,r in tcg.rlarrows:gmatch("(%d)(%d)") do
          tcg:arrow(tcg:R(r), tcg:L(l), tcg.eh)
        end
        return tcg
      end,
    print  = function (tcg) print(tcg); return tcg end,
    lprint = function (tcg) print(tcg:tolatex()); return tcg end,
    output = function (tcg) output(tcg:tolatex()); return tcg end,
  },
}

-- «TGC-tests» (to ".TGC-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
-- dims = {dv=2, dh=3, ev=0.32, eh=0.2}
opts = {scale="10pt", dims={dv=2, dh=3, ev=0.32, eh=0.2}}
opts = {scale="10pt",       dv=2, dh=3, ev=0.32, eh=0.2 }
tcg = TCG.new(opts, "foo", 4, 6, "12", "23 34"):lrs():vs():hs():lprint()
= tcg
= tcg:tolatex()

--]]

--]==]








--                 _ _               _               _       _
--   __ _ ___  ___(_|_)_ __ ___  ___| |_ _ __   ___ (_)_ __ | |_ ___
--  / _` / __|/ __| | | '__/ _ \/ __| __| '_ \ / _ \| | '_ \| __/ __|
-- | (_| \__ \ (__| | | | |  __/ (__| |_| |_) | (_) | | | | | |_\__ \
--  \__,_|___/\___|_|_|_|  \___|\___|\__| .__/ \___/|_|_| |_|\__|___/
--                                      |_|
--
-- Asciirects are a good way to specify ZSets and ZFunctions - for
-- example, ".1.|2.3|4.5" is (reading order on) the "House" ZSet.
-- THIS IS OBSOLETE, and has been superseded by:
--   (find-dn6 "luarects.lua" "AsciiRect")
--
-- «asciirectpoints» (to ".asciirectpoints")
asciirectpoints = function (lines)
    return cow(function ()
        if type(lines) == "string" then
          lines = splitlines((lines:gsub("|", "\n")))
        end
        for y=#lines-1,0,-1 do
          local line = lines[#lines-y]
          for x=0,#line-1 do
            local c = line:sub(x+1, x+1)
            coy(x, y, c)
          end
        end
      end)
  end

-- «asciirectpoints-tests» (to ".asciirectpoints-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
str = ".1.|2.3|4.5"
for x,y,c in asciirectpoints(str) do print(x, y, c) end
for x,y,c in asciirectpoints(str) do
  if c:match"%d" then
    local p = "#"..c
    print(v(x, y), c)
  end
end

opts = {def="dagHouse", scale="4pt", meta="p b s"}
mp = MixedPicture.new(opts)
PP(mp.lp)
for x,y,c in asciirectpoints(str) do
  if c:match"%d" then
    mp:put(v(x, y), "#"..c)
    mp.lp.def = mp.lp.def.."#"..c
  end
end
= mp
= mp:tolatex()

opts = {def="PvSTbullets", meta="p b s", scale="4pt"}
z = ZHA.fromspec("1234R3L21L"):print()
mp = MixedPicture.new(opts, z)
for v in z:points() do mp:put(v, "**") end
= mp
= mp:tolatex()

opts = {def="dagHouse", scale="4pt", meta="p b s"}
str = ".1.|2.3|4.5"
mp = MixedPicture.new(opts):zfunction(str)
= mp
= mp:tolatex()

mp = MixedPicture.new(opts):zsetbullets(str)
= mp
= mp:tolatex()

-- (find-istfile "1.org" "* 2-column graphs")
-- (find-istfile "1.org" "* 2-column graphs" "how to convert between proper")
z = ZHA.fromspec("12RR1234321L"):print()

--      56	             56
--        46                   ..
--      45  36               ..  ..
--    44  35  26           ..  ..  ..
--  43  34  25  16       43  ..  ..  16
--    33  24  15           33  ..  15
--      23  14               23  14
--        13                   ..
--      12  03               ..  03
--    11  02               ..  02
--  10  01	         10  01
--    00                   ..

-- (find-istfile "1.org" "* ZQuotients")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
z = ZHA.fromspec("12345RR4321"):print()
z = ZHA.fromspec("1234543RR21"):print()
z = ZHA.fromspec("1234543RR21"):print()
z = ZHA.fromspec("1R2R3212RL1"):print()
mp = MixedPicture.new({def="foo"}, z):zhalr()
mp:addcuts("c 4321/0 0123|45|6"):print()

--]]





--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
-- See: (find-LATEX "istanbul-july.tex" "partitiongraph")
dofile "zhas.lua"
opts = {def="graphid", scale="6pt", meta="p b s"}
mp = MixedPicture.new(opts)
spec = "012345"
for y=0,5 do mp:put(v(-1, y), y.."") end
for x=0,5 do mp:put(v(x, -1), x.."") end
for a=0,5 do local aP = spec:sub(a+1, a+1)+0; mp:put(v(a, aP), "*") end
mp.lp:addlineorvector(v(0, 0), v(6, 0), "vector")
mp.lp:addlineorvector(v(0, 0), v(0, 6), "vector")
mp:put(v(7, 0), "a")
mp:put(v(0, 7), "aP", "a^P")
= mp.lp
= mp
= mp.lp
= mp

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zhas.lua"
z = ZHA.fromspec("1R2R3212RL1"):print()
mp = MixedPicture.new({def="ZQ", scale="1pt", meta="b ss"}, z)
mp:zhadots()
mp:lprint()

--]]





-- Local Variables:
-- coding:  utf-8-unix
-- End: