Standard library
err · graph · io · mat · math · queue · search · sort · stack · vector
err.intlang
-- basic Result Type Simulation
-- main downsides of doing this library side are:
-- 1. to return an error one still needs a value of the type that is returned when computation
-- runs normally
-- 2. No enforcement, that the value is checked with ok before its unwrapped
-- 3. No nice shorthands like ? in Rust
-- 4. Problems with language futures can not cleanly make use of the error system
-- the main upside is that its 4 lines instead of 100's
let res = \v. (0,v)
let err = \v. (1,v)
let ok = \res. let (tag, value) = res in tag == 0
let uwrp = \res. let (tag, value) = res in value
graph.intlang
include math
include vector
include search
include sort
--make empty assoc vector of size n
let genGraph = \n. vecmk[-1,n,0]
-- if g has edge from v to u return 1 else 0, UNSAFE, does not check if v or u are a valid vertices
let _checkEdgeUnsave = \g v u. if (search.linearsearch_i32 vecget[g,v] u) == -1 then 0 else 1 end
-- if g has edge from v to u return 1 else 0
let checkEdge = \g v u.
let validverts = math.inbounds 0 (veclen[g]) in
if (validverts v) & (validverts u) then
_checkEdgeUnsave g v u
else
0
end
-- adds edge from v to u
let addEdge = \g v u.
let validverts = math.inbounds 0 (veclen[g]) in
if (validverts v) & (validverts u) then
if _checkEdgeUnsave g v u then
g
else
let oldneighbors = vecget[g,v] in
let m1idx = search.linearsearch_i32 oldneighbors (-1) in
let newneighbors =
if m1idx == -1 then
vecextend[oldneighbors, u, 1]
else
vecset[oldneighbors, u, m1idx]
end
in
vecset[g, newneighbors, v]
end
else
g
end
-- removes edge from v to u
let removeEdge = \g v u.
let validverts = math.inbounds 0 (veclen[g]) in
if (validverts v) & (validverts u) then
if _checkEdgeUnsave g v u then
let oldneighbors = vecget[g,v] in
let uidx = search.linearsearch_i32 oldneighbors u in
let newneighbors = vecset[oldneighbors, -1, uidx] in
vecset[g, newneighbors, v]
else
g
end
else
g
end
-- dfs:
-- assumes that all the entries in the graph and start are in [0,n-1] where n is veclen[g]
-- and that ord only permutes
-- ord is used to define the traversal order of neighbors
-- g is the graph
-- start is the starting vertex
-- returned the discover time, finish time, and parent vectors
let dfs = \ (ord : [i32] -> [i32]) (g : [[i32]]) (start : i32) => [i32]*[i32]*[i32].
let n = veclen[g] in
let rec dfsaux = \p acc v.
let (t, disct, fint, par) = acc in
if vecget[disct, v] >= 0 then
--already visited case
(t, disct, fint, par)
else
-- not yet visited
let ndisct = vecset[disct, t, v] in
let npar = vecset[par, p, v] in
let nacc = (t+1, ndisct, fint, npar) in
let neigh_unord = vecget[g, v] in
let neigh = ord neigh_unord in
let nnacc = vector.left_fold (\acc vloc. if vloc >= 0 then dfsaux v acc vloc else acc end) nacc neigh in
let (t2, disct2, fint2, par2) = nnacc in
let nfint = vecset[fint2, t2, v] in
(t2+1, disct2, nfint, par2)
end
in
let (_, disct, fint, par) = dfsaux (-1) (0, vecmk[-1,n], vecmk[-1, n], vecmk[-1, n]) start in
(disct, fint, par)
let dfs_canonicalord = dfs (sort.bubblesort_i32)
let dfs_defaultord = dfs (\x. x)
-- More ideas:
-- BFS, shortest path and bipartite check
-- Topsort, SCC, cycle detection with DFS
io.intlang
include err
-- READING
let read_str = \ (len : i32) => [i8].
let rec read_str_aux = \ (len : i32) (i : i32) (acc : [i8]) => [i8].
if i < len then
let c = readi8 () in
let next_acc = vecset[acc, c, i] in
read_str_aux len (i+1) next_acc
else
acc
end
in
read_str_aux len 0 (vecmk['\x00', len])
-- \() => [i8]. unit lambda annotation might be nice
let read_ln = \()
let rec read_ln_aux = \ (acc : [i8]) (i : i32) => [i8].
let acc_w_space = if i < veclen[acc] then acc else vecextend[acc, '\x00', veclen[acc]] end in
let c = readi8 () in
if c ==i8 '\n' then
vecslice[acc_w_space, 0, i] --resize to use the valid part of the buffer
else
read_ln_aux vecset[acc_w_space, c, i] (i+1)
end
in
read_ln_aux (vecmk['\x00', 128]) 0
-- WRITING
let write_str = \s : [i8] => unit.
let rec write_str_aux = \ (s : [i8]) (i : i32) => unit.
if i < veclen[s] then
(writei8 vecget[s, i];
write_str_aux s (i+1))
else
()
end
in
write_str_aux s 0
let write_ln = \s : [i8] => unit.
let rec write_ln_aux = \ (s : [i8]) (i : i32) => unit.
if i < veclen[s] then
(writei8 vecget[s, i];
write_ln_aux s (i+1))
else
writei8 '\n'
end
in
write_ln_aux s 0
-- HANDLING I32
let ascii_is_dig = \c : i8 => i32. ('\x30' <=i8 c) & (c <=i8 '\x39')
let ascii_dig_to_i32 = \c : i8 => i32. i8_to_i32 c - 48
let i32_dig_to_ascii = \i : i32 => i8. i32_to_i8 (i + 48)
let str_to_i32 = \s : [i8] => i32 * i32.
let rec strtoi32aux = \ (s : [i8]) (i : i32) (acc : i32) => i32 * i32.
if i < veclen[s] then
let c = vecget[s, i] in
if ascii_is_dig c then
let digit = ascii_dig_to_i32 c in
strtoi32aux s (i+1) (acc * 10 + digit)
else
err.err acc
end
else
err.res acc
end
in
if (0 < veclen[s]) & (vecget[s, 0] ==i8 '-') then
let res = strtoi32aux s 1 0 in
if err.ok res then err.res (-(err.uwrp res)) else err.err 0 end
else
strtoi32aux s 0 0
end
-- one could add error handling (or automatic buffer extension) here but it is a method that
-- should only be called form inside the io lib so I trust that the acc buffer that is passed is long enough
let i32_to_str_buff = \ (i : i32) -- i32 to convert
(idx : i32) -- buffer index to count down from
(acc : [i8]) -- buffer
=> (i32 * [i8]). -- updated buffer index and buffer
let rec _get_digits = \ (i : i32) -- the rest of the i32
(idx : i32) -- index into acc to use for the next char
(acc : [i8]) -- accumulator to make the thing tail recursive
=> i32 * [i8]. -- return the vector and the next usable index
let dig = i32_dig_to_ascii (i % 10) in
let next_i = i / 10 in -- think some weird shift and mutl can do this faster
let next_acc = vecset[acc, dig, idx] in
let next_idx = idx - 1 in
if next_i == 0 then
(next_idx, next_acc)
else
_get_digits next_i next_idx next_acc
end
in
-- range is +2147483647 to -2147483648
-- so we need 10 chars for the digits, on negative numbers 1 for the sign
if i == -2147483648 then --special case where we cant flip, we just "unroll" once and then flip
let acc = vecset[acc, '8', idx] in
let idx = idx - 1 in
let (idx, acc) = _get_digits (214748364) idx acc in
let acc = vecset[acc, '-', idx] in
let idx = idx - 1 in
(idx, acc)
else if 0 <= i then
let (idx, acc) = _get_digits i idx acc in
(idx, acc)
else
let (idx, acc) = _get_digits (-i) idx acc in
let acc = vecset[acc, '-', idx] in
let idx = idx - 1 in
(idx, acc)
end end
let i32_to_str = \i : i32 => [i8].
let (idx, acc) = i32_to_str_buff i 11 (vecmk['\x00', 12]) in
vecslice[acc, idx+1, veclen[acc]-(idx+1)] -- resize to the valid part of the buffer
-- COMMA SEPARATED I32
let csi32str_to_i32vec = \s : [i8] => i32 * [i32].
let n = veclen[s] in
let rec aux = \ (l : i32) (r : i32) (idx : i32) (acc : [i32]) => i32 * [i32].
if r < n then
let c = vecget[s, r] in
if c ==i8 ',' then
let num_str = vecslice[s, l, r-l] in
let num_res = str_to_i32 num_str in
if err.ok num_res then
aux (r+1) (r+1) (idx+1) vecset[acc, err.uwrp num_res, idx]
else
err.err vec[]
end
else
aux l (r+1) idx acc
end
else
let num_str = vecslice[s, l, n-l] in
let num_res = str_to_i32 num_str in
if err.ok num_res then
err.res vecslice[vecset[acc, err.uwrp num_res, idx], 0, idx+1]
else
err.err vec[]
end
end
in
aux 0 0 0 vecmk[0, n/2 + 1] -- there are at most veclen[s]/2 numbers in the string (only 0 to 9 for all numbers)
let i32vec_to_csi32str = \v : [i32] => [i8].
let n = veclen[v] in
let rec aux = \ (i : i32)
(idx : i32)
(acc : [i8])
=> [i8].
if 0 <= i then
let (idx, acc) = i32_to_str_buff (vecget[v, i]) idx acc in
let acc = vecset[acc, ',', idx] in
let idx = idx - 1 in
aux (i-1) idx acc
else
vecslice[acc, idx+2, veclen[acc]-(idx+2)] --also remove the last comma that was added
end
in
if n == 0 then
vecmk['\x00', 0]
else
aux (n-1) (n * 12 - 1) (vecmk['\x00', n * 12]) -- the max string size is 12 characters (10 digits + 1 sign + 1 comma per number)
end
-- Some shorthands for convenience
-- the read short hands do use functions that can return errors
-- but for the sake of simplicity they will return a default value on fail
-- but not 0 or similar since these small numbers might appear in the test sometimes
-- so just some big random number, it is a bit dirty but just makes the code in the test
-- so much more readable. This would be different if the Result Type was builtin
-- and well integrated
let readln_i32 = \() let res = str_to_i32 (read_ln ()) in if err.ok res then err.uwrp res else 987654 end
let readln_i32vec = \() let res = csi32str_to_i32vec (read_ln ()) in if err.ok res then err.uwrp res else vec[987654] end
let writeln_i32 = \i : i32 => unit. write_ln (i32_to_str i)
let write_i32 = \i : i32 => unit. write_str (i32_to_str i)
let writeln_i32vec = \v. write_ln (i32vec_to_csi32str v)
mat.intlang
-- lib for row major matrices
include math
include vector
-- create an identity matrix of size n x n
let matid = \n.
let n = math.abs n in
let rec matidaux = \i A.
let updtA = vecset[A, 1, i*n+i] in
if i < (n-1) then
matidaux (i+1) updtA
else
updtA
end
in
matidaux 0 vecmk[0,n*n]
-- "classic triple loop untiled" matmul
-- A is a nxl mat
-- B is a lxm mat
let matmul = \n l m A B.
-- C is a nxm mat (in here for tail recursion, the initial call must pass it as 0)
-- i,j,k are the loop/recursion vars
-- i is in [0,n]
-- j is in [0,m]
-- k is in [0,l]
let rec matmulaux = \C i j k.
let updtC = vecset[C,
vecget[C, i*m+j] + vecget[A,i*l+k]*vecget[B,k*m+j],
i*m+j
] in
if k < (l-1) then
matmulaux updtC i j (k+1)
else if j < (m-1) then
matmulaux updtC i (j+1) 0
else if i < (n-1) then
matmulaux updtC (i+1) 0 0
else
updtC
end end end
in
if (veclen[A] == n*l)*(veclen[B] == l*m) then
err.res (matmulaux vecmk[0,n*m] 0 0 0)
else
err.err vec[]
end
let matadd = \A B.
let rec mataddaux = \C i.
let updtC = vecset[C,
vecget[A,i] + vecget[B,i],
i
] in
if i < (veclen[A]-1) then
mataddaux updtC (i+1)
else
updtC
end
in
if (veclen[A] == veclen[B]) then
err.res (mataddaux vecmk[0,veclen[A]] 0)
else
err.err vec[]
end
let matsmul = \A s. vector.map (\x. x*s) A
let matsub = \A B. matadd A (matsmul B (-1)) --haha mb I should add negative int literals, haha I did
let sqmattrans = \n A.
let rec sqmattransaux = \A i j.
let tmp = vecget[A, i*n+j] in
let partA = vecset[A, vecget[A,j*n+i], i*n+j] in
let updtA = vecset[partA, tmp, j*n+i] in
if j < (n-1) then
sqmattransaux updtA i (j+1)
else if i < (n-2) then
sqmattransaux updtA (i+1) (i+2)
else
updtA
end end
in
if veclen[A] == n*n then
err.res (sqmattransaux A 0 0)
else
err.err vec[]
end
math.intlang
-- general math functions
-- Note: as of now they are not optimized just written to work :)
-- exp < 1 is just return base
let abstracpow = \mulf. \base. \exp.
let rec apowaux = \acc. \cnt.
if cnt < exp then
apowaux (mulf acc base) (cnt+1)
else
acc
end
in
apowaux base 1
-- here could work for exp = 0 but this way its so elegant, I refuse to change it
let pow = abstracpow (\x. \y. x * y)
let abs = \x. if x < 0 then -x else x end
let min = \a. \b. if a < b then a else b end
let max = \a. \b. if a > b then a else b end
-- sqrt only makes sense for possitive numbers, so the input will be
-- treated as unsigned
let sqrt = \x.
let rec sqrtaux = \guess.
if guess*guess <=u x then
sqrtaux (guess+1)
else
guess-1
end
in
sqrtaux 0
-- one could want a gcd for signed numbers but
-- this would mean more complexity for something not really needed
-- so the gcd is only defined for unsigned numbers
let rec gcd = \a. \b.
if b == 0 then
a
else
gcd b (a %u b)
end
let prime = \n.
let rec isprimeaux = \i.
if i*i <=u n then
if n %u i == 0 then
0
else
isprimeaux (i+1)
end
else
1
end
in
if n < 2 then
0
else
isprimeaux 2
end
let cmp_i32 = \a. \b.
if a < b then
-1
else if a > b then
1
else
0
end end
-- if low <= val < high then 1 else 0
let inbounds = \low high val. ((low <= val) & (val < high))
queue.intlang
-- a queue is (front, back, vec) where
-- the actual elements start at index front and end at index back-1 (all in the mod n group)
include err
include math
let mkQueue = \() (0,0, vec[])
let sizeQueue = \q.
let (front, back, v) = q in
if front <= back then back - front else veclen[v] - front + back end
let isemptyQueue = \q. sizeQueue q == 0
let isfullQueue = \q.
let (_, _, v) = q in
sizeQueue q >= veclen[v]-1 -- >= is for the zero edge case
let extendQueue = \q x.
let (front, back, oldvec) = q in
let oldlen = veclen[oldvec] in
let newvec = vecextend[oldvec, x, math.max oldlen 32] in -- the main reason to pass x and use it here is to keep the polymorphism
let rec aux = \acc i.
if i < back then
aux vecset[acc, vecget[oldvec, i], oldlen + i] (i+1)
else
(front, oldlen + i, acc)
end
in
if front > back then
aux newvec 0
else
(front, back, newvec)
end
let enQueue = \q x.
let (front, back, v) = if isfullQueue q then extendQueue q x else q end in
let newback = (back + 1) % veclen[v] in
(front, newback, vecset[v, x, back])
let deQueue = \q.
let (front, back, v) = q in
if isemptyQueue q then
err.err (vecget[v, 0], q)
else
let elm = vecget[v, front] in
let newfront = (front + 1) % veclen[v] in
err.res (elm, (newfront, back, v))
end
search.intlang
-- searching things in vectors
include math
-- should do as a left fold
let linearsearch = \cmpf arr target.
let rec linearsearchaux = \i.
if i < veclen[arr] then
if cmpf vecget[arr, i] target then
linearsearchaux (i+1) -- cmpf yields -1 or 1
else
i -- cmpf yields 0
end
else
-1
end
in
linearsearchaux 0
let linearsearch_i32 = linearsearch math.cmp_i32
let binarysearch = \cmpf arr target.
let rec binarysearchaux = \left right.
if left < right then
let mid = (left + right) / 2 in
let cmpresult = cmpf vecget[arr, mid] target in
if cmpresult == 0 then
let rec findleft = \i. -- makes the binary search O(n) in the worst case (while one could do the findleft as binary search too, I am simply too lazy to optimize this edge case)
if (0 < i) then
if cmpf vecget[arr, (i-1)] target == 0 then -- no logic shortcutting as of now :(
findleft (i-1)
else
i
end
else
i
end
in findleft mid
else if cmpresult < 0 then
binarysearchaux (mid+1) right
else
binarysearchaux left mid
end end
else
-1
end
in
binarysearchaux 0 veclen[arr]
let binarysearch_i32 = binarysearch math.cmp_i32
sort.intlang
include math
let bubblesort = \cmpf arr.
let len = veclen[arr] in
let rec bubblesortaux = \arr n i.
let arrupdt =
let left = vecget[arr, i] in
let right = vecget[arr, n] in
if cmpf left right < 0 then
arr
else
let temp = right in
let arr1 = vecset[arr, left, n] in
vecset[arr1, temp, i]
end
in
if i < (len-1) then
bubblesortaux arrupdt n (i+1)
else if n < (len-1) then
bubblesortaux arrupdt (n+1) 0
else
arr
end end
in
if len <= 1 then
arr
else
bubblesortaux arr 0 0
end
let bubblesort_i32 = bubblesort math.cmp_i32
stack.intlang
-- TODO
vector.intlang
-- lib for vectors
include err
let map = \f v.
let rec mapaux = \acc i.
if i < veclen[v] then
mapaux vecset[acc, f vecget[v, i], i] (i+1)
else
acc
end
in
if veclen[v] == 0 then
vec[]
else
mapaux vecmk[f vecget[v, 0], veclen[v]] 0
end
let map2 = \f v0 v1.
let rec mapaux = \acc i.
if i < veclen[v0] then
mapaux vecset[acc, f vecget[v0, i] vecget[v1, i], i] (i+1)
else
acc
end
in
if veclen[v0] != veclen[v1] then
err.err vec[]
else if veclen[v0] == 0 then
err.res vec[]
else
err.res (mapaux vecmk[f vecget[v0, 0] vecget[v1, 0], veclen[v0]] 0)
end end
let left_fold = \f acc v.
let rec leftfoldaux = \acc i.
if i < veclen[v] then
leftfoldaux (f acc vecget[v, i]) (i+1)
else
acc
end
in
leftfoldaux acc 0
let left_fold2 = \f acc v0 v1.
let rec leftfoldaux = \acc i.
if i < veclen[v0] then
leftfoldaux (f acc vecget[v0, i] vecget[v1, i]) (i+1)
else
acc
end
in
if veclen[v0] != veclen[v1] then
err.err acc
else
err.res (leftfoldaux acc 0)
end
let right_fold = \f v acc.
let rec rightfoldaux = \acc i.
if 0 <= i then
rightfoldaux (f (vecget[v, i]) acc) (i-1)
else
acc
end
in
rightfoldaux acc (veclen[v]-1)
let iter = \f v.
let rec iteraux = \f v i.
if i < veclen[v] then
(f vecget[v, i];
iteraux f v (i+1))
else
()
end
in
iteraux f v 0
let concat = \v1 v2.
let rec concataux = \acc i.
if i < veclen[v2] then
concataux (vecset[acc, vecget[v2, i], i + veclen[v1]]) (i+1)
else
acc
end
in
if veclen[v1] == 0 then
v2
else if veclen[v2] == 0 then
v1
else
concataux (vecextend[v1, vecget[v2, 0], veclen[v2]]) 0
end end
let rev = \v.
let rec revaux = \ vrev (i : i32) (irev :i32) .
if 0 <= irev then
revaux vecset[vrev, vecget[v,i], irev] (i+1) (irev-1)
else
vrev
end in
if veclen[v] <= 1 then
v
else
revaux vecmk[vecget[v,0], veclen[v]] 0 (veclen[v]-1)
end
let cmp = \cmpelm v1 v2.
let n1 = veclen[v1] in
let n2 = veclen[v2] in
let rec cmpaux = \i.
if i < n1 then
let elcmp_val = cmpelm vecget[v1, i] vecget[v2, i] in
if elcmp_val == 0 then
cmpaux (i+1)
else
elcmp_val
end
else
0
end
in
if n1 < n2 then
-1
else if n1 > n2 then
1
else
cmpaux 0
end end