about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2022-03-19 00:19:58 -0700
committerKartik K. Agaram <vc@akkartik.com>2022-03-19 00:19:58 -0700
commit7859317ece5477862e8924657298c3595dd8a8e9 (patch)
tree23cb8133e7cdfb4df5ce537b5eb6baf84028bce8
parentad0ce8b83e52a5a7b5ca4e7fc1466a9dc0d0898f (diff)
downloadteliva-7859317ece5477862e8924657298c3595dd8a8e9.tar.gz
standardize common helpers across all apps
In particular, I merged take_out in anagrams.tlv with all_but in
graphviz.
-rw-r--r--anagrams.tlv118
-rw-r--r--break.tlv68
-rw-r--r--gemini.tlv157
-rw-r--r--graphviz.tlv97
-rw-r--r--life.tlv68
-rw-r--r--lisp.tlv176
-rw-r--r--template.tlv68
-rw-r--r--toot-toot.tlv125
-rw-r--r--zet.tlv68
9 files changed, 883 insertions, 62 deletions
diff --git a/anagrams.tlv b/anagrams.tlv
index 1045332..b4dd01d 100644
--- a/anagrams.tlv
+++ b/anagrams.tlv
@@ -66,6 +66,36 @@
     >
     >-- TODO: backport utf-8 support from Lua 5.3
 - __teliva_timestamp: original
+  debugy:
+    >debugy = 5
+- __teliva_timestamp: original
+  dbg:
+    >-- helper for debug by print; overlay debug information towards the right
+    >-- reset debugy every time you refresh screen
+    >function dbg(window, s)
+    >  local oldy = 0
+    >  local oldx = 0
+    >  oldy, oldx = window:getyx()
+    >  window:mvaddstr(debugy, 60, s)
+    >  debugy = debugy+1
+    >  window:mvaddstr(oldy, oldx, '')
+    >end
+- __teliva_timestamp: original
+  check:
+    >function check(x, msg)
+    >  if x then
+    >    Window:addch('.')
+    >  else
+    >    print('F - '..msg)
+    >    print('  '..str(x)..' is false/nil')
+    >    teliva_num_test_failures = teliva_num_test_failures + 1
+    >    -- overlay first test failure on editors
+    >    if teliva_first_failure == nil then
+    >      teliva_first_failure = msg
+    >    end
+    >  end
+    >end
+- __teliva_timestamp: original
   check_eq:
     >function check_eq(x, expected, msg)
     >  if eq(x, expected) then
@@ -90,8 +120,13 @@
     >      if b[k] ~= v then
     >        return false
     >      end
-    >      return true
     >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
     >  end
     >  return a == b
     >end
@@ -222,6 +257,67 @@
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
@@ -317,27 +413,11 @@
     >  local result = {}
     >  for i=1, #s do
     >    if i == 1 or s[i] ~= s[i-1] then
-    >      append(result, combine(s[i], gather(take_out(s, i))))
+    >      append(result, combine(s[i], gather(all_but(s, i))))
     >    end
     >  end
     >  return result
     >end
-- __teliva_timestamp:
-    >Mon Feb 21 18:06:20 2022
-  take_out:
-    >function take_out(s, i)
-    >  if i < 1 then return s:sub(1) end
-    >  return s:sub(1, i-1) .. s:sub(i+1)
-    >end
-    >
-    >function test_take_out()
-    >  check_eq(take_out('', 0), '', 'take_out: empty')
-    >  check_eq(take_out('abc', 0), 'abc', 'take_out: invalid low index')
-    >  check_eq(take_out('abc', 4), 'abc', 'take_out: invalid high index')
-    >  check_eq(take_out('abc', 1), 'bc', 'take_out: first index')
-    >  check_eq(take_out('abc', 3), 'ab', 'take_out: final index')
-    >  check_eq(take_out('abc', 2), 'ac', 'take_out: middle index')
-    >end
 - __teliva_timestamp: original
   __teliva_note:
     >basic version
@@ -439,7 +519,7 @@
     >  local result = {}
     >  for i=1, #s do
     >    if i == 1 or s[i] ~= s[i-1] then
-    >      local subresult = gather(take_out(s, i))
+    >      local subresult = gather(all_but(s, i))
     >      if subresult == nil then return nil end  -- interrupted
     >      append(result, combine(s[i], subresult))
     >    end
diff --git a/break.tlv b/break.tlv
index 55f0e92..a34ee76 100644
--- a/break.tlv
+++ b/break.tlv
@@ -120,8 +120,13 @@
     >      if b[k] ~= v then
     >        return false
     >      end
