/* CAST (use basis) .sp product */ var versor = function() { var foreach = function(t, f) { for(var i=0; i < t.length; ++i) f(t[i], i); } /* Data structure representing a blade (coordinate + scale factor) b - bitwise representation of coordinate wt - scale factor */ var blade = function(b, wt) { return { id:b, w:wt }; } var type = function(key, bases, name) { return { key:key, bases:bases, name:name, generated:false, dual:false }; } var classname = function(name) { return "_"+name; } /* Calculate the grade of a coordinate b - bitwise representation of coordinate */ var grade = function(b) { var n = 0; while(b != 0) { if( (b&1)==1 ) n += 1; b >>= 1; } return n; } /* Calculate the sign of the product of two coordinates a - bitwise representation of coordinate b - bitwise representation of coordinate */ var sign = function(a, b) { var n = a>>1; var sum = 0; while(n != 0) { sum += grade(n&b) n >>= 1; } if((sum&1)==0) return 1; else return -1; } /* Calculate the product between two coordinates a - bitwise representation of coordinate b - bitwise representation of coordinate returns a blade */ var product = function(a, b) { var res = a^b; var s = sign(a, b); return blade(res, s); } /* Calculate the outer product between two coordinates a - bitwise representation of coordinate b - bitwise representation of coordinate returns a blade */ var outer = function(a, b) { if((a&b)!=0) return blade(0, 0); else return product(a, b); } var involute = function(x) { var g = grade(x); var n = Math.pow(-1, g); return blade(x, n); } var reverse = function(x) { var g = grade(x); var n = Math.pow(-1, (g*(g-1)/2.0)); return blade(x, n); } var conjugate = function(x) { var g = grade(x); var n = Math.pow(-1, (g*(g+1)/2.0)); return blade(x, n); } /* Calculate the name of a coordinate b - bitwise representation of coordinate */ var basisString = function(b) { var n=0; var res = ""; while(b != 0) { n += 1; if((b&1) == 1) res += n; b >>= 1; } if(n > 0) return "e"+res; else return "s"; } var basisBit = function(name) { if(name == "s") return 0; var x = 0; var lastn = parseInt(name.substr(name.length-1)); for(var i=lastn; i > 0; --i) { x <<= 1; if(name.search(i) >= 0) x += 1; } return x; } var basisBits = function(bases) { var ids = []; for(var i=0; i < bases.length; ++i) { ids[i] = basisBit(bases[i]); } return ids; } var basisNames = function(ty) { ty.sort(function(a, b) { return (a>= 1; ++i; } return tmp; } Space.prototype.metricInner = function(a, b) { var tmp = this.metricProduct(a, b); var g = grade(b) - grade(a); if(grade(a) > grade(b) || grade(tmp.id) != g) { return blade(0, 0); } else { return tmp; } } /* Create a key capable of representing all coordinates in a metric b - (optional) bitwise representation of coordinate */ Space.prototype.key = function(b) { var nkeys = Math.ceil(this.basis.length/32) var key = []; for(var i=0; i < nkeys; ++i) key[i] = 0; if(b != undefined) { var k = Math.ceil((b+1)/32); var shft = (b+1) - 32*(k-1); key[k-1] = 1< 0) { var bases = subspaces[g-1].bases; bases[bases.length] = b; } } return subspaces; } Space.prototype.registerSubspaces = function() { for(var i=0; i < this.subspaces.length; ++i) { var iv = this.subspaces[i]; this.types[iv.name] = type(this.basesKey(iv.bases), iv.bases, iv.name); } } Space.prototype.aliasType = function(oty, nty) { this.types[nty] = this.types[oty]; this.types[oty].alias = nty; //this.types[oty].alias = nty /*delete this.types[oty]; // rename subspace if necessary for(var i=0; i < this.subspaces.length; ++i) { var subs = this.subspaces[i]; if(subs.name == oty) { subs.name = nty break; } } */ } Space.prototype.createType = function(bases, name, aliasExisting) { var key = this.basesKey(bases); for(var tyname in this.types) { var ty = this.types[tyname]; if(keyCheck(key, ty.key)) { if(aliasExisting) { this.aliasType(tyname, name) return name; } else { return tyname; } } } this.types[name] = type(key, bases, name); return name; } Space.prototype.productList = function(bases1, bases2, opname) { var tally = []; // fetch table pairs of values in types var idx = 0 for(var i=0; i < bases1.length; ++i) { var iv = bases1[i]; for(var j=0; j < bases2.length; ++j) { var jv = bases2[j]; var prod = this.products[iv][opname][jv] for(var k=0; k < prod.length; ++k) { var instruction = { a: i, b: j, ida: basisString(iv), idb: basisString(jv), r: prod[k] }; tally[idx] = instruction; idx++; } } } var combined = {}; // check for similar ids in the tally, or if weight is 0 for(var i=0; i < tally.length; ++i) { var instruction = tally[i]; if(instruction.r.w == 0) continue; var b = instruction.r.id; if(combined[b]) { var instructions = combined[b]; instructions[instructions.length] = instruction; } else { combined[b] = [instruction]; } } return order(combined); } Space.prototype.generateType = function(name) { var ty = this.types[name]; var coords = basisNames(ty.bases); var getfields = []; var setfields = []; foreach(coords, function(v, i) { getfields[i] = "this["+i+"]"; setfields[i] = getfields[i]+" = "+v; }); var model = { name: name, classname: classname(name), parameters: coords.join(", "), coords: coords, getfields: getfields.join(", "), setfields: setfields, isdual: ty.dual, }; var create = [ "var "+model.name+" = function("+model.parameters+") {", "\treturn new "+model.classname+"("+model.parameters+");", "}" ].join("\n"); var def = [ "var "+model.classname+" = function("+model.parameters+") {", "\tthis.type = \""+model.name+"\";", "\tif(typeof "+coords[0]+" == \"object\") {", "\t\tthis.cast("+coords[0]+");", "\t}", "\telse {", model.setfields.join("\n"), "\t}", "};", "", model.classname+".prototype._cast = {};", model.classname+".prototype._ip = {};", model.classname+".prototype._op = {};", model.classname+".prototype._gp = {};", model.classname+".prototype._add = {};", model.classname+".prototype._sub = {};", "", model.classname+".prototype.inverse = function() {", "\tvar rev = this.reverse();", "\tvar sca = this.gp(rev)[0];", "\treturn rev.gp(1/sca);", "}", "", model.classname+".prototype.ip = function(b) {", "\tif(!this._ip[b.type]) {", "\t\tspace.createBinop('ip', this.type, b.type);", "\t}", "\treturn this._ip[b.type].call(this, b);", "}", "", model.classname+".prototype.op = function(b) {", "\tif(!this._op[b.type]) {", "\t\tspace.createBinop('op', this.type, b.type);", "\t}", "\treturn this._op[b.type].call(this, b);", "}", "", model.classname+".prototype.gp = function(b) {", "\tif(typeof b == \"number\") {", "\t\tb = space.s(b);", "\t}", "\tif(!this._gp[b.type]) {", "\t\tspace.createBinop('gp', this.type, b.type);", "\t}", "\treturn this._gp[b.type].call(this, b);", "}", "", model.classname+".prototype.sp = function(b) {", "\tvar v = this.inverse().gp(b).gp(this);", "\treturn "+"new b.__proto__.constructor(v);", "}", "", model.classname+".prototype.div = function(b) {", "\treturn this.gp(b.inverse());", "}", "", model.classname+".prototype.add = function(b) {", "\tif(typeof b == \"number\") {", "\t\tb = space.s(b);", "\t}", "\tif(!this._add[b.type]) {", "\t\tspace.createAffineOp('add', this.type, b.type);", "\t}", "\treturn this._add[b.type].call(this, b);", "}", "", model.classname+".prototype.sub = function(b) {", "\tif(typeof b == \"number\") {", "\t\tb = space.s(b);", "\t}", "\tif(!this._sub[b.type]) {", "\t\tspace.createAffineOp('sub', this.type, b.type);", "\t}", "\treturn this._sub[b.type].call(this, b);", "}", "", model.classname+".prototype.toArray = function() {", "\treturn ["+model.getfields+"];", "}", "", model.classname+".prototype.toString = function() {", "\treturn \""+model.name+"(\" + this.toArray().join(\", \") + \")\";", "}", model.classname+".prototype.cast = function(b) {", "\tif(!this._cast[b.type]) {", "\t\tspace.createCast(this.type, b.type);", "\t}", "\treturn this._cast[b.type].call(this, b);", "}", "", model.classname+".prototype.isdual = function() {", "\treturn "+model.isdual+";", "}", "", ].join("\n"); var code = [def]; code.push(this.generateUnop("reverse", name)); code.push(this.generateUnop("involute", name)); code.push(this.generateUnop("conjugate", name)); code.push(create); ty.generated = true; return code.join("\n\n"); } Space.prototype.createCast = function(toName, fromName) { var toTy = this.types[toName] var fromTy = this.types[fromName] var fromCoordMap = {} foreach(fromTy.bases, function(v, i) { fromCoordMap[v] = i; }); var ops = []; foreach(toTy.bases, function(v, i) { var src; if(typeof fromCoordMap[v] == "number") src = "b["+fromCoordMap[v]+"]"; else src = "0" ops[i] = "this["+i+"] = "+src+";" }); var model = { classname: classname(toName), fromTy:fromName, ops: ops.join("\n") }; var code = [ model.classname+".prototype._cast."+model.fromTy+" = function(b) {", model.ops, "};" ].join("\n"); var f = new Function(classname(toName), code); f(this.api.classes[toName]); } Space.prototype.generateUnop = function(opname, tyname) { var ty = this.types[tyname] var coords = basisNames(ty.bases); var _this = this; var ops = []; foreach(ty.bases, function(v, i) { var blade = _this.products[v][opname]; ops[i] = ((blade.w>0) ? "" : "-") + "this["+i+"]"; }); var model = { classname: classname(tyname), opname: opname, ops: ops.join(", ") }; return [ model.classname+".prototype."+model.opname+" = function() {", "\treturn new "+model.classname+"("+model.ops+");", "};" ].join("\n"); } Space.prototype.binopResultType = function(opname, tyname1, tyname2) { var ty1 = this.types[tyname1]; var ty2 = this.types[tyname2]; var op = this.productList(ty1.bases, ty2.bases, opname); var tynameRes if(op.blades.length == 0) { tynameRes = "s"; } else { tynameRes = this.createType(op.blades, tyname1+tyname2+"_"+opname, false); } return tynameRes; } Space.prototype.generateBinop = function(opname, tyname1, tyname2) { var ty1 = this.types[tyname1] var ty2 = this.types[tyname2] var op = this.productList(ty1.bases, ty2.bases, opname); var tynameRes if(op.blades.length == 0) { tynameRes = "s"; } else { tynameRes = this.createType(op.blades, tyname1+tyname2+"_"+opname, false); } var tyRes = this.types[tynameRes]; if(!tyRes) { console.log("ERROR: gentype " + tyname1+tyname2+"_"+opname, op.blades); } else if(this.initialized && !tyRes.generated) { // TODO: consolidate this with the generate() function var code = this.generateType(tynameRes); var functionBody = ["var api = { classes:{}, constructors:{} };"]; functionBody.push([ code, "api.constructors."+tynameRes+" = "+tynameRes+";", "api.classes."+tynameRes+" = "+classname(tynameRes)+";" ].join("\n") ); functionBody.push("return api;"); var f = new Function("space", functionBody.join("\n\n")); var api = f(this); for(var name in api.classes) { this.api.classes[name] = api.classes[name]; } for(var name in api.constructors) { this.api.constructors[name] = api.constructors[name]; } } var ops = []; if(op.blades.length == 0) { ops[0] = "0"; } else { for(var i=0; i < op.blades.length; ++i) { var blade = op.blades[i]; var inst = op.inst[blade]; var instbops = []; for(var j=0; j < inst.length; ++j) { var instop = inst[j]; var bop = "this["+instop.a+"]*b["+instop.b+"]"; if(instop.r.w < 0) bop = "-"+bop; instbops.push(bop); } ops.push(instbops.join(" + ")); } } var model = { classname1: classname(tyname1), tyname2: tyname2, opname: opname, tynameRes: tynameRes, ops: ops.join(",\n") }; return [ model.classname1+".prototype._"+model.opname+"."+model.tyname2+" = function(b) {", "\treturn "+model.tynameRes+"("+model.ops+");", "};" ].join("\n"); } Space.prototype.createBinop = function(opname, tyname1, tyname2) { var resultType = this.binopResultType(opname, tyname1, tyname2); var code = this.generateBinop(opname, tyname1, tyname2); var f = new Function(classname(tyname1), resultType, code); f(this.api.classes[tyname1], this.api.constructors[resultType]); } Space.prototype.createAffineOp = function(opname, tyname1, tyname2) { var opsym = opname == "add" ? "+" : "-"; var ty1 = this.types[tyname1]; var ty2 = this.types[tyname2]; var bases1Map = {}; var bases2Map = {}; var basesMap = {}; for(var i=0; i < ty1.bases.length; ++i) { bases1Map[ ty1.bases[i] ] = i; basesMap[ ty1.bases[i] ] = ty1.bases[i]; } for(var i=0; i < ty2.bases.length; ++i) { bases2Map[ ty2.bases[i] ] = i; basesMap[ ty2.bases[i] ] = ty2.bases[i]; } var bases = []; for(var name in basesMap) { bases.push(basesMap[name]); } bases.sort(function(a, b) { return (a