write('Testing tables ...\n\n');
t := time();

a := [];
b := [];

for i from -1k to 1k do
   insert i into a;
   insert i into b
od;

c := 0;
for i to 10 do
   inc c;
   if a <> b then
      print(tostring(c) & '>1|', '<>')
   fi;
   if not(a = b) then
      print(tostring(c) & '>2|', '=')
   fi;
   if not(a == b) then
      print(tostring(c) & '>3|', '==')
   fi;
   insert 'a' into a;
   if a = b then
      print(tostring(c) & '>4|', '<>')
   fi;
   if a = b then
      print(tostring(c) & '>5|', '=')
   fi;
   if a == b then
      print(tostring(c) & '>6|', '==')
   fi;
   delete 'a' from a;
   d := copy(a);
   if a <> d then
      print(tostring(c) & '>7|', '<>')
   fi;
   if not(a = d) then
      print(tostring(c) & '>8|', 'not/=')
   fi;
   if not(a == d) then
      print(tostring(c) & '>9|', 'not/==')
   fi;
   if size(a) <> size(d) then
      print(tostring(c), 'size')
   fi;
   if not(filled(a)) then
      print(tostring(c), 'filled')
   fi;
   if 'a' in a then
      print(tostring(c), 'in')
   fi;
   if not('a' notin a) then
      print(tostring(c), 'notin')
   fi;
   if not(0 in a) then
      print(tostring(c), 'in')
   fi;
   if 0 notin a then
      print(tostring(c), 'in')
   fi;
   if [1, 2] in [[1, 2]] <> true then
      print(tostring(c), 'in')
   fi;
   if [1, 2] in [] <> false then
      print(tostring(c), 'in')
   fi;
   if 1:2 in [1:2] <> true then
      print(tostring(c), 'in')
   fi;
   if 1:2 in [] <> false then
      print(tostring(c), 'in')
   fi;
   if seq(1, 2) in [seq(1, 2)] <> true then
      print(tostring(c), 'in')
   fi;
   if seq(1, 2) in [] <> false then
      print(tostring(c), 'in')
   fi;
   if reg(1, 2) in [reg(1, 2)] <> true then
      print(tostring(c), 'in')
   fi;
   if reg(1, 2) in [] <> false then
      print(tostring(c), 'in')
   fi;
   if not([1, 2] notin [[1, 2]]) <> true then
      print(tostring(c), 'notin')
   fi;
   if not([1, 2] notin []) <> false then
      print(tostring(c), 'notin')
   fi;
   if not(1:2 notin [1:2]) <> true then
      print(tostring(c), 'notin')
   fi;
   if not(1:2 notin []) <> false then
      print(tostring(c), 'notin')
   fi;
   if not(seq(1, 2) notin [seq(1, 2)]) <> true then
      print(tostring(c), 'notin')
   fi;
   if not(seq(1, 2) notin []) <> false then
      print(tostring(c), 'notin')
   fi;
   if not(reg(1, 2) notin [reg(1, 2)]) <> true then
      print(tostring(c), 'notin')
   fi;
   if not(reg(1, 2) notin []) <> false then
      print(tostring(c), 'notin')
   fi;
   if a intersect b <> a then
      print(tostring(c), 'intersect')
   fi;
   if a minus b <> [] then
      print(tostring(c), 'minus')
   fi;
   insert 'a' into b;
   if not(a subset b) then
      print(tostring(c), 'subset')
   fi;
   if not(a xsubset b) then
      print(tostring(c), 'xsubset')
   fi;
   delete 'a' from b;
   if bintersect(a, b) <> a then
      print(tostring(c), 'bintersect')
   fi;
   if bminus(a, b) <> [] then
      print(tostring(c), 'bminus')
   fi;
   if not(bisequal(a, b)) then
      print(tostring(c), 'bminus')
   fi;
   d := copy(a);
   insert 0 into d;
   if duplicates(d) <> [0] then
      print(tostring(c), 'duplicates')
   fi;
   clear d;
   d := tables.entries(a);
   if a <> d then
      print(tostring(c), 'tables.entries')
   fi;
   if tables.indices(a) <> tables.indices(b) then
      print(tostring(c), 'tables.indices')
   fi;
   if a <> unique(d) then
      print(tostring(c), 'unique')
   fi;
   if getentry(a, 1) <> -1k then
      print(tostring(c), 'getentry')
   fi;
   if getentry(a, -1) <> null then
      print(tostring(c), 'getentry')
   fi;
   e := []; f := ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
   tables.allocate(e, 1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 6, 'f', 7, 'g', 8, 'h');
   if not(e == f) then
      print(tostring(c), 'tables.allocate')
   fi;
   a1 := []; b1 := []; c1 := [];
   for i from 1 to 100 do
      insert i into a1;
      insert i+100 into b1;
   od;
   for i from 1 to 200 do
      insert i into c1
   od;
   if a1 union b1 <> c1 then
      print(tostring(c), 'union')
   fi;
   str := 'if tables.indices(a) <> tables.indices(b) then';
   if join(totable(str)) <> str then
      print(tostring(c), 'join, 1')
   fi;
   if join(['', ''], ',') <> ',' then
      print(tostring(c), 'join, 2')
   fi;
   if join([], ',') <> '' then
      print(tostring(c), 'join, 3')
   fi;
   d := [];
   for i to 1000 do insert i into d od;
   if qsadd(d) <> 333833500 then
      print(tostring(c), 'qsadd')
   fi;
   if sadd(d) <> 500500 then
      print(tostring(c), 'sadd')
   fi;
   clear d;
   d := [];
   for i from -1k to 1k do insert i^2 into d od;
   if sadd(d) <> sadd(map(<< x -> x^2 >>, a)) then
      print(tostring(c), 'sadd')
   fi;
   clear d;
   b2 := [3, 6, 'a' ~ -2];
   if map(<< (x) ->  x^2 >>, b2) <> [9, 36, 4] then
      print(tostring(c), 'map')
   fi;
   if reduce(<< item, accumulator -> item + accumulator >>, [1, 2, 3, 4], 0) <> 10 then
      print(tostring(c), 'reduce')
   fi;
   a2 := [1, 2];
   b2 := [1, 2];
   if zip(<< (x, y) ->  x + y >>, a2, b2) <> [2, 4] then
      print(tostring(c), 'zip')
   fi
   a3 := [];
   b3 := [];
   for i to 1000 do
      insert i into a3;
      if i > 500 then
         insert i into b3
      fi
   od;
   if select(<< x -> x > 500 >>, a3) <> b3 then
      print(tostring(c), 'select, 1')
   fi;
   c3 := totable(nseq(2, 1000, 2));  # table of even numbers
   if select( << x -> even(x) >>, a3, newarray=true) <> c3 then
      print(tostring(c), 'select, 2')
   fi;
   c4 := totable(nseq(1, 1000, 2));
   if remove( << x -> even(x) >>, a3) <> c4 then
      print(tostring(c), 'remove, 1')
   fi;
   if remove( << x -> even(x) >>, a3, newarray=true) <> c4 then
      print(tostring(c), 'remove, 2')
   fi;
   if remove( << x, y -> even(x-y) >>, a3, 1, newarray=true) <> c3 then
      print(tostring(c), 'remove, 3')
   fi;
   p, q := selectremove(<< x -> even(x) >>, a3);
   if p <> c3 then
      print(tostring(c), 'selectremove, 1')
   fi;
   if q <> c4 then
      print(tostring(c), 'selectremove, 2')
   fi;
   p, q := selectremove(<< x -> even(x) >>, a3, newarray=true);
   if p <> c3 then
      print(tostring(c), 'selectremove, 3')
   fi;
   if q <> c4 then
      print(tostring(c), 'selectremove, 4')
   fi;
   if countitems(<< x -> x > 500 >>, a3) <> 500 then
      print(tostring(c), 'countitems')
   fi;
   a4 := ['a', 3~'a', 1000, -1 ~ 'a'];
   if whereis(a4, 'a') <> [1, 3, -1] then
      print(tostring(c), 'whereis')
   fi;
   a5 := [];
   insert a5 into a5
   if environ.isselfref(a5) <> true then
      print(tostring(c), 'environ.isselfref')
   fi
   if smul([1.0, 1.1, 1.2, 1.3, 1.4, 1.5]) <> 3.6036 then
      print(tostring(c), 'smul')
   fi;
   a5 := [];
   a5[1] := 0; a5[2] := 0; a5[4] := 0;
   aa, bb, cc := tables.getsize(a5, true);
   if aa <> 4 or bb <> true or cc <> false then
      print(tostring(c), 'tables.getsize')
   fi
