made with requirebin
Created
August 17, 2016 19:00
-
-
Save mikolalysenko/921152d4d15b5d57351a665da6d274a7 to your computer and use it in GitHub Desktop.
requirebin sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const polytope = require('conway-hart')('tI') | |
const vec3 = require('gl-vec3') | |
const HEIGHT = 6 | |
const bounds = require('bound-points')(polytope.positions) | |
const radius = Math.max( | |
bounds[1][0] - bounds[0][0], | |
bounds[1][1] - bounds[0][1], | |
bounds[1][2] - bounds[0][2]) | |
const scale = HEIGHT / radius | |
const edges = [] | |
polytope.cells.forEach((c) => { | |
for (let i = 0; i < c.length; ++i) { | |
edges.push([c[i], c[(i + 1) % c.length]]) | |
} | |
}) | |
const lengths = edges.map(([i, j]) => | |
vec3.length(vec3.subtract([], polytope.positions[i], polytope.positions[j]))) | |
lengths.sort(function (a, b) { | |
return a - b | |
}) | |
const histogram = {} | |
let total = 0 | |
lengths.forEach((l) => { | |
const x = Math.round(scale * l * 1000.0) / 1000.0 | |
histogram[x] = (histogram[x] | 0) + 1 | |
total += x | |
}) | |
Object.keys(histogram).forEach(function (l) { | |
histogram[l] /= 2 | |
}) | |
if (typeof document !== 'undefined') { | |
document.body.appendChild(document.createTextNode( | |
`dome height: ${HEIGHT} lengths: ${JSON.stringify(histogram)} total: ${total / 2}`)) | |
} | |
console.log('dome height: ', HEIGHT) | |
console.log('lengths: ', histogram) | |
console.log('total: ', total / 2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
setTimeout(function(){ | |
;require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
module.exports = add; | |
/** | |
* Adds two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function add(out, a, b) { | |
out[0] = a[0] + b[0] | |
out[1] = a[1] + b[1] | |
out[2] = a[2] + b[2] | |
return out | |
} | |
},{}],2:[function(require,module,exports){ | |
module.exports = angle | |
var fromValues = require('./fromValues') | |
var normalize = require('./normalize') | |
var dot = require('./dot') | |
/** | |
* Get the angle between two 3D vectors | |
* @param {vec3} a The first operand | |
* @param {vec3} b The second operand | |
* @returns {Number} The angle in radians | |
*/ | |
function angle(a, b) { | |
var tempA = fromValues(a[0], a[1], a[2]) | |
var tempB = fromValues(b[0], b[1], b[2]) | |
normalize(tempA, tempA) | |
normalize(tempB, tempB) | |
var cosine = dot(tempA, tempB) | |
if(cosine > 1.0){ | |
return 0 | |
} else { | |
return Math.acos(cosine) | |
} | |
} | |
},{"./dot":9,"./fromValues":11,"./normalize":19}],3:[function(require,module,exports){ | |
module.exports = clone; | |
/** | |
* Creates a new vec3 initialized with values from an existing vector | |
* | |
* @param {vec3} a vector to clone | |
* @returns {vec3} a new 3D vector | |
*/ | |
function clone(a) { | |
var out = new Float32Array(3) | |
out[0] = a[0] | |
out[1] = a[1] | |
out[2] = a[2] | |
return out | |
} | |
},{}],4:[function(require,module,exports){ | |
module.exports = copy; | |
/** | |
* Copy the values from one vec3 to another | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the source vector | |
* @returns {vec3} out | |
*/ | |
function copy(out, a) { | |
out[0] = a[0] | |
out[1] = a[1] | |
out[2] = a[2] | |
return out | |
} | |
},{}],5:[function(require,module,exports){ | |
module.exports = create; | |
/** | |
* Creates a new, empty vec3 | |
* | |
* @returns {vec3} a new 3D vector | |
*/ | |
function create() { | |
var out = new Float32Array(3) | |
out[0] = 0 | |
out[1] = 0 | |
out[2] = 0 | |
return out | |
} | |
},{}],6:[function(require,module,exports){ | |
module.exports = cross; | |
/** | |
* Computes the cross product of two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function cross(out, a, b) { | |
var ax = a[0], ay = a[1], az = a[2], | |
bx = b[0], by = b[1], bz = b[2] | |
out[0] = ay * bz - az * by | |
out[1] = az * bx - ax * bz | |
out[2] = ax * by - ay * bx | |
return out | |
} | |
},{}],7:[function(require,module,exports){ | |
module.exports = distance; | |
/** | |
* Calculates the euclidian distance between two vec3's | |
* | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {Number} distance between a and b | |
*/ | |
function distance(a, b) { | |
var x = b[0] - a[0], | |
y = b[1] - a[1], | |
z = b[2] - a[2] | |
return Math.sqrt(x*x + y*y + z*z) | |
} | |
},{}],8:[function(require,module,exports){ | |
module.exports = divide; | |
/** | |
* Divides two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function divide(out, a, b) { | |
out[0] = a[0] / b[0] | |
out[1] = a[1] / b[1] | |
out[2] = a[2] / b[2] | |
return out | |
} | |
},{}],9:[function(require,module,exports){ | |
module.exports = dot; | |
/** | |
* Calculates the dot product of two vec3's | |
* | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {Number} dot product of a and b | |
*/ | |
function dot(a, b) { | |
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] | |
} | |
},{}],10:[function(require,module,exports){ | |
module.exports = forEach; | |
var vec = require('./create')() | |
/** | |
* Perform some operation over an array of vec3s. | |
* | |
* @param {Array} a the array of vectors to iterate over | |
* @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed | |
* @param {Number} offset Number of elements to skip at the beginning of the array | |
* @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array | |
* @param {Function} fn Function to call for each vector in the array | |
* @param {Object} [arg] additional argument to pass to fn | |
* @returns {Array} a | |
* @function | |
*/ | |
function forEach(a, stride, offset, count, fn, arg) { | |
var i, l | |
if(!stride) { | |
stride = 3 | |
} | |
if(!offset) { | |
offset = 0 | |
} | |
if(count) { | |
l = Math.min((count * stride) + offset, a.length) | |
} else { | |
l = a.length | |
} | |
for(i = offset; i < l; i += stride) { | |
vec[0] = a[i] | |
vec[1] = a[i+1] | |
vec[2] = a[i+2] | |
fn(vec, vec, arg) | |
a[i] = vec[0] | |
a[i+1] = vec[1] | |
a[i+2] = vec[2] | |
} | |
return a | |
} | |
},{"./create":5}],11:[function(require,module,exports){ | |
module.exports = fromValues; | |
/** | |
* Creates a new vec3 initialized with the given values | |
* | |
* @param {Number} x X component | |
* @param {Number} y Y component | |
* @param {Number} z Z component | |
* @returns {vec3} a new 3D vector | |
*/ | |
function fromValues(x, y, z) { | |
var out = new Float32Array(3) | |
out[0] = x | |
out[1] = y | |
out[2] = z | |
return out | |
} | |
},{}],12:[function(require,module,exports){ | |
module.exports = inverse; | |
/** | |
* Returns the inverse of the components of a vec3 | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a vector to invert | |
* @returns {vec3} out | |
*/ | |
function inverse(out, a) { | |
out[0] = 1.0 / a[0] | |
out[1] = 1.0 / a[1] | |
out[2] = 1.0 / a[2] | |
return out | |
} | |
},{}],13:[function(require,module,exports){ | |
module.exports = length; | |
/** | |
* Calculates the length of a vec3 | |
* | |
* @param {vec3} a vector to calculate length of | |
* @returns {Number} length of a | |
*/ | |
function length(a) { | |
var x = a[0], | |
y = a[1], | |
z = a[2] | |
return Math.sqrt(x*x + y*y + z*z) | |
} | |
},{}],14:[function(require,module,exports){ | |
module.exports = lerp; | |
/** | |
* Performs a linear interpolation between two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @param {Number} t interpolation amount between the two inputs | |
* @returns {vec3} out | |
*/ | |
function lerp(out, a, b, t) { | |
var ax = a[0], | |
ay = a[1], | |
az = a[2] | |
out[0] = ax + t * (b[0] - ax) | |
out[1] = ay + t * (b[1] - ay) | |
out[2] = az + t * (b[2] - az) | |
return out | |
} | |
},{}],15:[function(require,module,exports){ | |
module.exports = max; | |
/** | |
* Returns the maximum of two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function max(out, a, b) { | |
out[0] = Math.max(a[0], b[0]) | |
out[1] = Math.max(a[1], b[1]) | |
out[2] = Math.max(a[2], b[2]) | |
return out | |
} | |
},{}],16:[function(require,module,exports){ | |
module.exports = min; | |
/** | |
* Returns the minimum of two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function min(out, a, b) { | |
out[0] = Math.min(a[0], b[0]) | |
out[1] = Math.min(a[1], b[1]) | |
out[2] = Math.min(a[2], b[2]) | |
return out | |
} | |
},{}],17:[function(require,module,exports){ | |
module.exports = multiply; | |
/** | |
* Multiplies two vec3's | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function multiply(out, a, b) { | |
out[0] = a[0] * b[0] | |
out[1] = a[1] * b[1] | |
out[2] = a[2] * b[2] | |
return out | |
} | |
},{}],18:[function(require,module,exports){ | |
module.exports = negate; | |
/** | |
* Negates the components of a vec3 | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a vector to negate | |
* @returns {vec3} out | |
*/ | |
function negate(out, a) { | |
out[0] = -a[0] | |
out[1] = -a[1] | |
out[2] = -a[2] | |
return out | |
} | |
},{}],19:[function(require,module,exports){ | |
module.exports = normalize; | |
/** | |
* Normalize a vec3 | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a vector to normalize | |
* @returns {vec3} out | |
*/ | |
function normalize(out, a) { | |
var x = a[0], | |
y = a[1], | |
z = a[2] | |
var len = x*x + y*y + z*z | |
if (len > 0) { | |
//TODO: evaluate use of glm_invsqrt here? | |
len = 1 / Math.sqrt(len) | |
out[0] = a[0] * len | |
out[1] = a[1] * len | |
out[2] = a[2] * len | |
} | |
return out | |
} | |
},{}],20:[function(require,module,exports){ | |
module.exports = random; | |
/** | |
* Generates a random vector with the given scale | |
* | |
* @param {vec3} out the receiving vector | |
* @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned | |
* @returns {vec3} out | |
*/ | |
function random(out, scale) { | |
scale = scale || 1.0 | |
var r = Math.random() * 2.0 * Math.PI | |
var z = (Math.random() * 2.0) - 1.0 | |
var zScale = Math.sqrt(1.0-z*z) * scale | |
out[0] = Math.cos(r) * zScale | |
out[1] = Math.sin(r) * zScale | |
out[2] = z * scale | |
return out | |
} | |
},{}],21:[function(require,module,exports){ | |
module.exports = rotateX; | |
/** | |
* Rotate a 3D vector around the x-axis | |
* @param {vec3} out The receiving vec3 | |
* @param {vec3} a The vec3 point to rotate | |
* @param {vec3} b The origin of the rotation | |
* @param {Number} c The angle of rotation | |
* @returns {vec3} out | |
*/ | |
function rotateX(out, a, b, c){ | |
var p = [], r=[] | |
//Translate point to the origin | |
p[0] = a[0] - b[0] | |
p[1] = a[1] - b[1] | |
p[2] = a[2] - b[2] | |
//perform rotation | |
r[0] = p[0] | |
r[1] = p[1]*Math.cos(c) - p[2]*Math.sin(c) | |
r[2] = p[1]*Math.sin(c) + p[2]*Math.cos(c) | |
//translate to correct position | |
out[0] = r[0] + b[0] | |
out[1] = r[1] + b[1] | |
out[2] = r[2] + b[2] | |
return out | |
} | |
},{}],22:[function(require,module,exports){ | |
module.exports = rotateY; | |
/** | |
* Rotate a 3D vector around the y-axis | |
* @param {vec3} out The receiving vec3 | |
* @param {vec3} a The vec3 point to rotate | |
* @param {vec3} b The origin of the rotation | |
* @param {Number} c The angle of rotation | |
* @returns {vec3} out | |
*/ | |
function rotateY(out, a, b, c){ | |
var p = [], r=[] | |
//Translate point to the origin | |
p[0] = a[0] - b[0] | |
p[1] = a[1] - b[1] | |
p[2] = a[2] - b[2] | |
//perform rotation | |
r[0] = p[2]*Math.sin(c) + p[0]*Math.cos(c) | |
r[1] = p[1] | |
r[2] = p[2]*Math.cos(c) - p[0]*Math.sin(c) | |
//translate to correct position | |
out[0] = r[0] + b[0] | |
out[1] = r[1] + b[1] | |
out[2] = r[2] + b[2] | |
return out | |
} | |
},{}],23:[function(require,module,exports){ | |
module.exports = rotateZ; | |
/** | |
* Rotate a 3D vector around the z-axis | |
* @param {vec3} out The receiving vec3 | |
* @param {vec3} a The vec3 point to rotate | |
* @param {vec3} b The origin of the rotation | |
* @param {Number} c The angle of rotation | |
* @returns {vec3} out | |
*/ | |
function rotateZ(out, a, b, c){ | |
var p = [], r=[] | |
//Translate point to the origin | |
p[0] = a[0] - b[0] | |
p[1] = a[1] - b[1] | |
p[2] = a[2] - b[2] | |
//perform rotation | |
r[0] = p[0]*Math.cos(c) - p[1]*Math.sin(c) | |
r[1] = p[0]*Math.sin(c) + p[1]*Math.cos(c) | |
r[2] = p[2] | |
//translate to correct position | |
out[0] = r[0] + b[0] | |
out[1] = r[1] + b[1] | |
out[2] = r[2] + b[2] | |
return out | |
} | |
},{}],24:[function(require,module,exports){ | |
module.exports = scale; | |
/** | |
* Scales a vec3 by a scalar number | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the vector to scale | |
* @param {Number} b amount to scale the vector by | |
* @returns {vec3} out | |
*/ | |
function scale(out, a, b) { | |
out[0] = a[0] * b | |
out[1] = a[1] * b | |
out[2] = a[2] * b | |
return out | |
} | |
},{}],25:[function(require,module,exports){ | |
module.exports = scaleAndAdd; | |
/** | |
* Adds two vec3's after scaling the second operand by a scalar value | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @param {Number} scale the amount to scale b by before adding | |
* @returns {vec3} out | |
*/ | |
function scaleAndAdd(out, a, b, scale) { | |
out[0] = a[0] + (b[0] * scale) | |
out[1] = a[1] + (b[1] * scale) | |
out[2] = a[2] + (b[2] * scale) | |
return out | |
} | |
},{}],26:[function(require,module,exports){ | |
module.exports = set; | |
/** | |
* Set the components of a vec3 to the given values | |
* | |
* @param {vec3} out the receiving vector | |
* @param {Number} x X component | |
* @param {Number} y Y component | |
* @param {Number} z Z component | |
* @returns {vec3} out | |
*/ | |
function set(out, x, y, z) { | |
out[0] = x | |
out[1] = y | |
out[2] = z | |
return out | |
} | |
},{}],27:[function(require,module,exports){ | |
module.exports = squaredDistance; | |
/** | |
* Calculates the squared euclidian distance between two vec3's | |
* | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {Number} squared distance between a and b | |
*/ | |
function squaredDistance(a, b) { | |
var x = b[0] - a[0], | |
y = b[1] - a[1], | |
z = b[2] - a[2] | |
return x*x + y*y + z*z | |
} | |
},{}],28:[function(require,module,exports){ | |
module.exports = squaredLength; | |
/** | |
* Calculates the squared length of a vec3 | |
* | |
* @param {vec3} a vector to calculate squared length of | |
* @returns {Number} squared length of a | |
*/ | |
function squaredLength(a) { | |
var x = a[0], | |
y = a[1], | |
z = a[2] | |
return x*x + y*y + z*z | |
} | |
},{}],29:[function(require,module,exports){ | |
module.exports = subtract; | |
/** | |
* Subtracts vector b from vector a | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the first operand | |
* @param {vec3} b the second operand | |
* @returns {vec3} out | |
*/ | |
function subtract(out, a, b) { | |
out[0] = a[0] - b[0] | |
out[1] = a[1] - b[1] | |
out[2] = a[2] - b[2] | |
return out | |
} | |
},{}],30:[function(require,module,exports){ | |
module.exports = transformMat3; | |
/** | |
* Transforms the vec3 with a mat3. | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the vector to transform | |
* @param {mat4} m the 3x3 matrix to transform with | |
* @returns {vec3} out | |
*/ | |
function transformMat3(out, a, m) { | |
var x = a[0], y = a[1], z = a[2] | |
out[0] = x * m[0] + y * m[3] + z * m[6] | |
out[1] = x * m[1] + y * m[4] + z * m[7] | |
out[2] = x * m[2] + y * m[5] + z * m[8] | |
return out | |
} | |
},{}],31:[function(require,module,exports){ | |
module.exports = transformMat4; | |
/** | |
* Transforms the vec3 with a mat4. | |
* 4th vector component is implicitly '1' | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the vector to transform | |
* @param {mat4} m matrix to transform with | |
* @returns {vec3} out | |
*/ | |
function transformMat4(out, a, m) { | |
var x = a[0], y = a[1], z = a[2], | |
w = m[3] * x + m[7] * y + m[11] * z + m[15] | |
w = w || 1.0 | |
out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w | |
out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w | |
out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w | |
return out | |
} | |
},{}],32:[function(require,module,exports){ | |
module.exports = transformQuat; | |
/** | |
* Transforms the vec3 with a quat | |
* | |
* @param {vec3} out the receiving vector | |
* @param {vec3} a the vector to transform | |
* @param {quat} q quaternion to transform with | |
* @returns {vec3} out | |
*/ | |
function transformQuat(out, a, q) { | |
// benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations | |
var x = a[0], y = a[1], z = a[2], | |
qx = q[0], qy = q[1], qz = q[2], qw = q[3], | |
// calculate quat * vec | |
ix = qw * x + qy * z - qz * y, | |
iy = qw * y + qz * x - qx * z, | |
iz = qw * z + qx * y - qy * x, | |
iw = -qx * x - qy * y - qz * z | |
// calculate result * inverse quat | |
out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy | |
out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz | |
out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx | |
return out | |
} | |
},{}],"gl-vec3":[function(require,module,exports){ | |
module.exports = { | |
create: require('./create') | |
, clone: require('./clone') | |
, angle: require('./angle') | |
, fromValues: require('./fromValues') | |
, copy: require('./copy') | |
, set: require('./set') | |
, add: require('./add') | |
, subtract: require('./subtract') | |
, multiply: require('./multiply') | |
, divide: require('./divide') | |
, min: require('./min') | |
, max: require('./max') | |
, scale: require('./scale') | |
, scaleAndAdd: require('./scaleAndAdd') | |
, distance: require('./distance') | |
, squaredDistance: require('./squaredDistance') | |
, length: require('./length') | |
, squaredLength: require('./squaredLength') | |
, negate: require('./negate') | |
, inverse: require('./inverse') | |
, normalize: require('./normalize') | |
, dot: require('./dot') | |
, cross: require('./cross') | |
, lerp: require('./lerp') | |
, random: require('./random') | |
, transformMat4: require('./transformMat4') | |
, transformMat3: require('./transformMat3') | |
, transformQuat: require('./transformQuat') | |
, rotateX: require('./rotateX') | |
, rotateY: require('./rotateY') | |
, rotateZ: require('./rotateZ') | |
, forEach: require('./forEach') | |
} | |
},{"./add":1,"./angle":2,"./clone":3,"./copy":4,"./create":5,"./cross":6,"./distance":7,"./divide":8,"./dot":9,"./forEach":10,"./fromValues":11,"./inverse":12,"./length":13,"./lerp":14,"./max":15,"./min":16,"./multiply":17,"./negate":18,"./normalize":19,"./random":20,"./rotateX":21,"./rotateY":22,"./rotateZ":23,"./scale":24,"./scaleAndAdd":25,"./set":26,"./squaredDistance":27,"./squaredLength":28,"./subtract":29,"./transformMat3":30,"./transformMat4":31,"./transformQuat":32}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImFkZC5qcyIsImFuZ2xlLmpzIiwiY2xvbmUuanMiLCJjb3B5LmpzIiwiY3JlYXRlLmpzIiwiY3Jvc3MuanMiLCJkaXN0YW5jZS5qcyIsImRpdmlkZS5qcyIsImRvdC5qcyIsImZvckVhY2guanMiLCJmcm9tVmFsdWVzLmpzIiwiaW52ZXJzZS5qcyIsImxlbmd0aC5qcyIsImxlcnAuanMiLCJtYXguanMiLCJtaW4uanMiLCJtdWx0aXBseS5qcyIsIm5lZ2F0ZS5qcyIsIm5vcm1hbGl6ZS5qcyIsInJhbmRvbS5qcyIsInJvdGF0ZVguanMiLCJyb3RhdGVZLmpzIiwicm90YXRlWi5qcyIsInNjYWxlLmpzIiwic2NhbGVBbmRBZGQuanMiLCJzZXQuanMiLCJzcXVhcmVkRGlzdGFuY2UuanMiLCJzcXVhcmVkTGVuZ3RoLmpzIiwic3VidHJhY3QuanMiLCJ0cmFuc2Zvcm1NYXQzLmpzIiwidHJhbnNmb3JtTWF0NC5qcyIsInRyYW5zZm9ybVF1YXQuanMiLCJpbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDYkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNkQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNmQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDYkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDYkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIm1vZHVsZS5leHBvcnRzID0gYWRkO1xuXG4vKipcbiAqIEFkZHMgdHdvIHZlYzMnc1xuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIGZpcnN0IG9wZXJhbmRcbiAqIEBwYXJhbSB7dmVjM30gYiB0aGUgc2Vjb25kIG9wZXJhbmRcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gYWRkKG91dCwgYSwgYikge1xuICAgIG91dFswXSA9IGFbMF0gKyBiWzBdXG4gICAgb3V0WzFdID0gYVsxXSArIGJbMV1cbiAgICBvdXRbMl0gPSBhWzJdICsgYlsyXVxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IGFuZ2xlXG5cbnZhciBmcm9tVmFsdWVzID0gcmVxdWlyZSgnLi9mcm9tVmFsdWVzJylcbnZhciBub3JtYWxpemUgPSByZXF1aXJlKCcuL25vcm1hbGl6ZScpXG52YXIgZG90ID0gcmVxdWlyZSgnLi9kb3QnKVxuXG4vKipcbiAqIEdldCB0aGUgYW5nbGUgYmV0d2VlbiB0d28gM0QgdmVjdG9yc1xuICogQHBhcmFtIHt2ZWMzfSBhIFRoZSBmaXJzdCBvcGVyYW5kXG4gKiBAcGFyYW0ge3ZlYzN9IGIgVGhlIHNlY29uZCBvcGVyYW5kXG4gKiBAcmV0dXJucyB7TnVtYmVyfSBUaGUgYW5nbGUgaW4gcmFkaWFuc1xuICovXG5mdW5jdGlvbiBhbmdsZShhLCBiKSB7XG4gICAgdmFyIHRlbXBBID0gZnJvbVZhbHVlcyhhWzBdLCBhWzFdLCBhWzJdKVxuICAgIHZhciB0ZW1wQiA9IGZyb21WYWx1ZXMoYlswXSwgYlsxXSwgYlsyXSlcbiBcbiAgICBub3JtYWxpemUodGVtcEEsIHRlbXBBKVxuICAgIG5vcm1hbGl6ZSh0ZW1wQiwgdGVtcEIpXG4gXG4gICAgdmFyIGNvc2luZSA9IGRvdCh0ZW1wQSwgdGVtcEIpXG5cbiAgICBpZihjb3NpbmUgPiAxLjApe1xuICAgICAgICByZXR1cm4gMFxuICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBNYXRoLmFjb3MoY29zaW5lKVxuICAgIH0gICAgIFxufVxuIiwibW9kdWxlLmV4cG9ydHMgPSBjbG9uZTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgbmV3IHZlYzMgaW5pdGlhbGl6ZWQgd2l0aCB2YWx1ZXMgZnJvbSBhbiBleGlzdGluZyB2ZWN0b3JcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdmVjdG9yIHRvIGNsb25lXG4gKiBAcmV0dXJucyB7dmVjM30gYSBuZXcgM0QgdmVjdG9yXG4gKi9cbmZ1bmN0aW9uIGNsb25lKGEpIHtcbiAgICB2YXIgb3V0ID0gbmV3IEZsb2F0MzJBcnJheSgzKVxuICAgIG91dFswXSA9IGFbMF1cbiAgICBvdXRbMV0gPSBhWzFdXG4gICAgb3V0WzJdID0gYVsyXVxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IGNvcHk7XG5cbi8qKlxuICogQ29weSB0aGUgdmFsdWVzIGZyb20gb25lIHZlYzMgdG8gYW5vdGhlclxuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIHNvdXJjZSB2ZWN0b3JcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gY29weShvdXQsIGEpIHtcbiAgICBvdXRbMF0gPSBhWzBdXG4gICAgb3V0WzFdID0gYVsxXVxuICAgIG91dFsyXSA9IGFbMl1cbiAgICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSBjcmVhdGU7XG5cbi8qKlxuICogQ3JlYXRlcyBhIG5ldywgZW1wdHkgdmVjM1xuICpcbiAqIEByZXR1cm5zIHt2ZWMzfSBhIG5ldyAzRCB2ZWN0b3JcbiAqL1xuZnVuY3Rpb24gY3JlYXRlKCkge1xuICAgIHZhciBvdXQgPSBuZXcgRmxvYXQzMkFycmF5KDMpXG4gICAgb3V0WzBdID0gMFxuICAgIG91dFsxXSA9IDBcbiAgICBvdXRbMl0gPSAwXG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gY3Jvc3M7XG5cbi8qKlxuICogQ29tcHV0ZXMgdGhlIGNyb3NzIHByb2R1Y3Qgb2YgdHdvIHZlYzMnc1xuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIGZpcnN0IG9wZXJhbmRcbiAqIEBwYXJhbSB7dmVjM30gYiB0aGUgc2Vjb25kIG9wZXJhbmRcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gY3Jvc3Mob3V0LCBhLCBiKSB7XG4gICAgdmFyIGF4ID0gYVswXSwgYXkgPSBhWzFdLCBheiA9IGFbMl0sXG4gICAgICAgIGJ4ID0gYlswXSwgYnkgPSBiWzFdLCBieiA9IGJbMl1cblxuICAgIG91dFswXSA9IGF5ICogYnogLSBheiAqIGJ5XG4gICAgb3V0WzFdID0gYXogKiBieCAtIGF4ICogYnpcbiAgICBvdXRbMl0gPSBheCAqIGJ5IC0gYXkgKiBieFxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IGRpc3RhbmNlO1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgdGhlIGV1Y2xpZGlhbiBkaXN0YW5jZSBiZXR3ZWVuIHR3byB2ZWMzJ3NcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIGZpcnN0IG9wZXJhbmRcbiAqIEBwYXJhbSB7dmVjM30gYiB0aGUgc2Vjb25kIG9wZXJhbmRcbiAqIEByZXR1cm5zIHtOdW1iZXJ9IGRpc3RhbmNlIGJldHdlZW4gYSBhbmQgYlxuICovXG5mdW5jdGlvbiBkaXN0YW5jZShhLCBiKSB7XG4gICAgdmFyIHggPSBiWzBdIC0gYVswXSxcbiAgICAgICAgeSA9IGJbMV0gLSBhWzFdLFxuICAgICAgICB6ID0gYlsyXSAtIGFbMl1cbiAgICByZXR1cm4gTWF0aC5zcXJ0KHgqeCArIHkqeSArIHoqeilcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IGRpdmlkZTtcblxuLyoqXG4gKiBEaXZpZGVzIHR3byB2ZWMzJ3NcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHt2ZWMzfSBhIHRoZSBmaXJzdCBvcGVyYW5kXG4gKiBAcGFyYW0ge3ZlYzN9IGIgdGhlIHNlY29uZCBvcGVyYW5kXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIGRpdmlkZShvdXQsIGEsIGIpIHtcbiAgICBvdXRbMF0gPSBhWzBdIC8gYlswXVxuICAgIG91dFsxXSA9IGFbMV0gLyBiWzFdXG4gICAgb3V0WzJdID0gYVsyXSAvIGJbMl1cbiAgICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSBkb3Q7XG5cbi8qKlxuICogQ2FsY3VsYXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgdHdvIHZlYzMnc1xuICpcbiAqIEBwYXJhbSB7dmVjM30gYSB0aGUgZmlyc3Qgb3BlcmFuZFxuICogQHBhcmFtIHt2ZWMzfSBiIHRoZSBzZWNvbmQgb3BlcmFuZFxuICogQHJldHVybnMge051bWJlcn0gZG90IHByb2R1Y3Qgb2YgYSBhbmQgYlxuICovXG5mdW5jdGlvbiBkb3QoYSwgYikge1xuICAgIHJldHVybiBhWzBdICogYlswXSArIGFbMV0gKiBiWzFdICsgYVsyXSAqIGJbMl1cbn0iLCJtb2R1bGUuZXhwb3J0cyA9IGZvckVhY2g7XG5cbnZhciB2ZWMgPSByZXF1aXJlKCcuL2NyZWF0ZScpKClcblxuLyoqXG4gKiBQZXJmb3JtIHNvbWUgb3BlcmF0aW9uIG92ZXIgYW4gYXJyYXkgb2YgdmVjM3MuXG4gKlxuICogQHBhcmFtIHtBcnJheX0gYSB0aGUgYXJyYXkgb2YgdmVjdG9ycyB0byBpdGVyYXRlIG92ZXJcbiAqIEBwYXJhbSB7TnVtYmVyfSBzdHJpZGUgTnVtYmVyIG9mIGVsZW1lbnRzIGJldHdlZW4gdGhlIHN0YXJ0IG9mIGVhY2ggdmVjMy4gSWYgMCBhc3N1bWVzIHRpZ2h0bHkgcGFja2VkXG4gKiBAcGFyYW0ge051bWJlcn0gb2Zmc2V0IE51bWJlciBvZiBlbGVtZW50cyB0byBza2lwIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIGFycmF5XG4gKiBAcGFyYW0ge051bWJlcn0gY291bnQgTnVtYmVyIG9mIHZlYzNzIHRvIGl0ZXJhdGUgb3Zlci4gSWYgMCBpdGVyYXRlcyBvdmVyIGVudGlyZSBhcnJheVxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gRnVuY3Rpb24gdG8gY2FsbCBmb3IgZWFjaCB2ZWN0b3IgaW4gdGhlIGFycmF5XG4gKiBAcGFyYW0ge09iamVjdH0gW2FyZ10gYWRkaXRpb25hbCBhcmd1bWVudCB0byBwYXNzIHRvIGZuXG4gKiBAcmV0dXJucyB7QXJyYXl9IGFcbiAqIEBmdW5jdGlvblxuICovXG5mdW5jdGlvbiBmb3JFYWNoKGEsIHN0cmlkZSwgb2Zmc2V0LCBjb3VudCwgZm4sIGFyZykge1xuICAgICAgICB2YXIgaSwgbFxuICAgICAgICBpZighc3RyaWRlKSB7XG4gICAgICAgICAgICBzdHJpZGUgPSAzXG4gICAgICAgIH1cblxuICAgICAgICBpZighb2Zmc2V0KSB7XG4gICAgICAgICAgICBvZmZzZXQgPSAwXG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGlmKGNvdW50KSB7XG4gICAgICAgICAgICBsID0gTWF0aC5taW4oKGNvdW50ICogc3RyaWRlKSArIG9mZnNldCwgYS5sZW5ndGgpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsID0gYS5sZW5ndGhcbiAgICAgICAgfVxuXG4gICAgICAgIGZvcihpID0gb2Zmc2V0OyBpIDwgbDsgaSArPSBzdHJpZGUpIHtcbiAgICAgICAgICAgIHZlY1swXSA9IGFbaV0gXG4gICAgICAgICAgICB2ZWNbMV0gPSBhW2krMV0gXG4gICAgICAgICAgICB2ZWNbMl0gPSBhW2krMl1cbiAgICAgICAgICAgIGZuKHZlYywgdmVjLCBhcmcpXG4gICAgICAgICAgICBhW2ldID0gdmVjWzBdIFxuICAgICAgICAgICAgYVtpKzFdID0gdmVjWzFdIFxuICAgICAgICAgICAgYVtpKzJdID0gdmVjWzJdXG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHJldHVybiBhXG59IiwibW9kdWxlLmV4cG9ydHMgPSBmcm9tVmFsdWVzO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBuZXcgdmVjMyBpbml0aWFsaXplZCB3aXRoIHRoZSBnaXZlbiB2YWx1ZXNcbiAqXG4gKiBAcGFyYW0ge051bWJlcn0geCBYIGNvbXBvbmVudFxuICogQHBhcmFtIHtOdW1iZXJ9IHkgWSBjb21wb25lbnRcbiAqIEBwYXJhbSB7TnVtYmVyfSB6IFogY29tcG9uZW50XG4gKiBAcmV0dXJucyB7dmVjM30gYSBuZXcgM0QgdmVjdG9yXG4gKi9cbmZ1bmN0aW9uIGZyb21WYWx1ZXMoeCwgeSwgeikge1xuICAgIHZhciBvdXQgPSBuZXcgRmxvYXQzMkFycmF5KDMpXG4gICAgb3V0WzBdID0geFxuICAgIG91dFsxXSA9IHlcbiAgICBvdXRbMl0gPSB6XG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gaW52ZXJzZTtcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBpbnZlcnNlIG9mIHRoZSBjb21wb25lbnRzIG9mIGEgdmVjM1xuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdmVjdG9yIHRvIGludmVydFxuICogQHJldHVybnMge3ZlYzN9IG91dFxuICovXG5mdW5jdGlvbiBpbnZlcnNlKG91dCwgYSkge1xuICBvdXRbMF0gPSAxLjAgLyBhWzBdXG4gIG91dFsxXSA9IDEuMCAvIGFbMV1cbiAgb3V0WzJdID0gMS4wIC8gYVsyXVxuICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSBsZW5ndGg7XG5cbi8qKlxuICogQ2FsY3VsYXRlcyB0aGUgbGVuZ3RoIG9mIGEgdmVjM1xuICpcbiAqIEBwYXJhbSB7dmVjM30gYSB2ZWN0b3IgdG8gY2FsY3VsYXRlIGxlbmd0aCBvZlxuICogQHJldHVybnMge051bWJlcn0gbGVuZ3RoIG9mIGFcbiAqL1xuZnVuY3Rpb24gbGVuZ3RoKGEpIHtcbiAgICB2YXIgeCA9IGFbMF0sXG4gICAgICAgIHkgPSBhWzFdLFxuICAgICAgICB6ID0gYVsyXVxuICAgIHJldHVybiBNYXRoLnNxcnQoeCp4ICsgeSp5ICsgeip6KVxufSIsIm1vZHVsZS5leHBvcnRzID0gbGVycDtcblxuLyoqXG4gKiBQZXJmb3JtcyBhIGxpbmVhciBpbnRlcnBvbGF0aW9uIGJldHdlZW4gdHdvIHZlYzMnc1xuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIGZpcnN0IG9wZXJhbmRcbiAqIEBwYXJhbSB7dmVjM30gYiB0aGUgc2Vjb25kIG9wZXJhbmRcbiAqIEBwYXJhbSB7TnVtYmVyfSB0IGludGVycG9sYXRpb24gYW1vdW50IGJldHdlZW4gdGhlIHR3byBpbnB1dHNcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gbGVycChvdXQsIGEsIGIsIHQpIHtcbiAgICB2YXIgYXggPSBhWzBdLFxuICAgICAgICBheSA9IGFbMV0sXG4gICAgICAgIGF6ID0gYVsyXVxuICAgIG91dFswXSA9IGF4ICsgdCAqIChiWzBdIC0gYXgpXG4gICAgb3V0WzFdID0gYXkgKyB0ICogKGJbMV0gLSBheSlcbiAgICBvdXRbMl0gPSBheiArIHQgKiAoYlsyXSAtIGF6KVxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IG1heDtcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBtYXhpbXVtIG9mIHR3byB2ZWMzJ3NcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHt2ZWMzfSBhIHRoZSBmaXJzdCBvcGVyYW5kXG4gKiBAcGFyYW0ge3ZlYzN9IGIgdGhlIHNlY29uZCBvcGVyYW5kXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIG1heChvdXQsIGEsIGIpIHtcbiAgICBvdXRbMF0gPSBNYXRoLm1heChhWzBdLCBiWzBdKVxuICAgIG91dFsxXSA9IE1hdGgubWF4KGFbMV0sIGJbMV0pXG4gICAgb3V0WzJdID0gTWF0aC5tYXgoYVsyXSwgYlsyXSlcbiAgICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSBtaW47XG5cbi8qKlxuICogUmV0dXJucyB0aGUgbWluaW11bSBvZiB0d28gdmVjMydzXG4gKlxuICogQHBhcmFtIHt2ZWMzfSBvdXQgdGhlIHJlY2VpdmluZyB2ZWN0b3JcbiAqIEBwYXJhbSB7dmVjM30gYSB0aGUgZmlyc3Qgb3BlcmFuZFxuICogQHBhcmFtIHt2ZWMzfSBiIHRoZSBzZWNvbmQgb3BlcmFuZFxuICogQHJldHVybnMge3ZlYzN9IG91dFxuICovXG5mdW5jdGlvbiBtaW4ob3V0LCBhLCBiKSB7XG4gICAgb3V0WzBdID0gTWF0aC5taW4oYVswXSwgYlswXSlcbiAgICBvdXRbMV0gPSBNYXRoLm1pbihhWzFdLCBiWzFdKVxuICAgIG91dFsyXSA9IE1hdGgubWluKGFbMl0sIGJbMl0pXG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gbXVsdGlwbHk7XG5cbi8qKlxuICogTXVsdGlwbGllcyB0d28gdmVjMydzXG4gKlxuICogQHBhcmFtIHt2ZWMzfSBvdXQgdGhlIHJlY2VpdmluZyB2ZWN0b3JcbiAqIEBwYXJhbSB7dmVjM30gYSB0aGUgZmlyc3Qgb3BlcmFuZFxuICogQHBhcmFtIHt2ZWMzfSBiIHRoZSBzZWNvbmQgb3BlcmFuZFxuICogQHJldHVybnMge3ZlYzN9IG91dFxuICovXG5mdW5jdGlvbiBtdWx0aXBseShvdXQsIGEsIGIpIHtcbiAgICBvdXRbMF0gPSBhWzBdICogYlswXVxuICAgIG91dFsxXSA9IGFbMV0gKiBiWzFdXG4gICAgb3V0WzJdID0gYVsyXSAqIGJbMl1cbiAgICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSBuZWdhdGU7XG5cbi8qKlxuICogTmVnYXRlcyB0aGUgY29tcG9uZW50cyBvZiBhIHZlYzNcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHt2ZWMzfSBhIHZlY3RvciB0byBuZWdhdGVcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gbmVnYXRlKG91dCwgYSkge1xuICAgIG91dFswXSA9IC1hWzBdXG4gICAgb3V0WzFdID0gLWFbMV1cbiAgICBvdXRbMl0gPSAtYVsyXVxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IG5vcm1hbGl6ZTtcblxuLyoqXG4gKiBOb3JtYWxpemUgYSB2ZWMzXG4gKlxuICogQHBhcmFtIHt2ZWMzfSBvdXQgdGhlIHJlY2VpdmluZyB2ZWN0b3JcbiAqIEBwYXJhbSB7dmVjM30gYSB2ZWN0b3IgdG8gbm9ybWFsaXplXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZShvdXQsIGEpIHtcbiAgICB2YXIgeCA9IGFbMF0sXG4gICAgICAgIHkgPSBhWzFdLFxuICAgICAgICB6ID0gYVsyXVxuICAgIHZhciBsZW4gPSB4KnggKyB5KnkgKyB6KnpcbiAgICBpZiAobGVuID4gMCkge1xuICAgICAgICAvL1RPRE86IGV2YWx1YXRlIHVzZSBvZiBnbG1faW52c3FydCBoZXJlP1xuICAgICAgICBsZW4gPSAxIC8gTWF0aC5zcXJ0KGxlbilcbiAgICAgICAgb3V0WzBdID0gYVswXSAqIGxlblxuICAgICAgICBvdXRbMV0gPSBhWzFdICogbGVuXG4gICAgICAgIG91dFsyXSA9IGFbMl0gKiBsZW5cbiAgICB9XG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gcmFuZG9tO1xuXG4vKipcbiAqIEdlbmVyYXRlcyBhIHJhbmRvbSB2ZWN0b3Igd2l0aCB0aGUgZ2l2ZW4gc2NhbGVcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHtOdW1iZXJ9IFtzY2FsZV0gTGVuZ3RoIG9mIHRoZSByZXN1bHRpbmcgdmVjdG9yLiBJZiBvbW1pdHRlZCwgYSB1bml0IHZlY3RvciB3aWxsIGJlIHJldHVybmVkXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIHJhbmRvbShvdXQsIHNjYWxlKSB7XG4gICAgc2NhbGUgPSBzY2FsZSB8fCAxLjBcblxuICAgIHZhciByID0gTWF0aC5yYW5kb20oKSAqIDIuMCAqIE1hdGguUElcbiAgICB2YXIgeiA9IChNYXRoLnJhbmRvbSgpICogMi4wKSAtIDEuMFxuICAgIHZhciB6U2NhbGUgPSBNYXRoLnNxcnQoMS4wLXoqeikgKiBzY2FsZVxuXG4gICAgb3V0WzBdID0gTWF0aC5jb3MocikgKiB6U2NhbGVcbiAgICBvdXRbMV0gPSBNYXRoLnNpbihyKSAqIHpTY2FsZVxuICAgIG91dFsyXSA9IHogKiBzY2FsZVxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IHJvdGF0ZVg7XG5cbi8qKlxuICogUm90YXRlIGEgM0QgdmVjdG9yIGFyb3VuZCB0aGUgeC1heGlzXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCBUaGUgcmVjZWl2aW5nIHZlYzNcbiAqIEBwYXJhbSB7dmVjM30gYSBUaGUgdmVjMyBwb2ludCB0byByb3RhdGVcbiAqIEBwYXJhbSB7dmVjM30gYiBUaGUgb3JpZ2luIG9mIHRoZSByb3RhdGlvblxuICogQHBhcmFtIHtOdW1iZXJ9IGMgVGhlIGFuZ2xlIG9mIHJvdGF0aW9uXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIHJvdGF0ZVgob3V0LCBhLCBiLCBjKXtcbiAgICB2YXIgcCA9IFtdLCByPVtdXG4gICAgLy9UcmFuc2xhdGUgcG9pbnQgdG8gdGhlIG9yaWdpblxuICAgIHBbMF0gPSBhWzBdIC0gYlswXVxuICAgIHBbMV0gPSBhWzFdIC0gYlsxXVxuICAgIHBbMl0gPSBhWzJdIC0gYlsyXVxuXG4gICAgLy9wZXJmb3JtIHJvdGF0aW9uXG4gICAgclswXSA9IHBbMF1cbiAgICByWzFdID0gcFsxXSpNYXRoLmNvcyhjKSAtIHBbMl0qTWF0aC5zaW4oYylcbiAgICByWzJdID0gcFsxXSpNYXRoLnNpbihjKSArIHBbMl0qTWF0aC5jb3MoYylcblxuICAgIC8vdHJhbnNsYXRlIHRvIGNvcnJlY3QgcG9zaXRpb25cbiAgICBvdXRbMF0gPSByWzBdICsgYlswXVxuICAgIG91dFsxXSA9IHJbMV0gKyBiWzFdXG4gICAgb3V0WzJdID0gclsyXSArIGJbMl1cblxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IHJvdGF0ZVk7XG5cbi8qKlxuICogUm90YXRlIGEgM0QgdmVjdG9yIGFyb3VuZCB0aGUgeS1heGlzXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCBUaGUgcmVjZWl2aW5nIHZlYzNcbiAqIEBwYXJhbSB7dmVjM30gYSBUaGUgdmVjMyBwb2ludCB0byByb3RhdGVcbiAqIEBwYXJhbSB7dmVjM30gYiBUaGUgb3JpZ2luIG9mIHRoZSByb3RhdGlvblxuICogQHBhcmFtIHtOdW1iZXJ9IGMgVGhlIGFuZ2xlIG9mIHJvdGF0aW9uXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIHJvdGF0ZVkob3V0LCBhLCBiLCBjKXtcbiAgICB2YXIgcCA9IFtdLCByPVtdXG4gICAgLy9UcmFuc2xhdGUgcG9pbnQgdG8gdGhlIG9yaWdpblxuICAgIHBbMF0gPSBhWzBdIC0gYlswXVxuICAgIHBbMV0gPSBhWzFdIC0gYlsxXVxuICAgIHBbMl0gPSBhWzJdIC0gYlsyXVxuICBcbiAgICAvL3BlcmZvcm0gcm90YXRpb25cbiAgICByWzBdID0gcFsyXSpNYXRoLnNpbihjKSArIHBbMF0qTWF0aC5jb3MoYylcbiAgICByWzFdID0gcFsxXVxuICAgIHJbMl0gPSBwWzJdKk1hdGguY29zKGMpIC0gcFswXSpNYXRoLnNpbihjKVxuICBcbiAgICAvL3RyYW5zbGF0ZSB0byBjb3JyZWN0IHBvc2l0aW9uXG4gICAgb3V0WzBdID0gclswXSArIGJbMF1cbiAgICBvdXRbMV0gPSByWzFdICsgYlsxXVxuICAgIG91dFsyXSA9IHJbMl0gKyBiWzJdXG4gIFxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IHJvdGF0ZVo7XG5cbi8qKlxuICogUm90YXRlIGEgM0QgdmVjdG9yIGFyb3VuZCB0aGUgei1heGlzXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCBUaGUgcmVjZWl2aW5nIHZlYzNcbiAqIEBwYXJhbSB7dmVjM30gYSBUaGUgdmVjMyBwb2ludCB0byByb3RhdGVcbiAqIEBwYXJhbSB7dmVjM30gYiBUaGUgb3JpZ2luIG9mIHRoZSByb3RhdGlvblxuICogQHBhcmFtIHtOdW1iZXJ9IGMgVGhlIGFuZ2xlIG9mIHJvdGF0aW9uXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIHJvdGF0ZVoob3V0LCBhLCBiLCBjKXtcbiAgICB2YXIgcCA9IFtdLCByPVtdXG4gICAgLy9UcmFuc2xhdGUgcG9pbnQgdG8gdGhlIG9yaWdpblxuICAgIHBbMF0gPSBhWzBdIC0gYlswXVxuICAgIHBbMV0gPSBhWzFdIC0gYlsxXVxuICAgIHBbMl0gPSBhWzJdIC0gYlsyXVxuICBcbiAgICAvL3BlcmZvcm0gcm90YXRpb25cbiAgICByWzBdID0gcFswXSpNYXRoLmNvcyhjKSAtIHBbMV0qTWF0aC5zaW4oYylcbiAgICByWzFdID0gcFswXSpNYXRoLnNpbihjKSArIHBbMV0qTWF0aC5jb3MoYylcbiAgICByWzJdID0gcFsyXVxuICBcbiAgICAvL3RyYW5zbGF0ZSB0byBjb3JyZWN0IHBvc2l0aW9uXG4gICAgb3V0WzBdID0gclswXSArIGJbMF1cbiAgICBvdXRbMV0gPSByWzFdICsgYlsxXVxuICAgIG91dFsyXSA9IHJbMl0gKyBiWzJdXG4gIFxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IHNjYWxlO1xuXG4vKipcbiAqIFNjYWxlcyBhIHZlYzMgYnkgYSBzY2FsYXIgbnVtYmVyXG4gKlxuICogQHBhcmFtIHt2ZWMzfSBvdXQgdGhlIHJlY2VpdmluZyB2ZWN0b3JcbiAqIEBwYXJhbSB7dmVjM30gYSB0aGUgdmVjdG9yIHRvIHNjYWxlXG4gKiBAcGFyYW0ge051bWJlcn0gYiBhbW91bnQgdG8gc2NhbGUgdGhlIHZlY3RvciBieVxuICogQHJldHVybnMge3ZlYzN9IG91dFxuICovXG5mdW5jdGlvbiBzY2FsZShvdXQsIGEsIGIpIHtcbiAgICBvdXRbMF0gPSBhWzBdICogYlxuICAgIG91dFsxXSA9IGFbMV0gKiBiXG4gICAgb3V0WzJdID0gYVsyXSAqIGJcbiAgICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSBzY2FsZUFuZEFkZDtcblxuLyoqXG4gKiBBZGRzIHR3byB2ZWMzJ3MgYWZ0ZXIgc2NhbGluZyB0aGUgc2Vjb25kIG9wZXJhbmQgYnkgYSBzY2FsYXIgdmFsdWVcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHt2ZWMzfSBhIHRoZSBmaXJzdCBvcGVyYW5kXG4gKiBAcGFyYW0ge3ZlYzN9IGIgdGhlIHNlY29uZCBvcGVyYW5kXG4gKiBAcGFyYW0ge051bWJlcn0gc2NhbGUgdGhlIGFtb3VudCB0byBzY2FsZSBiIGJ5IGJlZm9yZSBhZGRpbmdcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gc2NhbGVBbmRBZGQob3V0LCBhLCBiLCBzY2FsZSkge1xuICAgIG91dFswXSA9IGFbMF0gKyAoYlswXSAqIHNjYWxlKVxuICAgIG91dFsxXSA9IGFbMV0gKyAoYlsxXSAqIHNjYWxlKVxuICAgIG91dFsyXSA9IGFbMl0gKyAoYlsyXSAqIHNjYWxlKVxuICAgIHJldHVybiBvdXRcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IHNldDtcblxuLyoqXG4gKiBTZXQgdGhlIGNvbXBvbmVudHMgb2YgYSB2ZWMzIHRvIHRoZSBnaXZlbiB2YWx1ZXNcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHtOdW1iZXJ9IHggWCBjb21wb25lbnRcbiAqIEBwYXJhbSB7TnVtYmVyfSB5IFkgY29tcG9uZW50XG4gKiBAcGFyYW0ge051bWJlcn0geiBaIGNvbXBvbmVudFxuICogQHJldHVybnMge3ZlYzN9IG91dFxuICovXG5mdW5jdGlvbiBzZXQob3V0LCB4LCB5LCB6KSB7XG4gICAgb3V0WzBdID0geFxuICAgIG91dFsxXSA9IHlcbiAgICBvdXRbMl0gPSB6XG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gc3F1YXJlZERpc3RhbmNlO1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgdGhlIHNxdWFyZWQgZXVjbGlkaWFuIGRpc3RhbmNlIGJldHdlZW4gdHdvIHZlYzMnc1xuICpcbiAqIEBwYXJhbSB7dmVjM30gYSB0aGUgZmlyc3Qgb3BlcmFuZFxuICogQHBhcmFtIHt2ZWMzfSBiIHRoZSBzZWNvbmQgb3BlcmFuZFxuICogQHJldHVybnMge051bWJlcn0gc3F1YXJlZCBkaXN0YW5jZSBiZXR3ZWVuIGEgYW5kIGJcbiAqL1xuZnVuY3Rpb24gc3F1YXJlZERpc3RhbmNlKGEsIGIpIHtcbiAgICB2YXIgeCA9IGJbMF0gLSBhWzBdLFxuICAgICAgICB5ID0gYlsxXSAtIGFbMV0sXG4gICAgICAgIHogPSBiWzJdIC0gYVsyXVxuICAgIHJldHVybiB4KnggKyB5KnkgKyB6Knpcbn0iLCJtb2R1bGUuZXhwb3J0cyA9IHNxdWFyZWRMZW5ndGg7XG5cbi8qKlxuICogQ2FsY3VsYXRlcyB0aGUgc3F1YXJlZCBsZW5ndGggb2YgYSB2ZWMzXG4gKlxuICogQHBhcmFtIHt2ZWMzfSBhIHZlY3RvciB0byBjYWxjdWxhdGUgc3F1YXJlZCBsZW5ndGggb2ZcbiAqIEByZXR1cm5zIHtOdW1iZXJ9IHNxdWFyZWQgbGVuZ3RoIG9mIGFcbiAqL1xuZnVuY3Rpb24gc3F1YXJlZExlbmd0aChhKSB7XG4gICAgdmFyIHggPSBhWzBdLFxuICAgICAgICB5ID0gYVsxXSxcbiAgICAgICAgeiA9IGFbMl1cbiAgICByZXR1cm4geCp4ICsgeSp5ICsgeip6XG59IiwibW9kdWxlLmV4cG9ydHMgPSBzdWJ0cmFjdDtcblxuLyoqXG4gKiBTdWJ0cmFjdHMgdmVjdG9yIGIgZnJvbSB2ZWN0b3IgYVxuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIGZpcnN0IG9wZXJhbmRcbiAqIEBwYXJhbSB7dmVjM30gYiB0aGUgc2Vjb25kIG9wZXJhbmRcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gc3VidHJhY3Qob3V0LCBhLCBiKSB7XG4gICAgb3V0WzBdID0gYVswXSAtIGJbMF1cbiAgICBvdXRbMV0gPSBhWzFdIC0gYlsxXVxuICAgIG91dFsyXSA9IGFbMl0gLSBiWzJdXG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gdHJhbnNmb3JtTWF0MztcblxuLyoqXG4gKiBUcmFuc2Zvcm1zIHRoZSB2ZWMzIHdpdGggYSBtYXQzLlxuICpcbiAqIEBwYXJhbSB7dmVjM30gb3V0IHRoZSByZWNlaXZpbmcgdmVjdG9yXG4gKiBAcGFyYW0ge3ZlYzN9IGEgdGhlIHZlY3RvciB0byB0cmFuc2Zvcm1cbiAqIEBwYXJhbSB7bWF0NH0gbSB0aGUgM3gzIG1hdHJpeCB0byB0cmFuc2Zvcm0gd2l0aFxuICogQHJldHVybnMge3ZlYzN9IG91dFxuICovXG5mdW5jdGlvbiB0cmFuc2Zvcm1NYXQzKG91dCwgYSwgbSkge1xuICAgIHZhciB4ID0gYVswXSwgeSA9IGFbMV0sIHogPSBhWzJdXG4gICAgb3V0WzBdID0geCAqIG1bMF0gKyB5ICogbVszXSArIHogKiBtWzZdXG4gICAgb3V0WzFdID0geCAqIG1bMV0gKyB5ICogbVs0XSArIHogKiBtWzddXG4gICAgb3V0WzJdID0geCAqIG1bMl0gKyB5ICogbVs1XSArIHogKiBtWzhdXG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gdHJhbnNmb3JtTWF0NDtcblxuLyoqXG4gKiBUcmFuc2Zvcm1zIHRoZSB2ZWMzIHdpdGggYSBtYXQ0LlxuICogNHRoIHZlY3RvciBjb21wb25lbnQgaXMgaW1wbGljaXRseSAnMSdcbiAqXG4gKiBAcGFyYW0ge3ZlYzN9IG91dCB0aGUgcmVjZWl2aW5nIHZlY3RvclxuICogQHBhcmFtIHt2ZWMzfSBhIHRoZSB2ZWN0b3IgdG8gdHJhbnNmb3JtXG4gKiBAcGFyYW0ge21hdDR9IG0gbWF0cml4IHRvIHRyYW5zZm9ybSB3aXRoXG4gKiBAcmV0dXJucyB7dmVjM30gb3V0XG4gKi9cbmZ1bmN0aW9uIHRyYW5zZm9ybU1hdDQob3V0LCBhLCBtKSB7XG4gICAgdmFyIHggPSBhWzBdLCB5ID0gYVsxXSwgeiA9IGFbMl0sXG4gICAgICAgIHcgPSBtWzNdICogeCArIG1bN10gKiB5ICsgbVsxMV0gKiB6ICsgbVsxNV1cbiAgICB3ID0gdyB8fCAxLjBcbiAgICBvdXRbMF0gPSAobVswXSAqIHggKyBtWzRdICogeSArIG1bOF0gKiB6ICsgbVsxMl0pIC8gd1xuICAgIG91dFsxXSA9IChtWzFdICogeCArIG1bNV0gKiB5ICsgbVs5XSAqIHogKyBtWzEzXSkgLyB3XG4gICAgb3V0WzJdID0gKG1bMl0gKiB4ICsgbVs2XSAqIHkgKyBtWzEwXSAqIHogKyBtWzE0XSkgLyB3XG4gICAgcmV0dXJuIG91dFxufSIsIm1vZHVsZS5leHBvcnRzID0gdHJhbnNmb3JtUXVhdDtcblxuLyoqXG4gKiBUcmFuc2Zvcm1zIHRoZSB2ZWMzIHdpdGggYSBxdWF0XG4gKlxuICogQHBhcmFtIHt2ZWMzfSBvdXQgdGhlIHJlY2VpdmluZyB2ZWN0b3JcbiAqIEBwYXJhbSB7dmVjM30gYSB0aGUgdmVjdG9yIHRvIHRyYW5zZm9ybVxuICogQHBhcmFtIHtxdWF0fSBxIHF1YXRlcm5pb24gdG8gdHJhbnNmb3JtIHdpdGhcbiAqIEByZXR1cm5zIHt2ZWMzfSBvdXRcbiAqL1xuZnVuY3Rpb24gdHJhbnNmb3JtUXVhdChvdXQsIGEsIHEpIHtcbiAgICAvLyBiZW5jaG1hcmtzOiBodHRwOi8vanNwZXJmLmNvbS9xdWF0ZXJuaW9uLXRyYW5zZm9ybS12ZWMzLWltcGxlbWVudGF0aW9uc1xuXG4gICAgdmFyIHggPSBhWzBdLCB5ID0gYVsxXSwgeiA9IGFbMl0sXG4gICAgICAgIHF4ID0gcVswXSwgcXkgPSBxWzFdLCBxeiA9IHFbMl0sIHF3ID0gcVszXSxcblxuICAgICAgICAvLyBjYWxjdWxhdGUgcXVhdCAqIHZlY1xuICAgICAgICBpeCA9IHF3ICogeCArIHF5ICogeiAtIHF6ICogeSxcbiAgICAgICAgaXkgPSBxdyAqIHkgKyBxeiAqIHggLSBxeCAqIHosXG4gICAgICAgIGl6ID0gcXcgKiB6ICsgcXggKiB5IC0gcXkgKiB4LFxuICAgICAgICBpdyA9IC1xeCAqIHggLSBxeSAqIHkgLSBxeiAqIHpcblxuICAgIC8vIGNhbGN1bGF0ZSByZXN1bHQgKiBpbnZlcnNlIHF1YXRcbiAgICBvdXRbMF0gPSBpeCAqIHF3ICsgaXcgKiAtcXggKyBpeSAqIC1xeiAtIGl6ICogLXF5XG4gICAgb3V0WzFdID0gaXkgKiBxdyArIGl3ICogLXF5ICsgaXogKiAtcXggLSBpeCAqIC1xelxuICAgIG91dFsyXSA9IGl6ICogcXcgKyBpdyAqIC1xeiArIGl4ICogLXF5IC0gaXkgKiAtcXhcbiAgICByZXR1cm4gb3V0XG59IiwibW9kdWxlLmV4cG9ydHMgPSB7XG4gIGNyZWF0ZTogcmVxdWlyZSgnLi9jcmVhdGUnKVxuICAsIGNsb25lOiByZXF1aXJlKCcuL2Nsb25lJylcbiAgLCBhbmdsZTogcmVxdWlyZSgnLi9hbmdsZScpXG4gICwgZnJvbVZhbHVlczogcmVxdWlyZSgnLi9mcm9tVmFsdWVzJylcbiAgLCBjb3B5OiByZXF1aXJlKCcuL2NvcHknKVxuICAsIHNldDogcmVxdWlyZSgnLi9zZXQnKVxuICAsIGFkZDogcmVxdWlyZSgnLi9hZGQnKVxuICAsIHN1YnRyYWN0OiByZXF1aXJlKCcuL3N1YnRyYWN0JylcbiAgLCBtdWx0aXBseTogcmVxdWlyZSgnLi9tdWx0aXBseScpXG4gICwgZGl2aWRlOiByZXF1aXJlKCcuL2RpdmlkZScpXG4gICwgbWluOiByZXF1aXJlKCcuL21pbicpXG4gICwgbWF4OiByZXF1aXJlKCcuL21heCcpXG4gICwgc2NhbGU6IHJlcXVpcmUoJy4vc2NhbGUnKVxuICAsIHNjYWxlQW5kQWRkOiByZXF1aXJlKCcuL3NjYWxlQW5kQWRkJylcbiAgLCBkaXN0YW5jZTogcmVxdWlyZSgnLi9kaXN0YW5jZScpXG4gICwgc3F1YXJlZERpc3RhbmNlOiByZXF1aXJlKCcuL3NxdWFyZWREaXN0YW5jZScpXG4gICwgbGVuZ3RoOiByZXF1aXJlKCcuL2xlbmd0aCcpXG4gICwgc3F1YXJlZExlbmd0aDogcmVxdWlyZSgnLi9zcXVhcmVkTGVuZ3RoJylcbiAgLCBuZWdhdGU6IHJlcXVpcmUoJy4vbmVnYXRlJylcbiAgLCBpbnZlcnNlOiByZXF1aXJlKCcuL2ludmVyc2UnKVxuICAsIG5vcm1hbGl6ZTogcmVxdWlyZSgnLi9ub3JtYWxpemUnKVxuICAsIGRvdDogcmVxdWlyZSgnLi9kb3QnKVxuICAsIGNyb3NzOiByZXF1aXJlKCcuL2Nyb3NzJylcbiAgLCBsZXJwOiByZXF1aXJlKCcuL2xlcnAnKVxuICAsIHJhbmRvbTogcmVxdWlyZSgnLi9yYW5kb20nKVxuICAsIHRyYW5zZm9ybU1hdDQ6IHJlcXVpcmUoJy4vdHJhbnNmb3JtTWF0NCcpXG4gICwgdHJhbnNmb3JtTWF0MzogcmVxdWlyZSgnLi90cmFuc2Zvcm1NYXQzJylcbiAgLCB0cmFuc2Zvcm1RdWF0OiByZXF1aXJlKCcuL3RyYW5zZm9ybVF1YXQnKVxuICAsIHJvdGF0ZVg6IHJlcXVpcmUoJy4vcm90YXRlWCcpXG4gICwgcm90YXRlWTogcmVxdWlyZSgnLi9yb3RhdGVZJylcbiAgLCByb3RhdGVaOiByZXF1aXJlKCcuL3JvdGF0ZVonKVxuICAsIGZvckVhY2g6IHJlcXVpcmUoJy4vZm9yRWFjaCcpXG59Il19 | |
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
function Assembler() { | |
this.flags = {}; | |
this.positions = {}; | |
this.faces = {}; | |
} | |
Assembler.prototype.newFlag = function(face, v1, v2) { | |
if (!this.flags[face]) { | |
this.flags[face] = { }; | |
} | |
this.flags[face][v1] = v2; | |
} | |
Assembler.prototype.newV = function(name, p) { | |
this.positions[name] = p; | |
} | |
Assembler.prototype.flags2poly = function() { | |
var rpositions = []; | |
var verts = {}; | |
for (var i in this.positions) { | |
verts[i] = rpositions.length; | |
rpositions.push(this.positions[i]); | |
} | |
var rfaces = []; | |
for (var i in this.flags) { | |
var flag = this.flags[i]; | |
var f = []; | |
var v0 = Object.keys(flag)[0]; | |
var v = v0; | |
do { | |
f.push(verts[v]); | |
v = flag[v]; | |
} while (v != v0); | |
rfaces.push(f); | |
} | |
return { | |
name: "?", | |
positions: rpositions, | |
faces: rfaces | |
}; | |
} | |
module.exports = Assembler; | |
},{}],2:[function(require,module,exports){ | |
//-------------------Canonicalization Algorithm-------------------------- | |
// True canonicalization rather slow. Using center of gravity of vertices for each | |
// face gives a quick "adjustment" which planarizes faces at least. | |
"use strict"; | |
var util = require("./util.js"); | |
var makeDual = require("./dual.js"); | |
function reciprocalN(poly) { // make array of vertices reciprocal to given planes | |
var ans = new Array(poly.faces.length); | |
for (var i=0; i<poly.faces.length; ++i) { // for each face: | |
var centroid = [0.0, 0.0, 0.0]; // running sum of vertex coords | |
var normal = [0.0, 0.0, 0.0]; // running sum of normal vectors | |
var avgEdgeDist = 0.; // running sum for avg edge distance | |
var v1 = poly.faces[i][poly.faces[i].length-2]; // preprevious vertex | |
var v2 = poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (var j=0; j<poly.faces[i].length; j++) { | |
var v3 = poly.faces[i][j]; // this vertex | |
centroid = util.add(centroid, poly.positions[v3]); | |
normal = util.add(normal, util.orthogonal(poly.positions[v1], poly.positions[v2], poly.positions[v3])); | |
avgEdgeDist = avgEdgeDist + util.edgeDist(poly.positions[v1], poly.positions[v2]); | |
v1 = v2; // shift over one | |
v2 = v3; | |
} | |
centroid = util.mult(1.0/poly.faces[i].length, centroid); | |
normal = util.unit(normal); | |
avgEdgeDist = avgEdgeDist / poly.faces[i].length; | |
ans[i] = util.reciprocal(util.mult(util.dot(centroid, normal), normal)); // based on face | |
ans[i] = util.mult((1+avgEdgeDist)/2, ans[i]); // edge correction | |
} | |
return (ans); | |
} | |
function reciprocalC(poly) { // return array of reciprocals of face centers | |
var center = faceCenters(poly); | |
for (var i=0; i<poly.faces.length; i++) { | |
var m2 = center[i][0]*center[i][0] + center[i][1]*center[i][1] + center[i][2]*center[i][2]; | |
center[i][0] = center[i][0] / m2; // divide each coord by magnitude squared | |
center[i][1] = center[i][1] / m2; | |
center[i][2] = center[i][2] / m2; | |
} | |
return(center); | |
} | |
function faceCenters(poly) { // return array of "face centers" | |
var ans = new Array(poly.faces.length); | |
for (var i=0; i<poly.faces.length; i++) { | |
ans[i] = util.vecZero(); // running sum | |
for (var j=0; j<poly.faces[i].length; j++) // just average vertex coords: | |
ans[i] = util.add(ans[i], poly.positions[poly.faces[i][j]]); // sum and... | |
ans[i] = util.mult(1./poly.faces[i].length, ans[i]); // ...divide by n | |
} | |
return (ans); | |
} | |
exports.faceCenters = faceCenters; | |
function canonicalpositions(poly, nIterations) { // compute new vertex coords. | |
var dpoly = makeDual(poly) // v's of dual are in order or arg's f's | |
for (var count=0; count<nIterations; count++) { // iteration: | |
dpoly.positions = reciprocalN(poly); | |
poly.positions = reciprocalN(dpoly); | |
} | |
} | |
exports.canonicalpositions = canonicalpositions; | |
function adjustpositions(poly, nIterations) { | |
var dpoly = makeDual(poly) // v's of dual are in order or arg's f's | |
for (var count=0; count<1; count++) { // iteration: | |
dpoly.positions = reciprocalC(poly); // reciprocate face centers | |
poly.positions = reciprocalC(dpoly); // reciprocate face centers | |
} | |
} | |
exports.adjustpositions = adjustpositions; | |
},{"./dual.js":3,"./util.js":6}],3:[function(require,module,exports){ | |
//--------------------------------Dual------------------------------------------ | |
// the makeDual function computes the dual's topology, needed for canonicalization, | |
// where positions's are determined. It is then saved in a global variable globSavedDual. | |
// when the d operator is executed, d just returns the saved value. | |
"use strict"; | |
var Assembler = require("./assembler.js"); | |
var util = require("./util.js"); | |
module.exports = function(poly) { // compute dual of argument, matching V and F indices | |
var result = new Assembler(); | |
var faces = new Array(poly.positions.length); // make table of face as fn of edge | |
for (var i=0; i<poly.positions.length; i++) { | |
faces[i] = new Object(); // create empty associative table | |
} | |
for (var i=0; i<poly.faces.length; i++) { | |
var v1 = poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (j=0; j<poly.faces[i].length; j++) { | |
var v2 = poly.faces[i][j]; // this vertex | |
faces[v1]["v" + v2] = i; // fill it. 2nd index is associative | |
v1 = v2; // current becomes previous | |
} | |
} | |
for (var i=0; i<poly.faces.length; i++) { // create d's v's per p's f's | |
result.newV(i, [0,0,0]); // only topology needed for canonicalize | |
} | |
for (var i=0; i<poly.faces.length; i++) { // one new flag for each old one | |
var v1 = poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (j=0; j<poly.faces[i].length; j++) { | |
var v2 = poly.faces[i][j]; // this vertex | |
result.newFlag(v1, faces[v2]["v" + v1], i); // look up face across edge | |
v1 = v2; // current becomes previous | |
} | |
} | |
var ans = result.flags2poly(); // this gives one indexing of answer | |
var sortF = new Array(ans.faces.length); // but f's of dual are randomly ordered, so sort | |
for (var i=0; i<ans.faces.length; i++) { | |
var j = util.intersect(poly.faces[ans.faces[i][0]],poly.faces[ans.faces[i][1]],poly.faces[ans.faces[i][2]]); | |
sortF[j] = ans.faces[i]; // p's v for d's f is common to three of p's f's | |
} | |
ans.faces = sortF; // replace with the sorted list of faces | |
if(poly.name) { | |
if (poly.name.substr(0,1)!="d") { | |
ans.name = "d" + poly.name; // dual name is same with "d" added... | |
} else { | |
ans.name = poly.name.substr(1); // ...or removed | |
} | |
} | |
return ans; | |
} | |
},{"./assembler.js":1,"./util.js":6}],4:[function(require,module,exports){ | |
"use strict"; | |
var util = require("./util.js"); | |
var makeDual = require("./dual.js"); | |
var Assembler = require("./assembler.js"); | |
var canonicalize = require("./canonicalize.js"); | |
exports.dual = function(poly) { | |
var dpoly = makeDual(poly); | |
dpoly.positions = canonicalize.faceCenters(poly); | |
return dpoly; | |
} | |
exports.canonicalize = function(poly) { | |
poly = util.clone(poly); | |
canonicalize.canonicalpositions(poly); | |
return poly; | |
} | |
function kis(poly, n) { // only kis n-sided faces, but n==0 means kiss all. | |
var result = new Assembler(); | |
for (var i=0; i<poly.positions.length; i++) { | |
result.newV("v"+i, poly.positions[i]); // each old vertex is a new vertex | |
} | |
var centers = canonicalize.faceCenters(poly); // new vertices in centers of n-sided face | |
var foundAny = false; // alert if don't find any | |
for (var i=0; i<poly.faces.length; i++) { | |
var v1 = "v" + poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (var j=0; j<poly.faces[i].length; j++) { | |
var v2 = "v" + poly.faces[i][j]; // this vertex | |
if (poly.faces[i].length == n || n==0) { // kiss the n's, or all | |
foundAny = true; // flag that we found some | |
result.newV("f"+i, centers[i]); // new vertex in face center | |
var fname = i + v1; | |
result.newFlag(fname, v1, v2); // three new flags, if n-sided | |
result.newFlag(fname, v2, "f"+i); | |
result.newFlag(fname, "f"+i, v1); | |
} | |
else | |
result.newFlag(i, v1, v2); // same old flag, if non-n | |
v1 = v2; // current becomes previous | |
} | |
} | |
var ans = result.flags2poly(); | |
ans.name = "k" + (n===0?"":n) + poly.name; | |
canonicalize.adjustpositions(ans, 3); // adjust and | |
return (ans); | |
} | |
exports.kis = kis; | |
function ambo(poly) { // compute ambo of argument | |
var result = new Assembler(); | |
for (var i=0; i<poly.faces.length; i++) { | |
var v1 = poly.faces[i][poly.faces[i].length-2]; // preprevious vertex | |
var v2 = poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (var j=0; j<poly.faces[i].length; j++) { | |
var v3 = poly.faces[i][j]; // this vertex | |
if (v1 < v2) // new vertices at edge midpoints | |
result.newV(midName(v1,v2), util.midpoint(poly.positions[v1],poly.positions[v2])); | |
result.newFlag("f"+i, midName(v1,v2), midName(v2,v3)); // two new flags | |
result.newFlag("v"+v2, midName(v2,v3), midName(v1,v2)); | |
v1 = v2; // shift over one | |
v2 = v3; | |
} | |
} | |
var ans = result.flags2poly(); | |
ans.name = "a" + poly.name; | |
canonicalize.adjustpositions(ans, 2); // canonicalize lightly | |
return (ans); | |
} | |
exports.ambo = ambo; | |
function midName(v1,v2) { // unique symbolic name, e.g. "1_2" | |
if (v1<v2) | |
return (v1 + "_" + v2); | |
else | |
return (v2 + "_" + v1); | |
} | |
function gyro(poly) { // compute gyro of argument | |
var result = new Assembler(); | |
for (var i=0; i<poly.positions.length; i++) | |
result.newV("v"+i, util.unit(poly.positions[i])); // each old vertex is a new vertex | |
var centers = canonicalize.faceCenters(poly); // new vertices in center of each face | |
for (var i=0; i<poly.faces.length; i++) | |
result.newV("f"+i, util.unit(centers[i])); | |
for (var i=0; i<poly.faces.length; i++) { | |
var v1 = poly.faces[i][poly.faces[i].length-2]; // preprevious vertex | |
var v2 = poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (var j=0; j<poly.faces[i].length; j++) { | |
var v3 = poly.faces[i][j]; // this vertex | |
result.newV(v1+"~"+v2, util.oneThird(poly.positions[v1],poly.positions[v2])); // new v in face | |
var fname = i + "f" + v1; | |
result.newFlag(fname, "f"+i, v1+"~"+v2); // five new flags | |
result.newFlag(fname, v1+"~"+v2, v2+"~"+v1); | |
result.newFlag(fname, v2+"~"+v1, "v"+v2); | |
result.newFlag(fname, "v"+v2, v2+"~"+v3); | |
result.newFlag(fname, v2+"~"+v3, "f"+i); | |
v1 = v2; // shift over one | |
v2 = v3; | |
} | |
} | |
var ans = result.flags2poly(); | |
ans.name = "g" + poly.name; | |
canonicalize.adjustpositions(ans, 3); // canonicalize lightly | |
return (ans); | |
} | |
exports.gyro = gyro; | |
function propellor(poly) { // compute propellor of argument | |
var result = new Assembler(); | |
for (var i=0; i<poly.positions.length; i++) | |
result.newV("v"+i, util.unit(poly.positions[i])); // each old vertex is a new vertex | |
for (var i=0; i<poly.faces.length; i++) { | |
var v1 = poly.faces[i][poly.faces[i].length-2]; // preprevious vertex | |
var v2 = poly.faces[i][poly.faces[i].length-1]; // previous vertex | |
for (var j=0; j<poly.faces[i].length; j++) { | |
var v3 = poly.faces[i][j]; // this vertex | |
result.newV(v1+"~"+v2, util.oneThird(poly.positions[v1],poly.positions[v2])); // new v in face | |
var fname = i + "f" + v2; | |
result.newFlag("v"+i, v1+"~"+v2, v2+"~"+v3); // five new flags | |
result.newFlag(fname, v1+"~"+v2, v2+"~"+v1); | |
result.newFlag(fname, v2+"~"+v1, "v"+v2); | |
result.newFlag(fname, "v"+v2, v2+"~"+v3); | |
result.newFlag(fname, v2+"~"+v3, v1+"~"+v2); | |
v1 = v2; // shift over one | |
v2 = v3; | |
} | |
} | |
var ans = result.flags2poly(); | |
ans.name = "p" + poly.name; | |
canonicalize.adjustpositions(ans, 3); // canonicalize lightly | |
return (ans); | |
} | |
exports.propellor = propellor; | |
function reflect(poly) { // compute reflection through origin | |
poly = util.clone(poly); | |
for (var i=0; i<poly.positions.length; i++) | |
poly.positions[i] = util.mult(-1, poly.positions[i]); // reflect each point | |
for (var i=0; i<poly.faces.length; i++) | |
poly.faces[i] = poly.faces[i].reverse(); // repair clockwise-ness | |
poly.name = "r" + poly.name; | |
canonicalize.adjustpositions(poly, 1); // build dual | |
return (poly); | |
} | |
exports.reflect = reflect; | |
exports.expand = function(poly) { | |
return ambo(ambo(poly)); | |
} | |
exports.bevel = function(poly) { | |
return truncate(ambo(poly)); | |
} | |
exports.ortho = function(poly) { | |
return join(join(poly)); | |
} | |
exports.meta = function(poly) { | |
return kis(join(poly)); | |
} | |
exports.truncate = function(poly, n) { | |
return dual(kis(dual(poly), n)); | |
} | |
exports.join = function(poly) { | |
return dual(ambo(dual(poly))); | |
} | |
exports.split = function(poly) { | |
return dual(gyro(dual(poly))); | |
} | |
},{"./assembler.js":1,"./canonicalize.js":2,"./dual.js":3,"./util.js":6}],5:[function(require,module,exports){ | |
"use strict"; | |
var util = require("./util.js"); | |
var canonicalize = require("./canonicalize.js"); | |
module.exports = { | |
tetrahedron: function() { | |
return { | |
name: "T", | |
faces: [ [0,1,2], [0,2,3], [0,3,1], [1,3,2] ], | |
positions: [ [1.,1.,1.], [1.,-1.,-1.], [-1.,1.,-1.], [-1.,-1.,1.] ] | |
}; | |
}, | |
octahedron: function() { | |
return { | |
name: "O", | |
faces: [ [0,1,2], [0,2,3], [0,3,4], [0,4,1], [1,4,5], [1,5,2], [2,5,3], [3,5,4] ], | |
positions: [ [0,0,1.414], [1.414,0,0], [0,1.414,0], [-1.414,0,0], [0,-1.414,0], [0,0,-1.414] ] | |
}; | |
}, | |
cube: function() { | |
return { | |
name: "C", | |
faces: [ [3,0,1,2], [3,4,5,0], [0,5,6,1], [1,6,7,2], [2,7,4,3], [5,4,7,6] ], | |
positions: [ [ 0.707, 0.707,0.707], [-0.707,0.707,0.707], | |
[-0.707,-0.707,0.707], [0.707,-0.707,0.707], | |
[0.707,-0.707,-0.707], [0.707,0.707,-0.707], | |
[-0.707,0.707,-0.707], [-0.707,-0.707,-0.707] ] | |
}; | |
}, | |
icosahedron: function() { | |
return { | |
name: "I", | |
faces: [ [0,1,2], [0,2,3], [0,3,4], [0,4,5], | |
[0,5,1], [1,5,7], [1,7,6], [1,6,2], | |
[2,6,8], [2,8,3], [3,8,9], [3,9,4], | |
[4,9,10], [4,10,5], [5,10,7], [6,7,11], | |
[6,11,8], [7,10,11], [8,11,9], [9,11,10] ], | |
positions: [ [0,0,1.176], [1.051,0,0.526], | |
[0.324,1.,0.525], [-0.851,0.618,0.526], | |
[-0.851,-0.618,0.526], [0.325,-1.,0.526], | |
[0.851,0.618,-0.526], [0.851,-0.618,-0.526], | |
[-0.325,1.,-0.526], [-1.051,0,-0.526], | |
[-0.325,-1.,-0.526], [0,0,-1.176] ] | |
}; | |
}, | |
dodecahedron: function() { | |
return { | |
name: "D", | |
faces: [ [0,1,4,7,2], [0,2,6,9,3], [0,3,8,5,1], | |
[1,5,11,10,4], [2,7,13,12,6], [3,9,15,14,8], | |
[4,10,16,13,7], [5,8,14,17,11], [6,12,18,15,9], | |
[10,11,17,19,16], [12,13,16,19,18], [14,15,18,19,17] ], | |
positions: [ [0,0,1.07047], [0.713644,0,0.797878], | |
[-0.356822,0.618,0.797878], [-0.356822,-0.618,0.797878], | |
[0.797878,0.618034,0.356822], [0.797878,-0.618,0.356822], | |
[-0.934172,0.381966,0.356822], [0.136294,1.,0.356822], | |
[0.136294,-1.,0.356822], [-0.934172,-0.381966,0.356822], | |
[0.934172,0.381966,-0.356822], [0.934172,-0.381966,-0.356822], | |
[-0.797878,0.618,-0.356822], [-0.136294,1.,-0.356822], | |
[-0.136294,-1.,-0.356822], [-0.797878,-0.618034,-0.356822], | |
[0.356822,0.618,-0.797878], [0.356822,-0.618,-0.797878], | |
[-0.713644,0,-0.797878], [0,0,-1.07047] ] | |
}; | |
}, | |
prism: function(n) { | |
var theta = Math.PI * 2.0 / n; | |
var h = Math.sin(theta/2); | |
var positions = []; | |
for (var i=0; i<n; i++) { | |
positions.push([Math.cos(i*theta), Math.sin(i*theta), h]); | |
} | |
for (var i=0; i<n; i++) { | |
positions.push([Math.cos(i*theta), Math.sin(i*theta),-h]); | |
} | |
var faces = [ util.sequence(n-1, 0), util.sequence(n, 2*n-1) ]; | |
for(var i=0; i<n; ++i) { | |
faces.push([i, (i+1)%n, (i+1)%n+n, i+n]); | |
} | |
var result = { | |
name: "P" + n, | |
positions: positions, | |
faces: faces | |
}; | |
canonicalize.adjustpositions(result, 1); | |
return result; | |
}, | |
antiprism: function(n) { | |
var theta = Math.PI * 2.0 / n; | |
var h = Math.sqrt(1-4/(4+2*Math.cos(theta/2)-2*Math.cos(theta))); | |
var r = Math.sqrt(1-h*h); | |
var f = Math.sqrt(h*h + Math.pow(r*Math.cos(theta/2), 2)); | |
r=r/f; | |
h=h/f; | |
var positions = []; | |
for (var i=0; i<n; i++) { | |
positions.push([r*Math.cos(i*theta), r*Math.sin(i*theta), h]); | |
} | |
for (var i=0; i<n; i++) { | |
positions.push([r*Math.cos((i+0.5)*theta), r*Math.sin((i+0.5)*theta), -h]); | |
} | |
var faces = [ util.sequence(n-1, 0), util.sequence(n, 2*n-1) ] | |
for (var i=0; i<n; i++) { | |
faces.push([i, (i+1)%n, i+n]); | |
faces.push([i, i+n, ((n+i-1)%n+n)]); | |
} | |
var result = { | |
name: "A" + n, | |
positions: positions, | |
faces: faces | |
}; | |
canonicalize.adjustpositions(result, 1); | |
return result; | |
}, | |
pyramid: function(n) { | |
var theta = Math.PI * 2.0 / n; | |
var positions = []; | |
for(var i=0; i<n; ++i) { | |
positions.push([Math.cos(i*theta), Math.sin(i*theta), .2]); | |
} | |
positions.push([0, 0, -2]); | |
var faces = [ util.sequence(n-1, 0) ]; | |
for (var i=0; i<n; ++i) { | |
faces.push([i, (i+1)%n, n]); | |
} | |
var result = { | |
name: "Y" + n, | |
positions: positions, | |
faces: faces | |
}; | |
canonicalize.canonicalpositions(result, 3); | |
return result; | |
} | |
} | |
},{"./canonicalize.js":2,"./util.js":6}],6:[function(require,module,exports){ | |
"use strict"; | |
function clone(poly) { | |
var npositions = new Array(poly.positions.length); | |
for(var i=0; i<poly.positions.length; ++i) { | |
npositions[i] = poly.positions[i].slice(0); | |
} | |
var nfaces = []; | |
for(var i=0; i<poly.faces.length; ++i) { | |
nfaces[i] = poly.faces[i].slice(0); | |
} | |
return { | |
name: poly.name.slice(0), | |
positions: npositions, | |
faces: nfaces | |
} | |
} | |
exports.clone = clone; | |
function intersect(set1, set2, set3) { // find element common to 3 sets | |
for (var i=0; i<set1.length; i++) { // by brute force search | |
for (var j=0; j<set2.length; j++) { | |
if (set1[i]===set2[j]) { | |
for (var k=0; k<set3.length; k++) { | |
if (set1[i]===set3[k]) { | |
return set1[i]; | |
} | |
} | |
} | |
} | |
} | |
throw new Error("program bug in intersect()"); | |
return null; | |
} | |
exports.intersect = intersect; | |
function sequence(start, stop) { // make list of integers, inclusive | |
var ans = new Array(); | |
if (start <= stop) { | |
for (var i=start; i<=stop; i++) { | |
ans.push(i); | |
} | |
} else { | |
for (var i=start; i>=stop; i--) { | |
ans.push(i); | |
} | |
} | |
return ans; | |
} | |
exports.sequence = sequence; | |
function orthogonal(v3, v2, v1) { // find unit vector orthog to plane of 3 pts | |
var d1 = sub(v2, v1); // adjacent edge vectors | |
var d2 = sub(v3, v2); | |
return [ d1[1]*d2[2] - d1[2]*d2[1], // cross product | |
d1[2]*d2[0] - d1[0]*d2[2], | |
d1[0]*d2[1] - d1[1]*d2[0] ] | |
} | |
exports.orthogonal = orthogonal; | |
function mag2(vec) { // magnitude squared of 3-vector | |
return vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]; | |
} | |
exports.mag2 = mag2; | |
function tangentPoint(v1, v2) { // point where line v1...v2 tangent to an origin sphere | |
var d = sub(v2,v1); // difference vector | |
console.log(d); | |
return(sub(v1, mult(dot(d,v1)/mag2(d), d))); | |
} | |
exports.tangentPoint = tangentPoint; | |
function edgeDist(v1, v2) { // distance of line v1...v2 to origin | |
return(Math.sqrt(mag2(tangentPoint(v1, v2)))); | |
} | |
exports.edgeDist = edgeDist; | |
function vecZero() { | |
return [0.0,0.0,0.0]; | |
} | |
exports.vecZero = vecZero; | |
function mult(c, vec) { // c times 3-vector | |
return [ c*vec[0], | |
c*vec[1], | |
c*vec[2] ]; | |
} | |
exports.mult = mult; | |
function add(vec1, vec2) { // sum two 3-vectors | |
return [ vec1[0]+vec2[0], | |
vec1[1]+vec2[1], | |
vec1[2]+vec2[2] ]; | |
} | |
exports.add = add; | |
function sub(vec1, vec2) { // subtract two 3-vectors | |
return [ vec1[0]-vec2[0], | |
vec1[1]-vec2[1], | |
vec1[2]-vec2[2] ] | |
} | |
exports.sub = sub; | |
function dot(vec1, vec2) { // dot product two 3-vectors | |
return (vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]); | |
} | |
exports.dot = dot; | |
function midpoint(vec1, vec2) { // mean of two 3-vectors | |
return [ 0.5*(vec1[0] + vec2[0]), | |
0.5*(vec1[1] + vec2[1]), | |
0.5*(vec1[2] + vec2[2]) ] | |
} | |
exports.midpoint = midpoint; | |
function oneThird(vec1, vec2) { // approx. (2/3)v1 + (1/3)v2 (assumes 3-vector) | |
return [ 0.7*vec1[0] + 0.3*vec2[0], | |
0.7*vec1[1] + 0.3*vec2[1], | |
0.7*vec1[2] + 0.3*vec2[2] ] | |
return ans; | |
} | |
exports.oneThird = oneThird; | |
function reciprocal(vec) { // reflect 3-vector in unit sphere | |
var factor = 1./mag2(vec); | |
return [ factor*vec[0], | |
factor*vec[1], | |
factor*vec[2] ] | |
} | |
exports.reciprocal = reciprocal; | |
function unit(vec) { // normalize 3-vector to unit magnitude | |
var size = mag2(vec); | |
if (size <= 1e-8) { // remove this test someday... | |
return (vec); | |
} | |
var c = 1./Math.sqrt(size); | |
return mult(c, vec); | |
} | |
exports.unit = unit; | |
},{}],"conway-hart":[function(require,module,exports){ | |
"use strict"; | |
//Import operators | |
var seeds = require("./lib/seeds.js"); | |
var operators = require("./lib/operators.js"); | |
//Seed types | |
var SEED_FUNCS = { | |
"T": seeds.tetrahedron, | |
"O": seeds.octahedron, | |
"C": seeds.cube, | |
"I": seeds.icosahedron, | |
"D": seeds.dodecahedron, | |
"P": seeds.prism, | |
"A": seeds.antiprism, | |
"Y": seeds.pyramid | |
}; | |
//Operator types | |
var OPERATOR_FUNCS = { | |
"k": operators.kis, | |
"a": operators.ambo, | |
"g": operators.gyro, | |
"p": operators.propellor, | |
"d": operators.dual, | |
"r": operators.reflect, | |
"c": operators.canonicalize | |
}; | |
//Checks if a seed is a token | |
function isToken(c) { | |
return "kagpdcrTOCIDPAY".indexOf(c) >= 0; | |
} | |
//Checks if a value is numeric | |
function isNumeric(c) { | |
return 48 <= c && c <= 57; | |
} | |
//Tokenize an expression in Conway notation | |
function tokenize(expr) { | |
expr = expr.replace(/P4$/g, "C") // P4 --> C (C is prism) | |
.replace(/A3$/g, "O") // A3 --> O (O is antiprism) | |
.replace(/Y3$/g, "T") // Y3 --> T (T is pyramid) | |
.replace(/e/g, "aa") // e --> aa (abbr. for explode) | |
.replace(/b/g, "ta") // b --> ta (abbr. for bevel) | |
.replace(/o/g, "jj") // o --> jj (abbr. for ortho) | |
.replace(/m/g, "kj") // m --> kj (abbr. for meta) | |
.replace(/t(\d*)/g, "dk$1d") // t(n) --> dk(n)d (dual operations) | |
.replace(/j/g, "dad") // j --> dad (dual operations) | |
.replace(/s/g, "dgd") // s --> dgd (dual operations) | |
.replace(/dd/g, "") // dd --> null (order 2) | |
.replace(/ad/g, "a") // ad --> a (a_ = ad_) | |
.replace(/gd/g, "g") // gd --> g (g_ = gd_) | |
.replace(/aY/g, "A") // aY --> A (interesting fact) | |
.replace(/dT/g, "T") // dT --> T (self-dual) | |
.replace(/gT/g, "D") // gT --> D (symm change) | |
.replace(/aT/g, "O") // aT --> O (symm change) | |
.replace(/dC/g, "O") // dC --> O (dual pair) | |
.replace(/dO/g, "C") // dO --> C (dual pair) | |
.replace(/dI/g, "D") // dI --> D (dual pair) | |
.replace(/dD/g, "I") // dD --> I (dual pair) | |
.replace(/aO/g, "aC") // aO --> aC (for uniqueness) | |
.replace(/aI/g, "aD") // aI --> aD (for uniqueness) | |
.replace(/gO/g, "gC") // gO --> gC (for uniqueness) | |
.replace(/gI/g, "gD"); // gI --> gD (for uniqueness) | |
var toks = []; | |
var ptr = 0; | |
while(ptr < expr.length) { | |
var op = expr.charAt(ptr); | |
if(!isToken(op)) { | |
throw new Error("Unexpected token: " + c + " in input " + expr + " at " + ptr); | |
} | |
var start_n = ++ptr; | |
while(ptr < expr.length && isNumeric(expr.charCodeAt(ptr))) { | |
++ptr; | |
} | |
var n = parseInt(expr.substr(start_n, ptr)); | |
toks.push({ | |
op: op, | |
n: n | 0 | |
}); | |
} | |
toks.reverse(); | |
return toks; | |
} | |
//Main expression interpreter | |
function evalConwayHart(expr) { | |
//Parse expression | |
var toks = tokenize(expr); | |
//Initialize seed | |
var ctor = SEED_FUNCS[toks[0].op]; | |
if(!ctor) { | |
throw Error("Invalid seed type: " + JSON.stringify(ops[0])); | |
} else if("PAY".indexOf(toks[0].op) >= 0) { | |
if(toks[0].n < 3) { | |
throw Error("Invalid number of faces for seed"); | |
} | |
} else if(toks[0].n !== 0) { | |
throw Error("Seed " + toks[0].op + " does not use a parameter"); | |
} | |
var poly = ctor(toks[0].n); | |
//Apply operators | |
for(var i=1; i<toks.length; ++i) { | |
var op = OPERATOR_FUNCS[toks[i].op]; | |
if(!op) { | |
throw Error("Invalid operator: " + toks[i]); | |
} | |
poly = op(poly, toks[i].n); | |
} | |
return { name: poly.name, cells: poly.faces, positions: poly.positions }; | |
} | |
module.exports = evalConwayHart; | |
},{"./lib/operators.js":4,"./lib/seeds.js":5}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImxpYi9hc3NlbWJsZXIuanMiLCJsaWIvY2Fub25pY2FsaXplLmpzIiwibGliL2R1YWwuanMiLCJsaWIvb3BlcmF0b3JzLmpzIiwibGliL3NlZWRzLmpzIiwibGliL3V0aWwuanMiLCJpbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdklBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1SUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJmdW5jdGlvbiBBc3NlbWJsZXIoKSB7XG4gIHRoaXMuZmxhZ3MgICAgICA9IHt9O1xuICB0aGlzLnBvc2l0aW9ucyAgPSB7fTtcbiAgdGhpcy5mYWNlcyAgICAgID0ge307XG59XG5cbkFzc2VtYmxlci5wcm90b3R5cGUubmV3RmxhZyA9IGZ1bmN0aW9uKGZhY2UsIHYxLCB2Mikge1xuICBpZiAoIXRoaXMuZmxhZ3NbZmFjZV0pIHtcbiAgICB0aGlzLmZsYWdzW2ZhY2VdID0geyB9O1xuICB9XG4gIHRoaXMuZmxhZ3NbZmFjZV1bdjFdID0gdjI7XG59XG5cbkFzc2VtYmxlci5wcm90b3R5cGUubmV3ViA9IGZ1bmN0aW9uKG5hbWUsIHApIHtcbiAgdGhpcy5wb3NpdGlvbnNbbmFtZV0gPSBwO1xufVxuXG5Bc3NlbWJsZXIucHJvdG90eXBlLmZsYWdzMnBvbHkgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHJwb3NpdGlvbnMgPSBbXTtcbiAgdmFyIHZlcnRzICAgICAgPSB7fTtcbiAgZm9yICh2YXIgaSBpbiB0aGlzLnBvc2l0aW9ucykge1xuICAgIHZlcnRzW2ldID0gcnBvc2l0aW9ucy5sZW5ndGg7XG4gICAgcnBvc2l0aW9ucy5wdXNoKHRoaXMucG9zaXRpb25zW2ldKTtcbiAgfVxuICB2YXIgcmZhY2VzID0gW107XG4gIGZvciAodmFyIGkgaW4gdGhpcy5mbGFncykge1xuICAgIHZhciBmbGFnID0gdGhpcy5mbGFnc1tpXTtcbiAgICB2YXIgZiA9IFtdO1xuICAgIHZhciB2MCA9IE9iamVjdC5rZXlzKGZsYWcpWzBdO1xuICAgIHZhciB2ID0gdjA7XG4gICAgZG8ge1xuICAgICAgZi5wdXNoKHZlcnRzW3ZdKTtcbiAgICAgIHYgPSBmbGFnW3ZdO1xuICAgIH0gd2hpbGUgKHYgIT0gdjApO1xuICAgIHJmYWNlcy5wdXNoKGYpO1xuICB9XG4gIHJldHVybiB7XG4gICAgbmFtZTogICAgICAgXCI/XCIsXG4gICAgcG9zaXRpb25zOiAgcnBvc2l0aW9ucyxcbiAgICBmYWNlczogICAgICByZmFjZXNcbiAgfTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBBc3NlbWJsZXI7XG4iLCIvLy0tLS0tLS0tLS0tLS0tLS0tLS1DYW5vbmljYWxpemF0aW9uIEFsZ29yaXRobS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBUcnVlIGNhbm9uaWNhbGl6YXRpb24gcmF0aGVyIHNsb3cuICBVc2luZyBjZW50ZXIgb2YgZ3Jhdml0eSBvZiB2ZXJ0aWNlcyBmb3IgZWFjaFxuLy8gZmFjZSBnaXZlcyBhIHF1aWNrIFwiYWRqdXN0bWVudFwiIHdoaWNoIHBsYW5hcml6ZXMgZmFjZXMgYXQgbGVhc3QuXG5cInVzZSBzdHJpY3RcIjtcblxudmFyIHV0aWwgPSByZXF1aXJlKFwiLi91dGlsLmpzXCIpO1xudmFyIG1ha2VEdWFsID0gcmVxdWlyZShcIi4vZHVhbC5qc1wiKTtcblxuZnVuY3Rpb24gcmVjaXByb2NhbE4ocG9seSkgeyAgICAvLyBtYWtlIGFycmF5IG9mIHZlcnRpY2VzIHJlY2lwcm9jYWwgdG8gZ2l2ZW4gcGxhbmVzXG4gIHZhciBhbnMgPSBuZXcgQXJyYXkocG9seS5mYWNlcy5sZW5ndGgpO1xuICBmb3IgKHZhciBpPTA7IGk8cG9seS5mYWNlcy5sZW5ndGg7ICsraSkgeyAgICAvLyBmb3IgZWFjaCBmYWNlOlxuICAgIHZhciBjZW50cm9pZCA9IFswLjAsIDAuMCwgMC4wXTsgICAgICAgICAgIC8vIHJ1bm5pbmcgc3VtIG9mIHZlcnRleCBjb29yZHNcbiAgICB2YXIgbm9ybWFsID0gWzAuMCwgMC4wLCAwLjBdOyAgICAgICAgICAgICAvLyBydW5uaW5nIHN1bSBvZiBub3JtYWwgdmVjdG9yc1xuICAgIHZhciBhdmdFZGdlRGlzdCA9IDAuOyAgICAgICAgICAgICAgIC8vIHJ1bm5pbmcgc3VtIGZvciBhdmcgZWRnZSBkaXN0YW5jZVxuICAgIHZhciB2MSA9IHBvbHkuZmFjZXNbaV1bcG9seS5mYWNlc1tpXS5sZW5ndGgtMl07ICAvLyBwcmVwcmV2aW91cyB2ZXJ0ZXhcbiAgICB2YXIgdjIgPSBwb2x5LmZhY2VzW2ldW3BvbHkuZmFjZXNbaV0ubGVuZ3RoLTFdOyAgLy8gcHJldmlvdXMgdmVydGV4XG4gICAgZm9yICh2YXIgaj0wOyBqPHBvbHkuZmFjZXNbaV0ubGVuZ3RoOyBqKyspICB7XG4gICAgICB2YXIgdjMgPSBwb2x5LmZhY2VzW2ldW2pdOyAgICAgICAgICAgICAgICAgIC8vIHRoaXMgdmVydGV4XG4gICAgICBjZW50cm9pZCA9IHV0aWwuYWRkKGNlbnRyb2lkLCBwb2x5LnBvc2l0aW9uc1t2M10pO1xuICAgICAgbm9ybWFsID0gdXRpbC5hZGQobm9ybWFsLCB1dGlsLm9ydGhvZ29uYWwocG9seS5wb3NpdGlvbnNbdjFdLCBwb2x5LnBvc2l0aW9uc1t2Ml0sIHBvbHkucG9zaXRpb25zW3YzXSkpO1xuICAgICAgYXZnRWRnZURpc3QgPSBhdmdFZGdlRGlzdCArIHV0aWwuZWRnZURpc3QocG9seS5wb3NpdGlvbnNbdjFdLCBwb2x5LnBvc2l0aW9uc1t2Ml0pO1xuICAgICAgdjEgPSB2MjsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNoaWZ0IG92ZXIgb25lXG4gICAgICB2MiA9IHYzO1xuICAgIH1cbiAgICBjZW50cm9pZCA9IHV0aWwubXVsdCgxLjAvcG9seS5mYWNlc1tpXS5sZW5ndGgsIGNlbnRyb2lkKTtcbiAgICBub3JtYWwgPSB1dGlsLnVuaXQobm9ybWFsKTtcbiAgICBhdmdFZGdlRGlzdCA9IGF2Z0VkZ2VEaXN0IC8gcG9seS5mYWNlc1tpXS5sZW5ndGg7XG4gICAgYW5zW2ldID0gdXRpbC5yZWNpcHJvY2FsKHV0aWwubXVsdCh1dGlsLmRvdChjZW50cm9pZCwgbm9ybWFsKSwgbm9ybWFsKSk7ICAvLyBiYXNlZCBvbiBmYWNlXG4gICAgYW5zW2ldID0gdXRpbC5tdWx0KCgxK2F2Z0VkZ2VEaXN0KS8yLCBhbnNbaV0pOyAgICAgICAgICAgICAgICAgIC8vIGVkZ2UgY29ycmVjdGlvblxuICB9XG4gIHJldHVybiAoYW5zKTtcbn1cblxuZnVuY3Rpb24gcmVjaXByb2NhbEMocG9seSkgeyAgICAgICAgICAgLy8gcmV0dXJuIGFycmF5IG9mIHJlY2lwcm9jYWxzIG9mIGZhY2UgY2VudGVyc1xuICB2YXIgY2VudGVyID0gZmFjZUNlbnRlcnMocG9seSk7XG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIG0yID0gY2VudGVyW2ldWzBdKmNlbnRlcltpXVswXSArIGNlbnRlcltpXVsxXSpjZW50ZXJbaV1bMV0gKyBjZW50ZXJbaV1bMl0qY2VudGVyW2ldWzJdO1xuICAgIGNlbnRlcltpXVswXSA9IGNlbnRlcltpXVswXSAvIG0yOyAgIC8vIGRpdmlkZSBlYWNoIGNvb3JkIGJ5IG1hZ25pdHVkZSBzcXVhcmVkXG4gICAgY2VudGVyW2ldWzFdID0gY2VudGVyW2ldWzFdIC8gbTI7XG4gICAgY2VudGVyW2ldWzJdID0gY2VudGVyW2ldWzJdIC8gbTI7XG4gIH1cbiAgcmV0dXJuKGNlbnRlcik7XG59XG5cbmZ1bmN0aW9uIGZhY2VDZW50ZXJzKHBvbHkpIHsgICAgICAgICAgICAgIC8vIHJldHVybiBhcnJheSBvZiBcImZhY2UgY2VudGVyc1wiXG4gIHZhciBhbnMgPSBuZXcgQXJyYXkocG9seS5mYWNlcy5sZW5ndGgpO1xuICBmb3IgKHZhciBpPTA7IGk8cG9seS5mYWNlcy5sZW5ndGg7IGkrKykge1xuICAgIGFuc1tpXSA9IHV0aWwudmVjWmVybygpOyAgICAgICAgICAgICAgICAgICAgICAvLyBydW5uaW5nIHN1bVxuICAgIGZvciAodmFyIGo9MDsgajxwb2x5LmZhY2VzW2ldLmxlbmd0aDsgaisrKSAgICAvLyBqdXN0IGF2ZXJhZ2UgdmVydGV4IGNvb3JkczpcbiAgICAgIGFuc1tpXSA9IHV0aWwuYWRkKGFuc1tpXSwgcG9seS5wb3NpdGlvbnNbcG9seS5mYWNlc1tpXVtqXV0pOyAgLy8gc3VtIGFuZC4uLlxuICAgIGFuc1tpXSA9IHV0aWwubXVsdCgxLi9wb2x5LmZhY2VzW2ldLmxlbmd0aCwgYW5zW2ldKTsgICAgICAgIC8vIC4uLmRpdmlkZSBieSBuXG4gIH1cbiAgcmV0dXJuIChhbnMpO1xufVxuZXhwb3J0cy5mYWNlQ2VudGVycyA9IGZhY2VDZW50ZXJzO1xuXG5mdW5jdGlvbiBjYW5vbmljYWxwb3NpdGlvbnMocG9seSwgbkl0ZXJhdGlvbnMpIHsgICAgICAvLyBjb21wdXRlIG5ldyB2ZXJ0ZXggY29vcmRzLlxuICB2YXIgZHBvbHkgPSBtYWtlRHVhbChwb2x5KSAgICAgLy8gdidzIG9mIGR1YWwgYXJlIGluIG9yZGVyIG9yIGFyZydzIGYnc1xuICBmb3IgKHZhciBjb3VudD0wOyBjb3VudDxuSXRlcmF0aW9uczsgY291bnQrKykgeyAgICAvLyBpdGVyYXRpb246XG4gICAgZHBvbHkucG9zaXRpb25zID0gcmVjaXByb2NhbE4ocG9seSk7XG4gICAgcG9seS5wb3NpdGlvbnMgPSByZWNpcHJvY2FsTihkcG9seSk7XG4gIH1cbn1cbmV4cG9ydHMuY2Fub25pY2FscG9zaXRpb25zID0gY2Fub25pY2FscG9zaXRpb25zO1xuXG5mdW5jdGlvbiBhZGp1c3Rwb3NpdGlvbnMocG9seSwgbkl0ZXJhdGlvbnMpIHtcbiAgdmFyIGRwb2x5ID0gbWFrZUR1YWwocG9seSkgICAgIC8vIHYncyBvZiBkdWFsIGFyZSBpbiBvcmRlciBvciBhcmcncyBmJ3NcbiAgZm9yICh2YXIgY291bnQ9MDsgY291bnQ8MTsgY291bnQrKykgeyAgICAvLyBpdGVyYXRpb246XG4gICAgZHBvbHkucG9zaXRpb25zID0gcmVjaXByb2NhbEMocG9seSk7ICAgICAgICAgICAgIC8vIHJlY2lwcm9jYXRlIGZhY2UgY2VudGVyc1xuICAgIHBvbHkucG9zaXRpb25zID0gcmVjaXByb2NhbEMoZHBvbHkpOyAgICAgICAgICAgICAvLyByZWNpcHJvY2F0ZSBmYWNlIGNlbnRlcnNcbiAgfVxufVxuZXhwb3J0cy5hZGp1c3Rwb3NpdGlvbnMgPSBhZGp1c3Rwb3NpdGlvbnM7XG4iLCIvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tRHVhbC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gdGhlIG1ha2VEdWFsIGZ1bmN0aW9uIGNvbXB1dGVzIHRoZSBkdWFsJ3MgdG9wb2xvZ3ksIG5lZWRlZCBmb3IgY2Fub25pY2FsaXphdGlvbixcbi8vIHdoZXJlIHBvc2l0aW9ucydzIGFyZSBkZXRlcm1pbmVkLiAgSXQgaXMgdGhlbiBzYXZlZCBpbiBhIGdsb2JhbCB2YXJpYWJsZSBnbG9iU2F2ZWREdWFsLlxuLy8gd2hlbiB0aGUgZCBvcGVyYXRvciBpcyBleGVjdXRlZCwgZCBqdXN0IHJldHVybnMgdGhlIHNhdmVkIHZhbHVlLlxuXG5cInVzZSBzdHJpY3RcIjtcblxudmFyIEFzc2VtYmxlciA9IHJlcXVpcmUoXCIuL2Fzc2VtYmxlci5qc1wiKTtcbnZhciB1dGlsID0gcmVxdWlyZShcIi4vdXRpbC5qc1wiKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihwb2x5KSB7ICAgLy8gY29tcHV0ZSBkdWFsIG9mIGFyZ3VtZW50LCBtYXRjaGluZyBWIGFuZCBGIGluZGljZXNcbiAgdmFyIHJlc3VsdCA9IG5ldyBBc3NlbWJsZXIoKTtcbiAgdmFyIGZhY2VzID0gbmV3IEFycmF5KHBvbHkucG9zaXRpb25zLmxlbmd0aCk7IC8vIG1ha2UgdGFibGUgb2YgZmFjZSBhcyBmbiBvZiBlZGdlXG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LnBvc2l0aW9ucy5sZW5ndGg7IGkrKykge1xuICAgIGZhY2VzW2ldID0gbmV3IE9iamVjdCgpOyAgICAvLyBjcmVhdGUgZW1wdHkgYXNzb2NpYXRpdmUgdGFibGVcbiAgfVxuICBmb3IgKHZhciBpPTA7IGk8cG9seS5mYWNlcy5sZW5ndGg7IGkrKykge1xuICAgIHZhciB2MSA9IHBvbHkuZmFjZXNbaV1bcG9seS5mYWNlc1tpXS5sZW5ndGgtMV07ICAvLyBwcmV2aW91cyB2ZXJ0ZXhcbiAgICBmb3IgKGo9MDsgajxwb2x5LmZhY2VzW2ldLmxlbmd0aDsgaisrKSB7XG4gICAgICB2YXIgdjIgPSBwb2x5LmZhY2VzW2ldW2pdOyAgICAgICAgICAgICAgICAgIC8vIHRoaXMgdmVydGV4XG4gICAgICBmYWNlc1t2MV1bXCJ2XCIgKyB2Ml0gPSBpOyAgICAvLyBmaWxsIGl0LiAgMm5kIGluZGV4IGlzIGFzc29jaWF0aXZlXG4gICAgICB2MSA9IHYyOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY3VycmVudCBiZWNvbWVzIHByZXZpb3VzXG4gICAgfVxuICB9XG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKSB7ICAgICAgICAgLy8gY3JlYXRlIGQncyB2J3MgcGVyIHAncyBmJ3NcbiAgICByZXN1bHQubmV3VihpLCBbMCwwLDBdKTsgICAgICAgICAgICAgICAgICAgICAgLy8gb25seSB0b3BvbG9neSBuZWVkZWQgZm9yIGNhbm9uaWNhbGl6ZVxuICB9XG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKSB7ICAgICAgIC8vIG9uZSBuZXcgZmxhZyBmb3IgZWFjaCBvbGQgb25lXG4gICAgdmFyIHYxID0gcG9seS5mYWNlc1tpXVtwb2x5LmZhY2VzW2ldLmxlbmd0aC0xXTsgIC8vIHByZXZpb3VzIHZlcnRleFxuICAgIGZvciAoaj0wOyBqPHBvbHkuZmFjZXNbaV0ubGVuZ3RoOyBqKyspIHtcbiAgICAgIHZhciB2MiA9IHBvbHkuZmFjZXNbaV1bal07ICAgICAgICAgICAgICAgICAgLy8gdGhpcyB2ZXJ0ZXhcbiAgICAgIHJlc3VsdC5uZXdGbGFnKHYxLCBmYWNlc1t2Ml1bXCJ2XCIgKyB2MV0sIGkpOyAgICAgICAgLy8gbG9vayB1cCBmYWNlIGFjcm9zcyBlZGdlXG4gICAgICB2MSA9IHYyOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY3VycmVudCBiZWNvbWVzIHByZXZpb3VzXG4gICAgfVxuICB9XG4gIHZhciBhbnMgPSByZXN1bHQuZmxhZ3MycG9seSgpOyAgICAgIC8vIHRoaXMgZ2l2ZXMgb25lIGluZGV4aW5nIG9mIGFuc3dlclxuICB2YXIgc29ydEYgPSBuZXcgQXJyYXkoYW5zLmZhY2VzLmxlbmd0aCk7ICAgICAvLyBidXQgZidzIG9mIGR1YWwgYXJlIHJhbmRvbWx5IG9yZGVyZWQsIHNvIHNvcnRcbiAgZm9yICh2YXIgaT0wOyBpPGFucy5mYWNlcy5sZW5ndGg7IGkrKykge1xuICAgIHZhciBqID0gdXRpbC5pbnRlcnNlY3QocG9seS5mYWNlc1thbnMuZmFjZXNbaV1bMF1dLHBvbHkuZmFjZXNbYW5zLmZhY2VzW2ldWzFdXSxwb2x5LmZhY2VzW2Fucy5mYWNlc1tpXVsyXV0pO1xuICAgIHNvcnRGW2pdID0gYW5zLmZhY2VzW2ldOyAgLy8gcCdzIHYgZm9yIGQncyBmIGlzIGNvbW1vbiB0byB0aHJlZSBvZiBwJ3MgZidzXG4gIH1cbiAgYW5zLmZhY2VzID0gc29ydEY7ICAgICAgICAgICAgLy8gcmVwbGFjZSB3aXRoIHRoZSBzb3J0ZWQgbGlzdCBvZiBmYWNlc1xuICBpZihwb2x5Lm5hbWUpIHtcbiAgICBpZiAocG9seS5uYW1lLnN1YnN0cigwLDEpIT1cImRcIikge1xuICAgICAgYW5zLm5hbWUgPSBcImRcIiArIHBvbHkubmFtZTsgICAgICAgIC8vIGR1YWwgbmFtZSBpcyBzYW1lIHdpdGggXCJkXCIgYWRkZWQuLi5cbiAgICB9IGVsc2Uge1xuICAgICAgYW5zLm5hbWUgPSBwb2x5Lm5hbWUuc3Vic3RyKDEpOyAgICAvLyAuLi5vciByZW1vdmVkXG4gICAgfVxuICB9XG4gIHJldHVybiBhbnM7XG59XG4iLCJcInVzZSBzdHJpY3RcIjtcblxudmFyIHV0aWwgPSByZXF1aXJlKFwiLi91dGlsLmpzXCIpO1xudmFyIG1ha2VEdWFsID0gcmVxdWlyZShcIi4vZHVhbC5qc1wiKTtcbnZhciBBc3NlbWJsZXIgPSByZXF1aXJlKFwiLi9hc3NlbWJsZXIuanNcIik7XG52YXIgY2Fub25pY2FsaXplID0gcmVxdWlyZShcIi4vY2Fub25pY2FsaXplLmpzXCIpO1xuXG5leHBvcnRzLmR1YWwgPSBmdW5jdGlvbihwb2x5KSB7XG4gIHZhciBkcG9seSA9IG1ha2VEdWFsKHBvbHkpO1xuICBkcG9seS5wb3NpdGlvbnMgPSBjYW5vbmljYWxpemUuZmFjZUNlbnRlcnMocG9seSk7XG4gIHJldHVybiBkcG9seTtcbn1cblxuZXhwb3J0cy5jYW5vbmljYWxpemUgPSBmdW5jdGlvbihwb2x5KSB7XG4gIHBvbHkgPSB1dGlsLmNsb25lKHBvbHkpO1xuICBjYW5vbmljYWxpemUuY2Fub25pY2FscG9zaXRpb25zKHBvbHkpO1xuICByZXR1cm4gcG9seTtcbn1cblxuZnVuY3Rpb24ga2lzKHBvbHksIG4pIHsgICAgIC8vIG9ubHkga2lzIG4tc2lkZWQgZmFjZXMsIGJ1dCBuPT0wIG1lYW5zIGtpc3MgYWxsLlxuICB2YXIgcmVzdWx0ID0gbmV3IEFzc2VtYmxlcigpO1xuICBmb3IgKHZhciBpPTA7IGk8cG9seS5wb3NpdGlvbnMubGVuZ3RoOyBpKyspIHtcbiAgICByZXN1bHQubmV3VihcInZcIitpLCBwb2x5LnBvc2l0aW9uc1tpXSk7ICAgICAgICAgICAgICAvLyBlYWNoIG9sZCB2ZXJ0ZXggaXMgYSBuZXcgdmVydGV4XG4gIH1cbiAgdmFyIGNlbnRlcnMgPSBjYW5vbmljYWxpemUuZmFjZUNlbnRlcnMocG9seSk7ICAgICAgICAgICAvLyBuZXcgdmVydGljZXMgaW4gY2VudGVycyBvZiBuLXNpZGVkIGZhY2VcbiAgdmFyIGZvdW5kQW55ID0gZmFsc2U7ICAgICAgICAgICAgICAgICAgICAgIC8vIGFsZXJ0IGlmIGRvbid0IGZpbmQgYW55XG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIHYxID0gXCJ2XCIgKyBwb2x5LmZhY2VzW2ldW3BvbHkuZmFjZXNbaV0ubGVuZ3RoLTFdOyAgLy8gcHJldmlvdXMgdmVydGV4XG4gICAgZm9yICh2YXIgaj0wOyBqPHBvbHkuZmFjZXNbaV0ubGVuZ3RoOyBqKyspICB7XG4gICAgICB2YXIgdjIgPSBcInZcIiArIHBvbHkuZmFjZXNbaV1bal07ICAgICAgICAgICAgICAgICAgLy8gdGhpcyB2ZXJ0ZXhcbiAgICAgIGlmIChwb2x5LmZhY2VzW2ldLmxlbmd0aCA9PSBuIHx8IG49PTApIHsgICAgLy8ga2lzcyB0aGUgbidzLCBvciBhbGxcbiAgICAgICAgZm91bmRBbnkgPSB0cnVlOyAgICAgICAgICAgICAgICAvLyBmbGFnIHRoYXQgd2UgZm91bmQgc29tZVxuICAgICAgICByZXN1bHQubmV3VihcImZcIitpLCBjZW50ZXJzW2ldKTsgICAgICAgIC8vIG5ldyB2ZXJ0ZXggaW4gZmFjZSBjZW50ZXJcbiAgICAgICAgdmFyIGZuYW1lID0gaSArIHYxO1xuICAgICAgICByZXN1bHQubmV3RmxhZyhmbmFtZSwgdjEsIHYyKTsgICAgICAgICAvLyB0aHJlZSBuZXcgZmxhZ3MsIGlmIG4tc2lkZWRcbiAgICAgICAgcmVzdWx0Lm5ld0ZsYWcoZm5hbWUsIHYyLCBcImZcIitpKTtcbiAgICAgICAgcmVzdWx0Lm5ld0ZsYWcoZm5hbWUsIFwiZlwiK2ksIHYxKTtcbiAgICAgIH1cbiAgICAgIGVsc2VcbiAgICAgICAgcmVzdWx0Lm5ld0ZsYWcoaSwgdjEsIHYyKTsgICAgICAgICAgICAgLy8gc2FtZSBvbGQgZmxhZywgaWYgbm9uLW5cbiAgICAgIHYxID0gdjI7ICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY3VycmVudCBiZWNvbWVzIHByZXZpb3VzXG4gICAgfVxuICB9XG4gIHZhciBhbnMgPSByZXN1bHQuZmxhZ3MycG9seSgpO1xuICBhbnMubmFtZSA9IFwia1wiICsgKG49PT0wP1wiXCI6bikgKyBwb2x5Lm5hbWU7XG4gIGNhbm9uaWNhbGl6ZS5hZGp1c3Rwb3NpdGlvbnMoYW5zLCAzKTsgICAgICAgICAgICAgICAvLyBhZGp1c3QgYW5kXG4gIHJldHVybiAoYW5zKTtcbn1cbmV4cG9ydHMua2lzID0ga2lzO1xuXG5mdW5jdGlvbiBhbWJvKHBvbHkpIHsgICAgICAgICAgICAgICAgICAgICAgLy8gY29tcHV0ZSBhbWJvIG9mIGFyZ3VtZW50XG4gIHZhciByZXN1bHQgPSBuZXcgQXNzZW1ibGVyKCk7XG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIHYxID0gcG9seS5mYWNlc1tpXVtwb2x5LmZhY2VzW2ldLmxlbmd0aC0yXTsgIC8vIHByZXByZXZpb3VzIHZlcnRleFxuICAgIHZhciB2MiA9IHBvbHkuZmFjZXNbaV1bcG9seS5mYWNlc1tpXS5sZW5ndGgtMV07ICAvLyBwcmV2aW91cyB2ZXJ0ZXhcbiAgICBmb3IgKHZhciBqPTA7IGo8cG9seS5mYWNlc1tpXS5sZW5ndGg7IGorKykgIHtcbiAgICAgIHZhciB2MyA9IHBvbHkuZmFjZXNbaV1bal07ICAgICAgICAvLyB0aGlzIHZlcnRleFxuICAgICAgaWYgKHYxIDwgdjIpICAgICAgICAgICAgICAgICAgICAgLy8gbmV3IHZlcnRpY2VzIGF0IGVkZ2UgbWlkcG9pbnRzXG4gICAgICAgIHJlc3VsdC5uZXdWKG1pZE5hbWUodjEsdjIpLCB1dGlsLm1pZHBvaW50KHBvbHkucG9zaXRpb25zW3YxXSxwb2x5LnBvc2l0aW9uc1t2Ml0pKTtcbiAgICAgIHJlc3VsdC5uZXdGbGFnKFwiZlwiK2ksIG1pZE5hbWUodjEsdjIpLCBtaWROYW1lKHYyLHYzKSk7ICAgICAvLyB0d28gbmV3IGZsYWdzXG4gICAgICByZXN1bHQubmV3RmxhZyhcInZcIit2MiwgbWlkTmFtZSh2Mix2MyksIG1pZE5hbWUodjEsdjIpKTtcbiAgICAgIHYxID0gdjI7ICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNoaWZ0IG92ZXIgb25lXG4gICAgICB2MiA9IHYzO1xuICAgIH1cbiAgfVxuICB2YXIgYW5zID0gcmVzdWx0LmZsYWdzMnBvbHkoKTtcbiAgYW5zLm5hbWUgPSBcImFcIiArIHBvbHkubmFtZTtcbiAgY2Fub25pY2FsaXplLmFkanVzdHBvc2l0aW9ucyhhbnMsIDIpOyAgICAgICAgICAgICAvLyBjYW5vbmljYWxpemUgbGlnaHRseVxuICByZXR1cm4gKGFucyk7XG59XG5leHBvcnRzLmFtYm8gPSBhbWJvO1xuXG5mdW5jdGlvbiBtaWROYW1lKHYxLHYyKSB7ICAgICAgICAgICAgICAvLyB1bmlxdWUgc3ltYm9saWMgbmFtZSwgZS5nLiBcIjFfMlwiXG4gIGlmICh2MTx2MilcbiAgICByZXR1cm4gKHYxICsgXCJfXCIgKyB2Mik7XG4gIGVsc2VcbiAgICByZXR1cm4gKHYyICsgXCJfXCIgKyB2MSk7XG59XG5cbmZ1bmN0aW9uIGd5cm8ocG9seSkgeyAgICAgICAgICAgICAgICAgICAgICAvLyBjb21wdXRlIGd5cm8gb2YgYXJndW1lbnRcbiAgdmFyIHJlc3VsdCA9IG5ldyBBc3NlbWJsZXIoKTtcbiAgZm9yICh2YXIgaT0wOyBpPHBvbHkucG9zaXRpb25zLmxlbmd0aDsgaSsrKVxuICAgIHJlc3VsdC5uZXdWKFwidlwiK2ksIHV0aWwudW5pdChwb2x5LnBvc2l0aW9uc1tpXSkpOyAgICAgICAgICAgLy8gZWFjaCBvbGQgdmVydGV4IGlzIGEgbmV3IHZlcnRleFxuICB2YXIgY2VudGVycyA9IGNhbm9uaWNhbGl6ZS5mYWNlQ2VudGVycyhwb2x5KTsgICAgICAgICAgICAgIC8vIG5ldyB2ZXJ0aWNlcyBpbiBjZW50ZXIgb2YgZWFjaCBmYWNlXG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKVxuICAgIHJlc3VsdC5uZXdWKFwiZlwiK2ksIHV0aWwudW5pdChjZW50ZXJzW2ldKSk7XG4gIGZvciAodmFyIGk9MDsgaTxwb2x5LmZhY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIHYxID0gcG9seS5mYWNlc1tpXVtwb2x5LmZhY2VzW2ldLmxlbmd0aC0yXTsgIC8vIHByZXByZXZpb3VzIHZlcnRleFxuICAgIHZhciB2MiA9IHBvbHkuZmFjZXNbaV1bcG9seS5mYWNlc1tpXS5sZW5ndGgtMV07ICAvLyBwcmV2aW91cyB2ZXJ0ZXhcbiAgICBmb3IgKHZhciBqPTA7IGo8cG9seS5mYWNlc1tpXS5sZW5ndGg7IGorKykgIHtcbiAgICAgIHZhciB2MyA9IHBvbHkuZmFjZXNbaV1bal07ICAgICAgICAgICAgICAgICAgLy8gdGhpcyB2ZXJ0ZXhcbiAgICAgIHJlc3VsdC5uZXdWKHYxK1wiflwiK3YyLCB1dGlsLm9uZVRoaXJkKHBvbHkucG9zaXRpb25zW3YxXSxwb2x5LnBvc2l0aW9uc1t2Ml0pKTsgIC8vIG5ldyB2IGluIGZhY2VcbiAgICAgIHZhciBmbmFtZSA9IGkgKyBcImZcIiArIHYxO1xuICAgICAgcmVzdWx0Lm5ld0ZsYWcoZm5hbWUsIFwiZlwiK2ksIHYxK1wiflwiK3YyKTsgICAgICAgICAgLy8gZml2ZSBuZXcgZmxhZ3NcbiAgICAgIHJlc3VsdC5uZXdGbGFnKGZuYW1lLCB2MStcIn5cIit2MiwgdjIrXCJ+XCIrdjEpO1xuICAgICAgcmVzdWx0Lm5ld0ZsYWcoZm5hbWUsIHYyK1wiflwiK3YxLCBcInZcIit2Mik7XG4gICAgICByZXN1bHQubmV3RmxhZyhmbmFtZSwgXCJ2XCIrdjIsIHYyK1wiflwiK3YzKTtcbiAgICAgIHJlc3VsdC5uZXdGbGFnKGZuYW1lLCB2MitcIn5cIit2MywgXCJmXCIraSk7XG4gICAgICB2MSA9IHYyOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gc2hpZnQgb3ZlciBvbmVcbiAgICAgIHYyID0gdjM7XG4gICAgfVxuICB9XG4gIHZhciBhbnMgPSByZXN1bHQuZmxhZ3MycG9seSgpO1xuICBhbnMubmFtZSA9IFwiZ1wiICsgcG9seS5uYW1lO1xuICBjYW5vbmljYWxpemUuYWRqdXN0cG9zaXRpb25zKGFucywgMyk7ICAgICAgICAgICAgICAgICAgICAgICAvLyBjYW5vbmljYWxpemUgbGlnaHRseVxuICByZXR1cm4gKGFucyk7XG59XG5leHBvcnRzLmd5cm8gPSBneXJvO1xuXG5mdW5jdGlvbiBwcm9wZWxsb3IocG9seSkgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY29tcHV0ZSBwcm9wZWxsb3Igb2YgYXJndW1lbnRcbiAgdmFyIHJlc3VsdCA9IG5ldyBBc3NlbWJsZXIoKTtcbiAgZm9yICh2YXIgaT0wOyBpPHBvbHkucG9zaXRpb25zLmxlbmd0aDsgaSsrKVxuICAgIHJlc3VsdC5uZXdWKFwidlwiK2ksIHV0aWwudW5pdChwb2x5LnBvc2l0aW9uc1tpXSkpOyAgICAgICAgICAgLy8gZWFjaCBvbGQgdmVydGV4IGlzIGEgbmV3IHZlcnRleFxuICBmb3IgKHZhciBpPTA7IGk8cG9seS5mYWNlcy5sZW5ndGg7IGkrKykge1xuICAgIHZhciB2MSA9IHBvbHkuZmFjZXNbaV1bcG9seS5mYWNlc1tpXS5sZW5ndGgtMl07ICAvLyBwcmVwcmV2aW91cyB2ZXJ0ZXhcbiAgICB2YXIgdjIgPSBwb2x5LmZhY2VzW2ldW3BvbHkuZmFjZXNbaV0ubGVuZ3RoLTFdOyAgLy8gcHJldmlvdXMgdmVydGV4XG4gICAgZm9yICh2YXIgaj0wOyBqPHBvbHkuZmFjZXNbaV0ubGVuZ3RoOyBqKyspICB7XG4gICAgICB2YXIgdjMgPSBwb2x5LmZhY2VzW2ldW2pdOyAgICAgICAgICAgICAgICAgIC8vIHRoaXMgdmVydGV4XG4gICAgICByZXN1bHQubmV3Vih2MStcIn5cIit2MiwgdXRpbC5vbmVUaGlyZChwb2x5LnBvc2l0aW9uc1t2MV0scG9seS5wb3NpdGlvbnNbdjJdKSk7ICAvLyBuZXcgdiBpbiBmYWNlXG4gICAgICB2YXIgZm5hbWUgPSBpICsgXCJmXCIgKyB2MjtcbiAgICAgIHJlc3VsdC5uZXdGbGFnKFwidlwiK2ksIHYxK1wiflwiK3YyLCB2MitcIn5cIit2Myk7ICAgICAgLy8gZml2ZSBuZXcgZmxhZ3NcbiAgICAgIHJlc3VsdC5uZXdGbGFnKGZuYW1lLCB2MStcIn5cIit2MiwgdjIrXCJ+XCIrdjEpO1xuICAgICAgcmVzdWx0Lm5ld0ZsYWcoZm5hbWUsIHYyK1wiflwiK3YxLCBcInZcIit2Mik7XG4gICAgICByZXN1bHQubmV3RmxhZyhmbmFtZSwgXCJ2XCIrdjIsIHYyK1wiflwiK3YzKTtcbiAgICAgIHJlc3VsdC5uZXdGbGFnKGZuYW1lLCB2MitcIn5cIit2MywgdjErXCJ+XCIrdjIpO1xuICAgICAgdjEgPSB2MjsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNoaWZ0IG92ZXIgb25lXG4gICAgICB2MiA9IHYzO1xuICAgIH1cbiAgfVxuICB2YXIgYW5zID0gcmVzdWx0LmZsYWdzMnBvbHkoKTtcbiAgYW5zLm5hbWUgPSBcInBcIiArIHBvbHkubmFtZTtcbiAgY2Fub25pY2FsaXplLmFkanVzdHBvc2l0aW9ucyhhbnMsIDMpOyAgICAgICAgICAgICAgICAgICAgICAgLy8gY2Fub25pY2FsaXplIGxpZ2h0bHlcbiAgcmV0dXJuIChhbnMpO1xufVxuZXhwb3J0cy5wcm9wZWxsb3IgPSBwcm9wZWxsb3I7XG5cbmZ1bmN0aW9uIHJlZmxlY3QocG9seSkgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNvbXB1dGUgcmVmbGVjdGlvbiB0aHJvdWdoIG9yaWdpblxuICBwb2x5ID0gdXRpbC5jbG9uZShwb2x5KTtcbiAgZm9yICh2YXIgaT0wOyBpPHBvbHkucG9zaXRpb25zLmxlbmd0aDsgaSsrKVxuICAgIHBvbHkucG9zaXRpb25zW2ldID0gdXRpbC5tdWx0KC0xLCBwb2x5LnBvc2l0aW9uc1tpXSk7ICAgICAgICAgICAvLyByZWZsZWN0IGVhY2ggcG9pbnRcbiAgZm9yICh2YXIgaT0wOyBpPHBvbHkuZmFjZXMubGVuZ3RoOyBpKyspXG4gICAgcG9seS5mYWNlc1tpXSA9IHBvbHkuZmFjZXNbaV0ucmV2ZXJzZSgpOyAgICAgICAgIC8vIHJlcGFpciBjbG9ja3dpc2UtbmVzc1xuICBwb2x5Lm5hbWUgPSBcInJcIiArIHBvbHkubmFtZTtcbiAgY2Fub25pY2FsaXplLmFkanVzdHBvc2l0aW9ucyhwb2x5LCAxKTsgICAgICAgICAgICAgICAgICAgICAvLyBidWlsZCBkdWFsXG4gIHJldHVybiAocG9seSk7XG59XG5leHBvcnRzLnJlZmxlY3QgPSByZWZsZWN0O1xuXG5cbmV4cG9ydHMuZXhwYW5kID0gZnVuY3Rpb24ocG9seSkge1xuICByZXR1cm4gYW1ibyhhbWJvKHBvbHkpKTtcbn1cblxuZXhwb3J0cy5iZXZlbCA9IGZ1bmN0aW9uKHBvbHkpIHtcbiAgcmV0dXJuIHRydW5jYXRlKGFtYm8ocG9seSkpO1xufVxuXG5leHBvcnRzLm9ydGhvID0gZnVuY3Rpb24ocG9seSkge1xuICByZXR1cm4gam9pbihqb2luKHBvbHkpKTtcbn1cblxuZXhwb3J0cy5tZXRhID0gZnVuY3Rpb24ocG9seSkge1xuICByZXR1cm4ga2lzKGpvaW4ocG9seSkpO1xufVxuXG5leHBvcnRzLnRydW5jYXRlID0gZnVuY3Rpb24ocG9seSwgbikge1xuICByZXR1cm4gZHVhbChraXMoZHVhbChwb2x5KSwgbikpO1xufVxuXG5leHBvcnRzLmpvaW4gPSBmdW5jdGlvbihwb2x5KSB7XG4gIHJldHVybiBkdWFsKGFtYm8oZHVhbChwb2x5KSkpO1xufVxuXG5leHBvcnRzLnNwbGl0ID0gZnVuY3Rpb24ocG9seSkge1xuICByZXR1cm4gZHVhbChneXJvKGR1YWwocG9seSkpKTtcbn1cbiIsIlwidXNlIHN0cmljdFwiO1xuXG52YXIgdXRpbCAgICAgICAgICA9IHJlcXVpcmUoXCIuL3V0aWwuanNcIik7XG52YXIgY2Fub25pY2FsaXplICA9IHJlcXVpcmUoXCIuL2Nhbm9uaWNhbGl6ZS5qc1wiKTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHRldHJhaGVkcm9uOiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbmFtZTogICAgICAgXCJUXCIsXG4gICAgICBmYWNlczogICAgICBbIFswLDEsMl0sIFswLDIsM10sIFswLDMsMV0sIFsxLDMsMl0gXSxcbiAgICAgIHBvc2l0aW9uczogIFsgWzEuLDEuLDEuXSwgWzEuLC0xLiwtMS5dLCBbLTEuLDEuLC0xLl0sIFstMS4sLTEuLDEuXSBdXG4gICAgfTtcbiAgfSxcbiAgb2N0YWhlZHJvbjogZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG5hbWU6ICAgICAgIFwiT1wiLFxuICAgICAgZmFjZXM6ICAgICAgWyBbMCwxLDJdLCBbMCwyLDNdLCBbMCwzLDRdLCBbMCw0LDFdLCBbMSw0LDVdLCBbMSw1LDJdLCBbMiw1LDNdLCBbMyw1LDRdIF0sXG4gICAgICBwb3NpdGlvbnM6ICBbIFswLDAsMS40MTRdLCBbMS40MTQsMCwwXSwgWzAsMS40MTQsMF0sIFstMS40MTQsMCwwXSwgWzAsLTEuNDE0LDBdLCBbMCwwLC0xLjQxNF0gXVxuICAgIH07XG4gIH0sXG4gIGN1YmU6IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB7XG4gICAgICBuYW1lOiAgICAgICBcIkNcIixcbiAgICAgIGZhY2VzOiAgICAgIFsgWzMsMCwxLDJdLCBbMyw0LDUsMF0sIFswLDUsNiwxXSwgWzEsNiw3LDJdLCBbMiw3LDQsM10sIFs1LDQsNyw2XSBdLFxuICAgICAgcG9zaXRpb25zOiAgWyBbIDAuNzA3LCAwLjcwNywwLjcwN10sIFstMC43MDcsMC43MDcsMC43MDddLFxuICAgICAgICAgICAgICAgICAgICBbLTAuNzA3LC0wLjcwNywwLjcwN10sIFswLjcwNywtMC43MDcsMC43MDddLFxuICAgICAgICAgICAgICAgICAgICBbMC43MDcsLTAuNzA3LC0wLjcwN10sIFswLjcwNywwLjcwNywtMC43MDddLFxuICAgICAgICAgICAgICAgICAgICBbLTAuNzA3LDAuNzA3LC0wLjcwN10sIFstMC43MDcsLTAuNzA3LC0wLjcwN10gXVxuICAgIH07XG4gIH0sXG4gIGljb3NhaGVkcm9uOiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbmFtZTogICAgICAgXCJJXCIsXG4gICAgICBmYWNlczogICAgICBbIFswLDEsMl0sICBbMCwyLDNdLCAgIFswLDMsNF0sICBbMCw0LDVdLFxuICAgICAgICAgICAgICAgICAgICBbMCw1LDFdLCAgWzEsNSw3XSwgICBbMSw3LDZdLCAgWzEsNiwyXSxcbiAgICAgICAgICAgICAgICAgICAgWzIsNiw4XSwgIFsyLDgsM10sICAgWzMsOCw5XSwgIFszLDksNF0sXG4gICAgICAgICAgICAgICAgICAgIFs0LDksMTBdLCBbNCwxMCw1XSwgIFs1LDEwLDddLCBbNiw3LDExXSxcbiAgICAgICAgICAgICAgICAgICAgWzYsMTEsOF0sIFs3LDEwLDExXSwgWzgsMTEsOV0sIFs5LDExLDEwXSBdLFxuICAgICAgcG9zaXRpb25zOiAgWyBbMCwwLDEuMTc2XSwgICAgICAgICAgICBbMS4wNTEsMCwwLjUyNl0sXG4gICAgICAgICAgICAgICAgICAgIFswLjMyNCwxLiwwLjUyNV0sICAgICAgIFstMC44NTEsMC42MTgsMC41MjZdLFxuICAgICAgICAgICAgICAgICAgICBbLTAuODUxLC0wLjYxOCwwLjUyNl0sICBbMC4zMjUsLTEuLDAuNTI2XSxcbiAgICAgICAgICAgICAgICAgICAgWzAuODUxLDAuNjE4LC0wLjUyNl0sICAgWzAuODUxLC0wLjYxOCwtMC41MjZdLFxuICAgICAgICAgICAgICAgICAgICBbLTAuMzI1LDEuLC0wLjUyNl0sICAgICBbLTEuMDUxLDAsLTAuNTI2XSxcbiAgICAgICAgICAgICAgICAgICAgWy0wLjMyNSwtMS4sLTAuNTI2XSwgICAgWzAsMCwtMS4xNzZdIF1cblxuICAgIH07XG4gIH0sXG4gIGRvZGVjYWhlZHJvbjogZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG5hbWU6ICAgICAgIFwiRFwiLFxuICAgICAgZmFjZXM6ICAgICAgWyBbMCwxLDQsNywyXSwgICAgICBbMCwyLDYsOSwzXSwgICAgICBbMCwzLDgsNSwxXSxcbiAgICAgICAgICAgICAgICAgICAgWzEsNSwxMSwxMCw0XSwgICAgWzIsNywxMywxMiw2XSwgICAgWzMsOSwxNSwxNCw4XSxcbiAgICAgICAgICAgICAgICAgICAgWzQsMTAsMTYsMTMsN10sICAgWzUsOCwxNCwxNywxMV0sICAgWzYsMTIsMTgsMTUsOV0sXG4gICAgICAgICAgICAgICAgICAgIFsxMCwxMSwxNywxOSwxNl0sIFsxMiwxMywxNiwxOSwxOF0sIFsxNCwxNSwxOCwxOSwxN10gXSxcbiAgICAgIHBvc2l0aW9uczogIFsgWzAsMCwxLjA3MDQ3XSwgWzAuNzEzNjQ0LDAsMC43OTc4NzhdLFxuICAgICAgICAgICAgICAgICAgICBbLTAuMzU2ODIyLDAuNjE4LDAuNzk3ODc4XSwgWy0wLjM1NjgyMiwtMC42MTgsMC43OTc4NzhdLFxuICAgICAgICAgICAgICAgICAgICBbMC43OTc4NzgsMC42MTgwMzQsMC4zNTY4MjJdLCBbMC43OTc4NzgsLTAuNjE4LDAuMzU2ODIyXSxcbiAgICAgICAgICAgICAgICAgICAgWy0wLjkzNDE3MiwwLjM4MTk2NiwwLjM1NjgyMl0sIFswLjEzNjI5NCwxLiwwLjM1NjgyMl0sXG4gICAgICAgICAgICAgICAgICAgIFswLjEzNjI5NCwtMS4sMC4zNTY4MjJdLCBbLTAuOTM0MTcyLC0wLjM4MTk2NiwwLjM1NjgyMl0sXG4gICAgICAgICAgICAgICAgICAgIFswLjkzNDE3MiwwLjM4MTk2NiwtMC4zNTY4MjJdLCBbMC45MzQxNzIsLTAuMzgxOTY2LC0wLjM1NjgyMl0sXG4gICAgICAgICAgICAgICAgICAgIFstMC43OTc4NzgsMC42MTgsLTAuMzU2ODIyXSwgWy0wLjEzNjI5NCwxLiwtMC4zNTY4MjJdLFxuICAgICAgICAgICAgICAgICAgICBbLTAuMTM2Mjk0LC0xLiwtMC4zNTY4MjJdLCBbLTAuNzk3ODc4LC0wLjYxODAzNCwtMC4zNTY4MjJdLFxuICAgICAgICAgICAgICAgICAgICBbMC4zNTY4MjIsMC42MTgsLTAuNzk3ODc4XSwgWzAuMzU2ODIyLC0wLjYxOCwtMC43OTc4NzhdLFxuICAgICAgICAgICAgICAgICAgICBbLTAuNzEzNjQ0LDAsLTAuNzk3ODc4XSwgWzAsMCwtMS4wNzA0N10gXVxuICAgIH07XG4gIH0sXG4gIHByaXNtOiBmdW5jdGlvbihuKSB7XG4gICAgdmFyIHRoZXRhICAgICA9IE1hdGguUEkgKiAyLjAgLyBuO1xuICAgIHZhciBoICAgICAgICAgPSBNYXRoLnNpbih0aGV0YS8yKTtcbiAgICB2YXIgcG9zaXRpb25zID0gW107XG4gICAgZm9yICh2YXIgaT0wOyBpPG47IGkrKykge1xuICAgICAgcG9zaXRpb25zLnB1c2goW01hdGguY29zKGkqdGhldGEpLCBNYXRoLnNpbihpKnRoZXRhKSwgaF0pO1xuICAgIH1cbiAgICBmb3IgKHZhciBpPTA7IGk8bjsgaSsrKSB7XG4gICAgICBwb3NpdGlvbnMucHVzaChbTWF0aC5jb3MoaSp0aGV0YSksIE1hdGguc2luKGkqdGhldGEpLC1oXSk7XG4gICAgfVxuICAgIHZhciBmYWNlcyAgICAgPSBbIHV0aWwuc2VxdWVuY2Uobi0xLCAwKSwgdXRpbC5zZXF1ZW5jZShuLCAyKm4tMSkgXTtcbiAgICBmb3IodmFyIGk9MDsgaTxuOyArK2kpIHtcbiAgICAgIGZhY2VzLnB1c2goW2ksIChpKzEpJW4sIChpKzEpJW4rbiwgaStuXSk7XG4gICAgfVxuICAgIHZhciByZXN1bHQgPSB7XG4gICAgICBuYW1lOiAgICAgICBcIlBcIiArIG4sXG4gICAgICBwb3NpdGlvbnM6ICBwb3NpdGlvbnMsXG4gICAgICBmYWNlczogICAgICBmYWNlc1xuICAgIH07XG4gICAgY2Fub25pY2FsaXplLmFkanVzdHBvc2l0aW9ucyhyZXN1bHQsIDEpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH0sXG4gIGFudGlwcmlzbTogZnVuY3Rpb24obikge1xuICAgIHZhciB0aGV0YSA9IE1hdGguUEkgKiAyLjAgLyBuO1xuICAgIHZhciBoID0gTWF0aC5zcXJ0KDEtNC8oNCsyKk1hdGguY29zKHRoZXRhLzIpLTIqTWF0aC5jb3ModGhldGEpKSk7XG4gICAgdmFyIHIgPSBNYXRoLnNxcnQoMS1oKmgpO1xuICAgIHZhciBmID0gTWF0aC5zcXJ0KGgqaCArIE1hdGgucG93KHIqTWF0aC5jb3ModGhldGEvMiksIDIpKTsgXG4gICAgcj1yL2Y7XG4gICAgaD1oL2Y7XG4gICAgdmFyIHBvc2l0aW9ucyA9IFtdO1xuICAgIGZvciAodmFyIGk9MDsgaTxuOyBpKyspIHtcbiAgICAgIHBvc2l0aW9ucy5wdXNoKFtyKk1hdGguY29zKGkqdGhldGEpLCByKk1hdGguc2luKGkqdGhldGEpLCBoXSk7XG4gICAgfVxuICAgIGZvciAodmFyIGk9MDsgaTxuOyBpKyspIHtcbiAgICAgIHBvc2l0aW9ucy5wdXNoKFtyKk1hdGguY29zKChpKzAuNSkqdGhldGEpLCByKk1hdGguc2luKChpKzAuNSkqdGhldGEpLCAtaF0pO1xuICAgIH1cbiAgICB2YXIgZmFjZXMgPSBbIHV0aWwuc2VxdWVuY2Uobi0xLCAwKSwgdXRpbC5zZXF1ZW5jZShuLCAyKm4tMSkgXVxuICAgIGZvciAodmFyIGk9MDsgaTxuOyBpKyspIHtcbiAgICAgIGZhY2VzLnB1c2goW2ksIChpKzEpJW4sIGkrbl0pO1xuICAgICAgZmFjZXMucHVzaChbaSwgaStuLCAgICAgKChuK2ktMSklbituKV0pO1xuICAgIH1cbiAgICB2YXIgcmVzdWx0ID0ge1xuICAgICAgbmFtZTogICAgICAgICBcIkFcIiArIG4sXG4gICAgICBwb3NpdGlvbnM6ICAgIHBvc2l0aW9ucyxcbiAgICAgIGZhY2VzOiAgICAgICAgZmFjZXNcbiAgICB9O1xuICAgIGNhbm9uaWNhbGl6ZS5hZGp1c3Rwb3NpdGlvbnMocmVzdWx0LCAxKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9LFxuICBweXJhbWlkOiBmdW5jdGlvbihuKSB7XG4gICAgdmFyIHRoZXRhID0gTWF0aC5QSSAqIDIuMCAvIG47XG4gICAgdmFyIHBvc2l0aW9ucyA9IFtdO1xuICAgIGZvcih2YXIgaT0wOyBpPG47ICsraSkge1xuICAgICAgcG9zaXRpb25zLnB1c2goW01hdGguY29zKGkqdGhldGEpLCBNYXRoLnNpbihpKnRoZXRhKSwgLjJdKTtcbiAgICB9XG4gICAgcG9zaXRpb25zLnB1c2goWzAsIDAsIC0yXSk7XG4gICAgdmFyIGZhY2VzID0gWyB1dGlsLnNlcXVlbmNlKG4tMSwgMCkgXTtcbiAgICBmb3IgKHZhciBpPTA7IGk8bjsgKytpKSB7XG4gICAgICBmYWNlcy5wdXNoKFtpLCAoaSsxKSVuLCBuXSk7XG4gICAgfVxuICAgIHZhciByZXN1bHQgPSB7XG4gICAgICBuYW1lOiAgICAgICBcIllcIiArIG4sXG4gICAgICBwb3NpdGlvbnM6ICBwb3NpdGlvbnMsXG4gICAgICBmYWNlczogICAgICBmYWNlc1xuICAgIH07XG4gICAgY2Fub25pY2FsaXplLmNhbm9uaWNhbHBvc2l0aW9ucyhyZXN1bHQsIDMpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbn1cbiIsIlwidXNlIHN0cmljdFwiO1xuXG5mdW5jdGlvbiBjbG9uZShwb2x5KSB7XG4gIHZhciBucG9zaXRpb25zID0gbmV3IEFycmF5KHBvbHkucG9zaXRpb25zLmxlbmd0aCk7XG4gIGZvcih2YXIgaT0wOyBpPHBvbHkucG9zaXRpb25zLmxlbmd0aDsgKytpKSB7XG4gICAgbnBvc2l0aW9uc1tpXSA9IHBvbHkucG9zaXRpb25zW2ldLnNsaWNlKDApO1xuICB9XG4gIHZhciBuZmFjZXMgPSBbXTtcbiAgZm9yKHZhciBpPTA7IGk8cG9seS5mYWNlcy5sZW5ndGg7ICsraSkge1xuICAgIG5mYWNlc1tpXSA9IHBvbHkuZmFjZXNbaV0uc2xpY2UoMCk7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICBuYW1lOiBwb2x5Lm5hbWUuc2xpY2UoMCksXG4gICAgcG9zaXRpb25zOiBucG9zaXRpb25zLFxuICAgIGZhY2VzOiBuZmFjZXNcbiAgfVxufVxuZXhwb3J0cy5jbG9uZSA9IGNsb25lO1xuXG5mdW5jdGlvbiBpbnRlcnNlY3Qoc2V0MSwgc2V0Miwgc2V0MykgeyAgLy8gZmluZCBlbGVtZW50IGNvbW1vbiB0byAzIHNldHNcbiAgZm9yICh2YXIgaT0wOyBpPHNldDEubGVuZ3RoOyBpKyspIHsgICAgLy8gYnkgYnJ1dGUgZm9yY2Ugc2VhcmNoXG4gICAgZm9yICh2YXIgaj0wOyBqPHNldDIubGVuZ3RoOyBqKyspIHtcbiAgICAgIGlmIChzZXQxW2ldPT09c2V0MltqXSkge1xuICAgICAgICBmb3IgKHZhciBrPTA7IGs8c2V0My5sZW5ndGg7IGsrKykge1xuICAgICAgICAgIGlmIChzZXQxW2ldPT09c2V0M1trXSkge1xuICAgICAgICAgICAgcmV0dXJuIHNldDFbaV07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHRocm93IG5ldyBFcnJvcihcInByb2dyYW0gYnVnIGluIGludGVyc2VjdCgpXCIpO1xuICByZXR1cm4gbnVsbDtcbn1cbmV4cG9ydHMuaW50ZXJzZWN0ID0gaW50ZXJzZWN0O1xuXG5mdW5jdGlvbiBzZXF1ZW5jZShzdGFydCwgc3RvcCkgeyAgICAvLyBtYWtlIGxpc3Qgb2YgaW50ZWdlcnMsIGluY2x1c2l2ZVxuICB2YXIgYW5zID0gbmV3IEFycmF5KCk7XG4gIGlmIChzdGFydCA8PSBzdG9wKSB7XG4gICAgZm9yICh2YXIgaT1zdGFydDsgaTw9c3RvcDsgaSsrKSB7XG4gICAgICBhbnMucHVzaChpKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgZm9yICh2YXIgaT1zdGFydDsgaT49c3RvcDsgaS0tKSB7XG4gICAgICBhbnMucHVzaChpKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGFucztcbn1cbmV4cG9ydHMuc2VxdWVuY2UgPSBzZXF1ZW5jZTtcblxuZnVuY3Rpb24gb3J0aG9nb25hbCh2MywgdjIsIHYxKSB7ICAgLy8gZmluZCB1bml0IHZlY3RvciBvcnRob2cgdG8gcGxhbmUgb2YgMyBwdHNcbiAgdmFyIGQxID0gc3ViKHYyLCB2MSk7ICAgICAgICAgICAgLy8gYWRqYWNlbnQgZWRnZSB2ZWN0b3JzXG4gIHZhciBkMiA9IHN1Yih2MywgdjIpO1xuICByZXR1cm4gWyAgZDFbMV0qZDJbMl0gLSBkMVsyXSpkMlsxXSwgICAgLy8gY3Jvc3MgcHJvZHVjdFxuICAgICAgICAgICAgZDFbMl0qZDJbMF0gLSBkMVswXSpkMlsyXSxcbiAgICAgICAgICAgIGQxWzBdKmQyWzFdIC0gZDFbMV0qZDJbMF0gXVxufVxuZXhwb3J0cy5vcnRob2dvbmFsID0gb3J0aG9nb25hbDtcblxuZnVuY3Rpb24gbWFnMih2ZWMpIHsgICAgICAgICAgLy8gbWFnbml0dWRlIHNxdWFyZWQgb2YgMy12ZWN0b3JcbiAgcmV0dXJuIHZlY1swXSp2ZWNbMF0gKyB2ZWNbMV0qdmVjWzFdICsgdmVjWzJdKnZlY1syXTtcbn1cbmV4cG9ydHMubWFnMiA9IG1hZzI7XG5cbmZ1bmN0aW9uIHRhbmdlbnRQb2ludCh2MSwgdjIpIHsgICAgLy8gcG9pbnQgd2hlcmUgbGluZSB2MS4uLnYyIHRhbmdlbnQgdG8gYW4gb3JpZ2luIHNwaGVyZVxuICB2YXIgZCA9IHN1Yih2Mix2MSk7ICAgICAgICAgICAgIC8vIGRpZmZlcmVuY2UgdmVjdG9yXG4gIGNvbnNvbGUubG9nKGQpO1xuICByZXR1cm4oc3ViKHYxLCBtdWx0KGRvdChkLHYxKS9tYWcyKGQpLCBkKSkpO1xufVxuZXhwb3J0cy50YW5nZW50UG9pbnQgPSB0YW5nZW50UG9pbnQ7XG5cbmZ1bmN0aW9uIGVkZ2VEaXN0KHYxLCB2MikgeyAgICAgICAgIC8vIGRpc3RhbmNlIG9mIGxpbmUgdjEuLi52MiB0byBvcmlnaW5cbiAgcmV0dXJuKE1hdGguc3FydChtYWcyKHRhbmdlbnRQb2ludCh2MSwgdjIpKSkpO1xufVxuZXhwb3J0cy5lZGdlRGlzdCA9IGVkZ2VEaXN0O1xuXG5mdW5jdGlvbiB2ZWNaZXJvKCkge1xuICByZXR1cm4gWzAuMCwwLjAsMC4wXTtcbn1cbmV4cG9ydHMudmVjWmVybyA9IHZlY1plcm87XG5cbmZ1bmN0aW9uIG11bHQoYywgdmVjKSB7ICAgICAgIC8vIGMgdGltZXMgMy12ZWN0b3JcbiAgcmV0dXJuIFsgYyp2ZWNbMF0sXG4gICAgICAgICAgIGMqdmVjWzFdLFxuICAgICAgICAgICBjKnZlY1syXSBdO1xufVxuZXhwb3J0cy5tdWx0ID0gbXVsdDtcblxuZnVuY3Rpb24gYWRkKHZlYzEsIHZlYzIpIHsgICAgLy8gc3VtIHR3byAzLXZlY3RvcnNcbiAgcmV0dXJuIFsgIHZlYzFbMF0rdmVjMlswXSxcbiAgICAgICAgICAgIHZlYzFbMV0rdmVjMlsxXSxcbiAgICAgICAgICAgIHZlYzFbMl0rdmVjMlsyXSBdO1xufVxuZXhwb3J0cy5hZGQgPSBhZGQ7XG5cbmZ1bmN0aW9uIHN1Yih2ZWMxLCB2ZWMyKSB7ICAgIC8vIHN1YnRyYWN0IHR3byAzLXZlY3RvcnNcbiAgcmV0dXJuIFsgIHZlYzFbMF0tdmVjMlswXSxcbiAgICAgICAgICAgIHZlYzFbMV0tdmVjMlsxXSxcbiAgICAgICAgICAgIHZlYzFbMl0tdmVjMlsyXSBdXG59XG5leHBvcnRzLnN1YiA9IHN1YjtcblxuZnVuY3Rpb24gZG90KHZlYzEsIHZlYzIpIHsgICAgLy8gZG90IHByb2R1Y3QgdHdvIDMtdmVjdG9yc1xuICByZXR1cm4gKHZlYzFbMF0qdmVjMlswXSArIHZlYzFbMV0qdmVjMlsxXSArIHZlYzFbMl0qdmVjMlsyXSk7XG59XG5leHBvcnRzLmRvdCA9IGRvdDtcblxuZnVuY3Rpb24gbWlkcG9pbnQodmVjMSwgdmVjMikgeyAgICAvLyBtZWFuIG9mIHR3byAzLXZlY3RvcnNcbiAgcmV0dXJuIFsgIDAuNSoodmVjMVswXSArIHZlYzJbMF0pLFxuICAgICAgICAgICAgMC41Kih2ZWMxWzFdICsgdmVjMlsxXSksXG4gICAgICAgICAgICAwLjUqKHZlYzFbMl0gKyB2ZWMyWzJdKSBdXG59XG5leHBvcnRzLm1pZHBvaW50ID0gbWlkcG9pbnQ7XG5cbmZ1bmN0aW9uIG9uZVRoaXJkKHZlYzEsIHZlYzIpIHsgICAgLy8gYXBwcm94LiAoMi8zKXYxICsgKDEvMyl2MiAgIChhc3N1bWVzIDMtdmVjdG9yKVxuICByZXR1cm4gWyAgMC43KnZlYzFbMF0gKyAwLjMqdmVjMlswXSxcbiAgICAgICAgICAgIDAuNyp2ZWMxWzFdICsgMC4zKnZlYzJbMV0sXG4gICAgICAgICAgICAwLjcqdmVjMVsyXSArIDAuMyp2ZWMyWzJdIF1cbiAgcmV0dXJuIGFucztcbn1cbmV4cG9ydHMub25lVGhpcmQgPSBvbmVUaGlyZDtcblxuZnVuY3Rpb24gcmVjaXByb2NhbCh2ZWMpIHsgICAgLy8gcmVmbGVjdCAzLXZlY3RvciBpbiB1bml0IHNwaGVyZVxuICB2YXIgZmFjdG9yID0gMS4vbWFnMih2ZWMpO1xuICByZXR1cm4gWyAgZmFjdG9yKnZlY1swXSxcbiAgICAgICAgICAgIGZhY3Rvcip2ZWNbMV0sXG4gICAgICAgICAgICBmYWN0b3IqdmVjWzJdIF1cbn1cbmV4cG9ydHMucmVjaXByb2NhbCA9IHJlY2lwcm9jYWw7XG5cbmZ1bmN0aW9uIHVuaXQodmVjKSB7ICAgICAgICAgIC8vIG5vcm1hbGl6ZSAzLXZlY3RvciB0byB1bml0IG1hZ25pdHVkZVxuICB2YXIgc2l6ZSA9IG1hZzIodmVjKTtcbiAgaWYgKHNpemUgPD0gMWUtOCkgeyAgICAgICAgICAvLyByZW1vdmUgdGhpcyB0ZXN0IHNvbWVkYXkuLi5cbiAgICByZXR1cm4gKHZlYyk7XG4gIH1cbiAgdmFyIGMgPSAxLi9NYXRoLnNxcnQoc2l6ZSk7XG4gIHJldHVybiBtdWx0KGMsIHZlYyk7XG59XG5leHBvcnRzLnVuaXQgPSB1bml0O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbi8vSW1wb3J0IG9wZXJhdG9yc1xudmFyIHNlZWRzID0gcmVxdWlyZShcIi4vbGliL3NlZWRzLmpzXCIpO1xudmFyIG9wZXJhdG9ycyA9IHJlcXVpcmUoXCIuL2xpYi9vcGVyYXRvcnMuanNcIik7XG5cbi8vU2VlZCB0eXBlc1xudmFyIFNFRURfRlVOQ1MgPSB7XG4gIFwiVFwiOiAgc2VlZHMudGV0cmFoZWRyb24sXG4gIFwiT1wiOiAgc2VlZHMub2N0YWhlZHJvbixcbiAgXCJDXCI6ICBzZWVkcy5jdWJlLFxuICBcIklcIjogIHNlZWRzLmljb3NhaGVkcm9uLFxuICBcIkRcIjogIHNlZWRzLmRvZGVjYWhlZHJvbixcbiAgXCJQXCI6ICBzZWVkcy5wcmlzbSxcbiAgXCJBXCI6ICBzZWVkcy5hbnRpcHJpc20sXG4gIFwiWVwiOiAgc2VlZHMucHlyYW1pZFxufTtcblxuLy9PcGVyYXRvciB0eXBlc1xudmFyIE9QRVJBVE9SX0ZVTkNTID0ge1xuICBcImtcIjogIG9wZXJhdG9ycy5raXMsXG4gIFwiYVwiOiAgb3BlcmF0b3JzLmFtYm8sXG4gIFwiZ1wiOiAgb3BlcmF0b3JzLmd5cm8sXG4gIFwicFwiOiAgb3BlcmF0b3JzLnByb3BlbGxvcixcbiAgXCJkXCI6ICBvcGVyYXRvcnMuZHVhbCxcbiAgXCJyXCI6ICBvcGVyYXRvcnMucmVmbGVjdCxcbiAgXCJjXCI6ICBvcGVyYXRvcnMuY2Fub25pY2FsaXplXG59O1xuXG4vL0NoZWNrcyBpZiBhIHNlZWQgaXMgYSB0b2tlblxuZnVuY3Rpb24gaXNUb2tlbihjKSB7XG4gIHJldHVybiBcImthZ3BkY3JUT0NJRFBBWVwiLmluZGV4T2YoYykgPj0gMDtcbn1cblxuLy9DaGVja3MgaWYgYSB2YWx1ZSBpcyBudW1lcmljXG5mdW5jdGlvbiBpc051bWVyaWMoYykge1xuICByZXR1cm4gNDggPD0gYyAmJiBjIDw9IDU3O1xufVxuXG4vL1Rva2VuaXplIGFuIGV4cHJlc3Npb24gaW4gQ29ud2F5IG5vdGF0aW9uXG5mdW5jdGlvbiB0b2tlbml6ZShleHByKSB7XG4gIGV4cHIgPSBleHByLnJlcGxhY2UoL1A0JC9nLCBcIkNcIikgIC8vIFA0IC0tPiBDICAgKEMgaXMgcHJpc20pXG4gICAgICAgICAgICAgLnJlcGxhY2UoL0EzJC9nLCBcIk9cIikgIC8vIEEzIC0tPiBPICAgKE8gaXMgYW50aXByaXNtKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9ZMyQvZywgXCJUXCIpICAvLyBZMyAtLT4gVCAgIChUIGlzIHB5cmFtaWQpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2UvZywgXCJhYVwiKSAgIC8vIGUgLS0+IGFhICAgKGFiYnIuIGZvciBleHBsb2RlKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9iL2csIFwidGFcIikgICAvLyBiIC0tPiB0YSAgIChhYmJyLiBmb3IgYmV2ZWwpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL28vZywgXCJqalwiKSAgIC8vIG8gLS0+IGpqICAgKGFiYnIuIGZvciBvcnRobylcbiAgICAgICAgICAgICAucmVwbGFjZSgvbS9nLCBcImtqXCIpICAgLy8gbSAtLT4ga2ogICAoYWJici4gZm9yIG1ldGEpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL3QoXFxkKikvZywgXCJkayQxZFwiKSAgLy8gdChuKSAtLT4gZGsobilkICAoZHVhbCBvcGVyYXRpb25zKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9qL2csIFwiZGFkXCIpICAvLyBqIC0tPiBkYWQgIChkdWFsIG9wZXJhdGlvbnMpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL3MvZywgXCJkZ2RcIikgIC8vIHMgLS0+IGRnZCAgKGR1YWwgb3BlcmF0aW9ucylcbiAgICAgICAgICAgICAucmVwbGFjZSgvZGQvZywgXCJcIikgICAgLy8gZGQgLS0+IG51bGwgIChvcmRlciAyKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9hZC9nLCBcImFcIikgICAvLyBhZCAtLT4gYSAgIChhXyA9IGFkXylcbiAgICAgICAgICAgICAucmVwbGFjZSgvZ2QvZywgXCJnXCIpICAgLy8gZ2QgLS0+IGcgICAoZ18gPSBnZF8pXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2FZL2csIFwiQVwiKSAgIC8vIGFZIC0tPiBBICAgKGludGVyZXN0aW5nIGZhY3QpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2RUL2csIFwiVFwiKSAgIC8vIGRUIC0tPiBUICAgKHNlbGYtZHVhbClcbiAgICAgICAgICAgICAucmVwbGFjZSgvZ1QvZywgXCJEXCIpICAgLy8gZ1QgLS0+IEQgICAoc3ltbSBjaGFuZ2UpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2FUL2csIFwiT1wiKSAgIC8vIGFUIC0tPiBPICAgKHN5bW0gY2hhbmdlKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9kQy9nLCBcIk9cIikgICAvLyBkQyAtLT4gTyAgIChkdWFsIHBhaXIpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2RPL2csIFwiQ1wiKSAgIC8vIGRPIC0tPiBDICAgKGR1YWwgcGFpcilcbiAgICAgICAgICAgICAucmVwbGFjZSgvZEkvZywgXCJEXCIpICAgLy8gZEkgLS0+IEQgICAoZHVhbCBwYWlyKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9kRC9nLCBcIklcIikgICAvLyBkRCAtLT4gSSAgIChkdWFsIHBhaXIpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2FPL2csIFwiYUNcIikgIC8vIGFPIC0tPiBhQyAgKGZvciB1bmlxdWVuZXNzKVxuICAgICAgICAgICAgIC5yZXBsYWNlKC9hSS9nLCBcImFEXCIpICAvLyBhSSAtLT4gYUQgIChmb3IgdW5pcXVlbmVzcylcbiAgICAgICAgICAgICAucmVwbGFjZSgvZ08vZywgXCJnQ1wiKSAgLy8gZ08gLS0+IGdDICAoZm9yIHVuaXF1ZW5lc3MpXG4gICAgICAgICAgICAgLnJlcGxhY2UoL2dJL2csIFwiZ0RcIik7IC8vIGdJIC0tPiBnRCAgKGZvciB1bmlxdWVuZXNzKVxuICB2YXIgdG9rcyA9IFtdO1xuICB2YXIgcHRyID0gMDtcbiAgd2hpbGUocHRyIDwgZXhwci5sZW5ndGgpIHtcbiAgICB2YXIgb3AgPSBleHByLmNoYXJBdChwdHIpO1xuICAgIGlmKCFpc1Rva2VuKG9wKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVW5leHBlY3RlZCB0b2tlbjogXCIgKyBjICsgXCIgaW4gaW5wdXQgXCIgKyBleHByICsgXCIgYXQgXCIgKyBwdHIpO1xuICAgIH1cbiAgICB2YXIgc3RhcnRfbiA9ICsrcHRyO1xuICAgIHdoaWxlKHB0ciA8IGV4cHIubGVuZ3RoICYmIGlzTnVtZXJpYyhleHByLmNoYXJDb2RlQXQocHRyKSkpIHtcbiAgICAgICsrcHRyO1xuICAgIH1cbiAgICB2YXIgbiA9IHBhcnNlSW50KGV4cHIuc3Vic3RyKHN0YXJ0X24sIHB0cikpO1xuICAgIHRva3MucHVzaCh7XG4gICAgICBvcDogb3AsXG4gICAgICBuOiAgbiB8IDBcbiAgICB9KTtcbiAgfVxuICB0b2tzLnJldmVyc2UoKTtcbiAgcmV0dXJuIHRva3M7XG59XG5cbi8vTWFpbiBleHByZXNzaW9uIGludGVycHJldGVyXG5mdW5jdGlvbiBldmFsQ29ud2F5SGFydChleHByKSB7XG5cbiAgLy9QYXJzZSBleHByZXNzaW9uXG4gIHZhciB0b2tzID0gdG9rZW5pemUoZXhwcik7XG4gIFxuICAvL0luaXRpYWxpemUgc2VlZFxuICB2YXIgY3RvciA9IFNFRURfRlVOQ1NbdG9rc1swXS5vcF07XG4gIGlmKCFjdG9yKSB7XG4gICAgdGhyb3cgRXJyb3IoXCJJbnZhbGlkIHNlZWQgdHlwZTogXCIgKyBKU09OLnN0cmluZ2lmeShvcHNbMF0pKTtcbiAgfSBlbHNlIGlmKFwiUEFZXCIuaW5kZXhPZih0b2tzWzBdLm9wKSA+PSAwKSB7XG4gICAgaWYodG9rc1swXS5uIDwgMykge1xuICAgICAgdGhyb3cgRXJyb3IoXCJJbnZhbGlkIG51bWJlciBvZiBmYWNlcyBmb3Igc2VlZFwiKTtcbiAgICB9XG4gIH0gZWxzZSBpZih0b2tzWzBdLm4gIT09IDApIHtcbiAgICB0aHJvdyBFcnJvcihcIlNlZWQgXCIgICsgdG9rc1swXS5vcCArIFwiIGRvZXMgbm90IHVzZSBhIHBhcmFtZXRlclwiKTtcbiAgfVxuICB2YXIgcG9seSA9IGN0b3IodG9rc1swXS5uKTtcbiAgXG4gIC8vQXBwbHkgb3BlcmF0b3JzXG4gIGZvcih2YXIgaT0xOyBpPHRva3MubGVuZ3RoOyArK2kpIHtcbiAgICB2YXIgb3AgPSBPUEVSQVRPUl9GVU5DU1t0b2tzW2ldLm9wXTtcbiAgICBpZighb3ApIHtcbiAgICAgIHRocm93IEVycm9yKFwiSW52YWxpZCBvcGVyYXRvcjogXCIgKyB0b2tzW2ldKTtcbiAgICB9XG4gICAgcG9seSA9IG9wKHBvbHksIHRva3NbaV0ubik7XG4gIH0gIFxuICByZXR1cm4geyBuYW1lOiBwb2x5Lm5hbWUsIGNlbGxzOiBwb2x5LmZhY2VzLCBwb3NpdGlvbnM6IHBvbHkucG9zaXRpb25zIH07XG59XG5tb2R1bGUuZXhwb3J0cyA9IGV2YWxDb253YXlIYXJ0O1xuXG4iXX0= | |
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"bound-points":[function(require,module,exports){ | |
'use strict' | |
module.exports = findBounds | |
function findBounds(points) { | |
var n = points.length | |
if(n === 0) { | |
return [[], []] | |
} | |
var d = points[0].length | |
var lo = points[0].slice() | |
var hi = points[0].slice() | |
for(var i=1; i<n; ++i) { | |
var p = points[i] | |
for(var j=0; j<d; ++j) { | |
var x = p[j] | |
lo[j] = Math.min(lo[j], x) | |
hi[j] = Math.max(hi[j], x) | |
} | |
} | |
return [lo, hi] | |
} | |
},{}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImJvdW5kcy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIid1c2Ugc3RyaWN0J1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZpbmRCb3VuZHNcblxuZnVuY3Rpb24gZmluZEJvdW5kcyhwb2ludHMpIHtcbiAgdmFyIG4gPSBwb2ludHMubGVuZ3RoXG4gIGlmKG4gPT09IDApIHtcbiAgICByZXR1cm4gW1tdLCBbXV1cbiAgfVxuICB2YXIgZCA9IHBvaW50c1swXS5sZW5ndGhcbiAgdmFyIGxvID0gcG9pbnRzWzBdLnNsaWNlKClcbiAgdmFyIGhpID0gcG9pbnRzWzBdLnNsaWNlKClcbiAgZm9yKHZhciBpPTE7IGk8bjsgKytpKSB7XG4gICAgdmFyIHAgPSBwb2ludHNbaV1cbiAgICBmb3IodmFyIGo9MDsgajxkOyArK2opIHtcbiAgICAgIHZhciB4ID0gcFtqXVxuICAgICAgbG9bal0gPSBNYXRoLm1pbihsb1tqXSwgeClcbiAgICAgIGhpW2pdID0gTWF0aC5tYXgoaGlbal0sIHgpXG4gICAgfVxuICB9XG4gIHJldHVybiBbbG8sIGhpXVxufSJdfQ== | |
const polytope = require('conway-hart')('tI') | |
const vec3 = require('gl-vec3') | |
const HEIGHT = 6 | |
const bounds = require('bound-points')(polytope.positions) | |
const radius = Math.max( | |
bounds[1][0] - bounds[0][0], | |
bounds[1][1] - bounds[0][1], | |
bounds[1][2] - bounds[0][2]) | |
const scale = HEIGHT / radius | |
const edges = [] | |
polytope.cells.forEach((c) => { | |
for (let i = 0; i < c.length; ++i) { | |
edges.push([c[i], c[(i + 1) % c.length]]) | |
} | |
}) | |
const lengths = edges.map(([i, j]) => | |
vec3.length(vec3.subtract([], polytope.positions[i], polytope.positions[j]))) | |
lengths.sort(function (a, b) { | |
return a - b | |
}) | |
const histogram = {} | |
let total = 0 | |
lengths.forEach((l) => { | |
const x = Math.round(scale * l * 1000.0) / 1000.0 | |
histogram[x] = (histogram[x] | 0) + 1 | |
total += x | |
}) | |
Object.keys(histogram).forEach(function (l) { | |
histogram[l] /= 2 | |
}) | |
if (typeof document !== 'undefined') { | |
document.body.appendChild(document.createTextNode( | |
`dome height: ${HEIGHT} lengths: ${JSON.stringify(histogram)} total: ${total / 2}`)) | |
} | |
console.log('dome height: ', HEIGHT) | |
console.log('lengths: ', histogram) | |
console.log('total: ', total / 2) | |
;}, 0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "requirebin-sketch", | |
"version": "1.0.0", | |
"dependencies": { | |
"gl-vec3": "1.0.3", | |
"conway-hart": "0.1.0", | |
"bound-points": "1.0.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- contents of this file will be placed inside the <body> --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- contents of this file will be placed inside the <head> --> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment