1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
|
-- requires penlight
local pl = _G['penlight'] or _G['pl'] -- penlight for this namespace is pl
local bind = bind or pl.func.bind
-- some bonus string operations, % text operator, and functional programmng
pl.stringx.import()
pl.text.format_operator()
pl.utils.import('pl.func')
function help_wrt(s1, s2) -- helpful printing, makes it easy to debug, s1 is object, s2 is note
local wrt = wrt or texio.write_nl
local wrt = wrt or print
s2 = s2 or ''
wrt('\nvvvvv '..s2..'\n')
if type(s1) == 'table' then
wrt(pl.pretty.write(s1))
else
wrt(tostring(s1))
end
wrt('\n^^^^^\n')
end
function prt_array2d(t)
for _, r in ipairs(t) do
local s = ''
for _, v in ipairs(r) do
s = s.. tostring(v)..', '
end
print(s)
end
end
-- -- -- -- --
-- -- -- -- functions below are helpers for arrays and 2d
local function compare_elements(a, b, op, ele)
op = op or pl.oper.gt
ele = ele or 1
return op(a[ele], b[ele])
end
local function comp_2ele_func(op, ele) -- make a 2 element comparison function,
--sort with function on element nnum
return bind(compare_elements, _1, _2, op, ele)
end
-- -- -- --
local function check_index(ij, rc) -- converts array index to positive value if negative
if type(ij) ~= 'number' then
return 1
else
if ij < 0 then
ij = rc + ij + 1
elseif ij > rc then
ij = rc
end
return ij
end
end
local function check_slice(M, i1, j1, i2, j2) -- ensure a slice is valid; i.e. all positive numbers
r, c = pl.array2d.size(M)
i1 = check_index(i1, r)
i2 = check_index(i2, r)
j1 = check_index(j1, c)
j2 = check_index(j2, c)
return i1, j1, i2, j2
end
local function check_func(func) -- check if a function is a PE, if so, make it a function
if type(func) ~= 'function' then
__func = I(func)
end
return __func
end
-- -- -- -- -- -- --
-- -- -- -- functions below extend the array2d module
function pl.array2d.map_slice1(func, L, i1, i2) -- map a function to a slice of an array, can use PlcExpr
i2 = i2 or i1
local len = #L
i1 = check_index(i1, len)
i2 = check_index(i2, len)
func = check_func(func)
for i in pl.seq.range(i1,i2) do
L[i] = func(L[i])
end
return L
end
function pl.array2d.map_slice2(func, M, i1, j1, i2, j2) -- map a function to a slice of a Matrix
i1, j1, i2, j2 = check_slice(M, i1, j1, i2, j2)
--for i,j in array2d.iter(M, true, i1, j1, i2, j2) do --todo this did not work, penlight may have fixed this
func = check_func(func)
for i in pl.seq.range(i1,i2) do
for j in pl.seq.range(j1,j2) do
M[i][j] = func(M[i][j])
end
end
return M
end
function pl.array2d.map_columns(func, M, j1, j2) -- map function to columns of matrix
j2 = j2 or j1
return map_slice2(func, M, 1, j1, -1, j2)
end
function pl.array2d.map_rows(func, M, i1, i2) -- map function to rows of matrix
i2 = i2 or i1
return map_slice2(func, M, i1, 1, i2, -1)
end
-- -- -- -- -- -- -- --
function pl.array2d.sortOP(M, op, ele) -- sort a 2d array based on operator criteria, ele is column, ie sort on which element
M_new = {}
for row in pl.seq.sort(M, comp_2ele_func(op, ele)) do
M_new[#M_new+1] = row
end
return M_new
end
function pl.array2d.like(M1, v)
v = v or 0
r, c = pl.array2d.size(M1)
return pl.array2d.new(r,c,v)
end
function pl.array2d.from_table(t) -- turns a labelled table to a 2d, label-free array
t_new = {}
for k, v in pairs(t) do
if type(v) == 'table' then
t_new_row = {k}
for _, v_ in ipairs(v) do
t_new_row[#t_new_row+1] = v_
end
t_new[#t_new+1] = t_new_row
else
t_new[#t_new+1] = {k, v}
end
end
return t_new
end
function pl.array2d.toTeX(M, EL) --puts & between columns, can choose to end line with \\ if EL is true (end-line)
EL = EL or false
if EL then EL = '\\\\' else EL = '' end
return pl.array2d.reduce2(_1..EL.._2, _1..'&'.._2, M)..EL
end
-- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- functions below extend the operator module
function pl.operator.strgt(a,b) return tostring(a) > tostring(b) end
function pl.operator.strlt(a,b) return tostring(a) < tostring(b) end
-- -- -- -- string stuff
local lpeg = require"lpeg"
local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
local number = P{"number",
number = (V"int" * V"frac"^-1 * V"exp"^-1) / tonumber,
int = V"sign"^-1 * (R"19" * V"digits" + V"digit"),
digits = V"digit" * V"digits" + V"digit",
digit = R"09",
sign = S"+-",
frac = P"." * V"digits",
exp = S"eE" * V"sign"^-1 * V"digits",
}
local mt = getmetatable("") -- register functions with str
function mt.__index.gnum(s)
return number:match(s)
end
function mt.__index.gextract(s, pat) --extract a pattern from string, returns both
local s_extr = ''
local s_rem = s
for e in s:gmatch(pat) do
s_extr = s_extr..e
s_rem = s_rem:gsub(e,'')
end
return s_extr, s_rem
end
function mt.__index.gfirst(s, t) -- get the first pattern found from a table of pattern
for _, pat in pairs(t) do
if string.find(s, pat) then
return pat
end
end
end
function mt.__index.appif(S, W, B, O) --append W ord to S tring if B oolean true, otherwise O ther
--append Word to String
if B then --if b is true
S = S .. W
else --consider Other word
O = O or ''
S = S .. O
end
return S
end
-- -- -- -- -- math stuffs
function mod(a, b) -- math modulo, return remainder only
return a - (math.floor(a/b)*b)
end
function mod2(a) -- math modulo 2
return mod(a,2)
end
function hasval(x) -- if something has value
if (type(x) == 'function') then
return true
elseif (x == nil) or (x == false) or (x == 0) or (x == '') then
return false
elseif (type(x) ~= 'boolean') and (type(x) ~= 'number') and (type(x) ~= 'string') then
if #x == 0 then
return false
else
return true
end
end
return true
end
-- Some simple and helpful LaTeX functions
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--Generic LuaLaTeX utilities for print commands or environments
-- http://lua-users.org/wiki/SplitJoin -- todo read me!!
__SKIP_TEX__ = __SKIP_TEX__ or false --if declared true before here, it will use regular print functions
-- (for troubleshooting with texlua)
-- xparse defaults
_xTrue = '\\BooleanTrue '
_xFalse = '\\BooleanFalse '
_xNoValue = '-NoValue-'
if not __SKIP_TEX__ then
prt = tex.sprint --can print lists, but will NOT put new line between them
prtn = tex.print --can print lists and will put new line. C-function not in Lua. think P rint R return
wrt = texio.write
wrtn = texio.write_nl
else
prt = io.write
prtn = print --print with new line
wrt = io.write
wrtn = io.write_nl
end
function prtl(str) -- prints a literal string to latex, adds new line between them
for line in str:gmatch"[^\n]*" do -- gets all characters up to a new line
prtn(line)
end
end
_NumBkts = 0
function reset_bkt_cnt(n)
n = n or 0
_NumBkts = n
end
function add_bkt_cnt(n)
-- add open bracket n times, returns brackets
n = n or 1
_NumBkts = _NumBkts + n
return ('{'):rep(n)
end
function close_bkt_cnt()
local s = ('}'):rep(_NumBkts)
reset_bkt_cnt()
return s
end
--definition helpers -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- todo add and improve this
local function defcmd_nest(cs) -- for option if you'd like your commands under a parent ex. \csparent{var}
tex.print('\\gdef\\'..cs..'#1{\\csname '..var..'--#1--\\endcsname}')
end
local function defcmd(cs, val, nargs)
if (nargs == nil) or (args == 0) then
token.set_macro(cs, tostring(val), 'global')
else
local args = '#1'
tex.print('\\gdef\\'..cs..args..'{'..val..'}')
-- todo https://tex.stackexchange.com/questions/57551/create-a-capitalized-macro-token-using-csname
-- \expandafter\gdef\csname Two\endcsname#1#2{1:#1, two:#2} --todo do it like this
end
end
local function prvcmd(cs, val) -- provide command via lua
if token.is_defined(cs) then
tex.print('\\PackageWarning{YAMLvars}{Variable '..cs..' already defined, could not declare}{}')
else
defcmd(cs, val)
end
end
local function newcmd(cs, val) -- provide command via lua
if token.is_defined(cs) then
tex.print('\\PackageError{luadefs}{Command '..cs..' already defined}{}')
else
defcmd(cs, val)
end
end
local function renewcmd(cs, val) -- provide command via lua
if token.is_defined(cs) then
defcmd(cs, val)
else
tex.print('\\PackageError{luadefs}{Command '..cs..' already defined}{}')
end
end
local function deccmd(cs, def)
if def == nil then
prvcmd(cs, '\\PackageError{luadefs}{Command "'..cs..'" was declared and used but, not set}{}')
else
prvcmd(cs, def)
end
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|