od;


a := [];
a.index := seq();
for i to 1k do
   insert i into a.index
od;

if size a.index <> 1k or unique(map(<< x -> type(x) >>, a.index)) <> seq(number) then
   inc c;
   print(tostring(c), 'insertion into a table using field names')
fi

a := tables.dimension(1:10, 1:10, 0);
for i to 1k do
   inc a[2, 2];
   inc a[2, 2], 3;
   dec a[2, 2];
   dec a[2, 2], 3
od;
if a[2, 2] <> 0 then
   print(tostring(c), 'inc and dec with a table using 2 indices')
fi;

f := proc() is
   local a := tables.dimension(1:10, 1:10, 0);
   for i to 100k do
      inc a[2, 2];
      inc a[2, 2], 3;
      dec a[2, 2];
      dec a[2, 2], 3
   od;
   return a;
end;
if f()[2, 2] <> 0 then
   print(tostring(c), 'inc and dec within proc with a table using 2 indices')
fi;

a := ['a', 'b', 'c', 'd'];
for i to 10k do
   c1 := join(a);
   c2 := join(a, '+', 2, 3);
od;
if c1 <> 'abcd' then print('error in join 1') fi;
if c2 <> 'b+c' then print('error in join 2') fi;

if join([strings.repeat('a', 512), strings.repeat('b', 512)]) <>
  strings.repeat('a', 512) & strings.repeat('b', 512) then
  print('error in join 3')
