diff --git a/squirrel/sqbaselib.cpp b/squirrel/sqbaselib.cpp index f835cbf1..ddc6cf46 100644 --- a/squirrel/sqbaselib.cpp +++ b/squirrel/sqbaselib.cpp @@ -726,75 +726,93 @@ static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger return true; } -static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, SQInteger func) -{ - SQInteger maxChild; - SQInteger done = 0; - SQInteger ret; - SQInteger root2; - while (((root2 = root * 2) <= bottom) && (!done)) - { - if (root2 == bottom) { - maxChild = root2; - } - else { - if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret)) - return false; - if (ret > 0) { - maxChild = root2; - } - else { - maxChild = root2 + 1; - } - } +/* +** The lua_auxsort code is adapted from from lua 5.1.5 +** {====================================================== +** Quicksort +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +** ======================================================= +*/ - if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret)) +static bool lua_auxsort (HSQUIRRELVM v, SQArray *arr, SQInteger l, SQInteger u, + SQInteger func) { + while (l < u) { /* for tail recursion */ + SQInteger i, j, ret; + bool rc; + /* sort elements a[l], a[(l+u)/2] and a[u] */ + if(!_sort_compare(v,arr->_values[u],arr->_values[l],func,ret)) + return false; + if (ret < 0) /* a[u] < a[l]? */ + _Swap(arr->_values[l],arr->_values[u]); /* swap a[l] - a[u] */ + if (u-l == 1) break; /* only 2 elements */ + i = (l+u)/2; + if(!_sort_compare(v,arr->_values[i],arr->_values[l],func,ret)) + return false; + if (ret < 0) /* a[i]_values[i],arr->_values[l]); + else { + if(!_sort_compare(v,arr->_values[u],arr->_values[i],func,ret)) + return false; + if (ret < 0) /* a[u]_values[i],arr->_values[u]); + } + if (u-l == 2) break; /* only 3 elements */ + SQObjectPtr P = arr->_values[i]; /* Pivot */ + _Swap(arr->_values[i],arr->_values[u-1]); + /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ + i = l; j = u-1; + for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */ + /* repeat ++i until a[i] >= P */ + while ((rc = _sort_compare(v,arr->_values[++i],P,func,ret)) && (ret < 0)) { + if (i>u) + { + sq_throwerror(v, _SC("invalid order function for sorting")); return false; - if (ret < 0) { - if (root == maxChild) { - v->Raise_Error(_SC("inconsistent compare function")); - return false; // We'd be swapping ourselve. The compare function is incorrect - } - - _Swap(arr->_values[root],arr->_values[maxChild]); - root = maxChild; } - else { - done = 1; + } + if(!rc) return false; + /* repeat --j until a[j] <= P */ + while ((rc = _sort_compare(v,P, arr->_values[--j],func,ret)) && (ret < 0)) { + if (j_values[i],arr->_values[j]); } - return true; -} - -static bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger SQ_UNUSED_ARG(l), SQInteger SQ_UNUSED_ARG(r),SQInteger func) -{ - SQArray *a = _array(arr); - SQInteger i; - SQInteger array_size = a->Size(); - for (i = (array_size / 2); i >= 0; i--) { - if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false; + _Swap(arr->_values[u-1],arr->_values[i]); /* swap pivot (a[u-1]) with a[i] */ + /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ + /* adjust so that smaller half is in [j..i] and larger one in [l..u] */ + if (i-l < u-i) { + j=l; i=i-1; l=i+2; } - - for (i = array_size-1; i >= 1; i--) - { - _Swap(a->_values[0],a->_values[i]); - if(!_hsort_sift_down(v,a, 0, i-1,func)) return false; + else { + j=i+1; i=u; u=j-2; } - return true; -} - -static SQInteger array_sort(HSQUIRRELVM v) -{ - SQInteger func = -1; - SQObjectPtr &o = stack_get(v,1); - if(_array(o)->Size() > 1) { - if(sq_gettop(v) == 2) func = 2; - if(!_hsort(v, o, 0, _array(o)->Size()-1, func)) - return SQ_ERROR; + if(!lua_auxsort(v, arr, j, i, func)) /* call recursively for upper interval */ + return false; + } /* repeat the routine for the larger one */ + return true; +} + +static SQRESULT array_sort(HSQUIRRELVM v) { + SQInteger func = -1; + SQObjectPtr &o = stack_get(v,1); + SQArray *arr = _array(o); + if(arr->Size() > 1) { + if(sq_gettop(v) == 2) func = 2; + if(!lua_auxsort(v, arr, 0, arr->Size()-1, func)) + return SQ_ERROR; - } - sq_settop(v,1); - return 1; + } + sq_settop(v,1); + return 1; } static SQInteger array_slice(HSQUIRRELVM v) diff --git a/squirrel/sqclosure.h b/squirrel/sqclosure.h index 66495b94..b204bb35 100644 --- a/squirrel/sqclosure.h +++ b/squirrel/sqclosure.h @@ -101,7 +101,7 @@ struct SQOuter : public CHAINABLE_OBJ SQObjectPtr *_valptr; /* pointer to value on stack, or _value below */ SQInteger _idx; /* idx in stack array, for relocation */ SQObjectPtr _value; /* value of outer after stack frame is closed */ - SQOuter *_next; /* pointer to next outer when frame is open */ + SQOuter *_next_outer; /* pointer to next outer when frame is open */ }; ////////////////////////////////////////////// diff --git a/squirrel/sqvm.cpp b/squirrel/sqvm.cpp index ecdcbb7d..30cf53b5 100644 --- a/squirrel/sqvm.cpp +++ b/squirrel/sqvm.cpp @@ -1632,10 +1632,10 @@ void SQVM::FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex) target = SQObjectPtr(p); return; } - pp = &p->_next; + pp = &p->_next_outer; } otr = SQOuter::Create(_ss(this), stackindex); - otr->_next = *pp; + otr->_next_outer = *pp; otr->_idx = (stackindex - _stack._vals); __ObjAddRef(otr); *pp = otr; @@ -1695,7 +1695,7 @@ void SQVM::RelocateOuters() SQOuter *p = _openouters; while (p) { p->_valptr = _stack._vals + p->_idx; - p = p->_next; + p = p->_next_outer; } } @@ -1704,7 +1704,7 @@ void SQVM::CloseOuters(SQObjectPtr *stackindex) { while ((p = _openouters) != NULL && p->_valptr >= stackindex) { p->_value = *(p->_valptr); p->_valptr = &p->_value; - _openouters = p->_next; + _openouters = p->_next_outer; __ObjRelease(p); } } diff --git a/squirrel/sqvm.h b/squirrel/sqvm.h index a75524da..de336e3f 100644 --- a/squirrel/sqvm.h +++ b/squirrel/sqvm.h @@ -168,7 +168,6 @@ typedef sqvector CallInfoVec; CallInfo *ci; SQUserPointer _foreignptr; //VMs sharing the same state - SQSharedState *_sharedstate; SQInteger _nnativecalls; SQInteger _nmetamethodscall; SQRELEASEHOOK _releasehook; diff --git a/tests/minctest.nut b/tests/minctest.nut new file mode 100644 index 00000000..07f16cec --- /dev/null +++ b/tests/minctest.nut @@ -0,0 +1,1335 @@ +// +// MINCTEST - Minimal SquiLu Test Library +// based on MINCTEST - Minimal Lua Test Library - 0.1.1 +// This is based on minctest.h (https://codeplea.com/minctest) +// +// Copyright (c) 2014, 2015, 2016 Lewis Van Winkle +// Copyright (c) 2017 Domingo Alvarez Duarte +// +// http://CodePlea.com +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + + +// MINCTEST - Minimal testing library for C +// +// +// Example: +// +// +// #include "minctest.nut" +// local l = minctest(); +// +// l.run("test1", function(){ +// l.ok('a' == 'a'); //assert true +// }); +// +// l.run("test2", function(){ +// l.equal(5, 6); //compare integers +// l.fequal(5.5, 5.6); //compare floats +// }); +// +// return l.results(); //show results +// +// +// Hints: +// All functions/variables start with the letter 'l'. +// +// + +function minctest() +{ + local self = {}; + const LTEST_FLOAT_TOLERANCE = 1e-12; //1e-6; 0.001; + + + local ltests = 0; + local lfails = 0; + local start_clock = clock(); + + + self.results <- function() + { + local total_time = floor((clock() - start_clock) * 1000) + "ms"; + if (lfails == 0) + print("ALL TESTS PASSED (" + ltests + "/" + ltests + ") " + total_time); + else + print("SOME TESTS FAILED (" + (ltests-lfails) + "/" + ltests + ") " + total_time); + + print("\n"); + return lfails != 0; + } + + + self.run <- function(name, testfunc) + { + local ts = ltests; + local fs = lfails; + local lclock = clock(); + local msg = format("\t%-24s", name); + print(msg); + testfunc(); + msg = format("pass: %2d fail: %2d %4dms\n", + (ltests-ts)-(lfails-fs), lfails-fs, + floor((clock() - lclock) * 1000)); + if(lfails) print("\n"); + print(msg); + } + + self.ok <- function(test) + { + ++ltests; + if ( ! test ) + { + ++lfails; + local stack_info = getstackinfos(2); + print(format("\n%s:%d:0 error", + stack_info.src, + stack_info.line)); + } + } + + self.equal <- function(a, b) + { + ++ltests; + if (a != b) + { + ++lfails; + local stack_info = getstackinfos(2); + print(format("%s:%d (%d != %d)\n", + stack_info.src, + stack_info.line, + a, b)); + } + } + + self.fequal <- function(a, b) + { + ++ltests; + if (fabs(a - b) > LTEST_FLOAT_TOLERANCE) + { + ++lfails; + local stack_info = getstackinfos(2); + print(format("%s:%d (%f != %f)\n", + stack_info.src, + stack_info.line, + a, b)); + } + } + + self.fails <- function() {return lfails;} + + return self; +} + +class Dad +{ + _n = null; + constructor(n){ _n = n;} + + function print() {printf("%d\n", _n);} + function onlyForFriends(){} + function onlyForUs(){} +}; + +local sqt = minctest(); + +local globals = getroottable(); + +sqt.run("closures", function(){ + + local A = 0, B = {g=10} + local f = function(x) + { + local a = [] + for(local i=1; i < 1000; ++i) + { + local y = 0 + { + a.append(function () {++B.g; y += x; return y+A;}); + } + } + local dummy = function () {return a[A];} + collectgarbage() + A = 1; + sqt.ok(dummy() == a[1]); + A = 0; + //print("a[0]", a[0]()) + sqt.ok(a[0]() == x) + sqt.ok(a[2]() == x) + collectgarbage() + sqt.ok(B.g == 12) + return a + } + + local a = f(10) + + // testing closures with 'for' control variable + a = [] + for(local i=0; i < 10; ++i) + { + local lv = i; + a.append({set = function(x){ lv=x; }, get = function (){ return lv; }}) + if( i == 2 ) break; + } + sqt.ok(a.len() == 3) + sqt.ok(a[0].get() == 0) + a[0].set(10) + sqt.ok(a[0].get() == 10) + sqt.ok(a[1].get() == 1) + a[1].set('a') + sqt.ok(a[1].get() == 'a') + //a[2].set(2) + sqt.ok(a[2].get() == 2) + + a = [] + foreach( i, k in ['a', 'b']) + { + local li = i + local lk = k + a.append( {set = function(x, y) {li=x; lk=y}, + get = function () {return [li, lk];}} ) + if( i == 2 ) break; + } + a[0].set(10, 20) + local rs = a[1].get() + sqt.ok(rs[0] == 1 && rs[1] == 'b') + rs = a[0].get() + sqt.ok(rs[0] == 10 && rs[1] == 20) + a[1].set('a', 'b') + rs = a[1].get() + sqt.ok(rs[0] == 'a' && rs[1] == 'b') + + // testing closures with 'for' control variable x break + for(local i=1; i <= 3; ++i) + { + local li = i + f = function () { return li;} + break + } + sqt.ok(f() == 1) + + foreach( k, v in ["a", "b"]) + { + local lk = k, lv = v + f = function () {return [lk, lv]; } + break + } + sqt.ok(([f()])[0][0] == 0) + sqt.ok(([f()])[0][1] == "a") + + + // testing closure x break x return x errors + + local b + f = function (x) + { + local first = 1 + while( 1 ) { + if( x == 3 && ! first ) return + local la = "xuxu" + b = function (op, y=0) { + if( op == "set" ) + la = x+y + else + return la + } + if( x == 1 ) { break } + else if( x == 2 ) return + else if( x != 3 ) throw("x != 3") + + first = null + } + } + + for(local i=1; i <= 3; ++i) { + f(i) + sqt.ok(b("get") == "xuxu") + b("set", 10); sqt.ok(b("get") == 10+i) + b = null + } + + //pcall(f, 4); + try{ f(4);}catch(e){} + sqt.ok(b("get") == "xuxu") + b("set", 10); sqt.ok(b("get") == 14) + + // testing multi-level closure + local w + f = function(x) + { + return function (y) + { + return function (z) {return w+x+y+z;} + } + } + + local y = f(10) + w = 1.345 + sqt.ok(y(20)(30) == 60+w) + + // test for correctly closing upvalues in tail calls of vararg functions + local function t () + { + local function c(a,b) {sqt.ok(a=="test" && b=="OK") } + local function v(f, ...) {c("test", f() != 1 && "FAILED" || "OK") } + local lx = 1 + return v(function() {return lx;}) + } + t() + +}); + +sqt.run("calls", function(){ + + // get the opportunity to test "type" too ;) + local Klass = class {}; + local aklass = Klass(); + + sqt.ok(type(1<2) == "bool") + sqt.ok(type(true) == "bool" && type(false) == "bool") + sqt.ok(type(null) == "null") + sqt.ok(type(-3) == "integer") + sqt.ok(type(-3.14) == "float") + sqt.ok(type("x") == "string") + sqt.ok(type({}) == "table") + sqt.ok(type(type) == "function") + sqt.ok(type(Klass) == "class") + sqt.ok(type(aklass) == "instance") + + sqt.ok(type(sqt.ok) == type(print)) + local f = null + f = function (x) {return a.x(x);} + sqt.ok(type(f) == "function") + + // testing local-function recursion + local fact = false + { + local res = 1 + local lfact; + lfact = function(n) + { + if( n==0 ) return res + else return n*lfact(n-1) + } + sqt.ok(lfact(5) == 120) + } + sqt.ok(fact == false) + + // testing declarations + local a = {i = 10} + local self = 20 + a.x <- function(x) {return x+this.i;} + a.y <- function(x) {return x+self;} + + sqt.ok(a.x(1)+10 == a.y(1)) + + a.t <- {i=-100} + a["t"].x <- function (a,b) {return this.i+a+b;} + + sqt.ok(a.t.x(2,3) == -95) + + { + local la = {x=0} + la.add <- function(x) {this.x = this.x+x; la.y <- 20; return this; } + sqt.ok(la.add(10).add(20).add(30).x == 60 && la.y == 20) + } + + a = {b={c={}}} + + a.b.c.f1 <- function(x) {return x+1;} + a.b.c.f2 <- function(x,y) {this[x] <- y;} + sqt.ok(a.b.c.f1(4) == 5) + a.b.c.f2("k", 12); sqt.ok(a.b.c.k == 12) + + local t = null // 'declare' t + f = function(a,b,c=null, e=null) {local d = 'a'; t=[a,b,c,d];} + + f( // this line change must be valid + 1,2) + sqt.ok(t[0] == 1 && t[1] == 2 && t[2] == null && t[3] == 'a') + + f(1,2, // this one too + 3,4) + sqt.ok(t[0] == 1 && t[1] == 2 && t[2] == 3 && t[3] == 'a') + + // fixed-point operator + local Y = function (le) + { + local function la (f) + { + return le(function (x) {return f(f)(x);}) + } + return la(la) + } + + + // non-recursive factorial + + local F = function (f) + { + return function (n) + { + if( n == 0 ) return 1 + else return n*f(n-1) + } + } + + local fat = Y(F) + + sqt.ok(fat(0) == 1 && fat(4) == 24 && Y(F)(5)==5*Y(F)(4)) + + local function g (z) + { + local function lf (a,b,c,d) + { + return function (x,y) {return a+b+c+d+a+x+y+z;} + } + return lf(z,z+1,z+2,z+3) + } + + f = g(10) + sqt.ok(f(9, 16) == 10+11+12+13+10+9+16+10) + + Y, F, f = null + + try { assert(true); sqt.ok(true);} catch(e) {sqt.ok(false);}; + try { assert(true, "opt msg"); sqt.ok(true);} catch(e) {sqt.ok(false);}; + try { assert(false); sqt.ok(false);} catch(e) {sqt.ok(true);}; + try { assert(false, "opt msg"); sqt.ok(false);} catch(e) {sqt.ok(e == "opt msg");}; + + local bugRecursionLocal; + bugRecursionLocal = function(str, num=666, car=777){ + if(str == "recurse") return bugRecursionLocal("recurring with recurse", 5); + return str + num; + } + + sqt.ok(bugRecursionLocal("dad") == "dad666"); + sqt.ok(bugRecursionLocal("recurse") == "recurring with recurse5"); + + local function f1(){ return "f1";} + local function f2(){ return "f2";} + local function f3(f=f2){ return "f3" + f();} + + sqt.ok(f3() == "f3f2"); + + local function f4(f=f2){ return "f4" + type(f);} + + sqt.ok(f4() == "f4function"); + + local ary = ["blue"] + local function f5(s, f=ary){ return "f5" + s + type(f);} + + sqt.ok(f5("ded") == "f5dedarray"); + + local ncount = 0; + local nested; + nested = function(p=88, q=66) + { + ++ncount; + local result = ncount + ":" + p + ":" + q; + local function ni(x=99) + { + result += ":" + x; + } + ni(); + if(p==88) result += "::" + nested(22); + return result; + } + + sqt.ok(nested() == "1:88:66:99::2:22:66:99"); + sqt.ok(nested("n") == "3:n:66:99"); + +}); + +sqt.run("sort", function(){ + local function check (a, f=null) + { + f = f || function (x,y) {return x 2; --n) sqt.ok(! f(a[n], a[n-1])) + } + + local a = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec"] + + a.sort() + check(a) + + limit <- 5000 + //if( "_soft" in globals) limit = 5000 + + a = [] + for(local i=0; i < limit; ++i) a.append(rand()) + + a.sort() + check(a) + + a.sort() + check(a) + + a = [] + for(local i=0; i < limit; ++i) a.append(rand()) + + local li=0 + a.sort(function(x,y) {li=li+1; return y<=>x;}) + check(a, function(x,y) {return y 2)); + sqt.ok(!(2 > 2)); + sqt.ok(2 > 1); + sqt.ok(!(1 >= 2)); + sqt.ok(2 >= 2); + sqt.ok(2 >= 1); + sqt.ok(!(0 < -0)); + sqt.ok(!(-0 < 0)); + sqt.ok(!(0 > -0)); + sqt.ok(!(-0 > 0)); + sqt.ok(0 <= -0); + sqt.ok(-0 <= 0); + sqt.ok(0 >= -0); + sqt.ok(-0 >= 0); + + sqt.ok((8 / 2) == 4); + sqt.fequal((12.34 / -0.4), -30.85); + //sqt.ok(!isfinite(3 / 0)); // == infinity); + //sqt.ok(!isfinite(-3 / 0)); // == -infinity); + //sqt.ok(isnan(0 / 0)); + //sqt.ok(isnan(-0 / 0)); + //sqt.ok(!isfinite(3 / -0)); // == -infinity); + //sqt.ok(!isfinite(-3 / -0)); // == infinity); + //sqt.ok(isnan(0 / -0)); + //sqt.ok(isnan(-0 / -0)); + + sqt.ok(123 == 123); + sqt.ok(123 != 124); + sqt.ok(-3 != 3); + sqt.ok(0 == -0); + sqt.ok(123 != "123"); + sqt.ok(1 == true); + sqt.ok(0 == false); + //sqt.ok(1 !== true); + //sqt.ok(0 !== false); + + sqt.ok((0 & 0) == 0); + sqt.ok((0xaaaaaaaa & 0x55555555) == 0); + //sqt.ok((0xf0f0f0f0 & 0x3c3c3c3c) == 808464432); + //sqt.ok((0xffffffff & 0xffffffff) == 4294967295); + + try{1 & false, sqt.ok(0)} catch(e) {sqt.ok(1);} // expect runtime error: Right operand must be a number. + + sqt.ok((0 << 0) == 0); + sqt.ok((1 << 0) == 1); + sqt.ok((0 << 1) == 0); + sqt.ok((1 << 1) == 2); + //sqt.ok((0xffffffff << 0) == 4294967295); + + sqt.ok((0 >> 0) == 0); + sqt.ok((1 >> 0) == 1); + sqt.ok((0 >> 1) == 0); + sqt.ok((1 >> 1) == 0); +/* + if(_intsize_ == 8) + { + sqt.ok((0xaaaaaaaa << 1) == 5726623060); + sqt.ok((0xf0f0f0f0 << 1) == 8084644320); + sqt.ok((0xaaaaaaaa >> 1) == 1431655765); + sqt.ok((0xf0f0f0f0 >> 1) == 2021161080); + sqt.ok((0xffffffff >> 1) == 2147483647); + } + else + { + sqt.ok((0xaaaaaaaa << 1) == 1431655764); + sqt.ok((0xf0f0f0f0 << 1) == -505290272); + sqt.ok((0xaaaaaaaa >> 1) == -715827883); + sqt.ok((0xf0f0f0f0 >> 1) == -126322568); + sqt.ok((0xffffffff >> 1) == -1); + } +*/ + sqt.ok((0 ^ 0) == 0); + sqt.ok((1 ^ 1) == 0); + sqt.ok((0 ^ 1) == 1); + sqt.ok((1 ^ 0) == 1); + //sqt.ok((0xaaaaaaaa ^ 0x55555555) == 4294967295); + //sqt.ok((0xf0f0f0f0 ^ 0x3c3c3c3c) == 3435973836); + //sqt.ok((0xffffffff ^ 0xffffffff) == 0); + + //sqt.ok((~0) == 4294967295); + //sqt.ok((~1) == 4294967294); + //sqt.ok((~23) == 4294967272); + //sqt.ok((~0xffffffff) == 0); + //sqt.ok((~1.23) == 4294967294); + //sqt.ok((~0.00123) == 4294967295); + //sqt.ok((~345.67) == 4294966950); + + sqt.ok((0 | 0) == 0); + //sqt.ok((0xaaaaaaaa | 0x55555555) == 4294967295); + //sqt.ok((0xcccccccc | 0x66666666) == 4008636142); + //sqt.ok((0xffffffff | 0xffffffff) == 4294967295); + + local a = 3; + sqt.ok((5 - 3) == 2); + sqt.fequal((3.1 - 0.24), 2.86); + sqt.ok((3 - 2 - 1) == 0); + sqt.ok(-a == -3); + + sqt.ok((1 + 2) == 3); + sqt.ok((12.34 + 0.13) == 12.47); + sqt.ok((3 + 5 + 2) == 10); + + sqt.ok((5 % 3) == 2); + sqt.ok((10 % 5) == 0); + sqt.ok((-4 % 3) == -1); + sqt.ok((4 % -3) == 1); + sqt.ok((-4 % -3) == -1); + sqt.ok((-4.2 % 3.1) == -1.1); + sqt.ok((4.2 % -3.1) == 1.1); + sqt.ok((-4.2 % -3.1) == -1.1); + sqt.ok((13 % 7 % 4) == 2); + sqt.ok((13 + 1 % 7) == 14); + + sqt.ok((5 * 3) == 15); + sqt.ok((12.34 * 0.3) == 3.702); + //10000000000000004 < 32BITS + //10000000000000002 < 64BITS + //print(format("%.17g", 1e16 + 2.9999) , "10000000000000002"); + //if(_intsize_ == 8) + sqt.ok(format("%.17g", 1e16 + 2.9999) == "10000000000000002"); + //else sqt.ok(format("%.17g", 1e16 + 2.9999) == "10000000000000004"); + +}); + +sqt.run("enum", function(){ + enum e1 {one=1, two}; + sqt.ok(e1.one == 1); + sqt.ok(e1.two == 2); + + enum e2 {one=-1, two, three}; + sqt.ok(e2.one == -1); + sqt.ok(e2.two == 0); + sqt.ok(e2.three == 1); + + enum e3 {one=-1, two, three, nine=9, ten}; + sqt.ok(e3.one == -1); + sqt.ok(e3.two == 0); + sqt.ok(e3.three == 1); + sqt.ok(e3.nine == 9); + sqt.ok(e3.ten == 10); +}); + +sqt.run("constants", function(){ + const ONE = 1; + const STR = "string"; + + sqt.ok(ONE == 1); + sqt.ok(STR == "string"); +}); + +sqt.run("class", function(){ + + class Comparable { + constructor(n) + { + name = n; + } + + function _typeof() {return "Comparable";}; + + function _cmp(other) + { + if(nameother.name) return 1; + return 0; + } + static function st() {return "st";}; + name = null; + id = 0; + static count = 0; + } + local a = Comparable("Alberto"); + local b = Comparable("Wouter"); + local c = Comparable("Alberto"); + sqt.ok(a < b); + sqt.ok(b > a); + sqt.ok(a.id == 0); + sqt.ok(Comparable.count == 0); + sqt.ok(a.count == 0); + sqt.ok(Comparable.st() == "st"); + sqt.ok(a.st() == "st"); + sqt.ok(typeof(a) == "Comparable"); + sqt.ok( c == a ); + //sqt.ok( c !== a ); + +}); + +sqt.run("globals", function(){ + + //sqt.ok( obj_clone(3) == 3 ); + //sqt.ok( obj_clone(3.4) == 3.4 ); + //sqt.ok( obj_clone("str") == "str" ); + +}); + +return sqt.results(); //show results