summaryrefslogtreecommitdiff
path: root/Data/Libraries/Penlight/tests/test-__vector.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Data/Libraries/Penlight/tests/test-__vector.lua')
-rw-r--r--Data/Libraries/Penlight/tests/test-__vector.lua123
1 files changed, 123 insertions, 0 deletions
diff --git a/Data/Libraries/Penlight/tests/test-__vector.lua b/Data/Libraries/Penlight/tests/test-__vector.lua
new file mode 100644
index 0000000..e040564
--- /dev/null
+++ b/Data/Libraries/Penlight/tests/test-__vector.lua
@@ -0,0 +1,123 @@
+---- deriving specialized classes from List
+-- illustrating covariance of List methods
+local test = require 'pl.test'
+local class = require 'pl.class'
+local types = require 'pl.types'
+local operator = require 'pl.operator'
+local List = require 'pl.List'
+
+local asserteq = test.asserteq
+
+class.Vector(List)
+
+
+function Vector.range (x1,x2,delta)
+ return Vector(List.range(x1,x2,delta))
+end
+
+local function vbinop (op,v1,v2,scalar)
+ if not Vector:class_of(v1) then
+ v2, v1 = v1, v2
+ end
+ if type(v2) ~= 'table' then
+ return v1:map(op,v2)
+ else
+ if scalar then error("operation not permitted on two vectors",3) end
+ if #v1 ~= #v2 then error("vectors have different lengths",3) end
+ return v1:map2(op,v2)
+ end
+end
+
+function Vector.__add (v1,v2)
+ return vbinop(operator.add,v1,v2)
+end
+
+function Vector.__sub (v1,v2)
+ return vbinop(operator.sub,v1,v2)
+end
+
+function Vector.__mul (v1,v2)
+ return vbinop(operator.mul,v1,v2,true)
+end
+
+function Vector.__div (v1,v2)
+ return vbinop(operator.div,v1,v2,true)
+end
+
+function Vector.__unm (v)
+ return v:map(operator.unm)
+end
+
+Vector:catch(List.default_map_with(math))
+
+v = Vector()
+
+assert(v:is_a(Vector))
+assert(Vector:class_of(v))
+
+v:append(10)
+v:append(20)
+asserteq(1+v,v+1)
+
+-- covariance: the inherited Vector.map returns a Vector
+asserteq(List{1,2} + v:map '2*_',{21,42})
+
+u = Vector{1,2}
+
+asserteq(v + u,{11,22})
+asserteq(v - u,{9,18})
+asserteq (v - 1, {9,19})
+asserteq(2 * v, {20,40})
+-- print(v * v) -- throws error: not permitted
+-- print(v + Vector{1,2,3}) -- throws error: different lengths
+asserteq(2*v + u, {21,42})
+asserteq(-v, {-10,-20})
+
+-- Vector.slice returns the Right Thing due to covariance
+asserteq(
+ Vector.range(0,1,0.1):slice(1,3)+1,
+ {1,1.1,1.2},
+ 1e-8)
+
+u:transform '_+1'
+asserteq(u,{2,3})
+
+u = Vector.range(0,1,0.1)
+asserteq(
+ u:map(math.sin),
+ {0,0.0998,0.1986,0.2955,0.3894,0.4794,0.5646,0.6442,0.7173,0.7833,0.8414},
+0.001)
+
+-- unknown Vector methods are assumed to be math.* functions
+asserteq(Vector{-1,2,3,-4}:abs(),Vector.range(1,4))
+
+local R = Vector.range
+
+-- concatenating two Vectors returns another vector (covariance again)
+-- note the operator precedence here...
+asserteq((
+ R(0,1,0.1)..R(1.2,2,0.2)) + 1,
+ {1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.2,2.4,2.6,2.8,3},
+ 1e-8)
+
+
+class.Strings(List)
+
+Strings:catch(List.default_map_with(string))
+
+ls = Strings{'one','two','three'}
+asserteq(ls:upper(),{'ONE','TWO','THREE'})
+asserteq(ls:sub(1,2),{'on','tw','th'})
+
+-- all map operations on specialized lists
+-- results in another list of that type! This isn't necessarily
+-- what you want.
+local sizes = ls:map '#'
+asserteq(sizes, {3,3,5})
+asserteq(types.type(sizes),'Strings')
+asserteq(sizes:is_a(Strings),true)
+sizes = Vector:cast(sizes)
+asserteq(types.type(sizes),'Vector')
+asserteq(sizes+1,{4,4,6})
+
+