fi;

# pop/insert/delete with tables
a := [unpack(nseq(1, 1024))];
a['hello'] := 'world';
s := size(a)-1;

for i to s/2 do
   pop top from a;
   if top(a) <> s-i then print('error in top', top(a), s-i) fi;
   pop bottom from a;
   if i <> s/2 and bottom(a) <> i+1 then print('error in bottom', bottom(a), i+1) fi;
od;

if a <> ['hello'~'world'] then print('error in pop with tables') fi;
if not(top(a) = null and bottom(a) = null) then print('error in bottom/top with tables') fi;

a := [];

for i to 16 do
   insert i into a;
   delete i from a
od;

if filled(a) then print('Error in tables/delete') fi;

for i to 16 do
   insert i into a;
   pop top from a;
   pop bottom from a
od;

if filled(a) then print('Error in tables/pop') fi;


a := [10, 20, 30, 40, 50];
for i to 16 do
   c := a[2 to 3]
od;

if c <> [20, 30] then print('error in new sublists/tables, 1') fi;

for i to 16 do
   local c1 := a[2 to 3]
   c := c1
od;

if c <> [20, 30] then print('error in new sublists/tables, 2') fi;

f := proc(x, a, b) is
   return x[a to b]
end;

for i to 1k do
   local c1 := f(a, 2, 3)
   c := c1
od;

if c <> [20, 30] then print('error in new sublists/tables, 3') fi;

a := [10, 20, 30, 40, 50, 'alex'~'walz', -1~-10, 0~0];

for i to 16 do
   if a[1 to 2] <> [1 ~ 10, 2 ~ 20] then print('error in new sublists/tables, 4') fi;
od;

for i to 16 do
   if a[-1 to 2] <> [-1 ~ -10, 0 ~ 0, 1 ~ 10, 2 ~ 20] then print('error in new sublists/tables, 5') fi;
od;

f := proc(a, b, c) is
   return a[b to c]
end;

for i to 16 do
   if f(a, -1, 2) <> [-1 ~ -10, 0 ~ 0, 1 ~ 10, 2 ~ 20] then print('error in new sublists/tables, 6') fi;
od;

f := proc(a, b) is
   return a[b[1] to b[2]]
end;

for i to 16 do
   if f(a, [-1, 2]) <> [-1 ~ -10, 0 ~ 0, 1 ~ 10, 2 ~ 20] then print('error in new sublists/tables, 6') fi;
od;

a := ['a', 'b', 'c'];
for i to 16 do
   c := values(a, 1, 2, 3)
od;
if a <> c then error('error in values/table') fi;

