dataship-frame
Version:
A Data Frame for Javascript. Crunch numbers in node and the browser.
316 lines (252 loc) • 6.13 kB
JavaScript
var async = require('async'),
path = require('path'),
floader = require('floader'),
aloader = require('arrayloader');
test = {};
test.DEFAULT_TYPE = DEFAULT_TYPE = "int32";
test.type_map = type_map = {
"int8" : ".i8",
"uint8" : ".u8",
"int16" : ".i16",
"uint16" : ".u16",
"int32" : ".i32",
"uint32" : ".u32",
"float32" : ".f32",
"float64" : ".f64",
"str8" : ".s8",
"str16" : ".s16"
};
test.extension_map = extension_map = {
".i8" : Int8Array,
".u8" : Uint8Array,
".i16" : Int16Array,
".u16" : Uint16Array,
".i32" : Int32Array,
".u32" : Uint32Array,
".f32" : Float32Array,
".f64" : Float64Array,
".s8" : Int8Array,
".s16" : Int16Array
};
test.float_types = {
"float32" : true,
"float64" : true
};
test.string_types = {
"str8" : true,
"str16" : true
};
/* load a binary file as a TypedArray with the type given by the extension */
function loadArray(filePath, cb){
var ext = path.extname(filePath);
ext = ext.toLowerCase();
if (ext in extension_map)
constructor = extension_map[ext];
else
constructor = Int32Array;
return aloader.load(filePath, constructor, cb);
}
test.load = function(directory, names, types, callback){
// array of paths to matrix data files for current test
var paths = names.map(function(name, i){
type = types[i];
if (!(type in type_map)) type = DEFAULT_TYPE;
ext = type_map[types[i]];
return directory + name + ext;
});
//console.log(testFiles);
async.map(paths, loadArray,
function(err, results){
if(err) return callback(err);
callback(err, results);
}
);
}
/* a key file is just a JSON array of strings
the index of the string in the array is it's code
*/
function loadKey(filePath, cb){
floader.load(filePath, function(err, key){
if(err) return cb(err);
return cb(null, JSON.parse(key));
});
}
test.load_key = function(directory, names, types, callback){
// array of paths to matrix data files for current test
var paths = names.map(function(name, i){
return directory + name + ".key";
});
//console.log(testFiles);
async.map(paths, loadKey,
function(err, results){
if(err) return callback(err);
callback(err, results);
}
);
}
function isobject(obj){ return Object.prototype.toString.call(obj) === "[object Object]";}
/* is there a key in object 'a' not found in object 'b'?
if so, return the first key that's not found
if not, return null
*/
function diffkeys(a, b){
same_keys = true;
for(key in a){
same_keys &= (key in b);
if(!same_keys){
return key;
}
}
return null;
}
/* comp is s comparison function for leaves
a - actual
b - expected
*/
function treediff(a, b, comp){
var p = "(r)";
var todo = [[a, b, p]]; // tuple of (a, b, p)
var parents = { p : null };
var diff_key = null,
diff_a = null,
diff_b = null;
var t;
while (todo.length > 0 && !diff_key){
t = todo.pop();
n_a = t[0];
n_b = t[1];
p = t[2];
// are all the keys the same?
diff_b = diffkeys(n_b, n_a);
if(diff_b){
diff_key = p;
break;
}
diff_a = diffkeys(n_a, n_b);
if(diff_a){
diff_key = p;
break;
}
// check children
for(key in n_b){
// both objects/internal nodes?
if(isobject(n_b[key]) && isobject(n_a[key])){
// yes, add to stack
parents[key] = p;
todo.push([n_a[key], n_b[key], key]);
// both leaves?
} else if(!isobject(n_b[key]) && !isobject(n_a[key])) {
// yes, compare values
if(!comp(n_b[key], n_a[key])){
diff_key = key;
diff_a = n_a[key];
diff_b = n_b[key];
break;
}
} else {
// one is leaf the other is internal
diff_key = key;
if(isobject(n_b)){
diff_a = n_a[key];
} else {
diff_b = n_b[key];
}
break;
}
}
}
var path;
// difference found?
if(diff_key){
// yes, reconstruct the path
var n = diff_key;
path = [n];
while(parents[n]){
n = parents[n];
path.push(n);
}
// diff_a and diff_b are both present on a leaf difference
// only one is present for an internal node difference
return {"path" : path.reverse(), "a" : diff_a, "b" : diff_b};
}
return null;
}
test.assert = {};
test.assert.tree = {};
/* determine whether two trees are equivalent
*/
test.assert.tree.equal = function(t, a, b, msg) {
var fail = treediff(a, b, function(a_n, b_n){
return a_n === b_n;
});
msg = msg || 'trees should be equal';
return treeassert(t, fail, msg);
};
/* determine whether two trees are approximately equivalent:
internal nodes are identical
leaves are within specified floating point tolerances
*/
test.assert.tree.allclose = function(t, a, b, msg, RTOL, ATOL) {
RTOL= RTOL || 1e-05; // for 32 bit precision: 1e-06
ATOL= ATOL || 1e-08;
// treeequal with a floating point comparison function
var fail = treediff(a, b, function(a_n, b_n){
return Math.abs(a_n - b_n) <= ATOL + RTOL * Math.abs(b_n)
});
msg = msg || 'trees should be allclose';
return treeassert(t, fail, msg);
};
test.assert.close = function(t, a, b, msg, RTOL, ATOL){
RTOL= RTOL || 1e-05; // for 32 bit precision: 1e-06
ATOL= ATOL || 1e-08;
// treeequal with a floating point comparison function
var success = Math.abs(a - b) <= ATOL + RTOL * Math.abs(b)
t._assert(success, {
message : msg,
operator : 'close',
actual : a,
expected : b,
extra : null
});
return success;
}
var NULL_PLACEHOLDER = "(null)";
function treeassert(t, fail, msg){
if(fail){
var actual = fail.path.join(" -> "),
expected = fail.path.join(" -> ");
fail.a = fail.a || NULL_PLACEHOLDER;
fail.b = fail.b || NULL_PLACEHOLDER;
actual += " -> " + fail.a;
expected += " -> " + fail.b;
}
t._assert(!fail, {
message : msg,
operator : 'tree.equal',
actual : actual,
expected : expected,
extra : null
});
return !fail;
};
test.generate = {
"Array" : {
"int" : randomIntArray,
"float" : randomFloatArray
}
};
function randomIntArray(N, K){
var data = [];
for(var i = 0; i < N; i++){
data.push(Math.random() * K | 0);
}
return data;
}
function randomFloatArray(N){
var data = [];
for(var i = 0; i < N; i++){
data.push(Math.random() / Math.sqrt(N));
}
return data;
}
module.exports = test;