├── .gitignore ├── Graphics ├── grid.png ├── grid.svg ├── mesh1.png ├── mesh1.svg ├── mesh2.png ├── mesh2.svg ├── octree.png ├── octree.svg ├── virtual.png └── virtual.svg ├── MineBench ├── benchmark.js ├── flatarray.js ├── intervaltree.js ├── octree.js └── virtual.js ├── Polynomials ├── Part1 │ ├── main.cpp │ └── symmetric_tensor.h └── Part2 │ ├── main.cpp │ └── symmetric_tensor.h └── README /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.o 3 | *~ 4 | *.log 5 | 6 | -------------------------------------------------------------------------------- /Graphics/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/grid.png -------------------------------------------------------------------------------- /Graphics/grid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 41 | 48 | 49 | 70 | 73 | 74 | 76 | 77 | 79 | image/svg+xml 80 | 82 | 83 | 84 | 85 | 86 | 90 | 94 | 98 | 102 | 106 | 110 | 114 | 118 | 122 | 126 | 130 | 134 | 138 | 142 | 146 | 150 | 154 | 158 | 162 | 166 | 170 | 174 | 178 | 182 | 186 | 190 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /Graphics/mesh1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/mesh1.png -------------------------------------------------------------------------------- /Graphics/mesh1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 41 | 48 | 49 | 67 | 70 | 71 | 73 | 74 | 76 | image/svg+xml 77 | 79 | 80 | 81 | 82 | 83 | 87 | 94 | 101 | 108 | 115 | 119 | 123 | 127 | 131 | 135 | x 145 | 146 | 147 | -------------------------------------------------------------------------------- /Graphics/mesh2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/mesh2.png -------------------------------------------------------------------------------- /Graphics/mesh2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 30 | 37 | 44 | 51 | 58 | 59 | 77 | 80 | 81 | 83 | 84 | 86 | image/svg+xml 87 | 89 | 90 | 91 | 92 | 93 | 97 | 104 | 111 | 118 | 125 | 129 | 133 | 137 | 141 | 145 | x 155 | x 165 | 166 | 167 | -------------------------------------------------------------------------------- /Graphics/octree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/octree.png -------------------------------------------------------------------------------- /Graphics/octree.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 30 | 37 | 44 | 51 | 52 | 73 | 76 | 77 | 79 | 80 | 82 | image/svg+xml 83 | 85 | 86 | 87 | 88 | 89 | 93 | 97 | 101 | 105 | 109 | 113 | 117 | 121 | 125 | 129 | 133 | 137 | 141 | 145 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /Graphics/virtual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/virtual.png -------------------------------------------------------------------------------- /Graphics/virtual.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 41 | 48 | 55 | 62 | 69 | 76 | 83 | 90 | 97 | 104 | 111 | 112 | 133 | 136 | 137 | 139 | 140 | 142 | image/svg+xml 143 | 145 | 146 | 147 | 148 | 149 | 153 | 155 | 159 | 163 | 167 | 171 | 175 | 179 | 183 | 187 | 191 | 195 | 199 | 203 | 207 | 211 | 215 | 219 | 223 | 227 | 231 | 235 | 239 | 243 | 247 | 251 | 255 | 259 | 263 | 264 | 265 | 268 | 272 | 276 | 280 | 284 | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | 320 | 324 | 328 | 332 | 336 | 340 | 344 | 348 | 352 | 356 | 360 | 364 | 368 | 372 | 376 | 377 | 378 | 381 | 385 | 389 | 393 | 397 | 401 | 405 | 409 | 413 | 417 | 421 | 425 | 429 | 433 | 437 | 441 | 445 | 449 | 453 | 457 | 461 | 465 | 469 | 473 | 477 | 481 | 485 | 489 | 490 | 491 | 494 | 498 | 502 | 506 | 510 | 514 | 518 | 522 | 526 | 530 | 534 | 538 | 542 | 546 | 550 | 554 | 558 | 562 | 566 | 570 | 574 | 578 | 582 | 586 | 590 | 594 | 598 | 602 | 603 | 604 | 607 | 611 | 615 | 619 | 623 | 627 | 631 | 635 | 639 | 643 | 647 | 651 | 655 | 659 | 663 | 667 | 671 | 675 | 679 | 683 | 687 | 691 | 695 | 699 | 703 | 707 | 711 | 715 | 716 | 717 | 720 | 724 | 728 | 732 | 736 | 740 | 744 | 748 | 752 | 756 | 760 | 764 | 768 | 772 | 776 | 780 | 784 | 788 | 792 | 796 | 800 | 804 | 808 | 812 | 816 | 820 | 824 | 828 | 829 | 830 | 831 | 832 | -------------------------------------------------------------------------------- /MineBench/benchmark.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var FlatArray = require("./flatarray.js").ChunkSet, 4 | Octree = require("./octree.js").ChunkSet, 5 | VirtualArray = require("./virtual.js").ChunkSet, 6 | IntervalTree = require("./intervaltree.js").ChunkSet; 7 | 8 | //Since flatarray, virtual and octrees don't support ranges, we add a generic naive foreach to each of them 9 | function naiveForeach(lo, hi, r, cb) { 10 | var nmod = 2*r + 1, 11 | size = nmod * nmod * nmod, 12 | nbhd = new Array(size), 13 | i, j, k, l, o, p, q; 14 | 15 | for(i=0; i=0; --l) { 6 | this.voxels[l]=0; 7 | } 8 | this.dims = dims; 9 | Object.freeze(this); 10 | } 11 | 12 | ChunkSet.prototype.get = function(x, y, z) { 13 | return this.voxels[ x + this.dims[0] * (y + this.dims[1] * z)]; 14 | } 15 | 16 | ChunkSet.prototype.set = function(x, y, z, v) { 17 | return this.voxels[ x + this.dims[0] * (y + this.dims[1] * z)] = v; 18 | } 19 | 20 | exports.ChunkSet = ChunkSet; 21 | -------------------------------------------------------------------------------- /MineBench/intervaltree.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | //This code is more complicated than the other methods since it was taken directly from an active project. It contains many features which are not strictly speaking required for the benchmark. -Mik 4 | 5 | var CHUNK_SHIFT_X = 8, 6 | CHUNK_SHIFT_Y = 4, 7 | CHUNK_SHIFT_Z = 4, 8 | CHUNK_X = (1<>2)) & 3272356035; 43 | x = (x | (x>>4)) & 251719695; 44 | x = (x | (x>>8)) & 4278190335; 45 | x = (x | (x>>16)) & 0x3FF; 46 | return (x<<22)>>22; 47 | 48 | } 49 | 50 | function hashCode(i,j,k) { 51 | return expand(i)+(expand(j)<<1)+(expand(k)<<2); 52 | }; 53 | 54 | function Chunk(x, y, z, data) { 55 | this.x = x; 56 | this.y = y; 57 | this.z = z; 58 | if(data) { 59 | this.data = data; 60 | } 61 | else { 62 | this.data = [0,0]; 63 | } 64 | Object.freeze(this); 65 | }; 66 | 67 | Chunk.prototype.isEmpty = function() { 68 | return (this.data.length === 2 && this.data[1] === 0); 69 | }; 70 | 71 | Chunk.prototype.bsearch = function (lo, hi, y) { 72 | //console.log(this.data, lo, hi, y); 73 | lo >>= 1; 74 | hi >>= 1; 75 | var m, x; 76 | while(lo+1 < hi) { 77 | 78 | m = (lo + hi) >> 1; 79 | x = this.data[m<<1]; 80 | //console.log(lo, m, hi, x, y); 81 | if( x > y ) { 82 | hi = m; 83 | } else if( x < y ) { 84 | lo = m; 85 | } else { 86 | //console.log("Here"); 87 | return m<<1; 88 | } 89 | } 90 | return lo<<1; 91 | }; 92 | 93 | Chunk.prototype.index = function(i, j, k) { 94 | var y = flattenIndex(i, j, k), 95 | lo = 0, 96 | hi = (this.data.length>>1)-1; 97 | return this.bsearch(0, this.data.length, y); 98 | }; 99 | 100 | Chunk.prototype.get = function(i, j, k) { 101 | return this.data[this.index(i,j,k)+1]; 102 | }; 103 | 104 | Chunk.prototype.set = function(i, j, k, v) { 105 | var y = flattenIndex(i, j, k), 106 | m = this.bsearch(0, this.data.length, y), 107 | interval_start = this.data[m], 108 | interval_end = (m+2 < this.data.length ? this.data[m+2] : CHUNK_SIZE), 109 | interval_val = this.data[m+1]; 110 | 111 | if(v === interval_val) { 112 | return interval_val; 113 | } 114 | if(y === interval_start) { 115 | if(interval_start + 1 === interval_end) { 116 | this.data[m+1] = v; 117 | 118 | if(m+3 0 && this.data[m-1] === v) { 120 | this.data.splice(m,4); 121 | } 122 | else { 123 | this.data.splice(m+2,2); 124 | } 125 | } 126 | else if(m > 0 && this.data[m-1] === v) { 127 | this.data.splice(m,2); 128 | } 129 | } 130 | else if(m > 0 && this.data[m-1] === v) { 131 | ++this.data[m]; 132 | } 133 | else { 134 | this.data.splice(m+1, 1, v, y+1, interval_val); 135 | } 136 | } 137 | else if(y === interval_end - 1) { 138 | if(m + 3 < this.data.length && this.data[m+3] === v) { 139 | --this.data[m+2]; 140 | } 141 | else { 142 | this.data.splice(m+2, 0, y, v); 143 | } 144 | } 145 | else { 146 | this.data.splice(m+2, 0, y, v, y+1, interval_val); 147 | } 148 | return interval_val; 149 | }; 150 | 151 | //Keeps track of voxel data 152 | function ChunkSet() { 153 | this.chunks = {}; 154 | Object.freeze(this); 155 | }; 156 | 157 | //Delete all old chunks 158 | ChunkSet.prototype.clear = function() { 159 | for(var c in this.chunks) { 160 | delete this.chunks[c]; 161 | } 162 | }; 163 | 164 | ChunkSet.prototype.insertChunk = function(chunk) { 165 | var nc = chunk; 166 | if(nc.constructor !== Chunk) { 167 | nc = new Chunk(chunk.x, chunk.y, chunk.z, chunk.data); 168 | } 169 | this.chunks[hashCode(chunk.x, chunk.y, chunk.z)] = nc; 170 | } 171 | 172 | ChunkSet.prototype.setChunk = function(cx,cy,cz,data) { 173 | var key = hashCode(cx,cy,cz), 174 | chunk = this.chunks[key]; 175 | if(chunk) { 176 | chunk.data = data; 177 | } 178 | else { 179 | chunk = new Chunk(cx,cy,cz,data); 180 | this.chunks[key] = chunk; 181 | } 182 | }; 183 | 184 | ChunkSet.prototype.getChunk = function(cx,cy,cz) { 185 | var h = hashCode(cx, cy, cz); 186 | if( h in this.chunks ) { 187 | return this.chunks[h]; 188 | } 189 | return null; 190 | }; 191 | 192 | ChunkSet.prototype.isPointMapped = function(x,y,z) { 193 | var cx = x>>CHUNK_SHIFT_X, 194 | cy = y>>CHUNK_SHIFT_Y, 195 | cz = z>>CHUNK_SHIFT_Z; 196 | return !!this.chunks[hashCode(cx,cy,cz)]; 197 | }; 198 | 199 | 200 | ChunkSet.prototype.isCellMapped = function(x,y,z) { 201 | var cx = x>>CHUNK_SHIFT_X, 202 | cy = y>>CHUNK_SHIFT_Y, 203 | cz = z>>CHUNK_SHIFT_Z, 204 | key = hashCode(cx,cy,cz), 205 | chunk = this.chunks[key]; 206 | 207 | if(!chunk) { 208 | return false; 209 | } 210 | 211 | //Look up position 212 | var ix = x& CHUNK_MASK_X, 213 | iy = y& CHUNK_MASK_Y, 214 | iz = z& CHUNK_MASK_Z, 215 | l_index = flattenIndex(ix, iy, iz), 216 | n = chunk.data.length, 217 | start_pos = chunk.bsearch(0, n, l_index); 218 | 219 | if(chunk.data[start_pos+1]) { 220 | return true; 221 | } 222 | 223 | var u_index = flattenIndex(ix+CELL_DIM,iy+CELL_DIM,iz+CELL_DIM), 224 | end_pos = chunk.bsearch(start_pos, n, u_index); 225 | return start_pos !== end_pos; 226 | }; 227 | 228 | 229 | ChunkSet.prototype.removeChunk = function(cx,cy,cz) { 230 | var key = hashCode(cx,cy,cz); 231 | if(key in this.chunks) { 232 | delete this.chunks[key]; 233 | } 234 | }; 235 | 236 | ChunkSet.prototype.set = function(x, y, z, v) { 237 | var cx = x>>CHUNK_SHIFT_X, 238 | cy = y>>CHUNK_SHIFT_Y, 239 | cz = z>>CHUNK_SHIFT_Z, 240 | ix = x& CHUNK_MASK_X, 241 | iy = y& CHUNK_MASK_Y, 242 | iz = z& CHUNK_MASK_Z; 243 | 244 | var key = hashCode(cx, cy, cz), 245 | chunk = this.chunks[key]; 246 | 247 | if(chunk) { 248 | var p = chunk.set(ix, iy, iz, v); 249 | if(chunk.isEmpty()) { 250 | delete this.chunks[key]; 251 | } 252 | return p; 253 | } 254 | else if(v !== 0) { 255 | chunk = new Chunk(cx, cy, cz); 256 | chunk.set(ix, iy, iz, v); 257 | this.chunks[key] = chunk; 258 | } 259 | return 0; 260 | }; 261 | 262 | ChunkSet.prototype.get = function(x, y, z) { 263 | var cx = x>>CHUNK_SHIFT_X, 264 | cy = y>>CHUNK_SHIFT_Y, 265 | cz = z>>CHUNK_SHIFT_Z; 266 | var chunk = this.getChunk(cx, cy, cz); 267 | if(chunk) { 268 | var ix = x& CHUNK_MASK_X, 269 | iy = y& CHUNK_MASK_Y, 270 | iz = z& CHUNK_MASK_Z; 271 | return chunk.get(ix, iy, iz); 272 | } 273 | return 0; 274 | }; 275 | 276 | //------------------------------------------------------------------------------ 277 | //Iterates over a range of values in the chunk set with varying neighborhood radii 278 | // 279 | // lo = lower bound of region 280 | // hi = upper bound of region 281 | // n = radius of window (actual window size is 2*n+1) 282 | // cb = User specified call back 283 | // 284 | // Arguments for cb: 285 | // x,y,z - Start of run 286 | // window - Values within window (flattened in xzy order) 287 | // lenght - Length of run 288 | // 289 | //------------------------------------------------------------------------------ 290 | ChunkSet.prototype.rangeForeach = function(lo, hi, n, cb) { 291 | var nmod = 2*n+1, 292 | size = nmod*nmod*nmod, 293 | itersize = nmod*nmod, 294 | iterators = new Array(itersize), 295 | vals = new Array(size), 296 | i, j, k, l, m, step, v, dx, dy, dz, can_skip; 297 | 298 | i = 0; 299 | for(dy=-n; dy<=n; ++dy) 300 | for(dz=-n; dz<=n; ++dz) { 301 | iterators[i++] = new ChunkIterator(this, lo[0]-n, lo[1]+dy, lo[2]+dz); 302 | } 303 | 304 | j=lo[1]; 305 | while(true) { 306 | k=lo[2]; 307 | while(true) { 308 | 309 | //Initialize window 310 | for(var l=0, m=1; l 0) { 385 | can_skip = vals[m+l-1] === vals[m+l]; 386 | } 387 | } 388 | } 389 | } 390 | if(vstep > 0) { 391 | cb(vi, j, k, vals, vstep); 392 | } 393 | 394 | //Check if this span is done 395 | if(vi + vstep >= hi[0]) { 396 | break; 397 | } 398 | 399 | //Otherwise move iterators 400 | i += step; 401 | for(l=0; l= hi[2]) 407 | break; 408 | for(l=0; l= hi[1]) 414 | break; 415 | for(l=0; l> CHUNK_SHIFT_X; 425 | this.cy = y >> CHUNK_SHIFT_Y; 426 | this.cz = z >> CHUNK_SHIFT_Z; 427 | this.ix = x & CHUNK_MASK_X; 428 | this.iy = y & CHUNK_MASK_Y; 429 | this.iz = z & CHUNK_MASK_Z; 430 | 431 | this.chunk = this.chunk_set.getChunk(this.cx, this.cy, this.cz); 432 | this.index = flattenIndex(this.ix, this.iy, this.iz); 433 | if(this.chunk !== null) { 434 | this.data_pos = this.chunk.bsearch(0, this.chunk.data.length, this.index); 435 | } 436 | else { 437 | this.data_pos = 0; 438 | } 439 | }; 440 | 441 | ChunkIterator.prototype.value = function() { 442 | if(this.chunk) { 443 | return this.chunk.data[this.data_pos+1]; 444 | } 445 | return 0; 446 | }; 447 | 448 | ChunkIterator.prototype.span = function() { 449 | var s = CHUNK_X - this.ix; 450 | if(this.chunk) { 451 | if(this.data_pos + 2 >= this.chunk.data.length) { 452 | var t = CHUNK_SIZE - this.index; 453 | if(t < s) { 454 | return t; 455 | } 456 | return s; 457 | } 458 | var t = this.chunk.data[this.data_pos+2] - this.index; 459 | if(t < s) { 460 | return t; 461 | } 462 | return s; 463 | } 464 | return s; 465 | }; 466 | 467 | ChunkIterator.prototype.coordinate = function() { 468 | return [ 469 | (this.cx<= CHUNK_X || 486 | ny < 0 || ny >= CHUNK_Y || 487 | nz < 0 || nz >= CHUNK_Z ) { 488 | 489 | this.cx = this.cx + (nx >> CHUNK_SHIFT_X); 490 | this.cy = this.cy + (ny >> CHUNK_SHIFT_Y); 491 | this.cz = this.cz + (nz >> CHUNK_SHIFT_Z); 492 | 493 | this.chunk = this.chunk_set.getChunk(this.cx, this.cy, this.cz); 494 | this.index = flattenIndex(this.ix, this.iy, this.iz); 495 | if(this.chunk !== null) { 496 | this.data_pos = this.chunk.bsearch(0, this.chunk.data.length, this.index); 497 | } 498 | else { 499 | this.data_pos = 0; 500 | } 501 | return; 502 | } 503 | 504 | //Otherwise, we are still in the same chunk, so we can save some time 505 | var pindex = this.index; 506 | this.index = flattenIndex(this.ix, this.iy, this.iz); 507 | 508 | //No chunk case 509 | if(!this.chunk) { 510 | this.data_pos = 0; 511 | return; 512 | } 513 | //Moved forward case 514 | else if(this.index >= pindex) { 515 | 516 | //Optimization: For moving at most 1 run forward/backward, don't do a full binary search 517 | // This saves a log factor when executing rangeForeach 518 | var p0 = this.data_pos + 2, 519 | N = this.chunk.data.length; 520 | if(p0 >= N) { 521 | return; 522 | } 523 | var i0 = this.chunk.data[p0]; 524 | if(this.index < i0) { 525 | return; 526 | } 527 | var p1 = this.data_pos + 4; 528 | if(p1 >= N || this.index < this.chunk.data[p1]) { 529 | this.data_pos = p0; 530 | return; 531 | } 532 | 533 | //Fallback: Moved more than 1 range, so do a binary search 534 | this.data_pos = this.chunk.bsearch(p1, N, this.index); 535 | } 536 | //Moved backward case 537 | else { 538 | 539 | //Check if still in same range 540 | var i0 = this.chunk.data[this.data_pos]; 541 | if(this.index >= i0) { 542 | return; 543 | } 544 | 545 | //Fallback: Do a binary search 546 | this.data_pos = this.chunk.bsearch(0, this.data_pos, this.index); 547 | } 548 | }; 549 | 550 | 551 | //Declare public methods 552 | exports.CHUNK_SHIFT_X = CHUNK_SHIFT_X; 553 | exports.CHUNK_SHIFT_Y = CHUNK_SHIFT_Y; 554 | exports.CHUNK_SHIFT_Z = CHUNK_SHIFT_Z; 555 | exports.CHUNK_X = CHUNK_X; 556 | exports.CHUNK_Y = CHUNK_Y; 557 | exports.CHUNK_Z = CHUNK_Z; 558 | exports.CHUNK_MASK_X = CHUNK_MASK_X; 559 | exports.CHUNK_MASK_Y = CHUNK_MASK_Y; 560 | exports.CHUNK_MASK_Z = CHUNK_MASK_Z; 561 | exports.CHUNK_SIZE = CHUNK_SIZE; 562 | exports.CELL_SHIFT = 4; 563 | exports.CELL_DIM = (1<> this.height) + 12 | ((j & bit) >> (this.height-1)) + 13 | ((k & bit) >> (this.height-2)); 14 | if(idx in this.children) { 15 | if(this.height > 0) { 16 | return this.children[idx].get(i, j, k); 17 | } 18 | return this.children[idx]; 19 | } 20 | return 0; 21 | } 22 | 23 | Octree.prototype.set = function(i,j,k,v) { 24 | var bit = (1 << this.height), 25 | idx = ((i & bit) >> this.height) + 26 | ((j & bit) >> (this.height-1)) + 27 | ((k & bit) >> (this.height-2)); 28 | 29 | if(this.height > 0) { 30 | if(idx in this.children) { 31 | return this.children[idx].set(i, j, k, v); 32 | } 33 | else { 34 | this.children[idx] = new Octree(this.height - 1); 35 | return this.children[idx].set(i,j,k,v); 36 | } 37 | } 38 | else { 39 | return this.children[idx] = v; 40 | } 41 | } 42 | 43 | exports.ChunkSet = Octree; 44 | -------------------------------------------------------------------------------- /MineBench/virtual.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var CHUNK_SHIFT_X = 5, 4 | CHUNK_SHIFT_Y = 5, 5 | CHUNK_SHIFT_Z = 5, 6 | CHUNK_X = (1 << CHUNK_SHIFT_X), 7 | CHUNK_Y = (1 << CHUNK_SHIFT_Y), 8 | CHUNK_Z = (1 << CHUNK_SHIFT_Z), 9 | CHUNK_SIZE = CHUNK_X * CHUNK_Y * CHUNK_Z, 10 | CHUNK_MASK_X = CHUNK_X - 1, 11 | CHUNK_MASK_Y = CHUNK_Y - 1, 12 | CHUNK_MASK_Z = CHUNK_Z - 1; 13 | 14 | function expand(x) { 15 | x &= 0x3FF; 16 | x = (x | (x<<16)) & 4278190335; 17 | x = (x | (x<<8)) & 251719695; 18 | x = (x | (x<<4)) & 3272356035; 19 | x = (x | (x<<2)) & 1227133513; 20 | return x; 21 | }; 22 | 23 | function hashCode(i,j,k) { 24 | return expand(i)+(expand(j)<<1)+(expand(k)<<2); 25 | }; 26 | 27 | 28 | function ChunkSet() { 29 | this.chunks = []; 30 | Object.freeze(this); 31 | } 32 | 33 | ChunkSet.prototype.get = function(i,j,k) { 34 | var cx = i >> CHUNK_SHIFT_X, 35 | cy = j >> CHUNK_SHIFT_Y, 36 | cz = k >> CHUNK_SHIFT_Z, 37 | chunk = this.chunks[hashCode(cx, cy, cz)]; 38 | if(chunk) { 39 | return chunk[ (i&CHUNK_MASK_X) + ((((k&CHUNK_MASK_Z)<< CHUNK_SHIFT_Y)+(j&CHUNK_MASK_Y)) << CHUNK_SHIFT_X)]; 40 | } 41 | return 0; 42 | } 43 | 44 | ChunkSet.prototype.set = function(i,j,k,v) { 45 | var cx = i >> CHUNK_SHIFT_X, 46 | cy = j >> CHUNK_SHIFT_Y, 47 | cz = k >> CHUNK_SHIFT_Z, 48 | key = hashCode(cx, cy, cz), 49 | chunk = this.chunks[key]; 50 | if(!chunk) { 51 | chunk = this.chunks[key] = new Array(CHUNK_SIZE); 52 | for(var l=CHUNK_SIZE-1; l>=0; --l) { 53 | chunk[l] = 0; 54 | } 55 | } 56 | return chunk[ (i&CHUNK_MASK_X) + ((((k&CHUNK_MASK_Z)<< CHUNK_SHIFT_Y)+(j&CHUNK_MASK_Y)) << CHUNK_SHIFT_X)] = v; 57 | } 58 | 59 | exports.ChunkSet = ChunkSet; 60 | 61 | -------------------------------------------------------------------------------- /Polynomials/Part1/main.cpp: -------------------------------------------------------------------------------- 1 | // Compile with: 2 | // 3 | // g++ --std=c++0x main.cpp 4 | // 5 | 6 | #include 7 | #include "symmetric_tensor.h" 8 | 9 | using namespace std; 10 | 11 | int main(int argc, char** argv) { 12 | 13 | SymmetricIndexIterator<3,4> iter, end(-1); 14 | 15 | 16 | //Test basic iteration 17 | cout << "Array\tTensor\tDegree\tComputed Array" << endl; 18 | while(iter != end) { 19 | 20 | auto tsr = iter.tensor_index(); 21 | auto deg = iter.degree_index(); 22 | auto arr = iter.array_index(); 23 | 24 | //Print out array index 25 | cout << arr << "\t"; 26 | 27 | //Print out tensor index 28 | cout << "("; 29 | for(int i=0; i 0) cout << ','; 31 | cout << tsr[i]; 32 | } 33 | cout << ")\t"; 34 | 35 | //Print out degree index 36 | cout << "("; 37 | for(int i=0; i 0) cout << ','; 39 | cout << deg[i]; 40 | } 41 | cout << ")\t"; 42 | 43 | //Print out computed index 44 | cout << iter.degree_to_array(deg) << endl; 45 | 46 | ++iter; 47 | } 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /Polynomials/Part1/symmetric_tensor.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMMETRIC_TENSOR_H 2 | #define SYMMETRIC_TENSOR_H 3 | 4 | #include 5 | 6 | //Combinatorial helper methods 7 | inline int factorial(int n) { 8 | int r = 1; 9 | for(int i=2; i<=n; ++i) 10 | r *= i; 11 | return r; 12 | } 13 | 14 | //Index iterator for tensors 15 | template 16 | struct SymmetricIndexIterator { 17 | 18 | static_assert(R >= 0, "Rank must be nonnegative"); 19 | static_assert(D > 0, "Dimension must be greater than 0"); 20 | 21 | //Constants 22 | enum { 23 | rank = R, 24 | dimension = D, 25 | }; 26 | 27 | //Type macros 28 | typedef std::array DegreeIndex; 29 | typedef std::array TensorIndex; 30 | 31 | //Constructors 32 | SymmetricIndexIterator() : index_(0) { 33 | for(int i=0; i(SymmetricIndexIterator const& other) const { return index_ > other.index_; } 47 | inline bool operator>=(SymmetricIndexIterator const& other) const { return index_ >= other.index_; } 48 | 49 | //Iteration 50 | SymmetricIndexIterator& operator++() { 51 | for(int i=0; i=0; --j) { 56 | seq_[j] = seq_[i]; 57 | } 58 | return *this; 59 | } 60 | } 61 | index_ = -1; 62 | return *this; 63 | } 64 | 65 | //Postfix iteration 66 | inline SymmetricIndexIterator operator++(int) { 67 | auto tmp = *this; 68 | ++*this; 69 | return tmp; 70 | } 71 | 72 | //Accessors 73 | inline DegreeIndex degree_index() const { return tensor_to_degree(seq_); } 74 | inline TensorIndex const& tensor_index() const { return seq_; } 75 | inline int array_index() const { return index_; } 76 | 77 | //Helper methods 78 | static inline int degree_to_array(DegreeIndex const& degrees) { 79 | int index = 0, sum = 0; 80 | for(int i=dimension-1; i>0; --i) { 81 | sum += degrees[i]; 82 | int w = 1; 83 | for(int j=0; j 2 | #include "symmetric_tensor.h" 3 | 4 | using namespace std; 5 | 6 | int main(int argc, char** argv) { 7 | 8 | //Create the linear polynomial, 3x + 5y" 9 | SymmetricTensor linear_poly; 10 | linear_poly({1,0}) = 3; 11 | linear_poly({0,1}) = 5; 12 | cout << "Linear poly = " << linear_poly << endl; 13 | 14 | SymmetricTensor B; 15 | B({1,0}) = 0; 16 | B({0,1}) = 1; 17 | cout << "B = " << B << endl; 18 | cout << "A*B = " << linear_poly.outer_product(B) << endl; 19 | 20 | //Create a quadratic polynomial, 2x^2 + xy - 3y^2 21 | SymmetricTensor quadratic_poly; 22 | quadratic_poly({2,0}) = 2; 23 | quadratic_poly({1,1}) = 1; 24 | quadratic_poly({0,2}) = -3; 25 | cout << "Quadratic poly = " << quadratic_poly << endl; 26 | 27 | 28 | //Compute product 29 | auto prod = quadratic_poly.outer_product(linear_poly); 30 | cout << "Product = " << prod << endl; 31 | 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Polynomials/Part2/symmetric_tensor.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMMETRIC_TENSOR_H 2 | #define SYMMETRIC_TENSOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | //Work around for broken const expr on g++ 4.4 9 | template 10 | struct Factorial { enum { value = n * Factorial::value }; }; 11 | 12 | template<> 13 | struct Factorial<0> { enum { value = 1 }; }; 14 | 15 | template 16 | struct Binomial { enum { value = Factorial::value / (Factorial::value * Factorial::value) }; }; 17 | 18 | //Combinatorial helper methods 19 | inline int factorial(int n) { 20 | int r = 1; 21 | for(int i=2; i<=n; ++i) 22 | r *= i; 23 | return r; 24 | } 25 | 26 | template 27 | inline int multinomial(std::array const& coeff) { 28 | int denom = 1, n = 0; 29 | for(int j=0; j 38 | struct SymmetricIndexIterator { 39 | 40 | static_assert(R >= 0, "Rank must be nonnegative"); 41 | static_assert(D > 0, "Dimension must be greater than 0"); 42 | 43 | //Constants 44 | enum { 45 | rank = R, 46 | dimension = D, 47 | }; 48 | 49 | //Type macros 50 | typedef std::array DegreeIndex; 51 | typedef std::array TensorIndex; 52 | 53 | //Constructors 54 | SymmetricIndexIterator() : index_(0) { 55 | for(int i=0; i(SymmetricIndexIterator const& other) const { return index_ > other.index_; } 69 | inline bool operator>=(SymmetricIndexIterator const& other) const { return index_ >= other.index_; } 70 | 71 | //Iteration 72 | SymmetricIndexIterator& operator++() { 73 | for(int i=0; i=0; --j) { 78 | seq_[j] = seq_[i]; 79 | } 80 | return *this; 81 | } 82 | } 83 | index_ = -1; 84 | return *this; 85 | } 86 | 87 | //Postfix iteration 88 | inline SymmetricIndexIterator operator++(int) { 89 | auto tmp = *this; 90 | ++*this; 91 | return tmp; 92 | } 93 | 94 | //Accessors 95 | inline DegreeIndex degree_index() const { return tensor_to_degree(seq_); } 96 | inline TensorIndex const& tensor_index() const { return seq_; } 97 | inline int array_index() const { return index_; } 98 | 99 | //Helper methods 100 | static inline int degree_to_array(DegreeIndex const& degrees) { 101 | int index = 0, sum = 0; 102 | for(int i=dimension-1; i>0; --i) { 103 | sum += degrees[i]; 104 | int w = 1; 105 | for(int j=0; j 142 | struct SymmetricTensor { 143 | 144 | static_assert(R >= 0, "Rank must be nonnegative"); 145 | static_assert(D > 0, "Dimension must be greater than 0"); 146 | 147 | //Type macros 148 | typedef Scalar_t Scalar; 149 | typedef SymmetricIndexIterator BaseIterator; 150 | typedef typename BaseIterator::DegreeIndex DegreeIndex; 151 | typedef typename BaseIterator::TensorIndex TensorIndex; 152 | 153 | //Constants 154 | enum { 155 | rank = BaseIterator::rank, 156 | dimension = BaseIterator::dimension, 157 | size = Binomial::value 158 | }; 159 | 160 | //Iterator type 161 | friend class Iterator; 162 | struct Iterator : public BaseIterator { 163 | Iterator(BaseIterator const& base, SymmetricTensor* t) : 164 | BaseIterator(base), owner(t) { } 165 | inline Scalar& operator*() { return owner->coefficients[this->array_index()]; } 166 | private: 167 | SymmetricTensor* owner; 168 | }; 169 | 170 | //Const iterator type 171 | friend class ConstIterator; 172 | struct ConstIterator : public BaseIterator { 173 | ConstIterator(BaseIterator const& base, const SymmetricTensor* t) : 174 | BaseIterator(base), owner(t) { } 175 | inline const Scalar& operator*() { return owner->coefficients[this->array_index()]; } 176 | private: 177 | const SymmetricTensor* owner; 178 | }; 179 | 180 | //Iterators 181 | inline Iterator begin() { return Iterator(BaseIterator(), this); } 182 | inline Iterator end() { return Iterator(BaseIterator(-1), this); } 183 | inline Iterator degree_index(DegreeIndex const& deg) { return Iterator(BaseIterator(deg), this); } 184 | inline Iterator tensor_index(TensorIndex const& i) { return Iterator(BaseIterator(BaseIterator::tensor_to_degree(i)), this); } 185 | inline ConstIterator begin() const { return ConstIterator(BaseIterator(), this); } 186 | inline ConstIterator end() const { return ConstIterator(BaseIterator(-1), this); } 187 | inline ConstIterator degree_index(DegreeIndex const& deg) const { return ConstIterator(BaseIterator(deg), this); } 188 | inline ConstIterator tensor_index(TensorIndex const& i) const { return ConstIterator(BaseIterator(BaseIterator::tensor_to_degree(i)), this); } 189 | 190 | //Coordinate indexing (by degree) 191 | inline Scalar const& operator()(std::initializer_list const& lst) const { 192 | DegreeIndex deg; 193 | std::copy(lst.begin(), lst.end(), deg.begin()); 194 | return coefficients[BaseIterator::degree_to_array(deg)]; 195 | } 196 | inline Scalar& operator()(std::initializer_list const& lst) { 197 | DegreeIndex deg; 198 | std::copy(lst.begin(), lst.end(), deg.begin()); 199 | return coefficients[BaseIterator::degree_to_array(deg)]; 200 | } 201 | 202 | //Tensor addition 203 | SymmetricTensor operator+(SymmetricTensor const& other) const { 204 | SymmetricTensor result; 205 | for(int i=0; i 224 | SymmetricTensor 225 | inner_product(SymmetricTensor const& other) const { 226 | 227 | enum { final_rank = rank - other_rank }; 228 | static_assert(final_rank >= 0, "Invalid tensor size for index contraction."); 229 | typedef SymmetricTensor A_type; 230 | typedef SymmetricTensor B_type; 231 | typedef SymmetricTensor C_type; 232 | 233 | C_type result; 234 | for(auto iter=result.begin(); iter!=result.end(); ++iter) { 235 | Scalar w(0); 236 | for(auto iter2=other.begin(); iter2!=other.end(); ++iter2) { 237 | DegreeIndex a, b = iter2.degrees(), c = iter.degrees(); 238 | for(int i=0; i 250 | SymmetricTensor 251 | outer_product(SymmetricTensor const& other) const { 252 | 253 | enum { final_rank = rank + other_rank }; 254 | typedef SymmetricTensor A_type; 255 | typedef SymmetricTensor B_type; 256 | typedef SymmetricTensor C_type; 257 | 258 | struct Visitor { 259 | A_type const& A; 260 | B_type const& B; 261 | DegreeIndex v, a, vpartial, apartial; 262 | Scalar w; 263 | 264 | Visitor(DegreeIndex const& vin, A_type const& Ain, B_type const& Bin) : 265 | A(Ain), B(Bin), v(vin), w(0) { 266 | for(int i=0; i 0) { 270 | vpartial[dimension - i-1] = vpartial[dimension - (i+2)] + v[dimension - (i+1)]; 271 | } 272 | else { 273 | vpartial[dimension - i-1] = 0; 274 | } 275 | } 276 | } 277 | 278 | void visit(const int k) { 279 | 280 | using namespace std; //TODO: REMOVE ME 281 | 282 | 283 | if(k == dimension) { 284 | DegreeIndex b; 285 | cout << "Visiting: " << endl; 286 | for(int i=0; i(a)) * (*A.degree_index(a)); 292 | auto b_val = Scalar(multinomial(b)) * (*B.degree_index(b)); 293 | 294 | cout << "a_val = " << a_val << endl 295 | << "b_val = " << b_val << endl; 296 | 297 | w += a_val * b_val; 298 | return; 299 | } 300 | 301 | if(k > 0) { 302 | apartial[k] = apartial[k-1] - a[k-1]; 303 | } else { 304 | apartial[k] = A_type::rank; 305 | } 306 | int l = max(0, apartial[k] - vpartial[k]), u = min(v[k], v[k] + apartial[k]); 307 | 308 | cout << "Looping k = " << k << ", lb = " << l << ", ub = " << u 309 | << ", apartial[k] = " << apartial[k] << ", vpartial[k] = " << vpartial[k] << endl; 310 | 311 | 312 | for(a[k] = l; a[k] <= u; ++a[k]) { 313 | visit(k+1); 314 | } 315 | } 316 | }; 317 | 318 | C_type result; 319 | for(auto iter=result.begin(); iter!=result.end(); ++iter) { 320 | auto deg = iter.degree_index(); 321 | Visitor visitor(deg, *this, other); 322 | visitor.visit(0); 323 | *iter = visitor.w / Scalar(multinomial(deg)); 324 | } 325 | return result; 326 | } 327 | 328 | private: 329 | //Coefficients 330 | Scalar coefficients[size]; 331 | }; 332 | 333 | //Prints out the components of a tensor as a polynomial in pretty latex formatting 334 | template 335 | std::ostream& operator<<(std::ostream& os, SymmetricTensor const& tensor) { 336 | bool need_plus = false; 337 | 338 | for(auto iter=tensor.begin(); iter!=tensor.end(); ++iter) { 339 | 340 | auto deg = iter.degree_index(); 341 | auto coef = Scalar(multinomial(deg)) * (*iter); 342 | 343 | if(coef == 0) { 344 | continue; 345 | } else if(need_plus) { 346 | if(coef > 0) { 347 | os << " + "; 348 | if( coef != 1 ) os << coef; 349 | } else { 350 | os << " - "; 351 | if( coef != -1 ) os << -coef; 352 | } 353 | } else if(coef == -1) { 354 | os << '-'; 355 | } else if(coef != 1) { 356 | os << coef; 357 | } 358 | need_plus = true; 359 | 360 | bool need_space = false; 361 | for(int i=0; i= 10) { 368 | os << "x_{" << i << '}'; 369 | } else { 370 | os << "x_" << i; 371 | } 372 | if(deg[i] >= 10) { 373 | os << "^{" << deg[i] << '}'; 374 | } else if(deg[i] > 1) { 375 | os << '^' << deg[i]; 376 | } 377 | need_space = true; 378 | } 379 | } 380 | return os; 381 | } 382 | 383 | #endif 384 | 385 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Code and examples for 0fps.wordpress.com 2 | --------------------------------------------------------------------------------