# tuned size op, 1.3.3
a := [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9];
a.alex := true;
if size a <> 37 then print('error in size, 1') fi;
delete 9 from a
if size a <> 33 then print('error in size, 2') fi;
pop bottom from a
if size a <> 32 then print('error in size, 3') fi;
pop top from a
if size a <> 31 then print('error in size, 4') fi;
a.alex := null;
if size a <> 30 then print('error in size, 5') fi;
clear a;

create table a;
a.alex := true;
if size a <> 1 then print('error in size, 6') fi;
a.beate := true
if size a <> 2 then print('error in size, 7') fi;
a.alex := null
if size a <> 1 then print('error in size, 8') fi;
a.beate := null
if size a <> 0 then print('error in size, 9') fi;

a := []
tables.allocate(a, 1, null, 'a', null)
if size a <> 0 then print('error in size, 10') fi;
a[1] := 10
if size a <> 1 then print('error in size, 11') fi;

a := []
tables.allocate(a, 1, true, 'a', false)
if size a <> 2 then print('error in size, 12') fi;
a[10] := 10
a[11] := 11
if size a <> 4 then print('error in size, 13') fi;
a[5] := null
if size a <> 4 then print('error in size, 14') fi;
if size tables.entries(a) <> 4 then print('error in size, 15') fi;

a := tables.entries(a)
if size a <> 4 then print('error in size, 15') fi;
a[3] := null
if size a <> 3 then print('error in size, 16') fi;

a := []
put(a, 1, true)
if size a <> 1 then print('error in size, 16') fi;
put(a, 2, false)
if size a <> 2 then print('error in size, 17') fi;
for i to 10 do put(a, i, true) od
if size a <> 12 then print('error in size, 18') fi;
put(a, 1, null)
if size a <> 12 then print('error in size, 19') fi;
purge(a, 1)
if size a <> 12 then print('error in size, 20') fi;
for i to 12 do purge(a, 1) od
if size a <> 0 then print('error in size, 21') fi;

create table a(10);  # size/dimtab patch 1.7.9a
a[2] := 1; a[8] := 8;
if size a <> 2 then print('error in size, 22') fi;
a.t := 0
if size a <> 3 then print('error in size, 23') fi;

create table a(10);  # size/dimtab patch 1.7.9b
a[8] := 1
if size a <> 1 then print('error in size, 23') fi;

x := totable(nseq(1, 100));

for i to 16 do
   rotate bottom x
od;

for i to 16 do
   rotate top x
od;

if x <> totable(nseq(1, 100)) then
   print('Error in rotate statement, 2')
fi;

for i from 100 to 1 by -1 do
   if pop(x) <> i then
      print('Error in pop statement, 3')
   fi
od;

if x <> [] then
   print('Error in pop statement, 4')
fi;

to 10 do
   s := [1, 2, 3, 4, 5, 'a' ~ 1000];
   to 10 do
      a := top(s)
   od
od;

if a <> 5 then
   print('Error in pop statement, 5')
fi;

to 10 do
   s := ['a'~100, 'b' ~ 200];
   a := pop(s); b := pop(s);
   if a <> null or b <> null or s <> ['a'~100, 'b' ~ 200] then
      print('Error in pop statement, 6')
   fi
od;

if // 0~0 1 2 -3 zero one two three '2and3' \\ <>
   [0 ~ 0, 1 ~ 1, 2 ~ 2, 3 ~ -3, 4 ~ 'zero', 5 ~ 'one', 6 ~ 'two', 7 ~ 'three', 8 ~ '2and3'] then
   print('Error in //')
fi;

# 2.9.5 `to` indexing patch

a := [10, 11, 12, 13, -2 ~ -11, 6 ~ 15, 7 ~ 17, 10 ~ 20];

if a[1 to 4] <> [10, 11, 12, 13] then
   print('Error, to indexing, 1')
fi;

if a[2 to 3] <> [2 ~ 11, 3 ~ 12] then
   print('Error, to indexing, 2')
fi;

if a[2 to 4] <> [2 ~ 11, 3 ~ 12, 4 ~ 13] then
   print('Error, to indexing, 3')
fi

if a[3 to 4] <> [3 ~ 12, 4 ~ 13] then
   print('Error, to indexing, 4')
fi

if a[4 to 4] <> [4 ~ 13] then
   print('Error, to indexing, 5')
