Last active
July 21, 2017 18:30
-
-
Save Fauntleroy/23a2bc13c8c235c52a4923737182ca37 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
// Welcome! require() some modules from npm (like you were using browserify) | |
// and then hit Run Code to run your code on the right side. | |
// Modules get downloaded from browserify-cdn and bundled in your browser. | |
const decomp = require('poly-decomp'); | |
window.decomp = decomp; | |
const Matter = require('matter-js'); | |
const { PI } = Math; | |
// module aliases | |
const { Engine, Render, World, Bodies, Vertices } = Matter; | |
const { circle, rectangle } = Bodies; | |
// create an engine | |
const engine = Engine.create(); | |
// create a renderer | |
const render = Render.create({ | |
element: document.body, | |
engine: engine, | |
options: { | |
width: 400, | |
height: 800 | |
} | |
}); | |
// create two boxes and a ground | |
const pellet = circle(200, 0, 10); | |
const staticBox1 = rectangle(200, 350, 40, 40, { angle: (PI / 4), isStatic: true }); | |
const staticBox2 = rectangle(300, 450, 40, 40, { angle: (PI / 4), isStatic: true }); | |
const staticBox3 = rectangle(100, 450, 40, 40, { angle: (PI / 4), isStatic: true }); | |
const leftWall = rectangle(-5, 400, 20, 800, { isStatic: true }); | |
const rightWall = rectangle(405, 400, 20, 800, { isStatic: true }); | |
const boundary1 = rectangle(100, 755, 5, 50, { isStatic: true }); | |
const boundary2 = rectangle(175, 755, 5, 50, { isStatic: true }); | |
const boundary3 = rectangle(225, 755, 5, 50, { isStatic: true }); | |
const boundary4 = rectangle(300, 755, 5, 50, { isStatic: true }); | |
const zone1 = rectangle(50, 790, 100, 20, { isStatic: true }); | |
const zone2 = rectangle(137, 790, 75, 20, { isStatic: true }); | |
const zone3 = rectangle(200, 790, 50, 20, { isStatic: true }); | |
const zone4 = rectangle(262, 790, 75, 20, { isStatic: true }); | |
const zone5 = rectangle(350, 790, 100, 20, { isStatic: true }); | |
// add all of the bodies to the world | |
World.add(engine.world, [ | |
pellet, | |
staticBox1, staticBox2, staticBox3, | |
leftWall, rightWall, | |
boundary1, boundary2, boundary3, boundary4, | |
zone1, zone2, zone3, zone4, zone5 | |
]); | |
// run the engine | |
Engine.run(engine); | |
// run the renderer | |
Render.run(render); |
This file has been truncated, but you can view the full file.
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})({"poly-decomp":[function(require,module,exports){ | |
module.exports = { | |
decomp: polygonDecomp, | |
quickDecomp: polygonQuickDecomp, | |
isSimple: polygonIsSimple, | |
removeCollinearPoints: polygonRemoveCollinearPoints, | |
makeCCW: polygonMakeCCW | |
}; | |
/** | |
* Compute the intersection between two lines. | |
* @static | |
* @method lineInt | |
* @param {Array} l1 Line vector 1 | |
* @param {Array} l2 Line vector 2 | |
* @param {Number} precision Precision to use when checking if the lines are parallel | |
* @return {Array} The intersection point. | |
*/ | |
function lineInt(l1,l2,precision){ | |
precision = precision || 0; | |
var i = [0,0]; // point | |
var a1, b1, c1, a2, b2, c2, det; // scalars | |
a1 = l1[1][1] - l1[0][1]; | |
b1 = l1[0][0] - l1[1][0]; | |
c1 = a1 * l1[0][0] + b1 * l1[0][1]; | |
a2 = l2[1][1] - l2[0][1]; | |
b2 = l2[0][0] - l2[1][0]; | |
c2 = a2 * l2[0][0] + b2 * l2[0][1]; | |
det = a1 * b2 - a2*b1; | |
if (!scalar_eq(det, 0, precision)) { // lines are not parallel | |
i[0] = (b2 * c1 - b1 * c2) / det; | |
i[1] = (a1 * c2 - a2 * c1) / det; | |
} | |
return i; | |
} | |
/** | |
* Checks if two line segments intersects. | |
* @method segmentsIntersect | |
* @param {Array} p1 The start vertex of the first line segment. | |
* @param {Array} p2 The end vertex of the first line segment. | |
* @param {Array} q1 The start vertex of the second line segment. | |
* @param {Array} q2 The end vertex of the second line segment. | |
* @return {Boolean} True if the two line segments intersect | |
*/ | |
function lineSegmentsIntersect(p1, p2, q1, q2){ | |
var dx = p2[0] - p1[0]; | |
var dy = p2[1] - p1[1]; | |
var da = q2[0] - q1[0]; | |
var db = q2[1] - q1[1]; | |
// segments are parallel | |
if((da*dy - db*dx) === 0){ | |
return false; | |
} | |
var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx); | |
var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy); | |
return (s>=0 && s<=1 && t>=0 && t<=1); | |
} | |
/** | |
* Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order. | |
* @static | |
* @method area | |
* @param {Array} a | |
* @param {Array} b | |
* @param {Array} c | |
* @return {Number} | |
*/ | |
function triangleArea(a,b,c){ | |
return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))); | |
} | |
function isLeft(a,b,c){ | |
return triangleArea(a,b,c) > 0; | |
} | |
function isLeftOn(a,b,c) { | |
return triangleArea(a, b, c) >= 0; | |
} | |
function isRight(a,b,c) { | |
return triangleArea(a, b, c) < 0; | |
} | |
function isRightOn(a,b,c) { | |
return triangleArea(a, b, c) <= 0; | |
} | |
var tmpPoint1 = [], | |
tmpPoint2 = []; | |
/** | |
* Check if three points are collinear | |
* @method collinear | |
* @param {Array} a | |
* @param {Array} b | |
* @param {Array} c | |
* @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision. | |
* @return {Boolean} | |
*/ | |
function collinear(a,b,c,thresholdAngle) { | |
if(!thresholdAngle){ | |
return triangleArea(a, b, c) === 0; | |
} else { | |
var ab = tmpPoint1, | |
bc = tmpPoint2; | |
ab[0] = b[0]-a[0]; | |
ab[1] = b[1]-a[1]; | |
bc[0] = c[0]-b[0]; | |
bc[1] = c[1]-b[1]; | |
var dot = ab[0]*bc[0] + ab[1]*bc[1], | |
magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]), | |
magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]), | |
angle = Math.acos(dot/(magA*magB)); | |
return angle < thresholdAngle; | |
} | |
} | |
function sqdist(a,b){ | |
var dx = b[0] - a[0]; | |
var dy = b[1] - a[1]; | |
return dx * dx + dy * dy; | |
} | |
/** | |
* Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle. | |
* @method at | |
* @param {Number} i | |
* @return {Array} | |
*/ | |
function polygonAt(polygon, i){ | |
var s = polygon.length; | |
return polygon[i < 0 ? i % s + s : i % s]; | |
} | |
/** | |
* Clear the polygon data | |
* @method clear | |
* @return {Array} | |
*/ | |
function polygonClear(polygon){ | |
polygon.length = 0; | |
} | |
/** | |
* Append points "from" to "to"-1 from an other polygon "poly" onto this one. | |
* @method append | |
* @param {Polygon} poly The polygon to get points from. | |
* @param {Number} from The vertex index in "poly". | |
* @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending. | |
* @return {Array} | |
*/ | |
function polygonAppend(polygon, poly, from, to){ | |
for(var i=from; i<to; i++){ | |
polygon.push(poly[i]); | |
} | |
} | |
/** | |
* Make sure that the polygon vertices are ordered counter-clockwise. | |
* @method makeCCW | |
*/ | |
function polygonMakeCCW(polygon){ | |
var br = 0, | |
v = polygon; | |
// find bottom right point | |
for (var i = 1; i < polygon.length; ++i) { | |
if (v[i][1] < v[br][1] || (v[i][1] === v[br][1] && v[i][0] > v[br][0])) { | |
br = i; | |
} | |
} | |
// reverse poly if clockwise | |
if (!isLeft(polygonAt(polygon, br - 1), polygonAt(polygon, br), polygonAt(polygon, br + 1))) { | |
polygonReverse(polygon); | |
} | |
} | |
/** | |
* Reverse the vertices in the polygon | |
* @method reverse | |
*/ | |
function polygonReverse(polygon){ | |
var tmp = []; | |
var N = polygon.length; | |
for(var i=0; i!==N; i++){ | |
tmp.push(polygon.pop()); | |
} | |
for(var i=0; i!==N; i++){ | |
polygon[i] = tmp[i]; | |
} | |
} | |
/** | |
* Check if a point in the polygon is a reflex point | |
* @method isReflex | |
* @param {Number} i | |
* @return {Boolean} | |
*/ | |
function polygonIsReflex(polygon, i){ | |
return isRight(polygonAt(polygon, i - 1), polygonAt(polygon, i), polygonAt(polygon, i + 1)); | |
} | |
var tmpLine1=[], | |
tmpLine2=[]; | |
/** | |
* Check if two vertices in the polygon can see each other | |
* @method canSee | |
* @param {Number} a Vertex index 1 | |
* @param {Number} b Vertex index 2 | |
* @return {Boolean} | |
*/ | |
function polygonCanSee(polygon, a,b) { | |
var p, dist, l1=tmpLine1, l2=tmpLine2; | |
if (isLeftOn(polygonAt(polygon, a + 1), polygonAt(polygon, a), polygonAt(polygon, b)) && isRightOn(polygonAt(polygon, a - 1), polygonAt(polygon, a), polygonAt(polygon, b))) { | |
return false; | |
} | |
dist = sqdist(polygonAt(polygon, a), polygonAt(polygon, b)); | |
for (var i = 0; i !== polygon.length; ++i) { // for each edge | |
if ((i + 1) % polygon.length === a || i === a){ // ignore incident edges | |
continue; | |
} | |
if (isLeftOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i + 1)) && isRightOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i))) { // if diag intersects an edge | |
l1[0] = polygonAt(polygon, a); | |
l1[1] = polygonAt(polygon, b); | |
l2[0] = polygonAt(polygon, i); | |
l2[1] = polygonAt(polygon, i + 1); | |
p = lineInt(l1,l2); | |
if (sqdist(polygonAt(polygon, a), p) < dist) { // if edge is blocking visibility to b | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
/** | |
* Copy the polygon from vertex i to vertex j. | |
* @method copy | |
* @param {Number} i | |
* @param {Number} j | |
* @param {Polygon} [targetPoly] Optional target polygon to save in. | |
* @return {Polygon} The resulting copy. | |
*/ | |
function polygonCopy(polygon, i,j,targetPoly){ | |
var p = targetPoly || []; | |
polygonClear(p); | |
if (i < j) { | |
// Insert all vertices from i to j | |
for(var k=i; k<=j; k++){ | |
p.push(polygon[k]); | |
} | |
} else { | |
// Insert vertices 0 to j | |
for(var k=0; k<=j; k++){ | |
p.push(polygon[k]); | |
} | |
// Insert vertices i to end | |
for(var k=i; k<polygon.length; k++){ | |
p.push(polygon[k]); | |
} | |
} | |
return p; | |
} | |
/** | |
* Decomposes the polygon into convex pieces. Returns a list of edges [[p1,p2],[p2,p3],...] that cuts the polygon. | |
* Note that this algorithm has complexity O(N^4) and will be very slow for polygons with many vertices. | |
* @method getCutEdges | |
* @return {Array} | |
*/ | |
function polygonGetCutEdges(polygon) { | |
var min=[], tmp1=[], tmp2=[], tmpPoly = []; | |
var nDiags = Number.MAX_VALUE; | |
for (var i = 0; i < polygon.length; ++i) { | |
if (polygonIsReflex(polygon, i)) { | |
for (var j = 0; j < polygon.length; ++j) { | |
if (polygonCanSee(polygon, i, j)) { | |
tmp1 = polygonGetCutEdges(polygonCopy(polygon, i, j, tmpPoly)); | |
tmp2 = polygonGetCutEdges(polygonCopy(polygon, j, i, tmpPoly)); | |
for(var k=0; k<tmp2.length; k++){ | |
tmp1.push(tmp2[k]); | |
} | |
if (tmp1.length < nDiags) { | |
min = tmp1; | |
nDiags = tmp1.length; | |
min.push([polygonAt(polygon, i), polygonAt(polygon, j)]); | |
} | |
} | |
} | |
} | |
} | |
return min; | |
} | |
/** | |
* Decomposes the polygon into one or more convex sub-Polygons. | |
* @method decomp | |
* @return {Array} An array or Polygon objects. | |
*/ | |
function polygonDecomp(polygon){ | |
var edges = polygonGetCutEdges(polygon); | |
if(edges.length > 0){ | |
return polygonSlice(polygon, edges); | |
} else { | |
return [polygon]; | |
} | |
} | |
/** | |
* Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons. | |
* @method slice | |
* @param {Array} cutEdges A list of edges, as returned by .getCutEdges() | |
* @return {Array} | |
*/ | |
function polygonSlice(polygon, cutEdges){ | |
if(cutEdges.length === 0){ | |
return [polygon]; | |
} | |
if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length===2 && cutEdges[0][0] instanceof Array){ | |
var polys = [polygon]; | |
for(var i=0; i<cutEdges.length; i++){ | |
var cutEdge = cutEdges[i]; | |
// Cut all polys | |
for(var j=0; j<polys.length; j++){ | |
var poly = polys[j]; | |
var result = polygonSlice(poly, cutEdge); | |
if(result){ | |
// Found poly! Cut and quit | |
polys.splice(j,1); | |
polys.push(result[0],result[1]); | |
break; | |
} | |
} | |
} | |
return polys; | |
} else { | |
// Was given one edge | |
var cutEdge = cutEdges; | |
var i = polygon.indexOf(cutEdge[0]); | |
var j = polygon.indexOf(cutEdge[1]); | |
if(i !== -1 && j !== -1){ | |
return [polygonCopy(polygon, i,j), | |
polygonCopy(polygon, j,i)]; | |
} else { | |
return false; | |
} | |
} | |
} | |
/** | |
* Checks that the line segments of this polygon do not intersect each other. | |
* @method isSimple | |
* @param {Array} path An array of vertices e.g. [[0,0],[0,1],...] | |
* @return {Boolean} | |
* @todo Should it check all segments with all others? | |
*/ | |
function polygonIsSimple(polygon){ | |
var path = polygon, i; | |
// Check | |
for(i=0; i<path.length-1; i++){ | |
for(var j=0; j<i-1; j++){ | |
if(lineSegmentsIntersect(path[i], path[i+1], path[j], path[j+1] )){ | |
return false; | |
} | |
} | |
} | |
// Check the segment between the last and the first point to all others | |
for(i=1; i<path.length-2; i++){ | |
if(lineSegmentsIntersect(path[0], path[path.length-1], path[i], path[i+1] )){ | |
return false; | |
} | |
} | |
return true; | |
} | |
function getIntersectionPoint(p1, p2, q1, q2, delta){ | |
delta = delta || 0; | |
var a1 = p2[1] - p1[1]; | |
var b1 = p1[0] - p2[0]; | |
var c1 = (a1 * p1[0]) + (b1 * p1[1]); | |
var a2 = q2[1] - q1[1]; | |
var b2 = q1[0] - q2[0]; | |
var c2 = (a2 * q1[0]) + (b2 * q1[1]); | |
var det = (a1 * b2) - (a2 * b1); | |
if(!scalar_eq(det,0,delta)){ | |
return [((b2 * c1) - (b1 * c2)) / det, ((a1 * c2) - (a2 * c1)) / det]; | |
} else { | |
return [0,0]; | |
} | |
} | |
/** | |
* Quickly decompose the Polygon into convex sub-polygons. | |
* @method quickDecomp | |
* @param {Array} result | |
* @param {Array} [reflexVertices] | |
* @param {Array} [steinerPoints] | |
* @param {Number} [delta] | |
* @param {Number} [maxlevel] | |
* @param {Number} [level] | |
* @return {Array} | |
*/ | |
function polygonQuickDecomp(polygon, result,reflexVertices,steinerPoints,delta,maxlevel,level){ | |
maxlevel = maxlevel || 100; | |
level = level || 0; | |
delta = delta || 25; | |
result = typeof(result)!=="undefined" ? result : []; | |
reflexVertices = reflexVertices || []; | |
steinerPoints = steinerPoints || []; | |
var upperInt=[0,0], lowerInt=[0,0], p=[0,0]; // Points | |
var upperDist=0, lowerDist=0, d=0, closestDist=0; // scalars | |
var upperIndex=0, lowerIndex=0, closestIndex=0; // Integers | |
var lowerPoly=[], upperPoly=[]; // polygons | |
var poly = polygon, | |
v = polygon; | |
if(v.length < 3){ | |
return result; | |
} | |
level++; | |
if(level > maxlevel){ | |
console.warn("quickDecomp: max level ("+maxlevel+") reached."); | |
return result; | |
} | |
for (var i = 0; i < polygon.length; ++i) { | |
if (polygonIsReflex(poly, i)) { | |
reflexVertices.push(poly[i]); | |
upperDist = lowerDist = Number.MAX_VALUE; | |
for (var j = 0; j < polygon.length; ++j) { | |
if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j - 1))) { // if line intersects with an edge | |
p = getIntersectionPoint(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j - 1)); // find the point of intersection | |
if (isRight(polygonAt(poly, i + 1), polygonAt(poly, i), p)) { // make sure it's inside the poly | |
d = sqdist(poly[i], p); | |
if (d < lowerDist) { // keep only the closest intersection | |
lowerDist = d; | |
lowerInt = p; | |
lowerIndex = j; | |
} | |
} | |
} | |
if (isLeft(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j + 1)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) { | |
p = getIntersectionPoint(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j + 1)); | |
if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), p)) { | |
d = sqdist(poly[i], p); | |
if (d < upperDist) { | |
upperDist = d; | |
upperInt = p; | |
upperIndex = j; | |
} | |
} | |
} | |
} | |
// if there are no vertices to connect to, choose a point in the middle | |
if (lowerIndex === (upperIndex + 1) % polygon.length) { | |
//console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+polygon.length+")"); | |
p[0] = (lowerInt[0] + upperInt[0]) / 2; | |
p[1] = (lowerInt[1] + upperInt[1]) / 2; | |
steinerPoints.push(p); | |
if (i < upperIndex) { | |
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1); | |
polygonAppend(lowerPoly, poly, i, upperIndex+1); | |
lowerPoly.push(p); | |
upperPoly.push(p); | |
if (lowerIndex !== 0){ | |
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end()); | |
polygonAppend(upperPoly, poly,lowerIndex,poly.length); | |
} | |
//upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1); | |
polygonAppend(upperPoly, poly,0,i+1); | |
} else { | |
if (i !== 0){ | |
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end()); | |
polygonAppend(lowerPoly, poly,i,poly.length); | |
} | |
//lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1); | |
polygonAppend(lowerPoly, poly,0,upperIndex+1); | |
lowerPoly.push(p); | |
upperPoly.push(p); | |
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); | |
polygonAppend(upperPoly, poly,lowerIndex,i+1); | |
} | |
} else { | |
// connect to the closest point within the triangle | |
//console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+polygon.length+")\n"); | |
if (lowerIndex > upperIndex) { | |
upperIndex += polygon.length; | |
} | |
closestDist = Number.MAX_VALUE; | |
if(upperIndex < lowerIndex){ | |
return result; | |
} | |
for (var j = lowerIndex; j <= upperIndex; ++j) { | |
if (isLeftOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) { | |
d = sqdist(polygonAt(poly, i), polygonAt(poly, j)); | |
if (d < closestDist) { | |
closestDist = d; | |
closestIndex = j % polygon.length; | |
} | |
} | |
} | |
if (i < closestIndex) { | |
polygonAppend(lowerPoly, poly,i,closestIndex+1); | |
if (closestIndex !== 0){ | |
polygonAppend(upperPoly, poly,closestIndex,v.length); | |
} | |
polygonAppend(upperPoly, poly,0,i+1); | |
} else { | |
if (i !== 0){ | |
polygonAppend(lowerPoly, poly,i,v.length); | |
} | |
polygonAppend(lowerPoly, poly,0,closestIndex+1); | |
polygonAppend(upperPoly, poly,closestIndex,i+1); | |
} | |
} | |
// solve smallest poly first | |
if (lowerPoly.length < upperPoly.length) { | |
polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); | |
polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); | |
} else { | |
polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); | |
polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); | |
} | |
return result; | |
} | |
} | |
result.push(polygon); | |
return result; | |
} | |
/** | |
* Remove collinear points in the polygon. | |
* @method removeCollinearPoints | |
* @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision. | |
* @return {Number} The number of points removed | |
*/ | |
function polygonRemoveCollinearPoints(polygon, precision){ | |
var num = 0; | |
for(var i=polygon.length-1; polygon.length>3 && i>=0; --i){ | |
if(collinear(polygonAt(polygon, i-1),polygonAt(polygon, i),polygonAt(polygon, i+1),precision)){ | |
// Remove the middle point | |
polygon.splice(i%polygon.length,1); | |
num++; | |
} | |
} | |
return num; | |
} | |
/** | |
* Check if two scalars are equal | |
* @static | |
* @method eq | |
* @param {Number} a | |
* @param {Number} b | |
* @param {Number} [precision] | |
* @return {Boolean} | |
*/ | |
function scalar_eq(a,b,precision){ | |
precision = precision || 0; | |
return Math.abs(a-b) < precision; | |
} | |
},{}]},{},[]) | |
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsInBvbHktZGVjb21wIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIm1vZHVsZS5leHBvcnRzID0ge1xuICAgIGRlY29tcDogcG9seWdvbkRlY29tcCxcbiAgICBxdWlja0RlY29tcDogcG9seWdvblF1aWNrRGVjb21wLFxuICAgIGlzU2ltcGxlOiBwb2x5Z29uSXNTaW1wbGUsXG4gICAgcmVtb3ZlQ29sbGluZWFyUG9pbnRzOiBwb2x5Z29uUmVtb3ZlQ29sbGluZWFyUG9pbnRzLFxuICAgIG1ha2VDQ1c6IHBvbHlnb25NYWtlQ0NXXG59O1xuXG4vKipcbiAqIENvbXB1dGUgdGhlIGludGVyc2VjdGlvbiBiZXR3ZWVuIHR3byBsaW5lcy5cbiAqIEBzdGF0aWNcbiAqIEBtZXRob2QgbGluZUludFxuICogQHBhcmFtICB7QXJyYXl9ICBsMSAgICAgICAgICBMaW5lIHZlY3RvciAxXG4gKiBAcGFyYW0gIHtBcnJheX0gIGwyICAgICAgICAgIExpbmUgdmVjdG9yIDJcbiAqIEBwYXJhbSAge051bWJlcn0gcHJlY2lzaW9uICAgUHJlY2lzaW9uIHRvIHVzZSB3aGVuIGNoZWNraW5nIGlmIHRoZSBsaW5lcyBhcmUgcGFyYWxsZWxcbiAqIEByZXR1cm4ge0FycmF5fSAgICAgICAgICAgICAgVGhlIGludGVyc2VjdGlvbiBwb2ludC5cbiAqL1xuZnVuY3Rpb24gbGluZUludChsMSxsMixwcmVjaXNpb24pe1xuICAgIHByZWNpc2lvbiA9IHByZWNpc2lvbiB8fCAwO1xuICAgIHZhciBpID0gWzAsMF07IC8vIHBvaW50XG4gICAgdmFyIGExLCBiMSwgYzEsIGEyLCBiMiwgYzIsIGRldDsgLy8gc2NhbGFyc1xuICAgIGExID0gbDFbMV1bMV0gLSBsMVswXVsxXTtcbiAgICBiMSA9IGwxWzBdWzBdIC0gbDFbMV1bMF07XG4gICAgYzEgPSBhMSAqIGwxWzBdWzBdICsgYjEgKiBsMVswXVsxXTtcbiAgICBhMiA9IGwyWzFdWzFdIC0gbDJbMF1bMV07XG4gICAgYjIgPSBsMlswXVswXSAtIGwyWzFdWzBdO1xuICAgIGMyID0gYTIgKiBsMlswXVswXSArIGIyICogbDJbMF1bMV07XG4gICAgZGV0ID0gYTEgKiBiMiAtIGEyKmIxO1xuICAgIGlmICghc2NhbGFyX2VxKGRldCwgMCwgcHJlY2lzaW9uKSkgeyAvLyBsaW5lcyBhcmUgbm90IHBhcmFsbGVsXG4gICAgICAgIGlbMF0gPSAoYjIgKiBjMSAtIGIxICogYzIpIC8gZGV0O1xuICAgICAgICBpWzFdID0gKGExICogYzIgLSBhMiAqIGMxKSAvIGRldDtcbiAgICB9XG4gICAgcmV0dXJuIGk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIHR3byBsaW5lIHNlZ21lbnRzIGludGVyc2VjdHMuXG4gKiBAbWV0aG9kIHNlZ21lbnRzSW50ZXJzZWN0XG4gKiBAcGFyYW0ge0FycmF5fSBwMSBUaGUgc3RhcnQgdmVydGV4IG9mIHRoZSBmaXJzdCBsaW5lIHNlZ21lbnQuXG4gKiBAcGFyYW0ge0FycmF5fSBwMiBUaGUgZW5kIHZlcnRleCBvZiB0aGUgZmlyc3QgbGluZSBzZWdtZW50LlxuICogQHBhcmFtIHtBcnJheX0gcTEgVGhlIHN0YXJ0IHZlcnRleCBvZiB0aGUgc2Vjb25kIGxpbmUgc2VnbWVudC5cbiAqIEBwYXJhbSB7QXJyYXl9IHEyIFRoZSBlbmQgdmVydGV4IG9mIHRoZSBzZWNvbmQgbGluZSBzZWdtZW50LlxuICogQHJldHVybiB7Qm9vbGVhbn0gVHJ1ZSBpZiB0aGUgdHdvIGxpbmUgc2VnbWVudHMgaW50ZXJzZWN0XG4gKi9cbmZ1bmN0aW9uIGxpbmVTZWdtZW50c0ludGVyc2VjdChwMSwgcDIsIHExLCBxMil7XG5cdHZhciBkeCA9IHAyWzBdIC0gcDFbMF07XG5cdHZhciBkeSA9IHAyWzFdIC0gcDFbMV07XG5cdHZhciBkYSA9IHEyWzBdIC0gcTFbMF07XG5cdHZhciBkYiA9IHEyWzFdIC0gcTFbMV07XG5cblx0Ly8gc2VnbWVudHMgYXJlIHBhcmFsbGVsXG5cdGlmKChkYSpkeSAtIGRiKmR4KSA9PT0gMCl7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG5cblx0dmFyIHMgPSAoZHggKiAocTFbMV0gLSBwMVsxXSkgKyBkeSAqIChwMVswXSAtIHExWzBdKSkgLyAoZGEgKiBkeSAtIGRiICogZHgpO1xuXHR2YXIgdCA9IChkYSAqIChwMVsxXSAtIHExWzFdKSArIGRiICogKHExWzBdIC0gcDFbMF0pKSAvIChkYiAqIGR4IC0gZGEgKiBkeSk7XG5cblx0cmV0dXJuIChzPj0wICYmIHM8PTEgJiYgdD49MCAmJiB0PD0xKTtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIGFyZWEgb2YgYSB0cmlhbmdsZSBzcGFubmVkIGJ5IHRoZSB0aHJlZSBnaXZlbiBwb2ludHMuIE5vdGUgdGhhdCB0aGUgYXJlYSB3aWxsIGJlIG5lZ2F0aXZlIGlmIHRoZSBwb2ludHMgYXJlIG5vdCBnaXZlbiBpbiBjb3VudGVyLWNsb2Nrd2lzZSBvcmRlci5cbiAqIEBzdGF0aWNcbiAqIEBtZXRob2QgYXJlYVxuICogQHBhcmFtICB7QXJyYXl9IGFcbiAqIEBwYXJhbSAge0FycmF5fSBiXG4gKiBAcGFyYW0gIHtBcnJheX0gY1xuICogQHJldHVybiB7TnVtYmVyfVxuICovXG5mdW5jdGlvbiB0cmlhbmdsZUFyZWEoYSxiLGMpe1xuICAgIHJldHVybiAoKChiWzBdIC0gYVswXSkqKGNbMV0gLSBhWzFdKSktKChjWzBdIC0gYVswXSkqKGJbMV0gLSBhWzFdKSkpO1xufVxuXG5mdW5jdGlvbiBpc0xlZnQoYSxiLGMpe1xuICAgIHJldHVybiB0cmlhbmdsZUFyZWEoYSxiLGMpID4gMDtcbn1cblxuZnVuY3Rpb24gaXNMZWZ0T24oYSxiLGMpIHtcbiAgICByZXR1cm4gdHJpYW5nbGVBcmVhKGEsIGIsIGMpID49IDA7XG59XG5cbmZ1bmN0aW9uIGlzUmlnaHQoYSxiLGMpIHtcbiAgICByZXR1cm4gdHJpYW5nbGVBcmVhKGEsIGIsIGMpIDwgMDtcbn1cblxuZnVuY3Rpb24gaXNSaWdodE9uKGEsYixjKSB7XG4gICAgcmV0dXJuIHRyaWFuZ2xlQXJlYShhLCBiLCBjKSA8PSAwO1xufVxuXG52YXIgdG1wUG9pbnQxID0gW10sXG4gICAgdG1wUG9pbnQyID0gW107XG5cbi8qKlxuICogQ2hlY2sgaWYgdGhyZWUgcG9pbnRzIGFyZSBjb2xsaW5lYXJcbiAqIEBtZXRob2QgY29sbGluZWFyXG4gKiBAcGFyYW0gIHtBcnJheX0gYVxuICogQHBhcmFtICB7QXJyYXl9IGJcbiAqIEBwYXJhbSAge0FycmF5fSBjXG4gKiBAcGFyYW0gIHtOdW1iZXJ9IFt0aHJlc2hvbGRBbmdsZT0wXSBUaHJlc2hvbGQgYW5nbGUgdG8gdXNlIHdoZW4gY29tcGFyaW5nIHRoZSB2ZWN0b3JzLiBUaGUgZnVuY3Rpb24gd2lsbCByZXR1cm4gdHJ1ZSBpZiB0aGUgYW5nbGUgYmV0d2VlbiB0aGUgcmVzdWx0aW5nIHZlY3RvcnMgaXMgbGVzcyB0aGFuIHRoaXMgdmFsdWUuIFVzZSB6ZXJvIGZvciBtYXggcHJlY2lzaW9uLlxuICogQHJldHVybiB7Qm9vbGVhbn1cbiAqL1xuZnVuY3Rpb24gY29sbGluZWFyKGEsYixjLHRocmVzaG9sZEFuZ2xlKSB7XG4gICAgaWYoIXRocmVzaG9sZEFuZ2xlKXtcbiAgICAgICAgcmV0dXJuIHRyaWFuZ2xlQXJlYShhLCBiLCBjKSA9PT0gMDtcbiAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgYWIgPSB0bXBQb2ludDEsXG4gICAgICAgICAgICBiYyA9IHRtcFBvaW50MjtcblxuICAgICAgICBhYlswXSA9IGJbMF0tYVswXTtcbiAgICAgICAgYWJbMV0gPSBiWzFdLWFbMV07XG4gICAgICAgIGJjWzBdID0gY1swXS1iWzBdO1xuICAgICAgICBiY1sxXSA9IGNbMV0tYlsxXTtcblxuICAgICAgICB2YXIgZG90ID0gYWJbMF0qYmNbMF0gKyBhYlsxXSpiY1sxXSxcbiAgICAgICAgICAgIG1hZ0EgPSBNYXRoLnNxcnQoYWJbMF0qYWJbMF0gKyBhYlsxXSphYlsxXSksXG4gICAgICAgICAgICBtYWdCID0gTWF0aC5zcXJ0KGJjWzBdKmJjWzBdICsgYmNbMV0qYmNbMV0pLFxuICAgICAgICAgICAgYW5nbGUgPSBNYXRoLmFjb3MoZG90LyhtYWdBKm1hZ0IpKTtcbiAgICAgICAgcmV0dXJuIGFuZ2xlIDwgdGhyZXNob2xkQW5nbGU7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBzcWRpc3QoYSxiKXtcbiAgICB2YXIgZHggPSBiWzBdIC0gYVswXTtcbiAgICB2YXIgZHkgPSBiWzFdIC0gYVsxXTtcbiAgICByZXR1cm4gZHggKiBkeCArIGR5ICogZHk7XG59XG5cbi8qKlxuICogR2V0IGEgdmVydGV4IGF0IHBvc2l0aW9uIGkuIEl0IGRvZXMgbm90IG1hdHRlciBpZiBpIGlzIG91dCBvZiBib3VuZHMsIHRoaXMgZnVuY3Rpb24gd2lsbCBqdXN0IGN5Y2xlLlxuICogQG1ldGhvZCBhdFxuICogQHBhcmFtICB7TnVtYmVyfSBpXG4gKiBAcmV0dXJuIHtBcnJheX1cbiAqL1xuZnVuY3Rpb24gcG9seWdvbkF0KHBvbHlnb24sIGkpe1xuICAgIHZhciBzID0gcG9seWdvbi5sZW5ndGg7XG4gICAgcmV0dXJuIHBvbHlnb25baSA8IDAgPyBpICUgcyArIHMgOiBpICUgc107XG59XG5cbi8qKlxuICogQ2xlYXIgdGhlIHBvbHlnb24gZGF0YVxuICogQG1ldGhvZCBjbGVhclxuICogQHJldHVybiB7QXJyYXl9XG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25DbGVhcihwb2x5Z29uKXtcbiAgICBwb2x5Z29uLmxlbmd0aCA9IDA7XG59XG5cbi8qKlxuICogQXBwZW5kIHBvaW50cyBcImZyb21cIiB0byBcInRvXCItMSBmcm9tIGFuIG90aGVyIHBvbHlnb24gXCJwb2x5XCIgb250byB0aGlzIG9uZS5cbiAqIEBtZXRob2QgYXBwZW5kXG4gKiBAcGFyYW0ge1BvbHlnb259IHBvbHkgVGhlIHBvbHlnb24gdG8gZ2V0IHBvaW50cyBmcm9tLlxuICogQHBhcmFtIHtOdW1iZXJ9ICBmcm9tIFRoZSB2ZXJ0ZXggaW5kZXggaW4gXCJwb2x5XCIuXG4gKiBAcGFyYW0ge051bWJlcn0gIHRvIFRoZSBlbmQgdmVydGV4IGluZGV4IGluIFwicG9seVwiLiBOb3RlIHRoYXQgdGhpcyB2ZXJ0ZXggaXMgTk9UIGluY2x1ZGVkIHdoZW4gYXBwZW5kaW5nLlxuICogQHJldHVybiB7QXJyYXl9XG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25BcHBlbmQocG9seWdvbiwgcG9seSwgZnJvbSwgdG8pe1xuICAgIGZvcih2YXIgaT1mcm9tOyBpPHRvOyBpKyspe1xuICAgICAgICBwb2x5Z29uLnB1c2gocG9seVtpXSk7XG4gICAgfVxufVxuXG4vKipcbiAqIE1ha2Ugc3VyZSB0aGF0IHRoZSBwb2x5Z29uIHZlcnRpY2VzIGFyZSBvcmRlcmVkIGNvdW50ZXItY2xvY2t3aXNlLlxuICogQG1ldGhvZCBtYWtlQ0NXXG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25NYWtlQ0NXKHBvbHlnb24pe1xuICAgIHZhciBiciA9IDAsXG4gICAgICAgIHYgPSBwb2x5Z29uO1xuXG4gICAgLy8gZmluZCBib3R0b20gcmlnaHQgcG9pbnRcbiAgICBmb3IgKHZhciBpID0gMTsgaSA8IHBvbHlnb24ubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgaWYgKHZbaV1bMV0gPCB2W2JyXVsxXSB8fCAodltpXVsxXSA9PT0gdlticl1bMV0gJiYgdltpXVswXSA+IHZbYnJdWzBdKSkge1xuICAgICAgICAgICAgYnIgPSBpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gcmV2ZXJzZSBwb2x5IGlmIGNsb2Nrd2lzZVxuICAgIGlmICghaXNMZWZ0KHBvbHlnb25BdChwb2x5Z29uLCBiciAtIDEpLCBwb2x5Z29uQXQocG9seWdvbiwgYnIpLCBwb2x5Z29uQXQocG9seWdvbiwgYnIgKyAxKSkpIHtcbiAgICAgICAgcG9seWdvblJldmVyc2UocG9seWdvbik7XG4gICAgfVxufVxuXG4vKipcbiAqIFJldmVyc2UgdGhlIHZlcnRpY2VzIGluIHRoZSBwb2x5Z29uXG4gKiBAbWV0aG9kIHJldmVyc2VcbiAqL1xuZnVuY3Rpb24gcG9seWdvblJldmVyc2UocG9seWdvbil7XG4gICAgdmFyIHRtcCA9IFtdO1xuICAgIHZhciBOID0gcG9seWdvbi5sZW5ndGg7XG4gICAgZm9yKHZhciBpPTA7IGkhPT1OOyBpKyspe1xuICAgICAgICB0bXAucHVzaChwb2x5Z29uLnBvcCgpKTtcbiAgICB9XG4gICAgZm9yKHZhciBpPTA7IGkhPT1OOyBpKyspe1xuXHRcdHBvbHlnb25baV0gPSB0bXBbaV07XG4gICAgfVxufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgcG9pbnQgaW4gdGhlIHBvbHlnb24gaXMgYSByZWZsZXggcG9pbnRcbiAqIEBtZXRob2QgaXNSZWZsZXhcbiAqIEBwYXJhbSAge051bWJlcn0gIGlcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25Jc1JlZmxleChwb2x5Z29uLCBpKXtcbiAgICByZXR1cm4gaXNSaWdodChwb2x5Z29uQXQocG9seWdvbiwgaSAtIDEpLCBwb2x5Z29uQXQocG9seWdvbiwgaSksIHBvbHlnb25BdChwb2x5Z29uLCBpICsgMSkpO1xufVxuXG52YXIgdG1wTGluZTE9W10sXG4gICAgdG1wTGluZTI9W107XG5cbi8qKlxuICogQ2hlY2sgaWYgdHdvIHZlcnRpY2VzIGluIHRoZSBwb2x5Z29uIGNhbiBzZWUgZWFjaCBvdGhlclxuICogQG1ldGhvZCBjYW5TZWVcbiAqIEBwYXJhbSAge051bWJlcn0gYSBWZXJ0ZXggaW5kZXggMVxuICogQHBhcmFtICB7TnVtYmVyfSBiIFZlcnRleCBpbmRleCAyXG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG5mdW5jdGlvbiBwb2x5Z29uQ2FuU2VlKHBvbHlnb24sIGEsYikge1xuICAgIHZhciBwLCBkaXN0LCBsMT10bXBMaW5lMSwgbDI9dG1wTGluZTI7XG5cbiAgICBpZiAoaXNMZWZ0T24ocG9seWdvbkF0KHBvbHlnb24sIGEgKyAxKSwgcG9seWdvbkF0KHBvbHlnb24sIGEpLCBwb2x5Z29uQXQocG9seWdvbiwgYikpICYmIGlzUmlnaHRPbihwb2x5Z29uQXQocG9seWdvbiwgYSAtIDEpLCBwb2x5Z29uQXQocG9seWdvbiwgYSksIHBvbHlnb25BdChwb2x5Z29uLCBiKSkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBkaXN0ID0gc3FkaXN0KHBvbHlnb25BdChwb2x5Z29uLCBhKSwgcG9seWdvbkF0KHBvbHlnb24sIGIpKTtcbiAgICBmb3IgKHZhciBpID0gMDsgaSAhPT0gcG9seWdvbi5sZW5ndGg7ICsraSkgeyAvLyBmb3IgZWFjaCBlZGdlXG4gICAgICAgIGlmICgoaSArIDEpICUgcG9seWdvbi5sZW5ndGggPT09IGEgfHwgaSA9PT0gYSl7IC8vIGlnbm9yZSBpbmNpZGVudCBlZGdlc1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGlzTGVmdE9uKHBvbHlnb25BdChwb2x5Z29uLCBhKSwgcG9seWdvbkF0KHBvbHlnb24sIGIpLCBwb2x5Z29uQXQocG9seWdvbiwgaSArIDEpKSAmJiBpc1JpZ2h0T24ocG9seWdvbkF0KHBvbHlnb24sIGEpLCBwb2x5Z29uQXQocG9seWdvbiwgYiksIHBvbHlnb25BdChwb2x5Z29uLCBpKSkpIHsgLy8gaWYgZGlhZyBpbnRlcnNlY3RzIGFuIGVkZ2VcbiAgICAgICAgICAgIGwxWzBdID0gcG9seWdvbkF0KHBvbHlnb24sIGEpO1xuICAgICAgICAgICAgbDFbMV0gPSBwb2x5Z29uQXQocG9seWdvbiwgYik7XG4gICAgICAgICAgICBsMlswXSA9IHBvbHlnb25BdChwb2x5Z29uLCBpKTtcbiAgICAgICAgICAgIGwyWzFdID0gcG9seWdvbkF0KHBvbHlnb24sIGkgKyAxKTtcbiAgICAgICAgICAgIHAgPSBsaW5lSW50KGwxLGwyKTtcbiAgICAgICAgICAgIGlmIChzcWRpc3QocG9seWdvbkF0KHBvbHlnb24sIGEpLCBwKSA8IGRpc3QpIHsgLy8gaWYgZWRnZSBpcyBibG9ja2luZyB2aXNpYmlsaXR5IHRvIGJcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqXG4gKiBDb3B5IHRoZSBwb2x5Z29uIGZyb20gdmVydGV4IGkgdG8gdmVydGV4IGouXG4gKiBAbWV0aG9kIGNvcHlcbiAqIEBwYXJhbSAge051bWJlcn0gaVxuICogQHBhcmFtICB7TnVtYmVyfSBqXG4gKiBAcGFyYW0gIHtQb2x5Z29ufSBbdGFyZ2V0UG9seV0gICBPcHRpb25hbCB0YXJnZXQgcG9seWdvbiB0byBzYXZlIGluLlxuICogQHJldHVybiB7UG9seWdvbn0gICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBjb3B5LlxuICovXG5mdW5jdGlvbiBwb2x5Z29uQ29weShwb2x5Z29uLCBpLGosdGFyZ2V0UG9seSl7XG4gICAgdmFyIHAgPSB0YXJnZXRQb2x5IHx8IFtdO1xuICAgIHBvbHlnb25DbGVhcihwKTtcbiAgICBpZiAoaSA8IGopIHtcbiAgICAgICAgLy8gSW5zZXJ0IGFsbCB2ZXJ0aWNlcyBmcm9tIGkgdG8galxuICAgICAgICBmb3IodmFyIGs9aTsgazw9ajsgaysrKXtcbiAgICAgICAgICAgIHAucHVzaChwb2x5Z29uW2tdKTtcbiAgICAgICAgfVxuXG4gICAgfSBlbHNlIHtcblxuICAgICAgICAvLyBJbnNlcnQgdmVydGljZXMgMCB0byBqXG4gICAgICAgIGZvcih2YXIgaz0wOyBrPD1qOyBrKyspe1xuICAgICAgICAgICAgcC5wdXNoKHBvbHlnb25ba10pO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSW5zZXJ0IHZlcnRpY2VzIGkgdG8gZW5kXG4gICAgICAgIGZvcih2YXIgaz1pOyBrPHBvbHlnb24ubGVuZ3RoOyBrKyspe1xuICAgICAgICAgICAgcC5wdXNoKHBvbHlnb25ba10pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHA7XG59XG5cbi8qKlxuICogRGVjb21wb3NlcyB0aGUgcG9seWdvbiBpbnRvIGNvbnZleCBwaWVjZXMuIFJldHVybnMgYSBsaXN0IG9mIGVkZ2VzIFtbcDEscDJdLFtwMixwM10sLi4uXSB0aGF0IGN1dHMgdGhlIHBvbHlnb24uXG4gKiBOb3RlIHRoYXQgdGhpcyBhbGdvcml0aG0gaGFzIGNvbXBsZXhpdHkgTyhOXjQpIGFuZCB3aWxsIGJlIHZlcnkgc2xvdyBmb3IgcG9seWdvbnMgd2l0aCBtYW55IHZlcnRpY2VzLlxuICogQG1ldGhvZCBnZXRDdXRFZGdlc1xuICogQHJldHVybiB7QXJyYXl9XG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25HZXRDdXRFZGdlcyhwb2x5Z29uKSB7XG4gICAgdmFyIG1pbj1bXSwgdG1wMT1bXSwgdG1wMj1bXSwgdG1wUG9seSA9IFtdO1xuICAgIHZhciBuRGlhZ3MgPSBOdW1iZXIuTUFYX1ZBTFVFO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwb2x5Z29uLmxlbmd0aDsgKytpKSB7XG4gICAgICAgIGlmIChwb2x5Z29uSXNSZWZsZXgocG9seWdvbiwgaSkpIHtcbiAgICAgICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgcG9seWdvbi5sZW5ndGg7ICsraikge1xuICAgICAgICAgICAgICAgIGlmIChwb2x5Z29uQ2FuU2VlKHBvbHlnb24sIGksIGopKSB7XG4gICAgICAgICAgICAgICAgICAgIHRtcDEgPSBwb2x5Z29uR2V0Q3V0RWRnZXMocG9seWdvbkNvcHkocG9seWdvbiwgaSwgaiwgdG1wUG9seSkpO1xuICAgICAgICAgICAgICAgICAgICB0bXAyID0gcG9seWdvbkdldEN1dEVkZ2VzKHBvbHlnb25Db3B5KHBvbHlnb24sIGosIGksIHRtcFBvbHkpKTtcblxuICAgICAgICAgICAgICAgICAgICBmb3IodmFyIGs9MDsgazx0bXAyLmxlbmd0aDsgaysrKXtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRtcDEucHVzaCh0bXAyW2tdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICh0bXAxLmxlbmd0aCA8IG5EaWFncykge1xuICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gdG1wMTtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5EaWFncyA9IHRtcDEubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgICAgbWluLnB1c2goW3BvbHlnb25BdChwb2x5Z29uLCBpKSwgcG9seWdvbkF0KHBvbHlnb24sIGopXSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbWluO1xufVxuXG4vKipcbiAqIERlY29tcG9zZXMgdGhlIHBvbHlnb24gaW50byBvbmUgb3IgbW9yZSBjb252ZXggc3ViLVBvbHlnb25zLlxuICogQG1ldGhvZCBkZWNvbXBcbiAqIEByZXR1cm4ge0FycmF5fSBBbiBhcnJheSBvciBQb2x5Z29uIG9iamVjdHMuXG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25EZWNvbXAocG9seWdvbil7XG4gICAgdmFyIGVkZ2VzID0gcG9seWdvbkdldEN1dEVkZ2VzKHBvbHlnb24pO1xuICAgIGlmKGVkZ2VzLmxlbmd0aCA+IDApe1xuICAgICAgICByZXR1cm4gcG9seWdvblNsaWNlKHBvbHlnb24sIGVkZ2VzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gW3BvbHlnb25dO1xuICAgIH1cbn1cblxuLyoqXG4gKiBTbGljZXMgdGhlIHBvbHlnb24gZ2l2ZW4gb25lIG9yIG1vcmUgY3V0IGVkZ2VzLiBJZiBnaXZlbiBvbmUsIHRoaXMgZnVuY3Rpb24gd2lsbCByZXR1cm4gdHdvIHBvbHlnb25zIChmYWxzZSBvbiBmYWlsdXJlKS4gSWYgbWFueSwgYW4gYXJyYXkgb2YgcG9seWdvbnMuXG4gKiBAbWV0aG9kIHNsaWNlXG4gKiBAcGFyYW0ge0FycmF5fSBjdXRFZGdlcyBBIGxpc3Qgb2YgZWRnZXMsIGFzIHJldHVybmVkIGJ5IC5nZXRDdXRFZGdlcygpXG4gKiBAcmV0dXJuIHtBcnJheX1cbiAqL1xuZnVuY3Rpb24gcG9seWdvblNsaWNlKHBvbHlnb24sIGN1dEVkZ2VzKXtcbiAgICBpZihjdXRFZGdlcy5sZW5ndGggPT09IDApe1xuXHRcdHJldHVybiBbcG9seWdvbl07XG4gICAgfVxuICAgIGlmKGN1dEVkZ2VzIGluc3RhbmNlb2YgQXJyYXkgJiYgY3V0RWRnZXMubGVuZ3RoICYmIGN1dEVkZ2VzWzBdIGluc3RhbmNlb2YgQXJyYXkgJiYgY3V0RWRnZXNbMF0ubGVuZ3RoPT09MiAmJiBjdXRFZGdlc1swXVswXSBpbnN0YW5jZW9mIEFycmF5KXtcblxuICAgICAgICB2YXIgcG9seXMgPSBbcG9seWdvbl07XG5cbiAgICAgICAgZm9yKHZhciBpPTA7IGk8Y3V0RWRnZXMubGVuZ3RoOyBpKyspe1xuICAgICAgICAgICAgdmFyIGN1dEVkZ2UgPSBjdXRFZGdlc1tpXTtcbiAgICAgICAgICAgIC8vIEN1dCBhbGwgcG9seXNcbiAgICAgICAgICAgIGZvcih2YXIgaj0wOyBqPHBvbHlzLmxlbmd0aDsgaisrKXtcbiAgICAgICAgICAgICAgICB2YXIgcG9seSA9IHBvbHlzW2pdO1xuICAgICAgICAgICAgICAgIHZhciByZXN1bHQgPSBwb2x5Z29uU2xpY2UocG9seSwgY3V0RWRnZSk7XG4gICAgICAgICAgICAgICAgaWYocmVzdWx0KXtcbiAgICAgICAgICAgICAgICAgICAgLy8gRm91bmQgcG9seSEgQ3V0IGFuZCBxdWl0XG4gICAgICAgICAgICAgICAgICAgIHBvbHlzLnNwbGljZShqLDEpO1xuICAgICAgICAgICAgICAgICAgICBwb2x5cy5wdXNoKHJlc3VsdFswXSxyZXN1bHRbMV0pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcG9seXM7XG4gICAgfSBlbHNlIHtcblxuICAgICAgICAvLyBXYXMgZ2l2ZW4gb25lIGVkZ2VcbiAgICAgICAgdmFyIGN1dEVkZ2UgPSBjdXRFZGdlcztcbiAgICAgICAgdmFyIGkgPSBwb2x5Z29uLmluZGV4T2YoY3V0RWRnZVswXSk7XG4gICAgICAgIHZhciBqID0gcG9seWdvbi5pbmRleE9mKGN1dEVkZ2VbMV0pO1xuXG4gICAgICAgIGlmKGkgIT09IC0xICYmIGogIT09IC0xKXtcbiAgICAgICAgICAgIHJldHVybiBbcG9seWdvbkNvcHkocG9seWdvbiwgaSxqKSxcbiAgICAgICAgICAgICAgICAgICAgcG9seWdvbkNvcHkocG9seWdvbiwgaixpKV07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICogQ2hlY2tzIHRoYXQgdGhlIGxpbmUgc2VnbWVudHMgb2YgdGhpcyBwb2x5Z29uIGRvIG5vdCBpbnRlcnNlY3QgZWFjaCBvdGhlci5cbiAqIEBtZXRob2QgaXNTaW1wbGVcbiAqIEBwYXJhbSAge0FycmF5fSBwYXRoIEFuIGFycmF5IG9mIHZlcnRpY2VzIGUuZy4gW1swLDBdLFswLDFdLC4uLl1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKiBAdG9kbyBTaG91bGQgaXQgY2hlY2sgYWxsIHNlZ21lbnRzIHdpdGggYWxsIG90aGVycz9cbiAqL1xuZnVuY3Rpb24gcG9seWdvbklzU2ltcGxlKHBvbHlnb24pe1xuICAgIHZhciBwYXRoID0gcG9seWdvbiwgaTtcbiAgICAvLyBDaGVja1xuICAgIGZvcihpPTA7IGk8cGF0aC5sZW5ndGgtMTsgaSsrKXtcbiAgICAgICAgZm9yKHZhciBqPTA7IGo8aS0xOyBqKyspe1xuICAgICAgICAgICAgaWYobGluZVNlZ21lbnRzSW50ZXJzZWN0KHBhdGhbaV0sIHBhdGhbaSsxXSwgcGF0aFtqXSwgcGF0aFtqKzFdICkpe1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIENoZWNrIHRoZSBzZWdtZW50IGJldHdlZW4gdGhlIGxhc3QgYW5kIHRoZSBmaXJzdCBwb2ludCB0byBhbGwgb3RoZXJzXG4gICAgZm9yKGk9MTsgaTxwYXRoLmxlbmd0aC0yOyBpKyspe1xuICAgICAgICBpZihsaW5lU2VnbWVudHNJbnRlcnNlY3QocGF0aFswXSwgcGF0aFtwYXRoLmxlbmd0aC0xXSwgcGF0aFtpXSwgcGF0aFtpKzFdICkpe1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWU7XG59XG5cbmZ1bmN0aW9uIGdldEludGVyc2VjdGlvblBvaW50KHAxLCBwMiwgcTEsIHEyLCBkZWx0YSl7XG5cdGRlbHRhID0gZGVsdGEgfHwgMDtcblx0dmFyIGExID0gcDJbMV0gLSBwMVsxXTtcblx0dmFyIGIxID0gcDFbMF0gLSBwMlswXTtcblx0dmFyIGMxID0gKGExICogcDFbMF0pICsgKGIxICogcDFbMV0pO1xuXHR2YXIgYTIgPSBxMlsxXSAtIHExWzFdO1xuXHR2YXIgYjIgPSBxMVswXSAtIHEyWzBdO1xuXHR2YXIgYzIgPSAoYTIgKiBxMVswXSkgKyAoYjIgKiBxMVsxXSk7XG5cdHZhciBkZXQgPSAoYTEgKiBiMikgLSAoYTIgKiBiMSk7XG5cblx0aWYoIXNjYWxhcl9lcShkZXQsMCxkZWx0YSkpe1xuXHRcdHJldHVybiBbKChiMiAqIGMxKSAtIChiMSAqIGMyKSkgLyBkZXQsICgoYTEgKiBjMikgLSAoYTIgKiBjMSkpIC8gZGV0XTtcblx0fSBlbHNlIHtcblx0XHRyZXR1cm4gWzAsMF07XG4gICAgfVxufVxuXG4vKipcbiAqIFF1aWNrbHkgZGVjb21wb3NlIHRoZSBQb2x5Z29uIGludG8gY29udmV4IHN1Yi1wb2x5Z29ucy5cbiAqIEBtZXRob2QgcXVpY2tEZWNvbXBcbiAqIEBwYXJhbSAge0FycmF5fSByZXN1bHRcbiAqIEBwYXJhbSAge0FycmF5fSBbcmVmbGV4VmVydGljZXNdXG4gKiBAcGFyYW0gIHtBcnJheX0gW3N0ZWluZXJQb2ludHNdXG4gKiBAcGFyYW0gIHtOdW1iZXJ9IFtkZWx0YV1cbiAqIEBwYXJhbSAge051bWJlcn0gW21heGxldmVsXVxuICogQHBhcmFtICB7TnVtYmVyfSBbbGV2ZWxdXG4gKiBAcmV0dXJuIHtBcnJheX1cbiAqL1xuZnVuY3Rpb24gcG9seWdvblF1aWNrRGVjb21wKHBvbHlnb24sIHJlc3VsdCxyZWZsZXhWZXJ0aWNlcyxzdGVpbmVyUG9pbnRzLGRlbHRhLG1heGxldmVsLGxldmVsKXtcbiAgICBtYXhsZXZlbCA9IG1heGxldmVsIHx8IDEwMDtcbiAgICBsZXZlbCA9IGxldmVsIHx8IDA7XG4gICAgZGVsdGEgPSBkZWx0YSB8fCAyNTtcbiAgICByZXN1bHQgPSB0eXBlb2YocmVzdWx0KSE9PVwidW5kZWZpbmVkXCIgPyByZXN1bHQgOiBbXTtcbiAgICByZWZsZXhWZXJ0aWNlcyA9IHJlZmxleFZlcnRpY2VzIHx8IFtdO1xuICAgIHN0ZWluZXJQb2ludHMgPSBzdGVpbmVyUG9pbnRzIHx8IFtdO1xuXG4gICAgdmFyIHVwcGVySW50PVswLDBdLCBsb3dlckludD1bMCwwXSwgcD1bMCwwXTsgLy8gUG9pbnRzXG4gICAgdmFyIHVwcGVyRGlzdD0wLCBsb3dlckRpc3Q9MCwgZD0wLCBjbG9zZXN0RGlzdD0wOyAvLyBzY2FsYXJzXG4gICAgdmFyIHVwcGVySW5kZXg9MCwgbG93ZXJJbmRleD0wLCBjbG9zZXN0SW5kZXg9MDsgLy8gSW50ZWdlcnNcbiAgICB2YXIgbG93ZXJQb2x5PVtdLCB1cHBlclBvbHk9W107IC8vIHBvbHlnb25zXG4gICAgdmFyIHBvbHkgPSBwb2x5Z29uLFxuICAgICAgICB2ID0gcG9seWdvbjtcblxuICAgIGlmKHYubGVuZ3RoIDwgMyl7XG5cdFx0cmV0dXJuIHJlc3VsdDtcbiAgICB9XG5cbiAgICBsZXZlbCsrO1xuICAgIGlmKGxldmVsID4gbWF4bGV2ZWwpe1xuICAgICAgICBjb25zb2xlLndhcm4oXCJxdWlja0RlY29tcDogbWF4IGxldmVsIChcIittYXhsZXZlbCtcIikgcmVhY2hlZC5cIik7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwb2x5Z29uLmxlbmd0aDsgKytpKSB7XG4gICAgICAgIGlmIChwb2x5Z29uSXNSZWZsZXgocG9seSwgaSkpIHtcbiAgICAgICAgICAgIHJlZmxleFZlcnRpY2VzLnB1c2gocG9seVtpXSk7XG4gICAgICAgICAgICB1cHBlckRpc3QgPSBsb3dlckRpc3QgPSBOdW1iZXIuTUFYX1ZBTFVFO1xuXG5cbiAgICAgICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgcG9seWdvbi5sZW5ndGg7ICsraikge1xuICAgICAgICAgICAgICAgIGlmIChpc0xlZnQocG9seWdvbkF0KHBvbHksIGkgLSAxKSwgcG9seWdvbkF0KHBvbHksIGkpLCBwb2x5Z29uQXQocG9seSwgaikpICYmIGlzUmlnaHRPbihwb2x5Z29uQXQocG9seSwgaSAtIDEpLCBwb2x5Z29uQXQocG9seSwgaSksIHBvbHlnb25BdChwb2x5LCBqIC0gMSkpKSB7IC8vIGlmIGxpbmUgaW50ZXJzZWN0cyB3aXRoIGFuIGVkZ2VcbiAgICAgICAgICAgICAgICAgICAgcCA9IGdldEludGVyc2VjdGlvblBvaW50KHBvbHlnb25BdChwb2x5LCBpIC0gMSksIHBvbHlnb25BdChwb2x5LCBpKSwgcG9seWdvbkF0KHBvbHksIGopLCBwb2x5Z29uQXQocG9seSwgaiAtIDEpKTsgLy8gZmluZCB0aGUgcG9pbnQgb2YgaW50ZXJzZWN0aW9uXG4gICAgICAgICAgICAgICAgICAgIGlmIChpc1JpZ2h0KHBvbHlnb25BdChwb2x5LCBpICsgMSksIHBvbHlnb25BdChwb2x5LCBpKSwgcCkpIHsgLy8gbWFrZSBzdXJlIGl0J3MgaW5zaWRlIHRoZSBwb2x5XG4gICAgICAgICAgICAgICAgICAgICAgICBkID0gc3FkaXN0KHBvbHlbaV0sIHApO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGQgPCBsb3dlckRpc3QpIHsgLy8ga2VlcCBvbmx5IHRoZSBjbG9zZXN0IGludGVyc2VjdGlvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvd2VyRGlzdCA9IGQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJJbnQgPSBwO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvd2VySW5kZXggPSBqO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChpc0xlZnQocG9seWdvbkF0KHBvbHksIGkgKyAxKSwgcG9seWdvbkF0KHBvbHksIGkpLCBwb2x5Z29uQXQocG9seSwgaiArIDEpKSAmJiBpc1JpZ2h0T24ocG9seWdvbkF0KHBvbHksIGkgKyAxKSwgcG9seWdvbkF0KHBvbHksIGkpLCBwb2x5Z29uQXQocG9seSwgaikpKSB7XG4gICAgICAgICAgICAgICAgICAgIHAgPSBnZXRJbnRlcnNlY3Rpb25Qb2ludChwb2x5Z29uQXQocG9seSwgaSArIDEpLCBwb2x5Z29uQXQocG9seSwgaSksIHBvbHlnb25BdChwb2x5LCBqKSwgcG9seWdvbkF0KHBvbHksIGogKyAxKSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChpc0xlZnQocG9seWdvbkF0KHBvbHksIGkgLSAxKSwgcG9seWdvbkF0KHBvbHksIGkpLCBwKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZCA9IHNxZGlzdChwb2x5W2ldLCBwKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkIDwgdXBwZXJEaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBwZXJEaXN0ID0gZDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cHBlckludCA9IHA7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBwZXJJbmRleCA9IGo7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGlmIHRoZXJlIGFyZSBubyB2ZXJ0aWNlcyB0byBjb25uZWN0IHRvLCBjaG9vc2UgYSBwb2ludCBpbiB0aGUgbWlkZGxlXG4gICAgICAgICAgICBpZiAobG93ZXJJbmRleCA9PT0gKHVwcGVySW5kZXggKyAxKSAlIHBvbHlnb24ubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhcIkNhc2UgMTogVmVydGV4KFwiK2krXCIpLCBsb3dlckluZGV4KFwiK2xvd2VySW5kZXgrXCIpLCB1cHBlckluZGV4KFwiK3VwcGVySW5kZXgrXCIpLCBwb2x5LnNpemUoXCIrcG9seWdvbi5sZW5ndGgrXCIpXCIpO1xuICAgICAgICAgICAgICAgIHBbMF0gPSAobG93ZXJJbnRbMF0gKyB1cHBlckludFswXSkgLyAyO1xuICAgICAgICAgICAgICAgIHBbMV0gPSAobG93ZXJJbnRbMV0gKyB1cHBlckludFsxXSkgLyAyO1xuICAgICAgICAgICAgICAgIHN0ZWluZXJQb2ludHMucHVzaChwKTtcblxuICAgICAgICAgICAgICAgIGlmIChpIDwgdXBwZXJJbmRleCkge1xuICAgICAgICAgICAgICAgICAgICAvL2xvd2VyUG9seS5pbnNlcnQobG93ZXJQb2x5LmVuZCgpLCBwb2x5LmJlZ2luKCkgKyBpLCBwb2x5LmJlZ2luKCkgKyB1cHBlckluZGV4ICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIHBvbHlnb25BcHBlbmQobG93ZXJQb2x5LCBwb2x5LCBpLCB1cHBlckluZGV4KzEpO1xuICAgICAgICAgICAgICAgICAgICBsb3dlclBvbHkucHVzaChwKTtcbiAgICAgICAgICAgICAgICAgICAgdXBwZXJQb2x5LnB1c2gocCk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChsb3dlckluZGV4ICE9PSAwKXtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vdXBwZXJQb2x5Lmluc2VydCh1cHBlclBvbHkuZW5kKCksIHBvbHkuYmVnaW4oKSArIGxvd2VySW5kZXgsIHBvbHkuZW5kKCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgcG9seWdvbkFwcGVuZCh1cHBlclBvbHksIHBvbHksbG93ZXJJbmRleCxwb2x5Lmxlbmd0aCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLy91cHBlclBvbHkuaW5zZXJ0KHVwcGVyUG9seS5lbmQoKSwgcG9seS5iZWdpbigpLCBwb2x5LmJlZ2luKCkgKyBpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIHBvbHlnb25BcHBlbmQodXBwZXJQb2x5LCBwb2x5LDAsaSsxKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBpZiAoaSAhPT0gMCl7XG4gICAgICAgICAgICAgICAgICAgICAgICAvL2xvd2VyUG9seS5pbnNlcnQobG93ZXJQb2x5LmVuZCgpLCBwb2x5LmJlZ2luKCkgKyBpLCBwb2x5LmVuZCgpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBvbHlnb25BcHBlbmQobG93ZXJQb2x5LCBwb2x5LGkscG9seS5sZW5ndGgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIC8vbG93ZXJQb2x5Lmluc2VydChsb3dlclBvbHkuZW5kKCksIHBvbHkuYmVnaW4oKSwgcG9seS5iZWdpbigpICsgdXBwZXJJbmRleCArIDEpO1xuICAgICAgICAgICAgICAgICAgICBwb2x5Z29uQXBwZW5kKGxvd2VyUG9seSwgcG9seSwwLHVwcGVySW5kZXgrMSk7XG4gICAgICAgICAgICAgICAgICAgIGxvd2VyUG9seS5wdXNoKHApO1xuICAgICAgICAgICAgICAgICAgICB1cHBlclBvbHkucHVzaChwKTtcbiAgICAgICAgICAgICAgICAgICAgLy91cHBlclBvbHkuaW5zZXJ0KHVwcGVyUG9seS5lbmQoKSwgcG9seS5iZWdpbigpICsgbG93ZXJJbmRleCwgcG9seS5iZWdpbigpICsgaSArIDEpO1xuICAgICAgICAgICAgICAgICAgICBwb2x5Z29uQXBwZW5kKHVwcGVyUG9seSwgcG9seSxsb3dlckluZGV4LGkrMSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBjb25uZWN0IHRvIHRoZSBjbG9zZXN0IHBvaW50IHdpdGhpbiB0aGUgdHJpYW5nbGVcbiAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKFwiQ2FzZSAyOiBWZXJ0ZXgoXCIraStcIiksIGNsb3Nlc3RJbmRleChcIitjbG9zZXN0SW5kZXgrXCIpLCBwb2x5LnNpemUoXCIrcG9seWdvbi5sZW5ndGgrXCIpXFxuXCIpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGxvd2VySW5kZXggPiB1cHBlckluZGV4KSB7XG4gICAgICAgICAgICAgICAgICAgIHVwcGVySW5kZXggKz0gcG9seWdvbi5sZW5ndGg7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNsb3Nlc3REaXN0ID0gTnVtYmVyLk1BWF9WQUxVRTtcblxuICAgICAgICAgICAgICAgIGlmKHVwcGVySW5kZXggPCBsb3dlckluZGV4KXtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gbG93ZXJJbmRleDsgaiA8PSB1cHBlckluZGV4OyArK2opIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGlzTGVmdE9uKHBvbHlnb25BdChwb2x5LCBpIC0gMSksIHBvbHlnb25BdChwb2x5LCBpKSwgcG9seWdvbkF0KHBvbHksIGopKSAmJiBpc1JpZ2h0T24ocG9seWdvbkF0KHBvbHksIGkgKyAxKSwgcG9seWdvbkF0KHBvbHksIGkpLCBwb2x5Z29uQXQocG9seSwgaikpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBkID0gc3FkaXN0KHBvbHlnb25BdChwb2x5LCBpKSwgcG9seWdvbkF0KHBvbHksIGopKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkIDwgY2xvc2VzdERpc3QpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9zZXN0RGlzdCA9IGQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xvc2VzdEluZGV4ID0gaiAlIHBvbHlnb24ubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGkgPCBjbG9zZXN0SW5kZXgpIHtcbiAgICAgICAgICAgICAgICAgICAgcG9seWdvbkFwcGVuZChsb3dlclBvbHksIHBvbHksaSxjbG9zZXN0SW5kZXgrMSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChjbG9zZXN0SW5kZXggIT09IDApe1xuICAgICAgICAgICAgICAgICAgICAgICAgcG9seWdvbkFwcGVuZCh1cHBlclBvbHksIHBvbHksY2xvc2VzdEluZGV4LHYubGVuZ3RoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBwb2x5Z29uQXBwZW5kKHVwcGVyUG9seSwgcG9seSwwLGkrMSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGkgIT09IDApe1xuICAgICAgICAgICAgICAgICAgICAgICAgcG9seWdvbkFwcGVuZChsb3dlclBvbHksIHBvbHksaSx2Lmxlbmd0aCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcG9seWdvbkFwcGVuZChsb3dlclBvbHksIHBvbHksMCxjbG9zZXN0SW5kZXgrMSk7XG4gICAgICAgICAgICAgICAgICAgIHBvbHlnb25BcHBlbmQodXBwZXJQb2x5LCBwb2x5LGNsb3Nlc3RJbmRleCxpKzEpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gc29sdmUgc21hbGxlc3QgcG9seSBmaXJzdFxuICAgICAgICAgICAgaWYgKGxvd2VyUG9seS5sZW5ndGggPCB1cHBlclBvbHkubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgcG9seWdvblF1aWNrRGVjb21wKGxvd2VyUG9seSxyZXN1bHQscmVmbGV4VmVydGljZXMsc3RlaW5lclBvaW50cyxkZWx0YSxtYXhsZXZlbCxsZXZlbCk7XG4gICAgICAgICAgICAgICAgcG9seWdvblF1aWNrRGVjb21wKHVwcGVyUG9seSxyZXN1bHQscmVmbGV4VmVydGljZXMsc3RlaW5lclBvaW50cyxkZWx0YSxtYXhsZXZlbCxsZXZlbCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHBvbHlnb25RdWlja0RlY29tcCh1cHBlclBvbHkscmVzdWx0LHJlZmxleFZlcnRpY2VzLHN0ZWluZXJQb2ludHMsZGVsdGEsbWF4bGV2ZWwsbGV2ZWwpO1xuICAgICAgICAgICAgICAgIHBvbHlnb25RdWlja0RlY29tcChsb3dlclBvbHkscmVzdWx0LHJlZmxleFZlcnRpY2VzLHN0ZWluZXJQb2ludHMsZGVsdGEsbWF4bGV2ZWwsbGV2ZWwpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9XG4gICAgfVxuICAgIHJlc3VsdC5wdXNoKHBvbHlnb24pO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBSZW1vdmUgY29sbGluZWFyIHBvaW50cyBpbiB0aGUgcG9seWdvbi5cbiAqIEBtZXRob2QgcmVtb3ZlQ29sbGluZWFyUG9pbnRzXG4gKiBAcGFyYW0gIHtOdW1iZXJ9IFtwcmVjaXNpb25dIFRoZSB0aHJlc2hvbGQgYW5nbGUgdG8gdXNlIHdoZW4gZGV0ZXJtaW5pbmcgd2hldGhlciB0d28gZWRnZXMgYXJlIGNvbGxpbmVhci4gVXNlIHplcm8gZm9yIGZpbmVzdCBwcmVjaXNpb24uXG4gKiBAcmV0dXJuIHtOdW1iZXJ9ICAgICAgICAgICBUaGUgbnVtYmVyIG9mIHBvaW50cyByZW1vdmVkXG4gKi9cbmZ1bmN0aW9uIHBvbHlnb25SZW1vdmVDb2xsaW5lYXJQb2ludHMocG9seWdvbiwgcHJlY2lzaW9uKXtcbiAgICB2YXIgbnVtID0gMDtcbiAgICBmb3IodmFyIGk9cG9seWdvbi5sZW5ndGgtMTsgcG9seWdvbi5sZW5ndGg+MyAmJiBpPj0wOyAtLWkpe1xuICAgICAgICBpZihjb2xsaW5lYXIocG9seWdvbkF0KHBvbHlnb24sIGktMSkscG9seWdvbkF0KHBvbHlnb24sIGkpLHBvbHlnb25BdChwb2x5Z29uLCBpKzEpLHByZWNpc2lvbikpe1xuICAgICAgICAgICAgLy8gUmVtb3ZlIHRoZSBtaWRkbGUgcG9pbnRcbiAgICAgICAgICAgIHBvbHlnb24uc3BsaWNlKGklcG9seWdvbi5sZW5ndGgsMSk7XG4gICAgICAgICAgICBudW0rKztcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVtO1xufVxuXG4vKipcbiAqIENoZWNrIGlmIHR3byBzY2FsYXJzIGFyZSBlcXVhbFxuICogQHN0YXRpY1xuICogQG1ldGhvZCBlcVxuICogQHBhcmFtICB7TnVtYmVyfSBhXG4gKiBAcGFyYW0gIHtOdW1iZXJ9IGJcbiAqIEBwYXJhbSAge051bWJlcn0gW3ByZWNpc2lvbl1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbmZ1bmN0aW9uIHNjYWxhcl9lcShhLGIscHJlY2lzaW9uKXtcbiAgICBwcmVjaXNpb24gPSBwcmVjaXNpb24gfHwgMDtcbiAgICByZXR1cm4gTWF0aC5hYnMoYS1iKSA8IHByZWNpc2lvbjtcbn1cbiJdfQ== | |
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})({"matter-js":[function(require,module,exports){ | |
(function (global){ | |
/** | |
* matter-js 0.13.0 by @liabru 2017-07-06 | |
* http://brm.io/matter-js/ | |
* License MIT | |
*/ | |
/** | |
* The MIT License (MIT) | |
* | |
* Copyright (c) Liam Brummitt and contributors. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
*/ | |
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Matter = f()}})(function(){var define,module,exports;return (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(_dereq_,module,exports){ | |
/** | |
* The `Matter.Body` module contains methods for creating and manipulating body models. | |
* A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`. | |
* Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the module `Matter.Bodies`. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* @class Body | |
*/ | |
var Body = {}; | |
module.exports = Body; | |
var Vertices = _dereq_('../geometry/Vertices'); | |
var Vector = _dereq_('../geometry/Vector'); | |
var Sleeping = _dereq_('../core/Sleeping'); | |
var Render = _dereq_('../render/Render'); | |
var Common = _dereq_('../core/Common'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
var Axes = _dereq_('../geometry/Axes'); | |
(function() { | |
Body._inertiaScale = 4; | |
Body._nextCollidingGroupId = 1; | |
Body._nextNonCollidingGroupId = -1; | |
Body._nextCategory = 0x0001; | |
/** | |
* Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults. | |
* All properties have default values, and many are pre-calculated automatically based on other properties. | |
* Vertices must be specified in clockwise order. | |
* See the properties section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @param {} options | |
* @return {body} body | |
*/ | |
Body.create = function(options) { | |
var defaults = { | |
id: Common.nextId(), | |
type: 'body', | |
label: 'Body', | |
parts: [], | |
plugin: {}, | |
angle: 0, | |
vertices: Vertices.fromPath('L 0 0 L 40 0 L 40 40 L 0 40'), | |
position: { x: 0, y: 0 }, | |
force: { x: 0, y: 0 }, | |
torque: 0, | |
positionImpulse: { x: 0, y: 0 }, | |
constraintImpulse: { x: 0, y: 0, angle: 0 }, | |
totalContacts: 0, | |
speed: 0, | |
angularSpeed: 0, | |
velocity: { x: 0, y: 0 }, | |
angularVelocity: 0, | |
isSensor: false, | |
isStatic: false, | |
isSleeping: false, | |
motion: 0, | |
sleepThreshold: 60, | |
density: 0.001, | |
restitution: 0, | |
friction: 0.1, | |
frictionStatic: 0.5, | |
frictionAir: 0.01, | |
collisionFilter: { | |
category: 0x0001, | |
mask: 0xFFFFFFFF, | |
group: 0 | |
}, | |
slop: 0.05, | |
timeScale: 1, | |
render: { | |
visible: true, | |
opacity: 1, | |
sprite: { | |
xScale: 1, | |
yScale: 1, | |
xOffset: 0, | |
yOffset: 0 | |
}, | |
lineWidth: 0 | |
} | |
}; | |
var body = Common.extend(defaults, options); | |
_initProperties(body, options); | |
return body; | |
}; | |
/** | |
* Returns the next unique group index for which bodies will collide. | |
* If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide. | |
* See `body.collisionFilter` for more information. | |
* @method nextGroup | |
* @param {bool} [isNonColliding=false] | |
* @return {Number} Unique group index | |
*/ | |
Body.nextGroup = function(isNonColliding) { | |
if (isNonColliding) | |
return Body._nextNonCollidingGroupId--; | |
return Body._nextCollidingGroupId++; | |
}; | |
/** | |
* Returns the next unique category bitfield (starting after the initial default category `0x0001`). | |
* There are 32 available. See `body.collisionFilter` for more information. | |
* @method nextCategory | |
* @return {Number} Unique category bitfield | |
*/ | |
Body.nextCategory = function() { | |
Body._nextCategory = Body._nextCategory << 1; | |
return Body._nextCategory; | |
}; | |
/** | |
* Initialises body properties. | |
* @method _initProperties | |
* @private | |
* @param {body} body | |
* @param {} [options] | |
*/ | |
var _initProperties = function(body, options) { | |
options = options || {}; | |
// init required properties (order is important) | |
Body.set(body, { | |
bounds: body.bounds || Bounds.create(body.vertices), | |
positionPrev: body.positionPrev || Vector.clone(body.position), | |
anglePrev: body.anglePrev || body.angle, | |
vertices: body.vertices, | |
parts: body.parts || [body], | |
isStatic: body.isStatic, | |
isSleeping: body.isSleeping, | |
parent: body.parent || body | |
}); | |
Vertices.rotate(body.vertices, body.angle, body.position); | |
Axes.rotate(body.axes, body.angle); | |
Bounds.update(body.bounds, body.vertices, body.velocity); | |
// allow options to override the automatically calculated properties | |
Body.set(body, { | |
axes: options.axes || body.axes, | |
area: options.area || body.area, | |
mass: options.mass || body.mass, | |
inertia: options.inertia || body.inertia | |
}); | |
// render properties | |
var defaultFillStyle = (body.isStatic ? '#2e2b44' : Common.choose(['#006BA6', '#0496FF', '#FFBC42', '#D81159', '#8F2D56'])), | |
defaultStrokeStyle = '#000'; | |
body.render.fillStyle = body.render.fillStyle || defaultFillStyle; | |
body.render.strokeStyle = body.render.strokeStyle || defaultStrokeStyle; | |
body.render.sprite.xOffset += -(body.bounds.min.x - body.position.x) / (body.bounds.max.x - body.bounds.min.x); | |
body.render.sprite.yOffset += -(body.bounds.min.y - body.position.y) / (body.bounds.max.y - body.bounds.min.y); | |
}; | |
/** | |
* Given a property and a value (or map of), sets the property(s) on the body, using the appropriate setter functions if they exist. | |
* Prefer to use the actual setter functions in performance critical situations. | |
* @method set | |
* @param {body} body | |
* @param {} settings A property name (or map of properties and values) to set on the body. | |
* @param {} value The value to set if `settings` is a single property name. | |
*/ | |
Body.set = function(body, settings, value) { | |
var property; | |
if (typeof settings === 'string') { | |
property = settings; | |
settings = {}; | |
settings[property] = value; | |
} | |
for (property in settings) { | |
value = settings[property]; | |
if (!settings.hasOwnProperty(property)) | |
continue; | |
switch (property) { | |
case 'isStatic': | |
Body.setStatic(body, value); | |
break; | |
case 'isSleeping': | |
Sleeping.set(body, value); | |
break; | |
case 'mass': | |
Body.setMass(body, value); | |
break; | |
case 'density': | |
Body.setDensity(body, value); | |
break; | |
case 'inertia': | |
Body.setInertia(body, value); | |
break; | |
case 'vertices': | |
Body.setVertices(body, value); | |
break; | |
case 'position': | |
Body.setPosition(body, value); | |
break; | |
case 'angle': | |
Body.setAngle(body, value); | |
break; | |
case 'velocity': | |
Body.setVelocity(body, value); | |
break; | |
case 'angularVelocity': | |
Body.setAngularVelocity(body, value); | |
break; | |
case 'parts': | |
Body.setParts(body, value); | |
break; | |
default: | |
body[property] = value; | |
} | |
} | |
}; | |
/** | |
* Sets the body as static, including isStatic flag and setting mass and inertia to Infinity. | |
* @method setStatic | |
* @param {body} body | |
* @param {bool} isStatic | |
*/ | |
Body.setStatic = function(body, isStatic) { | |
for (var i = 0; i < body.parts.length; i++) { | |
var part = body.parts[i]; | |
part.isStatic = isStatic; | |
if (isStatic) { | |
part._original = { | |
restitution: part.restitution, | |
friction: part.friction, | |
mass: part.mass, | |
inertia: part.inertia, | |
density: part.density, | |
inverseMass: part.inverseMass, | |
inverseInertia: part.inverseInertia | |
}; | |
part.restitution = 0; | |
part.friction = 1; | |
part.mass = part.inertia = part.density = Infinity; | |
part.inverseMass = part.inverseInertia = 0; | |
part.positionPrev.x = part.position.x; | |
part.positionPrev.y = part.position.y; | |
part.anglePrev = part.angle; | |
part.angularVelocity = 0; | |
part.speed = 0; | |
part.angularSpeed = 0; | |
part.motion = 0; | |
} else if (part._original) { | |
part.restitution = part._original.restitution; | |
part.friction = part._original.friction; | |
part.mass = part._original.mass; | |
part.inertia = part._original.inertia; | |
part.density = part._original.density; | |
part.inverseMass = part._original.inverseMass; | |
part.inverseInertia = part._original.inverseInertia; | |
delete part._original; | |
} | |
} | |
}; | |
/** | |
* Sets the mass of the body. Inverse mass and density are automatically updated to reflect the change. | |
* @method setMass | |
* @param {body} body | |
* @param {number} mass | |
*/ | |
Body.setMass = function(body, mass) { | |
body.mass = mass; | |
body.inverseMass = 1 / body.mass; | |
body.density = body.mass / body.area; | |
}; | |
/** | |
* Sets the density of the body. Mass is automatically updated to reflect the change. | |
* @method setDensity | |
* @param {body} body | |
* @param {number} density | |
*/ | |
Body.setDensity = function(body, density) { | |
Body.setMass(body, density * body.area); | |
body.density = density; | |
}; | |
/** | |
* Sets the moment of inertia (i.e. second moment of area) of the body of the body. | |
* Inverse inertia is automatically updated to reflect the change. Mass is not changed. | |
* @method setInertia | |
* @param {body} body | |
* @param {number} inertia | |
*/ | |
Body.setInertia = function(body, inertia) { | |
body.inertia = inertia; | |
body.inverseInertia = 1 / body.inertia; | |
}; | |
/** | |
* Sets the body's vertices and updates body properties accordingly, including inertia, area and mass (with respect to `body.density`). | |
* Vertices will be automatically transformed to be orientated around their centre of mass as the origin. | |
* They are then automatically translated to world space based on `body.position`. | |
* | |
* The `vertices` argument should be passed as an array of `Matter.Vector` points (or a `Matter.Vertices` array). | |
* Vertices must form a convex hull, concave hulls are not supported. | |
* | |
* @method setVertices | |
* @param {body} body | |
* @param {vector[]} vertices | |
*/ | |
Body.setVertices = function(body, vertices) { | |
// change vertices | |
if (vertices[0].body === body) { | |
body.vertices = vertices; | |
} else { | |
body.vertices = Vertices.create(vertices, body); | |
} | |
// update properties | |
body.axes = Axes.fromVertices(body.vertices); | |
body.area = Vertices.area(body.vertices); | |
Body.setMass(body, body.density * body.area); | |
// orient vertices around the centre of mass at origin (0, 0) | |
var centre = Vertices.centre(body.vertices); | |
Vertices.translate(body.vertices, centre, -1); | |
// update inertia while vertices are at origin (0, 0) | |
Body.setInertia(body, Body._inertiaScale * Vertices.inertia(body.vertices, body.mass)); | |
// update geometry | |
Vertices.translate(body.vertices, body.position); | |
Bounds.update(body.bounds, body.vertices, body.velocity); | |
}; | |
/** | |
* Sets the parts of the `body` and updates mass, inertia and centroid. | |
* Each part will have its parent set to `body`. | |
* By default the convex hull will be automatically computed and set on `body`, unless `autoHull` is set to `false.` | |
* Note that this method will ensure that the first part in `body.parts` will always be the `body`. | |
* @method setParts | |
* @param {body} body | |
* @param [body] parts | |
* @param {bool} [autoHull=true] | |
*/ | |
Body.setParts = function(body, parts, autoHull) { | |
var i; | |
// add all the parts, ensuring that the first part is always the parent body | |
parts = parts.slice(0); | |
body.parts.length = 0; | |
body.parts.push(body); | |
body.parent = body; | |
for (i = 0; i < parts.length; i++) { | |
var part = parts[i]; | |
if (part !== body) { | |
part.parent = body; | |
body.parts.push(part); | |
} | |
} | |
if (body.parts.length === 1) | |
return; | |
autoHull = typeof autoHull !== 'undefined' ? autoHull : true; | |
// find the convex hull of all parts to set on the parent body | |
if (autoHull) { | |
var vertices = []; | |
for (i = 0; i < parts.length; i++) { | |
vertices = vertices.concat(parts[i].vertices); | |
} | |
Vertices.clockwiseSort(vertices); | |
var hull = Vertices.hull(vertices), | |
hullCentre = Vertices.centre(hull); | |
Body.setVertices(body, hull); | |
Vertices.translate(body.vertices, hullCentre); | |
} | |
// sum the properties of all compound parts of the parent body | |
var total = _totalProperties(body); | |
body.area = total.area; | |
body.parent = body; | |
body.position.x = total.centre.x; | |
body.position.y = total.centre.y; | |
body.positionPrev.x = total.centre.x; | |
body.positionPrev.y = total.centre.y; | |
Body.setMass(body, total.mass); | |
Body.setInertia(body, total.inertia); | |
Body.setPosition(body, total.centre); | |
}; | |
/** | |
* Sets the position of the body instantly. Velocity, angle, force etc. are unchanged. | |
* @method setPosition | |
* @param {body} body | |
* @param {vector} position | |
*/ | |
Body.setPosition = function(body, position) { | |
var delta = Vector.sub(position, body.position); | |
body.positionPrev.x += delta.x; | |
body.positionPrev.y += delta.y; | |
for (var i = 0; i < body.parts.length; i++) { | |
var part = body.parts[i]; | |
part.position.x += delta.x; | |
part.position.y += delta.y; | |
Vertices.translate(part.vertices, delta); | |
Bounds.update(part.bounds, part.vertices, body.velocity); | |
} | |
}; | |
/** | |
* Sets the angle of the body instantly. Angular velocity, position, force etc. are unchanged. | |
* @method setAngle | |
* @param {body} body | |
* @param {number} angle | |
*/ | |
Body.setAngle = function(body, angle) { | |
var delta = angle - body.angle; | |
body.anglePrev += delta; | |
for (var i = 0; i < body.parts.length; i++) { | |
var part = body.parts[i]; | |
part.angle += delta; | |
Vertices.rotate(part.vertices, delta, body.position); | |
Axes.rotate(part.axes, delta); | |
Bounds.update(part.bounds, part.vertices, body.velocity); | |
if (i > 0) { | |
Vector.rotateAbout(part.position, delta, body.position, part.position); | |
} | |
} | |
}; | |
/** | |
* Sets the linear velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`. | |
* @method setVelocity | |
* @param {body} body | |
* @param {vector} velocity | |
*/ | |
Body.setVelocity = function(body, velocity) { | |
body.positionPrev.x = body.position.x - velocity.x; | |
body.positionPrev.y = body.position.y - velocity.y; | |
body.velocity.x = velocity.x; | |
body.velocity.y = velocity.y; | |
body.speed = Vector.magnitude(body.velocity); | |
}; | |
/** | |
* Sets the angular velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`. | |
* @method setAngularVelocity | |
* @param {body} body | |
* @param {number} velocity | |
*/ | |
Body.setAngularVelocity = function(body, velocity) { | |
body.anglePrev = body.angle - velocity; | |
body.angularVelocity = velocity; | |
body.angularSpeed = Math.abs(body.angularVelocity); | |
}; | |
/** | |
* Moves a body by a given vector relative to its current position, without imparting any velocity. | |
* @method translate | |
* @param {body} body | |
* @param {vector} translation | |
*/ | |
Body.translate = function(body, translation) { | |
Body.setPosition(body, Vector.add(body.position, translation)); | |
}; | |
/** | |
* Rotates a body by a given angle relative to its current angle, without imparting any angular velocity. | |
* @method rotate | |
* @param {body} body | |
* @param {number} rotation | |
* @param {vector} [point] | |
*/ | |
Body.rotate = function(body, rotation, point) { | |
if (!point) { | |
Body.setAngle(body, body.angle + rotation); | |
} else { | |
var cos = Math.cos(rotation), | |
sin = Math.sin(rotation), | |
dx = body.position.x - point.x, | |
dy = body.position.y - point.y; | |
Body.setPosition(body, { | |
x: point.x + (dx * cos - dy * sin), | |
y: point.y + (dx * sin + dy * cos) | |
}); | |
Body.setAngle(body, body.angle + rotation); | |
} | |
}; | |
/** | |
* Scales the body, including updating physical properties (mass, area, axes, inertia), from a world-space point (default is body centre). | |
* @method scale | |
* @param {body} body | |
* @param {number} scaleX | |
* @param {number} scaleY | |
* @param {vector} [point] | |
*/ | |
Body.scale = function(body, scaleX, scaleY, point) { | |
for (var i = 0; i < body.parts.length; i++) { | |
var part = body.parts[i]; | |
// scale vertices | |
Vertices.scale(part.vertices, scaleX, scaleY, body.position); | |
// update properties | |
part.axes = Axes.fromVertices(part.vertices); | |
if (!body.isStatic) { | |
part.area = Vertices.area(part.vertices); | |
Body.setMass(part, body.density * part.area); | |
// update inertia (requires vertices to be at origin) | |
Vertices.translate(part.vertices, { x: -part.position.x, y: -part.position.y }); | |
Body.setInertia(part, Vertices.inertia(part.vertices, part.mass)); | |
Vertices.translate(part.vertices, { x: part.position.x, y: part.position.y }); | |
} | |
// update bounds | |
Bounds.update(part.bounds, part.vertices, body.velocity); | |
} | |
// handle circles | |
if (body.circleRadius) { | |
if (scaleX === scaleY) { | |
body.circleRadius *= scaleX; | |
} else { | |
// body is no longer a circle | |
body.circleRadius = null; | |
} | |
} | |
if (!body.isStatic) { | |
var total = _totalProperties(body); | |
body.area = total.area; | |
Body.setMass(body, total.mass); | |
Body.setInertia(body, total.inertia); | |
} | |
}; | |
/** | |
* Performs a simulation step for the given `body`, including updating position and angle using Verlet integration. | |
* @method update | |
* @param {body} body | |
* @param {number} deltaTime | |
* @param {number} timeScale | |
* @param {number} correction | |
*/ | |
Body.update = function(body, deltaTime, timeScale, correction) { | |
var deltaTimeSquared = Math.pow(deltaTime * timeScale * body.timeScale, 2); | |
// from the previous step | |
var frictionAir = 1 - body.frictionAir * timeScale * body.timeScale, | |
velocityPrevX = body.position.x - body.positionPrev.x, | |
velocityPrevY = body.position.y - body.positionPrev.y; | |
// update velocity with Verlet integration | |
body.velocity.x = (velocityPrevX * frictionAir * correction) + (body.force.x / body.mass) * deltaTimeSquared; | |
body.velocity.y = (velocityPrevY * frictionAir * correction) + (body.force.y / body.mass) * deltaTimeSquared; | |
body.positionPrev.x = body.position.x; | |
body.positionPrev.y = body.position.y; | |
body.position.x += body.velocity.x; | |
body.position.y += body.velocity.y; | |
// update angular velocity with Verlet integration | |
body.angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared; | |
body.anglePrev = body.angle; | |
body.angle += body.angularVelocity; | |
// track speed and acceleration | |
body.speed = Vector.magnitude(body.velocity); | |
body.angularSpeed = Math.abs(body.angularVelocity); | |
// transform the body geometry | |
for (var i = 0; i < body.parts.length; i++) { | |
var part = body.parts[i]; | |
Vertices.translate(part.vertices, body.velocity); | |
if (i > 0) { | |
part.position.x += body.velocity.x; | |
part.position.y += body.velocity.y; | |
} | |
if (body.angularVelocity !== 0) { | |
Vertices.rotate(part.vertices, body.angularVelocity, body.position); | |
Axes.rotate(part.axes, body.angularVelocity); | |
if (i > 0) { | |
Vector.rotateAbout(part.position, body.angularVelocity, body.position, part.position); | |
} | |
} | |
Bounds.update(part.bounds, part.vertices, body.velocity); | |
} | |
}; | |
/** | |
* Applies a force to a body from a given world-space position, including resulting torque. | |
* @method applyForce | |
* @param {body} body | |
* @param {vector} position | |
* @param {vector} force | |
*/ | |
Body.applyForce = function(body, position, force) { | |
body.force.x += force.x; | |
body.force.y += force.y; | |
var offset = { x: position.x - body.position.x, y: position.y - body.position.y }; | |
body.torque += offset.x * force.y - offset.y * force.x; | |
}; | |
/** | |
* Returns the sums of the properties of all compound parts of the parent body. | |
* @method _totalProperties | |
* @private | |
* @param {body} body | |
* @return {} | |
*/ | |
var _totalProperties = function(body) { | |
// from equations at: | |
// https://ecourses.ou.edu/cgi-bin/ebook.cgi?doc=&topic=st&chap_sec=07.2&page=theory | |
// http://output.to/sideway/default.asp?qno=121100087 | |
var properties = { | |
mass: 0, | |
area: 0, | |
inertia: 0, | |
centre: { x: 0, y: 0 } | |
}; | |
// sum the properties of all compound parts of the parent body | |
for (var i = body.parts.length === 1 ? 0 : 1; i < body.parts.length; i++) { | |
var part = body.parts[i]; | |
properties.mass += part.mass; | |
properties.area += part.area; | |
properties.inertia += part.inertia; | |
properties.centre = Vector.add(properties.centre, | |
Vector.mult(part.position, part.mass !== Infinity ? part.mass : 1)); | |
} | |
properties.centre = Vector.div(properties.centre, | |
properties.mass !== Infinity ? properties.mass : body.parts.length); | |
return properties; | |
}; | |
/* | |
* | |
* Events Documentation | |
* | |
*/ | |
/** | |
* Fired when a body starts sleeping (where `this` is the body). | |
* | |
* @event sleepStart | |
* @this {body} The body that has started sleeping | |
* @param {} event An event object | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when a body ends sleeping (where `this` is the body). | |
* | |
* @event sleepEnd | |
* @this {body} The body that has ended sleeping | |
* @param {} event An event object | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* An integer `Number` uniquely identifying number generated in `Body.create` by `Common.nextId`. | |
* | |
* @property id | |
* @type number | |
*/ | |
/** | |
* A `String` denoting the type of object. | |
* | |
* @property type | |
* @type string | |
* @default "body" | |
* @readOnly | |
*/ | |
/** | |
* An arbitrary `String` name to help the user identify and manage bodies. | |
* | |
* @property label | |
* @type string | |
* @default "Body" | |
*/ | |
/** | |
* An array of bodies that make up this body. | |
* The first body in the array must always be a self reference to the current body instance. | |
* All bodies in the `parts` array together form a single rigid compound body. | |
* Parts are allowed to overlap, have gaps or holes or even form concave bodies. | |
* Parts themselves should never be added to a `World`, only the parent body should be. | |
* Use `Body.setParts` when setting parts to ensure correct updates of all properties. | |
* | |
* @property parts | |
* @type body[] | |
*/ | |
/** | |
* An object reserved for storing plugin-specific properties. | |
* | |
* @property plugin | |
* @type {} | |
*/ | |
/** | |
* A self reference if the body is _not_ a part of another body. | |
* Otherwise this is a reference to the body that this is a part of. | |
* See `body.parts`. | |
* | |
* @property parent | |
* @type body | |
*/ | |
/** | |
* A `Number` specifying the angle of the body, in radians. | |
* | |
* @property angle | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* An array of `Vector` objects that specify the convex hull of the rigid body. | |
* These should be provided about the origin `(0, 0)`. E.g. | |
* | |
* [{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }] | |
* | |
* When passed via `Body.create`, the vertices are translated relative to `body.position` (i.e. world-space, and constantly updated by `Body.update` during simulation). | |
* The `Vector` objects are also augmented with additional properties required for efficient collision detection. | |
* | |
* Other properties such as `inertia` and `bounds` are automatically calculated from the passed vertices (unless provided via `options`). | |
* Concave hulls are not currently supported. The module `Matter.Vertices` contains useful methods for working with vertices. | |
* | |
* @property vertices | |
* @type vector[] | |
*/ | |
/** | |
* A `Vector` that specifies the current world-space position of the body. | |
* | |
* @property position | |
* @type vector | |
* @default { x: 0, y: 0 } | |
*/ | |
/** | |
* A `Vector` that specifies the force to apply in the current step. It is zeroed after every `Body.update`. See also `Body.applyForce`. | |
* | |
* @property force | |
* @type vector | |
* @default { x: 0, y: 0 } | |
*/ | |
/** | |
* A `Number` that specifies the torque (turning force) to apply in the current step. It is zeroed after every `Body.update`. | |
* | |
* @property torque | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that _measures_ the current speed of the body after the last `Body.update`. It is read-only and always positive (it's the magnitude of `body.velocity`). | |
* | |
* @readOnly | |
* @property speed | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that _measures_ the current angular speed of the body after the last `Body.update`. It is read-only and always positive (it's the magnitude of `body.angularVelocity`). | |
* | |
* @readOnly | |
* @property angularSpeed | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Vector` that _measures_ the current velocity of the body after the last `Body.update`. It is read-only. | |
* If you need to modify a body's velocity directly, you should either apply a force or simply change the body's `position` (as the engine uses position-Verlet integration). | |
* | |
* @readOnly | |
* @property velocity | |
* @type vector | |
* @default { x: 0, y: 0 } | |
*/ | |
/** | |
* A `Number` that _measures_ the current angular velocity of the body after the last `Body.update`. It is read-only. | |
* If you need to modify a body's angular velocity directly, you should apply a torque or simply change the body's `angle` (as the engine uses position-Verlet integration). | |
* | |
* @readOnly | |
* @property angularVelocity | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A flag that indicates whether a body is considered static. A static body can never change position or angle and is completely fixed. | |
* If you need to set a body as static after its creation, you should use `Body.setStatic` as this requires more than just setting this flag. | |
* | |
* @property isStatic | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* A flag that indicates whether a body is a sensor. Sensor triggers collision events, but doesn't react with colliding body physically. | |
* | |
* @property isSensor | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* A flag that indicates whether the body is considered sleeping. A sleeping body acts similar to a static body, except it is only temporary and can be awoken. | |
* If you need to set a body as sleeping, you should use `Sleeping.set` as this requires more than just setting this flag. | |
* | |
* @property isSleeping | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* A `Number` that _measures_ the amount of movement a body currently has (a combination of `speed` and `angularSpeed`). It is read-only and always positive. | |
* It is used and updated by the `Matter.Sleeping` module during simulation to decide if a body has come to rest. | |
* | |
* @readOnly | |
* @property motion | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that defines the number of updates in which this body must have near-zero velocity before it is set as sleeping by the `Matter.Sleeping` module (if sleeping is enabled by the engine). | |
* | |
* @property sleepThreshold | |
* @type number | |
* @default 60 | |
*/ | |
/** | |
* A `Number` that defines the density of the body, that is its mass per unit area. | |
* If you pass the density via `Body.create` the `mass` property is automatically calculated for you based on the size (area) of the object. | |
* This is generally preferable to simply setting mass and allows for more intuitive definition of materials (e.g. rock has a higher density than wood). | |
* | |
* @property density | |
* @type number | |
* @default 0.001 | |
*/ | |
/** | |
* A `Number` that defines the mass of the body, although it may be more appropriate to specify the `density` property instead. | |
* If you modify this value, you must also modify the `body.inverseMass` property (`1 / mass`). | |
* | |
* @property mass | |
* @type number | |
*/ | |
/** | |
* A `Number` that defines the inverse mass of the body (`1 / mass`). | |
* If you modify this value, you must also modify the `body.mass` property. | |
* | |
* @property inverseMass | |
* @type number | |
*/ | |
/** | |
* A `Number` that defines the moment of inertia (i.e. second moment of area) of the body. | |
* It is automatically calculated from the given convex hull (`vertices` array) and density in `Body.create`. | |
* If you modify this value, you must also modify the `body.inverseInertia` property (`1 / inertia`). | |
* | |
* @property inertia | |
* @type number | |
*/ | |
/** | |
* A `Number` that defines the inverse moment of inertia of the body (`1 / inertia`). | |
* If you modify this value, you must also modify the `body.inertia` property. | |
* | |
* @property inverseInertia | |
* @type number | |
*/ | |
/** | |
* A `Number` that defines the restitution (elasticity) of the body. The value is always positive and is in the range `(0, 1)`. | |
* A value of `0` means collisions may be perfectly inelastic and no bouncing may occur. | |
* A value of `0.8` means the body may bounce back with approximately 80% of its kinetic energy. | |
* Note that collision response is based on _pairs_ of bodies, and that `restitution` values are _combined_ with the following formula: | |
* | |
* Math.max(bodyA.restitution, bodyB.restitution) | |
* | |
* @property restitution | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that defines the friction of the body. The value is always positive and is in the range `(0, 1)`. | |
* A value of `0` means that the body may slide indefinitely. | |
* A value of `1` means the body may come to a stop almost instantly after a force is applied. | |
* | |
* The effects of the value may be non-linear. | |
* High values may be unstable depending on the body. | |
* The engine uses a Coulomb friction model including static and kinetic friction. | |
* Note that collision response is based on _pairs_ of bodies, and that `friction` values are _combined_ with the following formula: | |
* | |
* Math.min(bodyA.friction, bodyB.friction) | |
* | |
* @property friction | |
* @type number | |
* @default 0.1 | |
*/ | |
/** | |
* A `Number` that defines the static friction of the body (in the Coulomb friction model). | |
* A value of `0` means the body will never 'stick' when it is nearly stationary and only dynamic `friction` is used. | |
* The higher the value (e.g. `10`), the more force it will take to initially get the body moving when nearly stationary. | |
* This value is multiplied with the `friction` property to make it easier to change `friction` and maintain an appropriate amount of static friction. | |
* | |
* @property frictionStatic | |
* @type number | |
* @default 0.5 | |
*/ | |
/** | |
* A `Number` that defines the air friction of the body (air resistance). | |
* A value of `0` means the body will never slow as it moves through space. | |
* The higher the value, the faster a body slows when moving through space. | |
* The effects of the value are non-linear. | |
* | |
* @property frictionAir | |
* @type number | |
* @default 0.01 | |
*/ | |
/** | |
* An `Object` that specifies the collision filtering properties of this body. | |
* | |
* Collisions between two bodies will obey the following rules: | |
* - If the two bodies have the same non-zero value of `collisionFilter.group`, | |
* they will always collide if the value is positive, and they will never collide | |
* if the value is negative. | |
* - If the two bodies have different values of `collisionFilter.group` or if one | |
* (or both) of the bodies has a value of 0, then the category/mask rules apply as follows: | |
* | |
* Each body belongs to a collision category, given by `collisionFilter.category`. This | |
* value is used as a bit field and the category should have only one bit set, meaning that | |
* the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32 | |
* different collision categories available. | |
* | |
* Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies | |
* the categories it collides with (the value is the bitwise AND value of all these categories). | |
* | |
* Using the category/mask rules, two bodies `A` and `B` collide if each includes the other's | |
* category in its mask, i.e. `(categoryA & maskB) !== 0` and `(categoryB & maskA) !== 0` | |
* are both true. | |
* | |
* @property collisionFilter | |
* @type object | |
*/ | |
/** | |
* An Integer `Number`, that specifies the collision group this body belongs to. | |
* See `body.collisionFilter` for more information. | |
* | |
* @property collisionFilter.group | |
* @type object | |
* @default 0 | |
*/ | |
/** | |
* A bit field that specifies the collision category this body belongs to. | |
* The category value should have only one bit set, for example `0x0001`. | |
* This means there are up to 32 unique collision categories available. | |
* See `body.collisionFilter` for more information. | |
* | |
* @property collisionFilter.category | |
* @type object | |
* @default 1 | |
*/ | |
/** | |
* A bit mask that specifies the collision categories this body may collide with. | |
* See `body.collisionFilter` for more information. | |
* | |
* @property collisionFilter.mask | |
* @type object | |
* @default -1 | |
*/ | |
/** | |
* A `Number` that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies. | |
* Avoid changing this value unless you understand the purpose of `slop` in physics engines. | |
* The default should generally suffice, although very large bodies may require larger values for stable stacking. | |
* | |
* @property slop | |
* @type number | |
* @default 0.05 | |
*/ | |
/** | |
* A `Number` that allows per-body time scaling, e.g. a force-field where bodies inside are in slow-motion, while others are at full speed. | |
* | |
* @property timeScale | |
* @type number | |
* @default 1 | |
*/ | |
/** | |
* An `Object` that defines the rendering properties to be consumed by the module `Matter.Render`. | |
* | |
* @property render | |
* @type object | |
*/ | |
/** | |
* A flag that indicates if the body should be rendered. | |
* | |
* @property render.visible | |
* @type boolean | |
* @default true | |
*/ | |
/** | |
* Sets the opacity to use when rendering. | |
* | |
* @property render.opacity | |
* @type number | |
* @default 1 | |
*/ | |
/** | |
* An `Object` that defines the sprite properties to use when rendering, if any. | |
* | |
* @property render.sprite | |
* @type object | |
*/ | |
/** | |
* An `String` that defines the path to the image to use as the sprite texture, if any. | |
* | |
* @property render.sprite.texture | |
* @type string | |
*/ | |
/** | |
* A `Number` that defines the scaling in the x-axis for the sprite, if any. | |
* | |
* @property render.sprite.xScale | |
* @type number | |
* @default 1 | |
*/ | |
/** | |
* A `Number` that defines the scaling in the y-axis for the sprite, if any. | |
* | |
* @property render.sprite.yScale | |
* @type number | |
* @default 1 | |
*/ | |
/** | |
* A `Number` that defines the offset in the x-axis for the sprite (normalised by texture width). | |
* | |
* @property render.sprite.xOffset | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that defines the offset in the y-axis for the sprite (normalised by texture height). | |
* | |
* @property render.sprite.yOffset | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that defines the line width to use when rendering the body outline (if a sprite is not defined). | |
* A value of `0` means no outline will be rendered. | |
* | |
* @property render.lineWidth | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `String` that defines the fill style to use when rendering the body (if a sprite is not defined). | |
* It is the same as when using a canvas, so it accepts CSS style property values. | |
* | |
* @property render.fillStyle | |
* @type string | |
* @default a random colour | |
*/ | |
/** | |
* A `String` that defines the stroke style to use when rendering the body outline (if a sprite is not defined). | |
* It is the same as when using a canvas, so it accepts CSS style property values. | |
* | |
* @property render.strokeStyle | |
* @type string | |
* @default a random colour | |
*/ | |
/** | |
* An array of unique axis vectors (edge normals) used for collision detection. | |
* These are automatically calculated from the given convex hull (`vertices` array) in `Body.create`. | |
* They are constantly updated by `Body.update` during the simulation. | |
* | |
* @property axes | |
* @type vector[] | |
*/ | |
/** | |
* A `Number` that _measures_ the area of the body's convex hull, calculated at creation by `Body.create`. | |
* | |
* @property area | |
* @type string | |
* @default | |
*/ | |
/** | |
* A `Bounds` object that defines the AABB region for the body. | |
* It is automatically calculated from the given convex hull (`vertices` array) in `Body.create` and constantly updated by `Body.update` during simulation. | |
* | |
* @property bounds | |
* @type bounds | |
*/ | |
})(); | |
},{"../core/Common":14,"../core/Sleeping":22,"../geometry/Axes":25,"../geometry/Bounds":26,"../geometry/Vector":28,"../geometry/Vertices":29,"../render/Render":31}],2:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Composite` module contains methods for creating and manipulating composite bodies. | |
* A composite body is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`, therefore composites form a tree structure. | |
* It is important to use the functions in this module to modify composites, rather than directly modifying their properties. | |
* Note that the `Matter.World` object is also a type of `Matter.Composite` and as such all composite methods here can also operate on a `Matter.World`. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Composite | |
*/ | |
var Composite = {}; | |
module.exports = Composite; | |
var Events = _dereq_('../core/Events'); | |
var Common = _dereq_('../core/Common'); | |
var Body = _dereq_('./Body'); | |
(function() { | |
/** | |
* Creates a new composite. The options parameter is an object that specifies any properties you wish to override the defaults. | |
* See the properites section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @param {} [options] | |
* @return {composite} A new composite | |
*/ | |
Composite.create = function(options) { | |
return Common.extend({ | |
id: Common.nextId(), | |
type: 'composite', | |
parent: null, | |
isModified: false, | |
bodies: [], | |
constraints: [], | |
composites: [], | |
label: 'Composite', | |
plugin: {} | |
}, options); | |
}; | |
/** | |
* Sets the composite's `isModified` flag. | |
* If `updateParents` is true, all parents will be set (default: false). | |
* If `updateChildren` is true, all children will be set (default: false). | |
* @method setModified | |
* @param {composite} composite | |
* @param {boolean} isModified | |
* @param {boolean} [updateParents=false] | |
* @param {boolean} [updateChildren=false] | |
*/ | |
Composite.setModified = function(composite, isModified, updateParents, updateChildren) { | |
composite.isModified = isModified; | |
if (updateParents && composite.parent) { | |
Composite.setModified(composite.parent, isModified, updateParents, updateChildren); | |
} | |
if (updateChildren) { | |
for(var i = 0; i < composite.composites.length; i++) { | |
var childComposite = composite.composites[i]; | |
Composite.setModified(childComposite, isModified, updateParents, updateChildren); | |
} | |
} | |
}; | |
/** | |
* Generic add function. Adds one or many body(s), constraint(s) or a composite(s) to the given composite. | |
* Triggers `beforeAdd` and `afterAdd` events on the `composite`. | |
* @method add | |
* @param {composite} composite | |
* @param {} object | |
* @return {composite} The original composite with the objects added | |
*/ | |
Composite.add = function(composite, object) { | |
var objects = [].concat(object); | |
Events.trigger(composite, 'beforeAdd', { object: object }); | |
for (var i = 0; i < objects.length; i++) { | |
var obj = objects[i]; | |
switch (obj.type) { | |
case 'body': | |
// skip adding compound parts | |
if (obj.parent !== obj) { | |
Common.warn('Composite.add: skipped adding a compound body part (you must add its parent instead)'); | |
break; | |
} | |
Composite.addBody(composite, obj); | |
break; | |
case 'constraint': | |
Composite.addConstraint(composite, obj); | |
break; | |
case 'composite': | |
Composite.addComposite(composite, obj); | |
break; | |
case 'mouseConstraint': | |
Composite.addConstraint(composite, obj.constraint); | |
break; | |
} | |
} | |
Events.trigger(composite, 'afterAdd', { object: object }); | |
return composite; | |
}; | |
/** | |
* Generic remove function. Removes one or many body(s), constraint(s) or a composite(s) to the given composite. | |
* Optionally searching its children recursively. | |
* Triggers `beforeRemove` and `afterRemove` events on the `composite`. | |
* @method remove | |
* @param {composite} composite | |
* @param {} object | |
* @param {boolean} [deep=false] | |
* @return {composite} The original composite with the objects removed | |
*/ | |
Composite.remove = function(composite, object, deep) { | |
var objects = [].concat(object); | |
Events.trigger(composite, 'beforeRemove', { object: object }); | |
for (var i = 0; i < objects.length; i++) { | |
var obj = objects[i]; | |
switch (obj.type) { | |
case 'body': | |
Composite.removeBody(composite, obj, deep); | |
break; | |
case 'constraint': | |
Composite.removeConstraint(composite, obj, deep); | |
break; | |
case 'composite': | |
Composite.removeComposite(composite, obj, deep); | |
break; | |
case 'mouseConstraint': | |
Composite.removeConstraint(composite, obj.constraint); | |
break; | |
} | |
} | |
Events.trigger(composite, 'afterRemove', { object: object }); | |
return composite; | |
}; | |
/** | |
* Adds a composite to the given composite. | |
* @private | |
* @method addComposite | |
* @param {composite} compositeA | |
* @param {composite} compositeB | |
* @return {composite} The original compositeA with the objects from compositeB added | |
*/ | |
Composite.addComposite = function(compositeA, compositeB) { | |
compositeA.composites.push(compositeB); | |
compositeB.parent = compositeA; | |
Composite.setModified(compositeA, true, true, false); | |
return compositeA; | |
}; | |
/** | |
* Removes a composite from the given composite, and optionally searching its children recursively. | |
* @private | |
* @method removeComposite | |
* @param {composite} compositeA | |
* @param {composite} compositeB | |
* @param {boolean} [deep=false] | |
* @return {composite} The original compositeA with the composite removed | |
*/ | |
Composite.removeComposite = function(compositeA, compositeB, deep) { | |
var position = Common.indexOf(compositeA.composites, compositeB); | |
if (position !== -1) { | |
Composite.removeCompositeAt(compositeA, position); | |
Composite.setModified(compositeA, true, true, false); | |
} | |
if (deep) { | |
for (var i = 0; i < compositeA.composites.length; i++){ | |
Composite.removeComposite(compositeA.composites[i], compositeB, true); | |
} | |
} | |
return compositeA; | |
}; | |
/** | |
* Removes a composite from the given composite. | |
* @private | |
* @method removeCompositeAt | |
* @param {composite} composite | |
* @param {number} position | |
* @return {composite} The original composite with the composite removed | |
*/ | |
Composite.removeCompositeAt = function(composite, position) { | |
composite.composites.splice(position, 1); | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Adds a body to the given composite. | |
* @private | |
* @method addBody | |
* @param {composite} composite | |
* @param {body} body | |
* @return {composite} The original composite with the body added | |
*/ | |
Composite.addBody = function(composite, body) { | |
composite.bodies.push(body); | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Removes a body from the given composite, and optionally searching its children recursively. | |
* @private | |
* @method removeBody | |
* @param {composite} composite | |
* @param {body} body | |
* @param {boolean} [deep=false] | |
* @return {composite} The original composite with the body removed | |
*/ | |
Composite.removeBody = function(composite, body, deep) { | |
var position = Common.indexOf(composite.bodies, body); | |
if (position !== -1) { | |
Composite.removeBodyAt(composite, position); | |
Composite.setModified(composite, true, true, false); | |
} | |
if (deep) { | |
for (var i = 0; i < composite.composites.length; i++){ | |
Composite.removeBody(composite.composites[i], body, true); | |
} | |
} | |
return composite; | |
}; | |
/** | |
* Removes a body from the given composite. | |
* @private | |
* @method removeBodyAt | |
* @param {composite} composite | |
* @param {number} position | |
* @return {composite} The original composite with the body removed | |
*/ | |
Composite.removeBodyAt = function(composite, position) { | |
composite.bodies.splice(position, 1); | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Adds a constraint to the given composite. | |
* @private | |
* @method addConstraint | |
* @param {composite} composite | |
* @param {constraint} constraint | |
* @return {composite} The original composite with the constraint added | |
*/ | |
Composite.addConstraint = function(composite, constraint) { | |
composite.constraints.push(constraint); | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Removes a constraint from the given composite, and optionally searching its children recursively. | |
* @private | |
* @method removeConstraint | |
* @param {composite} composite | |
* @param {constraint} constraint | |
* @param {boolean} [deep=false] | |
* @return {composite} The original composite with the constraint removed | |
*/ | |
Composite.removeConstraint = function(composite, constraint, deep) { | |
var position = Common.indexOf(composite.constraints, constraint); | |
if (position !== -1) { | |
Composite.removeConstraintAt(composite, position); | |
} | |
if (deep) { | |
for (var i = 0; i < composite.composites.length; i++){ | |
Composite.removeConstraint(composite.composites[i], constraint, true); | |
} | |
} | |
return composite; | |
}; | |
/** | |
* Removes a body from the given composite. | |
* @private | |
* @method removeConstraintAt | |
* @param {composite} composite | |
* @param {number} position | |
* @return {composite} The original composite with the constraint removed | |
*/ | |
Composite.removeConstraintAt = function(composite, position) { | |
composite.constraints.splice(position, 1); | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Removes all bodies, constraints and composites from the given composite. | |
* Optionally clearing its children recursively. | |
* @method clear | |
* @param {composite} composite | |
* @param {boolean} keepStatic | |
* @param {boolean} [deep=false] | |
*/ | |
Composite.clear = function(composite, keepStatic, deep) { | |
if (deep) { | |
for (var i = 0; i < composite.composites.length; i++){ | |
Composite.clear(composite.composites[i], keepStatic, true); | |
} | |
} | |
if (keepStatic) { | |
composite.bodies = composite.bodies.filter(function(body) { return body.isStatic; }); | |
} else { | |
composite.bodies.length = 0; | |
} | |
composite.constraints.length = 0; | |
composite.composites.length = 0; | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Returns all bodies in the given composite, including all bodies in its children, recursively. | |
* @method allBodies | |
* @param {composite} composite | |
* @return {body[]} All the bodies | |
*/ | |
Composite.allBodies = function(composite) { | |
var bodies = [].concat(composite.bodies); | |
for (var i = 0; i < composite.composites.length; i++) | |
bodies = bodies.concat(Composite.allBodies(composite.composites[i])); | |
return bodies; | |
}; | |
/** | |
* Returns all constraints in the given composite, including all constraints in its children, recursively. | |
* @method allConstraints | |
* @param {composite} composite | |
* @return {constraint[]} All the constraints | |
*/ | |
Composite.allConstraints = function(composite) { | |
var constraints = [].concat(composite.constraints); | |
for (var i = 0; i < composite.composites.length; i++) | |
constraints = constraints.concat(Composite.allConstraints(composite.composites[i])); | |
return constraints; | |
}; | |
/** | |
* Returns all composites in the given composite, including all composites in its children, recursively. | |
* @method allComposites | |
* @param {composite} composite | |
* @return {composite[]} All the composites | |
*/ | |
Composite.allComposites = function(composite) { | |
var composites = [].concat(composite.composites); | |
for (var i = 0; i < composite.composites.length; i++) | |
composites = composites.concat(Composite.allComposites(composite.composites[i])); | |
return composites; | |
}; | |
/** | |
* Searches the composite recursively for an object matching the type and id supplied, null if not found. | |
* @method get | |
* @param {composite} composite | |
* @param {number} id | |
* @param {string} type | |
* @return {object} The requested object, if found | |
*/ | |
Composite.get = function(composite, id, type) { | |
var objects, | |
object; | |
switch (type) { | |
case 'body': | |
objects = Composite.allBodies(composite); | |
break; | |
case 'constraint': | |
objects = Composite.allConstraints(composite); | |
break; | |
case 'composite': | |
objects = Composite.allComposites(composite).concat(composite); | |
break; | |
} | |
if (!objects) | |
return null; | |
object = objects.filter(function(object) { | |
return object.id.toString() === id.toString(); | |
}); | |
return object.length === 0 ? null : object[0]; | |
}; | |
/** | |
* Moves the given object(s) from compositeA to compositeB (equal to a remove followed by an add). | |
* @method move | |
* @param {compositeA} compositeA | |
* @param {object[]} objects | |
* @param {compositeB} compositeB | |
* @return {composite} Returns compositeA | |
*/ | |
Composite.move = function(compositeA, objects, compositeB) { | |
Composite.remove(compositeA, objects); | |
Composite.add(compositeB, objects); | |
return compositeA; | |
}; | |
/** | |
* Assigns new ids for all objects in the composite, recursively. | |
* @method rebase | |
* @param {composite} composite | |
* @return {composite} Returns composite | |
*/ | |
Composite.rebase = function(composite) { | |
var objects = Composite.allBodies(composite) | |
.concat(Composite.allConstraints(composite)) | |
.concat(Composite.allComposites(composite)); | |
for (var i = 0; i < objects.length; i++) { | |
objects[i].id = Common.nextId(); | |
} | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Translates all children in the composite by a given vector relative to their current positions, | |
* without imparting any velocity. | |
* @method translate | |
* @param {composite} composite | |
* @param {vector} translation | |
* @param {bool} [recursive=true] | |
*/ | |
Composite.translate = function(composite, translation, recursive) { | |
var bodies = recursive ? Composite.allBodies(composite) : composite.bodies; | |
for (var i = 0; i < bodies.length; i++) { | |
Body.translate(bodies[i], translation); | |
} | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Rotates all children in the composite by a given angle about the given point, without imparting any angular velocity. | |
* @method rotate | |
* @param {composite} composite | |
* @param {number} rotation | |
* @param {vector} point | |
* @param {bool} [recursive=true] | |
*/ | |
Composite.rotate = function(composite, rotation, point, recursive) { | |
var cos = Math.cos(rotation), | |
sin = Math.sin(rotation), | |
bodies = recursive ? Composite.allBodies(composite) : composite.bodies; | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i], | |
dx = body.position.x - point.x, | |
dy = body.position.y - point.y; | |
Body.setPosition(body, { | |
x: point.x + (dx * cos - dy * sin), | |
y: point.y + (dx * sin + dy * cos) | |
}); | |
Body.rotate(body, rotation); | |
} | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Scales all children in the composite, including updating physical properties (mass, area, axes, inertia), from a world-space point. | |
* @method scale | |
* @param {composite} composite | |
* @param {number} scaleX | |
* @param {number} scaleY | |
* @param {vector} point | |
* @param {bool} [recursive=true] | |
*/ | |
Composite.scale = function(composite, scaleX, scaleY, point, recursive) { | |
var bodies = recursive ? Composite.allBodies(composite) : composite.bodies; | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i], | |
dx = body.position.x - point.x, | |
dy = body.position.y - point.y; | |
Body.setPosition(body, { | |
x: point.x + dx * scaleX, | |
y: point.y + dy * scaleY | |
}); | |
Body.scale(body, scaleX, scaleY); | |
} | |
Composite.setModified(composite, true, true, false); | |
return composite; | |
}; | |
/** | |
* Returns the union of the bounds of all of the composite's bodies. | |
* @method bounds | |
* @param {composite} composite The composite. | |
* @returns {bounds} The composite bounds. | |
*/ | |
Composite.bounds = function(composite) { | |
var bodies = Matter.Composite.allBodies(composite), | |
vertices = []; | |
for (var i = 0; i < bodies.length; i += 1) { | |
var body = bodies[i]; | |
vertices.push(body.bounds.min, body.bounds.max); | |
} | |
return Matter.Bounds.create(vertices); | |
}; | |
/* | |
* | |
* Events Documentation | |
* | |
*/ | |
/** | |
* Fired when a call to `Composite.add` is made, before objects have been added. | |
* | |
* @event beforeAdd | |
* @param {} event An event object | |
* @param {} event.object The object(s) to be added (may be a single body, constraint, composite or a mixed array of these) | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when a call to `Composite.add` is made, after objects have been added. | |
* | |
* @event afterAdd | |
* @param {} event An event object | |
* @param {} event.object The object(s) that have been added (may be a single body, constraint, composite or a mixed array of these) | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when a call to `Composite.remove` is made, before objects have been removed. | |
* | |
* @event beforeRemove | |
* @param {} event An event object | |
* @param {} event.object The object(s) to be removed (may be a single body, constraint, composite or a mixed array of these) | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when a call to `Composite.remove` is made, after objects have been removed. | |
* | |
* @event afterRemove | |
* @param {} event An event object | |
* @param {} event.object The object(s) that have been removed (may be a single body, constraint, composite or a mixed array of these) | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* An integer `Number` uniquely identifying number generated in `Composite.create` by `Common.nextId`. | |
* | |
* @property id | |
* @type number | |
*/ | |
/** | |
* A `String` denoting the type of object. | |
* | |
* @property type | |
* @type string | |
* @default "composite" | |
* @readOnly | |
*/ | |
/** | |
* An arbitrary `String` name to help the user identify and manage composites. | |
* | |
* @property label | |
* @type string | |
* @default "Composite" | |
*/ | |
/** | |
* A flag that specifies whether the composite has been modified during the current step. | |
* Most `Matter.Composite` methods will automatically set this flag to `true` to inform the engine of changes to be handled. | |
* If you need to change it manually, you should use the `Composite.setModified` method. | |
* | |
* @property isModified | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* The `Composite` that is the parent of this composite. It is automatically managed by the `Matter.Composite` methods. | |
* | |
* @property parent | |
* @type composite | |
* @default null | |
*/ | |
/** | |
* An array of `Body` that are _direct_ children of this composite. | |
* To add or remove bodies you should use `Composite.add` and `Composite.remove` methods rather than directly modifying this property. | |
* If you wish to recursively find all descendants, you should use the `Composite.allBodies` method. | |
* | |
* @property bodies | |
* @type body[] | |
* @default [] | |
*/ | |
/** | |
* An array of `Constraint` that are _direct_ children of this composite. | |
* To add or remove constraints you should use `Composite.add` and `Composite.remove` methods rather than directly modifying this property. | |
* If you wish to recursively find all descendants, you should use the `Composite.allConstraints` method. | |
* | |
* @property constraints | |
* @type constraint[] | |
* @default [] | |
*/ | |
/** | |
* An array of `Composite` that are _direct_ children of this composite. | |
* To add or remove composites you should use `Composite.add` and `Composite.remove` methods rather than directly modifying this property. | |
* If you wish to recursively find all descendants, you should use the `Composite.allComposites` method. | |
* | |
* @property composites | |
* @type composite[] | |
* @default [] | |
*/ | |
/** | |
* An object reserved for storing plugin-specific properties. | |
* | |
* @property plugin | |
* @type {} | |
*/ | |
})(); | |
},{"../core/Common":14,"../core/Events":16,"./Body":1}],3:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.World` module contains methods for creating and manipulating the world composite. | |
* A `Matter.World` is a `Matter.Composite` body, which is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`. | |
* A `Matter.World` has a few additional properties including `gravity` and `bounds`. | |
* It is important to use the functions in the `Matter.Composite` module to modify the world composite, rather than directly modifying its properties. | |
* There are also a few methods here that alias those in `Matter.Composite` for easier readability. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class World | |
* @extends Composite | |
*/ | |
var World = {}; | |
module.exports = World; | |
var Composite = _dereq_('./Composite'); | |
var Constraint = _dereq_('../constraint/Constraint'); | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
/** | |
* Creates a new world composite. The options parameter is an object that specifies any properties you wish to override the defaults. | |
* See the properties section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @constructor | |
* @param {} options | |
* @return {world} A new world | |
*/ | |
World.create = function(options) { | |
var composite = Composite.create(); | |
var defaults = { | |
label: 'World', | |
gravity: { | |
x: 0, | |
y: 1, | |
scale: 0.001 | |
}, | |
bounds: { | |
min: { x: -Infinity, y: -Infinity }, | |
max: { x: Infinity, y: Infinity } | |
} | |
}; | |
return Common.extend(composite, defaults, options); | |
}; | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* The gravity to apply on the world. | |
* | |
* @property gravity | |
* @type object | |
*/ | |
/** | |
* The gravity x component. | |
* | |
* @property gravity.x | |
* @type object | |
* @default 0 | |
*/ | |
/** | |
* The gravity y component. | |
* | |
* @property gravity.y | |
* @type object | |
* @default 1 | |
*/ | |
/** | |
* The gravity scale factor. | |
* | |
* @property gravity.scale | |
* @type object | |
* @default 0.001 | |
*/ | |
/** | |
* A `Bounds` object that defines the world bounds for collision detection. | |
* | |
* @property bounds | |
* @type bounds | |
* @default { min: { x: -Infinity, y: -Infinity }, max: { x: Infinity, y: Infinity } } | |
*/ | |
// World is a Composite body | |
// see src/module/Outro.js for these aliases: | |
/** | |
* An alias for Composite.clear | |
* @method clear | |
* @param {world} world | |
* @param {boolean} keepStatic | |
*/ | |
/** | |
* An alias for Composite.add | |
* @method addComposite | |
* @param {world} world | |
* @param {composite} composite | |
* @return {world} The original world with the objects from composite added | |
*/ | |
/** | |
* An alias for Composite.addBody | |
* @method addBody | |
* @param {world} world | |
* @param {body} body | |
* @return {world} The original world with the body added | |
*/ | |
/** | |
* An alias for Composite.addConstraint | |
* @method addConstraint | |
* @param {world} world | |
* @param {constraint} constraint | |
* @return {world} The original world with the constraint added | |
*/ | |
})(); | |
},{"../constraint/Constraint":12,"../core/Common":14,"./Composite":2}],4:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Contact` module contains methods for creating and manipulating collision contacts. | |
* | |
* @class Contact | |
*/ | |
var Contact = {}; | |
module.exports = Contact; | |
(function() { | |
/** | |
* Creates a new contact. | |
* @method create | |
* @param {vertex} vertex | |
* @return {contact} A new contact | |
*/ | |
Contact.create = function(vertex) { | |
return { | |
id: Contact.id(vertex), | |
vertex: vertex, | |
normalImpulse: 0, | |
tangentImpulse: 0 | |
}; | |
}; | |
/** | |
* Generates a contact id. | |
* @method id | |
* @param {vertex} vertex | |
* @return {string} Unique contactID | |
*/ | |
Contact.id = function(vertex) { | |
return vertex.body.id + '_' + vertex.index; | |
}; | |
})(); | |
},{}],5:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Detector` module contains methods for detecting collisions given a set of pairs. | |
* | |
* @class Detector | |
*/ | |
// TODO: speculative contacts | |
var Detector = {}; | |
module.exports = Detector; | |
var SAT = _dereq_('./SAT'); | |
var Pair = _dereq_('./Pair'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
(function() { | |
/** | |
* Finds all collisions given a list of pairs. | |
* @method collisions | |
* @param {pair[]} broadphasePairs | |
* @param {engine} engine | |
* @return {array} collisions | |
*/ | |
Detector.collisions = function(broadphasePairs, engine) { | |
var collisions = [], | |
pairsTable = engine.pairs.table; | |
for (var i = 0; i < broadphasePairs.length; i++) { | |
var bodyA = broadphasePairs[i][0], | |
bodyB = broadphasePairs[i][1]; | |
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping)) | |
continue; | |
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) | |
continue; | |
// mid phase | |
if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) { | |
for (var j = bodyA.parts.length > 1 ? 1 : 0; j < bodyA.parts.length; j++) { | |
var partA = bodyA.parts[j]; | |
for (var k = bodyB.parts.length > 1 ? 1 : 0; k < bodyB.parts.length; k++) { | |
var partB = bodyB.parts[k]; | |
if ((partA === bodyA && partB === bodyB) || Bounds.overlaps(partA.bounds, partB.bounds)) { | |
// find a previous collision we could reuse | |
var pairId = Pair.id(partA, partB), | |
pair = pairsTable[pairId], | |
previousCollision; | |
if (pair && pair.isActive) { | |
previousCollision = pair.collision; | |
} else { | |
previousCollision = null; | |
} | |
// narrow phase | |
var collision = SAT.collides(partA, partB, previousCollision); | |
if (collision.collided) { | |
collisions.push(collision); | |
} | |
} | |
} | |
} | |
} | |
} | |
return collisions; | |
}; | |
/** | |
* Returns `true` if both supplied collision filters will allow a collision to occur. | |
* See `body.collisionFilter` for more information. | |
* @method canCollide | |
* @param {} filterA | |
* @param {} filterB | |
* @return {bool} `true` if collision can occur | |
*/ | |
Detector.canCollide = function(filterA, filterB) { | |
if (filterA.group === filterB.group && filterA.group !== 0) | |
return filterA.group > 0; | |
return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0; | |
}; | |
})(); | |
},{"../geometry/Bounds":26,"./Pair":7,"./SAT":11}],6:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Grid` module contains methods for creating and manipulating collision broadphase grid structures. | |
* | |
* @class Grid | |
*/ | |
var Grid = {}; | |
module.exports = Grid; | |
var Pair = _dereq_('./Pair'); | |
var Detector = _dereq_('./Detector'); | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
/** | |
* Creates a new grid. | |
* @method create | |
* @param {} options | |
* @return {grid} A new grid | |
*/ | |
Grid.create = function(options) { | |
var defaults = { | |
controller: Grid, | |
detector: Detector.collisions, | |
buckets: {}, | |
pairs: {}, | |
pairsList: [], | |
bucketWidth: 48, | |
bucketHeight: 48 | |
}; | |
return Common.extend(defaults, options); | |
}; | |
/** | |
* The width of a single grid bucket. | |
* | |
* @property bucketWidth | |
* @type number | |
* @default 48 | |
*/ | |
/** | |
* The height of a single grid bucket. | |
* | |
* @property bucketHeight | |
* @type number | |
* @default 48 | |
*/ | |
/** | |
* Updates the grid. | |
* @method update | |
* @param {grid} grid | |
* @param {body[]} bodies | |
* @param {engine} engine | |
* @param {boolean} forceUpdate | |
*/ | |
Grid.update = function(grid, bodies, engine, forceUpdate) { | |
var i, col, row, | |
world = engine.world, | |
buckets = grid.buckets, | |
bucket, | |
bucketId, | |
gridChanged = false; | |
for (i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (body.isSleeping && !forceUpdate) | |
continue; | |
// don't update out of world bodies | |
if (body.bounds.max.x < world.bounds.min.x || body.bounds.min.x > world.bounds.max.x | |
|| body.bounds.max.y < world.bounds.min.y || body.bounds.min.y > world.bounds.max.y) | |
continue; | |
var newRegion = _getRegion(grid, body); | |
// if the body has changed grid region | |
if (!body.region || newRegion.id !== body.region.id || forceUpdate) { | |
if (!body.region || forceUpdate) | |
body.region = newRegion; | |
var union = _regionUnion(newRegion, body.region); | |
// update grid buckets affected by region change | |
// iterate over the union of both regions | |
for (col = union.startCol; col <= union.endCol; col++) { | |
for (row = union.startRow; row <= union.endRow; row++) { | |
bucketId = _getBucketId(col, row); | |
bucket = buckets[bucketId]; | |
var isInsideNewRegion = (col >= newRegion.startCol && col <= newRegion.endCol | |
&& row >= newRegion.startRow && row <= newRegion.endRow); | |
var isInsideOldRegion = (col >= body.region.startCol && col <= body.region.endCol | |
&& row >= body.region.startRow && row <= body.region.endRow); | |
// remove from old region buckets | |
if (!isInsideNewRegion && isInsideOldRegion) { | |
if (isInsideOldRegion) { | |
if (bucket) | |
_bucketRemoveBody(grid, bucket, body); | |
} | |
} | |
// add to new region buckets | |
if (body.region === newRegion || (isInsideNewRegion && !isInsideOldRegion) || forceUpdate) { | |
if (!bucket) | |
bucket = _createBucket(buckets, bucketId); | |
_bucketAddBody(grid, bucket, body); | |
} | |
} | |
} | |
// set the new region | |
body.region = newRegion; | |
// flag changes so we can update pairs | |
gridChanged = true; | |
} | |
} | |
// update pairs list only if pairs changed (i.e. a body changed region) | |
if (gridChanged) | |
grid.pairsList = _createActivePairsList(grid); | |
}; | |
/** | |
* Clears the grid. | |
* @method clear | |
* @param {grid} grid | |
*/ | |
Grid.clear = function(grid) { | |
grid.buckets = {}; | |
grid.pairs = {}; | |
grid.pairsList = []; | |
}; | |
/** | |
* Finds the union of two regions. | |
* @method _regionUnion | |
* @private | |
* @param {} regionA | |
* @param {} regionB | |
* @return {} region | |
*/ | |
var _regionUnion = function(regionA, regionB) { | |
var startCol = Math.min(regionA.startCol, regionB.startCol), | |
endCol = Math.max(regionA.endCol, regionB.endCol), | |
startRow = Math.min(regionA.startRow, regionB.startRow), | |
endRow = Math.max(regionA.endRow, regionB.endRow); | |
return _createRegion(startCol, endCol, startRow, endRow); | |
}; | |
/** | |
* Gets the region a given body falls in for a given grid. | |
* @method _getRegion | |
* @private | |
* @param {} grid | |
* @param {} body | |
* @return {} region | |
*/ | |
var _getRegion = function(grid, body) { | |
var bounds = body.bounds, | |
startCol = Math.floor(bounds.min.x / grid.bucketWidth), | |
endCol = Math.floor(bounds.max.x / grid.bucketWidth), | |
startRow = Math.floor(bounds.min.y / grid.bucketHeight), | |
endRow = Math.floor(bounds.max.y / grid.bucketHeight); | |
return _createRegion(startCol, endCol, startRow, endRow); | |
}; | |
/** | |
* Creates a region. | |
* @method _createRegion | |
* @private | |
* @param {} startCol | |
* @param {} endCol | |
* @param {} startRow | |
* @param {} endRow | |
* @return {} region | |
*/ | |
var _createRegion = function(startCol, endCol, startRow, endRow) { | |
return { | |
id: startCol + ',' + endCol + ',' + startRow + ',' + endRow, | |
startCol: startCol, | |
endCol: endCol, | |
startRow: startRow, | |
endRow: endRow | |
}; | |
}; | |
/** | |
* Gets the bucket id at the given position. | |
* @method _getBucketId | |
* @private | |
* @param {} column | |
* @param {} row | |
* @return {string} bucket id | |
*/ | |
var _getBucketId = function(column, row) { | |
return 'C' + column + 'R' + row; | |
}; | |
/** | |
* Creates a bucket. | |
* @method _createBucket | |
* @private | |
* @param {} buckets | |
* @param {} bucketId | |
* @return {} bucket | |
*/ | |
var _createBucket = function(buckets, bucketId) { | |
var bucket = buckets[bucketId] = []; | |
return bucket; | |
}; | |
/** | |
* Adds a body to a bucket. | |
* @method _bucketAddBody | |
* @private | |
* @param {} grid | |
* @param {} bucket | |
* @param {} body | |
*/ | |
var _bucketAddBody = function(grid, bucket, body) { | |
// add new pairs | |
for (var i = 0; i < bucket.length; i++) { | |
var bodyB = bucket[i]; | |
if (body.id === bodyB.id || (body.isStatic && bodyB.isStatic)) | |
continue; | |
// keep track of the number of buckets the pair exists in | |
// important for Grid.update to work | |
var pairId = Pair.id(body, bodyB), | |
pair = grid.pairs[pairId]; | |
if (pair) { | |
pair[2] += 1; | |
} else { | |
grid.pairs[pairId] = [body, bodyB, 1]; | |
} | |
} | |
// add to bodies (after pairs, otherwise pairs with self) | |
bucket.push(body); | |
}; | |
/** | |
* Removes a body from a bucket. | |
* @method _bucketRemoveBody | |
* @private | |
* @param {} grid | |
* @param {} bucket | |
* @param {} body | |
*/ | |
var _bucketRemoveBody = function(grid, bucket, body) { | |
// remove from bucket | |
bucket.splice(Common.indexOf(bucket, body), 1); | |
// update pair counts | |
for (var i = 0; i < bucket.length; i++) { | |
// keep track of the number of buckets the pair exists in | |
// important for _createActivePairsList to work | |
var bodyB = bucket[i], | |
pairId = Pair.id(body, bodyB), | |
pair = grid.pairs[pairId]; | |
if (pair) | |
pair[2] -= 1; | |
} | |
}; | |
/** | |
* Generates a list of the active pairs in the grid. | |
* @method _createActivePairsList | |
* @private | |
* @param {} grid | |
* @return [] pairs | |
*/ | |
var _createActivePairsList = function(grid) { | |
var pairKeys, | |
pair, | |
pairs = []; | |
// grid.pairs is used as a hashmap | |
pairKeys = Common.keys(grid.pairs); | |
// iterate over grid.pairs | |
for (var k = 0; k < pairKeys.length; k++) { | |
pair = grid.pairs[pairKeys[k]]; | |
// if pair exists in at least one bucket | |
// it is a pair that needs further collision testing so push it | |
if (pair[2] > 0) { | |
pairs.push(pair); | |
} else { | |
delete grid.pairs[pairKeys[k]]; | |
} | |
} | |
return pairs; | |
}; | |
})(); | |
},{"../core/Common":14,"./Detector":5,"./Pair":7}],7:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Pair` module contains methods for creating and manipulating collision pairs. | |
* | |
* @class Pair | |
*/ | |
var Pair = {}; | |
module.exports = Pair; | |
var Contact = _dereq_('./Contact'); | |
(function() { | |
/** | |
* Creates a pair. | |
* @method create | |
* @param {collision} collision | |
* @param {number} timestamp | |
* @return {pair} A new pair | |
*/ | |
Pair.create = function(collision, timestamp) { | |
var bodyA = collision.bodyA, | |
bodyB = collision.bodyB, | |
parentA = collision.parentA, | |
parentB = collision.parentB; | |
var pair = { | |
id: Pair.id(bodyA, bodyB), | |
bodyA: bodyA, | |
bodyB: bodyB, | |
contacts: {}, | |
activeContacts: [], | |
separation: 0, | |
isActive: true, | |
isSensor: bodyA.isSensor || bodyB.isSensor, | |
timeCreated: timestamp, | |
timeUpdated: timestamp, | |
inverseMass: parentA.inverseMass + parentB.inverseMass, | |
friction: Math.min(parentA.friction, parentB.friction), | |
frictionStatic: Math.max(parentA.frictionStatic, parentB.frictionStatic), | |
restitution: Math.max(parentA.restitution, parentB.restitution), | |
slop: Math.max(parentA.slop, parentB.slop) | |
}; | |
Pair.update(pair, collision, timestamp); | |
return pair; | |
}; | |
/** | |
* Updates a pair given a collision. | |
* @method update | |
* @param {pair} pair | |
* @param {collision} collision | |
* @param {number} timestamp | |
*/ | |
Pair.update = function(pair, collision, timestamp) { | |
var contacts = pair.contacts, | |
supports = collision.supports, | |
activeContacts = pair.activeContacts, | |
parentA = collision.parentA, | |
parentB = collision.parentB; | |
pair.collision = collision; | |
pair.inverseMass = parentA.inverseMass + parentB.inverseMass; | |
pair.friction = Math.min(parentA.friction, parentB.friction); | |
pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic); | |
pair.restitution = Math.max(parentA.restitution, parentB.restitution); | |
pair.slop = Math.max(parentA.slop, parentB.slop); | |
activeContacts.length = 0; | |
if (collision.collided) { | |
for (var i = 0; i < supports.length; i++) { | |
var support = supports[i], | |
contactId = Contact.id(support), | |
contact = contacts[contactId]; | |
if (contact) { | |
activeContacts.push(contact); | |
} else { | |
activeContacts.push(contacts[contactId] = Contact.create(support)); | |
} | |
} | |
pair.separation = collision.depth; | |
Pair.setActive(pair, true, timestamp); | |
} else { | |
if (pair.isActive === true) | |
Pair.setActive(pair, false, timestamp); | |
} | |
}; | |
/** | |
* Set a pair as active or inactive. | |
* @method setActive | |
* @param {pair} pair | |
* @param {bool} isActive | |
* @param {number} timestamp | |
*/ | |
Pair.setActive = function(pair, isActive, timestamp) { | |
if (isActive) { | |
pair.isActive = true; | |
pair.timeUpdated = timestamp; | |
} else { | |
pair.isActive = false; | |
pair.activeContacts.length = 0; | |
} | |
}; | |
/** | |
* Get the id for the given pair. | |
* @method id | |
* @param {body} bodyA | |
* @param {body} bodyB | |
* @return {string} Unique pairId | |
*/ | |
Pair.id = function(bodyA, bodyB) { | |
if (bodyA.id < bodyB.id) { | |
return 'A' + bodyA.id + 'B' + bodyB.id; | |
} else { | |
return 'A' + bodyB.id + 'B' + bodyA.id; | |
} | |
}; | |
})(); | |
},{"./Contact":4}],8:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Pairs` module contains methods for creating and manipulating collision pair sets. | |
* | |
* @class Pairs | |
*/ | |
var Pairs = {}; | |
module.exports = Pairs; | |
var Pair = _dereq_('./Pair'); | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
var _pairMaxIdleLife = 1000; | |
/** | |
* Creates a new pairs structure. | |
* @method create | |
* @param {object} options | |
* @return {pairs} A new pairs structure | |
*/ | |
Pairs.create = function(options) { | |
return Common.extend({ | |
table: {}, | |
list: [], | |
collisionStart: [], | |
collisionActive: [], | |
collisionEnd: [] | |
}, options); | |
}; | |
/** | |
* Updates pairs given a list of collisions. | |
* @method update | |
* @param {object} pairs | |
* @param {collision[]} collisions | |
* @param {number} timestamp | |
*/ | |
Pairs.update = function(pairs, collisions, timestamp) { | |
var pairsList = pairs.list, | |
pairsTable = pairs.table, | |
collisionStart = pairs.collisionStart, | |
collisionEnd = pairs.collisionEnd, | |
collisionActive = pairs.collisionActive, | |
activePairIds = [], | |
collision, | |
pairId, | |
pair, | |
i; | |
// clear collision state arrays, but maintain old reference | |
collisionStart.length = 0; | |
collisionEnd.length = 0; | |
collisionActive.length = 0; | |
for (i = 0; i < collisions.length; i++) { | |
collision = collisions[i]; | |
if (collision.collided) { | |
pairId = Pair.id(collision.bodyA, collision.bodyB); | |
activePairIds.push(pairId); | |
pair = pairsTable[pairId]; | |
if (pair) { | |
// pair already exists (but may or may not be active) | |
if (pair.isActive) { | |
// pair exists and is active | |
collisionActive.push(pair); | |
} else { | |
// pair exists but was inactive, so a collision has just started again | |
collisionStart.push(pair); | |
} | |
// update the pair | |
Pair.update(pair, collision, timestamp); | |
} else { | |
// pair did not exist, create a new pair | |
pair = Pair.create(collision, timestamp); | |
pairsTable[pairId] = pair; | |
// push the new pair | |
collisionStart.push(pair); | |
pairsList.push(pair); | |
} | |
} | |
} | |
// deactivate previously active pairs that are now inactive | |
for (i = 0; i < pairsList.length; i++) { | |
pair = pairsList[i]; | |
if (pair.isActive && Common.indexOf(activePairIds, pair.id) === -1) { | |
Pair.setActive(pair, false, timestamp); | |
collisionEnd.push(pair); | |
} | |
} | |
}; | |
/** | |
* Finds and removes pairs that have been inactive for a set amount of time. | |
* @method removeOld | |
* @param {object} pairs | |
* @param {number} timestamp | |
*/ | |
Pairs.removeOld = function(pairs, timestamp) { | |
var pairsList = pairs.list, | |
pairsTable = pairs.table, | |
indexesToRemove = [], | |
pair, | |
collision, | |
pairIndex, | |
i; | |
for (i = 0; i < pairsList.length; i++) { | |
pair = pairsList[i]; | |
collision = pair.collision; | |
// never remove sleeping pairs | |
if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) { | |
pair.timeUpdated = timestamp; | |
continue; | |
} | |
// if pair is inactive for too long, mark it to be removed | |
if (timestamp - pair.timeUpdated > _pairMaxIdleLife) { | |
indexesToRemove.push(i); | |
} | |
} | |
// remove marked pairs | |
for (i = 0; i < indexesToRemove.length; i++) { | |
pairIndex = indexesToRemove[i] - i; | |
pair = pairsList[pairIndex]; | |
delete pairsTable[pair.id]; | |
pairsList.splice(pairIndex, 1); | |
} | |
}; | |
/** | |
* Clears the given pairs structure. | |
* @method clear | |
* @param {pairs} pairs | |
* @return {pairs} pairs | |
*/ | |
Pairs.clear = function(pairs) { | |
pairs.table = {}; | |
pairs.list.length = 0; | |
pairs.collisionStart.length = 0; | |
pairs.collisionActive.length = 0; | |
pairs.collisionEnd.length = 0; | |
return pairs; | |
}; | |
})(); | |
},{"../core/Common":14,"./Pair":7}],9:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Query` module contains methods for performing collision queries. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Query | |
*/ | |
var Query = {}; | |
module.exports = Query; | |
var Vector = _dereq_('../geometry/Vector'); | |
var SAT = _dereq_('./SAT'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
var Bodies = _dereq_('../factory/Bodies'); | |
var Vertices = _dereq_('../geometry/Vertices'); | |
(function() { | |
/** | |
* Casts a ray segment against a set of bodies and returns all collisions, ray width is optional. Intersection points are not provided. | |
* @method ray | |
* @param {body[]} bodies | |
* @param {vector} startPoint | |
* @param {vector} endPoint | |
* @param {number} [rayWidth] | |
* @return {object[]} Collisions | |
*/ | |
Query.ray = function(bodies, startPoint, endPoint, rayWidth) { | |
rayWidth = rayWidth || 1e-100; | |
var rayAngle = Vector.angle(startPoint, endPoint), | |
rayLength = Vector.magnitude(Vector.sub(startPoint, endPoint)), | |
rayX = (endPoint.x + startPoint.x) * 0.5, | |
rayY = (endPoint.y + startPoint.y) * 0.5, | |
ray = Bodies.rectangle(rayX, rayY, rayLength, rayWidth, { angle: rayAngle }), | |
collisions = []; | |
for (var i = 0; i < bodies.length; i++) { | |
var bodyA = bodies[i]; | |
if (Bounds.overlaps(bodyA.bounds, ray.bounds)) { | |
for (var j = bodyA.parts.length === 1 ? 0 : 1; j < bodyA.parts.length; j++) { | |
var part = bodyA.parts[j]; | |
if (Bounds.overlaps(part.bounds, ray.bounds)) { | |
var collision = SAT.collides(part, ray); | |
if (collision.collided) { | |
collision.body = collision.bodyA = collision.bodyB = bodyA; | |
collisions.push(collision); | |
break; | |
} | |
} | |
} | |
} | |
} | |
return collisions; | |
}; | |
/** | |
* Returns all bodies whose bounds are inside (or outside if set) the given set of bounds, from the given set of bodies. | |
* @method region | |
* @param {body[]} bodies | |
* @param {bounds} bounds | |
* @param {bool} [outside=false] | |
* @return {body[]} The bodies matching the query | |
*/ | |
Query.region = function(bodies, bounds, outside) { | |
var result = []; | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i], | |
overlaps = Bounds.overlaps(body.bounds, bounds); | |
if ((overlaps && !outside) || (!overlaps && outside)) | |
result.push(body); | |
} | |
return result; | |
}; | |
/** | |
* Returns all bodies whose vertices contain the given point, from the given set of bodies. | |
* @method point | |
* @param {body[]} bodies | |
* @param {vector} point | |
* @return {body[]} The bodies matching the query | |
*/ | |
Query.point = function(bodies, point) { | |
var result = []; | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (Bounds.contains(body.bounds, point)) { | |
for (var j = body.parts.length === 1 ? 0 : 1; j < body.parts.length; j++) { | |
var part = body.parts[j]; | |
if (Bounds.contains(part.bounds, point) | |
&& Vertices.contains(part.vertices, point)) { | |
result.push(body); | |
break; | |
} | |
} | |
} | |
} | |
return result; | |
}; | |
})(); | |
},{"../factory/Bodies":23,"../geometry/Bounds":26,"../geometry/Vector":28,"../geometry/Vertices":29,"./SAT":11}],10:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Resolver` module contains methods for resolving collision pairs. | |
* | |
* @class Resolver | |
*/ | |
var Resolver = {}; | |
module.exports = Resolver; | |
var Vertices = _dereq_('../geometry/Vertices'); | |
var Vector = _dereq_('../geometry/Vector'); | |
var Common = _dereq_('../core/Common'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
(function() { | |
Resolver._restingThresh = 4; | |
Resolver._restingThreshTangent = 6; | |
Resolver._positionDampen = 0.9; | |
Resolver._positionWarming = 0.8; | |
Resolver._frictionNormalMultiplier = 5; | |
/** | |
* Prepare pairs for position solving. | |
* @method preSolvePosition | |
* @param {pair[]} pairs | |
*/ | |
Resolver.preSolvePosition = function(pairs) { | |
var i, | |
pair, | |
activeCount; | |
// find total contacts on each body | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive) | |
continue; | |
activeCount = pair.activeContacts.length; | |
pair.collision.parentA.totalContacts += activeCount; | |
pair.collision.parentB.totalContacts += activeCount; | |
} | |
}; | |
/** | |
* Find a solution for pair positions. | |
* @method solvePosition | |
* @param {pair[]} pairs | |
* @param {number} timeScale | |
*/ | |
Resolver.solvePosition = function(pairs, timeScale) { | |
var i, | |
pair, | |
collision, | |
bodyA, | |
bodyB, | |
normal, | |
bodyBtoA, | |
contactShare, | |
positionImpulse, | |
contactCount = {}, | |
tempA = Vector._temp[0], | |
tempB = Vector._temp[1], | |
tempC = Vector._temp[2], | |
tempD = Vector._temp[3]; | |
// find impulses required to resolve penetration | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive || pair.isSensor) | |
continue; | |
collision = pair.collision; | |
bodyA = collision.parentA; | |
bodyB = collision.parentB; | |
normal = collision.normal; | |
// get current separation between body edges involved in collision | |
bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA), | |
Vector.add(bodyA.positionImpulse, | |
Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD); | |
pair.separation = Vector.dot(normal, bodyBtoA); | |
} | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive || pair.isSensor) | |
continue; | |
collision = pair.collision; | |
bodyA = collision.parentA; | |
bodyB = collision.parentB; | |
normal = collision.normal; | |
positionImpulse = (pair.separation - pair.slop) * timeScale; | |
if (bodyA.isStatic || bodyB.isStatic) | |
positionImpulse *= 2; | |
if (!(bodyA.isStatic || bodyA.isSleeping)) { | |
contactShare = Resolver._positionDampen / bodyA.totalContacts; | |
bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare; | |
bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare; | |
} | |
if (!(bodyB.isStatic || bodyB.isSleeping)) { | |
contactShare = Resolver._positionDampen / bodyB.totalContacts; | |
bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare; | |
bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare; | |
} | |
} | |
}; | |
/** | |
* Apply position resolution. | |
* @method postSolvePosition | |
* @param {body[]} bodies | |
*/ | |
Resolver.postSolvePosition = function(bodies) { | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
// reset contact count | |
body.totalContacts = 0; | |
if (body.positionImpulse.x !== 0 || body.positionImpulse.y !== 0) { | |
// update body geometry | |
for (var j = 0; j < body.parts.length; j++) { | |
var part = body.parts[j]; | |
Vertices.translate(part.vertices, body.positionImpulse); | |
Bounds.update(part.bounds, part.vertices, body.velocity); | |
part.position.x += body.positionImpulse.x; | |
part.position.y += body.positionImpulse.y; | |
} | |
// move the body without changing velocity | |
body.positionPrev.x += body.positionImpulse.x; | |
body.positionPrev.y += body.positionImpulse.y; | |
if (Vector.dot(body.positionImpulse, body.velocity) < 0) { | |
// reset cached impulse if the body has velocity along it | |
body.positionImpulse.x = 0; | |
body.positionImpulse.y = 0; | |
} else { | |
// warm the next iteration | |
body.positionImpulse.x *= Resolver._positionWarming; | |
body.positionImpulse.y *= Resolver._positionWarming; | |
} | |
} | |
} | |
}; | |
/** | |
* Prepare pairs for velocity solving. | |
* @method preSolveVelocity | |
* @param {pair[]} pairs | |
*/ | |
Resolver.preSolveVelocity = function(pairs) { | |
var i, | |
j, | |
pair, | |
contacts, | |
collision, | |
bodyA, | |
bodyB, | |
normal, | |
tangent, | |
contact, | |
contactVertex, | |
normalImpulse, | |
tangentImpulse, | |
offset, | |
impulse = Vector._temp[0], | |
tempA = Vector._temp[1]; | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive || pair.isSensor) | |
continue; | |
contacts = pair.activeContacts; | |
collision = pair.collision; | |
bodyA = collision.parentA; | |
bodyB = collision.parentB; | |
normal = collision.normal; | |
tangent = collision.tangent; | |
// resolve each contact | |
for (j = 0; j < contacts.length; j++) { | |
contact = contacts[j]; | |
contactVertex = contact.vertex; | |
normalImpulse = contact.normalImpulse; | |
tangentImpulse = contact.tangentImpulse; | |
if (normalImpulse !== 0 || tangentImpulse !== 0) { | |
// total impulse from contact | |
impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse); | |
impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse); | |
// apply impulse from contact | |
if (!(bodyA.isStatic || bodyA.isSleeping)) { | |
offset = Vector.sub(contactVertex, bodyA.position, tempA); | |
bodyA.positionPrev.x += impulse.x * bodyA.inverseMass; | |
bodyA.positionPrev.y += impulse.y * bodyA.inverseMass; | |
bodyA.anglePrev += Vector.cross(offset, impulse) * bodyA.inverseInertia; | |
} | |
if (!(bodyB.isStatic || bodyB.isSleeping)) { | |
offset = Vector.sub(contactVertex, bodyB.position, tempA); | |
bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass; | |
bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass; | |
bodyB.anglePrev -= Vector.cross(offset, impulse) * bodyB.inverseInertia; | |
} | |
} | |
} | |
} | |
}; | |
/** | |
* Find a solution for pair velocities. | |
* @method solveVelocity | |
* @param {pair[]} pairs | |
* @param {number} timeScale | |
*/ | |
Resolver.solveVelocity = function(pairs, timeScale) { | |
var timeScaleSquared = timeScale * timeScale, | |
impulse = Vector._temp[0], | |
tempA = Vector._temp[1], | |
tempB = Vector._temp[2], | |
tempC = Vector._temp[3], | |
tempD = Vector._temp[4], | |
tempE = Vector._temp[5]; | |
for (var i = 0; i < pairs.length; i++) { | |
var pair = pairs[i]; | |
if (!pair.isActive || pair.isSensor) | |
continue; | |
var collision = pair.collision, | |
bodyA = collision.parentA, | |
bodyB = collision.parentB, | |
normal = collision.normal, | |
tangent = collision.tangent, | |
contacts = pair.activeContacts, | |
contactShare = 1 / contacts.length; | |
// update body velocities | |
bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x; | |
bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y; | |
bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x; | |
bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y; | |
bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev; | |
bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev; | |
// resolve each contact | |
for (var j = 0; j < contacts.length; j++) { | |
var contact = contacts[j], | |
contactVertex = contact.vertex, | |
offsetA = Vector.sub(contactVertex, bodyA.position, tempA), | |
offsetB = Vector.sub(contactVertex, bodyB.position, tempB), | |
velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity), tempC), | |
velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity), tempD), | |
relativeVelocity = Vector.sub(velocityPointA, velocityPointB, tempE), | |
normalVelocity = Vector.dot(normal, relativeVelocity); | |
var tangentVelocity = Vector.dot(tangent, relativeVelocity), | |
tangentSpeed = Math.abs(tangentVelocity), | |
tangentVelocityDirection = Common.sign(tangentVelocity); | |
// raw impulses | |
var normalImpulse = (1 + pair.restitution) * normalVelocity, | |
normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * Resolver._frictionNormalMultiplier; | |
// coulomb friction | |
var tangentImpulse = tangentVelocity, | |
maxFriction = Infinity; | |
if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) { | |
maxFriction = tangentSpeed; | |
tangentImpulse = Common.clamp( | |
pair.friction * tangentVelocityDirection * timeScaleSquared, | |
-maxFriction, maxFriction | |
); | |
} | |
// modify impulses accounting for mass, inertia and offset | |
var oAcN = Vector.cross(offsetA, normal), | |
oBcN = Vector.cross(offsetB, normal), | |
share = contactShare / (bodyA.inverseMass + bodyB.inverseMass + bodyA.inverseInertia * oAcN * oAcN + bodyB.inverseInertia * oBcN * oBcN); | |
normalImpulse *= share; | |
tangentImpulse *= share; | |
// handle high velocity and resting collisions separately | |
if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) { | |
// high normal velocity so clear cached contact normal impulse | |
contact.normalImpulse = 0; | |
} else { | |
// solve resting collision constraints using Erin Catto's method (GDC08) | |
// impulse constraint tends to 0 | |
var contactNormalImpulse = contact.normalImpulse; | |
contact.normalImpulse = Math.min(contact.normalImpulse + normalImpulse, 0); | |
normalImpulse = contact.normalImpulse - contactNormalImpulse; | |
} | |
// handle high velocity and resting collisions separately | |
if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) { | |
// high tangent velocity so clear cached contact tangent impulse | |
contact.tangentImpulse = 0; | |
} else { | |
// solve resting collision constraints using Erin Catto's method (GDC08) | |
// tangent impulse tends to -tangentSpeed or +tangentSpeed | |
var contactTangentImpulse = contact.tangentImpulse; | |
contact.tangentImpulse = Common.clamp(contact.tangentImpulse + tangentImpulse, -maxFriction, maxFriction); | |
tangentImpulse = contact.tangentImpulse - contactTangentImpulse; | |
} | |
// total impulse from contact | |
impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse); | |
impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse); | |
// apply impulse from contact | |
if (!(bodyA.isStatic || bodyA.isSleeping)) { | |
bodyA.positionPrev.x += impulse.x * bodyA.inverseMass; | |
bodyA.positionPrev.y += impulse.y * bodyA.inverseMass; | |
bodyA.anglePrev += Vector.cross(offsetA, impulse) * bodyA.inverseInertia; | |
} | |
if (!(bodyB.isStatic || bodyB.isSleeping)) { | |
bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass; | |
bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass; | |
bodyB.anglePrev -= Vector.cross(offsetB, impulse) * bodyB.inverseInertia; | |
} | |
} | |
} | |
}; | |
})(); | |
},{"../core/Common":14,"../geometry/Bounds":26,"../geometry/Vector":28,"../geometry/Vertices":29}],11:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.SAT` module contains methods for detecting collisions using the Separating Axis Theorem. | |
* | |
* @class SAT | |
*/ | |
// TODO: true circles and curves | |
var SAT = {}; | |
module.exports = SAT; | |
var Vertices = _dereq_('../geometry/Vertices'); | |
var Vector = _dereq_('../geometry/Vector'); | |
(function() { | |
/** | |
* Detect collision between two bodies using the Separating Axis Theorem. | |
* @method collides | |
* @param {body} bodyA | |
* @param {body} bodyB | |
* @param {collision} previousCollision | |
* @return {collision} collision | |
*/ | |
SAT.collides = function(bodyA, bodyB, previousCollision) { | |
var overlapAB, | |
overlapBA, | |
minOverlap, | |
collision, | |
canReusePrevCol = false; | |
if (previousCollision) { | |
// estimate total motion | |
var parentA = bodyA.parent, | |
parentB = bodyB.parent, | |
motion = parentA.speed * parentA.speed + parentA.angularSpeed * parentA.angularSpeed | |
+ parentB.speed * parentB.speed + parentB.angularSpeed * parentB.angularSpeed; | |
// we may be able to (partially) reuse collision result | |
// but only safe if collision was resting | |
canReusePrevCol = previousCollision && previousCollision.collided && motion < 0.2; | |
// reuse collision object | |
collision = previousCollision; | |
} else { | |
collision = { collided: false, bodyA: bodyA, bodyB: bodyB }; | |
} | |
if (previousCollision && canReusePrevCol) { | |
// if we can reuse the collision result | |
// we only need to test the previously found axis | |
var axisBodyA = collision.axisBody, | |
axisBodyB = axisBodyA === bodyA ? bodyB : bodyA, | |
axes = [axisBodyA.axes[previousCollision.axisNumber]]; | |
minOverlap = _overlapAxes(axisBodyA.vertices, axisBodyB.vertices, axes); | |
collision.reused = true; | |
if (minOverlap.overlap <= 0) { | |
collision.collided = false; | |
return collision; | |
} | |
} else { | |
// if we can't reuse a result, perform a full SAT test | |
overlapAB = _overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes); | |
if (overlapAB.overlap <= 0) { | |
collision.collided = false; | |
return collision; | |
} | |
overlapBA = _overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes); | |
if (overlapBA.overlap <= 0) { | |
collision.collided = false; | |
return collision; | |
} | |
if (overlapAB.overlap < overlapBA.overlap) { | |
minOverlap = overlapAB; | |
collision.axisBody = bodyA; | |
} else { | |
minOverlap = overlapBA; | |
collision.axisBody = bodyB; | |
} | |
// important for reuse later | |
collision.axisNumber = minOverlap.axisNumber; | |
} | |
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB; | |
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA; | |
collision.collided = true; | |
collision.depth = minOverlap.overlap; | |
collision.parentA = collision.bodyA.parent; | |
collision.parentB = collision.bodyB.parent; | |
bodyA = collision.bodyA; | |
bodyB = collision.bodyB; | |
// ensure normal is facing away from bodyA | |
if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) { | |
collision.normal = { | |
x: minOverlap.axis.x, | |
y: minOverlap.axis.y | |
}; | |
} else { | |
collision.normal = { | |
x: -minOverlap.axis.x, | |
y: -minOverlap.axis.y | |
}; | |
} | |
collision.tangent = Vector.perp(collision.normal); | |
collision.penetration = collision.penetration || {}; | |
collision.penetration.x = collision.normal.x * collision.depth; | |
collision.penetration.y = collision.normal.y * collision.depth; | |
// find support points, there is always either exactly one or two | |
var verticesB = _findSupports(bodyA, bodyB, collision.normal), | |
supports = []; | |
// find the supports from bodyB that are inside bodyA | |
if (Vertices.contains(bodyA.vertices, verticesB[0])) | |
supports.push(verticesB[0]); | |
if (Vertices.contains(bodyA.vertices, verticesB[1])) | |
supports.push(verticesB[1]); | |
// find the supports from bodyA that are inside bodyB | |
if (supports.length < 2) { | |
var verticesA = _findSupports(bodyB, bodyA, Vector.neg(collision.normal)); | |
if (Vertices.contains(bodyB.vertices, verticesA[0])) | |
supports.push(verticesA[0]); | |
if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1])) | |
supports.push(verticesA[1]); | |
} | |
// account for the edge case of overlapping but no vertex containment | |
if (supports.length < 1) | |
supports = [verticesB[0]]; | |
collision.supports = supports; | |
return collision; | |
}; | |
/** | |
* Find the overlap between two sets of vertices. | |
* @method _overlapAxes | |
* @private | |
* @param {} verticesA | |
* @param {} verticesB | |
* @param {} axes | |
* @return result | |
*/ | |
var _overlapAxes = function(verticesA, verticesB, axes) { | |
var projectionA = Vector._temp[0], | |
projectionB = Vector._temp[1], | |
result = { overlap: Number.MAX_VALUE }, | |
overlap, | |
axis; | |
for (var i = 0; i < axes.length; i++) { | |
axis = axes[i]; | |
_projectToAxis(projectionA, verticesA, axis); | |
_projectToAxis(projectionB, verticesB, axis); | |
overlap = Math.min(projectionA.max - projectionB.min, projectionB.max - projectionA.min); | |
if (overlap <= 0) { | |
result.overlap = overlap; | |
return result; | |
} | |
if (overlap < result.overlap) { | |
result.overlap = overlap; | |
result.axis = axis; | |
result.axisNumber = i; | |
} | |
} | |
return result; | |
}; | |
/** | |
* Projects vertices on an axis and returns an interval. | |
* @method _projectToAxis | |
* @private | |
* @param {} projection | |
* @param {} vertices | |
* @param {} axis | |
*/ | |
var _projectToAxis = function(projection, vertices, axis) { | |
var min = Vector.dot(vertices[0], axis), | |
max = min; | |
for (var i = 1; i < vertices.length; i += 1) { | |
var dot = Vector.dot(vertices[i], axis); | |
if (dot > max) { | |
max = dot; | |
} else if (dot < min) { | |
min = dot; | |
} | |
} | |
projection.min = min; | |
projection.max = max; | |
}; | |
/** | |
* Finds supporting vertices given two bodies along a given direction using hill-climbing. | |
* @method _findSupports | |
* @private | |
* @param {} bodyA | |
* @param {} bodyB | |
* @param {} normal | |
* @return [vector] | |
*/ | |
var _findSupports = function(bodyA, bodyB, normal) { | |
var nearestDistance = Number.MAX_VALUE, | |
vertexToBody = Vector._temp[0], | |
vertices = bodyB.vertices, | |
bodyAPosition = bodyA.position, | |
distance, | |
vertex, | |
vertexA, | |
vertexB; | |
// find closest vertex on bodyB | |
for (var i = 0; i < vertices.length; i++) { | |
vertex = vertices[i]; | |
vertexToBody.x = vertex.x - bodyAPosition.x; | |
vertexToBody.y = vertex.y - bodyAPosition.y; | |
distance = -Vector.dot(normal, vertexToBody); | |
if (distance < nearestDistance) { | |
nearestDistance = distance; | |
vertexA = vertex; | |
} | |
} | |
// find next closest vertex using the two connected to it | |
var prevIndex = vertexA.index - 1 >= 0 ? vertexA.index - 1 : vertices.length - 1; | |
vertex = vertices[prevIndex]; | |
vertexToBody.x = vertex.x - bodyAPosition.x; | |
vertexToBody.y = vertex.y - bodyAPosition.y; | |
nearestDistance = -Vector.dot(normal, vertexToBody); | |
vertexB = vertex; | |
var nextIndex = (vertexA.index + 1) % vertices.length; | |
vertex = vertices[nextIndex]; | |
vertexToBody.x = vertex.x - bodyAPosition.x; | |
vertexToBody.y = vertex.y - bodyAPosition.y; | |
distance = -Vector.dot(normal, vertexToBody); | |
if (distance < nearestDistance) { | |
vertexB = vertex; | |
} | |
return [vertexA, vertexB]; | |
}; | |
})(); | |
},{"../geometry/Vector":28,"../geometry/Vertices":29}],12:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Constraint` module contains methods for creating and manipulating constraints. | |
* Constraints are used for specifying that a fixed distance must be maintained between two bodies (or a body and a fixed world-space position). | |
* The stiffness of constraints can be modified to create springs or elastic. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Constraint | |
*/ | |
var Constraint = {}; | |
module.exports = Constraint; | |
var Vertices = _dereq_('../geometry/Vertices'); | |
var Vector = _dereq_('../geometry/Vector'); | |
var Sleeping = _dereq_('../core/Sleeping'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
var Axes = _dereq_('../geometry/Axes'); | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
Constraint._warming = 0.4; | |
Constraint._torqueDampen = 1; | |
Constraint._minLength = 0.000001; | |
/** | |
* Creates a new constraint. | |
* All properties have default values, and many are pre-calculated automatically based on other properties. | |
* To simulate a revolute constraint (or pin joint) set `length: 0` and a high `stiffness` value (e.g. `0.7` or above). | |
* If the constraint is unstable, try lowering the `stiffness` value and / or increasing `engine.constraintIterations`. | |
* See the properties section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @param {} options | |
* @return {constraint} constraint | |
*/ | |
Constraint.create = function(options) { | |
var constraint = options; | |
// if bodies defined but no points, use body centre | |
if (constraint.bodyA && !constraint.pointA) | |
constraint.pointA = { x: 0, y: 0 }; | |
if (constraint.bodyB && !constraint.pointB) | |
constraint.pointB = { x: 0, y: 0 }; | |
// calculate static length using initial world space points | |
var initialPointA = constraint.bodyA ? Vector.add(constraint.bodyA.position, constraint.pointA) : constraint.pointA, | |
initialPointB = constraint.bodyB ? Vector.add(constraint.bodyB.position, constraint.pointB) : constraint.pointB, | |
length = Vector.magnitude(Vector.sub(initialPointA, initialPointB)); | |
constraint.length = typeof constraint.length !== 'undefined' ? constraint.length : length; | |
// option defaults | |
constraint.id = constraint.id || Common.nextId(); | |
constraint.label = constraint.label || 'Constraint'; | |
constraint.type = 'constraint'; | |
constraint.stiffness = constraint.stiffness || (constraint.length > 0 ? 1 : 0.7); | |
constraint.damping = constraint.damping || 0; | |
constraint.angularStiffness = constraint.angularStiffness || 0; | |
constraint.angleA = constraint.bodyA ? constraint.bodyA.angle : constraint.angleA; | |
constraint.angleB = constraint.bodyB ? constraint.bodyB.angle : constraint.angleB; | |
constraint.plugin = {}; | |
// render | |
var render = { | |
visible: true, | |
lineWidth: 2, | |
strokeStyle: '#ffffff', | |
type: 'line', | |
anchors: true | |
}; | |
if (constraint.length === 0 && constraint.stiffness > 0.1) { | |
render.type = 'pin'; | |
render.anchors = false; | |
} else if (constraint.stiffness < 0.9) { | |
render.type = 'spring'; | |
} | |
constraint.render = Common.extend(render, constraint.render); | |
return constraint; | |
}; | |
/** | |
* Prepares for solving by constraint warming. | |
* @private | |
* @method preSolveAll | |
* @param {body[]} bodies | |
*/ | |
Constraint.preSolveAll = function(bodies) { | |
for (var i = 0; i < bodies.length; i += 1) { | |
var body = bodies[i], | |
impulse = body.constraintImpulse; | |
if (body.isStatic || (impulse.x === 0 && impulse.y === 0 && impulse.angle === 0)) { | |
continue; | |
} | |
body.position.x += impulse.x; | |
body.position.y += impulse.y; | |
body.angle += impulse.angle; | |
} | |
}; | |
/** | |
* Solves all constraints in a list of collisions. | |
* @private | |
* @method solveAll | |
* @param {constraint[]} constraints | |
* @param {number} timeScale | |
*/ | |
Constraint.solveAll = function(constraints, timeScale) { | |
// Solve fixed constraints first. | |
for (var i = 0; i < constraints.length; i += 1) { | |
var constraint = constraints[i], | |
fixedA = !constraint.bodyA || (constraint.bodyA && constraint.bodyA.isStatic), | |
fixedB = !constraint.bodyB || (constraint.bodyB && constraint.bodyB.isStatic); | |
if (fixedA || fixedB) { | |
Constraint.solve(constraints[i], timeScale); | |
} | |
} | |
// Solve free constraints last. | |
for (i = 0; i < constraints.length; i += 1) { | |
constraint = constraints[i]; | |
fixedA = !constraint.bodyA || (constraint.bodyA && constraint.bodyA.isStatic); | |
fixedB = !constraint.bodyB || (constraint.bodyB && constraint.bodyB.isStatic); | |
if (!fixedA && !fixedB) { | |
Constraint.solve(constraints[i], timeScale); | |
} | |
} | |
}; | |
/** | |
* Solves a distance constraint with Gauss-Siedel method. | |
* @private | |
* @method solve | |
* @param {constraint} constraint | |
* @param {number} timeScale | |
*/ | |
Constraint.solve = function(constraint, timeScale) { | |
var bodyA = constraint.bodyA, | |
bodyB = constraint.bodyB, | |
pointA = constraint.pointA, | |
pointB = constraint.pointB; | |
if (!bodyA && !bodyB) | |
return; | |
// update reference angle | |
if (bodyA && !bodyA.isStatic) { | |
Vector.rotate(pointA, bodyA.angle - constraint.angleA, pointA); | |
constraint.angleA = bodyA.angle; | |
} | |
// update reference angle | |
if (bodyB && !bodyB.isStatic) { | |
Vector.rotate(pointB, bodyB.angle - constraint.angleB, pointB); | |
constraint.angleB = bodyB.angle; | |
} | |
var pointAWorld = pointA, | |
pointBWorld = pointB; | |
if (bodyA) pointAWorld = Vector.add(bodyA.position, pointA); | |
if (bodyB) pointBWorld = Vector.add(bodyB.position, pointB); | |
if (!pointAWorld || !pointBWorld) | |
return; | |
var delta = Vector.sub(pointAWorld, pointBWorld), | |
currentLength = Vector.magnitude(delta); | |
// prevent singularity | |
if (currentLength < Constraint._minLength) { | |
currentLength = Constraint._minLength; | |
} | |
// solve distance constraint with Gauss-Siedel method | |
var difference = (currentLength - constraint.length) / currentLength, | |
stiffness = constraint.stiffness < 1 ? constraint.stiffness * timeScale : constraint.stiffness, | |
force = Vector.mult(delta, difference * stiffness), | |
massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0), | |
inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0), | |
resistanceTotal = massTotal + inertiaTotal, | |
torque, | |
share, | |
normal, | |
normalVelocity, | |
relativeVelocity; | |
if (constraint.damping) { | |
var zero = Vector.create(); | |
normal = Vector.div(delta, currentLength); | |
relativeVelocity = Vector.sub( | |
bodyB && Vector.sub(bodyB.position, bodyB.positionPrev) || zero, | |
bodyA && Vector.sub(bodyA.position, bodyA.positionPrev) || zero | |
); | |
normalVelocity = Vector.dot(normal, relativeVelocity); | |
} | |
if (bodyA && !bodyA.isStatic) { | |
share = bodyA.inverseMass / massTotal; | |
// keep track of applied impulses for post solving | |
bodyA.constraintImpulse.x -= force.x * share; | |
bodyA.constraintImpulse.y -= force.y * share; | |
// apply forces | |
bodyA.position.x -= force.x * share; | |
bodyA.position.y -= force.y * share; | |
// apply damping | |
if (constraint.damping) { | |
bodyA.positionPrev.x -= constraint.damping * normal.x * normalVelocity * share; | |
bodyA.positionPrev.y -= constraint.damping * normal.y * normalVelocity * share; | |
} | |
// apply torque | |
torque = (Vector.cross(pointA, force) / resistanceTotal) * Constraint._torqueDampen * bodyA.inverseInertia * (1 - constraint.angularStiffness); | |
bodyA.constraintImpulse.angle -= torque; | |
bodyA.angle -= torque; | |
} | |
if (bodyB && !bodyB.isStatic) { | |
share = bodyB.inverseMass / massTotal; | |
// keep track of applied impulses for post solving | |
bodyB.constraintImpulse.x += force.x * share; | |
bodyB.constraintImpulse.y += force.y * share; | |
// apply forces | |
bodyB.position.x += force.x * share; | |
bodyB.position.y += force.y * share; | |
// apply damping | |
if (constraint.damping) { | |
bodyB.positionPrev.x += constraint.damping * normal.x * normalVelocity * share; | |
bodyB.positionPrev.y += constraint.damping * normal.y * normalVelocity * share; | |
} | |
// apply torque | |
torque = (Vector.cross(pointB, force) / resistanceTotal) * Constraint._torqueDampen * bodyB.inverseInertia * (1 - constraint.angularStiffness); | |
bodyB.constraintImpulse.angle += torque; | |
bodyB.angle += torque; | |
} | |
}; | |
/** | |
* Performs body updates required after solving constraints. | |
* @private | |
* @method postSolveAll | |
* @param {body[]} bodies | |
*/ | |
Constraint.postSolveAll = function(bodies) { | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i], | |
impulse = body.constraintImpulse; | |
if (body.isStatic || (impulse.x === 0 && impulse.y === 0 && impulse.angle === 0)) { | |
continue; | |
} | |
Sleeping.set(body, false); | |
// update geometry and reset | |
for (var j = 0; j < body.parts.length; j++) { | |
var part = body.parts[j]; | |
Vertices.translate(part.vertices, impulse); | |
if (j > 0) { | |
part.position.x += impulse.x; | |
part.position.y += impulse.y; | |
} | |
if (impulse.angle !== 0) { | |
Vertices.rotate(part.vertices, impulse.angle, body.position); | |
Axes.rotate(part.axes, impulse.angle); | |
if (j > 0) { | |
Vector.rotateAbout(part.position, impulse.angle, body.position, part.position); | |
} | |
} | |
Bounds.update(part.bounds, part.vertices, body.velocity); | |
} | |
// dampen the cached impulse for warming next step | |
impulse.angle *= Constraint._warming; | |
impulse.x *= Constraint._warming; | |
impulse.y *= Constraint._warming; | |
} | |
}; | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* An integer `Number` uniquely identifying number generated in `Composite.create` by `Common.nextId`. | |
* | |
* @property id | |
* @type number | |
*/ | |
/** | |
* A `String` denoting the type of object. | |
* | |
* @property type | |
* @type string | |
* @default "constraint" | |
* @readOnly | |
*/ | |
/** | |
* An arbitrary `String` name to help the user identify and manage bodies. | |
* | |
* @property label | |
* @type string | |
* @default "Constraint" | |
*/ | |
/** | |
* An `Object` that defines the rendering properties to be consumed by the module `Matter.Render`. | |
* | |
* @property render | |
* @type object | |
*/ | |
/** | |
* A flag that indicates if the constraint should be rendered. | |
* | |
* @property render.visible | |
* @type boolean | |
* @default true | |
*/ | |
/** | |
* A `Number` that defines the line width to use when rendering the constraint outline. | |
* A value of `0` means no outline will be rendered. | |
* | |
* @property render.lineWidth | |
* @type number | |
* @default 2 | |
*/ | |
/** | |
* A `String` that defines the stroke style to use when rendering the constraint outline. | |
* It is the same as when using a canvas, so it accepts CSS style property values. | |
* | |
* @property render.strokeStyle | |
* @type string | |
* @default a random colour | |
*/ | |
/** | |
* A `String` that defines the constraint rendering type. | |
* The possible values are 'line', 'pin', 'spring'. | |
* An appropriate render type will be automatically chosen unless one is given in options. | |
* | |
* @property render.type | |
* @type string | |
* @default 'line' | |
*/ | |
/** | |
* A `Boolean` that defines if the constraint's anchor points should be rendered. | |
* | |
* @property render.anchors | |
* @type boolean | |
* @default true | |
*/ | |
/** | |
* The first possible `Body` that this constraint is attached to. | |
* | |
* @property bodyA | |
* @type body | |
* @default null | |
*/ | |
/** | |
* The second possible `Body` that this constraint is attached to. | |
* | |
* @property bodyB | |
* @type body | |
* @default null | |
*/ | |
/** | |
* A `Vector` that specifies the offset of the constraint from center of the `constraint.bodyA` if defined, otherwise a world-space position. | |
* | |
* @property pointA | |
* @type vector | |
* @default { x: 0, y: 0 } | |
*/ | |
/** | |
* A `Vector` that specifies the offset of the constraint from center of the `constraint.bodyA` if defined, otherwise a world-space position. | |
* | |
* @property pointB | |
* @type vector | |
* @default { x: 0, y: 0 } | |
*/ | |
/** | |
* A `Number` that specifies the stiffness of the constraint, i.e. the rate at which it returns to its resting `constraint.length`. | |
* A value of `1` means the constraint should be very stiff. | |
* A value of `0.2` means the constraint acts like a soft spring. | |
* | |
* @property stiffness | |
* @type number | |
* @default 1 | |
*/ | |
/** | |
* A `Number` that specifies the damping of the constraint, | |
* i.e. the amount of resistance applied to each body based on their velocities to limit the amount of oscillation. | |
* Damping will only be apparent when the constraint also has a very low `stiffness`. | |
* A value of `0.1` means the constraint will apply heavy damping, resulting in little to no oscillation. | |
* A value of `0` means the constraint will apply no damping. | |
* | |
* @property damping | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* A `Number` that specifies the target resting length of the constraint. | |
* It is calculated automatically in `Constraint.create` from initial positions of the `constraint.bodyA` and `constraint.bodyB`. | |
* | |
* @property length | |
* @type number | |
*/ | |
/** | |
* An object reserved for storing plugin-specific properties. | |
* | |
* @property plugin | |
* @type {} | |
*/ | |
})(); | |
},{"../core/Common":14,"../core/Sleeping":22,"../geometry/Axes":25,"../geometry/Bounds":26,"../geometry/Vector":28,"../geometry/Vertices":29}],13:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.MouseConstraint` module contains methods for creating mouse constraints. | |
* Mouse constraints are used for allowing user interaction, providing the ability to move bodies via the mouse or touch. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class MouseConstraint | |
*/ | |
var MouseConstraint = {}; | |
module.exports = MouseConstraint; | |
var Vertices = _dereq_('../geometry/Vertices'); | |
var Sleeping = _dereq_('../core/Sleeping'); | |
var Mouse = _dereq_('../core/Mouse'); | |
var Events = _dereq_('../core/Events'); | |
var Detector = _dereq_('../collision/Detector'); | |
var Constraint = _dereq_('./Constraint'); | |
var Composite = _dereq_('../body/Composite'); | |
var Common = _dereq_('../core/Common'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
(function() { | |
/** | |
* Creates a new mouse constraint. | |
* All properties have default values, and many are pre-calculated automatically based on other properties. | |
* See the properties section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @param {engine} engine | |
* @param {} options | |
* @return {MouseConstraint} A new MouseConstraint | |
*/ | |
MouseConstraint.create = function(engine, options) { | |
var mouse = (engine ? engine.mouse : null) || (options ? options.mouse : null); | |
if (!mouse) { | |
if (engine && engine.render && engine.render.canvas) { | |
mouse = Mouse.create(engine.render.canvas); | |
} else if (options && options.element) { | |
mouse = Mouse.create(options.element); | |
} else { | |
mouse = Mouse.create(); | |
Common.warn('MouseConstraint.create: options.mouse was undefined, options.element was undefined, may not function as expected'); | |
} | |
} | |
var constraint = Constraint.create({ | |
label: 'Mouse Constraint', | |
pointA: mouse.position, | |
pointB: { x: 0, y: 0 }, | |
length: 0.01, | |
stiffness: 0.1, | |
angularStiffness: 1, | |
render: { | |
strokeStyle: '#90EE90', | |
lineWidth: 3 | |
} | |
}); | |
var defaults = { | |
type: 'mouseConstraint', | |
mouse: mouse, | |
element: null, | |
body: null, | |
constraint: constraint, | |
collisionFilter: { | |
category: 0x0001, | |
mask: 0xFFFFFFFF, | |
group: 0 | |
} | |
}; | |
var mouseConstraint = Common.extend(defaults, options); | |
Events.on(engine, 'beforeUpdate', function() { | |
var allBodies = Composite.allBodies(engine.world); | |
MouseConstraint.update(mouseConstraint, allBodies); | |
_triggerEvents(mouseConstraint); | |
}); | |
return mouseConstraint; | |
}; | |
/** | |
* Updates the given mouse constraint. | |
* @private | |
* @method update | |
* @param {MouseConstraint} mouseConstraint | |
* @param {body[]} bodies | |
*/ | |
MouseConstraint.update = function(mouseConstraint, bodies) { | |
var mouse = mouseConstraint.mouse, | |
constraint = mouseConstraint.constraint, | |
body = mouseConstraint.body; | |
if (mouse.button === 0) { | |
if (!constraint.bodyB) { | |
for (var i = 0; i < bodies.length; i++) { | |
body = bodies[i]; | |
if (Bounds.contains(body.bounds, mouse.position) | |
&& Detector.canCollide(body.collisionFilter, mouseConstraint.collisionFilter)) { | |
for (var j = body.parts.length > 1 ? 1 : 0; j < body.parts.length; j++) { | |
var part = body.parts[j]; | |
if (Vertices.contains(part.vertices, mouse.position)) { | |
constraint.pointA = mouse.position; | |
constraint.bodyB = mouseConstraint.body = body; | |
constraint.pointB = { x: mouse.position.x - body.position.x, y: mouse.position.y - body.position.y }; | |
constraint.angleB = body.angle; | |
Sleeping.set(body, false); | |
Events.trigger(mouseConstraint, 'startdrag', { mouse: mouse, body: body }); | |
break; | |
} | |
} | |
} | |
} | |
} else { | |
Sleeping.set(constraint.bodyB, false); | |
constraint.pointA = mouse.position; | |
} | |
} else { | |
constraint.bodyB = mouseConstraint.body = null; | |
constraint.pointB = null; | |
if (body) | |
Events.trigger(mouseConstraint, 'enddrag', { mouse: mouse, body: body }); | |
} | |
}; | |
/** | |
* Triggers mouse constraint events. | |
* @method _triggerEvents | |
* @private | |
* @param {mouse} mouseConstraint | |
*/ | |
var _triggerEvents = function(mouseConstraint) { | |
var mouse = mouseConstraint.mouse, | |
mouseEvents = mouse.sourceEvents; | |
if (mouseEvents.mousemove) | |
Events.trigger(mouseConstraint, 'mousemove', { mouse: mouse }); | |
if (mouseEvents.mousedown) | |
Events.trigger(mouseConstraint, 'mousedown', { mouse: mouse }); | |
if (mouseEvents.mouseup) | |
Events.trigger(mouseConstraint, 'mouseup', { mouse: mouse }); | |
// reset the mouse state ready for the next step | |
Mouse.clearSourceEvents(mouse); | |
}; | |
/* | |
* | |
* Events Documentation | |
* | |
*/ | |
/** | |
* Fired when the mouse has moved (or a touch moves) during the last step | |
* | |
* @event mousemove | |
* @param {} event An event object | |
* @param {mouse} event.mouse The engine's mouse instance | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when the mouse is down (or a touch has started) during the last step | |
* | |
* @event mousedown | |
* @param {} event An event object | |
* @param {mouse} event.mouse The engine's mouse instance | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when the mouse is up (or a touch has ended) during the last step | |
* | |
* @event mouseup | |
* @param {} event An event object | |
* @param {mouse} event.mouse The engine's mouse instance | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when the user starts dragging a body | |
* | |
* @event startdrag | |
* @param {} event An event object | |
* @param {mouse} event.mouse The engine's mouse instance | |
* @param {body} event.body The body being dragged | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired when the user ends dragging a body | |
* | |
* @event enddrag | |
* @param {} event An event object | |
* @param {mouse} event.mouse The engine's mouse instance | |
* @param {body} event.body The body that has stopped being dragged | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* A `String` denoting the type of object. | |
* | |
* @property type | |
* @type string | |
* @default "constraint" | |
* @readOnly | |
*/ | |
/** | |
* The `Mouse` instance in use. If not supplied in `MouseConstraint.create`, one will be created. | |
* | |
* @property mouse | |
* @type mouse | |
* @default mouse | |
*/ | |
/** | |
* The `Body` that is currently being moved by the user, or `null` if no body. | |
* | |
* @property body | |
* @type body | |
* @default null | |
*/ | |
/** | |
* The `Constraint` object that is used to move the body during interaction. | |
* | |
* @property constraint | |
* @type constraint | |
*/ | |
/** | |
* An `Object` that specifies the collision filter properties. | |
* The collision filter allows the user to define which types of body this mouse constraint can interact with. | |
* See `body.collisionFilter` for more information. | |
* | |
* @property collisionFilter | |
* @type object | |
*/ | |
})(); | |
},{"../body/Composite":2,"../collision/Detector":5,"../core/Common":14,"../core/Events":16,"../core/Mouse":19,"../core/Sleeping":22,"../geometry/Bounds":26,"../geometry/Vertices":29,"./Constraint":12}],14:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Common` module contains utility functions that are common to all modules. | |
* | |
* @class Common | |
*/ | |
var Common = {}; | |
module.exports = Common; | |
(function() { | |
Common._nextId = 0; | |
Common._seed = 0; | |
Common._nowStartTime = +(new Date()); | |
/** | |
* Extends the object in the first argument using the object in the second argument. | |
* @method extend | |
* @param {} obj | |
* @param {boolean} deep | |
* @return {} obj extended | |
*/ | |
Common.extend = function(obj, deep) { | |
var argsStart, | |
args, | |
deepClone; | |
if (typeof deep === 'boolean') { | |
argsStart = 2; | |
deepClone = deep; | |
} else { | |
argsStart = 1; | |
deepClone = true; | |
} | |
for (var i = argsStart; i < arguments.length; i++) { | |
var source = arguments[i]; | |
if (source) { | |
for (var prop in source) { | |
if (deepClone && source[prop] && source[prop].constructor === Object) { | |
if (!obj[prop] || obj[prop].constructor === Object) { | |
obj[prop] = obj[prop] || {}; | |
Common.extend(obj[prop], deepClone, source[prop]); | |
} else { | |
obj[prop] = source[prop]; | |
} | |
} else { | |
obj[prop] = source[prop]; | |
} | |
} | |
} | |
} | |
return obj; | |
}; | |
/** | |
* Creates a new clone of the object, if deep is true references will also be cloned. | |
* @method clone | |
* @param {} obj | |
* @param {bool} deep | |
* @return {} obj cloned | |
*/ | |
Common.clone = function(obj, deep) { | |
return Common.extend({}, deep, obj); | |
}; | |
/** | |
* Returns the list of keys for the given object. | |
* @method keys | |
* @param {} obj | |
* @return {string[]} keys | |
*/ | |
Common.keys = function(obj) { | |
if (Object.keys) | |
return Object.keys(obj); | |
// avoid hasOwnProperty for performance | |
var keys = []; | |
for (var key in obj) | |
keys.push(key); | |
return keys; | |
}; | |
/** | |
* Returns the list of values for the given object. | |
* @method values | |
* @param {} obj | |
* @return {array} Array of the objects property values | |
*/ | |
Common.values = function(obj) { | |
var values = []; | |
if (Object.keys) { | |
var keys = Object.keys(obj); | |
for (var i = 0; i < keys.length; i++) { | |
values.push(obj[keys[i]]); | |
} | |
return values; | |
} | |
// avoid hasOwnProperty for performance | |
for (var key in obj) | |
values.push(obj[key]); | |
return values; | |
}; | |
/** | |
* Gets a value from `base` relative to the `path` string. | |
* @method get | |
* @param {} obj The base object | |
* @param {string} path The path relative to `base`, e.g. 'Foo.Bar.baz' | |
* @param {number} [begin] Path slice begin | |
* @param {number} [end] Path slice end | |
* @return {} The object at the given path | |
*/ | |
Common.get = function(obj, path, begin, end) { | |
path = path.split('.').slice(begin, end); | |
for (var i = 0; i < path.length; i += 1) { | |
obj = obj[path[i]]; | |
} | |
return obj; | |
}; | |
/** | |
* Sets a value on `base` relative to the given `path` string. | |
* @method set | |
* @param {} obj The base object | |
* @param {string} path The path relative to `base`, e.g. 'Foo.Bar.baz' | |
* @param {} val The value to set | |
* @param {number} [begin] Path slice begin | |
* @param {number} [end] Path slice end | |
* @return {} Pass through `val` for chaining | |
*/ | |
Common.set = function(obj, path, val, begin, end) { | |
var parts = path.split('.').slice(begin, end); | |
Common.get(obj, path, 0, -1)[parts[parts.length - 1]] = val; | |
return val; | |
}; | |
/** | |
* Shuffles the given array in-place. | |
* The function uses a seeded random generator. | |
* @method shuffle | |
* @param {array} array | |
* @return {array} array shuffled randomly | |
*/ | |
Common.shuffle = function(array) { | |
for (var i = array.length - 1; i > 0; i--) { | |
var j = Math.floor(Common.random() * (i + 1)); | |
var temp = array[i]; | |
array[i] = array[j]; | |
array[j] = temp; | |
} | |
return array; | |
}; | |
/** | |
* Randomly chooses a value from a list with equal probability. | |
* The function uses a seeded random generator. | |
* @method choose | |
* @param {array} choices | |
* @return {object} A random choice object from the array | |
*/ | |
Common.choose = function(choices) { | |
return choices[Math.floor(Common.random() * choices.length)]; | |
}; | |
/** | |
* Returns true if the object is a HTMLElement, otherwise false. | |
* @method isElement | |
* @param {object} obj | |
* @return {boolean} True if the object is a HTMLElement, otherwise false | |
*/ | |
Common.isElement = function(obj) { | |
return obj instanceof HTMLElement; | |
}; | |
/** | |
* Returns true if the object is an array. | |
* @method isArray | |
* @param {object} obj | |
* @return {boolean} True if the object is an array, otherwise false | |
*/ | |
Common.isArray = function(obj) { | |
return Object.prototype.toString.call(obj) === '[object Array]'; | |
}; | |
/** | |
* Returns true if the object is a function. | |
* @method isFunction | |
* @param {object} obj | |
* @return {boolean} True if the object is a function, otherwise false | |
*/ | |
Common.isFunction = function(obj) { | |
return typeof obj === "function"; | |
}; | |
/** | |
* Returns true if the object is a plain object. | |
* @method isPlainObject | |
* @param {object} obj | |
* @return {boolean} True if the object is a plain object, otherwise false | |
*/ | |
Common.isPlainObject = function(obj) { | |
return typeof obj === 'object' && obj.constructor === Object; | |
}; | |
/** | |
* Returns true if the object is a string. | |
* @method isString | |
* @param {object} obj | |
* @return {boolean} True if the object is a string, otherwise false | |
*/ | |
Common.isString = function(obj) { | |
return toString.call(obj) === '[object String]'; | |
}; | |
/** | |
* Returns the given value clamped between a minimum and maximum value. | |
* @method clamp | |
* @param {number} value | |
* @param {number} min | |
* @param {number} max | |
* @return {number} The value clamped between min and max inclusive | |
*/ | |
Common.clamp = function(value, min, max) { | |
if (value < min) | |
return min; | |
if (value > max) | |
return max; | |
return value; | |
}; | |
/** | |
* Returns the sign of the given value. | |
* @method sign | |
* @param {number} value | |
* @return {number} -1 if negative, +1 if 0 or positive | |
*/ | |
Common.sign = function(value) { | |
return value < 0 ? -1 : 1; | |
}; | |
/** | |
* Returns the current timestamp since the time origin (e.g. from page load). | |
* The result will be high-resolution including decimal places if available. | |
* @method now | |
* @return {number} the current timestamp | |
*/ | |
Common.now = function() { | |
if (window.performance) { | |
if (window.performance.now) { | |
return window.performance.now(); | |
} else if (window.performance.webkitNow) { | |
return window.performance.webkitNow(); | |
} | |
} | |
return (new Date()) - Common._nowStartTime; | |
}; | |
/** | |
* Returns a random value between a minimum and a maximum value inclusive. | |
* The function uses a seeded random generator. | |
* @method random | |
* @param {number} min | |
* @param {number} max | |
* @return {number} A random number between min and max inclusive | |
*/ | |
Common.random = function(min, max) { | |
min = (typeof min !== "undefined") ? min : 0; | |
max = (typeof max !== "undefined") ? max : 1; | |
return min + _seededRandom() * (max - min); | |
}; | |
var _seededRandom = function() { | |
// https://en.wikipedia.org/wiki/Linear_congruential_generator | |
Common._seed = (Common._seed * 9301 + 49297) % 233280; | |
return Common._seed / 233280; | |
}; | |
/** | |
* Converts a CSS hex colour string into an integer. | |
* @method colorToNumber | |
* @param {string} colorString | |
* @return {number} An integer representing the CSS hex string | |
*/ | |
Common.colorToNumber = function(colorString) { | |
colorString = colorString.replace('#',''); | |
if (colorString.length == 3) { | |
colorString = colorString.charAt(0) + colorString.charAt(0) | |
+ colorString.charAt(1) + colorString.charAt(1) | |
+ colorString.charAt(2) + colorString.charAt(2); | |
} | |
return parseInt(colorString, 16); | |
}; | |
/** | |
* The console logging level to use, where each level includes all levels above and excludes the levels below. | |
* The default level is 'debug' which shows all console messages. | |
* | |
* Possible level values are: | |
* - 0 = None | |
* - 1 = Debug | |
* - 2 = Info | |
* - 3 = Warn | |
* - 4 = Error | |
* @property Common.logLevel | |
* @type {Number} | |
* @default 1 | |
*/ | |
Common.logLevel = 1; | |
/** | |
* Shows a `console.log` message only if the current `Common.logLevel` allows it. | |
* The message will be prefixed with 'matter-js' to make it easily identifiable. | |
* @method log | |
* @param ...objs {} The objects to log. | |
*/ | |
Common.log = function() { | |
if (console && Common.logLevel > 0 && Common.logLevel <= 3) { | |
console.log.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); | |
} | |
}; | |
/** | |
* Shows a `console.info` message only if the current `Common.logLevel` allows it. | |
* The message will be prefixed with 'matter-js' to make it easily identifiable. | |
* @method info | |
* @param ...objs {} The objects to log. | |
*/ | |
Common.info = function() { | |
if (console && Common.logLevel > 0 && Common.logLevel <= 2) { | |
console.info.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); | |
} | |
}; | |
/** | |
* Shows a `console.warn` message only if the current `Common.logLevel` allows it. | |
* The message will be prefixed with 'matter-js' to make it easily identifiable. | |
* @method warn | |
* @param ...objs {} The objects to log. | |
*/ | |
Common.warn = function() { | |
if (console && Common.logLevel > 0 && Common.logLevel <= 3) { | |
console.warn.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); | |
} | |
}; | |
/** | |
* Returns the next unique sequential ID. | |
* @method nextId | |
* @return {Number} Unique sequential ID | |
*/ | |
Common.nextId = function() { | |
return Common._nextId++; | |
}; | |
/** | |
* A cross browser compatible indexOf implementation. | |
* @method indexOf | |
* @param {array} haystack | |
* @param {object} needle | |
* @return {number} The position of needle in haystack, otherwise -1. | |
*/ | |
Common.indexOf = function(haystack, needle) { | |
if (haystack.indexOf) | |
return haystack.indexOf(needle); | |
for (var i = 0; i < haystack.length; i++) { | |
if (haystack[i] === needle) | |
return i; | |
} | |
return -1; | |
}; | |
/** | |
* A cross browser compatible array map implementation. | |
* @method map | |
* @param {array} list | |
* @param {function} func | |
* @return {array} Values from list transformed by func. | |
*/ | |
Common.map = function(list, func) { | |
if (list.map) { | |
return list.map(func); | |
} | |
var mapped = []; | |
for (var i = 0; i < list.length; i += 1) { | |
mapped.push(func(list[i])); | |
} | |
return mapped; | |
}; | |
/** | |
* Takes a directed graph and returns the partially ordered set of vertices in topological order. | |
* Circular dependencies are allowed. | |
* @method topologicalSort | |
* @param {object} graph | |
* @return {array} Partially ordered set of vertices in topological order. | |
*/ | |
Common.topologicalSort = function(graph) { | |
// https://github.com/mgechev/javascript-algorithms | |
// Copyright (c) Minko Gechev (MIT license) | |
// Modifications: tidy formatting and naming | |
var result = [], | |
visited = [], | |
temp = []; | |
for (var node in graph) { | |
if (!visited[node] && !temp[node]) { | |
_topologicalSort(node, visited, temp, graph, result); | |
} | |
} | |
return result; | |
}; | |
var _topologicalSort = function(node, visited, temp, graph, result) { | |
var neighbors = graph[node] || []; | |
temp[node] = true; | |
for (var i = 0; i < neighbors.length; i += 1) { | |
var neighbor = neighbors[i]; | |
if (temp[neighbor]) { | |
// skip circular dependencies | |
continue; | |
} | |
if (!visited[neighbor]) { | |
_topologicalSort(neighbor, visited, temp, graph, result); | |
} | |
} | |
temp[node] = false; | |
visited[node] = true; | |
result.push(node); | |
}; | |
/** | |
* Takes _n_ functions as arguments and returns a new function that calls them in order. | |
* The arguments applied when calling the new function will also be applied to every function passed. | |
* The value of `this` refers to the last value returned in the chain that was not `undefined`. | |
* Therefore if a passed function does not return a value, the previously returned value is maintained. | |
* After all passed functions have been called the new function returns the last returned value (if any). | |
* If any of the passed functions are a chain, then the chain will be flattened. | |
* @method chain | |
* @param ...funcs {function} The functions to chain. | |
* @return {function} A new function that calls the passed functions in order. | |
*/ | |
Common.chain = function() { | |
var funcs = []; | |
for (var i = 0; i < arguments.length; i += 1) { | |
var func = arguments[i]; | |
if (func._chained) { | |
// flatten already chained functions | |
funcs.push.apply(funcs, func._chained); | |
} else { | |
funcs.push(func); | |
} | |
} | |
var chain = function() { | |
// https://github.com/GoogleChrome/devtools-docs/issues/53#issuecomment-51941358 | |
var lastResult, | |
args = new Array(arguments.length); | |
for (var i = 0, l = arguments.length; i < l; i++) { | |
args[i] = arguments[i]; | |
} | |
for (i = 0; i < funcs.length; i += 1) { | |
var result = funcs[i].apply(lastResult, args); | |
if (typeof result !== 'undefined') { | |
lastResult = result; | |
} | |
} | |
return lastResult; | |
}; | |
chain._chained = funcs; | |
return chain; | |
}; | |
/** | |
* Chains a function to excute before the original function on the given `path` relative to `base`. | |
* See also docs for `Common.chain`. | |
* @method chainPathBefore | |
* @param {} base The base object | |
* @param {string} path The path relative to `base` | |
* @param {function} func The function to chain before the original | |
* @return {function} The chained function that replaced the original | |
*/ | |
Common.chainPathBefore = function(base, path, func) { | |
return Common.set(base, path, Common.chain( | |
func, | |
Common.get(base, path) | |
)); | |
}; | |
/** | |
* Chains a function to excute after the original function on the given `path` relative to `base`. | |
* See also docs for `Common.chain`. | |
* @method chainPathAfter | |
* @param {} base The base object | |
* @param {string} path The path relative to `base` | |
* @param {function} func The function to chain after the original | |
* @return {function} The chained function that replaced the original | |
*/ | |
Common.chainPathAfter = function(base, path, func) { | |
return Common.set(base, path, Common.chain( | |
Common.get(base, path), | |
func | |
)); | |
}; | |
})(); | |
},{}],15:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Engine` module contains methods for creating and manipulating engines. | |
* An engine is a controller that manages updating the simulation of the world. | |
* See `Matter.Runner` for an optional game loop utility. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Engine | |
*/ | |
var Engine = {}; | |
module.exports = Engine; | |
var World = _dereq_('../body/World'); | |
var Sleeping = _dereq_('./Sleeping'); | |
var Resolver = _dereq_('../collision/Resolver'); | |
var Render = _dereq_('../render/Render'); | |
var Pairs = _dereq_('../collision/Pairs'); | |
var Metrics = _dereq_('./Metrics'); | |
var Grid = _dereq_('../collision/Grid'); | |
var Events = _dereq_('./Events'); | |
var Composite = _dereq_('../body/Composite'); | |
var Constraint = _dereq_('../constraint/Constraint'); | |
var Common = _dereq_('./Common'); | |
var Body = _dereq_('../body/Body'); | |
(function() { | |
/** | |
* Creates a new engine. The options parameter is an object that specifies any properties you wish to override the defaults. | |
* All properties have default values, and many are pre-calculated automatically based on other properties. | |
* See the properties section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @param {object} [options] | |
* @return {engine} engine | |
*/ | |
Engine.create = function(element, options) { | |
// options may be passed as the first (and only) argument | |
options = Common.isElement(element) ? options : element; | |
element = Common.isElement(element) ? element : null; | |
options = options || {}; | |
if (element || options.render) { | |
Common.warn('Engine.create: engine.render is deprecated (see docs)'); | |
} | |
var defaults = { | |
positionIterations: 6, | |
velocityIterations: 4, | |
constraintIterations: 2, | |
enableSleeping: false, | |
events: [], | |
plugin: {}, | |
timing: { | |
timestamp: 0, | |
timeScale: 1 | |
}, | |
broadphase: { | |
controller: Grid | |
} | |
}; | |
var engine = Common.extend(defaults, options); | |
// @deprecated | |
if (element || engine.render) { | |
var renderDefaults = { | |
element: element, | |
controller: Render | |
}; | |
engine.render = Common.extend(renderDefaults, engine.render); | |
} | |
// @deprecated | |
if (engine.render && engine.render.controller) { | |
engine.render = engine.render.controller.create(engine.render); | |
} | |
// @deprecated | |
if (engine.render) { | |
engine.render.engine = engine; | |
} | |
engine.world = options.world || World.create(engine.world); | |
engine.pairs = Pairs.create(); | |
engine.broadphase = engine.broadphase.controller.create(engine.broadphase); | |
engine.metrics = engine.metrics || { extended: false }; | |
return engine; | |
}; | |
/** | |
* Moves the simulation forward in time by `delta` ms. | |
* The `correction` argument is an optional `Number` that specifies the time correction factor to apply to the update. | |
* This can help improve the accuracy of the simulation in cases where `delta` is changing between updates. | |
* The value of `correction` is defined as `delta / lastDelta`, i.e. the percentage change of `delta` over the last step. | |
* Therefore the value is always `1` (no correction) when `delta` constant (or when no correction is desired, which is the default). | |
* See the paper on <a href="http://lonesock.net/article/verlet.html">Time Corrected Verlet</a> for more information. | |
* | |
* Triggers `beforeUpdate` and `afterUpdate` events. | |
* Triggers `collisionStart`, `collisionActive` and `collisionEnd` events. | |
* @method update | |
* @param {engine} engine | |
* @param {number} [delta=16.666] | |
* @param {number} [correction=1] | |
*/ | |
Engine.update = function(engine, delta, correction) { | |
delta = delta || 1000 / 60; | |
correction = correction || 1; | |
var world = engine.world, | |
timing = engine.timing, | |
broadphase = engine.broadphase, | |
broadphasePairs = [], | |
i; | |
// increment timestamp | |
timing.timestamp += delta * timing.timeScale; | |
// create an event object | |
var event = { | |
timestamp: timing.timestamp | |
}; | |
Events.trigger(engine, 'beforeUpdate', event); | |
// get lists of all bodies and constraints, no matter what composites they are in | |
var allBodies = Composite.allBodies(world), | |
allConstraints = Composite.allConstraints(world); | |
// if sleeping enabled, call the sleeping controller | |
if (engine.enableSleeping) | |
Sleeping.update(allBodies, timing.timeScale); | |
// applies gravity to all bodies | |
_bodiesApplyGravity(allBodies, world.gravity); | |
// update all body position and rotation by integration | |
_bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds); | |
// update all constraints (first pass) | |
Constraint.preSolveAll(allBodies); | |
for (i = 0; i < engine.constraintIterations; i++) { | |
Constraint.solveAll(allConstraints, timing.timeScale); | |
} | |
Constraint.postSolveAll(allBodies); | |
// broadphase pass: find potential collision pairs | |
if (broadphase.controller) { | |
// if world is dirty, we must flush the whole grid | |
if (world.isModified) | |
broadphase.controller.clear(broadphase); | |
// update the grid buckets based on current bodies | |
broadphase.controller.update(broadphase, allBodies, engine, world.isModified); | |
broadphasePairs = broadphase.pairsList; | |
} else { | |
// if no broadphase set, we just pass all bodies | |
broadphasePairs = allBodies; | |
} | |
// clear all composite modified flags | |
if (world.isModified) { | |
Composite.setModified(world, false, false, true); | |
} | |
// narrowphase pass: find actual collisions, then create or update collision pairs | |
var collisions = broadphase.detector(broadphasePairs, engine); | |
// update collision pairs | |
var pairs = engine.pairs, | |
timestamp = timing.timestamp; | |
Pairs.update(pairs, collisions, timestamp); | |
Pairs.removeOld(pairs, timestamp); | |
// wake up bodies involved in collisions | |
if (engine.enableSleeping) | |
Sleeping.afterCollisions(pairs.list, timing.timeScale); | |
// trigger collision events | |
if (pairs.collisionStart.length > 0) | |
Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart }); | |
// iteratively resolve position between collisions | |
Resolver.preSolvePosition(pairs.list); | |
for (i = 0; i < engine.positionIterations; i++) { | |
Resolver.solvePosition(pairs.list, timing.timeScale); | |
} | |
Resolver.postSolvePosition(allBodies); | |
// update all constraints (second pass) | |
Constraint.preSolveAll(allBodies); | |
for (i = 0; i < engine.constraintIterations; i++) { | |
Constraint.solveAll(allConstraints, timing.timeScale); | |
} | |
Constraint.postSolveAll(allBodies); | |
// iteratively resolve velocity between collisions | |
Resolver.preSolveVelocity(pairs.list); | |
for (i = 0; i < engine.velocityIterations; i++) { | |
Resolver.solveVelocity(pairs.list, timing.timeScale); | |
} | |
// trigger collision events | |
if (pairs.collisionActive.length > 0) | |
Events.trigger(engine, 'collisionActive', { pairs: pairs.collisionActive }); | |
if (pairs.collisionEnd.length > 0) | |
Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd }); | |
// clear force buffers | |
_bodiesClearForces(allBodies); | |
Events.trigger(engine, 'afterUpdate', event); | |
return engine; | |
}; | |
/** | |
* Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`. | |
* @method merge | |
* @param {engine} engineA | |
* @param {engine} engineB | |
*/ | |
Engine.merge = function(engineA, engineB) { | |
Common.extend(engineA, engineB); | |
if (engineB.world) { | |
engineA.world = engineB.world; | |
Engine.clear(engineA); | |
var bodies = Composite.allBodies(engineA.world); | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
Sleeping.set(body, false); | |
body.id = Common.nextId(); | |
} | |
} | |
}; | |
/** | |
* Clears the engine including the world, pairs and broadphase. | |
* @method clear | |
* @param {engine} engine | |
*/ | |
Engine.clear = function(engine) { | |
var world = engine.world; | |
Pairs.clear(engine.pairs); | |
var broadphase = engine.broadphase; | |
if (broadphase.controller) { | |
var bodies = Composite.allBodies(world); | |
broadphase.controller.clear(broadphase); | |
broadphase.controller.update(broadphase, bodies, engine, true); | |
} | |
}; | |
/** | |
* Zeroes the `body.force` and `body.torque` force buffers. | |
* @method bodiesClearForces | |
* @private | |
* @param {body[]} bodies | |
*/ | |
var _bodiesClearForces = function(bodies) { | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
// reset force buffers | |
body.force.x = 0; | |
body.force.y = 0; | |
body.torque = 0; | |
} | |
}; | |
/** | |
* Applys a mass dependant force to all given bodies. | |
* @method bodiesApplyGravity | |
* @private | |
* @param {body[]} bodies | |
* @param {vector} gravity | |
*/ | |
var _bodiesApplyGravity = function(bodies, gravity) { | |
var gravityScale = typeof gravity.scale !== 'undefined' ? gravity.scale : 0.001; | |
if ((gravity.x === 0 && gravity.y === 0) || gravityScale === 0) { | |
return; | |
} | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (body.isStatic || body.isSleeping) | |
continue; | |
// apply gravity | |
body.force.y += body.mass * gravity.y * gravityScale; | |
body.force.x += body.mass * gravity.x * gravityScale; | |
} | |
}; | |
/** | |
* Applys `Body.update` to all given `bodies`. | |
* @method updateAll | |
* @private | |
* @param {body[]} bodies | |
* @param {number} deltaTime | |
* The amount of time elapsed between updates | |
* @param {number} timeScale | |
* @param {number} correction | |
* The Verlet correction factor (deltaTime / lastDeltaTime) | |
* @param {bounds} worldBounds | |
*/ | |
var _bodiesUpdate = function(bodies, deltaTime, timeScale, correction, worldBounds) { | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (body.isStatic || body.isSleeping) | |
continue; | |
Body.update(body, deltaTime, timeScale, correction); | |
} | |
}; | |
/** | |
* An alias for `Runner.run`, see `Matter.Runner` for more information. | |
* @method run | |
* @param {engine} engine | |
*/ | |
/** | |
* Fired just before an update | |
* | |
* @event beforeUpdate | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after engine update and all collision events | |
* | |
* @event afterUpdate | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any) | |
* | |
* @event collisionStart | |
* @param {} event An event object | |
* @param {} event.pairs List of affected pairs | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any) | |
* | |
* @event collisionActive | |
* @param {} event An event object | |
* @param {} event.pairs List of affected pairs | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any) | |
* | |
* @event collisionEnd | |
* @param {} event An event object | |
* @param {} event.pairs List of affected pairs | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* An integer `Number` that specifies the number of position iterations to perform each update. | |
* The higher the value, the higher quality the simulation will be at the expense of performance. | |
* | |
* @property positionIterations | |
* @type number | |
* @default 6 | |
*/ | |
/** | |
* An integer `Number` that specifies the number of velocity iterations to perform each update. | |
* The higher the value, the higher quality the simulation will be at the expense of performance. | |
* | |
* @property velocityIterations | |
* @type number | |
* @default 4 | |
*/ | |
/** | |
* An integer `Number` that specifies the number of constraint iterations to perform each update. | |
* The higher the value, the higher quality the simulation will be at the expense of performance. | |
* The default value of `2` is usually very adequate. | |
* | |
* @property constraintIterations | |
* @type number | |
* @default 2 | |
*/ | |
/** | |
* A flag that specifies whether the engine should allow sleeping via the `Matter.Sleeping` module. | |
* Sleeping can improve stability and performance, but often at the expense of accuracy. | |
* | |
* @property enableSleeping | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* An `Object` containing properties regarding the timing systems of the engine. | |
* | |
* @property timing | |
* @type object | |
*/ | |
/** | |
* A `Number` that specifies the global scaling factor of time for all bodies. | |
* A value of `0` freezes the simulation. | |
* A value of `0.1` gives a slow-motion effect. | |
* A value of `1.2` gives a speed-up effect. | |
* | |
* @property timing.timeScale | |
* @type number | |
* @default 1 | |
*/ | |
/** | |
* A `Number` that specifies the current simulation-time in milliseconds starting from `0`. | |
* It is incremented on every `Engine.update` by the given `delta` argument. | |
* | |
* @property timing.timestamp | |
* @type number | |
* @default 0 | |
*/ | |
/** | |
* An instance of a `Render` controller. The default value is a `Matter.Render` instance created by `Engine.create`. | |
* One may also develop a custom renderer module based on `Matter.Render` and pass an instance of it to `Engine.create` via `options.render`. | |
* | |
* A minimal custom renderer object must define at least three functions: `create`, `clear` and `world` (see `Matter.Render`). | |
* It is also possible to instead pass the _module_ reference via `options.render.controller` and `Engine.create` will instantiate one for you. | |
* | |
* @property render | |
* @type render | |
* @deprecated see Demo.js for an example of creating a renderer | |
* @default a Matter.Render instance | |
*/ | |
/** | |
* An instance of a broadphase controller. The default value is a `Matter.Grid` instance created by `Engine.create`. | |
* | |
* @property broadphase | |
* @type grid | |
* @default a Matter.Grid instance | |
*/ | |
/** | |
* A `World` composite object that will contain all simulated bodies and constraints. | |
* | |
* @property world | |
* @type world | |
* @default a Matter.World instance | |
*/ | |
/** | |
* An object reserved for storing plugin-specific properties. | |
* | |
* @property plugin | |
* @type {} | |
*/ | |
})(); | |
},{"../body/Body":1,"../body/Composite":2,"../body/World":3,"../collision/Grid":6,"../collision/Pairs":8,"../collision/Resolver":10,"../constraint/Constraint":12,"../render/Render":31,"./Common":14,"./Events":16,"./Metrics":18,"./Sleeping":22}],16:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Events` module contains methods to fire and listen to events on other objects. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Events | |
*/ | |
var Events = {}; | |
module.exports = Events; | |
var Common = _dereq_('./Common'); | |
(function() { | |
/** | |
* Subscribes a callback function to the given object's `eventName`. | |
* @method on | |
* @param {} object | |
* @param {string} eventNames | |
* @param {function} callback | |
*/ | |
Events.on = function(object, eventNames, callback) { | |
var names = eventNames.split(' '), | |
name; | |
for (var i = 0; i < names.length; i++) { | |
name = names[i]; | |
object.events = object.events || {}; | |
object.events[name] = object.events[name] || []; | |
object.events[name].push(callback); | |
} | |
return callback; | |
}; | |
/** | |
* Removes the given event callback. If no callback, clears all callbacks in `eventNames`. If no `eventNames`, clears all events. | |
* @method off | |
* @param {} object | |
* @param {string} eventNames | |
* @param {function} callback | |
*/ | |
Events.off = function(object, eventNames, callback) { | |
if (!eventNames) { | |
object.events = {}; | |
return; | |
} | |
// handle Events.off(object, callback) | |
if (typeof eventNames === 'function') { | |
callback = eventNames; | |
eventNames = Common.keys(object.events).join(' '); | |
} | |
var names = eventNames.split(' '); | |
for (var i = 0; i < names.length; i++) { | |
var callbacks = object.events[names[i]], | |
newCallbacks = []; | |
if (callback && callbacks) { | |
for (var j = 0; j < callbacks.length; j++) { | |
if (callbacks[j] !== callback) | |
newCallbacks.push(callbacks[j]); | |
} | |
} | |
object.events[names[i]] = newCallbacks; | |
} | |
}; | |
/** | |
* Fires all the callbacks subscribed to the given object's `eventName`, in the order they subscribed, if any. | |
* @method trigger | |
* @param {} object | |
* @param {string} eventNames | |
* @param {} event | |
*/ | |
Events.trigger = function(object, eventNames, event) { | |
var names, | |
name, | |
callbacks, | |
eventClone; | |
if (object.events) { | |
if (!event) | |
event = {}; | |
names = eventNames.split(' '); | |
for (var i = 0; i < names.length; i++) { | |
name = names[i]; | |
callbacks = object.events[name]; | |
if (callbacks) { | |
eventClone = Common.clone(event, false); | |
eventClone.name = name; | |
eventClone.source = object; | |
for (var j = 0; j < callbacks.length; j++) { | |
callbacks[j].apply(object, [eventClone]); | |
} | |
} | |
} | |
} | |
}; | |
})(); | |
},{"./Common":14}],17:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter` module is the top level namespace. It also includes a function for installing plugins on top of the library. | |
* | |
* @class Matter | |
*/ | |
var Matter = {}; | |
module.exports = Matter; | |
var Plugin = _dereq_('./Plugin'); | |
var Common = _dereq_('./Common'); | |
(function() { | |
/** | |
* The library name. | |
* @property name | |
* @readOnly | |
* @type {String} | |
*/ | |
Matter.name = 'matter-js'; | |
/** | |
* The library version. | |
* @property version | |
* @readOnly | |
* @type {String} | |
*/ | |
Matter.version = '0.13.0'; | |
/** | |
* A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`. | |
* Alternatively you may set `Matter.uses` manually and install them by calling `Plugin.use(Matter)`. | |
* @property uses | |
* @type {Array} | |
*/ | |
Matter.uses = []; | |
/** | |
* The plugins that have been installed through `Matter.Plugin.install`. Read only. | |
* @property used | |
* @readOnly | |
* @type {Array} | |
*/ | |
Matter.used = []; | |
/** | |
* Installs the given plugins on the `Matter` namespace. | |
* This is a short-hand for `Plugin.use`, see it for more information. | |
* Call this function once at the start of your code, with all of the plugins you wish to install as arguments. | |
* Avoid calling this function multiple times unless you intend to manually control installation order. | |
* @method use | |
* @param ...plugin {Function} The plugin(s) to install on `base` (multi-argument). | |
*/ | |
Matter.use = function() { | |
Plugin.use(Matter, Array.prototype.slice.call(arguments)); | |
}; | |
/** | |
* Chains a function to excute before the original function on the given `path` relative to `Matter`. | |
* See also docs for `Common.chain`. | |
* @method before | |
* @param {string} path The path relative to `Matter` | |
* @param {function} func The function to chain before the original | |
* @return {function} The chained function that replaced the original | |
*/ | |
Matter.before = function(path, func) { | |
path = path.replace(/^Matter./, ''); | |
return Common.chainPathBefore(Matter, path, func); | |
}; | |
/** | |
* Chains a function to excute after the original function on the given `path` relative to `Matter`. | |
* See also docs for `Common.chain`. | |
* @method after | |
* @param {string} path The path relative to `Matter` | |
* @param {function} func The function to chain after the original | |
* @return {function} The chained function that replaced the original | |
*/ | |
Matter.after = function(path, func) { | |
path = path.replace(/^Matter./, ''); | |
return Common.chainPathAfter(Matter, path, func); | |
}; | |
})(); | |
},{"./Common":14,"./Plugin":20}],18:[function(_dereq_,module,exports){ | |
},{"../body/Composite":2,"./Common":14}],19:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Mouse` module contains methods for creating and manipulating mouse inputs. | |
* | |
* @class Mouse | |
*/ | |
var Mouse = {}; | |
module.exports = Mouse; | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
/** | |
* Creates a mouse input. | |
* @method create | |
* @param {HTMLElement} element | |
* @return {mouse} A new mouse | |
*/ | |
Mouse.create = function(element) { | |
var mouse = {}; | |
if (!element) { | |
Common.log('Mouse.create: element was undefined, defaulting to document.body', 'warn'); | |
} | |
mouse.element = element || document.body; | |
mouse.absolute = { x: 0, y: 0 }; | |
mouse.position = { x: 0, y: 0 }; | |
mouse.mousedownPosition = { x: 0, y: 0 }; | |
mouse.mouseupPosition = { x: 0, y: 0 }; | |
mouse.offset = { x: 0, y: 0 }; | |
mouse.scale = { x: 1, y: 1 }; | |
mouse.wheelDelta = 0; | |
mouse.button = -1; | |
mouse.pixelRatio = mouse.element.getAttribute('data-pixel-ratio') || 1; | |
mouse.sourceEvents = { | |
mousemove: null, | |
mousedown: null, | |
mouseup: null, | |
mousewheel: null | |
}; | |
mouse.mousemove = function(event) { | |
var position = _getRelativeMousePosition(event, mouse.element, mouse.pixelRatio), | |
touches = event.changedTouches; | |
if (touches) { | |
mouse.button = 0; | |
event.preventDefault(); | |
} | |
mouse.absolute.x = position.x; | |
mouse.absolute.y = position.y; | |
mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x; | |
mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y; | |
mouse.sourceEvents.mousemove = event; | |
}; | |
mouse.mousedown = function(event) { | |
var position = _getRelativeMousePosition(event, mouse.element, mouse.pixelRatio), | |
touches = event.changedTouches; | |
if (touches) { | |
mouse.button = 0; | |
event.preventDefault(); | |
} else { | |
mouse.button = event.button; | |
} | |
mouse.absolute.x = position.x; | |
mouse.absolute.y = position.y; | |
mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x; | |
mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y; | |
mouse.mousedownPosition.x = mouse.position.x; | |
mouse.mousedownPosition.y = mouse.position.y; | |
mouse.sourceEvents.mousedown = event; | |
}; | |
mouse.mouseup = function(event) { | |
var position = _getRelativeMousePosition(event, mouse.element, mouse.pixelRatio), | |
touches = event.changedTouches; | |
if (touches) { | |
event.preventDefault(); | |
} | |
mouse.button = -1; | |
mouse.absolute.x = position.x; | |
mouse.absolute.y = position.y; | |
mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x; | |
mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y; | |
mouse.mouseupPosition.x = mouse.position.x; | |
mouse.mouseupPosition.y = mouse.position.y; | |
mouse.sourceEvents.mouseup = event; | |
}; | |
mouse.mousewheel = function(event) { | |
mouse.wheelDelta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail)); | |
event.preventDefault(); | |
}; | |
Mouse.setElement(mouse, mouse.element); | |
return mouse; | |
}; | |
/** | |
* Sets the element the mouse is bound to (and relative to). | |
* @method setElement | |
* @param {mouse} mouse | |
* @param {HTMLElement} element | |
*/ | |
Mouse.setElement = function(mouse, element) { | |
mouse.element = element; | |
element.addEventListener('mousemove', mouse.mousemove); | |
element.addEventListener('mousedown', mouse.mousedown); | |
element.addEventListener('mouseup', mouse.mouseup); | |
element.addEventListener('mousewheel', mouse.mousewheel); | |
element.addEventListener('DOMMouseScroll', mouse.mousewheel); | |
element.addEventListener('touchmove', mouse.mousemove); | |
element.addEventListener('touchstart', mouse.mousedown); | |
element.addEventListener('touchend', mouse.mouseup); | |
}; | |
/** | |
* Clears all captured source events. | |
* @method clearSourceEvents | |
* @param {mouse} mouse | |
*/ | |
Mouse.clearSourceEvents = function(mouse) { | |
mouse.sourceEvents.mousemove = null; | |
mouse.sourceEvents.mousedown = null; | |
mouse.sourceEvents.mouseup = null; | |
mouse.sourceEvents.mousewheel = null; | |
mouse.wheelDelta = 0; | |
}; | |
/** | |
* Sets the mouse position offset. | |
* @method setOffset | |
* @param {mouse} mouse | |
* @param {vector} offset | |
*/ | |
Mouse.setOffset = function(mouse, offset) { | |
mouse.offset.x = offset.x; | |
mouse.offset.y = offset.y; | |
mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x; | |
mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y; | |
}; | |
/** | |
* Sets the mouse position scale. | |
* @method setScale | |
* @param {mouse} mouse | |
* @param {vector} scale | |
*/ | |
Mouse.setScale = function(mouse, scale) { | |
mouse.scale.x = scale.x; | |
mouse.scale.y = scale.y; | |
mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x; | |
mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y; | |
}; | |
/** | |
* Gets the mouse position relative to an element given a screen pixel ratio. | |
* @method _getRelativeMousePosition | |
* @private | |
* @param {} event | |
* @param {} element | |
* @param {number} pixelRatio | |
* @return {} | |
*/ | |
var _getRelativeMousePosition = function(event, element, pixelRatio) { | |
var elementBounds = element.getBoundingClientRect(), | |
rootNode = (document.documentElement || document.body.parentNode || document.body), | |
scrollX = (window.pageXOffset !== undefined) ? window.pageXOffset : rootNode.scrollLeft, | |
scrollY = (window.pageYOffset !== undefined) ? window.pageYOffset : rootNode.scrollTop, | |
touches = event.changedTouches, | |
x, y; | |
if (touches) { | |
x = touches[0].pageX - elementBounds.left - scrollX; | |
y = touches[0].pageY - elementBounds.top - scrollY; | |
} else { | |
x = event.pageX - elementBounds.left - scrollX; | |
y = event.pageY - elementBounds.top - scrollY; | |
} | |
return { | |
x: x / (element.clientWidth / (element.width || element.clientWidth) * pixelRatio), | |
y: y / (element.clientHeight / (element.height || element.clientHeight) * pixelRatio) | |
}; | |
}; | |
})(); | |
},{"../core/Common":14}],20:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Plugin` module contains functions for registering and installing plugins on modules. | |
* | |
* @class Plugin | |
*/ | |
var Plugin = {}; | |
module.exports = Plugin; | |
var Common = _dereq_('./Common'); | |
(function() { | |
Plugin._registry = {}; | |
/** | |
* Registers a plugin object so it can be resolved later by name. | |
* @method register | |
* @param plugin {} The plugin to register. | |
* @return {object} The plugin. | |
*/ | |
Plugin.register = function(plugin) { | |
if (!Plugin.isPlugin(plugin)) { | |
Common.warn('Plugin.register:', Plugin.toString(plugin), 'does not implement all required fields.'); | |
} | |
if (plugin.name in Plugin._registry) { | |
var registered = Plugin._registry[plugin.name], | |
pluginVersion = Plugin.versionParse(plugin.version).number, | |
registeredVersion = Plugin.versionParse(registered.version).number; | |
if (pluginVersion > registeredVersion) { | |
Common.warn('Plugin.register:', Plugin.toString(registered), 'was upgraded to', Plugin.toString(plugin)); | |
Plugin._registry[plugin.name] = plugin; | |
} else if (pluginVersion < registeredVersion) { | |
Common.warn('Plugin.register:', Plugin.toString(registered), 'can not be downgraded to', Plugin.toString(plugin)); | |
} else if (plugin !== registered) { | |
Common.warn('Plugin.register:', Plugin.toString(plugin), 'is already registered to different plugin object'); | |
} | |
} else { | |
Plugin._registry[plugin.name] = plugin; | |
} | |
return plugin; | |
}; | |
/** | |
* Resolves a dependency to a plugin object from the registry if it exists. | |
* The `dependency` may contain a version, but only the name matters when resolving. | |
* @method resolve | |
* @param dependency {string} The dependency. | |
* @return {object} The plugin if resolved, otherwise `undefined`. | |
*/ | |
Plugin.resolve = function(dependency) { | |
return Plugin._registry[Plugin.dependencyParse(dependency).name]; | |
}; | |
/** | |
* Returns a pretty printed plugin name and version. | |
* @method toString | |
* @param plugin {} The plugin. | |
* @return {string} Pretty printed plugin name and version. | |
*/ | |
Plugin.toString = function(plugin) { | |
return typeof plugin === 'string' ? plugin : (plugin.name || 'anonymous') + '@' + (plugin.version || plugin.range || '0.0.0'); | |
}; | |
/** | |
* Returns `true` if the object meets the minimum standard to be considered a plugin. | |
* This means it must define the following properties: | |
* - `name` | |
* - `version` | |
* - `install` | |
* @method isPlugin | |
* @param obj {} The obj to test. | |
* @return {boolean} `true` if the object can be considered a plugin otherwise `false`. | |
*/ | |
Plugin.isPlugin = function(obj) { | |
return obj && obj.name && obj.version && obj.install; | |
}; | |
/** | |
* Returns `true` if a plugin with the given `name` been installed on `module`. | |
* @method isUsed | |
* @param module {} The module. | |
* @param name {string} The plugin name. | |
* @return {boolean} `true` if a plugin with the given `name` been installed on `module`, otherwise `false`. | |
*/ | |
Plugin.isUsed = function(module, name) { | |
return module.used.indexOf(name) > -1; | |
}; | |
/** | |
* Returns `true` if `plugin.for` is applicable to `module` by comparing against `module.name` and `module.version`. | |
* If `plugin.for` is not specified then it is assumed to be applicable. | |
* The value of `plugin.for` is a string of the format `'module-name'` or `'module-name@version'`. | |
* @method isFor | |
* @param plugin {} The plugin. | |
* @param module {} The module. | |
* @return {boolean} `true` if `plugin.for` is applicable to `module`, otherwise `false`. | |
*/ | |
Plugin.isFor = function(plugin, module) { | |
var parsed = plugin.for && Plugin.dependencyParse(plugin.for); | |
return !plugin.for || (module.name === parsed.name && Plugin.versionSatisfies(module.version, parsed.range)); | |
}; | |
/** | |
* Installs the plugins by calling `plugin.install` on each plugin specified in `plugins` if passed, otherwise `module.uses`. | |
* For installing plugins on `Matter` see the convenience function `Matter.use`. | |
* Plugins may be specified either by their name or a reference to the plugin object. | |
* Plugins themselves may specify further dependencies, but each plugin is installed only once. | |
* Order is important, a topological sort is performed to find the best resulting order of installation. | |
* This sorting attempts to satisfy every dependency's requested ordering, but may not be exact in all cases. | |
* This function logs the resulting status of each dependency in the console, along with any warnings. | |
* - A green tick ✅ indicates a dependency was resolved and installed. | |
* - An orange diamond 🔶 indicates a dependency was resolved but a warning was thrown for it or one if its dependencies. | |
* - A red cross ❌ indicates a dependency could not be resolved. | |
* Avoid calling this function multiple times on the same module unless you intend to manually control installation order. | |
* @method use | |
* @param module {} The module install plugins on. | |
* @param [plugins=module.uses] {} The plugins to install on module (optional, defaults to `module.uses`). | |
*/ | |
Plugin.use = function(module, plugins) { | |
module.uses = (module.uses || []).concat(plugins || []); | |
if (module.uses.length === 0) { | |
Common.warn('Plugin.use:', Plugin.toString(module), 'does not specify any dependencies to install.'); | |
return; | |
} | |
var dependencies = Plugin.dependencies(module), | |
sortedDependencies = Common.topologicalSort(dependencies), | |
status = []; | |
for (var i = 0; i < sortedDependencies.length; i += 1) { | |
if (sortedDependencies[i] === module.name) { | |
continue; | |
} | |
var plugin = Plugin.resolve(sortedDependencies[i]); | |
if (!plugin) { | |
status.push('❌ ' + sortedDependencies[i]); | |
continue; | |
} | |
if (Plugin.isUsed(module, plugin.name)) { | |
continue; | |
} | |
if (!Plugin.isFor(plugin, module)) { | |
Common.warn('Plugin.use:', Plugin.toString(plugin), 'is for', plugin.for, 'but installed on', Plugin.toString(module) + '.'); | |
plugin._warned = true; | |
} | |
if (plugin.install) { | |
plugin.install(module); | |
} else { | |
Common.warn('Plugin.use:', Plugin.toString(plugin), 'does not specify an install function.'); | |
plugin._warned = true; | |
} | |
if (plugin._warned) { | |
status.push('🔶 ' + Plugin.toString(plugin)); | |
delete plugin._warned; | |
} else { | |
status.push('✅ ' + Plugin.toString(plugin)); | |
} | |
module.used.push(plugin.name); | |
} | |
if (status.length > 0) { | |
Common.info(status.join(' ')); | |
} | |
}; | |
/** | |
* Recursively finds all of a module's dependencies and returns a flat dependency graph. | |
* @method dependencies | |
* @param module {} The module. | |
* @return {object} A dependency graph. | |
*/ | |
Plugin.dependencies = function(module, tracked) { | |
var parsedBase = Plugin.dependencyParse(module), | |
name = parsedBase.name; | |
tracked = tracked || {}; | |
if (name in tracked) { | |
return; | |
} | |
module = Plugin.resolve(module) || module; | |
tracked[name] = Common.map(module.uses || [], function(dependency) { | |
if (Plugin.isPlugin(dependency)) { | |
Plugin.register(dependency); | |
} | |
var parsed = Plugin.dependencyParse(dependency), | |
resolved = Plugin.resolve(dependency); | |
if (resolved && !Plugin.versionSatisfies(resolved.version, parsed.range)) { | |
Common.warn( | |
'Plugin.dependencies:', Plugin.toString(resolved), 'does not satisfy', | |
Plugin.toString(parsed), 'used by', Plugin.toString(parsedBase) + '.' | |
); | |
resolved._warned = true; | |
module._warned = true; | |
} else if (!resolved) { | |
Common.warn( | |
'Plugin.dependencies:', Plugin.toString(dependency), 'used by', | |
Plugin.toString(parsedBase), 'could not be resolved.' | |
); | |
module._warned = true; | |
} | |
return parsed.name; | |
}); | |
for (var i = 0; i < tracked[name].length; i += 1) { | |
Plugin.dependencies(tracked[name][i], tracked); | |
} | |
return tracked; | |
}; | |
/** | |
* Parses a dependency string into its components. | |
* The `dependency` is a string of the format `'module-name'` or `'module-name@version'`. | |
* See documentation for `Plugin.versionParse` for a description of the format. | |
* This function can also handle dependencies that are already resolved (e.g. a module object). | |
* @method dependencyParse | |
* @param dependency {string} The dependency of the format `'module-name'` or `'module-name@version'`. | |
* @return {object} The dependency parsed into its components. | |
*/ | |
Plugin.dependencyParse = function(dependency) { | |
if (Common.isString(dependency)) { | |
var pattern = /^[\w-]+(@(\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-]+)?))?$/; | |
if (!pattern.test(dependency)) { | |
Common.warn('Plugin.dependencyParse:', dependency, 'is not a valid dependency string.'); | |
} | |
return { | |
name: dependency.split('@')[0], | |
range: dependency.split('@')[1] || '*' | |
}; | |
} | |
return { | |
name: dependency.name, | |
range: dependency.range || dependency.version | |
}; | |
}; | |
/** | |
* Parses a version string into its components. | |
* Versions are strictly of the format `x.y.z` (as in [semver](http://semver.org/)). | |
* Versions may optionally have a prerelease tag in the format `x.y.z-alpha`. | |
* Ranges are a strict subset of [npm ranges](https://docs.npmjs.com/misc/semver#advanced-range-syntax). | |
* Only the following range types are supported: | |
* - Tilde ranges e.g. `~1.2.3` | |
* - Caret ranges e.g. `^1.2.3` | |
* - Exact version e.g. `1.2.3` | |
* - Any version `*` | |
* @method versionParse | |
* @param range {string} The version string. | |
* @return {object} The version range parsed into its components. | |
*/ | |
Plugin.versionParse = function(range) { | |
var pattern = /^\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-]+)?$/; | |
if (!pattern.test(range)) { | |
Common.warn('Plugin.versionParse:', range, 'is not a valid version or range.'); | |
} | |
var identifiers = range.split('-'); | |
range = identifiers[0]; | |
var isRange = isNaN(Number(range[0])), | |
version = isRange ? range.substr(1) : range, | |
parts = Common.map(version.split('.'), function(part) { | |
return Number(part); | |
}); | |
return { | |
isRange: isRange, | |
version: version, | |
range: range, | |
operator: isRange ? range[0] : '', | |
parts: parts, | |
prerelease: identifiers[1], | |
number: parts[0] * 1e8 + parts[1] * 1e4 + parts[2] | |
}; | |
}; | |
/** | |
* Returns `true` if `version` satisfies the given `range`. | |
* See documentation for `Plugin.versionParse` for a description of the format. | |
* If a version or range is not specified, then any version (`*`) is assumed to satisfy. | |
* @method versionSatisfies | |
* @param version {string} The version string. | |
* @param range {string} The range string. | |
* @return {boolean} `true` if `version` satisfies `range`, otherwise `false`. | |
*/ | |
Plugin.versionSatisfies = function(version, range) { | |
range = range || '*'; | |
var rangeParsed = Plugin.versionParse(range), | |
rangeParts = rangeParsed.parts, | |
versionParsed = Plugin.versionParse(version), | |
versionParts = versionParsed.parts; | |
if (rangeParsed.isRange) { | |
if (rangeParsed.operator === '*' || version === '*') { | |
return true; | |
} | |
if (rangeParsed.operator === '~') { | |
return versionParts[0] === rangeParts[0] && versionParts[1] === rangeParts[1] && versionParts[2] >= rangeParts[2]; | |
} | |
if (rangeParsed.operator === '^') { | |
if (rangeParts[0] > 0) { | |
return versionParts[0] === rangeParts[0] && versionParsed.number >= rangeParsed.number; | |
} | |
if (rangeParts[1] > 0) { | |
return versionParts[1] === rangeParts[1] && versionParts[2] >= rangeParts[2]; | |
} | |
return versionParts[2] === rangeParts[2]; | |
} | |
} | |
return version === range || version === '*'; | |
}; | |
})(); | |
},{"./Common":14}],21:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Runner` module is an optional utility which provides a game loop, | |
* that handles continuously updating a `Matter.Engine` for you within a browser. | |
* It is intended for development and debugging purposes, but may also be suitable for simple games. | |
* If you are using your own game loop instead, then you do not need the `Matter.Runner` module. | |
* Instead just call `Engine.update(engine, delta)` in your own loop. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Runner | |
*/ | |
var Runner = {}; | |
module.exports = Runner; | |
var Events = _dereq_('./Events'); | |
var Engine = _dereq_('./Engine'); | |
var Common = _dereq_('./Common'); | |
(function() { | |
var _requestAnimationFrame, | |
_cancelAnimationFrame; | |
if (typeof window !== 'undefined') { | |
_requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame | |
|| window.mozRequestAnimationFrame || window.msRequestAnimationFrame; | |
_cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame | |
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; | |
} | |
if (!_requestAnimationFrame) { | |
var _frameTimeout; | |
_requestAnimationFrame = function(callback){ | |
_frameTimeout = setTimeout(function() { | |
callback(Common.now()); | |
}, 1000 / 60); | |
}; | |
_cancelAnimationFrame = function() { | |
clearTimeout(_frameTimeout); | |
}; | |
} | |
/** | |
* Creates a new Runner. The options parameter is an object that specifies any properties you wish to override the defaults. | |
* @method create | |
* @param {} options | |
*/ | |
Runner.create = function(options) { | |
var defaults = { | |
fps: 60, | |
correction: 1, | |
deltaSampleSize: 60, | |
counterTimestamp: 0, | |
frameCounter: 0, | |
deltaHistory: [], | |
timePrev: null, | |
timeScalePrev: 1, | |
frameRequestId: null, | |
isFixed: false, | |
enabled: true | |
}; | |
var runner = Common.extend(defaults, options); | |
runner.delta = runner.delta || 1000 / runner.fps; | |
runner.deltaMin = runner.deltaMin || 1000 / runner.fps; | |
runner.deltaMax = runner.deltaMax || 1000 / (runner.fps * 0.5); | |
runner.fps = 1000 / runner.delta; | |
return runner; | |
}; | |
/** | |
* Continuously ticks a `Matter.Engine` by calling `Runner.tick` on the `requestAnimationFrame` event. | |
* @method run | |
* @param {engine} engine | |
*/ | |
Runner.run = function(runner, engine) { | |
// create runner if engine is first argument | |
if (typeof runner.positionIterations !== 'undefined') { | |
engine = runner; | |
runner = Runner.create(); | |
} | |
(function render(time){ | |
runner.frameRequestId = _requestAnimationFrame(render); | |
if (time && runner.enabled) { | |
Runner.tick(runner, engine, time); | |
} | |
})(); | |
return runner; | |
}; | |
/** | |
* A game loop utility that updates the engine and renderer by one step (a 'tick'). | |
* Features delta smoothing, time correction and fixed or dynamic timing. | |
* Triggers `beforeTick`, `tick` and `afterTick` events on the engine. | |
* Consider just `Engine.update(engine, delta)` if you're using your own loop. | |
* @method tick | |
* @param {runner} runner | |
* @param {engine} engine | |
* @param {number} time | |
*/ | |
Runner.tick = function(runner, engine, time) { | |
var timing = engine.timing, | |
correction = 1, | |
delta; | |
// create an event object | |
var event = { | |
timestamp: timing.timestamp | |
}; | |
Events.trigger(runner, 'beforeTick', event); | |
Events.trigger(engine, 'beforeTick', event); // @deprecated | |
if (runner.isFixed) { | |
// fixed timestep | |
delta = runner.delta; | |
} else { | |
// dynamic timestep based on wall clock between calls | |
delta = (time - runner.timePrev) || runner.delta; | |
runner.timePrev = time; | |
// optimistically filter delta over a few frames, to improve stability | |
runner.deltaHistory.push(delta); | |
runner.deltaHistory = runner.deltaHistory.slice(-runner.deltaSampleSize); | |
delta = Math.min.apply(null, runner.deltaHistory); | |
// limit delta | |
delta = delta < runner.deltaMin ? runner.deltaMin : delta; | |
delta = delta > runner.deltaMax ? runner.deltaMax : delta; | |
// correction for delta | |
correction = delta / runner.delta; | |
// update engine timing object | |
runner.delta = delta; | |
} | |
// time correction for time scaling | |
if (runner.timeScalePrev !== 0) | |
correction *= timing.timeScale / runner.timeScalePrev; | |
if (timing.timeScale === 0) | |
correction = 0; | |
runner.timeScalePrev = timing.timeScale; | |
runner.correction = correction; | |
// fps counter | |
runner.frameCounter += 1; | |
if (time - runner.counterTimestamp >= 1000) { | |
runner.fps = runner.frameCounter * ((time - runner.counterTimestamp) / 1000); | |
runner.counterTimestamp = time; | |
runner.frameCounter = 0; | |
} | |
Events.trigger(runner, 'tick', event); | |
Events.trigger(engine, 'tick', event); // @deprecated | |
// if world has been modified, clear the render scene graph | |
if (engine.world.isModified | |
&& engine.render | |
&& engine.render.controller | |
&& engine.render.controller.clear) { | |
engine.render.controller.clear(engine.render); // @deprecated | |
} | |
// update | |
Events.trigger(runner, 'beforeUpdate', event); | |
Engine.update(engine, delta, correction); | |
Events.trigger(runner, 'afterUpdate', event); | |
// render | |
// @deprecated | |
if (engine.render && engine.render.controller) { | |
Events.trigger(runner, 'beforeRender', event); | |
Events.trigger(engine, 'beforeRender', event); // @deprecated | |
engine.render.controller.world(engine.render); | |
Events.trigger(runner, 'afterRender', event); | |
Events.trigger(engine, 'afterRender', event); // @deprecated | |
} | |
Events.trigger(runner, 'afterTick', event); | |
Events.trigger(engine, 'afterTick', event); // @deprecated | |
}; | |
/** | |
* Ends execution of `Runner.run` on the given `runner`, by canceling the animation frame request event loop. | |
* If you wish to only temporarily pause the engine, see `engine.enabled` instead. | |
* @method stop | |
* @param {runner} runner | |
*/ | |
Runner.stop = function(runner) { | |
_cancelAnimationFrame(runner.frameRequestId); | |
}; | |
/** | |
* Alias for `Runner.run`. | |
* @method start | |
* @param {runner} runner | |
* @param {engine} engine | |
*/ | |
Runner.start = function(runner, engine) { | |
Runner.run(runner, engine); | |
}; | |
/* | |
* | |
* Events Documentation | |
* | |
*/ | |
/** | |
* Fired at the start of a tick, before any updates to the engine or timing | |
* | |
* @event beforeTick | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after engine timing updated, but just before update | |
* | |
* @event tick | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired at the end of a tick, after engine update and after rendering | |
* | |
* @event afterTick | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired before update | |
* | |
* @event beforeUpdate | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after update | |
* | |
* @event afterUpdate | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired before rendering | |
* | |
* @event beforeRender | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
* @deprecated | |
*/ | |
/** | |
* Fired after rendering | |
* | |
* @event afterRender | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
* @deprecated | |
*/ | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* A flag that specifies whether the runner is running or not. | |
* | |
* @property enabled | |
* @type boolean | |
* @default true | |
*/ | |
/** | |
* A `Boolean` that specifies if the runner should use a fixed timestep (otherwise it is variable). | |
* If timing is fixed, then the apparent simulation speed will change depending on the frame rate (but behaviour will be deterministic). | |
* If the timing is variable, then the apparent simulation speed will be constant (approximately, but at the cost of determininism). | |
* | |
* @property isFixed | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* A `Number` that specifies the time step between updates in milliseconds. | |
* If `engine.timing.isFixed` is set to `true`, then `delta` is fixed. | |
* If it is `false`, then `delta` can dynamically change to maintain the correct apparent simulation speed. | |
* | |
* @property delta | |
* @type number | |
* @default 1000 / 60 | |
*/ | |
})(); | |
},{"./Common":14,"./Engine":15,"./Events":16}],22:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Sleeping` module contains methods to manage the sleeping state of bodies. | |
* | |
* @class Sleeping | |
*/ | |
var Sleeping = {}; | |
module.exports = Sleeping; | |
var Events = _dereq_('./Events'); | |
(function() { | |
Sleeping._motionWakeThreshold = 0.18; | |
Sleeping._motionSleepThreshold = 0.08; | |
Sleeping._minBias = 0.9; | |
/** | |
* Puts bodies to sleep or wakes them up depending on their motion. | |
* @method update | |
* @param {body[]} bodies | |
* @param {number} timeScale | |
*/ | |
Sleeping.update = function(bodies, timeScale) { | |
var timeFactor = timeScale * timeScale * timeScale; | |
// update bodies sleeping status | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i], | |
motion = body.speed * body.speed + body.angularSpeed * body.angularSpeed; | |
// wake up bodies if they have a force applied | |
if (body.force.x !== 0 || body.force.y !== 0) { | |
Sleeping.set(body, false); | |
continue; | |
} | |
var minMotion = Math.min(body.motion, motion), | |
maxMotion = Math.max(body.motion, motion); | |
// biased average motion estimation between frames | |
body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion; | |
if (body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeFactor) { | |
body.sleepCounter += 1; | |
if (body.sleepCounter >= body.sleepThreshold) | |
Sleeping.set(body, true); | |
} else if (body.sleepCounter > 0) { | |
body.sleepCounter -= 1; | |
} | |
} | |
}; | |
/** | |
* Given a set of colliding pairs, wakes the sleeping bodies involved. | |
* @method afterCollisions | |
* @param {pair[]} pairs | |
* @param {number} timeScale | |
*/ | |
Sleeping.afterCollisions = function(pairs, timeScale) { | |
var timeFactor = timeScale * timeScale * timeScale; | |
// wake up bodies involved in collisions | |
for (var i = 0; i < pairs.length; i++) { | |
var pair = pairs[i]; | |
// don't wake inactive pairs | |
if (!pair.isActive) | |
continue; | |
var collision = pair.collision, | |
bodyA = collision.bodyA.parent, | |
bodyB = collision.bodyB.parent; | |
// don't wake if at least one body is static | |
if ((bodyA.isSleeping && bodyB.isSleeping) || bodyA.isStatic || bodyB.isStatic) | |
continue; | |
if (bodyA.isSleeping || bodyB.isSleeping) { | |
var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB, | |
movingBody = sleepingBody === bodyA ? bodyB : bodyA; | |
if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) { | |
Sleeping.set(sleepingBody, false); | |
} | |
} | |
} | |
}; | |
/** | |
* Set a body as sleeping or awake. | |
* @method set | |
* @param {body} body | |
* @param {boolean} isSleeping | |
*/ | |
Sleeping.set = function(body, isSleeping) { | |
var wasSleeping = body.isSleeping; | |
if (isSleeping) { | |
body.isSleeping = true; | |
body.sleepCounter = body.sleepThreshold; | |
body.positionImpulse.x = 0; | |
body.positionImpulse.y = 0; | |
body.positionPrev.x = body.position.x; | |
body.positionPrev.y = body.position.y; | |
body.anglePrev = body.angle; | |
body.speed = 0; | |
body.angularSpeed = 0; | |
body.motion = 0; | |
if (!wasSleeping) { | |
Events.trigger(body, 'sleepStart'); | |
} | |
} else { | |
body.isSleeping = false; | |
body.sleepCounter = 0; | |
if (wasSleeping) { | |
Events.trigger(body, 'sleepEnd'); | |
} | |
} | |
}; | |
})(); | |
},{"./Events":16}],23:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* The `Matter.Bodies` module contains factory methods for creating rigid body models | |
* with commonly used body configurations (such as rectangles, circles and other polygons). | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Bodies | |
*/ | |
// TODO: true circle bodies | |
var Bodies = {}; | |
module.exports = Bodies; | |
var Vertices = _dereq_('../geometry/Vertices'); | |
var Common = _dereq_('../core/Common'); | |
var Body = _dereq_('../body/Body'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
var Vector = _dereq_('../geometry/Vector'); | |
var decomp = (typeof window !== "undefined" ? window['decomp'] : typeof global !== "undefined" ? global['decomp'] : null); | |
(function() { | |
/** | |
* Creates a new rigid body model with a rectangle hull. | |
* The options parameter is an object that specifies any properties you wish to override the defaults. | |
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object. | |
* @method rectangle | |
* @param {number} x | |
* @param {number} y | |
* @param {number} width | |
* @param {number} height | |
* @param {object} [options] | |
* @return {body} A new rectangle body | |
*/ | |
Bodies.rectangle = function(x, y, width, height, options) { | |
options = options || {}; | |
var rectangle = { | |
label: 'Rectangle Body', | |
position: { x: x, y: y }, | |
vertices: Vertices.fromPath('L 0 0 L ' + width + ' 0 L ' + width + ' ' + height + ' L 0 ' + height) | |
}; | |
if (options.chamfer) { | |
var chamfer = options.chamfer; | |
rectangle.vertices = Vertices.chamfer(rectangle.vertices, chamfer.radius, | |
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax); | |
delete options.chamfer; | |
} | |
return Body.create(Common.extend({}, rectangle, options)); | |
}; | |
/** | |
* Creates a new rigid body model with a trapezoid hull. | |
* The options parameter is an object that specifies any properties you wish to override the defaults. | |
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object. | |
* @method trapezoid | |
* @param {number} x | |
* @param {number} y | |
* @param {number} width | |
* @param {number} height | |
* @param {number} slope | |
* @param {object} [options] | |
* @return {body} A new trapezoid body | |
*/ | |
Bodies.trapezoid = function(x, y, width, height, slope, options) { | |
options = options || {}; | |
slope *= 0.5; | |
var roof = (1 - (slope * 2)) * width; | |
var x1 = width * slope, | |
x2 = x1 + roof, | |
x3 = x2 + x1, | |
verticesPath; | |
if (slope < 0.5) { | |
verticesPath = 'L 0 0 L ' + x1 + ' ' + (-height) + ' L ' + x2 + ' ' + (-height) + ' L ' + x3 + ' 0'; | |
} else { | |
verticesPath = 'L 0 0 L ' + x2 + ' ' + (-height) + ' L ' + x3 + ' 0'; | |
} | |
var trapezoid = { | |
label: 'Trapezoid Body', | |
position: { x: x, y: y }, | |
vertices: Vertices.fromPath(verticesPath) | |
}; | |
if (options.chamfer) { | |
var chamfer = options.chamfer; | |
trapezoid.vertices = Vertices.chamfer(trapezoid.vertices, chamfer.radius, | |
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax); | |
delete options.chamfer; | |
} | |
return Body.create(Common.extend({}, trapezoid, options)); | |
}; | |
/** | |
* Creates a new rigid body model with a circle hull. | |
* The options parameter is an object that specifies any properties you wish to override the defaults. | |
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object. | |
* @method circle | |
* @param {number} x | |
* @param {number} y | |
* @param {number} radius | |
* @param {object} [options] | |
* @param {number} [maxSides] | |
* @return {body} A new circle body | |
*/ | |
Bodies.circle = function(x, y, radius, options, maxSides) { | |
options = options || {}; | |
var circle = { | |
label: 'Circle Body', | |
circleRadius: radius | |
}; | |
// approximate circles with polygons until true circles implemented in SAT | |
maxSides = maxSides || 25; | |
var sides = Math.ceil(Math.max(10, Math.min(maxSides, radius))); | |
// optimisation: always use even number of sides (half the number of unique axes) | |
if (sides % 2 === 1) | |
sides += 1; | |
return Bodies.polygon(x, y, sides, radius, Common.extend({}, circle, options)); | |
}; | |
/** | |
* Creates a new rigid body model with a regular polygon hull with the given number of sides. | |
* The options parameter is an object that specifies any properties you wish to override the defaults. | |
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object. | |
* @method polygon | |
* @param {number} x | |
* @param {number} y | |
* @param {number} sides | |
* @param {number} radius | |
* @param {object} [options] | |
* @return {body} A new regular polygon body | |
*/ | |
Bodies.polygon = function(x, y, sides, radius, options) { | |
options = options || {}; | |
if (sides < 3) | |
return Bodies.circle(x, y, radius, options); | |
var theta = 2 * Math.PI / sides, | |
path = '', | |
offset = theta * 0.5; | |
for (var i = 0; i < sides; i += 1) { | |
var angle = offset + (i * theta), | |
xx = Math.cos(angle) * radius, | |
yy = Math.sin(angle) * radius; | |
path += 'L ' + xx.toFixed(3) + ' ' + yy.toFixed(3) + ' '; | |
} | |
var polygon = { | |
label: 'Polygon Body', | |
position: { x: x, y: y }, | |
vertices: Vertices.fromPath(path) | |
}; | |
if (options.chamfer) { | |
var chamfer = options.chamfer; | |
polygon.vertices = Vertices.chamfer(polygon.vertices, chamfer.radius, | |
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax); | |
delete options.chamfer; | |
} | |
return Body.create(Common.extend({}, polygon, options)); | |
}; | |
/** | |
* Creates a body using the supplied vertices (or an array containing multiple sets of vertices). | |
* If the vertices are convex, they will pass through as supplied. | |
* Otherwise if the vertices are concave, they will be decomposed if [poly-decomp.js](https://github.com/schteppe/poly-decomp.js) is available. | |
* Note that this process is not guaranteed to support complex sets of vertices (e.g. those with holes may fail). | |
* By default the decomposition will discard collinear edges (to improve performance). | |
* It can also optionally discard any parts that have an area less than `minimumArea`. | |
* If the vertices can not be decomposed, the result will fall back to using the convex hull. | |
* The options parameter is an object that specifies any `Matter.Body` properties you wish to override the defaults. | |
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object. | |
* @method fromVertices | |
* @param {number} x | |
* @param {number} y | |
* @param [[vector]] vertexSets | |
* @param {object} [options] | |
* @param {bool} [flagInternal=false] | |
* @param {number} [removeCollinear=0.01] | |
* @param {number} [minimumArea=10] | |
* @return {body} | |
*/ | |
Bodies.fromVertices = function(x, y, vertexSets, options, flagInternal, removeCollinear, minimumArea) { | |
var body, | |
parts, | |
isConvex, | |
vertices, | |
i, | |
j, | |
k, | |
v, | |
z; | |
options = options || {}; | |
parts = []; | |
flagInternal = typeof flagInternal !== 'undefined' ? flagInternal : false; | |
removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : 0.01; | |
minimumArea = typeof minimumArea !== 'undefined' ? minimumArea : 10; | |
if (!decomp) { | |
Common.warn('Bodies.fromVertices: poly-decomp.js required. Could not decompose vertices. Fallback to convex hull.'); | |
} | |
// ensure vertexSets is an array of arrays | |
if (!Common.isArray(vertexSets[0])) { | |
vertexSets = [vertexSets]; | |
} | |
for (v = 0; v < vertexSets.length; v += 1) { | |
vertices = vertexSets[v]; | |
isConvex = Vertices.isConvex(vertices); | |
if (isConvex || !decomp) { | |
if (isConvex) { | |
vertices = Vertices.clockwiseSort(vertices); | |
} else { | |
// fallback to convex hull when decomposition is not possible | |
vertices = Vertices.hull(vertices); | |
} | |
parts.push({ | |
position: { x: x, y: y }, | |
vertices: vertices | |
}); | |
} else { | |
// initialise a decomposition | |
var concave = vertices.map(function(vertex) { | |
return [vertex.x, vertex.y]; | |
}); | |
// vertices are concave and simple, we can decompose into parts | |
decomp.makeCCW(concave); | |
if (removeCollinear !== false) | |
decomp.removeCollinearPoints(concave, removeCollinear); | |
// use the quick decomposition algorithm (Bayazit) | |
var decomposed = decomp.quickDecomp(concave); | |
// for each decomposed chunk | |
for (i = 0; i < decomposed.length; i++) { | |
var chunk = decomposed[i]; | |
// convert vertices into the correct structure | |
var chunkVertices = chunk.map(function(vertices) { | |
return { | |
x: vertices[0], | |
y: vertices[1] | |
}; | |
}); | |
// skip small chunks | |
if (minimumArea > 0 && Vertices.area(chunkVertices) < minimumArea) | |
continue; | |
// create a compound part | |
parts.push({ | |
position: Vertices.centre(chunkVertices), | |
vertices: chunkVertices | |
}); | |
} | |
} | |
} | |
// create body parts | |
for (i = 0; i < parts.length; i++) { | |
parts[i] = Body.create(Common.extend(parts[i], options)); | |
} | |
// flag internal edges (coincident part edges) | |
if (flagInternal) { | |
var coincident_max_dist = 5; | |
for (i = 0; i < parts.length; i++) { | |
var partA = parts[i]; | |
for (j = i + 1; j < parts.length; j++) { | |
var partB = parts[j]; | |
if (Bounds.overlaps(partA.bounds, partB.bounds)) { | |
var pav = partA.vertices, | |
pbv = partB.vertices; | |
// iterate vertices of both parts | |
for (k = 0; k < partA.vertices.length; k++) { | |
for (z = 0; z < partB.vertices.length; z++) { | |
// find distances between the vertices | |
var da = Vector.magnitudeSquared(Vector.sub(pav[(k + 1) % pav.length], pbv[z])), | |
db = Vector.magnitudeSquared(Vector.sub(pav[k], pbv[(z + 1) % pbv.length])); | |
// if both vertices are very close, consider the edge concident (internal) | |
if (da < coincident_max_dist && db < coincident_max_dist) { | |
pav[k].isInternal = true; | |
pbv[z].isInternal = true; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
if (parts.length > 1) { | |
// create the parent body to be returned, that contains generated compound parts | |
body = Body.create(Common.extend({ parts: parts.slice(0) }, options)); | |
Body.setPosition(body, { x: x, y: y }); | |
return body; | |
} else { | |
return parts[0]; | |
} | |
}; | |
})(); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"../body/Body":1,"../core/Common":14,"../geometry/Bounds":26,"../geometry/Vector":28,"../geometry/Vertices":29}],24:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Composites` module contains factory methods for creating composite bodies | |
* with commonly used configurations (such as stacks and chains). | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Composites | |
*/ | |
var Composites = {}; | |
module.exports = Composites; | |
var Composite = _dereq_('../body/Composite'); | |
var Constraint = _dereq_('../constraint/Constraint'); | |
var Common = _dereq_('../core/Common'); | |
var Body = _dereq_('../body/Body'); | |
var Bodies = _dereq_('./Bodies'); | |
(function() { | |
/** | |
* Create a new composite containing bodies created in the callback in a grid arrangement. | |
* This function uses the body's bounds to prevent overlaps. | |
* @method stack | |
* @param {number} xx | |
* @param {number} yy | |
* @param {number} columns | |
* @param {number} rows | |
* @param {number} columnGap | |
* @param {number} rowGap | |
* @param {function} callback | |
* @return {composite} A new composite containing objects created in the callback | |
*/ | |
Composites.stack = function(xx, yy, columns, rows, columnGap, rowGap, callback) { | |
var stack = Composite.create({ label: 'Stack' }), | |
x = xx, | |
y = yy, | |
lastBody, | |
i = 0; | |
for (var row = 0; row < rows; row++) { | |
var maxHeight = 0; | |
for (var column = 0; column < columns; column++) { | |
var body = callback(x, y, column, row, lastBody, i); | |
if (body) { | |
var bodyHeight = body.bounds.max.y - body.bounds.min.y, | |
bodyWidth = body.bounds.max.x - body.bounds.min.x; | |
if (bodyHeight > maxHeight) | |
maxHeight = bodyHeight; | |
Body.translate(body, { x: bodyWidth * 0.5, y: bodyHeight * 0.5 }); | |
x = body.bounds.max.x + columnGap; | |
Composite.addBody(stack, body); | |
lastBody = body; | |
i += 1; | |
} else { | |
x += columnGap; | |
} | |
} | |
y += maxHeight + rowGap; | |
x = xx; | |
} | |
return stack; | |
}; | |
/** | |
* Chains all bodies in the given composite together using constraints. | |
* @method chain | |
* @param {composite} composite | |
* @param {number} xOffsetA | |
* @param {number} yOffsetA | |
* @param {number} xOffsetB | |
* @param {number} yOffsetB | |
* @param {object} options | |
* @return {composite} A new composite containing objects chained together with constraints | |
*/ | |
Composites.chain = function(composite, xOffsetA, yOffsetA, xOffsetB, yOffsetB, options) { | |
var bodies = composite.bodies; | |
for (var i = 1; i < bodies.length; i++) { | |
var bodyA = bodies[i - 1], | |
bodyB = bodies[i], | |
bodyAHeight = bodyA.bounds.max.y - bodyA.bounds.min.y, | |
bodyAWidth = bodyA.bounds.max.x - bodyA.bounds.min.x, | |
bodyBHeight = bodyB.bounds.max.y - bodyB.bounds.min.y, | |
bodyBWidth = bodyB.bounds.max.x - bodyB.bounds.min.x; | |
var defaults = { | |
bodyA: bodyA, | |
pointA: { x: bodyAWidth * xOffsetA, y: bodyAHeight * yOffsetA }, | |
bodyB: bodyB, | |
pointB: { x: bodyBWidth * xOffsetB, y: bodyBHeight * yOffsetB } | |
}; | |
var constraint = Common.extend(defaults, options); | |
Composite.addConstraint(composite, Constraint.create(constraint)); | |
} | |
composite.label += ' Chain'; | |
return composite; | |
}; | |
/** | |
* Connects bodies in the composite with constraints in a grid pattern, with optional cross braces. | |
* @method mesh | |
* @param {composite} composite | |
* @param {number} columns | |
* @param {number} rows | |
* @param {boolean} crossBrace | |
* @param {object} options | |
* @return {composite} The composite containing objects meshed together with constraints | |
*/ | |
Composites.mesh = function(composite, columns, rows, crossBrace, options) { | |
var bodies = composite.bodies, | |
row, | |
col, | |
bodyA, | |
bodyB, | |
bodyC; | |
for (row = 0; row < rows; row++) { | |
for (col = 1; col < columns; col++) { | |
bodyA = bodies[(col - 1) + (row * columns)]; | |
bodyB = bodies[col + (row * columns)]; | |
Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyA, bodyB: bodyB }, options))); | |
} | |
if (row > 0) { | |
for (col = 0; col < columns; col++) { | |
bodyA = bodies[col + ((row - 1) * columns)]; | |
bodyB = bodies[col + (row * columns)]; | |
Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyA, bodyB: bodyB }, options))); | |
if (crossBrace && col > 0) { | |
bodyC = bodies[(col - 1) + ((row - 1) * columns)]; | |
Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyC, bodyB: bodyB }, options))); | |
} | |
if (crossBrace && col < columns - 1) { | |
bodyC = bodies[(col + 1) + ((row - 1) * columns)]; | |
Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyC, bodyB: bodyB }, options))); | |
} | |
} | |
} | |
} | |
composite.label += ' Mesh'; | |
return composite; | |
}; | |
/** | |
* Create a new composite containing bodies created in the callback in a pyramid arrangement. | |
* This function uses the body's bounds to prevent overlaps. | |
* @method pyramid | |
* @param {number} xx | |
* @param {number} yy | |
* @param {number} columns | |
* @param {number} rows | |
* @param {number} columnGap | |
* @param {number} rowGap | |
* @param {function} callback | |
* @return {composite} A new composite containing objects created in the callback | |
*/ | |
Composites.pyramid = function(xx, yy, columns, rows, columnGap, rowGap, callback) { | |
return Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y, column, row, lastBody, i) { | |
var actualRows = Math.min(rows, Math.ceil(columns / 2)), | |
lastBodyWidth = lastBody ? lastBody.bounds.max.x - lastBody.bounds.min.x : 0; | |
if (row > actualRows) | |
return; | |
// reverse row order | |
row = actualRows - row; | |
var start = row, | |
end = columns - 1 - row; | |
if (column < start || column > end) | |
return; | |
// retroactively fix the first body's position, since width was unknown | |
if (i === 1) { | |
Body.translate(lastBody, { x: (column + (columns % 2 === 1 ? 1 : -1)) * lastBodyWidth, y: 0 }); | |
} | |
var xOffset = lastBody ? column * lastBodyWidth : 0; | |
return callback(xx + xOffset + column * columnGap, y, column, row, lastBody, i); | |
}); | |
}; | |
/** | |
* Creates a composite with a Newton's Cradle setup of bodies and constraints. | |
* @method newtonsCradle | |
* @param {number} xx | |
* @param {number} yy | |
* @param {number} number | |
* @param {number} size | |
* @param {number} length | |
* @return {composite} A new composite newtonsCradle body | |
*/ | |
Composites.newtonsCradle = function(xx, yy, number, size, length) { | |
var newtonsCradle = Composite.create({ label: 'Newtons Cradle' }); | |
for (var i = 0; i < number; i++) { | |
var separation = 1.9, | |
circle = Bodies.circle(xx + i * (size * separation), yy + length, size, | |
{ inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0.0001, slop: 1 }), | |
constraint = Constraint.create({ pointA: { x: xx + i * (size * separation), y: yy }, bodyB: circle }); | |
Composite.addBody(newtonsCradle, circle); | |
Composite.addConstraint(newtonsCradle, constraint); | |
} | |
return newtonsCradle; | |
}; | |
/** | |
* Creates a composite with simple car setup of bodies and constraints. | |
* @method car | |
* @param {number} xx | |
* @param {number} yy | |
* @param {number} width | |
* @param {number} height | |
* @param {number} wheelSize | |
* @return {composite} A new composite car body | |
*/ | |
Composites.car = function(xx, yy, width, height, wheelSize) { | |
var group = Body.nextGroup(true), | |
wheelBase = 20, | |
wheelAOffset = -width * 0.5 + wheelBase, | |
wheelBOffset = width * 0.5 - wheelBase, | |
wheelYOffset = 0; | |
var car = Composite.create({ label: 'Car' }), | |
body = Bodies.rectangle(xx, yy, width, height, { | |
collisionFilter: { | |
group: group | |
}, | |
chamfer: { | |
radius: height * 0.5 | |
}, | |
density: 0.0002 | |
}); | |
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, { | |
collisionFilter: { | |
group: group | |
}, | |
friction: 0.8 | |
}); | |
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, { | |
collisionFilter: { | |
group: group | |
}, | |
friction: 0.8 | |
}); | |
var axelA = Constraint.create({ | |
bodyB: body, | |
pointB: { x: wheelAOffset, y: wheelYOffset }, | |
bodyA: wheelA, | |
stiffness: 1, | |
length: 0 | |
}); | |
var axelB = Constraint.create({ | |
bodyB: body, | |
pointB: { x: wheelBOffset, y: wheelYOffset }, | |
bodyA: wheelB, | |
stiffness: 1, | |
length: 0 | |
}); | |
Composite.addBody(car, body); | |
Composite.addBody(car, wheelA); | |
Composite.addBody(car, wheelB); | |
Composite.addConstraint(car, axelA); | |
Composite.addConstraint(car, axelB); | |
return car; | |
}; | |
/** | |
* Creates a simple soft body like object. | |
* @method softBody | |
* @param {number} xx | |
* @param {number} yy | |
* @param {number} columns | |
* @param {number} rows | |
* @param {number} columnGap | |
* @param {number} rowGap | |
* @param {boolean} crossBrace | |
* @param {number} particleRadius | |
* @param {} particleOptions | |
* @param {} constraintOptions | |
* @return {composite} A new composite softBody | |
*/ | |
Composites.softBody = function(xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) { | |
particleOptions = Common.extend({ inertia: Infinity }, particleOptions); | |
constraintOptions = Common.extend({ stiffness: 0.2, render: { type: 'line', anchors: false } }, constraintOptions); | |
var softBody = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y) { | |
return Bodies.circle(x, y, particleRadius, particleOptions); | |
}); | |
Composites.mesh(softBody, columns, rows, crossBrace, constraintOptions); | |
softBody.label = 'Soft Body'; | |
return softBody; | |
}; | |
})(); | |
},{"../body/Body":1,"../body/Composite":2,"../constraint/Constraint":12,"../core/Common":14,"./Bodies":23}],25:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Axes` module contains methods for creating and manipulating sets of axes. | |
* | |
* @class Axes | |
*/ | |
var Axes = {}; | |
module.exports = Axes; | |
var Vector = _dereq_('../geometry/Vector'); | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
/** | |
* Creates a new set of axes from the given vertices. | |
* @method fromVertices | |
* @param {vertices} vertices | |
* @return {axes} A new axes from the given vertices | |
*/ | |
Axes.fromVertices = function(vertices) { | |
var axes = {}; | |
// find the unique axes, using edge normal gradients | |
for (var i = 0; i < vertices.length; i++) { | |
var j = (i + 1) % vertices.length, | |
normal = Vector.normalise({ | |
x: vertices[j].y - vertices[i].y, | |
y: vertices[i].x - vertices[j].x | |
}), | |
gradient = (normal.y === 0) ? Infinity : (normal.x / normal.y); | |
// limit precision | |
gradient = gradient.toFixed(3).toString(); | |
axes[gradient] = normal; | |
} | |
return Common.values(axes); | |
}; | |
/** | |
* Rotates a set of axes by the given angle. | |
* @method rotate | |
* @param {axes} axes | |
* @param {number} angle | |
*/ | |
Axes.rotate = function(axes, angle) { | |
if (angle === 0) | |
return; | |
var cos = Math.cos(angle), | |
sin = Math.sin(angle); | |
for (var i = 0; i < axes.length; i++) { | |
var axis = axes[i], | |
xx; | |
xx = axis.x * cos - axis.y * sin; | |
axis.y = axis.x * sin + axis.y * cos; | |
axis.x = xx; | |
} | |
}; | |
})(); | |
},{"../core/Common":14,"../geometry/Vector":28}],26:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Bounds` module contains methods for creating and manipulating axis-aligned bounding boxes (AABB). | |
* | |
* @class Bounds | |
*/ | |
var Bounds = {}; | |
module.exports = Bounds; | |
(function() { | |
/** | |
* Creates a new axis-aligned bounding box (AABB) for the given vertices. | |
* @method create | |
* @param {vertices} vertices | |
* @return {bounds} A new bounds object | |
*/ | |
Bounds.create = function(vertices) { | |
var bounds = { | |
min: { x: 0, y: 0 }, | |
max: { x: 0, y: 0 } | |
}; | |
if (vertices) | |
Bounds.update(bounds, vertices); | |
return bounds; | |
}; | |
/** | |
* Updates bounds using the given vertices and extends the bounds given a velocity. | |
* @method update | |
* @param {bounds} bounds | |
* @param {vertices} vertices | |
* @param {vector} velocity | |
*/ | |
Bounds.update = function(bounds, vertices, velocity) { | |
bounds.min.x = Infinity; | |
bounds.max.x = -Infinity; | |
bounds.min.y = Infinity; | |
bounds.max.y = -Infinity; | |
for (var i = 0; i < vertices.length; i++) { | |
var vertex = vertices[i]; | |
if (vertex.x > bounds.max.x) bounds.max.x = vertex.x; | |
if (vertex.x < bounds.min.x) bounds.min.x = vertex.x; | |
if (vertex.y > bounds.max.y) bounds.max.y = vertex.y; | |
if (vertex.y < bounds.min.y) bounds.min.y = vertex.y; | |
} | |
if (velocity) { | |
if (velocity.x > 0) { | |
bounds.max.x += velocity.x; | |
} else { | |
bounds.min.x += velocity.x; | |
} | |
if (velocity.y > 0) { | |
bounds.max.y += velocity.y; | |
} else { | |
bounds.min.y += velocity.y; | |
} | |
} | |
}; | |
/** | |
* Returns true if the bounds contains the given point. | |
* @method contains | |
* @param {bounds} bounds | |
* @param {vector} point | |
* @return {boolean} True if the bounds contain the point, otherwise false | |
*/ | |
Bounds.contains = function(bounds, point) { | |
return point.x >= bounds.min.x && point.x <= bounds.max.x | |
&& point.y >= bounds.min.y && point.y <= bounds.max.y; | |
}; | |
/** | |
* Returns true if the two bounds intersect. | |
* @method overlaps | |
* @param {bounds} boundsA | |
* @param {bounds} boundsB | |
* @return {boolean} True if the bounds overlap, otherwise false | |
*/ | |
Bounds.overlaps = function(boundsA, boundsB) { | |
return (boundsA.min.x <= boundsB.max.x && boundsA.max.x >= boundsB.min.x | |
&& boundsA.max.y >= boundsB.min.y && boundsA.min.y <= boundsB.max.y); | |
}; | |
/** | |
* Translates the bounds by the given vector. | |
* @method translate | |
* @param {bounds} bounds | |
* @param {vector} vector | |
*/ | |
Bounds.translate = function(bounds, vector) { | |
bounds.min.x += vector.x; | |
bounds.max.x += vector.x; | |
bounds.min.y += vector.y; | |
bounds.max.y += vector.y; | |
}; | |
/** | |
* Shifts the bounds to the given position. | |
* @method shift | |
* @param {bounds} bounds | |
* @param {vector} position | |
*/ | |
Bounds.shift = function(bounds, position) { | |
var deltaX = bounds.max.x - bounds.min.x, | |
deltaY = bounds.max.y - bounds.min.y; | |
bounds.min.x = position.x; | |
bounds.max.x = position.x + deltaX; | |
bounds.min.y = position.y; | |
bounds.max.y = position.y + deltaY; | |
}; | |
})(); | |
},{}],27:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Svg` module contains methods for converting SVG images into an array of vector points. | |
* | |
* To use this module you also need the SVGPathSeg polyfill: https://github.com/progers/pathseg | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Svg | |
*/ | |
var Svg = {}; | |
module.exports = Svg; | |
var Bounds = _dereq_('../geometry/Bounds'); | |
(function() { | |
/** | |
* Converts an SVG path into an array of vector points. | |
* If the input path forms a concave shape, you must decompose the result into convex parts before use. | |
* See `Bodies.fromVertices` which provides support for this. | |
* Note that this function is not guaranteed to support complex paths (such as those with holes). | |
* @method pathToVertices | |
* @param {SVGPathElement} path | |
* @param {Number} [sampleLength=15] | |
* @return {Vector[]} points | |
*/ | |
Svg.pathToVertices = function(path, sampleLength) { | |
// https://github.com/wout/svg.topoly.js/blob/master/svg.topoly.js | |
var i, il, total, point, segment, segments, | |
segmentsQueue, lastSegment, | |
lastPoint, segmentIndex, points = [], | |
lx, ly, length = 0, x = 0, y = 0; | |
sampleLength = sampleLength || 15; | |
var addPoint = function(px, py, pathSegType) { | |
// all odd-numbered path types are relative except PATHSEG_CLOSEPATH (1) | |
var isRelative = pathSegType % 2 === 1 && pathSegType > 1; | |
// when the last point doesn't equal the current point add the current point | |
if (!lastPoint || px != lastPoint.x || py != lastPoint.y) { | |
if (lastPoint && isRelative) { | |
lx = lastPoint.x; | |
ly = lastPoint.y; | |
} else { | |
lx = 0; | |
ly = 0; | |
} | |
var point = { | |
x: lx + px, | |
y: ly + py | |
}; | |
// set last point | |
if (isRelative || !lastPoint) { | |
lastPoint = point; | |
} | |
points.push(point); | |
x = lx + px; | |
y = ly + py; | |
} | |
}; | |
var addSegmentPoint = function(segment) { | |
var segType = segment.pathSegTypeAsLetter.toUpperCase(); | |
// skip path ends | |
if (segType === 'Z') | |
return; | |
// map segment to x and y | |
switch (segType) { | |
case 'M': | |
case 'L': | |
case 'T': | |
case 'C': | |
case 'S': | |
case 'Q': | |
x = segment.x; | |
y = segment.y; | |
break; | |
case 'H': | |
x = segment.x; | |
break; | |
case 'V': | |
y = segment.y; | |
break; | |
} | |
addPoint(x, y, segment.pathSegType); | |
}; | |
// ensure path is absolute | |
_svgPathToAbsolute(path); | |
// get total length | |
total = path.getTotalLength(); | |
// queue segments | |
segments = []; | |
for (i = 0; i < path.pathSegList.numberOfItems; i += 1) | |
segments.push(path.pathSegList.getItem(i)); | |
segmentsQueue = segments.concat(); | |
// sample through path | |
while (length < total) { | |
// get segment at position | |
segmentIndex = path.getPathSegAtLength(length); | |
segment = segments[segmentIndex]; | |
// new segment | |
if (segment != lastSegment) { | |
while (segmentsQueue.length && segmentsQueue[0] != segment) | |
addSegmentPoint(segmentsQueue.shift()); | |
lastSegment = segment; | |
} | |
// add points in between when curving | |
// TODO: adaptive sampling | |
switch (segment.pathSegTypeAsLetter.toUpperCase()) { | |
case 'C': | |
case 'T': | |
case 'S': | |
case 'Q': | |
case 'A': | |
point = path.getPointAtLength(length); | |
addPoint(point.x, point.y, 0); | |
break; | |
} | |
// increment by sample value | |
length += sampleLength; | |
} | |
// add remaining segments not passed by sampling | |
for (i = 0, il = segmentsQueue.length; i < il; ++i) | |
addSegmentPoint(segmentsQueue[i]); | |
return points; | |
}; | |
var _svgPathToAbsolute = function(path) { | |
// http://phrogz.net/convert-svg-path-to-all-absolute-commands | |
// Copyright (c) Gavin Kistner | |
// http://phrogz.net/js/_ReuseLicense.txt | |
// Modifications: tidy formatting and naming | |
var x0, y0, x1, y1, x2, y2, segs = path.pathSegList, | |
x = 0, y = 0, len = segs.numberOfItems; | |
for (var i = 0; i < len; ++i) { | |
var seg = segs.getItem(i), | |
segType = seg.pathSegTypeAsLetter; | |
if (/[MLHVCSQTA]/.test(segType)) { | |
if ('x' in seg) x = seg.x; | |
if ('y' in seg) y = seg.y; | |
} else { | |
if ('x1' in seg) x1 = x + seg.x1; | |
if ('x2' in seg) x2 = x + seg.x2; | |
if ('y1' in seg) y1 = y + seg.y1; | |
if ('y2' in seg) y2 = y + seg.y2; | |
if ('x' in seg) x += seg.x; | |
if ('y' in seg) y += seg.y; | |
switch (segType) { | |
case 'm': | |
segs.replaceItem(path.createSVGPathSegMovetoAbs(x, y), i); | |
break; | |
case 'l': | |
segs.replaceItem(path.createSVGPathSegLinetoAbs(x, y), i); | |
break; | |
case 'h': | |
segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x), i); | |
break; | |
case 'v': | |
segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y), i); | |
break; | |
case 'c': | |
segs.replaceItem(path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i); | |
break; | |
case 's': | |
segs.replaceItem(path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i); | |
break; | |
case 'q': | |
segs.replaceItem(path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i); | |
break; | |
case 't': | |
segs.replaceItem(path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i); | |
break; | |
case 'a': | |
segs.replaceItem(path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i); | |
break; | |
case 'z': | |
case 'Z': | |
x = x0; | |
y = y0; | |
break; | |
} | |
} | |
if (segType == 'M' || segType == 'm') { | |
x0 = x; | |
y0 = y; | |
} | |
} | |
}; | |
})(); | |
},{"../geometry/Bounds":26}],28:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Vector` module contains methods for creating and manipulating vectors. | |
* Vectors are the basis of all the geometry related operations in the engine. | |
* A `Matter.Vector` object is of the form `{ x: 0, y: 0 }`. | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Vector | |
*/ | |
// TODO: consider params for reusing vector objects | |
var Vector = {}; | |
module.exports = Vector; | |
(function() { | |
/** | |
* Creates a new vector. | |
* @method create | |
* @param {number} x | |
* @param {number} y | |
* @return {vector} A new vector | |
*/ | |
Vector.create = function(x, y) { | |
return { x: x || 0, y: y || 0 }; | |
}; | |
/** | |
* Returns a new vector with `x` and `y` copied from the given `vector`. | |
* @method clone | |
* @param {vector} vector | |
* @return {vector} A new cloned vector | |
*/ | |
Vector.clone = function(vector) { | |
return { x: vector.x, y: vector.y }; | |
}; | |
/** | |
* Returns the magnitude (length) of a vector. | |
* @method magnitude | |
* @param {vector} vector | |
* @return {number} The magnitude of the vector | |
*/ | |
Vector.magnitude = function(vector) { | |
return Math.sqrt((vector.x * vector.x) + (vector.y * vector.y)); | |
}; | |
/** | |
* Returns the magnitude (length) of a vector (therefore saving a `sqrt` operation). | |
* @method magnitudeSquared | |
* @param {vector} vector | |
* @return {number} The squared magnitude of the vector | |
*/ | |
Vector.magnitudeSquared = function(vector) { | |
return (vector.x * vector.x) + (vector.y * vector.y); | |
}; | |
/** | |
* Rotates the vector about (0, 0) by specified angle. | |
* @method rotate | |
* @param {vector} vector | |
* @param {number} angle | |
* @param {vector} [output] | |
* @return {vector} The vector rotated about (0, 0) | |
*/ | |
Vector.rotate = function(vector, angle, output) { | |
var cos = Math.cos(angle), sin = Math.sin(angle); | |
if (!output) output = {}; | |
var x = vector.x * cos - vector.y * sin; | |
output.y = vector.x * sin + vector.y * cos; | |
output.x = x; | |
return output; | |
}; | |
/** | |
* Rotates the vector about a specified point by specified angle. | |
* @method rotateAbout | |
* @param {vector} vector | |
* @param {number} angle | |
* @param {vector} point | |
* @param {vector} [output] | |
* @return {vector} A new vector rotated about the point | |
*/ | |
Vector.rotateAbout = function(vector, angle, point, output) { | |
var cos = Math.cos(angle), sin = Math.sin(angle); | |
if (!output) output = {}; | |
var x = point.x + ((vector.x - point.x) * cos - (vector.y - point.y) * sin); | |
output.y = point.y + ((vector.x - point.x) * sin + (vector.y - point.y) * cos); | |
output.x = x; | |
return output; | |
}; | |
/** | |
* Normalises a vector (such that its magnitude is `1`). | |
* @method normalise | |
* @param {vector} vector | |
* @return {vector} A new vector normalised | |
*/ | |
Vector.normalise = function(vector) { | |
var magnitude = Vector.magnitude(vector); | |
if (magnitude === 0) | |
return { x: 0, y: 0 }; | |
return { x: vector.x / magnitude, y: vector.y / magnitude }; | |
}; | |
/** | |
* Returns the dot-product of two vectors. | |
* @method dot | |
* @param {vector} vectorA | |
* @param {vector} vectorB | |
* @return {number} The dot product of the two vectors | |
*/ | |
Vector.dot = function(vectorA, vectorB) { | |
return (vectorA.x * vectorB.x) + (vectorA.y * vectorB.y); | |
}; | |
/** | |
* Returns the cross-product of two vectors. | |
* @method cross | |
* @param {vector} vectorA | |
* @param {vector} vectorB | |
* @return {number} The cross product of the two vectors | |
*/ | |
Vector.cross = function(vectorA, vectorB) { | |
return (vectorA.x * vectorB.y) - (vectorA.y * vectorB.x); | |
}; | |
/** | |
* Returns the cross-product of three vectors. | |
* @method cross3 | |
* @param {vector} vectorA | |
* @param {vector} vectorB | |
* @param {vector} vectorC | |
* @return {number} The cross product of the three vectors | |
*/ | |
Vector.cross3 = function(vectorA, vectorB, vectorC) { | |
return (vectorB.x - vectorA.x) * (vectorC.y - vectorA.y) - (vectorB.y - vectorA.y) * (vectorC.x - vectorA.x); | |
}; | |
/** | |
* Adds the two vectors. | |
* @method add | |
* @param {vector} vectorA | |
* @param {vector} vectorB | |
* @param {vector} [output] | |
* @return {vector} A new vector of vectorA and vectorB added | |
*/ | |
Vector.add = function(vectorA, vectorB, output) { | |
if (!output) output = {}; | |
output.x = vectorA.x + vectorB.x; | |
output.y = vectorA.y + vectorB.y; | |
return output; | |
}; | |
/** | |
* Subtracts the two vectors. | |
* @method sub | |
* @param {vector} vectorA | |
* @param {vector} vectorB | |
* @param {vector} [output] | |
* @return {vector} A new vector of vectorA and vectorB subtracted | |
*/ | |
Vector.sub = function(vectorA, vectorB, output) { | |
if (!output) output = {}; | |
output.x = vectorA.x - vectorB.x; | |
output.y = vectorA.y - vectorB.y; | |
return output; | |
}; | |
/** | |
* Multiplies a vector and a scalar. | |
* @method mult | |
* @param {vector} vector | |
* @param {number} scalar | |
* @return {vector} A new vector multiplied by scalar | |
*/ | |
Vector.mult = function(vector, scalar) { | |
return { x: vector.x * scalar, y: vector.y * scalar }; | |
}; | |
/** | |
* Divides a vector and a scalar. | |
* @method div | |
* @param {vector} vector | |
* @param {number} scalar | |
* @return {vector} A new vector divided by scalar | |
*/ | |
Vector.div = function(vector, scalar) { | |
return { x: vector.x / scalar, y: vector.y / scalar }; | |
}; | |
/** | |
* Returns the perpendicular vector. Set `negate` to true for the perpendicular in the opposite direction. | |
* @method perp | |
* @param {vector} vector | |
* @param {bool} [negate=false] | |
* @return {vector} The perpendicular vector | |
*/ | |
Vector.perp = function(vector, negate) { | |
negate = negate === true ? -1 : 1; | |
return { x: negate * -vector.y, y: negate * vector.x }; | |
}; | |
/** | |
* Negates both components of a vector such that it points in the opposite direction. | |
* @method neg | |
* @param {vector} vector | |
* @return {vector} The negated vector | |
*/ | |
Vector.neg = function(vector) { | |
return { x: -vector.x, y: -vector.y }; | |
}; | |
/** | |
* Returns the angle in radians between the two vectors relative to the x-axis. | |
* @method angle | |
* @param {vector} vectorA | |
* @param {vector} vectorB | |
* @return {number} The angle in radians | |
*/ | |
Vector.angle = function(vectorA, vectorB) { | |
return Math.atan2(vectorB.y - vectorA.y, vectorB.x - vectorA.x); | |
}; | |
/** | |
* Temporary vector pool (not thread-safe). | |
* @property _temp | |
* @type {vector[]} | |
* @private | |
*/ | |
Vector._temp = [ | |
Vector.create(), Vector.create(), | |
Vector.create(), Vector.create(), | |
Vector.create(), Vector.create() | |
]; | |
})(); | |
},{}],29:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices. | |
* A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`. | |
* A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull). | |
* | |
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). | |
* | |
* @class Vertices | |
*/ | |
var Vertices = {}; | |
module.exports = Vertices; | |
var Vector = _dereq_('../geometry/Vector'); | |
var Common = _dereq_('../core/Common'); | |
(function() { | |
/** | |
* Creates a new set of `Matter.Body` compatible vertices. | |
* The `points` argument accepts an array of `Matter.Vector` points orientated around the origin `(0, 0)`, for example: | |
* | |
* [{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }] | |
* | |
* The `Vertices.create` method returns a new array of vertices, which are similar to Matter.Vector objects, | |
* but with some additional references required for efficient collision detection routines. | |
* | |
* Vertices must be specified in clockwise order. | |
* | |
* Note that the `body` argument is not optional, a `Matter.Body` reference must be provided. | |
* | |
* @method create | |
* @param {vector[]} points | |
* @param {body} body | |
*/ | |
Vertices.create = function(points, body) { | |
var vertices = []; | |
for (var i = 0; i < points.length; i++) { | |
var point = points[i], | |
vertex = { | |
x: point.x, | |
y: point.y, | |
index: i, | |
body: body, | |
isInternal: false | |
}; | |
vertices.push(vertex); | |
} | |
return vertices; | |
}; | |
/** | |
* Parses a string containing ordered x y pairs separated by spaces (and optionally commas), | |
* into a `Matter.Vertices` object for the given `Matter.Body`. | |
* For parsing SVG paths, see `Svg.pathToVertices`. | |
* @method fromPath | |
* @param {string} path | |
* @param {body} body | |
* @return {vertices} vertices | |
*/ | |
Vertices.fromPath = function(path, body) { | |
var pathPattern = /L?\s*([\-\d\.e]+)[\s,]*([\-\d\.e]+)*/ig, | |
points = []; | |
path.replace(pathPattern, function(match, x, y) { | |
points.push({ x: parseFloat(x), y: parseFloat(y) }); | |
}); | |
return Vertices.create(points, body); | |
}; | |
/** | |
* Returns the centre (centroid) of the set of vertices. | |
* @method centre | |
* @param {vertices} vertices | |
* @return {vector} The centre point | |
*/ | |
Vertices.centre = function(vertices) { | |
var area = Vertices.area(vertices, true), | |
centre = { x: 0, y: 0 }, | |
cross, | |
temp, | |
j; | |
for (var i = 0; i < vertices.length; i++) { | |
j = (i + 1) % vertices.length; | |
cross = Vector.cross(vertices[i], vertices[j]); | |
temp = Vector.mult(Vector.add(vertices[i], vertices[j]), cross); | |
centre = Vector.add(centre, temp); | |
} | |
return Vector.div(centre, 6 * area); | |
}; | |
/** | |
* Returns the average (mean) of the set of vertices. | |
* @method mean | |
* @param {vertices} vertices | |
* @return {vector} The average point | |
*/ | |
Vertices.mean = function(vertices) { | |
var average = { x: 0, y: 0 }; | |
for (var i = 0; i < vertices.length; i++) { | |
average.x += vertices[i].x; | |
average.y += vertices[i].y; | |
} | |
return Vector.div(average, vertices.length); | |
}; | |
/** | |
* Returns the area of the set of vertices. | |
* @method area | |
* @param {vertices} vertices | |
* @param {bool} signed | |
* @return {number} The area | |
*/ | |
Vertices.area = function(vertices, signed) { | |
var area = 0, | |
j = vertices.length - 1; | |
for (var i = 0; i < vertices.length; i++) { | |
area += (vertices[j].x - vertices[i].x) * (vertices[j].y + vertices[i].y); | |
j = i; | |
} | |
if (signed) | |
return area / 2; | |
return Math.abs(area) / 2; | |
}; | |
/** | |
* Returns the moment of inertia (second moment of area) of the set of vertices given the total mass. | |
* @method inertia | |
* @param {vertices} vertices | |
* @param {number} mass | |
* @return {number} The polygon's moment of inertia | |
*/ | |
Vertices.inertia = function(vertices, mass) { | |
var numerator = 0, | |
denominator = 0, | |
v = vertices, | |
cross, | |
j; | |
// find the polygon's moment of inertia, using second moment of area | |
// from equations at http://www.physicsforums.com/showthread.php?t=25293 | |
for (var n = 0; n < v.length; n++) { | |
j = (n + 1) % v.length; | |
cross = Math.abs(Vector.cross(v[j], v[n])); | |
numerator += cross * (Vector.dot(v[j], v[j]) + Vector.dot(v[j], v[n]) + Vector.dot(v[n], v[n])); | |
denominator += cross; | |
} | |
return (mass / 6) * (numerator / denominator); | |
}; | |
/** | |
* Translates the set of vertices in-place. | |
* @method translate | |
* @param {vertices} vertices | |
* @param {vector} vector | |
* @param {number} scalar | |
*/ | |
Vertices.translate = function(vertices, vector, scalar) { | |
var i; | |
if (scalar) { | |
for (i = 0; i < vertices.length; i++) { | |
vertices[i].x += vector.x * scalar; | |
vertices[i].y += vector.y * scalar; | |
} | |
} else { | |
for (i = 0; i < vertices.length; i++) { | |
vertices[i].x += vector.x; | |
vertices[i].y += vector.y; | |
} | |
} | |
return vertices; | |
}; | |
/** | |
* Rotates the set of vertices in-place. | |
* @method rotate | |
* @param {vertices} vertices | |
* @param {number} angle | |
* @param {vector} point | |
*/ | |
Vertices.rotate = function(vertices, angle, point) { | |
if (angle === 0) | |
return; | |
var cos = Math.cos(angle), | |
sin = Math.sin(angle); | |
for (var i = 0; i < vertices.length; i++) { | |
var vertice = vertices[i], | |
dx = vertice.x - point.x, | |
dy = vertice.y - point.y; | |
vertice.x = point.x + (dx * cos - dy * sin); | |
vertice.y = point.y + (dx * sin + dy * cos); | |
} | |
return vertices; | |
}; | |
/** | |
* Returns `true` if the `point` is inside the set of `vertices`. | |
* @method contains | |
* @param {vertices} vertices | |
* @param {vector} point | |
* @return {boolean} True if the vertices contains point, otherwise false | |
*/ | |
Vertices.contains = function(vertices, point) { | |
for (var i = 0; i < vertices.length; i++) { | |
var vertice = vertices[i], | |
nextVertice = vertices[(i + 1) % vertices.length]; | |
if ((point.x - vertice.x) * (nextVertice.y - vertice.y) + (point.y - vertice.y) * (vertice.x - nextVertice.x) > 0) { | |
return false; | |
} | |
} | |
return true; | |
}; | |
/** | |
* Scales the vertices from a point (default is centre) in-place. | |
* @method scale | |
* @param {vertices} vertices | |
* @param {number} scaleX | |
* @param {number} scaleY | |
* @param {vector} point | |
*/ | |
Vertices.scale = function(vertices, scaleX, scaleY, point) { | |
if (scaleX === 1 && scaleY === 1) | |
return vertices; | |
point = point || Vertices.centre(vertices); | |
var vertex, | |
delta; | |
for (var i = 0; i < vertices.length; i++) { | |
vertex = vertices[i]; | |
delta = Vector.sub(vertex, point); | |
vertices[i].x = point.x + delta.x * scaleX; | |
vertices[i].y = point.y + delta.y * scaleY; | |
} | |
return vertices; | |
}; | |
/** | |
* Chamfers a set of vertices by giving them rounded corners, returns a new set of vertices. | |
* The radius parameter is a single number or an array to specify the radius for each vertex. | |
* @method chamfer | |
* @param {vertices} vertices | |
* @param {number[]} radius | |
* @param {number} quality | |
* @param {number} qualityMin | |
* @param {number} qualityMax | |
*/ | |
Vertices.chamfer = function(vertices, radius, quality, qualityMin, qualityMax) { | |
radius = radius || [8]; | |
if (!radius.length) | |
radius = [radius]; | |
// quality defaults to -1, which is auto | |
quality = (typeof quality !== 'undefined') ? quality : -1; | |
qualityMin = qualityMin || 2; | |
qualityMax = qualityMax || 14; | |
var newVertices = []; | |
for (var i = 0; i < vertices.length; i++) { | |
var prevVertex = vertices[i - 1 >= 0 ? i - 1 : vertices.length - 1], | |
vertex = vertices[i], | |
nextVertex = vertices[(i + 1) % vertices.length], | |
currentRadius = radius[i < radius.length ? i : radius.length - 1]; | |
if (currentRadius === 0) { | |
newVertices.push(vertex); | |
continue; | |
} | |
var prevNormal = Vector.normalise({ | |
x: vertex.y - prevVertex.y, | |
y: prevVertex.x - vertex.x | |
}); | |
var nextNormal = Vector.normalise({ | |
x: nextVertex.y - vertex.y, | |
y: vertex.x - nextVertex.x | |
}); | |
var diagonalRadius = Math.sqrt(2 * Math.pow(currentRadius, 2)), | |
radiusVector = Vector.mult(Common.clone(prevNormal), currentRadius), | |
midNormal = Vector.normalise(Vector.mult(Vector.add(prevNormal, nextNormal), 0.5)), | |
scaledVertex = Vector.sub(vertex, Vector.mult(midNormal, diagonalRadius)); | |
var precision = quality; | |
if (quality === -1) { | |
// automatically decide precision | |
precision = Math.pow(currentRadius, 0.32) * 1.75; | |
} | |
precision = Common.clamp(precision, qualityMin, qualityMax); | |
// use an even value for precision, more likely to reduce axes by using symmetry | |
if (precision % 2 === 1) | |
precision += 1; | |
var alpha = Math.acos(Vector.dot(prevNormal, nextNormal)), | |
theta = alpha / precision; | |
for (var j = 0; j < precision; j++) { | |
newVertices.push(Vector.add(Vector.rotate(radiusVector, theta * j), scaledVertex)); | |
} | |
} | |
return newVertices; | |
}; | |
/** | |
* Sorts the input vertices into clockwise order in place. | |
* @method clockwiseSort | |
* @param {vertices} vertices | |
* @return {vertices} vertices | |
*/ | |
Vertices.clockwiseSort = function(vertices) { | |
var centre = Vertices.mean(vertices); | |
vertices.sort(function(vertexA, vertexB) { | |
return Vector.angle(centre, vertexA) - Vector.angle(centre, vertexB); | |
}); | |
return vertices; | |
}; | |
/** | |
* Returns true if the vertices form a convex shape (vertices must be in clockwise order). | |
* @method isConvex | |
* @param {vertices} vertices | |
* @return {bool} `true` if the `vertices` are convex, `false` if not (or `null` if not computable). | |
*/ | |
Vertices.isConvex = function(vertices) { | |
// http://paulbourke.net/geometry/polygonmesh/ | |
// Copyright (c) Paul Bourke (use permitted) | |
var flag = 0, | |
n = vertices.length, | |
i, | |
j, | |
k, | |
z; | |
if (n < 3) | |
return null; | |
for (i = 0; i < n; i++) { | |
j = (i + 1) % n; | |
k = (i + 2) % n; | |
z = (vertices[j].x - vertices[i].x) * (vertices[k].y - vertices[j].y); | |
z -= (vertices[j].y - vertices[i].y) * (vertices[k].x - vertices[j].x); | |
if (z < 0) { | |
flag |= 1; | |
} else if (z > 0) { | |
flag |= 2; | |
} | |
if (flag === 3) { | |
return false; | |
} | |
} | |
if (flag !== 0){ | |
return true; | |
} else { | |
return null; | |
} | |
}; | |
/** | |
* Returns the convex hull of the input vertices as a new array of points. | |
* @method hull | |
* @param {vertices} vertices | |
* @return [vertex] vertices | |
*/ | |
Vertices.hull = function(vertices) { | |
// http://geomalgorithms.com/a10-_hull-1.html | |
var upper = [], | |
lower = [], | |
vertex, | |
i; | |
// sort vertices on x-axis (y-axis for ties) | |
vertices = vertices.slice(0); | |
vertices.sort(function(vertexA, vertexB) { | |
var dx = vertexA.x - vertexB.x; | |
return dx !== 0 ? dx : vertexA.y - vertexB.y; | |
}); | |
// build lower hull | |
for (i = 0; i < vertices.length; i += 1) { | |
vertex = vertices[i]; | |
while (lower.length >= 2 | |
&& Vector.cross3(lower[lower.length - 2], lower[lower.length - 1], vertex) <= 0) { | |
lower.pop(); | |
} | |
lower.push(vertex); | |
} | |
// build upper hull | |
for (i = vertices.length - 1; i >= 0; i -= 1) { | |
vertex = vertices[i]; | |
while (upper.length >= 2 | |
&& Vector.cross3(upper[upper.length - 2], upper[upper.length - 1], vertex) <= 0) { | |
upper.pop(); | |
} | |
upper.push(vertex); | |
} | |
// concatenation of the lower and upper hulls gives the convex hull | |
// omit last points because they are repeated at the beginning of the other list | |
upper.pop(); | |
lower.pop(); | |
return upper.concat(lower); | |
}; | |
})(); | |
},{"../core/Common":14,"../geometry/Vector":28}],30:[function(_dereq_,module,exports){ | |
var Matter = module.exports = _dereq_('../core/Matter'); | |
Matter.Body = _dereq_('../body/Body'); | |
Matter.Composite = _dereq_('../body/Composite'); | |
Matter.World = _dereq_('../body/World'); | |
Matter.Contact = _dereq_('../collision/Contact'); | |
Matter.Detector = _dereq_('../collision/Detector'); | |
Matter.Grid = _dereq_('../collision/Grid'); | |
Matter.Pairs = _dereq_('../collision/Pairs'); | |
Matter.Pair = _dereq_('../collision/Pair'); | |
Matter.Query = _dereq_('../collision/Query'); | |
Matter.Resolver = _dereq_('../collision/Resolver'); | |
Matter.SAT = _dereq_('../collision/SAT'); | |
Matter.Constraint = _dereq_('../constraint/Constraint'); | |
Matter.MouseConstraint = _dereq_('../constraint/MouseConstraint'); | |
Matter.Common = _dereq_('../core/Common'); | |
Matter.Engine = _dereq_('../core/Engine'); | |
Matter.Events = _dereq_('../core/Events'); | |
Matter.Mouse = _dereq_('../core/Mouse'); | |
Matter.Runner = _dereq_('../core/Runner'); | |
Matter.Sleeping = _dereq_('../core/Sleeping'); | |
Matter.Plugin = _dereq_('../core/Plugin'); | |
Matter.Bodies = _dereq_('../factory/Bodies'); | |
Matter.Composites = _dereq_('../factory/Composites'); | |
Matter.Axes = _dereq_('../geometry/Axes'); | |
Matter.Bounds = _dereq_('../geometry/Bounds'); | |
Matter.Svg = _dereq_('../geometry/Svg'); | |
Matter.Vector = _dereq_('../geometry/Vector'); | |
Matter.Vertices = _dereq_('../geometry/Vertices'); | |
Matter.Render = _dereq_('../render/Render'); | |
Matter.RenderPixi = _dereq_('../render/RenderPixi'); | |
// aliases | |
Matter.World.add = Matter.Composite.add; | |
Matter.World.remove = Matter.Composite.remove; | |
Matter.World.addComposite = Matter.Composite.addComposite; | |
Matter.World.addBody = Matter.Composite.addBody; | |
Matter.World.addConstraint = Matter.Composite.addConstraint; | |
Matter.World.clear = Matter.Composite.clear; | |
Matter.Engine.run = Matter.Runner.run; | |
},{"../body/Body":1,"../body/Composite":2,"../body/World":3,"../collision/Contact":4,"../collision/Detector":5,"../collision/Grid":6,"../collision/Pair":7,"../collision/Pairs":8,"../collision/Query":9,"../collision/Resolver":10,"../collision/SAT":11,"../constraint/Constraint":12,"../constraint/MouseConstraint":13,"../core/Common":14,"../core/Engine":15,"../core/Events":16,"../core/Matter":17,"../core/Metrics":18,"../core/Mouse":19,"../core/Plugin":20,"../core/Runner":21,"../core/Sleeping":22,"../factory/Bodies":23,"../factory/Composites":24,"../geometry/Axes":25,"../geometry/Bounds":26,"../geometry/Svg":27,"../geometry/Vector":28,"../geometry/Vertices":29,"../render/Render":31,"../render/RenderPixi":32}],31:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.Render` module is a simple HTML5 canvas based renderer for visualising instances of `Matter.Engine`. | |
* It is intended for development and debugging purposes, but may also be suitable for simple games. | |
* It includes a number of drawing options including wireframe, vector with support for sprites and viewports. | |
* | |
* @class Render | |
*/ | |
var Render = {}; | |
module.exports = Render; | |
var Common = _dereq_('../core/Common'); | |
var Composite = _dereq_('../body/Composite'); | |
var Bounds = _dereq_('../geometry/Bounds'); | |
var Events = _dereq_('../core/Events'); | |
var Grid = _dereq_('../collision/Grid'); | |
var Vector = _dereq_('../geometry/Vector'); | |
var Mouse = _dereq_('../core/Mouse'); | |
(function() { | |
var _requestAnimationFrame, | |
_cancelAnimationFrame; | |
if (typeof window !== 'undefined') { | |
_requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame | |
|| window.mozRequestAnimationFrame || window.msRequestAnimationFrame | |
|| function(callback){ window.setTimeout(function() { callback(Common.now()); }, 1000 / 60); }; | |
_cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame | |
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; | |
} | |
/** | |
* Creates a new renderer. The options parameter is an object that specifies any properties you wish to override the defaults. | |
* All properties have default values, and many are pre-calculated automatically based on other properties. | |
* See the properties section below for detailed information on what you can pass via the `options` object. | |
* @method create | |
* @param {object} [options] | |
* @return {render} A new renderer | |
*/ | |
Render.create = function(options) { | |
var defaults = { | |
controller: Render, | |
engine: null, | |
element: null, | |
canvas: null, | |
mouse: null, | |
frameRequestId: null, | |
options: { | |
width: 800, | |
height: 600, | |
pixelRatio: 1, | |
background: '#18181d', | |
wireframeBackground: '#0f0f13', | |
hasBounds: !!options.bounds, | |
enabled: true, | |
wireframes: true, | |
showSleeping: true, | |
showDebug: false, | |
showBroadphase: false, | |
showBounds: false, | |
showVelocity: false, | |
showCollisions: false, | |
showSeparations: false, | |
showAxes: false, | |
showPositions: false, | |
showAngleIndicator: false, | |
showIds: false, | |
showShadows: false, | |
showVertexNumbers: false, | |
showConvexHulls: false, | |
showInternalEdges: false, | |
showMousePosition: false | |
} | |
}; | |
var render = Common.extend(defaults, options); | |
if (render.canvas) { | |
render.canvas.width = render.options.width || render.canvas.width; | |
render.canvas.height = render.options.height || render.canvas.height; | |
} | |
render.mouse = options.mouse; | |
render.engine = options.engine; | |
render.canvas = render.canvas || _createCanvas(render.options.width, render.options.height); | |
render.context = render.canvas.getContext('2d'); | |
render.textures = {}; | |
render.bounds = render.bounds || { | |
min: { | |
x: 0, | |
y: 0 | |
}, | |
max: { | |
x: render.canvas.width, | |
y: render.canvas.height | |
} | |
}; | |
if (render.options.pixelRatio !== 1) { | |
Render.setPixelRatio(render, render.options.pixelRatio); | |
} | |
if (Common.isElement(render.element)) { | |
render.element.appendChild(render.canvas); | |
} else { | |
Common.log('Render.create: options.element was undefined, render.canvas was created but not appended', 'warn'); | |
} | |
return render; | |
}; | |
/** | |
* Continuously updates the render canvas on the `requestAnimationFrame` event. | |
* @method run | |
* @param {render} render | |
*/ | |
Render.run = function(render) { | |
(function loop(time){ | |
render.frameRequestId = _requestAnimationFrame(loop); | |
Render.world(render); | |
})(); | |
}; | |
/** | |
* Ends execution of `Render.run` on the given `render`, by canceling the animation frame request event loop. | |
* @method stop | |
* @param {render} render | |
*/ | |
Render.stop = function(render) { | |
_cancelAnimationFrame(render.frameRequestId); | |
}; | |
/** | |
* Sets the pixel ratio of the renderer and updates the canvas. | |
* To automatically detect the correct ratio, pass the string `'auto'` for `pixelRatio`. | |
* @method setPixelRatio | |
* @param {render} render | |
* @param {number} pixelRatio | |
*/ | |
Render.setPixelRatio = function(render, pixelRatio) { | |
var options = render.options, | |
canvas = render.canvas; | |
if (pixelRatio === 'auto') { | |
pixelRatio = _getPixelRatio(canvas); | |
} | |
options.pixelRatio = pixelRatio; | |
canvas.setAttribute('data-pixel-ratio', pixelRatio); | |
canvas.width = options.width * pixelRatio; | |
canvas.height = options.height * pixelRatio; | |
canvas.style.width = options.width + 'px'; | |
canvas.style.height = options.height + 'px'; | |
render.context.scale(pixelRatio, pixelRatio); | |
}; | |
/** | |
* Positions and sizes the viewport around the given object bounds. | |
* Objects must have at least one of the following properties: | |
* - `object.bounds` | |
* - `object.position` | |
* - `object.min` and `object.max` | |
* - `object.x` and `object.y` | |
* @method lookAt | |
* @param {render} render | |
* @param {object[]} objects | |
* @param {vector} [padding] | |
* @param {bool} [center=true] | |
*/ | |
Render.lookAt = function(render, objects, padding, center) { | |
center = typeof center !== 'undefined' ? center : true; | |
objects = Common.isArray(objects) ? objects : [objects]; | |
padding = padding || { | |
x: 0, | |
y: 0 | |
}; | |
// find bounds of all objects | |
var bounds = { | |
min: { x: Infinity, y: Infinity }, | |
max: { x: -Infinity, y: -Infinity } | |
}; | |
for (var i = 0; i < objects.length; i += 1) { | |
var object = objects[i], | |
min = object.bounds ? object.bounds.min : (object.min || object.position || object), | |
max = object.bounds ? object.bounds.max : (object.max || object.position || object); | |
if (min && max) { | |
if (min.x < bounds.min.x) | |
bounds.min.x = min.x; | |
if (max.x > bounds.max.x) | |
bounds.max.x = max.x; | |
if (min.y < bounds.min.y) | |
bounds.min.y = min.y; | |
if (max.y > bounds.max.y) | |
bounds.max.y = max.y; | |
} | |
} | |
// find ratios | |
var width = (bounds.max.x - bounds.min.x) + 2 * padding.x, | |
height = (bounds.max.y - bounds.min.y) + 2 * padding.y, | |
viewHeight = render.canvas.height, | |
viewWidth = render.canvas.width, | |
outerRatio = viewWidth / viewHeight, | |
innerRatio = width / height, | |
scaleX = 1, | |
scaleY = 1; | |
// find scale factor | |
if (innerRatio > outerRatio) { | |
scaleY = innerRatio / outerRatio; | |
} else { | |
scaleX = outerRatio / innerRatio; | |
} | |
// enable bounds | |
render.options.hasBounds = true; | |
// position and size | |
render.bounds.min.x = bounds.min.x; | |
render.bounds.max.x = bounds.min.x + width * scaleX; | |
render.bounds.min.y = bounds.min.y; | |
render.bounds.max.y = bounds.min.y + height * scaleY; | |
// center | |
if (center) { | |
render.bounds.min.x += width * 0.5 - (width * scaleX) * 0.5; | |
render.bounds.max.x += width * 0.5 - (width * scaleX) * 0.5; | |
render.bounds.min.y += height * 0.5 - (height * scaleY) * 0.5; | |
render.bounds.max.y += height * 0.5 - (height * scaleY) * 0.5; | |
} | |
// padding | |
render.bounds.min.x -= padding.x; | |
render.bounds.max.x -= padding.x; | |
render.bounds.min.y -= padding.y; | |
render.bounds.max.y -= padding.y; | |
// update mouse | |
if (render.mouse) { | |
Mouse.setScale(render.mouse, { | |
x: (render.bounds.max.x - render.bounds.min.x) / render.canvas.width, | |
y: (render.bounds.max.y - render.bounds.min.y) / render.canvas.height | |
}); | |
Mouse.setOffset(render.mouse, render.bounds.min); | |
} | |
}; | |
/** | |
* Applies viewport transforms based on `render.bounds` to a render context. | |
* @method startViewTransform | |
* @param {render} render | |
*/ | |
Render.startViewTransform = function(render) { | |
var boundsWidth = render.bounds.max.x - render.bounds.min.x, | |
boundsHeight = render.bounds.max.y - render.bounds.min.y, | |
boundsScaleX = boundsWidth / render.options.width, | |
boundsScaleY = boundsHeight / render.options.height; | |
render.context.scale(1 / boundsScaleX, 1 / boundsScaleY); | |
render.context.translate(-render.bounds.min.x, -render.bounds.min.y); | |
}; | |
/** | |
* Resets all transforms on the render context. | |
* @method endViewTransform | |
* @param {render} render | |
*/ | |
Render.endViewTransform = function(render) { | |
render.context.setTransform(render.options.pixelRatio, 0, 0, render.options.pixelRatio, 0, 0); | |
}; | |
/** | |
* Renders the given `engine`'s `Matter.World` object. | |
* This is the entry point for all rendering and should be called every time the scene changes. | |
* @method world | |
* @param {render} render | |
*/ | |
Render.world = function(render) { | |
var engine = render.engine, | |
world = engine.world, | |
canvas = render.canvas, | |
context = render.context, | |
options = render.options, | |
allBodies = Composite.allBodies(world), | |
allConstraints = Composite.allConstraints(world), | |
background = options.wireframes ? options.wireframeBackground : options.background, | |
bodies = [], | |
constraints = [], | |
i; | |
var event = { | |
timestamp: engine.timing.timestamp | |
}; | |
Events.trigger(render, 'beforeRender', event); | |
// apply background if it has changed | |
if (render.currentBackground !== background) | |
_applyBackground(render, background); | |
// clear the canvas with a transparent fill, to allow the canvas background to show | |
context.globalCompositeOperation = 'source-in'; | |
context.fillStyle = "transparent"; | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
context.globalCompositeOperation = 'source-over'; | |
// handle bounds | |
if (options.hasBounds) { | |
// filter out bodies that are not in view | |
for (i = 0; i < allBodies.length; i++) { | |
var body = allBodies[i]; | |
if (Bounds.overlaps(body.bounds, render.bounds)) | |
bodies.push(body); | |
} | |
// filter out constraints that are not in view | |
for (i = 0; i < allConstraints.length; i++) { | |
var constraint = allConstraints[i], | |
bodyA = constraint.bodyA, | |
bodyB = constraint.bodyB, | |
pointAWorld = constraint.pointA, | |
pointBWorld = constraint.pointB; | |
if (bodyA) pointAWorld = Vector.add(bodyA.position, constraint.pointA); | |
if (bodyB) pointBWorld = Vector.add(bodyB.position, constraint.pointB); | |
if (!pointAWorld || !pointBWorld) | |
continue; | |
if (Bounds.contains(render.bounds, pointAWorld) || Bounds.contains(render.bounds, pointBWorld)) | |
constraints.push(constraint); | |
} | |
// transform the view | |
Render.startViewTransform(render); | |
// update mouse | |
if (render.mouse) { | |
Mouse.setScale(render.mouse, { | |
x: (render.bounds.max.x - render.bounds.min.x) / render.canvas.width, | |
y: (render.bounds.max.y - render.bounds.min.y) / render.canvas.height | |
}); | |
Mouse.setOffset(render.mouse, render.bounds.min); | |
} | |
} else { | |
constraints = allConstraints; | |
bodies = allBodies; | |
} | |
if (!options.wireframes || (engine.enableSleeping && options.showSleeping)) { | |
// fully featured rendering of bodies | |
Render.bodies(render, bodies, context); | |
} else { | |
if (options.showConvexHulls) | |
Render.bodyConvexHulls(render, bodies, context); | |
// optimised method for wireframes only | |
Render.bodyWireframes(render, bodies, context); | |
} | |
if (options.showBounds) | |
Render.bodyBounds(render, bodies, context); | |
if (options.showAxes || options.showAngleIndicator) | |
Render.bodyAxes(render, bodies, context); | |
if (options.showPositions) | |
Render.bodyPositions(render, bodies, context); | |
if (options.showVelocity) | |
Render.bodyVelocity(render, bodies, context); | |
if (options.showIds) | |
Render.bodyIds(render, bodies, context); | |
if (options.showSeparations) | |
Render.separations(render, engine.pairs.list, context); | |
if (options.showCollisions) | |
Render.collisions(render, engine.pairs.list, context); | |
if (options.showVertexNumbers) | |
Render.vertexNumbers(render, bodies, context); | |
if (options.showMousePosition) | |
Render.mousePosition(render, render.mouse, context); | |
Render.constraints(constraints, context); | |
if (options.showBroadphase && engine.broadphase.controller === Grid) | |
Render.grid(render, engine.broadphase, context); | |
if (options.showDebug) | |
Render.debug(render, context); | |
if (options.hasBounds) { | |
// revert view transforms | |
Render.endViewTransform(render); | |
} | |
Events.trigger(render, 'afterRender', event); | |
}; | |
/** | |
* Description | |
* @private | |
* @method debug | |
* @param {render} render | |
* @param {RenderingContext} context | |
*/ | |
Render.debug = function(render, context) { | |
var c = context, | |
engine = render.engine, | |
world = engine.world, | |
metrics = engine.metrics, | |
options = render.options, | |
bodies = Composite.allBodies(world), | |
space = " "; | |
if (engine.timing.timestamp - (render.debugTimestamp || 0) >= 500) { | |
var text = ""; | |
if (metrics.timing) { | |
text += "fps: " + Math.round(metrics.timing.fps) + space; | |
} | |
render.debugString = text; | |
render.debugTimestamp = engine.timing.timestamp; | |
} | |
if (render.debugString) { | |
c.font = "12px Arial"; | |
if (options.wireframes) { | |
c.fillStyle = 'rgba(255,255,255,0.5)'; | |
} else { | |
c.fillStyle = 'rgba(0,0,0,0.5)'; | |
} | |
var split = render.debugString.split('\n'); | |
for (var i = 0; i < split.length; i++) { | |
c.fillText(split[i], 50, 50 + i * 18); | |
} | |
} | |
}; | |
/** | |
* Description | |
* @private | |
* @method constraints | |
* @param {constraint[]} constraints | |
* @param {RenderingContext} context | |
*/ | |
Render.constraints = function(constraints, context) { | |
var c = context; | |
for (var i = 0; i < constraints.length; i++) { | |
var constraint = constraints[i]; | |
if (!constraint.render.visible || !constraint.pointA || !constraint.pointB) | |
continue; | |
var bodyA = constraint.bodyA, | |
bodyB = constraint.bodyB, | |
start, | |
end; | |
if (bodyA) { | |
start = Vector.add(bodyA.position, constraint.pointA); | |
} else { | |
start = constraint.pointA; | |
} | |
if (constraint.render.type === 'pin') { | |
c.beginPath(); | |
c.arc(start.x, start.y, 3, 0, 2 * Math.PI); | |
c.closePath(); | |
} else { | |
if (bodyB) { | |
end = Vector.add(bodyB.position, constraint.pointB); | |
} else { | |
end = constraint.pointB; | |
} | |
c.beginPath(); | |
c.moveTo(start.x, start.y); | |
if (constraint.render.type === 'spring') { | |
var delta = Vector.sub(end, start), | |
normal = Vector.perp(Vector.normalise(delta)), | |
coils = Math.ceil(Common.clamp(constraint.length / 5, 12, 20)), | |
offset; | |
for (var j = 1; j < coils; j += 1) { | |
offset = j % 2 === 0 ? 1 : -1; | |
c.lineTo( | |
start.x + delta.x * (j / coils) + normal.x * offset * 4, | |
start.y + delta.y * (j / coils) + normal.y * offset * 4 | |
); | |
} | |
} | |
c.lineTo(end.x, end.y); | |
} | |
if (constraint.render.lineWidth) { | |
c.lineWidth = constraint.render.lineWidth; | |
c.strokeStyle = constraint.render.strokeStyle; | |
c.stroke(); | |
} | |
if (constraint.render.anchors) { | |
c.fillStyle = constraint.render.strokeStyle; | |
c.beginPath(); | |
c.arc(start.x, start.y, 3, 0, 2 * Math.PI); | |
c.arc(end.x, end.y, 3, 0, 2 * Math.PI); | |
c.closePath(); | |
c.fill(); | |
} | |
} | |
}; | |
/** | |
* Description | |
* @private | |
* @method bodyShadows | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyShadows = function(render, bodies, context) { | |
var c = context, | |
engine = render.engine; | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (!body.render.visible) | |
continue; | |
if (body.circleRadius) { | |
c.beginPath(); | |
c.arc(body.position.x, body.position.y, body.circleRadius, 0, 2 * Math.PI); | |
c.closePath(); | |
} else { | |
c.beginPath(); | |
c.moveTo(body.vertices[0].x, body.vertices[0].y); | |
for (var j = 1; j < body.vertices.length; j++) { | |
c.lineTo(body.vertices[j].x, body.vertices[j].y); | |
} | |
c.closePath(); | |
} | |
var distanceX = body.position.x - render.options.width * 0.5, | |
distanceY = body.position.y - render.options.height * 0.2, | |
distance = Math.abs(distanceX) + Math.abs(distanceY); | |
c.shadowColor = 'rgba(0,0,0,0.15)'; | |
c.shadowOffsetX = 0.05 * distanceX; | |
c.shadowOffsetY = 0.05 * distanceY; | |
c.shadowBlur = 1 + 12 * Math.min(1, distance / 1000); | |
c.fill(); | |
c.shadowColor = null; | |
c.shadowOffsetX = null; | |
c.shadowOffsetY = null; | |
c.shadowBlur = null; | |
} | |
}; | |
/** | |
* Description | |
* @private | |
* @method bodies | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodies = function(render, bodies, context) { | |
var c = context, | |
engine = render.engine, | |
options = render.options, | |
showInternalEdges = options.showInternalEdges || !options.wireframes, | |
body, | |
part, | |
i, | |
k; | |
for (i = 0; i < bodies.length; i++) { | |
body = bodies[i]; | |
if (!body.render.visible) | |
continue; | |
// handle compound parts | |
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) { | |
part = body.parts[k]; | |
if (!part.render.visible) | |
continue; | |
if (options.showSleeping && body.isSleeping) { | |
c.globalAlpha = 0.5 * part.render.opacity; | |
} else if (part.render.opacity !== 1) { | |
c.globalAlpha = part.render.opacity; | |
} | |
if (part.render.sprite && part.render.sprite.texture && !options.wireframes) { | |
// part sprite | |
var sprite = part.render.sprite, | |
texture = _getTexture(render, sprite.texture); | |
c.translate(part.position.x, part.position.y); | |
c.rotate(part.angle); | |
c.drawImage( | |
texture, | |
texture.width * -sprite.xOffset * sprite.xScale, | |
texture.height * -sprite.yOffset * sprite.yScale, | |
texture.width * sprite.xScale, | |
texture.height * sprite.yScale | |
); | |
// revert translation, hopefully faster than save / restore | |
c.rotate(-part.angle); | |
c.translate(-part.position.x, -part.position.y); | |
} else { | |
// part polygon | |
if (part.circleRadius) { | |
c.beginPath(); | |
c.arc(part.position.x, part.position.y, part.circleRadius, 0, 2 * Math.PI); | |
} else { | |
c.beginPath(); | |
c.moveTo(part.vertices[0].x, part.vertices[0].y); | |
for (var j = 1; j < part.vertices.length; j++) { | |
if (!part.vertices[j - 1].isInternal || showInternalEdges) { | |
c.lineTo(part.vertices[j].x, part.vertices[j].y); | |
} else { | |
c.moveTo(part.vertices[j].x, part.vertices[j].y); | |
} | |
if (part.vertices[j].isInternal && !showInternalEdges) { | |
c.moveTo(part.vertices[(j + 1) % part.vertices.length].x, part.vertices[(j + 1) % part.vertices.length].y); | |
} | |
} | |
c.lineTo(part.vertices[0].x, part.vertices[0].y); | |
c.closePath(); | |
} | |
if (!options.wireframes) { | |
c.fillStyle = part.render.fillStyle; | |
if (part.render.lineWidth) { | |
c.lineWidth = part.render.lineWidth; | |
c.strokeStyle = part.render.strokeStyle; | |
c.stroke(); | |
} | |
c.fill(); | |
} else { | |
c.lineWidth = 1; | |
c.strokeStyle = '#bbb'; | |
c.stroke(); | |
} | |
} | |
c.globalAlpha = 1; | |
} | |
} | |
}; | |
/** | |
* Optimised method for drawing body wireframes in one pass | |
* @private | |
* @method bodyWireframes | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyWireframes = function(render, bodies, context) { | |
var c = context, | |
showInternalEdges = render.options.showInternalEdges, | |
body, | |
part, | |
i, | |
j, | |
k; | |
c.beginPath(); | |
// render all bodies | |
for (i = 0; i < bodies.length; i++) { | |
body = bodies[i]; | |
if (!body.render.visible) | |
continue; | |
// handle compound parts | |
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) { | |
part = body.parts[k]; | |
c.moveTo(part.vertices[0].x, part.vertices[0].y); | |
for (j = 1; j < part.vertices.length; j++) { | |
if (!part.vertices[j - 1].isInternal || showInternalEdges) { | |
c.lineTo(part.vertices[j].x, part.vertices[j].y); | |
} else { | |
c.moveTo(part.vertices[j].x, part.vertices[j].y); | |
} | |
if (part.vertices[j].isInternal && !showInternalEdges) { | |
c.moveTo(part.vertices[(j + 1) % part.vertices.length].x, part.vertices[(j + 1) % part.vertices.length].y); | |
} | |
} | |
c.lineTo(part.vertices[0].x, part.vertices[0].y); | |
} | |
} | |
c.lineWidth = 1; | |
c.strokeStyle = '#bbb'; | |
c.stroke(); | |
}; | |
/** | |
* Optimised method for drawing body convex hull wireframes in one pass | |
* @private | |
* @method bodyConvexHulls | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyConvexHulls = function(render, bodies, context) { | |
var c = context, | |
body, | |
part, | |
i, | |
j, | |
k; | |
c.beginPath(); | |
// render convex hulls | |
for (i = 0; i < bodies.length; i++) { | |
body = bodies[i]; | |
if (!body.render.visible || body.parts.length === 1) | |
continue; | |
c.moveTo(body.vertices[0].x, body.vertices[0].y); | |
for (j = 1; j < body.vertices.length; j++) { | |
c.lineTo(body.vertices[j].x, body.vertices[j].y); | |
} | |
c.lineTo(body.vertices[0].x, body.vertices[0].y); | |
} | |
c.lineWidth = 1; | |
c.strokeStyle = 'rgba(255,255,255,0.2)'; | |
c.stroke(); | |
}; | |
/** | |
* Renders body vertex numbers. | |
* @private | |
* @method vertexNumbers | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.vertexNumbers = function(render, bodies, context) { | |
var c = context, | |
i, | |
j, | |
k; | |
for (i = 0; i < bodies.length; i++) { | |
var parts = bodies[i].parts; | |
for (k = parts.length > 1 ? 1 : 0; k < parts.length; k++) { | |
var part = parts[k]; | |
for (j = 0; j < part.vertices.length; j++) { | |
c.fillStyle = 'rgba(255,255,255,0.2)'; | |
c.fillText(i + '_' + j, part.position.x + (part.vertices[j].x - part.position.x) * 0.8, part.position.y + (part.vertices[j].y - part.position.y) * 0.8); | |
} | |
} | |
} | |
}; | |
/** | |
* Renders mouse position. | |
* @private | |
* @method mousePosition | |
* @param {render} render | |
* @param {mouse} mouse | |
* @param {RenderingContext} context | |
*/ | |
Render.mousePosition = function(render, mouse, context) { | |
var c = context; | |
c.fillStyle = 'rgba(255,255,255,0.8)'; | |
c.fillText(mouse.position.x + ' ' + mouse.position.y, mouse.position.x + 5, mouse.position.y - 5); | |
}; | |
/** | |
* Draws body bounds | |
* @private | |
* @method bodyBounds | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyBounds = function(render, bodies, context) { | |
var c = context, | |
engine = render.engine, | |
options = render.options; | |
c.beginPath(); | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (body.render.visible) { | |
var parts = bodies[i].parts; | |
for (var j = parts.length > 1 ? 1 : 0; j < parts.length; j++) { | |
var part = parts[j]; | |
c.rect(part.bounds.min.x, part.bounds.min.y, part.bounds.max.x - part.bounds.min.x, part.bounds.max.y - part.bounds.min.y); | |
} | |
} | |
} | |
if (options.wireframes) { | |
c.strokeStyle = 'rgba(255,255,255,0.08)'; | |
} else { | |
c.strokeStyle = 'rgba(0,0,0,0.1)'; | |
} | |
c.lineWidth = 1; | |
c.stroke(); | |
}; | |
/** | |
* Draws body angle indicators and axes | |
* @private | |
* @method bodyAxes | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyAxes = function(render, bodies, context) { | |
var c = context, | |
engine = render.engine, | |
options = render.options, | |
part, | |
i, | |
j, | |
k; | |
c.beginPath(); | |
for (i = 0; i < bodies.length; i++) { | |
var body = bodies[i], | |
parts = body.parts; | |
if (!body.render.visible) | |
continue; | |
if (options.showAxes) { | |
// render all axes | |
for (j = parts.length > 1 ? 1 : 0; j < parts.length; j++) { | |
part = parts[j]; | |
for (k = 0; k < part.axes.length; k++) { | |
var axis = part.axes[k]; | |
c.moveTo(part.position.x, part.position.y); | |
c.lineTo(part.position.x + axis.x * 20, part.position.y + axis.y * 20); | |
} | |
} | |
} else { | |
for (j = parts.length > 1 ? 1 : 0; j < parts.length; j++) { | |
part = parts[j]; | |
for (k = 0; k < part.axes.length; k++) { | |
// render a single axis indicator | |
c.moveTo(part.position.x, part.position.y); | |
c.lineTo((part.vertices[0].x + part.vertices[part.vertices.length-1].x) / 2, | |
(part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2); | |
} | |
} | |
} | |
} | |
if (options.wireframes) { | |
c.strokeStyle = 'indianred'; | |
c.lineWidth = 1; | |
} else { | |
c.strokeStyle = 'rgba(255, 255, 255, 0.4)'; | |
c.globalCompositeOperation = 'overlay'; | |
c.lineWidth = 2; | |
} | |
c.stroke(); | |
c.globalCompositeOperation = 'source-over'; | |
}; | |
/** | |
* Draws body positions | |
* @private | |
* @method bodyPositions | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyPositions = function(render, bodies, context) { | |
var c = context, | |
engine = render.engine, | |
options = render.options, | |
body, | |
part, | |
i, | |
k; | |
c.beginPath(); | |
// render current positions | |
for (i = 0; i < bodies.length; i++) { | |
body = bodies[i]; | |
if (!body.render.visible) | |
continue; | |
// handle compound parts | |
for (k = 0; k < body.parts.length; k++) { | |
part = body.parts[k]; | |
c.arc(part.position.x, part.position.y, 3, 0, 2 * Math.PI, false); | |
c.closePath(); | |
} | |
} | |
if (options.wireframes) { | |
c.fillStyle = 'indianred'; | |
} else { | |
c.fillStyle = 'rgba(0,0,0,0.5)'; | |
} | |
c.fill(); | |
c.beginPath(); | |
// render previous positions | |
for (i = 0; i < bodies.length; i++) { | |
body = bodies[i]; | |
if (body.render.visible) { | |
c.arc(body.positionPrev.x, body.positionPrev.y, 2, 0, 2 * Math.PI, false); | |
c.closePath(); | |
} | |
} | |
c.fillStyle = 'rgba(255,165,0,0.8)'; | |
c.fill(); | |
}; | |
/** | |
* Draws body velocity | |
* @private | |
* @method bodyVelocity | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyVelocity = function(render, bodies, context) { | |
var c = context; | |
c.beginPath(); | |
for (var i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
if (!body.render.visible) | |
continue; | |
c.moveTo(body.position.x, body.position.y); | |
c.lineTo(body.position.x + (body.position.x - body.positionPrev.x) * 2, body.position.y + (body.position.y - body.positionPrev.y) * 2); | |
} | |
c.lineWidth = 3; | |
c.strokeStyle = 'cornflowerblue'; | |
c.stroke(); | |
}; | |
/** | |
* Draws body ids | |
* @private | |
* @method bodyIds | |
* @param {render} render | |
* @param {body[]} bodies | |
* @param {RenderingContext} context | |
*/ | |
Render.bodyIds = function(render, bodies, context) { | |
var c = context, | |
i, | |
j; | |
for (i = 0; i < bodies.length; i++) { | |
if (!bodies[i].render.visible) | |
continue; | |
var parts = bodies[i].parts; | |
for (j = parts.length > 1 ? 1 : 0; j < parts.length; j++) { | |
var part = parts[j]; | |
c.font = "12px Arial"; | |
c.fillStyle = 'rgba(255,255,255,0.5)'; | |
c.fillText(part.id, part.position.x + 10, part.position.y - 10); | |
} | |
} | |
}; | |
/** | |
* Description | |
* @private | |
* @method collisions | |
* @param {render} render | |
* @param {pair[]} pairs | |
* @param {RenderingContext} context | |
*/ | |
Render.collisions = function(render, pairs, context) { | |
var c = context, | |
options = render.options, | |
pair, | |
collision, | |
corrected, | |
bodyA, | |
bodyB, | |
i, | |
j; | |
c.beginPath(); | |
// render collision positions | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive) | |
continue; | |
collision = pair.collision; | |
for (j = 0; j < pair.activeContacts.length; j++) { | |
var contact = pair.activeContacts[j], | |
vertex = contact.vertex; | |
c.rect(vertex.x - 1.5, vertex.y - 1.5, 3.5, 3.5); | |
} | |
} | |
if (options.wireframes) { | |
c.fillStyle = 'rgba(255,255,255,0.7)'; | |
} else { | |
c.fillStyle = 'orange'; | |
} | |
c.fill(); | |
c.beginPath(); | |
// render collision normals | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive) | |
continue; | |
collision = pair.collision; | |
if (pair.activeContacts.length > 0) { | |
var normalPosX = pair.activeContacts[0].vertex.x, | |
normalPosY = pair.activeContacts[0].vertex.y; | |
if (pair.activeContacts.length === 2) { | |
normalPosX = (pair.activeContacts[0].vertex.x + pair.activeContacts[1].vertex.x) / 2; | |
normalPosY = (pair.activeContacts[0].vertex.y + pair.activeContacts[1].vertex.y) / 2; | |
} | |
if (collision.bodyB === collision.supports[0].body || collision.bodyA.isStatic === true) { | |
c.moveTo(normalPosX - collision.normal.x * 8, normalPosY - collision.normal.y * 8); | |
} else { | |
c.moveTo(normalPosX + collision.normal.x * 8, normalPosY + collision.normal.y * 8); | |
} | |
c.lineTo(normalPosX, normalPosY); | |
} | |
} | |
if (options.wireframes) { | |
c.strokeStyle = 'rgba(255,165,0,0.7)'; | |
} else { | |
c.strokeStyle = 'orange'; | |
} | |
c.lineWidth = 1; | |
c.stroke(); | |
}; | |
/** | |
* Description | |
* @private | |
* @method separations | |
* @param {render} render | |
* @param {pair[]} pairs | |
* @param {RenderingContext} context | |
*/ | |
Render.separations = function(render, pairs, context) { | |
var c = context, | |
options = render.options, | |
pair, | |
collision, | |
corrected, | |
bodyA, | |
bodyB, | |
i, | |
j; | |
c.beginPath(); | |
// render separations | |
for (i = 0; i < pairs.length; i++) { | |
pair = pairs[i]; | |
if (!pair.isActive) | |
continue; | |
collision = pair.collision; | |
bodyA = collision.bodyA; | |
bodyB = collision.bodyB; | |
var k = 1; | |
if (!bodyB.isStatic && !bodyA.isStatic) k = 0.5; | |
if (bodyB.isStatic) k = 0; | |
c.moveTo(bodyB.position.x, bodyB.position.y); | |
c.lineTo(bodyB.position.x - collision.penetration.x * k, bodyB.position.y - collision.penetration.y * k); | |
k = 1; | |
if (!bodyB.isStatic && !bodyA.isStatic) k = 0.5; | |
if (bodyA.isStatic) k = 0; | |
c.moveTo(bodyA.position.x, bodyA.position.y); | |
c.lineTo(bodyA.position.x + collision.penetration.x * k, bodyA.position.y + collision.penetration.y * k); | |
} | |
if (options.wireframes) { | |
c.strokeStyle = 'rgba(255,165,0,0.5)'; | |
} else { | |
c.strokeStyle = 'orange'; | |
} | |
c.stroke(); | |
}; | |
/** | |
* Description | |
* @private | |
* @method grid | |
* @param {render} render | |
* @param {grid} grid | |
* @param {RenderingContext} context | |
*/ | |
Render.grid = function(render, grid, context) { | |
var c = context, | |
options = render.options; | |
if (options.wireframes) { | |
c.strokeStyle = 'rgba(255,180,0,0.1)'; | |
} else { | |
c.strokeStyle = 'rgba(255,180,0,0.5)'; | |
} | |
c.beginPath(); | |
var bucketKeys = Common.keys(grid.buckets); | |
for (var i = 0; i < bucketKeys.length; i++) { | |
var bucketId = bucketKeys[i]; | |
if (grid.buckets[bucketId].length < 2) | |
continue; | |
var region = bucketId.split(/C|R/); | |
c.rect(0.5 + parseInt(region[1], 10) * grid.bucketWidth, | |
0.5 + parseInt(region[2], 10) * grid.bucketHeight, | |
grid.bucketWidth, | |
grid.bucketHeight); | |
} | |
c.lineWidth = 1; | |
c.stroke(); | |
}; | |
/** | |
* Description | |
* @private | |
* @method inspector | |
* @param {inspector} inspector | |
* @param {RenderingContext} context | |
*/ | |
Render.inspector = function(inspector, context) { | |
var engine = inspector.engine, | |
selected = inspector.selected, | |
render = inspector.render, | |
options = render.options, | |
bounds; | |
if (options.hasBounds) { | |
var boundsWidth = render.bounds.max.x - render.bounds.min.x, | |
boundsHeight = render.bounds.max.y - render.bounds.min.y, | |
boundsScaleX = boundsWidth / render.options.width, | |
boundsScaleY = boundsHeight / render.options.height; | |
context.scale(1 / boundsScaleX, 1 / boundsScaleY); | |
context.translate(-render.bounds.min.x, -render.bounds.min.y); | |
} | |
for (var i = 0; i < selected.length; i++) { | |
var item = selected[i].data; | |
context.translate(0.5, 0.5); | |
context.lineWidth = 1; | |
context.strokeStyle = 'rgba(255,165,0,0.9)'; | |
context.setLineDash([1,2]); | |
switch (item.type) { | |
case 'body': | |
// render body selections | |
bounds = item.bounds; | |
context.beginPath(); | |
context.rect(Math.floor(bounds.min.x - 3), Math.floor(bounds.min.y - 3), | |
Math.floor(bounds.max.x - bounds.min.x + 6), Math.floor(bounds.max.y - bounds.min.y + 6)); | |
context.closePath(); | |
context.stroke(); | |
break; | |
case 'constraint': | |
// render constraint selections | |
var point = item.pointA; | |
if (item.bodyA) | |
point = item.pointB; | |
context.beginPath(); | |
context.arc(point.x, point.y, 10, 0, 2 * Math.PI); | |
context.closePath(); | |
context.stroke(); | |
break; | |
} | |
context.setLineDash([]); | |
context.translate(-0.5, -0.5); | |
} | |
// render selection region | |
if (inspector.selectStart !== null) { | |
context.translate(0.5, 0.5); | |
context.lineWidth = 1; | |
context.strokeStyle = 'rgba(255,165,0,0.6)'; | |
context.fillStyle = 'rgba(255,165,0,0.1)'; | |
bounds = inspector.selectBounds; | |
context.beginPath(); | |
context.rect(Math.floor(bounds.min.x), Math.floor(bounds.min.y), | |
Math.floor(bounds.max.x - bounds.min.x), Math.floor(bounds.max.y - bounds.min.y)); | |
context.closePath(); | |
context.stroke(); | |
context.fill(); | |
context.translate(-0.5, -0.5); | |
} | |
if (options.hasBounds) | |
context.setTransform(1, 0, 0, 1, 0, 0); | |
}; | |
/** | |
* Description | |
* @method _createCanvas | |
* @private | |
* @param {} width | |
* @param {} height | |
* @return canvas | |
*/ | |
var _createCanvas = function(width, height) { | |
var canvas = document.createElement('canvas'); | |
canvas.width = width; | |
canvas.height = height; | |
canvas.oncontextmenu = function() { return false; }; | |
canvas.onselectstart = function() { return false; }; | |
return canvas; | |
}; | |
/** | |
* Gets the pixel ratio of the canvas. | |
* @method _getPixelRatio | |
* @private | |
* @param {HTMLElement} canvas | |
* @return {Number} pixel ratio | |
*/ | |
var _getPixelRatio = function(canvas) { | |
var context = canvas.getContext('2d'), | |
devicePixelRatio = window.devicePixelRatio || 1, | |
backingStorePixelRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio | |
|| context.msBackingStorePixelRatio || context.oBackingStorePixelRatio | |
|| context.backingStorePixelRatio || 1; | |
return devicePixelRatio / backingStorePixelRatio; | |
}; | |
/** | |
* Gets the requested texture (an Image) via its path | |
* @method _getTexture | |
* @private | |
* @param {render} render | |
* @param {string} imagePath | |
* @return {Image} texture | |
*/ | |
var _getTexture = function(render, imagePath) { | |
var image = render.textures[imagePath]; | |
if (image) | |
return image; | |
image = render.textures[imagePath] = new Image(); | |
image.src = imagePath; | |
return image; | |
}; | |
/** | |
* Applies the background to the canvas using CSS. | |
* @method applyBackground | |
* @private | |
* @param {render} render | |
* @param {string} background | |
*/ | |
var _applyBackground = function(render, background) { | |
var cssBackground = background; | |
if (/(jpg|gif|png)$/.test(background)) | |
cssBackground = 'url(' + background + ')'; | |
render.canvas.style.background = cssBackground; | |
render.canvas.style.backgroundSize = "contain"; | |
render.currentBackground = background; | |
}; | |
/* | |
* | |
* Events Documentation | |
* | |
*/ | |
/** | |
* Fired before rendering | |
* | |
* @event beforeRender | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/** | |
* Fired after rendering | |
* | |
* @event afterRender | |
* @param {} event An event object | |
* @param {number} event.timestamp The engine.timing.timestamp of the event | |
* @param {} event.source The source object of the event | |
* @param {} event.name The name of the event | |
*/ | |
/* | |
* | |
* Properties Documentation | |
* | |
*/ | |
/** | |
* A back-reference to the `Matter.Render` module. | |
* | |
* @property controller | |
* @type render | |
*/ | |
/** | |
* A reference to the `Matter.Engine` instance to be used. | |
* | |
* @property engine | |
* @type engine | |
*/ | |
/** | |
* A reference to the element where the canvas is to be inserted (if `render.canvas` has not been specified) | |
* | |
* @property element | |
* @type HTMLElement | |
* @default null | |
*/ | |
/** | |
* The canvas element to render to. If not specified, one will be created if `render.element` has been specified. | |
* | |
* @property canvas | |
* @type HTMLCanvasElement | |
* @default null | |
*/ | |
/** | |
* The configuration options of the renderer. | |
* | |
* @property options | |
* @type {} | |
*/ | |
/** | |
* The target width in pixels of the `render.canvas` to be created. | |
* | |
* @property options.width | |
* @type number | |
* @default 800 | |
*/ | |
/** | |
* The target height in pixels of the `render.canvas` to be created. | |
* | |
* @property options.height | |
* @type number | |
* @default 600 | |
*/ | |
/** | |
* A flag that specifies if `render.bounds` should be used when rendering. | |
* | |
* @property options.hasBounds | |
* @type boolean | |
* @default false | |
*/ | |
/** | |
* A `Bounds` object that specifies the drawing view region. | |
* Rendering will be automatically transformed and scaled to fit within the canvas size (`render.options.width` and `render.options.height`). | |
* This allows for creating views that can pan or zoom around the scene. | |
* You must also set `render.options.hasBounds` to `true` to enable bounded rendering. | |
* | |
* @property bounds | |
* @type bounds | |
*/ | |
/** | |
* The 2d rendering context from the `render.canvas` element. | |
* | |
* @property context | |
* @type CanvasRenderingContext2D | |
*/ | |
/** | |
* The sprite texture cache. | |
* | |
* @property textures | |
* @type {} | |
*/ | |
})(); | |
},{"../body/Composite":2,"../collision/Grid":6,"../core/Common":14,"../core/Events":16,"../core/Mouse":19,"../geometry/Bounds":26,"../geometry/Vector":28}],32:[function(_dereq_,module,exports){ | |
/** | |
* The `Matter.RenderPixi` module is an example renderer using pixi.js. | |
* See also `Matter.Render` for a canvas based renderer. | |
* | |
* @class RenderPixi | |
* @deprecated the Matter.RenderPixi module will soon be removed from the Matter.js core. | |
* It will likely be moved to its own repository (but maintenance will be limited). | |
*/ | |
var RenderPixi = {}; | |
module.exports = RenderPixi; | |
var Bounds = _dereq_('../geometry/Bounds'); | |
var Composite = _dereq_('../body/Composite'); | |
var Common = _dereq_('../core/Common'); | |
var Events = _dereq_('../core/Events'); | |
var Vector = _dereq_('../geometry/Vector'); | |
(function() { | |
var _requestAnimationFrame, | |
_cancelAnimationFrame; | |
if (typeof window !== 'undefined') { | |
_requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame | |
|| window.mozRequestAnimationFrame || window.msRequestAnimationFrame | |
|| function(callback){ window.setTimeout(function() { callback(Common.now()); }, 1000 / 60); }; | |
_cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame | |
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; | |
} | |
/** | |
* Creates a new Pixi.js WebGL renderer | |
* @method create | |
* @param {object} options | |
* @return {RenderPixi} A new renderer | |
* @deprecated | |
*/ | |
RenderPixi.create = function(options) { | |
Common.warn('RenderPixi.create: Matter.RenderPixi is deprecated (see docs)'); | |
var defaults = { | |
controller: RenderPixi, | |
engine: null, | |
element: null, | |
frameRequestId: null, | |
canvas: null, | |
renderer: null, | |
container: null, | |
spriteContainer: null, | |
pixiOptions: null, | |
options: { | |
width: 800, | |
height: 600, | |
background: '#fafafa', | |
wireframeBackground: '#222', | |
hasBounds: false, | |
enabled: true, | |
wireframes: true, | |
showSleeping: true, | |
showDebug: false, | |
showBroadphase: false, | |
showBounds: false, | |
showVelocity: false, | |
showCollisions: false, | |
showAxes: false, | |
showPositions: false, | |
showAngleIndicator: false, | |
showIds: false, | |
showShadows: false | |
} | |
}; | |
var render = Common.extend(defaults, options), | |
transparent = !render.options.wireframes && render.options.background === 'transparent'; | |
// init pixi | |
render.pixiOptions = render.pixiOptions || { | |
view: render.canvas, | |
transparent: transparent, | |
antialias: true, | |
backgroundColor: options.background | |
}; | |
render.mouse = options.mouse; | |
render.engine = options.engine; | |
render.renderer = render.renderer || new PIXI.WebGLRenderer(render.options.width, render.options.height, render.pixiOptions); | |
render.container = render.container || new PIXI.Container(); | |
render.spriteContainer = render.spriteContainer || new PIXI.Container(); | |
render.canvas = render.canvas || render.renderer.view; | |
render.bounds = render.bounds || { | |
min: { | |
x: 0, | |
y: 0 | |
}, | |
max: { | |
x: render.options.width, | |
y: render.options.height | |
} | |
}; | |
// event listeners | |
Events.on(render.engine, 'beforeUpdate', function() { | |
RenderPixi.clear(render); | |
}); | |
// caches | |
render.textures = {}; | |
render.sprites = {}; | |
render.primitives = {}; | |
// use a sprite batch for performance | |
render.container.addChild(render.spriteContainer); | |
// insert canvas | |
if (Common.isElement(render.element)) { | |
render.element.appendChild(render.canvas); | |
} else { | |
Common.warn('No "render.element" passed, "render.canvas" was not inserted into document.'); | |
} | |
// prevent menus on canvas | |
render.canvas.oncontextmenu = function() { return false; }; | |
render.canvas.onselectstart = function() { return false; }; | |
return render; | |
}; | |
/** | |
* Continuously updates the render canvas on the `requestAnimationFrame` event. | |
* @method run | |
* @param {render} render | |
* @deprecated | |
*/ | |
RenderPixi.run = function(render) { | |
(function loop(time){ | |
render.frameRequestId = _requestAnimationFrame(loop); | |
RenderPixi.world(render); | |
})(); | |
}; | |
/** | |
* Ends execution of `Render.run` on the given `render`, by canceling the animation frame request event loop. | |
* @method stop | |
* @param {render} render | |
* @deprecated | |
*/ | |
RenderPixi.stop = function(render) { | |
_cancelAnimationFrame(render.frameRequestId); | |
}; | |
/** | |
* Clears the scene graph | |
* @method clear | |
* @param {RenderPixi} render | |
* @deprecated | |
*/ | |
RenderPixi.clear = function(render) { | |
var container = render.container, | |
spriteContainer = render.spriteContainer; | |
// clear stage container | |
while (container.children[0]) { | |
container.removeChild(container.children[0]); | |
} | |
// clear sprite batch | |
while (spriteContainer.children[0]) { | |
spriteContainer.removeChild(spriteContainer.children[0]); | |
} | |
var bgSprite = render.sprites['bg-0']; | |
// clear caches | |
render.textures = {}; | |
render.sprites = {}; | |
render.primitives = {}; | |
// set background sprite | |
render.sprites['bg-0'] = bgSprite; | |
if (bgSprite) | |
container.addChildAt(bgSprite, 0); | |
// add sprite batch back into container | |
render.container.addChild(render.spriteContainer); | |
// reset background state | |
render.currentBackground = null; | |
// reset bounds transforms | |
container.scale.set(1, 1); | |
container.position.set(0, 0); | |
}; | |
/** | |
* Sets the background of the canvas | |
* @method setBackground | |
* @param {RenderPixi} render | |
* @param {string} background | |
* @deprecated | |
*/ | |
RenderPixi.setBackground = function(render, background) { | |
if (render.currentBackground !== background) { | |
var isColor = background.indexOf && background.indexOf('#') !== -1, | |
bgSprite = render.sprites['bg-0']; | |
if (isColor) { | |
// if solid background color | |
var color = Common.colorToNumber(background); | |
render.renderer.backgroundColor = color; | |
// remove background sprite if existing | |
if (bgSprite) | |
render.container.removeChild(bgSprite); | |
} else { | |
// initialise background sprite if needed | |
if (!bgSprite) { | |
var texture = _getTexture(render, background); | |
bgSprite = render.sprites['bg-0'] = new PIXI.Sprite(texture); | |
bgSprite.position.x = 0; | |
bgSprite.position.y = 0; | |
render.container.addChildAt(bgSprite, 0); | |
} | |
} | |
render.currentBackground = background; | |
} | |
}; | |
/** | |
* Description | |
* @method world | |
* @param {engine} engine | |
* @deprecated | |
*/ | |
RenderPixi.world = function(render) { | |
var engine = render.engine, | |
world = engine.world, | |
renderer = render.renderer, | |
container = render.container, | |
options = render.options, | |
bodies = Composite.allBodies(world), | |
allConstraints = Composite.allConstraints(world), | |
constraints = [], | |
i; | |
if (options.wireframes) { | |
RenderPixi.setBackground(render, options.wireframeBackground); | |
} else { | |
RenderPixi.setBackground(render, options.background); | |
} | |
// handle bounds | |
var boundsWidth = render.bounds.max.x - render.bounds.min.x, | |
boundsHeight = render.bounds.max.y - render.bounds.min.y, | |
boundsScaleX = boundsWidth / render.options.width, | |
boundsScaleY = boundsHeight / render.options.height; | |
if (options.hasBounds) { | |
// Hide bodies that are not in view | |
for (i = 0; i < bodies.length; i++) { | |
var body = bodies[i]; | |
body.render.sprite.visible = Bounds.overlaps(body.bounds, render.bounds); | |
} | |
// filter out constraints that are not in view | |
for (i = 0; i < allConstraints.length; i++) { | |
var constraint = allConstraints[i], | |
bodyA = constraint.bodyA, | |
bodyB = constraint.bodyB, | |
pointAWorld = constraint.pointA, | |
pointBWorld = constraint.pointB; | |
if (bodyA) pointAWorld = Vector.add(bodyA.position, constraint.pointA); | |
if (bodyB) pointBWorld = Vector.add(bodyB.position, constraint.pointB); | |
if (!pointAWorld || !pointBWorld) | |
continue; | |
if (Bounds.contains(render.bounds, pointAWorld) || Bounds.contains(render.bounds, pointBWorld)) | |
constraints.push(constraint); | |
} | |
// transform the view | |
container.scale.set(1 / boundsScaleX, 1 / boundsScaleY); | |
container.position.set(-render.bounds.min.x * (1 / boundsScaleX), -render.bounds.min.y * (1 / boundsScaleY)); | |
} else { | |
constraints = allConstraints; | |
} | |
for (i = 0; i < bodies.length; i++) | |
RenderPixi.body(render, bodies[i]); | |
for (i = 0; i < constraints.length; i++) | |
RenderPixi.constraint(render, constraints[i]); | |
renderer.render(container); | |
}; | |
/** | |
* Description | |
* @method constraint | |
* @param {engine} engine | |
* @param {constraint} constraint | |
* @deprecated | |
*/ | |
RenderPixi.constraint = function(render, constraint) { | |
var engine = render.engine, | |
bodyA = constraint.bodyA, | |
bodyB = constraint.bodyB, | |
pointA = constraint.pointA, | |
pointB = constraint.pointB, | |
container = render.container, | |
constraintRender = constraint.render, | |
primitiveId = 'c-' + constraint.id, | |
primitive = render.primitives[primitiveId]; | |
// initialise constraint primitive if not existing | |
if (!primitive) | |
primitive = render.primitives[primitiveId] = new PIXI.Graphics(); | |
// don't render if constraint does not have two end points | |
if (!constraintRender.visible || !constraint.pointA || !constraint.pointB) { | |
primitive.clear(); | |
return; | |
} | |
// add to scene graph if not already there | |
if (Common.indexOf(container.children, primitive) === -1) | |
container.addChild(primitive); | |
// render the constraint on every update, since they can change dynamically | |
primitive.clear(); | |
primitive.beginFill(0, 0); | |
primitive.lineStyle(constraintRender.lineWidth, Common.colorToNumber(constraintRender.strokeStyle), 1); | |
if (bodyA) { | |
primitive.moveTo(bodyA.position.x + pointA.x, bodyA.position.y + pointA.y); | |
} else { | |
primitive.moveTo(pointA.x, pointA.y); | |
} | |
if (bodyB) { | |
primitive.lineTo(bodyB.position.x + pointB.x, bodyB.position.y + pointB.y); | |
} else { | |
primitive.lineTo(pointB.x, pointB.y); | |
} | |
primitive.endFill(); | |
}; | |
/** | |
* Description | |
* @method body | |
* @param {engine} engine | |
* @param {body} body | |
* @deprecated | |
*/ | |
RenderPixi.body = function(render, body) { | |
var engine = render.engine, | |
bodyRender = body.render; | |
if (!bodyRender.visible) | |
return; | |
if (bodyRender.sprite && bodyRender.sprite.texture) { | |
var spriteId = 'b-' + body.id, | |
sprite = render.sprites[spriteId], | |
spriteContainer = render.spriteContainer; | |
// initialise body sprite if not existing | |
if (!sprite) | |
sprite = render.sprites[spriteId] = _createBodySprite(render, body); | |
// add to scene graph if not already there | |
if (Common.indexOf(spriteContainer.children, sprite) === -1) | |
spriteContainer.addChild(sprite); | |
// update body sprite | |
sprite.position.x = body.position.x; | |
sprite.position.y = body.position.y; | |
sprite.rotation = body.angle; | |
sprite.scale.x = bodyRender.sprite.xScale || 1; | |
sprite.scale.y = bodyRender.sprite.yScale || 1; | |
} else { | |
var primitiveId = 'b-' + body.id, | |
primitive = render.primitives[primitiveId], | |
container = render.container; | |
// initialise body primitive if not existing | |
if (!primitive) { | |
primitive = render.primitives[primitiveId] = _createBodyPrimitive(render, body); | |
primitive.initialAngle = body.angle; | |
} | |
// add to scene graph if not already there | |
if (Common.indexOf(container.children, primitive) === -1) | |
container.addChild(primitive); | |
// update body primitive | |
primitive.position.x = body.position.x; | |
primitive.position.y = body.position.y; | |
primitive.rotation = body.angle - primitive.initialAngle; | |
} | |
}; | |
/** | |
* Creates a body sprite | |
* @method _createBodySprite | |
* @private | |
* @param {RenderPixi} render | |
* @param {body} body | |
* @return {PIXI.Sprite} sprite | |
* @deprecated | |
*/ | |
var _createBodySprite = function(render, body) { | |
var bodyRender = body.render, | |
texturePath = bodyRender.sprite.texture, | |
texture = _getTexture(render, texturePath), | |
sprite = new PIXI.Sprite(texture); | |
sprite.anchor.x = body.render.sprite.xOffset; | |
sprite.anchor.y = body.render.sprite.yOffset; | |
return sprite; | |
}; | |
/** | |
* Creates a body primitive | |
* @method _createBodyPrimitive | |
* @private | |
* @param {RenderPixi} render | |
* @param {body} body | |
* @return {PIXI.Graphics} graphics | |
* @deprecated | |
*/ | |
var _createBodyPrimitive = function(render, body) { | |
var bodyRender = body.render, | |
options = render.options, | |
primitive = new PIXI.Graphics(), | |
fillStyle = Common.colorToNumber(bodyRender.fillStyle), | |
strokeStyle = Common.colorToNumber(bodyRender.strokeStyle), | |
strokeStyleIndicator = Common.colorToNumber(bodyRender.strokeStyle), | |
strokeStyleWireframe = Common.colorToNumber('#bbb'), | |
strokeStyleWireframeIndicator = Common.colorToNumber('#CD5C5C'), | |
part; | |
primitive.clear(); | |
// handle compound parts | |
for (var k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) { | |
part = body.parts[k]; | |
if (!options.wireframes) { | |
primitive.beginFill(fillStyle, 1); | |
primitive.lineStyle(bodyRender.lineWidth, strokeStyle, 1); | |
} else { | |
primitive.beginFill(0, 0); | |
primitive.lineStyle(1, strokeStyleWireframe, 1); | |
} | |
primitive.moveTo(part.vertices[0].x - body.position.x, part.vertices[0].y - body.position.y); | |
for (var j = 1; j < part.vertices.length; j++) { | |
primitive.lineTo(part.vertices[j].x - body.position.x, part.vertices[j].y - body.position.y); | |
} | |
primitive.lineTo(part.vertices[0].x - body.position.x, part.vertices[0].y - body.position.y); | |
primitive.endFill(); | |
// angle indicator | |
if (options.showAngleIndicator || options.showAxes) { | |
primitive.beginFill(0, 0); | |
if (options.wireframes) { | |
primitive.lineStyle(1, strokeStyleWireframeIndicator, 1); | |
} else { | |
primitive.lineStyle(1, strokeStyleIndicator); | |
} | |
primitive.moveTo(part.position.x - body.position.x, part.position.y - body.position.y); | |
primitive.lineTo(((part.vertices[0].x + part.vertices[part.vertices.length-1].x) / 2 - body.position.x), | |
((part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2 - body.position.y)); | |
primitive.endFill(); | |
} | |
} | |
return primitive; | |
}; | |
/** | |
* Gets the requested texture (a PIXI.Texture) via its path | |
* @method _getTexture | |
* @private | |
* @param {RenderPixi} render | |
* @param {string} imagePath | |
* @return {PIXI.Texture} texture | |
* @deprecated | |
*/ | |
var _getTexture = function(render, imagePath) { | |
var texture = render.textures[imagePath]; | |
if (!texture) | |
texture = render.textures[imagePath] = PIXI.Texture.fromImage(imagePath); | |
return texture; | |
}; | |
})(); | |
},{"../body/Composite":2,"../core/Common":14,"../core/Events":16,"../geometry/Bounds":26,"../geometry/Vector":28}]},{},[30])(30) | |
}); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}]},{},[]) | |
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImJ1aWxkL21hdHRlci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qKlxuKiBtYXR0ZXItanMgMC4xMy4wIGJ5IEBsaWFicnUgMjAxNy0wNy0wNlxuKiBodHRwOi8vYnJtLmlvL21hdHRlci1qcy9cbiogTGljZW5zZSBNSVRcbiovXG5cbi8qKlxuICogVGhlIE1JVCBMaWNlbnNlIChNSVQpXG4gKiBcbiAqIENvcHlyaWdodCAoYykgTGlhbSBCcnVtbWl0dCBhbmQgY29udHJpYnV0b3JzLlxuICogXG4gKiBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4gKiBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0byBkZWFsXG4gKiBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzXG4gKiB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsXG4gKiBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbiAqIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4gKiBcbiAqIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluXG4gKiBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cbiAqIFxuICogVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuICogSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksXG4gKiBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbiAqIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbiAqIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sXG4gKiBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4gKiBUSEUgU09GVFdBUkUuXG4gKi9cblxuKGZ1bmN0aW9uKGYpe2lmKHR5cGVvZiBleHBvcnRzPT09XCJvYmplY3RcIiYmdHlwZW9mIG1vZHVsZSE9PVwidW5kZWZpbmVkXCIpe21vZHVsZS5leHBvcnRzPWYoKX1lbHNlIGlmKHR5cGVvZiBkZWZpbmU9PT1cImZ1bmN0aW9uXCImJmRlZmluZS5hbWQpe2RlZmluZShbXSxmKX1lbHNle3ZhciBnO2lmKHR5cGVvZiB3aW5kb3chPT1cInVuZGVmaW5lZFwiKXtnPXdpbmRvd31lbHNlIGlmKHR5cGVvZiBnbG9iYWwhPT1cInVuZGVmaW5lZFwiKXtnPWdsb2JhbH1lbHNlIGlmKHR5cGVvZiBzZWxmIT09XCJ1bmRlZmluZWRcIil7Zz1zZWxmfWVsc2V7Zz10aGlzfWcuTWF0dGVyID0gZigpfX0pKGZ1bmN0aW9uKCl7dmFyIGRlZmluZSxtb2R1bGUsZXhwb3J0cztyZXR1cm4gKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkoezE6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLkJvZHlgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIGZvciBjcmVhdGluZyBhbmQgbWFuaXB1bGF0aW5nIGJvZHkgbW9kZWxzLlxuKiBBIGBNYXR0ZXIuQm9keWAgaXMgYSByaWdpZCBib2R5IHRoYXQgY2FuIGJlIHNpbXVsYXRlZCBieSBhIGBNYXR0ZXIuRW5naW5lYC5cbiogRmFjdG9yaWVzIGZvciBjb21tb25seSB1c2VkIGJvZHkgY29uZmlndXJhdGlvbnMgKHN1Y2ggYXMgcmVjdGFuZ2xlcywgY2lyY2xlcyBhbmQgb3RoZXIgcG9seWdvbnMpIGNhbiBiZSBmb3VuZCBpbiB0aGUgbW9kdWxlIGBNYXR0ZXIuQm9kaWVzYC5cbipcbiogU2VlIHRoZSBpbmNsdWRlZCB1c2FnZSBbZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9saWFicnUvbWF0dGVyLWpzL3RyZWUvbWFzdGVyL2V4YW1wbGVzKS5cblxuKiBAY2xhc3MgQm9keVxuKi9cblxudmFyIEJvZHkgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBCb2R5O1xuXG52YXIgVmVydGljZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZXJ0aWNlcycpO1xudmFyIFZlY3RvciA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L1ZlY3RvcicpO1xudmFyIFNsZWVwaW5nID0gX2RlcmVxXygnLi4vY29yZS9TbGVlcGluZycpO1xudmFyIFJlbmRlciA9IF9kZXJlcV8oJy4uL3JlbmRlci9SZW5kZXInKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xudmFyIEJvdW5kcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L0JvdW5kcycpO1xudmFyIEF4ZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9BeGVzJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIEJvZHkuX2luZXJ0aWFTY2FsZSA9IDQ7XG4gICAgQm9keS5fbmV4dENvbGxpZGluZ0dyb3VwSWQgPSAxO1xuICAgIEJvZHkuX25leHROb25Db2xsaWRpbmdHcm91cElkID0gLTE7XG4gICAgQm9keS5fbmV4dENhdGVnb3J5ID0gMHgwMDAxO1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyByaWdpZCBib2R5IG1vZGVsLiBUaGUgb3B0aW9ucyBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXQgc3BlY2lmaWVzIGFueSBwcm9wZXJ0aWVzIHlvdSB3aXNoIHRvIG92ZXJyaWRlIHRoZSBkZWZhdWx0cy5cbiAgICAgKiBBbGwgcHJvcGVydGllcyBoYXZlIGRlZmF1bHQgdmFsdWVzLCBhbmQgbWFueSBhcmUgcHJlLWNhbGN1bGF0ZWQgYXV0b21hdGljYWxseSBiYXNlZCBvbiBvdGhlciBwcm9wZXJ0aWVzLlxuICAgICAqIFZlcnRpY2VzIG11c3QgYmUgc3BlY2lmaWVkIGluIGNsb2Nrd2lzZSBvcmRlci5cbiAgICAgKiBTZWUgdGhlIHByb3BlcnRpZXMgc2VjdGlvbiBiZWxvdyBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gd2hhdCB5b3UgY2FuIHBhc3MgdmlhIHRoZSBgb3B0aW9uc2Agb2JqZWN0LlxuICAgICAqIEBtZXRob2QgY3JlYXRlXG4gICAgICogQHBhcmFtIHt9IG9wdGlvbnNcbiAgICAgKiBAcmV0dXJuIHtib2R5fSBib2R5XG4gICAgICovXG4gICAgQm9keS5jcmVhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgICAgIGlkOiBDb21tb24ubmV4dElkKCksXG4gICAgICAgICAgICB0eXBlOiAnYm9keScsXG4gICAgICAgICAgICBsYWJlbDogJ0JvZHknLFxuICAgICAgICAgICAgcGFydHM6IFtdLFxuICAgICAgICAgICAgcGx1Z2luOiB7fSxcbiAgICAgICAgICAgIGFuZ2xlOiAwLFxuICAgICAgICAgICAgdmVydGljZXM6IFZlcnRpY2VzLmZyb21QYXRoKCdMIDAgMCBMIDQwIDAgTCA0MCA0MCBMIDAgNDAnKSxcbiAgICAgICAgICAgIHBvc2l0aW9uOiB7IHg6IDAsIHk6IDAgfSxcbiAgICAgICAgICAgIGZvcmNlOiB7IHg6IDAsIHk6IDAgfSxcbiAgICAgICAgICAgIHRvcnF1ZTogMCxcbiAgICAgICAgICAgIHBvc2l0aW9uSW1wdWxzZTogeyB4OiAwLCB5OiAwIH0sXG4gICAgICAgICAgICBjb25zdHJhaW50SW1wdWxzZTogeyB4OiAwLCB5OiAwLCBhbmdsZTogMCB9LFxuICAgICAgICAgICAgdG90YWxDb250YWN0czogMCxcbiAgICAgICAgICAgIHNwZWVkOiAwLFxuICAgICAgICAgICAgYW5ndWxhclNwZWVkOiAwLFxuICAgICAgICAgICAgdmVsb2NpdHk6IHsgeDogMCwgeTogMCB9LFxuICAgICAgICAgICAgYW5ndWxhclZlbG9jaXR5OiAwLFxuICAgICAgICAgICAgaXNTZW5zb3I6IGZhbHNlLFxuICAgICAgICAgICAgaXNTdGF0aWM6IGZhbHNlLFxuICAgICAgICAgICAgaXNTbGVlcGluZzogZmFsc2UsXG4gICAgICAgICAgICBtb3Rpb246IDAsXG4gICAgICAgICAgICBzbGVlcFRocmVzaG9sZDogNjAsXG4gICAgICAgICAgICBkZW5zaXR5OiAwLjAwMSxcbiAgICAgICAgICAgIHJlc3RpdHV0aW9uOiAwLFxuICAgICAgICAgICAgZnJpY3Rpb246IDAuMSxcbiAgICAgICAgICAgIGZyaWN0aW9uU3RhdGljOiAwLjUsXG4gICAgICAgICAgICBmcmljdGlvbkFpcjogMC4wMSxcbiAgICAgICAgICAgIGNvbGxpc2lvbkZpbHRlcjoge1xuICAgICAgICAgICAgICAgIGNhdGVnb3J5OiAweDAwMDEsXG4gICAgICAgICAgICAgICAgbWFzazogMHhGRkZGRkZGRixcbiAgICAgICAgICAgICAgICBncm91cDogMFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHNsb3A6IDAuMDUsXG4gICAgICAgICAgICB0aW1lU2NhbGU6IDEsXG4gICAgICAgICAgICByZW5kZXI6IHtcbiAgICAgICAgICAgICAgICB2aXNpYmxlOiB0cnVlLFxuICAgICAgICAgICAgICAgIG9wYWNpdHk6IDEsXG4gICAgICAgICAgICAgICAgc3ByaXRlOiB7XG4gICAgICAgICAgICAgICAgICAgIHhTY2FsZTogMSxcbiAgICAgICAgICAgICAgICAgICAgeVNjYWxlOiAxLFxuICAgICAgICAgICAgICAgICAgICB4T2Zmc2V0OiAwLFxuICAgICAgICAgICAgICAgICAgICB5T2Zmc2V0OiAwXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBsaW5lV2lkdGg6IDBcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICB2YXIgYm9keSA9IENvbW1vbi5leHRlbmQoZGVmYXVsdHMsIG9wdGlvbnMpO1xuXG4gICAgICAgIF9pbml0UHJvcGVydGllcyhib2R5LCBvcHRpb25zKTtcblxuICAgICAgICByZXR1cm4gYm9keTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbmV4dCB1bmlxdWUgZ3JvdXAgaW5kZXggZm9yIHdoaWNoIGJvZGllcyB3aWxsIGNvbGxpZGUuXG4gICAgICogSWYgYGlzTm9uQ29sbGlkaW5nYCBpcyBgdHJ1ZWAsIHJldHVybnMgdGhlIG5leHQgdW5pcXVlIGdyb3VwIGluZGV4IGZvciB3aGljaCBib2RpZXMgd2lsbCBfbm90XyBjb2xsaWRlLlxuICAgICAqIFNlZSBgYm9keS5jb2xsaXNpb25GaWx0ZXJgIGZvciBtb3JlIGluZm9ybWF0aW9uLlxuICAgICAqIEBtZXRob2QgbmV4dEdyb3VwXG4gICAgICogQHBhcmFtIHtib29sfSBbaXNOb25Db2xsaWRpbmc9ZmFsc2VdXG4gICAgICogQHJldHVybiB7TnVtYmVyfSBVbmlxdWUgZ3JvdXAgaW5kZXhcbiAgICAgKi9cbiAgICBCb2R5Lm5leHRHcm91cCA9IGZ1bmN0aW9uKGlzTm9uQ29sbGlkaW5nKSB7XG4gICAgICAgIGlmIChpc05vbkNvbGxpZGluZylcbiAgICAgICAgICAgIHJldHVybiBCb2R5Ll9uZXh0Tm9uQ29sbGlkaW5nR3JvdXBJZC0tO1xuXG4gICAgICAgIHJldHVybiBCb2R5Ll9uZXh0Q29sbGlkaW5nR3JvdXBJZCsrO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBuZXh0IHVuaXF1ZSBjYXRlZ29yeSBiaXRmaWVsZCAoc3RhcnRpbmcgYWZ0ZXIgdGhlIGluaXRpYWwgZGVmYXVsdCBjYXRlZ29yeSBgMHgwMDAxYCkuXG4gICAgICogVGhlcmUgYXJlIDMyIGF2YWlsYWJsZS4gU2VlIGBib2R5LmNvbGxpc2lvbkZpbHRlcmAgZm9yIG1vcmUgaW5mb3JtYXRpb24uXG4gICAgICogQG1ldGhvZCBuZXh0Q2F0ZWdvcnlcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IFVuaXF1ZSBjYXRlZ29yeSBiaXRmaWVsZFxuICAgICAqL1xuICAgIEJvZHkubmV4dENhdGVnb3J5ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIEJvZHkuX25leHRDYXRlZ29yeSA9IEJvZHkuX25leHRDYXRlZ29yeSA8PCAxO1xuICAgICAgICByZXR1cm4gQm9keS5fbmV4dENhdGVnb3J5O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXNlcyBib2R5IHByb3BlcnRpZXMuXG4gICAgICogQG1ldGhvZCBfaW5pdFByb3BlcnRpZXNcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEBwYXJhbSB7fSBbb3B0aW9uc11cbiAgICAgKi9cbiAgICB2YXIgX2luaXRQcm9wZXJ0aWVzID0gZnVuY3Rpb24oYm9keSwgb3B0aW9ucykge1xuICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAvLyBpbml0IHJlcXVpcmVkIHByb3BlcnRpZXMgKG9yZGVyIGlzIGltcG9ydGFudClcbiAgICAgICAgQm9keS5zZXQoYm9keSwge1xuICAgICAgICAgICAgYm91bmRzOiBib2R5LmJvdW5kcyB8fCBCb3VuZHMuY3JlYXRlKGJvZHkudmVydGljZXMpLFxuICAgICAgICAgICAgcG9zaXRpb25QcmV2OiBib2R5LnBvc2l0aW9uUHJldiB8fCBWZWN0b3IuY2xvbmUoYm9keS5wb3NpdGlvbiksXG4gICAgICAgICAgICBhbmdsZVByZXY6IGJvZHkuYW5nbGVQcmV2IHx8IGJvZHkuYW5nbGUsXG4gICAgICAgICAgICB2ZXJ0aWNlczogYm9keS52ZXJ0aWNlcyxcbiAgICAgICAgICAgIHBhcnRzOiBib2R5LnBhcnRzIHx8IFtib2R5XSxcbiAgICAgICAgICAgIGlzU3RhdGljOiBib2R5LmlzU3RhdGljLFxuICAgICAgICAgICAgaXNTbGVlcGluZzogYm9keS5pc1NsZWVwaW5nLFxuICAgICAgICAgICAgcGFyZW50OiBib2R5LnBhcmVudCB8fCBib2R5XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFZlcnRpY2VzLnJvdGF0ZShib2R5LnZlcnRpY2VzLCBib2R5LmFuZ2xlLCBib2R5LnBvc2l0aW9uKTtcbiAgICAgICAgQXhlcy5yb3RhdGUoYm9keS5heGVzLCBib2R5LmFuZ2xlKTtcbiAgICAgICAgQm91bmRzLnVwZGF0ZShib2R5LmJvdW5kcywgYm9keS52ZXJ0aWNlcywgYm9keS52ZWxvY2l0eSk7XG5cbiAgICAgICAgLy8gYWxsb3cgb3B0aW9ucyB0byBvdmVycmlkZSB0aGUgYXV0b21hdGljYWxseSBjYWxjdWxhdGVkIHByb3BlcnRpZXNcbiAgICAgICAgQm9keS5zZXQoYm9keSwge1xuICAgICAgICAgICAgYXhlczogb3B0aW9ucy5heGVzIHx8IGJvZHkuYXhlcyxcbiAgICAgICAgICAgIGFyZWE6IG9wdGlvbnMuYXJlYSB8fCBib2R5LmFyZWEsXG4gICAgICAgICAgICBtYXNzOiBvcHRpb25zLm1hc3MgfHwgYm9keS5tYXNzLFxuICAgICAgICAgICAgaW5lcnRpYTogb3B0aW9ucy5pbmVydGlhIHx8IGJvZHkuaW5lcnRpYVxuICAgICAgICB9KTtcblxuICAgICAgICAvLyByZW5kZXIgcHJvcGVydGllc1xuICAgICAgICB2YXIgZGVmYXVsdEZpbGxTdHlsZSA9IChib2R5LmlzU3RhdGljID8gJyMyZTJiNDQnIDogQ29tbW9uLmNob29zZShbJyMwMDZCQTYnLCAnIzA0OTZGRicsICcjRkZCQzQyJywgJyNEODExNTknLCAnIzhGMkQ1NiddKSksXG4gICAgICAgICAgICBkZWZhdWx0U3Ryb2tlU3R5bGUgPSAnIzAwMCc7XG4gICAgICAgIGJvZHkucmVuZGVyLmZpbGxTdHlsZSA9IGJvZHkucmVuZGVyLmZpbGxTdHlsZSB8fCBkZWZhdWx0RmlsbFN0eWxlO1xuICAgICAgICBib2R5LnJlbmRlci5zdHJva2VTdHlsZSA9IGJvZHkucmVuZGVyLnN0cm9rZVN0eWxlIHx8IGRlZmF1bHRTdHJva2VTdHlsZTtcbiAgICAgICAgYm9keS5yZW5kZXIuc3ByaXRlLnhPZmZzZXQgKz0gLShib2R5LmJvdW5kcy5taW4ueCAtIGJvZHkucG9zaXRpb24ueCkgLyAoYm9keS5ib3VuZHMubWF4LnggLSBib2R5LmJvdW5kcy5taW4ueCk7XG4gICAgICAgIGJvZHkucmVuZGVyLnNwcml0ZS55T2Zmc2V0ICs9IC0oYm9keS5ib3VuZHMubWluLnkgLSBib2R5LnBvc2l0aW9uLnkpIC8gKGJvZHkuYm91bmRzLm1heC55IC0gYm9keS5ib3VuZHMubWluLnkpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBHaXZlbiBhIHByb3BlcnR5IGFuZCBhIHZhbHVlIChvciBtYXAgb2YpLCBzZXRzIHRoZSBwcm9wZXJ0eShzKSBvbiB0aGUgYm9keSwgdXNpbmcgdGhlIGFwcHJvcHJpYXRlIHNldHRlciBmdW5jdGlvbnMgaWYgdGhleSBleGlzdC5cbiAgICAgKiBQcmVmZXIgdG8gdXNlIHRoZSBhY3R1YWwgc2V0dGVyIGZ1bmN0aW9ucyBpbiBwZXJmb3JtYW5jZSBjcml0aWNhbCBzaXR1YXRpb25zLlxuICAgICAqIEBtZXRob2Qgc2V0XG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHt9IHNldHRpbmdzIEEgcHJvcGVydHkgbmFtZSAob3IgbWFwIG9mIHByb3BlcnRpZXMgYW5kIHZhbHVlcykgdG8gc2V0IG9uIHRoZSBib2R5LlxuICAgICAqIEBwYXJhbSB7fSB2YWx1ZSBUaGUgdmFsdWUgdG8gc2V0IGlmIGBzZXR0aW5nc2AgaXMgYSBzaW5nbGUgcHJvcGVydHkgbmFtZS5cbiAgICAgKi9cbiAgICBCb2R5LnNldCA9IGZ1bmN0aW9uKGJvZHksIHNldHRpbmdzLCB2YWx1ZSkge1xuICAgICAgICB2YXIgcHJvcGVydHk7XG5cbiAgICAgICAgaWYgKHR5cGVvZiBzZXR0aW5ncyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHByb3BlcnR5ID0gc2V0dGluZ3M7XG4gICAgICAgICAgICBzZXR0aW5ncyA9IHt9O1xuICAgICAgICAgICAgc2V0dGluZ3NbcHJvcGVydHldID0gdmFsdWU7XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKHByb3BlcnR5IGluIHNldHRpbmdzKSB7XG4gICAgICAgICAgICB2YWx1ZSA9IHNldHRpbmdzW3Byb3BlcnR5XTtcblxuICAgICAgICAgICAgaWYgKCFzZXR0aW5ncy5oYXNPd25Qcm9wZXJ0eShwcm9wZXJ0eSkpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIHN3aXRjaCAocHJvcGVydHkpIHtcblxuICAgICAgICAgICAgY2FzZSAnaXNTdGF0aWMnOlxuICAgICAgICAgICAgICAgIEJvZHkuc2V0U3RhdGljKGJvZHksIHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2lzU2xlZXBpbmcnOlxuICAgICAgICAgICAgICAgIFNsZWVwaW5nLnNldChib2R5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdtYXNzJzpcbiAgICAgICAgICAgICAgICBCb2R5LnNldE1hc3MoYm9keSwgdmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnZGVuc2l0eSc6XG4gICAgICAgICAgICAgICAgQm9keS5zZXREZW5zaXR5KGJvZHksIHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2luZXJ0aWEnOlxuICAgICAgICAgICAgICAgIEJvZHkuc2V0SW5lcnRpYShib2R5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICd2ZXJ0aWNlcyc6XG4gICAgICAgICAgICAgICAgQm9keS5zZXRWZXJ0aWNlcyhib2R5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdwb3NpdGlvbic6XG4gICAgICAgICAgICAgICAgQm9keS5zZXRQb3NpdGlvbihib2R5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbmdsZSc6XG4gICAgICAgICAgICAgICAgQm9keS5zZXRBbmdsZShib2R5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICd2ZWxvY2l0eSc6XG4gICAgICAgICAgICAgICAgQm9keS5zZXRWZWxvY2l0eShib2R5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbmd1bGFyVmVsb2NpdHknOlxuICAgICAgICAgICAgICAgIEJvZHkuc2V0QW5ndWxhclZlbG9jaXR5KGJvZHksIHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ3BhcnRzJzpcbiAgICAgICAgICAgICAgICBCb2R5LnNldFBhcnRzKGJvZHksIHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgYm9keVtwcm9wZXJ0eV0gPSB2YWx1ZTtcblxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGJvZHkgYXMgc3RhdGljLCBpbmNsdWRpbmcgaXNTdGF0aWMgZmxhZyBhbmQgc2V0dGluZyBtYXNzIGFuZCBpbmVydGlhIHRvIEluZmluaXR5LlxuICAgICAqIEBtZXRob2Qgc2V0U3RhdGljXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHtib29sfSBpc1N0YXRpY1xuICAgICAqL1xuICAgIEJvZHkuc2V0U3RhdGljID0gZnVuY3Rpb24oYm9keSwgaXNTdGF0aWMpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2R5LnBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgcGFydCA9IGJvZHkucGFydHNbaV07XG4gICAgICAgICAgICBwYXJ0LmlzU3RhdGljID0gaXNTdGF0aWM7XG5cbiAgICAgICAgICAgIGlmIChpc1N0YXRpYykge1xuICAgICAgICAgICAgICAgIHBhcnQuX29yaWdpbmFsID0ge1xuICAgICAgICAgICAgICAgICAgICByZXN0aXR1dGlvbjogcGFydC5yZXN0aXR1dGlvbixcbiAgICAgICAgICAgICAgICAgICAgZnJpY3Rpb246IHBhcnQuZnJpY3Rpb24sXG4gICAgICAgICAgICAgICAgICAgIG1hc3M6IHBhcnQubWFzcyxcbiAgICAgICAgICAgICAgICAgICAgaW5lcnRpYTogcGFydC5pbmVydGlhLFxuICAgICAgICAgICAgICAgICAgICBkZW5zaXR5OiBwYXJ0LmRlbnNpdHksXG4gICAgICAgICAgICAgICAgICAgIGludmVyc2VNYXNzOiBwYXJ0LmludmVyc2VNYXNzLFxuICAgICAgICAgICAgICAgICAgICBpbnZlcnNlSW5lcnRpYTogcGFydC5pbnZlcnNlSW5lcnRpYVxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBwYXJ0LnJlc3RpdHV0aW9uID0gMDtcbiAgICAgICAgICAgICAgICBwYXJ0LmZyaWN0aW9uID0gMTtcbiAgICAgICAgICAgICAgICBwYXJ0Lm1hc3MgPSBwYXJ0LmluZXJ0aWEgPSBwYXJ0LmRlbnNpdHkgPSBJbmZpbml0eTtcbiAgICAgICAgICAgICAgICBwYXJ0LmludmVyc2VNYXNzID0gcGFydC5pbnZlcnNlSW5lcnRpYSA9IDA7XG5cbiAgICAgICAgICAgICAgICBwYXJ0LnBvc2l0aW9uUHJldi54ID0gcGFydC5wb3NpdGlvbi54O1xuICAgICAgICAgICAgICAgIHBhcnQucG9zaXRpb25QcmV2LnkgPSBwYXJ0LnBvc2l0aW9uLnk7XG4gICAgICAgICAgICAgICAgcGFydC5hbmdsZVByZXYgPSBwYXJ0LmFuZ2xlO1xuICAgICAgICAgICAgICAgIHBhcnQuYW5ndWxhclZlbG9jaXR5ID0gMDtcbiAgICAgICAgICAgICAgICBwYXJ0LnNwZWVkID0gMDtcbiAgICAgICAgICAgICAgICBwYXJ0LmFuZ3VsYXJTcGVlZCA9IDA7XG4gICAgICAgICAgICAgICAgcGFydC5tb3Rpb24gPSAwO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0Ll9vcmlnaW5hbCkge1xuICAgICAgICAgICAgICAgIHBhcnQucmVzdGl0dXRpb24gPSBwYXJ0Ll9vcmlnaW5hbC5yZXN0aXR1dGlvbjtcbiAgICAgICAgICAgICAgICBwYXJ0LmZyaWN0aW9uID0gcGFydC5fb3JpZ2luYWwuZnJpY3Rpb247XG4gICAgICAgICAgICAgICAgcGFydC5tYXNzID0gcGFydC5fb3JpZ2luYWwubWFzcztcbiAgICAgICAgICAgICAgICBwYXJ0LmluZXJ0aWEgPSBwYXJ0Ll9vcmlnaW5hbC5pbmVydGlhO1xuICAgICAgICAgICAgICAgIHBhcnQuZGVuc2l0eSA9IHBhcnQuX29yaWdpbmFsLmRlbnNpdHk7XG4gICAgICAgICAgICAgICAgcGFydC5pbnZlcnNlTWFzcyA9IHBhcnQuX29yaWdpbmFsLmludmVyc2VNYXNzO1xuICAgICAgICAgICAgICAgIHBhcnQuaW52ZXJzZUluZXJ0aWEgPSBwYXJ0Ll9vcmlnaW5hbC5pbnZlcnNlSW5lcnRpYTtcblxuICAgICAgICAgICAgICAgIGRlbGV0ZSBwYXJ0Ll9vcmlnaW5hbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBtYXNzIG9mIHRoZSBib2R5LiBJbnZlcnNlIG1hc3MgYW5kIGRlbnNpdHkgYXJlIGF1dG9tYXRpY2FsbHkgdXBkYXRlZCB0byByZWZsZWN0IHRoZSBjaGFuZ2UuXG4gICAgICogQG1ldGhvZCBzZXRNYXNzXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IG1hc3NcbiAgICAgKi9cbiAgICBCb2R5LnNldE1hc3MgPSBmdW5jdGlvbihib2R5LCBtYXNzKSB7XG4gICAgICAgIGJvZHkubWFzcyA9IG1hc3M7XG4gICAgICAgIGJvZHkuaW52ZXJzZU1hc3MgPSAxIC8gYm9keS5tYXNzO1xuICAgICAgICBib2R5LmRlbnNpdHkgPSBib2R5Lm1hc3MgLyBib2R5LmFyZWE7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGRlbnNpdHkgb2YgdGhlIGJvZHkuIE1hc3MgaXMgYXV0b21hdGljYWxseSB1cGRhdGVkIHRvIHJlZmxlY3QgdGhlIGNoYW5nZS5cbiAgICAgKiBAbWV0aG9kIHNldERlbnNpdHlcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gZGVuc2l0eVxuICAgICAqL1xuICAgIEJvZHkuc2V0RGVuc2l0eSA9IGZ1bmN0aW9uKGJvZHksIGRlbnNpdHkpIHtcbiAgICAgICAgQm9keS5zZXRNYXNzKGJvZHksIGRlbnNpdHkgKiBib2R5LmFyZWEpO1xuICAgICAgICBib2R5LmRlbnNpdHkgPSBkZW5zaXR5O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBtb21lbnQgb2YgaW5lcnRpYSAoaS5lLiBzZWNvbmQgbW9tZW50IG9mIGFyZWEpIG9mIHRoZSBib2R5IG9mIHRoZSBib2R5LiBcbiAgICAgKiBJbnZlcnNlIGluZXJ0aWEgaXMgYXV0b21hdGljYWxseSB1cGRhdGVkIHRvIHJlZmxlY3QgdGhlIGNoYW5nZS4gTWFzcyBpcyBub3QgY2hhbmdlZC5cbiAgICAgKiBAbWV0aG9kIHNldEluZXJ0aWFcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gaW5lcnRpYVxuICAgICAqL1xuICAgIEJvZHkuc2V0SW5lcnRpYSA9IGZ1bmN0aW9uKGJvZHksIGluZXJ0aWEpIHtcbiAgICAgICAgYm9keS5pbmVydGlhID0gaW5lcnRpYTtcbiAgICAgICAgYm9keS5pbnZlcnNlSW5lcnRpYSA9IDEgLyBib2R5LmluZXJ0aWE7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGJvZHkncyB2ZXJ0aWNlcyBhbmQgdXBkYXRlcyBib2R5IHByb3BlcnRpZXMgYWNjb3JkaW5nbHksIGluY2x1ZGluZyBpbmVydGlhLCBhcmVhIGFuZCBtYXNzICh3aXRoIHJlc3BlY3QgdG8gYGJvZHkuZGVuc2l0eWApLlxuICAgICAqIFZlcnRpY2VzIHdpbGwgYmUgYXV0b21hdGljYWxseSB0cmFuc2Zvcm1lZCB0byBiZSBvcmllbnRhdGVkIGFyb3VuZCB0aGVpciBjZW50cmUgb2YgbWFzcyBhcyB0aGUgb3JpZ2luLlxuICAgICAqIFRoZXkgYXJlIHRoZW4gYXV0b21hdGljYWxseSB0cmFuc2xhdGVkIHRvIHdvcmxkIHNwYWNlIGJhc2VkIG9uIGBib2R5LnBvc2l0aW9uYC5cbiAgICAgKlxuICAgICAqIFRoZSBgdmVydGljZXNgIGFyZ3VtZW50IHNob3VsZCBiZSBwYXNzZWQgYXMgYW4gYXJyYXkgb2YgYE1hdHRlci5WZWN0b3JgIHBvaW50cyAob3IgYSBgTWF0dGVyLlZlcnRpY2VzYCBhcnJheSkuXG4gICAgICogVmVydGljZXMgbXVzdCBmb3JtIGEgY29udmV4IGh1bGwsIGNvbmNhdmUgaHVsbHMgYXJlIG5vdCBzdXBwb3J0ZWQuXG4gICAgICpcbiAgICAgKiBAbWV0aG9kIHNldFZlcnRpY2VzXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHt2ZWN0b3JbXX0gdmVydGljZXNcbiAgICAgKi9cbiAgICBCb2R5LnNldFZlcnRpY2VzID0gZnVuY3Rpb24oYm9keSwgdmVydGljZXMpIHtcbiAgICAgICAgLy8gY2hhbmdlIHZlcnRpY2VzXG4gICAgICAgIGlmICh2ZXJ0aWNlc1swXS5ib2R5ID09PSBib2R5KSB7XG4gICAgICAgICAgICBib2R5LnZlcnRpY2VzID0gdmVydGljZXM7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBib2R5LnZlcnRpY2VzID0gVmVydGljZXMuY3JlYXRlKHZlcnRpY2VzLCBib2R5KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHVwZGF0ZSBwcm9wZXJ0aWVzXG4gICAgICAgIGJvZHkuYXhlcyA9IEF4ZXMuZnJvbVZlcnRpY2VzKGJvZHkudmVydGljZXMpO1xuICAgICAgICBib2R5LmFyZWEgPSBWZXJ0aWNlcy5hcmVhKGJvZHkudmVydGljZXMpO1xuICAgICAgICBCb2R5LnNldE1hc3MoYm9keSwgYm9keS5kZW5zaXR5ICogYm9keS5hcmVhKTtcblxuICAgICAgICAvLyBvcmllbnQgdmVydGljZXMgYXJvdW5kIHRoZSBjZW50cmUgb2YgbWFzcyBhdCBvcmlnaW4gKDAsIDApXG4gICAgICAgIHZhciBjZW50cmUgPSBWZXJ0aWNlcy5jZW50cmUoYm9keS52ZXJ0aWNlcyk7XG4gICAgICAgIFZlcnRpY2VzLnRyYW5zbGF0ZShib2R5LnZlcnRpY2VzLCBjZW50cmUsIC0xKTtcblxuICAgICAgICAvLyB1cGRhdGUgaW5lcnRpYSB3aGlsZSB2ZXJ0aWNlcyBhcmUgYXQgb3JpZ2luICgwLCAwKVxuICAgICAgICBCb2R5LnNldEluZXJ0aWEoYm9keSwgQm9keS5faW5lcnRpYVNjYWxlICogVmVydGljZXMuaW5lcnRpYShib2R5LnZlcnRpY2VzLCBib2R5Lm1hc3MpKTtcblxuICAgICAgICAvLyB1cGRhdGUgZ2VvbWV0cnlcbiAgICAgICAgVmVydGljZXMudHJhbnNsYXRlKGJvZHkudmVydGljZXMsIGJvZHkucG9zaXRpb24pO1xuICAgICAgICBCb3VuZHMudXBkYXRlKGJvZHkuYm91bmRzLCBib2R5LnZlcnRpY2VzLCBib2R5LnZlbG9jaXR5KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgcGFydHMgb2YgdGhlIGBib2R5YCBhbmQgdXBkYXRlcyBtYXNzLCBpbmVydGlhIGFuZCBjZW50cm9pZC5cbiAgICAgKiBFYWNoIHBhcnQgd2lsbCBoYXZlIGl0cyBwYXJlbnQgc2V0IHRvIGBib2R5YC5cbiAgICAgKiBCeSBkZWZhdWx0IHRoZSBjb252ZXggaHVsbCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgY29tcHV0ZWQgYW5kIHNldCBvbiBgYm9keWAsIHVubGVzcyBgYXV0b0h1bGxgIGlzIHNldCB0byBgZmFsc2UuYFxuICAgICAqIE5vdGUgdGhhdCB0aGlzIG1ldGhvZCB3aWxsIGVuc3VyZSB0aGF0IHRoZSBmaXJzdCBwYXJ0IGluIGBib2R5LnBhcnRzYCB3aWxsIGFsd2F5cyBiZSB0aGUgYGJvZHlgLlxuICAgICAqIEBtZXRob2Qgc2V0UGFydHNcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKiBAcGFyYW0gW2JvZHldIHBhcnRzXG4gICAgICogQHBhcmFtIHtib29sfSBbYXV0b0h1bGw9dHJ1ZV1cbiAgICAgKi9cbiAgICBCb2R5LnNldFBhcnRzID0gZnVuY3Rpb24oYm9keSwgcGFydHMsIGF1dG9IdWxsKSB7XG4gICAgICAgIHZhciBpO1xuXG4gICAgICAgIC8vIGFkZCBhbGwgdGhlIHBhcnRzLCBlbnN1cmluZyB0aGF0IHRoZSBmaXJzdCBwYXJ0IGlzIGFsd2F5cyB0aGUgcGFyZW50IGJvZHlcbiAgICAgICAgcGFydHMgPSBwYXJ0cy5zbGljZSgwKTtcbiAgICAgICAgYm9keS5wYXJ0cy5sZW5ndGggPSAwO1xuICAgICAgICBib2R5LnBhcnRzLnB1c2goYm9keSk7XG4gICAgICAgIGJvZHkucGFyZW50ID0gYm9keTtcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwYXJ0ID0gcGFydHNbaV07XG4gICAgICAgICAgICBpZiAocGFydCAhPT0gYm9keSkge1xuICAgICAgICAgICAgICAgIHBhcnQucGFyZW50ID0gYm9keTtcbiAgICAgICAgICAgICAgICBib2R5LnBhcnRzLnB1c2gocGFydCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYm9keS5wYXJ0cy5sZW5ndGggPT09IDEpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgYXV0b0h1bGwgPSB0eXBlb2YgYXV0b0h1bGwgIT09ICd1bmRlZmluZWQnID8gYXV0b0h1bGwgOiB0cnVlO1xuXG4gICAgICAgIC8vIGZpbmQgdGhlIGNvbnZleCBodWxsIG9mIGFsbCBwYXJ0cyB0byBzZXQgb24gdGhlIHBhcmVudCBib2R5XG4gICAgICAgIGlmIChhdXRvSHVsbCkge1xuICAgICAgICAgICAgdmFyIHZlcnRpY2VzID0gW107XG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2ZXJ0aWNlcyA9IHZlcnRpY2VzLmNvbmNhdChwYXJ0c1tpXS52ZXJ0aWNlcyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFZlcnRpY2VzLmNsb2Nrd2lzZVNvcnQodmVydGljZXMpO1xuXG4gICAgICAgICAgICB2YXIgaHVsbCA9IFZlcnRpY2VzLmh1bGwodmVydGljZXMpLFxuICAgICAgICAgICAgICAgIGh1bGxDZW50cmUgPSBWZXJ0aWNlcy5jZW50cmUoaHVsbCk7XG5cbiAgICAgICAgICAgIEJvZHkuc2V0VmVydGljZXMoYm9keSwgaHVsbCk7XG4gICAgICAgICAgICBWZXJ0aWNlcy50cmFuc2xhdGUoYm9keS52ZXJ0aWNlcywgaHVsbENlbnRyZSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBzdW0gdGhlIHByb3BlcnRpZXMgb2YgYWxsIGNvbXBvdW5kIHBhcnRzIG9mIHRoZSBwYXJlbnQgYm9keVxuICAgICAgICB2YXIgdG90YWwgPSBfdG90YWxQcm9wZXJ0aWVzKGJvZHkpO1xuXG4gICAgICAgIGJvZHkuYXJlYSA9IHRvdGFsLmFyZWE7XG4gICAgICAgIGJvZHkucGFyZW50ID0gYm9keTtcbiAgICAgICAgYm9keS5wb3NpdGlvbi54ID0gdG90YWwuY2VudHJlLng7XG4gICAgICAgIGJvZHkucG9zaXRpb24ueSA9IHRvdGFsLmNlbnRyZS55O1xuICAgICAgICBib2R5LnBvc2l0aW9uUHJldi54ID0gdG90YWwuY2VudHJlLng7XG4gICAgICAgIGJvZHkucG9zaXRpb25QcmV2LnkgPSB0b3RhbC5jZW50cmUueTtcblxuICAgICAgICBCb2R5LnNldE1hc3MoYm9keSwgdG90YWwubWFzcyk7XG4gICAgICAgIEJvZHkuc2V0SW5lcnRpYShib2R5LCB0b3RhbC5pbmVydGlhKTtcbiAgICAgICAgQm9keS5zZXRQb3NpdGlvbihib2R5LCB0b3RhbC5jZW50cmUpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBwb3NpdGlvbiBvZiB0aGUgYm9keSBpbnN0YW50bHkuIFZlbG9jaXR5LCBhbmdsZSwgZm9yY2UgZXRjLiBhcmUgdW5jaGFuZ2VkLlxuICAgICAqIEBtZXRob2Qgc2V0UG9zaXRpb25cbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gcG9zaXRpb25cbiAgICAgKi9cbiAgICBCb2R5LnNldFBvc2l0aW9uID0gZnVuY3Rpb24oYm9keSwgcG9zaXRpb24pIHtcbiAgICAgICAgdmFyIGRlbHRhID0gVmVjdG9yLnN1Yihwb3NpdGlvbiwgYm9keS5wb3NpdGlvbik7XG4gICAgICAgIGJvZHkucG9zaXRpb25QcmV2LnggKz0gZGVsdGEueDtcbiAgICAgICAgYm9keS5wb3NpdGlvblByZXYueSArPSBkZWx0YS55O1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9keS5wYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHBhcnQgPSBib2R5LnBhcnRzW2ldO1xuICAgICAgICAgICAgcGFydC5wb3NpdGlvbi54ICs9IGRlbHRhLng7XG4gICAgICAgICAgICBwYXJ0LnBvc2l0aW9uLnkgKz0gZGVsdGEueTtcbiAgICAgICAgICAgIFZlcnRpY2VzLnRyYW5zbGF0ZShwYXJ0LnZlcnRpY2VzLCBkZWx0YSk7XG4gICAgICAgICAgICBCb3VuZHMudXBkYXRlKHBhcnQuYm91bmRzLCBwYXJ0LnZlcnRpY2VzLCBib2R5LnZlbG9jaXR5KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBhbmdsZSBvZiB0aGUgYm9keSBpbnN0YW50bHkuIEFuZ3VsYXIgdmVsb2NpdHksIHBvc2l0aW9uLCBmb3JjZSBldGMuIGFyZSB1bmNoYW5nZWQuXG4gICAgICogQG1ldGhvZCBzZXRBbmdsZVxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBhbmdsZVxuICAgICAqL1xuICAgIEJvZHkuc2V0QW5nbGUgPSBmdW5jdGlvbihib2R5LCBhbmdsZSkge1xuICAgICAgICB2YXIgZGVsdGEgPSBhbmdsZSAtIGJvZHkuYW5nbGU7XG4gICAgICAgIGJvZHkuYW5nbGVQcmV2ICs9IGRlbHRhO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9keS5wYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHBhcnQgPSBib2R5LnBhcnRzW2ldO1xuICAgICAgICAgICAgcGFydC5hbmdsZSArPSBkZWx0YTtcbiAgICAgICAgICAgIFZlcnRpY2VzLnJvdGF0ZShwYXJ0LnZlcnRpY2VzLCBkZWx0YSwgYm9keS5wb3NpdGlvbik7XG4gICAgICAgICAgICBBeGVzLnJvdGF0ZShwYXJ0LmF4ZXMsIGRlbHRhKTtcbiAgICAgICAgICAgIEJvdW5kcy51cGRhdGUocGFydC5ib3VuZHMsIHBhcnQudmVydGljZXMsIGJvZHkudmVsb2NpdHkpO1xuICAgICAgICAgICAgaWYgKGkgPiAwKSB7XG4gICAgICAgICAgICAgICAgVmVjdG9yLnJvdGF0ZUFib3V0KHBhcnQucG9zaXRpb24sIGRlbHRhLCBib2R5LnBvc2l0aW9uLCBwYXJ0LnBvc2l0aW9uKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBsaW5lYXIgdmVsb2NpdHkgb2YgdGhlIGJvZHkgaW5zdGFudGx5LiBQb3NpdGlvbiwgYW5nbGUsIGZvcmNlIGV0Yy4gYXJlIHVuY2hhbmdlZC4gU2VlIGFsc28gYEJvZHkuYXBwbHlGb3JjZWAuXG4gICAgICogQG1ldGhvZCBzZXRWZWxvY2l0eVxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWxvY2l0eVxuICAgICAqL1xuICAgIEJvZHkuc2V0VmVsb2NpdHkgPSBmdW5jdGlvbihib2R5LCB2ZWxvY2l0eSkge1xuICAgICAgICBib2R5LnBvc2l0aW9uUHJldi54ID0gYm9keS5wb3NpdGlvbi54IC0gdmVsb2NpdHkueDtcbiAgICAgICAgYm9keS5wb3NpdGlvblByZXYueSA9IGJvZHkucG9zaXRpb24ueSAtIHZlbG9jaXR5Lnk7XG4gICAgICAgIGJvZHkudmVsb2NpdHkueCA9IHZlbG9jaXR5Lng7XG4gICAgICAgIGJvZHkudmVsb2NpdHkueSA9IHZlbG9jaXR5Lnk7XG4gICAgICAgIGJvZHkuc3BlZWQgPSBWZWN0b3IubWFnbml0dWRlKGJvZHkudmVsb2NpdHkpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBhbmd1bGFyIHZlbG9jaXR5IG9mIHRoZSBib2R5IGluc3RhbnRseS4gUG9zaXRpb24sIGFuZ2xlLCBmb3JjZSBldGMuIGFyZSB1bmNoYW5nZWQuIFNlZSBhbHNvIGBCb2R5LmFwcGx5Rm9yY2VgLlxuICAgICAqIEBtZXRob2Qgc2V0QW5ndWxhclZlbG9jaXR5XG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHZlbG9jaXR5XG4gICAgICovXG4gICAgQm9keS5zZXRBbmd1bGFyVmVsb2NpdHkgPSBmdW5jdGlvbihib2R5LCB2ZWxvY2l0eSkge1xuICAgICAgICBib2R5LmFuZ2xlUHJldiA9IGJvZHkuYW5nbGUgLSB2ZWxvY2l0eTtcbiAgICAgICAgYm9keS5hbmd1bGFyVmVsb2NpdHkgPSB2ZWxvY2l0eTtcbiAgICAgICAgYm9keS5hbmd1bGFyU3BlZWQgPSBNYXRoLmFicyhib2R5LmFuZ3VsYXJWZWxvY2l0eSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE1vdmVzIGEgYm9keSBieSBhIGdpdmVuIHZlY3RvciByZWxhdGl2ZSB0byBpdHMgY3VycmVudCBwb3NpdGlvbiwgd2l0aG91dCBpbXBhcnRpbmcgYW55IHZlbG9jaXR5LlxuICAgICAqIEBtZXRob2QgdHJhbnNsYXRlXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHRyYW5zbGF0aW9uXG4gICAgICovXG4gICAgQm9keS50cmFuc2xhdGUgPSBmdW5jdGlvbihib2R5LCB0cmFuc2xhdGlvbikge1xuICAgICAgICBCb2R5LnNldFBvc2l0aW9uKGJvZHksIFZlY3Rvci5hZGQoYm9keS5wb3NpdGlvbiwgdHJhbnNsYXRpb24pKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUm90YXRlcyBhIGJvZHkgYnkgYSBnaXZlbiBhbmdsZSByZWxhdGl2ZSB0byBpdHMgY3VycmVudCBhbmdsZSwgd2l0aG91dCBpbXBhcnRpbmcgYW55IGFuZ3VsYXIgdmVsb2NpdHkuXG4gICAgICogQG1ldGhvZCByb3RhdGVcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcm90YXRpb25cbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gW3BvaW50XVxuICAgICAqL1xuICAgIEJvZHkucm90YXRlID0gZnVuY3Rpb24oYm9keSwgcm90YXRpb24sIHBvaW50KSB7XG4gICAgICAgIGlmICghcG9pbnQpIHtcbiAgICAgICAgICAgIEJvZHkuc2V0QW5nbGUoYm9keSwgYm9keS5hbmdsZSArIHJvdGF0aW9uKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHZhciBjb3MgPSBNYXRoLmNvcyhyb3RhdGlvbiksXG4gICAgICAgICAgICAgICAgc2luID0gTWF0aC5zaW4ocm90YXRpb24pLFxuICAgICAgICAgICAgICAgIGR4ID0gYm9keS5wb3NpdGlvbi54IC0gcG9pbnQueCxcbiAgICAgICAgICAgICAgICBkeSA9IGJvZHkucG9zaXRpb24ueSAtIHBvaW50Lnk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICBCb2R5LnNldFBvc2l0aW9uKGJvZHksIHtcbiAgICAgICAgICAgICAgICB4OiBwb2ludC54ICsgKGR4ICogY29zIC0gZHkgKiBzaW4pLFxuICAgICAgICAgICAgICAgIHk6IHBvaW50LnkgKyAoZHggKiBzaW4gKyBkeSAqIGNvcylcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBCb2R5LnNldEFuZ2xlKGJvZHksIGJvZHkuYW5nbGUgKyByb3RhdGlvbik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2NhbGVzIHRoZSBib2R5LCBpbmNsdWRpbmcgdXBkYXRpbmcgcGh5c2ljYWwgcHJvcGVydGllcyAobWFzcywgYXJlYSwgYXhlcywgaW5lcnRpYSksIGZyb20gYSB3b3JsZC1zcGFjZSBwb2ludCAoZGVmYXVsdCBpcyBib2R5IGNlbnRyZSkuXG4gICAgICogQG1ldGhvZCBzY2FsZVxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBzY2FsZVhcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2NhbGVZXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IFtwb2ludF1cbiAgICAgKi9cbiAgICBCb2R5LnNjYWxlID0gZnVuY3Rpb24oYm9keSwgc2NhbGVYLCBzY2FsZVksIHBvaW50KSB7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9keS5wYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHBhcnQgPSBib2R5LnBhcnRzW2ldO1xuXG4gICAgICAgICAgICAvLyBzY2FsZSB2ZXJ0aWNlc1xuICAgICAgICAgICAgVmVydGljZXMuc2NhbGUocGFydC52ZXJ0aWNlcywgc2NhbGVYLCBzY2FsZVksIGJvZHkucG9zaXRpb24pO1xuXG4gICAgICAgICAgICAvLyB1cGRhdGUgcHJvcGVydGllc1xuICAgICAgICAgICAgcGFydC5heGVzID0gQXhlcy5mcm9tVmVydGljZXMocGFydC52ZXJ0aWNlcyk7XG5cbiAgICAgICAgICAgIGlmICghYm9keS5pc1N0YXRpYykge1xuICAgICAgICAgICAgICAgIHBhcnQuYXJlYSA9IFZlcnRpY2VzLmFyZWEocGFydC52ZXJ0aWNlcyk7XG4gICAgICAgICAgICAgICAgQm9keS5zZXRNYXNzKHBhcnQsIGJvZHkuZGVuc2l0eSAqIHBhcnQuYXJlYSk7XG5cbiAgICAgICAgICAgICAgICAvLyB1cGRhdGUgaW5lcnRpYSAocmVxdWlyZXMgdmVydGljZXMgdG8gYmUgYXQgb3JpZ2luKVxuICAgICAgICAgICAgICAgIFZlcnRpY2VzLnRyYW5zbGF0ZShwYXJ0LnZlcnRpY2VzLCB7IHg6IC1wYXJ0LnBvc2l0aW9uLngsIHk6IC1wYXJ0LnBvc2l0aW9uLnkgfSk7XG4gICAgICAgICAgICAgICAgQm9keS5zZXRJbmVydGlhKHBhcnQsIFZlcnRpY2VzLmluZXJ0aWEocGFydC52ZXJ0aWNlcywgcGFydC5tYXNzKSk7XG4gICAgICAgICAgICAgICAgVmVydGljZXMudHJhbnNsYXRlKHBhcnQudmVydGljZXMsIHsgeDogcGFydC5wb3NpdGlvbi54LCB5OiBwYXJ0LnBvc2l0aW9uLnkgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSBib3VuZHNcbiAgICAgICAgICAgIEJvdW5kcy51cGRhdGUocGFydC5ib3VuZHMsIHBhcnQudmVydGljZXMsIGJvZHkudmVsb2NpdHkpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gaGFuZGxlIGNpcmNsZXNcbiAgICAgICAgaWYgKGJvZHkuY2lyY2xlUmFkaXVzKSB7IFxuICAgICAgICAgICAgaWYgKHNjYWxlWCA9PT0gc2NhbGVZKSB7XG4gICAgICAgICAgICAgICAgYm9keS5jaXJjbGVSYWRpdXMgKj0gc2NhbGVYO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBib2R5IGlzIG5vIGxvbmdlciBhIGNpcmNsZVxuICAgICAgICAgICAgICAgIGJvZHkuY2lyY2xlUmFkaXVzID0gbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghYm9keS5pc1N0YXRpYykge1xuICAgICAgICAgICAgdmFyIHRvdGFsID0gX3RvdGFsUHJvcGVydGllcyhib2R5KTtcbiAgICAgICAgICAgIGJvZHkuYXJlYSA9IHRvdGFsLmFyZWE7XG4gICAgICAgICAgICBCb2R5LnNldE1hc3MoYm9keSwgdG90YWwubWFzcyk7XG4gICAgICAgICAgICBCb2R5LnNldEluZXJ0aWEoYm9keSwgdG90YWwuaW5lcnRpYSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUGVyZm9ybXMgYSBzaW11bGF0aW9uIHN0ZXAgZm9yIHRoZSBnaXZlbiBgYm9keWAsIGluY2x1ZGluZyB1cGRhdGluZyBwb3NpdGlvbiBhbmQgYW5nbGUgdXNpbmcgVmVybGV0IGludGVncmF0aW9uLlxuICAgICAqIEBtZXRob2QgdXBkYXRlXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGRlbHRhVGltZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lU2NhbGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gY29ycmVjdGlvblxuICAgICAqL1xuICAgIEJvZHkudXBkYXRlID0gZnVuY3Rpb24oYm9keSwgZGVsdGFUaW1lLCB0aW1lU2NhbGUsIGNvcnJlY3Rpb24pIHtcbiAgICAgICAgdmFyIGRlbHRhVGltZVNxdWFyZWQgPSBNYXRoLnBvdyhkZWx0YVRpbWUgKiB0aW1lU2NhbGUgKiBib2R5LnRpbWVTY2FsZSwgMik7XG5cbiAgICAgICAgLy8gZnJvbSB0aGUgcHJldmlvdXMgc3RlcFxuICAgICAgICB2YXIgZnJpY3Rpb25BaXIgPSAxIC0gYm9keS5mcmljdGlvbkFpciAqIHRpbWVTY2FsZSAqIGJvZHkudGltZVNjYWxlLFxuICAgICAgICAgICAgdmVsb2NpdHlQcmV2WCA9IGJvZHkucG9zaXRpb24ueCAtIGJvZHkucG9zaXRpb25QcmV2LngsXG4gICAgICAgICAgICB2ZWxvY2l0eVByZXZZID0gYm9keS5wb3NpdGlvbi55IC0gYm9keS5wb3NpdGlvblByZXYueTtcblxuICAgICAgICAvLyB1cGRhdGUgdmVsb2NpdHkgd2l0aCBWZXJsZXQgaW50ZWdyYXRpb25cbiAgICAgICAgYm9keS52ZWxvY2l0eS54ID0gKHZlbG9jaXR5UHJldlggKiBmcmljdGlvbkFpciAqIGNvcnJlY3Rpb24pICsgKGJvZHkuZm9yY2UueCAvIGJvZHkubWFzcykgKiBkZWx0YVRpbWVTcXVhcmVkO1xuICAgICAgICBib2R5LnZlbG9jaXR5LnkgPSAodmVsb2NpdHlQcmV2WSAqIGZyaWN0aW9uQWlyICogY29ycmVjdGlvbikgKyAoYm9keS5mb3JjZS55IC8gYm9keS5tYXNzKSAqIGRlbHRhVGltZVNxdWFyZWQ7XG5cbiAgICAgICAgYm9keS5wb3NpdGlvblByZXYueCA9IGJvZHkucG9zaXRpb24ueDtcbiAgICAgICAgYm9keS5wb3NpdGlvblByZXYueSA9IGJvZHkucG9zaXRpb24ueTtcbiAgICAgICAgYm9keS5wb3NpdGlvbi54ICs9IGJvZHkudmVsb2NpdHkueDtcbiAgICAgICAgYm9keS5wb3NpdGlvbi55ICs9IGJvZHkudmVsb2NpdHkueTtcblxuICAgICAgICAvLyB1cGRhdGUgYW5ndWxhciB2ZWxvY2l0eSB3aXRoIFZlcmxldCBpbnRlZ3JhdGlvblxuICAgICAgICBib2R5LmFuZ3VsYXJWZWxvY2l0eSA9ICgoYm9keS5hbmdsZSAtIGJvZHkuYW5nbGVQcmV2KSAqIGZyaWN0aW9uQWlyICogY29ycmVjdGlvbikgKyAoYm9keS50b3JxdWUgLyBib2R5LmluZXJ0aWEpICogZGVsdGFUaW1lU3F1YXJlZDtcbiAgICAgICAgYm9keS5hbmdsZVByZXYgPSBib2R5LmFuZ2xlO1xuICAgICAgICBib2R5LmFuZ2xlICs9IGJvZHkuYW5ndWxhclZlbG9jaXR5O1xuXG4gICAgICAgIC8vIHRyYWNrIHNwZWVkIGFuZCBhY2NlbGVyYXRpb25cbiAgICAgICAgYm9keS5zcGVlZCA9IFZlY3Rvci5tYWduaXR1ZGUoYm9keS52ZWxvY2l0eSk7XG4gICAgICAgIGJvZHkuYW5ndWxhclNwZWVkID0gTWF0aC5hYnMoYm9keS5hbmd1bGFyVmVsb2NpdHkpO1xuXG4gICAgICAgIC8vIHRyYW5zZm9ybSB0aGUgYm9keSBnZW9tZXRyeVxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZHkucGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwYXJ0ID0gYm9keS5wYXJ0c1tpXTtcblxuICAgICAgICAgICAgVmVydGljZXMudHJhbnNsYXRlKHBhcnQudmVydGljZXMsIGJvZHkudmVsb2NpdHkpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoaSA+IDApIHtcbiAgICAgICAgICAgICAgICBwYXJ0LnBvc2l0aW9uLnggKz0gYm9keS52ZWxvY2l0eS54O1xuICAgICAgICAgICAgICAgIHBhcnQucG9zaXRpb24ueSArPSBib2R5LnZlbG9jaXR5Lnk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChib2R5LmFuZ3VsYXJWZWxvY2l0eSAhPT0gMCkge1xuICAgICAgICAgICAgICAgIFZlcnRpY2VzLnJvdGF0ZShwYXJ0LnZlcnRpY2VzLCBib2R5LmFuZ3VsYXJWZWxvY2l0eSwgYm9keS5wb3NpdGlvbik7XG4gICAgICAgICAgICAgICAgQXhlcy5yb3RhdGUocGFydC5heGVzLCBib2R5LmFuZ3VsYXJWZWxvY2l0eSk7XG4gICAgICAgICAgICAgICAgaWYgKGkgPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIFZlY3Rvci5yb3RhdGVBYm91dChwYXJ0LnBvc2l0aW9uLCBib2R5LmFuZ3VsYXJWZWxvY2l0eSwgYm9keS5wb3NpdGlvbiwgcGFydC5wb3NpdGlvbik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBCb3VuZHMudXBkYXRlKHBhcnQuYm91bmRzLCBwYXJ0LnZlcnRpY2VzLCBib2R5LnZlbG9jaXR5KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBBcHBsaWVzIGEgZm9yY2UgdG8gYSBib2R5IGZyb20gYSBnaXZlbiB3b3JsZC1zcGFjZSBwb3NpdGlvbiwgaW5jbHVkaW5nIHJlc3VsdGluZyB0b3JxdWUuXG4gICAgICogQG1ldGhvZCBhcHBseUZvcmNlXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHBvc2l0aW9uXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IGZvcmNlXG4gICAgICovXG4gICAgQm9keS5hcHBseUZvcmNlID0gZnVuY3Rpb24oYm9keSwgcG9zaXRpb24sIGZvcmNlKSB7XG4gICAgICAgIGJvZHkuZm9yY2UueCArPSBmb3JjZS54O1xuICAgICAgICBib2R5LmZvcmNlLnkgKz0gZm9yY2UueTtcbiAgICAgICAgdmFyIG9mZnNldCA9IHsgeDogcG9zaXRpb24ueCAtIGJvZHkucG9zaXRpb24ueCwgeTogcG9zaXRpb24ueSAtIGJvZHkucG9zaXRpb24ueSB9O1xuICAgICAgICBib2R5LnRvcnF1ZSArPSBvZmZzZXQueCAqIGZvcmNlLnkgLSBvZmZzZXQueSAqIGZvcmNlLng7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHN1bXMgb2YgdGhlIHByb3BlcnRpZXMgb2YgYWxsIGNvbXBvdW5kIHBhcnRzIG9mIHRoZSBwYXJlbnQgYm9keS5cbiAgICAgKiBAbWV0aG9kIF90b3RhbFByb3BlcnRpZXNcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEByZXR1cm4ge31cbiAgICAgKi9cbiAgICB2YXIgX3RvdGFsUHJvcGVydGllcyA9IGZ1bmN0aW9uKGJvZHkpIHtcbiAgICAgICAgLy8gZnJvbSBlcXVhdGlvbnMgYXQ6XG4gICAgICAgIC8vIGh0dHBzOi8vZWNvdXJzZXMub3UuZWR1L2NnaS1iaW4vZWJvb2suY2dpP2RvYz0mdG9waWM9c3QmY2hhcF9zZWM9MDcuMiZwYWdlPXRoZW9yeVxuICAgICAgICAvLyBodHRwOi8vb3V0cHV0LnRvL3NpZGV3YXkvZGVmYXVsdC5hc3A/cW5vPTEyMTEwMDA4N1xuXG4gICAgICAgIHZhciBwcm9wZXJ0aWVzID0ge1xuICAgICAgICAgICAgbWFzczogMCxcbiAgICAgICAgICAgIGFyZWE6IDAsXG4gICAgICAgICAgICBpbmVydGlhOiAwLFxuICAgICAgICAgICAgY2VudHJlOiB7IHg6IDAsIHk6IDAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIHN1bSB0aGUgcHJvcGVydGllcyBvZiBhbGwgY29tcG91bmQgcGFydHMgb2YgdGhlIHBhcmVudCBib2R5XG4gICAgICAgIGZvciAodmFyIGkgPSBib2R5LnBhcnRzLmxlbmd0aCA9PT0gMSA/IDAgOiAxOyBpIDwgYm9keS5wYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHBhcnQgPSBib2R5LnBhcnRzW2ldO1xuICAgICAgICAgICAgcHJvcGVydGllcy5tYXNzICs9IHBhcnQubWFzcztcbiAgICAgICAgICAgIHByb3BlcnRpZXMuYXJlYSArPSBwYXJ0LmFyZWE7XG4gICAgICAgICAgICBwcm9wZXJ0aWVzLmluZXJ0aWEgKz0gcGFydC5pbmVydGlhO1xuICAgICAgICAgICAgcHJvcGVydGllcy5jZW50cmUgPSBWZWN0b3IuYWRkKHByb3BlcnRpZXMuY2VudHJlLCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWZWN0b3IubXVsdChwYXJ0LnBvc2l0aW9uLCBwYXJ0Lm1hc3MgIT09IEluZmluaXR5ID8gcGFydC5tYXNzIDogMSkpO1xuICAgICAgICB9XG5cbiAgICAgICAgcHJvcGVydGllcy5jZW50cmUgPSBWZWN0b3IuZGl2KHByb3BlcnRpZXMuY2VudHJlLCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXMubWFzcyAhPT0gSW5maW5pdHkgPyBwcm9wZXJ0aWVzLm1hc3MgOiBib2R5LnBhcnRzLmxlbmd0aCk7XG5cbiAgICAgICAgcmV0dXJuIHByb3BlcnRpZXM7XG4gICAgfTtcblxuICAgIC8qXG4gICAgKlxuICAgICogIEV2ZW50cyBEb2N1bWVudGF0aW9uXG4gICAgKlxuICAgICovXG5cbiAgICAvKipcbiAgICAqIEZpcmVkIHdoZW4gYSBib2R5IHN0YXJ0cyBzbGVlcGluZyAod2hlcmUgYHRoaXNgIGlzIHRoZSBib2R5KS5cbiAgICAqXG4gICAgKiBAZXZlbnQgc2xlZXBTdGFydFxuICAgICogQHRoaXMge2JvZHl9IFRoZSBib2R5IHRoYXQgaGFzIHN0YXJ0ZWQgc2xlZXBpbmdcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCB3aGVuIGEgYm9keSBlbmRzIHNsZWVwaW5nICh3aGVyZSBgdGhpc2AgaXMgdGhlIGJvZHkpLlxuICAgICpcbiAgICAqIEBldmVudCBzbGVlcEVuZFxuICAgICogQHRoaXMge2JvZHl9IFRoZSBib2R5IHRoYXQgaGFzIGVuZGVkIHNsZWVwaW5nXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qXG4gICAgKlxuICAgICogIFByb3BlcnRpZXMgRG9jdW1lbnRhdGlvblxuICAgICpcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gaW50ZWdlciBgTnVtYmVyYCB1bmlxdWVseSBpZGVudGlmeWluZyBudW1iZXIgZ2VuZXJhdGVkIGluIGBCb2R5LmNyZWF0ZWAgYnkgYENvbW1vbi5uZXh0SWRgLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGlkXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBTdHJpbmdgIGRlbm90aW5nIHRoZSB0eXBlIG9mIG9iamVjdC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0eXBlXG4gICAgICogQHR5cGUgc3RyaW5nXG4gICAgICogQGRlZmF1bHQgXCJib2R5XCJcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGFyYml0cmFyeSBgU3RyaW5nYCBuYW1lIHRvIGhlbHAgdGhlIHVzZXIgaWRlbnRpZnkgYW5kIG1hbmFnZSBib2RpZXMuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgbGFiZWxcbiAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgKiBAZGVmYXVsdCBcIkJvZHlcIlxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gYXJyYXkgb2YgYm9kaWVzIHRoYXQgbWFrZSB1cCB0aGlzIGJvZHkuIFxuICAgICAqIFRoZSBmaXJzdCBib2R5IGluIHRoZSBhcnJheSBtdXN0IGFsd2F5cyBiZSBhIHNlbGYgcmVmZXJlbmNlIHRvIHRoZSBjdXJyZW50IGJvZHkgaW5zdGFuY2UuXG4gICAgICogQWxsIGJvZGllcyBpbiB0aGUgYHBhcnRzYCBhcnJheSB0b2dldGhlciBmb3JtIGEgc2luZ2xlIHJpZ2lkIGNvbXBvdW5kIGJvZHkuXG4gICAgICogUGFydHMgYXJlIGFsbG93ZWQgdG8gb3ZlcmxhcCwgaGF2ZSBnYXBzIG9yIGhvbGVzIG9yIGV2ZW4gZm9ybSBjb25jYXZlIGJvZGllcy5cbiAgICAgKiBQYXJ0cyB0aGVtc2VsdmVzIHNob3VsZCBuZXZlciBiZSBhZGRlZCB0byBhIGBXb3JsZGAsIG9ubHkgdGhlIHBhcmVudCBib2R5IHNob3VsZCBiZS5cbiAgICAgKiBVc2UgYEJvZHkuc2V0UGFydHNgIHdoZW4gc2V0dGluZyBwYXJ0cyB0byBlbnN1cmUgY29ycmVjdCB1cGRhdGVzIG9mIGFsbCBwcm9wZXJ0aWVzLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHBhcnRzXG4gICAgICogQHR5cGUgYm9keVtdXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBvYmplY3QgcmVzZXJ2ZWQgZm9yIHN0b3JpbmcgcGx1Z2luLXNwZWNpZmljIHByb3BlcnRpZXMuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcGx1Z2luXG4gICAgICogQHR5cGUge31cbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgc2VsZiByZWZlcmVuY2UgaWYgdGhlIGJvZHkgaXMgX25vdF8gYSBwYXJ0IG9mIGFub3RoZXIgYm9keS5cbiAgICAgKiBPdGhlcndpc2UgdGhpcyBpcyBhIHJlZmVyZW5jZSB0byB0aGUgYm9keSB0aGF0IHRoaXMgaXMgYSBwYXJ0IG9mLlxuICAgICAqIFNlZSBgYm9keS5wYXJ0c2AuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcGFyZW50XG4gICAgICogQHR5cGUgYm9keVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCBzcGVjaWZ5aW5nIHRoZSBhbmdsZSBvZiB0aGUgYm9keSwgaW4gcmFkaWFucy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBhbmdsZVxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDBcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGFycmF5IG9mIGBWZWN0b3JgIG9iamVjdHMgdGhhdCBzcGVjaWZ5IHRoZSBjb252ZXggaHVsbCBvZiB0aGUgcmlnaWQgYm9keS5cbiAgICAgKiBUaGVzZSBzaG91bGQgYmUgcHJvdmlkZWQgYWJvdXQgdGhlIG9yaWdpbiBgKDAsIDApYC4gRS5nLlxuICAgICAqXG4gICAgICogICAgIFt7IHg6IDAsIHk6IDAgfSwgeyB4OiAyNSwgeTogNTAgfSwgeyB4OiA1MCwgeTogMCB9XVxuICAgICAqXG4gICAgICogV2hlbiBwYXNzZWQgdmlhIGBCb2R5LmNyZWF0ZWAsIHRoZSB2ZXJ0aWNlcyBhcmUgdHJhbnNsYXRlZCByZWxhdGl2ZSB0byBgYm9keS5wb3NpdGlvbmAgKGkuZS4gd29ybGQtc3BhY2UsIGFuZCBjb25zdGFudGx5IHVwZGF0ZWQgYnkgYEJvZHkudXBkYXRlYCBkdXJpbmcgc2ltdWxhdGlvbikuXG4gICAgICogVGhlIGBWZWN0b3JgIG9iamVjdHMgYXJlIGFsc28gYXVnbWVudGVkIHdpdGggYWRkaXRpb25hbCBwcm9wZXJ0aWVzIHJlcXVpcmVkIGZvciBlZmZpY2llbnQgY29sbGlzaW9uIGRldGVjdGlvbi4gXG4gICAgICpcbiAgICAgKiBPdGhlciBwcm9wZXJ0aWVzIHN1Y2ggYXMgYGluZXJ0aWFgIGFuZCBgYm91bmRzYCBhcmUgYXV0b21hdGljYWxseSBjYWxjdWxhdGVkIGZyb20gdGhlIHBhc3NlZCB2ZXJ0aWNlcyAodW5sZXNzIHByb3ZpZGVkIHZpYSBgb3B0aW9uc2ApLlxuICAgICAqIENvbmNhdmUgaHVsbHMgYXJlIG5vdCBjdXJyZW50bHkgc3VwcG9ydGVkLiBUaGUgbW9kdWxlIGBNYXR0ZXIuVmVydGljZXNgIGNvbnRhaW5zIHVzZWZ1bCBtZXRob2RzIGZvciB3b3JraW5nIHdpdGggdmVydGljZXMuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgdmVydGljZXNcbiAgICAgKiBAdHlwZSB2ZWN0b3JbXVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgVmVjdG9yYCB0aGF0IHNwZWNpZmllcyB0aGUgY3VycmVudCB3b3JsZC1zcGFjZSBwb3NpdGlvbiBvZiB0aGUgYm9keS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBwb3NpdGlvblxuICAgICAqIEB0eXBlIHZlY3RvclxuICAgICAqIEBkZWZhdWx0IHsgeDogMCwgeTogMCB9XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBWZWN0b3JgIHRoYXQgc3BlY2lmaWVzIHRoZSBmb3JjZSB0byBhcHBseSBpbiB0aGUgY3VycmVudCBzdGVwLiBJdCBpcyB6ZXJvZWQgYWZ0ZXIgZXZlcnkgYEJvZHkudXBkYXRlYC4gU2VlIGFsc28gYEJvZHkuYXBwbHlGb3JjZWAuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgZm9yY2VcbiAgICAgKiBAdHlwZSB2ZWN0b3JcbiAgICAgKiBAZGVmYXVsdCB7IHg6IDAsIHk6IDAgfVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IHNwZWNpZmllcyB0aGUgdG9ycXVlICh0dXJuaW5nIGZvcmNlKSB0byBhcHBseSBpbiB0aGUgY3VycmVudCBzdGVwLiBJdCBpcyB6ZXJvZWQgYWZ0ZXIgZXZlcnkgYEJvZHkudXBkYXRlYC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0b3JxdWVcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAwXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgX21lYXN1cmVzXyB0aGUgY3VycmVudCBzcGVlZCBvZiB0aGUgYm9keSBhZnRlciB0aGUgbGFzdCBgQm9keS51cGRhdGVgLiBJdCBpcyByZWFkLW9ubHkgYW5kIGFsd2F5cyBwb3NpdGl2ZSAoaXQncyB0aGUgbWFnbml0dWRlIG9mIGBib2R5LnZlbG9jaXR5YCkuXG4gICAgICpcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKiBAcHJvcGVydHkgc3BlZWRcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAwXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgX21lYXN1cmVzXyB0aGUgY3VycmVudCBhbmd1bGFyIHNwZWVkIG9mIHRoZSBib2R5IGFmdGVyIHRoZSBsYXN0IGBCb2R5LnVwZGF0ZWAuIEl0IGlzIHJlYWQtb25seSBhbmQgYWx3YXlzIHBvc2l0aXZlIChpdCdzIHRoZSBtYWduaXR1ZGUgb2YgYGJvZHkuYW5ndWxhclZlbG9jaXR5YCkuXG4gICAgICpcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKiBAcHJvcGVydHkgYW5ndWxhclNwZWVkXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgVmVjdG9yYCB0aGF0IF9tZWFzdXJlc18gdGhlIGN1cnJlbnQgdmVsb2NpdHkgb2YgdGhlIGJvZHkgYWZ0ZXIgdGhlIGxhc3QgYEJvZHkudXBkYXRlYC4gSXQgaXMgcmVhZC1vbmx5LiBcbiAgICAgKiBJZiB5b3UgbmVlZCB0byBtb2RpZnkgYSBib2R5J3MgdmVsb2NpdHkgZGlyZWN0bHksIHlvdSBzaG91bGQgZWl0aGVyIGFwcGx5IGEgZm9yY2Ugb3Igc2ltcGx5IGNoYW5nZSB0aGUgYm9keSdzIGBwb3NpdGlvbmAgKGFzIHRoZSBlbmdpbmUgdXNlcyBwb3NpdGlvbi1WZXJsZXQgaW50ZWdyYXRpb24pLlxuICAgICAqXG4gICAgICogQHJlYWRPbmx5XG4gICAgICogQHByb3BlcnR5IHZlbG9jaXR5XG4gICAgICogQHR5cGUgdmVjdG9yXG4gICAgICogQGRlZmF1bHQgeyB4OiAwLCB5OiAwIH1cbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBfbWVhc3VyZXNfIHRoZSBjdXJyZW50IGFuZ3VsYXIgdmVsb2NpdHkgb2YgdGhlIGJvZHkgYWZ0ZXIgdGhlIGxhc3QgYEJvZHkudXBkYXRlYC4gSXQgaXMgcmVhZC1vbmx5LiBcbiAgICAgKiBJZiB5b3UgbmVlZCB0byBtb2RpZnkgYSBib2R5J3MgYW5ndWxhciB2ZWxvY2l0eSBkaXJlY3RseSwgeW91IHNob3VsZCBhcHBseSBhIHRvcnF1ZSBvciBzaW1wbHkgY2hhbmdlIHRoZSBib2R5J3MgYGFuZ2xlYCAoYXMgdGhlIGVuZ2luZSB1c2VzIHBvc2l0aW9uLVZlcmxldCBpbnRlZ3JhdGlvbikuXG4gICAgICpcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKiBAcHJvcGVydHkgYW5ndWxhclZlbG9jaXR5XG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBmbGFnIHRoYXQgaW5kaWNhdGVzIHdoZXRoZXIgYSBib2R5IGlzIGNvbnNpZGVyZWQgc3RhdGljLiBBIHN0YXRpYyBib2R5IGNhbiBuZXZlciBjaGFuZ2UgcG9zaXRpb24gb3IgYW5nbGUgYW5kIGlzIGNvbXBsZXRlbHkgZml4ZWQuXG4gICAgICogSWYgeW91IG5lZWQgdG8gc2V0IGEgYm9keSBhcyBzdGF0aWMgYWZ0ZXIgaXRzIGNyZWF0aW9uLCB5b3Ugc2hvdWxkIHVzZSBgQm9keS5zZXRTdGF0aWNgIGFzIHRoaXMgcmVxdWlyZXMgbW9yZSB0aGFuIGp1c3Qgc2V0dGluZyB0aGlzIGZsYWcuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgaXNTdGF0aWNcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgZmxhZyB0aGF0IGluZGljYXRlcyB3aGV0aGVyIGEgYm9keSBpcyBhIHNlbnNvci4gU2Vuc29yIHRyaWdnZXJzIGNvbGxpc2lvbiBldmVudHMsIGJ1dCBkb2Vzbid0IHJlYWN0IHdpdGggY29sbGlkaW5nIGJvZHkgcGh5c2ljYWxseS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBpc1NlbnNvclxuICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBmbGFnIHRoYXQgaW5kaWNhdGVzIHdoZXRoZXIgdGhlIGJvZHkgaXMgY29uc2lkZXJlZCBzbGVlcGluZy4gQSBzbGVlcGluZyBib2R5IGFjdHMgc2ltaWxhciB0byBhIHN0YXRpYyBib2R5LCBleGNlcHQgaXQgaXMgb25seSB0ZW1wb3JhcnkgYW5kIGNhbiBiZSBhd29rZW4uXG4gICAgICogSWYgeW91IG5lZWQgdG8gc2V0IGEgYm9keSBhcyBzbGVlcGluZywgeW91IHNob3VsZCB1c2UgYFNsZWVwaW5nLnNldGAgYXMgdGhpcyByZXF1aXJlcyBtb3JlIHRoYW4ganVzdCBzZXR0aW5nIHRoaXMgZmxhZy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBpc1NsZWVwaW5nXG4gICAgICogQHR5cGUgYm9vbGVhblxuICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgX21lYXN1cmVzXyB0aGUgYW1vdW50IG9mIG1vdmVtZW50IGEgYm9keSBjdXJyZW50bHkgaGFzIChhIGNvbWJpbmF0aW9uIG9mIGBzcGVlZGAgYW5kIGBhbmd1bGFyU3BlZWRgKS4gSXQgaXMgcmVhZC1vbmx5IGFuZCBhbHdheXMgcG9zaXRpdmUuXG4gICAgICogSXQgaXMgdXNlZCBhbmQgdXBkYXRlZCBieSB0aGUgYE1hdHRlci5TbGVlcGluZ2AgbW9kdWxlIGR1cmluZyBzaW11bGF0aW9uIHRvIGRlY2lkZSBpZiBhIGJvZHkgaGFzIGNvbWUgdG8gcmVzdC5cbiAgICAgKlxuICAgICAqIEByZWFkT25seVxuICAgICAqIEBwcm9wZXJ0eSBtb3Rpb25cbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAwXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgZGVmaW5lcyB0aGUgbnVtYmVyIG9mIHVwZGF0ZXMgaW4gd2hpY2ggdGhpcyBib2R5IG11c3QgaGF2ZSBuZWFyLXplcm8gdmVsb2NpdHkgYmVmb3JlIGl0IGlzIHNldCBhcyBzbGVlcGluZyBieSB0aGUgYE1hdHRlci5TbGVlcGluZ2AgbW9kdWxlIChpZiBzbGVlcGluZyBpcyBlbmFibGVkIGJ5IHRoZSBlbmdpbmUpLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHNsZWVwVGhyZXNob2xkXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgNjBcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBkZWZpbmVzIHRoZSBkZW5zaXR5IG9mIHRoZSBib2R5LCB0aGF0IGlzIGl0cyBtYXNzIHBlciB1bml0IGFyZWEuXG4gICAgICogSWYgeW91IHBhc3MgdGhlIGRlbnNpdHkgdmlhIGBCb2R5LmNyZWF0ZWAgdGhlIGBtYXNzYCBwcm9wZXJ0eSBpcyBhdXRvbWF0aWNhbGx5IGNhbGN1bGF0ZWQgZm9yIHlvdSBiYXNlZCBvbiB0aGUgc2l6ZSAoYXJlYSkgb2YgdGhlIG9iamVjdC5cbiAgICAgKiBUaGlzIGlzIGdlbmVyYWxseSBwcmVmZXJhYmxlIHRvIHNpbXBseSBzZXR0aW5nIG1hc3MgYW5kIGFsbG93cyBmb3IgbW9yZSBpbnR1aXRpdmUgZGVmaW5pdGlvbiBvZiBtYXRlcmlhbHMgKGUuZy4gcm9jayBoYXMgYSBoaWdoZXIgZGVuc2l0eSB0aGFuIHdvb2QpLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGRlbnNpdHlcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAwLjAwMVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIG1hc3Mgb2YgdGhlIGJvZHksIGFsdGhvdWdoIGl0IG1heSBiZSBtb3JlIGFwcHJvcHJpYXRlIHRvIHNwZWNpZnkgdGhlIGBkZW5zaXR5YCBwcm9wZXJ0eSBpbnN0ZWFkLlxuICAgICAqIElmIHlvdSBtb2RpZnkgdGhpcyB2YWx1ZSwgeW91IG11c3QgYWxzbyBtb2RpZnkgdGhlIGBib2R5LmludmVyc2VNYXNzYCBwcm9wZXJ0eSAoYDEgLyBtYXNzYCkuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgbWFzc1xuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIGludmVyc2UgbWFzcyBvZiB0aGUgYm9keSAoYDEgLyBtYXNzYCkuXG4gICAgICogSWYgeW91IG1vZGlmeSB0aGlzIHZhbHVlLCB5b3UgbXVzdCBhbHNvIG1vZGlmeSB0aGUgYGJvZHkubWFzc2AgcHJvcGVydHkuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgaW52ZXJzZU1hc3NcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBkZWZpbmVzIHRoZSBtb21lbnQgb2YgaW5lcnRpYSAoaS5lLiBzZWNvbmQgbW9tZW50IG9mIGFyZWEpIG9mIHRoZSBib2R5LlxuICAgICAqIEl0IGlzIGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlZCBmcm9tIHRoZSBnaXZlbiBjb252ZXggaHVsbCAoYHZlcnRpY2VzYCBhcnJheSkgYW5kIGRlbnNpdHkgaW4gYEJvZHkuY3JlYXRlYC5cbiAgICAgKiBJZiB5b3UgbW9kaWZ5IHRoaXMgdmFsdWUsIHlvdSBtdXN0IGFsc28gbW9kaWZ5IHRoZSBgYm9keS5pbnZlcnNlSW5lcnRpYWAgcHJvcGVydHkgKGAxIC8gaW5lcnRpYWApLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGluZXJ0aWFcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBkZWZpbmVzIHRoZSBpbnZlcnNlIG1vbWVudCBvZiBpbmVydGlhIG9mIHRoZSBib2R5IChgMSAvIGluZXJ0aWFgKS5cbiAgICAgKiBJZiB5b3UgbW9kaWZ5IHRoaXMgdmFsdWUsIHlvdSBtdXN0IGFsc28gbW9kaWZ5IHRoZSBgYm9keS5pbmVydGlhYCBwcm9wZXJ0eS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBpbnZlcnNlSW5lcnRpYVxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIHJlc3RpdHV0aW9uIChlbGFzdGljaXR5KSBvZiB0aGUgYm9keS4gVGhlIHZhbHVlIGlzIGFsd2F5cyBwb3NpdGl2ZSBhbmQgaXMgaW4gdGhlIHJhbmdlIGAoMCwgMSlgLlxuICAgICAqIEEgdmFsdWUgb2YgYDBgIG1lYW5zIGNvbGxpc2lvbnMgbWF5IGJlIHBlcmZlY3RseSBpbmVsYXN0aWMgYW5kIG5vIGJvdW5jaW5nIG1heSBvY2N1ci4gXG4gICAgICogQSB2YWx1ZSBvZiBgMC44YCBtZWFucyB0aGUgYm9keSBtYXkgYm91bmNlIGJhY2sgd2l0aCBhcHByb3hpbWF0ZWx5IDgwJSBvZiBpdHMga2luZXRpYyBlbmVyZ3kuXG4gICAgICogTm90ZSB0aGF0IGNvbGxpc2lvbiByZXNwb25zZSBpcyBiYXNlZCBvbiBfcGFpcnNfIG9mIGJvZGllcywgYW5kIHRoYXQgYHJlc3RpdHV0aW9uYCB2YWx1ZXMgYXJlIF9jb21iaW5lZF8gd2l0aCB0aGUgZm9sbG93aW5nIGZvcm11bGE6XG4gICAgICpcbiAgICAgKiAgICAgTWF0aC5tYXgoYm9keUEucmVzdGl0dXRpb24sIGJvZHlCLnJlc3RpdHV0aW9uKVxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHJlc3RpdHV0aW9uXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIGZyaWN0aW9uIG9mIHRoZSBib2R5LiBUaGUgdmFsdWUgaXMgYWx3YXlzIHBvc2l0aXZlIGFuZCBpcyBpbiB0aGUgcmFuZ2UgYCgwLCAxKWAuXG4gICAgICogQSB2YWx1ZSBvZiBgMGAgbWVhbnMgdGhhdCB0aGUgYm9keSBtYXkgc2xpZGUgaW5kZWZpbml0ZWx5LlxuICAgICAqIEEgdmFsdWUgb2YgYDFgIG1lYW5zIHRoZSBib2R5IG1heSBjb21lIHRvIGEgc3RvcCBhbG1vc3QgaW5zdGFudGx5IGFmdGVyIGEgZm9yY2UgaXMgYXBwbGllZC5cbiAgICAgKlxuICAgICAqIFRoZSBlZmZlY3RzIG9mIHRoZSB2YWx1ZSBtYXkgYmUgbm9uLWxpbmVhci4gXG4gICAgICogSGlnaCB2YWx1ZXMgbWF5IGJlIHVuc3RhYmxlIGRlcGVuZGluZyBvbiB0aGUgYm9keS5cbiAgICAgKiBUaGUgZW5naW5lIHVzZXMgYSBDb3Vsb21iIGZyaWN0aW9uIG1vZGVsIGluY2x1ZGluZyBzdGF0aWMgYW5kIGtpbmV0aWMgZnJpY3Rpb24uXG4gICAgICogTm90ZSB0aGF0IGNvbGxpc2lvbiByZXNwb25zZSBpcyBiYXNlZCBvbiBfcGFpcnNfIG9mIGJvZGllcywgYW5kIHRoYXQgYGZyaWN0aW9uYCB2YWx1ZXMgYXJlIF9jb21iaW5lZF8gd2l0aCB0aGUgZm9sbG93aW5nIGZvcm11bGE6XG4gICAgICpcbiAgICAgKiAgICAgTWF0aC5taW4oYm9keUEuZnJpY3Rpb24sIGJvZHlCLmZyaWN0aW9uKVxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGZyaWN0aW9uXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMC4xXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgZGVmaW5lcyB0aGUgc3RhdGljIGZyaWN0aW9uIG9mIHRoZSBib2R5IChpbiB0aGUgQ291bG9tYiBmcmljdGlvbiBtb2RlbCkuIFxuICAgICAqIEEgdmFsdWUgb2YgYDBgIG1lYW5zIHRoZSBib2R5IHdpbGwgbmV2ZXIgJ3N0aWNrJyB3aGVuIGl0IGlzIG5lYXJseSBzdGF0aW9uYXJ5IGFuZCBvbmx5IGR5bmFtaWMgYGZyaWN0aW9uYCBpcyB1c2VkLlxuICAgICAqIFRoZSBoaWdoZXIgdGhlIHZhbHVlIChlLmcuIGAxMGApLCB0aGUgbW9yZSBmb3JjZSBpdCB3aWxsIHRha2UgdG8gaW5pdGlhbGx5IGdldCB0aGUgYm9keSBtb3Zpbmcgd2hlbiBuZWFybHkgc3RhdGlvbmFyeS5cbiAgICAgKiBUaGlzIHZhbHVlIGlzIG11bHRpcGxpZWQgd2l0aCB0aGUgYGZyaWN0aW9uYCBwcm9wZXJ0eSB0byBtYWtlIGl0IGVhc2llciB0byBjaGFuZ2UgYGZyaWN0aW9uYCBhbmQgbWFpbnRhaW4gYW4gYXBwcm9wcmlhdGUgYW1vdW50IG9mIHN0YXRpYyBmcmljdGlvbi5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBmcmljdGlvblN0YXRpY1xuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDAuNVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIGFpciBmcmljdGlvbiBvZiB0aGUgYm9keSAoYWlyIHJlc2lzdGFuY2UpLiBcbiAgICAgKiBBIHZhbHVlIG9mIGAwYCBtZWFucyB0aGUgYm9keSB3aWxsIG5ldmVyIHNsb3cgYXMgaXQgbW92ZXMgdGhyb3VnaCBzcGFjZS5cbiAgICAgKiBUaGUgaGlnaGVyIHRoZSB2YWx1ZSwgdGhlIGZhc3RlciBhIGJvZHkgc2xvd3Mgd2hlbiBtb3ZpbmcgdGhyb3VnaCBzcGFjZS5cbiAgICAgKiBUaGUgZWZmZWN0cyBvZiB0aGUgdmFsdWUgYXJlIG5vbi1saW5lYXIuIFxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGZyaWN0aW9uQWlyXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMC4wMVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gYE9iamVjdGAgdGhhdCBzcGVjaWZpZXMgdGhlIGNvbGxpc2lvbiBmaWx0ZXJpbmcgcHJvcGVydGllcyBvZiB0aGlzIGJvZHkuXG4gICAgICpcbiAgICAgKiBDb2xsaXNpb25zIGJldHdlZW4gdHdvIGJvZGllcyB3aWxsIG9iZXkgdGhlIGZvbGxvd2luZyBydWxlczpcbiAgICAgKiAtIElmIHRoZSB0d28gYm9kaWVzIGhhdmUgdGhlIHNhbWUgbm9uLXplcm8gdmFsdWUgb2YgYGNvbGxpc2lvbkZpbHRlci5ncm91cGAsXG4gICAgICogICB0aGV5IHdpbGwgYWx3YXlzIGNvbGxpZGUgaWYgdGhlIHZhbHVlIGlzIHBvc2l0aXZlLCBhbmQgdGhleSB3aWxsIG5ldmVyIGNvbGxpZGVcbiAgICAgKiAgIGlmIHRoZSB2YWx1ZSBpcyBuZWdhdGl2ZS5cbiAgICAgKiAtIElmIHRoZSB0d28gYm9kaWVzIGhhdmUgZGlmZmVyZW50IHZhbHVlcyBvZiBgY29sbGlzaW9uRmlsdGVyLmdyb3VwYCBvciBpZiBvbmVcbiAgICAgKiAgIChvciBib3RoKSBvZiB0aGUgYm9kaWVzIGhhcyBhIHZhbHVlIG9mIDAsIHRoZW4gdGhlIGNhdGVnb3J5L21hc2sgcnVsZXMgYXBwbHkgYXMgZm9sbG93czpcbiAgICAgKlxuICAgICAqIEVhY2ggYm9keSBiZWxvbmdzIHRvIGEgY29sbGlzaW9uIGNhdGVnb3J5LCBnaXZlbiBieSBgY29sbGlzaW9uRmlsdGVyLmNhdGVnb3J5YC4gVGhpc1xuICAgICAqIHZhbHVlIGlzIHVzZWQgYXMgYSBiaXQgZmllbGQgYW5kIHRoZSBjYXRlZ29yeSBzaG91bGQgaGF2ZSBvbmx5IG9uZSBiaXQgc2V0LCBtZWFuaW5nIHRoYXRcbiAgICAgKiB0aGUgdmFsdWUgb2YgdGhpcyBwcm9wZXJ0eSBpcyBhIHBvd2VyIG9mIHR3byBpbiB0aGUgcmFuZ2UgWzEsIDJeMzFdLiBUaHVzLCB0aGVyZSBhcmUgMzJcbiAgICAgKiBkaWZmZXJlbnQgY29sbGlzaW9uIGNhdGVnb3JpZXMgYXZhaWxhYmxlLlxuICAgICAqXG4gICAgICogRWFjaCBib2R5IGFsc28gZGVmaW5lcyBhIGNvbGxpc2lvbiBiaXRtYXNrLCBnaXZlbiBieSBgY29sbGlzaW9uRmlsdGVyLm1hc2tgIHdoaWNoIHNwZWNpZmllc1xuICAgICAqIHRoZSBjYXRlZ29yaWVzIGl0IGNvbGxpZGVzIHdpdGggKHRoZSB2YWx1ZSBpcyB0aGUgYml0d2lzZSBBTkQgdmFsdWUgb2YgYWxsIHRoZXNlIGNhdGVnb3JpZXMpLlxuICAgICAqXG4gICAgICogVXNpbmcgdGhlIGNhdGVnb3J5L21hc2sgcnVsZXMsIHR3byBib2RpZXMgYEFgIGFuZCBgQmAgY29sbGlkZSBpZiBlYWNoIGluY2x1ZGVzIHRoZSBvdGhlcidzXG4gICAgICogY2F0ZWdvcnkgaW4gaXRzIG1hc2ssIGkuZS4gYChjYXRlZ29yeUEgJiBtYXNrQikgIT09IDBgIGFuZCBgKGNhdGVnb3J5QiAmIG1hc2tBKSAhPT0gMGBcbiAgICAgKiBhcmUgYm90aCB0cnVlLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGNvbGxpc2lvbkZpbHRlclxuICAgICAqIEB0eXBlIG9iamVjdFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gSW50ZWdlciBgTnVtYmVyYCwgdGhhdCBzcGVjaWZpZXMgdGhlIGNvbGxpc2lvbiBncm91cCB0aGlzIGJvZHkgYmVsb25ncyB0by5cbiAgICAgKiBTZWUgYGJvZHkuY29sbGlzaW9uRmlsdGVyYCBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBjb2xsaXNpb25GaWx0ZXIuZ3JvdXBcbiAgICAgKiBAdHlwZSBvYmplY3RcbiAgICAgKiBAZGVmYXVsdCAwXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGJpdCBmaWVsZCB0aGF0IHNwZWNpZmllcyB0aGUgY29sbGlzaW9uIGNhdGVnb3J5IHRoaXMgYm9keSBiZWxvbmdzIHRvLlxuICAgICAqIFRoZSBjYXRlZ29yeSB2YWx1ZSBzaG91bGQgaGF2ZSBvbmx5IG9uZSBiaXQgc2V0LCBmb3IgZXhhbXBsZSBgMHgwMDAxYC5cbiAgICAgKiBUaGlzIG1lYW5zIHRoZXJlIGFyZSB1cCB0byAzMiB1bmlxdWUgY29sbGlzaW9uIGNhdGVnb3JpZXMgYXZhaWxhYmxlLlxuICAgICAqIFNlZSBgYm9keS5jb2xsaXNpb25GaWx0ZXJgIGZvciBtb3JlIGluZm9ybWF0aW9uLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGNvbGxpc2lvbkZpbHRlci5jYXRlZ29yeVxuICAgICAqIEB0eXBlIG9iamVjdFxuICAgICAqIEBkZWZhdWx0IDFcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYml0IG1hc2sgdGhhdCBzcGVjaWZpZXMgdGhlIGNvbGxpc2lvbiBjYXRlZ29yaWVzIHRoaXMgYm9keSBtYXkgY29sbGlkZSB3aXRoLlxuICAgICAqIFNlZSBgYm9keS5jb2xsaXNpb25GaWx0ZXJgIGZvciBtb3JlIGluZm9ybWF0aW9uLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGNvbGxpc2lvbkZpbHRlci5tYXNrXG4gICAgICogQHR5cGUgb2JqZWN0XG4gICAgICogQGRlZmF1bHQgLTFcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBzcGVjaWZpZXMgYSB0b2xlcmFuY2Ugb24gaG93IGZhciBhIGJvZHkgaXMgYWxsb3dlZCB0byAnc2luaycgb3Igcm90YXRlIGludG8gb3RoZXIgYm9kaWVzLlxuICAgICAqIEF2b2lkIGNoYW5naW5nIHRoaXMgdmFsdWUgdW5sZXNzIHlvdSB1bmRlcnN0YW5kIHRoZSBwdXJwb3NlIG9mIGBzbG9wYCBpbiBwaHlzaWNzIGVuZ2luZXMuXG4gICAgICogVGhlIGRlZmF1bHQgc2hvdWxkIGdlbmVyYWxseSBzdWZmaWNlLCBhbHRob3VnaCB2ZXJ5IGxhcmdlIGJvZGllcyBtYXkgcmVxdWlyZSBsYXJnZXIgdmFsdWVzIGZvciBzdGFibGUgc3RhY2tpbmcuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgc2xvcFxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDAuMDVcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBhbGxvd3MgcGVyLWJvZHkgdGltZSBzY2FsaW5nLCBlLmcuIGEgZm9yY2UtZmllbGQgd2hlcmUgYm9kaWVzIGluc2lkZSBhcmUgaW4gc2xvdy1tb3Rpb24sIHdoaWxlIG90aGVycyBhcmUgYXQgZnVsbCBzcGVlZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0aW1lU2NhbGVcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAxXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBgT2JqZWN0YCB0aGF0IGRlZmluZXMgdGhlIHJlbmRlcmluZyBwcm9wZXJ0aWVzIHRvIGJlIGNvbnN1bWVkIGJ5IHRoZSBtb2R1bGUgYE1hdHRlci5SZW5kZXJgLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHJlbmRlclxuICAgICAqIEB0eXBlIG9iamVjdFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBmbGFnIHRoYXQgaW5kaWNhdGVzIGlmIHRoZSBib2R5IHNob3VsZCBiZSByZW5kZXJlZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIudmlzaWJsZVxuICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgKiBAZGVmYXVsdCB0cnVlXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBvcGFjaXR5IHRvIHVzZSB3aGVuIHJlbmRlcmluZy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIub3BhY2l0eVxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDFcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gYE9iamVjdGAgdGhhdCBkZWZpbmVzIHRoZSBzcHJpdGUgcHJvcGVydGllcyB0byB1c2Ugd2hlbiByZW5kZXJpbmcsIGlmIGFueS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIuc3ByaXRlXG4gICAgICogQHR5cGUgb2JqZWN0XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBgU3RyaW5nYCB0aGF0IGRlZmluZXMgdGhlIHBhdGggdG8gdGhlIGltYWdlIHRvIHVzZSBhcyB0aGUgc3ByaXRlIHRleHR1cmUsIGlmIGFueS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIuc3ByaXRlLnRleHR1cmVcbiAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgKi9cbiAgICAgXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIHNjYWxpbmcgaW4gdGhlIHgtYXhpcyBmb3IgdGhlIHNwcml0ZSwgaWYgYW55LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHJlbmRlci5zcHJpdGUueFNjYWxlXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIHNjYWxpbmcgaW4gdGhlIHktYXhpcyBmb3IgdGhlIHNwcml0ZSwgaWYgYW55LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHJlbmRlci5zcHJpdGUueVNjYWxlXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMVxuICAgICAqL1xuXG4gICAgIC8qKlxuICAgICAgKiBBIGBOdW1iZXJgIHRoYXQgZGVmaW5lcyB0aGUgb2Zmc2V0IGluIHRoZSB4LWF4aXMgZm9yIHRoZSBzcHJpdGUgKG5vcm1hbGlzZWQgYnkgdGV4dHVyZSB3aWR0aCkuXG4gICAgICAqXG4gICAgICAqIEBwcm9wZXJ0eSByZW5kZXIuc3ByaXRlLnhPZmZzZXRcbiAgICAgICogQHR5cGUgbnVtYmVyXG4gICAgICAqIEBkZWZhdWx0IDBcbiAgICAgICovXG5cbiAgICAgLyoqXG4gICAgICAqIEEgYE51bWJlcmAgdGhhdCBkZWZpbmVzIHRoZSBvZmZzZXQgaW4gdGhlIHktYXhpcyBmb3IgdGhlIHNwcml0ZSAobm9ybWFsaXNlZCBieSB0ZXh0dXJlIGhlaWdodCkuXG4gICAgICAqXG4gICAgICAqIEBwcm9wZXJ0eSByZW5kZXIuc3ByaXRlLnlPZmZzZXRcbiAgICAgICogQHR5cGUgbnVtYmVyXG4gICAgICAqIEBkZWZhdWx0IDBcbiAgICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgZGVmaW5lcyB0aGUgbGluZSB3aWR0aCB0byB1c2Ugd2hlbiByZW5kZXJpbmcgdGhlIGJvZHkgb3V0bGluZSAoaWYgYSBzcHJpdGUgaXMgbm90IGRlZmluZWQpLlxuICAgICAqIEEgdmFsdWUgb2YgYDBgIG1lYW5zIG5vIG91dGxpbmUgd2lsbCBiZSByZW5kZXJlZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIubGluZVdpZHRoXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgU3RyaW5nYCB0aGF0IGRlZmluZXMgdGhlIGZpbGwgc3R5bGUgdG8gdXNlIHdoZW4gcmVuZGVyaW5nIHRoZSBib2R5IChpZiBhIHNwcml0ZSBpcyBub3QgZGVmaW5lZCkuXG4gICAgICogSXQgaXMgdGhlIHNhbWUgYXMgd2hlbiB1c2luZyBhIGNhbnZhcywgc28gaXQgYWNjZXB0cyBDU1Mgc3R5bGUgcHJvcGVydHkgdmFsdWVzLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHJlbmRlci5maWxsU3R5bGVcbiAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgKiBAZGVmYXVsdCBhIHJhbmRvbSBjb2xvdXJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYFN0cmluZ2AgdGhhdCBkZWZpbmVzIHRoZSBzdHJva2Ugc3R5bGUgdG8gdXNlIHdoZW4gcmVuZGVyaW5nIHRoZSBib2R5IG91dGxpbmUgKGlmIGEgc3ByaXRlIGlzIG5vdCBkZWZpbmVkKS5cbiAgICAgKiBJdCBpcyB0aGUgc2FtZSBhcyB3aGVuIHVzaW5nIGEgY2FudmFzLCBzbyBpdCBhY2NlcHRzIENTUyBzdHlsZSBwcm9wZXJ0eSB2YWx1ZXMuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcmVuZGVyLnN0cm9rZVN0eWxlXG4gICAgICogQHR5cGUgc3RyaW5nXG4gICAgICogQGRlZmF1bHQgYSByYW5kb20gY29sb3VyXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBhcnJheSBvZiB1bmlxdWUgYXhpcyB2ZWN0b3JzIChlZGdlIG5vcm1hbHMpIHVzZWQgZm9yIGNvbGxpc2lvbiBkZXRlY3Rpb24uXG4gICAgICogVGhlc2UgYXJlIGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlZCBmcm9tIHRoZSBnaXZlbiBjb252ZXggaHVsbCAoYHZlcnRpY2VzYCBhcnJheSkgaW4gYEJvZHkuY3JlYXRlYC5cbiAgICAgKiBUaGV5IGFyZSBjb25zdGFudGx5IHVwZGF0ZWQgYnkgYEJvZHkudXBkYXRlYCBkdXJpbmcgdGhlIHNpbXVsYXRpb24uXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgYXhlc1xuICAgICAqIEB0eXBlIHZlY3RvcltdXG4gICAgICovXG4gICAgIFxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBfbWVhc3VyZXNfIHRoZSBhcmVhIG9mIHRoZSBib2R5J3MgY29udmV4IGh1bGwsIGNhbGN1bGF0ZWQgYXQgY3JlYXRpb24gYnkgYEJvZHkuY3JlYXRlYC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBhcmVhXG4gICAgICogQHR5cGUgc3RyaW5nXG4gICAgICogQGRlZmF1bHQgXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBCb3VuZHNgIG9iamVjdCB0aGF0IGRlZmluZXMgdGhlIEFBQkIgcmVnaW9uIGZvciB0aGUgYm9keS5cbiAgICAgKiBJdCBpcyBhdXRvbWF0aWNhbGx5IGNhbGN1bGF0ZWQgZnJvbSB0aGUgZ2l2ZW4gY29udmV4IGh1bGwgKGB2ZXJ0aWNlc2AgYXJyYXkpIGluIGBCb2R5LmNyZWF0ZWAgYW5kIGNvbnN0YW50bHkgdXBkYXRlZCBieSBgQm9keS51cGRhdGVgIGR1cmluZyBzaW11bGF0aW9uLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGJvdW5kc1xuICAgICAqIEB0eXBlIGJvdW5kc1xuICAgICAqL1xuXG59KSgpO1xuXG59LHtcIi4uL2NvcmUvQ29tbW9uXCI6MTQsXCIuLi9jb3JlL1NsZWVwaW5nXCI6MjIsXCIuLi9nZW9tZXRyeS9BeGVzXCI6MjUsXCIuLi9nZW9tZXRyeS9Cb3VuZHNcIjoyNixcIi4uL2dlb21ldHJ5L1ZlY3RvclwiOjI4LFwiLi4vZ2VvbWV0cnkvVmVydGljZXNcIjoyOSxcIi4uL3JlbmRlci9SZW5kZXJcIjozMX1dLDI6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLkNvbXBvc2l0ZWAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGNyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgY29tcG9zaXRlIGJvZGllcy5cbiogQSBjb21wb3NpdGUgYm9keSBpcyBhIGNvbGxlY3Rpb24gb2YgYE1hdHRlci5Cb2R5YCwgYE1hdHRlci5Db25zdHJhaW50YCBhbmQgb3RoZXIgYE1hdHRlci5Db21wb3NpdGVgLCB0aGVyZWZvcmUgY29tcG9zaXRlcyBmb3JtIGEgdHJlZSBzdHJ1Y3R1cmUuXG4qIEl0IGlzIGltcG9ydGFudCB0byB1c2UgdGhlIGZ1bmN0aW9ucyBpbiB0aGlzIG1vZHVsZSB0byBtb2RpZnkgY29tcG9zaXRlcywgcmF0aGVyIHRoYW4gZGlyZWN0bHkgbW9kaWZ5aW5nIHRoZWlyIHByb3BlcnRpZXMuXG4qIE5vdGUgdGhhdCB0aGUgYE1hdHRlci5Xb3JsZGAgb2JqZWN0IGlzIGFsc28gYSB0eXBlIG9mIGBNYXR0ZXIuQ29tcG9zaXRlYCBhbmQgYXMgc3VjaCBhbGwgY29tcG9zaXRlIG1ldGhvZHMgaGVyZSBjYW4gYWxzbyBvcGVyYXRlIG9uIGEgYE1hdHRlci5Xb3JsZGAuXG4qXG4qIFNlZSB0aGUgaW5jbHVkZWQgdXNhZ2UgW2V4YW1wbGVzXShodHRwczovL2dpdGh1Yi5jb20vbGlhYnJ1L21hdHRlci1qcy90cmVlL21hc3Rlci9leGFtcGxlcykuXG4qXG4qIEBjbGFzcyBDb21wb3NpdGVcbiovXG5cbnZhciBDb21wb3NpdGUgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb21wb3NpdGU7XG5cbnZhciBFdmVudHMgPSBfZGVyZXFfKCcuLi9jb3JlL0V2ZW50cycpO1xudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4uL2NvcmUvQ29tbW9uJyk7XG52YXIgQm9keSA9IF9kZXJlcV8oJy4vQm9keScpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IGNvbXBvc2l0ZS4gVGhlIG9wdGlvbnMgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0IHNwZWNpZmllcyBhbnkgcHJvcGVydGllcyB5b3Ugd2lzaCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdHMuXG4gICAgICogU2VlIHRoZSBwcm9wZXJpdGVzIHNlY3Rpb24gYmVsb3cgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIG9uIHdoYXQgeW91IGNhbiBwYXNzIHZpYSB0aGUgYG9wdGlvbnNgIG9iamVjdC5cbiAgICAgKiBAbWV0aG9kIGNyZWF0ZVxuICAgICAqIEBwYXJhbSB7fSBbb3B0aW9uc11cbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IEEgbmV3IGNvbXBvc2l0ZVxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5jcmVhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgICAgIHJldHVybiBDb21tb24uZXh0ZW5kKHsgXG4gICAgICAgICAgICBpZDogQ29tbW9uLm5leHRJZCgpLFxuICAgICAgICAgICAgdHlwZTogJ2NvbXBvc2l0ZScsXG4gICAgICAgICAgICBwYXJlbnQ6IG51bGwsXG4gICAgICAgICAgICBpc01vZGlmaWVkOiBmYWxzZSxcbiAgICAgICAgICAgIGJvZGllczogW10sIFxuICAgICAgICAgICAgY29uc3RyYWludHM6IFtdLCBcbiAgICAgICAgICAgIGNvbXBvc2l0ZXM6IFtdLFxuICAgICAgICAgICAgbGFiZWw6ICdDb21wb3NpdGUnLFxuICAgICAgICAgICAgcGx1Z2luOiB7fVxuICAgICAgICB9LCBvcHRpb25zKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgY29tcG9zaXRlJ3MgYGlzTW9kaWZpZWRgIGZsYWcuIFxuICAgICAqIElmIGB1cGRhdGVQYXJlbnRzYCBpcyB0cnVlLCBhbGwgcGFyZW50cyB3aWxsIGJlIHNldCAoZGVmYXVsdDogZmFsc2UpLlxuICAgICAqIElmIGB1cGRhdGVDaGlsZHJlbmAgaXMgdHJ1ZSwgYWxsIGNoaWxkcmVuIHdpbGwgYmUgc2V0IChkZWZhdWx0OiBmYWxzZSkuXG4gICAgICogQG1ldGhvZCBzZXRNb2RpZmllZFxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGlzTW9kaWZpZWRcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFt1cGRhdGVQYXJlbnRzPWZhbHNlXVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3VwZGF0ZUNoaWxkcmVuPWZhbHNlXVxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgaXNNb2RpZmllZCwgdXBkYXRlUGFyZW50cywgdXBkYXRlQ2hpbGRyZW4pIHtcbiAgICAgICAgY29tcG9zaXRlLmlzTW9kaWZpZWQgPSBpc01vZGlmaWVkO1xuXG4gICAgICAgIGlmICh1cGRhdGVQYXJlbnRzICYmIGNvbXBvc2l0ZS5wYXJlbnQpIHtcbiAgICAgICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjb21wb3NpdGUucGFyZW50LCBpc01vZGlmaWVkLCB1cGRhdGVQYXJlbnRzLCB1cGRhdGVDaGlsZHJlbik7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodXBkYXRlQ2hpbGRyZW4pIHtcbiAgICAgICAgICAgIGZvcih2YXIgaSA9IDA7IGkgPCBjb21wb3NpdGUuY29tcG9zaXRlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBjaGlsZENvbXBvc2l0ZSA9IGNvbXBvc2l0ZS5jb21wb3NpdGVzW2ldO1xuICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjaGlsZENvbXBvc2l0ZSwgaXNNb2RpZmllZCwgdXBkYXRlUGFyZW50cywgdXBkYXRlQ2hpbGRyZW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdlbmVyaWMgYWRkIGZ1bmN0aW9uLiBBZGRzIG9uZSBvciBtYW55IGJvZHkocyksIGNvbnN0cmFpbnQocykgb3IgYSBjb21wb3NpdGUocykgdG8gdGhlIGdpdmVuIGNvbXBvc2l0ZS5cbiAgICAgKiBUcmlnZ2VycyBgYmVmb3JlQWRkYCBhbmQgYGFmdGVyQWRkYCBldmVudHMgb24gdGhlIGBjb21wb3NpdGVgLlxuICAgICAqIEBtZXRob2QgYWRkXG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZVxuICAgICAqIEBwYXJhbSB7fSBvYmplY3RcbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IFRoZSBvcmlnaW5hbCBjb21wb3NpdGUgd2l0aCB0aGUgb2JqZWN0cyBhZGRlZFxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5hZGQgPSBmdW5jdGlvbihjb21wb3NpdGUsIG9iamVjdCkge1xuICAgICAgICB2YXIgb2JqZWN0cyA9IFtdLmNvbmNhdChvYmplY3QpO1xuXG4gICAgICAgIEV2ZW50cy50cmlnZ2VyKGNvbXBvc2l0ZSwgJ2JlZm9yZUFkZCcsIHsgb2JqZWN0OiBvYmplY3QgfSk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvYmplY3RzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgb2JqID0gb2JqZWN0c1tpXTtcblxuICAgICAgICAgICAgc3dpdGNoIChvYmoudHlwZSkge1xuXG4gICAgICAgICAgICBjYXNlICdib2R5JzpcbiAgICAgICAgICAgICAgICAvLyBza2lwIGFkZGluZyBjb21wb3VuZCBwYXJ0c1xuICAgICAgICAgICAgICAgIGlmIChvYmoucGFyZW50ICE9PSBvYmopIHtcbiAgICAgICAgICAgICAgICAgICAgQ29tbW9uLndhcm4oJ0NvbXBvc2l0ZS5hZGQ6IHNraXBwZWQgYWRkaW5nIGEgY29tcG91bmQgYm9keSBwYXJ0ICh5b3UgbXVzdCBhZGQgaXRzIHBhcmVudCBpbnN0ZWFkKScpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBDb21wb3NpdGUuYWRkQm9keShjb21wb3NpdGUsIG9iaik7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdjb25zdHJhaW50JzpcbiAgICAgICAgICAgICAgICBDb21wb3NpdGUuYWRkQ29uc3RyYWludChjb21wb3NpdGUsIG9iaik7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdjb21wb3NpdGUnOlxuICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5hZGRDb21wb3NpdGUoY29tcG9zaXRlLCBvYmopO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnbW91c2VDb25zdHJhaW50JzpcbiAgICAgICAgICAgICAgICBDb21wb3NpdGUuYWRkQ29uc3RyYWludChjb21wb3NpdGUsIG9iai5jb25zdHJhaW50KTtcbiAgICAgICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgRXZlbnRzLnRyaWdnZXIoY29tcG9zaXRlLCAnYWZ0ZXJBZGQnLCB7IG9iamVjdDogb2JqZWN0IH0pO1xuXG4gICAgICAgIHJldHVybiBjb21wb3NpdGU7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdlbmVyaWMgcmVtb3ZlIGZ1bmN0aW9uLiBSZW1vdmVzIG9uZSBvciBtYW55IGJvZHkocyksIGNvbnN0cmFpbnQocykgb3IgYSBjb21wb3NpdGUocykgdG8gdGhlIGdpdmVuIGNvbXBvc2l0ZS5cbiAgICAgKiBPcHRpb25hbGx5IHNlYXJjaGluZyBpdHMgY2hpbGRyZW4gcmVjdXJzaXZlbHkuXG4gICAgICogVHJpZ2dlcnMgYGJlZm9yZVJlbW92ZWAgYW5kIGBhZnRlclJlbW92ZWAgZXZlbnRzIG9uIHRoZSBgY29tcG9zaXRlYC5cbiAgICAgKiBAbWV0aG9kIHJlbW92ZVxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge30gb2JqZWN0XG4gICAgICogQHBhcmFtIHtib29sZWFufSBbZGVlcD1mYWxzZV1cbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IFRoZSBvcmlnaW5hbCBjb21wb3NpdGUgd2l0aCB0aGUgb2JqZWN0cyByZW1vdmVkXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnJlbW92ZSA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgb2JqZWN0LCBkZWVwKSB7XG4gICAgICAgIHZhciBvYmplY3RzID0gW10uY29uY2F0KG9iamVjdCk7XG5cbiAgICAgICAgRXZlbnRzLnRyaWdnZXIoY29tcG9zaXRlLCAnYmVmb3JlUmVtb3ZlJywgeyBvYmplY3Q6IG9iamVjdCB9KTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9iamVjdHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBvYmogPSBvYmplY3RzW2ldO1xuXG4gICAgICAgICAgICBzd2l0Y2ggKG9iai50eXBlKSB7XG5cbiAgICAgICAgICAgIGNhc2UgJ2JvZHknOlxuICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5yZW1vdmVCb2R5KGNvbXBvc2l0ZSwgb2JqLCBkZWVwKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2NvbnN0cmFpbnQnOlxuICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5yZW1vdmVDb25zdHJhaW50KGNvbXBvc2l0ZSwgb2JqLCBkZWVwKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2NvbXBvc2l0ZSc6XG4gICAgICAgICAgICAgICAgQ29tcG9zaXRlLnJlbW92ZUNvbXBvc2l0ZShjb21wb3NpdGUsIG9iaiwgZGVlcCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdtb3VzZUNvbnN0cmFpbnQnOlxuICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5yZW1vdmVDb25zdHJhaW50KGNvbXBvc2l0ZSwgb2JqLmNvbnN0cmFpbnQpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBFdmVudHMudHJpZ2dlcihjb21wb3NpdGUsICdhZnRlclJlbW92ZScsIHsgb2JqZWN0OiBvYmplY3QgfSk7XG5cbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQWRkcyBhIGNvbXBvc2l0ZSB0byB0aGUgZ2l2ZW4gY29tcG9zaXRlLlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBhZGRDb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlQVxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVCXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBUaGUgb3JpZ2luYWwgY29tcG9zaXRlQSB3aXRoIHRoZSBvYmplY3RzIGZyb20gY29tcG9zaXRlQiBhZGRlZFxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5hZGRDb21wb3NpdGUgPSBmdW5jdGlvbihjb21wb3NpdGVBLCBjb21wb3NpdGVCKSB7XG4gICAgICAgIGNvbXBvc2l0ZUEuY29tcG9zaXRlcy5wdXNoKGNvbXBvc2l0ZUIpO1xuICAgICAgICBjb21wb3NpdGVCLnBhcmVudCA9IGNvbXBvc2l0ZUE7XG4gICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjb21wb3NpdGVBLCB0cnVlLCB0cnVlLCBmYWxzZSk7XG4gICAgICAgIHJldHVybiBjb21wb3NpdGVBO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgY29tcG9zaXRlIGZyb20gdGhlIGdpdmVuIGNvbXBvc2l0ZSwgYW5kIG9wdGlvbmFsbHkgc2VhcmNoaW5nIGl0cyBjaGlsZHJlbiByZWN1cnNpdmVseS5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgcmVtb3ZlQ29tcG9zaXRlXG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZUFcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlQlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2RlZXA9ZmFsc2VdXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBUaGUgb3JpZ2luYWwgY29tcG9zaXRlQSB3aXRoIHRoZSBjb21wb3NpdGUgcmVtb3ZlZFxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5yZW1vdmVDb21wb3NpdGUgPSBmdW5jdGlvbihjb21wb3NpdGVBLCBjb21wb3NpdGVCLCBkZWVwKSB7XG4gICAgICAgIHZhciBwb3NpdGlvbiA9IENvbW1vbi5pbmRleE9mKGNvbXBvc2l0ZUEuY29tcG9zaXRlcywgY29tcG9zaXRlQik7XG4gICAgICAgIGlmIChwb3NpdGlvbiAhPT0gLTEpIHtcbiAgICAgICAgICAgIENvbXBvc2l0ZS5yZW1vdmVDb21wb3NpdGVBdChjb21wb3NpdGVBLCBwb3NpdGlvbik7XG4gICAgICAgICAgICBDb21wb3NpdGUuc2V0TW9kaWZpZWQoY29tcG9zaXRlQSwgdHJ1ZSwgdHJ1ZSwgZmFsc2UpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGRlZXApIHtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY29tcG9zaXRlQS5jb21wb3NpdGVzLmxlbmd0aDsgaSsrKXtcbiAgICAgICAgICAgICAgICBDb21wb3NpdGUucmVtb3ZlQ29tcG9zaXRlKGNvbXBvc2l0ZUEuY29tcG9zaXRlc1tpXSwgY29tcG9zaXRlQiwgdHJ1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY29tcG9zaXRlQTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyBhIGNvbXBvc2l0ZSBmcm9tIHRoZSBnaXZlbiBjb21wb3NpdGUuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHJlbW92ZUNvbXBvc2l0ZUF0XG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBwb3NpdGlvblxuICAgICAqIEByZXR1cm4ge2NvbXBvc2l0ZX0gVGhlIG9yaWdpbmFsIGNvbXBvc2l0ZSB3aXRoIHRoZSBjb21wb3NpdGUgcmVtb3ZlZFxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5yZW1vdmVDb21wb3NpdGVBdCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgcG9zaXRpb24pIHtcbiAgICAgICAgY29tcG9zaXRlLmNvbXBvc2l0ZXMuc3BsaWNlKHBvc2l0aW9uLCAxKTtcbiAgICAgICAgQ29tcG9zaXRlLnNldE1vZGlmaWVkKGNvbXBvc2l0ZSwgdHJ1ZSwgdHJ1ZSwgZmFsc2UpO1xuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgYm9keSB0byB0aGUgZ2l2ZW4gY29tcG9zaXRlLlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBhZGRCb2R5XG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZVxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEByZXR1cm4ge2NvbXBvc2l0ZX0gVGhlIG9yaWdpbmFsIGNvbXBvc2l0ZSB3aXRoIHRoZSBib2R5IGFkZGVkXG4gICAgICovXG4gICAgQ29tcG9zaXRlLmFkZEJvZHkgPSBmdW5jdGlvbihjb21wb3NpdGUsIGJvZHkpIHtcbiAgICAgICAgY29tcG9zaXRlLmJvZGllcy5wdXNoKGJvZHkpO1xuICAgICAgICBDb21wb3NpdGUuc2V0TW9kaWZpZWQoY29tcG9zaXRlLCB0cnVlLCB0cnVlLCBmYWxzZSk7XG4gICAgICAgIHJldHVybiBjb21wb3NpdGU7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlbW92ZXMgYSBib2R5IGZyb20gdGhlIGdpdmVuIGNvbXBvc2l0ZSwgYW5kIG9wdGlvbmFsbHkgc2VhcmNoaW5nIGl0cyBjaGlsZHJlbiByZWN1cnNpdmVseS5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgcmVtb3ZlQm9keVxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtkZWVwPWZhbHNlXVxuICAgICAqIEByZXR1cm4ge2NvbXBvc2l0ZX0gVGhlIG9yaWdpbmFsIGNvbXBvc2l0ZSB3aXRoIHRoZSBib2R5IHJlbW92ZWRcbiAgICAgKi9cbiAgICBDb21wb3NpdGUucmVtb3ZlQm9keSA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgYm9keSwgZGVlcCkge1xuICAgICAgICB2YXIgcG9zaXRpb24gPSBDb21tb24uaW5kZXhPZihjb21wb3NpdGUuYm9kaWVzLCBib2R5KTtcbiAgICAgICAgaWYgKHBvc2l0aW9uICE9PSAtMSkge1xuICAgICAgICAgICAgQ29tcG9zaXRlLnJlbW92ZUJvZHlBdChjb21wb3NpdGUsIHBvc2l0aW9uKTtcbiAgICAgICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjb21wb3NpdGUsIHRydWUsIHRydWUsIGZhbHNlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChkZWVwKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNvbXBvc2l0ZS5jb21wb3NpdGVzLmxlbmd0aDsgaSsrKXtcbiAgICAgICAgICAgICAgICBDb21wb3NpdGUucmVtb3ZlQm9keShjb21wb3NpdGUuY29tcG9zaXRlc1tpXSwgYm9keSwgdHJ1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgYm9keSBmcm9tIHRoZSBnaXZlbiBjb21wb3NpdGUuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHJlbW92ZUJvZHlBdFxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcG9zaXRpb25cbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IFRoZSBvcmlnaW5hbCBjb21wb3NpdGUgd2l0aCB0aGUgYm9keSByZW1vdmVkXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnJlbW92ZUJvZHlBdCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgcG9zaXRpb24pIHtcbiAgICAgICAgY29tcG9zaXRlLmJvZGllcy5zcGxpY2UocG9zaXRpb24sIDEpO1xuICAgICAgICBDb21wb3NpdGUuc2V0TW9kaWZpZWQoY29tcG9zaXRlLCB0cnVlLCB0cnVlLCBmYWxzZSk7XG4gICAgICAgIHJldHVybiBjb21wb3NpdGU7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEFkZHMgYSBjb25zdHJhaW50IHRvIHRoZSBnaXZlbiBjb21wb3NpdGUuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIGFkZENvbnN0cmFpbnRcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHBhcmFtIHtjb25zdHJhaW50fSBjb25zdHJhaW50XG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBUaGUgb3JpZ2luYWwgY29tcG9zaXRlIHdpdGggdGhlIGNvbnN0cmFpbnQgYWRkZWRcbiAgICAgKi9cbiAgICBDb21wb3NpdGUuYWRkQ29uc3RyYWludCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgY29uc3RyYWludCkge1xuICAgICAgICBjb21wb3NpdGUuY29uc3RyYWludHMucHVzaChjb25zdHJhaW50KTtcbiAgICAgICAgQ29tcG9zaXRlLnNldE1vZGlmaWVkKGNvbXBvc2l0ZSwgdHJ1ZSwgdHJ1ZSwgZmFsc2UpO1xuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgY29uc3RyYWludCBmcm9tIHRoZSBnaXZlbiBjb21wb3NpdGUsIGFuZCBvcHRpb25hbGx5IHNlYXJjaGluZyBpdHMgY2hpbGRyZW4gcmVjdXJzaXZlbHkuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHJlbW92ZUNvbnN0cmFpbnRcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHBhcmFtIHtjb25zdHJhaW50fSBjb25zdHJhaW50XG4gICAgICogQHBhcmFtIHtib29sZWFufSBbZGVlcD1mYWxzZV1cbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IFRoZSBvcmlnaW5hbCBjb21wb3NpdGUgd2l0aCB0aGUgY29uc3RyYWludCByZW1vdmVkXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnJlbW92ZUNvbnN0cmFpbnQgPSBmdW5jdGlvbihjb21wb3NpdGUsIGNvbnN0cmFpbnQsIGRlZXApIHtcbiAgICAgICAgdmFyIHBvc2l0aW9uID0gQ29tbW9uLmluZGV4T2YoY29tcG9zaXRlLmNvbnN0cmFpbnRzLCBjb25zdHJhaW50KTtcbiAgICAgICAgaWYgKHBvc2l0aW9uICE9PSAtMSkge1xuICAgICAgICAgICAgQ29tcG9zaXRlLnJlbW92ZUNvbnN0cmFpbnRBdChjb21wb3NpdGUsIHBvc2l0aW9uKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChkZWVwKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNvbXBvc2l0ZS5jb21wb3NpdGVzLmxlbmd0aDsgaSsrKXtcbiAgICAgICAgICAgICAgICBDb21wb3NpdGUucmVtb3ZlQ29uc3RyYWludChjb21wb3NpdGUuY29tcG9zaXRlc1tpXSwgY29uc3RyYWludCwgdHJ1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgYm9keSBmcm9tIHRoZSBnaXZlbiBjb21wb3NpdGUuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHJlbW92ZUNvbnN0cmFpbnRBdFxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcG9zaXRpb25cbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IFRoZSBvcmlnaW5hbCBjb21wb3NpdGUgd2l0aCB0aGUgY29uc3RyYWludCByZW1vdmVkXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnJlbW92ZUNvbnN0cmFpbnRBdCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgcG9zaXRpb24pIHtcbiAgICAgICAgY29tcG9zaXRlLmNvbnN0cmFpbnRzLnNwbGljZShwb3NpdGlvbiwgMSk7XG4gICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjb21wb3NpdGUsIHRydWUsIHRydWUsIGZhbHNlKTtcbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyBhbGwgYm9kaWVzLCBjb25zdHJhaW50cyBhbmQgY29tcG9zaXRlcyBmcm9tIHRoZSBnaXZlbiBjb21wb3NpdGUuXG4gICAgICogT3B0aW9uYWxseSBjbGVhcmluZyBpdHMgY2hpbGRyZW4gcmVjdXJzaXZlbHkuXG4gICAgICogQG1ldGhvZCBjbGVhclxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGtlZXBTdGF0aWNcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtkZWVwPWZhbHNlXVxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5jbGVhciA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwga2VlcFN0YXRpYywgZGVlcCkge1xuICAgICAgICBpZiAoZGVlcCkge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb21wb3NpdGUuY29tcG9zaXRlcy5sZW5ndGg7IGkrKyl7XG4gICAgICAgICAgICAgICAgQ29tcG9zaXRlLmNsZWFyKGNvbXBvc2l0ZS5jb21wb3NpdGVzW2ldLCBrZWVwU3RhdGljLCB0cnVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKGtlZXBTdGF0aWMpIHtcbiAgICAgICAgICAgIGNvbXBvc2l0ZS5ib2RpZXMgPSBjb21wb3NpdGUuYm9kaWVzLmZpbHRlcihmdW5jdGlvbihib2R5KSB7IHJldHVybiBib2R5LmlzU3RhdGljOyB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbXBvc2l0ZS5ib2RpZXMubGVuZ3RoID0gMDtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbXBvc2l0ZS5jb25zdHJhaW50cy5sZW5ndGggPSAwO1xuICAgICAgICBjb21wb3NpdGUuY29tcG9zaXRlcy5sZW5ndGggPSAwO1xuICAgICAgICBDb21wb3NpdGUuc2V0TW9kaWZpZWQoY29tcG9zaXRlLCB0cnVlLCB0cnVlLCBmYWxzZSk7XG5cbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhbGwgYm9kaWVzIGluIHRoZSBnaXZlbiBjb21wb3NpdGUsIGluY2x1ZGluZyBhbGwgYm9kaWVzIGluIGl0cyBjaGlsZHJlbiwgcmVjdXJzaXZlbHkuXG4gICAgICogQG1ldGhvZCBhbGxCb2RpZXNcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHJldHVybiB7Ym9keVtdfSBBbGwgdGhlIGJvZGllc1xuICAgICAqL1xuICAgIENvbXBvc2l0ZS5hbGxCb2RpZXMgPSBmdW5jdGlvbihjb21wb3NpdGUpIHtcbiAgICAgICAgdmFyIGJvZGllcyA9IFtdLmNvbmNhdChjb21wb3NpdGUuYm9kaWVzKTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNvbXBvc2l0ZS5jb21wb3NpdGVzLmxlbmd0aDsgaSsrKVxuICAgICAgICAgICAgYm9kaWVzID0gYm9kaWVzLmNvbmNhdChDb21wb3NpdGUuYWxsQm9kaWVzKGNvbXBvc2l0ZS5jb21wb3NpdGVzW2ldKSk7XG5cbiAgICAgICAgcmV0dXJuIGJvZGllcztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhbGwgY29uc3RyYWludHMgaW4gdGhlIGdpdmVuIGNvbXBvc2l0ZSwgaW5jbHVkaW5nIGFsbCBjb25zdHJhaW50cyBpbiBpdHMgY2hpbGRyZW4sIHJlY3Vyc2l2ZWx5LlxuICAgICAqIEBtZXRob2QgYWxsQ29uc3RyYWludHNcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHJldHVybiB7Y29uc3RyYWludFtdfSBBbGwgdGhlIGNvbnN0cmFpbnRzXG4gICAgICovXG4gICAgQ29tcG9zaXRlLmFsbENvbnN0cmFpbnRzID0gZnVuY3Rpb24oY29tcG9zaXRlKSB7XG4gICAgICAgIHZhciBjb25zdHJhaW50cyA9IFtdLmNvbmNhdChjb21wb3NpdGUuY29uc3RyYWludHMpO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY29tcG9zaXRlLmNvbXBvc2l0ZXMubGVuZ3RoOyBpKyspXG4gICAgICAgICAgICBjb25zdHJhaW50cyA9IGNvbnN0cmFpbnRzLmNvbmNhdChDb21wb3NpdGUuYWxsQ29uc3RyYWludHMoY29tcG9zaXRlLmNvbXBvc2l0ZXNbaV0pKTtcblxuICAgICAgICByZXR1cm4gY29uc3RyYWludHM7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYWxsIGNvbXBvc2l0ZXMgaW4gdGhlIGdpdmVuIGNvbXBvc2l0ZSwgaW5jbHVkaW5nIGFsbCBjb21wb3NpdGVzIGluIGl0cyBjaGlsZHJlbiwgcmVjdXJzaXZlbHkuXG4gICAgICogQG1ldGhvZCBhbGxDb21wb3NpdGVzXG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZVxuICAgICAqIEByZXR1cm4ge2NvbXBvc2l0ZVtdfSBBbGwgdGhlIGNvbXBvc2l0ZXNcbiAgICAgKi9cbiAgICBDb21wb3NpdGUuYWxsQ29tcG9zaXRlcyA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSkge1xuICAgICAgICB2YXIgY29tcG9zaXRlcyA9IFtdLmNvbmNhdChjb21wb3NpdGUuY29tcG9zaXRlcyk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb21wb3NpdGUuY29tcG9zaXRlcy5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIGNvbXBvc2l0ZXMgPSBjb21wb3NpdGVzLmNvbmNhdChDb21wb3NpdGUuYWxsQ29tcG9zaXRlcyhjb21wb3NpdGUuY29tcG9zaXRlc1tpXSkpO1xuXG4gICAgICAgIHJldHVybiBjb21wb3NpdGVzO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZWFyY2hlcyB0aGUgY29tcG9zaXRlIHJlY3Vyc2l2ZWx5IGZvciBhbiBvYmplY3QgbWF0Y2hpbmcgdGhlIHR5cGUgYW5kIGlkIHN1cHBsaWVkLCBudWxsIGlmIG5vdCBmb3VuZC5cbiAgICAgKiBAbWV0aG9kIGdldFxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gaWRcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZVxuICAgICAqIEByZXR1cm4ge29iamVjdH0gVGhlIHJlcXVlc3RlZCBvYmplY3QsIGlmIGZvdW5kXG4gICAgICovXG4gICAgQ29tcG9zaXRlLmdldCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgaWQsIHR5cGUpIHtcbiAgICAgICAgdmFyIG9iamVjdHMsXG4gICAgICAgICAgICBvYmplY3Q7XG5cbiAgICAgICAgc3dpdGNoICh0eXBlKSB7XG4gICAgICAgIGNhc2UgJ2JvZHknOlxuICAgICAgICAgICAgb2JqZWN0cyA9IENvbXBvc2l0ZS5hbGxCb2RpZXMoY29tcG9zaXRlKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdjb25zdHJhaW50JzpcbiAgICAgICAgICAgIG9iamVjdHMgPSBDb21wb3NpdGUuYWxsQ29uc3RyYWludHMoY29tcG9zaXRlKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdjb21wb3NpdGUnOlxuICAgICAgICAgICAgb2JqZWN0cyA9IENvbXBvc2l0ZS5hbGxDb21wb3NpdGVzKGNvbXBvc2l0ZSkuY29uY2F0KGNvbXBvc2l0ZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghb2JqZWN0cylcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuXG4gICAgICAgIG9iamVjdCA9IG9iamVjdHMuZmlsdGVyKGZ1bmN0aW9uKG9iamVjdCkgeyBcbiAgICAgICAgICAgIHJldHVybiBvYmplY3QuaWQudG9TdHJpbmcoKSA9PT0gaWQudG9TdHJpbmcoKTsgXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBvYmplY3QubGVuZ3RoID09PSAwID8gbnVsbCA6IG9iamVjdFswXTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogTW92ZXMgdGhlIGdpdmVuIG9iamVjdChzKSBmcm9tIGNvbXBvc2l0ZUEgdG8gY29tcG9zaXRlQiAoZXF1YWwgdG8gYSByZW1vdmUgZm9sbG93ZWQgYnkgYW4gYWRkKS5cbiAgICAgKiBAbWV0aG9kIG1vdmVcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZUF9IGNvbXBvc2l0ZUFcbiAgICAgKiBAcGFyYW0ge29iamVjdFtdfSBvYmplY3RzXG4gICAgICogQHBhcmFtIHtjb21wb3NpdGVCfSBjb21wb3NpdGVCXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBSZXR1cm5zIGNvbXBvc2l0ZUFcbiAgICAgKi9cbiAgICBDb21wb3NpdGUubW92ZSA9IGZ1bmN0aW9uKGNvbXBvc2l0ZUEsIG9iamVjdHMsIGNvbXBvc2l0ZUIpIHtcbiAgICAgICAgQ29tcG9zaXRlLnJlbW92ZShjb21wb3NpdGVBLCBvYmplY3RzKTtcbiAgICAgICAgQ29tcG9zaXRlLmFkZChjb21wb3NpdGVCLCBvYmplY3RzKTtcbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZUE7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEFzc2lnbnMgbmV3IGlkcyBmb3IgYWxsIG9iamVjdHMgaW4gdGhlIGNvbXBvc2l0ZSwgcmVjdXJzaXZlbHkuXG4gICAgICogQG1ldGhvZCByZWJhc2VcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBSZXR1cm5zIGNvbXBvc2l0ZVxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5yZWJhc2UgPSBmdW5jdGlvbihjb21wb3NpdGUpIHtcbiAgICAgICAgdmFyIG9iamVjdHMgPSBDb21wb3NpdGUuYWxsQm9kaWVzKGNvbXBvc2l0ZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jb25jYXQoQ29tcG9zaXRlLmFsbENvbnN0cmFpbnRzKGNvbXBvc2l0ZSkpXG4gICAgICAgICAgICAgICAgICAgICAgICAuY29uY2F0KENvbXBvc2l0ZS5hbGxDb21wb3NpdGVzKGNvbXBvc2l0ZSkpO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb2JqZWN0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgb2JqZWN0c1tpXS5pZCA9IENvbW1vbi5uZXh0SWQoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjb21wb3NpdGUsIHRydWUsIHRydWUsIGZhbHNlKTtcblxuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBUcmFuc2xhdGVzIGFsbCBjaGlsZHJlbiBpbiB0aGUgY29tcG9zaXRlIGJ5IGEgZ2l2ZW4gdmVjdG9yIHJlbGF0aXZlIHRvIHRoZWlyIGN1cnJlbnQgcG9zaXRpb25zLCBcbiAgICAgKiB3aXRob3V0IGltcGFydGluZyBhbnkgdmVsb2NpdHkuXG4gICAgICogQG1ldGhvZCB0cmFuc2xhdGVcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHRyYW5zbGF0aW9uXG4gICAgICogQHBhcmFtIHtib29sfSBbcmVjdXJzaXZlPXRydWVdXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnRyYW5zbGF0ZSA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgdHJhbnNsYXRpb24sIHJlY3Vyc2l2ZSkge1xuICAgICAgICB2YXIgYm9kaWVzID0gcmVjdXJzaXZlID8gQ29tcG9zaXRlLmFsbEJvZGllcyhjb21wb3NpdGUpIDogY29tcG9zaXRlLmJvZGllcztcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgQm9keS50cmFuc2xhdGUoYm9kaWVzW2ldLCB0cmFuc2xhdGlvbik7XG4gICAgICAgIH1cblxuICAgICAgICBDb21wb3NpdGUuc2V0TW9kaWZpZWQoY29tcG9zaXRlLCB0cnVlLCB0cnVlLCBmYWxzZSk7XG5cbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUm90YXRlcyBhbGwgY2hpbGRyZW4gaW4gdGhlIGNvbXBvc2l0ZSBieSBhIGdpdmVuIGFuZ2xlIGFib3V0IHRoZSBnaXZlbiBwb2ludCwgd2l0aG91dCBpbXBhcnRpbmcgYW55IGFuZ3VsYXIgdmVsb2NpdHkuXG4gICAgICogQG1ldGhvZCByb3RhdGVcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHJvdGF0aW9uXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHBvaW50XG4gICAgICogQHBhcmFtIHtib29sfSBbcmVjdXJzaXZlPXRydWVdXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnJvdGF0ZSA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgcm90YXRpb24sIHBvaW50LCByZWN1cnNpdmUpIHtcbiAgICAgICAgdmFyIGNvcyA9IE1hdGguY29zKHJvdGF0aW9uKSxcbiAgICAgICAgICAgIHNpbiA9IE1hdGguc2luKHJvdGF0aW9uKSxcbiAgICAgICAgICAgIGJvZGllcyA9IHJlY3Vyc2l2ZSA/IENvbXBvc2l0ZS5hbGxCb2RpZXMoY29tcG9zaXRlKSA6IGNvbXBvc2l0ZS5ib2RpZXM7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5ID0gYm9kaWVzW2ldLFxuICAgICAgICAgICAgICAgIGR4ID0gYm9keS5wb3NpdGlvbi54IC0gcG9pbnQueCxcbiAgICAgICAgICAgICAgICBkeSA9IGJvZHkucG9zaXRpb24ueSAtIHBvaW50Lnk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICBCb2R5LnNldFBvc2l0aW9uKGJvZHksIHtcbiAgICAgICAgICAgICAgICB4OiBwb2ludC54ICsgKGR4ICogY29zIC0gZHkgKiBzaW4pLFxuICAgICAgICAgICAgICAgIHk6IHBvaW50LnkgKyAoZHggKiBzaW4gKyBkeSAqIGNvcylcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBCb2R5LnJvdGF0ZShib2R5LCByb3RhdGlvbik7XG4gICAgICAgIH1cblxuICAgICAgICBDb21wb3NpdGUuc2V0TW9kaWZpZWQoY29tcG9zaXRlLCB0cnVlLCB0cnVlLCBmYWxzZSk7XG5cbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2NhbGVzIGFsbCBjaGlsZHJlbiBpbiB0aGUgY29tcG9zaXRlLCBpbmNsdWRpbmcgdXBkYXRpbmcgcGh5c2ljYWwgcHJvcGVydGllcyAobWFzcywgYXJlYSwgYXhlcywgaW5lcnRpYSksIGZyb20gYSB3b3JsZC1zcGFjZSBwb2ludC5cbiAgICAgKiBAbWV0aG9kIHNjYWxlXG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBzY2FsZVhcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2NhbGVZXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHBvaW50XG4gICAgICogQHBhcmFtIHtib29sfSBbcmVjdXJzaXZlPXRydWVdXG4gICAgICovXG4gICAgQ29tcG9zaXRlLnNjYWxlID0gZnVuY3Rpb24oY29tcG9zaXRlLCBzY2FsZVgsIHNjYWxlWSwgcG9pbnQsIHJlY3Vyc2l2ZSkge1xuICAgICAgICB2YXIgYm9kaWVzID0gcmVjdXJzaXZlID8gQ29tcG9zaXRlLmFsbEJvZGllcyhjb21wb3NpdGUpIDogY29tcG9zaXRlLmJvZGllcztcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGJvZHkgPSBib2RpZXNbaV0sXG4gICAgICAgICAgICAgICAgZHggPSBib2R5LnBvc2l0aW9uLnggLSBwb2ludC54LFxuICAgICAgICAgICAgICAgIGR5ID0gYm9keS5wb3NpdGlvbi55IC0gcG9pbnQueTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgIEJvZHkuc2V0UG9zaXRpb24oYm9keSwge1xuICAgICAgICAgICAgICAgIHg6IHBvaW50LnggKyBkeCAqIHNjYWxlWCxcbiAgICAgICAgICAgICAgICB5OiBwb2ludC55ICsgZHkgKiBzY2FsZVlcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBCb2R5LnNjYWxlKGJvZHksIHNjYWxlWCwgc2NhbGVZKTtcbiAgICAgICAgfVxuXG4gICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZChjb21wb3NpdGUsIHRydWUsIHRydWUsIGZhbHNlKTtcblxuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSB1bmlvbiBvZiB0aGUgYm91bmRzIG9mIGFsbCBvZiB0aGUgY29tcG9zaXRlJ3MgYm9kaWVzLlxuICAgICAqIEBtZXRob2QgYm91bmRzXG4gICAgICogQHBhcmFtIHtjb21wb3NpdGV9IGNvbXBvc2l0ZSBUaGUgY29tcG9zaXRlLlxuICAgICAqIEByZXR1cm5zIHtib3VuZHN9IFRoZSBjb21wb3NpdGUgYm91bmRzLlxuICAgICAqL1xuICAgIENvbXBvc2l0ZS5ib3VuZHMgPSBmdW5jdGlvbihjb21wb3NpdGUpIHtcbiAgICAgICAgdmFyIGJvZGllcyA9IE1hdHRlci5Db21wb3NpdGUuYWxsQm9kaWVzKGNvbXBvc2l0ZSksXG4gICAgICAgICAgICB2ZXJ0aWNlcyA9IFtdO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXTtcbiAgICAgICAgICAgIHZlcnRpY2VzLnB1c2goYm9keS5ib3VuZHMubWluLCBib2R5LmJvdW5kcy5tYXgpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIE1hdHRlci5Cb3VuZHMuY3JlYXRlKHZlcnRpY2VzKTtcbiAgICB9O1xuXG4gICAgLypcbiAgICAqXG4gICAgKiAgRXZlbnRzIERvY3VtZW50YXRpb25cbiAgICAqXG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgd2hlbiBhIGNhbGwgdG8gYENvbXBvc2l0ZS5hZGRgIGlzIG1hZGUsIGJlZm9yZSBvYmplY3RzIGhhdmUgYmVlbiBhZGRlZC5cbiAgICAqXG4gICAgKiBAZXZlbnQgYmVmb3JlQWRkXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge30gZXZlbnQub2JqZWN0IFRoZSBvYmplY3QocykgdG8gYmUgYWRkZWQgKG1heSBiZSBhIHNpbmdsZSBib2R5LCBjb25zdHJhaW50LCBjb21wb3NpdGUgb3IgYSBtaXhlZCBhcnJheSBvZiB0aGVzZSlcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCB3aGVuIGEgY2FsbCB0byBgQ29tcG9zaXRlLmFkZGAgaXMgbWFkZSwgYWZ0ZXIgb2JqZWN0cyBoYXZlIGJlZW4gYWRkZWQuXG4gICAgKlxuICAgICogQGV2ZW50IGFmdGVyQWRkXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge30gZXZlbnQub2JqZWN0IFRoZSBvYmplY3QocykgdGhhdCBoYXZlIGJlZW4gYWRkZWQgKG1heSBiZSBhIHNpbmdsZSBib2R5LCBjb25zdHJhaW50LCBjb21wb3NpdGUgb3IgYSBtaXhlZCBhcnJheSBvZiB0aGVzZSlcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCB3aGVuIGEgY2FsbCB0byBgQ29tcG9zaXRlLnJlbW92ZWAgaXMgbWFkZSwgYmVmb3JlIG9iamVjdHMgaGF2ZSBiZWVuIHJlbW92ZWQuXG4gICAgKlxuICAgICogQGV2ZW50IGJlZm9yZVJlbW92ZVxuICAgICogQHBhcmFtIHt9IGV2ZW50IEFuIGV2ZW50IG9iamVjdFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm9iamVjdCBUaGUgb2JqZWN0KHMpIHRvIGJlIHJlbW92ZWQgKG1heSBiZSBhIHNpbmdsZSBib2R5LCBjb25zdHJhaW50LCBjb21wb3NpdGUgb3IgYSBtaXhlZCBhcnJheSBvZiB0aGVzZSlcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCB3aGVuIGEgY2FsbCB0byBgQ29tcG9zaXRlLnJlbW92ZWAgaXMgbWFkZSwgYWZ0ZXIgb2JqZWN0cyBoYXZlIGJlZW4gcmVtb3ZlZC5cbiAgICAqXG4gICAgKiBAZXZlbnQgYWZ0ZXJSZW1vdmVcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7fSBldmVudC5vYmplY3QgVGhlIG9iamVjdChzKSB0aGF0IGhhdmUgYmVlbiByZW1vdmVkIChtYXkgYmUgYSBzaW5nbGUgYm9keSwgY29uc3RyYWludCwgY29tcG9zaXRlIG9yIGEgbWl4ZWQgYXJyYXkgb2YgdGhlc2UpXG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qXG4gICAgKlxuICAgICogIFByb3BlcnRpZXMgRG9jdW1lbnRhdGlvblxuICAgICpcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gaW50ZWdlciBgTnVtYmVyYCB1bmlxdWVseSBpZGVudGlmeWluZyBudW1iZXIgZ2VuZXJhdGVkIGluIGBDb21wb3NpdGUuY3JlYXRlYCBieSBgQ29tbW9uLm5leHRJZGAuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgaWRcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYFN0cmluZ2AgZGVub3RpbmcgdGhlIHR5cGUgb2Ygb2JqZWN0LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHR5cGVcbiAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgKiBAZGVmYXVsdCBcImNvbXBvc2l0ZVwiXG4gICAgICogQHJlYWRPbmx5XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBhcmJpdHJhcnkgYFN0cmluZ2AgbmFtZSB0byBoZWxwIHRoZSB1c2VyIGlkZW50aWZ5IGFuZCBtYW5hZ2UgY29tcG9zaXRlcy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBsYWJlbFxuICAgICAqIEB0eXBlIHN0cmluZ1xuICAgICAqIEBkZWZhdWx0IFwiQ29tcG9zaXRlXCJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgZmxhZyB0aGF0IHNwZWNpZmllcyB3aGV0aGVyIHRoZSBjb21wb3NpdGUgaGFzIGJlZW4gbW9kaWZpZWQgZHVyaW5nIHRoZSBjdXJyZW50IHN0ZXAuXG4gICAgICogTW9zdCBgTWF0dGVyLkNvbXBvc2l0ZWAgbWV0aG9kcyB3aWxsIGF1dG9tYXRpY2FsbHkgc2V0IHRoaXMgZmxhZyB0byBgdHJ1ZWAgdG8gaW5mb3JtIHRoZSBlbmdpbmUgb2YgY2hhbmdlcyB0byBiZSBoYW5kbGVkLlxuICAgICAqIElmIHlvdSBuZWVkIHRvIGNoYW5nZSBpdCBtYW51YWxseSwgeW91IHNob3VsZCB1c2UgdGhlIGBDb21wb3NpdGUuc2V0TW9kaWZpZWRgIG1ldGhvZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBpc01vZGlmaWVkXG4gICAgICogQHR5cGUgYm9vbGVhblxuICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBUaGUgYENvbXBvc2l0ZWAgdGhhdCBpcyB0aGUgcGFyZW50IG9mIHRoaXMgY29tcG9zaXRlLiBJdCBpcyBhdXRvbWF0aWNhbGx5IG1hbmFnZWQgYnkgdGhlIGBNYXR0ZXIuQ29tcG9zaXRlYCBtZXRob2RzLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHBhcmVudFxuICAgICAqIEB0eXBlIGNvbXBvc2l0ZVxuICAgICAqIEBkZWZhdWx0IG51bGxcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGFycmF5IG9mIGBCb2R5YCB0aGF0IGFyZSBfZGlyZWN0XyBjaGlsZHJlbiBvZiB0aGlzIGNvbXBvc2l0ZS5cbiAgICAgKiBUbyBhZGQgb3IgcmVtb3ZlIGJvZGllcyB5b3Ugc2hvdWxkIHVzZSBgQ29tcG9zaXRlLmFkZGAgYW5kIGBDb21wb3NpdGUucmVtb3ZlYCBtZXRob2RzIHJhdGhlciB0aGFuIGRpcmVjdGx5IG1vZGlmeWluZyB0aGlzIHByb3BlcnR5LlxuICAgICAqIElmIHlvdSB3aXNoIHRvIHJlY3Vyc2l2ZWx5IGZpbmQgYWxsIGRlc2NlbmRhbnRzLCB5b3Ugc2hvdWxkIHVzZSB0aGUgYENvbXBvc2l0ZS5hbGxCb2RpZXNgIG1ldGhvZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBib2RpZXNcbiAgICAgKiBAdHlwZSBib2R5W11cbiAgICAgKiBAZGVmYXVsdCBbXVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gYXJyYXkgb2YgYENvbnN0cmFpbnRgIHRoYXQgYXJlIF9kaXJlY3RfIGNoaWxkcmVuIG9mIHRoaXMgY29tcG9zaXRlLlxuICAgICAqIFRvIGFkZCBvciByZW1vdmUgY29uc3RyYWludHMgeW91IHNob3VsZCB1c2UgYENvbXBvc2l0ZS5hZGRgIGFuZCBgQ29tcG9zaXRlLnJlbW92ZWAgbWV0aG9kcyByYXRoZXIgdGhhbiBkaXJlY3RseSBtb2RpZnlpbmcgdGhpcyBwcm9wZXJ0eS5cbiAgICAgKiBJZiB5b3Ugd2lzaCB0byByZWN1cnNpdmVseSBmaW5kIGFsbCBkZXNjZW5kYW50cywgeW91IHNob3VsZCB1c2UgdGhlIGBDb21wb3NpdGUuYWxsQ29uc3RyYWludHNgIG1ldGhvZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBjb25zdHJhaW50c1xuICAgICAqIEB0eXBlIGNvbnN0cmFpbnRbXVxuICAgICAqIEBkZWZhdWx0IFtdXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBhcnJheSBvZiBgQ29tcG9zaXRlYCB0aGF0IGFyZSBfZGlyZWN0XyBjaGlsZHJlbiBvZiB0aGlzIGNvbXBvc2l0ZS5cbiAgICAgKiBUbyBhZGQgb3IgcmVtb3ZlIGNvbXBvc2l0ZXMgeW91IHNob3VsZCB1c2UgYENvbXBvc2l0ZS5hZGRgIGFuZCBgQ29tcG9zaXRlLnJlbW92ZWAgbWV0aG9kcyByYXRoZXIgdGhhbiBkaXJlY3RseSBtb2RpZnlpbmcgdGhpcyBwcm9wZXJ0eS5cbiAgICAgKiBJZiB5b3Ugd2lzaCB0byByZWN1cnNpdmVseSBmaW5kIGFsbCBkZXNjZW5kYW50cywgeW91IHNob3VsZCB1c2UgdGhlIGBDb21wb3NpdGUuYWxsQ29tcG9zaXRlc2AgbWV0aG9kLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGNvbXBvc2l0ZXNcbiAgICAgKiBAdHlwZSBjb21wb3NpdGVbXVxuICAgICAqIEBkZWZhdWx0IFtdXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBvYmplY3QgcmVzZXJ2ZWQgZm9yIHN0b3JpbmcgcGx1Z2luLXNwZWNpZmljIHByb3BlcnRpZXMuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcGx1Z2luXG4gICAgICogQHR5cGUge31cbiAgICAgKi9cblxufSkoKTtcblxufSx7XCIuLi9jb3JlL0NvbW1vblwiOjE0LFwiLi4vY29yZS9FdmVudHNcIjoxNixcIi4vQm9keVwiOjF9XSwzOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5Xb3JsZGAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGNyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgdGhlIHdvcmxkIGNvbXBvc2l0ZS5cbiogQSBgTWF0dGVyLldvcmxkYCBpcyBhIGBNYXR0ZXIuQ29tcG9zaXRlYCBib2R5LCB3aGljaCBpcyBhIGNvbGxlY3Rpb24gb2YgYE1hdHRlci5Cb2R5YCwgYE1hdHRlci5Db25zdHJhaW50YCBhbmQgb3RoZXIgYE1hdHRlci5Db21wb3NpdGVgLlxuKiBBIGBNYXR0ZXIuV29ybGRgIGhhcyBhIGZldyBhZGRpdGlvbmFsIHByb3BlcnRpZXMgaW5jbHVkaW5nIGBncmF2aXR5YCBhbmQgYGJvdW5kc2AuXG4qIEl0IGlzIGltcG9ydGFudCB0byB1c2UgdGhlIGZ1bmN0aW9ucyBpbiB0aGUgYE1hdHRlci5Db21wb3NpdGVgIG1vZHVsZSB0byBtb2RpZnkgdGhlIHdvcmxkIGNvbXBvc2l0ZSwgcmF0aGVyIHRoYW4gZGlyZWN0bHkgbW9kaWZ5aW5nIGl0cyBwcm9wZXJ0aWVzLlxuKiBUaGVyZSBhcmUgYWxzbyBhIGZldyBtZXRob2RzIGhlcmUgdGhhdCBhbGlhcyB0aG9zZSBpbiBgTWF0dGVyLkNvbXBvc2l0ZWAgZm9yIGVhc2llciByZWFkYWJpbGl0eS5cbipcbiogU2VlIHRoZSBpbmNsdWRlZCB1c2FnZSBbZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9saWFicnUvbWF0dGVyLWpzL3RyZWUvbWFzdGVyL2V4YW1wbGVzKS5cbipcbiogQGNsYXNzIFdvcmxkXG4qIEBleHRlbmRzIENvbXBvc2l0ZVxuKi9cblxudmFyIFdvcmxkID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gV29ybGQ7XG5cbnZhciBDb21wb3NpdGUgPSBfZGVyZXFfKCcuL0NvbXBvc2l0ZScpO1xudmFyIENvbnN0cmFpbnQgPSBfZGVyZXFfKCcuLi9jb25zdHJhaW50L0NvbnN0cmFpbnQnKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHdvcmxkIGNvbXBvc2l0ZS4gVGhlIG9wdGlvbnMgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0IHNwZWNpZmllcyBhbnkgcHJvcGVydGllcyB5b3Ugd2lzaCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdHMuXG4gICAgICogU2VlIHRoZSBwcm9wZXJ0aWVzIHNlY3Rpb24gYmVsb3cgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIG9uIHdoYXQgeW91IGNhbiBwYXNzIHZpYSB0aGUgYG9wdGlvbnNgIG9iamVjdC5cbiAgICAgKiBAbWV0aG9kIGNyZWF0ZVxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqIEBwYXJhbSB7fSBvcHRpb25zXG4gICAgICogQHJldHVybiB7d29ybGR9IEEgbmV3IHdvcmxkXG4gICAgICovXG4gICAgV29ybGQuY3JlYXRlID0gZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgICB2YXIgY29tcG9zaXRlID0gQ29tcG9zaXRlLmNyZWF0ZSgpO1xuXG4gICAgICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgICAgIGxhYmVsOiAnV29ybGQnLFxuICAgICAgICAgICAgZ3Jhdml0eToge1xuICAgICAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICAgICAgeTogMSxcbiAgICAgICAgICAgICAgICBzY2FsZTogMC4wMDFcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBib3VuZHM6IHsgXG4gICAgICAgICAgICAgICAgbWluOiB7IHg6IC1JbmZpbml0eSwgeTogLUluZmluaXR5IH0sIFxuICAgICAgICAgICAgICAgIG1heDogeyB4OiBJbmZpbml0eSwgeTogSW5maW5pdHkgfSBcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiBDb21tb24uZXh0ZW5kKGNvbXBvc2l0ZSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xuICAgIH07XG5cbiAgICAvKlxuICAgICpcbiAgICAqICBQcm9wZXJ0aWVzIERvY3VtZW50YXRpb25cbiAgICAqXG4gICAgKi9cblxuICAgIC8qKlxuICAgICAqIFRoZSBncmF2aXR5IHRvIGFwcGx5IG9uIHRoZSB3b3JsZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBncmF2aXR5XG4gICAgICogQHR5cGUgb2JqZWN0XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBUaGUgZ3Jhdml0eSB4IGNvbXBvbmVudC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBncmF2aXR5LnhcbiAgICAgKiBAdHlwZSBvYmplY3RcbiAgICAgKiBAZGVmYXVsdCAwXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBUaGUgZ3Jhdml0eSB5IGNvbXBvbmVudC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBncmF2aXR5LnlcbiAgICAgKiBAdHlwZSBvYmplY3RcbiAgICAgKiBAZGVmYXVsdCAxXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBUaGUgZ3Jhdml0eSBzY2FsZSBmYWN0b3IuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgZ3Jhdml0eS5zY2FsZVxuICAgICAqIEB0eXBlIG9iamVjdFxuICAgICAqIEBkZWZhdWx0IDAuMDAxXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBCb3VuZHNgIG9iamVjdCB0aGF0IGRlZmluZXMgdGhlIHdvcmxkIGJvdW5kcyBmb3IgY29sbGlzaW9uIGRldGVjdGlvbi5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBib3VuZHNcbiAgICAgKiBAdHlwZSBib3VuZHNcbiAgICAgKiBAZGVmYXVsdCB7IG1pbjogeyB4OiAtSW5maW5pdHksIHk6IC1JbmZpbml0eSB9LCBtYXg6IHsgeDogSW5maW5pdHksIHk6IEluZmluaXR5IH0gfVxuICAgICAqL1xuXG4gICAgLy8gV29ybGQgaXMgYSBDb21wb3NpdGUgYm9keVxuICAgIC8vIHNlZSBzcmMvbW9kdWxlL091dHJvLmpzIGZvciB0aGVzZSBhbGlhc2VzOlxuICAgIFxuICAgIC8qKlxuICAgICAqIEFuIGFsaWFzIGZvciBDb21wb3NpdGUuY2xlYXJcbiAgICAgKiBAbWV0aG9kIGNsZWFyXG4gICAgICogQHBhcmFtIHt3b3JsZH0gd29ybGRcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGtlZXBTdGF0aWNcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGFsaWFzIGZvciBDb21wb3NpdGUuYWRkXG4gICAgICogQG1ldGhvZCBhZGRDb21wb3NpdGVcbiAgICAgKiBAcGFyYW0ge3dvcmxkfSB3b3JsZFxuICAgICAqIEBwYXJhbSB7Y29tcG9zaXRlfSBjb21wb3NpdGVcbiAgICAgKiBAcmV0dXJuIHt3b3JsZH0gVGhlIG9yaWdpbmFsIHdvcmxkIHdpdGggdGhlIG9iamVjdHMgZnJvbSBjb21wb3NpdGUgYWRkZWRcbiAgICAgKi9cbiAgICBcbiAgICAgLyoqXG4gICAgICAqIEFuIGFsaWFzIGZvciBDb21wb3NpdGUuYWRkQm9keVxuICAgICAgKiBAbWV0aG9kIGFkZEJvZHlcbiAgICAgICogQHBhcmFtIHt3b3JsZH0gd29ybGRcbiAgICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICAqIEByZXR1cm4ge3dvcmxkfSBUaGUgb3JpZ2luYWwgd29ybGQgd2l0aCB0aGUgYm9keSBhZGRlZFxuICAgICAgKi9cblxuICAgICAvKipcbiAgICAgICogQW4gYWxpYXMgZm9yIENvbXBvc2l0ZS5hZGRDb25zdHJhaW50XG4gICAgICAqIEBtZXRob2QgYWRkQ29uc3RyYWludFxuICAgICAgKiBAcGFyYW0ge3dvcmxkfSB3b3JsZFxuICAgICAgKiBAcGFyYW0ge2NvbnN0cmFpbnR9IGNvbnN0cmFpbnRcbiAgICAgICogQHJldHVybiB7d29ybGR9IFRoZSBvcmlnaW5hbCB3b3JsZCB3aXRoIHRoZSBjb25zdHJhaW50IGFkZGVkXG4gICAgICAqL1xuXG59KSgpO1xuXG59LHtcIi4uL2NvbnN0cmFpbnQvQ29uc3RyYWludFwiOjEyLFwiLi4vY29yZS9Db21tb25cIjoxNCxcIi4vQ29tcG9zaXRlXCI6Mn1dLDQ6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLkNvbnRhY3RgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIGZvciBjcmVhdGluZyBhbmQgbWFuaXB1bGF0aW5nIGNvbGxpc2lvbiBjb250YWN0cy5cbipcbiogQGNsYXNzIENvbnRhY3RcbiovXG5cbnZhciBDb250YWN0ID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gQ29udGFjdDtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBjb250YWN0LlxuICAgICAqIEBtZXRob2QgY3JlYXRlXG4gICAgICogQHBhcmFtIHt2ZXJ0ZXh9IHZlcnRleFxuICAgICAqIEByZXR1cm4ge2NvbnRhY3R9IEEgbmV3IGNvbnRhY3RcbiAgICAgKi9cbiAgICBDb250YWN0LmNyZWF0ZSA9IGZ1bmN0aW9uKHZlcnRleCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgaWQ6IENvbnRhY3QuaWQodmVydGV4KSxcbiAgICAgICAgICAgIHZlcnRleDogdmVydGV4LFxuICAgICAgICAgICAgbm9ybWFsSW1wdWxzZTogMCxcbiAgICAgICAgICAgIHRhbmdlbnRJbXB1bHNlOiAwXG4gICAgICAgIH07XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBHZW5lcmF0ZXMgYSBjb250YWN0IGlkLlxuICAgICAqIEBtZXRob2QgaWRcbiAgICAgKiBAcGFyYW0ge3ZlcnRleH0gdmVydGV4XG4gICAgICogQHJldHVybiB7c3RyaW5nfSBVbmlxdWUgY29udGFjdElEXG4gICAgICovXG4gICAgQ29udGFjdC5pZCA9IGZ1bmN0aW9uKHZlcnRleCkge1xuICAgICAgICByZXR1cm4gdmVydGV4LmJvZHkuaWQgKyAnXycgKyB2ZXJ0ZXguaW5kZXg7XG4gICAgfTtcblxufSkoKTtcblxufSx7fV0sNTpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuRGV0ZWN0b3JgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIGZvciBkZXRlY3RpbmcgY29sbGlzaW9ucyBnaXZlbiBhIHNldCBvZiBwYWlycy5cbipcbiogQGNsYXNzIERldGVjdG9yXG4qL1xuXG4vLyBUT0RPOiBzcGVjdWxhdGl2ZSBjb250YWN0c1xuXG52YXIgRGV0ZWN0b3IgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBEZXRlY3RvcjtcblxudmFyIFNBVCA9IF9kZXJlcV8oJy4vU0FUJyk7XG52YXIgUGFpciA9IF9kZXJlcV8oJy4vUGFpcicpO1xudmFyIEJvdW5kcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L0JvdW5kcycpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBGaW5kcyBhbGwgY29sbGlzaW9ucyBnaXZlbiBhIGxpc3Qgb2YgcGFpcnMuXG4gICAgICogQG1ldGhvZCBjb2xsaXNpb25zXG4gICAgICogQHBhcmFtIHtwYWlyW119IGJyb2FkcGhhc2VQYWlyc1xuICAgICAqIEBwYXJhbSB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKiBAcmV0dXJuIHthcnJheX0gY29sbGlzaW9uc1xuICAgICAqL1xuICAgIERldGVjdG9yLmNvbGxpc2lvbnMgPSBmdW5jdGlvbihicm9hZHBoYXNlUGFpcnMsIGVuZ2luZSkge1xuICAgICAgICB2YXIgY29sbGlzaW9ucyA9IFtdLFxuICAgICAgICAgICAgcGFpcnNUYWJsZSA9IGVuZ2luZS5wYWlycy50YWJsZTtcblxuICAgICAgICBcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBicm9hZHBoYXNlUGFpcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5QSA9IGJyb2FkcGhhc2VQYWlyc1tpXVswXSwgXG4gICAgICAgICAgICAgICAgYm9keUIgPSBicm9hZHBoYXNlUGFpcnNbaV1bMV07XG5cbiAgICAgICAgICAgIGlmICgoYm9keUEuaXNTdGF0aWMgfHwgYm9keUEuaXNTbGVlcGluZykgJiYgKGJvZHlCLmlzU3RhdGljIHx8IGJvZHlCLmlzU2xlZXBpbmcpKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoIURldGVjdG9yLmNhbkNvbGxpZGUoYm9keUEuY29sbGlzaW9uRmlsdGVyLCBib2R5Qi5jb2xsaXNpb25GaWx0ZXIpKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG5cbiAgICAgICAgICAgIC8vIG1pZCBwaGFzZVxuICAgICAgICAgICAgaWYgKEJvdW5kcy5vdmVybGFwcyhib2R5QS5ib3VuZHMsIGJvZHlCLmJvdW5kcykpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gYm9keUEucGFydHMubGVuZ3RoID4gMSA/IDEgOiAwOyBqIDwgYm9keUEucGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcnRBID0gYm9keUEucGFydHNbal07XG5cbiAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgayA9IGJvZHlCLnBhcnRzLmxlbmd0aCA+IDEgPyAxIDogMDsgayA8IGJvZHlCLnBhcnRzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcGFydEIgPSBib2R5Qi5wYXJ0c1trXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKChwYXJ0QSA9PT0gYm9keUEgJiYgcGFydEIgPT09IGJvZHlCKSB8fCBCb3VuZHMub3ZlcmxhcHMocGFydEEuYm91bmRzLCBwYXJ0Qi5ib3VuZHMpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZmluZCBhIHByZXZpb3VzIGNvbGxpc2lvbiB3ZSBjb3VsZCByZXVzZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBwYWlySWQgPSBQYWlyLmlkKHBhcnRBLCBwYXJ0QiksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhaXIgPSBwYWlyc1RhYmxlW3BhaXJJZF0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZpb3VzQ29sbGlzaW9uO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhaXIgJiYgcGFpci5pc0FjdGl2ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2aW91c0NvbGxpc2lvbiA9IHBhaXIuY29sbGlzaW9uO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZpb3VzQ29sbGlzaW9uID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBuYXJyb3cgcGhhc2VcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgY29sbGlzaW9uID0gU0FULmNvbGxpZGVzKHBhcnRBLCBwYXJ0QiwgcHJldmlvdXNDb2xsaXNpb24pO1xuXG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY29sbGlzaW9uLmNvbGxpZGVkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxpc2lvbnMucHVzaChjb2xsaXNpb24pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBjb2xsaXNpb25zO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGB0cnVlYCBpZiBib3RoIHN1cHBsaWVkIGNvbGxpc2lvbiBmaWx0ZXJzIHdpbGwgYWxsb3cgYSBjb2xsaXNpb24gdG8gb2NjdXIuXG4gICAgICogU2VlIGBib2R5LmNvbGxpc2lvbkZpbHRlcmAgZm9yIG1vcmUgaW5mb3JtYXRpb24uXG4gICAgICogQG1ldGhvZCBjYW5Db2xsaWRlXG4gICAgICogQHBhcmFtIHt9IGZpbHRlckFcbiAgICAgKiBAcGFyYW0ge30gZmlsdGVyQlxuICAgICAqIEByZXR1cm4ge2Jvb2x9IGB0cnVlYCBpZiBjb2xsaXNpb24gY2FuIG9jY3VyXG4gICAgICovXG4gICAgRGV0ZWN0b3IuY2FuQ29sbGlkZSA9IGZ1bmN0aW9uKGZpbHRlckEsIGZpbHRlckIpIHtcbiAgICAgICAgaWYgKGZpbHRlckEuZ3JvdXAgPT09IGZpbHRlckIuZ3JvdXAgJiYgZmlsdGVyQS5ncm91cCAhPT0gMClcbiAgICAgICAgICAgIHJldHVybiBmaWx0ZXJBLmdyb3VwID4gMDtcblxuICAgICAgICByZXR1cm4gKGZpbHRlckEubWFzayAmIGZpbHRlckIuY2F0ZWdvcnkpICE9PSAwICYmIChmaWx0ZXJCLm1hc2sgJiBmaWx0ZXJBLmNhdGVnb3J5KSAhPT0gMDtcbiAgICB9O1xuXG59KSgpO1xuXG59LHtcIi4uL2dlb21ldHJ5L0JvdW5kc1wiOjI2LFwiLi9QYWlyXCI6NyxcIi4vU0FUXCI6MTF9XSw2OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5HcmlkYCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyBmb3IgY3JlYXRpbmcgYW5kIG1hbmlwdWxhdGluZyBjb2xsaXNpb24gYnJvYWRwaGFzZSBncmlkIHN0cnVjdHVyZXMuXG4qXG4qIEBjbGFzcyBHcmlkXG4qL1xuXG52YXIgR3JpZCA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEdyaWQ7XG5cbnZhciBQYWlyID0gX2RlcmVxXygnLi9QYWlyJyk7XG52YXIgRGV0ZWN0b3IgPSBfZGVyZXFfKCcuL0RldGVjdG9yJyk7XG52YXIgQ29tbW9uID0gX2RlcmVxXygnLi4vY29yZS9Db21tb24nKTtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBncmlkLlxuICAgICAqIEBtZXRob2QgY3JlYXRlXG4gICAgICogQHBhcmFtIHt9IG9wdGlvbnNcbiAgICAgKiBAcmV0dXJuIHtncmlkfSBBIG5ldyBncmlkXG4gICAgICovXG4gICAgR3JpZC5jcmVhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgICAgIGNvbnRyb2xsZXI6IEdyaWQsXG4gICAgICAgICAgICBkZXRlY3RvcjogRGV0ZWN0b3IuY29sbGlzaW9ucyxcbiAgICAgICAgICAgIGJ1Y2tldHM6IHt9LFxuICAgICAgICAgICAgcGFpcnM6IHt9LFxuICAgICAgICAgICAgcGFpcnNMaXN0OiBbXSxcbiAgICAgICAgICAgIGJ1Y2tldFdpZHRoOiA0OCxcbiAgICAgICAgICAgIGJ1Y2tldEhlaWdodDogNDhcbiAgICAgICAgfTtcblxuICAgICAgICByZXR1cm4gQ29tbW9uLmV4dGVuZChkZWZhdWx0cywgb3B0aW9ucyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFRoZSB3aWR0aCBvZiBhIHNpbmdsZSBncmlkIGJ1Y2tldC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBidWNrZXRXaWR0aFxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDQ4XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBUaGUgaGVpZ2h0IG9mIGEgc2luZ2xlIGdyaWQgYnVja2V0LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGJ1Y2tldEhlaWdodFxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDQ4XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBncmlkLlxuICAgICAqIEBtZXRob2QgdXBkYXRlXG4gICAgICogQHBhcmFtIHtncmlkfSBncmlkXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGZvcmNlVXBkYXRlXG4gICAgICovXG4gICAgR3JpZC51cGRhdGUgPSBmdW5jdGlvbihncmlkLCBib2RpZXMsIGVuZ2luZSwgZm9yY2VVcGRhdGUpIHtcbiAgICAgICAgdmFyIGksIGNvbCwgcm93LFxuICAgICAgICAgICAgd29ybGQgPSBlbmdpbmUud29ybGQsXG4gICAgICAgICAgICBidWNrZXRzID0gZ3JpZC5idWNrZXRzLFxuICAgICAgICAgICAgYnVja2V0LFxuICAgICAgICAgICAgYnVja2V0SWQsXG4gICAgICAgICAgICBncmlkQ2hhbmdlZCA9IGZhbHNlO1xuXG5cbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGJvZHkgPSBib2RpZXNbaV07XG5cbiAgICAgICAgICAgIGlmIChib2R5LmlzU2xlZXBpbmcgJiYgIWZvcmNlVXBkYXRlKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAvLyBkb24ndCB1cGRhdGUgb3V0IG9mIHdvcmxkIGJvZGllc1xuICAgICAgICAgICAgaWYgKGJvZHkuYm91bmRzLm1heC54IDwgd29ybGQuYm91bmRzLm1pbi54IHx8IGJvZHkuYm91bmRzLm1pbi54ID4gd29ybGQuYm91bmRzLm1heC54XG4gICAgICAgICAgICAgICAgfHwgYm9keS5ib3VuZHMubWF4LnkgPCB3b3JsZC5ib3VuZHMubWluLnkgfHwgYm9keS5ib3VuZHMubWluLnkgPiB3b3JsZC5ib3VuZHMubWF4LnkpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIHZhciBuZXdSZWdpb24gPSBfZ2V0UmVnaW9uKGdyaWQsIGJvZHkpO1xuXG4gICAgICAgICAgICAvLyBpZiB0aGUgYm9keSBoYXMgY2hhbmdlZCBncmlkIHJlZ2lvblxuICAgICAgICAgICAgaWYgKCFib2R5LnJlZ2lvbiB8fCBuZXdSZWdpb24uaWQgIT09IGJvZHkucmVnaW9uLmlkIHx8IGZvcmNlVXBkYXRlKSB7XG5cblxuICAgICAgICAgICAgICAgIGlmICghYm9keS5yZWdpb24gfHwgZm9yY2VVcGRhdGUpXG4gICAgICAgICAgICAgICAgICAgIGJvZHkucmVnaW9uID0gbmV3UmVnaW9uO1xuXG4gICAgICAgICAgICAgICAgdmFyIHVuaW9uID0gX3JlZ2lvblVuaW9uKG5ld1JlZ2lvbiwgYm9keS5yZWdpb24pO1xuXG4gICAgICAgICAgICAgICAgLy8gdXBkYXRlIGdyaWQgYnVja2V0cyBhZmZlY3RlZCBieSByZWdpb24gY2hhbmdlXG4gICAgICAgICAgICAgICAgLy8gaXRlcmF0ZSBvdmVyIHRoZSB1bmlvbiBvZiBib3RoIHJlZ2lvbnNcbiAgICAgICAgICAgICAgICBmb3IgKGNvbCA9IHVuaW9uLnN0YXJ0Q29sOyBjb2wgPD0gdW5pb24uZW5kQ29sOyBjb2wrKykge1xuICAgICAgICAgICAgICAgICAgICBmb3IgKHJvdyA9IHVuaW9uLnN0YXJ0Um93OyByb3cgPD0gdW5pb24uZW5kUm93OyByb3crKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgYnVja2V0SWQgPSBfZ2V0QnVja2V0SWQoY29sLCByb3cpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnVja2V0ID0gYnVja2V0c1tidWNrZXRJZF07XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpc0luc2lkZU5ld1JlZ2lvbiA9IChjb2wgPj0gbmV3UmVnaW9uLnN0YXJ0Q29sICYmIGNvbCA8PSBuZXdSZWdpb24uZW5kQ29sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmJiByb3cgPj0gbmV3UmVnaW9uLnN0YXJ0Um93ICYmIHJvdyA8PSBuZXdSZWdpb24uZW5kUm93KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGlzSW5zaWRlT2xkUmVnaW9uID0gKGNvbCA+PSBib2R5LnJlZ2lvbi5zdGFydENvbCAmJiBjb2wgPD0gYm9keS5yZWdpb24uZW5kQ29sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmJiByb3cgPj0gYm9keS5yZWdpb24uc3RhcnRSb3cgJiYgcm93IDw9IGJvZHkucmVnaW9uLmVuZFJvdyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHJlbW92ZSBmcm9tIG9sZCByZWdpb24gYnVja2V0c1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpc0luc2lkZU5ld1JlZ2lvbiAmJiBpc0luc2lkZU9sZFJlZ2lvbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc0luc2lkZU9sZFJlZ2lvbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoYnVja2V0KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgX2J1Y2tldFJlbW92ZUJvZHkoZ3JpZCwgYnVja2V0LCBib2R5KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFkZCB0byBuZXcgcmVnaW9uIGJ1Y2tldHNcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChib2R5LnJlZ2lvbiA9PT0gbmV3UmVnaW9uIHx8IChpc0luc2lkZU5ld1JlZ2lvbiAmJiAhaXNJbnNpZGVPbGRSZWdpb24pIHx8IGZvcmNlVXBkYXRlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFidWNrZXQpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ1Y2tldCA9IF9jcmVhdGVCdWNrZXQoYnVja2V0cywgYnVja2V0SWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIF9idWNrZXRBZGRCb2R5KGdyaWQsIGJ1Y2tldCwgYm9keSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBzZXQgdGhlIG5ldyByZWdpb25cbiAgICAgICAgICAgICAgICBib2R5LnJlZ2lvbiA9IG5ld1JlZ2lvbjtcblxuICAgICAgICAgICAgICAgIC8vIGZsYWcgY2hhbmdlcyBzbyB3ZSBjYW4gdXBkYXRlIHBhaXJzXG4gICAgICAgICAgICAgICAgZ3JpZENoYW5nZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gdXBkYXRlIHBhaXJzIGxpc3Qgb25seSBpZiBwYWlycyBjaGFuZ2VkIChpLmUuIGEgYm9keSBjaGFuZ2VkIHJlZ2lvbilcbiAgICAgICAgaWYgKGdyaWRDaGFuZ2VkKVxuICAgICAgICAgICAgZ3JpZC5wYWlyc0xpc3QgPSBfY3JlYXRlQWN0aXZlUGFpcnNMaXN0KGdyaWQpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDbGVhcnMgdGhlIGdyaWQuXG4gICAgICogQG1ldGhvZCBjbGVhclxuICAgICAqIEBwYXJhbSB7Z3JpZH0gZ3JpZFxuICAgICAqL1xuICAgIEdyaWQuY2xlYXIgPSBmdW5jdGlvbihncmlkKSB7XG4gICAgICAgIGdyaWQuYnVja2V0cyA9IHt9O1xuICAgICAgICBncmlkLnBhaXJzID0ge307XG4gICAgICAgIGdyaWQucGFpcnNMaXN0ID0gW107XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEZpbmRzIHRoZSB1bmlvbiBvZiB0d28gcmVnaW9ucy5cbiAgICAgKiBAbWV0aG9kIF9yZWdpb25VbmlvblxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IHJlZ2lvbkFcbiAgICAgKiBAcGFyYW0ge30gcmVnaW9uQlxuICAgICAqIEByZXR1cm4ge30gcmVnaW9uXG4gICAgICovXG4gICAgdmFyIF9yZWdpb25VbmlvbiA9IGZ1bmN0aW9uKHJlZ2lvbkEsIHJlZ2lvbkIpIHtcbiAgICAgICAgdmFyIHN0YXJ0Q29sID0gTWF0aC5taW4ocmVnaW9uQS5zdGFydENvbCwgcmVnaW9uQi5zdGFydENvbCksXG4gICAgICAgICAgICBlbmRDb2wgPSBNYXRoLm1heChyZWdpb25BLmVuZENvbCwgcmVnaW9uQi5lbmRDb2wpLFxuICAgICAgICAgICAgc3RhcnRSb3cgPSBNYXRoLm1pbihyZWdpb25BLnN0YXJ0Um93LCByZWdpb25CLnN0YXJ0Um93KSxcbiAgICAgICAgICAgIGVuZFJvdyA9IE1hdGgubWF4KHJlZ2lvbkEuZW5kUm93LCByZWdpb25CLmVuZFJvdyk7XG5cbiAgICAgICAgcmV0dXJuIF9jcmVhdGVSZWdpb24oc3RhcnRDb2wsIGVuZENvbCwgc3RhcnRSb3csIGVuZFJvdyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdldHMgdGhlIHJlZ2lvbiBhIGdpdmVuIGJvZHkgZmFsbHMgaW4gZm9yIGEgZ2l2ZW4gZ3JpZC5cbiAgICAgKiBAbWV0aG9kIF9nZXRSZWdpb25cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7fSBncmlkXG4gICAgICogQHBhcmFtIHt9IGJvZHlcbiAgICAgKiBAcmV0dXJuIHt9IHJlZ2lvblxuICAgICAqL1xuICAgIHZhciBfZ2V0UmVnaW9uID0gZnVuY3Rpb24oZ3JpZCwgYm9keSkge1xuICAgICAgICB2YXIgYm91bmRzID0gYm9keS5ib3VuZHMsXG4gICAgICAgICAgICBzdGFydENvbCA9IE1hdGguZmxvb3IoYm91bmRzLm1pbi54IC8gZ3JpZC5idWNrZXRXaWR0aCksXG4gICAgICAgICAgICBlbmRDb2wgPSBNYXRoLmZsb29yKGJvdW5kcy5tYXgueCAvIGdyaWQuYnVja2V0V2lkdGgpLFxuICAgICAgICAgICAgc3RhcnRSb3cgPSBNYXRoLmZsb29yKGJvdW5kcy5taW4ueSAvIGdyaWQuYnVja2V0SGVpZ2h0KSxcbiAgICAgICAgICAgIGVuZFJvdyA9IE1hdGguZmxvb3IoYm91bmRzLm1heC55IC8gZ3JpZC5idWNrZXRIZWlnaHQpO1xuXG4gICAgICAgIHJldHVybiBfY3JlYXRlUmVnaW9uKHN0YXJ0Q29sLCBlbmRDb2wsIHN0YXJ0Um93LCBlbmRSb3cpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgcmVnaW9uLlxuICAgICAqIEBtZXRob2QgX2NyZWF0ZVJlZ2lvblxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IHN0YXJ0Q29sXG4gICAgICogQHBhcmFtIHt9IGVuZENvbFxuICAgICAqIEBwYXJhbSB7fSBzdGFydFJvd1xuICAgICAqIEBwYXJhbSB7fSBlbmRSb3dcbiAgICAgKiBAcmV0dXJuIHt9IHJlZ2lvblxuICAgICAqL1xuICAgIHZhciBfY3JlYXRlUmVnaW9uID0gZnVuY3Rpb24oc3RhcnRDb2wsIGVuZENvbCwgc3RhcnRSb3csIGVuZFJvdykge1xuICAgICAgICByZXR1cm4geyBcbiAgICAgICAgICAgIGlkOiBzdGFydENvbCArICcsJyArIGVuZENvbCArICcsJyArIHN0YXJ0Um93ICsgJywnICsgZW5kUm93LFxuICAgICAgICAgICAgc3RhcnRDb2w6IHN0YXJ0Q29sLCBcbiAgICAgICAgICAgIGVuZENvbDogZW5kQ29sLCBcbiAgICAgICAgICAgIHN0YXJ0Um93OiBzdGFydFJvdywgXG4gICAgICAgICAgICBlbmRSb3c6IGVuZFJvdyBcbiAgICAgICAgfTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogR2V0cyB0aGUgYnVja2V0IGlkIGF0IHRoZSBnaXZlbiBwb3NpdGlvbi5cbiAgICAgKiBAbWV0aG9kIF9nZXRCdWNrZXRJZFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IGNvbHVtblxuICAgICAqIEBwYXJhbSB7fSByb3dcbiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IGJ1Y2tldCBpZFxuICAgICAqL1xuICAgIHZhciBfZ2V0QnVja2V0SWQgPSBmdW5jdGlvbihjb2x1bW4sIHJvdykge1xuICAgICAgICByZXR1cm4gJ0MnICsgY29sdW1uICsgJ1InICsgcm93O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgYnVja2V0LlxuICAgICAqIEBtZXRob2QgX2NyZWF0ZUJ1Y2tldFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IGJ1Y2tldHNcbiAgICAgKiBAcGFyYW0ge30gYnVja2V0SWRcbiAgICAgKiBAcmV0dXJuIHt9IGJ1Y2tldFxuICAgICAqL1xuICAgIHZhciBfY3JlYXRlQnVja2V0ID0gZnVuY3Rpb24oYnVja2V0cywgYnVja2V0SWQpIHtcbiAgICAgICAgdmFyIGJ1Y2tldCA9IGJ1Y2tldHNbYnVja2V0SWRdID0gW107XG4gICAgICAgIHJldHVybiBidWNrZXQ7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEFkZHMgYSBib2R5IHRvIGEgYnVja2V0LlxuICAgICAqIEBtZXRob2QgX2J1Y2tldEFkZEJvZHlcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7fSBncmlkXG4gICAgICogQHBhcmFtIHt9IGJ1Y2tldFxuICAgICAqIEBwYXJhbSB7fSBib2R5XG4gICAgICovXG4gICAgdmFyIF9idWNrZXRBZGRCb2R5ID0gZnVuY3Rpb24oZ3JpZCwgYnVja2V0LCBib2R5KSB7XG4gICAgICAgIC8vIGFkZCBuZXcgcGFpcnNcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBidWNrZXQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5QiA9IGJ1Y2tldFtpXTtcblxuICAgICAgICAgICAgaWYgKGJvZHkuaWQgPT09IGJvZHlCLmlkIHx8IChib2R5LmlzU3RhdGljICYmIGJvZHlCLmlzU3RhdGljKSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgLy8ga2VlcCB0cmFjayBvZiB0aGUgbnVtYmVyIG9mIGJ1Y2tldHMgdGhlIHBhaXIgZXhpc3RzIGluXG4gICAgICAgICAgICAvLyBpbXBvcnRhbnQgZm9yIEdyaWQudXBkYXRlIHRvIHdvcmtcbiAgICAgICAgICAgIHZhciBwYWlySWQgPSBQYWlyLmlkKGJvZHksIGJvZHlCKSxcbiAgICAgICAgICAgICAgICBwYWlyID0gZ3JpZC5wYWlyc1twYWlySWRdO1xuXG4gICAgICAgICAgICBpZiAocGFpcikge1xuICAgICAgICAgICAgICAgIHBhaXJbMl0gKz0gMTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZ3JpZC5wYWlyc1twYWlySWRdID0gW2JvZHksIGJvZHlCLCAxXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGFkZCB0byBib2RpZXMgKGFmdGVyIHBhaXJzLCBvdGhlcndpc2UgcGFpcnMgd2l0aCBzZWxmKVxuICAgICAgICBidWNrZXQucHVzaChib2R5KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyBhIGJvZHkgZnJvbSBhIGJ1Y2tldC5cbiAgICAgKiBAbWV0aG9kIF9idWNrZXRSZW1vdmVCb2R5XG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge30gZ3JpZFxuICAgICAqIEBwYXJhbSB7fSBidWNrZXRcbiAgICAgKiBAcGFyYW0ge30gYm9keVxuICAgICAqL1xuICAgIHZhciBfYnVja2V0UmVtb3ZlQm9keSA9IGZ1bmN0aW9uKGdyaWQsIGJ1Y2tldCwgYm9keSkge1xuICAgICAgICAvLyByZW1vdmUgZnJvbSBidWNrZXRcbiAgICAgICAgYnVja2V0LnNwbGljZShDb21tb24uaW5kZXhPZihidWNrZXQsIGJvZHkpLCAxKTtcblxuICAgICAgICAvLyB1cGRhdGUgcGFpciBjb3VudHNcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBidWNrZXQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIC8vIGtlZXAgdHJhY2sgb2YgdGhlIG51bWJlciBvZiBidWNrZXRzIHRoZSBwYWlyIGV4aXN0cyBpblxuICAgICAgICAgICAgLy8gaW1wb3J0YW50IGZvciBfY3JlYXRlQWN0aXZlUGFpcnNMaXN0IHRvIHdvcmtcbiAgICAgICAgICAgIHZhciBib2R5QiA9IGJ1Y2tldFtpXSxcbiAgICAgICAgICAgICAgICBwYWlySWQgPSBQYWlyLmlkKGJvZHksIGJvZHlCKSxcbiAgICAgICAgICAgICAgICBwYWlyID0gZ3JpZC5wYWlyc1twYWlySWRdO1xuXG4gICAgICAgICAgICBpZiAocGFpcilcbiAgICAgICAgICAgICAgICBwYWlyWzJdIC09IDE7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogR2VuZXJhdGVzIGEgbGlzdCBvZiB0aGUgYWN0aXZlIHBhaXJzIGluIHRoZSBncmlkLlxuICAgICAqIEBtZXRob2QgX2NyZWF0ZUFjdGl2ZVBhaXJzTGlzdFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IGdyaWRcbiAgICAgKiBAcmV0dXJuIFtdIHBhaXJzXG4gICAgICovXG4gICAgdmFyIF9jcmVhdGVBY3RpdmVQYWlyc0xpc3QgPSBmdW5jdGlvbihncmlkKSB7XG4gICAgICAgIHZhciBwYWlyS2V5cyxcbiAgICAgICAgICAgIHBhaXIsXG4gICAgICAgICAgICBwYWlycyA9IFtdO1xuXG4gICAgICAgIC8vIGdyaWQucGFpcnMgaXMgdXNlZCBhcyBhIGhhc2htYXBcbiAgICAgICAgcGFpcktleXMgPSBDb21tb24ua2V5cyhncmlkLnBhaXJzKTtcblxuICAgICAgICAvLyBpdGVyYXRlIG92ZXIgZ3JpZC5wYWlyc1xuICAgICAgICBmb3IgKHZhciBrID0gMDsgayA8IHBhaXJLZXlzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICBwYWlyID0gZ3JpZC5wYWlyc1twYWlyS2V5c1trXV07XG5cbiAgICAgICAgICAgIC8vIGlmIHBhaXIgZXhpc3RzIGluIGF0IGxlYXN0IG9uZSBidWNrZXRcbiAgICAgICAgICAgIC8vIGl0IGlzIGEgcGFpciB0aGF0IG5lZWRzIGZ1cnRoZXIgY29sbGlzaW9uIHRlc3Rpbmcgc28gcHVzaCBpdFxuICAgICAgICAgICAgaWYgKHBhaXJbMl0gPiAwKSB7XG4gICAgICAgICAgICAgICAgcGFpcnMucHVzaChwYWlyKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZGVsZXRlIGdyaWQucGFpcnNbcGFpcktleXNba11dO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHBhaXJzO1xuICAgIH07XG4gICAgXG59KSgpO1xuXG59LHtcIi4uL2NvcmUvQ29tbW9uXCI6MTQsXCIuL0RldGVjdG9yXCI6NSxcIi4vUGFpclwiOjd9XSw3OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5QYWlyYCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyBmb3IgY3JlYXRpbmcgYW5kIG1hbmlwdWxhdGluZyBjb2xsaXNpb24gcGFpcnMuXG4qXG4qIEBjbGFzcyBQYWlyXG4qL1xuXG52YXIgUGFpciA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFBhaXI7XG5cbnZhciBDb250YWN0ID0gX2RlcmVxXygnLi9Db250YWN0Jyk7XG5cbihmdW5jdGlvbigpIHtcbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgcGFpci5cbiAgICAgKiBAbWV0aG9kIGNyZWF0ZVxuICAgICAqIEBwYXJhbSB7Y29sbGlzaW9ufSBjb2xsaXNpb25cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gdGltZXN0YW1wXG4gICAgICogQHJldHVybiB7cGFpcn0gQSBuZXcgcGFpclxuICAgICAqL1xuICAgIFBhaXIuY3JlYXRlID0gZnVuY3Rpb24oY29sbGlzaW9uLCB0aW1lc3RhbXApIHtcbiAgICAgICAgdmFyIGJvZHlBID0gY29sbGlzaW9uLmJvZHlBLFxuICAgICAgICAgICAgYm9keUIgPSBjb2xsaXNpb24uYm9keUIsXG4gICAgICAgICAgICBwYXJlbnRBID0gY29sbGlzaW9uLnBhcmVudEEsXG4gICAgICAgICAgICBwYXJlbnRCID0gY29sbGlzaW9uLnBhcmVudEI7XG5cbiAgICAgICAgdmFyIHBhaXIgPSB7XG4gICAgICAgICAgICBpZDogUGFpci5pZChib2R5QSwgYm9keUIpLFxuICAgICAgICAgICAgYm9keUE6IGJvZHlBLFxuICAgICAgICAgICAgYm9keUI6IGJvZHlCLFxuICAgICAgICAgICAgY29udGFjdHM6IHt9LFxuICAgICAgICAgICAgYWN0aXZlQ29udGFjdHM6IFtdLFxuICAgICAgICAgICAgc2VwYXJhdGlvbjogMCxcbiAgICAgICAgICAgIGlzQWN0aXZlOiB0cnVlLFxuICAgICAgICAgICAgaXNTZW5zb3I6IGJvZHlBLmlzU2Vuc29yIHx8IGJvZHlCLmlzU2Vuc29yLFxuICAgICAgICAgICAgdGltZUNyZWF0ZWQ6IHRpbWVzdGFtcCxcbiAgICAgICAgICAgIHRpbWVVcGRhdGVkOiB0aW1lc3RhbXAsXG4gICAgICAgICAgICBpbnZlcnNlTWFzczogcGFyZW50QS5pbnZlcnNlTWFzcyArIHBhcmVudEIuaW52ZXJzZU1hc3MsXG4gICAgICAgICAgICBmcmljdGlvbjogTWF0aC5taW4ocGFyZW50QS5mcmljdGlvbiwgcGFyZW50Qi5mcmljdGlvbiksXG4gICAgICAgICAgICBmcmljdGlvblN0YXRpYzogTWF0aC5tYXgocGFyZW50QS5mcmljdGlvblN0YXRpYywgcGFyZW50Qi5mcmljdGlvblN0YXRpYyksXG4gICAgICAgICAgICByZXN0aXR1dGlvbjogTWF0aC5tYXgocGFyZW50QS5yZXN0aXR1dGlvbiwgcGFyZW50Qi5yZXN0aXR1dGlvbiksXG4gICAgICAgICAgICBzbG9wOiBNYXRoLm1heChwYXJlbnRBLnNsb3AsIHBhcmVudEIuc2xvcClcbiAgICAgICAgfTtcblxuICAgICAgICBQYWlyLnVwZGF0ZShwYWlyLCBjb2xsaXNpb24sIHRpbWVzdGFtcCk7XG5cbiAgICAgICAgcmV0dXJuIHBhaXI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgYSBwYWlyIGdpdmVuIGEgY29sbGlzaW9uLlxuICAgICAqIEBtZXRob2QgdXBkYXRlXG4gICAgICogQHBhcmFtIHtwYWlyfSBwYWlyXG4gICAgICogQHBhcmFtIHtjb2xsaXNpb259IGNvbGxpc2lvblxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lc3RhbXBcbiAgICAgKi9cbiAgICBQYWlyLnVwZGF0ZSA9IGZ1bmN0aW9uKHBhaXIsIGNvbGxpc2lvbiwgdGltZXN0YW1wKSB7XG4gICAgICAgIHZhciBjb250YWN0cyA9IHBhaXIuY29udGFjdHMsXG4gICAgICAgICAgICBzdXBwb3J0cyA9IGNvbGxpc2lvbi5zdXBwb3J0cyxcbiAgICAgICAgICAgIGFjdGl2ZUNvbnRhY3RzID0gcGFpci5hY3RpdmVDb250YWN0cyxcbiAgICAgICAgICAgIHBhcmVudEEgPSBjb2xsaXNpb24ucGFyZW50QSxcbiAgICAgICAgICAgIHBhcmVudEIgPSBjb2xsaXNpb24ucGFyZW50QjtcbiAgICAgICAgXG4gICAgICAgIHBhaXIuY29sbGlzaW9uID0gY29sbGlzaW9uO1xuICAgICAgICBwYWlyLmludmVyc2VNYXNzID0gcGFyZW50QS5pbnZlcnNlTWFzcyArIHBhcmVudEIuaW52ZXJzZU1hc3M7XG4gICAgICAgIHBhaXIuZnJpY3Rpb24gPSBNYXRoLm1pbihwYXJlbnRBLmZyaWN0aW9uLCBwYXJlbnRCLmZyaWN0aW9uKTtcbiAgICAgICAgcGFpci5mcmljdGlvblN0YXRpYyA9IE1hdGgubWF4KHBhcmVudEEuZnJpY3Rpb25TdGF0aWMsIHBhcmVudEIuZnJpY3Rpb25TdGF0aWMpO1xuICAgICAgICBwYWlyLnJlc3RpdHV0aW9uID0gTWF0aC5tYXgocGFyZW50QS5yZXN0aXR1dGlvbiwgcGFyZW50Qi5yZXN0aXR1dGlvbik7XG4gICAgICAgIHBhaXIuc2xvcCA9IE1hdGgubWF4KHBhcmVudEEuc2xvcCwgcGFyZW50Qi5zbG9wKTtcbiAgICAgICAgYWN0aXZlQ29udGFjdHMubGVuZ3RoID0gMDtcbiAgICAgICAgXG4gICAgICAgIGlmIChjb2xsaXNpb24uY29sbGlkZWQpIHtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc3VwcG9ydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgc3VwcG9ydCA9IHN1cHBvcnRzW2ldLFxuICAgICAgICAgICAgICAgICAgICBjb250YWN0SWQgPSBDb250YWN0LmlkKHN1cHBvcnQpLFxuICAgICAgICAgICAgICAgICAgICBjb250YWN0ID0gY29udGFjdHNbY29udGFjdElkXTtcblxuICAgICAgICAgICAgICAgIGlmIChjb250YWN0KSB7XG4gICAgICAgICAgICAgICAgICAgIGFjdGl2ZUNvbnRhY3RzLnB1c2goY29udGFjdCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgYWN0aXZlQ29udGFjdHMucHVzaChjb250YWN0c1tjb250YWN0SWRdID0gQ29udGFjdC5jcmVhdGUoc3VwcG9ydCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcGFpci5zZXBhcmF0aW9uID0gY29sbGlzaW9uLmRlcHRoO1xuICAgICAgICAgICAgUGFpci5zZXRBY3RpdmUocGFpciwgdHJ1ZSwgdGltZXN0YW1wKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChwYWlyLmlzQWN0aXZlID09PSB0cnVlKVxuICAgICAgICAgICAgICAgIFBhaXIuc2V0QWN0aXZlKHBhaXIsIGZhbHNlLCB0aW1lc3RhbXApO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBTZXQgYSBwYWlyIGFzIGFjdGl2ZSBvciBpbmFjdGl2ZS5cbiAgICAgKiBAbWV0aG9kIHNldEFjdGl2ZVxuICAgICAqIEBwYXJhbSB7cGFpcn0gcGFpclxuICAgICAqIEBwYXJhbSB7Ym9vbH0gaXNBY3RpdmVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gdGltZXN0YW1wXG4gICAgICovXG4gICAgUGFpci5zZXRBY3RpdmUgPSBmdW5jdGlvbihwYWlyLCBpc0FjdGl2ZSwgdGltZXN0YW1wKSB7XG4gICAgICAgIGlmIChpc0FjdGl2ZSkge1xuICAgICAgICAgICAgcGFpci5pc0FjdGl2ZSA9IHRydWU7XG4gICAgICAgICAgICBwYWlyLnRpbWVVcGRhdGVkID0gdGltZXN0YW1wO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcGFpci5pc0FjdGl2ZSA9IGZhbHNlO1xuICAgICAgICAgICAgcGFpci5hY3RpdmVDb250YWN0cy5sZW5ndGggPSAwO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgaWQgZm9yIHRoZSBnaXZlbiBwYWlyLlxuICAgICAqIEBtZXRob2QgaWRcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlBXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5QlxuICAgICAqIEByZXR1cm4ge3N0cmluZ30gVW5pcXVlIHBhaXJJZFxuICAgICAqL1xuICAgIFBhaXIuaWQgPSBmdW5jdGlvbihib2R5QSwgYm9keUIpIHtcbiAgICAgICAgaWYgKGJvZHlBLmlkIDwgYm9keUIuaWQpIHtcbiAgICAgICAgICAgIHJldHVybiAnQScgKyBib2R5QS5pZCArICdCJyArIGJvZHlCLmlkO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuICdBJyArIGJvZHlCLmlkICsgJ0InICsgYm9keUEuaWQ7XG4gICAgICAgIH1cbiAgICB9O1xuXG59KSgpO1xuXG59LHtcIi4vQ29udGFjdFwiOjR9XSw4OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5QYWlyc2AgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGNyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgY29sbGlzaW9uIHBhaXIgc2V0cy5cbipcbiogQGNsYXNzIFBhaXJzXG4qL1xuXG52YXIgUGFpcnMgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBQYWlycztcblxudmFyIFBhaXIgPSBfZGVyZXFfKCcuL1BhaXInKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xuXG4oZnVuY3Rpb24oKSB7XG4gICAgXG4gICAgdmFyIF9wYWlyTWF4SWRsZUxpZmUgPSAxMDAwO1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBwYWlycyBzdHJ1Y3R1cmUuXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9uc1xuICAgICAqIEByZXR1cm4ge3BhaXJzfSBBIG5ldyBwYWlycyBzdHJ1Y3R1cmVcbiAgICAgKi9cbiAgICBQYWlycy5jcmVhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgICAgIHJldHVybiBDb21tb24uZXh0ZW5kKHsgXG4gICAgICAgICAgICB0YWJsZToge30sXG4gICAgICAgICAgICBsaXN0OiBbXSxcbiAgICAgICAgICAgIGNvbGxpc2lvblN0YXJ0OiBbXSxcbiAgICAgICAgICAgIGNvbGxpc2lvbkFjdGl2ZTogW10sXG4gICAgICAgICAgICBjb2xsaXNpb25FbmQ6IFtdXG4gICAgICAgIH0sIG9wdGlvbnMpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHBhaXJzIGdpdmVuIGEgbGlzdCBvZiBjb2xsaXNpb25zLlxuICAgICAqIEBtZXRob2QgdXBkYXRlXG4gICAgICogQHBhcmFtIHtvYmplY3R9IHBhaXJzXG4gICAgICogQHBhcmFtIHtjb2xsaXNpb25bXX0gY29sbGlzaW9uc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lc3RhbXBcbiAgICAgKi9cbiAgICBQYWlycy51cGRhdGUgPSBmdW5jdGlvbihwYWlycywgY29sbGlzaW9ucywgdGltZXN0YW1wKSB7XG4gICAgICAgIHZhciBwYWlyc0xpc3QgPSBwYWlycy5saXN0LFxuICAgICAgICAgICAgcGFpcnNUYWJsZSA9IHBhaXJzLnRhYmxlLFxuICAgICAgICAgICAgY29sbGlzaW9uU3RhcnQgPSBwYWlycy5jb2xsaXNpb25TdGFydCxcbiAgICAgICAgICAgIGNvbGxpc2lvbkVuZCA9IHBhaXJzLmNvbGxpc2lvbkVuZCxcbiAgICAgICAgICAgIGNvbGxpc2lvbkFjdGl2ZSA9IHBhaXJzLmNvbGxpc2lvbkFjdGl2ZSxcbiAgICAgICAgICAgIGFjdGl2ZVBhaXJJZHMgPSBbXSxcbiAgICAgICAgICAgIGNvbGxpc2lvbixcbiAgICAgICAgICAgIHBhaXJJZCxcbiAgICAgICAgICAgIHBhaXIsXG4gICAgICAgICAgICBpO1xuXG4gICAgICAgIC8vIGNsZWFyIGNvbGxpc2lvbiBzdGF0ZSBhcnJheXMsIGJ1dCBtYWludGFpbiBvbGQgcmVmZXJlbmNlXG4gICAgICAgIGNvbGxpc2lvblN0YXJ0Lmxlbmd0aCA9IDA7XG4gICAgICAgIGNvbGxpc2lvbkVuZC5sZW5ndGggPSAwO1xuICAgICAgICBjb2xsaXNpb25BY3RpdmUubGVuZ3RoID0gMDtcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgY29sbGlzaW9ucy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY29sbGlzaW9uID0gY29sbGlzaW9uc1tpXTtcblxuICAgICAgICAgICAgaWYgKGNvbGxpc2lvbi5jb2xsaWRlZCkge1xuICAgICAgICAgICAgICAgIHBhaXJJZCA9IFBhaXIuaWQoY29sbGlzaW9uLmJvZHlBLCBjb2xsaXNpb24uYm9keUIpO1xuICAgICAgICAgICAgICAgIGFjdGl2ZVBhaXJJZHMucHVzaChwYWlySWQpO1xuXG4gICAgICAgICAgICAgICAgcGFpciA9IHBhaXJzVGFibGVbcGFpcklkXTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBpZiAocGFpcikge1xuICAgICAgICAgICAgICAgICAgICAvLyBwYWlyIGFscmVhZHkgZXhpc3RzIChidXQgbWF5IG9yIG1heSBub3QgYmUgYWN0aXZlKVxuICAgICAgICAgICAgICAgICAgICBpZiAocGFpci5pc0FjdGl2ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gcGFpciBleGlzdHMgYW5kIGlzIGFjdGl2ZVxuICAgICAgICAgICAgICAgICAgICAgICAgY29sbGlzaW9uQWN0aXZlLnB1c2gocGFpcik7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBwYWlyIGV4aXN0cyBidXQgd2FzIGluYWN0aXZlLCBzbyBhIGNvbGxpc2lvbiBoYXMganVzdCBzdGFydGVkIGFnYWluXG4gICAgICAgICAgICAgICAgICAgICAgICBjb2xsaXNpb25TdGFydC5wdXNoKHBhaXIpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gdXBkYXRlIHRoZSBwYWlyXG4gICAgICAgICAgICAgICAgICAgIFBhaXIudXBkYXRlKHBhaXIsIGNvbGxpc2lvbiwgdGltZXN0YW1wKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAvLyBwYWlyIGRpZCBub3QgZXhpc3QsIGNyZWF0ZSBhIG5ldyBwYWlyXG4gICAgICAgICAgICAgICAgICAgIHBhaXIgPSBQYWlyLmNyZWF0ZShjb2xsaXNpb24sIHRpbWVzdGFtcCk7XG4gICAgICAgICAgICAgICAgICAgIHBhaXJzVGFibGVbcGFpcklkXSA9IHBhaXI7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gcHVzaCB0aGUgbmV3IHBhaXJcbiAgICAgICAgICAgICAgICAgICAgY29sbGlzaW9uU3RhcnQucHVzaChwYWlyKTtcbiAgICAgICAgICAgICAgICAgICAgcGFpcnNMaXN0LnB1c2gocGFpcik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gZGVhY3RpdmF0ZSBwcmV2aW91c2x5IGFjdGl2ZSBwYWlycyB0aGF0IGFyZSBub3cgaW5hY3RpdmVcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHBhaXJzTGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgcGFpciA9IHBhaXJzTGlzdFtpXTtcbiAgICAgICAgICAgIGlmIChwYWlyLmlzQWN0aXZlICYmIENvbW1vbi5pbmRleE9mKGFjdGl2ZVBhaXJJZHMsIHBhaXIuaWQpID09PSAtMSkge1xuICAgICAgICAgICAgICAgIFBhaXIuc2V0QWN0aXZlKHBhaXIsIGZhbHNlLCB0aW1lc3RhbXApO1xuICAgICAgICAgICAgICAgIGNvbGxpc2lvbkVuZC5wdXNoKHBhaXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBGaW5kcyBhbmQgcmVtb3ZlcyBwYWlycyB0aGF0IGhhdmUgYmVlbiBpbmFjdGl2ZSBmb3IgYSBzZXQgYW1vdW50IG9mIHRpbWUuXG4gICAgICogQG1ldGhvZCByZW1vdmVPbGRcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFpcnNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gdGltZXN0YW1wXG4gICAgICovXG4gICAgUGFpcnMucmVtb3ZlT2xkID0gZnVuY3Rpb24ocGFpcnMsIHRpbWVzdGFtcCkge1xuICAgICAgICB2YXIgcGFpcnNMaXN0ID0gcGFpcnMubGlzdCxcbiAgICAgICAgICAgIHBhaXJzVGFibGUgPSBwYWlycy50YWJsZSxcbiAgICAgICAgICAgIGluZGV4ZXNUb1JlbW92ZSA9IFtdLFxuICAgICAgICAgICAgcGFpcixcbiAgICAgICAgICAgIGNvbGxpc2lvbixcbiAgICAgICAgICAgIHBhaXJJbmRleCxcbiAgICAgICAgICAgIGk7XG5cbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHBhaXJzTGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgcGFpciA9IHBhaXJzTGlzdFtpXTtcbiAgICAgICAgICAgIGNvbGxpc2lvbiA9IHBhaXIuY29sbGlzaW9uO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBuZXZlciByZW1vdmUgc2xlZXBpbmcgcGFpcnNcbiAgICAgICAgICAgIGlmIChjb2xsaXNpb24uYm9keUEuaXNTbGVlcGluZyB8fCBjb2xsaXNpb24uYm9keUIuaXNTbGVlcGluZykge1xuICAgICAgICAgICAgICAgIHBhaXIudGltZVVwZGF0ZWQgPSB0aW1lc3RhbXA7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGlmIHBhaXIgaXMgaW5hY3RpdmUgZm9yIHRvbyBsb25nLCBtYXJrIGl0IHRvIGJlIHJlbW92ZWRcbiAgICAgICAgICAgIGlmICh0aW1lc3RhbXAgLSBwYWlyLnRpbWVVcGRhdGVkID4gX3BhaXJNYXhJZGxlTGlmZSkge1xuICAgICAgICAgICAgICAgIGluZGV4ZXNUb1JlbW92ZS5wdXNoKGkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gcmVtb3ZlIG1hcmtlZCBwYWlyc1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW5kZXhlc1RvUmVtb3ZlLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBwYWlySW5kZXggPSBpbmRleGVzVG9SZW1vdmVbaV0gLSBpO1xuICAgICAgICAgICAgcGFpciA9IHBhaXJzTGlzdFtwYWlySW5kZXhdO1xuICAgICAgICAgICAgZGVsZXRlIHBhaXJzVGFibGVbcGFpci5pZF07XG4gICAgICAgICAgICBwYWlyc0xpc3Quc3BsaWNlKHBhaXJJbmRleCwgMSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2xlYXJzIHRoZSBnaXZlbiBwYWlycyBzdHJ1Y3R1cmUuXG4gICAgICogQG1ldGhvZCBjbGVhclxuICAgICAqIEBwYXJhbSB7cGFpcnN9IHBhaXJzXG4gICAgICogQHJldHVybiB7cGFpcnN9IHBhaXJzXG4gICAgICovXG4gICAgUGFpcnMuY2xlYXIgPSBmdW5jdGlvbihwYWlycykge1xuICAgICAgICBwYWlycy50YWJsZSA9IHt9O1xuICAgICAgICBwYWlycy5saXN0Lmxlbmd0aCA9IDA7XG4gICAgICAgIHBhaXJzLmNvbGxpc2lvblN0YXJ0Lmxlbmd0aCA9IDA7XG4gICAgICAgIHBhaXJzLmNvbGxpc2lvbkFjdGl2ZS5sZW5ndGggPSAwO1xuICAgICAgICBwYWlycy5jb2xsaXNpb25FbmQubGVuZ3RoID0gMDtcbiAgICAgICAgcmV0dXJuIHBhaXJzO1xuICAgIH07XG5cbn0pKCk7XG5cbn0se1wiLi4vY29yZS9Db21tb25cIjoxNCxcIi4vUGFpclwiOjd9XSw5OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5RdWVyeWAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIHBlcmZvcm1pbmcgY29sbGlzaW9uIHF1ZXJpZXMuXG4qXG4qIFNlZSB0aGUgaW5jbHVkZWQgdXNhZ2UgW2V4YW1wbGVzXShodHRwczovL2dpdGh1Yi5jb20vbGlhYnJ1L21hdHRlci1qcy90cmVlL21hc3Rlci9leGFtcGxlcykuXG4qXG4qIEBjbGFzcyBRdWVyeVxuKi9cblxudmFyIFF1ZXJ5ID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gUXVlcnk7XG5cbnZhciBWZWN0b3IgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZWN0b3InKTtcbnZhciBTQVQgPSBfZGVyZXFfKCcuL1NBVCcpO1xudmFyIEJvdW5kcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L0JvdW5kcycpO1xudmFyIEJvZGllcyA9IF9kZXJlcV8oJy4uL2ZhY3RvcnkvQm9kaWVzJyk7XG52YXIgVmVydGljZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZXJ0aWNlcycpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDYXN0cyBhIHJheSBzZWdtZW50IGFnYWluc3QgYSBzZXQgb2YgYm9kaWVzIGFuZCByZXR1cm5zIGFsbCBjb2xsaXNpb25zLCByYXkgd2lkdGggaXMgb3B0aW9uYWwuIEludGVyc2VjdGlvbiBwb2ludHMgYXJlIG5vdCBwcm92aWRlZC5cbiAgICAgKiBAbWV0aG9kIHJheVxuICAgICAqIEBwYXJhbSB7Ym9keVtdfSBib2RpZXNcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gc3RhcnRQb2ludFxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBlbmRQb2ludFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbcmF5V2lkdGhdXG4gICAgICogQHJldHVybiB7b2JqZWN0W119IENvbGxpc2lvbnNcbiAgICAgKi9cbiAgICBRdWVyeS5yYXkgPSBmdW5jdGlvbihib2RpZXMsIHN0YXJ0UG9pbnQsIGVuZFBvaW50LCByYXlXaWR0aCkge1xuICAgICAgICByYXlXaWR0aCA9IHJheVdpZHRoIHx8IDFlLTEwMDtcblxuICAgICAgICB2YXIgcmF5QW5nbGUgPSBWZWN0b3IuYW5nbGUoc3RhcnRQb2ludCwgZW5kUG9pbnQpLFxuICAgICAgICAgICAgcmF5TGVuZ3RoID0gVmVjdG9yLm1hZ25pdHVkZShWZWN0b3Iuc3ViKHN0YXJ0UG9pbnQsIGVuZFBvaW50KSksXG4gICAgICAgICAgICByYXlYID0gKGVuZFBvaW50LnggKyBzdGFydFBvaW50LngpICogMC41LFxuICAgICAgICAgICAgcmF5WSA9IChlbmRQb2ludC55ICsgc3RhcnRQb2ludC55KSAqIDAuNSxcbiAgICAgICAgICAgIHJheSA9IEJvZGllcy5yZWN0YW5nbGUocmF5WCwgcmF5WSwgcmF5TGVuZ3RoLCByYXlXaWR0aCwgeyBhbmdsZTogcmF5QW5nbGUgfSksXG4gICAgICAgICAgICBjb2xsaXNpb25zID0gW107XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5QSA9IGJvZGllc1tpXTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKEJvdW5kcy5vdmVybGFwcyhib2R5QS5ib3VuZHMsIHJheS5ib3VuZHMpKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaiA9IGJvZHlBLnBhcnRzLmxlbmd0aCA9PT0gMSA/IDAgOiAxOyBqIDwgYm9keUEucGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcnQgPSBib2R5QS5wYXJ0c1tqXTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoQm91bmRzLm92ZXJsYXBzKHBhcnQuYm91bmRzLCByYXkuYm91bmRzKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGNvbGxpc2lvbiA9IFNBVC5jb2xsaWRlcyhwYXJ0LCByYXkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNvbGxpc2lvbi5jb2xsaWRlZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxpc2lvbi5ib2R5ID0gY29sbGlzaW9uLmJvZHlBID0gY29sbGlzaW9uLmJvZHlCID0gYm9keUE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGlzaW9ucy5wdXNoKGNvbGxpc2lvbik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY29sbGlzaW9ucztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhbGwgYm9kaWVzIHdob3NlIGJvdW5kcyBhcmUgaW5zaWRlIChvciBvdXRzaWRlIGlmIHNldCkgdGhlIGdpdmVuIHNldCBvZiBib3VuZHMsIGZyb20gdGhlIGdpdmVuIHNldCBvZiBib2RpZXMuXG4gICAgICogQG1ldGhvZCByZWdpb25cbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHtib3VuZHN9IGJvdW5kc1xuICAgICAqIEBwYXJhbSB7Ym9vbH0gW291dHNpZGU9ZmFsc2VdXG4gICAgICogQHJldHVybiB7Ym9keVtdfSBUaGUgYm9kaWVzIG1hdGNoaW5nIHRoZSBxdWVyeVxuICAgICAqL1xuICAgIFF1ZXJ5LnJlZ2lvbiA9IGZ1bmN0aW9uKGJvZGllcywgYm91bmRzLCBvdXRzaWRlKSB7XG4gICAgICAgIHZhciByZXN1bHQgPSBbXTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGJvZHkgPSBib2RpZXNbaV0sXG4gICAgICAgICAgICAgICAgb3ZlcmxhcHMgPSBCb3VuZHMub3ZlcmxhcHMoYm9keS5ib3VuZHMsIGJvdW5kcyk7XG4gICAgICAgICAgICBpZiAoKG92ZXJsYXBzICYmICFvdXRzaWRlKSB8fCAoIW92ZXJsYXBzICYmIG91dHNpZGUpKVxuICAgICAgICAgICAgICAgIHJlc3VsdC5wdXNoKGJvZHkpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhbGwgYm9kaWVzIHdob3NlIHZlcnRpY2VzIGNvbnRhaW4gdGhlIGdpdmVuIHBvaW50LCBmcm9tIHRoZSBnaXZlbiBzZXQgb2YgYm9kaWVzLlxuICAgICAqIEBtZXRob2QgcG9pbnRcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHBvaW50XG4gICAgICogQHJldHVybiB7Ym9keVtdfSBUaGUgYm9kaWVzIG1hdGNoaW5nIHRoZSBxdWVyeVxuICAgICAqL1xuICAgIFF1ZXJ5LnBvaW50ID0gZnVuY3Rpb24oYm9kaWVzLCBwb2ludCkge1xuICAgICAgICB2YXIgcmVzdWx0ID0gW107XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5ID0gYm9kaWVzW2ldO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoQm91bmRzLmNvbnRhaW5zKGJvZHkuYm91bmRzLCBwb2ludCkpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gYm9keS5wYXJ0cy5sZW5ndGggPT09IDEgPyAwIDogMTsgaiA8IGJvZHkucGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcnQgPSBib2R5LnBhcnRzW2pdO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChCb3VuZHMuY29udGFpbnMocGFydC5ib3VuZHMsIHBvaW50KVxuICAgICAgICAgICAgICAgICAgICAgICAgJiYgVmVydGljZXMuY29udGFpbnMocGFydC52ZXJ0aWNlcywgcG9pbnQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQucHVzaChib2R5KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuXG59KSgpO1xuXG59LHtcIi4uL2ZhY3RvcnkvQm9kaWVzXCI6MjMsXCIuLi9nZW9tZXRyeS9Cb3VuZHNcIjoyNixcIi4uL2dlb21ldHJ5L1ZlY3RvclwiOjI4LFwiLi4vZ2VvbWV0cnkvVmVydGljZXNcIjoyOSxcIi4vU0FUXCI6MTF9XSwxMDpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuUmVzb2x2ZXJgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIGZvciByZXNvbHZpbmcgY29sbGlzaW9uIHBhaXJzLlxuKlxuKiBAY2xhc3MgUmVzb2x2ZXJcbiovXG5cbnZhciBSZXNvbHZlciA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJlc29sdmVyO1xuXG52YXIgVmVydGljZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZXJ0aWNlcycpO1xudmFyIFZlY3RvciA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L1ZlY3RvcicpO1xudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4uL2NvcmUvQ29tbW9uJyk7XG52YXIgQm91bmRzID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvQm91bmRzJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIFJlc29sdmVyLl9yZXN0aW5nVGhyZXNoID0gNDtcbiAgICBSZXNvbHZlci5fcmVzdGluZ1RocmVzaFRhbmdlbnQgPSA2O1xuICAgIFJlc29sdmVyLl9wb3NpdGlvbkRhbXBlbiA9IDAuOTtcbiAgICBSZXNvbHZlci5fcG9zaXRpb25XYXJtaW5nID0gMC44O1xuICAgIFJlc29sdmVyLl9mcmljdGlvbk5vcm1hbE11bHRpcGxpZXIgPSA1O1xuXG4gICAgLyoqXG4gICAgICogUHJlcGFyZSBwYWlycyBmb3IgcG9zaXRpb24gc29sdmluZy5cbiAgICAgKiBAbWV0aG9kIHByZVNvbHZlUG9zaXRpb25cbiAgICAgKiBAcGFyYW0ge3BhaXJbXX0gcGFpcnNcbiAgICAgKi9cbiAgICBSZXNvbHZlci5wcmVTb2x2ZVBvc2l0aW9uID0gZnVuY3Rpb24ocGFpcnMpIHtcbiAgICAgICAgdmFyIGksXG4gICAgICAgICAgICBwYWlyLFxuICAgICAgICAgICAgYWN0aXZlQ291bnQ7XG5cbiAgICAgICAgLy8gZmluZCB0b3RhbCBjb250YWN0cyBvbiBlYWNoIGJvZHlcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHBhaXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBwYWlyID0gcGFpcnNbaV07XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmICghcGFpci5pc0FjdGl2ZSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgYWN0aXZlQ291bnQgPSBwYWlyLmFjdGl2ZUNvbnRhY3RzLmxlbmd0aDtcbiAgICAgICAgICAgIHBhaXIuY29sbGlzaW9uLnBhcmVudEEudG90YWxDb250YWN0cyArPSBhY3RpdmVDb3VudDtcbiAgICAgICAgICAgIHBhaXIuY29sbGlzaW9uLnBhcmVudEIudG90YWxDb250YWN0cyArPSBhY3RpdmVDb3VudDtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBGaW5kIGEgc29sdXRpb24gZm9yIHBhaXIgcG9zaXRpb25zLlxuICAgICAqIEBtZXRob2Qgc29sdmVQb3NpdGlvblxuICAgICAqIEBwYXJhbSB7cGFpcltdfSBwYWlyc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lU2NhbGVcbiAgICAgKi9cbiAgICBSZXNvbHZlci5zb2x2ZVBvc2l0aW9uID0gZnVuY3Rpb24ocGFpcnMsIHRpbWVTY2FsZSkge1xuICAgICAgICB2YXIgaSxcbiAgICAgICAgICAgIHBhaXIsXG4gICAgICAgICAgICBjb2xsaXNpb24sXG4gICAgICAgICAgICBib2R5QSxcbiAgICAgICAgICAgIGJvZHlCLFxuICAgICAgICAgICAgbm9ybWFsLFxuICAgICAgICAgICAgYm9keUJ0b0EsXG4gICAgICAgICAgICBjb250YWN0U2hhcmUsXG4gICAgICAgICAgICBwb3NpdGlvbkltcHVsc2UsXG4gICAgICAgICAgICBjb250YWN0Q291bnQgPSB7fSxcbiAgICAgICAgICAgIHRlbXBBID0gVmVjdG9yLl90ZW1wWzBdLFxuICAgICAgICAgICAgdGVtcEIgPSBWZWN0b3IuX3RlbXBbMV0sXG4gICAgICAgICAgICB0ZW1wQyA9IFZlY3Rvci5fdGVtcFsyXSxcbiAgICAgICAgICAgIHRlbXBEID0gVmVjdG9yLl90ZW1wWzNdO1xuXG4gICAgICAgIC8vIGZpbmQgaW1wdWxzZXMgcmVxdWlyZWQgdG8gcmVzb2x2ZSBwZW5ldHJhdGlvblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFpcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHBhaXIgPSBwYWlyc1tpXTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKCFwYWlyLmlzQWN0aXZlIHx8IHBhaXIuaXNTZW5zb3IpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIGNvbGxpc2lvbiA9IHBhaXIuY29sbGlzaW9uO1xuICAgICAgICAgICAgYm9keUEgPSBjb2xsaXNpb24ucGFyZW50QTtcbiAgICAgICAgICAgIGJvZHlCID0gY29sbGlzaW9uLnBhcmVudEI7XG4gICAgICAgICAgICBub3JtYWwgPSBjb2xsaXNpb24ubm9ybWFsO1xuXG4gICAgICAgICAgICAvLyBnZXQgY3VycmVudCBzZXBhcmF0aW9uIGJldHdlZW4gYm9keSBlZGdlcyBpbnZvbHZlZCBpbiBjb2xsaXNpb25cbiAgICAgICAgICAgIGJvZHlCdG9BID0gVmVjdG9yLnN1YihWZWN0b3IuYWRkKGJvZHlCLnBvc2l0aW9uSW1wdWxzZSwgYm9keUIucG9zaXRpb24sIHRlbXBBKSwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWZWN0b3IuYWRkKGJvZHlBLnBvc2l0aW9uSW1wdWxzZSwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmVjdG9yLnN1Yihib2R5Qi5wb3NpdGlvbiwgY29sbGlzaW9uLnBlbmV0cmF0aW9uLCB0ZW1wQiksIHRlbXBDKSwgdGVtcEQpO1xuXG4gICAgICAgICAgICBwYWlyLnNlcGFyYXRpb24gPSBWZWN0b3IuZG90KG5vcm1hbCwgYm9keUJ0b0EpO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFpcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHBhaXIgPSBwYWlyc1tpXTtcblxuICAgICAgICAgICAgaWYgKCFwYWlyLmlzQWN0aXZlIHx8IHBhaXIuaXNTZW5zb3IpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNvbGxpc2lvbiA9IHBhaXIuY29sbGlzaW9uO1xuICAgICAgICAgICAgYm9keUEgPSBjb2xsaXNpb24ucGFyZW50QTtcbiAgICAgICAgICAgIGJvZHlCID0gY29sbGlzaW9uLnBhcmVudEI7XG4gICAgICAgICAgICBub3JtYWwgPSBjb2xsaXNpb24ubm9ybWFsO1xuICAgICAgICAgICAgcG9zaXRpb25JbXB1bHNlID0gKHBhaXIuc2VwYXJhdGlvbiAtIHBhaXIuc2xvcCkgKiB0aW1lU2NhbGU7XG5cbiAgICAgICAgICAgIGlmIChib2R5QS5pc1N0YXRpYyB8fCBib2R5Qi5pc1N0YXRpYylcbiAgICAgICAgICAgICAgICBwb3NpdGlvbkltcHVsc2UgKj0gMjtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKCEoYm9keUEuaXNTdGF0aWMgfHwgYm9keUEuaXNTbGVlcGluZykpIHtcbiAgICAgICAgICAgICAgICBjb250YWN0U2hhcmUgPSBSZXNvbHZlci5fcG9zaXRpb25EYW1wZW4gLyBib2R5QS50b3RhbENvbnRhY3RzO1xuICAgICAgICAgICAgICAgIGJvZHlBLnBvc2l0aW9uSW1wdWxzZS54ICs9IG5vcm1hbC54ICogcG9zaXRpb25JbXB1bHNlICogY29udGFjdFNoYXJlO1xuICAgICAgICAgICAgICAgIGJvZHlBLnBvc2l0aW9uSW1wdWxzZS55ICs9IG5vcm1hbC55ICogcG9zaXRpb25JbXB1bHNlICogY29udGFjdFNoYXJlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIShib2R5Qi5pc1N0YXRpYyB8fCBib2R5Qi5pc1NsZWVwaW5nKSkge1xuICAgICAgICAgICAgICAgIGNvbnRhY3RTaGFyZSA9IFJlc29sdmVyLl9wb3NpdGlvbkRhbXBlbiAvIGJvZHlCLnRvdGFsQ29udGFjdHM7XG4gICAgICAgICAgICAgICAgYm9keUIucG9zaXRpb25JbXB1bHNlLnggLT0gbm9ybWFsLnggKiBwb3NpdGlvbkltcHVsc2UgKiBjb250YWN0U2hhcmU7XG4gICAgICAgICAgICAgICAgYm9keUIucG9zaXRpb25JbXB1bHNlLnkgLT0gbm9ybWFsLnkgKiBwb3NpdGlvbkltcHVsc2UgKiBjb250YWN0U2hhcmU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwbHkgcG9zaXRpb24gcmVzb2x1dGlvbi5cbiAgICAgKiBAbWV0aG9kIHBvc3RTb2x2ZVBvc2l0aW9uXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqL1xuICAgIFJlc29sdmVyLnBvc3RTb2x2ZVBvc2l0aW9uID0gZnVuY3Rpb24oYm9kaWVzKSB7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXTtcblxuICAgICAgICAgICAgLy8gcmVzZXQgY29udGFjdCBjb3VudFxuICAgICAgICAgICAgYm9keS50b3RhbENvbnRhY3RzID0gMDtcblxuICAgICAgICAgICAgaWYgKGJvZHkucG9zaXRpb25JbXB1bHNlLnggIT09IDAgfHwgYm9keS5wb3NpdGlvbkltcHVsc2UueSAhPT0gMCkge1xuICAgICAgICAgICAgICAgIC8vIHVwZGF0ZSBib2R5IGdlb21ldHJ5XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBib2R5LnBhcnRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwYXJ0ID0gYm9keS5wYXJ0c1tqXTtcbiAgICAgICAgICAgICAgICAgICAgVmVydGljZXMudHJhbnNsYXRlKHBhcnQudmVydGljZXMsIGJvZHkucG9zaXRpb25JbXB1bHNlKTtcbiAgICAgICAgICAgICAgICAgICAgQm91bmRzLnVwZGF0ZShwYXJ0LmJvdW5kcywgcGFydC52ZXJ0aWNlcywgYm9keS52ZWxvY2l0eSk7XG4gICAgICAgICAgICAgICAgICAgIHBhcnQucG9zaXRpb24ueCArPSBib2R5LnBvc2l0aW9uSW1wdWxzZS54O1xuICAgICAgICAgICAgICAgICAgICBwYXJ0LnBvc2l0aW9uLnkgKz0gYm9keS5wb3NpdGlvbkltcHVsc2UueTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBtb3ZlIHRoZSBib2R5IHdpdGhvdXQgY2hhbmdpbmcgdmVsb2NpdHlcbiAgICAgICAgICAgICAgICBib2R5LnBvc2l0aW9uUHJldi54ICs9IGJvZHkucG9zaXRpb25JbXB1bHNlLng7XG4gICAgICAgICAgICAgICAgYm9keS5wb3NpdGlvblByZXYueSArPSBib2R5LnBvc2l0aW9uSW1wdWxzZS55O1xuXG4gICAgICAgICAgICAgICAgaWYgKFZlY3Rvci5kb3QoYm9keS5wb3NpdGlvbkltcHVsc2UsIGJvZHkudmVsb2NpdHkpIDwgMCkge1xuICAgICAgICAgICAgICAgICAgICAvLyByZXNldCBjYWNoZWQgaW1wdWxzZSBpZiB0aGUgYm9keSBoYXMgdmVsb2NpdHkgYWxvbmcgaXRcbiAgICAgICAgICAgICAgICAgICAgYm9keS5wb3NpdGlvbkltcHVsc2UueCA9IDA7XG4gICAgICAgICAgICAgICAgICAgIGJvZHkucG9zaXRpb25JbXB1bHNlLnkgPSAwO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHdhcm0gdGhlIG5leHQgaXRlcmF0aW9uXG4gICAgICAgICAgICAgICAgICAgIGJvZHkucG9zaXRpb25JbXB1bHNlLnggKj0gUmVzb2x2ZXIuX3Bvc2l0aW9uV2FybWluZztcbiAgICAgICAgICAgICAgICAgICAgYm9keS5wb3NpdGlvbkltcHVsc2UueSAqPSBSZXNvbHZlci5fcG9zaXRpb25XYXJtaW5nO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBQcmVwYXJlIHBhaXJzIGZvciB2ZWxvY2l0eSBzb2x2aW5nLlxuICAgICAqIEBtZXRob2QgcHJlU29sdmVWZWxvY2l0eVxuICAgICAqIEBwYXJhbSB7cGFpcltdfSBwYWlyc1xuICAgICAqL1xuICAgIFJlc29sdmVyLnByZVNvbHZlVmVsb2NpdHkgPSBmdW5jdGlvbihwYWlycykge1xuICAgICAgICB2YXIgaSxcbiAgICAgICAgICAgIGosXG4gICAgICAgICAgICBwYWlyLFxuICAgICAgICAgICAgY29udGFjdHMsXG4gICAgICAgICAgICBjb2xsaXNpb24sXG4gICAgICAgICAgICBib2R5QSxcbiAgICAgICAgICAgIGJvZHlCLFxuICAgICAgICAgICAgbm9ybWFsLFxuICAgICAgICAgICAgdGFuZ2VudCxcbiAgICAgICAgICAgIGNvbnRhY3QsXG4gICAgICAgICAgICBjb250YWN0VmVydGV4LFxuICAgICAgICAgICAgbm9ybWFsSW1wdWxzZSxcbiAgICAgICAgICAgIHRhbmdlbnRJbXB1bHNlLFxuICAgICAgICAgICAgb2Zmc2V0LFxuICAgICAgICAgICAgaW1wdWxzZSA9IFZlY3Rvci5fdGVtcFswXSxcbiAgICAgICAgICAgIHRlbXBBID0gVmVjdG9yLl90ZW1wWzFdO1xuICAgICAgICBcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHBhaXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBwYWlyID0gcGFpcnNbaV07XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmICghcGFpci5pc0FjdGl2ZSB8fCBwYWlyLmlzU2Vuc29yKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjb250YWN0cyA9IHBhaXIuYWN0aXZlQ29udGFjdHM7XG4gICAgICAgICAgICBjb2xsaXNpb24gPSBwYWlyLmNvbGxpc2lvbjtcbiAgICAgICAgICAgIGJvZHlBID0gY29sbGlzaW9uLnBhcmVudEE7XG4gICAgICAgICAgICBib2R5QiA9IGNvbGxpc2lvbi5wYXJlbnRCO1xuICAgICAgICAgICAgbm9ybWFsID0gY29sbGlzaW9uLm5vcm1hbDtcbiAgICAgICAgICAgIHRhbmdlbnQgPSBjb2xsaXNpb24udGFuZ2VudDtcblxuICAgICAgICAgICAgLy8gcmVzb2x2ZSBlYWNoIGNvbnRhY3RcbiAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBjb250YWN0cy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIGNvbnRhY3QgPSBjb250YWN0c1tqXTtcbiAgICAgICAgICAgICAgICBjb250YWN0VmVydGV4ID0gY29udGFjdC52ZXJ0ZXg7XG4gICAgICAgICAgICAgICAgbm9ybWFsSW1wdWxzZSA9IGNvbnRhY3Qubm9ybWFsSW1wdWxzZTtcbiAgICAgICAgICAgICAgICB0YW5nZW50SW1wdWxzZSA9IGNvbnRhY3QudGFuZ2VudEltcHVsc2U7XG5cbiAgICAgICAgICAgICAgICBpZiAobm9ybWFsSW1wdWxzZSAhPT0gMCB8fCB0YW5nZW50SW1wdWxzZSAhPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAvLyB0b3RhbCBpbXB1bHNlIGZyb20gY29udGFjdFxuICAgICAgICAgICAgICAgICAgICBpbXB1bHNlLnggPSAobm9ybWFsLnggKiBub3JtYWxJbXB1bHNlKSArICh0YW5nZW50LnggKiB0YW5nZW50SW1wdWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIGltcHVsc2UueSA9IChub3JtYWwueSAqIG5vcm1hbEltcHVsc2UpICsgKHRhbmdlbnQueSAqIHRhbmdlbnRJbXB1bHNlKTtcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgIC8vIGFwcGx5IGltcHVsc2UgZnJvbSBjb250YWN0XG4gICAgICAgICAgICAgICAgICAgIGlmICghKGJvZHlBLmlzU3RhdGljIHx8IGJvZHlBLmlzU2xlZXBpbmcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSBWZWN0b3Iuc3ViKGNvbnRhY3RWZXJ0ZXgsIGJvZHlBLnBvc2l0aW9uLCB0ZW1wQSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBib2R5QS5wb3NpdGlvblByZXYueCArPSBpbXB1bHNlLnggKiBib2R5QS5pbnZlcnNlTWFzcztcbiAgICAgICAgICAgICAgICAgICAgICAgIGJvZHlBLnBvc2l0aW9uUHJldi55ICs9IGltcHVsc2UueSAqIGJvZHlBLmludmVyc2VNYXNzO1xuICAgICAgICAgICAgICAgICAgICAgICAgYm9keUEuYW5nbGVQcmV2ICs9IFZlY3Rvci5jcm9zcyhvZmZzZXQsIGltcHVsc2UpICogYm9keUEuaW52ZXJzZUluZXJ0aWE7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoIShib2R5Qi5pc1N0YXRpYyB8fCBib2R5Qi5pc1NsZWVwaW5nKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gVmVjdG9yLnN1Yihjb250YWN0VmVydGV4LCBib2R5Qi5wb3NpdGlvbiwgdGVtcEEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYm9keUIucG9zaXRpb25QcmV2LnggLT0gaW1wdWxzZS54ICogYm9keUIuaW52ZXJzZU1hc3M7XG4gICAgICAgICAgICAgICAgICAgICAgICBib2R5Qi5wb3NpdGlvblByZXYueSAtPSBpbXB1bHNlLnkgKiBib2R5Qi5pbnZlcnNlTWFzcztcbiAgICAgICAgICAgICAgICAgICAgICAgIGJvZHlCLmFuZ2xlUHJldiAtPSBWZWN0b3IuY3Jvc3Mob2Zmc2V0LCBpbXB1bHNlKSAqIGJvZHlCLmludmVyc2VJbmVydGlhO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEZpbmQgYSBzb2x1dGlvbiBmb3IgcGFpciB2ZWxvY2l0aWVzLlxuICAgICAqIEBtZXRob2Qgc29sdmVWZWxvY2l0eVxuICAgICAqIEBwYXJhbSB7cGFpcltdfSBwYWlyc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lU2NhbGVcbiAgICAgKi9cbiAgICBSZXNvbHZlci5zb2x2ZVZlbG9jaXR5ID0gZnVuY3Rpb24ocGFpcnMsIHRpbWVTY2FsZSkge1xuICAgICAgICB2YXIgdGltZVNjYWxlU3F1YXJlZCA9IHRpbWVTY2FsZSAqIHRpbWVTY2FsZSxcbiAgICAgICAgICAgIGltcHVsc2UgPSBWZWN0b3IuX3RlbXBbMF0sXG4gICAgICAgICAgICB0ZW1wQSA9IFZlY3Rvci5fdGVtcFsxXSxcbiAgICAgICAgICAgIHRlbXBCID0gVmVjdG9yLl90ZW1wWzJdLFxuICAgICAgICAgICAgdGVtcEMgPSBWZWN0b3IuX3RlbXBbM10sXG4gICAgICAgICAgICB0ZW1wRCA9IFZlY3Rvci5fdGVtcFs0XSxcbiAgICAgICAgICAgIHRlbXBFID0gVmVjdG9yLl90ZW1wWzVdO1xuICAgICAgICBcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYWlycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHBhaXIgPSBwYWlyc1tpXTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKCFwYWlyLmlzQWN0aXZlIHx8IHBhaXIuaXNTZW5zb3IpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHZhciBjb2xsaXNpb24gPSBwYWlyLmNvbGxpc2lvbixcbiAgICAgICAgICAgICAgICBib2R5QSA9IGNvbGxpc2lvbi5wYXJlbnRBLFxuICAgICAgICAgICAgICAgIGJvZHlCID0gY29sbGlzaW9uLnBhcmVudEIsXG4gICAgICAgICAgICAgICAgbm9ybWFsID0gY29sbGlzaW9uLm5vcm1hbCxcbiAgICAgICAgICAgICAgICB0YW5nZW50ID0gY29sbGlzaW9uLnRhbmdlbnQsXG4gICAgICAgICAgICAgICAgY29udGFjdHMgPSBwYWlyLmFjdGl2ZUNvbnRhY3RzLFxuICAgICAgICAgICAgICAgIGNvbnRhY3RTaGFyZSA9IDEgLyBjb250YWN0cy5sZW5ndGg7XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSBib2R5IHZlbG9jaXRpZXNcbiAgICAgICAgICAgIGJvZHlBLnZlbG9jaXR5LnggPSBib2R5QS5wb3NpdGlvbi54IC0gYm9keUEucG9zaXRpb25QcmV2Lng7XG4gICAgICAgICAgICBib2R5QS52ZWxvY2l0eS55ID0gYm9keUEucG9zaXRpb24ueSAtIGJvZHlBLnBvc2l0aW9uUHJldi55O1xuICAgICAgICAgICAgYm9keUIudmVsb2NpdHkueCA9IGJvZHlCLnBvc2l0aW9uLnggLSBib2R5Qi5wb3NpdGlvblByZXYueDtcbiAgICAgICAgICAgIGJvZHlCLnZlbG9jaXR5LnkgPSBib2R5Qi5wb3NpdGlvbi55IC0gYm9keUIucG9zaXRpb25QcmV2Lnk7XG4gICAgICAgICAgICBib2R5QS5hbmd1bGFyVmVsb2NpdHkgPSBib2R5QS5hbmdsZSAtIGJvZHlBLmFuZ2xlUHJldjtcbiAgICAgICAgICAgIGJvZHlCLmFuZ3VsYXJWZWxvY2l0eSA9IGJvZHlCLmFuZ2xlIC0gYm9keUIuYW5nbGVQcmV2O1xuXG4gICAgICAgICAgICAvLyByZXNvbHZlIGVhY2ggY29udGFjdFxuICAgICAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBjb250YWN0cy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIHZhciBjb250YWN0ID0gY29udGFjdHNbal0sXG4gICAgICAgICAgICAgICAgICAgIGNvbnRhY3RWZXJ0ZXggPSBjb250YWN0LnZlcnRleCxcbiAgICAgICAgICAgICAgICAgICAgb2Zmc2V0QSA9IFZlY3Rvci5zdWIoY29udGFjdFZlcnRleCwgYm9keUEucG9zaXRpb24sIHRlbXBBKSxcbiAgICAgICAgICAgICAgICAgICAgb2Zmc2V0QiA9IFZlY3Rvci5zdWIoY29udGFjdFZlcnRleCwgYm9keUIucG9zaXRpb24sIHRlbXBCKSxcbiAgICAgICAgICAgICAgICAgICAgdmVsb2NpdHlQb2ludEEgPSBWZWN0b3IuYWRkKGJvZHlBLnZlbG9jaXR5LCBWZWN0b3IubXVsdChWZWN0b3IucGVycChvZmZzZXRBKSwgYm9keUEuYW5ndWxhclZlbG9jaXR5KSwgdGVtcEMpLFxuICAgICAgICAgICAgICAgICAgICB2ZWxvY2l0eVBvaW50QiA9IFZlY3Rvci5hZGQoYm9keUIudmVsb2NpdHksIFZlY3Rvci5tdWx0KFZlY3Rvci5wZXJwKG9mZnNldEIpLCBib2R5Qi5hbmd1bGFyVmVsb2NpdHkpLCB0ZW1wRCksIFxuICAgICAgICAgICAgICAgICAgICByZWxhdGl2ZVZlbG9jaXR5ID0gVmVjdG9yLnN1Yih2ZWxvY2l0eVBvaW50QSwgdmVsb2NpdHlQb2ludEIsIHRlbXBFKSxcbiAgICAgICAgICAgICAgICAgICAgbm9ybWFsVmVsb2NpdHkgPSBWZWN0b3IuZG90KG5vcm1hbCwgcmVsYXRpdmVWZWxvY2l0eSk7XG5cbiAgICAgICAgICAgICAgICB2YXIgdGFuZ2VudFZlbG9jaXR5ID0gVmVjdG9yLmRvdCh0YW5nZW50LCByZWxhdGl2ZVZlbG9jaXR5KSxcbiAgICAgICAgICAgICAgICAgICAgdGFuZ2VudFNwZWVkID0gTWF0aC5hYnModGFuZ2VudFZlbG9jaXR5KSxcbiAgICAgICAgICAgICAgICAgICAgdGFuZ2VudFZlbG9jaXR5RGlyZWN0aW9uID0gQ29tbW9uLnNpZ24odGFuZ2VudFZlbG9jaXR5KTtcblxuICAgICAgICAgICAgICAgIC8vIHJhdyBpbXB1bHNlc1xuICAgICAgICAgICAgICAgIHZhciBub3JtYWxJbXB1bHNlID0gKDEgKyBwYWlyLnJlc3RpdHV0aW9uKSAqIG5vcm1hbFZlbG9jaXR5LFxuICAgICAgICAgICAgICAgICAgICBub3JtYWxGb3JjZSA9IENvbW1vbi5jbGFtcChwYWlyLnNlcGFyYXRpb24gKyBub3JtYWxWZWxvY2l0eSwgMCwgMSkgKiBSZXNvbHZlci5fZnJpY3Rpb25Ob3JtYWxNdWx0aXBsaWVyO1xuXG4gICAgICAgICAgICAgICAgLy8gY291bG9tYiBmcmljdGlvblxuICAgICAgICAgICAgICAgIHZhciB0YW5nZW50SW1wdWxzZSA9IHRhbmdlbnRWZWxvY2l0eSxcbiAgICAgICAgICAgICAgICAgICAgbWF4RnJpY3Rpb24gPSBJbmZpbml0eTtcblxuICAgICAgICAgICAgICAgIGlmICh0YW5nZW50U3BlZWQgPiBwYWlyLmZyaWN0aW9uICogcGFpci5mcmljdGlvblN0YXRpYyAqIG5vcm1hbEZvcmNlICogdGltZVNjYWxlU3F1YXJlZCkge1xuICAgICAgICAgICAgICAgICAgICBtYXhGcmljdGlvbiA9IHRhbmdlbnRTcGVlZDtcbiAgICAgICAgICAgICAgICAgICAgdGFuZ2VudEltcHVsc2UgPSBDb21tb24uY2xhbXAoXG4gICAgICAgICAgICAgICAgICAgICAgICBwYWlyLmZyaWN0aW9uICogdGFuZ2VudFZlbG9jaXR5RGlyZWN0aW9uICogdGltZVNjYWxlU3F1YXJlZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIC1tYXhGcmljdGlvbiwgbWF4RnJpY3Rpb25cbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBtb2RpZnkgaW1wdWxzZXMgYWNjb3VudGluZyBmb3IgbWFzcywgaW5lcnRpYSBhbmQgb2Zmc2V0XG4gICAgICAgICAgICAgICAgdmFyIG9BY04gPSBWZWN0b3IuY3Jvc3Mob2Zmc2V0QSwgbm9ybWFsKSxcbiAgICAgICAgICAgICAgICAgICAgb0JjTiA9IFZlY3Rvci5jcm9zcyhvZmZzZXRCLCBub3JtYWwpLFxuICAgICAgICAgICAgICAgICAgICBzaGFyZSA9IGNvbnRhY3RTaGFyZSAvIChib2R5QS5pbnZlcnNlTWFzcyArIGJvZHlCLmludmVyc2VNYXNzICsgYm9keUEuaW52ZXJzZUluZXJ0aWEgKiBvQWNOICogb0FjTiAgKyBib2R5Qi5pbnZlcnNlSW5lcnRpYSAqIG9CY04gKiBvQmNOKTtcblxuICAgICAgICAgICAgICAgIG5vcm1hbEltcHVsc2UgKj0gc2hhcmU7XG4gICAgICAgICAgICAgICAgdGFuZ2VudEltcHVsc2UgKj0gc2hhcmU7XG5cbiAgICAgICAgICAgICAgICAvLyBoYW5kbGUgaGlnaCB2ZWxvY2l0eSBhbmQgcmVzdGluZyBjb2xsaXNpb25zIHNlcGFyYXRlbHlcbiAgICAgICAgICAgICAgICBpZiAobm9ybWFsVmVsb2NpdHkgPCAwICYmIG5vcm1hbFZlbG9jaXR5ICogbm9ybWFsVmVsb2NpdHkgPiBSZXNvbHZlci5fcmVzdGluZ1RocmVzaCAqIHRpbWVTY2FsZVNxdWFyZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gaGlnaCBub3JtYWwgdmVsb2NpdHkgc28gY2xlYXIgY2FjaGVkIGNvbnRhY3Qgbm9ybWFsIGltcHVsc2VcbiAgICAgICAgICAgICAgICAgICAgY29udGFjdC5ub3JtYWxJbXB1bHNlID0gMDtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAvLyBzb2x2ZSByZXN0aW5nIGNvbGxpc2lvbiBjb25zdHJhaW50cyB1c2luZyBFcmluIENhdHRvJ3MgbWV0aG9kIChHREMwOClcbiAgICAgICAgICAgICAgICAgICAgLy8gaW1wdWxzZSBjb25zdHJhaW50IHRlbmRzIHRvIDBcbiAgICAgICAgICAgICAgICAgICAgdmFyIGNvbnRhY3ROb3JtYWxJbXB1bHNlID0gY29udGFjdC5ub3JtYWxJbXB1bHNlO1xuICAgICAgICAgICAgICAgICAgICBjb250YWN0Lm5vcm1hbEltcHVsc2UgPSBNYXRoLm1pbihjb250YWN0Lm5vcm1hbEltcHVsc2UgKyBub3JtYWxJbXB1bHNlLCAwKTtcbiAgICAgICAgICAgICAgICAgICAgbm9ybWFsSW1wdWxzZSA9IGNvbnRhY3Qubm9ybWFsSW1wdWxzZSAtIGNvbnRhY3ROb3JtYWxJbXB1bHNlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIGhhbmRsZSBoaWdoIHZlbG9jaXR5IGFuZCByZXN0aW5nIGNvbGxpc2lvbnMgc2VwYXJhdGVseVxuICAgICAgICAgICAgICAgIGlmICh0YW5nZW50VmVsb2NpdHkgKiB0YW5nZW50VmVsb2NpdHkgPiBSZXNvbHZlci5fcmVzdGluZ1RocmVzaFRhbmdlbnQgKiB0aW1lU2NhbGVTcXVhcmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGhpZ2ggdGFuZ2VudCB2ZWxvY2l0eSBzbyBjbGVhciBjYWNoZWQgY29udGFjdCB0YW5nZW50IGltcHVsc2VcbiAgICAgICAgICAgICAgICAgICAgY29udGFjdC50YW5nZW50SW1wdWxzZSA9IDA7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gc29sdmUgcmVzdGluZyBjb2xsaXNpb24gY29uc3RyYWludHMgdXNpbmcgRXJpbiBDYXR0bydzIG1ldGhvZCAoR0RDMDgpXG4gICAgICAgICAgICAgICAgICAgIC8vIHRhbmdlbnQgaW1wdWxzZSB0ZW5kcyB0byAtdGFuZ2VudFNwZWVkIG9yICt0YW5nZW50U3BlZWRcbiAgICAgICAgICAgICAgICAgICAgdmFyIGNvbnRhY3RUYW5nZW50SW1wdWxzZSA9IGNvbnRhY3QudGFuZ2VudEltcHVsc2U7XG4gICAgICAgICAgICAgICAgICAgIGNvbnRhY3QudGFuZ2VudEltcHVsc2UgPSBDb21tb24uY2xhbXAoY29udGFjdC50YW5nZW50SW1wdWxzZSArIHRhbmdlbnRJbXB1bHNlLCAtbWF4RnJpY3Rpb24sIG1heEZyaWN0aW9uKTtcbiAgICAgICAgICAgICAgICAgICAgdGFuZ2VudEltcHVsc2UgPSBjb250YWN0LnRhbmdlbnRJbXB1bHNlIC0gY29udGFjdFRhbmdlbnRJbXB1bHNlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIHRvdGFsIGltcHVsc2UgZnJvbSBjb250YWN0XG4gICAgICAgICAgICAgICAgaW1wdWxzZS54ID0gKG5vcm1hbC54ICogbm9ybWFsSW1wdWxzZSkgKyAodGFuZ2VudC54ICogdGFuZ2VudEltcHVsc2UpO1xuICAgICAgICAgICAgICAgIGltcHVsc2UueSA9IChub3JtYWwueSAqIG5vcm1hbEltcHVsc2UpICsgKHRhbmdlbnQueSAqIHRhbmdlbnRJbXB1bHNlKTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyBhcHBseSBpbXB1bHNlIGZyb20gY29udGFjdFxuICAgICAgICAgICAgICAgIGlmICghKGJvZHlBLmlzU3RhdGljIHx8IGJvZHlBLmlzU2xlZXBpbmcpKSB7XG4gICAgICAgICAgICAgICAgICAgIGJvZHlBLnBvc2l0aW9uUHJldi54ICs9IGltcHVsc2UueCAqIGJvZHlBLmludmVyc2VNYXNzO1xuICAgICAgICAgICAgICAgICAgICBib2R5QS5wb3NpdGlvblByZXYueSArPSBpbXB1bHNlLnkgKiBib2R5QS5pbnZlcnNlTWFzcztcbiAgICAgICAgICAgICAgICAgICAgYm9keUEuYW5nbGVQcmV2ICs9IFZlY3Rvci5jcm9zcyhvZmZzZXRBLCBpbXB1bHNlKSAqIGJvZHlBLmludmVyc2VJbmVydGlhO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICghKGJvZHlCLmlzU3RhdGljIHx8IGJvZHlCLmlzU2xlZXBpbmcpKSB7XG4gICAgICAgICAgICAgICAgICAgIGJvZHlCLnBvc2l0aW9uUHJldi54IC09IGltcHVsc2UueCAqIGJvZHlCLmludmVyc2VNYXNzO1xuICAgICAgICAgICAgICAgICAgICBib2R5Qi5wb3NpdGlvblByZXYueSAtPSBpbXB1bHNlLnkgKiBib2R5Qi5pbnZlcnNlTWFzcztcbiAgICAgICAgICAgICAgICAgICAgYm9keUIuYW5nbGVQcmV2IC09IFZlY3Rvci5jcm9zcyhvZmZzZXRCLCBpbXB1bHNlKSAqIGJvZHlCLmludmVyc2VJbmVydGlhO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbn0pKCk7XG5cbn0se1wiLi4vY29yZS9Db21tb25cIjoxNCxcIi4uL2dlb21ldHJ5L0JvdW5kc1wiOjI2LFwiLi4vZ2VvbWV0cnkvVmVjdG9yXCI6MjgsXCIuLi9nZW9tZXRyeS9WZXJ0aWNlc1wiOjI5fV0sMTE6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLlNBVGAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGRldGVjdGluZyBjb2xsaXNpb25zIHVzaW5nIHRoZSBTZXBhcmF0aW5nIEF4aXMgVGhlb3JlbS5cbipcbiogQGNsYXNzIFNBVFxuKi9cblxuLy8gVE9ETzogdHJ1ZSBjaXJjbGVzIGFuZCBjdXJ2ZXNcblxudmFyIFNBVCA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNBVDtcblxudmFyIFZlcnRpY2VzID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvVmVydGljZXMnKTtcbnZhciBWZWN0b3IgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZWN0b3InKTtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogRGV0ZWN0IGNvbGxpc2lvbiBiZXR3ZWVuIHR3byBib2RpZXMgdXNpbmcgdGhlIFNlcGFyYXRpbmcgQXhpcyBUaGVvcmVtLlxuICAgICAqIEBtZXRob2QgY29sbGlkZXNcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlBXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5QlxuICAgICAqIEBwYXJhbSB7Y29sbGlzaW9ufSBwcmV2aW91c0NvbGxpc2lvblxuICAgICAqIEByZXR1cm4ge2NvbGxpc2lvbn0gY29sbGlzaW9uXG4gICAgICovXG4gICAgU0FULmNvbGxpZGVzID0gZnVuY3Rpb24oYm9keUEsIGJvZHlCLCBwcmV2aW91c0NvbGxpc2lvbikge1xuICAgICAgICB2YXIgb3ZlcmxhcEFCLFxuICAgICAgICAgICAgb3ZlcmxhcEJBLCBcbiAgICAgICAgICAgIG1pbk92ZXJsYXAsXG4gICAgICAgICAgICBjb2xsaXNpb24sXG4gICAgICAgICAgICBjYW5SZXVzZVByZXZDb2wgPSBmYWxzZTtcblxuICAgICAgICBpZiAocHJldmlvdXNDb2xsaXNpb24pIHtcbiAgICAgICAgICAgIC8vIGVzdGltYXRlIHRvdGFsIG1vdGlvblxuICAgICAgICAgICAgdmFyIHBhcmVudEEgPSBib2R5QS5wYXJlbnQsXG4gICAgICAgICAgICAgICAgcGFyZW50QiA9IGJvZHlCLnBhcmVudCxcbiAgICAgICAgICAgICAgICBtb3Rpb24gPSBwYXJlbnRBLnNwZWVkICogcGFyZW50QS5zcGVlZCArIHBhcmVudEEuYW5ndWxhclNwZWVkICogcGFyZW50QS5hbmd1bGFyU3BlZWRcbiAgICAgICAgICAgICAgICAgICAgICAgKyBwYXJlbnRCLnNwZWVkICogcGFyZW50Qi5zcGVlZCArIHBhcmVudEIuYW5ndWxhclNwZWVkICogcGFyZW50Qi5hbmd1bGFyU3BlZWQ7XG5cbiAgICAgICAgICAgIC8vIHdlIG1heSBiZSBhYmxlIHRvIChwYXJ0aWFsbHkpIHJldXNlIGNvbGxpc2lvbiByZXN1bHQgXG4gICAgICAgICAgICAvLyBidXQgb25seSBzYWZlIGlmIGNvbGxpc2lvbiB3YXMgcmVzdGluZ1xuICAgICAgICAgICAgY2FuUmV1c2VQcmV2Q29sID0gcHJldmlvdXNDb2xsaXNpb24gJiYgcHJldmlvdXNDb2xsaXNpb24uY29sbGlkZWQgJiYgbW90aW9uIDwgMC4yO1xuXG4gICAgICAgICAgICAvLyByZXVzZSBjb2xsaXNpb24gb2JqZWN0XG4gICAgICAgICAgICBjb2xsaXNpb24gPSBwcmV2aW91c0NvbGxpc2lvbjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbGxpc2lvbiA9IHsgY29sbGlkZWQ6IGZhbHNlLCBib2R5QTogYm9keUEsIGJvZHlCOiBib2R5QiB9O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHByZXZpb3VzQ29sbGlzaW9uICYmIGNhblJldXNlUHJldkNvbCkge1xuICAgICAgICAgICAgLy8gaWYgd2UgY2FuIHJldXNlIHRoZSBjb2xsaXNpb24gcmVzdWx0XG4gICAgICAgICAgICAvLyB3ZSBvbmx5IG5lZWQgdG8gdGVzdCB0aGUgcHJldmlvdXNseSBmb3VuZCBheGlzXG4gICAgICAgICAgICB2YXIgYXhpc0JvZHlBID0gY29sbGlzaW9uLmF4aXNCb2R5LFxuICAgICAgICAgICAgICAgIGF4aXNCb2R5QiA9IGF4aXNCb2R5QSA9PT0gYm9keUEgPyBib2R5QiA6IGJvZHlBLFxuICAgICAgICAgICAgICAgIGF4ZXMgPSBbYXhpc0JvZHlBLmF4ZXNbcHJldmlvdXNDb2xsaXNpb24uYXhpc051bWJlcl1dO1xuXG4gICAgICAgICAgICBtaW5PdmVybGFwID0gX292ZXJsYXBBeGVzKGF4aXNCb2R5QS52ZXJ0aWNlcywgYXhpc0JvZHlCLnZlcnRpY2VzLCBheGVzKTtcbiAgICAgICAgICAgIGNvbGxpc2lvbi5yZXVzZWQgPSB0cnVlO1xuXG4gICAgICAgICAgICBpZiAobWluT3ZlcmxhcC5vdmVybGFwIDw9IDApIHtcbiAgICAgICAgICAgICAgICBjb2xsaXNpb24uY29sbGlkZWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICByZXR1cm4gY29sbGlzaW9uO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gaWYgd2UgY2FuJ3QgcmV1c2UgYSByZXN1bHQsIHBlcmZvcm0gYSBmdWxsIFNBVCB0ZXN0XG5cbiAgICAgICAgICAgIG92ZXJsYXBBQiA9IF9vdmVybGFwQXhlcyhib2R5QS52ZXJ0aWNlcywgYm9keUIudmVydGljZXMsIGJvZHlBLmF4ZXMpO1xuXG4gICAgICAgICAgICBpZiAob3ZlcmxhcEFCLm92ZXJsYXAgPD0gMCkge1xuICAgICAgICAgICAgICAgIGNvbGxpc2lvbi5jb2xsaWRlZCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHJldHVybiBjb2xsaXNpb247XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG92ZXJsYXBCQSA9IF9vdmVybGFwQXhlcyhib2R5Qi52ZXJ0aWNlcywgYm9keUEudmVydGljZXMsIGJvZHlCLmF4ZXMpO1xuXG4gICAgICAgICAgICBpZiAob3ZlcmxhcEJBLm92ZXJsYXAgPD0gMCkge1xuICAgICAgICAgICAgICAgIGNvbGxpc2lvbi5jb2xsaWRlZCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHJldHVybiBjb2xsaXNpb247XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChvdmVybGFwQUIub3ZlcmxhcCA8IG92ZXJsYXBCQS5vdmVybGFwKSB7XG4gICAgICAgICAgICAgICAgbWluT3ZlcmxhcCA9IG92ZXJsYXBBQjtcbiAgICAgICAgICAgICAgICBjb2xsaXNpb24uYXhpc0JvZHkgPSBib2R5QTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbWluT3ZlcmxhcCA9IG92ZXJsYXBCQTtcbiAgICAgICAgICAgICAgICBjb2xsaXNpb24uYXhpc0JvZHkgPSBib2R5QjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gaW1wb3J0YW50IGZvciByZXVzZSBsYXRlclxuICAgICAgICAgICAgY29sbGlzaW9uLmF4aXNOdW1iZXIgPSBtaW5PdmVybGFwLmF4aXNOdW1iZXI7XG4gICAgICAgIH1cblxuICAgICAgICBjb2xsaXNpb24uYm9keUEgPSBib2R5QS5pZCA8IGJvZHlCLmlkID8gYm9keUEgOiBib2R5QjtcbiAgICAgICAgY29sbGlzaW9uLmJvZHlCID0gYm9keUEuaWQgPCBib2R5Qi5pZCA/IGJvZHlCIDogYm9keUE7XG4gICAgICAgIGNvbGxpc2lvbi5jb2xsaWRlZCA9IHRydWU7XG4gICAgICAgIGNvbGxpc2lvbi5kZXB0aCA9IG1pbk92ZXJsYXAub3ZlcmxhcDtcbiAgICAgICAgY29sbGlzaW9uLnBhcmVudEEgPSBjb2xsaXNpb24uYm9keUEucGFyZW50O1xuICAgICAgICBjb2xsaXNpb24ucGFyZW50QiA9IGNvbGxpc2lvbi5ib2R5Qi5wYXJlbnQ7XG4gICAgICAgIFxuICAgICAgICBib2R5QSA9IGNvbGxpc2lvbi5ib2R5QTtcbiAgICAgICAgYm9keUIgPSBjb2xsaXNpb24uYm9keUI7XG5cbiAgICAgICAgLy8gZW5zdXJlIG5vcm1hbCBpcyBmYWNpbmcgYXdheSBmcm9tIGJvZHlBXG4gICAgICAgIGlmIChWZWN0b3IuZG90KG1pbk92ZXJsYXAuYXhpcywgVmVjdG9yLnN1Yihib2R5Qi5wb3NpdGlvbiwgYm9keUEucG9zaXRpb24pKSA8IDApIHtcbiAgICAgICAgICAgIGNvbGxpc2lvbi5ub3JtYWwgPSB7XG4gICAgICAgICAgICAgICAgeDogbWluT3ZlcmxhcC5heGlzLngsXG4gICAgICAgICAgICAgICAgeTogbWluT3ZlcmxhcC5heGlzLnlcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb2xsaXNpb24ubm9ybWFsID0ge1xuICAgICAgICAgICAgICAgIHg6IC1taW5PdmVybGFwLmF4aXMueCxcbiAgICAgICAgICAgICAgICB5OiAtbWluT3ZlcmxhcC5heGlzLnlcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cblxuICAgICAgICBjb2xsaXNpb24udGFuZ2VudCA9IFZlY3Rvci5wZXJwKGNvbGxpc2lvbi5ub3JtYWwpO1xuXG4gICAgICAgIGNvbGxpc2lvbi5wZW5ldHJhdGlvbiA9IGNvbGxpc2lvbi5wZW5ldHJhdGlvbiB8fCB7fTtcbiAgICAgICAgY29sbGlzaW9uLnBlbmV0cmF0aW9uLnggPSBjb2xsaXNpb24ubm9ybWFsLnggKiBjb2xsaXNpb24uZGVwdGg7XG4gICAgICAgIGNvbGxpc2lvbi5wZW5ldHJhdGlvbi55ID0gY29sbGlzaW9uLm5vcm1hbC55ICogY29sbGlzaW9uLmRlcHRoOyBcblxuICAgICAgICAvLyBmaW5kIHN1cHBvcnQgcG9pbnRzLCB0aGVyZSBpcyBhbHdheXMgZWl0aGVyIGV4YWN0bHkgb25lIG9yIHR3b1xuICAgICAgICB2YXIgdmVydGljZXNCID0gX2ZpbmRTdXBwb3J0cyhib2R5QSwgYm9keUIsIGNvbGxpc2lvbi5ub3JtYWwpLFxuICAgICAgICAgICAgc3VwcG9ydHMgPSBbXTtcblxuICAgICAgICAvLyBmaW5kIHRoZSBzdXBwb3J0cyBmcm9tIGJvZHlCIHRoYXQgYXJlIGluc2lkZSBib2R5QVxuICAgICAgICBpZiAoVmVydGljZXMuY29udGFpbnMoYm9keUEudmVydGljZXMsIHZlcnRpY2VzQlswXSkpXG4gICAgICAgICAgICBzdXBwb3J0cy5wdXNoKHZlcnRpY2VzQlswXSk7XG5cbiAgICAgICAgaWYgKFZlcnRpY2VzLmNvbnRhaW5zKGJvZHlBLnZlcnRpY2VzLCB2ZXJ0aWNlc0JbMV0pKVxuICAgICAgICAgICAgc3VwcG9ydHMucHVzaCh2ZXJ0aWNlc0JbMV0pO1xuXG4gICAgICAgIC8vIGZpbmQgdGhlIHN1cHBvcnRzIGZyb20gYm9keUEgdGhhdCBhcmUgaW5zaWRlIGJvZHlCXG4gICAgICAgIGlmIChzdXBwb3J0cy5sZW5ndGggPCAyKSB7XG4gICAgICAgICAgICB2YXIgdmVydGljZXNBID0gX2ZpbmRTdXBwb3J0cyhib2R5QiwgYm9keUEsIFZlY3Rvci5uZWcoY29sbGlzaW9uLm5vcm1hbCkpO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKFZlcnRpY2VzLmNvbnRhaW5zKGJvZHlCLnZlcnRpY2VzLCB2ZXJ0aWNlc0FbMF0pKVxuICAgICAgICAgICAgICAgIHN1cHBvcnRzLnB1c2godmVydGljZXNBWzBdKTtcblxuICAgICAgICAgICAgaWYgKHN1cHBvcnRzLmxlbmd0aCA8IDIgJiYgVmVydGljZXMuY29udGFpbnMoYm9keUIudmVydGljZXMsIHZlcnRpY2VzQVsxXSkpXG4gICAgICAgICAgICAgICAgc3VwcG9ydHMucHVzaCh2ZXJ0aWNlc0FbMV0pO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gYWNjb3VudCBmb3IgdGhlIGVkZ2UgY2FzZSBvZiBvdmVybGFwcGluZyBidXQgbm8gdmVydGV4IGNvbnRhaW5tZW50XG4gICAgICAgIGlmIChzdXBwb3J0cy5sZW5ndGggPCAxKVxuICAgICAgICAgICAgc3VwcG9ydHMgPSBbdmVydGljZXNCWzBdXTtcbiAgICAgICAgXG4gICAgICAgIGNvbGxpc2lvbi5zdXBwb3J0cyA9IHN1cHBvcnRzO1xuXG4gICAgICAgIHJldHVybiBjb2xsaXNpb247XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEZpbmQgdGhlIG92ZXJsYXAgYmV0d2VlbiB0d28gc2V0cyBvZiB2ZXJ0aWNlcy5cbiAgICAgKiBAbWV0aG9kIF9vdmVybGFwQXhlc1xuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IHZlcnRpY2VzQVxuICAgICAqIEBwYXJhbSB7fSB2ZXJ0aWNlc0JcbiAgICAgKiBAcGFyYW0ge30gYXhlc1xuICAgICAqIEByZXR1cm4gcmVzdWx0XG4gICAgICovXG4gICAgdmFyIF9vdmVybGFwQXhlcyA9IGZ1bmN0aW9uKHZlcnRpY2VzQSwgdmVydGljZXNCLCBheGVzKSB7XG4gICAgICAgIHZhciBwcm9qZWN0aW9uQSA9IFZlY3Rvci5fdGVtcFswXSwgXG4gICAgICAgICAgICBwcm9qZWN0aW9uQiA9IFZlY3Rvci5fdGVtcFsxXSxcbiAgICAgICAgICAgIHJlc3VsdCA9IHsgb3ZlcmxhcDogTnVtYmVyLk1BWF9WQUxVRSB9LFxuICAgICAgICAgICAgb3ZlcmxhcCxcbiAgICAgICAgICAgIGF4aXM7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBheGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBheGlzID0gYXhlc1tpXTtcblxuICAgICAgICAgICAgX3Byb2plY3RUb0F4aXMocHJvamVjdGlvbkEsIHZlcnRpY2VzQSwgYXhpcyk7XG4gICAgICAgICAgICBfcHJvamVjdFRvQXhpcyhwcm9qZWN0aW9uQiwgdmVydGljZXNCLCBheGlzKTtcblxuICAgICAgICAgICAgb3ZlcmxhcCA9IE1hdGgubWluKHByb2plY3Rpb25BLm1heCAtIHByb2plY3Rpb25CLm1pbiwgcHJvamVjdGlvbkIubWF4IC0gcHJvamVjdGlvbkEubWluKTtcblxuICAgICAgICAgICAgaWYgKG92ZXJsYXAgPD0gMCkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5vdmVybGFwID0gb3ZlcmxhcDtcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAob3ZlcmxhcCA8IHJlc3VsdC5vdmVybGFwKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0Lm92ZXJsYXAgPSBvdmVybGFwO1xuICAgICAgICAgICAgICAgIHJlc3VsdC5heGlzID0gYXhpcztcbiAgICAgICAgICAgICAgICByZXN1bHQuYXhpc051bWJlciA9IGk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBQcm9qZWN0cyB2ZXJ0aWNlcyBvbiBhbiBheGlzIGFuZCByZXR1cm5zIGFuIGludGVydmFsLlxuICAgICAqIEBtZXRob2QgX3Byb2plY3RUb0F4aXNcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7fSBwcm9qZWN0aW9uXG4gICAgICogQHBhcmFtIHt9IHZlcnRpY2VzXG4gICAgICogQHBhcmFtIHt9IGF4aXNcbiAgICAgKi9cbiAgICB2YXIgX3Byb2plY3RUb0F4aXMgPSBmdW5jdGlvbihwcm9qZWN0aW9uLCB2ZXJ0aWNlcywgYXhpcykge1xuICAgICAgICB2YXIgbWluID0gVmVjdG9yLmRvdCh2ZXJ0aWNlc1swXSwgYXhpcyksXG4gICAgICAgICAgICBtYXggPSBtaW47XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCB2ZXJ0aWNlcy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICAgICAgdmFyIGRvdCA9IFZlY3Rvci5kb3QodmVydGljZXNbaV0sIGF4aXMpO1xuXG4gICAgICAgICAgICBpZiAoZG90ID4gbWF4KSB7IFxuICAgICAgICAgICAgICAgIG1heCA9IGRvdDsgXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGRvdCA8IG1pbikgeyBcbiAgICAgICAgICAgICAgICBtaW4gPSBkb3Q7IFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcHJvamVjdGlvbi5taW4gPSBtaW47XG4gICAgICAgIHByb2plY3Rpb24ubWF4ID0gbWF4O1xuICAgIH07XG4gICAgXG4gICAgLyoqXG4gICAgICogRmluZHMgc3VwcG9ydGluZyB2ZXJ0aWNlcyBnaXZlbiB0d28gYm9kaWVzIGFsb25nIGEgZ2l2ZW4gZGlyZWN0aW9uIHVzaW5nIGhpbGwtY2xpbWJpbmcuXG4gICAgICogQG1ldGhvZCBfZmluZFN1cHBvcnRzXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge30gYm9keUFcbiAgICAgKiBAcGFyYW0ge30gYm9keUJcbiAgICAgKiBAcGFyYW0ge30gbm9ybWFsXG4gICAgICogQHJldHVybiBbdmVjdG9yXVxuICAgICAqL1xuICAgIHZhciBfZmluZFN1cHBvcnRzID0gZnVuY3Rpb24oYm9keUEsIGJvZHlCLCBub3JtYWwpIHtcbiAgICAgICAgdmFyIG5lYXJlc3REaXN0YW5jZSA9IE51bWJlci5NQVhfVkFMVUUsXG4gICAgICAgICAgICB2ZXJ0ZXhUb0JvZHkgPSBWZWN0b3IuX3RlbXBbMF0sXG4gICAgICAgICAgICB2ZXJ0aWNlcyA9IGJvZHlCLnZlcnRpY2VzLFxuICAgICAgICAgICAgYm9keUFQb3NpdGlvbiA9IGJvZHlBLnBvc2l0aW9uLFxuICAgICAgICAgICAgZGlzdGFuY2UsXG4gICAgICAgICAgICB2ZXJ0ZXgsXG4gICAgICAgICAgICB2ZXJ0ZXhBLFxuICAgICAgICAgICAgdmVydGV4QjtcblxuICAgICAgICAvLyBmaW5kIGNsb3Nlc3QgdmVydGV4IG9uIGJvZHlCXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmVydGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZlcnRleCA9IHZlcnRpY2VzW2ldO1xuICAgICAgICAgICAgdmVydGV4VG9Cb2R5LnggPSB2ZXJ0ZXgueCAtIGJvZHlBUG9zaXRpb24ueDtcbiAgICAgICAgICAgIHZlcnRleFRvQm9keS55ID0gdmVydGV4LnkgLSBib2R5QVBvc2l0aW9uLnk7XG4gICAgICAgICAgICBkaXN0YW5jZSA9IC1WZWN0b3IuZG90KG5vcm1hbCwgdmVydGV4VG9Cb2R5KTtcblxuICAgICAgICAgICAgaWYgKGRpc3RhbmNlIDwgbmVhcmVzdERpc3RhbmNlKSB7XG4gICAgICAgICAgICAgICAgbmVhcmVzdERpc3RhbmNlID0gZGlzdGFuY2U7XG4gICAgICAgICAgICAgICAgdmVydGV4QSA9IHZlcnRleDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGZpbmQgbmV4dCBjbG9zZXN0IHZlcnRleCB1c2luZyB0aGUgdHdvIGNvbm5lY3RlZCB0byBpdFxuICAgICAgICB2YXIgcHJldkluZGV4ID0gdmVydGV4QS5pbmRleCAtIDEgPj0gMCA/IHZlcnRleEEuaW5kZXggLSAxIDogdmVydGljZXMubGVuZ3RoIC0gMTtcbiAgICAgICAgdmVydGV4ID0gdmVydGljZXNbcHJldkluZGV4XTtcbiAgICAgICAgdmVydGV4VG9Cb2R5LnggPSB2ZXJ0ZXgueCAtIGJvZHlBUG9zaXRpb24ueDtcbiAgICAgICAgdmVydGV4VG9Cb2R5LnkgPSB2ZXJ0ZXgueSAtIGJvZHlBUG9zaXRpb24ueTtcbiAgICAgICAgbmVhcmVzdERpc3RhbmNlID0gLVZlY3Rvci5kb3Qobm9ybWFsLCB2ZXJ0ZXhUb0JvZHkpO1xuICAgICAgICB2ZXJ0ZXhCID0gdmVydGV4O1xuXG4gICAgICAgIHZhciBuZXh0SW5kZXggPSAodmVydGV4QS5pbmRleCArIDEpICUgdmVydGljZXMubGVuZ3RoO1xuICAgICAgICB2ZXJ0ZXggPSB2ZXJ0aWNlc1tuZXh0SW5kZXhdO1xuICAgICAgICB2ZXJ0ZXhUb0JvZHkueCA9IHZlcnRleC54IC0gYm9keUFQb3NpdGlvbi54O1xuICAgICAgICB2ZXJ0ZXhUb0JvZHkueSA9IHZlcnRleC55IC0gYm9keUFQb3NpdGlvbi55O1xuICAgICAgICBkaXN0YW5jZSA9IC1WZWN0b3IuZG90KG5vcm1hbCwgdmVydGV4VG9Cb2R5KTtcbiAgICAgICAgaWYgKGRpc3RhbmNlIDwgbmVhcmVzdERpc3RhbmNlKSB7XG4gICAgICAgICAgICB2ZXJ0ZXhCID0gdmVydGV4O1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIFt2ZXJ0ZXhBLCB2ZXJ0ZXhCXTtcbiAgICB9O1xuXG59KSgpO1xuXG59LHtcIi4uL2dlb21ldHJ5L1ZlY3RvclwiOjI4LFwiLi4vZ2VvbWV0cnkvVmVydGljZXNcIjoyOX1dLDEyOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5Db25zdHJhaW50YCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyBmb3IgY3JlYXRpbmcgYW5kIG1hbmlwdWxhdGluZyBjb25zdHJhaW50cy5cbiogQ29uc3RyYWludHMgYXJlIHVzZWQgZm9yIHNwZWNpZnlpbmcgdGhhdCBhIGZpeGVkIGRpc3RhbmNlIG11c3QgYmUgbWFpbnRhaW5lZCBiZXR3ZWVuIHR3byBib2RpZXMgKG9yIGEgYm9keSBhbmQgYSBmaXhlZCB3b3JsZC1zcGFjZSBwb3NpdGlvbikuXG4qIFRoZSBzdGlmZm5lc3Mgb2YgY29uc3RyYWludHMgY2FuIGJlIG1vZGlmaWVkIHRvIGNyZWF0ZSBzcHJpbmdzIG9yIGVsYXN0aWMuXG4qXG4qIFNlZSB0aGUgaW5jbHVkZWQgdXNhZ2UgW2V4YW1wbGVzXShodHRwczovL2dpdGh1Yi5jb20vbGlhYnJ1L21hdHRlci1qcy90cmVlL21hc3Rlci9leGFtcGxlcykuXG4qXG4qIEBjbGFzcyBDb25zdHJhaW50XG4qL1xuXG52YXIgQ29uc3RyYWludCA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IENvbnN0cmFpbnQ7XG5cbnZhciBWZXJ0aWNlcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L1ZlcnRpY2VzJyk7XG52YXIgVmVjdG9yID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvVmVjdG9yJyk7XG52YXIgU2xlZXBpbmcgPSBfZGVyZXFfKCcuLi9jb3JlL1NsZWVwaW5nJyk7XG52YXIgQm91bmRzID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvQm91bmRzJyk7XG52YXIgQXhlcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L0F4ZXMnKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICBDb25zdHJhaW50Ll93YXJtaW5nID0gMC40O1xuICAgIENvbnN0cmFpbnQuX3RvcnF1ZURhbXBlbiA9IDE7XG4gICAgQ29uc3RyYWludC5fbWluTGVuZ3RoID0gMC4wMDAwMDE7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IGNvbnN0cmFpbnQuXG4gICAgICogQWxsIHByb3BlcnRpZXMgaGF2ZSBkZWZhdWx0IHZhbHVlcywgYW5kIG1hbnkgYXJlIHByZS1jYWxjdWxhdGVkIGF1dG9tYXRpY2FsbHkgYmFzZWQgb24gb3RoZXIgcHJvcGVydGllcy5cbiAgICAgKiBUbyBzaW11bGF0ZSBhIHJldm9sdXRlIGNvbnN0cmFpbnQgKG9yIHBpbiBqb2ludCkgc2V0IGBsZW5ndGg6IDBgIGFuZCBhIGhpZ2ggYHN0aWZmbmVzc2AgdmFsdWUgKGUuZy4gYDAuN2Agb3IgYWJvdmUpLlxuICAgICAqIElmIHRoZSBjb25zdHJhaW50IGlzIHVuc3RhYmxlLCB0cnkgbG93ZXJpbmcgdGhlIGBzdGlmZm5lc3NgIHZhbHVlIGFuZCAvIG9yIGluY3JlYXNpbmcgYGVuZ2luZS5jb25zdHJhaW50SXRlcmF0aW9uc2AuXG4gICAgICogU2VlIHRoZSBwcm9wZXJ0aWVzIHNlY3Rpb24gYmVsb3cgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIG9uIHdoYXQgeW91IGNhbiBwYXNzIHZpYSB0aGUgYG9wdGlvbnNgIG9iamVjdC5cbiAgICAgKiBAbWV0aG9kIGNyZWF0ZVxuICAgICAqIEBwYXJhbSB7fSBvcHRpb25zXG4gICAgICogQHJldHVybiB7Y29uc3RyYWludH0gY29uc3RyYWludFxuICAgICAqL1xuICAgIENvbnN0cmFpbnQuY3JlYXRlID0gZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgICB2YXIgY29uc3RyYWludCA9IG9wdGlvbnM7XG5cbiAgICAgICAgLy8gaWYgYm9kaWVzIGRlZmluZWQgYnV0IG5vIHBvaW50cywgdXNlIGJvZHkgY2VudHJlXG4gICAgICAgIGlmIChjb25zdHJhaW50LmJvZHlBICYmICFjb25zdHJhaW50LnBvaW50QSlcbiAgICAgICAgICAgIGNvbnN0cmFpbnQucG9pbnRBID0geyB4OiAwLCB5OiAwIH07XG4gICAgICAgIGlmIChjb25zdHJhaW50LmJvZHlCICYmICFjb25zdHJhaW50LnBvaW50QilcbiAgICAgICAgICAgIGNvbnN0cmFpbnQucG9pbnRCID0geyB4OiAwLCB5OiAwIH07XG5cbiAgICAgICAgLy8gY2FsY3VsYXRlIHN0YXRpYyBsZW5ndGggdXNpbmcgaW5pdGlhbCB3b3JsZCBzcGFjZSBwb2ludHNcbiAgICAgICAgdmFyIGluaXRpYWxQb2ludEEgPSBjb25zdHJhaW50LmJvZHlBID8gVmVjdG9yLmFkZChjb25zdHJhaW50LmJvZHlBLnBvc2l0aW9uLCBjb25zdHJhaW50LnBvaW50QSkgOiBjb25zdHJhaW50LnBvaW50QSxcbiAgICAgICAgICAgIGluaXRpYWxQb2ludEIgPSBjb25zdHJhaW50LmJvZHlCID8gVmVjdG9yLmFkZChjb25zdHJhaW50LmJvZHlCLnBvc2l0aW9uLCBjb25zdHJhaW50LnBvaW50QikgOiBjb25zdHJhaW50LnBvaW50QixcbiAgICAgICAgICAgIGxlbmd0aCA9IFZlY3Rvci5tYWduaXR1ZGUoVmVjdG9yLnN1Yihpbml0aWFsUG9pbnRBLCBpbml0aWFsUG9pbnRCKSk7XG4gICAgXG4gICAgICAgIGNvbnN0cmFpbnQubGVuZ3RoID0gdHlwZW9mIGNvbnN0cmFpbnQubGVuZ3RoICE9PSAndW5kZWZpbmVkJyA/IGNvbnN0cmFpbnQubGVuZ3RoIDogbGVuZ3RoO1xuXG4gICAgICAgIC8vIG9wdGlvbiBkZWZhdWx0c1xuICAgICAgICBjb25zdHJhaW50LmlkID0gY29uc3RyYWludC5pZCB8fCBDb21tb24ubmV4dElkKCk7XG4gICAgICAgIGNvbnN0cmFpbnQubGFiZWwgPSBjb25zdHJhaW50LmxhYmVsIHx8ICdDb25zdHJhaW50JztcbiAgICAgICAgY29uc3RyYWludC50eXBlID0gJ2NvbnN0cmFpbnQnO1xuICAgICAgICBjb25zdHJhaW50LnN0aWZmbmVzcyA9IGNvbnN0cmFpbnQuc3RpZmZuZXNzIHx8IChjb25zdHJhaW50Lmxlbmd0aCA+IDAgPyAxIDogMC43KTtcbiAgICAgICAgY29uc3RyYWludC5kYW1waW5nID0gY29uc3RyYWludC5kYW1waW5nIHx8IDA7XG4gICAgICAgIGNvbnN0cmFpbnQuYW5ndWxhclN0aWZmbmVzcyA9IGNvbnN0cmFpbnQuYW5ndWxhclN0aWZmbmVzcyB8fCAwO1xuICAgICAgICBjb25zdHJhaW50LmFuZ2xlQSA9IGNvbnN0cmFpbnQuYm9keUEgPyBjb25zdHJhaW50LmJvZHlBLmFuZ2xlIDogY29uc3RyYWludC5hbmdsZUE7XG4gICAgICAgIGNvbnN0cmFpbnQuYW5nbGVCID0gY29uc3RyYWludC5ib2R5QiA/IGNvbnN0cmFpbnQuYm9keUIuYW5nbGUgOiBjb25zdHJhaW50LmFuZ2xlQjtcbiAgICAgICAgY29uc3RyYWludC5wbHVnaW4gPSB7fTtcblxuICAgICAgICAvLyByZW5kZXJcbiAgICAgICAgdmFyIHJlbmRlciA9IHtcbiAgICAgICAgICAgIHZpc2libGU6IHRydWUsXG4gICAgICAgICAgICBsaW5lV2lkdGg6IDIsXG4gICAgICAgICAgICBzdHJva2VTdHlsZTogJyNmZmZmZmYnLFxuICAgICAgICAgICAgdHlwZTogJ2xpbmUnLFxuICAgICAgICAgICAgYW5jaG9yczogdHJ1ZVxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChjb25zdHJhaW50Lmxlbmd0aCA9PT0gMCAmJiBjb25zdHJhaW50LnN0aWZmbmVzcyA+IDAuMSkge1xuICAgICAgICAgICAgcmVuZGVyLnR5cGUgPSAncGluJztcbiAgICAgICAgICAgIHJlbmRlci5hbmNob3JzID0gZmFsc2U7XG4gICAgICAgIH0gZWxzZSBpZiAoY29uc3RyYWludC5zdGlmZm5lc3MgPCAwLjkpIHtcbiAgICAgICAgICAgIHJlbmRlci50eXBlID0gJ3NwcmluZyc7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdHJhaW50LnJlbmRlciA9IENvbW1vbi5leHRlbmQocmVuZGVyLCBjb25zdHJhaW50LnJlbmRlcik7XG5cbiAgICAgICAgcmV0dXJuIGNvbnN0cmFpbnQ7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFByZXBhcmVzIGZvciBzb2x2aW5nIGJ5IGNvbnN0cmFpbnQgd2FybWluZy5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgcHJlU29sdmVBbGxcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICovXG4gICAgQ29uc3RyYWludC5wcmVTb2x2ZUFsbCA9IGZ1bmN0aW9uKGJvZGllcykge1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICAgICAgdmFyIGJvZHkgPSBib2RpZXNbaV0sXG4gICAgICAgICAgICAgICAgaW1wdWxzZSA9IGJvZHkuY29uc3RyYWludEltcHVsc2U7XG5cbiAgICAgICAgICAgIGlmIChib2R5LmlzU3RhdGljIHx8IChpbXB1bHNlLnggPT09IDAgJiYgaW1wdWxzZS55ID09PSAwICYmIGltcHVsc2UuYW5nbGUgPT09IDApKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGJvZHkucG9zaXRpb24ueCArPSBpbXB1bHNlLng7XG4gICAgICAgICAgICBib2R5LnBvc2l0aW9uLnkgKz0gaW1wdWxzZS55O1xuICAgICAgICAgICAgYm9keS5hbmdsZSArPSBpbXB1bHNlLmFuZ2xlO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNvbHZlcyBhbGwgY29uc3RyYWludHMgaW4gYSBsaXN0IG9mIGNvbGxpc2lvbnMuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHNvbHZlQWxsXG4gICAgICogQHBhcmFtIHtjb25zdHJhaW50W119IGNvbnN0cmFpbnRzXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHRpbWVTY2FsZVxuICAgICAqL1xuICAgIENvbnN0cmFpbnQuc29sdmVBbGwgPSBmdW5jdGlvbihjb25zdHJhaW50cywgdGltZVNjYWxlKSB7XG4gICAgICAgIC8vIFNvbHZlIGZpeGVkIGNvbnN0cmFpbnRzIGZpcnN0LlxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNvbnN0cmFpbnRzLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICAgICAgICB2YXIgY29uc3RyYWludCA9IGNvbnN0cmFpbnRzW2ldLFxuICAgICAgICAgICAgICAgIGZpeGVkQSA9ICFjb25zdHJhaW50LmJvZHlBIHx8IChjb25zdHJhaW50LmJvZHlBICYmIGNvbnN0cmFpbnQuYm9keUEuaXNTdGF0aWMpLFxuICAgICAgICAgICAgICAgIGZpeGVkQiA9ICFjb25zdHJhaW50LmJvZHlCIHx8IChjb25zdHJhaW50LmJvZHlCICYmIGNvbnN0cmFpbnQuYm9keUIuaXNTdGF0aWMpO1xuXG4gICAgICAgICAgICBpZiAoZml4ZWRBIHx8IGZpeGVkQikge1xuICAgICAgICAgICAgICAgIENvbnN0cmFpbnQuc29sdmUoY29uc3RyYWludHNbaV0sIHRpbWVTY2FsZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTb2x2ZSBmcmVlIGNvbnN0cmFpbnRzIGxhc3QuXG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBjb25zdHJhaW50cy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICAgICAgY29uc3RyYWludCA9IGNvbnN0cmFpbnRzW2ldO1xuICAgICAgICAgICAgZml4ZWRBID0gIWNvbnN0cmFpbnQuYm9keUEgfHwgKGNvbnN0cmFpbnQuYm9keUEgJiYgY29uc3RyYWludC5ib2R5QS5pc1N0YXRpYyk7XG4gICAgICAgICAgICBmaXhlZEIgPSAhY29uc3RyYWludC5ib2R5QiB8fCAoY29uc3RyYWludC5ib2R5QiAmJiBjb25zdHJhaW50LmJvZHlCLmlzU3RhdGljKTtcblxuICAgICAgICAgICAgaWYgKCFmaXhlZEEgJiYgIWZpeGVkQikge1xuICAgICAgICAgICAgICAgIENvbnN0cmFpbnQuc29sdmUoY29uc3RyYWludHNbaV0sIHRpbWVTY2FsZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU29sdmVzIGEgZGlzdGFuY2UgY29uc3RyYWludCB3aXRoIEdhdXNzLVNpZWRlbCBtZXRob2QuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHNvbHZlXG4gICAgICogQHBhcmFtIHtjb25zdHJhaW50fSBjb25zdHJhaW50XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHRpbWVTY2FsZVxuICAgICAqL1xuICAgIENvbnN0cmFpbnQuc29sdmUgPSBmdW5jdGlvbihjb25zdHJhaW50LCB0aW1lU2NhbGUpIHtcbiAgICAgICAgdmFyIGJvZHlBID0gY29uc3RyYWludC5ib2R5QSxcbiAgICAgICAgICAgIGJvZHlCID0gY29uc3RyYWludC5ib2R5QixcbiAgICAgICAgICAgIHBvaW50QSA9IGNvbnN0cmFpbnQucG9pbnRBLFxuICAgICAgICAgICAgcG9pbnRCID0gY29uc3RyYWludC5wb2ludEI7XG5cbiAgICAgICAgaWYgKCFib2R5QSAmJiAhYm9keUIpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgLy8gdXBkYXRlIHJlZmVyZW5jZSBhbmdsZVxuICAgICAgICBpZiAoYm9keUEgJiYgIWJvZHlBLmlzU3RhdGljKSB7XG4gICAgICAgICAgICBWZWN0b3Iucm90YXRlKHBvaW50QSwgYm9keUEuYW5nbGUgLSBjb25zdHJhaW50LmFuZ2xlQSwgcG9pbnRBKTtcbiAgICAgICAgICAgIGNvbnN0cmFpbnQuYW5nbGVBID0gYm9keUEuYW5nbGU7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIHVwZGF0ZSByZWZlcmVuY2UgYW5nbGVcbiAgICAgICAgaWYgKGJvZHlCICYmICFib2R5Qi5pc1N0YXRpYykge1xuICAgICAgICAgICAgVmVjdG9yLnJvdGF0ZShwb2ludEIsIGJvZHlCLmFuZ2xlIC0gY29uc3RyYWludC5hbmdsZUIsIHBvaW50Qik7XG4gICAgICAgICAgICBjb25zdHJhaW50LmFuZ2xlQiA9IGJvZHlCLmFuZ2xlO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHBvaW50QVdvcmxkID0gcG9pbnRBLFxuICAgICAgICAgICAgcG9pbnRCV29ybGQgPSBwb2ludEI7XG5cbiAgICAgICAgaWYgKGJvZHlBKSBwb2ludEFXb3JsZCA9IFZlY3Rvci5hZGQoYm9keUEucG9zaXRpb24sIHBvaW50QSk7XG4gICAgICAgIGlmIChib2R5QikgcG9pbnRCV29ybGQgPSBWZWN0b3IuYWRkKGJvZHlCLnBvc2l0aW9uLCBwb2ludEIpO1xuXG4gICAgICAgIGlmICghcG9pbnRBV29ybGQgfHwgIXBvaW50QldvcmxkKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBkZWx0YSA9IFZlY3Rvci5zdWIocG9pbnRBV29ybGQsIHBvaW50QldvcmxkKSxcbiAgICAgICAgICAgIGN1cnJlbnRMZW5ndGggPSBWZWN0b3IubWFnbml0dWRlKGRlbHRhKTtcblxuICAgICAgICAvLyBwcmV2ZW50IHNpbmd1bGFyaXR5XG4gICAgICAgIGlmIChjdXJyZW50TGVuZ3RoIDwgQ29uc3RyYWludC5fbWluTGVuZ3RoKSB7XG4gICAgICAgICAgICBjdXJyZW50TGVuZ3RoID0gQ29uc3RyYWludC5fbWluTGVuZ3RoO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gc29sdmUgZGlzdGFuY2UgY29uc3RyYWludCB3aXRoIEdhdXNzLVNpZWRlbCBtZXRob2RcbiAgICAgICAgdmFyIGRpZmZlcmVuY2UgPSAoY3VycmVudExlbmd0aCAtIGNvbnN0cmFpbnQubGVuZ3RoKSAvIGN1cnJlbnRMZW5ndGgsXG4gICAgICAgICAgICBzdGlmZm5lc3MgPSBjb25zdHJhaW50LnN0aWZmbmVzcyA8IDEgPyBjb25zdHJhaW50LnN0aWZmbmVzcyAqIHRpbWVTY2FsZSA6IGNvbnN0cmFpbnQuc3RpZmZuZXNzLFxuICAgICAgICAgICAgZm9yY2UgPSBWZWN0b3IubXVsdChkZWx0YSwgZGlmZmVyZW5jZSAqIHN0aWZmbmVzcyksXG4gICAgICAgICAgICBtYXNzVG90YWwgPSAoYm9keUEgPyBib2R5QS5pbnZlcnNlTWFzcyA6IDApICsgKGJvZHlCID8gYm9keUIuaW52ZXJzZU1hc3MgOiAwKSxcbiAgICAgICAgICAgIGluZXJ0aWFUb3RhbCA9IChib2R5QSA/IGJvZHlBLmludmVyc2VJbmVydGlhIDogMCkgKyAoYm9keUIgPyBib2R5Qi5pbnZlcnNlSW5lcnRpYSA6IDApLFxuICAgICAgICAgICAgcmVzaXN0YW5jZVRvdGFsID0gbWFzc1RvdGFsICsgaW5lcnRpYVRvdGFsLFxuICAgICAgICAgICAgdG9ycXVlLFxuICAgICAgICAgICAgc2hhcmUsXG4gICAgICAgICAgICBub3JtYWwsXG4gICAgICAgICAgICBub3JtYWxWZWxvY2l0eSxcbiAgICAgICAgICAgIHJlbGF0aXZlVmVsb2NpdHk7XG5cbiAgICAgICAgaWYgKGNvbnN0cmFpbnQuZGFtcGluZykge1xuICAgICAgICAgICAgdmFyIHplcm8gPSBWZWN0b3IuY3JlYXRlKCk7XG4gICAgICAgICAgICBub3JtYWwgPSBWZWN0b3IuZGl2KGRlbHRhLCBjdXJyZW50TGVuZ3RoKTtcblxuICAgICAgICAgICAgcmVsYXRpdmVWZWxvY2l0eSA9IFZlY3Rvci5zdWIoXG4gICAgICAgICAgICAgICAgYm9keUIgJiYgVmVjdG9yLnN1Yihib2R5Qi5wb3NpdGlvbiwgYm9keUIucG9zaXRpb25QcmV2KSB8fCB6ZXJvLFxuICAgICAgICAgICAgICAgIGJvZHlBICYmIFZlY3Rvci5zdWIoYm9keUEucG9zaXRpb24sIGJvZHlBLnBvc2l0aW9uUHJldikgfHwgemVyb1xuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgbm9ybWFsVmVsb2NpdHkgPSBWZWN0b3IuZG90KG5vcm1hbCwgcmVsYXRpdmVWZWxvY2l0eSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYm9keUEgJiYgIWJvZHlBLmlzU3RhdGljKSB7XG4gICAgICAgICAgICBzaGFyZSA9IGJvZHlBLmludmVyc2VNYXNzIC8gbWFzc1RvdGFsO1xuXG4gICAgICAgICAgICAvLyBrZWVwIHRyYWNrIG9mIGFwcGxpZWQgaW1wdWxzZXMgZm9yIHBvc3Qgc29sdmluZ1xuICAgICAgICAgICAgYm9keUEuY29uc3RyYWludEltcHVsc2UueCAtPSBmb3JjZS54ICogc2hhcmU7XG4gICAgICAgICAgICBib2R5QS5jb25zdHJhaW50SW1wdWxzZS55IC09IGZvcmNlLnkgKiBzaGFyZTtcblxuICAgICAgICAgICAgLy8gYXBwbHkgZm9yY2VzXG4gICAgICAgICAgICBib2R5QS5wb3NpdGlvbi54IC09IGZvcmNlLnggKiBzaGFyZTtcbiAgICAgICAgICAgIGJvZHlBLnBvc2l0aW9uLnkgLT0gZm9yY2UueSAqIHNoYXJlO1xuXG4gICAgICAgICAgICAvLyBhcHBseSBkYW1waW5nXG4gICAgICAgICAgICBpZiAoY29uc3RyYWludC5kYW1waW5nKSB7XG4gICAgICAgICAgICAgICAgYm9keUEucG9zaXRpb25QcmV2LnggLT0gY29uc3RyYWludC5kYW1waW5nICogbm9ybWFsLnggKiBub3JtYWxWZWxvY2l0eSAqIHNoYXJlO1xuICAgICAgICAgICAgICAgIGJvZHlBLnBvc2l0aW9uUHJldi55IC09IGNvbnN0cmFpbnQuZGFtcGluZyAqIG5vcm1hbC55ICogbm9ybWFsVmVsb2NpdHkgKiBzaGFyZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gYXBwbHkgdG9ycXVlXG4gICAgICAgICAgICB0b3JxdWUgPSAoVmVjdG9yLmNyb3NzKHBvaW50QSwgZm9yY2UpIC8gcmVzaXN0YW5jZVRvdGFsKSAqIENvbnN0cmFpbnQuX3RvcnF1ZURhbXBlbiAqIGJvZHlBLmludmVyc2VJbmVydGlhICogKDEgLSBjb25zdHJhaW50LmFuZ3VsYXJTdGlmZm5lc3MpO1xuICAgICAgICAgICAgYm9keUEuY29uc3RyYWludEltcHVsc2UuYW5nbGUgLT0gdG9ycXVlO1xuICAgICAgICAgICAgYm9keUEuYW5nbGUgLT0gdG9ycXVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGJvZHlCICYmICFib2R5Qi5pc1N0YXRpYykge1xuICAgICAgICAgICAgc2hhcmUgPSBib2R5Qi5pbnZlcnNlTWFzcyAvIG1hc3NUb3RhbDtcblxuICAgICAgICAgICAgLy8ga2VlcCB0cmFjayBvZiBhcHBsaWVkIGltcHVsc2VzIGZvciBwb3N0IHNvbHZpbmdcbiAgICAgICAgICAgIGJvZHlCLmNvbnN0cmFpbnRJbXB1bHNlLnggKz0gZm9yY2UueCAqIHNoYXJlO1xuICAgICAgICAgICAgYm9keUIuY29uc3RyYWludEltcHVsc2UueSArPSBmb3JjZS55ICogc2hhcmU7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIGFwcGx5IGZvcmNlc1xuICAgICAgICAgICAgYm9keUIucG9zaXRpb24ueCArPSBmb3JjZS54ICogc2hhcmU7XG4gICAgICAgICAgICBib2R5Qi5wb3NpdGlvbi55ICs9IGZvcmNlLnkgKiBzaGFyZTtcblxuICAgICAgICAgICAgLy8gYXBwbHkgZGFtcGluZ1xuICAgICAgICAgICAgaWYgKGNvbnN0cmFpbnQuZGFtcGluZykge1xuICAgICAgICAgICAgICAgIGJvZHlCLnBvc2l0aW9uUHJldi54ICs9IGNvbnN0cmFpbnQuZGFtcGluZyAqIG5vcm1hbC54ICogbm9ybWFsVmVsb2NpdHkgKiBzaGFyZTtcbiAgICAgICAgICAgICAgICBib2R5Qi5wb3NpdGlvblByZXYueSArPSBjb25zdHJhaW50LmRhbXBpbmcgKiBub3JtYWwueSAqIG5vcm1hbFZlbG9jaXR5ICogc2hhcmU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGFwcGx5IHRvcnF1ZVxuICAgICAgICAgICAgdG9ycXVlID0gKFZlY3Rvci5jcm9zcyhwb2ludEIsIGZvcmNlKSAvIHJlc2lzdGFuY2VUb3RhbCkgKiBDb25zdHJhaW50Ll90b3JxdWVEYW1wZW4gKiBib2R5Qi5pbnZlcnNlSW5lcnRpYSAqICgxIC0gY29uc3RyYWludC5hbmd1bGFyU3RpZmZuZXNzKTtcbiAgICAgICAgICAgIGJvZHlCLmNvbnN0cmFpbnRJbXB1bHNlLmFuZ2xlICs9IHRvcnF1ZTtcbiAgICAgICAgICAgIGJvZHlCLmFuZ2xlICs9IHRvcnF1ZTtcbiAgICAgICAgfVxuXG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFBlcmZvcm1zIGJvZHkgdXBkYXRlcyByZXF1aXJlZCBhZnRlciBzb2x2aW5nIGNvbnN0cmFpbnRzLlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBwb3N0U29sdmVBbGxcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICovXG4gICAgQ29uc3RyYWludC5wb3N0U29sdmVBbGwgPSBmdW5jdGlvbihib2RpZXMpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5ID0gYm9kaWVzW2ldLFxuICAgICAgICAgICAgICAgIGltcHVsc2UgPSBib2R5LmNvbnN0cmFpbnRJbXB1bHNlO1xuXG4gICAgICAgICAgICBpZiAoYm9keS5pc1N0YXRpYyB8fCAoaW1wdWxzZS54ID09PSAwICYmIGltcHVsc2UueSA9PT0gMCAmJiBpbXB1bHNlLmFuZ2xlID09PSAwKSkge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBTbGVlcGluZy5zZXQoYm9keSwgZmFsc2UpO1xuXG4gICAgICAgICAgICAvLyB1cGRhdGUgZ2VvbWV0cnkgYW5kIHJlc2V0XG4gICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGJvZHkucGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgcGFydCA9IGJvZHkucGFydHNbal07XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgVmVydGljZXMudHJhbnNsYXRlKHBhcnQudmVydGljZXMsIGltcHVsc2UpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGogPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHBhcnQucG9zaXRpb24ueCArPSBpbXB1bHNlLng7XG4gICAgICAgICAgICAgICAgICAgIHBhcnQucG9zaXRpb24ueSArPSBpbXB1bHNlLnk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGltcHVsc2UuYW5nbGUgIT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgVmVydGljZXMucm90YXRlKHBhcnQudmVydGljZXMsIGltcHVsc2UuYW5nbGUsIGJvZHkucG9zaXRpb24pO1xuICAgICAgICAgICAgICAgICAgICBBeGVzLnJvdGF0ZShwYXJ0LmF4ZXMsIGltcHVsc2UuYW5nbGUpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoaiA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFZlY3Rvci5yb3RhdGVBYm91dChwYXJ0LnBvc2l0aW9uLCBpbXB1bHNlLmFuZ2xlLCBib2R5LnBvc2l0aW9uLCBwYXJ0LnBvc2l0aW9uKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIEJvdW5kcy51cGRhdGUocGFydC5ib3VuZHMsIHBhcnQudmVydGljZXMsIGJvZHkudmVsb2NpdHkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBkYW1wZW4gdGhlIGNhY2hlZCBpbXB1bHNlIGZvciB3YXJtaW5nIG5leHQgc3RlcFxuICAgICAgICAgICAgaW1wdWxzZS5hbmdsZSAqPSBDb25zdHJhaW50Ll93YXJtaW5nO1xuICAgICAgICAgICAgaW1wdWxzZS54ICo9IENvbnN0cmFpbnQuX3dhcm1pbmc7XG4gICAgICAgICAgICBpbXB1bHNlLnkgKj0gQ29uc3RyYWludC5fd2FybWluZztcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKlxuICAgICpcbiAgICAqICBQcm9wZXJ0aWVzIERvY3VtZW50YXRpb25cbiAgICAqXG4gICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGludGVnZXIgYE51bWJlcmAgdW5pcXVlbHkgaWRlbnRpZnlpbmcgbnVtYmVyIGdlbmVyYXRlZCBpbiBgQ29tcG9zaXRlLmNyZWF0ZWAgYnkgYENvbW1vbi5uZXh0SWRgLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGlkXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBTdHJpbmdgIGRlbm90aW5nIHRoZSB0eXBlIG9mIG9iamVjdC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0eXBlXG4gICAgICogQHR5cGUgc3RyaW5nXG4gICAgICogQGRlZmF1bHQgXCJjb25zdHJhaW50XCJcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGFyYml0cmFyeSBgU3RyaW5nYCBuYW1lIHRvIGhlbHAgdGhlIHVzZXIgaWRlbnRpZnkgYW5kIG1hbmFnZSBib2RpZXMuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgbGFiZWxcbiAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgKiBAZGVmYXVsdCBcIkNvbnN0cmFpbnRcIlxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gYE9iamVjdGAgdGhhdCBkZWZpbmVzIHRoZSByZW5kZXJpbmcgcHJvcGVydGllcyB0byBiZSBjb25zdW1lZCBieSB0aGUgbW9kdWxlIGBNYXR0ZXIuUmVuZGVyYC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXJcbiAgICAgKiBAdHlwZSBvYmplY3RcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgZmxhZyB0aGF0IGluZGljYXRlcyBpZiB0aGUgY29uc3RyYWludCBzaG91bGQgYmUgcmVuZGVyZWQuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcmVuZGVyLnZpc2libGVcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgdHJ1ZVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IGRlZmluZXMgdGhlIGxpbmUgd2lkdGggdG8gdXNlIHdoZW4gcmVuZGVyaW5nIHRoZSBjb25zdHJhaW50IG91dGxpbmUuXG4gICAgICogQSB2YWx1ZSBvZiBgMGAgbWVhbnMgbm8gb3V0bGluZSB3aWxsIGJlIHJlbmRlcmVkLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHJlbmRlci5saW5lV2lkdGhcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAyXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBTdHJpbmdgIHRoYXQgZGVmaW5lcyB0aGUgc3Ryb2tlIHN0eWxlIHRvIHVzZSB3aGVuIHJlbmRlcmluZyB0aGUgY29uc3RyYWludCBvdXRsaW5lLlxuICAgICAqIEl0IGlzIHRoZSBzYW1lIGFzIHdoZW4gdXNpbmcgYSBjYW52YXMsIHNvIGl0IGFjY2VwdHMgQ1NTIHN0eWxlIHByb3BlcnR5IHZhbHVlcy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIuc3Ryb2tlU3R5bGVcbiAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgKiBAZGVmYXVsdCBhIHJhbmRvbSBjb2xvdXJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYFN0cmluZ2AgdGhhdCBkZWZpbmVzIHRoZSBjb25zdHJhaW50IHJlbmRlcmluZyB0eXBlLiBcbiAgICAgKiBUaGUgcG9zc2libGUgdmFsdWVzIGFyZSAnbGluZScsICdwaW4nLCAnc3ByaW5nJy5cbiAgICAgKiBBbiBhcHByb3ByaWF0ZSByZW5kZXIgdHlwZSB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgY2hvc2VuIHVubGVzcyBvbmUgaXMgZ2l2ZW4gaW4gb3B0aW9ucy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSByZW5kZXIudHlwZVxuICAgICAqIEB0eXBlIHN0cmluZ1xuICAgICAqIEBkZWZhdWx0ICdsaW5lJ1xuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgQm9vbGVhbmAgdGhhdCBkZWZpbmVzIGlmIHRoZSBjb25zdHJhaW50J3MgYW5jaG9yIHBvaW50cyBzaG91bGQgYmUgcmVuZGVyZWQuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcmVuZGVyLmFuY2hvcnNcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgdHJ1ZVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogVGhlIGZpcnN0IHBvc3NpYmxlIGBCb2R5YCB0aGF0IHRoaXMgY29uc3RyYWludCBpcyBhdHRhY2hlZCB0by5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBib2R5QVxuICAgICAqIEB0eXBlIGJvZHlcbiAgICAgKiBAZGVmYXVsdCBudWxsXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBUaGUgc2Vjb25kIHBvc3NpYmxlIGBCb2R5YCB0aGF0IHRoaXMgY29uc3RyYWludCBpcyBhdHRhY2hlZCB0by5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBib2R5QlxuICAgICAqIEB0eXBlIGJvZHlcbiAgICAgKiBAZGVmYXVsdCBudWxsXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBWZWN0b3JgIHRoYXQgc3BlY2lmaWVzIHRoZSBvZmZzZXQgb2YgdGhlIGNvbnN0cmFpbnQgZnJvbSBjZW50ZXIgb2YgdGhlIGBjb25zdHJhaW50LmJvZHlBYCBpZiBkZWZpbmVkLCBvdGhlcndpc2UgYSB3b3JsZC1zcGFjZSBwb3NpdGlvbi5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBwb2ludEFcbiAgICAgKiBAdHlwZSB2ZWN0b3JcbiAgICAgKiBAZGVmYXVsdCB7IHg6IDAsIHk6IDAgfVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgVmVjdG9yYCB0aGF0IHNwZWNpZmllcyB0aGUgb2Zmc2V0IG9mIHRoZSBjb25zdHJhaW50IGZyb20gY2VudGVyIG9mIHRoZSBgY29uc3RyYWludC5ib2R5QWAgaWYgZGVmaW5lZCwgb3RoZXJ3aXNlIGEgd29ybGQtc3BhY2UgcG9zaXRpb24uXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcG9pbnRCXG4gICAgICogQHR5cGUgdmVjdG9yXG4gICAgICogQGRlZmF1bHQgeyB4OiAwLCB5OiAwIH1cbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBzcGVjaWZpZXMgdGhlIHN0aWZmbmVzcyBvZiB0aGUgY29uc3RyYWludCwgaS5lLiB0aGUgcmF0ZSBhdCB3aGljaCBpdCByZXR1cm5zIHRvIGl0cyByZXN0aW5nIGBjb25zdHJhaW50Lmxlbmd0aGAuXG4gICAgICogQSB2YWx1ZSBvZiBgMWAgbWVhbnMgdGhlIGNvbnN0cmFpbnQgc2hvdWxkIGJlIHZlcnkgc3RpZmYuXG4gICAgICogQSB2YWx1ZSBvZiBgMC4yYCBtZWFucyB0aGUgY29uc3RyYWludCBhY3RzIGxpa2UgYSBzb2Z0IHNwcmluZy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBzdGlmZm5lc3NcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAxXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBOdW1iZXJgIHRoYXQgc3BlY2lmaWVzIHRoZSBkYW1waW5nIG9mIHRoZSBjb25zdHJhaW50LCBcbiAgICAgKiBpLmUuIHRoZSBhbW91bnQgb2YgcmVzaXN0YW5jZSBhcHBsaWVkIHRvIGVhY2ggYm9keSBiYXNlZCBvbiB0aGVpciB2ZWxvY2l0aWVzIHRvIGxpbWl0IHRoZSBhbW91bnQgb2Ygb3NjaWxsYXRpb24uXG4gICAgICogRGFtcGluZyB3aWxsIG9ubHkgYmUgYXBwYXJlbnQgd2hlbiB0aGUgY29uc3RyYWludCBhbHNvIGhhcyBhIHZlcnkgbG93IGBzdGlmZm5lc3NgLlxuICAgICAqIEEgdmFsdWUgb2YgYDAuMWAgbWVhbnMgdGhlIGNvbnN0cmFpbnQgd2lsbCBhcHBseSBoZWF2eSBkYW1waW5nLCByZXN1bHRpbmcgaW4gbGl0dGxlIHRvIG5vIG9zY2lsbGF0aW9uLlxuICAgICAqIEEgdmFsdWUgb2YgYDBgIG1lYW5zIHRoZSBjb25zdHJhaW50IHdpbGwgYXBwbHkgbm8gZGFtcGluZy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBkYW1waW5nXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IHNwZWNpZmllcyB0aGUgdGFyZ2V0IHJlc3RpbmcgbGVuZ3RoIG9mIHRoZSBjb25zdHJhaW50LiBcbiAgICAgKiBJdCBpcyBjYWxjdWxhdGVkIGF1dG9tYXRpY2FsbHkgaW4gYENvbnN0cmFpbnQuY3JlYXRlYCBmcm9tIGluaXRpYWwgcG9zaXRpb25zIG9mIHRoZSBgY29uc3RyYWludC5ib2R5QWAgYW5kIGBjb25zdHJhaW50LmJvZHlCYC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBsZW5ndGhcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIG9iamVjdCByZXNlcnZlZCBmb3Igc3RvcmluZyBwbHVnaW4tc3BlY2lmaWMgcHJvcGVydGllcy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBwbHVnaW5cbiAgICAgKiBAdHlwZSB7fVxuICAgICAqL1xuXG59KSgpO1xuXG59LHtcIi4uL2NvcmUvQ29tbW9uXCI6MTQsXCIuLi9jb3JlL1NsZWVwaW5nXCI6MjIsXCIuLi9nZW9tZXRyeS9BeGVzXCI6MjUsXCIuLi9nZW9tZXRyeS9Cb3VuZHNcIjoyNixcIi4uL2dlb21ldHJ5L1ZlY3RvclwiOjI4LFwiLi4vZ2VvbWV0cnkvVmVydGljZXNcIjoyOX1dLDEzOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5Nb3VzZUNvbnN0cmFpbnRgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIGZvciBjcmVhdGluZyBtb3VzZSBjb25zdHJhaW50cy5cbiogTW91c2UgY29uc3RyYWludHMgYXJlIHVzZWQgZm9yIGFsbG93aW5nIHVzZXIgaW50ZXJhY3Rpb24sIHByb3ZpZGluZyB0aGUgYWJpbGl0eSB0byBtb3ZlIGJvZGllcyB2aWEgdGhlIG1vdXNlIG9yIHRvdWNoLlxuKlxuKiBTZWUgdGhlIGluY2x1ZGVkIHVzYWdlIFtleGFtcGxlc10oaHR0cHM6Ly9naXRodWIuY29tL2xpYWJydS9tYXR0ZXItanMvdHJlZS9tYXN0ZXIvZXhhbXBsZXMpLlxuKlxuKiBAY2xhc3MgTW91c2VDb25zdHJhaW50XG4qL1xuXG52YXIgTW91c2VDb25zdHJhaW50ID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gTW91c2VDb25zdHJhaW50O1xuXG52YXIgVmVydGljZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZXJ0aWNlcycpO1xudmFyIFNsZWVwaW5nID0gX2RlcmVxXygnLi4vY29yZS9TbGVlcGluZycpO1xudmFyIE1vdXNlID0gX2RlcmVxXygnLi4vY29yZS9Nb3VzZScpO1xudmFyIEV2ZW50cyA9IF9kZXJlcV8oJy4uL2NvcmUvRXZlbnRzJyk7XG52YXIgRGV0ZWN0b3IgPSBfZGVyZXFfKCcuLi9jb2xsaXNpb24vRGV0ZWN0b3InKTtcbnZhciBDb25zdHJhaW50ID0gX2RlcmVxXygnLi9Db25zdHJhaW50Jyk7XG52YXIgQ29tcG9zaXRlID0gX2RlcmVxXygnLi4vYm9keS9Db21wb3NpdGUnKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xudmFyIEJvdW5kcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L0JvdW5kcycpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IG1vdXNlIGNvbnN0cmFpbnQuXG4gICAgICogQWxsIHByb3BlcnRpZXMgaGF2ZSBkZWZhdWx0IHZhbHVlcywgYW5kIG1hbnkgYXJlIHByZS1jYWxjdWxhdGVkIGF1dG9tYXRpY2FsbHkgYmFzZWQgb24gb3RoZXIgcHJvcGVydGllcy5cbiAgICAgKiBTZWUgdGhlIHByb3BlcnRpZXMgc2VjdGlvbiBiZWxvdyBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gd2hhdCB5b3UgY2FuIHBhc3MgdmlhIHRoZSBgb3B0aW9uc2Agb2JqZWN0LlxuICAgICAqIEBtZXRob2QgY3JlYXRlXG4gICAgICogQHBhcmFtIHtlbmdpbmV9IGVuZ2luZVxuICAgICAqIEBwYXJhbSB7fSBvcHRpb25zXG4gICAgICogQHJldHVybiB7TW91c2VDb25zdHJhaW50fSBBIG5ldyBNb3VzZUNvbnN0cmFpbnRcbiAgICAgKi9cbiAgICBNb3VzZUNvbnN0cmFpbnQuY3JlYXRlID0gZnVuY3Rpb24oZW5naW5lLCBvcHRpb25zKSB7XG4gICAgICAgIHZhciBtb3VzZSA9IChlbmdpbmUgPyBlbmdpbmUubW91c2UgOiBudWxsKSB8fCAob3B0aW9ucyA/IG9wdGlvbnMubW91c2UgOiBudWxsKTtcblxuICAgICAgICBpZiAoIW1vdXNlKSB7XG4gICAgICAgICAgICBpZiAoZW5naW5lICYmIGVuZ2luZS5yZW5kZXIgJiYgZW5naW5lLnJlbmRlci5jYW52YXMpIHtcbiAgICAgICAgICAgICAgICBtb3VzZSA9IE1vdXNlLmNyZWF0ZShlbmdpbmUucmVuZGVyLmNhbnZhcyk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5lbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgbW91c2UgPSBNb3VzZS5jcmVhdGUob3B0aW9ucy5lbGVtZW50KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbW91c2UgPSBNb3VzZS5jcmVhdGUoKTtcbiAgICAgICAgICAgICAgICBDb21tb24ud2FybignTW91c2VDb25zdHJhaW50LmNyZWF0ZTogb3B0aW9ucy5tb3VzZSB3YXMgdW5kZWZpbmVkLCBvcHRpb25zLmVsZW1lbnQgd2FzIHVuZGVmaW5lZCwgbWF5IG5vdCBmdW5jdGlvbiBhcyBleHBlY3RlZCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGNvbnN0cmFpbnQgPSBDb25zdHJhaW50LmNyZWF0ZSh7IFxuICAgICAgICAgICAgbGFiZWw6ICdNb3VzZSBDb25zdHJhaW50JyxcbiAgICAgICAgICAgIHBvaW50QTogbW91c2UucG9zaXRpb24sXG4gICAgICAgICAgICBwb2ludEI6IHsgeDogMCwgeTogMCB9LFxuICAgICAgICAgICAgbGVuZ3RoOiAwLjAxLCBcbiAgICAgICAgICAgIHN0aWZmbmVzczogMC4xLFxuICAgICAgICAgICAgYW5ndWxhclN0aWZmbmVzczogMSxcbiAgICAgICAgICAgIHJlbmRlcjoge1xuICAgICAgICAgICAgICAgIHN0cm9rZVN0eWxlOiAnIzkwRUU5MCcsXG4gICAgICAgICAgICAgICAgbGluZVdpZHRoOiAzXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgICAgIHR5cGU6ICdtb3VzZUNvbnN0cmFpbnQnLFxuICAgICAgICAgICAgbW91c2U6IG1vdXNlLFxuICAgICAgICAgICAgZWxlbWVudDogbnVsbCxcbiAgICAgICAgICAgIGJvZHk6IG51bGwsXG4gICAgICAgICAgICBjb25zdHJhaW50OiBjb25zdHJhaW50LFxuICAgICAgICAgICAgY29sbGlzaW9uRmlsdGVyOiB7XG4gICAgICAgICAgICAgICAgY2F0ZWdvcnk6IDB4MDAwMSxcbiAgICAgICAgICAgICAgICBtYXNrOiAweEZGRkZGRkZGLFxuICAgICAgICAgICAgICAgIGdyb3VwOiAwXG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgdmFyIG1vdXNlQ29uc3RyYWludCA9IENvbW1vbi5leHRlbmQoZGVmYXVsdHMsIG9wdGlvbnMpO1xuXG4gICAgICAgIEV2ZW50cy5vbihlbmdpbmUsICdiZWZvcmVVcGRhdGUnLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHZhciBhbGxCb2RpZXMgPSBDb21wb3NpdGUuYWxsQm9kaWVzKGVuZ2luZS53b3JsZCk7XG4gICAgICAgICAgICBNb3VzZUNvbnN0cmFpbnQudXBkYXRlKG1vdXNlQ29uc3RyYWludCwgYWxsQm9kaWVzKTtcbiAgICAgICAgICAgIF90cmlnZ2VyRXZlbnRzKG1vdXNlQ29uc3RyYWludCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBtb3VzZUNvbnN0cmFpbnQ7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGdpdmVuIG1vdXNlIGNvbnN0cmFpbnQuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHVwZGF0ZVxuICAgICAqIEBwYXJhbSB7TW91c2VDb25zdHJhaW50fSBtb3VzZUNvbnN0cmFpbnRcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICovXG4gICAgTW91c2VDb25zdHJhaW50LnVwZGF0ZSA9IGZ1bmN0aW9uKG1vdXNlQ29uc3RyYWludCwgYm9kaWVzKSB7XG4gICAgICAgIHZhciBtb3VzZSA9IG1vdXNlQ29uc3RyYWludC5tb3VzZSxcbiAgICAgICAgICAgIGNvbnN0cmFpbnQgPSBtb3VzZUNvbnN0cmFpbnQuY29uc3RyYWludCxcbiAgICAgICAgICAgIGJvZHkgPSBtb3VzZUNvbnN0cmFpbnQuYm9keTtcblxuICAgICAgICBpZiAobW91c2UuYnV0dG9uID09PSAwKSB7XG4gICAgICAgICAgICBpZiAoIWNvbnN0cmFpbnQuYm9keUIpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICBib2R5ID0gYm9kaWVzW2ldO1xuICAgICAgICAgICAgICAgICAgICBpZiAoQm91bmRzLmNvbnRhaW5zKGJvZHkuYm91bmRzLCBtb3VzZS5wb3NpdGlvbikgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJiYgRGV0ZWN0b3IuY2FuQ29sbGlkZShib2R5LmNvbGxpc2lvbkZpbHRlciwgbW91c2VDb25zdHJhaW50LmNvbGxpc2lvbkZpbHRlcikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGogPSBib2R5LnBhcnRzLmxlbmd0aCA+IDEgPyAxIDogMDsgaiA8IGJvZHkucGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcGFydCA9IGJvZHkucGFydHNbal07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKFZlcnRpY2VzLmNvbnRhaW5zKHBhcnQudmVydGljZXMsIG1vdXNlLnBvc2l0aW9uKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdHJhaW50LnBvaW50QSA9IG1vdXNlLnBvc2l0aW9uO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdHJhaW50LmJvZHlCID0gbW91c2VDb25zdHJhaW50LmJvZHkgPSBib2R5O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdHJhaW50LnBvaW50QiA9IHsgeDogbW91c2UucG9zaXRpb24ueCAtIGJvZHkucG9zaXRpb24ueCwgeTogbW91c2UucG9zaXRpb24ueSAtIGJvZHkucG9zaXRpb24ueSB9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdHJhaW50LmFuZ2xlQiA9IGJvZHkuYW5nbGU7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2xlZXBpbmcuc2V0KGJvZHksIGZhbHNlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRzLnRyaWdnZXIobW91c2VDb25zdHJhaW50LCAnc3RhcnRkcmFnJywgeyBtb3VzZTogbW91c2UsIGJvZHk6IGJvZHkgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBTbGVlcGluZy5zZXQoY29uc3RyYWludC5ib2R5QiwgZmFsc2UpO1xuICAgICAgICAgICAgICAgIGNvbnN0cmFpbnQucG9pbnRBID0gbW91c2UucG9zaXRpb247XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdHJhaW50LmJvZHlCID0gbW91c2VDb25zdHJhaW50LmJvZHkgPSBudWxsO1xuICAgICAgICAgICAgY29uc3RyYWludC5wb2ludEIgPSBudWxsO1xuXG4gICAgICAgICAgICBpZiAoYm9keSlcbiAgICAgICAgICAgICAgICBFdmVudHMudHJpZ2dlcihtb3VzZUNvbnN0cmFpbnQsICdlbmRkcmFnJywgeyBtb3VzZTogbW91c2UsIGJvZHk6IGJvZHkgfSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVHJpZ2dlcnMgbW91c2UgY29uc3RyYWludCBldmVudHMuXG4gICAgICogQG1ldGhvZCBfdHJpZ2dlckV2ZW50c1xuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHttb3VzZX0gbW91c2VDb25zdHJhaW50XG4gICAgICovXG4gICAgdmFyIF90cmlnZ2VyRXZlbnRzID0gZnVuY3Rpb24obW91c2VDb25zdHJhaW50KSB7XG4gICAgICAgIHZhciBtb3VzZSA9IG1vdXNlQ29uc3RyYWludC5tb3VzZSxcbiAgICAgICAgICAgIG1vdXNlRXZlbnRzID0gbW91c2Uuc291cmNlRXZlbnRzO1xuXG4gICAgICAgIGlmIChtb3VzZUV2ZW50cy5tb3VzZW1vdmUpXG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihtb3VzZUNvbnN0cmFpbnQsICdtb3VzZW1vdmUnLCB7IG1vdXNlOiBtb3VzZSB9KTtcblxuICAgICAgICBpZiAobW91c2VFdmVudHMubW91c2Vkb3duKVxuICAgICAgICAgICAgRXZlbnRzLnRyaWdnZXIobW91c2VDb25zdHJhaW50LCAnbW91c2Vkb3duJywgeyBtb3VzZTogbW91c2UgfSk7XG5cbiAgICAgICAgaWYgKG1vdXNlRXZlbnRzLm1vdXNldXApXG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihtb3VzZUNvbnN0cmFpbnQsICdtb3VzZXVwJywgeyBtb3VzZTogbW91c2UgfSk7XG5cbiAgICAgICAgLy8gcmVzZXQgdGhlIG1vdXNlIHN0YXRlIHJlYWR5IGZvciB0aGUgbmV4dCBzdGVwXG4gICAgICAgIE1vdXNlLmNsZWFyU291cmNlRXZlbnRzKG1vdXNlKTtcbiAgICB9O1xuXG4gICAgLypcbiAgICAqXG4gICAgKiAgRXZlbnRzIERvY3VtZW50YXRpb25cbiAgICAqXG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgd2hlbiB0aGUgbW91c2UgaGFzIG1vdmVkIChvciBhIHRvdWNoIG1vdmVzKSBkdXJpbmcgdGhlIGxhc3Qgc3RlcFxuICAgICpcbiAgICAqIEBldmVudCBtb3VzZW1vdmVcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bW91c2V9IGV2ZW50Lm1vdXNlIFRoZSBlbmdpbmUncyBtb3VzZSBpbnN0YW5jZVxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICovXG5cbiAgICAvKipcbiAgICAqIEZpcmVkIHdoZW4gdGhlIG1vdXNlIGlzIGRvd24gKG9yIGEgdG91Y2ggaGFzIHN0YXJ0ZWQpIGR1cmluZyB0aGUgbGFzdCBzdGVwXG4gICAgKlxuICAgICogQGV2ZW50IG1vdXNlZG93blxuICAgICogQHBhcmFtIHt9IGV2ZW50IEFuIGV2ZW50IG9iamVjdFxuICAgICogQHBhcmFtIHttb3VzZX0gZXZlbnQubW91c2UgVGhlIGVuZ2luZSdzIG1vdXNlIGluc3RhbmNlXG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgd2hlbiB0aGUgbW91c2UgaXMgdXAgKG9yIGEgdG91Y2ggaGFzIGVuZGVkKSBkdXJpbmcgdGhlIGxhc3Qgc3RlcFxuICAgICpcbiAgICAqIEBldmVudCBtb3VzZXVwXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge21vdXNlfSBldmVudC5tb3VzZSBUaGUgZW5naW5lJ3MgbW91c2UgaW5zdGFuY2VcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCB3aGVuIHRoZSB1c2VyIHN0YXJ0cyBkcmFnZ2luZyBhIGJvZHlcbiAgICAqXG4gICAgKiBAZXZlbnQgc3RhcnRkcmFnXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge21vdXNlfSBldmVudC5tb3VzZSBUaGUgZW5naW5lJ3MgbW91c2UgaW5zdGFuY2VcbiAgICAqIEBwYXJhbSB7Ym9keX0gZXZlbnQuYm9keSBUaGUgYm9keSBiZWluZyBkcmFnZ2VkXG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgd2hlbiB0aGUgdXNlciBlbmRzIGRyYWdnaW5nIGEgYm9keVxuICAgICpcbiAgICAqIEBldmVudCBlbmRkcmFnXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge21vdXNlfSBldmVudC5tb3VzZSBUaGUgZW5naW5lJ3MgbW91c2UgaW5zdGFuY2VcbiAgICAqIEBwYXJhbSB7Ym9keX0gZXZlbnQuYm9keSBUaGUgYm9keSB0aGF0IGhhcyBzdG9wcGVkIGJlaW5nIGRyYWdnZWRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLypcbiAgICAqXG4gICAgKiAgUHJvcGVydGllcyBEb2N1bWVudGF0aW9uXG4gICAgKlxuICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGBTdHJpbmdgIGRlbm90aW5nIHRoZSB0eXBlIG9mIG9iamVjdC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0eXBlXG4gICAgICogQHR5cGUgc3RyaW5nXG4gICAgICogQGRlZmF1bHQgXCJjb25zdHJhaW50XCJcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIFRoZSBgTW91c2VgIGluc3RhbmNlIGluIHVzZS4gSWYgbm90IHN1cHBsaWVkIGluIGBNb3VzZUNvbnN0cmFpbnQuY3JlYXRlYCwgb25lIHdpbGwgYmUgY3JlYXRlZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBtb3VzZVxuICAgICAqIEB0eXBlIG1vdXNlXG4gICAgICogQGRlZmF1bHQgbW91c2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIFRoZSBgQm9keWAgdGhhdCBpcyBjdXJyZW50bHkgYmVpbmcgbW92ZWQgYnkgdGhlIHVzZXIsIG9yIGBudWxsYCBpZiBubyBib2R5LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGJvZHlcbiAgICAgKiBAdHlwZSBib2R5XG4gICAgICogQGRlZmF1bHQgbnVsbFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogVGhlIGBDb25zdHJhaW50YCBvYmplY3QgdGhhdCBpcyB1c2VkIHRvIG1vdmUgdGhlIGJvZHkgZHVyaW5nIGludGVyYWN0aW9uLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGNvbnN0cmFpbnRcbiAgICAgKiBAdHlwZSBjb25zdHJhaW50XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBgT2JqZWN0YCB0aGF0IHNwZWNpZmllcyB0aGUgY29sbGlzaW9uIGZpbHRlciBwcm9wZXJ0aWVzLlxuICAgICAqIFRoZSBjb2xsaXNpb24gZmlsdGVyIGFsbG93cyB0aGUgdXNlciB0byBkZWZpbmUgd2hpY2ggdHlwZXMgb2YgYm9keSB0aGlzIG1vdXNlIGNvbnN0cmFpbnQgY2FuIGludGVyYWN0IHdpdGguXG4gICAgICogU2VlIGBib2R5LmNvbGxpc2lvbkZpbHRlcmAgZm9yIG1vcmUgaW5mb3JtYXRpb24uXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgY29sbGlzaW9uRmlsdGVyXG4gICAgICogQHR5cGUgb2JqZWN0XG4gICAgICovXG5cbn0pKCk7XG5cbn0se1wiLi4vYm9keS9Db21wb3NpdGVcIjoyLFwiLi4vY29sbGlzaW9uL0RldGVjdG9yXCI6NSxcIi4uL2NvcmUvQ29tbW9uXCI6MTQsXCIuLi9jb3JlL0V2ZW50c1wiOjE2LFwiLi4vY29yZS9Nb3VzZVwiOjE5LFwiLi4vY29yZS9TbGVlcGluZ1wiOjIyLFwiLi4vZ2VvbWV0cnkvQm91bmRzXCI6MjYsXCIuLi9nZW9tZXRyeS9WZXJ0aWNlc1wiOjI5LFwiLi9Db25zdHJhaW50XCI6MTJ9XSwxNDpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuQ29tbW9uYCBtb2R1bGUgY29udGFpbnMgdXRpbGl0eSBmdW5jdGlvbnMgdGhhdCBhcmUgY29tbW9uIHRvIGFsbCBtb2R1bGVzLlxuKlxuKiBAY2xhc3MgQ29tbW9uXG4qL1xuXG52YXIgQ29tbW9uID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gQ29tbW9uO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICBDb21tb24uX25leHRJZCA9IDA7XG4gICAgQ29tbW9uLl9zZWVkID0gMDtcbiAgICBDb21tb24uX25vd1N0YXJ0VGltZSA9ICsobmV3IERhdGUoKSk7XG5cbiAgICAvKipcbiAgICAgKiBFeHRlbmRzIHRoZSBvYmplY3QgaW4gdGhlIGZpcnN0IGFyZ3VtZW50IHVzaW5nIHRoZSBvYmplY3QgaW4gdGhlIHNlY29uZCBhcmd1bWVudC5cbiAgICAgKiBAbWV0aG9kIGV4dGVuZFxuICAgICAqIEBwYXJhbSB7fSBvYmpcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGRlZXBcbiAgICAgKiBAcmV0dXJuIHt9IG9iaiBleHRlbmRlZFxuICAgICAqL1xuICAgIENvbW1vbi5leHRlbmQgPSBmdW5jdGlvbihvYmosIGRlZXApIHtcbiAgICAgICAgdmFyIGFyZ3NTdGFydCxcbiAgICAgICAgICAgIGFyZ3MsXG4gICAgICAgICAgICBkZWVwQ2xvbmU7XG5cbiAgICAgICAgaWYgKHR5cGVvZiBkZWVwID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgIGFyZ3NTdGFydCA9IDI7XG4gICAgICAgICAgICBkZWVwQ2xvbmUgPSBkZWVwO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYXJnc1N0YXJ0ID0gMTtcbiAgICAgICAgICAgIGRlZXBDbG9uZSA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKHZhciBpID0gYXJnc1N0YXJ0OyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgc291cmNlID0gYXJndW1lbnRzW2ldO1xuXG4gICAgICAgICAgICBpZiAoc291cmNlKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgcHJvcCBpbiBzb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGRlZXBDbG9uZSAmJiBzb3VyY2VbcHJvcF0gJiYgc291cmNlW3Byb3BdLmNvbnN0cnVjdG9yID09PSBPYmplY3QpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghb2JqW3Byb3BdIHx8IG9ialtwcm9wXS5jb25zdHJ1Y3RvciA9PT0gT2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb2JqW3Byb3BdID0gb2JqW3Byb3BdIHx8IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbW1vbi5leHRlbmQob2JqW3Byb3BdLCBkZWVwQ2xvbmUsIHNvdXJjZVtwcm9wXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ialtwcm9wXSA9IHNvdXJjZVtwcm9wXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9ialtwcm9wXSA9IHNvdXJjZVtwcm9wXTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIG9iajtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBjbG9uZSBvZiB0aGUgb2JqZWN0LCBpZiBkZWVwIGlzIHRydWUgcmVmZXJlbmNlcyB3aWxsIGFsc28gYmUgY2xvbmVkLlxuICAgICAqIEBtZXRob2QgY2xvbmVcbiAgICAgKiBAcGFyYW0ge30gb2JqXG4gICAgICogQHBhcmFtIHtib29sfSBkZWVwXG4gICAgICogQHJldHVybiB7fSBvYmogY2xvbmVkXG4gICAgICovXG4gICAgQ29tbW9uLmNsb25lID0gZnVuY3Rpb24ob2JqLCBkZWVwKSB7XG4gICAgICAgIHJldHVybiBDb21tb24uZXh0ZW5kKHt9LCBkZWVwLCBvYmopO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBsaXN0IG9mIGtleXMgZm9yIHRoZSBnaXZlbiBvYmplY3QuXG4gICAgICogQG1ldGhvZCBrZXlzXG4gICAgICogQHBhcmFtIHt9IG9ialxuICAgICAqIEByZXR1cm4ge3N0cmluZ1tdfSBrZXlzXG4gICAgICovXG4gICAgQ29tbW9uLmtleXMgPSBmdW5jdGlvbihvYmopIHtcbiAgICAgICAgaWYgKE9iamVjdC5rZXlzKVxuICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5rZXlzKG9iaik7XG5cbiAgICAgICAgLy8gYXZvaWQgaGFzT3duUHJvcGVydHkgZm9yIHBlcmZvcm1hbmNlXG4gICAgICAgIHZhciBrZXlzID0gW107XG4gICAgICAgIGZvciAodmFyIGtleSBpbiBvYmopXG4gICAgICAgICAgICBrZXlzLnB1c2goa2V5KTtcbiAgICAgICAgcmV0dXJuIGtleXM7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGxpc3Qgb2YgdmFsdWVzIGZvciB0aGUgZ2l2ZW4gb2JqZWN0LlxuICAgICAqIEBtZXRob2QgdmFsdWVzXG4gICAgICogQHBhcmFtIHt9IG9ialxuICAgICAqIEByZXR1cm4ge2FycmF5fSBBcnJheSBvZiB0aGUgb2JqZWN0cyBwcm9wZXJ0eSB2YWx1ZXNcbiAgICAgKi9cbiAgICBDb21tb24udmFsdWVzID0gZnVuY3Rpb24ob2JqKSB7XG4gICAgICAgIHZhciB2YWx1ZXMgPSBbXTtcbiAgICAgICAgXG4gICAgICAgIGlmIChPYmplY3Qua2V5cykge1xuICAgICAgICAgICAgdmFyIGtleXMgPSBPYmplY3Qua2V5cyhvYmopO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBrZXlzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdmFsdWVzLnB1c2gob2JqW2tleXNbaV1dKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB2YWx1ZXM7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIGF2b2lkIGhhc093blByb3BlcnR5IGZvciBwZXJmb3JtYW5jZVxuICAgICAgICBmb3IgKHZhciBrZXkgaW4gb2JqKVxuICAgICAgICAgICAgdmFsdWVzLnB1c2gob2JqW2tleV0pO1xuICAgICAgICByZXR1cm4gdmFsdWVzO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBHZXRzIGEgdmFsdWUgZnJvbSBgYmFzZWAgcmVsYXRpdmUgdG8gdGhlIGBwYXRoYCBzdHJpbmcuXG4gICAgICogQG1ldGhvZCBnZXRcbiAgICAgKiBAcGFyYW0ge30gb2JqIFRoZSBiYXNlIG9iamVjdFxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFRoZSBwYXRoIHJlbGF0aXZlIHRvIGBiYXNlYCwgZS5nLiAnRm9vLkJhci5iYXonXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtiZWdpbl0gUGF0aCBzbGljZSBiZWdpblxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbZW5kXSBQYXRoIHNsaWNlIGVuZFxuICAgICAqIEByZXR1cm4ge30gVGhlIG9iamVjdCBhdCB0aGUgZ2l2ZW4gcGF0aFxuICAgICAqL1xuICAgIENvbW1vbi5nZXQgPSBmdW5jdGlvbihvYmosIHBhdGgsIGJlZ2luLCBlbmQpIHtcbiAgICAgICAgcGF0aCA9IHBhdGguc3BsaXQoJy4nKS5zbGljZShiZWdpbiwgZW5kKTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHBhdGgubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIG9iaiA9IG9ialtwYXRoW2ldXTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBvYmo7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNldHMgYSB2YWx1ZSBvbiBgYmFzZWAgcmVsYXRpdmUgdG8gdGhlIGdpdmVuIGBwYXRoYCBzdHJpbmcuXG4gICAgICogQG1ldGhvZCBzZXRcbiAgICAgKiBAcGFyYW0ge30gb2JqIFRoZSBiYXNlIG9iamVjdFxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFRoZSBwYXRoIHJlbGF0aXZlIHRvIGBiYXNlYCwgZS5nLiAnRm9vLkJhci5iYXonXG4gICAgICogQHBhcmFtIHt9IHZhbCBUaGUgdmFsdWUgdG8gc2V0XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtiZWdpbl0gUGF0aCBzbGljZSBiZWdpblxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbZW5kXSBQYXRoIHNsaWNlIGVuZFxuICAgICAqIEByZXR1cm4ge30gUGFzcyB0aHJvdWdoIGB2YWxgIGZvciBjaGFpbmluZ1xuICAgICAqL1xuICAgIENvbW1vbi5zZXQgPSBmdW5jdGlvbihvYmosIHBhdGgsIHZhbCwgYmVnaW4sIGVuZCkge1xuICAgICAgICB2YXIgcGFydHMgPSBwYXRoLnNwbGl0KCcuJykuc2xpY2UoYmVnaW4sIGVuZCk7XG4gICAgICAgIENvbW1vbi5nZXQob2JqLCBwYXRoLCAwLCAtMSlbcGFydHNbcGFydHMubGVuZ3RoIC0gMV1dID0gdmFsO1xuICAgICAgICByZXR1cm4gdmFsO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaHVmZmxlcyB0aGUgZ2l2ZW4gYXJyYXkgaW4tcGxhY2UuXG4gICAgICogVGhlIGZ1bmN0aW9uIHVzZXMgYSBzZWVkZWQgcmFuZG9tIGdlbmVyYXRvci5cbiAgICAgKiBAbWV0aG9kIHNodWZmbGVcbiAgICAgKiBAcGFyYW0ge2FycmF5fSBhcnJheVxuICAgICAqIEByZXR1cm4ge2FycmF5fSBhcnJheSBzaHVmZmxlZCByYW5kb21seVxuICAgICAqL1xuICAgIENvbW1vbi5zaHVmZmxlID0gZnVuY3Rpb24oYXJyYXkpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IGFycmF5Lmxlbmd0aCAtIDE7IGkgPiAwOyBpLS0pIHtcbiAgICAgICAgICAgIHZhciBqID0gTWF0aC5mbG9vcihDb21tb24ucmFuZG9tKCkgKiAoaSArIDEpKTtcbiAgICAgICAgICAgIHZhciB0ZW1wID0gYXJyYXlbaV07XG4gICAgICAgICAgICBhcnJheVtpXSA9IGFycmF5W2pdO1xuICAgICAgICAgICAgYXJyYXlbal0gPSB0ZW1wO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhcnJheTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmFuZG9tbHkgY2hvb3NlcyBhIHZhbHVlIGZyb20gYSBsaXN0IHdpdGggZXF1YWwgcHJvYmFiaWxpdHkuXG4gICAgICogVGhlIGZ1bmN0aW9uIHVzZXMgYSBzZWVkZWQgcmFuZG9tIGdlbmVyYXRvci5cbiAgICAgKiBAbWV0aG9kIGNob29zZVxuICAgICAqIEBwYXJhbSB7YXJyYXl9IGNob2ljZXNcbiAgICAgKiBAcmV0dXJuIHtvYmplY3R9IEEgcmFuZG9tIGNob2ljZSBvYmplY3QgZnJvbSB0aGUgYXJyYXlcbiAgICAgKi9cbiAgICBDb21tb24uY2hvb3NlID0gZnVuY3Rpb24oY2hvaWNlcykge1xuICAgICAgICByZXR1cm4gY2hvaWNlc1tNYXRoLmZsb29yKENvbW1vbi5yYW5kb20oKSAqIGNob2ljZXMubGVuZ3RoKV07XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGEgSFRNTEVsZW1lbnQsIG90aGVyd2lzZSBmYWxzZS5cbiAgICAgKiBAbWV0aG9kIGlzRWxlbWVudFxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvYmpcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgYSBIVE1MRWxlbWVudCwgb3RoZXJ3aXNlIGZhbHNlXG4gICAgICovXG4gICAgQ29tbW9uLmlzRWxlbWVudCA9IGZ1bmN0aW9uKG9iaikge1xuICAgICAgICByZXR1cm4gb2JqIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQ7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGFuIGFycmF5LlxuICAgICAqIEBtZXRob2QgaXNBcnJheVxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvYmpcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgYW4gYXJyYXksIG90aGVyd2lzZSBmYWxzZVxuICAgICAqL1xuICAgIENvbW1vbi5pc0FycmF5ID0gZnVuY3Rpb24ob2JqKSB7XG4gICAgICAgIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwob2JqKSA9PT0gJ1tvYmplY3QgQXJyYXldJztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0cnVlIGlmIHRoZSBvYmplY3QgaXMgYSBmdW5jdGlvbi5cbiAgICAgKiBAbWV0aG9kIGlzRnVuY3Rpb25cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb2JqXG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGEgZnVuY3Rpb24sIG90aGVyd2lzZSBmYWxzZVxuICAgICAqL1xuICAgIENvbW1vbi5pc0Z1bmN0aW9uID0gZnVuY3Rpb24ob2JqKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2Ygb2JqID09PSBcImZ1bmN0aW9uXCI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGEgcGxhaW4gb2JqZWN0LlxuICAgICAqIEBtZXRob2QgaXNQbGFpbk9iamVjdFxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvYmpcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgYSBwbGFpbiBvYmplY3QsIG90aGVyd2lzZSBmYWxzZVxuICAgICAqL1xuICAgIENvbW1vbi5pc1BsYWluT2JqZWN0ID0gZnVuY3Rpb24ob2JqKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiBvYmouY29uc3RydWN0b3IgPT09IE9iamVjdDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0cnVlIGlmIHRoZSBvYmplY3QgaXMgYSBzdHJpbmcuXG4gICAgICogQG1ldGhvZCBpc1N0cmluZ1xuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvYmpcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgYSBzdHJpbmcsIG90aGVyd2lzZSBmYWxzZVxuICAgICAqL1xuICAgIENvbW1vbi5pc1N0cmluZyA9IGZ1bmN0aW9uKG9iaikge1xuICAgICAgICByZXR1cm4gdG9TdHJpbmcuY2FsbChvYmopID09PSAnW29iamVjdCBTdHJpbmddJztcbiAgICB9O1xuICAgIFxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGdpdmVuIHZhbHVlIGNsYW1wZWQgYmV0d2VlbiBhIG1pbmltdW0gYW5kIG1heGltdW0gdmFsdWUuXG4gICAgICogQG1ldGhvZCBjbGFtcFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBtaW5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbWF4XG4gICAgICogQHJldHVybiB7bnVtYmVyfSBUaGUgdmFsdWUgY2xhbXBlZCBiZXR3ZWVuIG1pbiBhbmQgbWF4IGluY2x1c2l2ZVxuICAgICAqL1xuICAgIENvbW1vbi5jbGFtcCA9IGZ1bmN0aW9uKHZhbHVlLCBtaW4sIG1heCkge1xuICAgICAgICBpZiAodmFsdWUgPCBtaW4pXG4gICAgICAgICAgICByZXR1cm4gbWluO1xuICAgICAgICBpZiAodmFsdWUgPiBtYXgpXG4gICAgICAgICAgICByZXR1cm4gbWF4O1xuICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBzaWduIG9mIHRoZSBnaXZlbiB2YWx1ZS5cbiAgICAgKiBAbWV0aG9kIHNpZ25cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gdmFsdWVcbiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IC0xIGlmIG5lZ2F0aXZlLCArMSBpZiAwIG9yIHBvc2l0aXZlXG4gICAgICovXG4gICAgQ29tbW9uLnNpZ24gPSBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgICByZXR1cm4gdmFsdWUgPCAwID8gLTEgOiAxO1xuICAgIH07XG4gICAgXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCB0aW1lc3RhbXAgc2luY2UgdGhlIHRpbWUgb3JpZ2luIChlLmcuIGZyb20gcGFnZSBsb2FkKS5cbiAgICAgKiBUaGUgcmVzdWx0IHdpbGwgYmUgaGlnaC1yZXNvbHV0aW9uIGluY2x1ZGluZyBkZWNpbWFsIHBsYWNlcyBpZiBhdmFpbGFibGUuXG4gICAgICogQG1ldGhvZCBub3dcbiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IHRoZSBjdXJyZW50IHRpbWVzdGFtcFxuICAgICAqL1xuICAgIENvbW1vbi5ub3cgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKHdpbmRvdy5wZXJmb3JtYW5jZSkge1xuICAgICAgICAgICAgaWYgKHdpbmRvdy5wZXJmb3JtYW5jZS5ub3cpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gd2luZG93LnBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICh3aW5kb3cucGVyZm9ybWFuY2Uud2Via2l0Tm93KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHdpbmRvdy5wZXJmb3JtYW5jZS53ZWJraXROb3coKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAobmV3IERhdGUoKSkgLSBDb21tb24uX25vd1N0YXJ0VGltZTtcbiAgICB9O1xuICAgIFxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYSByYW5kb20gdmFsdWUgYmV0d2VlbiBhIG1pbmltdW0gYW5kIGEgbWF4aW11bSB2YWx1ZSBpbmNsdXNpdmUuXG4gICAgICogVGhlIGZ1bmN0aW9uIHVzZXMgYSBzZWVkZWQgcmFuZG9tIGdlbmVyYXRvci5cbiAgICAgKiBAbWV0aG9kIHJhbmRvbVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBtaW5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbWF4XG4gICAgICogQHJldHVybiB7bnVtYmVyfSBBIHJhbmRvbSBudW1iZXIgYmV0d2VlbiBtaW4gYW5kIG1heCBpbmNsdXNpdmVcbiAgICAgKi9cbiAgICBDb21tb24ucmFuZG9tID0gZnVuY3Rpb24obWluLCBtYXgpIHtcbiAgICAgICAgbWluID0gKHR5cGVvZiBtaW4gIT09IFwidW5kZWZpbmVkXCIpID8gbWluIDogMDtcbiAgICAgICAgbWF4ID0gKHR5cGVvZiBtYXggIT09IFwidW5kZWZpbmVkXCIpID8gbWF4IDogMTtcbiAgICAgICAgcmV0dXJuIG1pbiArIF9zZWVkZWRSYW5kb20oKSAqIChtYXggLSBtaW4pO1xuICAgIH07XG5cbiAgICB2YXIgX3NlZWRlZFJhbmRvbSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAvLyBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaW5lYXJfY29uZ3J1ZW50aWFsX2dlbmVyYXRvclxuICAgICAgICBDb21tb24uX3NlZWQgPSAoQ29tbW9uLl9zZWVkICogOTMwMSArIDQ5Mjk3KSAlIDIzMzI4MDtcbiAgICAgICAgcmV0dXJuIENvbW1vbi5fc2VlZCAvIDIzMzI4MDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ29udmVydHMgYSBDU1MgaGV4IGNvbG91ciBzdHJpbmcgaW50byBhbiBpbnRlZ2VyLlxuICAgICAqIEBtZXRob2QgY29sb3JUb051bWJlclxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBjb2xvclN0cmluZ1xuICAgICAqIEByZXR1cm4ge251bWJlcn0gQW4gaW50ZWdlciByZXByZXNlbnRpbmcgdGhlIENTUyBoZXggc3RyaW5nXG4gICAgICovXG4gICAgQ29tbW9uLmNvbG9yVG9OdW1iZXIgPSBmdW5jdGlvbihjb2xvclN0cmluZykge1xuICAgICAgICBjb2xvclN0cmluZyA9IGNvbG9yU3RyaW5nLnJlcGxhY2UoJyMnLCcnKTtcblxuICAgICAgICBpZiAoY29sb3JTdHJpbmcubGVuZ3RoID09IDMpIHtcbiAgICAgICAgICAgIGNvbG9yU3RyaW5nID0gY29sb3JTdHJpbmcuY2hhckF0KDApICsgY29sb3JTdHJpbmcuY2hhckF0KDApXG4gICAgICAgICAgICAgICAgICAgICAgICArIGNvbG9yU3RyaW5nLmNoYXJBdCgxKSArIGNvbG9yU3RyaW5nLmNoYXJBdCgxKVxuICAgICAgICAgICAgICAgICAgICAgICAgKyBjb2xvclN0cmluZy5jaGFyQXQoMikgKyBjb2xvclN0cmluZy5jaGFyQXQoMik7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcGFyc2VJbnQoY29sb3JTdHJpbmcsIDE2KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVGhlIGNvbnNvbGUgbG9nZ2luZyBsZXZlbCB0byB1c2UsIHdoZXJlIGVhY2ggbGV2ZWwgaW5jbHVkZXMgYWxsIGxldmVscyBhYm92ZSBhbmQgZXhjbHVkZXMgdGhlIGxldmVscyBiZWxvdy5cbiAgICAgKiBUaGUgZGVmYXVsdCBsZXZlbCBpcyAnZGVidWcnIHdoaWNoIHNob3dzIGFsbCBjb25zb2xlIG1lc3NhZ2VzLiAgXG4gICAgICpcbiAgICAgKiBQb3NzaWJsZSBsZXZlbCB2YWx1ZXMgYXJlOlxuICAgICAqIC0gMCA9IE5vbmVcbiAgICAgKiAtIDEgPSBEZWJ1Z1xuICAgICAqIC0gMiA9IEluZm9cbiAgICAgKiAtIDMgPSBXYXJuXG4gICAgICogLSA0ID0gRXJyb3JcbiAgICAgKiBAcHJvcGVydHkgQ29tbW9uLmxvZ0xldmVsXG4gICAgICogQHR5cGUge051bWJlcn1cbiAgICAgKiBAZGVmYXVsdCAxXG4gICAgICovXG4gICAgQ29tbW9uLmxvZ0xldmVsID0gMTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgYGNvbnNvbGUubG9nYCBtZXNzYWdlIG9ubHkgaWYgdGhlIGN1cnJlbnQgYENvbW1vbi5sb2dMZXZlbGAgYWxsb3dzIGl0LlxuICAgICAqIFRoZSBtZXNzYWdlIHdpbGwgYmUgcHJlZml4ZWQgd2l0aCAnbWF0dGVyLWpzJyB0byBtYWtlIGl0IGVhc2lseSBpZGVudGlmaWFibGUuXG4gICAgICogQG1ldGhvZCBsb2dcbiAgICAgKiBAcGFyYW0gLi4ub2JqcyB7fSBUaGUgb2JqZWN0cyB0byBsb2cuXG4gICAgICovXG4gICAgQ29tbW9uLmxvZyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoY29uc29sZSAmJiBDb21tb24ubG9nTGV2ZWwgPiAwICYmIENvbW1vbi5sb2dMZXZlbCA8PSAzKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZy5hcHBseShjb25zb2xlLCBbJ21hdHRlci1qczonXS5jb25jYXQoQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSkpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgYGNvbnNvbGUuaW5mb2AgbWVzc2FnZSBvbmx5IGlmIHRoZSBjdXJyZW50IGBDb21tb24ubG9nTGV2ZWxgIGFsbG93cyBpdC5cbiAgICAgKiBUaGUgbWVzc2FnZSB3aWxsIGJlIHByZWZpeGVkIHdpdGggJ21hdHRlci1qcycgdG8gbWFrZSBpdCBlYXNpbHkgaWRlbnRpZmlhYmxlLlxuICAgICAqIEBtZXRob2QgaW5mb1xuICAgICAqIEBwYXJhbSAuLi5vYmpzIHt9IFRoZSBvYmplY3RzIHRvIGxvZy5cbiAgICAgKi9cbiAgICBDb21tb24uaW5mbyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoY29uc29sZSAmJiBDb21tb24ubG9nTGV2ZWwgPiAwICYmIENvbW1vbi5sb2dMZXZlbCA8PSAyKSB7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8uYXBwbHkoY29uc29sZSwgWydtYXR0ZXItanM6J10uY29uY2F0KEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIGBjb25zb2xlLndhcm5gIG1lc3NhZ2Ugb25seSBpZiB0aGUgY3VycmVudCBgQ29tbW9uLmxvZ0xldmVsYCBhbGxvd3MgaXQuXG4gICAgICogVGhlIG1lc3NhZ2Ugd2lsbCBiZSBwcmVmaXhlZCB3aXRoICdtYXR0ZXItanMnIHRvIG1ha2UgaXQgZWFzaWx5IGlkZW50aWZpYWJsZS5cbiAgICAgKiBAbWV0aG9kIHdhcm5cbiAgICAgKiBAcGFyYW0gLi4ub2JqcyB7fSBUaGUgb2JqZWN0cyB0byBsb2cuXG4gICAgICovXG4gICAgQ29tbW9uLndhcm4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKGNvbnNvbGUgJiYgQ29tbW9uLmxvZ0xldmVsID4gMCAmJiBDb21tb24ubG9nTGV2ZWwgPD0gMykge1xuICAgICAgICAgICAgY29uc29sZS53YXJuLmFwcGx5KGNvbnNvbGUsIFsnbWF0dGVyLWpzOiddLmNvbmNhdChBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpKSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbmV4dCB1bmlxdWUgc2VxdWVudGlhbCBJRC5cbiAgICAgKiBAbWV0aG9kIG5leHRJZFxuICAgICAqIEByZXR1cm4ge051bWJlcn0gVW5pcXVlIHNlcXVlbnRpYWwgSURcbiAgICAgKi9cbiAgICBDb21tb24ubmV4dElkID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBDb21tb24uX25leHRJZCsrO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBBIGNyb3NzIGJyb3dzZXIgY29tcGF0aWJsZSBpbmRleE9mIGltcGxlbWVudGF0aW9uLlxuICAgICAqIEBtZXRob2QgaW5kZXhPZlxuICAgICAqIEBwYXJhbSB7YXJyYXl9IGhheXN0YWNrXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG5lZWRsZVxuICAgICAqIEByZXR1cm4ge251bWJlcn0gVGhlIHBvc2l0aW9uIG9mIG5lZWRsZSBpbiBoYXlzdGFjaywgb3RoZXJ3aXNlIC0xLlxuICAgICAqL1xuICAgIENvbW1vbi5pbmRleE9mID0gZnVuY3Rpb24oaGF5c3RhY2ssIG5lZWRsZSkge1xuICAgICAgICBpZiAoaGF5c3RhY2suaW5kZXhPZilcbiAgICAgICAgICAgIHJldHVybiBoYXlzdGFjay5pbmRleE9mKG5lZWRsZSk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoYXlzdGFjay5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKGhheXN0YWNrW2ldID09PSBuZWVkbGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuIGk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gLTE7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEEgY3Jvc3MgYnJvd3NlciBjb21wYXRpYmxlIGFycmF5IG1hcCBpbXBsZW1lbnRhdGlvbi5cbiAgICAgKiBAbWV0aG9kIG1hcFxuICAgICAqIEBwYXJhbSB7YXJyYXl9IGxpc3RcbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBmdW5jXG4gICAgICogQHJldHVybiB7YXJyYXl9IFZhbHVlcyBmcm9tIGxpc3QgdHJhbnNmb3JtZWQgYnkgZnVuYy5cbiAgICAgKi9cbiAgICBDb21tb24ubWFwID0gZnVuY3Rpb24obGlzdCwgZnVuYykge1xuICAgICAgICBpZiAobGlzdC5tYXApIHtcbiAgICAgICAgICAgIHJldHVybiBsaXN0Lm1hcChmdW5jKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBtYXBwZWQgPSBbXTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpc3QubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIG1hcHBlZC5wdXNoKGZ1bmMobGlzdFtpXSkpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG1hcHBlZDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVGFrZXMgYSBkaXJlY3RlZCBncmFwaCBhbmQgcmV0dXJucyB0aGUgcGFydGlhbGx5IG9yZGVyZWQgc2V0IG9mIHZlcnRpY2VzIGluIHRvcG9sb2dpY2FsIG9yZGVyLlxuICAgICAqIENpcmN1bGFyIGRlcGVuZGVuY2llcyBhcmUgYWxsb3dlZC5cbiAgICAgKiBAbWV0aG9kIHRvcG9sb2dpY2FsU29ydFxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBncmFwaFxuICAgICAqIEByZXR1cm4ge2FycmF5fSBQYXJ0aWFsbHkgb3JkZXJlZCBzZXQgb2YgdmVydGljZXMgaW4gdG9wb2xvZ2ljYWwgb3JkZXIuXG4gICAgICovXG4gICAgQ29tbW9uLnRvcG9sb2dpY2FsU29ydCA9IGZ1bmN0aW9uKGdyYXBoKSB7XG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9tZ2VjaGV2L2phdmFzY3JpcHQtYWxnb3JpdGhtc1xuICAgICAgICAvLyBDb3B5cmlnaHQgKGMpIE1pbmtvIEdlY2hldiAoTUlUIGxpY2Vuc2UpXG4gICAgICAgIC8vIE1vZGlmaWNhdGlvbnM6IHRpZHkgZm9ybWF0dGluZyBhbmQgbmFtaW5nXG4gICAgICAgIHZhciByZXN1bHQgPSBbXSxcbiAgICAgICAgICAgIHZpc2l0ZWQgPSBbXSxcbiAgICAgICAgICAgIHRlbXAgPSBbXTtcblxuICAgICAgICBmb3IgKHZhciBub2RlIGluIGdyYXBoKSB7XG4gICAgICAgICAgICBpZiAoIXZpc2l0ZWRbbm9kZV0gJiYgIXRlbXBbbm9kZV0pIHtcbiAgICAgICAgICAgICAgICBfdG9wb2xvZ2ljYWxTb3J0KG5vZGUsIHZpc2l0ZWQsIHRlbXAsIGdyYXBoLCByZXN1bHQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuXG4gICAgdmFyIF90b3BvbG9naWNhbFNvcnQgPSBmdW5jdGlvbihub2RlLCB2aXNpdGVkLCB0ZW1wLCBncmFwaCwgcmVzdWx0KSB7XG4gICAgICAgIHZhciBuZWlnaGJvcnMgPSBncmFwaFtub2RlXSB8fCBbXTtcbiAgICAgICAgdGVtcFtub2RlXSA9IHRydWU7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBuZWlnaGJvcnMubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIHZhciBuZWlnaGJvciA9IG5laWdoYm9yc1tpXTtcblxuICAgICAgICAgICAgaWYgKHRlbXBbbmVpZ2hib3JdKSB7XG4gICAgICAgICAgICAgICAgLy8gc2tpcCBjaXJjdWxhciBkZXBlbmRlbmNpZXNcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCF2aXNpdGVkW25laWdoYm9yXSkge1xuICAgICAgICAgICAgICAgIF90b3BvbG9naWNhbFNvcnQobmVpZ2hib3IsIHZpc2l0ZWQsIHRlbXAsIGdyYXBoLCByZXN1bHQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdGVtcFtub2RlXSA9IGZhbHNlO1xuICAgICAgICB2aXNpdGVkW25vZGVdID0gdHJ1ZTtcblxuICAgICAgICByZXN1bHQucHVzaChub2RlKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVGFrZXMgX25fIGZ1bmN0aW9ucyBhcyBhcmd1bWVudHMgYW5kIHJldHVybnMgYSBuZXcgZnVuY3Rpb24gdGhhdCBjYWxscyB0aGVtIGluIG9yZGVyLlxuICAgICAqIFRoZSBhcmd1bWVudHMgYXBwbGllZCB3aGVuIGNhbGxpbmcgdGhlIG5ldyBmdW5jdGlvbiB3aWxsIGFsc28gYmUgYXBwbGllZCB0byBldmVyeSBmdW5jdGlvbiBwYXNzZWQuXG4gICAgICogVGhlIHZhbHVlIG9mIGB0aGlzYCByZWZlcnMgdG8gdGhlIGxhc3QgdmFsdWUgcmV0dXJuZWQgaW4gdGhlIGNoYWluIHRoYXQgd2FzIG5vdCBgdW5kZWZpbmVkYC5cbiAgICAgKiBUaGVyZWZvcmUgaWYgYSBwYXNzZWQgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUsIHRoZSBwcmV2aW91c2x5IHJldHVybmVkIHZhbHVlIGlzIG1haW50YWluZWQuXG4gICAgICogQWZ0ZXIgYWxsIHBhc3NlZCBmdW5jdGlvbnMgaGF2ZSBiZWVuIGNhbGxlZCB0aGUgbmV3IGZ1bmN0aW9uIHJldHVybnMgdGhlIGxhc3QgcmV0dXJuZWQgdmFsdWUgKGlmIGFueSkuXG4gICAgICogSWYgYW55IG9mIHRoZSBwYXNzZWQgZnVuY3Rpb25zIGFyZSBhIGNoYWluLCB0aGVuIHRoZSBjaGFpbiB3aWxsIGJlIGZsYXR0ZW5lZC5cbiAgICAgKiBAbWV0aG9kIGNoYWluXG4gICAgICogQHBhcmFtIC4uLmZ1bmNzIHtmdW5jdGlvbn0gVGhlIGZ1bmN0aW9ucyB0byBjaGFpbi5cbiAgICAgKiBAcmV0dXJuIHtmdW5jdGlvbn0gQSBuZXcgZnVuY3Rpb24gdGhhdCBjYWxscyB0aGUgcGFzc2VkIGZ1bmN0aW9ucyBpbiBvcmRlci5cbiAgICAgKi9cbiAgICBDb21tb24uY2hhaW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGZ1bmNzID0gW107XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIHZhciBmdW5jID0gYXJndW1lbnRzW2ldO1xuXG4gICAgICAgICAgICBpZiAoZnVuYy5fY2hhaW5lZCkge1xuICAgICAgICAgICAgICAgIC8vIGZsYXR0ZW4gYWxyZWFkeSBjaGFpbmVkIGZ1bmN0aW9uc1xuICAgICAgICAgICAgICAgIGZ1bmNzLnB1c2guYXBwbHkoZnVuY3MsIGZ1bmMuX2NoYWluZWQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBmdW5jcy5wdXNoKGZ1bmMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGNoYWluID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vR29vZ2xlQ2hyb21lL2RldnRvb2xzLWRvY3MvaXNzdWVzLzUzI2lzc3VlY29tbWVudC01MTk0MTM1OFxuICAgICAgICAgICAgdmFyIGxhc3RSZXN1bHQsXG4gICAgICAgICAgICAgICAgYXJncyA9IG5ldyBBcnJheShhcmd1bWVudHMubGVuZ3RoKTtcblxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBhcmd1bWVudHMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgYXJnc1tpXSA9IGFyZ3VtZW50c1tpXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IGZ1bmNzLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdCA9IGZ1bmNzW2ldLmFwcGx5KGxhc3RSZXN1bHQsIGFyZ3MpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiByZXN1bHQgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICAgICAgICAgIGxhc3RSZXN1bHQgPSByZXN1bHQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gbGFzdFJlc3VsdDtcbiAgICAgICAgfTtcblxuICAgICAgICBjaGFpbi5fY2hhaW5lZCA9IGZ1bmNzO1xuXG4gICAgICAgIHJldHVybiBjaGFpbjtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2hhaW5zIGEgZnVuY3Rpb24gdG8gZXhjdXRlIGJlZm9yZSB0aGUgb3JpZ2luYWwgZnVuY3Rpb24gb24gdGhlIGdpdmVuIGBwYXRoYCByZWxhdGl2ZSB0byBgYmFzZWAuXG4gICAgICogU2VlIGFsc28gZG9jcyBmb3IgYENvbW1vbi5jaGFpbmAuXG4gICAgICogQG1ldGhvZCBjaGFpblBhdGhCZWZvcmVcbiAgICAgKiBAcGFyYW0ge30gYmFzZSBUaGUgYmFzZSBvYmplY3RcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCBUaGUgcGF0aCByZWxhdGl2ZSB0byBgYmFzZWBcbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBjaGFpbiBiZWZvcmUgdGhlIG9yaWdpbmFsXG4gICAgICogQHJldHVybiB7ZnVuY3Rpb259IFRoZSBjaGFpbmVkIGZ1bmN0aW9uIHRoYXQgcmVwbGFjZWQgdGhlIG9yaWdpbmFsXG4gICAgICovXG4gICAgQ29tbW9uLmNoYWluUGF0aEJlZm9yZSA9IGZ1bmN0aW9uKGJhc2UsIHBhdGgsIGZ1bmMpIHtcbiAgICAgICAgcmV0dXJuIENvbW1vbi5zZXQoYmFzZSwgcGF0aCwgQ29tbW9uLmNoYWluKFxuICAgICAgICAgICAgZnVuYyxcbiAgICAgICAgICAgIENvbW1vbi5nZXQoYmFzZSwgcGF0aClcbiAgICAgICAgKSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENoYWlucyBhIGZ1bmN0aW9uIHRvIGV4Y3V0ZSBhZnRlciB0aGUgb3JpZ2luYWwgZnVuY3Rpb24gb24gdGhlIGdpdmVuIGBwYXRoYCByZWxhdGl2ZSB0byBgYmFzZWAuXG4gICAgICogU2VlIGFsc28gZG9jcyBmb3IgYENvbW1vbi5jaGFpbmAuXG4gICAgICogQG1ldGhvZCBjaGFpblBhdGhBZnRlclxuICAgICAqIEBwYXJhbSB7fSBiYXNlIFRoZSBiYXNlIG9iamVjdFxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFRoZSBwYXRoIHJlbGF0aXZlIHRvIGBiYXNlYFxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGNoYWluIGFmdGVyIHRoZSBvcmlnaW5hbFxuICAgICAqIEByZXR1cm4ge2Z1bmN0aW9ufSBUaGUgY2hhaW5lZCBmdW5jdGlvbiB0aGF0IHJlcGxhY2VkIHRoZSBvcmlnaW5hbFxuICAgICAqL1xuICAgIENvbW1vbi5jaGFpblBhdGhBZnRlciA9IGZ1bmN0aW9uKGJhc2UsIHBhdGgsIGZ1bmMpIHtcbiAgICAgICAgcmV0dXJuIENvbW1vbi5zZXQoYmFzZSwgcGF0aCwgQ29tbW9uLmNoYWluKFxuICAgICAgICAgICAgQ29tbW9uLmdldChiYXNlLCBwYXRoKSxcbiAgICAgICAgICAgIGZ1bmNcbiAgICAgICAgKSk7XG4gICAgfTtcblxufSkoKTtcblxufSx7fV0sMTU6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLkVuZ2luZWAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGNyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgZW5naW5lcy5cbiogQW4gZW5naW5lIGlzIGEgY29udHJvbGxlciB0aGF0IG1hbmFnZXMgdXBkYXRpbmcgdGhlIHNpbXVsYXRpb24gb2YgdGhlIHdvcmxkLlxuKiBTZWUgYE1hdHRlci5SdW5uZXJgIGZvciBhbiBvcHRpb25hbCBnYW1lIGxvb3AgdXRpbGl0eS5cbipcbiogU2VlIHRoZSBpbmNsdWRlZCB1c2FnZSBbZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9saWFicnUvbWF0dGVyLWpzL3RyZWUvbWFzdGVyL2V4YW1wbGVzKS5cbipcbiogQGNsYXNzIEVuZ2luZVxuKi9cblxudmFyIEVuZ2luZSA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEVuZ2luZTtcblxudmFyIFdvcmxkID0gX2RlcmVxXygnLi4vYm9keS9Xb3JsZCcpO1xudmFyIFNsZWVwaW5nID0gX2RlcmVxXygnLi9TbGVlcGluZycpO1xudmFyIFJlc29sdmVyID0gX2RlcmVxXygnLi4vY29sbGlzaW9uL1Jlc29sdmVyJyk7XG52YXIgUmVuZGVyID0gX2RlcmVxXygnLi4vcmVuZGVyL1JlbmRlcicpO1xudmFyIFBhaXJzID0gX2RlcmVxXygnLi4vY29sbGlzaW9uL1BhaXJzJyk7XG52YXIgTWV0cmljcyA9IF9kZXJlcV8oJy4vTWV0cmljcycpO1xudmFyIEdyaWQgPSBfZGVyZXFfKCcuLi9jb2xsaXNpb24vR3JpZCcpO1xudmFyIEV2ZW50cyA9IF9kZXJlcV8oJy4vRXZlbnRzJyk7XG52YXIgQ29tcG9zaXRlID0gX2RlcmVxXygnLi4vYm9keS9Db21wb3NpdGUnKTtcbnZhciBDb25zdHJhaW50ID0gX2RlcmVxXygnLi4vY29uc3RyYWludC9Db25zdHJhaW50Jyk7XG52YXIgQ29tbW9uID0gX2RlcmVxXygnLi9Db21tb24nKTtcbnZhciBCb2R5ID0gX2RlcmVxXygnLi4vYm9keS9Cb2R5Jyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBuZXcgZW5naW5lLiBUaGUgb3B0aW9ucyBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXQgc3BlY2lmaWVzIGFueSBwcm9wZXJ0aWVzIHlvdSB3aXNoIHRvIG92ZXJyaWRlIHRoZSBkZWZhdWx0cy5cbiAgICAgKiBBbGwgcHJvcGVydGllcyBoYXZlIGRlZmF1bHQgdmFsdWVzLCBhbmQgbWFueSBhcmUgcHJlLWNhbGN1bGF0ZWQgYXV0b21hdGljYWxseSBiYXNlZCBvbiBvdGhlciBwcm9wZXJ0aWVzLlxuICAgICAqIFNlZSB0aGUgcHJvcGVydGllcyBzZWN0aW9uIGJlbG93IGZvciBkZXRhaWxlZCBpbmZvcm1hdGlvbiBvbiB3aGF0IHlvdSBjYW4gcGFzcyB2aWEgdGhlIGBvcHRpb25zYCBvYmplY3QuXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdGlvbnNdXG4gICAgICogQHJldHVybiB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKi9cbiAgICBFbmdpbmUuY3JlYXRlID0gZnVuY3Rpb24oZWxlbWVudCwgb3B0aW9ucykge1xuICAgICAgICAvLyBvcHRpb25zIG1heSBiZSBwYXNzZWQgYXMgdGhlIGZpcnN0IChhbmQgb25seSkgYXJndW1lbnRcbiAgICAgICAgb3B0aW9ucyA9IENvbW1vbi5pc0VsZW1lbnQoZWxlbWVudCkgPyBvcHRpb25zIDogZWxlbWVudDtcbiAgICAgICAgZWxlbWVudCA9IENvbW1vbi5pc0VsZW1lbnQoZWxlbWVudCkgPyBlbGVtZW50IDogbnVsbDtcbiAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgaWYgKGVsZW1lbnQgfHwgb3B0aW9ucy5yZW5kZXIpIHtcbiAgICAgICAgICAgIENvbW1vbi53YXJuKCdFbmdpbmUuY3JlYXRlOiBlbmdpbmUucmVuZGVyIGlzIGRlcHJlY2F0ZWQgKHNlZSBkb2NzKScpO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAgICAgcG9zaXRpb25JdGVyYXRpb25zOiA2LFxuICAgICAgICAgICAgdmVsb2NpdHlJdGVyYXRpb25zOiA0LFxuICAgICAgICAgICAgY29uc3RyYWludEl0ZXJhdGlvbnM6IDIsXG4gICAgICAgICAgICBlbmFibGVTbGVlcGluZzogZmFsc2UsXG4gICAgICAgICAgICBldmVudHM6IFtdLFxuICAgICAgICAgICAgcGx1Z2luOiB7fSxcbiAgICAgICAgICAgIHRpbWluZzoge1xuICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogMCxcbiAgICAgICAgICAgICAgICB0aW1lU2NhbGU6IDFcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBicm9hZHBoYXNlOiB7XG4gICAgICAgICAgICAgICAgY29udHJvbGxlcjogR3JpZFxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIHZhciBlbmdpbmUgPSBDb21tb24uZXh0ZW5kKGRlZmF1bHRzLCBvcHRpb25zKTtcblxuICAgICAgICAvLyBAZGVwcmVjYXRlZFxuICAgICAgICBpZiAoZWxlbWVudCB8fCBlbmdpbmUucmVuZGVyKSB7XG4gICAgICAgICAgICB2YXIgcmVuZGVyRGVmYXVsdHMgPSB7XG4gICAgICAgICAgICAgICAgZWxlbWVudDogZWxlbWVudCxcbiAgICAgICAgICAgICAgICBjb250cm9sbGVyOiBSZW5kZXJcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGVuZ2luZS5yZW5kZXIgPSBDb21tb24uZXh0ZW5kKHJlbmRlckRlZmF1bHRzLCBlbmdpbmUucmVuZGVyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEBkZXByZWNhdGVkXG4gICAgICAgIGlmIChlbmdpbmUucmVuZGVyICYmIGVuZ2luZS5yZW5kZXIuY29udHJvbGxlcikge1xuICAgICAgICAgICAgZW5naW5lLnJlbmRlciA9IGVuZ2luZS5yZW5kZXIuY29udHJvbGxlci5jcmVhdGUoZW5naW5lLnJlbmRlcik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBAZGVwcmVjYXRlZFxuICAgICAgICBpZiAoZW5naW5lLnJlbmRlcikge1xuICAgICAgICAgICAgZW5naW5lLnJlbmRlci5lbmdpbmUgPSBlbmdpbmU7XG4gICAgICAgIH1cblxuICAgICAgICBlbmdpbmUud29ybGQgPSBvcHRpb25zLndvcmxkIHx8IFdvcmxkLmNyZWF0ZShlbmdpbmUud29ybGQpO1xuICAgICAgICBlbmdpbmUucGFpcnMgPSBQYWlycy5jcmVhdGUoKTtcbiAgICAgICAgZW5naW5lLmJyb2FkcGhhc2UgPSBlbmdpbmUuYnJvYWRwaGFzZS5jb250cm9sbGVyLmNyZWF0ZShlbmdpbmUuYnJvYWRwaGFzZSk7XG4gICAgICAgIGVuZ2luZS5tZXRyaWNzID0gZW5naW5lLm1ldHJpY3MgfHwgeyBleHRlbmRlZDogZmFsc2UgfTtcblxuXG4gICAgICAgIHJldHVybiBlbmdpbmU7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE1vdmVzIHRoZSBzaW11bGF0aW9uIGZvcndhcmQgaW4gdGltZSBieSBgZGVsdGFgIG1zLlxuICAgICAqIFRoZSBgY29ycmVjdGlvbmAgYXJndW1lbnQgaXMgYW4gb3B0aW9uYWwgYE51bWJlcmAgdGhhdCBzcGVjaWZpZXMgdGhlIHRpbWUgY29ycmVjdGlvbiBmYWN0b3IgdG8gYXBwbHkgdG8gdGhlIHVwZGF0ZS5cbiAgICAgKiBUaGlzIGNhbiBoZWxwIGltcHJvdmUgdGhlIGFjY3VyYWN5IG9mIHRoZSBzaW11bGF0aW9uIGluIGNhc2VzIHdoZXJlIGBkZWx0YWAgaXMgY2hhbmdpbmcgYmV0d2VlbiB1cGRhdGVzLlxuICAgICAqIFRoZSB2YWx1ZSBvZiBgY29ycmVjdGlvbmAgaXMgZGVmaW5lZCBhcyBgZGVsdGEgLyBsYXN0RGVsdGFgLCBpLmUuIHRoZSBwZXJjZW50YWdlIGNoYW5nZSBvZiBgZGVsdGFgIG92ZXIgdGhlIGxhc3Qgc3RlcC5cbiAgICAgKiBUaGVyZWZvcmUgdGhlIHZhbHVlIGlzIGFsd2F5cyBgMWAgKG5vIGNvcnJlY3Rpb24pIHdoZW4gYGRlbHRhYCBjb25zdGFudCAob3Igd2hlbiBubyBjb3JyZWN0aW9uIGlzIGRlc2lyZWQsIHdoaWNoIGlzIHRoZSBkZWZhdWx0KS5cbiAgICAgKiBTZWUgdGhlIHBhcGVyIG9uIDxhIGhyZWY9XCJodHRwOi8vbG9uZXNvY2submV0L2FydGljbGUvdmVybGV0Lmh0bWxcIj5UaW1lIENvcnJlY3RlZCBWZXJsZXQ8L2E+IGZvciBtb3JlIGluZm9ybWF0aW9uLlxuICAgICAqXG4gICAgICogVHJpZ2dlcnMgYGJlZm9yZVVwZGF0ZWAgYW5kIGBhZnRlclVwZGF0ZWAgZXZlbnRzLlxuICAgICAqIFRyaWdnZXJzIGBjb2xsaXNpb25TdGFydGAsIGBjb2xsaXNpb25BY3RpdmVgIGFuZCBgY29sbGlzaW9uRW5kYCBldmVudHMuXG4gICAgICogQG1ldGhvZCB1cGRhdGVcbiAgICAgKiBAcGFyYW0ge2VuZ2luZX0gZW5naW5lXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtkZWx0YT0xNi42NjZdXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtjb3JyZWN0aW9uPTFdXG4gICAgICovXG4gICAgRW5naW5lLnVwZGF0ZSA9IGZ1bmN0aW9uKGVuZ2luZSwgZGVsdGEsIGNvcnJlY3Rpb24pIHtcbiAgICAgICAgZGVsdGEgPSBkZWx0YSB8fCAxMDAwIC8gNjA7XG4gICAgICAgIGNvcnJlY3Rpb24gPSBjb3JyZWN0aW9uIHx8IDE7XG5cbiAgICAgICAgdmFyIHdvcmxkID0gZW5naW5lLndvcmxkLFxuICAgICAgICAgICAgdGltaW5nID0gZW5naW5lLnRpbWluZyxcbiAgICAgICAgICAgIGJyb2FkcGhhc2UgPSBlbmdpbmUuYnJvYWRwaGFzZSxcbiAgICAgICAgICAgIGJyb2FkcGhhc2VQYWlycyA9IFtdLFxuICAgICAgICAgICAgaTtcblxuICAgICAgICAvLyBpbmNyZW1lbnQgdGltZXN0YW1wXG4gICAgICAgIHRpbWluZy50aW1lc3RhbXAgKz0gZGVsdGEgKiB0aW1pbmcudGltZVNjYWxlO1xuXG4gICAgICAgIC8vIGNyZWF0ZSBhbiBldmVudCBvYmplY3RcbiAgICAgICAgdmFyIGV2ZW50ID0ge1xuICAgICAgICAgICAgdGltZXN0YW1wOiB0aW1pbmcudGltZXN0YW1wXG4gICAgICAgIH07XG5cbiAgICAgICAgRXZlbnRzLnRyaWdnZXIoZW5naW5lLCAnYmVmb3JlVXBkYXRlJywgZXZlbnQpO1xuXG4gICAgICAgIC8vIGdldCBsaXN0cyBvZiBhbGwgYm9kaWVzIGFuZCBjb25zdHJhaW50cywgbm8gbWF0dGVyIHdoYXQgY29tcG9zaXRlcyB0aGV5IGFyZSBpblxuICAgICAgICB2YXIgYWxsQm9kaWVzID0gQ29tcG9zaXRlLmFsbEJvZGllcyh3b3JsZCksXG4gICAgICAgICAgICBhbGxDb25zdHJhaW50cyA9IENvbXBvc2l0ZS5hbGxDb25zdHJhaW50cyh3b3JsZCk7XG5cblxuICAgICAgICAvLyBpZiBzbGVlcGluZyBlbmFibGVkLCBjYWxsIHRoZSBzbGVlcGluZyBjb250cm9sbGVyXG4gICAgICAgIGlmIChlbmdpbmUuZW5hYmxlU2xlZXBpbmcpXG4gICAgICAgICAgICBTbGVlcGluZy51cGRhdGUoYWxsQm9kaWVzLCB0aW1pbmcudGltZVNjYWxlKTtcblxuICAgICAgICAvLyBhcHBsaWVzIGdyYXZpdHkgdG8gYWxsIGJvZGllc1xuICAgICAgICBfYm9kaWVzQXBwbHlHcmF2aXR5KGFsbEJvZGllcywgd29ybGQuZ3Jhdml0eSk7XG5cbiAgICAgICAgLy8gdXBkYXRlIGFsbCBib2R5IHBvc2l0aW9uIGFuZCByb3RhdGlvbiBieSBpbnRlZ3JhdGlvblxuICAgICAgICBfYm9kaWVzVXBkYXRlKGFsbEJvZGllcywgZGVsdGEsIHRpbWluZy50aW1lU2NhbGUsIGNvcnJlY3Rpb24sIHdvcmxkLmJvdW5kcyk7XG5cbiAgICAgICAgLy8gdXBkYXRlIGFsbCBjb25zdHJhaW50cyAoZmlyc3QgcGFzcylcbiAgICAgICAgQ29uc3RyYWludC5wcmVTb2x2ZUFsbChhbGxCb2RpZXMpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgZW5naW5lLmNvbnN0cmFpbnRJdGVyYXRpb25zOyBpKyspIHtcbiAgICAgICAgICAgIENvbnN0cmFpbnQuc29sdmVBbGwoYWxsQ29uc3RyYWludHMsIHRpbWluZy50aW1lU2NhbGUpO1xuICAgICAgICB9XG4gICAgICAgIENvbnN0cmFpbnQucG9zdFNvbHZlQWxsKGFsbEJvZGllcyk7XG5cbiAgICAgICAgLy8gYnJvYWRwaGFzZSBwYXNzOiBmaW5kIHBvdGVudGlhbCBjb2xsaXNpb24gcGFpcnNcbiAgICAgICAgaWYgKGJyb2FkcGhhc2UuY29udHJvbGxlcikge1xuICAgICAgICAgICAgLy8gaWYgd29ybGQgaXMgZGlydHksIHdlIG11c3QgZmx1c2ggdGhlIHdob2xlIGdyaWRcbiAgICAgICAgICAgIGlmICh3b3JsZC5pc01vZGlmaWVkKVxuICAgICAgICAgICAgICAgIGJyb2FkcGhhc2UuY29udHJvbGxlci5jbGVhcihicm9hZHBoYXNlKTtcblxuICAgICAgICAgICAgLy8gdXBkYXRlIHRoZSBncmlkIGJ1Y2tldHMgYmFzZWQgb24gY3VycmVudCBib2RpZXNcbiAgICAgICAgICAgIGJyb2FkcGhhc2UuY29udHJvbGxlci51cGRhdGUoYnJvYWRwaGFzZSwgYWxsQm9kaWVzLCBlbmdpbmUsIHdvcmxkLmlzTW9kaWZpZWQpO1xuICAgICAgICAgICAgYnJvYWRwaGFzZVBhaXJzID0gYnJvYWRwaGFzZS5wYWlyc0xpc3Q7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBpZiBubyBicm9hZHBoYXNlIHNldCwgd2UganVzdCBwYXNzIGFsbCBib2RpZXNcbiAgICAgICAgICAgIGJyb2FkcGhhc2VQYWlycyA9IGFsbEJvZGllcztcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGNsZWFyIGFsbCBjb21wb3NpdGUgbW9kaWZpZWQgZmxhZ3NcbiAgICAgICAgaWYgKHdvcmxkLmlzTW9kaWZpZWQpIHtcbiAgICAgICAgICAgIENvbXBvc2l0ZS5zZXRNb2RpZmllZCh3b3JsZCwgZmFsc2UsIGZhbHNlLCB0cnVlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG5hcnJvd3BoYXNlIHBhc3M6IGZpbmQgYWN0dWFsIGNvbGxpc2lvbnMsIHRoZW4gY3JlYXRlIG9yIHVwZGF0ZSBjb2xsaXNpb24gcGFpcnNcbiAgICAgICAgdmFyIGNvbGxpc2lvbnMgPSBicm9hZHBoYXNlLmRldGVjdG9yKGJyb2FkcGhhc2VQYWlycywgZW5naW5lKTtcblxuICAgICAgICAvLyB1cGRhdGUgY29sbGlzaW9uIHBhaXJzXG4gICAgICAgIHZhciBwYWlycyA9IGVuZ2luZS5wYWlycyxcbiAgICAgICAgICAgIHRpbWVzdGFtcCA9IHRpbWluZy50aW1lc3RhbXA7XG4gICAgICAgIFBhaXJzLnVwZGF0ZShwYWlycywgY29sbGlzaW9ucywgdGltZXN0YW1wKTtcbiAgICAgICAgUGFpcnMucmVtb3ZlT2xkKHBhaXJzLCB0aW1lc3RhbXApO1xuXG4gICAgICAgIC8vIHdha2UgdXAgYm9kaWVzIGludm9sdmVkIGluIGNvbGxpc2lvbnNcbiAgICAgICAgaWYgKGVuZ2luZS5lbmFibGVTbGVlcGluZylcbiAgICAgICAgICAgIFNsZWVwaW5nLmFmdGVyQ29sbGlzaW9ucyhwYWlycy5saXN0LCB0aW1pbmcudGltZVNjYWxlKTtcblxuICAgICAgICAvLyB0cmlnZ2VyIGNvbGxpc2lvbiBldmVudHNcbiAgICAgICAgaWYgKHBhaXJzLmNvbGxpc2lvblN0YXJ0Lmxlbmd0aCA+IDApXG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihlbmdpbmUsICdjb2xsaXNpb25TdGFydCcsIHsgcGFpcnM6IHBhaXJzLmNvbGxpc2lvblN0YXJ0IH0pO1xuXG4gICAgICAgIC8vIGl0ZXJhdGl2ZWx5IHJlc29sdmUgcG9zaXRpb24gYmV0d2VlbiBjb2xsaXNpb25zXG4gICAgICAgIFJlc29sdmVyLnByZVNvbHZlUG9zaXRpb24ocGFpcnMubGlzdCk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBlbmdpbmUucG9zaXRpb25JdGVyYXRpb25zOyBpKyspIHtcbiAgICAgICAgICAgIFJlc29sdmVyLnNvbHZlUG9zaXRpb24ocGFpcnMubGlzdCwgdGltaW5nLnRpbWVTY2FsZSk7XG4gICAgICAgIH1cbiAgICAgICAgUmVzb2x2ZXIucG9zdFNvbHZlUG9zaXRpb24oYWxsQm9kaWVzKTtcblxuICAgICAgICAvLyB1cGRhdGUgYWxsIGNvbnN0cmFpbnRzIChzZWNvbmQgcGFzcylcbiAgICAgICAgQ29uc3RyYWludC5wcmVTb2x2ZUFsbChhbGxCb2RpZXMpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgZW5naW5lLmNvbnN0cmFpbnRJdGVyYXRpb25zOyBpKyspIHtcbiAgICAgICAgICAgIENvbnN0cmFpbnQuc29sdmVBbGwoYWxsQ29uc3RyYWludHMsIHRpbWluZy50aW1lU2NhbGUpO1xuICAgICAgICB9XG4gICAgICAgIENvbnN0cmFpbnQucG9zdFNvbHZlQWxsKGFsbEJvZGllcyk7XG5cbiAgICAgICAgLy8gaXRlcmF0aXZlbHkgcmVzb2x2ZSB2ZWxvY2l0eSBiZXR3ZWVuIGNvbGxpc2lvbnNcbiAgICAgICAgUmVzb2x2ZXIucHJlU29sdmVWZWxvY2l0eShwYWlycy5saXN0KTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGVuZ2luZS52ZWxvY2l0eUl0ZXJhdGlvbnM7IGkrKykge1xuICAgICAgICAgICAgUmVzb2x2ZXIuc29sdmVWZWxvY2l0eShwYWlycy5saXN0LCB0aW1pbmcudGltZVNjYWxlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHRyaWdnZXIgY29sbGlzaW9uIGV2ZW50c1xuICAgICAgICBpZiAocGFpcnMuY29sbGlzaW9uQWN0aXZlLmxlbmd0aCA+IDApXG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihlbmdpbmUsICdjb2xsaXNpb25BY3RpdmUnLCB7IHBhaXJzOiBwYWlycy5jb2xsaXNpb25BY3RpdmUgfSk7XG5cbiAgICAgICAgaWYgKHBhaXJzLmNvbGxpc2lvbkVuZC5sZW5ndGggPiAwKVxuICAgICAgICAgICAgRXZlbnRzLnRyaWdnZXIoZW5naW5lLCAnY29sbGlzaW9uRW5kJywgeyBwYWlyczogcGFpcnMuY29sbGlzaW9uRW5kIH0pO1xuXG5cbiAgICAgICAgLy8gY2xlYXIgZm9yY2UgYnVmZmVyc1xuICAgICAgICBfYm9kaWVzQ2xlYXJGb3JjZXMoYWxsQm9kaWVzKTtcblxuICAgICAgICBFdmVudHMudHJpZ2dlcihlbmdpbmUsICdhZnRlclVwZGF0ZScsIGV2ZW50KTtcblxuICAgICAgICByZXR1cm4gZW5naW5lO1xuICAgIH07XG4gICAgXG4gICAgLyoqXG4gICAgICogTWVyZ2VzIHR3byBlbmdpbmVzIGJ5IGtlZXBpbmcgdGhlIGNvbmZpZ3VyYXRpb24gb2YgYGVuZ2luZUFgIGJ1dCByZXBsYWNpbmcgdGhlIHdvcmxkIHdpdGggdGhlIG9uZSBmcm9tIGBlbmdpbmVCYC5cbiAgICAgKiBAbWV0aG9kIG1lcmdlXG4gICAgICogQHBhcmFtIHtlbmdpbmV9IGVuZ2luZUFcbiAgICAgKiBAcGFyYW0ge2VuZ2luZX0gZW5naW5lQlxuICAgICAqL1xuICAgIEVuZ2luZS5tZXJnZSA9IGZ1bmN0aW9uKGVuZ2luZUEsIGVuZ2luZUIpIHtcbiAgICAgICAgQ29tbW9uLmV4dGVuZChlbmdpbmVBLCBlbmdpbmVCKTtcbiAgICAgICAgXG4gICAgICAgIGlmIChlbmdpbmVCLndvcmxkKSB7XG4gICAgICAgICAgICBlbmdpbmVBLndvcmxkID0gZW5naW5lQi53b3JsZDtcblxuICAgICAgICAgICAgRW5naW5lLmNsZWFyKGVuZ2luZUEpO1xuXG4gICAgICAgICAgICB2YXIgYm9kaWVzID0gQ29tcG9zaXRlLmFsbEJvZGllcyhlbmdpbmVBLndvcmxkKTtcblxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXTtcbiAgICAgICAgICAgICAgICBTbGVlcGluZy5zZXQoYm9keSwgZmFsc2UpO1xuICAgICAgICAgICAgICAgIGJvZHkuaWQgPSBDb21tb24ubmV4dElkKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2xlYXJzIHRoZSBlbmdpbmUgaW5jbHVkaW5nIHRoZSB3b3JsZCwgcGFpcnMgYW5kIGJyb2FkcGhhc2UuXG4gICAgICogQG1ldGhvZCBjbGVhclxuICAgICAqIEBwYXJhbSB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKi9cbiAgICBFbmdpbmUuY2xlYXIgPSBmdW5jdGlvbihlbmdpbmUpIHtcbiAgICAgICAgdmFyIHdvcmxkID0gZW5naW5lLndvcmxkO1xuICAgICAgICBcbiAgICAgICAgUGFpcnMuY2xlYXIoZW5naW5lLnBhaXJzKTtcblxuICAgICAgICB2YXIgYnJvYWRwaGFzZSA9IGVuZ2luZS5icm9hZHBoYXNlO1xuICAgICAgICBpZiAoYnJvYWRwaGFzZS5jb250cm9sbGVyKSB7XG4gICAgICAgICAgICB2YXIgYm9kaWVzID0gQ29tcG9zaXRlLmFsbEJvZGllcyh3b3JsZCk7XG4gICAgICAgICAgICBicm9hZHBoYXNlLmNvbnRyb2xsZXIuY2xlYXIoYnJvYWRwaGFzZSk7XG4gICAgICAgICAgICBicm9hZHBoYXNlLmNvbnRyb2xsZXIudXBkYXRlKGJyb2FkcGhhc2UsIGJvZGllcywgZW5naW5lLCB0cnVlKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBaZXJvZXMgdGhlIGBib2R5LmZvcmNlYCBhbmQgYGJvZHkudG9ycXVlYCBmb3JjZSBidWZmZXJzLlxuICAgICAqIEBtZXRob2QgYm9kaWVzQ2xlYXJGb3JjZXNcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7Ym9keVtdfSBib2RpZXNcbiAgICAgKi9cbiAgICB2YXIgX2JvZGllc0NsZWFyRm9yY2VzID0gZnVuY3Rpb24oYm9kaWVzKSB7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXTtcblxuICAgICAgICAgICAgLy8gcmVzZXQgZm9yY2UgYnVmZmVyc1xuICAgICAgICAgICAgYm9keS5mb3JjZS54ID0gMDtcbiAgICAgICAgICAgIGJvZHkuZm9yY2UueSA9IDA7XG4gICAgICAgICAgICBib2R5LnRvcnF1ZSA9IDA7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwbHlzIGEgbWFzcyBkZXBlbmRhbnQgZm9yY2UgdG8gYWxsIGdpdmVuIGJvZGllcy5cbiAgICAgKiBAbWV0aG9kIGJvZGllc0FwcGx5R3Jhdml0eVxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBncmF2aXR5XG4gICAgICovXG4gICAgdmFyIF9ib2RpZXNBcHBseUdyYXZpdHkgPSBmdW5jdGlvbihib2RpZXMsIGdyYXZpdHkpIHtcbiAgICAgICAgdmFyIGdyYXZpdHlTY2FsZSA9IHR5cGVvZiBncmF2aXR5LnNjYWxlICE9PSAndW5kZWZpbmVkJyA/IGdyYXZpdHkuc2NhbGUgOiAwLjAwMTtcblxuICAgICAgICBpZiAoKGdyYXZpdHkueCA9PT0gMCAmJiBncmF2aXR5LnkgPT09IDApIHx8IGdyYXZpdHlTY2FsZSA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGJvZHkgPSBib2RpZXNbaV07XG5cbiAgICAgICAgICAgIGlmIChib2R5LmlzU3RhdGljIHx8IGJvZHkuaXNTbGVlcGluZylcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgLy8gYXBwbHkgZ3Jhdml0eVxuICAgICAgICAgICAgYm9keS5mb3JjZS55ICs9IGJvZHkubWFzcyAqIGdyYXZpdHkueSAqIGdyYXZpdHlTY2FsZTtcbiAgICAgICAgICAgIGJvZHkuZm9yY2UueCArPSBib2R5Lm1hc3MgKiBncmF2aXR5LnggKiBncmF2aXR5U2NhbGU7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwbHlzIGBCb2R5LnVwZGF0ZWAgdG8gYWxsIGdpdmVuIGBib2RpZXNgLlxuICAgICAqIEBtZXRob2QgdXBkYXRlQWxsXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGRlbHRhVGltZSBcbiAgICAgKiBUaGUgYW1vdW50IG9mIHRpbWUgZWxhcHNlZCBiZXR3ZWVuIHVwZGF0ZXNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gdGltZVNjYWxlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNvcnJlY3Rpb24gXG4gICAgICogVGhlIFZlcmxldCBjb3JyZWN0aW9uIGZhY3RvciAoZGVsdGFUaW1lIC8gbGFzdERlbHRhVGltZSlcbiAgICAgKiBAcGFyYW0ge2JvdW5kc30gd29ybGRCb3VuZHNcbiAgICAgKi9cbiAgICB2YXIgX2JvZGllc1VwZGF0ZSA9IGZ1bmN0aW9uKGJvZGllcywgZGVsdGFUaW1lLCB0aW1lU2NhbGUsIGNvcnJlY3Rpb24sIHdvcmxkQm91bmRzKSB7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXTtcblxuICAgICAgICAgICAgaWYgKGJvZHkuaXNTdGF0aWMgfHwgYm9keS5pc1NsZWVwaW5nKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICBCb2R5LnVwZGF0ZShib2R5LCBkZWx0YVRpbWUsIHRpbWVTY2FsZSwgY29ycmVjdGlvbik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQW4gYWxpYXMgZm9yIGBSdW5uZXIucnVuYCwgc2VlIGBNYXR0ZXIuUnVubmVyYCBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAgICAgKiBAbWV0aG9kIHJ1blxuICAgICAqIEBwYXJhbSB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQganVzdCBiZWZvcmUgYW4gdXBkYXRlXG4gICAgKlxuICAgICogQGV2ZW50IGJlZm9yZVVwZGF0ZVxuICAgICogQHBhcmFtIHt9IGV2ZW50IEFuIGV2ZW50IG9iamVjdFxuICAgICogQHBhcmFtIHtudW1iZXJ9IGV2ZW50LnRpbWVzdGFtcCBUaGUgZW5naW5lLnRpbWluZy50aW1lc3RhbXAgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgYWZ0ZXIgZW5naW5lIHVwZGF0ZSBhbmQgYWxsIGNvbGxpc2lvbiBldmVudHNcbiAgICAqXG4gICAgKiBAZXZlbnQgYWZ0ZXJVcGRhdGVcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudC50aW1lc3RhbXAgVGhlIGVuZ2luZS50aW1pbmcudGltZXN0YW1wIG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICovXG5cbiAgICAvKipcbiAgICAqIEZpcmVkIGFmdGVyIGVuZ2luZSB1cGRhdGUsIHByb3ZpZGVzIGEgbGlzdCBvZiBhbGwgcGFpcnMgdGhhdCBoYXZlIHN0YXJ0ZWQgdG8gY29sbGlkZSBpbiB0aGUgY3VycmVudCB0aWNrIChpZiBhbnkpXG4gICAgKlxuICAgICogQGV2ZW50IGNvbGxpc2lvblN0YXJ0XG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge30gZXZlbnQucGFpcnMgTGlzdCBvZiBhZmZlY3RlZCBwYWlyc1xuICAgICogQHBhcmFtIHtudW1iZXJ9IGV2ZW50LnRpbWVzdGFtcCBUaGUgZW5naW5lLnRpbWluZy50aW1lc3RhbXAgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgYWZ0ZXIgZW5naW5lIHVwZGF0ZSwgcHJvdmlkZXMgYSBsaXN0IG9mIGFsbCBwYWlycyB0aGF0IGFyZSBjb2xsaWRpbmcgaW4gdGhlIGN1cnJlbnQgdGljayAoaWYgYW55KVxuICAgICpcbiAgICAqIEBldmVudCBjb2xsaXNpb25BY3RpdmVcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7fSBldmVudC5wYWlycyBMaXN0IG9mIGFmZmVjdGVkIHBhaXJzXG4gICAgKiBAcGFyYW0ge251bWJlcn0gZXZlbnQudGltZXN0YW1wIFRoZSBlbmdpbmUudGltaW5nLnRpbWVzdGFtcCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCBhZnRlciBlbmdpbmUgdXBkYXRlLCBwcm92aWRlcyBhIGxpc3Qgb2YgYWxsIHBhaXJzIHRoYXQgaGF2ZSBlbmRlZCBjb2xsaXNpb24gaW4gdGhlIGN1cnJlbnQgdGljayAoaWYgYW55KVxuICAgICpcbiAgICAqIEBldmVudCBjb2xsaXNpb25FbmRcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7fSBldmVudC5wYWlycyBMaXN0IG9mIGFmZmVjdGVkIHBhaXJzXG4gICAgKiBAcGFyYW0ge251bWJlcn0gZXZlbnQudGltZXN0YW1wIFRoZSBlbmdpbmUudGltaW5nLnRpbWVzdGFtcCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLypcbiAgICAqXG4gICAgKiAgUHJvcGVydGllcyBEb2N1bWVudGF0aW9uXG4gICAgKlxuICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBpbnRlZ2VyIGBOdW1iZXJgIHRoYXQgc3BlY2lmaWVzIHRoZSBudW1iZXIgb2YgcG9zaXRpb24gaXRlcmF0aW9ucyB0byBwZXJmb3JtIGVhY2ggdXBkYXRlLlxuICAgICAqIFRoZSBoaWdoZXIgdGhlIHZhbHVlLCB0aGUgaGlnaGVyIHF1YWxpdHkgdGhlIHNpbXVsYXRpb24gd2lsbCBiZSBhdCB0aGUgZXhwZW5zZSBvZiBwZXJmb3JtYW5jZS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBwb3NpdGlvbkl0ZXJhdGlvbnNcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCA2XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBpbnRlZ2VyIGBOdW1iZXJgIHRoYXQgc3BlY2lmaWVzIHRoZSBudW1iZXIgb2YgdmVsb2NpdHkgaXRlcmF0aW9ucyB0byBwZXJmb3JtIGVhY2ggdXBkYXRlLlxuICAgICAqIFRoZSBoaWdoZXIgdGhlIHZhbHVlLCB0aGUgaGlnaGVyIHF1YWxpdHkgdGhlIHNpbXVsYXRpb24gd2lsbCBiZSBhdCB0aGUgZXhwZW5zZSBvZiBwZXJmb3JtYW5jZS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB2ZWxvY2l0eUl0ZXJhdGlvbnNcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCA0XG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBbiBpbnRlZ2VyIGBOdW1iZXJgIHRoYXQgc3BlY2lmaWVzIHRoZSBudW1iZXIgb2YgY29uc3RyYWludCBpdGVyYXRpb25zIHRvIHBlcmZvcm0gZWFjaCB1cGRhdGUuXG4gICAgICogVGhlIGhpZ2hlciB0aGUgdmFsdWUsIHRoZSBoaWdoZXIgcXVhbGl0eSB0aGUgc2ltdWxhdGlvbiB3aWxsIGJlIGF0IHRoZSBleHBlbnNlIG9mIHBlcmZvcm1hbmNlLlxuICAgICAqIFRoZSBkZWZhdWx0IHZhbHVlIG9mIGAyYCBpcyB1c3VhbGx5IHZlcnkgYWRlcXVhdGUuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgY29uc3RyYWludEl0ZXJhdGlvbnNcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCAyXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGZsYWcgdGhhdCBzcGVjaWZpZXMgd2hldGhlciB0aGUgZW5naW5lIHNob3VsZCBhbGxvdyBzbGVlcGluZyB2aWEgdGhlIGBNYXR0ZXIuU2xlZXBpbmdgIG1vZHVsZS5cbiAgICAgKiBTbGVlcGluZyBjYW4gaW1wcm92ZSBzdGFiaWxpdHkgYW5kIHBlcmZvcm1hbmNlLCBidXQgb2Z0ZW4gYXQgdGhlIGV4cGVuc2Ugb2YgYWNjdXJhY3kuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgZW5hYmxlU2xlZXBpbmdcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIGBPYmplY3RgIGNvbnRhaW5pbmcgcHJvcGVydGllcyByZWdhcmRpbmcgdGhlIHRpbWluZyBzeXN0ZW1zIG9mIHRoZSBlbmdpbmUuIFxuICAgICAqXG4gICAgICogQHByb3BlcnR5IHRpbWluZ1xuICAgICAqIEB0eXBlIG9iamVjdFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgTnVtYmVyYCB0aGF0IHNwZWNpZmllcyB0aGUgZ2xvYmFsIHNjYWxpbmcgZmFjdG9yIG9mIHRpbWUgZm9yIGFsbCBib2RpZXMuXG4gICAgICogQSB2YWx1ZSBvZiBgMGAgZnJlZXplcyB0aGUgc2ltdWxhdGlvbi5cbiAgICAgKiBBIHZhbHVlIG9mIGAwLjFgIGdpdmVzIGEgc2xvdy1tb3Rpb24gZWZmZWN0LlxuICAgICAqIEEgdmFsdWUgb2YgYDEuMmAgZ2l2ZXMgYSBzcGVlZC11cCBlZmZlY3QuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgdGltaW5nLnRpbWVTY2FsZVxuICAgICAqIEB0eXBlIG51bWJlclxuICAgICAqIEBkZWZhdWx0IDFcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBzcGVjaWZpZXMgdGhlIGN1cnJlbnQgc2ltdWxhdGlvbi10aW1lIGluIG1pbGxpc2Vjb25kcyBzdGFydGluZyBmcm9tIGAwYC4gXG4gICAgICogSXQgaXMgaW5jcmVtZW50ZWQgb24gZXZlcnkgYEVuZ2luZS51cGRhdGVgIGJ5IHRoZSBnaXZlbiBgZGVsdGFgIGFyZ3VtZW50LiBcbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0aW1pbmcudGltZXN0YW1wXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gaW5zdGFuY2Ugb2YgYSBgUmVuZGVyYCBjb250cm9sbGVyLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhIGBNYXR0ZXIuUmVuZGVyYCBpbnN0YW5jZSBjcmVhdGVkIGJ5IGBFbmdpbmUuY3JlYXRlYC5cbiAgICAgKiBPbmUgbWF5IGFsc28gZGV2ZWxvcCBhIGN1c3RvbSByZW5kZXJlciBtb2R1bGUgYmFzZWQgb24gYE1hdHRlci5SZW5kZXJgIGFuZCBwYXNzIGFuIGluc3RhbmNlIG9mIGl0IHRvIGBFbmdpbmUuY3JlYXRlYCB2aWEgYG9wdGlvbnMucmVuZGVyYC5cbiAgICAgKlxuICAgICAqIEEgbWluaW1hbCBjdXN0b20gcmVuZGVyZXIgb2JqZWN0IG11c3QgZGVmaW5lIGF0IGxlYXN0IHRocmVlIGZ1bmN0aW9uczogYGNyZWF0ZWAsIGBjbGVhcmAgYW5kIGB3b3JsZGAgKHNlZSBgTWF0dGVyLlJlbmRlcmApLlxuICAgICAqIEl0IGlzIGFsc28gcG9zc2libGUgdG8gaW5zdGVhZCBwYXNzIHRoZSBfbW9kdWxlXyByZWZlcmVuY2UgdmlhIGBvcHRpb25zLnJlbmRlci5jb250cm9sbGVyYCBhbmQgYEVuZ2luZS5jcmVhdGVgIHdpbGwgaW5zdGFudGlhdGUgb25lIGZvciB5b3UuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgcmVuZGVyXG4gICAgICogQHR5cGUgcmVuZGVyXG4gICAgICogQGRlcHJlY2F0ZWQgc2VlIERlbW8uanMgZm9yIGFuIGV4YW1wbGUgb2YgY3JlYXRpbmcgYSByZW5kZXJlclxuICAgICAqIEBkZWZhdWx0IGEgTWF0dGVyLlJlbmRlciBpbnN0YW5jZVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQW4gaW5zdGFuY2Ugb2YgYSBicm9hZHBoYXNlIGNvbnRyb2xsZXIuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGEgYE1hdHRlci5HcmlkYCBpbnN0YW5jZSBjcmVhdGVkIGJ5IGBFbmdpbmUuY3JlYXRlYC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBicm9hZHBoYXNlXG4gICAgICogQHR5cGUgZ3JpZFxuICAgICAqIEBkZWZhdWx0IGEgTWF0dGVyLkdyaWQgaW5zdGFuY2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYFdvcmxkYCBjb21wb3NpdGUgb2JqZWN0IHRoYXQgd2lsbCBjb250YWluIGFsbCBzaW11bGF0ZWQgYm9kaWVzIGFuZCBjb25zdHJhaW50cy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB3b3JsZFxuICAgICAqIEB0eXBlIHdvcmxkXG4gICAgICogQGRlZmF1bHQgYSBNYXR0ZXIuV29ybGQgaW5zdGFuY2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEFuIG9iamVjdCByZXNlcnZlZCBmb3Igc3RvcmluZyBwbHVnaW4tc3BlY2lmaWMgcHJvcGVydGllcy5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBwbHVnaW5cbiAgICAgKiBAdHlwZSB7fVxuICAgICAqL1xuXG59KSgpO1xuXG59LHtcIi4uL2JvZHkvQm9keVwiOjEsXCIuLi9ib2R5L0NvbXBvc2l0ZVwiOjIsXCIuLi9ib2R5L1dvcmxkXCI6MyxcIi4uL2NvbGxpc2lvbi9HcmlkXCI6NixcIi4uL2NvbGxpc2lvbi9QYWlyc1wiOjgsXCIuLi9jb2xsaXNpb24vUmVzb2x2ZXJcIjoxMCxcIi4uL2NvbnN0cmFpbnQvQ29uc3RyYWludFwiOjEyLFwiLi4vcmVuZGVyL1JlbmRlclwiOjMxLFwiLi9Db21tb25cIjoxNCxcIi4vRXZlbnRzXCI6MTYsXCIuL01ldHJpY3NcIjoxOCxcIi4vU2xlZXBpbmdcIjoyMn1dLDE2OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5FdmVudHNgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIHRvIGZpcmUgYW5kIGxpc3RlbiB0byBldmVudHMgb24gb3RoZXIgb2JqZWN0cy5cbipcbiogU2VlIHRoZSBpbmNsdWRlZCB1c2FnZSBbZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9saWFicnUvbWF0dGVyLWpzL3RyZWUvbWFzdGVyL2V4YW1wbGVzKS5cbipcbiogQGNsYXNzIEV2ZW50c1xuKi9cblxudmFyIEV2ZW50cyA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEV2ZW50cztcblxudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4vQ29tbW9uJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIC8qKlxuICAgICAqIFN1YnNjcmliZXMgYSBjYWxsYmFjayBmdW5jdGlvbiB0byB0aGUgZ2l2ZW4gb2JqZWN0J3MgYGV2ZW50TmFtZWAuXG4gICAgICogQG1ldGhvZCBvblxuICAgICAqIEBwYXJhbSB7fSBvYmplY3RcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lc1xuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrXG4gICAgICovXG4gICAgRXZlbnRzLm9uID0gZnVuY3Rpb24ob2JqZWN0LCBldmVudE5hbWVzLCBjYWxsYmFjaykge1xuICAgICAgICB2YXIgbmFtZXMgPSBldmVudE5hbWVzLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBuYW1lO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbmFtZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIG5hbWUgPSBuYW1lc1tpXTtcbiAgICAgICAgICAgIG9iamVjdC5ldmVudHMgPSBvYmplY3QuZXZlbnRzIHx8IHt9O1xuICAgICAgICAgICAgb2JqZWN0LmV2ZW50c1tuYW1lXSA9IG9iamVjdC5ldmVudHNbbmFtZV0gfHwgW107XG4gICAgICAgICAgICBvYmplY3QuZXZlbnRzW25hbWVdLnB1c2goY2FsbGJhY2spO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIHRoZSBnaXZlbiBldmVudCBjYWxsYmFjay4gSWYgbm8gY2FsbGJhY2ssIGNsZWFycyBhbGwgY2FsbGJhY2tzIGluIGBldmVudE5hbWVzYC4gSWYgbm8gYGV2ZW50TmFtZXNgLCBjbGVhcnMgYWxsIGV2ZW50cy5cbiAgICAgKiBAbWV0aG9kIG9mZlxuICAgICAqIEBwYXJhbSB7fSBvYmplY3RcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lc1xuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrXG4gICAgICovXG4gICAgRXZlbnRzLm9mZiA9IGZ1bmN0aW9uKG9iamVjdCwgZXZlbnROYW1lcywgY2FsbGJhY2spIHtcbiAgICAgICAgaWYgKCFldmVudE5hbWVzKSB7XG4gICAgICAgICAgICBvYmplY3QuZXZlbnRzID0ge307XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBoYW5kbGUgRXZlbnRzLm9mZihvYmplY3QsIGNhbGxiYWNrKVxuICAgICAgICBpZiAodHlwZW9mIGV2ZW50TmFtZXMgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIGNhbGxiYWNrID0gZXZlbnROYW1lcztcbiAgICAgICAgICAgIGV2ZW50TmFtZXMgPSBDb21tb24ua2V5cyhvYmplY3QuZXZlbnRzKS5qb2luKCcgJyk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgbmFtZXMgPSBldmVudE5hbWVzLnNwbGl0KCcgJyk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBuYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGNhbGxiYWNrcyA9IG9iamVjdC5ldmVudHNbbmFtZXNbaV1dLFxuICAgICAgICAgICAgICAgIG5ld0NhbGxiYWNrcyA9IFtdO1xuXG4gICAgICAgICAgICBpZiAoY2FsbGJhY2sgJiYgY2FsbGJhY2tzKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBjYWxsYmFja3MubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNhbGxiYWNrc1tqXSAhPT0gY2FsbGJhY2spXG4gICAgICAgICAgICAgICAgICAgICAgICBuZXdDYWxsYmFja3MucHVzaChjYWxsYmFja3Nbal0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgb2JqZWN0LmV2ZW50c1tuYW1lc1tpXV0gPSBuZXdDYWxsYmFja3M7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRmlyZXMgYWxsIHRoZSBjYWxsYmFja3Mgc3Vic2NyaWJlZCB0byB0aGUgZ2l2ZW4gb2JqZWN0J3MgYGV2ZW50TmFtZWAsIGluIHRoZSBvcmRlciB0aGV5IHN1YnNjcmliZWQsIGlmIGFueS5cbiAgICAgKiBAbWV0aG9kIHRyaWdnZXJcbiAgICAgKiBAcGFyYW0ge30gb2JqZWN0XG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZXNcbiAgICAgKiBAcGFyYW0ge30gZXZlbnRcbiAgICAgKi9cbiAgICBFdmVudHMudHJpZ2dlciA9IGZ1bmN0aW9uKG9iamVjdCwgZXZlbnROYW1lcywgZXZlbnQpIHtcbiAgICAgICAgdmFyIG5hbWVzLFxuICAgICAgICAgICAgbmFtZSxcbiAgICAgICAgICAgIGNhbGxiYWNrcyxcbiAgICAgICAgICAgIGV2ZW50Q2xvbmU7XG5cbiAgICAgICAgaWYgKG9iamVjdC5ldmVudHMpIHtcbiAgICAgICAgICAgIGlmICghZXZlbnQpXG4gICAgICAgICAgICAgICAgZXZlbnQgPSB7fTtcblxuICAgICAgICAgICAgbmFtZXMgPSBldmVudE5hbWVzLnNwbGl0KCcgJyk7XG5cbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbmFtZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBuYW1lID0gbmFtZXNbaV07XG4gICAgICAgICAgICAgICAgY2FsbGJhY2tzID0gb2JqZWN0LmV2ZW50c1tuYW1lXTtcblxuICAgICAgICAgICAgICAgIGlmIChjYWxsYmFja3MpIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRDbG9uZSA9IENvbW1vbi5jbG9uZShldmVudCwgZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICBldmVudENsb25lLm5hbWUgPSBuYW1lO1xuICAgICAgICAgICAgICAgICAgICBldmVudENsb25lLnNvdXJjZSA9IG9iamVjdDtcblxuICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGNhbGxiYWNrcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2tzW2pdLmFwcGx5KG9iamVjdCwgW2V2ZW50Q2xvbmVdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbn0pKCk7XG5cbn0se1wiLi9Db21tb25cIjoxNH1dLDE3OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlcmAgbW9kdWxlIGlzIHRoZSB0b3AgbGV2ZWwgbmFtZXNwYWNlLiBJdCBhbHNvIGluY2x1ZGVzIGEgZnVuY3Rpb24gZm9yIGluc3RhbGxpbmcgcGx1Z2lucyBvbiB0b3Agb2YgdGhlIGxpYnJhcnkuXG4qXG4qIEBjbGFzcyBNYXR0ZXJcbiovXG5cbnZhciBNYXR0ZXIgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBNYXR0ZXI7XG5cbnZhciBQbHVnaW4gPSBfZGVyZXFfKCcuL1BsdWdpbicpO1xudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4vQ29tbW9uJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIC8qKlxuICAgICAqIFRoZSBsaWJyYXJ5IG5hbWUuXG4gICAgICogQHByb3BlcnR5IG5hbWVcbiAgICAgKiBAcmVhZE9ubHlcbiAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAqL1xuICAgIE1hdHRlci5uYW1lID0gJ21hdHRlci1qcyc7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgbGlicmFyeSB2ZXJzaW9uLlxuICAgICAqIEBwcm9wZXJ0eSB2ZXJzaW9uXG4gICAgICogQHJlYWRPbmx5XG4gICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgKi9cbiAgICBNYXR0ZXIudmVyc2lvbiA9ICcwLjEzLjAnO1xuXG4gICAgLyoqXG4gICAgICogQSBsaXN0IG9mIHBsdWdpbiBkZXBlbmRlbmNpZXMgdG8gYmUgaW5zdGFsbGVkLiBUaGVzZSBhcmUgbm9ybWFsbHkgc2V0IGFuZCBpbnN0YWxsZWQgdGhyb3VnaCBgTWF0dGVyLnVzZWAuXG4gICAgICogQWx0ZXJuYXRpdmVseSB5b3UgbWF5IHNldCBgTWF0dGVyLnVzZXNgIG1hbnVhbGx5IGFuZCBpbnN0YWxsIHRoZW0gYnkgY2FsbGluZyBgUGx1Z2luLnVzZShNYXR0ZXIpYC5cbiAgICAgKiBAcHJvcGVydHkgdXNlc1xuICAgICAqIEB0eXBlIHtBcnJheX1cbiAgICAgKi9cbiAgICBNYXR0ZXIudXNlcyA9IFtdO1xuXG4gICAgLyoqXG4gICAgICogVGhlIHBsdWdpbnMgdGhhdCBoYXZlIGJlZW4gaW5zdGFsbGVkIHRocm91Z2ggYE1hdHRlci5QbHVnaW4uaW5zdGFsbGAuIFJlYWQgb25seS5cbiAgICAgKiBAcHJvcGVydHkgdXNlZFxuICAgICAqIEByZWFkT25seVxuICAgICAqIEB0eXBlIHtBcnJheX1cbiAgICAgKi9cbiAgICBNYXR0ZXIudXNlZCA9IFtdO1xuXG4gICAgLyoqXG4gICAgICogSW5zdGFsbHMgdGhlIGdpdmVuIHBsdWdpbnMgb24gdGhlIGBNYXR0ZXJgIG5hbWVzcGFjZS5cbiAgICAgKiBUaGlzIGlzIGEgc2hvcnQtaGFuZCBmb3IgYFBsdWdpbi51c2VgLCBzZWUgaXQgZm9yIG1vcmUgaW5mb3JtYXRpb24uXG4gICAgICogQ2FsbCB0aGlzIGZ1bmN0aW9uIG9uY2UgYXQgdGhlIHN0YXJ0IG9mIHlvdXIgY29kZSwgd2l0aCBhbGwgb2YgdGhlIHBsdWdpbnMgeW91IHdpc2ggdG8gaW5zdGFsbCBhcyBhcmd1bWVudHMuXG4gICAgICogQXZvaWQgY2FsbGluZyB0aGlzIGZ1bmN0aW9uIG11bHRpcGxlIHRpbWVzIHVubGVzcyB5b3UgaW50ZW5kIHRvIG1hbnVhbGx5IGNvbnRyb2wgaW5zdGFsbGF0aW9uIG9yZGVyLlxuICAgICAqIEBtZXRob2QgdXNlXG4gICAgICogQHBhcmFtIC4uLnBsdWdpbiB7RnVuY3Rpb259IFRoZSBwbHVnaW4ocykgdG8gaW5zdGFsbCBvbiBgYmFzZWAgKG11bHRpLWFyZ3VtZW50KS5cbiAgICAgKi9cbiAgICBNYXR0ZXIudXNlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIFBsdWdpbi51c2UoTWF0dGVyLCBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2hhaW5zIGEgZnVuY3Rpb24gdG8gZXhjdXRlIGJlZm9yZSB0aGUgb3JpZ2luYWwgZnVuY3Rpb24gb24gdGhlIGdpdmVuIGBwYXRoYCByZWxhdGl2ZSB0byBgTWF0dGVyYC5cbiAgICAgKiBTZWUgYWxzbyBkb2NzIGZvciBgQ29tbW9uLmNoYWluYC5cbiAgICAgKiBAbWV0aG9kIGJlZm9yZVxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFRoZSBwYXRoIHJlbGF0aXZlIHRvIGBNYXR0ZXJgXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gY2hhaW4gYmVmb3JlIHRoZSBvcmlnaW5hbFxuICAgICAqIEByZXR1cm4ge2Z1bmN0aW9ufSBUaGUgY2hhaW5lZCBmdW5jdGlvbiB0aGF0IHJlcGxhY2VkIHRoZSBvcmlnaW5hbFxuICAgICAqL1xuICAgIE1hdHRlci5iZWZvcmUgPSBmdW5jdGlvbihwYXRoLCBmdW5jKSB7XG4gICAgICAgIHBhdGggPSBwYXRoLnJlcGxhY2UoL15NYXR0ZXIuLywgJycpO1xuICAgICAgICByZXR1cm4gQ29tbW9uLmNoYWluUGF0aEJlZm9yZShNYXR0ZXIsIHBhdGgsIGZ1bmMpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDaGFpbnMgYSBmdW5jdGlvbiB0byBleGN1dGUgYWZ0ZXIgdGhlIG9yaWdpbmFsIGZ1bmN0aW9uIG9uIHRoZSBnaXZlbiBgcGF0aGAgcmVsYXRpdmUgdG8gYE1hdHRlcmAuXG4gICAgICogU2VlIGFsc28gZG9jcyBmb3IgYENvbW1vbi5jaGFpbmAuXG4gICAgICogQG1ldGhvZCBhZnRlclxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFRoZSBwYXRoIHJlbGF0aXZlIHRvIGBNYXR0ZXJgXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gY2hhaW4gYWZ0ZXIgdGhlIG9yaWdpbmFsXG4gICAgICogQHJldHVybiB7ZnVuY3Rpb259IFRoZSBjaGFpbmVkIGZ1bmN0aW9uIHRoYXQgcmVwbGFjZWQgdGhlIG9yaWdpbmFsXG4gICAgICovXG4gICAgTWF0dGVyLmFmdGVyID0gZnVuY3Rpb24ocGF0aCwgZnVuYykge1xuICAgICAgICBwYXRoID0gcGF0aC5yZXBsYWNlKC9eTWF0dGVyLi8sICcnKTtcbiAgICAgICAgcmV0dXJuIENvbW1vbi5jaGFpblBhdGhBZnRlcihNYXR0ZXIsIHBhdGgsIGZ1bmMpO1xuICAgIH07XG5cbn0pKCk7XG5cbn0se1wiLi9Db21tb25cIjoxNCxcIi4vUGx1Z2luXCI6MjB9XSwxODpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG5cbn0se1wiLi4vYm9keS9Db21wb3NpdGVcIjoyLFwiLi9Db21tb25cIjoxNH1dLDE5OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5Nb3VzZWAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGNyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgbW91c2UgaW5wdXRzLlxuKlxuKiBAY2xhc3MgTW91c2VcbiovXG5cbnZhciBNb3VzZSA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IE1vdXNlO1xuXG52YXIgQ29tbW9uID0gX2RlcmVxXygnLi4vY29yZS9Db21tb24nKTtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG1vdXNlIGlucHV0LlxuICAgICAqIEBtZXRob2QgY3JlYXRlXG4gICAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZWxlbWVudFxuICAgICAqIEByZXR1cm4ge21vdXNlfSBBIG5ldyBtb3VzZVxuICAgICAqL1xuICAgIE1vdXNlLmNyZWF0ZSA9IGZ1bmN0aW9uKGVsZW1lbnQpIHtcbiAgICAgICAgdmFyIG1vdXNlID0ge307XG5cbiAgICAgICAgaWYgKCFlbGVtZW50KSB7XG4gICAgICAgICAgICBDb21tb24ubG9nKCdNb3VzZS5jcmVhdGU6IGVsZW1lbnQgd2FzIHVuZGVmaW5lZCwgZGVmYXVsdGluZyB0byBkb2N1bWVudC5ib2R5JywgJ3dhcm4nKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgbW91c2UuZWxlbWVudCA9IGVsZW1lbnQgfHwgZG9jdW1lbnQuYm9keTtcbiAgICAgICAgbW91c2UuYWJzb2x1dGUgPSB7IHg6IDAsIHk6IDAgfTtcbiAgICAgICAgbW91c2UucG9zaXRpb24gPSB7IHg6IDAsIHk6IDAgfTtcbiAgICAgICAgbW91c2UubW91c2Vkb3duUG9zaXRpb24gPSB7IHg6IDAsIHk6IDAgfTtcbiAgICAgICAgbW91c2UubW91c2V1cFBvc2l0aW9uID0geyB4OiAwLCB5OiAwIH07XG4gICAgICAgIG1vdXNlLm9mZnNldCA9IHsgeDogMCwgeTogMCB9O1xuICAgICAgICBtb3VzZS5zY2FsZSA9IHsgeDogMSwgeTogMSB9O1xuICAgICAgICBtb3VzZS53aGVlbERlbHRhID0gMDtcbiAgICAgICAgbW91c2UuYnV0dG9uID0gLTE7XG4gICAgICAgIG1vdXNlLnBpeGVsUmF0aW8gPSBtb3VzZS5lbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1waXhlbC1yYXRpbycpIHx8IDE7XG5cbiAgICAgICAgbW91c2Uuc291cmNlRXZlbnRzID0ge1xuICAgICAgICAgICAgbW91c2Vtb3ZlOiBudWxsLFxuICAgICAgICAgICAgbW91c2Vkb3duOiBudWxsLFxuICAgICAgICAgICAgbW91c2V1cDogbnVsbCxcbiAgICAgICAgICAgIG1vdXNld2hlZWw6IG51bGxcbiAgICAgICAgfTtcbiAgICAgICAgXG4gICAgICAgIG1vdXNlLm1vdXNlbW92ZSA9IGZ1bmN0aW9uKGV2ZW50KSB7IFxuICAgICAgICAgICAgdmFyIHBvc2l0aW9uID0gX2dldFJlbGF0aXZlTW91c2VQb3NpdGlvbihldmVudCwgbW91c2UuZWxlbWVudCwgbW91c2UucGl4ZWxSYXRpbyksXG4gICAgICAgICAgICAgICAgdG91Y2hlcyA9IGV2ZW50LmNoYW5nZWRUb3VjaGVzO1xuXG4gICAgICAgICAgICBpZiAodG91Y2hlcykge1xuICAgICAgICAgICAgICAgIG1vdXNlLmJ1dHRvbiA9IDA7XG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbW91c2UuYWJzb2x1dGUueCA9IHBvc2l0aW9uLng7XG4gICAgICAgICAgICBtb3VzZS5hYnNvbHV0ZS55ID0gcG9zaXRpb24ueTtcbiAgICAgICAgICAgIG1vdXNlLnBvc2l0aW9uLnggPSBtb3VzZS5hYnNvbHV0ZS54ICogbW91c2Uuc2NhbGUueCArIG1vdXNlLm9mZnNldC54O1xuICAgICAgICAgICAgbW91c2UucG9zaXRpb24ueSA9IG1vdXNlLmFic29sdXRlLnkgKiBtb3VzZS5zY2FsZS55ICsgbW91c2Uub2Zmc2V0Lnk7XG4gICAgICAgICAgICBtb3VzZS5zb3VyY2VFdmVudHMubW91c2Vtb3ZlID0gZXZlbnQ7XG4gICAgICAgIH07XG4gICAgICAgIFxuICAgICAgICBtb3VzZS5tb3VzZWRvd24gPSBmdW5jdGlvbihldmVudCkge1xuICAgICAgICAgICAgdmFyIHBvc2l0aW9uID0gX2dldFJlbGF0aXZlTW91c2VQb3NpdGlvbihldmVudCwgbW91c2UuZWxlbWVudCwgbW91c2UucGl4ZWxSYXRpbyksXG4gICAgICAgICAgICAgICAgdG91Y2hlcyA9IGV2ZW50LmNoYW5nZWRUb3VjaGVzO1xuXG4gICAgICAgICAgICBpZiAodG91Y2hlcykge1xuICAgICAgICAgICAgICAgIG1vdXNlLmJ1dHRvbiA9IDA7XG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbW91c2UuYnV0dG9uID0gZXZlbnQuYnV0dG9uO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBtb3VzZS5hYnNvbHV0ZS54ID0gcG9zaXRpb24ueDtcbiAgICAgICAgICAgIG1vdXNlLmFic29sdXRlLnkgPSBwb3NpdGlvbi55O1xuICAgICAgICAgICAgbW91c2UucG9zaXRpb24ueCA9IG1vdXNlLmFic29sdXRlLnggKiBtb3VzZS5zY2FsZS54ICsgbW91c2Uub2Zmc2V0Lng7XG4gICAgICAgICAgICBtb3VzZS5wb3NpdGlvbi55ID0gbW91c2UuYWJzb2x1dGUueSAqIG1vdXNlLnNjYWxlLnkgKyBtb3VzZS5vZmZzZXQueTtcbiAgICAgICAgICAgIG1vdXNlLm1vdXNlZG93blBvc2l0aW9uLnggPSBtb3VzZS5wb3NpdGlvbi54O1xuICAgICAgICAgICAgbW91c2UubW91c2Vkb3duUG9zaXRpb24ueSA9IG1vdXNlLnBvc2l0aW9uLnk7XG4gICAgICAgICAgICBtb3VzZS5zb3VyY2VFdmVudHMubW91c2Vkb3duID0gZXZlbnQ7XG4gICAgICAgIH07XG4gICAgICAgIFxuICAgICAgICBtb3VzZS5tb3VzZXVwID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICAgIHZhciBwb3NpdGlvbiA9IF9nZXRSZWxhdGl2ZU1vdXNlUG9zaXRpb24oZXZlbnQsIG1vdXNlLmVsZW1lbnQsIG1vdXNlLnBpeGVsUmF0aW8pLFxuICAgICAgICAgICAgICAgIHRvdWNoZXMgPSBldmVudC5jaGFuZ2VkVG91Y2hlcztcblxuICAgICAgICAgICAgaWYgKHRvdWNoZXMpIHtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBtb3VzZS5idXR0b24gPSAtMTtcbiAgICAgICAgICAgIG1vdXNlLmFic29sdXRlLnggPSBwb3NpdGlvbi54O1xuICAgICAgICAgICAgbW91c2UuYWJzb2x1dGUueSA9IHBvc2l0aW9uLnk7XG4gICAgICAgICAgICBtb3VzZS5wb3NpdGlvbi54ID0gbW91c2UuYWJzb2x1dGUueCAqIG1vdXNlLnNjYWxlLnggKyBtb3VzZS5vZmZzZXQueDtcbiAgICAgICAgICAgIG1vdXNlLnBvc2l0aW9uLnkgPSBtb3VzZS5hYnNvbHV0ZS55ICogbW91c2Uuc2NhbGUueSArIG1vdXNlLm9mZnNldC55O1xuICAgICAgICAgICAgbW91c2UubW91c2V1cFBvc2l0aW9uLnggPSBtb3VzZS5wb3NpdGlvbi54O1xuICAgICAgICAgICAgbW91c2UubW91c2V1cFBvc2l0aW9uLnkgPSBtb3VzZS5wb3NpdGlvbi55O1xuICAgICAgICAgICAgbW91c2Uuc291cmNlRXZlbnRzLm1vdXNldXAgPSBldmVudDtcbiAgICAgICAgfTtcblxuICAgICAgICBtb3VzZS5tb3VzZXdoZWVsID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICAgIG1vdXNlLndoZWVsRGVsdGEgPSBNYXRoLm1heCgtMSwgTWF0aC5taW4oMSwgZXZlbnQud2hlZWxEZWx0YSB8fCAtZXZlbnQuZGV0YWlsKSk7XG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB9O1xuXG4gICAgICAgIE1vdXNlLnNldEVsZW1lbnQobW91c2UsIG1vdXNlLmVsZW1lbnQpO1xuXG4gICAgICAgIHJldHVybiBtb3VzZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgZWxlbWVudCB0aGUgbW91c2UgaXMgYm91bmQgdG8gKGFuZCByZWxhdGl2ZSB0bykuXG4gICAgICogQG1ldGhvZCBzZXRFbGVtZW50XG4gICAgICogQHBhcmFtIHttb3VzZX0gbW91c2VcbiAgICAgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbGVtZW50XG4gICAgICovXG4gICAgTW91c2Uuc2V0RWxlbWVudCA9IGZ1bmN0aW9uKG1vdXNlLCBlbGVtZW50KSB7XG4gICAgICAgIG1vdXNlLmVsZW1lbnQgPSBlbGVtZW50O1xuXG4gICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgbW91c2UubW91c2Vtb3ZlKTtcbiAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWRvd24nLCBtb3VzZS5tb3VzZWRvd24pO1xuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNldXAnLCBtb3VzZS5tb3VzZXVwKTtcbiAgICAgICAgXG4gICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V3aGVlbCcsIG1vdXNlLm1vdXNld2hlZWwpO1xuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTU1vdXNlU2Nyb2xsJywgbW91c2UubW91c2V3aGVlbCk7XG5cbiAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaG1vdmUnLCBtb3VzZS5tb3VzZW1vdmUpO1xuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBtb3VzZS5tb3VzZWRvd24pO1xuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoZW5kJywgbW91c2UubW91c2V1cCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENsZWFycyBhbGwgY2FwdHVyZWQgc291cmNlIGV2ZW50cy5cbiAgICAgKiBAbWV0aG9kIGNsZWFyU291cmNlRXZlbnRzXG4gICAgICogQHBhcmFtIHttb3VzZX0gbW91c2VcbiAgICAgKi9cbiAgICBNb3VzZS5jbGVhclNvdXJjZUV2ZW50cyA9IGZ1bmN0aW9uKG1vdXNlKSB7XG4gICAgICAgIG1vdXNlLnNvdXJjZUV2ZW50cy5tb3VzZW1vdmUgPSBudWxsO1xuICAgICAgICBtb3VzZS5zb3VyY2VFdmVudHMubW91c2Vkb3duID0gbnVsbDtcbiAgICAgICAgbW91c2Uuc291cmNlRXZlbnRzLm1vdXNldXAgPSBudWxsO1xuICAgICAgICBtb3VzZS5zb3VyY2VFdmVudHMubW91c2V3aGVlbCA9IG51bGw7XG4gICAgICAgIG1vdXNlLndoZWVsRGVsdGEgPSAwO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBtb3VzZSBwb3NpdGlvbiBvZmZzZXQuXG4gICAgICogQG1ldGhvZCBzZXRPZmZzZXRcbiAgICAgKiBAcGFyYW0ge21vdXNlfSBtb3VzZVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBvZmZzZXRcbiAgICAgKi9cbiAgICBNb3VzZS5zZXRPZmZzZXQgPSBmdW5jdGlvbihtb3VzZSwgb2Zmc2V0KSB7XG4gICAgICAgIG1vdXNlLm9mZnNldC54ID0gb2Zmc2V0Lng7XG4gICAgICAgIG1vdXNlLm9mZnNldC55ID0gb2Zmc2V0Lnk7XG4gICAgICAgIG1vdXNlLnBvc2l0aW9uLnggPSBtb3VzZS5hYnNvbHV0ZS54ICogbW91c2Uuc2NhbGUueCArIG1vdXNlLm9mZnNldC54O1xuICAgICAgICBtb3VzZS5wb3NpdGlvbi55ID0gbW91c2UuYWJzb2x1dGUueSAqIG1vdXNlLnNjYWxlLnkgKyBtb3VzZS5vZmZzZXQueTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgbW91c2UgcG9zaXRpb24gc2NhbGUuXG4gICAgICogQG1ldGhvZCBzZXRTY2FsZVxuICAgICAqIEBwYXJhbSB7bW91c2V9IG1vdXNlXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHNjYWxlXG4gICAgICovXG4gICAgTW91c2Uuc2V0U2NhbGUgPSBmdW5jdGlvbihtb3VzZSwgc2NhbGUpIHtcbiAgICAgICAgbW91c2Uuc2NhbGUueCA9IHNjYWxlLng7XG4gICAgICAgIG1vdXNlLnNjYWxlLnkgPSBzY2FsZS55O1xuICAgICAgICBtb3VzZS5wb3NpdGlvbi54ID0gbW91c2UuYWJzb2x1dGUueCAqIG1vdXNlLnNjYWxlLnggKyBtb3VzZS5vZmZzZXQueDtcbiAgICAgICAgbW91c2UucG9zaXRpb24ueSA9IG1vdXNlLmFic29sdXRlLnkgKiBtb3VzZS5zY2FsZS55ICsgbW91c2Uub2Zmc2V0Lnk7XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBHZXRzIHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byBhbiBlbGVtZW50IGdpdmVuIGEgc2NyZWVuIHBpeGVsIHJhdGlvLlxuICAgICAqIEBtZXRob2QgX2dldFJlbGF0aXZlTW91c2VQb3NpdGlvblxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHt9IGV2ZW50XG4gICAgICogQHBhcmFtIHt9IGVsZW1lbnRcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcGl4ZWxSYXRpb1xuICAgICAqIEByZXR1cm4ge31cbiAgICAgKi9cbiAgICB2YXIgX2dldFJlbGF0aXZlTW91c2VQb3NpdGlvbiA9IGZ1bmN0aW9uKGV2ZW50LCBlbGVtZW50LCBwaXhlbFJhdGlvKSB7XG4gICAgICAgIHZhciBlbGVtZW50Qm91bmRzID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSxcbiAgICAgICAgICAgIHJvb3ROb2RlID0gKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCB8fCBkb2N1bWVudC5ib2R5LnBhcmVudE5vZGUgfHwgZG9jdW1lbnQuYm9keSksXG4gICAgICAgICAgICBzY3JvbGxYID0gKHdpbmRvdy5wYWdlWE9mZnNldCAhPT0gdW5kZWZpbmVkKSA/IHdpbmRvdy5wYWdlWE9mZnNldCA6IHJvb3ROb2RlLnNjcm9sbExlZnQsXG4gICAgICAgICAgICBzY3JvbGxZID0gKHdpbmRvdy5wYWdlWU9mZnNldCAhPT0gdW5kZWZpbmVkKSA/IHdpbmRvdy5wYWdlWU9mZnNldCA6IHJvb3ROb2RlLnNjcm9sbFRvcCxcbiAgICAgICAgICAgIHRvdWNoZXMgPSBldmVudC5jaGFuZ2VkVG91Y2hlcyxcbiAgICAgICAgICAgIHgsIHk7XG4gICAgICAgIFxuICAgICAgICBpZiAodG91Y2hlcykge1xuICAgICAgICAgICAgeCA9IHRvdWNoZXNbMF0ucGFnZVggLSBlbGVtZW50Qm91bmRzLmxlZnQgLSBzY3JvbGxYO1xuICAgICAgICAgICAgeSA9IHRvdWNoZXNbMF0ucGFnZVkgLSBlbGVtZW50Qm91bmRzLnRvcCAtIHNjcm9sbFk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB4ID0gZXZlbnQucGFnZVggLSBlbGVtZW50Qm91bmRzLmxlZnQgLSBzY3JvbGxYO1xuICAgICAgICAgICAgeSA9IGV2ZW50LnBhZ2VZIC0gZWxlbWVudEJvdW5kcy50b3AgLSBzY3JvbGxZO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHsgXG4gICAgICAgICAgICB4OiB4IC8gKGVsZW1lbnQuY2xpZW50V2lkdGggLyAoZWxlbWVudC53aWR0aCB8fCBlbGVtZW50LmNsaWVudFdpZHRoKSAqIHBpeGVsUmF0aW8pLFxuICAgICAgICAgICAgeTogeSAvIChlbGVtZW50LmNsaWVudEhlaWdodCAvIChlbGVtZW50LmhlaWdodCB8fCBlbGVtZW50LmNsaWVudEhlaWdodCkgKiBwaXhlbFJhdGlvKVxuICAgICAgICB9O1xuICAgIH07XG5cbn0pKCk7XG5cbn0se1wiLi4vY29yZS9Db21tb25cIjoxNH1dLDIwOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5QbHVnaW5gIG1vZHVsZSBjb250YWlucyBmdW5jdGlvbnMgZm9yIHJlZ2lzdGVyaW5nIGFuZCBpbnN0YWxsaW5nIHBsdWdpbnMgb24gbW9kdWxlcy5cbipcbiogQGNsYXNzIFBsdWdpblxuKi9cblxudmFyIFBsdWdpbiA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFBsdWdpbjtcblxudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4vQ29tbW9uJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIFBsdWdpbi5fcmVnaXN0cnkgPSB7fTtcblxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVycyBhIHBsdWdpbiBvYmplY3Qgc28gaXQgY2FuIGJlIHJlc29sdmVkIGxhdGVyIGJ5IG5hbWUuXG4gICAgICogQG1ldGhvZCByZWdpc3RlclxuICAgICAqIEBwYXJhbSBwbHVnaW4ge30gVGhlIHBsdWdpbiB0byByZWdpc3Rlci5cbiAgICAgKiBAcmV0dXJuIHtvYmplY3R9IFRoZSBwbHVnaW4uXG4gICAgICovXG4gICAgUGx1Z2luLnJlZ2lzdGVyID0gZnVuY3Rpb24ocGx1Z2luKSB7XG4gICAgICAgIGlmICghUGx1Z2luLmlzUGx1Z2luKHBsdWdpbikpIHtcbiAgICAgICAgICAgIENvbW1vbi53YXJuKCdQbHVnaW4ucmVnaXN0ZXI6JywgUGx1Z2luLnRvU3RyaW5nKHBsdWdpbiksICdkb2VzIG5vdCBpbXBsZW1lbnQgYWxsIHJlcXVpcmVkIGZpZWxkcy4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChwbHVnaW4ubmFtZSBpbiBQbHVnaW4uX3JlZ2lzdHJ5KSB7XG4gICAgICAgICAgICB2YXIgcmVnaXN0ZXJlZCA9IFBsdWdpbi5fcmVnaXN0cnlbcGx1Z2luLm5hbWVdLFxuICAgICAgICAgICAgICAgIHBsdWdpblZlcnNpb24gPSBQbHVnaW4udmVyc2lvblBhcnNlKHBsdWdpbi52ZXJzaW9uKS5udW1iZXIsXG4gICAgICAgICAgICAgICAgcmVnaXN0ZXJlZFZlcnNpb24gPSBQbHVnaW4udmVyc2lvblBhcnNlKHJlZ2lzdGVyZWQudmVyc2lvbikubnVtYmVyO1xuXG4gICAgICAgICAgICBpZiAocGx1Z2luVmVyc2lvbiA+IHJlZ2lzdGVyZWRWZXJzaW9uKSB7XG4gICAgICAgICAgICAgICAgQ29tbW9uLndhcm4oJ1BsdWdpbi5yZWdpc3RlcjonLCBQbHVnaW4udG9TdHJpbmcocmVnaXN0ZXJlZCksICd3YXMgdXBncmFkZWQgdG8nLCBQbHVnaW4udG9TdHJpbmcocGx1Z2luKSk7XG4gICAgICAgICAgICAgICAgUGx1Z2luLl9yZWdpc3RyeVtwbHVnaW4ubmFtZV0gPSBwbHVnaW47XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHBsdWdpblZlcnNpb24gPCByZWdpc3RlcmVkVmVyc2lvbikge1xuICAgICAgICAgICAgICAgIENvbW1vbi53YXJuKCdQbHVnaW4ucmVnaXN0ZXI6JywgUGx1Z2luLnRvU3RyaW5nKHJlZ2lzdGVyZWQpLCAnY2FuIG5vdCBiZSBkb3duZ3JhZGVkIHRvJywgUGx1Z2luLnRvU3RyaW5nKHBsdWdpbikpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChwbHVnaW4gIT09IHJlZ2lzdGVyZWQpIHtcbiAgICAgICAgICAgICAgICBDb21tb24ud2FybignUGx1Z2luLnJlZ2lzdGVyOicsIFBsdWdpbi50b1N0cmluZyhwbHVnaW4pLCAnaXMgYWxyZWFkeSByZWdpc3RlcmVkIHRvIGRpZmZlcmVudCBwbHVnaW4gb2JqZWN0Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBQbHVnaW4uX3JlZ2lzdHJ5W3BsdWdpbi5uYW1lXSA9IHBsdWdpbjtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBwbHVnaW47XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc29sdmVzIGEgZGVwZW5kZW5jeSB0byBhIHBsdWdpbiBvYmplY3QgZnJvbSB0aGUgcmVnaXN0cnkgaWYgaXQgZXhpc3RzLiBcbiAgICAgKiBUaGUgYGRlcGVuZGVuY3lgIG1heSBjb250YWluIGEgdmVyc2lvbiwgYnV0IG9ubHkgdGhlIG5hbWUgbWF0dGVycyB3aGVuIHJlc29sdmluZy5cbiAgICAgKiBAbWV0aG9kIHJlc29sdmVcbiAgICAgKiBAcGFyYW0gZGVwZW5kZW5jeSB7c3RyaW5nfSBUaGUgZGVwZW5kZW5jeS5cbiAgICAgKiBAcmV0dXJuIHtvYmplY3R9IFRoZSBwbHVnaW4gaWYgcmVzb2x2ZWQsIG90aGVyd2lzZSBgdW5kZWZpbmVkYC5cbiAgICAgKi9cbiAgICBQbHVnaW4ucmVzb2x2ZSA9IGZ1bmN0aW9uKGRlcGVuZGVuY3kpIHtcbiAgICAgICAgcmV0dXJuIFBsdWdpbi5fcmVnaXN0cnlbUGx1Z2luLmRlcGVuZGVuY3lQYXJzZShkZXBlbmRlbmN5KS5uYW1lXTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhIHByZXR0eSBwcmludGVkIHBsdWdpbiBuYW1lIGFuZCB2ZXJzaW9uLlxuICAgICAqIEBtZXRob2QgdG9TdHJpbmdcbiAgICAgKiBAcGFyYW0gcGx1Z2luIHt9IFRoZSBwbHVnaW4uXG4gICAgICogQHJldHVybiB7c3RyaW5nfSBQcmV0dHkgcHJpbnRlZCBwbHVnaW4gbmFtZSBhbmQgdmVyc2lvbi5cbiAgICAgKi9cbiAgICBQbHVnaW4udG9TdHJpbmcgPSBmdW5jdGlvbihwbHVnaW4pIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiBwbHVnaW4gPT09ICdzdHJpbmcnID8gcGx1Z2luIDogKHBsdWdpbi5uYW1lIHx8ICdhbm9ueW1vdXMnKSArICdAJyArIChwbHVnaW4udmVyc2lvbiB8fCBwbHVnaW4ucmFuZ2UgfHwgJzAuMC4wJyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYHRydWVgIGlmIHRoZSBvYmplY3QgbWVldHMgdGhlIG1pbmltdW0gc3RhbmRhcmQgdG8gYmUgY29uc2lkZXJlZCBhIHBsdWdpbi5cbiAgICAgKiBUaGlzIG1lYW5zIGl0IG11c3QgZGVmaW5lIHRoZSBmb2xsb3dpbmcgcHJvcGVydGllczpcbiAgICAgKiAtIGBuYW1lYFxuICAgICAqIC0gYHZlcnNpb25gXG4gICAgICogLSBgaW5zdGFsbGBcbiAgICAgKiBAbWV0aG9kIGlzUGx1Z2luXG4gICAgICogQHBhcmFtIG9iaiB7fSBUaGUgb2JqIHRvIHRlc3QuXG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIHRoZSBvYmplY3QgY2FuIGJlIGNvbnNpZGVyZWQgYSBwbHVnaW4gb3RoZXJ3aXNlIGBmYWxzZWAuXG4gICAgICovXG4gICAgUGx1Z2luLmlzUGx1Z2luID0gZnVuY3Rpb24ob2JqKSB7XG4gICAgICAgIHJldHVybiBvYmogJiYgb2JqLm5hbWUgJiYgb2JqLnZlcnNpb24gJiYgb2JqLmluc3RhbGw7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYHRydWVgIGlmIGEgcGx1Z2luIHdpdGggdGhlIGdpdmVuIGBuYW1lYCBiZWVuIGluc3RhbGxlZCBvbiBgbW9kdWxlYC5cbiAgICAgKiBAbWV0aG9kIGlzVXNlZFxuICAgICAqIEBwYXJhbSBtb2R1bGUge30gVGhlIG1vZHVsZS5cbiAgICAgKiBAcGFyYW0gbmFtZSB7c3RyaW5nfSBUaGUgcGx1Z2luIG5hbWUuXG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIGEgcGx1Z2luIHdpdGggdGhlIGdpdmVuIGBuYW1lYCBiZWVuIGluc3RhbGxlZCBvbiBgbW9kdWxlYCwgb3RoZXJ3aXNlIGBmYWxzZWAuXG4gICAgICovXG4gICAgUGx1Z2luLmlzVXNlZCA9IGZ1bmN0aW9uKG1vZHVsZSwgbmFtZSkge1xuICAgICAgICByZXR1cm4gbW9kdWxlLnVzZWQuaW5kZXhPZihuYW1lKSA+IC0xO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGB0cnVlYCBpZiBgcGx1Z2luLmZvcmAgaXMgYXBwbGljYWJsZSB0byBgbW9kdWxlYCBieSBjb21wYXJpbmcgYWdhaW5zdCBgbW9kdWxlLm5hbWVgIGFuZCBgbW9kdWxlLnZlcnNpb25gLlxuICAgICAqIElmIGBwbHVnaW4uZm9yYCBpcyBub3Qgc3BlY2lmaWVkIHRoZW4gaXQgaXMgYXNzdW1lZCB0byBiZSBhcHBsaWNhYmxlLlxuICAgICAqIFRoZSB2YWx1ZSBvZiBgcGx1Z2luLmZvcmAgaXMgYSBzdHJpbmcgb2YgdGhlIGZvcm1hdCBgJ21vZHVsZS1uYW1lJ2Agb3IgYCdtb2R1bGUtbmFtZUB2ZXJzaW9uJ2AuXG4gICAgICogQG1ldGhvZCBpc0ZvclxuICAgICAqIEBwYXJhbSBwbHVnaW4ge30gVGhlIHBsdWdpbi5cbiAgICAgKiBAcGFyYW0gbW9kdWxlIHt9IFRoZSBtb2R1bGUuXG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIGBwbHVnaW4uZm9yYCBpcyBhcHBsaWNhYmxlIHRvIGBtb2R1bGVgLCBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICAgKi9cbiAgICBQbHVnaW4uaXNGb3IgPSBmdW5jdGlvbihwbHVnaW4sIG1vZHVsZSkge1xuICAgICAgICB2YXIgcGFyc2VkID0gcGx1Z2luLmZvciAmJiBQbHVnaW4uZGVwZW5kZW5jeVBhcnNlKHBsdWdpbi5mb3IpO1xuICAgICAgICByZXR1cm4gIXBsdWdpbi5mb3IgfHwgKG1vZHVsZS5uYW1lID09PSBwYXJzZWQubmFtZSAmJiBQbHVnaW4udmVyc2lvblNhdGlzZmllcyhtb2R1bGUudmVyc2lvbiwgcGFyc2VkLnJhbmdlKSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEluc3RhbGxzIHRoZSBwbHVnaW5zIGJ5IGNhbGxpbmcgYHBsdWdpbi5pbnN0YWxsYCBvbiBlYWNoIHBsdWdpbiBzcGVjaWZpZWQgaW4gYHBsdWdpbnNgIGlmIHBhc3NlZCwgb3RoZXJ3aXNlIGBtb2R1bGUudXNlc2AuXG4gICAgICogRm9yIGluc3RhbGxpbmcgcGx1Z2lucyBvbiBgTWF0dGVyYCBzZWUgdGhlIGNvbnZlbmllbmNlIGZ1bmN0aW9uIGBNYXR0ZXIudXNlYC5cbiAgICAgKiBQbHVnaW5zIG1heSBiZSBzcGVjaWZpZWQgZWl0aGVyIGJ5IHRoZWlyIG5hbWUgb3IgYSByZWZlcmVuY2UgdG8gdGhlIHBsdWdpbiBvYmplY3QuXG4gICAgICogUGx1Z2lucyB0aGVtc2VsdmVzIG1heSBzcGVjaWZ5IGZ1cnRoZXIgZGVwZW5kZW5jaWVzLCBidXQgZWFjaCBwbHVnaW4gaXMgaW5zdGFsbGVkIG9ubHkgb25jZS5cbiAgICAgKiBPcmRlciBpcyBpbXBvcnRhbnQsIGEgdG9wb2xvZ2ljYWwgc29ydCBpcyBwZXJmb3JtZWQgdG8gZmluZCB0aGUgYmVzdCByZXN1bHRpbmcgb3JkZXIgb2YgaW5zdGFsbGF0aW9uLlxuICAgICAqIFRoaXMgc29ydGluZyBhdHRlbXB0cyB0byBzYXRpc2Z5IGV2ZXJ5IGRlcGVuZGVuY3kncyByZXF1ZXN0ZWQgb3JkZXJpbmcsIGJ1dCBtYXkgbm90IGJlIGV4YWN0IGluIGFsbCBjYXNlcy5cbiAgICAgKiBUaGlzIGZ1bmN0aW9uIGxvZ3MgdGhlIHJlc3VsdGluZyBzdGF0dXMgb2YgZWFjaCBkZXBlbmRlbmN5IGluIHRoZSBjb25zb2xlLCBhbG9uZyB3aXRoIGFueSB3YXJuaW5ncy5cbiAgICAgKiAtIEEgZ3JlZW4gdGljayDinIUgaW5kaWNhdGVzIGEgZGVwZW5kZW5jeSB3YXMgcmVzb2x2ZWQgYW5kIGluc3RhbGxlZC5cbiAgICAgKiAtIEFuIG9yYW5nZSBkaWFtb25kIPCflLYgaW5kaWNhdGVzIGEgZGVwZW5kZW5jeSB3YXMgcmVzb2x2ZWQgYnV0IGEgd2FybmluZyB3YXMgdGhyb3duIGZvciBpdCBvciBvbmUgaWYgaXRzIGRlcGVuZGVuY2llcy5cbiAgICAgKiAtIEEgcmVkIGNyb3NzIOKdjCBpbmRpY2F0ZXMgYSBkZXBlbmRlbmN5IGNvdWxkIG5vdCBiZSByZXNvbHZlZC5cbiAgICAgKiBBdm9pZCBjYWxsaW5nIHRoaXMgZnVuY3Rpb24gbXVsdGlwbGUgdGltZXMgb24gdGhlIHNhbWUgbW9kdWxlIHVubGVzcyB5b3UgaW50ZW5kIHRvIG1hbnVhbGx5IGNvbnRyb2wgaW5zdGFsbGF0aW9uIG9yZGVyLlxuICAgICAqIEBtZXRob2QgdXNlXG4gICAgICogQHBhcmFtIG1vZHVsZSB7fSBUaGUgbW9kdWxlIGluc3RhbGwgcGx1Z2lucyBvbi5cbiAgICAgKiBAcGFyYW0gW3BsdWdpbnM9bW9kdWxlLnVzZXNdIHt9IFRoZSBwbHVnaW5zIHRvIGluc3RhbGwgb24gbW9kdWxlIChvcHRpb25hbCwgZGVmYXVsdHMgdG8gYG1vZHVsZS51c2VzYCkuXG4gICAgICovXG4gICAgUGx1Z2luLnVzZSA9IGZ1bmN0aW9uKG1vZHVsZSwgcGx1Z2lucykge1xuICAgICAgICBtb2R1bGUudXNlcyA9IChtb2R1bGUudXNlcyB8fCBbXSkuY29uY2F0KHBsdWdpbnMgfHwgW10pO1xuXG4gICAgICAgIGlmIChtb2R1bGUudXNlcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIENvbW1vbi53YXJuKCdQbHVnaW4udXNlOicsIFBsdWdpbi50b1N0cmluZyhtb2R1bGUpLCAnZG9lcyBub3Qgc3BlY2lmeSBhbnkgZGVwZW5kZW5jaWVzIHRvIGluc3RhbGwuJyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgZGVwZW5kZW5jaWVzID0gUGx1Z2luLmRlcGVuZGVuY2llcyhtb2R1bGUpLFxuICAgICAgICAgICAgc29ydGVkRGVwZW5kZW5jaWVzID0gQ29tbW9uLnRvcG9sb2dpY2FsU29ydChkZXBlbmRlbmNpZXMpLFxuICAgICAgICAgICAgc3RhdHVzID0gW107XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzb3J0ZWREZXBlbmRlbmNpZXMubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIGlmIChzb3J0ZWREZXBlbmRlbmNpZXNbaV0gPT09IG1vZHVsZS5uYW1lKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBwbHVnaW4gPSBQbHVnaW4ucmVzb2x2ZShzb3J0ZWREZXBlbmRlbmNpZXNbaV0pO1xuXG4gICAgICAgICAgICBpZiAoIXBsdWdpbikge1xuICAgICAgICAgICAgICAgIHN0YXR1cy5wdXNoKCfinYwgJyArIHNvcnRlZERlcGVuZGVuY2llc1tpXSk7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChQbHVnaW4uaXNVc2VkKG1vZHVsZSwgcGx1Z2luLm5hbWUpKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghUGx1Z2luLmlzRm9yKHBsdWdpbiwgbW9kdWxlKSkge1xuICAgICAgICAgICAgICAgIENvbW1vbi53YXJuKCdQbHVnaW4udXNlOicsIFBsdWdpbi50b1N0cmluZyhwbHVnaW4pLCAnaXMgZm9yJywgcGx1Z2luLmZvciwgJ2J1dCBpbnN0YWxsZWQgb24nLCBQbHVnaW4udG9TdHJpbmcobW9kdWxlKSArICcuJyk7XG4gICAgICAgICAgICAgICAgcGx1Z2luLl93YXJuZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAocGx1Z2luLmluc3RhbGwpIHtcbiAgICAgICAgICAgICAgICBwbHVnaW4uaW5zdGFsbChtb2R1bGUpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBDb21tb24ud2FybignUGx1Z2luLnVzZTonLCBQbHVnaW4udG9TdHJpbmcocGx1Z2luKSwgJ2RvZXMgbm90IHNwZWNpZnkgYW4gaW5zdGFsbCBmdW5jdGlvbi4nKTtcbiAgICAgICAgICAgICAgICBwbHVnaW4uX3dhcm5lZCA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChwbHVnaW4uX3dhcm5lZCkge1xuICAgICAgICAgICAgICAgIHN0YXR1cy5wdXNoKCfwn5S2ICcgKyBQbHVnaW4udG9TdHJpbmcocGx1Z2luKSk7XG4gICAgICAgICAgICAgICAgZGVsZXRlIHBsdWdpbi5fd2FybmVkO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzdGF0dXMucHVzaCgn4pyFICcgKyBQbHVnaW4udG9TdHJpbmcocGx1Z2luKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG1vZHVsZS51c2VkLnB1c2gocGx1Z2luLm5hbWUpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHN0YXR1cy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBDb21tb24uaW5mbyhzdGF0dXMuam9pbignICAnKSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVjdXJzaXZlbHkgZmluZHMgYWxsIG9mIGEgbW9kdWxlJ3MgZGVwZW5kZW5jaWVzIGFuZCByZXR1cm5zIGEgZmxhdCBkZXBlbmRlbmN5IGdyYXBoLlxuICAgICAqIEBtZXRob2QgZGVwZW5kZW5jaWVzXG4gICAgICogQHBhcmFtIG1vZHVsZSB7fSBUaGUgbW9kdWxlLlxuICAgICAqIEByZXR1cm4ge29iamVjdH0gQSBkZXBlbmRlbmN5IGdyYXBoLlxuICAgICAqL1xuICAgIFBsdWdpbi5kZXBlbmRlbmNpZXMgPSBmdW5jdGlvbihtb2R1bGUsIHRyYWNrZWQpIHtcbiAgICAgICAgdmFyIHBhcnNlZEJhc2UgPSBQbHVnaW4uZGVwZW5kZW5jeVBhcnNlKG1vZHVsZSksXG4gICAgICAgICAgICBuYW1lID0gcGFyc2VkQmFzZS5uYW1lO1xuXG4gICAgICAgIHRyYWNrZWQgPSB0cmFja2VkIHx8IHt9O1xuXG4gICAgICAgIGlmIChuYW1lIGluIHRyYWNrZWQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIG1vZHVsZSA9IFBsdWdpbi5yZXNvbHZlKG1vZHVsZSkgfHwgbW9kdWxlO1xuXG4gICAgICAgIHRyYWNrZWRbbmFtZV0gPSBDb21tb24ubWFwKG1vZHVsZS51c2VzIHx8IFtdLCBmdW5jdGlvbihkZXBlbmRlbmN5KSB7XG4gICAgICAgICAgICBpZiAoUGx1Z2luLmlzUGx1Z2luKGRlcGVuZGVuY3kpKSB7XG4gICAgICAgICAgICAgICAgUGx1Z2luLnJlZ2lzdGVyKGRlcGVuZGVuY3kpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcGFyc2VkID0gUGx1Z2luLmRlcGVuZGVuY3lQYXJzZShkZXBlbmRlbmN5KSxcbiAgICAgICAgICAgICAgICByZXNvbHZlZCA9IFBsdWdpbi5yZXNvbHZlKGRlcGVuZGVuY3kpO1xuXG4gICAgICAgICAgICBpZiAocmVzb2x2ZWQgJiYgIVBsdWdpbi52ZXJzaW9uU2F0aXNmaWVzKHJlc29sdmVkLnZlcnNpb24sIHBhcnNlZC5yYW5nZSkpIHtcbiAgICAgICAgICAgICAgICBDb21tb24ud2FybihcbiAgICAgICAgICAgICAgICAgICAgJ1BsdWdpbi5kZXBlbmRlbmNpZXM6JywgUGx1Z2luLnRvU3RyaW5nKHJlc29sdmVkKSwgJ2RvZXMgbm90IHNhdGlzZnknLFxuICAgICAgICAgICAgICAgICAgICBQbHVnaW4udG9TdHJpbmcocGFyc2VkKSwgJ3VzZWQgYnknLCBQbHVnaW4udG9TdHJpbmcocGFyc2VkQmFzZSkgKyAnLidcbiAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgcmVzb2x2ZWQuX3dhcm5lZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgbW9kdWxlLl93YXJuZWQgPSB0cnVlO1xuICAgICAgICAgICAgfSBlbHNlIGlmICghcmVzb2x2ZWQpIHtcbiAgICAgICAgICAgICAgICBDb21tb24ud2FybihcbiAgICAgICAgICAgICAgICAgICAgJ1BsdWdpbi5kZXBlbmRlbmNpZXM6JywgUGx1Z2luLnRvU3RyaW5nKGRlcGVuZGVuY3kpLCAndXNlZCBieScsXG4gICAgICAgICAgICAgICAgICAgIFBsdWdpbi50b1N0cmluZyhwYXJzZWRCYXNlKSwgJ2NvdWxkIG5vdCBiZSByZXNvbHZlZC4nXG4gICAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAgIG1vZHVsZS5fd2FybmVkID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHBhcnNlZC5uYW1lO1xuICAgICAgICB9KTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRyYWNrZWRbbmFtZV0ubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIFBsdWdpbi5kZXBlbmRlbmNpZXModHJhY2tlZFtuYW1lXVtpXSwgdHJhY2tlZCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdHJhY2tlZDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUGFyc2VzIGEgZGVwZW5kZW5jeSBzdHJpbmcgaW50byBpdHMgY29tcG9uZW50cy5cbiAgICAgKiBUaGUgYGRlcGVuZGVuY3lgIGlzIGEgc3RyaW5nIG9mIHRoZSBmb3JtYXQgYCdtb2R1bGUtbmFtZSdgIG9yIGAnbW9kdWxlLW5hbWVAdmVyc2lvbidgLlxuICAgICAqIFNlZSBkb2N1bWVudGF0aW9uIGZvciBgUGx1Z2luLnZlcnNpb25QYXJzZWAgZm9yIGEgZGVzY3JpcHRpb24gb2YgdGhlIGZvcm1hdC5cbiAgICAgKiBUaGlzIGZ1bmN0aW9uIGNhbiBhbHNvIGhhbmRsZSBkZXBlbmRlbmNpZXMgdGhhdCBhcmUgYWxyZWFkeSByZXNvbHZlZCAoZS5nLiBhIG1vZHVsZSBvYmplY3QpLlxuICAgICAqIEBtZXRob2QgZGVwZW5kZW5jeVBhcnNlXG4gICAgICogQHBhcmFtIGRlcGVuZGVuY3kge3N0cmluZ30gVGhlIGRlcGVuZGVuY3kgb2YgdGhlIGZvcm1hdCBgJ21vZHVsZS1uYW1lJ2Agb3IgYCdtb2R1bGUtbmFtZUB2ZXJzaW9uJ2AuXG4gICAgICogQHJldHVybiB7b2JqZWN0fSBUaGUgZGVwZW5kZW5jeSBwYXJzZWQgaW50byBpdHMgY29tcG9uZW50cy5cbiAgICAgKi9cbiAgICBQbHVnaW4uZGVwZW5kZW5jeVBhcnNlID0gZnVuY3Rpb24oZGVwZW5kZW5jeSkge1xuICAgICAgICBpZiAoQ29tbW9uLmlzU3RyaW5nKGRlcGVuZGVuY3kpKSB7XG4gICAgICAgICAgICB2YXIgcGF0dGVybiA9IC9eW1xcdy1dKyhAKFxcKnxbXFxefl0/XFxkK1xcLlxcZCtcXC5cXGQrKC1bMC05QS1aYS16LV0rKT8pKT8kLztcblxuICAgICAgICAgICAgaWYgKCFwYXR0ZXJuLnRlc3QoZGVwZW5kZW5jeSkpIHtcbiAgICAgICAgICAgICAgICBDb21tb24ud2FybignUGx1Z2luLmRlcGVuZGVuY3lQYXJzZTonLCBkZXBlbmRlbmN5LCAnaXMgbm90IGEgdmFsaWQgZGVwZW5kZW5jeSBzdHJpbmcuJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgbmFtZTogZGVwZW5kZW5jeS5zcGxpdCgnQCcpWzBdLFxuICAgICAgICAgICAgICAgIHJhbmdlOiBkZXBlbmRlbmN5LnNwbGl0KCdAJylbMV0gfHwgJyonXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIG5hbWU6IGRlcGVuZGVuY3kubmFtZSxcbiAgICAgICAgICAgIHJhbmdlOiBkZXBlbmRlbmN5LnJhbmdlIHx8IGRlcGVuZGVuY3kudmVyc2lvblxuICAgICAgICB9O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBQYXJzZXMgYSB2ZXJzaW9uIHN0cmluZyBpbnRvIGl0cyBjb21wb25lbnRzLiAgXG4gICAgICogVmVyc2lvbnMgYXJlIHN0cmljdGx5IG9mIHRoZSBmb3JtYXQgYHgueS56YCAoYXMgaW4gW3NlbXZlcl0oaHR0cDovL3NlbXZlci5vcmcvKSkuXG4gICAgICogVmVyc2lvbnMgbWF5IG9wdGlvbmFsbHkgaGF2ZSBhIHByZXJlbGVhc2UgdGFnIGluIHRoZSBmb3JtYXQgYHgueS56LWFscGhhYC5cbiAgICAgKiBSYW5nZXMgYXJlIGEgc3RyaWN0IHN1YnNldCBvZiBbbnBtIHJhbmdlc10oaHR0cHM6Ly9kb2NzLm5wbWpzLmNvbS9taXNjL3NlbXZlciNhZHZhbmNlZC1yYW5nZS1zeW50YXgpLlxuICAgICAqIE9ubHkgdGhlIGZvbGxvd2luZyByYW5nZSB0eXBlcyBhcmUgc3VwcG9ydGVkOlxuICAgICAqIC0gVGlsZGUgcmFuZ2VzIGUuZy4gYH4xLjIuM2BcbiAgICAgKiAtIENhcmV0IHJhbmdlcyBlLmcuIGBeMS4yLjNgXG4gICAgICogLSBFeGFjdCB2ZXJzaW9uIGUuZy4gYDEuMi4zYFxuICAgICAqIC0gQW55IHZlcnNpb24gYCpgXG4gICAgICogQG1ldGhvZCB2ZXJzaW9uUGFyc2VcbiAgICAgKiBAcGFyYW0gcmFuZ2Uge3N0cmluZ30gVGhlIHZlcnNpb24gc3RyaW5nLlxuICAgICAqIEByZXR1cm4ge29iamVjdH0gVGhlIHZlcnNpb24gcmFuZ2UgcGFyc2VkIGludG8gaXRzIGNvbXBvbmVudHMuXG4gICAgICovXG4gICAgUGx1Z2luLnZlcnNpb25QYXJzZSA9IGZ1bmN0aW9uKHJhbmdlKSB7XG4gICAgICAgIHZhciBwYXR0ZXJuID0gL15cXCp8W1xcXn5dP1xcZCtcXC5cXGQrXFwuXFxkKygtWzAtOUEtWmEtei1dKyk/JC87XG5cbiAgICAgICAgaWYgKCFwYXR0ZXJuLnRlc3QocmFuZ2UpKSB7XG4gICAgICAgICAgICBDb21tb24ud2FybignUGx1Z2luLnZlcnNpb25QYXJzZTonLCByYW5nZSwgJ2lzIG5vdCBhIHZhbGlkIHZlcnNpb24gb3IgcmFuZ2UuJyk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgaWRlbnRpZmllcnMgPSByYW5nZS5zcGxpdCgnLScpO1xuICAgICAgICByYW5nZSA9IGlkZW50aWZpZXJzWzBdO1xuXG4gICAgICAgIHZhciBpc1JhbmdlID0gaXNOYU4oTnVtYmVyKHJhbmdlWzBdKSksXG4gICAgICAgICAgICB2ZXJzaW9uID0gaXNSYW5nZSA/IHJhbmdlLnN1YnN0cigxKSA6IHJhbmdlLFxuICAgICAgICAgICAgcGFydHMgPSBDb21tb24ubWFwKHZlcnNpb24uc3BsaXQoJy4nKSwgZnVuY3Rpb24ocGFydCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBOdW1iZXIocGFydCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgaXNSYW5nZTogaXNSYW5nZSxcbiAgICAgICAgICAgIHZlcnNpb246IHZlcnNpb24sXG4gICAgICAgICAgICByYW5nZTogcmFuZ2UsXG4gICAgICAgICAgICBvcGVyYXRvcjogaXNSYW5nZSA/IHJhbmdlWzBdIDogJycsXG4gICAgICAgICAgICBwYXJ0czogcGFydHMsXG4gICAgICAgICAgICBwcmVyZWxlYXNlOiBpZGVudGlmaWVyc1sxXSxcbiAgICAgICAgICAgIG51bWJlcjogcGFydHNbMF0gKiAxZTggKyBwYXJ0c1sxXSAqIDFlNCArIHBhcnRzWzJdXG4gICAgICAgIH07XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYHRydWVgIGlmIGB2ZXJzaW9uYCBzYXRpc2ZpZXMgdGhlIGdpdmVuIGByYW5nZWAuXG4gICAgICogU2VlIGRvY3VtZW50YXRpb24gZm9yIGBQbHVnaW4udmVyc2lvblBhcnNlYCBmb3IgYSBkZXNjcmlwdGlvbiBvZiB0aGUgZm9ybWF0LlxuICAgICAqIElmIGEgdmVyc2lvbiBvciByYW5nZSBpcyBub3Qgc3BlY2lmaWVkLCB0aGVuIGFueSB2ZXJzaW9uIChgKmApIGlzIGFzc3VtZWQgdG8gc2F0aXNmeS5cbiAgICAgKiBAbWV0aG9kIHZlcnNpb25TYXRpc2ZpZXNcbiAgICAgKiBAcGFyYW0gdmVyc2lvbiB7c3RyaW5nfSBUaGUgdmVyc2lvbiBzdHJpbmcuXG4gICAgICogQHBhcmFtIHJhbmdlIHtzdHJpbmd9IFRoZSByYW5nZSBzdHJpbmcuXG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIGB2ZXJzaW9uYCBzYXRpc2ZpZXMgYHJhbmdlYCwgb3RoZXJ3aXNlIGBmYWxzZWAuXG4gICAgICovXG4gICAgUGx1Z2luLnZlcnNpb25TYXRpc2ZpZXMgPSBmdW5jdGlvbih2ZXJzaW9uLCByYW5nZSkge1xuICAgICAgICByYW5nZSA9IHJhbmdlIHx8ICcqJztcblxuICAgICAgICB2YXIgcmFuZ2VQYXJzZWQgPSBQbHVnaW4udmVyc2lvblBhcnNlKHJhbmdlKSxcbiAgICAgICAgICAgIHJhbmdlUGFydHMgPSByYW5nZVBhcnNlZC5wYXJ0cyxcbiAgICAgICAgICAgIHZlcnNpb25QYXJzZWQgPSBQbHVnaW4udmVyc2lvblBhcnNlKHZlcnNpb24pLFxuICAgICAgICAgICAgdmVyc2lvblBhcnRzID0gdmVyc2lvblBhcnNlZC5wYXJ0cztcblxuICAgICAgICBpZiAocmFuZ2VQYXJzZWQuaXNSYW5nZSkge1xuICAgICAgICAgICAgaWYgKHJhbmdlUGFyc2VkLm9wZXJhdG9yID09PSAnKicgfHwgdmVyc2lvbiA9PT0gJyonKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChyYW5nZVBhcnNlZC5vcGVyYXRvciA9PT0gJ34nKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHZlcnNpb25QYXJ0c1swXSA9PT0gcmFuZ2VQYXJ0c1swXSAmJiB2ZXJzaW9uUGFydHNbMV0gPT09IHJhbmdlUGFydHNbMV0gJiYgdmVyc2lvblBhcnRzWzJdID49IHJhbmdlUGFydHNbMl07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChyYW5nZVBhcnNlZC5vcGVyYXRvciA9PT0gJ14nKSB7XG4gICAgICAgICAgICAgICAgaWYgKHJhbmdlUGFydHNbMF0gPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB2ZXJzaW9uUGFydHNbMF0gPT09IHJhbmdlUGFydHNbMF0gJiYgdmVyc2lvblBhcnNlZC5udW1iZXIgPj0gcmFuZ2VQYXJzZWQubnVtYmVyO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChyYW5nZVBhcnRzWzFdID4gMCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdmVyc2lvblBhcnRzWzFdID09PSByYW5nZVBhcnRzWzFdICYmIHZlcnNpb25QYXJ0c1syXSA+PSByYW5nZVBhcnRzWzJdO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiB2ZXJzaW9uUGFydHNbMl0gPT09IHJhbmdlUGFydHNbMl07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdmVyc2lvbiA9PT0gcmFuZ2UgfHwgdmVyc2lvbiA9PT0gJyonO1xuICAgIH07XG5cbn0pKCk7XG5cbn0se1wiLi9Db21tb25cIjoxNH1dLDIxOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5SdW5uZXJgIG1vZHVsZSBpcyBhbiBvcHRpb25hbCB1dGlsaXR5IHdoaWNoIHByb3ZpZGVzIGEgZ2FtZSBsb29wLCBcbiogdGhhdCBoYW5kbGVzIGNvbnRpbnVvdXNseSB1cGRhdGluZyBhIGBNYXR0ZXIuRW5naW5lYCBmb3IgeW91IHdpdGhpbiBhIGJyb3dzZXIuXG4qIEl0IGlzIGludGVuZGVkIGZvciBkZXZlbG9wbWVudCBhbmQgZGVidWdnaW5nIHB1cnBvc2VzLCBidXQgbWF5IGFsc28gYmUgc3VpdGFibGUgZm9yIHNpbXBsZSBnYW1lcy5cbiogSWYgeW91IGFyZSB1c2luZyB5b3VyIG93biBnYW1lIGxvb3AgaW5zdGVhZCwgdGhlbiB5b3UgZG8gbm90IG5lZWQgdGhlIGBNYXR0ZXIuUnVubmVyYCBtb2R1bGUuXG4qIEluc3RlYWQganVzdCBjYWxsIGBFbmdpbmUudXBkYXRlKGVuZ2luZSwgZGVsdGEpYCBpbiB5b3VyIG93biBsb29wLlxuKlxuKiBTZWUgdGhlIGluY2x1ZGVkIHVzYWdlIFtleGFtcGxlc10oaHR0cHM6Ly9naXRodWIuY29tL2xpYWJydS9tYXR0ZXItanMvdHJlZS9tYXN0ZXIvZXhhbXBsZXMpLlxuKlxuKiBAY2xhc3MgUnVubmVyXG4qL1xuXG52YXIgUnVubmVyID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gUnVubmVyO1xuXG52YXIgRXZlbnRzID0gX2RlcmVxXygnLi9FdmVudHMnKTtcbnZhciBFbmdpbmUgPSBfZGVyZXFfKCcuL0VuZ2luZScpO1xudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4vQ29tbW9uJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIHZhciBfcmVxdWVzdEFuaW1hdGlvbkZyYW1lLFxuICAgICAgICBfY2FuY2VsQW5pbWF0aW9uRnJhbWU7XG5cbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgX3JlcXVlc3RBbmltYXRpb25GcmFtZSA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgd2luZG93LndlYmtpdFJlcXVlc3RBbmltYXRpb25GcmFtZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCB3aW5kb3cubW96UmVxdWVzdEFuaW1hdGlvbkZyYW1lIHx8IHdpbmRvdy5tc1JlcXVlc3RBbmltYXRpb25GcmFtZTtcbiAgIFxuICAgICAgICBfY2FuY2VsQW5pbWF0aW9uRnJhbWUgPSB3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUgfHwgd2luZG93Lm1vekNhbmNlbEFuaW1hdGlvbkZyYW1lIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCB3aW5kb3cud2Via2l0Q2FuY2VsQW5pbWF0aW9uRnJhbWUgfHwgd2luZG93Lm1zQ2FuY2VsQW5pbWF0aW9uRnJhbWU7XG4gICAgfVxuXG4gICAgaWYgKCFfcmVxdWVzdEFuaW1hdGlvbkZyYW1lKSB7XG4gICAgICAgIHZhciBfZnJhbWVUaW1lb3V0O1xuXG4gICAgICAgIF9yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPSBmdW5jdGlvbihjYWxsYmFjayl7IFxuICAgICAgICAgICAgX2ZyYW1lVGltZW91dCA9IHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IFxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKENvbW1vbi5ub3coKSk7IFxuICAgICAgICAgICAgfSwgMTAwMCAvIDYwKTtcbiAgICAgICAgfTtcblxuICAgICAgICBfY2FuY2VsQW5pbWF0aW9uRnJhbWUgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChfZnJhbWVUaW1lb3V0KTtcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IFJ1bm5lci4gVGhlIG9wdGlvbnMgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0IHNwZWNpZmllcyBhbnkgcHJvcGVydGllcyB5b3Ugd2lzaCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdHMuXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge30gb3B0aW9uc1xuICAgICAqL1xuICAgIFJ1bm5lci5jcmVhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgICAgIGZwczogNjAsXG4gICAgICAgICAgICBjb3JyZWN0aW9uOiAxLFxuICAgICAgICAgICAgZGVsdGFTYW1wbGVTaXplOiA2MCxcbiAgICAgICAgICAgIGNvdW50ZXJUaW1lc3RhbXA6IDAsXG4gICAgICAgICAgICBmcmFtZUNvdW50ZXI6IDAsXG4gICAgICAgICAgICBkZWx0YUhpc3Rvcnk6IFtdLFxuICAgICAgICAgICAgdGltZVByZXY6IG51bGwsXG4gICAgICAgICAgICB0aW1lU2NhbGVQcmV2OiAxLFxuICAgICAgICAgICAgZnJhbWVSZXF1ZXN0SWQ6IG51bGwsXG4gICAgICAgICAgICBpc0ZpeGVkOiBmYWxzZSxcbiAgICAgICAgICAgIGVuYWJsZWQ6IHRydWVcbiAgICAgICAgfTtcblxuICAgICAgICB2YXIgcnVubmVyID0gQ29tbW9uLmV4dGVuZChkZWZhdWx0cywgb3B0aW9ucyk7XG5cbiAgICAgICAgcnVubmVyLmRlbHRhID0gcnVubmVyLmRlbHRhIHx8IDEwMDAgLyBydW5uZXIuZnBzO1xuICAgICAgICBydW5uZXIuZGVsdGFNaW4gPSBydW5uZXIuZGVsdGFNaW4gfHwgMTAwMCAvIHJ1bm5lci5mcHM7XG4gICAgICAgIHJ1bm5lci5kZWx0YU1heCA9IHJ1bm5lci5kZWx0YU1heCB8fCAxMDAwIC8gKHJ1bm5lci5mcHMgKiAwLjUpO1xuICAgICAgICBydW5uZXIuZnBzID0gMTAwMCAvIHJ1bm5lci5kZWx0YTtcblxuICAgICAgICByZXR1cm4gcnVubmVyO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDb250aW51b3VzbHkgdGlja3MgYSBgTWF0dGVyLkVuZ2luZWAgYnkgY2FsbGluZyBgUnVubmVyLnRpY2tgIG9uIHRoZSBgcmVxdWVzdEFuaW1hdGlvbkZyYW1lYCBldmVudC5cbiAgICAgKiBAbWV0aG9kIHJ1blxuICAgICAqIEBwYXJhbSB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKi9cbiAgICBSdW5uZXIucnVuID0gZnVuY3Rpb24ocnVubmVyLCBlbmdpbmUpIHtcbiAgICAgICAgLy8gY3JlYXRlIHJ1bm5lciBpZiBlbmdpbmUgaXMgZmlyc3QgYXJndW1lbnRcbiAgICAgICAgaWYgKHR5cGVvZiBydW5uZXIucG9zaXRpb25JdGVyYXRpb25zICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgZW5naW5lID0gcnVubmVyO1xuICAgICAgICAgICAgcnVubmVyID0gUnVubmVyLmNyZWF0ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgKGZ1bmN0aW9uIHJlbmRlcih0aW1lKXtcbiAgICAgICAgICAgIHJ1bm5lci5mcmFtZVJlcXVlc3RJZCA9IF9yZXF1ZXN0QW5pbWF0aW9uRnJhbWUocmVuZGVyKTtcblxuICAgICAgICAgICAgaWYgKHRpbWUgJiYgcnVubmVyLmVuYWJsZWQpIHtcbiAgICAgICAgICAgICAgICBSdW5uZXIudGljayhydW5uZXIsIGVuZ2luZSwgdGltZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pKCk7XG5cbiAgICAgICAgcmV0dXJuIHJ1bm5lcjtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQSBnYW1lIGxvb3AgdXRpbGl0eSB0aGF0IHVwZGF0ZXMgdGhlIGVuZ2luZSBhbmQgcmVuZGVyZXIgYnkgb25lIHN0ZXAgKGEgJ3RpY2snKS5cbiAgICAgKiBGZWF0dXJlcyBkZWx0YSBzbW9vdGhpbmcsIHRpbWUgY29ycmVjdGlvbiBhbmQgZml4ZWQgb3IgZHluYW1pYyB0aW1pbmcuXG4gICAgICogVHJpZ2dlcnMgYGJlZm9yZVRpY2tgLCBgdGlja2AgYW5kIGBhZnRlclRpY2tgIGV2ZW50cyBvbiB0aGUgZW5naW5lLlxuICAgICAqIENvbnNpZGVyIGp1c3QgYEVuZ2luZS51cGRhdGUoZW5naW5lLCBkZWx0YSlgIGlmIHlvdSdyZSB1c2luZyB5b3VyIG93biBsb29wLlxuICAgICAqIEBtZXRob2QgdGlja1xuICAgICAqIEBwYXJhbSB7cnVubmVyfSBydW5uZXJcbiAgICAgKiBAcGFyYW0ge2VuZ2luZX0gZW5naW5lXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHRpbWVcbiAgICAgKi9cbiAgICBSdW5uZXIudGljayA9IGZ1bmN0aW9uKHJ1bm5lciwgZW5naW5lLCB0aW1lKSB7XG4gICAgICAgIHZhciB0aW1pbmcgPSBlbmdpbmUudGltaW5nLFxuICAgICAgICAgICAgY29ycmVjdGlvbiA9IDEsXG4gICAgICAgICAgICBkZWx0YTtcblxuICAgICAgICAvLyBjcmVhdGUgYW4gZXZlbnQgb2JqZWN0XG4gICAgICAgIHZhciBldmVudCA9IHtcbiAgICAgICAgICAgIHRpbWVzdGFtcDogdGltaW5nLnRpbWVzdGFtcFxuICAgICAgICB9O1xuXG4gICAgICAgIEV2ZW50cy50cmlnZ2VyKHJ1bm5lciwgJ2JlZm9yZVRpY2snLCBldmVudCk7XG4gICAgICAgIEV2ZW50cy50cmlnZ2VyKGVuZ2luZSwgJ2JlZm9yZVRpY2snLCBldmVudCk7IC8vIEBkZXByZWNhdGVkXG5cbiAgICAgICAgaWYgKHJ1bm5lci5pc0ZpeGVkKSB7XG4gICAgICAgICAgICAvLyBmaXhlZCB0aW1lc3RlcFxuICAgICAgICAgICAgZGVsdGEgPSBydW5uZXIuZGVsdGE7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBkeW5hbWljIHRpbWVzdGVwIGJhc2VkIG9uIHdhbGwgY2xvY2sgYmV0d2VlbiBjYWxsc1xuICAgICAgICAgICAgZGVsdGEgPSAodGltZSAtIHJ1bm5lci50aW1lUHJldikgfHwgcnVubmVyLmRlbHRhO1xuICAgICAgICAgICAgcnVubmVyLnRpbWVQcmV2ID0gdGltZTtcblxuICAgICAgICAgICAgLy8gb3B0aW1pc3RpY2FsbHkgZmlsdGVyIGRlbHRhIG92ZXIgYSBmZXcgZnJhbWVzLCB0byBpbXByb3ZlIHN0YWJpbGl0eVxuICAgICAgICAgICAgcnVubmVyLmRlbHRhSGlzdG9yeS5wdXNoKGRlbHRhKTtcbiAgICAgICAgICAgIHJ1bm5lci5kZWx0YUhpc3RvcnkgPSBydW5uZXIuZGVsdGFIaXN0b3J5LnNsaWNlKC1ydW5uZXIuZGVsdGFTYW1wbGVTaXplKTtcbiAgICAgICAgICAgIGRlbHRhID0gTWF0aC5taW4uYXBwbHkobnVsbCwgcnVubmVyLmRlbHRhSGlzdG9yeSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIGxpbWl0IGRlbHRhXG4gICAgICAgICAgICBkZWx0YSA9IGRlbHRhIDwgcnVubmVyLmRlbHRhTWluID8gcnVubmVyLmRlbHRhTWluIDogZGVsdGE7XG4gICAgICAgICAgICBkZWx0YSA9IGRlbHRhID4gcnVubmVyLmRlbHRhTWF4ID8gcnVubmVyLmRlbHRhTWF4IDogZGVsdGE7XG5cbiAgICAgICAgICAgIC8vIGNvcnJlY3Rpb24gZm9yIGRlbHRhXG4gICAgICAgICAgICBjb3JyZWN0aW9uID0gZGVsdGEgLyBydW5uZXIuZGVsdGE7XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSBlbmdpbmUgdGltaW5nIG9iamVjdFxuICAgICAgICAgICAgcnVubmVyLmRlbHRhID0gZGVsdGE7XG4gICAgICAgIH1cblxuICAgICAgICAvLyB0aW1lIGNvcnJlY3Rpb24gZm9yIHRpbWUgc2NhbGluZ1xuICAgICAgICBpZiAocnVubmVyLnRpbWVTY2FsZVByZXYgIT09IDApXG4gICAgICAgICAgICBjb3JyZWN0aW9uICo9IHRpbWluZy50aW1lU2NhbGUgLyBydW5uZXIudGltZVNjYWxlUHJldjtcblxuICAgICAgICBpZiAodGltaW5nLnRpbWVTY2FsZSA9PT0gMClcbiAgICAgICAgICAgIGNvcnJlY3Rpb24gPSAwO1xuXG4gICAgICAgIHJ1bm5lci50aW1lU2NhbGVQcmV2ID0gdGltaW5nLnRpbWVTY2FsZTtcbiAgICAgICAgcnVubmVyLmNvcnJlY3Rpb24gPSBjb3JyZWN0aW9uO1xuXG4gICAgICAgIC8vIGZwcyBjb3VudGVyXG4gICAgICAgIHJ1bm5lci5mcmFtZUNvdW50ZXIgKz0gMTtcbiAgICAgICAgaWYgKHRpbWUgLSBydW5uZXIuY291bnRlclRpbWVzdGFtcCA+PSAxMDAwKSB7XG4gICAgICAgICAgICBydW5uZXIuZnBzID0gcnVubmVyLmZyYW1lQ291bnRlciAqICgodGltZSAtIHJ1bm5lci5jb3VudGVyVGltZXN0YW1wKSAvIDEwMDApO1xuICAgICAgICAgICAgcnVubmVyLmNvdW50ZXJUaW1lc3RhbXAgPSB0aW1lO1xuICAgICAgICAgICAgcnVubmVyLmZyYW1lQ291bnRlciA9IDA7XG4gICAgICAgIH1cblxuICAgICAgICBFdmVudHMudHJpZ2dlcihydW5uZXIsICd0aWNrJywgZXZlbnQpO1xuICAgICAgICBFdmVudHMudHJpZ2dlcihlbmdpbmUsICd0aWNrJywgZXZlbnQpOyAvLyBAZGVwcmVjYXRlZFxuXG4gICAgICAgIC8vIGlmIHdvcmxkIGhhcyBiZWVuIG1vZGlmaWVkLCBjbGVhciB0aGUgcmVuZGVyIHNjZW5lIGdyYXBoXG4gICAgICAgIGlmIChlbmdpbmUud29ybGQuaXNNb2RpZmllZCBcbiAgICAgICAgICAgICYmIGVuZ2luZS5yZW5kZXJcbiAgICAgICAgICAgICYmIGVuZ2luZS5yZW5kZXIuY29udHJvbGxlclxuICAgICAgICAgICAgJiYgZW5naW5lLnJlbmRlci5jb250cm9sbGVyLmNsZWFyKSB7XG4gICAgICAgICAgICBlbmdpbmUucmVuZGVyLmNvbnRyb2xsZXIuY2xlYXIoZW5naW5lLnJlbmRlcik7IC8vIEBkZXByZWNhdGVkXG4gICAgICAgIH1cblxuICAgICAgICAvLyB1cGRhdGVcbiAgICAgICAgRXZlbnRzLnRyaWdnZXIocnVubmVyLCAnYmVmb3JlVXBkYXRlJywgZXZlbnQpO1xuICAgICAgICBFbmdpbmUudXBkYXRlKGVuZ2luZSwgZGVsdGEsIGNvcnJlY3Rpb24pO1xuICAgICAgICBFdmVudHMudHJpZ2dlcihydW5uZXIsICdhZnRlclVwZGF0ZScsIGV2ZW50KTtcblxuICAgICAgICAvLyByZW5kZXJcbiAgICAgICAgLy8gQGRlcHJlY2F0ZWRcbiAgICAgICAgaWYgKGVuZ2luZS5yZW5kZXIgJiYgZW5naW5lLnJlbmRlci5jb250cm9sbGVyKSB7XG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihydW5uZXIsICdiZWZvcmVSZW5kZXInLCBldmVudCk7XG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihlbmdpbmUsICdiZWZvcmVSZW5kZXInLCBldmVudCk7IC8vIEBkZXByZWNhdGVkXG5cbiAgICAgICAgICAgIGVuZ2luZS5yZW5kZXIuY29udHJvbGxlci53b3JsZChlbmdpbmUucmVuZGVyKTtcblxuICAgICAgICAgICAgRXZlbnRzLnRyaWdnZXIocnVubmVyLCAnYWZ0ZXJSZW5kZXInLCBldmVudCk7XG4gICAgICAgICAgICBFdmVudHMudHJpZ2dlcihlbmdpbmUsICdhZnRlclJlbmRlcicsIGV2ZW50KTsgLy8gQGRlcHJlY2F0ZWRcbiAgICAgICAgfVxuXG4gICAgICAgIEV2ZW50cy50cmlnZ2VyKHJ1bm5lciwgJ2FmdGVyVGljaycsIGV2ZW50KTtcbiAgICAgICAgRXZlbnRzLnRyaWdnZXIoZW5naW5lLCAnYWZ0ZXJUaWNrJywgZXZlbnQpOyAvLyBAZGVwcmVjYXRlZFxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBFbmRzIGV4ZWN1dGlvbiBvZiBgUnVubmVyLnJ1bmAgb24gdGhlIGdpdmVuIGBydW5uZXJgLCBieSBjYW5jZWxpbmcgdGhlIGFuaW1hdGlvbiBmcmFtZSByZXF1ZXN0IGV2ZW50IGxvb3AuXG4gICAgICogSWYgeW91IHdpc2ggdG8gb25seSB0ZW1wb3JhcmlseSBwYXVzZSB0aGUgZW5naW5lLCBzZWUgYGVuZ2luZS5lbmFibGVkYCBpbnN0ZWFkLlxuICAgICAqIEBtZXRob2Qgc3RvcFxuICAgICAqIEBwYXJhbSB7cnVubmVyfSBydW5uZXJcbiAgICAgKi9cbiAgICBSdW5uZXIuc3RvcCA9IGZ1bmN0aW9uKHJ1bm5lcikge1xuICAgICAgICBfY2FuY2VsQW5pbWF0aW9uRnJhbWUocnVubmVyLmZyYW1lUmVxdWVzdElkKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQWxpYXMgZm9yIGBSdW5uZXIucnVuYC5cbiAgICAgKiBAbWV0aG9kIHN0YXJ0XG4gICAgICogQHBhcmFtIHtydW5uZXJ9IHJ1bm5lclxuICAgICAqIEBwYXJhbSB7ZW5naW5lfSBlbmdpbmVcbiAgICAgKi9cbiAgICBSdW5uZXIuc3RhcnQgPSBmdW5jdGlvbihydW5uZXIsIGVuZ2luZSkge1xuICAgICAgICBSdW5uZXIucnVuKHJ1bm5lciwgZW5naW5lKTtcbiAgICB9O1xuXG4gICAgLypcbiAgICAqXG4gICAgKiAgRXZlbnRzIERvY3VtZW50YXRpb25cbiAgICAqXG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgYXQgdGhlIHN0YXJ0IG9mIGEgdGljaywgYmVmb3JlIGFueSB1cGRhdGVzIHRvIHRoZSBlbmdpbmUgb3IgdGltaW5nXG4gICAgKlxuICAgICogQGV2ZW50IGJlZm9yZVRpY2tcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudC50aW1lc3RhbXAgVGhlIGVuZ2luZS50aW1pbmcudGltZXN0YW1wIG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICovXG5cbiAgICAvKipcbiAgICAqIEZpcmVkIGFmdGVyIGVuZ2luZSB0aW1pbmcgdXBkYXRlZCwgYnV0IGp1c3QgYmVmb3JlIHVwZGF0ZVxuICAgICpcbiAgICAqIEBldmVudCB0aWNrXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge251bWJlcn0gZXZlbnQudGltZXN0YW1wIFRoZSBlbmdpbmUudGltaW5nLnRpbWVzdGFtcCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCBhdCB0aGUgZW5kIG9mIGEgdGljaywgYWZ0ZXIgZW5naW5lIHVwZGF0ZSBhbmQgYWZ0ZXIgcmVuZGVyaW5nXG4gICAgKlxuICAgICogQGV2ZW50IGFmdGVyVGlja1xuICAgICogQHBhcmFtIHt9IGV2ZW50IEFuIGV2ZW50IG9iamVjdFxuICAgICogQHBhcmFtIHtudW1iZXJ9IGV2ZW50LnRpbWVzdGFtcCBUaGUgZW5naW5lLnRpbWluZy50aW1lc3RhbXAgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgYmVmb3JlIHVwZGF0ZVxuICAgICpcbiAgICAqIEBldmVudCBiZWZvcmVVcGRhdGVcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudC50aW1lc3RhbXAgVGhlIGVuZ2luZS50aW1pbmcudGltZXN0YW1wIG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICovXG5cbiAgICAvKipcbiAgICAqIEZpcmVkIGFmdGVyIHVwZGF0ZVxuICAgICpcbiAgICAqIEBldmVudCBhZnRlclVwZGF0ZVxuICAgICogQHBhcmFtIHt9IGV2ZW50IEFuIGV2ZW50IG9iamVjdFxuICAgICogQHBhcmFtIHtudW1iZXJ9IGV2ZW50LnRpbWVzdGFtcCBUaGUgZW5naW5lLnRpbWluZy50aW1lc3RhbXAgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQuc291cmNlIFRoZSBzb3VyY2Ugb2JqZWN0IG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50Lm5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50XG4gICAgKi9cblxuICAgIC8qKlxuICAgICogRmlyZWQgYmVmb3JlIHJlbmRlcmluZ1xuICAgICpcbiAgICAqIEBldmVudCBiZWZvcmVSZW5kZXJcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudC50aW1lc3RhbXAgVGhlIGVuZ2luZS50aW1pbmcudGltZXN0YW1wIG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICogQGRlcHJlY2F0ZWRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCBhZnRlciByZW5kZXJpbmdcbiAgICAqXG4gICAgKiBAZXZlbnQgYWZ0ZXJSZW5kZXJcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudC50aW1lc3RhbXAgVGhlIGVuZ2luZS50aW1pbmcudGltZXN0YW1wIG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICogQGRlcHJlY2F0ZWRcbiAgICAqL1xuXG4gICAgLypcbiAgICAqXG4gICAgKiAgUHJvcGVydGllcyBEb2N1bWVudGF0aW9uXG4gICAgKlxuICAgICovXG5cbiAgICAvKipcbiAgICAgKiBBIGZsYWcgdGhhdCBzcGVjaWZpZXMgd2hldGhlciB0aGUgcnVubmVyIGlzIHJ1bm5pbmcgb3Igbm90LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGVuYWJsZWRcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgdHJ1ZVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSBgQm9vbGVhbmAgdGhhdCBzcGVjaWZpZXMgaWYgdGhlIHJ1bm5lciBzaG91bGQgdXNlIGEgZml4ZWQgdGltZXN0ZXAgKG90aGVyd2lzZSBpdCBpcyB2YXJpYWJsZSkuXG4gICAgICogSWYgdGltaW5nIGlzIGZpeGVkLCB0aGVuIHRoZSBhcHBhcmVudCBzaW11bGF0aW9uIHNwZWVkIHdpbGwgY2hhbmdlIGRlcGVuZGluZyBvbiB0aGUgZnJhbWUgcmF0ZSAoYnV0IGJlaGF2aW91ciB3aWxsIGJlIGRldGVybWluaXN0aWMpLlxuICAgICAqIElmIHRoZSB0aW1pbmcgaXMgdmFyaWFibGUsIHRoZW4gdGhlIGFwcGFyZW50IHNpbXVsYXRpb24gc3BlZWQgd2lsbCBiZSBjb25zdGFudCAoYXBwcm94aW1hdGVseSwgYnV0IGF0IHRoZSBjb3N0IG9mIGRldGVybWluaW5pc20pLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGlzRml4ZWRcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYE51bWJlcmAgdGhhdCBzcGVjaWZpZXMgdGhlIHRpbWUgc3RlcCBiZXR3ZWVuIHVwZGF0ZXMgaW4gbWlsbGlzZWNvbmRzLlxuICAgICAqIElmIGBlbmdpbmUudGltaW5nLmlzRml4ZWRgIGlzIHNldCB0byBgdHJ1ZWAsIHRoZW4gYGRlbHRhYCBpcyBmaXhlZC5cbiAgICAgKiBJZiBpdCBpcyBgZmFsc2VgLCB0aGVuIGBkZWx0YWAgY2FuIGR5bmFtaWNhbGx5IGNoYW5nZSB0byBtYWludGFpbiB0aGUgY29ycmVjdCBhcHBhcmVudCBzaW11bGF0aW9uIHNwZWVkLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGRlbHRhXG4gICAgICogQHR5cGUgbnVtYmVyXG4gICAgICogQGRlZmF1bHQgMTAwMCAvIDYwXG4gICAgICovXG5cbn0pKCk7XG5cbn0se1wiLi9Db21tb25cIjoxNCxcIi4vRW5naW5lXCI6MTUsXCIuL0V2ZW50c1wiOjE2fV0sMjI6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLlNsZWVwaW5nYCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyB0byBtYW5hZ2UgdGhlIHNsZWVwaW5nIHN0YXRlIG9mIGJvZGllcy5cbipcbiogQGNsYXNzIFNsZWVwaW5nXG4qL1xuXG52YXIgU2xlZXBpbmcgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBTbGVlcGluZztcblxudmFyIEV2ZW50cyA9IF9kZXJlcV8oJy4vRXZlbnRzJyk7XG5cbihmdW5jdGlvbigpIHtcblxuICAgIFNsZWVwaW5nLl9tb3Rpb25XYWtlVGhyZXNob2xkID0gMC4xODtcbiAgICBTbGVlcGluZy5fbW90aW9uU2xlZXBUaHJlc2hvbGQgPSAwLjA4O1xuICAgIFNsZWVwaW5nLl9taW5CaWFzID0gMC45O1xuXG4gICAgLyoqXG4gICAgICogUHV0cyBib2RpZXMgdG8gc2xlZXAgb3Igd2FrZXMgdGhlbSB1cCBkZXBlbmRpbmcgb24gdGhlaXIgbW90aW9uLlxuICAgICAqIEBtZXRob2QgdXBkYXRlXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lU2NhbGVcbiAgICAgKi9cbiAgICBTbGVlcGluZy51cGRhdGUgPSBmdW5jdGlvbihib2RpZXMsIHRpbWVTY2FsZSkge1xuICAgICAgICB2YXIgdGltZUZhY3RvciA9IHRpbWVTY2FsZSAqIHRpbWVTY2FsZSAqIHRpbWVTY2FsZTtcblxuICAgICAgICAvLyB1cGRhdGUgYm9kaWVzIHNsZWVwaW5nIHN0YXR1c1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGJvZHkgPSBib2RpZXNbaV0sXG4gICAgICAgICAgICAgICAgbW90aW9uID0gYm9keS5zcGVlZCAqIGJvZHkuc3BlZWQgKyBib2R5LmFuZ3VsYXJTcGVlZCAqIGJvZHkuYW5ndWxhclNwZWVkO1xuXG4gICAgICAgICAgICAvLyB3YWtlIHVwIGJvZGllcyBpZiB0aGV5IGhhdmUgYSBmb3JjZSBhcHBsaWVkXG4gICAgICAgICAgICBpZiAoYm9keS5mb3JjZS54ICE9PSAwIHx8IGJvZHkuZm9yY2UueSAhPT0gMCkge1xuICAgICAgICAgICAgICAgIFNsZWVwaW5nLnNldChib2R5LCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBtaW5Nb3Rpb24gPSBNYXRoLm1pbihib2R5Lm1vdGlvbiwgbW90aW9uKSxcbiAgICAgICAgICAgICAgICBtYXhNb3Rpb24gPSBNYXRoLm1heChib2R5Lm1vdGlvbiwgbW90aW9uKTtcbiAgICAgICAgXG4gICAgICAgICAgICAvLyBiaWFzZWQgYXZlcmFnZSBtb3Rpb24gZXN0aW1hdGlvbiBiZXR3ZWVuIGZyYW1lc1xuICAgICAgICAgICAgYm9keS5tb3Rpb24gPSBTbGVlcGluZy5fbWluQmlhcyAqIG1pbk1vdGlvbiArICgxIC0gU2xlZXBpbmcuX21pbkJpYXMpICogbWF4TW90aW9uO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoYm9keS5zbGVlcFRocmVzaG9sZCA+IDAgJiYgYm9keS5tb3Rpb24gPCBTbGVlcGluZy5fbW90aW9uU2xlZXBUaHJlc2hvbGQgKiB0aW1lRmFjdG9yKSB7XG4gICAgICAgICAgICAgICAgYm9keS5zbGVlcENvdW50ZXIgKz0gMTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBpZiAoYm9keS5zbGVlcENvdW50ZXIgPj0gYm9keS5zbGVlcFRocmVzaG9sZClcbiAgICAgICAgICAgICAgICAgICAgU2xlZXBpbmcuc2V0KGJvZHksIHRydWUpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChib2R5LnNsZWVwQ291bnRlciA+IDApIHtcbiAgICAgICAgICAgICAgICBib2R5LnNsZWVwQ291bnRlciAtPSAxO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdpdmVuIGEgc2V0IG9mIGNvbGxpZGluZyBwYWlycywgd2FrZXMgdGhlIHNsZWVwaW5nIGJvZGllcyBpbnZvbHZlZC5cbiAgICAgKiBAbWV0aG9kIGFmdGVyQ29sbGlzaW9uc1xuICAgICAqIEBwYXJhbSB7cGFpcltdfSBwYWlyc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lU2NhbGVcbiAgICAgKi9cbiAgICBTbGVlcGluZy5hZnRlckNvbGxpc2lvbnMgPSBmdW5jdGlvbihwYWlycywgdGltZVNjYWxlKSB7XG4gICAgICAgIHZhciB0aW1lRmFjdG9yID0gdGltZVNjYWxlICogdGltZVNjYWxlICogdGltZVNjYWxlO1xuXG4gICAgICAgIC8vIHdha2UgdXAgYm9kaWVzIGludm9sdmVkIGluIGNvbGxpc2lvbnNcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYWlycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHBhaXIgPSBwYWlyc1tpXTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gZG9uJ3Qgd2FrZSBpbmFjdGl2ZSBwYWlyc1xuICAgICAgICAgICAgaWYgKCFwYWlyLmlzQWN0aXZlKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICB2YXIgY29sbGlzaW9uID0gcGFpci5jb2xsaXNpb24sXG4gICAgICAgICAgICAgICAgYm9keUEgPSBjb2xsaXNpb24uYm9keUEucGFyZW50LCBcbiAgICAgICAgICAgICAgICBib2R5QiA9IGNvbGxpc2lvbi5ib2R5Qi5wYXJlbnQ7XG4gICAgICAgIFxuICAgICAgICAgICAgLy8gZG9uJ3Qgd2FrZSBpZiBhdCBsZWFzdCBvbmUgYm9keSBpcyBzdGF0aWNcbiAgICAgICAgICAgIGlmICgoYm9keUEuaXNTbGVlcGluZyAmJiBib2R5Qi5pc1NsZWVwaW5nKSB8fCBib2R5QS5pc1N0YXRpYyB8fCBib2R5Qi5pc1N0YXRpYylcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgXG4gICAgICAgICAgICBpZiAoYm9keUEuaXNTbGVlcGluZyB8fCBib2R5Qi5pc1NsZWVwaW5nKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNsZWVwaW5nQm9keSA9IChib2R5QS5pc1NsZWVwaW5nICYmICFib2R5QS5pc1N0YXRpYykgPyBib2R5QSA6IGJvZHlCLFxuICAgICAgICAgICAgICAgICAgICBtb3ZpbmdCb2R5ID0gc2xlZXBpbmdCb2R5ID09PSBib2R5QSA/IGJvZHlCIDogYm9keUE7XG5cbiAgICAgICAgICAgICAgICBpZiAoIXNsZWVwaW5nQm9keS5pc1N0YXRpYyAmJiBtb3ZpbmdCb2R5Lm1vdGlvbiA+IFNsZWVwaW5nLl9tb3Rpb25XYWtlVGhyZXNob2xkICogdGltZUZhY3Rvcikge1xuICAgICAgICAgICAgICAgICAgICBTbGVlcGluZy5zZXQoc2xlZXBpbmdCb2R5LCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcbiAgXG4gICAgLyoqXG4gICAgICogU2V0IGEgYm9keSBhcyBzbGVlcGluZyBvciBhd2FrZS5cbiAgICAgKiBAbWV0aG9kIHNldFxuICAgICAqIEBwYXJhbSB7Ym9keX0gYm9keVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gaXNTbGVlcGluZ1xuICAgICAqL1xuICAgIFNsZWVwaW5nLnNldCA9IGZ1bmN0aW9uKGJvZHksIGlzU2xlZXBpbmcpIHtcbiAgICAgICAgdmFyIHdhc1NsZWVwaW5nID0gYm9keS5pc1NsZWVwaW5nO1xuXG4gICAgICAgIGlmIChpc1NsZWVwaW5nKSB7XG4gICAgICAgICAgICBib2R5LmlzU2xlZXBpbmcgPSB0cnVlO1xuICAgICAgICAgICAgYm9keS5zbGVlcENvdW50ZXIgPSBib2R5LnNsZWVwVGhyZXNob2xkO1xuXG4gICAgICAgICAgICBib2R5LnBvc2l0aW9uSW1wdWxzZS54ID0gMDtcbiAgICAgICAgICAgIGJvZHkucG9zaXRpb25JbXB1bHNlLnkgPSAwO1xuXG4gICAgICAgICAgICBib2R5LnBvc2l0aW9uUHJldi54ID0gYm9keS5wb3NpdGlvbi54O1xuICAgICAgICAgICAgYm9keS5wb3NpdGlvblByZXYueSA9IGJvZHkucG9zaXRpb24ueTtcblxuICAgICAgICAgICAgYm9keS5hbmdsZVByZXYgPSBib2R5LmFuZ2xlO1xuICAgICAgICAgICAgYm9keS5zcGVlZCA9IDA7XG4gICAgICAgICAgICBib2R5LmFuZ3VsYXJTcGVlZCA9IDA7XG4gICAgICAgICAgICBib2R5Lm1vdGlvbiA9IDA7XG5cbiAgICAgICAgICAgIGlmICghd2FzU2xlZXBpbmcpIHtcbiAgICAgICAgICAgICAgICBFdmVudHMudHJpZ2dlcihib2R5LCAnc2xlZXBTdGFydCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYm9keS5pc1NsZWVwaW5nID0gZmFsc2U7XG4gICAgICAgICAgICBib2R5LnNsZWVwQ291bnRlciA9IDA7XG5cbiAgICAgICAgICAgIGlmICh3YXNTbGVlcGluZykge1xuICAgICAgICAgICAgICAgIEV2ZW50cy50cmlnZ2VyKGJvZHksICdzbGVlcEVuZCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxufSkoKTtcblxufSx7XCIuL0V2ZW50c1wiOjE2fV0sMjM6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuKGZ1bmN0aW9uIChnbG9iYWwpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLkJvZGllc2AgbW9kdWxlIGNvbnRhaW5zIGZhY3RvcnkgbWV0aG9kcyBmb3IgY3JlYXRpbmcgcmlnaWQgYm9keSBtb2RlbHMgXG4qIHdpdGggY29tbW9ubHkgdXNlZCBib2R5IGNvbmZpZ3VyYXRpb25zIChzdWNoIGFzIHJlY3RhbmdsZXMsIGNpcmNsZXMgYW5kIG90aGVyIHBvbHlnb25zKS5cbipcbiogU2VlIHRoZSBpbmNsdWRlZCB1c2FnZSBbZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9saWFicnUvbWF0dGVyLWpzL3RyZWUvbWFzdGVyL2V4YW1wbGVzKS5cbipcbiogQGNsYXNzIEJvZGllc1xuKi9cblxuLy8gVE9ETzogdHJ1ZSBjaXJjbGUgYm9kaWVzXG5cbnZhciBCb2RpZXMgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBCb2RpZXM7XG5cbnZhciBWZXJ0aWNlcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L1ZlcnRpY2VzJyk7XG52YXIgQ29tbW9uID0gX2RlcmVxXygnLi4vY29yZS9Db21tb24nKTtcbnZhciBCb2R5ID0gX2RlcmVxXygnLi4vYm9keS9Cb2R5Jyk7XG52YXIgQm91bmRzID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvQm91bmRzJyk7XG52YXIgVmVjdG9yID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvVmVjdG9yJyk7XG52YXIgZGVjb21wID0gKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgPyB3aW5kb3dbJ2RlY29tcCddIDogdHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIiA/IGdsb2JhbFsnZGVjb21wJ10gOiBudWxsKTtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyByaWdpZCBib2R5IG1vZGVsIHdpdGggYSByZWN0YW5nbGUgaHVsbC4gXG4gICAgICogVGhlIG9wdGlvbnMgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0IHNwZWNpZmllcyBhbnkgcHJvcGVydGllcyB5b3Ugd2lzaCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdHMuXG4gICAgICogU2VlIHRoZSBwcm9wZXJ0aWVzIHNlY3Rpb24gb2YgdGhlIGBNYXR0ZXIuQm9keWAgbW9kdWxlIGZvciBkZXRhaWxlZCBpbmZvcm1hdGlvbiBvbiB3aGF0IHlvdSBjYW4gcGFzcyB2aWEgdGhlIGBvcHRpb25zYCBvYmplY3QuXG4gICAgICogQG1ldGhvZCByZWN0YW5nbGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodFxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0aW9uc11cbiAgICAgKiBAcmV0dXJuIHtib2R5fSBBIG5ldyByZWN0YW5nbGUgYm9keVxuICAgICAqL1xuICAgIEJvZGllcy5yZWN0YW5nbGUgPSBmdW5jdGlvbih4LCB5LCB3aWR0aCwgaGVpZ2h0LCBvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgIHZhciByZWN0YW5nbGUgPSB7IFxuICAgICAgICAgICAgbGFiZWw6ICdSZWN0YW5nbGUgQm9keScsXG4gICAgICAgICAgICBwb3NpdGlvbjogeyB4OiB4LCB5OiB5IH0sXG4gICAgICAgICAgICB2ZXJ0aWNlczogVmVydGljZXMuZnJvbVBhdGgoJ0wgMCAwIEwgJyArIHdpZHRoICsgJyAwIEwgJyArIHdpZHRoICsgJyAnICsgaGVpZ2h0ICsgJyBMIDAgJyArIGhlaWdodClcbiAgICAgICAgfTtcblxuICAgICAgICBpZiAob3B0aW9ucy5jaGFtZmVyKSB7XG4gICAgICAgICAgICB2YXIgY2hhbWZlciA9IG9wdGlvbnMuY2hhbWZlcjtcbiAgICAgICAgICAgIHJlY3RhbmdsZS52ZXJ0aWNlcyA9IFZlcnRpY2VzLmNoYW1mZXIocmVjdGFuZ2xlLnZlcnRpY2VzLCBjaGFtZmVyLnJhZGl1cywgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGFtZmVyLnF1YWxpdHksIGNoYW1mZXIucXVhbGl0eU1pbiwgY2hhbWZlci5xdWFsaXR5TWF4KTtcbiAgICAgICAgICAgIGRlbGV0ZSBvcHRpb25zLmNoYW1mZXI7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gQm9keS5jcmVhdGUoQ29tbW9uLmV4dGVuZCh7fSwgcmVjdGFuZ2xlLCBvcHRpb25zKSk7XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHJpZ2lkIGJvZHkgbW9kZWwgd2l0aCBhIHRyYXBlem9pZCBodWxsLiBcbiAgICAgKiBUaGUgb3B0aW9ucyBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXQgc3BlY2lmaWVzIGFueSBwcm9wZXJ0aWVzIHlvdSB3aXNoIHRvIG92ZXJyaWRlIHRoZSBkZWZhdWx0cy5cbiAgICAgKiBTZWUgdGhlIHByb3BlcnRpZXMgc2VjdGlvbiBvZiB0aGUgYE1hdHRlci5Cb2R5YCBtb2R1bGUgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIG9uIHdoYXQgeW91IGNhbiBwYXNzIHZpYSB0aGUgYG9wdGlvbnNgIG9iamVjdC5cbiAgICAgKiBAbWV0aG9kIHRyYXBlem9pZFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gd2lkdGhcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHNsb3BlXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRpb25zXVxuICAgICAqIEByZXR1cm4ge2JvZHl9IEEgbmV3IHRyYXBlem9pZCBib2R5XG4gICAgICovXG4gICAgQm9kaWVzLnRyYXBlem9pZCA9IGZ1bmN0aW9uKHgsIHksIHdpZHRoLCBoZWlnaHQsIHNsb3BlLCBvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgIHNsb3BlICo9IDAuNTtcbiAgICAgICAgdmFyIHJvb2YgPSAoMSAtIChzbG9wZSAqIDIpKSAqIHdpZHRoO1xuICAgICAgICBcbiAgICAgICAgdmFyIHgxID0gd2lkdGggKiBzbG9wZSxcbiAgICAgICAgICAgIHgyID0geDEgKyByb29mLFxuICAgICAgICAgICAgeDMgPSB4MiArIHgxLFxuICAgICAgICAgICAgdmVydGljZXNQYXRoO1xuXG4gICAgICAgIGlmIChzbG9wZSA8IDAuNSkge1xuICAgICAgICAgICAgdmVydGljZXNQYXRoID0gJ0wgMCAwIEwgJyArIHgxICsgJyAnICsgKC1oZWlnaHQpICsgJyBMICcgKyB4MiArICcgJyArICgtaGVpZ2h0KSArICcgTCAnICsgeDMgKyAnIDAnO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdmVydGljZXNQYXRoID0gJ0wgMCAwIEwgJyArIHgyICsgJyAnICsgKC1oZWlnaHQpICsgJyBMICcgKyB4MyArICcgMCc7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgdHJhcGV6b2lkID0geyBcbiAgICAgICAgICAgIGxhYmVsOiAnVHJhcGV6b2lkIEJvZHknLFxuICAgICAgICAgICAgcG9zaXRpb246IHsgeDogeCwgeTogeSB9LFxuICAgICAgICAgICAgdmVydGljZXM6IFZlcnRpY2VzLmZyb21QYXRoKHZlcnRpY2VzUGF0aClcbiAgICAgICAgfTtcblxuICAgICAgICBpZiAob3B0aW9ucy5jaGFtZmVyKSB7XG4gICAgICAgICAgICB2YXIgY2hhbWZlciA9IG9wdGlvbnMuY2hhbWZlcjtcbiAgICAgICAgICAgIHRyYXBlem9pZC52ZXJ0aWNlcyA9IFZlcnRpY2VzLmNoYW1mZXIodHJhcGV6b2lkLnZlcnRpY2VzLCBjaGFtZmVyLnJhZGl1cywgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGFtZmVyLnF1YWxpdHksIGNoYW1mZXIucXVhbGl0eU1pbiwgY2hhbWZlci5xdWFsaXR5TWF4KTtcbiAgICAgICAgICAgIGRlbGV0ZSBvcHRpb25zLmNoYW1mZXI7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gQm9keS5jcmVhdGUoQ29tbW9uLmV4dGVuZCh7fSwgdHJhcGV6b2lkLCBvcHRpb25zKSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBuZXcgcmlnaWQgYm9keSBtb2RlbCB3aXRoIGEgY2lyY2xlIGh1bGwuIFxuICAgICAqIFRoZSBvcHRpb25zIHBhcmFtZXRlciBpcyBhbiBvYmplY3QgdGhhdCBzcGVjaWZpZXMgYW55IHByb3BlcnRpZXMgeW91IHdpc2ggdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHRzLlxuICAgICAqIFNlZSB0aGUgcHJvcGVydGllcyBzZWN0aW9uIG9mIHRoZSBgTWF0dGVyLkJvZHlgIG1vZHVsZSBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gd2hhdCB5b3UgY2FuIHBhc3MgdmlhIHRoZSBgb3B0aW9uc2Agb2JqZWN0LlxuICAgICAqIEBtZXRob2QgY2lyY2xlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHhcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSByYWRpdXNcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdGlvbnNdXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFttYXhTaWRlc11cbiAgICAgKiBAcmV0dXJuIHtib2R5fSBBIG5ldyBjaXJjbGUgYm9keVxuICAgICAqL1xuICAgIEJvZGllcy5jaXJjbGUgPSBmdW5jdGlvbih4LCB5LCByYWRpdXMsIG9wdGlvbnMsIG1heFNpZGVzKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgIHZhciBjaXJjbGUgPSB7XG4gICAgICAgICAgICBsYWJlbDogJ0NpcmNsZSBCb2R5JyxcbiAgICAgICAgICAgIGNpcmNsZVJhZGl1czogcmFkaXVzXG4gICAgICAgIH07XG4gICAgICAgIFxuICAgICAgICAvLyBhcHByb3hpbWF0ZSBjaXJjbGVzIHdpdGggcG9seWdvbnMgdW50aWwgdHJ1ZSBjaXJjbGVzIGltcGxlbWVudGVkIGluIFNBVFxuICAgICAgICBtYXhTaWRlcyA9IG1heFNpZGVzIHx8IDI1O1xuICAgICAgICB2YXIgc2lkZXMgPSBNYXRoLmNlaWwoTWF0aC5tYXgoMTAsIE1hdGgubWluKG1heFNpZGVzLCByYWRpdXMpKSk7XG5cbiAgICAgICAgLy8gb3B0aW1pc2F0aW9uOiBhbHdheXMgdXNlIGV2ZW4gbnVtYmVyIG9mIHNpZGVzIChoYWxmIHRoZSBudW1iZXIgb2YgdW5pcXVlIGF4ZXMpXG4gICAgICAgIGlmIChzaWRlcyAlIDIgPT09IDEpXG4gICAgICAgICAgICBzaWRlcyArPSAxO1xuXG4gICAgICAgIHJldHVybiBCb2RpZXMucG9seWdvbih4LCB5LCBzaWRlcywgcmFkaXVzLCBDb21tb24uZXh0ZW5kKHt9LCBjaXJjbGUsIG9wdGlvbnMpKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyByaWdpZCBib2R5IG1vZGVsIHdpdGggYSByZWd1bGFyIHBvbHlnb24gaHVsbCB3aXRoIHRoZSBnaXZlbiBudW1iZXIgb2Ygc2lkZXMuIFxuICAgICAqIFRoZSBvcHRpb25zIHBhcmFtZXRlciBpcyBhbiBvYmplY3QgdGhhdCBzcGVjaWZpZXMgYW55IHByb3BlcnRpZXMgeW91IHdpc2ggdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHRzLlxuICAgICAqIFNlZSB0aGUgcHJvcGVydGllcyBzZWN0aW9uIG9mIHRoZSBgTWF0dGVyLkJvZHlgIG1vZHVsZSBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gd2hhdCB5b3UgY2FuIHBhc3MgdmlhIHRoZSBgb3B0aW9uc2Agb2JqZWN0LlxuICAgICAqIEBtZXRob2QgcG9seWdvblxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2lkZXNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcmFkaXVzXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRpb25zXVxuICAgICAqIEByZXR1cm4ge2JvZHl9IEEgbmV3IHJlZ3VsYXIgcG9seWdvbiBib2R5XG4gICAgICovXG4gICAgQm9kaWVzLnBvbHlnb24gPSBmdW5jdGlvbih4LCB5LCBzaWRlcywgcmFkaXVzLCBvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgIGlmIChzaWRlcyA8IDMpXG4gICAgICAgICAgICByZXR1cm4gQm9kaWVzLmNpcmNsZSh4LCB5LCByYWRpdXMsIG9wdGlvbnMpO1xuXG4gICAgICAgIHZhciB0aGV0YSA9IDIgKiBNYXRoLlBJIC8gc2lkZXMsXG4gICAgICAgICAgICBwYXRoID0gJycsXG4gICAgICAgICAgICBvZmZzZXQgPSB0aGV0YSAqIDAuNTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNpZGVzOyBpICs9IDEpIHtcbiAgICAgICAgICAgIHZhciBhbmdsZSA9IG9mZnNldCArIChpICogdGhldGEpLFxuICAgICAgICAgICAgICAgIHh4ID0gTWF0aC5jb3MoYW5nbGUpICogcmFkaXVzLFxuICAgICAgICAgICAgICAgIHl5ID0gTWF0aC5zaW4oYW5nbGUpICogcmFkaXVzO1xuXG4gICAgICAgICAgICBwYXRoICs9ICdMICcgKyB4eC50b0ZpeGVkKDMpICsgJyAnICsgeXkudG9GaXhlZCgzKSArICcgJztcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBwb2x5Z29uID0geyBcbiAgICAgICAgICAgIGxhYmVsOiAnUG9seWdvbiBCb2R5JyxcbiAgICAgICAgICAgIHBvc2l0aW9uOiB7IHg6IHgsIHk6IHkgfSxcbiAgICAgICAgICAgIHZlcnRpY2VzOiBWZXJ0aWNlcy5mcm9tUGF0aChwYXRoKVxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChvcHRpb25zLmNoYW1mZXIpIHtcbiAgICAgICAgICAgIHZhciBjaGFtZmVyID0gb3B0aW9ucy5jaGFtZmVyO1xuICAgICAgICAgICAgcG9seWdvbi52ZXJ0aWNlcyA9IFZlcnRpY2VzLmNoYW1mZXIocG9seWdvbi52ZXJ0aWNlcywgY2hhbWZlci5yYWRpdXMsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hhbWZlci5xdWFsaXR5LCBjaGFtZmVyLnF1YWxpdHlNaW4sIGNoYW1mZXIucXVhbGl0eU1heCk7XG4gICAgICAgICAgICBkZWxldGUgb3B0aW9ucy5jaGFtZmVyO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIEJvZHkuY3JlYXRlKENvbW1vbi5leHRlbmQoe30sIHBvbHlnb24sIG9wdGlvbnMpKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIGJvZHkgdXNpbmcgdGhlIHN1cHBsaWVkIHZlcnRpY2VzIChvciBhbiBhcnJheSBjb250YWluaW5nIG11bHRpcGxlIHNldHMgb2YgdmVydGljZXMpLlxuICAgICAqIElmIHRoZSB2ZXJ0aWNlcyBhcmUgY29udmV4LCB0aGV5IHdpbGwgcGFzcyB0aHJvdWdoIGFzIHN1cHBsaWVkLlxuICAgICAqIE90aGVyd2lzZSBpZiB0aGUgdmVydGljZXMgYXJlIGNvbmNhdmUsIHRoZXkgd2lsbCBiZSBkZWNvbXBvc2VkIGlmIFtwb2x5LWRlY29tcC5qc10oaHR0cHM6Ly9naXRodWIuY29tL3NjaHRlcHBlL3BvbHktZGVjb21wLmpzKSBpcyBhdmFpbGFibGUuXG4gICAgICogTm90ZSB0aGF0IHRoaXMgcHJvY2VzcyBpcyBub3QgZ3VhcmFudGVlZCB0byBzdXBwb3J0IGNvbXBsZXggc2V0cyBvZiB2ZXJ0aWNlcyAoZS5nLiB0aG9zZSB3aXRoIGhvbGVzIG1heSBmYWlsKS5cbiAgICAgKiBCeSBkZWZhdWx0IHRoZSBkZWNvbXBvc2l0aW9uIHdpbGwgZGlzY2FyZCBjb2xsaW5lYXIgZWRnZXMgKHRvIGltcHJvdmUgcGVyZm9ybWFuY2UpLlxuICAgICAqIEl0IGNhbiBhbHNvIG9wdGlvbmFsbHkgZGlzY2FyZCBhbnkgcGFydHMgdGhhdCBoYXZlIGFuIGFyZWEgbGVzcyB0aGFuIGBtaW5pbXVtQXJlYWAuXG4gICAgICogSWYgdGhlIHZlcnRpY2VzIGNhbiBub3QgYmUgZGVjb21wb3NlZCwgdGhlIHJlc3VsdCB3aWxsIGZhbGwgYmFjayB0byB1c2luZyB0aGUgY29udmV4IGh1bGwuXG4gICAgICogVGhlIG9wdGlvbnMgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0IHNwZWNpZmllcyBhbnkgYE1hdHRlci5Cb2R5YCBwcm9wZXJ0aWVzIHlvdSB3aXNoIHRvIG92ZXJyaWRlIHRoZSBkZWZhdWx0cy5cbiAgICAgKiBTZWUgdGhlIHByb3BlcnRpZXMgc2VjdGlvbiBvZiB0aGUgYE1hdHRlci5Cb2R5YCBtb2R1bGUgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIG9uIHdoYXQgeW91IGNhbiBwYXNzIHZpYSB0aGUgYG9wdGlvbnNgIG9iamVjdC5cbiAgICAgKiBAbWV0aG9kIGZyb21WZXJ0aWNlc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHlcbiAgICAgKiBAcGFyYW0gW1t2ZWN0b3JdXSB2ZXJ0ZXhTZXRzXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRpb25zXVxuICAgICAqIEBwYXJhbSB7Ym9vbH0gW2ZsYWdJbnRlcm5hbD1mYWxzZV1cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3JlbW92ZUNvbGxpbmVhcj0wLjAxXVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbbWluaW11bUFyZWE9MTBdXG4gICAgICogQHJldHVybiB7Ym9keX1cbiAgICAgKi9cbiAgICBCb2RpZXMuZnJvbVZlcnRpY2VzID0gZnVuY3Rpb24oeCwgeSwgdmVydGV4U2V0cywgb3B0aW9ucywgZmxhZ0ludGVybmFsLCByZW1vdmVDb2xsaW5lYXIsIG1pbmltdW1BcmVhKSB7XG4gICAgICAgIHZhciBib2R5LFxuICAgICAgICAgICAgcGFydHMsXG4gICAgICAgICAgICBpc0NvbnZleCxcbiAgICAgICAgICAgIHZlcnRpY2VzLFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGosXG4gICAgICAgICAgICBrLFxuICAgICAgICAgICAgdixcbiAgICAgICAgICAgIHo7XG5cbiAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgIHBhcnRzID0gW107XG5cbiAgICAgICAgZmxhZ0ludGVybmFsID0gdHlwZW9mIGZsYWdJbnRlcm5hbCAhPT0gJ3VuZGVmaW5lZCcgPyBmbGFnSW50ZXJuYWwgOiBmYWxzZTtcbiAgICAgICAgcmVtb3ZlQ29sbGluZWFyID0gdHlwZW9mIHJlbW92ZUNvbGxpbmVhciAhPT0gJ3VuZGVmaW5lZCcgPyByZW1vdmVDb2xsaW5lYXIgOiAwLjAxO1xuICAgICAgICBtaW5pbXVtQXJlYSA9IHR5cGVvZiBtaW5pbXVtQXJlYSAhPT0gJ3VuZGVmaW5lZCcgPyBtaW5pbXVtQXJlYSA6IDEwO1xuXG4gICAgICAgIGlmICghZGVjb21wKSB7XG4gICAgICAgICAgICBDb21tb24ud2FybignQm9kaWVzLmZyb21WZXJ0aWNlczogcG9seS1kZWNvbXAuanMgcmVxdWlyZWQuIENvdWxkIG5vdCBkZWNvbXBvc2UgdmVydGljZXMuIEZhbGxiYWNrIHRvIGNvbnZleCBodWxsLicpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gZW5zdXJlIHZlcnRleFNldHMgaXMgYW4gYXJyYXkgb2YgYXJyYXlzXG4gICAgICAgIGlmICghQ29tbW9uLmlzQXJyYXkodmVydGV4U2V0c1swXSkpIHtcbiAgICAgICAgICAgIHZlcnRleFNldHMgPSBbdmVydGV4U2V0c107XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKHYgPSAwOyB2IDwgdmVydGV4U2V0cy5sZW5ndGg7IHYgKz0gMSkge1xuICAgICAgICAgICAgdmVydGljZXMgPSB2ZXJ0ZXhTZXRzW3ZdO1xuICAgICAgICAgICAgaXNDb252ZXggPSBWZXJ0aWNlcy5pc0NvbnZleCh2ZXJ0aWNlcyk7XG5cbiAgICAgICAgICAgIGlmIChpc0NvbnZleCB8fCAhZGVjb21wKSB7XG4gICAgICAgICAgICAgICAgaWYgKGlzQ29udmV4KSB7XG4gICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzID0gVmVydGljZXMuY2xvY2t3aXNlU29ydCh2ZXJ0aWNlcyk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gZmFsbGJhY2sgdG8gY29udmV4IGh1bGwgd2hlbiBkZWNvbXBvc2l0aW9uIGlzIG5vdCBwb3NzaWJsZVxuICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlcyA9IFZlcnRpY2VzLmh1bGwodmVydGljZXMpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHBhcnRzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbjogeyB4OiB4LCB5OiB5IH0sXG4gICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzOiB2ZXJ0aWNlc1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBpbml0aWFsaXNlIGEgZGVjb21wb3NpdGlvblxuICAgICAgICAgICAgICAgIHZhciBjb25jYXZlID0gdmVydGljZXMubWFwKGZ1bmN0aW9uKHZlcnRleCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gW3ZlcnRleC54LCB2ZXJ0ZXgueV07XG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAvLyB2ZXJ0aWNlcyBhcmUgY29uY2F2ZSBhbmQgc2ltcGxlLCB3ZSBjYW4gZGVjb21wb3NlIGludG8gcGFydHNcbiAgICAgICAgICAgICAgICBkZWNvbXAubWFrZUNDVyhjb25jYXZlKTtcbiAgICAgICAgICAgICAgICBpZiAocmVtb3ZlQ29sbGluZWFyICE9PSBmYWxzZSlcbiAgICAgICAgICAgICAgICAgICAgZGVjb21wLnJlbW92ZUNvbGxpbmVhclBvaW50cyhjb25jYXZlLCByZW1vdmVDb2xsaW5lYXIpO1xuXG4gICAgICAgICAgICAgICAgLy8gdXNlIHRoZSBxdWljayBkZWNvbXBvc2l0aW9uIGFsZ29yaXRobSAoQmF5YXppdClcbiAgICAgICAgICAgICAgICB2YXIgZGVjb21wb3NlZCA9IGRlY29tcC5xdWlja0RlY29tcChjb25jYXZlKTtcblxuICAgICAgICAgICAgICAgIC8vIGZvciBlYWNoIGRlY29tcG9zZWQgY2h1bmtcbiAgICAgICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgZGVjb21wb3NlZC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgY2h1bmsgPSBkZWNvbXBvc2VkW2ldO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIGNvbnZlcnQgdmVydGljZXMgaW50byB0aGUgY29ycmVjdCBzdHJ1Y3R1cmVcbiAgICAgICAgICAgICAgICAgICAgdmFyIGNodW5rVmVydGljZXMgPSBjaHVuay5tYXAoZnVuY3Rpb24odmVydGljZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgeDogdmVydGljZXNbMF0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgeTogdmVydGljZXNbMV1cbiAgICAgICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIHNraXAgc21hbGwgY2h1bmtzXG4gICAgICAgICAgICAgICAgICAgIGlmIChtaW5pbXVtQXJlYSA+IDAgJiYgVmVydGljZXMuYXJlYShjaHVua1ZlcnRpY2VzKSA8IG1pbmltdW1BcmVhKVxuICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gY3JlYXRlIGEgY29tcG91bmQgcGFydFxuICAgICAgICAgICAgICAgICAgICBwYXJ0cy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBWZXJ0aWNlcy5jZW50cmUoY2h1bmtWZXJ0aWNlcyksXG4gICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlczogY2h1bmtWZXJ0aWNlc1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBjcmVhdGUgYm9keSBwYXJ0c1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHBhcnRzW2ldID0gQm9keS5jcmVhdGUoQ29tbW9uLmV4dGVuZChwYXJ0c1tpXSwgb3B0aW9ucykpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gZmxhZyBpbnRlcm5hbCBlZGdlcyAoY29pbmNpZGVudCBwYXJ0IGVkZ2VzKVxuICAgICAgICBpZiAoZmxhZ0ludGVybmFsKSB7XG4gICAgICAgICAgICB2YXIgY29pbmNpZGVudF9tYXhfZGlzdCA9IDU7XG5cbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBwYXJ0QSA9IHBhcnRzW2ldO1xuXG4gICAgICAgICAgICAgICAgZm9yIChqID0gaSArIDE7IGogPCBwYXJ0cy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgcGFydEIgPSBwYXJ0c1tqXTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoQm91bmRzLm92ZXJsYXBzKHBhcnRBLmJvdW5kcywgcGFydEIuYm91bmRzKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHBhdiA9IHBhcnRBLnZlcnRpY2VzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBidiA9IHBhcnRCLnZlcnRpY2VzO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpdGVyYXRlIHZlcnRpY2VzIG9mIGJvdGggcGFydHNcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCBwYXJ0QS52ZXJ0aWNlcy5sZW5ndGg7IGsrKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoeiA9IDA7IHogPCBwYXJ0Qi52ZXJ0aWNlcy5sZW5ndGg7IHorKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBmaW5kIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZSB2ZXJ0aWNlc1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgZGEgPSBWZWN0b3IubWFnbml0dWRlU3F1YXJlZChWZWN0b3Iuc3ViKHBhdlsoayArIDEpICUgcGF2Lmxlbmd0aF0sIHBidlt6XSkpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGIgPSBWZWN0b3IubWFnbml0dWRlU3F1YXJlZChWZWN0b3Iuc3ViKHBhdltrXSwgcGJ2Wyh6ICsgMSkgJSBwYnYubGVuZ3RoXSkpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlmIGJvdGggdmVydGljZXMgYXJlIHZlcnkgY2xvc2UsIGNvbnNpZGVyIHRoZSBlZGdlIGNvbmNpZGVudCAoaW50ZXJuYWwpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkYSA8IGNvaW5jaWRlbnRfbWF4X2Rpc3QgJiYgZGIgPCBjb2luY2lkZW50X21heF9kaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXZba10uaXNJbnRlcm5hbCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYnZbel0uaXNJbnRlcm5hbCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChwYXJ0cy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAvLyBjcmVhdGUgdGhlIHBhcmVudCBib2R5IHRvIGJlIHJldHVybmVkLCB0aGF0IGNvbnRhaW5zIGdlbmVyYXRlZCBjb21wb3VuZCBwYXJ0c1xuICAgICAgICAgICAgYm9keSA9IEJvZHkuY3JlYXRlKENvbW1vbi5leHRlbmQoeyBwYXJ0czogcGFydHMuc2xpY2UoMCkgfSwgb3B0aW9ucykpO1xuICAgICAgICAgICAgQm9keS5zZXRQb3NpdGlvbihib2R5LCB7IHg6IHgsIHk6IHkgfSk7XG5cbiAgICAgICAgICAgIHJldHVybiBib2R5O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHBhcnRzWzBdO1xuICAgICAgICB9XG4gICAgfTtcblxufSkoKTtcblxufSkuY2FsbCh0aGlzLHR5cGVvZiBnbG9iYWwgIT09IFwidW5kZWZpbmVkXCIgPyBnbG9iYWwgOiB0eXBlb2Ygc2VsZiAhPT0gXCJ1bmRlZmluZWRcIiA/IHNlbGYgOiB0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiID8gd2luZG93IDoge30pXG59LHtcIi4uL2JvZHkvQm9keVwiOjEsXCIuLi9jb3JlL0NvbW1vblwiOjE0LFwiLi4vZ2VvbWV0cnkvQm91bmRzXCI6MjYsXCIuLi9nZW9tZXRyeS9WZWN0b3JcIjoyOCxcIi4uL2dlb21ldHJ5L1ZlcnRpY2VzXCI6Mjl9XSwyNDpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuQ29tcG9zaXRlc2AgbW9kdWxlIGNvbnRhaW5zIGZhY3RvcnkgbWV0aG9kcyBmb3IgY3JlYXRpbmcgY29tcG9zaXRlIGJvZGllc1xuKiB3aXRoIGNvbW1vbmx5IHVzZWQgY29uZmlndXJhdGlvbnMgKHN1Y2ggYXMgc3RhY2tzIGFuZCBjaGFpbnMpLlxuKlxuKiBTZWUgdGhlIGluY2x1ZGVkIHVzYWdlIFtleGFtcGxlc10oaHR0cHM6Ly9naXRodWIuY29tL2xpYWJydS9tYXR0ZXItanMvdHJlZS9tYXN0ZXIvZXhhbXBsZXMpLlxuKlxuKiBAY2xhc3MgQ29tcG9zaXRlc1xuKi9cblxudmFyIENvbXBvc2l0ZXMgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb21wb3NpdGVzO1xuXG52YXIgQ29tcG9zaXRlID0gX2RlcmVxXygnLi4vYm9keS9Db21wb3NpdGUnKTtcbnZhciBDb25zdHJhaW50ID0gX2RlcmVxXygnLi4vY29uc3RyYWludC9Db25zdHJhaW50Jyk7XG52YXIgQ29tbW9uID0gX2RlcmVxXygnLi4vY29yZS9Db21tb24nKTtcbnZhciBCb2R5ID0gX2RlcmVxXygnLi4vYm9keS9Cb2R5Jyk7XG52YXIgQm9kaWVzID0gX2RlcmVxXygnLi9Cb2RpZXMnKTtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGEgbmV3IGNvbXBvc2l0ZSBjb250YWluaW5nIGJvZGllcyBjcmVhdGVkIGluIHRoZSBjYWxsYmFjayBpbiBhIGdyaWQgYXJyYW5nZW1lbnQuXG4gICAgICogVGhpcyBmdW5jdGlvbiB1c2VzIHRoZSBib2R5J3MgYm91bmRzIHRvIHByZXZlbnQgb3ZlcmxhcHMuXG4gICAgICogQG1ldGhvZCBzdGFja1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4eFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5eVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBjb2x1bW5zXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHJvd3NcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gY29sdW1uR2FwXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHJvd0dhcFxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBBIG5ldyBjb21wb3NpdGUgY29udGFpbmluZyBvYmplY3RzIGNyZWF0ZWQgaW4gdGhlIGNhbGxiYWNrXG4gICAgICovXG4gICAgQ29tcG9zaXRlcy5zdGFjayA9IGZ1bmN0aW9uKHh4LCB5eSwgY29sdW1ucywgcm93cywgY29sdW1uR2FwLCByb3dHYXAsIGNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBzdGFjayA9IENvbXBvc2l0ZS5jcmVhdGUoeyBsYWJlbDogJ1N0YWNrJyB9KSxcbiAgICAgICAgICAgIHggPSB4eCxcbiAgICAgICAgICAgIHkgPSB5eSxcbiAgICAgICAgICAgIGxhc3RCb2R5LFxuICAgICAgICAgICAgaSA9IDA7XG5cbiAgICAgICAgZm9yICh2YXIgcm93ID0gMDsgcm93IDwgcm93czsgcm93KyspIHtcbiAgICAgICAgICAgIHZhciBtYXhIZWlnaHQgPSAwO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBmb3IgKHZhciBjb2x1bW4gPSAwOyBjb2x1bW4gPCBjb2x1bW5zOyBjb2x1bW4rKykge1xuICAgICAgICAgICAgICAgIHZhciBib2R5ID0gY2FsbGJhY2soeCwgeSwgY29sdW1uLCByb3csIGxhc3RCb2R5LCBpKTtcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgaWYgKGJvZHkpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGJvZHlIZWlnaHQgPSBib2R5LmJvdW5kcy5tYXgueSAtIGJvZHkuYm91bmRzLm1pbi55LFxuICAgICAgICAgICAgICAgICAgICAgICAgYm9keVdpZHRoID0gYm9keS5ib3VuZHMubWF4LnggLSBib2R5LmJvdW5kcy5taW4ueDsgXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGJvZHlIZWlnaHQgPiBtYXhIZWlnaHQpXG4gICAgICAgICAgICAgICAgICAgICAgICBtYXhIZWlnaHQgPSBib2R5SGVpZ2h0O1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgQm9keS50cmFuc2xhdGUoYm9keSwgeyB4OiBib2R5V2lkdGggKiAwLjUsIHk6IGJvZHlIZWlnaHQgKiAwLjUgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgeCA9IGJvZHkuYm91bmRzLm1heC54ICsgY29sdW1uR2FwO1xuXG4gICAgICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5hZGRCb2R5KHN0YWNrLCBib2R5KTtcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgIGxhc3RCb2R5ID0gYm9keTtcbiAgICAgICAgICAgICAgICAgICAgaSArPSAxO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHggKz0gY29sdW1uR2FwO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgeSArPSBtYXhIZWlnaHQgKyByb3dHYXA7XG4gICAgICAgICAgICB4ID0geHg7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gc3RhY2s7XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBDaGFpbnMgYWxsIGJvZGllcyBpbiB0aGUgZ2l2ZW4gY29tcG9zaXRlIHRvZ2V0aGVyIHVzaW5nIGNvbnN0cmFpbnRzLlxuICAgICAqIEBtZXRob2QgY2hhaW5cbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHhPZmZzZXRBXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHlPZmZzZXRBXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHhPZmZzZXRCXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHlPZmZzZXRCXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnNcbiAgICAgKiBAcmV0dXJuIHtjb21wb3NpdGV9IEEgbmV3IGNvbXBvc2l0ZSBjb250YWluaW5nIG9iamVjdHMgY2hhaW5lZCB0b2dldGhlciB3aXRoIGNvbnN0cmFpbnRzXG4gICAgICovXG4gICAgQ29tcG9zaXRlcy5jaGFpbiA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgeE9mZnNldEEsIHlPZmZzZXRBLCB4T2Zmc2V0QiwgeU9mZnNldEIsIG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIGJvZGllcyA9IGNvbXBvc2l0ZS5ib2RpZXM7XG4gICAgICAgIFxuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGJvZHlBID0gYm9kaWVzW2kgLSAxXSxcbiAgICAgICAgICAgICAgICBib2R5QiA9IGJvZGllc1tpXSxcbiAgICAgICAgICAgICAgICBib2R5QUhlaWdodCA9IGJvZHlBLmJvdW5kcy5tYXgueSAtIGJvZHlBLmJvdW5kcy5taW4ueSxcbiAgICAgICAgICAgICAgICBib2R5QVdpZHRoID0gYm9keUEuYm91bmRzLm1heC54IC0gYm9keUEuYm91bmRzLm1pbi54LCBcbiAgICAgICAgICAgICAgICBib2R5QkhlaWdodCA9IGJvZHlCLmJvdW5kcy5tYXgueSAtIGJvZHlCLmJvdW5kcy5taW4ueSxcbiAgICAgICAgICAgICAgICBib2R5QldpZHRoID0gYm9keUIuYm91bmRzLm1heC54IC0gYm9keUIuYm91bmRzLm1pbi54O1xuICAgICAgICBcbiAgICAgICAgICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgICAgICAgICBib2R5QTogYm9keUEsXG4gICAgICAgICAgICAgICAgcG9pbnRBOiB7IHg6IGJvZHlBV2lkdGggKiB4T2Zmc2V0QSwgeTogYm9keUFIZWlnaHQgKiB5T2Zmc2V0QSB9LFxuICAgICAgICAgICAgICAgIGJvZHlCOiBib2R5QixcbiAgICAgICAgICAgICAgICBwb2ludEI6IHsgeDogYm9keUJXaWR0aCAqIHhPZmZzZXRCLCB5OiBib2R5QkhlaWdodCAqIHlPZmZzZXRCIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHZhciBjb25zdHJhaW50ID0gQ29tbW9uLmV4dGVuZChkZWZhdWx0cywgb3B0aW9ucyk7XG4gICAgICAgIFxuICAgICAgICAgICAgQ29tcG9zaXRlLmFkZENvbnN0cmFpbnQoY29tcG9zaXRlLCBDb25zdHJhaW50LmNyZWF0ZShjb25zdHJhaW50KSk7XG4gICAgICAgIH1cblxuICAgICAgICBjb21wb3NpdGUubGFiZWwgKz0gJyBDaGFpbic7XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gY29tcG9zaXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDb25uZWN0cyBib2RpZXMgaW4gdGhlIGNvbXBvc2l0ZSB3aXRoIGNvbnN0cmFpbnRzIGluIGEgZ3JpZCBwYXR0ZXJuLCB3aXRoIG9wdGlvbmFsIGNyb3NzIGJyYWNlcy5cbiAgICAgKiBAbWV0aG9kIG1lc2hcbiAgICAgKiBAcGFyYW0ge2NvbXBvc2l0ZX0gY29tcG9zaXRlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNvbHVtbnNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcm93c1xuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gY3Jvc3NCcmFjZVxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBUaGUgY29tcG9zaXRlIGNvbnRhaW5pbmcgb2JqZWN0cyBtZXNoZWQgdG9nZXRoZXIgd2l0aCBjb25zdHJhaW50c1xuICAgICAqL1xuICAgIENvbXBvc2l0ZXMubWVzaCA9IGZ1bmN0aW9uKGNvbXBvc2l0ZSwgY29sdW1ucywgcm93cywgY3Jvc3NCcmFjZSwgb3B0aW9ucykge1xuICAgICAgICB2YXIgYm9kaWVzID0gY29tcG9zaXRlLmJvZGllcyxcbiAgICAgICAgICAgIHJvdyxcbiAgICAgICAgICAgIGNvbCxcbiAgICAgICAgICAgIGJvZHlBLFxuICAgICAgICAgICAgYm9keUIsXG4gICAgICAgICAgICBib2R5QztcbiAgICAgICAgXG4gICAgICAgIGZvciAocm93ID0gMDsgcm93IDwgcm93czsgcm93KyspIHtcbiAgICAgICAgICAgIGZvciAoY29sID0gMTsgY29sIDwgY29sdW1uczsgY29sKyspIHtcbiAgICAgICAgICAgICAgICBib2R5QSA9IGJvZGllc1soY29sIC0gMSkgKyAocm93ICogY29sdW1ucyldO1xuICAgICAgICAgICAgICAgIGJvZHlCID0gYm9kaWVzW2NvbCArIChyb3cgKiBjb2x1bW5zKV07XG4gICAgICAgICAgICAgICAgQ29tcG9zaXRlLmFkZENvbnN0cmFpbnQoY29tcG9zaXRlLCBDb25zdHJhaW50LmNyZWF0ZShDb21tb24uZXh0ZW5kKHsgYm9keUE6IGJvZHlBLCBib2R5QjogYm9keUIgfSwgb3B0aW9ucykpKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHJvdyA+IDApIHtcbiAgICAgICAgICAgICAgICBmb3IgKGNvbCA9IDA7IGNvbCA8IGNvbHVtbnM7IGNvbCsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGJvZHlBID0gYm9kaWVzW2NvbCArICgocm93IC0gMSkgKiBjb2x1bW5zKV07XG4gICAgICAgICAgICAgICAgICAgIGJvZHlCID0gYm9kaWVzW2NvbCArIChyb3cgKiBjb2x1bW5zKV07XG4gICAgICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5hZGRDb25zdHJhaW50KGNvbXBvc2l0ZSwgQ29uc3RyYWludC5jcmVhdGUoQ29tbW9uLmV4dGVuZCh7IGJvZHlBOiBib2R5QSwgYm9keUI6IGJvZHlCIH0sIG9wdGlvbnMpKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGNyb3NzQnJhY2UgJiYgY29sID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYm9keUMgPSBib2RpZXNbKGNvbCAtIDEpICsgKChyb3cgLSAxKSAqIGNvbHVtbnMpXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5hZGRDb25zdHJhaW50KGNvbXBvc2l0ZSwgQ29uc3RyYWludC5jcmVhdGUoQ29tbW9uLmV4dGVuZCh7IGJvZHlBOiBib2R5QywgYm9keUI6IGJvZHlCIH0sIG9wdGlvbnMpKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoY3Jvc3NCcmFjZSAmJiBjb2wgPCBjb2x1bW5zIC0gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYm9keUMgPSBib2RpZXNbKGNvbCArIDEpICsgKChyb3cgLSAxKSAqIGNvbHVtbnMpXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIENvbXBvc2l0ZS5hZGRDb25zdHJhaW50KGNvbXBvc2l0ZSwgQ29uc3RyYWludC5jcmVhdGUoQ29tbW9uLmV4dGVuZCh7IGJvZHlBOiBib2R5QywgYm9keUI6IGJvZHlCIH0sIG9wdGlvbnMpKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb21wb3NpdGUubGFiZWwgKz0gJyBNZXNoJztcbiAgICAgICAgXG4gICAgICAgIHJldHVybiBjb21wb3NpdGU7XG4gICAgfTtcbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBuZXcgY29tcG9zaXRlIGNvbnRhaW5pbmcgYm9kaWVzIGNyZWF0ZWQgaW4gdGhlIGNhbGxiYWNrIGluIGEgcHlyYW1pZCBhcnJhbmdlbWVudC5cbiAgICAgKiBUaGlzIGZ1bmN0aW9uIHVzZXMgdGhlIGJvZHkncyBib3VuZHMgdG8gcHJldmVudCBvdmVybGFwcy5cbiAgICAgKiBAbWV0aG9kIHB5cmFtaWRcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geHhcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geXlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gY29sdW1uc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSByb3dzXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNvbHVtbkdhcFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSByb3dHYXBcbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBjYWxsYmFja1xuICAgICAqIEByZXR1cm4ge2NvbXBvc2l0ZX0gQSBuZXcgY29tcG9zaXRlIGNvbnRhaW5pbmcgb2JqZWN0cyBjcmVhdGVkIGluIHRoZSBjYWxsYmFja1xuICAgICAqL1xuICAgIENvbXBvc2l0ZXMucHlyYW1pZCA9IGZ1bmN0aW9uKHh4LCB5eSwgY29sdW1ucywgcm93cywgY29sdW1uR2FwLCByb3dHYXAsIGNhbGxiYWNrKSB7XG4gICAgICAgIHJldHVybiBDb21wb3NpdGVzLnN0YWNrKHh4LCB5eSwgY29sdW1ucywgcm93cywgY29sdW1uR2FwLCByb3dHYXAsIGZ1bmN0aW9uKHgsIHksIGNvbHVtbiwgcm93LCBsYXN0Qm9keSwgaSkge1xuICAgICAgICAgICAgdmFyIGFjdHVhbFJvd3MgPSBNYXRoLm1pbihyb3dzLCBNYXRoLmNlaWwoY29sdW1ucyAvIDIpKSxcbiAgICAgICAgICAgICAgICBsYXN0Qm9keVdpZHRoID0gbGFzdEJvZHkgPyBsYXN0Qm9keS5ib3VuZHMubWF4LnggLSBsYXN0Qm9keS5ib3VuZHMubWluLnggOiAwO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAocm93ID4gYWN0dWFsUm93cylcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIHJldmVyc2Ugcm93IG9yZGVyXG4gICAgICAgICAgICByb3cgPSBhY3R1YWxSb3dzIC0gcm93O1xuICAgICAgICAgICAgXG4gICAgICAgICAgICB2YXIgc3RhcnQgPSByb3csXG4gICAgICAgICAgICAgICAgZW5kID0gY29sdW1ucyAtIDEgLSByb3c7XG5cbiAgICAgICAgICAgIGlmIChjb2x1bW4gPCBzdGFydCB8fCBjb2x1bW4gPiBlbmQpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyByZXRyb2FjdGl2ZWx5IGZpeCB0aGUgZmlyc3QgYm9keSdzIHBvc2l0aW9uLCBzaW5jZSB3aWR0aCB3YXMgdW5rbm93blxuICAgICAgICAgICAgaWYgKGkgPT09IDEpIHtcbiAgICAgICAgICAgICAgICBCb2R5LnRyYW5zbGF0ZShsYXN0Qm9keSwgeyB4OiAoY29sdW1uICsgKGNvbHVtbnMgJSAyID09PSAxID8gMSA6IC0xKSkgKiBsYXN0Qm9keVdpZHRoLCB5OiAwIH0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgeE9mZnNldCA9IGxhc3RCb2R5ID8gY29sdW1uICogbGFzdEJvZHlXaWR0aCA6IDA7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayh4eCArIHhPZmZzZXQgKyBjb2x1bW4gKiBjb2x1bW5HYXAsIHksIGNvbHVtbiwgcm93LCBsYXN0Qm9keSwgaSk7XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgY29tcG9zaXRlIHdpdGggYSBOZXd0b24ncyBDcmFkbGUgc2V0dXAgb2YgYm9kaWVzIGFuZCBjb25zdHJhaW50cy5cbiAgICAgKiBAbWV0aG9kIG5ld3RvbnNDcmFkbGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geHhcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geXlcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbnVtYmVyXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHNpemVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbGVuZ3RoXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBBIG5ldyBjb21wb3NpdGUgbmV3dG9uc0NyYWRsZSBib2R5XG4gICAgICovXG4gICAgQ29tcG9zaXRlcy5uZXd0b25zQ3JhZGxlID0gZnVuY3Rpb24oeHgsIHl5LCBudW1iZXIsIHNpemUsIGxlbmd0aCkge1xuICAgICAgICB2YXIgbmV3dG9uc0NyYWRsZSA9IENvbXBvc2l0ZS5jcmVhdGUoeyBsYWJlbDogJ05ld3RvbnMgQ3JhZGxlJyB9KTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG51bWJlcjsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgc2VwYXJhdGlvbiA9IDEuOSxcbiAgICAgICAgICAgICAgICBjaXJjbGUgPSBCb2RpZXMuY2lyY2xlKHh4ICsgaSAqIChzaXplICogc2VwYXJhdGlvbiksIHl5ICsgbGVuZ3RoLCBzaXplLCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7IGluZXJ0aWE6IEluZmluaXR5LCByZXN0aXR1dGlvbjogMSwgZnJpY3Rpb246IDAsIGZyaWN0aW9uQWlyOiAwLjAwMDEsIHNsb3A6IDEgfSksXG4gICAgICAgICAgICAgICAgY29uc3RyYWludCA9IENvbnN0cmFpbnQuY3JlYXRlKHsgcG9pbnRBOiB7IHg6IHh4ICsgaSAqIChzaXplICogc2VwYXJhdGlvbiksIHk6IHl5IH0sIGJvZHlCOiBjaXJjbGUgfSk7XG5cbiAgICAgICAgICAgIENvbXBvc2l0ZS5hZGRCb2R5KG5ld3RvbnNDcmFkbGUsIGNpcmNsZSk7XG4gICAgICAgICAgICBDb21wb3NpdGUuYWRkQ29uc3RyYWludChuZXd0b25zQ3JhZGxlLCBjb25zdHJhaW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBuZXd0b25zQ3JhZGxlO1xuICAgIH07XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIGNvbXBvc2l0ZSB3aXRoIHNpbXBsZSBjYXIgc2V0dXAgb2YgYm9kaWVzIGFuZCBjb25zdHJhaW50cy5cbiAgICAgKiBAbWV0aG9kIGNhclxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4eFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5eVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHRcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gd2hlZWxTaXplXG4gICAgICogQHJldHVybiB7Y29tcG9zaXRlfSBBIG5ldyBjb21wb3NpdGUgY2FyIGJvZHlcbiAgICAgKi9cbiAgICBDb21wb3NpdGVzLmNhciA9IGZ1bmN0aW9uKHh4LCB5eSwgd2lkdGgsIGhlaWdodCwgd2hlZWxTaXplKSB7XG4gICAgICAgIHZhciBncm91cCA9IEJvZHkubmV4dEdyb3VwKHRydWUpLFxuICAgICAgICAgICAgd2hlZWxCYXNlID0gMjAsXG4gICAgICAgICAgICB3aGVlbEFPZmZzZXQgPSAtd2lkdGggKiAwLjUgKyB3aGVlbEJhc2UsXG4gICAgICAgICAgICB3aGVlbEJPZmZzZXQgPSB3aWR0aCAqIDAuNSAtIHdoZWVsQmFzZSxcbiAgICAgICAgICAgIHdoZWVsWU9mZnNldCA9IDA7XG4gICAgXG4gICAgICAgIHZhciBjYXIgPSBDb21wb3NpdGUuY3JlYXRlKHsgbGFiZWw6ICdDYXInIH0pLFxuICAgICAgICAgICAgYm9keSA9IEJvZGllcy5yZWN0YW5nbGUoeHgsIHl5LCB3aWR0aCwgaGVpZ2h0LCB7IFxuICAgICAgICAgICAgICAgIGNvbGxpc2lvbkZpbHRlcjoge1xuICAgICAgICAgICAgICAgICAgICBncm91cDogZ3JvdXBcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGNoYW1mZXI6IHtcbiAgICAgICAgICAgICAgICAgICAgcmFkaXVzOiBoZWlnaHQgKiAwLjVcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGRlbnNpdHk6IDAuMDAwMlxuICAgICAgICAgICAgfSk7XG4gICAgXG4gICAgICAgIHZhciB3aGVlbEEgPSBCb2RpZXMuY2lyY2xlKHh4ICsgd2hlZWxBT2Zmc2V0LCB5eSArIHdoZWVsWU9mZnNldCwgd2hlZWxTaXplLCB7IFxuICAgICAgICAgICAgY29sbGlzaW9uRmlsdGVyOiB7XG4gICAgICAgICAgICAgICAgZ3JvdXA6IGdyb3VwXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnJpY3Rpb246IDAuOFxuICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgIHZhciB3aGVlbEIgPSBCb2RpZXMuY2lyY2xlKHh4ICsgd2hlZWxCT2Zmc2V0LCB5eSArIHdoZWVsWU9mZnNldCwgd2hlZWxTaXplLCB7IFxuICAgICAgICAgICAgY29sbGlzaW9uRmlsdGVyOiB7XG4gICAgICAgICAgICAgICAgZ3JvdXA6IGdyb3VwXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnJpY3Rpb246IDAuOFxuICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgIHZhciBheGVsQSA9IENvbnN0cmFpbnQuY3JlYXRlKHtcbiAgICAgICAgICAgIGJvZHlCOiBib2R5LFxuICAgICAgICAgICAgcG9pbnRCOiB7IHg6IHdoZWVsQU9mZnNldCwgeTogd2hlZWxZT2Zmc2V0IH0sXG4gICAgICAgICAgICBib2R5QTogd2hlZWxBLFxuICAgICAgICAgICAgc3RpZmZuZXNzOiAxLFxuICAgICAgICAgICAgbGVuZ3RoOiAwXG4gICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgIHZhciBheGVsQiA9IENvbnN0cmFpbnQuY3JlYXRlKHtcbiAgICAgICAgICAgIGJvZHlCOiBib2R5LFxuICAgICAgICAgICAgcG9pbnRCOiB7IHg6IHdoZWVsQk9mZnNldCwgeTogd2hlZWxZT2Zmc2V0IH0sXG4gICAgICAgICAgICBib2R5QTogd2hlZWxCLFxuICAgICAgICAgICAgc3RpZmZuZXNzOiAxLFxuICAgICAgICAgICAgbGVuZ3RoOiAwXG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgQ29tcG9zaXRlLmFkZEJvZHkoY2FyLCBib2R5KTtcbiAgICAgICAgQ29tcG9zaXRlLmFkZEJvZHkoY2FyLCB3aGVlbEEpO1xuICAgICAgICBDb21wb3NpdGUuYWRkQm9keShjYXIsIHdoZWVsQik7XG4gICAgICAgIENvbXBvc2l0ZS5hZGRDb25zdHJhaW50KGNhciwgYXhlbEEpO1xuICAgICAgICBDb21wb3NpdGUuYWRkQ29uc3RyYWludChjYXIsIGF4ZWxCKTtcblxuICAgICAgICByZXR1cm4gY2FyO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgc2ltcGxlIHNvZnQgYm9keSBsaWtlIG9iamVjdC5cbiAgICAgKiBAbWV0aG9kIHNvZnRCb2R5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHh4XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHl5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNvbHVtbnNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcm93c1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBjb2x1bW5HYXBcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcm93R2FwXG4gICAgICogQHBhcmFtIHtib29sZWFufSBjcm9zc0JyYWNlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHBhcnRpY2xlUmFkaXVzXG4gICAgICogQHBhcmFtIHt9IHBhcnRpY2xlT3B0aW9uc1xuICAgICAqIEBwYXJhbSB7fSBjb25zdHJhaW50T3B0aW9uc1xuICAgICAqIEByZXR1cm4ge2NvbXBvc2l0ZX0gQSBuZXcgY29tcG9zaXRlIHNvZnRCb2R5XG4gICAgICovXG4gICAgQ29tcG9zaXRlcy5zb2Z0Qm9keSA9IGZ1bmN0aW9uKHh4LCB5eSwgY29sdW1ucywgcm93cywgY29sdW1uR2FwLCByb3dHYXAsIGNyb3NzQnJhY2UsIHBhcnRpY2xlUmFkaXVzLCBwYXJ0aWNsZU9wdGlvbnMsIGNvbnN0cmFpbnRPcHRpb25zKSB7XG4gICAgICAgIHBhcnRpY2xlT3B0aW9ucyA9IENvbW1vbi5leHRlbmQoeyBpbmVydGlhOiBJbmZpbml0eSB9LCBwYXJ0aWNsZU9wdGlvbnMpO1xuICAgICAgICBjb25zdHJhaW50T3B0aW9ucyA9IENvbW1vbi5leHRlbmQoeyBzdGlmZm5lc3M6IDAuMiwgcmVuZGVyOiB7IHR5cGU6ICdsaW5lJywgYW5jaG9yczogZmFsc2UgfSB9LCBjb25zdHJhaW50T3B0aW9ucyk7XG5cbiAgICAgICAgdmFyIHNvZnRCb2R5ID0gQ29tcG9zaXRlcy5zdGFjayh4eCwgeXksIGNvbHVtbnMsIHJvd3MsIGNvbHVtbkdhcCwgcm93R2FwLCBmdW5jdGlvbih4LCB5KSB7XG4gICAgICAgICAgICByZXR1cm4gQm9kaWVzLmNpcmNsZSh4LCB5LCBwYXJ0aWNsZVJhZGl1cywgcGFydGljbGVPcHRpb25zKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgQ29tcG9zaXRlcy5tZXNoKHNvZnRCb2R5LCBjb2x1bW5zLCByb3dzLCBjcm9zc0JyYWNlLCBjb25zdHJhaW50T3B0aW9ucyk7XG5cbiAgICAgICAgc29mdEJvZHkubGFiZWwgPSAnU29mdCBCb2R5JztcblxuICAgICAgICByZXR1cm4gc29mdEJvZHk7XG4gICAgfTtcblxufSkoKTtcblxufSx7XCIuLi9ib2R5L0JvZHlcIjoxLFwiLi4vYm9keS9Db21wb3NpdGVcIjoyLFwiLi4vY29uc3RyYWludC9Db25zdHJhaW50XCI6MTIsXCIuLi9jb3JlL0NvbW1vblwiOjE0LFwiLi9Cb2RpZXNcIjoyM31dLDI1OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qKlxuKiBUaGUgYE1hdHRlci5BeGVzYCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyBmb3IgY3JlYXRpbmcgYW5kIG1hbmlwdWxhdGluZyBzZXRzIG9mIGF4ZXMuXG4qXG4qIEBjbGFzcyBBeGVzXG4qL1xuXG52YXIgQXhlcyA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEF4ZXM7XG5cbnZhciBWZWN0b3IgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZWN0b3InKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHNldCBvZiBheGVzIGZyb20gdGhlIGdpdmVuIHZlcnRpY2VzLlxuICAgICAqIEBtZXRob2QgZnJvbVZlcnRpY2VzXG4gICAgICogQHBhcmFtIHt2ZXJ0aWNlc30gdmVydGljZXNcbiAgICAgKiBAcmV0dXJuIHtheGVzfSBBIG5ldyBheGVzIGZyb20gdGhlIGdpdmVuIHZlcnRpY2VzXG4gICAgICovXG4gICAgQXhlcy5mcm9tVmVydGljZXMgPSBmdW5jdGlvbih2ZXJ0aWNlcykge1xuICAgICAgICB2YXIgYXhlcyA9IHt9O1xuXG4gICAgICAgIC8vIGZpbmQgdGhlIHVuaXF1ZSBheGVzLCB1c2luZyBlZGdlIG5vcm1hbCBncmFkaWVudHNcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB2ZXJ0aWNlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGogPSAoaSArIDEpICUgdmVydGljZXMubGVuZ3RoLCBcbiAgICAgICAgICAgICAgICBub3JtYWwgPSBWZWN0b3Iubm9ybWFsaXNlKHsgXG4gICAgICAgICAgICAgICAgICAgIHg6IHZlcnRpY2VzW2pdLnkgLSB2ZXJ0aWNlc1tpXS55LCBcbiAgICAgICAgICAgICAgICAgICAgeTogdmVydGljZXNbaV0ueCAtIHZlcnRpY2VzW2pdLnhcbiAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICBncmFkaWVudCA9IChub3JtYWwueSA9PT0gMCkgPyBJbmZpbml0eSA6IChub3JtYWwueCAvIG5vcm1hbC55KTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gbGltaXQgcHJlY2lzaW9uXG4gICAgICAgICAgICBncmFkaWVudCA9IGdyYWRpZW50LnRvRml4ZWQoMykudG9TdHJpbmcoKTtcbiAgICAgICAgICAgIGF4ZXNbZ3JhZGllbnRdID0gbm9ybWFsO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIENvbW1vbi52YWx1ZXMoYXhlcyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJvdGF0ZXMgYSBzZXQgb2YgYXhlcyBieSB0aGUgZ2l2ZW4gYW5nbGUuXG4gICAgICogQG1ldGhvZCByb3RhdGVcbiAgICAgKiBAcGFyYW0ge2F4ZXN9IGF4ZXNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gYW5nbGVcbiAgICAgKi9cbiAgICBBeGVzLnJvdGF0ZSA9IGZ1bmN0aW9uKGF4ZXMsIGFuZ2xlKSB7XG4gICAgICAgIGlmIChhbmdsZSA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgXG4gICAgICAgIHZhciBjb3MgPSBNYXRoLmNvcyhhbmdsZSksXG4gICAgICAgICAgICBzaW4gPSBNYXRoLnNpbihhbmdsZSk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBheGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgYXhpcyA9IGF4ZXNbaV0sXG4gICAgICAgICAgICAgICAgeHg7XG4gICAgICAgICAgICB4eCA9IGF4aXMueCAqIGNvcyAtIGF4aXMueSAqIHNpbjtcbiAgICAgICAgICAgIGF4aXMueSA9IGF4aXMueCAqIHNpbiArIGF4aXMueSAqIGNvcztcbiAgICAgICAgICAgIGF4aXMueCA9IHh4O1xuICAgICAgICB9XG4gICAgfTtcblxufSkoKTtcblxufSx7XCIuLi9jb3JlL0NvbW1vblwiOjE0LFwiLi4vZ2VvbWV0cnkvVmVjdG9yXCI6Mjh9XSwyNjpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuQm91bmRzYCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyBmb3IgY3JlYXRpbmcgYW5kIG1hbmlwdWxhdGluZyBheGlzLWFsaWduZWQgYm91bmRpbmcgYm94ZXMgKEFBQkIpLlxuKlxuKiBAY2xhc3MgQm91bmRzXG4qL1xuXG52YXIgQm91bmRzID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gQm91bmRzO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IGF4aXMtYWxpZ25lZCBib3VuZGluZyBib3ggKEFBQkIpIGZvciB0aGUgZ2l2ZW4gdmVydGljZXMuXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge3ZlcnRpY2VzfSB2ZXJ0aWNlc1xuICAgICAqIEByZXR1cm4ge2JvdW5kc30gQSBuZXcgYm91bmRzIG9iamVjdFxuICAgICAqL1xuICAgIEJvdW5kcy5jcmVhdGUgPSBmdW5jdGlvbih2ZXJ0aWNlcykge1xuICAgICAgICB2YXIgYm91bmRzID0geyBcbiAgICAgICAgICAgIG1pbjogeyB4OiAwLCB5OiAwIH0sIFxuICAgICAgICAgICAgbWF4OiB7IHg6IDAsIHk6IDAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIGlmICh2ZXJ0aWNlcylcbiAgICAgICAgICAgIEJvdW5kcy51cGRhdGUoYm91bmRzLCB2ZXJ0aWNlcyk7XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gYm91bmRzO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIGJvdW5kcyB1c2luZyB0aGUgZ2l2ZW4gdmVydGljZXMgYW5kIGV4dGVuZHMgdGhlIGJvdW5kcyBnaXZlbiBhIHZlbG9jaXR5LlxuICAgICAqIEBtZXRob2QgdXBkYXRlXG4gICAgICogQHBhcmFtIHtib3VuZHN9IGJvdW5kc1xuICAgICAqIEBwYXJhbSB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlbG9jaXR5XG4gICAgICovXG4gICAgQm91bmRzLnVwZGF0ZSA9IGZ1bmN0aW9uKGJvdW5kcywgdmVydGljZXMsIHZlbG9jaXR5KSB7XG4gICAgICAgIGJvdW5kcy5taW4ueCA9IEluZmluaXR5O1xuICAgICAgICBib3VuZHMubWF4LnggPSAtSW5maW5pdHk7XG4gICAgICAgIGJvdW5kcy5taW4ueSA9IEluZmluaXR5O1xuICAgICAgICBib3VuZHMubWF4LnkgPSAtSW5maW5pdHk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB2ZXJ0aWNlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHZlcnRleCA9IHZlcnRpY2VzW2ldO1xuICAgICAgICAgICAgaWYgKHZlcnRleC54ID4gYm91bmRzLm1heC54KSBib3VuZHMubWF4LnggPSB2ZXJ0ZXgueDtcbiAgICAgICAgICAgIGlmICh2ZXJ0ZXgueCA8IGJvdW5kcy5taW4ueCkgYm91bmRzLm1pbi54ID0gdmVydGV4Lng7XG4gICAgICAgICAgICBpZiAodmVydGV4LnkgPiBib3VuZHMubWF4LnkpIGJvdW5kcy5tYXgueSA9IHZlcnRleC55O1xuICAgICAgICAgICAgaWYgKHZlcnRleC55IDwgYm91bmRzLm1pbi55KSBib3VuZHMubWluLnkgPSB2ZXJ0ZXgueTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKHZlbG9jaXR5KSB7XG4gICAgICAgICAgICBpZiAodmVsb2NpdHkueCA+IDApIHtcbiAgICAgICAgICAgICAgICBib3VuZHMubWF4LnggKz0gdmVsb2NpdHkueDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYm91bmRzLm1pbi54ICs9IHZlbG9jaXR5Lng7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmICh2ZWxvY2l0eS55ID4gMCkge1xuICAgICAgICAgICAgICAgIGJvdW5kcy5tYXgueSArPSB2ZWxvY2l0eS55O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBib3VuZHMubWluLnkgKz0gdmVsb2NpdHkueTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRydWUgaWYgdGhlIGJvdW5kcyBjb250YWlucyB0aGUgZ2l2ZW4gcG9pbnQuXG4gICAgICogQG1ldGhvZCBjb250YWluc1xuICAgICAqIEBwYXJhbSB7Ym91bmRzfSBib3VuZHNcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBib3VuZHMgY29udGFpbiB0aGUgcG9pbnQsIG90aGVyd2lzZSBmYWxzZVxuICAgICAqL1xuICAgIEJvdW5kcy5jb250YWlucyA9IGZ1bmN0aW9uKGJvdW5kcywgcG9pbnQpIHtcbiAgICAgICAgcmV0dXJuIHBvaW50LnggPj0gYm91bmRzLm1pbi54ICYmIHBvaW50LnggPD0gYm91bmRzLm1heC54IFxuICAgICAgICAgICAgICAgJiYgcG9pbnQueSA+PSBib3VuZHMubWluLnkgJiYgcG9pbnQueSA8PSBib3VuZHMubWF4Lnk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgdHdvIGJvdW5kcyBpbnRlcnNlY3QuXG4gICAgICogQG1ldGhvZCBvdmVybGFwc1xuICAgICAqIEBwYXJhbSB7Ym91bmRzfSBib3VuZHNBXG4gICAgICogQHBhcmFtIHtib3VuZHN9IGJvdW5kc0JcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBib3VuZHMgb3ZlcmxhcCwgb3RoZXJ3aXNlIGZhbHNlXG4gICAgICovXG4gICAgQm91bmRzLm92ZXJsYXBzID0gZnVuY3Rpb24oYm91bmRzQSwgYm91bmRzQikge1xuICAgICAgICByZXR1cm4gKGJvdW5kc0EubWluLnggPD0gYm91bmRzQi5tYXgueCAmJiBib3VuZHNBLm1heC54ID49IGJvdW5kc0IubWluLnhcbiAgICAgICAgICAgICAgICAmJiBib3VuZHNBLm1heC55ID49IGJvdW5kc0IubWluLnkgJiYgYm91bmRzQS5taW4ueSA8PSBib3VuZHNCLm1heC55KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVHJhbnNsYXRlcyB0aGUgYm91bmRzIGJ5IHRoZSBnaXZlbiB2ZWN0b3IuXG4gICAgICogQG1ldGhvZCB0cmFuc2xhdGVcbiAgICAgKiBAcGFyYW0ge2JvdW5kc30gYm91bmRzXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvclxuICAgICAqL1xuICAgIEJvdW5kcy50cmFuc2xhdGUgPSBmdW5jdGlvbihib3VuZHMsIHZlY3Rvcikge1xuICAgICAgICBib3VuZHMubWluLnggKz0gdmVjdG9yLng7XG4gICAgICAgIGJvdW5kcy5tYXgueCArPSB2ZWN0b3IueDtcbiAgICAgICAgYm91bmRzLm1pbi55ICs9IHZlY3Rvci55O1xuICAgICAgICBib3VuZHMubWF4LnkgKz0gdmVjdG9yLnk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNoaWZ0cyB0aGUgYm91bmRzIHRvIHRoZSBnaXZlbiBwb3NpdGlvbi5cbiAgICAgKiBAbWV0aG9kIHNoaWZ0XG4gICAgICogQHBhcmFtIHtib3VuZHN9IGJvdW5kc1xuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBwb3NpdGlvblxuICAgICAqL1xuICAgIEJvdW5kcy5zaGlmdCA9IGZ1bmN0aW9uKGJvdW5kcywgcG9zaXRpb24pIHtcbiAgICAgICAgdmFyIGRlbHRhWCA9IGJvdW5kcy5tYXgueCAtIGJvdW5kcy5taW4ueCxcbiAgICAgICAgICAgIGRlbHRhWSA9IGJvdW5kcy5tYXgueSAtIGJvdW5kcy5taW4ueTtcbiAgICAgICAgICAgIFxuICAgICAgICBib3VuZHMubWluLnggPSBwb3NpdGlvbi54O1xuICAgICAgICBib3VuZHMubWF4LnggPSBwb3NpdGlvbi54ICsgZGVsdGFYO1xuICAgICAgICBib3VuZHMubWluLnkgPSBwb3NpdGlvbi55O1xuICAgICAgICBib3VuZHMubWF4LnkgPSBwb3NpdGlvbi55ICsgZGVsdGFZO1xuICAgIH07XG4gICAgXG59KSgpO1xuXG59LHt9XSwyNzpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuU3ZnYCBtb2R1bGUgY29udGFpbnMgbWV0aG9kcyBmb3IgY29udmVydGluZyBTVkcgaW1hZ2VzIGludG8gYW4gYXJyYXkgb2YgdmVjdG9yIHBvaW50cy5cbipcbiogVG8gdXNlIHRoaXMgbW9kdWxlIHlvdSBhbHNvIG5lZWQgdGhlIFNWR1BhdGhTZWcgcG9seWZpbGw6IGh0dHBzOi8vZ2l0aHViLmNvbS9wcm9nZXJzL3BhdGhzZWdcbipcbiogU2VlIHRoZSBpbmNsdWRlZCB1c2FnZSBbZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9saWFicnUvbWF0dGVyLWpzL3RyZWUvbWFzdGVyL2V4YW1wbGVzKS5cbipcbiogQGNsYXNzIFN2Z1xuKi9cblxudmFyIFN2ZyA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN2ZztcblxudmFyIEJvdW5kcyA9IF9kZXJlcV8oJy4uL2dlb21ldHJ5L0JvdW5kcycpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDb252ZXJ0cyBhbiBTVkcgcGF0aCBpbnRvIGFuIGFycmF5IG9mIHZlY3RvciBwb2ludHMuXG4gICAgICogSWYgdGhlIGlucHV0IHBhdGggZm9ybXMgYSBjb25jYXZlIHNoYXBlLCB5b3UgbXVzdCBkZWNvbXBvc2UgdGhlIHJlc3VsdCBpbnRvIGNvbnZleCBwYXJ0cyBiZWZvcmUgdXNlLlxuICAgICAqIFNlZSBgQm9kaWVzLmZyb21WZXJ0aWNlc2Agd2hpY2ggcHJvdmlkZXMgc3VwcG9ydCBmb3IgdGhpcy5cbiAgICAgKiBOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBpcyBub3QgZ3VhcmFudGVlZCB0byBzdXBwb3J0IGNvbXBsZXggcGF0aHMgKHN1Y2ggYXMgdGhvc2Ugd2l0aCBob2xlcykuXG4gICAgICogQG1ldGhvZCBwYXRoVG9WZXJ0aWNlc1xuICAgICAqIEBwYXJhbSB7U1ZHUGF0aEVsZW1lbnR9IHBhdGhcbiAgICAgKiBAcGFyYW0ge051bWJlcn0gW3NhbXBsZUxlbmd0aD0xNV1cbiAgICAgKiBAcmV0dXJuIHtWZWN0b3JbXX0gcG9pbnRzXG4gICAgICovXG4gICAgU3ZnLnBhdGhUb1ZlcnRpY2VzID0gZnVuY3Rpb24ocGF0aCwgc2FtcGxlTGVuZ3RoKSB7XG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS93b3V0L3N2Zy50b3BvbHkuanMvYmxvYi9tYXN0ZXIvc3ZnLnRvcG9seS5qc1xuICAgICAgICB2YXIgaSwgaWwsIHRvdGFsLCBwb2ludCwgc2VnbWVudCwgc2VnbWVudHMsIFxuICAgICAgICAgICAgc2VnbWVudHNRdWV1ZSwgbGFzdFNlZ21lbnQsIFxuICAgICAgICAgICAgbGFzdFBvaW50LCBzZWdtZW50SW5kZXgsIHBvaW50cyA9IFtdLFxuICAgICAgICAgICAgbHgsIGx5LCBsZW5ndGggPSAwLCB4ID0gMCwgeSA9IDA7XG5cbiAgICAgICAgc2FtcGxlTGVuZ3RoID0gc2FtcGxlTGVuZ3RoIHx8IDE1O1xuXG4gICAgICAgIHZhciBhZGRQb2ludCA9IGZ1bmN0aW9uKHB4LCBweSwgcGF0aFNlZ1R5cGUpIHtcbiAgICAgICAgICAgIC8vIGFsbCBvZGQtbnVtYmVyZWQgcGF0aCB0eXBlcyBhcmUgcmVsYXRpdmUgZXhjZXB0IFBBVEhTRUdfQ0xPU0VQQVRIICgxKVxuICAgICAgICAgICAgdmFyIGlzUmVsYXRpdmUgPSBwYXRoU2VnVHlwZSAlIDIgPT09IDEgJiYgcGF0aFNlZ1R5cGUgPiAxO1xuXG4gICAgICAgICAgICAvLyB3aGVuIHRoZSBsYXN0IHBvaW50IGRvZXNuJ3QgZXF1YWwgdGhlIGN1cnJlbnQgcG9pbnQgYWRkIHRoZSBjdXJyZW50IHBvaW50XG4gICAgICAgICAgICBpZiAoIWxhc3RQb2ludCB8fCBweCAhPSBsYXN0UG9pbnQueCB8fCBweSAhPSBsYXN0UG9pbnQueSkge1xuICAgICAgICAgICAgICAgIGlmIChsYXN0UG9pbnQgJiYgaXNSZWxhdGl2ZSkge1xuICAgICAgICAgICAgICAgICAgICBseCA9IGxhc3RQb2ludC54O1xuICAgICAgICAgICAgICAgICAgICBseSA9IGxhc3RQb2ludC55O1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGx4ID0gMDtcbiAgICAgICAgICAgICAgICAgICAgbHkgPSAwO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciBwb2ludCA9IHtcbiAgICAgICAgICAgICAgICAgICAgeDogbHggKyBweCxcbiAgICAgICAgICAgICAgICAgICAgeTogbHkgKyBweVxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICAvLyBzZXQgbGFzdCBwb2ludFxuICAgICAgICAgICAgICAgIGlmIChpc1JlbGF0aXZlIHx8ICFsYXN0UG9pbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgbGFzdFBvaW50ID0gcG9pbnQ7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcG9pbnRzLnB1c2gocG9pbnQpO1xuXG4gICAgICAgICAgICAgICAgeCA9IGx4ICsgcHg7XG4gICAgICAgICAgICAgICAgeSA9IGx5ICsgcHk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgdmFyIGFkZFNlZ21lbnRQb2ludCA9IGZ1bmN0aW9uKHNlZ21lbnQpIHtcbiAgICAgICAgICAgIHZhciBzZWdUeXBlID0gc2VnbWVudC5wYXRoU2VnVHlwZUFzTGV0dGVyLnRvVXBwZXJDYXNlKCk7XG5cbiAgICAgICAgICAgIC8vIHNraXAgcGF0aCBlbmRzXG4gICAgICAgICAgICBpZiAoc2VnVHlwZSA9PT0gJ1onKSBcbiAgICAgICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgICAgIC8vIG1hcCBzZWdtZW50IHRvIHggYW5kIHlcbiAgICAgICAgICAgIHN3aXRjaCAoc2VnVHlwZSkge1xuXG4gICAgICAgICAgICBjYXNlICdNJzpcbiAgICAgICAgICAgIGNhc2UgJ0wnOlxuICAgICAgICAgICAgY2FzZSAnVCc6XG4gICAgICAgICAgICBjYXNlICdDJzpcbiAgICAgICAgICAgIGNhc2UgJ1MnOlxuICAgICAgICAgICAgY2FzZSAnUSc6XG4gICAgICAgICAgICAgICAgeCA9IHNlZ21lbnQueDtcbiAgICAgICAgICAgICAgICB5ID0gc2VnbWVudC55O1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnSCc6XG4gICAgICAgICAgICAgICAgeCA9IHNlZ21lbnQueDtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ1YnOlxuICAgICAgICAgICAgICAgIHkgPSBzZWdtZW50Lnk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGFkZFBvaW50KHgsIHksIHNlZ21lbnQucGF0aFNlZ1R5cGUpO1xuICAgICAgICB9O1xuXG4gICAgICAgIC8vIGVuc3VyZSBwYXRoIGlzIGFic29sdXRlXG4gICAgICAgIF9zdmdQYXRoVG9BYnNvbHV0ZShwYXRoKTtcblxuICAgICAgICAvLyBnZXQgdG90YWwgbGVuZ3RoXG4gICAgICAgIHRvdGFsID0gcGF0aC5nZXRUb3RhbExlbmd0aCgpO1xuXG4gICAgICAgIC8vIHF1ZXVlIHNlZ21lbnRzXG4gICAgICAgIHNlZ21lbnRzID0gW107XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBwYXRoLnBhdGhTZWdMaXN0Lm51bWJlck9mSXRlbXM7IGkgKz0gMSlcbiAgICAgICAgICAgIHNlZ21lbnRzLnB1c2gocGF0aC5wYXRoU2VnTGlzdC5nZXRJdGVtKGkpKTtcblxuICAgICAgICBzZWdtZW50c1F1ZXVlID0gc2VnbWVudHMuY29uY2F0KCk7XG5cbiAgICAgICAgLy8gc2FtcGxlIHRocm91Z2ggcGF0aFxuICAgICAgICB3aGlsZSAobGVuZ3RoIDwgdG90YWwpIHtcbiAgICAgICAgICAgIC8vIGdldCBzZWdtZW50IGF0IHBvc2l0aW9uXG4gICAgICAgICAgICBzZWdtZW50SW5kZXggPSBwYXRoLmdldFBhdGhTZWdBdExlbmd0aChsZW5ndGgpO1xuICAgICAgICAgICAgc2VnbWVudCA9IHNlZ21lbnRzW3NlZ21lbnRJbmRleF07XG5cbiAgICAgICAgICAgIC8vIG5ldyBzZWdtZW50XG4gICAgICAgICAgICBpZiAoc2VnbWVudCAhPSBsYXN0U2VnbWVudCkge1xuICAgICAgICAgICAgICAgIHdoaWxlIChzZWdtZW50c1F1ZXVlLmxlbmd0aCAmJiBzZWdtZW50c1F1ZXVlWzBdICE9IHNlZ21lbnQpXG4gICAgICAgICAgICAgICAgICAgIGFkZFNlZ21lbnRQb2ludChzZWdtZW50c1F1ZXVlLnNoaWZ0KCkpO1xuXG4gICAgICAgICAgICAgICAgbGFzdFNlZ21lbnQgPSBzZWdtZW50O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBhZGQgcG9pbnRzIGluIGJldHdlZW4gd2hlbiBjdXJ2aW5nXG4gICAgICAgICAgICAvLyBUT0RPOiBhZGFwdGl2ZSBzYW1wbGluZ1xuICAgICAgICAgICAgc3dpdGNoIChzZWdtZW50LnBhdGhTZWdUeXBlQXNMZXR0ZXIudG9VcHBlckNhc2UoKSkge1xuXG4gICAgICAgICAgICBjYXNlICdDJzpcbiAgICAgICAgICAgIGNhc2UgJ1QnOlxuICAgICAgICAgICAgY2FzZSAnUyc6XG4gICAgICAgICAgICBjYXNlICdRJzpcbiAgICAgICAgICAgIGNhc2UgJ0EnOlxuICAgICAgICAgICAgICAgIHBvaW50ID0gcGF0aC5nZXRQb2ludEF0TGVuZ3RoKGxlbmd0aCk7XG4gICAgICAgICAgICAgICAgYWRkUG9pbnQocG9pbnQueCwgcG9pbnQueSwgMCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gaW5jcmVtZW50IGJ5IHNhbXBsZSB2YWx1ZVxuICAgICAgICAgICAgbGVuZ3RoICs9IHNhbXBsZUxlbmd0aDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGFkZCByZW1haW5pbmcgc2VnbWVudHMgbm90IHBhc3NlZCBieSBzYW1wbGluZ1xuICAgICAgICBmb3IgKGkgPSAwLCBpbCA9IHNlZ21lbnRzUXVldWUubGVuZ3RoOyBpIDwgaWw7ICsraSlcbiAgICAgICAgICAgIGFkZFNlZ21lbnRQb2ludChzZWdtZW50c1F1ZXVlW2ldKTtcblxuICAgICAgICByZXR1cm4gcG9pbnRzO1xuICAgIH07XG5cbiAgICB2YXIgX3N2Z1BhdGhUb0Fic29sdXRlID0gZnVuY3Rpb24ocGF0aCkge1xuICAgICAgICAvLyBodHRwOi8vcGhyb2d6Lm5ldC9jb252ZXJ0LXN2Zy1wYXRoLXRvLWFsbC1hYnNvbHV0ZS1jb21tYW5kc1xuICAgICAgICAvLyBDb3B5cmlnaHQgKGMpIEdhdmluIEtpc3RuZXJcbiAgICAgICAgLy8gaHR0cDovL3Bocm9nei5uZXQvanMvX1JldXNlTGljZW5zZS50eHRcbiAgICAgICAgLy8gTW9kaWZpY2F0aW9uczogdGlkeSBmb3JtYXR0aW5nIGFuZCBuYW1pbmdcbiAgICAgICAgdmFyIHgwLCB5MCwgeDEsIHkxLCB4MiwgeTIsIHNlZ3MgPSBwYXRoLnBhdGhTZWdMaXN0LFxuICAgICAgICAgICAgeCA9IDAsIHkgPSAwLCBsZW4gPSBzZWdzLm51bWJlck9mSXRlbXM7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47ICsraSkge1xuICAgICAgICAgICAgdmFyIHNlZyA9IHNlZ3MuZ2V0SXRlbShpKSxcbiAgICAgICAgICAgICAgICBzZWdUeXBlID0gc2VnLnBhdGhTZWdUeXBlQXNMZXR0ZXI7XG5cbiAgICAgICAgICAgIGlmICgvW01MSFZDU1FUQV0vLnRlc3Qoc2VnVHlwZSkpIHtcbiAgICAgICAgICAgICAgICBpZiAoJ3gnIGluIHNlZykgeCA9IHNlZy54O1xuICAgICAgICAgICAgICAgIGlmICgneScgaW4gc2VnKSB5ID0gc2VnLnk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlmICgneDEnIGluIHNlZykgeDEgPSB4ICsgc2VnLngxO1xuICAgICAgICAgICAgICAgIGlmICgneDInIGluIHNlZykgeDIgPSB4ICsgc2VnLngyO1xuICAgICAgICAgICAgICAgIGlmICgneTEnIGluIHNlZykgeTEgPSB5ICsgc2VnLnkxO1xuICAgICAgICAgICAgICAgIGlmICgneTInIGluIHNlZykgeTIgPSB5ICsgc2VnLnkyO1xuICAgICAgICAgICAgICAgIGlmICgneCcgaW4gc2VnKSB4ICs9IHNlZy54O1xuICAgICAgICAgICAgICAgIGlmICgneScgaW4gc2VnKSB5ICs9IHNlZy55O1xuXG4gICAgICAgICAgICAgICAgc3dpdGNoIChzZWdUeXBlKSB7XG5cbiAgICAgICAgICAgICAgICBjYXNlICdtJzpcbiAgICAgICAgICAgICAgICAgICAgc2Vncy5yZXBsYWNlSXRlbShwYXRoLmNyZWF0ZVNWR1BhdGhTZWdNb3ZldG9BYnMoeCwgeSksIGkpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdsJzpcbiAgICAgICAgICAgICAgICAgICAgc2Vncy5yZXBsYWNlSXRlbShwYXRoLmNyZWF0ZVNWR1BhdGhTZWdMaW5ldG9BYnMoeCwgeSksIGkpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdoJzpcbiAgICAgICAgICAgICAgICAgICAgc2Vncy5yZXBsYWNlSXRlbShwYXRoLmNyZWF0ZVNWR1BhdGhTZWdMaW5ldG9Ib3Jpem9udGFsQWJzKHgpLCBpKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAndic6XG4gICAgICAgICAgICAgICAgICAgIHNlZ3MucmVwbGFjZUl0ZW0ocGF0aC5jcmVhdGVTVkdQYXRoU2VnTGluZXRvVmVydGljYWxBYnMoeSksIGkpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdjJzpcbiAgICAgICAgICAgICAgICAgICAgc2Vncy5yZXBsYWNlSXRlbShwYXRoLmNyZWF0ZVNWR1BhdGhTZWdDdXJ2ZXRvQ3ViaWNBYnMoeCwgeSwgeDEsIHkxLCB4MiwgeTIpLCBpKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAncyc6XG4gICAgICAgICAgICAgICAgICAgIHNlZ3MucmVwbGFjZUl0ZW0ocGF0aC5jcmVhdGVTVkdQYXRoU2VnQ3VydmV0b0N1YmljU21vb3RoQWJzKHgsIHksIHgyLCB5MiksIGkpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdxJzpcbiAgICAgICAgICAgICAgICAgICAgc2Vncy5yZXBsYWNlSXRlbShwYXRoLmNyZWF0ZVNWR1BhdGhTZWdDdXJ2ZXRvUXVhZHJhdGljQWJzKHgsIHksIHgxLCB5MSksIGkpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICd0JzpcbiAgICAgICAgICAgICAgICAgICAgc2Vncy5yZXBsYWNlSXRlbShwYXRoLmNyZWF0ZVNWR1BhdGhTZWdDdXJ2ZXRvUXVhZHJhdGljU21vb3RoQWJzKHgsIHkpLCBpKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnYSc6XG4gICAgICAgICAgICAgICAgICAgIHNlZ3MucmVwbGFjZUl0ZW0ocGF0aC5jcmVhdGVTVkdQYXRoU2VnQXJjQWJzKHgsIHksIHNlZy5yMSwgc2VnLnIyLCBzZWcuYW5nbGUsIHNlZy5sYXJnZUFyY0ZsYWcsIHNlZy5zd2VlcEZsYWcpLCBpKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAneic6XG4gICAgICAgICAgICAgICAgY2FzZSAnWic6XG4gICAgICAgICAgICAgICAgICAgIHggPSB4MDtcbiAgICAgICAgICAgICAgICAgICAgeSA9IHkwO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHNlZ1R5cGUgPT0gJ00nIHx8IHNlZ1R5cGUgPT0gJ20nKSB7XG4gICAgICAgICAgICAgICAgeDAgPSB4O1xuICAgICAgICAgICAgICAgIHkwID0geTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbn0pKCk7XG59LHtcIi4uL2dlb21ldHJ5L0JvdW5kc1wiOjI2fV0sMjg6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLlZlY3RvcmAgbW9kdWxlIGNvbnRhaW5zIG1ldGhvZHMgZm9yIGNyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgdmVjdG9ycy5cbiogVmVjdG9ycyBhcmUgdGhlIGJhc2lzIG9mIGFsbCB0aGUgZ2VvbWV0cnkgcmVsYXRlZCBvcGVyYXRpb25zIGluIHRoZSBlbmdpbmUuXG4qIEEgYE1hdHRlci5WZWN0b3JgIG9iamVjdCBpcyBvZiB0aGUgZm9ybSBgeyB4OiAwLCB5OiAwIH1gLlxuKlxuKiBTZWUgdGhlIGluY2x1ZGVkIHVzYWdlIFtleGFtcGxlc10oaHR0cHM6Ly9naXRodWIuY29tL2xpYWJydS9tYXR0ZXItanMvdHJlZS9tYXN0ZXIvZXhhbXBsZXMpLlxuKlxuKiBAY2xhc3MgVmVjdG9yXG4qL1xuXG4vLyBUT0RPOiBjb25zaWRlciBwYXJhbXMgZm9yIHJldXNpbmcgdmVjdG9yIG9iamVjdHNcblxudmFyIFZlY3RvciA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFZlY3RvcjtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyB2ZWN0b3IuXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0geFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5XG4gICAgICogQHJldHVybiB7dmVjdG9yfSBBIG5ldyB2ZWN0b3JcbiAgICAgKi9cbiAgICBWZWN0b3IuY3JlYXRlID0gZnVuY3Rpb24oeCwgeSkge1xuICAgICAgICByZXR1cm4geyB4OiB4IHx8IDAsIHk6IHkgfHwgMCB9O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgbmV3IHZlY3RvciB3aXRoIGB4YCBhbmQgYHlgIGNvcGllZCBmcm9tIHRoZSBnaXZlbiBgdmVjdG9yYC5cbiAgICAgKiBAbWV0aG9kIGNsb25lXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvclxuICAgICAqIEByZXR1cm4ge3ZlY3Rvcn0gQSBuZXcgY2xvbmVkIHZlY3RvclxuICAgICAqL1xuICAgIFZlY3Rvci5jbG9uZSA9IGZ1bmN0aW9uKHZlY3Rvcikge1xuICAgICAgICByZXR1cm4geyB4OiB2ZWN0b3IueCwgeTogdmVjdG9yLnkgfTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbWFnbml0dWRlIChsZW5ndGgpIG9mIGEgdmVjdG9yLlxuICAgICAqIEBtZXRob2QgbWFnbml0dWRlXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvclxuICAgICAqIEByZXR1cm4ge251bWJlcn0gVGhlIG1hZ25pdHVkZSBvZiB0aGUgdmVjdG9yXG4gICAgICovXG4gICAgVmVjdG9yLm1hZ25pdHVkZSA9IGZ1bmN0aW9uKHZlY3Rvcikge1xuICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KCh2ZWN0b3IueCAqIHZlY3Rvci54KSArICh2ZWN0b3IueSAqIHZlY3Rvci55KSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIG1hZ25pdHVkZSAobGVuZ3RoKSBvZiBhIHZlY3RvciAodGhlcmVmb3JlIHNhdmluZyBhIGBzcXJ0YCBvcGVyYXRpb24pLlxuICAgICAqIEBtZXRob2QgbWFnbml0dWRlU3F1YXJlZFxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JcbiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IFRoZSBzcXVhcmVkIG1hZ25pdHVkZSBvZiB0aGUgdmVjdG9yXG4gICAgICovXG4gICAgVmVjdG9yLm1hZ25pdHVkZVNxdWFyZWQgPSBmdW5jdGlvbih2ZWN0b3IpIHtcbiAgICAgICAgcmV0dXJuICh2ZWN0b3IueCAqIHZlY3Rvci54KSArICh2ZWN0b3IueSAqIHZlY3Rvci55KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUm90YXRlcyB0aGUgdmVjdG9yIGFib3V0ICgwLCAwKSBieSBzcGVjaWZpZWQgYW5nbGUuXG4gICAgICogQG1ldGhvZCByb3RhdGVcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGFuZ2xlXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IFtvdXRwdXRdXG4gICAgICogQHJldHVybiB7dmVjdG9yfSBUaGUgdmVjdG9yIHJvdGF0ZWQgYWJvdXQgKDAsIDApXG4gICAgICovXG4gICAgVmVjdG9yLnJvdGF0ZSA9IGZ1bmN0aW9uKHZlY3RvciwgYW5nbGUsIG91dHB1dCkge1xuICAgICAgICB2YXIgY29zID0gTWF0aC5jb3MoYW5nbGUpLCBzaW4gPSBNYXRoLnNpbihhbmdsZSk7XG4gICAgICAgIGlmICghb3V0cHV0KSBvdXRwdXQgPSB7fTtcbiAgICAgICAgdmFyIHggPSB2ZWN0b3IueCAqIGNvcyAtIHZlY3Rvci55ICogc2luO1xuICAgICAgICBvdXRwdXQueSA9IHZlY3Rvci54ICogc2luICsgdmVjdG9yLnkgKiBjb3M7XG4gICAgICAgIG91dHB1dC54ID0geDtcbiAgICAgICAgcmV0dXJuIG91dHB1dDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUm90YXRlcyB0aGUgdmVjdG9yIGFib3V0IGEgc3BlY2lmaWVkIHBvaW50IGJ5IHNwZWNpZmllZCBhbmdsZS5cbiAgICAgKiBAbWV0aG9kIHJvdGF0ZUFib3V0XG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvclxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBhbmdsZVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBwb2ludFxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBbb3V0cHV0XVxuICAgICAqIEByZXR1cm4ge3ZlY3Rvcn0gQSBuZXcgdmVjdG9yIHJvdGF0ZWQgYWJvdXQgdGhlIHBvaW50XG4gICAgICovXG4gICAgVmVjdG9yLnJvdGF0ZUFib3V0ID0gZnVuY3Rpb24odmVjdG9yLCBhbmdsZSwgcG9pbnQsIG91dHB1dCkge1xuICAgICAgICB2YXIgY29zID0gTWF0aC5jb3MoYW5nbGUpLCBzaW4gPSBNYXRoLnNpbihhbmdsZSk7XG4gICAgICAgIGlmICghb3V0cHV0KSBvdXRwdXQgPSB7fTtcbiAgICAgICAgdmFyIHggPSBwb2ludC54ICsgKCh2ZWN0b3IueCAtIHBvaW50LngpICogY29zIC0gKHZlY3Rvci55IC0gcG9pbnQueSkgKiBzaW4pO1xuICAgICAgICBvdXRwdXQueSA9IHBvaW50LnkgKyAoKHZlY3Rvci54IC0gcG9pbnQueCkgKiBzaW4gKyAodmVjdG9yLnkgLSBwb2ludC55KSAqIGNvcyk7XG4gICAgICAgIG91dHB1dC54ID0geDtcbiAgICAgICAgcmV0dXJuIG91dHB1dDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogTm9ybWFsaXNlcyBhIHZlY3RvciAoc3VjaCB0aGF0IGl0cyBtYWduaXR1ZGUgaXMgYDFgKS5cbiAgICAgKiBAbWV0aG9kIG5vcm1hbGlzZVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JcbiAgICAgKiBAcmV0dXJuIHt2ZWN0b3J9IEEgbmV3IHZlY3RvciBub3JtYWxpc2VkXG4gICAgICovXG4gICAgVmVjdG9yLm5vcm1hbGlzZSA9IGZ1bmN0aW9uKHZlY3Rvcikge1xuICAgICAgICB2YXIgbWFnbml0dWRlID0gVmVjdG9yLm1hZ25pdHVkZSh2ZWN0b3IpO1xuICAgICAgICBpZiAobWFnbml0dWRlID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuIHsgeDogMCwgeTogMCB9O1xuICAgICAgICByZXR1cm4geyB4OiB2ZWN0b3IueCAvIG1hZ25pdHVkZSwgeTogdmVjdG9yLnkgLyBtYWduaXR1ZGUgfTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgZG90LXByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMuXG4gICAgICogQG1ldGhvZCBkb3RcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yQVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JCXG4gICAgICogQHJldHVybiB7bnVtYmVyfSBUaGUgZG90IHByb2R1Y3Qgb2YgdGhlIHR3byB2ZWN0b3JzXG4gICAgICovXG4gICAgVmVjdG9yLmRvdCA9IGZ1bmN0aW9uKHZlY3RvckEsIHZlY3RvckIpIHtcbiAgICAgICAgcmV0dXJuICh2ZWN0b3JBLnggKiB2ZWN0b3JCLngpICsgKHZlY3RvckEueSAqIHZlY3RvckIueSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNyb3NzLXByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMuXG4gICAgICogQG1ldGhvZCBjcm9zc1xuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JBXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvckJcbiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IFRoZSBjcm9zcyBwcm9kdWN0IG9mIHRoZSB0d28gdmVjdG9yc1xuICAgICAqL1xuICAgIFZlY3Rvci5jcm9zcyA9IGZ1bmN0aW9uKHZlY3RvckEsIHZlY3RvckIpIHtcbiAgICAgICAgcmV0dXJuICh2ZWN0b3JBLnggKiB2ZWN0b3JCLnkpIC0gKHZlY3RvckEueSAqIHZlY3RvckIueCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNyb3NzLXByb2R1Y3Qgb2YgdGhyZWUgdmVjdG9ycy5cbiAgICAgKiBAbWV0aG9kIGNyb3NzM1xuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JBXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvckJcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yQ1xuICAgICAqIEByZXR1cm4ge251bWJlcn0gVGhlIGNyb3NzIHByb2R1Y3Qgb2YgdGhlIHRocmVlIHZlY3RvcnNcbiAgICAgKi9cbiAgICBWZWN0b3IuY3Jvc3MzID0gZnVuY3Rpb24odmVjdG9yQSwgdmVjdG9yQiwgdmVjdG9yQykge1xuICAgICAgICByZXR1cm4gKHZlY3RvckIueCAtIHZlY3RvckEueCkgKiAodmVjdG9yQy55IC0gdmVjdG9yQS55KSAtICh2ZWN0b3JCLnkgLSB2ZWN0b3JBLnkpICogKHZlY3RvckMueCAtIHZlY3RvckEueCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEFkZHMgdGhlIHR3byB2ZWN0b3JzLlxuICAgICAqIEBtZXRob2QgYWRkXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvckFcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yQlxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBbb3V0cHV0XVxuICAgICAqIEByZXR1cm4ge3ZlY3Rvcn0gQSBuZXcgdmVjdG9yIG9mIHZlY3RvckEgYW5kIHZlY3RvckIgYWRkZWRcbiAgICAgKi9cbiAgICBWZWN0b3IuYWRkID0gZnVuY3Rpb24odmVjdG9yQSwgdmVjdG9yQiwgb3V0cHV0KSB7XG4gICAgICAgIGlmICghb3V0cHV0KSBvdXRwdXQgPSB7fTtcbiAgICAgICAgb3V0cHV0LnggPSB2ZWN0b3JBLnggKyB2ZWN0b3JCLng7XG4gICAgICAgIG91dHB1dC55ID0gdmVjdG9yQS55ICsgdmVjdG9yQi55O1xuICAgICAgICByZXR1cm4gb3V0cHV0O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTdWJ0cmFjdHMgdGhlIHR3byB2ZWN0b3JzLlxuICAgICAqIEBtZXRob2Qgc3ViXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvckFcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yQlxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBbb3V0cHV0XVxuICAgICAqIEByZXR1cm4ge3ZlY3Rvcn0gQSBuZXcgdmVjdG9yIG9mIHZlY3RvckEgYW5kIHZlY3RvckIgc3VidHJhY3RlZFxuICAgICAqL1xuICAgIFZlY3Rvci5zdWIgPSBmdW5jdGlvbih2ZWN0b3JBLCB2ZWN0b3JCLCBvdXRwdXQpIHtcbiAgICAgICAgaWYgKCFvdXRwdXQpIG91dHB1dCA9IHt9O1xuICAgICAgICBvdXRwdXQueCA9IHZlY3RvckEueCAtIHZlY3RvckIueDtcbiAgICAgICAgb3V0cHV0LnkgPSB2ZWN0b3JBLnkgLSB2ZWN0b3JCLnk7XG4gICAgICAgIHJldHVybiBvdXRwdXQ7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE11bHRpcGxpZXMgYSB2ZWN0b3IgYW5kIGEgc2NhbGFyLlxuICAgICAqIEBtZXRob2QgbXVsdFxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2NhbGFyXG4gICAgICogQHJldHVybiB7dmVjdG9yfSBBIG5ldyB2ZWN0b3IgbXVsdGlwbGllZCBieSBzY2FsYXJcbiAgICAgKi9cbiAgICBWZWN0b3IubXVsdCA9IGZ1bmN0aW9uKHZlY3Rvciwgc2NhbGFyKSB7XG4gICAgICAgIHJldHVybiB7IHg6IHZlY3Rvci54ICogc2NhbGFyLCB5OiB2ZWN0b3IueSAqIHNjYWxhciB9O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBEaXZpZGVzIGEgdmVjdG9yIGFuZCBhIHNjYWxhci5cbiAgICAgKiBAbWV0aG9kIGRpdlxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2NhbGFyXG4gICAgICogQHJldHVybiB7dmVjdG9yfSBBIG5ldyB2ZWN0b3IgZGl2aWRlZCBieSBzY2FsYXJcbiAgICAgKi9cbiAgICBWZWN0b3IuZGl2ID0gZnVuY3Rpb24odmVjdG9yLCBzY2FsYXIpIHtcbiAgICAgICAgcmV0dXJuIHsgeDogdmVjdG9yLnggLyBzY2FsYXIsIHk6IHZlY3Rvci55IC8gc2NhbGFyIH07XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBlcnBlbmRpY3VsYXIgdmVjdG9yLiBTZXQgYG5lZ2F0ZWAgdG8gdHJ1ZSBmb3IgdGhlIHBlcnBlbmRpY3VsYXIgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvbi5cbiAgICAgKiBAbWV0aG9kIHBlcnBcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yXG4gICAgICogQHBhcmFtIHtib29sfSBbbmVnYXRlPWZhbHNlXVxuICAgICAqIEByZXR1cm4ge3ZlY3Rvcn0gVGhlIHBlcnBlbmRpY3VsYXIgdmVjdG9yXG4gICAgICovXG4gICAgVmVjdG9yLnBlcnAgPSBmdW5jdGlvbih2ZWN0b3IsIG5lZ2F0ZSkge1xuICAgICAgICBuZWdhdGUgPSBuZWdhdGUgPT09IHRydWUgPyAtMSA6IDE7XG4gICAgICAgIHJldHVybiB7IHg6IG5lZ2F0ZSAqIC12ZWN0b3IueSwgeTogbmVnYXRlICogdmVjdG9yLnggfTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogTmVnYXRlcyBib3RoIGNvbXBvbmVudHMgb2YgYSB2ZWN0b3Igc3VjaCB0aGF0IGl0IHBvaW50cyBpbiB0aGUgb3Bwb3NpdGUgZGlyZWN0aW9uLlxuICAgICAqIEBtZXRob2QgbmVnXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvclxuICAgICAqIEByZXR1cm4ge3ZlY3Rvcn0gVGhlIG5lZ2F0ZWQgdmVjdG9yXG4gICAgICovXG4gICAgVmVjdG9yLm5lZyA9IGZ1bmN0aW9uKHZlY3Rvcikge1xuICAgICAgICByZXR1cm4geyB4OiAtdmVjdG9yLngsIHk6IC12ZWN0b3IueSB9O1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBhbmdsZSBpbiByYWRpYW5zIGJldHdlZW4gdGhlIHR3byB2ZWN0b3JzIHJlbGF0aXZlIHRvIHRoZSB4LWF4aXMuXG4gICAgICogQG1ldGhvZCBhbmdsZVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSB2ZWN0b3JBXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHZlY3RvckJcbiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IFRoZSBhbmdsZSBpbiByYWRpYW5zXG4gICAgICovXG4gICAgVmVjdG9yLmFuZ2xlID0gZnVuY3Rpb24odmVjdG9yQSwgdmVjdG9yQikge1xuICAgICAgICByZXR1cm4gTWF0aC5hdGFuMih2ZWN0b3JCLnkgLSB2ZWN0b3JBLnksIHZlY3RvckIueCAtIHZlY3RvckEueCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFRlbXBvcmFyeSB2ZWN0b3IgcG9vbCAobm90IHRocmVhZC1zYWZlKS5cbiAgICAgKiBAcHJvcGVydHkgX3RlbXBcbiAgICAgKiBAdHlwZSB7dmVjdG9yW119XG4gICAgICogQHByaXZhdGVcbiAgICAgKi9cbiAgICBWZWN0b3IuX3RlbXAgPSBbXG4gICAgICAgIFZlY3Rvci5jcmVhdGUoKSwgVmVjdG9yLmNyZWF0ZSgpLCBcbiAgICAgICAgVmVjdG9yLmNyZWF0ZSgpLCBWZWN0b3IuY3JlYXRlKCksIFxuICAgICAgICBWZWN0b3IuY3JlYXRlKCksIFZlY3Rvci5jcmVhdGUoKVxuICAgIF07XG5cbn0pKCk7XG59LHt9XSwyOTpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuVmVydGljZXNgIG1vZHVsZSBjb250YWlucyBtZXRob2RzIGZvciBjcmVhdGluZyBhbmQgbWFuaXB1bGF0aW5nIHNldHMgb2YgdmVydGljZXMuXG4qIEEgc2V0IG9mIHZlcnRpY2VzIGlzIGFuIGFycmF5IG9mIGBNYXR0ZXIuVmVjdG9yYCB3aXRoIGFkZGl0aW9uYWwgaW5kZXhpbmcgcHJvcGVydGllcyBpbnNlcnRlZCBieSBgVmVydGljZXMuY3JlYXRlYC5cbiogQSBgTWF0dGVyLkJvZHlgIG1haW50YWlucyBhIHNldCBvZiB2ZXJ0aWNlcyB0byByZXByZXNlbnQgdGhlIHNoYXBlIG9mIHRoZSBvYmplY3QgKGl0cyBjb252ZXggaHVsbCkuXG4qXG4qIFNlZSB0aGUgaW5jbHVkZWQgdXNhZ2UgW2V4YW1wbGVzXShodHRwczovL2dpdGh1Yi5jb20vbGlhYnJ1L21hdHRlci1qcy90cmVlL21hc3Rlci9leGFtcGxlcykuXG4qXG4qIEBjbGFzcyBWZXJ0aWNlc1xuKi9cblxudmFyIFZlcnRpY2VzID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gVmVydGljZXM7XG5cbnZhciBWZWN0b3IgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZWN0b3InKTtcbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xuXG4oZnVuY3Rpb24oKSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHNldCBvZiBgTWF0dGVyLkJvZHlgIGNvbXBhdGlibGUgdmVydGljZXMuXG4gICAgICogVGhlIGBwb2ludHNgIGFyZ3VtZW50IGFjY2VwdHMgYW4gYXJyYXkgb2YgYE1hdHRlci5WZWN0b3JgIHBvaW50cyBvcmllbnRhdGVkIGFyb3VuZCB0aGUgb3JpZ2luIGAoMCwgMClgLCBmb3IgZXhhbXBsZTpcbiAgICAgKlxuICAgICAqICAgICBbeyB4OiAwLCB5OiAwIH0sIHsgeDogMjUsIHk6IDUwIH0sIHsgeDogNTAsIHk6IDAgfV1cbiAgICAgKlxuICAgICAqIFRoZSBgVmVydGljZXMuY3JlYXRlYCBtZXRob2QgcmV0dXJucyBhIG5ldyBhcnJheSBvZiB2ZXJ0aWNlcywgd2hpY2ggYXJlIHNpbWlsYXIgdG8gTWF0dGVyLlZlY3RvciBvYmplY3RzLFxuICAgICAqIGJ1dCB3aXRoIHNvbWUgYWRkaXRpb25hbCByZWZlcmVuY2VzIHJlcXVpcmVkIGZvciBlZmZpY2llbnQgY29sbGlzaW9uIGRldGVjdGlvbiByb3V0aW5lcy5cbiAgICAgKlxuICAgICAqIFZlcnRpY2VzIG11c3QgYmUgc3BlY2lmaWVkIGluIGNsb2Nrd2lzZSBvcmRlci5cbiAgICAgKlxuICAgICAqIE5vdGUgdGhhdCB0aGUgYGJvZHlgIGFyZ3VtZW50IGlzIG5vdCBvcHRpb25hbCwgYSBgTWF0dGVyLkJvZHlgIHJlZmVyZW5jZSBtdXN0IGJlIHByb3ZpZGVkLlxuICAgICAqXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge3ZlY3RvcltdfSBwb2ludHNcbiAgICAgKiBAcGFyYW0ge2JvZHl9IGJvZHlcbiAgICAgKi9cbiAgICBWZXJ0aWNlcy5jcmVhdGUgPSBmdW5jdGlvbihwb2ludHMsIGJvZHkpIHtcbiAgICAgICAgdmFyIHZlcnRpY2VzID0gW107XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwb2ludHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwb2ludCA9IHBvaW50c1tpXSxcbiAgICAgICAgICAgICAgICB2ZXJ0ZXggPSB7XG4gICAgICAgICAgICAgICAgICAgIHg6IHBvaW50LngsXG4gICAgICAgICAgICAgICAgICAgIHk6IHBvaW50LnksXG4gICAgICAgICAgICAgICAgICAgIGluZGV4OiBpLFxuICAgICAgICAgICAgICAgICAgICBib2R5OiBib2R5LFxuICAgICAgICAgICAgICAgICAgICBpc0ludGVybmFsOiBmYWxzZVxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHZlcnRpY2VzLnB1c2godmVydGV4KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB2ZXJ0aWNlcztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUGFyc2VzIGEgc3RyaW5nIGNvbnRhaW5pbmcgb3JkZXJlZCB4IHkgcGFpcnMgc2VwYXJhdGVkIGJ5IHNwYWNlcyAoYW5kIG9wdGlvbmFsbHkgY29tbWFzKSwgXG4gICAgICogaW50byBhIGBNYXR0ZXIuVmVydGljZXNgIG9iamVjdCBmb3IgdGhlIGdpdmVuIGBNYXR0ZXIuQm9keWAuXG4gICAgICogRm9yIHBhcnNpbmcgU1ZHIHBhdGhzLCBzZWUgYFN2Zy5wYXRoVG9WZXJ0aWNlc2AuXG4gICAgICogQG1ldGhvZCBmcm9tUGF0aFxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoXG4gICAgICogQHBhcmFtIHtib2R5fSBib2R5XG4gICAgICogQHJldHVybiB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICovXG4gICAgVmVydGljZXMuZnJvbVBhdGggPSBmdW5jdGlvbihwYXRoLCBib2R5KSB7XG4gICAgICAgIHZhciBwYXRoUGF0dGVybiA9IC9MP1xccyooW1xcLVxcZFxcLmVdKylbXFxzLF0qKFtcXC1cXGRcXC5lXSspKi9pZyxcbiAgICAgICAgICAgIHBvaW50cyA9IFtdO1xuXG4gICAgICAgIHBhdGgucmVwbGFjZShwYXRoUGF0dGVybiwgZnVuY3Rpb24obWF0Y2gsIHgsIHkpIHtcbiAgICAgICAgICAgIHBvaW50cy5wdXNoKHsgeDogcGFyc2VGbG9hdCh4KSwgeTogcGFyc2VGbG9hdCh5KSB9KTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIFZlcnRpY2VzLmNyZWF0ZShwb2ludHMsIGJvZHkpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjZW50cmUgKGNlbnRyb2lkKSBvZiB0aGUgc2V0IG9mIHZlcnRpY2VzLlxuICAgICAqIEBtZXRob2QgY2VudHJlXG4gICAgICogQHBhcmFtIHt2ZXJ0aWNlc30gdmVydGljZXNcbiAgICAgKiBAcmV0dXJuIHt2ZWN0b3J9IFRoZSBjZW50cmUgcG9pbnRcbiAgICAgKi9cbiAgICBWZXJ0aWNlcy5jZW50cmUgPSBmdW5jdGlvbih2ZXJ0aWNlcykge1xuICAgICAgICB2YXIgYXJlYSA9IFZlcnRpY2VzLmFyZWEodmVydGljZXMsIHRydWUpLFxuICAgICAgICAgICAgY2VudHJlID0geyB4OiAwLCB5OiAwIH0sXG4gICAgICAgICAgICBjcm9zcyxcbiAgICAgICAgICAgIHRlbXAsXG4gICAgICAgICAgICBqO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmVydGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGogPSAoaSArIDEpICUgdmVydGljZXMubGVuZ3RoO1xuICAgICAgICAgICAgY3Jvc3MgPSBWZWN0b3IuY3Jvc3ModmVydGljZXNbaV0sIHZlcnRpY2VzW2pdKTtcbiAgICAgICAgICAgIHRlbXAgPSBWZWN0b3IubXVsdChWZWN0b3IuYWRkKHZlcnRpY2VzW2ldLCB2ZXJ0aWNlc1tqXSksIGNyb3NzKTtcbiAgICAgICAgICAgIGNlbnRyZSA9IFZlY3Rvci5hZGQoY2VudHJlLCB0ZW1wKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBWZWN0b3IuZGl2KGNlbnRyZSwgNiAqIGFyZWEpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBhdmVyYWdlIChtZWFuKSBvZiB0aGUgc2V0IG9mIHZlcnRpY2VzLlxuICAgICAqIEBtZXRob2QgbWVhblxuICAgICAqIEBwYXJhbSB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICogQHJldHVybiB7dmVjdG9yfSBUaGUgYXZlcmFnZSBwb2ludFxuICAgICAqL1xuICAgIFZlcnRpY2VzLm1lYW4gPSBmdW5jdGlvbih2ZXJ0aWNlcykge1xuICAgICAgICB2YXIgYXZlcmFnZSA9IHsgeDogMCwgeTogMCB9O1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmVydGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGF2ZXJhZ2UueCArPSB2ZXJ0aWNlc1tpXS54O1xuICAgICAgICAgICAgYXZlcmFnZS55ICs9IHZlcnRpY2VzW2ldLnk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gVmVjdG9yLmRpdihhdmVyYWdlLCB2ZXJ0aWNlcy5sZW5ndGgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBhcmVhIG9mIHRoZSBzZXQgb2YgdmVydGljZXMuXG4gICAgICogQG1ldGhvZCBhcmVhXG4gICAgICogQHBhcmFtIHt2ZXJ0aWNlc30gdmVydGljZXNcbiAgICAgKiBAcGFyYW0ge2Jvb2x9IHNpZ25lZFxuICAgICAqIEByZXR1cm4ge251bWJlcn0gVGhlIGFyZWFcbiAgICAgKi9cbiAgICBWZXJ0aWNlcy5hcmVhID0gZnVuY3Rpb24odmVydGljZXMsIHNpZ25lZCkge1xuICAgICAgICB2YXIgYXJlYSA9IDAsXG4gICAgICAgICAgICBqID0gdmVydGljZXMubGVuZ3RoIC0gMTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHZlcnRpY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBhcmVhICs9ICh2ZXJ0aWNlc1tqXS54IC0gdmVydGljZXNbaV0ueCkgKiAodmVydGljZXNbal0ueSArIHZlcnRpY2VzW2ldLnkpO1xuICAgICAgICAgICAgaiA9IGk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoc2lnbmVkKVxuICAgICAgICAgICAgcmV0dXJuIGFyZWEgLyAyO1xuXG4gICAgICAgIHJldHVybiBNYXRoLmFicyhhcmVhKSAvIDI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIG1vbWVudCBvZiBpbmVydGlhIChzZWNvbmQgbW9tZW50IG9mIGFyZWEpIG9mIHRoZSBzZXQgb2YgdmVydGljZXMgZ2l2ZW4gdGhlIHRvdGFsIG1hc3MuXG4gICAgICogQG1ldGhvZCBpbmVydGlhXG4gICAgICogQHBhcmFtIHt2ZXJ0aWNlc30gdmVydGljZXNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbWFzc1xuICAgICAqIEByZXR1cm4ge251bWJlcn0gVGhlIHBvbHlnb24ncyBtb21lbnQgb2YgaW5lcnRpYVxuICAgICAqL1xuICAgIFZlcnRpY2VzLmluZXJ0aWEgPSBmdW5jdGlvbih2ZXJ0aWNlcywgbWFzcykge1xuICAgICAgICB2YXIgbnVtZXJhdG9yID0gMCxcbiAgICAgICAgICAgIGRlbm9taW5hdG9yID0gMCxcbiAgICAgICAgICAgIHYgPSB2ZXJ0aWNlcyxcbiAgICAgICAgICAgIGNyb3NzLFxuICAgICAgICAgICAgajtcblxuICAgICAgICAvLyBmaW5kIHRoZSBwb2x5Z29uJ3MgbW9tZW50IG9mIGluZXJ0aWEsIHVzaW5nIHNlY29uZCBtb21lbnQgb2YgYXJlYVxuICAgICAgICAvLyBmcm9tIGVxdWF0aW9ucyBhdCBodHRwOi8vd3d3LnBoeXNpY3Nmb3J1bXMuY29tL3Nob3d0aHJlYWQucGhwP3Q9MjUyOTNcbiAgICAgICAgZm9yICh2YXIgbiA9IDA7IG4gPCB2Lmxlbmd0aDsgbisrKSB7XG4gICAgICAgICAgICBqID0gKG4gKyAxKSAlIHYubGVuZ3RoO1xuICAgICAgICAgICAgY3Jvc3MgPSBNYXRoLmFicyhWZWN0b3IuY3Jvc3ModltqXSwgdltuXSkpO1xuICAgICAgICAgICAgbnVtZXJhdG9yICs9IGNyb3NzICogKFZlY3Rvci5kb3QodltqXSwgdltqXSkgKyBWZWN0b3IuZG90KHZbal0sIHZbbl0pICsgVmVjdG9yLmRvdCh2W25dLCB2W25dKSk7XG4gICAgICAgICAgICBkZW5vbWluYXRvciArPSBjcm9zcztcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAobWFzcyAvIDYpICogKG51bWVyYXRvciAvIGRlbm9taW5hdG9yKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVHJhbnNsYXRlcyB0aGUgc2V0IG9mIHZlcnRpY2VzIGluLXBsYWNlLlxuICAgICAqIEBtZXRob2QgdHJhbnNsYXRlXG4gICAgICogQHBhcmFtIHt2ZXJ0aWNlc30gdmVydGljZXNcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gdmVjdG9yXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHNjYWxhclxuICAgICAqL1xuICAgIFZlcnRpY2VzLnRyYW5zbGF0ZSA9IGZ1bmN0aW9uKHZlcnRpY2VzLCB2ZWN0b3IsIHNjYWxhcikge1xuICAgICAgICB2YXIgaTtcbiAgICAgICAgaWYgKHNjYWxhcikge1xuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHZlcnRpY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdmVydGljZXNbaV0ueCArPSB2ZWN0b3IueCAqIHNjYWxhcjtcbiAgICAgICAgICAgICAgICB2ZXJ0aWNlc1tpXS55ICs9IHZlY3Rvci55ICogc2NhbGFyO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHZlcnRpY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdmVydGljZXNbaV0ueCArPSB2ZWN0b3IueDtcbiAgICAgICAgICAgICAgICB2ZXJ0aWNlc1tpXS55ICs9IHZlY3Rvci55O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHZlcnRpY2VzO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSb3RhdGVzIHRoZSBzZXQgb2YgdmVydGljZXMgaW4tcGxhY2UuXG4gICAgICogQG1ldGhvZCByb3RhdGVcbiAgICAgKiBAcGFyYW0ge3ZlcnRpY2VzfSB2ZXJ0aWNlc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBhbmdsZVxuICAgICAqIEBwYXJhbSB7dmVjdG9yfSBwb2ludFxuICAgICAqL1xuICAgIFZlcnRpY2VzLnJvdGF0ZSA9IGZ1bmN0aW9uKHZlcnRpY2VzLCBhbmdsZSwgcG9pbnQpIHtcbiAgICAgICAgaWYgKGFuZ2xlID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBjb3MgPSBNYXRoLmNvcyhhbmdsZSksXG4gICAgICAgICAgICBzaW4gPSBNYXRoLnNpbihhbmdsZSk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB2ZXJ0aWNlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIHZlcnRpY2UgPSB2ZXJ0aWNlc1tpXSxcbiAgICAgICAgICAgICAgICBkeCA9IHZlcnRpY2UueCAtIHBvaW50LngsXG4gICAgICAgICAgICAgICAgZHkgPSB2ZXJ0aWNlLnkgLSBwb2ludC55O1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgdmVydGljZS54ID0gcG9pbnQueCArIChkeCAqIGNvcyAtIGR5ICogc2luKTtcbiAgICAgICAgICAgIHZlcnRpY2UueSA9IHBvaW50LnkgKyAoZHggKiBzaW4gKyBkeSAqIGNvcyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdmVydGljZXM7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYHRydWVgIGlmIHRoZSBgcG9pbnRgIGlzIGluc2lkZSB0aGUgc2V0IG9mIGB2ZXJ0aWNlc2AuXG4gICAgICogQG1ldGhvZCBjb250YWluc1xuICAgICAqIEBwYXJhbSB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IHBvaW50XG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgdmVydGljZXMgY29udGFpbnMgcG9pbnQsIG90aGVyd2lzZSBmYWxzZVxuICAgICAqL1xuICAgIFZlcnRpY2VzLmNvbnRhaW5zID0gZnVuY3Rpb24odmVydGljZXMsIHBvaW50KSB7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmVydGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciB2ZXJ0aWNlID0gdmVydGljZXNbaV0sXG4gICAgICAgICAgICAgICAgbmV4dFZlcnRpY2UgPSB2ZXJ0aWNlc1soaSArIDEpICUgdmVydGljZXMubGVuZ3RoXTtcbiAgICAgICAgICAgIGlmICgocG9pbnQueCAtIHZlcnRpY2UueCkgKiAobmV4dFZlcnRpY2UueSAtIHZlcnRpY2UueSkgKyAocG9pbnQueSAtIHZlcnRpY2UueSkgKiAodmVydGljZS54IC0gbmV4dFZlcnRpY2UueCkgPiAwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNjYWxlcyB0aGUgdmVydGljZXMgZnJvbSBhIHBvaW50IChkZWZhdWx0IGlzIGNlbnRyZSkgaW4tcGxhY2UuXG4gICAgICogQG1ldGhvZCBzY2FsZVxuICAgICAqIEBwYXJhbSB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHNjYWxlWFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBzY2FsZVlcbiAgICAgKiBAcGFyYW0ge3ZlY3Rvcn0gcG9pbnRcbiAgICAgKi9cbiAgICBWZXJ0aWNlcy5zY2FsZSA9IGZ1bmN0aW9uKHZlcnRpY2VzLCBzY2FsZVgsIHNjYWxlWSwgcG9pbnQpIHtcbiAgICAgICAgaWYgKHNjYWxlWCA9PT0gMSAmJiBzY2FsZVkgPT09IDEpXG4gICAgICAgICAgICByZXR1cm4gdmVydGljZXM7XG5cbiAgICAgICAgcG9pbnQgPSBwb2ludCB8fCBWZXJ0aWNlcy5jZW50cmUodmVydGljZXMpO1xuXG4gICAgICAgIHZhciB2ZXJ0ZXgsXG4gICAgICAgICAgICBkZWx0YTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHZlcnRpY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2ZXJ0ZXggPSB2ZXJ0aWNlc1tpXTtcbiAgICAgICAgICAgIGRlbHRhID0gVmVjdG9yLnN1Yih2ZXJ0ZXgsIHBvaW50KTtcbiAgICAgICAgICAgIHZlcnRpY2VzW2ldLnggPSBwb2ludC54ICsgZGVsdGEueCAqIHNjYWxlWDtcbiAgICAgICAgICAgIHZlcnRpY2VzW2ldLnkgPSBwb2ludC55ICsgZGVsdGEueSAqIHNjYWxlWTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB2ZXJ0aWNlcztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2hhbWZlcnMgYSBzZXQgb2YgdmVydGljZXMgYnkgZ2l2aW5nIHRoZW0gcm91bmRlZCBjb3JuZXJzLCByZXR1cm5zIGEgbmV3IHNldCBvZiB2ZXJ0aWNlcy5cbiAgICAgKiBUaGUgcmFkaXVzIHBhcmFtZXRlciBpcyBhIHNpbmdsZSBudW1iZXIgb3IgYW4gYXJyYXkgdG8gc3BlY2lmeSB0aGUgcmFkaXVzIGZvciBlYWNoIHZlcnRleC5cbiAgICAgKiBAbWV0aG9kIGNoYW1mZXJcbiAgICAgKiBAcGFyYW0ge3ZlcnRpY2VzfSB2ZXJ0aWNlc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyW119IHJhZGl1c1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBxdWFsaXR5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHF1YWxpdHlNaW5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcXVhbGl0eU1heFxuICAgICAqL1xuICAgIFZlcnRpY2VzLmNoYW1mZXIgPSBmdW5jdGlvbih2ZXJ0aWNlcywgcmFkaXVzLCBxdWFsaXR5LCBxdWFsaXR5TWluLCBxdWFsaXR5TWF4KSB7XG4gICAgICAgIHJhZGl1cyA9IHJhZGl1cyB8fCBbOF07XG5cbiAgICAgICAgaWYgKCFyYWRpdXMubGVuZ3RoKVxuICAgICAgICAgICAgcmFkaXVzID0gW3JhZGl1c107XG5cbiAgICAgICAgLy8gcXVhbGl0eSBkZWZhdWx0cyB0byAtMSwgd2hpY2ggaXMgYXV0b1xuICAgICAgICBxdWFsaXR5ID0gKHR5cGVvZiBxdWFsaXR5ICE9PSAndW5kZWZpbmVkJykgPyBxdWFsaXR5IDogLTE7XG4gICAgICAgIHF1YWxpdHlNaW4gPSBxdWFsaXR5TWluIHx8IDI7XG4gICAgICAgIHF1YWxpdHlNYXggPSBxdWFsaXR5TWF4IHx8IDE0O1xuXG4gICAgICAgIHZhciBuZXdWZXJ0aWNlcyA9IFtdO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmVydGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwcmV2VmVydGV4ID0gdmVydGljZXNbaSAtIDEgPj0gMCA/IGkgLSAxIDogdmVydGljZXMubGVuZ3RoIC0gMV0sXG4gICAgICAgICAgICAgICAgdmVydGV4ID0gdmVydGljZXNbaV0sXG4gICAgICAgICAgICAgICAgbmV4dFZlcnRleCA9IHZlcnRpY2VzWyhpICsgMSkgJSB2ZXJ0aWNlcy5sZW5ndGhdLFxuICAgICAgICAgICAgICAgIGN1cnJlbnRSYWRpdXMgPSByYWRpdXNbaSA8IHJhZGl1cy5sZW5ndGggPyBpIDogcmFkaXVzLmxlbmd0aCAtIDFdO1xuXG4gICAgICAgICAgICBpZiAoY3VycmVudFJhZGl1cyA9PT0gMCkge1xuICAgICAgICAgICAgICAgIG5ld1ZlcnRpY2VzLnB1c2godmVydGV4KTtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHByZXZOb3JtYWwgPSBWZWN0b3Iubm9ybWFsaXNlKHsgXG4gICAgICAgICAgICAgICAgeDogdmVydGV4LnkgLSBwcmV2VmVydGV4LnksIFxuICAgICAgICAgICAgICAgIHk6IHByZXZWZXJ0ZXgueCAtIHZlcnRleC54XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdmFyIG5leHROb3JtYWwgPSBWZWN0b3Iubm9ybWFsaXNlKHsgXG4gICAgICAgICAgICAgICAgeDogbmV4dFZlcnRleC55IC0gdmVydGV4LnksIFxuICAgICAgICAgICAgICAgIHk6IHZlcnRleC54IC0gbmV4dFZlcnRleC54XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdmFyIGRpYWdvbmFsUmFkaXVzID0gTWF0aC5zcXJ0KDIgKiBNYXRoLnBvdyhjdXJyZW50UmFkaXVzLCAyKSksXG4gICAgICAgICAgICAgICAgcmFkaXVzVmVjdG9yID0gVmVjdG9yLm11bHQoQ29tbW9uLmNsb25lKHByZXZOb3JtYWwpLCBjdXJyZW50UmFkaXVzKSxcbiAgICAgICAgICAgICAgICBtaWROb3JtYWwgPSBWZWN0b3Iubm9ybWFsaXNlKFZlY3Rvci5tdWx0KFZlY3Rvci5hZGQocHJldk5vcm1hbCwgbmV4dE5vcm1hbCksIDAuNSkpLFxuICAgICAgICAgICAgICAgIHNjYWxlZFZlcnRleCA9IFZlY3Rvci5zdWIodmVydGV4LCBWZWN0b3IubXVsdChtaWROb3JtYWwsIGRpYWdvbmFsUmFkaXVzKSk7XG5cbiAgICAgICAgICAgIHZhciBwcmVjaXNpb24gPSBxdWFsaXR5O1xuXG4gICAgICAgICAgICBpZiAocXVhbGl0eSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAvLyBhdXRvbWF0aWNhbGx5IGRlY2lkZSBwcmVjaXNpb25cbiAgICAgICAgICAgICAgICBwcmVjaXNpb24gPSBNYXRoLnBvdyhjdXJyZW50UmFkaXVzLCAwLjMyKSAqIDEuNzU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZWNpc2lvbiA9IENvbW1vbi5jbGFtcChwcmVjaXNpb24sIHF1YWxpdHlNaW4sIHF1YWxpdHlNYXgpO1xuXG4gICAgICAgICAgICAvLyB1c2UgYW4gZXZlbiB2YWx1ZSBmb3IgcHJlY2lzaW9uLCBtb3JlIGxpa2VseSB0byByZWR1Y2UgYXhlcyBieSB1c2luZyBzeW1tZXRyeVxuICAgICAgICAgICAgaWYgKHByZWNpc2lvbiAlIDIgPT09IDEpXG4gICAgICAgICAgICAgICAgcHJlY2lzaW9uICs9IDE7XG5cbiAgICAgICAgICAgIHZhciBhbHBoYSA9IE1hdGguYWNvcyhWZWN0b3IuZG90KHByZXZOb3JtYWwsIG5leHROb3JtYWwpKSxcbiAgICAgICAgICAgICAgICB0aGV0YSA9IGFscGhhIC8gcHJlY2lzaW9uO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IHByZWNpc2lvbjsgaisrKSB7XG4gICAgICAgICAgICAgICAgbmV3VmVydGljZXMucHVzaChWZWN0b3IuYWRkKFZlY3Rvci5yb3RhdGUocmFkaXVzVmVjdG9yLCB0aGV0YSAqIGopLCBzY2FsZWRWZXJ0ZXgpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBuZXdWZXJ0aWNlcztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU29ydHMgdGhlIGlucHV0IHZlcnRpY2VzIGludG8gY2xvY2t3aXNlIG9yZGVyIGluIHBsYWNlLlxuICAgICAqIEBtZXRob2QgY2xvY2t3aXNlU29ydFxuICAgICAqIEBwYXJhbSB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICogQHJldHVybiB7dmVydGljZXN9IHZlcnRpY2VzXG4gICAgICovXG4gICAgVmVydGljZXMuY2xvY2t3aXNlU29ydCA9IGZ1bmN0aW9uKHZlcnRpY2VzKSB7XG4gICAgICAgIHZhciBjZW50cmUgPSBWZXJ0aWNlcy5tZWFuKHZlcnRpY2VzKTtcblxuICAgICAgICB2ZXJ0aWNlcy5zb3J0KGZ1bmN0aW9uKHZlcnRleEEsIHZlcnRleEIpIHtcbiAgICAgICAgICAgIHJldHVybiBWZWN0b3IuYW5nbGUoY2VudHJlLCB2ZXJ0ZXhBKSAtIFZlY3Rvci5hbmdsZShjZW50cmUsIHZlcnRleEIpO1xuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gdmVydGljZXM7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgdmVydGljZXMgZm9ybSBhIGNvbnZleCBzaGFwZSAodmVydGljZXMgbXVzdCBiZSBpbiBjbG9ja3dpc2Ugb3JkZXIpLlxuICAgICAqIEBtZXRob2QgaXNDb252ZXhcbiAgICAgKiBAcGFyYW0ge3ZlcnRpY2VzfSB2ZXJ0aWNlc1xuICAgICAqIEByZXR1cm4ge2Jvb2x9IGB0cnVlYCBpZiB0aGUgYHZlcnRpY2VzYCBhcmUgY29udmV4LCBgZmFsc2VgIGlmIG5vdCAob3IgYG51bGxgIGlmIG5vdCBjb21wdXRhYmxlKS5cbiAgICAgKi9cbiAgICBWZXJ0aWNlcy5pc0NvbnZleCA9IGZ1bmN0aW9uKHZlcnRpY2VzKSB7XG4gICAgICAgIC8vIGh0dHA6Ly9wYXVsYm91cmtlLm5ldC9nZW9tZXRyeS9wb2x5Z29ubWVzaC9cbiAgICAgICAgLy8gQ29weXJpZ2h0IChjKSBQYXVsIEJvdXJrZSAodXNlIHBlcm1pdHRlZClcblxuICAgICAgICB2YXIgZmxhZyA9IDAsXG4gICAgICAgICAgICBuID0gdmVydGljZXMubGVuZ3RoLFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGosXG4gICAgICAgICAgICBrLFxuICAgICAgICAgICAgejtcblxuICAgICAgICBpZiAobiA8IDMpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgbjsgaSsrKSB7XG4gICAgICAgICAgICBqID0gKGkgKyAxKSAlIG47XG4gICAgICAgICAgICBrID0gKGkgKyAyKSAlIG47XG4gICAgICAgICAgICB6ID0gKHZlcnRpY2VzW2pdLnggLSB2ZXJ0aWNlc1tpXS54KSAqICh2ZXJ0aWNlc1trXS55IC0gdmVydGljZXNbal0ueSk7XG4gICAgICAgICAgICB6IC09ICh2ZXJ0aWNlc1tqXS55IC0gdmVydGljZXNbaV0ueSkgKiAodmVydGljZXNba10ueCAtIHZlcnRpY2VzW2pdLngpO1xuXG4gICAgICAgICAgICBpZiAoeiA8IDApIHtcbiAgICAgICAgICAgICAgICBmbGFnIHw9IDE7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHogPiAwKSB7XG4gICAgICAgICAgICAgICAgZmxhZyB8PSAyO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoZmxhZyA9PT0gMykge1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChmbGFnICE9PSAwKXtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29udmV4IGh1bGwgb2YgdGhlIGlucHV0IHZlcnRpY2VzIGFzIGEgbmV3IGFycmF5IG9mIHBvaW50cy5cbiAgICAgKiBAbWV0aG9kIGh1bGxcbiAgICAgKiBAcGFyYW0ge3ZlcnRpY2VzfSB2ZXJ0aWNlc1xuICAgICAqIEByZXR1cm4gW3ZlcnRleF0gdmVydGljZXNcbiAgICAgKi9cbiAgICBWZXJ0aWNlcy5odWxsID0gZnVuY3Rpb24odmVydGljZXMpIHtcbiAgICAgICAgLy8gaHR0cDovL2dlb21hbGdvcml0aG1zLmNvbS9hMTAtX2h1bGwtMS5odG1sXG5cbiAgICAgICAgdmFyIHVwcGVyID0gW10sXG4gICAgICAgICAgICBsb3dlciA9IFtdLCBcbiAgICAgICAgICAgIHZlcnRleCxcbiAgICAgICAgICAgIGk7XG5cbiAgICAgICAgLy8gc29ydCB2ZXJ0aWNlcyBvbiB4LWF4aXMgKHktYXhpcyBmb3IgdGllcylcbiAgICAgICAgdmVydGljZXMgPSB2ZXJ0aWNlcy5zbGljZSgwKTtcbiAgICAgICAgdmVydGljZXMuc29ydChmdW5jdGlvbih2ZXJ0ZXhBLCB2ZXJ0ZXhCKSB7XG4gICAgICAgICAgICB2YXIgZHggPSB2ZXJ0ZXhBLnggLSB2ZXJ0ZXhCLng7XG4gICAgICAgICAgICByZXR1cm4gZHggIT09IDAgPyBkeCA6IHZlcnRleEEueSAtIHZlcnRleEIueTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gYnVpbGQgbG93ZXIgaHVsbFxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgdmVydGljZXMubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgIHZlcnRleCA9IHZlcnRpY2VzW2ldO1xuXG4gICAgICAgICAgICB3aGlsZSAobG93ZXIubGVuZ3RoID49IDIgXG4gICAgICAgICAgICAgICAgICAgJiYgVmVjdG9yLmNyb3NzMyhsb3dlcltsb3dlci5sZW5ndGggLSAyXSwgbG93ZXJbbG93ZXIubGVuZ3RoIC0gMV0sIHZlcnRleCkgPD0gMCkge1xuICAgICAgICAgICAgICAgIGxvd2VyLnBvcCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsb3dlci5wdXNoKHZlcnRleCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBidWlsZCB1cHBlciBodWxsXG4gICAgICAgIGZvciAoaSA9IHZlcnRpY2VzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaSAtPSAxKSB7XG4gICAgICAgICAgICB2ZXJ0ZXggPSB2ZXJ0aWNlc1tpXTtcblxuICAgICAgICAgICAgd2hpbGUgKHVwcGVyLmxlbmd0aCA+PSAyIFxuICAgICAgICAgICAgICAgICAgICYmIFZlY3Rvci5jcm9zczModXBwZXJbdXBwZXIubGVuZ3RoIC0gMl0sIHVwcGVyW3VwcGVyLmxlbmd0aCAtIDFdLCB2ZXJ0ZXgpIDw9IDApIHtcbiAgICAgICAgICAgICAgICB1cHBlci5wb3AoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdXBwZXIucHVzaCh2ZXJ0ZXgpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gY29uY2F0ZW5hdGlvbiBvZiB0aGUgbG93ZXIgYW5kIHVwcGVyIGh1bGxzIGdpdmVzIHRoZSBjb252ZXggaHVsbFxuICAgICAgICAvLyBvbWl0IGxhc3QgcG9pbnRzIGJlY2F1c2UgdGhleSBhcmUgcmVwZWF0ZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgb3RoZXIgbGlzdFxuICAgICAgICB1cHBlci5wb3AoKTtcbiAgICAgICAgbG93ZXIucG9wKCk7XG5cbiAgICAgICAgcmV0dXJuIHVwcGVyLmNvbmNhdChsb3dlcik7XG4gICAgfTtcblxufSkoKTtcblxufSx7XCIuLi9jb3JlL0NvbW1vblwiOjE0LFwiLi4vZ2VvbWV0cnkvVmVjdG9yXCI6Mjh9XSwzMDpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG52YXIgTWF0dGVyID0gbW9kdWxlLmV4cG9ydHMgPSBfZGVyZXFfKCcuLi9jb3JlL01hdHRlcicpO1xuXG5NYXR0ZXIuQm9keSA9IF9kZXJlcV8oJy4uL2JvZHkvQm9keScpO1xuTWF0dGVyLkNvbXBvc2l0ZSA9IF9kZXJlcV8oJy4uL2JvZHkvQ29tcG9zaXRlJyk7XG5NYXR0ZXIuV29ybGQgPSBfZGVyZXFfKCcuLi9ib2R5L1dvcmxkJyk7XG5cbk1hdHRlci5Db250YWN0ID0gX2RlcmVxXygnLi4vY29sbGlzaW9uL0NvbnRhY3QnKTtcbk1hdHRlci5EZXRlY3RvciA9IF9kZXJlcV8oJy4uL2NvbGxpc2lvbi9EZXRlY3RvcicpO1xuTWF0dGVyLkdyaWQgPSBfZGVyZXFfKCcuLi9jb2xsaXNpb24vR3JpZCcpO1xuTWF0dGVyLlBhaXJzID0gX2RlcmVxXygnLi4vY29sbGlzaW9uL1BhaXJzJyk7XG5NYXR0ZXIuUGFpciA9IF9kZXJlcV8oJy4uL2NvbGxpc2lvbi9QYWlyJyk7XG5NYXR0ZXIuUXVlcnkgPSBfZGVyZXFfKCcuLi9jb2xsaXNpb24vUXVlcnknKTtcbk1hdHRlci5SZXNvbHZlciA9IF9kZXJlcV8oJy4uL2NvbGxpc2lvbi9SZXNvbHZlcicpO1xuTWF0dGVyLlNBVCA9IF9kZXJlcV8oJy4uL2NvbGxpc2lvbi9TQVQnKTtcblxuTWF0dGVyLkNvbnN0cmFpbnQgPSBfZGVyZXFfKCcuLi9jb25zdHJhaW50L0NvbnN0cmFpbnQnKTtcbk1hdHRlci5Nb3VzZUNvbnN0cmFpbnQgPSBfZGVyZXFfKCcuLi9jb25zdHJhaW50L01vdXNlQ29uc3RyYWludCcpO1xuXG5NYXR0ZXIuQ29tbW9uID0gX2RlcmVxXygnLi4vY29yZS9Db21tb24nKTtcbk1hdHRlci5FbmdpbmUgPSBfZGVyZXFfKCcuLi9jb3JlL0VuZ2luZScpO1xuTWF0dGVyLkV2ZW50cyA9IF9kZXJlcV8oJy4uL2NvcmUvRXZlbnRzJyk7XG5NYXR0ZXIuTW91c2UgPSBfZGVyZXFfKCcuLi9jb3JlL01vdXNlJyk7XG5NYXR0ZXIuUnVubmVyID0gX2RlcmVxXygnLi4vY29yZS9SdW5uZXInKTtcbk1hdHRlci5TbGVlcGluZyA9IF9kZXJlcV8oJy4uL2NvcmUvU2xlZXBpbmcnKTtcbk1hdHRlci5QbHVnaW4gPSBfZGVyZXFfKCcuLi9jb3JlL1BsdWdpbicpO1xuXG5cbk1hdHRlci5Cb2RpZXMgPSBfZGVyZXFfKCcuLi9mYWN0b3J5L0JvZGllcycpO1xuTWF0dGVyLkNvbXBvc2l0ZXMgPSBfZGVyZXFfKCcuLi9mYWN0b3J5L0NvbXBvc2l0ZXMnKTtcblxuTWF0dGVyLkF4ZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9BeGVzJyk7XG5NYXR0ZXIuQm91bmRzID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvQm91bmRzJyk7XG5NYXR0ZXIuU3ZnID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvU3ZnJyk7XG5NYXR0ZXIuVmVjdG9yID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvVmVjdG9yJyk7XG5NYXR0ZXIuVmVydGljZXMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZXJ0aWNlcycpO1xuXG5NYXR0ZXIuUmVuZGVyID0gX2RlcmVxXygnLi4vcmVuZGVyL1JlbmRlcicpO1xuTWF0dGVyLlJlbmRlclBpeGkgPSBfZGVyZXFfKCcuLi9yZW5kZXIvUmVuZGVyUGl4aScpO1xuXG4vLyBhbGlhc2VzXG5cbk1hdHRlci5Xb3JsZC5hZGQgPSBNYXR0ZXIuQ29tcG9zaXRlLmFkZDtcbk1hdHRlci5Xb3JsZC5yZW1vdmUgPSBNYXR0ZXIuQ29tcG9zaXRlLnJlbW92ZTtcbk1hdHRlci5Xb3JsZC5hZGRDb21wb3NpdGUgPSBNYXR0ZXIuQ29tcG9zaXRlLmFkZENvbXBvc2l0ZTtcbk1hdHRlci5Xb3JsZC5hZGRCb2R5ID0gTWF0dGVyLkNvbXBvc2l0ZS5hZGRCb2R5O1xuTWF0dGVyLldvcmxkLmFkZENvbnN0cmFpbnQgPSBNYXR0ZXIuQ29tcG9zaXRlLmFkZENvbnN0cmFpbnQ7XG5NYXR0ZXIuV29ybGQuY2xlYXIgPSBNYXR0ZXIuQ29tcG9zaXRlLmNsZWFyO1xuTWF0dGVyLkVuZ2luZS5ydW4gPSBNYXR0ZXIuUnVubmVyLnJ1bjtcblxufSx7XCIuLi9ib2R5L0JvZHlcIjoxLFwiLi4vYm9keS9Db21wb3NpdGVcIjoyLFwiLi4vYm9keS9Xb3JsZFwiOjMsXCIuLi9jb2xsaXNpb24vQ29udGFjdFwiOjQsXCIuLi9jb2xsaXNpb24vRGV0ZWN0b3JcIjo1LFwiLi4vY29sbGlzaW9uL0dyaWRcIjo2LFwiLi4vY29sbGlzaW9uL1BhaXJcIjo3LFwiLi4vY29sbGlzaW9uL1BhaXJzXCI6OCxcIi4uL2NvbGxpc2lvbi9RdWVyeVwiOjksXCIuLi9jb2xsaXNpb24vUmVzb2x2ZXJcIjoxMCxcIi4uL2NvbGxpc2lvbi9TQVRcIjoxMSxcIi4uL2NvbnN0cmFpbnQvQ29uc3RyYWludFwiOjEyLFwiLi4vY29uc3RyYWludC9Nb3VzZUNvbnN0cmFpbnRcIjoxMyxcIi4uL2NvcmUvQ29tbW9uXCI6MTQsXCIuLi9jb3JlL0VuZ2luZVwiOjE1LFwiLi4vY29yZS9FdmVudHNcIjoxNixcIi4uL2NvcmUvTWF0dGVyXCI6MTcsXCIuLi9jb3JlL01ldHJpY3NcIjoxOCxcIi4uL2NvcmUvTW91c2VcIjoxOSxcIi4uL2NvcmUvUGx1Z2luXCI6MjAsXCIuLi9jb3JlL1J1bm5lclwiOjIxLFwiLi4vY29yZS9TbGVlcGluZ1wiOjIyLFwiLi4vZmFjdG9yeS9Cb2RpZXNcIjoyMyxcIi4uL2ZhY3RvcnkvQ29tcG9zaXRlc1wiOjI0LFwiLi4vZ2VvbWV0cnkvQXhlc1wiOjI1LFwiLi4vZ2VvbWV0cnkvQm91bmRzXCI6MjYsXCIuLi9nZW9tZXRyeS9TdmdcIjoyNyxcIi4uL2dlb21ldHJ5L1ZlY3RvclwiOjI4LFwiLi4vZ2VvbWV0cnkvVmVydGljZXNcIjoyOSxcIi4uL3JlbmRlci9SZW5kZXJcIjozMSxcIi4uL3JlbmRlci9SZW5kZXJQaXhpXCI6MzJ9XSwzMTpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKipcbiogVGhlIGBNYXR0ZXIuUmVuZGVyYCBtb2R1bGUgaXMgYSBzaW1wbGUgSFRNTDUgY2FudmFzIGJhc2VkIHJlbmRlcmVyIGZvciB2aXN1YWxpc2luZyBpbnN0YW5jZXMgb2YgYE1hdHRlci5FbmdpbmVgLlxuKiBJdCBpcyBpbnRlbmRlZCBmb3IgZGV2ZWxvcG1lbnQgYW5kIGRlYnVnZ2luZyBwdXJwb3NlcywgYnV0IG1heSBhbHNvIGJlIHN1aXRhYmxlIGZvciBzaW1wbGUgZ2FtZXMuXG4qIEl0IGluY2x1ZGVzIGEgbnVtYmVyIG9mIGRyYXdpbmcgb3B0aW9ucyBpbmNsdWRpbmcgd2lyZWZyYW1lLCB2ZWN0b3Igd2l0aCBzdXBwb3J0IGZvciBzcHJpdGVzIGFuZCB2aWV3cG9ydHMuXG4qXG4qIEBjbGFzcyBSZW5kZXJcbiovXG5cbnZhciBSZW5kZXIgPSB7fTtcblxubW9kdWxlLmV4cG9ydHMgPSBSZW5kZXI7XG5cbnZhciBDb21tb24gPSBfZGVyZXFfKCcuLi9jb3JlL0NvbW1vbicpO1xudmFyIENvbXBvc2l0ZSA9IF9kZXJlcV8oJy4uL2JvZHkvQ29tcG9zaXRlJyk7XG52YXIgQm91bmRzID0gX2RlcmVxXygnLi4vZ2VvbWV0cnkvQm91bmRzJyk7XG52YXIgRXZlbnRzID0gX2RlcmVxXygnLi4vY29yZS9FdmVudHMnKTtcbnZhciBHcmlkID0gX2RlcmVxXygnLi4vY29sbGlzaW9uL0dyaWQnKTtcbnZhciBWZWN0b3IgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZWN0b3InKTtcbnZhciBNb3VzZSA9IF9kZXJlcV8oJy4uL2NvcmUvTW91c2UnKTtcblxuKGZ1bmN0aW9uKCkge1xuICAgIFxuICAgIHZhciBfcmVxdWVzdEFuaW1hdGlvbkZyYW1lLFxuICAgICAgICBfY2FuY2VsQW5pbWF0aW9uRnJhbWU7XG5cbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgX3JlcXVlc3RBbmltYXRpb25GcmFtZSA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgd2luZG93LndlYmtpdFJlcXVlc3RBbmltYXRpb25GcmFtZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCB3aW5kb3cubW96UmVxdWVzdEFuaW1hdGlvbkZyYW1lIHx8IHdpbmRvdy5tc1JlcXVlc3RBbmltYXRpb25GcmFtZSBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHwgZnVuY3Rpb24oY2FsbGJhY2speyB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHsgY2FsbGJhY2soQ29tbW9uLm5vdygpKTsgfSwgMTAwMCAvIDYwKTsgfTtcbiAgIFxuICAgICAgICBfY2FuY2VsQW5pbWF0aW9uRnJhbWUgPSB3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUgfHwgd2luZG93Lm1vekNhbmNlbEFuaW1hdGlvbkZyYW1lIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCB3aW5kb3cud2Via2l0Q2FuY2VsQW5pbWF0aW9uRnJhbWUgfHwgd2luZG93Lm1zQ2FuY2VsQW5pbWF0aW9uRnJhbWU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyByZW5kZXJlci4gVGhlIG9wdGlvbnMgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0IHNwZWNpZmllcyBhbnkgcHJvcGVydGllcyB5b3Ugd2lzaCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdHMuXG4gICAgICogQWxsIHByb3BlcnRpZXMgaGF2ZSBkZWZhdWx0IHZhbHVlcywgYW5kIG1hbnkgYXJlIHByZS1jYWxjdWxhdGVkIGF1dG9tYXRpY2FsbHkgYmFzZWQgb24gb3RoZXIgcHJvcGVydGllcy5cbiAgICAgKiBTZWUgdGhlIHByb3BlcnRpZXMgc2VjdGlvbiBiZWxvdyBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gd2hhdCB5b3UgY2FuIHBhc3MgdmlhIHRoZSBgb3B0aW9uc2Agb2JqZWN0LlxuICAgICAqIEBtZXRob2QgY3JlYXRlXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRpb25zXVxuICAgICAqIEByZXR1cm4ge3JlbmRlcn0gQSBuZXcgcmVuZGVyZXJcbiAgICAgKi9cbiAgICBSZW5kZXIuY3JlYXRlID0gZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgICAgICBjb250cm9sbGVyOiBSZW5kZXIsXG4gICAgICAgICAgICBlbmdpbmU6IG51bGwsXG4gICAgICAgICAgICBlbGVtZW50OiBudWxsLFxuICAgICAgICAgICAgY2FudmFzOiBudWxsLFxuICAgICAgICAgICAgbW91c2U6IG51bGwsXG4gICAgICAgICAgICBmcmFtZVJlcXVlc3RJZDogbnVsbCxcbiAgICAgICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgICAgICB3aWR0aDogODAwLFxuICAgICAgICAgICAgICAgIGhlaWdodDogNjAwLFxuICAgICAgICAgICAgICAgIHBpeGVsUmF0aW86IDEsXG4gICAgICAgICAgICAgICAgYmFja2dyb3VuZDogJyMxODE4MWQnLFxuICAgICAgICAgICAgICAgIHdpcmVmcmFtZUJhY2tncm91bmQ6ICcjMGYwZjEzJyxcbiAgICAgICAgICAgICAgICBoYXNCb3VuZHM6ICEhb3B0aW9ucy5ib3VuZHMsXG4gICAgICAgICAgICAgICAgZW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgICAgICAgICB3aXJlZnJhbWVzOiB0cnVlLFxuICAgICAgICAgICAgICAgIHNob3dTbGVlcGluZzogdHJ1ZSxcbiAgICAgICAgICAgICAgICBzaG93RGVidWc6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dCcm9hZHBoYXNlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93Qm91bmRzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93VmVsb2NpdHk6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dDb2xsaXNpb25zOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93U2VwYXJhdGlvbnM6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dBeGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93UG9zaXRpb25zOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93QW5nbGVJbmRpY2F0b3I6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dJZHM6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dTaGFkb3dzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93VmVydGV4TnVtYmVyczogZmFsc2UsXG4gICAgICAgICAgICAgICAgc2hvd0NvbnZleEh1bGxzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93SW50ZXJuYWxFZGdlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgc2hvd01vdXNlUG9zaXRpb246IGZhbHNlXG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgdmFyIHJlbmRlciA9IENvbW1vbi5leHRlbmQoZGVmYXVsdHMsIG9wdGlvbnMpO1xuXG4gICAgICAgIGlmIChyZW5kZXIuY2FudmFzKSB7XG4gICAgICAgICAgICByZW5kZXIuY2FudmFzLndpZHRoID0gcmVuZGVyLm9wdGlvbnMud2lkdGggfHwgcmVuZGVyLmNhbnZhcy53aWR0aDtcbiAgICAgICAgICAgIHJlbmRlci5jYW52YXMuaGVpZ2h0ID0gcmVuZGVyLm9wdGlvbnMuaGVpZ2h0IHx8IHJlbmRlci5jYW52YXMuaGVpZ2h0O1xuICAgICAgICB9XG5cbiAgICAgICAgcmVuZGVyLm1vdXNlID0gb3B0aW9ucy5tb3VzZTtcbiAgICAgICAgcmVuZGVyLmVuZ2luZSA9IG9wdGlvbnMuZW5naW5lO1xuICAgICAgICByZW5kZXIuY2FudmFzID0gcmVuZGVyLmNhbnZhcyB8fCBfY3JlYXRlQ2FudmFzKHJlbmRlci5vcHRpb25zLndpZHRoLCByZW5kZXIub3B0aW9ucy5oZWlnaHQpO1xuICAgICAgICByZW5kZXIuY29udGV4dCA9IHJlbmRlci5jYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcbiAgICAgICAgcmVuZGVyLnRleHR1cmVzID0ge307XG5cbiAgICAgICAgcmVuZGVyLmJvdW5kcyA9IHJlbmRlci5ib3VuZHMgfHwgeyBcbiAgICAgICAgICAgIG1pbjogeyBcbiAgICAgICAgICAgICAgICB4OiAwLFxuICAgICAgICAgICAgICAgIHk6IDBcbiAgICAgICAgICAgIH0sIFxuICAgICAgICAgICAgbWF4OiB7IFxuICAgICAgICAgICAgICAgIHg6IHJlbmRlci5jYW52YXMud2lkdGgsXG4gICAgICAgICAgICAgICAgeTogcmVuZGVyLmNhbnZhcy5oZWlnaHRcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBpZiAocmVuZGVyLm9wdGlvbnMucGl4ZWxSYXRpbyAhPT0gMSkge1xuICAgICAgICAgICAgUmVuZGVyLnNldFBpeGVsUmF0aW8ocmVuZGVyLCByZW5kZXIub3B0aW9ucy5waXhlbFJhdGlvKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChDb21tb24uaXNFbGVtZW50KHJlbmRlci5lbGVtZW50KSkge1xuICAgICAgICAgICAgcmVuZGVyLmVsZW1lbnQuYXBwZW5kQ2hpbGQocmVuZGVyLmNhbnZhcyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBDb21tb24ubG9nKCdSZW5kZXIuY3JlYXRlOiBvcHRpb25zLmVsZW1lbnQgd2FzIHVuZGVmaW5lZCwgcmVuZGVyLmNhbnZhcyB3YXMgY3JlYXRlZCBidXQgbm90IGFwcGVuZGVkJywgJ3dhcm4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiByZW5kZXI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENvbnRpbnVvdXNseSB1cGRhdGVzIHRoZSByZW5kZXIgY2FudmFzIG9uIHRoZSBgcmVxdWVzdEFuaW1hdGlvbkZyYW1lYCBldmVudC5cbiAgICAgKiBAbWV0aG9kIHJ1blxuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKi9cbiAgICBSZW5kZXIucnVuID0gZnVuY3Rpb24ocmVuZGVyKSB7XG4gICAgICAgIChmdW5jdGlvbiBsb29wKHRpbWUpe1xuICAgICAgICAgICAgcmVuZGVyLmZyYW1lUmVxdWVzdElkID0gX3JlcXVlc3RBbmltYXRpb25GcmFtZShsb29wKTtcbiAgICAgICAgICAgIFJlbmRlci53b3JsZChyZW5kZXIpO1xuICAgICAgICB9KSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBFbmRzIGV4ZWN1dGlvbiBvZiBgUmVuZGVyLnJ1bmAgb24gdGhlIGdpdmVuIGByZW5kZXJgLCBieSBjYW5jZWxpbmcgdGhlIGFuaW1hdGlvbiBmcmFtZSByZXF1ZXN0IGV2ZW50IGxvb3AuXG4gICAgICogQG1ldGhvZCBzdG9wXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqL1xuICAgIFJlbmRlci5zdG9wID0gZnVuY3Rpb24ocmVuZGVyKSB7XG4gICAgICAgIF9jYW5jZWxBbmltYXRpb25GcmFtZShyZW5kZXIuZnJhbWVSZXF1ZXN0SWQpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBwaXhlbCByYXRpbyBvZiB0aGUgcmVuZGVyZXIgYW5kIHVwZGF0ZXMgdGhlIGNhbnZhcy5cbiAgICAgKiBUbyBhdXRvbWF0aWNhbGx5IGRldGVjdCB0aGUgY29ycmVjdCByYXRpbywgcGFzcyB0aGUgc3RyaW5nIGAnYXV0bydgIGZvciBgcGl4ZWxSYXRpb2AuXG4gICAgICogQG1ldGhvZCBzZXRQaXhlbFJhdGlvXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBwaXhlbFJhdGlvXG4gICAgICovXG4gICAgUmVuZGVyLnNldFBpeGVsUmF0aW8gPSBmdW5jdGlvbihyZW5kZXIsIHBpeGVsUmF0aW8pIHtcbiAgICAgICAgdmFyIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIGNhbnZhcyA9IHJlbmRlci5jYW52YXM7XG5cbiAgICAgICAgaWYgKHBpeGVsUmF0aW8gPT09ICdhdXRvJykge1xuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IF9nZXRQaXhlbFJhdGlvKGNhbnZhcyk7XG4gICAgICAgIH1cblxuICAgICAgICBvcHRpb25zLnBpeGVsUmF0aW8gPSBwaXhlbFJhdGlvO1xuICAgICAgICBjYW52YXMuc2V0QXR0cmlidXRlKCdkYXRhLXBpeGVsLXJhdGlvJywgcGl4ZWxSYXRpbyk7XG4gICAgICAgIGNhbnZhcy53aWR0aCA9IG9wdGlvbnMud2lkdGggKiBwaXhlbFJhdGlvO1xuICAgICAgICBjYW52YXMuaGVpZ2h0ID0gb3B0aW9ucy5oZWlnaHQgKiBwaXhlbFJhdGlvO1xuICAgICAgICBjYW52YXMuc3R5bGUud2lkdGggPSBvcHRpb25zLndpZHRoICsgJ3B4JztcbiAgICAgICAgY2FudmFzLnN0eWxlLmhlaWdodCA9IG9wdGlvbnMuaGVpZ2h0ICsgJ3B4JztcbiAgICAgICAgcmVuZGVyLmNvbnRleHQuc2NhbGUocGl4ZWxSYXRpbywgcGl4ZWxSYXRpbyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFBvc2l0aW9ucyBhbmQgc2l6ZXMgdGhlIHZpZXdwb3J0IGFyb3VuZCB0aGUgZ2l2ZW4gb2JqZWN0IGJvdW5kcy5cbiAgICAgKiBPYmplY3RzIG11c3QgaGF2ZSBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZyBwcm9wZXJ0aWVzOlxuICAgICAqIC0gYG9iamVjdC5ib3VuZHNgXG4gICAgICogLSBgb2JqZWN0LnBvc2l0aW9uYFxuICAgICAqIC0gYG9iamVjdC5taW5gIGFuZCBgb2JqZWN0Lm1heGBcbiAgICAgKiAtIGBvYmplY3QueGAgYW5kIGBvYmplY3QueWBcbiAgICAgKiBAbWV0aG9kIGxvb2tBdFxuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge29iamVjdFtdfSBvYmplY3RzXG4gICAgICogQHBhcmFtIHt2ZWN0b3J9IFtwYWRkaW5nXVxuICAgICAqIEBwYXJhbSB7Ym9vbH0gW2NlbnRlcj10cnVlXVxuICAgICAqL1xuICAgIFJlbmRlci5sb29rQXQgPSBmdW5jdGlvbihyZW5kZXIsIG9iamVjdHMsIHBhZGRpbmcsIGNlbnRlcikge1xuICAgICAgICBjZW50ZXIgPSB0eXBlb2YgY2VudGVyICE9PSAndW5kZWZpbmVkJyA/IGNlbnRlciA6IHRydWU7XG4gICAgICAgIG9iamVjdHMgPSBDb21tb24uaXNBcnJheShvYmplY3RzKSA/IG9iamVjdHMgOiBbb2JqZWN0c107XG4gICAgICAgIHBhZGRpbmcgPSBwYWRkaW5nIHx8IHtcbiAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICB5OiAwXG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gZmluZCBib3VuZHMgb2YgYWxsIG9iamVjdHNcbiAgICAgICAgdmFyIGJvdW5kcyA9IHtcbiAgICAgICAgICAgIG1pbjogeyB4OiBJbmZpbml0eSwgeTogSW5maW5pdHkgfSxcbiAgICAgICAgICAgIG1heDogeyB4OiAtSW5maW5pdHksIHk6IC1JbmZpbml0eSB9XG4gICAgICAgIH07XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvYmplY3RzLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICAgICAgICB2YXIgb2JqZWN0ID0gb2JqZWN0c1tpXSxcbiAgICAgICAgICAgICAgICBtaW4gPSBvYmplY3QuYm91bmRzID8gb2JqZWN0LmJvdW5kcy5taW4gOiAob2JqZWN0Lm1pbiB8fCBvYmplY3QucG9zaXRpb24gfHwgb2JqZWN0KSxcbiAgICAgICAgICAgICAgICBtYXggPSBvYmplY3QuYm91bmRzID8gb2JqZWN0LmJvdW5kcy5tYXggOiAob2JqZWN0Lm1heCB8fCBvYmplY3QucG9zaXRpb24gfHwgb2JqZWN0KTsgXG5cbiAgICAgICAgICAgIGlmIChtaW4gJiYgbWF4KSB7IFxuICAgICAgICAgICAgICAgIGlmIChtaW4ueCA8IGJvdW5kcy5taW4ueCkgXG4gICAgICAgICAgICAgICAgICAgIGJvdW5kcy5taW4ueCA9IG1pbi54O1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBpZiAobWF4LnggPiBib3VuZHMubWF4LngpIFxuICAgICAgICAgICAgICAgICAgICBib3VuZHMubWF4LnggPSBtYXgueDtcblxuICAgICAgICAgICAgICAgIGlmIChtaW4ueSA8IGJvdW5kcy5taW4ueSkgXG4gICAgICAgICAgICAgICAgICAgIGJvdW5kcy5taW4ueSA9IG1pbi55O1xuXG4gICAgICAgICAgICAgICAgaWYgKG1heC55ID4gYm91bmRzLm1heC55KSBcbiAgICAgICAgICAgICAgICAgICAgYm91bmRzLm1heC55ID0gbWF4Lnk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBmaW5kIHJhdGlvc1xuICAgICAgICB2YXIgd2lkdGggPSAoYm91bmRzLm1heC54IC0gYm91bmRzLm1pbi54KSArIDIgKiBwYWRkaW5nLngsXG4gICAgICAgICAgICBoZWlnaHQgPSAoYm91bmRzLm1heC55IC0gYm91bmRzLm1pbi55KSArIDIgKiBwYWRkaW5nLnksXG4gICAgICAgICAgICB2aWV3SGVpZ2h0ID0gcmVuZGVyLmNhbnZhcy5oZWlnaHQsXG4gICAgICAgICAgICB2aWV3V2lkdGggPSByZW5kZXIuY2FudmFzLndpZHRoLFxuICAgICAgICAgICAgb3V0ZXJSYXRpbyA9IHZpZXdXaWR0aCAvIHZpZXdIZWlnaHQsXG4gICAgICAgICAgICBpbm5lclJhdGlvID0gd2lkdGggLyBoZWlnaHQsXG4gICAgICAgICAgICBzY2FsZVggPSAxLFxuICAgICAgICAgICAgc2NhbGVZID0gMTtcblxuICAgICAgICAvLyBmaW5kIHNjYWxlIGZhY3RvclxuICAgICAgICBpZiAoaW5uZXJSYXRpbyA+IG91dGVyUmF0aW8pIHtcbiAgICAgICAgICAgIHNjYWxlWSA9IGlubmVyUmF0aW8gLyBvdXRlclJhdGlvO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc2NhbGVYID0gb3V0ZXJSYXRpbyAvIGlubmVyUmF0aW87XG4gICAgICAgIH1cblxuICAgICAgICAvLyBlbmFibGUgYm91bmRzXG4gICAgICAgIHJlbmRlci5vcHRpb25zLmhhc0JvdW5kcyA9IHRydWU7XG5cbiAgICAgICAgLy8gcG9zaXRpb24gYW5kIHNpemVcbiAgICAgICAgcmVuZGVyLmJvdW5kcy5taW4ueCA9IGJvdW5kcy5taW4ueDtcbiAgICAgICAgcmVuZGVyLmJvdW5kcy5tYXgueCA9IGJvdW5kcy5taW4ueCArIHdpZHRoICogc2NhbGVYO1xuICAgICAgICByZW5kZXIuYm91bmRzLm1pbi55ID0gYm91bmRzLm1pbi55O1xuICAgICAgICByZW5kZXIuYm91bmRzLm1heC55ID0gYm91bmRzLm1pbi55ICsgaGVpZ2h0ICogc2NhbGVZO1xuXG4gICAgICAgIC8vIGNlbnRlclxuICAgICAgICBpZiAoY2VudGVyKSB7XG4gICAgICAgICAgICByZW5kZXIuYm91bmRzLm1pbi54ICs9IHdpZHRoICogMC41IC0gKHdpZHRoICogc2NhbGVYKSAqIDAuNTtcbiAgICAgICAgICAgIHJlbmRlci5ib3VuZHMubWF4LnggKz0gd2lkdGggKiAwLjUgLSAod2lkdGggKiBzY2FsZVgpICogMC41O1xuICAgICAgICAgICAgcmVuZGVyLmJvdW5kcy5taW4ueSArPSBoZWlnaHQgKiAwLjUgLSAoaGVpZ2h0ICogc2NhbGVZKSAqIDAuNTtcbiAgICAgICAgICAgIHJlbmRlci5ib3VuZHMubWF4LnkgKz0gaGVpZ2h0ICogMC41IC0gKGhlaWdodCAqIHNjYWxlWSkgKiAwLjU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBwYWRkaW5nXG4gICAgICAgIHJlbmRlci5ib3VuZHMubWluLnggLT0gcGFkZGluZy54O1xuICAgICAgICByZW5kZXIuYm91bmRzLm1heC54IC09IHBhZGRpbmcueDtcbiAgICAgICAgcmVuZGVyLmJvdW5kcy5taW4ueSAtPSBwYWRkaW5nLnk7XG4gICAgICAgIHJlbmRlci5ib3VuZHMubWF4LnkgLT0gcGFkZGluZy55O1xuXG4gICAgICAgIC8vIHVwZGF0ZSBtb3VzZVxuICAgICAgICBpZiAocmVuZGVyLm1vdXNlKSB7XG4gICAgICAgICAgICBNb3VzZS5zZXRTY2FsZShyZW5kZXIubW91c2UsIHtcbiAgICAgICAgICAgICAgICB4OiAocmVuZGVyLmJvdW5kcy5tYXgueCAtIHJlbmRlci5ib3VuZHMubWluLngpIC8gcmVuZGVyLmNhbnZhcy53aWR0aCxcbiAgICAgICAgICAgICAgICB5OiAocmVuZGVyLmJvdW5kcy5tYXgueSAtIHJlbmRlci5ib3VuZHMubWluLnkpIC8gcmVuZGVyLmNhbnZhcy5oZWlnaHRcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBNb3VzZS5zZXRPZmZzZXQocmVuZGVyLm1vdXNlLCByZW5kZXIuYm91bmRzLm1pbik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwbGllcyB2aWV3cG9ydCB0cmFuc2Zvcm1zIGJhc2VkIG9uIGByZW5kZXIuYm91bmRzYCB0byBhIHJlbmRlciBjb250ZXh0LlxuICAgICAqIEBtZXRob2Qgc3RhcnRWaWV3VHJhbnNmb3JtXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqL1xuICAgIFJlbmRlci5zdGFydFZpZXdUcmFuc2Zvcm0gPSBmdW5jdGlvbihyZW5kZXIpIHtcbiAgICAgICAgdmFyIGJvdW5kc1dpZHRoID0gcmVuZGVyLmJvdW5kcy5tYXgueCAtIHJlbmRlci5ib3VuZHMubWluLngsXG4gICAgICAgICAgICBib3VuZHNIZWlnaHQgPSByZW5kZXIuYm91bmRzLm1heC55IC0gcmVuZGVyLmJvdW5kcy5taW4ueSxcbiAgICAgICAgICAgIGJvdW5kc1NjYWxlWCA9IGJvdW5kc1dpZHRoIC8gcmVuZGVyLm9wdGlvbnMud2lkdGgsXG4gICAgICAgICAgICBib3VuZHNTY2FsZVkgPSBib3VuZHNIZWlnaHQgLyByZW5kZXIub3B0aW9ucy5oZWlnaHQ7XG5cbiAgICAgICAgcmVuZGVyLmNvbnRleHQuc2NhbGUoMSAvIGJvdW5kc1NjYWxlWCwgMSAvIGJvdW5kc1NjYWxlWSk7XG4gICAgICAgIHJlbmRlci5jb250ZXh0LnRyYW5zbGF0ZSgtcmVuZGVyLmJvdW5kcy5taW4ueCwgLXJlbmRlci5ib3VuZHMubWluLnkpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNldHMgYWxsIHRyYW5zZm9ybXMgb24gdGhlIHJlbmRlciBjb250ZXh0LlxuICAgICAqIEBtZXRob2QgZW5kVmlld1RyYW5zZm9ybVxuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKi9cbiAgICBSZW5kZXIuZW5kVmlld1RyYW5zZm9ybSA9IGZ1bmN0aW9uKHJlbmRlcikge1xuICAgICAgICByZW5kZXIuY29udGV4dC5zZXRUcmFuc2Zvcm0ocmVuZGVyLm9wdGlvbnMucGl4ZWxSYXRpbywgMCwgMCwgcmVuZGVyLm9wdGlvbnMucGl4ZWxSYXRpbywgMCwgMCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlbmRlcnMgdGhlIGdpdmVuIGBlbmdpbmVgJ3MgYE1hdHRlci5Xb3JsZGAgb2JqZWN0LlxuICAgICAqIFRoaXMgaXMgdGhlIGVudHJ5IHBvaW50IGZvciBhbGwgcmVuZGVyaW5nIGFuZCBzaG91bGQgYmUgY2FsbGVkIGV2ZXJ5IHRpbWUgdGhlIHNjZW5lIGNoYW5nZXMuXG4gICAgICogQG1ldGhvZCB3b3JsZFxuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKi9cbiAgICBSZW5kZXIud29ybGQgPSBmdW5jdGlvbihyZW5kZXIpIHtcbiAgICAgICAgdmFyIGVuZ2luZSA9IHJlbmRlci5lbmdpbmUsXG4gICAgICAgICAgICB3b3JsZCA9IGVuZ2luZS53b3JsZCxcbiAgICAgICAgICAgIGNhbnZhcyA9IHJlbmRlci5jYW52YXMsXG4gICAgICAgICAgICBjb250ZXh0ID0gcmVuZGVyLmNvbnRleHQsXG4gICAgICAgICAgICBvcHRpb25zID0gcmVuZGVyLm9wdGlvbnMsXG4gICAgICAgICAgICBhbGxCb2RpZXMgPSBDb21wb3NpdGUuYWxsQm9kaWVzKHdvcmxkKSxcbiAgICAgICAgICAgIGFsbENvbnN0cmFpbnRzID0gQ29tcG9zaXRlLmFsbENvbnN0cmFpbnRzKHdvcmxkKSxcbiAgICAgICAgICAgIGJhY2tncm91bmQgPSBvcHRpb25zLndpcmVmcmFtZXMgPyBvcHRpb25zLndpcmVmcmFtZUJhY2tncm91bmQgOiBvcHRpb25zLmJhY2tncm91bmQsXG4gICAgICAgICAgICBib2RpZXMgPSBbXSxcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzID0gW10sXG4gICAgICAgICAgICBpO1xuXG4gICAgICAgIHZhciBldmVudCA9IHtcbiAgICAgICAgICAgIHRpbWVzdGFtcDogZW5naW5lLnRpbWluZy50aW1lc3RhbXBcbiAgICAgICAgfTtcblxuICAgICAgICBFdmVudHMudHJpZ2dlcihyZW5kZXIsICdiZWZvcmVSZW5kZXInLCBldmVudCk7XG5cbiAgICAgICAgLy8gYXBwbHkgYmFja2dyb3VuZCBpZiBpdCBoYXMgY2hhbmdlZFxuICAgICAgICBpZiAocmVuZGVyLmN1cnJlbnRCYWNrZ3JvdW5kICE9PSBiYWNrZ3JvdW5kKVxuICAgICAgICAgICAgX2FwcGx5QmFja2dyb3VuZChyZW5kZXIsIGJhY2tncm91bmQpO1xuXG4gICAgICAgIC8vIGNsZWFyIHRoZSBjYW52YXMgd2l0aCBhIHRyYW5zcGFyZW50IGZpbGwsIHRvIGFsbG93IHRoZSBjYW52YXMgYmFja2dyb3VuZCB0byBzaG93XG4gICAgICAgIGNvbnRleHQuZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uID0gJ3NvdXJjZS1pbic7XG4gICAgICAgIGNvbnRleHQuZmlsbFN0eWxlID0gXCJ0cmFuc3BhcmVudFwiO1xuICAgICAgICBjb250ZXh0LmZpbGxSZWN0KDAsIDAsIGNhbnZhcy53aWR0aCwgY2FudmFzLmhlaWdodCk7XG4gICAgICAgIGNvbnRleHQuZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uID0gJ3NvdXJjZS1vdmVyJztcblxuICAgICAgICAvLyBoYW5kbGUgYm91bmRzXG4gICAgICAgIGlmIChvcHRpb25zLmhhc0JvdW5kcykge1xuICAgICAgICAgICAgLy8gZmlsdGVyIG91dCBib2RpZXMgdGhhdCBhcmUgbm90IGluIHZpZXdcbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhbGxCb2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgYm9keSA9IGFsbEJvZGllc1tpXTtcbiAgICAgICAgICAgICAgICBpZiAoQm91bmRzLm92ZXJsYXBzKGJvZHkuYm91bmRzLCByZW5kZXIuYm91bmRzKSlcbiAgICAgICAgICAgICAgICAgICAgYm9kaWVzLnB1c2goYm9keSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGZpbHRlciBvdXQgY29uc3RyYWludHMgdGhhdCBhcmUgbm90IGluIHZpZXdcbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhbGxDb25zdHJhaW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBjb25zdHJhaW50ID0gYWxsQ29uc3RyYWludHNbaV0sXG4gICAgICAgICAgICAgICAgICAgIGJvZHlBID0gY29uc3RyYWludC5ib2R5QSxcbiAgICAgICAgICAgICAgICAgICAgYm9keUIgPSBjb25zdHJhaW50LmJvZHlCLFxuICAgICAgICAgICAgICAgICAgICBwb2ludEFXb3JsZCA9IGNvbnN0cmFpbnQucG9pbnRBLFxuICAgICAgICAgICAgICAgICAgICBwb2ludEJXb3JsZCA9IGNvbnN0cmFpbnQucG9pbnRCO1xuXG4gICAgICAgICAgICAgICAgaWYgKGJvZHlBKSBwb2ludEFXb3JsZCA9IFZlY3Rvci5hZGQoYm9keUEucG9zaXRpb24sIGNvbnN0cmFpbnQucG9pbnRBKTtcbiAgICAgICAgICAgICAgICBpZiAoYm9keUIpIHBvaW50QldvcmxkID0gVmVjdG9yLmFkZChib2R5Qi5wb3NpdGlvbiwgY29uc3RyYWludC5wb2ludEIpO1xuXG4gICAgICAgICAgICAgICAgaWYgKCFwb2ludEFXb3JsZCB8fCAhcG9pbnRCV29ybGQpXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAgICAgaWYgKEJvdW5kcy5jb250YWlucyhyZW5kZXIuYm91bmRzLCBwb2ludEFXb3JsZCkgfHwgQm91bmRzLmNvbnRhaW5zKHJlbmRlci5ib3VuZHMsIHBvaW50QldvcmxkKSlcbiAgICAgICAgICAgICAgICAgICAgY29uc3RyYWludHMucHVzaChjb25zdHJhaW50KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gdHJhbnNmb3JtIHRoZSB2aWV3XG4gICAgICAgICAgICBSZW5kZXIuc3RhcnRWaWV3VHJhbnNmb3JtKHJlbmRlcik7XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSBtb3VzZVxuICAgICAgICAgICAgaWYgKHJlbmRlci5tb3VzZSkge1xuICAgICAgICAgICAgICAgIE1vdXNlLnNldFNjYWxlKHJlbmRlci5tb3VzZSwge1xuICAgICAgICAgICAgICAgICAgICB4OiAocmVuZGVyLmJvdW5kcy5tYXgueCAtIHJlbmRlci5ib3VuZHMubWluLngpIC8gcmVuZGVyLmNhbnZhcy53aWR0aCxcbiAgICAgICAgICAgICAgICAgICAgeTogKHJlbmRlci5ib3VuZHMubWF4LnkgLSByZW5kZXIuYm91bmRzLm1pbi55KSAvIHJlbmRlci5jYW52YXMuaGVpZ2h0XG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBNb3VzZS5zZXRPZmZzZXQocmVuZGVyLm1vdXNlLCByZW5kZXIuYm91bmRzLm1pbik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdHJhaW50cyA9IGFsbENvbnN0cmFpbnRzO1xuICAgICAgICAgICAgYm9kaWVzID0gYWxsQm9kaWVzO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFvcHRpb25zLndpcmVmcmFtZXMgfHwgKGVuZ2luZS5lbmFibGVTbGVlcGluZyAmJiBvcHRpb25zLnNob3dTbGVlcGluZykpIHtcbiAgICAgICAgICAgIC8vIGZ1bGx5IGZlYXR1cmVkIHJlbmRlcmluZyBvZiBib2RpZXNcbiAgICAgICAgICAgIFJlbmRlci5ib2RpZXMocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMuc2hvd0NvbnZleEh1bGxzKVxuICAgICAgICAgICAgICAgIFJlbmRlci5ib2R5Q29udmV4SHVsbHMocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpO1xuXG4gICAgICAgICAgICAvLyBvcHRpbWlzZWQgbWV0aG9kIGZvciB3aXJlZnJhbWVzIG9ubHlcbiAgICAgICAgICAgIFJlbmRlci5ib2R5V2lyZWZyYW1lcyhyZW5kZXIsIGJvZGllcywgY29udGV4dCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAob3B0aW9ucy5zaG93Qm91bmRzKVxuICAgICAgICAgICAgUmVuZGVyLmJvZHlCb3VuZHMocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpO1xuXG4gICAgICAgIGlmIChvcHRpb25zLnNob3dBeGVzIHx8IG9wdGlvbnMuc2hvd0FuZ2xlSW5kaWNhdG9yKVxuICAgICAgICAgICAgUmVuZGVyLmJvZHlBeGVzKHJlbmRlciwgYm9kaWVzLCBjb250ZXh0KTtcbiAgICAgICAgXG4gICAgICAgIGlmIChvcHRpb25zLnNob3dQb3NpdGlvbnMpXG4gICAgICAgICAgICBSZW5kZXIuYm9keVBvc2l0aW9ucyhyZW5kZXIsIGJvZGllcywgY29udGV4dCk7XG5cbiAgICAgICAgaWYgKG9wdGlvbnMuc2hvd1ZlbG9jaXR5KVxuICAgICAgICAgICAgUmVuZGVyLmJvZHlWZWxvY2l0eShyZW5kZXIsIGJvZGllcywgY29udGV4dCk7XG5cbiAgICAgICAgaWYgKG9wdGlvbnMuc2hvd0lkcylcbiAgICAgICAgICAgIFJlbmRlci5ib2R5SWRzKHJlbmRlciwgYm9kaWVzLCBjb250ZXh0KTtcblxuICAgICAgICBpZiAob3B0aW9ucy5zaG93U2VwYXJhdGlvbnMpXG4gICAgICAgICAgICBSZW5kZXIuc2VwYXJhdGlvbnMocmVuZGVyLCBlbmdpbmUucGFpcnMubGlzdCwgY29udGV4dCk7XG5cbiAgICAgICAgaWYgKG9wdGlvbnMuc2hvd0NvbGxpc2lvbnMpXG4gICAgICAgICAgICBSZW5kZXIuY29sbGlzaW9ucyhyZW5kZXIsIGVuZ2luZS5wYWlycy5saXN0LCBjb250ZXh0KTtcblxuICAgICAgICBpZiAob3B0aW9ucy5zaG93VmVydGV4TnVtYmVycylcbiAgICAgICAgICAgIFJlbmRlci52ZXJ0ZXhOdW1iZXJzKHJlbmRlciwgYm9kaWVzLCBjb250ZXh0KTtcblxuICAgICAgICBpZiAob3B0aW9ucy5zaG93TW91c2VQb3NpdGlvbilcbiAgICAgICAgICAgIFJlbmRlci5tb3VzZVBvc2l0aW9uKHJlbmRlciwgcmVuZGVyLm1vdXNlLCBjb250ZXh0KTtcblxuICAgICAgICBSZW5kZXIuY29uc3RyYWludHMoY29uc3RyYWludHMsIGNvbnRleHQpO1xuXG4gICAgICAgIGlmIChvcHRpb25zLnNob3dCcm9hZHBoYXNlICYmIGVuZ2luZS5icm9hZHBoYXNlLmNvbnRyb2xsZXIgPT09IEdyaWQpXG4gICAgICAgICAgICBSZW5kZXIuZ3JpZChyZW5kZXIsIGVuZ2luZS5icm9hZHBoYXNlLCBjb250ZXh0KTtcblxuICAgICAgICBpZiAob3B0aW9ucy5zaG93RGVidWcpXG4gICAgICAgICAgICBSZW5kZXIuZGVidWcocmVuZGVyLCBjb250ZXh0KTtcblxuICAgICAgICBpZiAob3B0aW9ucy5oYXNCb3VuZHMpIHtcbiAgICAgICAgICAgIC8vIHJldmVydCB2aWV3IHRyYW5zZm9ybXNcbiAgICAgICAgICAgIFJlbmRlci5lbmRWaWV3VHJhbnNmb3JtKHJlbmRlcik7XG4gICAgICAgIH1cblxuICAgICAgICBFdmVudHMudHJpZ2dlcihyZW5kZXIsICdhZnRlclJlbmRlcicsIGV2ZW50KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRGVzY3JpcHRpb25cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgZGVidWdcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLmRlYnVnID0gZnVuY3Rpb24ocmVuZGVyLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBjID0gY29udGV4dCxcbiAgICAgICAgICAgIGVuZ2luZSA9IHJlbmRlci5lbmdpbmUsXG4gICAgICAgICAgICB3b3JsZCA9IGVuZ2luZS53b3JsZCxcbiAgICAgICAgICAgIG1ldHJpY3MgPSBlbmdpbmUubWV0cmljcyxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIGJvZGllcyA9IENvbXBvc2l0ZS5hbGxCb2RpZXMod29ybGQpLFxuICAgICAgICAgICAgc3BhY2UgPSBcIiAgICBcIjtcblxuICAgICAgICBpZiAoZW5naW5lLnRpbWluZy50aW1lc3RhbXAgLSAocmVuZGVyLmRlYnVnVGltZXN0YW1wIHx8IDApID49IDUwMCkge1xuICAgICAgICAgICAgdmFyIHRleHQgPSBcIlwiO1xuXG4gICAgICAgICAgICBpZiAobWV0cmljcy50aW1pbmcpIHtcbiAgICAgICAgICAgICAgICB0ZXh0ICs9IFwiZnBzOiBcIiArIE1hdGgucm91bmQobWV0cmljcy50aW1pbmcuZnBzKSArIHNwYWNlO1xuICAgICAgICAgICAgfVxuXG5cbiAgICAgICAgICAgIHJlbmRlci5kZWJ1Z1N0cmluZyA9IHRleHQ7XG4gICAgICAgICAgICByZW5kZXIuZGVidWdUaW1lc3RhbXAgPSBlbmdpbmUudGltaW5nLnRpbWVzdGFtcDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZW5kZXIuZGVidWdTdHJpbmcpIHtcbiAgICAgICAgICAgIGMuZm9udCA9IFwiMTJweCBBcmlhbFwiO1xuXG4gICAgICAgICAgICBpZiAob3B0aW9ucy53aXJlZnJhbWVzKSB7XG4gICAgICAgICAgICAgICAgYy5maWxsU3R5bGUgPSAncmdiYSgyNTUsMjU1LDI1NSwwLjUpJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYy5maWxsU3R5bGUgPSAncmdiYSgwLDAsMCwwLjUpJztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHNwbGl0ID0gcmVuZGVyLmRlYnVnU3RyaW5nLnNwbGl0KCdcXG4nKTtcblxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzcGxpdC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGMuZmlsbFRleHQoc3BsaXRbaV0sIDUwLCA1MCArIGkgKiAxOCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRGVzY3JpcHRpb25cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgY29uc3RyYWludHNcbiAgICAgKiBAcGFyYW0ge2NvbnN0cmFpbnRbXX0gY29uc3RyYWludHNcbiAgICAgKiBAcGFyYW0ge1JlbmRlcmluZ0NvbnRleHR9IGNvbnRleHRcbiAgICAgKi9cbiAgICBSZW5kZXIuY29uc3RyYWludHMgPSBmdW5jdGlvbihjb25zdHJhaW50cywgY29udGV4dCkge1xuICAgICAgICB2YXIgYyA9IGNvbnRleHQ7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb25zdHJhaW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGNvbnN0cmFpbnQgPSBjb25zdHJhaW50c1tpXTtcblxuICAgICAgICAgICAgaWYgKCFjb25zdHJhaW50LnJlbmRlci52aXNpYmxlIHx8ICFjb25zdHJhaW50LnBvaW50QSB8fCAhY29uc3RyYWludC5wb2ludEIpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIHZhciBib2R5QSA9IGNvbnN0cmFpbnQuYm9keUEsXG4gICAgICAgICAgICAgICAgYm9keUIgPSBjb25zdHJhaW50LmJvZHlCLFxuICAgICAgICAgICAgICAgIHN0YXJ0LFxuICAgICAgICAgICAgICAgIGVuZDtcblxuICAgICAgICAgICAgaWYgKGJvZHlBKSB7XG4gICAgICAgICAgICAgICAgc3RhcnQgPSBWZWN0b3IuYWRkKGJvZHlBLnBvc2l0aW9uLCBjb25zdHJhaW50LnBvaW50QSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHN0YXJ0ID0gY29uc3RyYWludC5wb2ludEE7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChjb25zdHJhaW50LnJlbmRlci50eXBlID09PSAncGluJykge1xuICAgICAgICAgICAgICAgIGMuYmVnaW5QYXRoKCk7XG4gICAgICAgICAgICAgICAgYy5hcmMoc3RhcnQueCwgc3RhcnQueSwgMywgMCwgMiAqIE1hdGguUEkpO1xuICAgICAgICAgICAgICAgIGMuY2xvc2VQYXRoKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlmIChib2R5Qikge1xuICAgICAgICAgICAgICAgICAgICBlbmQgPSBWZWN0b3IuYWRkKGJvZHlCLnBvc2l0aW9uLCBjb25zdHJhaW50LnBvaW50Qik7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZW5kID0gY29uc3RyYWludC5wb2ludEI7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgYy5iZWdpblBhdGgoKTtcbiAgICAgICAgICAgICAgICBjLm1vdmVUbyhzdGFydC54LCBzdGFydC55KTtcblxuICAgICAgICAgICAgICAgIGlmIChjb25zdHJhaW50LnJlbmRlci50eXBlID09PSAnc3ByaW5nJykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgZGVsdGEgPSBWZWN0b3Iuc3ViKGVuZCwgc3RhcnQpLFxuICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsID0gVmVjdG9yLnBlcnAoVmVjdG9yLm5vcm1hbGlzZShkZWx0YSkpLFxuICAgICAgICAgICAgICAgICAgICAgICAgY29pbHMgPSBNYXRoLmNlaWwoQ29tbW9uLmNsYW1wKGNvbnN0cmFpbnQubGVuZ3RoIC8gNSwgMTIsIDIwKSksXG4gICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQ7XG5cbiAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgaiA9IDE7IGogPCBjb2lsczsgaiArPSAxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSBqICUgMiA9PT0gMCA/IDEgOiAtMTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgYy5saW5lVG8oXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnQueCArIGRlbHRhLnggKiAoaiAvIGNvaWxzKSArIG5vcm1hbC54ICogb2Zmc2V0ICogNCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydC55ICsgZGVsdGEueSAqIChqIC8gY29pbHMpICsgbm9ybWFsLnkgKiBvZmZzZXQgKiA0XG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgYy5saW5lVG8oZW5kLngsIGVuZC55KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGNvbnN0cmFpbnQucmVuZGVyLmxpbmVXaWR0aCkge1xuICAgICAgICAgICAgICAgIGMubGluZVdpZHRoID0gY29uc3RyYWludC5yZW5kZXIubGluZVdpZHRoO1xuICAgICAgICAgICAgICAgIGMuc3Ryb2tlU3R5bGUgPSBjb25zdHJhaW50LnJlbmRlci5zdHJva2VTdHlsZTtcbiAgICAgICAgICAgICAgICBjLnN0cm9rZSgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoY29uc3RyYWludC5yZW5kZXIuYW5jaG9ycykge1xuICAgICAgICAgICAgICAgIGMuZmlsbFN0eWxlID0gY29uc3RyYWludC5yZW5kZXIuc3Ryb2tlU3R5bGU7XG4gICAgICAgICAgICAgICAgYy5iZWdpblBhdGgoKTtcbiAgICAgICAgICAgICAgICBjLmFyYyhzdGFydC54LCBzdGFydC55LCAzLCAwLCAyICogTWF0aC5QSSk7XG4gICAgICAgICAgICAgICAgYy5hcmMoZW5kLngsIGVuZC55LCAzLCAwLCAyICogTWF0aC5QSSk7XG4gICAgICAgICAgICAgICAgYy5jbG9zZVBhdGgoKTtcbiAgICAgICAgICAgICAgICBjLmZpbGwoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG4gICAgXG4gICAgLyoqXG4gICAgICogRGVzY3JpcHRpb25cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgYm9keVNoYWRvd3NcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7UmVuZGVyaW5nQ29udGV4dH0gY29udGV4dFxuICAgICAqL1xuICAgIFJlbmRlci5ib2R5U2hhZG93cyA9IGZ1bmN0aW9uKHJlbmRlciwgYm9kaWVzLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBjID0gY29udGV4dCxcbiAgICAgICAgICAgIGVuZ2luZSA9IHJlbmRlci5lbmdpbmU7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5ID0gYm9kaWVzW2ldO1xuXG4gICAgICAgICAgICBpZiAoIWJvZHkucmVuZGVyLnZpc2libGUpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIGlmIChib2R5LmNpcmNsZVJhZGl1cykge1xuICAgICAgICAgICAgICAgIGMuYmVnaW5QYXRoKCk7XG4gICAgICAgICAgICAgICAgYy5hcmMoYm9keS5wb3NpdGlvbi54LCBib2R5LnBvc2l0aW9uLnksIGJvZHkuY2lyY2xlUmFkaXVzLCAwLCAyICogTWF0aC5QSSk7XG4gICAgICAgICAgICAgICAgYy5jbG9zZVBhdGgoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYy5iZWdpblBhdGgoKTtcbiAgICAgICAgICAgICAgICBjLm1vdmVUbyhib2R5LnZlcnRpY2VzWzBdLngsIGJvZHkudmVydGljZXNbMF0ueSk7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaiA9IDE7IGogPCBib2R5LnZlcnRpY2VzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIGMubGluZVRvKGJvZHkudmVydGljZXNbal0ueCwgYm9keS52ZXJ0aWNlc1tqXS55KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYy5jbG9zZVBhdGgoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIGRpc3RhbmNlWCA9IGJvZHkucG9zaXRpb24ueCAtIHJlbmRlci5vcHRpb25zLndpZHRoICogMC41LFxuICAgICAgICAgICAgICAgIGRpc3RhbmNlWSA9IGJvZHkucG9zaXRpb24ueSAtIHJlbmRlci5vcHRpb25zLmhlaWdodCAqIDAuMixcbiAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IE1hdGguYWJzKGRpc3RhbmNlWCkgKyBNYXRoLmFicyhkaXN0YW5jZVkpO1xuXG4gICAgICAgICAgICBjLnNoYWRvd0NvbG9yID0gJ3JnYmEoMCwwLDAsMC4xNSknO1xuICAgICAgICAgICAgYy5zaGFkb3dPZmZzZXRYID0gMC4wNSAqIGRpc3RhbmNlWDtcbiAgICAgICAgICAgIGMuc2hhZG93T2Zmc2V0WSA9IDAuMDUgKiBkaXN0YW5jZVk7XG4gICAgICAgICAgICBjLnNoYWRvd0JsdXIgPSAxICsgMTIgKiBNYXRoLm1pbigxLCBkaXN0YW5jZSAvIDEwMDApO1xuXG4gICAgICAgICAgICBjLmZpbGwoKTtcblxuICAgICAgICAgICAgYy5zaGFkb3dDb2xvciA9IG51bGw7XG4gICAgICAgICAgICBjLnNoYWRvd09mZnNldFggPSBudWxsO1xuICAgICAgICAgICAgYy5zaGFkb3dPZmZzZXRZID0gbnVsbDtcbiAgICAgICAgICAgIGMuc2hhZG93Qmx1ciA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRGVzY3JpcHRpb25cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgYm9kaWVzXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBwYXJhbSB7Ym9keVtdfSBib2RpZXNcbiAgICAgKiBAcGFyYW0ge1JlbmRlcmluZ0NvbnRleHR9IGNvbnRleHRcbiAgICAgKi9cbiAgICBSZW5kZXIuYm9kaWVzID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0LFxuICAgICAgICAgICAgZW5naW5lID0gcmVuZGVyLmVuZ2luZSxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIHNob3dJbnRlcm5hbEVkZ2VzID0gb3B0aW9ucy5zaG93SW50ZXJuYWxFZGdlcyB8fCAhb3B0aW9ucy53aXJlZnJhbWVzLFxuICAgICAgICAgICAgYm9keSxcbiAgICAgICAgICAgIHBhcnQsXG4gICAgICAgICAgICBpLFxuICAgICAgICAgICAgaztcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBib2R5ID0gYm9kaWVzW2ldO1xuXG4gICAgICAgICAgICBpZiAoIWJvZHkucmVuZGVyLnZpc2libGUpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIC8vIGhhbmRsZSBjb21wb3VuZCBwYXJ0c1xuICAgICAgICAgICAgZm9yIChrID0gYm9keS5wYXJ0cy5sZW5ndGggPiAxID8gMSA6IDA7IGsgPCBib2R5LnBhcnRzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgcGFydCA9IGJvZHkucGFydHNba107XG5cbiAgICAgICAgICAgICAgICBpZiAoIXBhcnQucmVuZGVyLnZpc2libGUpXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMuc2hvd1NsZWVwaW5nICYmIGJvZHkuaXNTbGVlcGluZykge1xuICAgICAgICAgICAgICAgICAgICBjLmdsb2JhbEFscGhhID0gMC41ICogcGFydC5yZW5kZXIub3BhY2l0eTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHBhcnQucmVuZGVyLm9wYWNpdHkgIT09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgYy5nbG9iYWxBbHBoYSA9IHBhcnQucmVuZGVyLm9wYWNpdHk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHBhcnQucmVuZGVyLnNwcml0ZSAmJiBwYXJ0LnJlbmRlci5zcHJpdGUudGV4dHVyZSAmJiAhb3B0aW9ucy53aXJlZnJhbWVzKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHBhcnQgc3ByaXRlXG4gICAgICAgICAgICAgICAgICAgIHZhciBzcHJpdGUgPSBwYXJ0LnJlbmRlci5zcHJpdGUsXG4gICAgICAgICAgICAgICAgICAgICAgICB0ZXh0dXJlID0gX2dldFRleHR1cmUocmVuZGVyLCBzcHJpdGUudGV4dHVyZSk7XG5cbiAgICAgICAgICAgICAgICAgICAgYy50cmFuc2xhdGUocGFydC5wb3NpdGlvbi54LCBwYXJ0LnBvc2l0aW9uLnkpOyBcbiAgICAgICAgICAgICAgICAgICAgYy5yb3RhdGUocGFydC5hbmdsZSk7XG5cbiAgICAgICAgICAgICAgICAgICAgYy5kcmF3SW1hZ2UoXG4gICAgICAgICAgICAgICAgICAgICAgICB0ZXh0dXJlLFxuICAgICAgICAgICAgICAgICAgICAgICAgdGV4dHVyZS53aWR0aCAqIC1zcHJpdGUueE9mZnNldCAqIHNwcml0ZS54U2NhbGUsIFxuICAgICAgICAgICAgICAgICAgICAgICAgdGV4dHVyZS5oZWlnaHQgKiAtc3ByaXRlLnlPZmZzZXQgKiBzcHJpdGUueVNjYWxlLCBcbiAgICAgICAgICAgICAgICAgICAgICAgIHRleHR1cmUud2lkdGggKiBzcHJpdGUueFNjYWxlLCBcbiAgICAgICAgICAgICAgICAgICAgICAgIHRleHR1cmUuaGVpZ2h0ICogc3ByaXRlLnlTY2FsZVxuICAgICAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIHJldmVydCB0cmFuc2xhdGlvbiwgaG9wZWZ1bGx5IGZhc3RlciB0aGFuIHNhdmUgLyByZXN0b3JlXG4gICAgICAgICAgICAgICAgICAgIGMucm90YXRlKC1wYXJ0LmFuZ2xlKTtcbiAgICAgICAgICAgICAgICAgICAgYy50cmFuc2xhdGUoLXBhcnQucG9zaXRpb24ueCwgLXBhcnQucG9zaXRpb24ueSk7IFxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHBhcnQgcG9seWdvblxuICAgICAgICAgICAgICAgICAgICBpZiAocGFydC5jaXJjbGVSYWRpdXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGMuYmVnaW5QYXRoKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjLmFyYyhwYXJ0LnBvc2l0aW9uLngsIHBhcnQucG9zaXRpb24ueSwgcGFydC5jaXJjbGVSYWRpdXMsIDAsIDIgKiBNYXRoLlBJKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGMuYmVnaW5QYXRoKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjLm1vdmVUbyhwYXJ0LnZlcnRpY2VzWzBdLngsIHBhcnQudmVydGljZXNbMF0ueSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGogPSAxOyBqIDwgcGFydC52ZXJ0aWNlcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghcGFydC52ZXJ0aWNlc1tqIC0gMV0uaXNJbnRlcm5hbCB8fCBzaG93SW50ZXJuYWxFZGdlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjLmxpbmVUbyhwYXJ0LnZlcnRpY2VzW2pdLngsIHBhcnQudmVydGljZXNbal0ueSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYy5tb3ZlVG8ocGFydC52ZXJ0aWNlc1tqXS54LCBwYXJ0LnZlcnRpY2VzW2pdLnkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJ0LnZlcnRpY2VzW2pdLmlzSW50ZXJuYWwgJiYgIXNob3dJbnRlcm5hbEVkZ2VzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMubW92ZVRvKHBhcnQudmVydGljZXNbKGogKyAxKSAlIHBhcnQudmVydGljZXMubGVuZ3RoXS54LCBwYXJ0LnZlcnRpY2VzWyhqICsgMSkgJSBwYXJ0LnZlcnRpY2VzLmxlbmd0aF0ueSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICBjLmxpbmVUbyhwYXJ0LnZlcnRpY2VzWzBdLngsIHBhcnQudmVydGljZXNbMF0ueSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjLmNsb3NlUGF0aCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCFvcHRpb25zLndpcmVmcmFtZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGMuZmlsbFN0eWxlID0gcGFydC5yZW5kZXIuZmlsbFN0eWxlO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFydC5yZW5kZXIubGluZVdpZHRoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYy5saW5lV2lkdGggPSBwYXJ0LnJlbmRlci5saW5lV2lkdGg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYy5zdHJva2VTdHlsZSA9IHBhcnQucmVuZGVyLnN0cm9rZVN0eWxlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMuc3Ryb2tlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGMuZmlsbCgpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgYy5saW5lV2lkdGggPSAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgYy5zdHJva2VTdHlsZSA9ICcjYmJiJztcbiAgICAgICAgICAgICAgICAgICAgICAgIGMuc3Ryb2tlKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjLmdsb2JhbEFscGhhID0gMTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcHRpbWlzZWQgbWV0aG9kIGZvciBkcmF3aW5nIGJvZHkgd2lyZWZyYW1lcyBpbiBvbmUgcGFzc1xuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBib2R5V2lyZWZyYW1lc1xuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLmJvZHlXaXJlZnJhbWVzID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0LFxuICAgICAgICAgICAgc2hvd0ludGVybmFsRWRnZXMgPSByZW5kZXIub3B0aW9ucy5zaG93SW50ZXJuYWxFZGdlcyxcbiAgICAgICAgICAgIGJvZHksXG4gICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGosXG4gICAgICAgICAgICBrO1xuXG4gICAgICAgIGMuYmVnaW5QYXRoKCk7XG5cbiAgICAgICAgLy8gcmVuZGVyIGFsbCBib2RpZXNcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgYm9keSA9IGJvZGllc1tpXTtcblxuICAgICAgICAgICAgaWYgKCFib2R5LnJlbmRlci52aXNpYmxlKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAvLyBoYW5kbGUgY29tcG91bmQgcGFydHNcbiAgICAgICAgICAgIGZvciAoayA9IGJvZHkucGFydHMubGVuZ3RoID4gMSA/IDEgOiAwOyBrIDwgYm9keS5wYXJ0cy5sZW5ndGg7IGsrKykge1xuICAgICAgICAgICAgICAgIHBhcnQgPSBib2R5LnBhcnRzW2tdO1xuXG4gICAgICAgICAgICAgICAgYy5tb3ZlVG8ocGFydC52ZXJ0aWNlc1swXS54LCBwYXJ0LnZlcnRpY2VzWzBdLnkpO1xuXG4gICAgICAgICAgICAgICAgZm9yIChqID0gMTsgaiA8IHBhcnQudmVydGljZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFwYXJ0LnZlcnRpY2VzW2ogLSAxXS5pc0ludGVybmFsIHx8IHNob3dJbnRlcm5hbEVkZ2VzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjLmxpbmVUbyhwYXJ0LnZlcnRpY2VzW2pdLngsIHBhcnQudmVydGljZXNbal0ueSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjLm1vdmVUbyhwYXJ0LnZlcnRpY2VzW2pdLngsIHBhcnQudmVydGljZXNbal0ueSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAocGFydC52ZXJ0aWNlc1tqXS5pc0ludGVybmFsICYmICFzaG93SW50ZXJuYWxFZGdlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgYy5tb3ZlVG8ocGFydC52ZXJ0aWNlc1soaiArIDEpICUgcGFydC52ZXJ0aWNlcy5sZW5ndGhdLngsIHBhcnQudmVydGljZXNbKGogKyAxKSAlIHBhcnQudmVydGljZXMubGVuZ3RoXS55KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBjLmxpbmVUbyhwYXJ0LnZlcnRpY2VzWzBdLngsIHBhcnQudmVydGljZXNbMF0ueSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjLmxpbmVXaWR0aCA9IDE7XG4gICAgICAgIGMuc3Ryb2tlU3R5bGUgPSAnI2JiYic7XG4gICAgICAgIGMuc3Ryb2tlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9wdGltaXNlZCBtZXRob2QgZm9yIGRyYXdpbmcgYm9keSBjb252ZXggaHVsbCB3aXJlZnJhbWVzIGluIG9uZSBwYXNzXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIGJvZHlDb252ZXhIdWxsc1xuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLmJvZHlDb252ZXhIdWxscyA9IGZ1bmN0aW9uKHJlbmRlciwgYm9kaWVzLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBjID0gY29udGV4dCxcbiAgICAgICAgICAgIGJvZHksXG4gICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGosXG4gICAgICAgICAgICBrO1xuXG4gICAgICAgIGMuYmVnaW5QYXRoKCk7XG5cbiAgICAgICAgLy8gcmVuZGVyIGNvbnZleCBodWxsc1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBib2R5ID0gYm9kaWVzW2ldO1xuXG4gICAgICAgICAgICBpZiAoIWJvZHkucmVuZGVyLnZpc2libGUgfHwgYm9keS5wYXJ0cy5sZW5ndGggPT09IDEpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIGMubW92ZVRvKGJvZHkudmVydGljZXNbMF0ueCwgYm9keS52ZXJ0aWNlc1swXS55KTtcblxuICAgICAgICAgICAgZm9yIChqID0gMTsgaiA8IGJvZHkudmVydGljZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICBjLmxpbmVUbyhib2R5LnZlcnRpY2VzW2pdLngsIGJvZHkudmVydGljZXNbal0ueSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGMubGluZVRvKGJvZHkudmVydGljZXNbMF0ueCwgYm9keS52ZXJ0aWNlc1swXS55KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGMubGluZVdpZHRoID0gMTtcbiAgICAgICAgYy5zdHJva2VTdHlsZSA9ICdyZ2JhKDI1NSwyNTUsMjU1LDAuMiknO1xuICAgICAgICBjLnN0cm9rZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW5kZXJzIGJvZHkgdmVydGV4IG51bWJlcnMuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIHZlcnRleE51bWJlcnNcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7UmVuZGVyaW5nQ29udGV4dH0gY29udGV4dFxuICAgICAqL1xuICAgIFJlbmRlci52ZXJ0ZXhOdW1iZXJzID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0LFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGosXG4gICAgICAgICAgICBrO1xuXG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwYXJ0cyA9IGJvZGllc1tpXS5wYXJ0cztcbiAgICAgICAgICAgIGZvciAoayA9IHBhcnRzLmxlbmd0aCA+IDEgPyAxIDogMDsgayA8IHBhcnRzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgdmFyIHBhcnQgPSBwYXJ0c1trXTtcbiAgICAgICAgICAgICAgICBmb3IgKGogPSAwOyBqIDwgcGFydC52ZXJ0aWNlcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICBjLmZpbGxTdHlsZSA9ICdyZ2JhKDI1NSwyNTUsMjU1LDAuMiknO1xuICAgICAgICAgICAgICAgICAgICBjLmZpbGxUZXh0KGkgKyAnXycgKyBqLCBwYXJ0LnBvc2l0aW9uLnggKyAocGFydC52ZXJ0aWNlc1tqXS54IC0gcGFydC5wb3NpdGlvbi54KSAqIDAuOCwgcGFydC5wb3NpdGlvbi55ICsgKHBhcnQudmVydGljZXNbal0ueSAtIHBhcnQucG9zaXRpb24ueSkgKiAwLjgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW5kZXJzIG1vdXNlIHBvc2l0aW9uLlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBtb3VzZVBvc2l0aW9uXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBwYXJhbSB7bW91c2V9IG1vdXNlXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLm1vdXNlUG9zaXRpb24gPSBmdW5jdGlvbihyZW5kZXIsIG1vdXNlLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBjID0gY29udGV4dDtcbiAgICAgICAgYy5maWxsU3R5bGUgPSAncmdiYSgyNTUsMjU1LDI1NSwwLjgpJztcbiAgICAgICAgYy5maWxsVGV4dChtb3VzZS5wb3NpdGlvbi54ICsgJyAgJyArIG1vdXNlLnBvc2l0aW9uLnksIG1vdXNlLnBvc2l0aW9uLnggKyA1LCBtb3VzZS5wb3NpdGlvbi55IC0gNSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERyYXdzIGJvZHkgYm91bmRzXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIGJvZHlCb3VuZHNcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7UmVuZGVyaW5nQ29udGV4dH0gY29udGV4dFxuICAgICAqL1xuICAgIFJlbmRlci5ib2R5Qm91bmRzID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0LFxuICAgICAgICAgICAgZW5naW5lID0gcmVuZGVyLmVuZ2luZSxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucztcblxuICAgICAgICBjLmJlZ2luUGF0aCgpO1xuXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXTtcblxuICAgICAgICAgICAgaWYgKGJvZHkucmVuZGVyLnZpc2libGUpIHtcbiAgICAgICAgICAgICAgICB2YXIgcGFydHMgPSBib2RpZXNbaV0ucGFydHM7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaiA9IHBhcnRzLmxlbmd0aCA+IDEgPyAxIDogMDsgaiA8IHBhcnRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwYXJ0ID0gcGFydHNbal07XG4gICAgICAgICAgICAgICAgICAgIGMucmVjdChwYXJ0LmJvdW5kcy5taW4ueCwgcGFydC5ib3VuZHMubWluLnksIHBhcnQuYm91bmRzLm1heC54IC0gcGFydC5ib3VuZHMubWluLngsIHBhcnQuYm91bmRzLm1heC55IC0gcGFydC5ib3VuZHMubWluLnkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChvcHRpb25zLndpcmVmcmFtZXMpIHtcbiAgICAgICAgICAgIGMuc3Ryb2tlU3R5bGUgPSAncmdiYSgyNTUsMjU1LDI1NSwwLjA4KSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjLnN0cm9rZVN0eWxlID0gJ3JnYmEoMCwwLDAsMC4xKSc7XG4gICAgICAgIH1cblxuICAgICAgICBjLmxpbmVXaWR0aCA9IDE7XG4gICAgICAgIGMuc3Ryb2tlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERyYXdzIGJvZHkgYW5nbGUgaW5kaWNhdG9ycyBhbmQgYXhlc1xuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBib2R5QXhlc1xuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLmJvZHlBeGVzID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0LFxuICAgICAgICAgICAgZW5naW5lID0gcmVuZGVyLmVuZ2luZSxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIHBhcnQsXG4gICAgICAgICAgICBpLFxuICAgICAgICAgICAgaixcbiAgICAgICAgICAgIGs7XG5cbiAgICAgICAgYy5iZWdpblBhdGgoKTtcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgYm9keSA9IGJvZGllc1tpXSxcbiAgICAgICAgICAgICAgICBwYXJ0cyA9IGJvZHkucGFydHM7XG5cbiAgICAgICAgICAgIGlmICghYm9keS5yZW5kZXIudmlzaWJsZSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgaWYgKG9wdGlvbnMuc2hvd0F4ZXMpIHtcbiAgICAgICAgICAgICAgICAvLyByZW5kZXIgYWxsIGF4ZXNcbiAgICAgICAgICAgICAgICBmb3IgKGogPSBwYXJ0cy5sZW5ndGggPiAxID8gMSA6IDA7IGogPCBwYXJ0cy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICBwYXJ0ID0gcGFydHNbal07XG4gICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCBwYXJ0LmF4ZXMubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBheGlzID0gcGFydC5heGVzW2tdO1xuICAgICAgICAgICAgICAgICAgICAgICAgYy5tb3ZlVG8ocGFydC5wb3NpdGlvbi54LCBwYXJ0LnBvc2l0aW9uLnkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYy5saW5lVG8ocGFydC5wb3NpdGlvbi54ICsgYXhpcy54ICogMjAsIHBhcnQucG9zaXRpb24ueSArIGF4aXMueSAqIDIwKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZm9yIChqID0gcGFydHMubGVuZ3RoID4gMSA/IDEgOiAwOyBqIDwgcGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgcGFydCA9IHBhcnRzW2pdO1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGsgPSAwOyBrIDwgcGFydC5heGVzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyByZW5kZXIgYSBzaW5nbGUgYXhpcyBpbmRpY2F0b3JcbiAgICAgICAgICAgICAgICAgICAgICAgIGMubW92ZVRvKHBhcnQucG9zaXRpb24ueCwgcGFydC5wb3NpdGlvbi55KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGMubGluZVRvKChwYXJ0LnZlcnRpY2VzWzBdLnggKyBwYXJ0LnZlcnRpY2VzW3BhcnQudmVydGljZXMubGVuZ3RoLTFdLngpIC8gMiwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAocGFydC52ZXJ0aWNlc1swXS55ICsgcGFydC52ZXJ0aWNlc1twYXJ0LnZlcnRpY2VzLmxlbmd0aC0xXS55KSAvIDIpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG9wdGlvbnMud2lyZWZyYW1lcykge1xuICAgICAgICAgICAgYy5zdHJva2VTdHlsZSA9ICdpbmRpYW5yZWQnO1xuICAgICAgICAgICAgYy5saW5lV2lkdGggPSAxO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYy5zdHJva2VTdHlsZSA9ICdyZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNCknO1xuICAgICAgICAgICAgYy5nbG9iYWxDb21wb3NpdGVPcGVyYXRpb24gPSAnb3ZlcmxheSc7XG4gICAgICAgICAgICBjLmxpbmVXaWR0aCA9IDI7XG4gICAgICAgIH1cblxuICAgICAgICBjLnN0cm9rZSgpO1xuICAgICAgICBjLmdsb2JhbENvbXBvc2l0ZU9wZXJhdGlvbiA9ICdzb3VyY2Utb3Zlcic7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERyYXdzIGJvZHkgcG9zaXRpb25zXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIGJvZHlQb3NpdGlvbnNcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtib2R5W119IGJvZGllc1xuICAgICAqIEBwYXJhbSB7UmVuZGVyaW5nQ29udGV4dH0gY29udGV4dFxuICAgICAqL1xuICAgIFJlbmRlci5ib2R5UG9zaXRpb25zID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0LFxuICAgICAgICAgICAgZW5naW5lID0gcmVuZGVyLmVuZ2luZSxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIGJvZHksXG4gICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGs7XG5cbiAgICAgICAgYy5iZWdpblBhdGgoKTtcblxuICAgICAgICAvLyByZW5kZXIgY3VycmVudCBwb3NpdGlvbnNcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGJvZGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgYm9keSA9IGJvZGllc1tpXTtcblxuICAgICAgICAgICAgaWYgKCFib2R5LnJlbmRlci52aXNpYmxlKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAvLyBoYW5kbGUgY29tcG91bmQgcGFydHNcbiAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCBib2R5LnBhcnRzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgcGFydCA9IGJvZHkucGFydHNba107XG4gICAgICAgICAgICAgICAgYy5hcmMocGFydC5wb3NpdGlvbi54LCBwYXJ0LnBvc2l0aW9uLnksIDMsIDAsIDIgKiBNYXRoLlBJLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgYy5jbG9zZVBhdGgoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChvcHRpb25zLndpcmVmcmFtZXMpIHtcbiAgICAgICAgICAgIGMuZmlsbFN0eWxlID0gJ2luZGlhbnJlZCc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjLmZpbGxTdHlsZSA9ICdyZ2JhKDAsMCwwLDAuNSknO1xuICAgICAgICB9XG4gICAgICAgIGMuZmlsbCgpO1xuXG4gICAgICAgIGMuYmVnaW5QYXRoKCk7XG5cbiAgICAgICAgLy8gcmVuZGVyIHByZXZpb3VzIHBvc2l0aW9uc1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBib2R5ID0gYm9kaWVzW2ldO1xuICAgICAgICAgICAgaWYgKGJvZHkucmVuZGVyLnZpc2libGUpIHtcbiAgICAgICAgICAgICAgICBjLmFyYyhib2R5LnBvc2l0aW9uUHJldi54LCBib2R5LnBvc2l0aW9uUHJldi55LCAyLCAwLCAyICogTWF0aC5QSSwgZmFsc2UpO1xuICAgICAgICAgICAgICAgIGMuY2xvc2VQYXRoKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjLmZpbGxTdHlsZSA9ICdyZ2JhKDI1NSwxNjUsMCwwLjgpJztcbiAgICAgICAgYy5maWxsKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERyYXdzIGJvZHkgdmVsb2NpdHlcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgYm9keVZlbG9jaXR5XG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBwYXJhbSB7Ym9keVtdfSBib2RpZXNcbiAgICAgKiBAcGFyYW0ge1JlbmRlcmluZ0NvbnRleHR9IGNvbnRleHRcbiAgICAgKi9cbiAgICBSZW5kZXIuYm9keVZlbG9jaXR5ID0gZnVuY3Rpb24ocmVuZGVyLCBib2RpZXMsIGNvbnRleHQpIHtcbiAgICAgICAgdmFyIGMgPSBjb250ZXh0O1xuXG4gICAgICAgIGMuYmVnaW5QYXRoKCk7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBib2RpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBib2R5ID0gYm9kaWVzW2ldO1xuXG4gICAgICAgICAgICBpZiAoIWJvZHkucmVuZGVyLnZpc2libGUpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIGMubW92ZVRvKGJvZHkucG9zaXRpb24ueCwgYm9keS5wb3NpdGlvbi55KTtcbiAgICAgICAgICAgIGMubGluZVRvKGJvZHkucG9zaXRpb24ueCArIChib2R5LnBvc2l0aW9uLnggLSBib2R5LnBvc2l0aW9uUHJldi54KSAqIDIsIGJvZHkucG9zaXRpb24ueSArIChib2R5LnBvc2l0aW9uLnkgLSBib2R5LnBvc2l0aW9uUHJldi55KSAqIDIpO1xuICAgICAgICB9XG5cbiAgICAgICAgYy5saW5lV2lkdGggPSAzO1xuICAgICAgICBjLnN0cm9rZVN0eWxlID0gJ2Nvcm5mbG93ZXJibHVlJztcbiAgICAgICAgYy5zdHJva2UoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRHJhd3MgYm9keSBpZHNcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgYm9keUlkc1xuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge2JvZHlbXX0gYm9kaWVzXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLmJvZHlJZHMgPSBmdW5jdGlvbihyZW5kZXIsIGJvZGllcywgY29udGV4dCkge1xuICAgICAgICB2YXIgYyA9IGNvbnRleHQsXG4gICAgICAgICAgICBpLFxuICAgICAgICAgICAgajtcblxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYm9kaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoIWJvZGllc1tpXS5yZW5kZXIudmlzaWJsZSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgdmFyIHBhcnRzID0gYm9kaWVzW2ldLnBhcnRzO1xuICAgICAgICAgICAgZm9yIChqID0gcGFydHMubGVuZ3RoID4gMSA/IDEgOiAwOyBqIDwgcGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgcGFydCA9IHBhcnRzW2pdO1xuICAgICAgICAgICAgICAgIGMuZm9udCA9IFwiMTJweCBBcmlhbFwiO1xuICAgICAgICAgICAgICAgIGMuZmlsbFN0eWxlID0gJ3JnYmEoMjU1LDI1NSwyNTUsMC41KSc7XG4gICAgICAgICAgICAgICAgYy5maWxsVGV4dChwYXJ0LmlkLCBwYXJ0LnBvc2l0aW9uLnggKyAxMCwgcGFydC5wb3NpdGlvbi55IC0gMTApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERlc2NyaXB0aW9uXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIGNvbGxpc2lvbnNcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtwYWlyW119IHBhaXJzXG4gICAgICogQHBhcmFtIHtSZW5kZXJpbmdDb250ZXh0fSBjb250ZXh0XG4gICAgICovXG4gICAgUmVuZGVyLmNvbGxpc2lvbnMgPSBmdW5jdGlvbihyZW5kZXIsIHBhaXJzLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBjID0gY29udGV4dCxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIHBhaXIsXG4gICAgICAgICAgICBjb2xsaXNpb24sXG4gICAgICAgICAgICBjb3JyZWN0ZWQsXG4gICAgICAgICAgICBib2R5QSxcbiAgICAgICAgICAgIGJvZHlCLFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGo7XG5cbiAgICAgICAgYy5iZWdpblBhdGgoKTtcblxuICAgICAgICAvLyByZW5kZXIgY29sbGlzaW9uIHBvc2l0aW9uc1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFpcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHBhaXIgPSBwYWlyc1tpXTtcblxuICAgICAgICAgICAgaWYgKCFwYWlyLmlzQWN0aXZlKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICBjb2xsaXNpb24gPSBwYWlyLmNvbGxpc2lvbjtcbiAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBwYWlyLmFjdGl2ZUNvbnRhY3RzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgdmFyIGNvbnRhY3QgPSBwYWlyLmFjdGl2ZUNvbnRhY3RzW2pdLFxuICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXggPSBjb250YWN0LnZlcnRleDtcbiAgICAgICAgICAgICAgICBjLnJlY3QodmVydGV4LnggLSAxLjUsIHZlcnRleC55IC0gMS41LCAzLjUsIDMuNSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAob3B0aW9ucy53aXJlZnJhbWVzKSB7XG4gICAgICAgICAgICBjLmZpbGxTdHlsZSA9ICdyZ2JhKDI1NSwyNTUsMjU1LDAuNyknO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYy5maWxsU3R5bGUgPSAnb3JhbmdlJztcbiAgICAgICAgfVxuICAgICAgICBjLmZpbGwoKTtcblxuICAgICAgICBjLmJlZ2luUGF0aCgpO1xuICAgICAgICAgICAgXG4gICAgICAgIC8vIHJlbmRlciBjb2xsaXNpb24gbm9ybWFsc1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgcGFpcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHBhaXIgPSBwYWlyc1tpXTtcblxuICAgICAgICAgICAgaWYgKCFwYWlyLmlzQWN0aXZlKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICBjb2xsaXNpb24gPSBwYWlyLmNvbGxpc2lvbjtcblxuICAgICAgICAgICAgaWYgKHBhaXIuYWN0aXZlQ29udGFjdHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIHZhciBub3JtYWxQb3NYID0gcGFpci5hY3RpdmVDb250YWN0c1swXS52ZXJ0ZXgueCxcbiAgICAgICAgICAgICAgICAgICAgbm9ybWFsUG9zWSA9IHBhaXIuYWN0aXZlQ29udGFjdHNbMF0udmVydGV4Lnk7XG5cbiAgICAgICAgICAgICAgICBpZiAocGFpci5hY3RpdmVDb250YWN0cy5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgICAgICAgICAgICAgbm9ybWFsUG9zWCA9IChwYWlyLmFjdGl2ZUNvbnRhY3RzWzBdLnZlcnRleC54ICsgcGFpci5hY3RpdmVDb250YWN0c1sxXS52ZXJ0ZXgueCkgLyAyO1xuICAgICAgICAgICAgICAgICAgICBub3JtYWxQb3NZID0gKHBhaXIuYWN0aXZlQ29udGFjdHNbMF0udmVydGV4LnkgKyBwYWlyLmFjdGl2ZUNvbnRhY3RzWzFdLnZlcnRleC55KSAvIDI7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIGlmIChjb2xsaXNpb24uYm9keUIgPT09IGNvbGxpc2lvbi5zdXBwb3J0c1swXS5ib2R5IHx8IGNvbGxpc2lvbi5ib2R5QS5pc1N0YXRpYyA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgICAgICBjLm1vdmVUbyhub3JtYWxQb3NYIC0gY29sbGlzaW9uLm5vcm1hbC54ICogOCwgbm9ybWFsUG9zWSAtIGNvbGxpc2lvbi5ub3JtYWwueSAqIDgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGMubW92ZVRvKG5vcm1hbFBvc1ggKyBjb2xsaXNpb24ubm9ybWFsLnggKiA4LCBub3JtYWxQb3NZICsgY29sbGlzaW9uLm5vcm1hbC55ICogOCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgYy5saW5lVG8obm9ybWFsUG9zWCwgbm9ybWFsUG9zWSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAob3B0aW9ucy53aXJlZnJhbWVzKSB7XG4gICAgICAgICAgICBjLnN0cm9rZVN0eWxlID0gJ3JnYmEoMjU1LDE2NSwwLDAuNyknO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYy5zdHJva2VTdHlsZSA9ICdvcmFuZ2UnO1xuICAgICAgICB9XG5cbiAgICAgICAgYy5saW5lV2lkdGggPSAxO1xuICAgICAgICBjLnN0cm9rZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBEZXNjcmlwdGlvblxuICAgICAqIEBwcml2YXRlXG4gICAgICogQG1ldGhvZCBzZXBhcmF0aW9uc1xuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge3BhaXJbXX0gcGFpcnNcbiAgICAgKiBAcGFyYW0ge1JlbmRlcmluZ0NvbnRleHR9IGNvbnRleHRcbiAgICAgKi9cbiAgICBSZW5kZXIuc2VwYXJhdGlvbnMgPSBmdW5jdGlvbihyZW5kZXIsIHBhaXJzLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBjID0gY29udGV4dCxcbiAgICAgICAgICAgIG9wdGlvbnMgPSByZW5kZXIub3B0aW9ucyxcbiAgICAgICAgICAgIHBhaXIsXG4gICAgICAgICAgICBjb2xsaXNpb24sXG4gICAgICAgICAgICBjb3JyZWN0ZWQsXG4gICAgICAgICAgICBib2R5QSxcbiAgICAgICAgICAgIGJvZHlCLFxuICAgICAgICAgICAgaSxcbiAgICAgICAgICAgIGo7XG5cbiAgICAgICAgYy5iZWdpblBhdGgoKTtcblxuICAgICAgICAvLyByZW5kZXIgc2VwYXJhdGlvbnNcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHBhaXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBwYWlyID0gcGFpcnNbaV07XG5cbiAgICAgICAgICAgIGlmICghcGFpci5pc0FjdGl2ZSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgY29sbGlzaW9uID0gcGFpci5jb2xsaXNpb247XG4gICAgICAgICAgICBib2R5QSA9IGNvbGxpc2lvbi5ib2R5QTtcbiAgICAgICAgICAgIGJvZHlCID0gY29sbGlzaW9uLmJvZHlCO1xuXG4gICAgICAgICAgICB2YXIgayA9IDE7XG5cbiAgICAgICAgICAgIGlmICghYm9keUIuaXNTdGF0aWMgJiYgIWJvZHlBLmlzU3RhdGljKSBrID0gMC41O1xuICAgICAgICAgICAgaWYgKGJvZHlCLmlzU3RhdGljKSBrID0gMDtcblxuICAgICAgICAgICAgYy5tb3ZlVG8oYm9keUIucG9zaXRpb24ueCwgYm9keUIucG9zaXRpb24ueSk7XG4gICAgICAgICAgICBjLmxpbmVUbyhib2R5Qi5wb3NpdGlvbi54IC0gY29sbGlzaW9uLnBlbmV0cmF0aW9uLnggKiBrLCBib2R5Qi5wb3NpdGlvbi55IC0gY29sbGlzaW9uLnBlbmV0cmF0aW9uLnkgKiBrKTtcblxuICAgICAgICAgICAgayA9IDE7XG5cbiAgICAgICAgICAgIGlmICghYm9keUIuaXNTdGF0aWMgJiYgIWJvZHlBLmlzU3RhdGljKSBrID0gMC41O1xuICAgICAgICAgICAgaWYgKGJvZHlBLmlzU3RhdGljKSBrID0gMDtcblxuICAgICAgICAgICAgYy5tb3ZlVG8oYm9keUEucG9zaXRpb24ueCwgYm9keUEucG9zaXRpb24ueSk7XG4gICAgICAgICAgICBjLmxpbmVUbyhib2R5QS5wb3NpdGlvbi54ICsgY29sbGlzaW9uLnBlbmV0cmF0aW9uLnggKiBrLCBib2R5QS5wb3NpdGlvbi55ICsgY29sbGlzaW9uLnBlbmV0cmF0aW9uLnkgKiBrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChvcHRpb25zLndpcmVmcmFtZXMpIHtcbiAgICAgICAgICAgIGMuc3Ryb2tlU3R5bGUgPSAncmdiYSgyNTUsMTY1LDAsMC41KSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjLnN0cm9rZVN0eWxlID0gJ29yYW5nZSc7XG4gICAgICAgIH1cbiAgICAgICAgYy5zdHJva2UoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRGVzY3JpcHRpb25cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBtZXRob2QgZ3JpZFxuICAgICAqIEBwYXJhbSB7cmVuZGVyfSByZW5kZXJcbiAgICAgKiBAcGFyYW0ge2dyaWR9IGdyaWRcbiAgICAgKiBAcGFyYW0ge1JlbmRlcmluZ0NvbnRleHR9IGNvbnRleHRcbiAgICAgKi9cbiAgICBSZW5kZXIuZ3JpZCA9IGZ1bmN0aW9uKHJlbmRlciwgZ3JpZCwgY29udGV4dCkge1xuICAgICAgICB2YXIgYyA9IGNvbnRleHQsXG4gICAgICAgICAgICBvcHRpb25zID0gcmVuZGVyLm9wdGlvbnM7XG5cbiAgICAgICAgaWYgKG9wdGlvbnMud2lyZWZyYW1lcykge1xuICAgICAgICAgICAgYy5zdHJva2VTdHlsZSA9ICdyZ2JhKDI1NSwxODAsMCwwLjEpJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGMuc3Ryb2tlU3R5bGUgPSAncmdiYSgyNTUsMTgwLDAsMC41KSc7XG4gICAgICAgIH1cblxuICAgICAgICBjLmJlZ2luUGF0aCgpO1xuXG4gICAgICAgIHZhciBidWNrZXRLZXlzID0gQ29tbW9uLmtleXMoZ3JpZC5idWNrZXRzKTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJ1Y2tldEtleXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBidWNrZXRJZCA9IGJ1Y2tldEtleXNbaV07XG5cbiAgICAgICAgICAgIGlmIChncmlkLmJ1Y2tldHNbYnVja2V0SWRdLmxlbmd0aCA8IDIpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIHZhciByZWdpb24gPSBidWNrZXRJZC5zcGxpdCgvQ3xSLyk7XG4gICAgICAgICAgICBjLnJlY3QoMC41ICsgcGFyc2VJbnQocmVnaW9uWzFdLCAxMCkgKiBncmlkLmJ1Y2tldFdpZHRoLCBcbiAgICAgICAgICAgICAgICAgICAgMC41ICsgcGFyc2VJbnQocmVnaW9uWzJdLCAxMCkgKiBncmlkLmJ1Y2tldEhlaWdodCwgXG4gICAgICAgICAgICAgICAgICAgIGdyaWQuYnVja2V0V2lkdGgsIFxuICAgICAgICAgICAgICAgICAgICBncmlkLmJ1Y2tldEhlaWdodCk7XG4gICAgICAgIH1cblxuICAgICAgICBjLmxpbmVXaWR0aCA9IDE7XG4gICAgICAgIGMuc3Ryb2tlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERlc2NyaXB0aW9uXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAbWV0aG9kIGluc3BlY3RvclxuICAgICAqIEBwYXJhbSB7aW5zcGVjdG9yfSBpbnNwZWN0b3JcbiAgICAgKiBAcGFyYW0ge1JlbmRlcmluZ0NvbnRleHR9IGNvbnRleHRcbiAgICAgKi9cbiAgICBSZW5kZXIuaW5zcGVjdG9yID0gZnVuY3Rpb24oaW5zcGVjdG9yLCBjb250ZXh0KSB7XG4gICAgICAgIHZhciBlbmdpbmUgPSBpbnNwZWN0b3IuZW5naW5lLFxuICAgICAgICAgICAgc2VsZWN0ZWQgPSBpbnNwZWN0b3Iuc2VsZWN0ZWQsXG4gICAgICAgICAgICByZW5kZXIgPSBpbnNwZWN0b3IucmVuZGVyLFxuICAgICAgICAgICAgb3B0aW9ucyA9IHJlbmRlci5vcHRpb25zLFxuICAgICAgICAgICAgYm91bmRzO1xuXG4gICAgICAgIGlmIChvcHRpb25zLmhhc0JvdW5kcykge1xuICAgICAgICAgICAgdmFyIGJvdW5kc1dpZHRoID0gcmVuZGVyLmJvdW5kcy5tYXgueCAtIHJlbmRlci5ib3VuZHMubWluLngsXG4gICAgICAgICAgICAgICAgYm91bmRzSGVpZ2h0ID0gcmVuZGVyLmJvdW5kcy5tYXgueSAtIHJlbmRlci5ib3VuZHMubWluLnksXG4gICAgICAgICAgICAgICAgYm91bmRzU2NhbGVYID0gYm91bmRzV2lkdGggLyByZW5kZXIub3B0aW9ucy53aWR0aCxcbiAgICAgICAgICAgICAgICBib3VuZHNTY2FsZVkgPSBib3VuZHNIZWlnaHQgLyByZW5kZXIub3B0aW9ucy5oZWlnaHQ7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNvbnRleHQuc2NhbGUoMSAvIGJvdW5kc1NjYWxlWCwgMSAvIGJvdW5kc1NjYWxlWSk7XG4gICAgICAgICAgICBjb250ZXh0LnRyYW5zbGF0ZSgtcmVuZGVyLmJvdW5kcy5taW4ueCwgLXJlbmRlci5ib3VuZHMubWluLnkpO1xuICAgICAgICB9XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzZWxlY3RlZC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdmFyIGl0ZW0gPSBzZWxlY3RlZFtpXS5kYXRhO1xuXG4gICAgICAgICAgICBjb250ZXh0LnRyYW5zbGF0ZSgwLjUsIDAuNSk7XG4gICAgICAgICAgICBjb250ZXh0LmxpbmVXaWR0aCA9IDE7XG4gICAgICAgICAgICBjb250ZXh0LnN0cm9rZVN0eWxlID0gJ3JnYmEoMjU1LDE2NSwwLDAuOSknO1xuICAgICAgICAgICAgY29udGV4dC5zZXRMaW5lRGFzaChbMSwyXSk7XG5cbiAgICAgICAgICAgIHN3aXRjaCAoaXRlbS50eXBlKSB7XG5cbiAgICAgICAgICAgIGNhc2UgJ2JvZHknOlxuXG4gICAgICAgICAgICAgICAgLy8gcmVuZGVyIGJvZHkgc2VsZWN0aW9uc1xuICAgICAgICAgICAgICAgIGJvdW5kcyA9IGl0ZW0uYm91bmRzO1xuICAgICAgICAgICAgICAgIGNvbnRleHQuYmVnaW5QYXRoKCk7XG4gICAgICAgICAgICAgICAgY29udGV4dC5yZWN0KE1hdGguZmxvb3IoYm91bmRzLm1pbi54IC0gMyksIE1hdGguZmxvb3IoYm91bmRzLm1pbi55IC0gMyksIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXRoLmZsb29yKGJvdW5kcy5tYXgueCAtIGJvdW5kcy5taW4ueCArIDYpLCBNYXRoLmZsb29yKGJvdW5kcy5tYXgueSAtIGJvdW5kcy5taW4ueSArIDYpKTtcbiAgICAgICAgICAgICAgICBjb250ZXh0LmNsb3NlUGF0aCgpO1xuICAgICAgICAgICAgICAgIGNvbnRleHQuc3Ryb2tlKCk7XG5cbiAgICAgICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgICAgY2FzZSAnY29uc3RyYWludCc6XG5cbiAgICAgICAgICAgICAgICAvLyByZW5kZXIgY29uc3RyYWludCBzZWxlY3Rpb25zXG4gICAgICAgICAgICAgICAgdmFyIHBvaW50ID0gaXRlbS5wb2ludEE7XG4gICAgICAgICAgICAgICAgaWYgKGl0ZW0uYm9keUEpXG4gICAgICAgICAgICAgICAgICAgIHBvaW50ID0gaXRlbS5wb2ludEI7XG4gICAgICAgICAgICAgICAgY29udGV4dC5iZWdpblBhdGgoKTtcbiAgICAgICAgICAgICAgICBjb250ZXh0LmFyYyhwb2ludC54LCBwb2ludC55LCAxMCwgMCwgMiAqIE1hdGguUEkpO1xuICAgICAgICAgICAgICAgIGNvbnRleHQuY2xvc2VQYXRoKCk7XG4gICAgICAgICAgICAgICAgY29udGV4dC5zdHJva2UoKTtcblxuICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnRleHQuc2V0TGluZURhc2goW10pO1xuICAgICAgICAgICAgY29udGV4dC50cmFuc2xhdGUoLTAuNSwgLTAuNSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyByZW5kZXIgc2VsZWN0aW9uIHJlZ2lvblxuICAgICAgICBpZiAoaW5zcGVjdG9yLnNlbGVjdFN0YXJ0ICE9PSBudWxsKSB7XG4gICAgICAgICAgICBjb250ZXh0LnRyYW5zbGF0ZSgwLjUsIDAuNSk7XG4gICAgICAgICAgICBjb250ZXh0LmxpbmVXaWR0aCA9IDE7XG4gICAgICAgICAgICBjb250ZXh0LnN0cm9rZVN0eWxlID0gJ3JnYmEoMjU1LDE2NSwwLDAuNiknO1xuICAgICAgICAgICAgY29udGV4dC5maWxsU3R5bGUgPSAncmdiYSgyNTUsMTY1LDAsMC4xKSc7XG4gICAgICAgICAgICBib3VuZHMgPSBpbnNwZWN0b3Iuc2VsZWN0Qm91bmRzO1xuICAgICAgICAgICAgY29udGV4dC5iZWdpblBhdGgoKTtcbiAgICAgICAgICAgIGNvbnRleHQucmVjdChNYXRoLmZsb29yKGJvdW5kcy5taW4ueCksIE1hdGguZmxvb3IoYm91bmRzLm1pbi55KSwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5mbG9vcihib3VuZHMubWF4LnggLSBib3VuZHMubWluLngpLCBNYXRoLmZsb29yKGJvdW5kcy5tYXgueSAtIGJvdW5kcy5taW4ueSkpO1xuICAgICAgICAgICAgY29udGV4dC5jbG9zZVBhdGgoKTtcbiAgICAgICAgICAgIGNvbnRleHQuc3Ryb2tlKCk7XG4gICAgICAgICAgICBjb250ZXh0LmZpbGwoKTtcbiAgICAgICAgICAgIGNvbnRleHQudHJhbnNsYXRlKC0wLjUsIC0wLjUpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG9wdGlvbnMuaGFzQm91bmRzKVxuICAgICAgICAgICAgY29udGV4dC5zZXRUcmFuc2Zvcm0oMSwgMCwgMCwgMSwgMCwgMCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERlc2NyaXB0aW9uXG4gICAgICogQG1ldGhvZCBfY3JlYXRlQ2FudmFzXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge30gd2lkdGhcbiAgICAgKiBAcGFyYW0ge30gaGVpZ2h0XG4gICAgICogQHJldHVybiBjYW52YXNcbiAgICAgKi9cbiAgICB2YXIgX2NyZWF0ZUNhbnZhcyA9IGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQpIHtcbiAgICAgICAgdmFyIGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgICAgICBjYW52YXMud2lkdGggPSB3aWR0aDtcbiAgICAgICAgY2FudmFzLmhlaWdodCA9IGhlaWdodDtcbiAgICAgICAgY2FudmFzLm9uY29udGV4dG1lbnUgPSBmdW5jdGlvbigpIHsgcmV0dXJuIGZhbHNlOyB9O1xuICAgICAgICBjYW52YXMub25zZWxlY3RzdGFydCA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gZmFsc2U7IH07XG4gICAgICAgIHJldHVybiBjYW52YXM7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdldHMgdGhlIHBpeGVsIHJhdGlvIG9mIHRoZSBjYW52YXMuXG4gICAgICogQG1ldGhvZCBfZ2V0UGl4ZWxSYXRpb1xuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gY2FudmFzXG4gICAgICogQHJldHVybiB7TnVtYmVyfSBwaXhlbCByYXRpb1xuICAgICAqL1xuICAgIHZhciBfZ2V0UGl4ZWxSYXRpbyA9IGZ1bmN0aW9uKGNhbnZhcykge1xuICAgICAgICB2YXIgY29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpLFxuICAgICAgICAgICAgZGV2aWNlUGl4ZWxSYXRpbyA9IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIHx8IDEsXG4gICAgICAgICAgICBiYWNraW5nU3RvcmVQaXhlbFJhdGlvID0gY29udGV4dC53ZWJraXRCYWNraW5nU3RvcmVQaXhlbFJhdGlvIHx8IGNvbnRleHQubW96QmFja2luZ1N0b3JlUGl4ZWxSYXRpb1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCBjb250ZXh0Lm1zQmFja2luZ1N0b3JlUGl4ZWxSYXRpbyB8fCBjb250ZXh0Lm9CYWNraW5nU3RvcmVQaXhlbFJhdGlvXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8IGNvbnRleHQuYmFja2luZ1N0b3JlUGl4ZWxSYXRpbyB8fCAxO1xuXG4gICAgICAgIHJldHVybiBkZXZpY2VQaXhlbFJhdGlvIC8gYmFja2luZ1N0b3JlUGl4ZWxSYXRpbztcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogR2V0cyB0aGUgcmVxdWVzdGVkIHRleHR1cmUgKGFuIEltYWdlKSB2aWEgaXRzIHBhdGhcbiAgICAgKiBAbWV0aG9kIF9nZXRUZXh0dXJlXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge3JlbmRlcn0gcmVuZGVyXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGltYWdlUGF0aFxuICAgICAqIEByZXR1cm4ge0ltYWdlfSB0ZXh0dXJlXG4gICAgICovXG4gICAgdmFyIF9nZXRUZXh0dXJlID0gZnVuY3Rpb24ocmVuZGVyLCBpbWFnZVBhdGgpIHtcbiAgICAgICAgdmFyIGltYWdlID0gcmVuZGVyLnRleHR1cmVzW2ltYWdlUGF0aF07XG5cbiAgICAgICAgaWYgKGltYWdlKVxuICAgICAgICAgICAgcmV0dXJuIGltYWdlO1xuXG4gICAgICAgIGltYWdlID0gcmVuZGVyLnRleHR1cmVzW2ltYWdlUGF0aF0gPSBuZXcgSW1hZ2UoKTtcbiAgICAgICAgaW1hZ2Uuc3JjID0gaW1hZ2VQYXRoO1xuXG4gICAgICAgIHJldHVybiBpbWFnZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwbGllcyB0aGUgYmFja2dyb3VuZCB0byB0aGUgY2FudmFzIHVzaW5nIENTUy5cbiAgICAgKiBAbWV0aG9kIGFwcGx5QmFja2dyb3VuZFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBiYWNrZ3JvdW5kXG4gICAgICovXG4gICAgdmFyIF9hcHBseUJhY2tncm91bmQgPSBmdW5jdGlvbihyZW5kZXIsIGJhY2tncm91bmQpIHtcbiAgICAgICAgdmFyIGNzc0JhY2tncm91bmQgPSBiYWNrZ3JvdW5kO1xuXG4gICAgICAgIGlmICgvKGpwZ3xnaWZ8cG5nKSQvLnRlc3QoYmFja2dyb3VuZCkpXG4gICAgICAgICAgICBjc3NCYWNrZ3JvdW5kID0gJ3VybCgnICsgYmFja2dyb3VuZCArICcpJztcblxuICAgICAgICByZW5kZXIuY2FudmFzLnN0eWxlLmJhY2tncm91bmQgPSBjc3NCYWNrZ3JvdW5kO1xuICAgICAgICByZW5kZXIuY2FudmFzLnN0eWxlLmJhY2tncm91bmRTaXplID0gXCJjb250YWluXCI7XG4gICAgICAgIHJlbmRlci5jdXJyZW50QmFja2dyb3VuZCA9IGJhY2tncm91bmQ7XG4gICAgfTtcblxuICAgIC8qXG4gICAgKlxuICAgICogIEV2ZW50cyBEb2N1bWVudGF0aW9uXG4gICAgKlxuICAgICovXG5cbiAgICAvKipcbiAgICAqIEZpcmVkIGJlZm9yZSByZW5kZXJpbmdcbiAgICAqXG4gICAgKiBAZXZlbnQgYmVmb3JlUmVuZGVyXG4gICAgKiBAcGFyYW0ge30gZXZlbnQgQW4gZXZlbnQgb2JqZWN0XG4gICAgKiBAcGFyYW0ge251bWJlcn0gZXZlbnQudGltZXN0YW1wIFRoZSBlbmdpbmUudGltaW5nLnRpbWVzdGFtcCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5zb3VyY2UgVGhlIHNvdXJjZSBvYmplY3Qgb2YgdGhlIGV2ZW50XG4gICAgKiBAcGFyYW0ge30gZXZlbnQubmFtZSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAgICAqL1xuXG4gICAgLyoqXG4gICAgKiBGaXJlZCBhZnRlciByZW5kZXJpbmdcbiAgICAqXG4gICAgKiBAZXZlbnQgYWZ0ZXJSZW5kZXJcbiAgICAqIEBwYXJhbSB7fSBldmVudCBBbiBldmVudCBvYmplY3RcbiAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudC50aW1lc3RhbXAgVGhlIGVuZ2luZS50aW1pbmcudGltZXN0YW1wIG9mIHRoZSBldmVudFxuICAgICogQHBhcmFtIHt9IGV2ZW50LnNvdXJjZSBUaGUgc291cmNlIG9iamVjdCBvZiB0aGUgZXZlbnRcbiAgICAqIEBwYXJhbSB7fSBldmVudC5uYW1lIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICAgICovXG5cbiAgICAvKlxuICAgICpcbiAgICAqICBQcm9wZXJ0aWVzIERvY3VtZW50YXRpb25cbiAgICAqXG4gICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYmFjay1yZWZlcmVuY2UgdG8gdGhlIGBNYXR0ZXIuUmVuZGVyYCBtb2R1bGUuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgY29udHJvbGxlclxuICAgICAqIEB0eXBlIHJlbmRlclxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSByZWZlcmVuY2UgdG8gdGhlIGBNYXR0ZXIuRW5naW5lYCBpbnN0YW5jZSB0byBiZSB1c2VkLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGVuZ2luZVxuICAgICAqIEB0eXBlIGVuZ2luZVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogQSByZWZlcmVuY2UgdG8gdGhlIGVsZW1lbnQgd2hlcmUgdGhlIGNhbnZhcyBpcyB0byBiZSBpbnNlcnRlZCAoaWYgYHJlbmRlci5jYW52YXNgIGhhcyBub3QgYmVlbiBzcGVjaWZpZWQpXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgZWxlbWVudFxuICAgICAqIEB0eXBlIEhUTUxFbGVtZW50XG4gICAgICogQGRlZmF1bHQgbnVsbFxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogVGhlIGNhbnZhcyBlbGVtZW50IHRvIHJlbmRlciB0by4gSWYgbm90IHNwZWNpZmllZCwgb25lIHdpbGwgYmUgY3JlYXRlZCBpZiBgcmVuZGVyLmVsZW1lbnRgIGhhcyBiZWVuIHNwZWNpZmllZC5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSBjYW52YXNcbiAgICAgKiBAdHlwZSBIVE1MQ2FudmFzRWxlbWVudFxuICAgICAqIEBkZWZhdWx0IG51bGxcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIFRoZSBjb25maWd1cmF0aW9uIG9wdGlvbnMgb2YgdGhlIHJlbmRlcmVyLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IG9wdGlvbnNcbiAgICAgKiBAdHlwZSB7fVxuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogVGhlIHRhcmdldCB3aWR0aCBpbiBwaXhlbHMgb2YgdGhlIGByZW5kZXIuY2FudmFzYCB0byBiZSBjcmVhdGVkLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IG9wdGlvbnMud2lkdGhcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCA4MDBcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIFRoZSB0YXJnZXQgaGVpZ2h0IGluIHBpeGVscyBvZiB0aGUgYHJlbmRlci5jYW52YXNgIHRvIGJlIGNyZWF0ZWQuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgb3B0aW9ucy5oZWlnaHRcbiAgICAgKiBAdHlwZSBudW1iZXJcbiAgICAgKiBAZGVmYXVsdCA2MDBcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgZmxhZyB0aGF0IHNwZWNpZmllcyBpZiBgcmVuZGVyLmJvdW5kc2Agc2hvdWxkIGJlIHVzZWQgd2hlbiByZW5kZXJpbmcuXG4gICAgICpcbiAgICAgKiBAcHJvcGVydHkgb3B0aW9ucy5oYXNCb3VuZHNcbiAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIEEgYEJvdW5kc2Agb2JqZWN0IHRoYXQgc3BlY2lmaWVzIHRoZSBkcmF3aW5nIHZpZXcgcmVnaW9uLiBcbiAgICAgKiBSZW5kZXJpbmcgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IHRyYW5zZm9ybWVkIGFuZCBzY2FsZWQgdG8gZml0IHdpdGhpbiB0aGUgY2FudmFzIHNpemUgKGByZW5kZXIub3B0aW9ucy53aWR0aGAgYW5kIGByZW5kZXIub3B0aW9ucy5oZWlnaHRgKS5cbiAgICAgKiBUaGlzIGFsbG93cyBmb3IgY3JlYXRpbmcgdmlld3MgdGhhdCBjYW4gcGFuIG9yIHpvb20gYXJvdW5kIHRoZSBzY2VuZS5cbiAgICAgKiBZb3UgbXVzdCBhbHNvIHNldCBgcmVuZGVyLm9wdGlvbnMuaGFzQm91bmRzYCB0byBgdHJ1ZWAgdG8gZW5hYmxlIGJvdW5kZWQgcmVuZGVyaW5nLlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGJvdW5kc1xuICAgICAqIEB0eXBlIGJvdW5kc1xuICAgICAqL1xuXG4gICAgLyoqXG4gICAgICogVGhlIDJkIHJlbmRlcmluZyBjb250ZXh0IGZyb20gdGhlIGByZW5kZXIuY2FudmFzYCBlbGVtZW50LlxuICAgICAqXG4gICAgICogQHByb3BlcnR5IGNvbnRleHRcbiAgICAgKiBAdHlwZSBDYW52YXNSZW5kZXJpbmdDb250ZXh0MkRcbiAgICAgKi9cblxuICAgIC8qKlxuICAgICAqIFRoZSBzcHJpdGUgdGV4dHVyZSBjYWNoZS5cbiAgICAgKlxuICAgICAqIEBwcm9wZXJ0eSB0ZXh0dXJlc1xuICAgICAqIEB0eXBlIHt9XG4gICAgICovXG5cbn0pKCk7XG5cbn0se1wiLi4vYm9keS9Db21wb3NpdGVcIjoyLFwiLi4vY29sbGlzaW9uL0dyaWRcIjo2LFwiLi4vY29yZS9Db21tb25cIjoxNCxcIi4uL2NvcmUvRXZlbnRzXCI6MTYsXCIuLi9jb3JlL01vdXNlXCI6MTksXCIuLi9nZW9tZXRyeS9Cb3VuZHNcIjoyNixcIi4uL2dlb21ldHJ5L1ZlY3RvclwiOjI4fV0sMzI6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLyoqXG4qIFRoZSBgTWF0dGVyLlJlbmRlclBpeGlgIG1vZHVsZSBpcyBhbiBleGFtcGxlIHJlbmRlcmVyIHVzaW5nIHBpeGkuanMuXG4qIFNlZSBhbHNvIGBNYXR0ZXIuUmVuZGVyYCBmb3IgYSBjYW52YXMgYmFzZWQgcmVuZGVyZXIuXG4qXG4qIEBjbGFzcyBSZW5kZXJQaXhpXG4qIEBkZXByZWNhdGVkIHRoZSBNYXR0ZXIuUmVuZGVyUGl4aSBtb2R1bGUgd2lsbCBzb29uIGJlIHJlbW92ZWQgZnJvbSB0aGUgTWF0dGVyLmpzIGNvcmUuXG4qIEl0IHdpbGwgbGlrZWx5IGJlIG1vdmVkIHRvIGl0cyBvd24gcmVwb3NpdG9yeSAoYnV0IG1haW50ZW5hbmNlIHdpbGwgYmUgbGltaXRlZCkuXG4qL1xuXG52YXIgUmVuZGVyUGl4aSA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJlbmRlclBpeGk7XG5cbnZhciBCb3VuZHMgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9Cb3VuZHMnKTtcbnZhciBDb21wb3NpdGUgPSBfZGVyZXFfKCcuLi9ib2R5L0NvbXBvc2l0ZScpO1xudmFyIENvbW1vbiA9IF9kZXJlcV8oJy4uL2NvcmUvQ29tbW9uJyk7XG52YXIgRXZlbnRzID0gX2RlcmVxXygnLi4vY29yZS9FdmVudHMnKTtcbnZhciBWZWN0b3IgPSBfZGVyZXFfKCcuLi9nZW9tZXRyeS9WZWN0b3InKTtcblxuKGZ1bmN0aW9uKCkge1xuXG4gICAgdmFyIF9yZXF1ZXN0QW5pbWF0aW9uRnJhbWUsXG4gICAgICAgIF9jYW5jZWxBbmltYXRpb25GcmFtZTtcblxuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICBfcmVxdWVzdEFuaW1hdGlvbkZyYW1lID0gd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSB8fCB3aW5kb3cud2Via2l0UmVxdWVzdEFuaW1hdGlvbkZyYW1lXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8IHdpbmRvdy5tb3pSZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgd2luZG93Lm1zUmVxdWVzdEFuaW1hdGlvbkZyYW1lIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCBmdW5jdGlvbihjYWxsYmFjayl7IHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkgeyBjYWxsYmFjayhDb21tb24ubm93KCkpOyB9LCAxMDAwIC8gNjApOyB9O1xuICAgXG4gICAgICAgIF9jYW5jZWxBbmltYXRpb25GcmFtZSA9IHdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZSB8fCB3aW5kb3cubW96Q2FuY2VsQW5pbWF0aW9uRnJhbWUgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8IHdpbmRvdy53ZWJraXRDYW5jZWxBbmltYXRpb25GcmFtZSB8fCB3aW5kb3cubXNDYW5jZWxBbmltYXRpb25GcmFtZTtcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBQaXhpLmpzIFdlYkdMIHJlbmRlcmVyXG4gICAgICogQG1ldGhvZCBjcmVhdGVcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9uc1xuICAgICAqIEByZXR1cm4ge1JlbmRlclBpeGl9IEEgbmV3IHJlbmRlcmVyXG4gICAgICogQGRlcHJlY2F0ZWRcbiAgICAgKi9cbiAgICBSZW5kZXJQaXhpLmNyZWF0ZSA9IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICAgICAgQ29tbW9uLndhcm4oJ1JlbmRlclBpeGkuY3JlYXRlOiBNYXR0ZXIuUmVuZGVyUGl4aSBpcyBkZXByZWNhdGVkIChzZWUgZG9jcyknKTtcblxuICAgICAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgICAgICBjb250cm9sbGVyOiBSZW5kZXJQaXhpLFxuICAgICAgICAgICAgZW5naW5lOiBudWxsLFxuICAgICAgICAgICAgZWxlbWVudDogbnVsbCxcbiAgICAgICAgICAgIGZyYW1lUmVxdWVzdElkOiBudWxsLFxuICAgICAgICAgICAgY2FudmFzOiBudWxsLFxuICAgICAgICAgICAgcmVuZGVyZXI6IG51bGwsXG4gICAgICAgICAgICBjb250YWluZXI6IG51bGwsXG4gICAgICAgICAgICBzcHJpdGVDb250YWluZXI6IG51bGwsXG4gICAgICAgICAgICBwaXhpT3B0aW9uczogbnVsbCxcbiAgICAgICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgICAgICB3aWR0aDogODAwLFxuICAgICAgICAgICAgICAgIGhlaWdodDogNjAwLFxuICAgICAgICAgICAgICAgIGJhY2tncm91bmQ6ICcjZmFmYWZhJyxcbiAgICAgICAgICAgICAgICB3aXJlZnJhbWVCYWNrZ3JvdW5kOiAnIzIyMicsXG4gICAgICAgICAgICAgICAgaGFzQm91bmRzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICAgICAgICAgIHdpcmVmcmFtZXM6IHRydWUsXG4gICAgICAgICAgICAgICAgc2hvd1NsZWVwaW5nOiB0cnVlLFxuICAgICAgICAgICAgICAgIHNob3dEZWJ1ZzogZmFsc2UsXG4gICAgICAgICAgICAgICAgc2hvd0Jyb2FkcGhhc2U6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dCb3VuZHM6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dWZWxvY2l0eTogZmFsc2UsXG4gICAgICAgICAgICAgICAgc2hvd0NvbGxpc2lvbnM6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dBeGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93UG9zaXRpb25zOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBzaG93QW5nbGVJbmRpY2F0b3I6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dJZHM6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHNob3dTaGFkb3dzOiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIHZhciByZW5kZXIgPSBDb21tb24uZXh0ZW5kKGRlZmF1bHRzLCBvcHRpb25zKSxcbiAgICAgICAgICAgIHRyYW5zcGFyZW50ID0gIXJlbmRlci5vcHRpb25zLndpcmVmcmFtZXMgJiYgcmVuZGVyLm9wdGlvbnMuYmFja2dyb3VuZCA9PT0gJ3RyYW5zcGFyZW50JztcblxuICAgICAgICAvLyBpbml0IHBpeGlcbiAgICAgICAgcmVuZGVyLnBpeGlPcHRpb25zID0gcmVuZGVyLnBpeGlPcHRpb25zIHx8IHtcbiAgICAgICAgICAgIHZpZXc6IHJlbmRlci5jYW52YXMsXG4gICAgICAgICAgICB0cmFuc3BhcmVudDogdHJhbnNwYXJlbnQsXG4gICAgICAgICAgICBhbnRpYWxpYXM6IHRydWUsXG4gICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I6IG9wdGlvbnMuYmFja2dyb3VuZFxuICAgICAgICB9O1xuXG4gICAgICAgIHJlbmRlci5tb3VzZSA9IG9wdGlvbnMubW91c2U7XG4gICAgICAgIHJlbmRlci5lbmdpbmUgPSBvcHRpb25zLmVuZ2luZTtcbiAgICAgICAgcmVuZGVyLnJlbmRlcmVyID0gcmVuZGVyLnJlbmRlcmVyIHx8IG5ldyBQSVhJLldlYkdMUmVuZGVyZXIocmVuZGVyLm9wdGlvbnMud2lkdGgsIHJlbmRlci5vcHRpb25zLmhlaWdodCwgcmVuZGVyLnBpeGlPcHRpb25zKTtcbiAgICAgICAgcmVuZGVyLmNvbnRhaW5lciA9IHJlbmRlci5jb250YWluZXIgfHwgbmV3IFBJWEkuQ29udGFpbmVyKCk7XG4gICAgICAgIHJlbmRlci5zcHJpdGVDb250YWluZXIgPSByZW5kZXIuc3ByaXRlQ29udGFpbmVyIHx8IG5ldyBQSVhJLkNvbnRhaW5lcigpO1xuICAgICAgICByZW5kZXIuY2FudmFzID0gcmVuZGVyLmNhbnZhcyB8fCByZW5kZXIucmVuZGVyZXIudmlldztcbiAgICAgICAgcmVuZGVyLmJvdW5kcyA9IHJlbmRlci5ib3VuZHMgfHwgeyBcbiAgICAgICAgICAgIG1pbjoge1xuICAgICAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICAgICAgeTogMFxuICAgICAgICAgICAgfSwgXG4gICAgICAgICAgICBtYXg6IHsgXG4gICAgICAgICAgICAgICAgeDogcmVuZGVyLm9wdGlvbnMud2lkdGgsXG4gICAgICAgICAgICAgICAgeTogcmVuZGVyLm9wdGlvbnMuaGVpZ2h0XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gZXZlbnQgbGlzdGVuZXJzXG4gICAgICAgIEV2ZW50cy5vbihyZW5kZXIuZW5naW5lLCAnYmVmb3JlVXBkYXRlJywgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBSZW5kZXJQaXhpLmNsZWFyKHJlbmRlcik7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIGNhY2hlc1xuICAgICAgICByZW5kZXIudGV4dHVyZXMgPSB7fTtcbiAgICAgICAgcmVuZGVyLnNwcml0ZXMgPSB7fTtcbiAgICAgICAgcmVuZGVyLnByaW1pdGl2ZXMgPSB7fTtcblxuICAgICAgICAvLyB1c2UgYSBzcHJpdGUgYmF0Y2ggZm9yIHBlcmZvcm1hbmNlXG4gICAgICAgIHJlbmRlci5jb250YWluZXIuYWRkQ2hpbGQocmVuZGVyLnNwcml0ZUNvbnRhaW5lcik7XG5cbiAgICAgICAgLy8gaW5zZXJ0IGNhbnZhc1xuICAgICAgICBpZiAoQ29tbW9uLmlzRWxlbWVudChyZW5kZXIuZWxlbWVudCkpIHtcbiAgICAgICAgICAgIHJlbmRlci5lbGVtZW50LmFwcGVuZENoaWxkKHJlbmRlci5jYW52YXMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgQ29tbW9uLndhcm4oJ05vIFwicmVuZGVyLmVsZW1lbnRcIiBwYXNzZWQsIFwicmVuZGVyLmNhbnZhc1wiIHdhcyBub3QgaW5zZXJ0ZWQgaW50byBkb2N1bWVudC4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHByZXZlbnQgbWVudXMgb24gY2FudmFzXG4gICAgICAgIHJlbmRlci5jYW52YXMub25jb250ZXh0bWVudSA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gZmFsc2U7IH07XG4gICAgICAgIHJlbmRlci5jYW52YXMub25zZWxlY3RzdGFydCA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gZmFsc2U7IH07XG5cbiAgICAgICAgcmV0dXJuIHJlbmRlcjtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ29udGludW91c2x5IHVwZGF0ZXMgdGhlIHJlbmRlciBjYW52YXMgb24gdGhlIGByZXF1ZXN0QW5pbWF0aW9uRnJhbWVgIGV2ZW50LlxuICAgICAqIEBtZXRob2QgcnVuXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBkZXByZWNhdGVkXG4gICAgICovXG4gICAgUmVuZGVyUGl4aS5ydW4gPSBmdW5jdGlvbihyZW5kZXIpIHtcbiAgICAgICAgKGZ1bmN0aW9uIGxvb3AodGltZSl7XG4gICAgICAgICAgICByZW5kZXIuZnJhbWVSZXF1ZXN0SWQgPSBfcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGxvb3ApO1xuICAgICAgICAgICAgUmVuZGVyUGl4aS53b3JsZChyZW5kZXIpO1xuICAgICAgICB9KSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBFbmRzIGV4ZWN1dGlvbiBvZiBgUmVuZGVyLnJ1bmAgb24gdGhlIGdpdmVuIGByZW5kZXJgLCBieSBjYW5jZWxpbmcgdGhlIGFuaW1hdGlvbiBmcmFtZSByZXF1ZXN0IGV2ZW50IGxvb3AuXG4gICAgICogQG1ldGhvZCBzdG9wXG4gICAgICogQHBhcmFtIHtyZW5kZXJ9IHJlbmRlclxuICAgICAqIEBkZXByZWNhdGVkXG4gICAgICovXG4gICAgUmVuZGVyUGl4aS5zdG9wID0gZnVuY3Rpb24ocmVuZGVyKSB7XG4gICAgICAgIF9jYW5jZWxBbmltYXRpb25GcmFtZShyZW5kZXIuZnJhbWVSZXF1ZXN0SWQpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDbGVhcnMgdGhlIHNjZW5lIGdyYXBoXG4gICAgICogQG1ldGhvZCBjbGVhclxuICAgICAqIEBwYXJhbSB7UmVuZGVyUGl4aX0gcmVuZGVyXG4gICAgICogQGRlcHJlY2F0ZWRcbiAgICAgKi9cbiAgICBSZW5kZXJQaXhpLmNsZWFyID0gZnVuY3Rpb24ocmVuZGVyKSB7XG4gICAgICAgIHZhciBjb250YWluZXIgPSBy |
View raw
(Sorry about that, but we can’t show files that are this big right now.)
View raw
(Sorry about that, but we can’t show files that are this big right now.)
View raw
(Sorry about that, but we can’t show files that are this big right now.)
View raw
(Sorry about that, but we can’t show files that are this big right now.)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment