├── .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 |
196 |
--------------------------------------------------------------------------------
/Graphics/mesh1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/mesh1.png
--------------------------------------------------------------------------------
/Graphics/mesh1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
147 |
--------------------------------------------------------------------------------
/Graphics/mesh2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/mesh2.png
--------------------------------------------------------------------------------
/Graphics/mesh2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
167 |
--------------------------------------------------------------------------------
/Graphics/octree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/octree.png
--------------------------------------------------------------------------------
/Graphics/octree.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
151 |
--------------------------------------------------------------------------------
/Graphics/virtual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikolalysenko/0fpsBlog/146be85103086a8cfdeb7288b150f155ab8ae8b2/Graphics/virtual.png
--------------------------------------------------------------------------------
/Graphics/virtual.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 |
--------------------------------------------------------------------------------