fi

if a[-2 to 4] <> [-2 ~ -11, 1 ~ 10, 2 ~ 11, 3 ~ 12, 4 ~ 13] then
   print('Error, to indexing, 6')
fi;

if a[5 to 7] <> [6 ~ 15, 7 ~ 17] then
   print('Error, to indexing, 7')
fi;

if a[6 to 20] <> [6 ~ 15, 7 ~ 17, 10 ~ 20] then
   print('Error, to indexing, 8')
fi;

if a[1 to 5] <> [10, 11, 12, 13] then
   print('Error, to indexing, 9')
fi;

if a[30 to 40] <> [] then
   print('Error, to indexing, 10')
fi;

if a[0 to 0] <> [] then
   print('Error, to indexing, 11')
fi;

a := [10, 11, 12, 13, -2 ~ -11, 6 ~ 15, 7 ~ 17, 10 ~ 20, 0 ~ 0];

if a[0 to 3] <> [0 ~ 0, 1 ~ 10, 2 ~ 11, 3 ~ 12] then
   print('Error, to indexing, 12')
fi;

if a[0 to 0] <> [0 ~ 0] then
   print('Error, to indexing, 13')
fi;


to 10 do
    create local table r(10);
    if environ.attrib(r).array_allocated <> 10 then
       print('Error in create table array/hash, 1')
    fi
od;

to 10 do
    local r;
    create table r(10);
    if environ.attrib(r).array_allocated <> 10 then
       print('Error in create table array/hash, 1')
    fi
od;

to 10 do
    create local table r(1, 2);
    if environ.attrib(r).array_allocated <> 1 or environ.attrib(r).hash_allocated <> 2 then
       print('Error in create table array/hash, 1')
    fi
od;

to 10 do
    local r;
    create table r(1, 2);
    if environ.attrib(r).array_allocated <> 1 or environ.attrib(r).hash_allocated <> 2 then
       print('Error in create table array/hash, 2')
    fi
od;

to 10 do
   local r, s, t;
   create table r(1, 2), table s(10, 20), table t(100);
   if environ.attrib(r).array_allocated <> 1 or environ.attrib(r).hash_allocated <> 2 or
      environ.attrib(s).array_allocated <> 10 or environ.attrib(s).hash_allocated <> 32 or
      environ.attrib(t).array_allocated <> 100 or environ.attrib(t).hash_allocated <> 0 then
      print('Error in create table array/hash, 3')
   fi
od;

to 10 do
   create local table r(1, 2), table s(10, 20), table t(100);
   if environ.attrib(r).array_allocated <> 1 or environ.attrib(r).hash_allocated <> 2 or
      environ.attrib(s).array_allocated <> 10 or environ.attrib(s).hash_allocated <> 32 or
      environ.attrib(t).array_allocated <> 100 or environ.attrib(t).hash_allocated <> 0 then
      print('Error in create table array/hash, 4')
   fi
od;


to 10 do
   create table r(1, 2), table tbl(100), table s(10, 20);
   if environ.attrib(r).array_allocated <> 1 or environ.attrib(r).hash_allocated <> 2 or
      environ.attrib(s).array_allocated <> 10 or environ.attrib(s).hash_allocated <> 32 or
      environ.attrib(tbl).array_allocated <> 100 or environ.attrib(tbl).hash_allocated <> 0 then
      print('Error in create table array/hash, 5')
   fi
od;


to 10 do
   tbl := tables.newtable(10, 20);
   if environ.attrib(tbl).array_allocated <> 10 or environ.attrib(tbl).hash_allocated <> 32 then
      print('Error in create table array/hash, 7')
   fi
od;


a := [];
to 10 do
   for i to 5 do
      if a[i] then
         insert 0, 0 into a[i]
      else
         a[i] := [0, 0]
      fi
   od
od;
b := [];
to 10 do
   for i to 5 do
      tables.include(b, i, 0, 0)
   od
od;
if not(a == b) then
   print('Error in create tables.include')
fi;

writeline('\nDone in ' & time()-t & ' seconds');

# delete all global variables
clear a, a1, a2, a3, a4, a5, aa, b, b1, b2, b3, bb, c, c1, c2, c3, c4, cc, e, f, i, k, p, q, r, str, s, t, tbl, v, x;