-    >      return true
     >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
     >  end
     >  return a == b
     >end
@@ -252,6 +257,67 @@
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
diff --git a/gemini.tlv b/gemini.tlv
index f6e0b8b..e4fbb21 100644
--- a/gemini.tlv
+++ b/gemini.tlv
@@ -81,6 +81,74 @@
     >  window:mvaddstr(oldy, oldx, '')
     >end
 - __teliva_timestamp: original
+  check:
+    >function check(x, msg)
+    >  if x then
+    >    Window:addch('.')
+    >  else
+    >    print('F - '..msg)
+    >    print('  '..str(x)..' is false/nil')
+    >    teliva_num_test_failures = teliva_num_test_failures + 1
+    >    -- overlay first test failure on editors
+    >    if teliva_first_failure == nil then
+    >      teliva_first_failure = msg
+    >    end
+    >  end
+    >end
+- __teliva_timestamp: original
+  check_eq:
+    >function check_eq(x, expected, msg)
+    >  if eq(x, expected) then
+    >    Window:addch('.')
+    >  else
+    >    print('F - '..msg)
+    >    print('  expected '..str(expected)..' but got '..str(x))
+    >    teliva_num_test_failures = teliva_num_test_failures + 1
+    >    -- overlay first test failure on editors
+    >    if teliva_first_failure == nil then
+    >      teliva_first_failure = msg
+    >    end
+    >  end
+    >end
+- __teliva_timestamp: original
+  eq:
+    >function eq(a, b)
+    >  if type(a) ~= type(b) then return false end
+    >  if type(a) == 'table' then
+    >    if #a ~= #b then return false end
+    >    for k, v in pairs(a) do
+    >      if b[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
+    >  end
+    >  return a == b
+    >end
+- __teliva_timestamp: original
+  str:
+    >-- smarter tostring
+    >-- slow; used only for debugging
+    >function str(x)
+    >  if type(x) == 'table' then
+    >    local result = ''
+    >    result = result..#x..'{'
+    >    for k, v in pairs(x) do
+    >      result = result..str(k)..'='..str(v)..', '
+    >    end
+    >    result = result..'}'
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    return '"'..x..'"'
+    >  end
+    >  return tostring(x)
+    >end
+- __teliva_timestamp: original
   map:
     >-- only for arrays
     >function map(l, f)
@@ -147,13 +215,96 @@
     >  return result
     >end
 - __teliva_timestamp: original
+  sort_letters:
+    >function sort_letters(s)
+    >  tmp = {}
+    >  for i=1,#s do
+    >    table.insert(tmp, s[i])
+    >  end
+    >  table.sort(tmp)
+    >  local result = ''
+    >  for _, c in pairs(tmp) do
+    >    result = result..c
+    >  end
+    >  return result
+    >end
+    >
+    >function test_sort_letters(s)
+    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')
+    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')
+    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')
+    >end
+- __teliva_timestamp: original
+  count_letters:
+    >function count_letters(s)
+    >  local result = {}
+    >  for i=1,s:len() do
+    >    local c = s[i]
+    >    if result[c] == nil then
+    >      result[c] = 1
+    >    else
+    >      result[c] = result[c] + 1
+    >    end
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
   append:
-    >function append(target, src)
-    >  for _, line in ipairs(src) do
-    >    table.insert(target, line)
+    >-- concatenate list 'elems' into 'l', modifying 'l' in the process
+    >function append(l, elems)
+    >  for i=1,#elems do
+    >    table.insert(l, elems[i])
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
   clear:
     >function clear(lines)
     >  while #lines > 0 do
diff --git a/graphviz.tlv b/graphviz.tlv
index 1793b34..a93d506 100644
--- a/graphviz.tlv
+++ b/graphviz.tlv
@@ -115,6 +115,7 @@
     >function eq(a, b)
     >  if type(a) ~= type(b) then return false end
     >  if type(a) == 'table' then
+    >    if #a ~= #b then return false end
     >    for k, v in pairs(a) do
     >      if b[k] ~= v then
     >        return false
@@ -256,6 +257,67 @@
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
@@ -1050,38 +1112,3 @@
     >    window:addstr(' ')
     >  end
     >end
-- __teliva_timestamp:
-    >Fri Mar 18 20:32:18 2022
-  all_but:
-    >function all_but(l, idx)
-    >  local result = {}
-    >  for i, elem in ipairs(l) do
-    >    if i ~= idx then
-    >      table.insert(result,elem)
-    >    end
-    >  end
-    >  return result
-    >end
-- __teliva_timestamp:
-    >Fri Mar 18 20:32:18 2022
-  set:
-    >function set(l)
-    >  local result = {}
-    >  for i, elem in ipairs(l) do
-    >    result[elem] = true
-    >  end
-    >  return result
-    >end
-- __teliva_timestamp:
-    >Fri Mar 18 20:32:18 2022
-  set_eq:
-    >function set_eq(l1, l2)
-    >  return eq(set(l1), set(l2))
-    >end
-    >
-    >function test_set_eq()
-    >  check(set_eq({1}, {1}), 'set_eq: identical')
-    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
-    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
-    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
-    >end
diff --git a/life.tlv b/life.tlv
index 146b56f..4445de8 100644
--- a/life.tlv
+++ b/life.tlv
@@ -120,8 +120,13 @@
     >      if b[k] ~= v then
     >        return false
     >      end
-    >      return true
     >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
     >  end
     >  return a == b
     >end
@@ -252,6 +257,67 @@
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
diff --git a/lisp.tlv b/lisp.tlv
index 47285df..894995e 100644
--- a/lisp.tlv
+++ b/lisp.tlv
@@ -81,6 +81,74 @@
     >  window:mvaddstr(oldy, oldx, '')
     >end
 - __teliva_timestamp: original
+  check:
+    >function check(x, msg)
+    >  if x then
+    >    Window:addch('.')
+    >  else
+    >    print('F - '..msg)
+    >    print('  '..str(x)..' is false/nil')
+    >    teliva_num_test_failures = teliva_num_test_failures + 1
+    >    -- overlay first test failure on editors
+    >    if teliva_first_failure == nil then
+    >      teliva_first_failure = msg
+    >    end
+    >  end
+    >end
+- __teliva_timestamp: original
+  check_eq:
+    >function check_eq(x, expected, msg)
+    >  if eq(x, expected) then
+    >    Window:addch('.')
+    >  else
+    >    print('F - '..msg)
+    >    print('  expected '..str(expected)..' but got '..str(x))
+    >    teliva_num_test_failures = teliva_num_test_failures + 1
+    >    -- overlay first test failure on editors
+    >    if teliva_first_failure == nil then
+    >      teliva_first_failure = msg
+    >    end
+    >  end
+    >end
+- __teliva_timestamp: original
+  eq:
+    >function eq(a, b)
+    >  if type(a) ~= type(b) then return false end
+    >  if type(a) == 'table' then
+    >    if #a ~= #b then return false end
+    >    for k, v in pairs(a) do
+    >      if b[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
+    >  end
+    >  return a == b
+    >end
+- __teliva_timestamp: original
+  str:
+    >-- smarter tostring
+    >-- slow; used only for debugging
+    >function str(x)
+    >  if type(x) == 'table' then
+    >    local result = ''
+    >    result = result..#x..'{'
+    >    for k, v in pairs(x) do
+    >      result = result..str(k)..'='..str(v)..', '
+    >    end
+    >    result = result..'}'
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    return '"'..x..'"'
+    >  end
+    >  return tostring(x)
+    >end
+- __teliva_timestamp: original
   map:
     >-- only for arrays
     >function map(l, f)
@@ -147,6 +215,114 @@
     >  return result
     >end
 - __teliva_timestamp: original
+  sort_letters:
+    >function sort_letters(s)
+    >  tmp = {}
+    >  for i=1,#s do
+    >    table.insert(tmp, s[i])
+    >  end
+    >  table.sort(tmp)
+    >  local result = ''
+    >  for _, c in pairs(tmp) do
+    >    result = result..c
+    >  end
+    >  return result
+    >end
+    >
+    >function test_sort_letters(s)
+    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')
+    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')
+    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')
+    >end
+- __teliva_timestamp: original
+  count_letters:
+    >function count_letters(s)
+    >  local result = {}
+    >  for i=1,s:len() do
+    >    local c = s[i]
+    >    if result[c] == nil then
+    >      result[c] = 1
+    >    else
+    >      result[c] = result[c] + 1
+    >    end
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  append:
+    >-- concatenate list 'elems' into 'l', modifying 'l' in the process
+    >function append(l, elems)
+    >  for i=1,#elems do
+    >    table.insert(l, elems[i])
+    >  end
+    >end
+- __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
+  menu:
+    >-- To show app-specific hotkeys in the menu bar, add hotkey/command
+    >-- arrays of strings to the menu array.
+    >menu = {}
+- __teliva_timestamp: original
   Window:
     >Window = curses.stdscr()
 - __teliva_timestamp: original
diff --git a/template.tlv b/template.tlv
index bab9aa3..49d35ed 100644
--- a/template.tlv
+++ b/template.tlv
@@ -120,8 +120,13 @@
     >      if b[k] ~= v then
     >        return false
     >      end
-    >      return true
     >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
     >  end
     >  return a == b
     >end
@@ -252,6 +257,67 @@
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
diff --git a/toot-toot.tlv b/toot-toot.tlv
index 08fce32..0590202 100644
--- a/toot-toot.tlv
+++ b/toot-toot.tlv
@@ -81,6 +81,21 @@
     >  window:mvaddstr(oldy, oldx, '')
     >end
 - __teliva_timestamp: original
+  check:
+    >function check(x, msg)
+    >  if x then
+    >    Window:addch('.')
+    >  else
+    >    print('F - '..msg)
+    >    print('  '..str(x)..' is false/nil')
+    >    teliva_num_test_failures = teliva_num_test_failures + 1
+    >    -- overlay first test failure on editors
+    >    if teliva_first_failure == nil then
+    >      teliva_first_failure = msg
+    >    end
+    >  end
+    >end
+- __teliva_timestamp: original
   check_eq:
     >function check_eq(x, expected, msg)
     >  if eq(x, expected) then
@@ -105,8 +120,13 @@
     >      if b[k] ~= v then
     >        return false
     >      end
-    >      return true
     >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
     >  end
     >  return a == b
     >end
@@ -195,6 +215,109 @@
     >  return result
     >end
 - __teliva_timestamp: original
+  sort_letters:
+    >function sort_letters(s)
+    >  tmp = {}
+    >  for i=1,#s do
+    >    table.insert(tmp, s[i])
+    >  end
+    >  table.sort(tmp)
+    >  local result = ''
+    >  for _, c in pairs(tmp) do
+    >    result = result..c
+    >  end
+    >  return result
+    >end
+    >
+    >function test_sort_letters(s)
+    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')
+    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')
+    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')
+    >end
+- __teliva_timestamp: original
+  count_letters:
+    >function count_letters(s)
+    >  local result = {}
+    >  for i=1,s:len() do
+    >    local c = s[i]
+    >    if result[c] == nil then
+    >      result[c] = 1
+    >    else
+    >      result[c] = result[c] + 1
+    >    end
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  append:
+    >-- concatenate list 'elems' into 'l', modifying 'l' in the process
+    >function append(l, elems)
+    >  for i=1,#elems do
+    >    table.insert(l, elems[i])
+    >  end
+    >end
+- __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
diff --git a/zet.tlv b/zet.tlv
index 2d7387b..3864f51 100644
--- a/zet.tlv
+++ b/zet.tlv
@@ -120,8 +120,13 @@
     >      if b[k] ~= v then
     >        return false
     >      end
-    >      return true
     >    end
+    >    for k, v in pairs(b) do
+    >      if a[k] ~= v then
+    >        return false
+    >      end
+    >    end
+    >    return true
     >  end
     >  return a == b
     >end
@@ -252,6 +257,67 @@
     >  end
     >end
 - __teliva_timestamp: original
+  all_but:
+    >function all_but(x, idx)
+    >  if type(x) == 'table' then
+    >    local result = {}
+    >    for i, elem in ipairs(x) do
+    >      if i ~= idx then
+    >        table.insert(result,elem)
+    >      end
+    >    end
+    >    return result
+    >  elseif type(x) == 'string' then
+    >    if idx < 1 then return x:sub(1) end
+    >    return x:sub(1, idx-1) .. x:sub(idx+1)
+    >  else
+    >    error('all_but: unsupported type '..type(x))
+    >  end
+    >end
+    >
+    >function test_all_but()
+    >  check_eq(all_but('', 0), '', 'all_but: empty')
+    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
+    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
+    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
+    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
+    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
+    >end
+- __teliva_timestamp: original
+  set:
+    >function set(l)
+    >  local result = {}
+    >  for i, elem in ipairs(l) do
+    >    result[elem] = true
+    >  end
+    >  return result
+    >end
+- __teliva_timestamp: original
+  set_eq:
+    >function set_eq(l1, l2)
+    >  return eq(set(l1), set(l2))
+    >end
+    >
+    >function test_set_eq()
+    >  check(set_eq({1}, {1}), 'set_eq: identical')
+    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
+    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
+    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
+    >end
+- __teliva_timestamp: original
+  clear:
+    >function clear(lines)
+    >  while #lines > 0 do
+    >    table.remove(lines)
+    >  end
+    >end
+- __teliva_timestamp: original
+  zap:
+    >function zap(target, src)
+    >  clear(target)
+    >  append(target, src)
+    >end
+- __teliva_timestamp: original
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.