@gisatcz/deckgl-geolib 2.6.0-dev.4 → 2.6.0-dev.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -6331,7 +6331,8 @@ class ReliefCompositor {
6331
6331
  }
6332
6332
 
6333
6333
  class TerrainGenerator {
6334
- static async generate(input, options, meshMaxError) {
6334
+ static async generate(input, options, meshMaxError, workerPool, // TerrainWorkerPool (optional for flexibility)
6335
+ signal) {
6335
6336
  const { width, height } = input;
6336
6337
  const isKernel = width === 258;
6337
6338
  // 1. Compute Terrain Data (Extract Elevation)
@@ -6339,27 +6340,47 @@ class TerrainGenerator {
6339
6340
  // For kernel tiles, the mesh uses the inner 257×257 sub-grid (rows 1–257, cols 1–257)
6340
6341
  // so that row 0 / col 0 (kernel padding) is dropped while the bottom/right stitching
6341
6342
  // overlap is preserved.
6342
- const meshTerrain = isKernel ? this.extractMeshRaster(terrain) : terrain;
6343
+ let meshTerrain = isKernel ? this.extractMeshRaster(terrain) : terrain;
6343
6344
  const meshWidth = isKernel ? 257 : width;
6344
6345
  const meshHeight = isKernel ? 257 : height;
6345
6346
  // 2. Tesselate (Generate Mesh)
6346
6347
  const { terrainSkirtHeight, verticalExaggeration = 1.0 } = options;
6347
6348
  let mesh;
6348
- switch (options.tesselator) {
6349
- case 'martini':
6350
- mesh = this.getMartiniTileMesh(meshMaxError, meshWidth, meshTerrain);
6351
- break;
6352
- case 'delatin':
6353
- mesh = this.getDelatinTileMesh(meshMaxError, meshWidth, meshHeight, meshTerrain);
6354
- break;
6355
- default:
6356
- // Intentional: default to Martini for any unspecified or unrecognized tesselator.
6357
- mesh = this.getMartiniTileMesh(meshMaxError, meshWidth, meshTerrain);
6358
- break;
6349
+ let meshTerrainForAttributes; // ← Will hold terrain for getMeshAttributes()
6350
+ if (workerPool) {
6351
+ // NEW: Offload to Web Worker with ZERO-COPY ROUNDTRIP
6352
+ // Transfer meshTerrain to worker; worker transfers it back alongside mesh
6353
+ // This avoids meshTerrain.slice() allocation on main thread
6354
+ const result = await workerPool.computeMesh({
6355
+ terrain: meshTerrain, // ← Transferred to worker (detached here)
6356
+ meshMaxError,
6357
+ tesselator: options.tesselator || 'martini',
6358
+ width: meshWidth,
6359
+ height: meshHeight,
6360
+ signal, // ← Thread cancellation signal to worker
6361
+ });
6362
+ mesh = { vertices: result.vertices, triangles: result.triangles };
6363
+ meshTerrainForAttributes = result.terrain; // ← Transferred back from worker
6364
+ meshTerrain = result.terrain; // ← Reassign for downstream use (tileResult.raw, etc.)
6365
+ }
6366
+ else {
6367
+ // ❌ FALLBACK: Synchronous (old behavior, kept for safety)
6368
+ switch (options.tesselator) {
6369
+ case 'martini':
6370
+ mesh = this.getMartiniTileMesh(meshMaxError, meshWidth, meshTerrain);
6371
+ break;
6372
+ case 'delatin':
6373
+ mesh = this.getDelatinTileMesh(meshMaxError, meshWidth, meshHeight, meshTerrain);
6374
+ break;
6375
+ default:
6376
+ mesh = this.getMartiniTileMesh(meshMaxError, meshWidth, meshTerrain);
6377
+ break;
6378
+ }
6379
+ meshTerrainForAttributes = meshTerrain; // ← Use original
6359
6380
  }
6360
6381
  const { vertices } = mesh;
6361
6382
  let { triangles } = mesh;
6362
- let attributes = this.getMeshAttributes(vertices, meshTerrain, meshWidth, meshHeight, input.bounds, verticalExaggeration);
6383
+ let attributes = this.getMeshAttributes(vertices, meshTerrainForAttributes, meshWidth, meshHeight, input.bounds, verticalExaggeration);
6363
6384
  // Compute bounding box before adding skirt so that z values are not skewed
6364
6385
  const boundingBox = schema.getMeshBoundingBox(attributes);
6365
6386
  if (terrainSkirtHeight) {
@@ -6626,13 +6647,14 @@ class GeoImage {
6626
6647
  const data = await tiff.getImage(0);
6627
6648
  this.data = data;
6628
6649
  }
6629
- async getMap(input, options, meshMaxError) {
6650
+ async getMap(input, options, meshMaxError, workerPool, // TerrainWorkerPool (optional)
6651
+ signal) {
6630
6652
  const mergedOptions = GeoImage.resolveVisualizationMode({ ...DefaultGeoImageOptions, ...options }, options);
6631
6653
  switch (mergedOptions.type) {
6632
6654
  case 'image':
6633
6655
  return this.getBitmap(input, mergedOptions);
6634
6656
  case 'terrain':
6635
- return this.getHeightmap(input, mergedOptions, meshMaxError);
6657
+ return this.getHeightmap(input, mergedOptions, meshMaxError, workerPool, signal);
6636
6658
  default:
6637
6659
  return null;
6638
6660
  }
@@ -6689,7 +6711,8 @@ class GeoImage {
6689
6711
  return resolved;
6690
6712
  }
6691
6713
  // GetHeightmap uses only "useChannel" and "multiplier" options
6692
- async getHeightmap(input, options, meshMaxError) {
6714
+ async getHeightmap(input, options, meshMaxError, workerPool, // TerrainWorkerPool (optional)
6715
+ signal) {
6693
6716
  let rasters = [];
6694
6717
  let width;
6695
6718
  let height;
@@ -6711,8 +6734,8 @@ class GeoImage {
6711
6734
  bounds = input.bounds;
6712
6735
  cellSizeMeters = input.cellSizeMeters;
6713
6736
  }
6714
- // Delegate to TerrainGenerator
6715
- return await TerrainGenerator.generate({ width, height, rasters, bounds, cellSizeMeters }, options, meshMaxError);
6737
+ // Delegate to TerrainGenerator with worker pool and cancellation signal
6738
+ return await TerrainGenerator.generate({ width, height, rasters, bounds, cellSizeMeters }, options, meshMaxError, workerPool, signal);
6716
6739
  }
6717
6740
  async getBitmap(input, options) {
6718
6741
  let rasters = [];
@@ -6736,6 +6759,214 @@ class GeoImage {
6736
6759
  }
6737
6760
  }
6738
6761
 
6762
+ function decodeBase64(base64, enableUnicode) {
6763
+ var binaryString = atob(base64);
6764
+ return binaryString;
6765
+ }
6766
+
6767
+ function createURL(base64, sourcemapArg, enableUnicodeArg) {
6768
+ var source = decodeBase64(base64);
6769
+ var start = source.indexOf('\n', 10) + 1;
6770
+ var body = source.substring(start) + ('');
6771
+ var blob = new Blob([body], { type: 'application/javascript' });
6772
+ return URL.createObjectURL(blob);
6773
+ }
6774
+
6775
+ function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {
6776
+ var url;
6777
+ return function WorkerFactory(options) {
6778
+ url = url || createURL(base64);
6779
+ return new Worker(url, options);
6780
+ };
6781
+ }
6782
+
6783
+ var WorkerFactory = /*#__PURE__*/createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNsYXNzIE1hcnRpbmkgewogICAgICAgIGNvbnN0cnVjdG9yKGdyaWRTaXplID0gMjU3KSB7CiAgICAgICAgICAgIHRoaXMuZ3JpZFNpemUgPSBncmlkU2l6ZTsKICAgICAgICAgICAgY29uc3QgdGlsZVNpemUgPSBncmlkU2l6ZSAtIDE7CiAgICAgICAgICAgIGlmICh0aWxlU2l6ZSAmICh0aWxlU2l6ZSAtIDEpKSB0aHJvdyBuZXcgRXJyb3IoCiAgICAgICAgICAgICAgICBgRXhwZWN0ZWQgZ3JpZCBzaXplIHRvIGJlIDJebisxLCBnb3QgJHtncmlkU2l6ZX0uYCk7CgogICAgICAgICAgICB0aGlzLm51bVRyaWFuZ2xlcyA9IHRpbGVTaXplICogdGlsZVNpemUgKiAyIC0gMjsKICAgICAgICAgICAgdGhpcy5udW1QYXJlbnRUcmlhbmdsZXMgPSB0aGlzLm51bVRyaWFuZ2xlcyAtIHRpbGVTaXplICogdGlsZVNpemU7CgogICAgICAgICAgICB0aGlzLmluZGljZXMgPSBuZXcgVWludDMyQXJyYXkodGhpcy5ncmlkU2l6ZSAqIHRoaXMuZ3JpZFNpemUpOwoKICAgICAgICAgICAgLy8gY29vcmRpbmF0ZXMgZm9yIGFsbCBwb3NzaWJsZSB0cmlhbmdsZXMgaW4gYW4gUlRJTiB0aWxlCiAgICAgICAgICAgIHRoaXMuY29vcmRzID0gbmV3IFVpbnQxNkFycmF5KHRoaXMubnVtVHJpYW5nbGVzICogNCk7CgogICAgICAgICAgICAvLyBnZXQgdHJpYW5nbGUgY29vcmRpbmF0ZXMgZnJvbSBpdHMgaW5kZXggaW4gYW4gaW1wbGljaXQgYmluYXJ5IHRyZWUKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bVRyaWFuZ2xlczsgaSsrKSB7CiAgICAgICAgICAgICAgICBsZXQgaWQgPSBpICsgMjsKICAgICAgICAgICAgICAgIGxldCBheCA9IDAsIGF5ID0gMCwgYnggPSAwLCBieSA9IDAsIGN4ID0gMCwgY3kgPSAwOwogICAgICAgICAgICAgICAgaWYgKGlkICYgMSkgewogICAgICAgICAgICAgICAgICAgIGJ4ID0gYnkgPSBjeCA9IHRpbGVTaXplOyAvLyBib3R0b20tbGVmdCB0cmlhbmdsZQogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBheCA9IGF5ID0gY3kgPSB0aWxlU2l6ZTsgLy8gdG9wLXJpZ2h0IHRyaWFuZ2xlCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB3aGlsZSAoKGlkID4+PSAxKSA+IDEpIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCBteCA9IChheCArIGJ4KSA+PiAxOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IG15ID0gKGF5ICsgYnkpID4+IDE7CgogICAgICAgICAgICAgICAgICAgIGlmIChpZCAmIDEpIHsgLy8gbGVmdCBoYWxmCiAgICAgICAgICAgICAgICAgICAgICAgIGJ4ID0gYXg7IGJ5ID0gYXk7CiAgICAgICAgICAgICAgICAgICAgICAgIGF4ID0gY3g7IGF5ID0gY3k7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsgLy8gcmlnaHQgaGFsZgogICAgICAgICAgICAgICAgICAgICAgICBheCA9IGJ4OyBheSA9IGJ5OwogICAgICAgICAgICAgICAgICAgICAgICBieCA9IGN4OyBieSA9IGN5OwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBjeCA9IG14OyBjeSA9IG15OwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY29uc3QgayA9IGkgKiA0OwogICAgICAgICAgICAgICAgdGhpcy5jb29yZHNbayArIDBdID0gYXg7CiAgICAgICAgICAgICAgICB0aGlzLmNvb3Jkc1trICsgMV0gPSBheTsKICAgICAgICAgICAgICAgIHRoaXMuY29vcmRzW2sgKyAyXSA9IGJ4OwogICAgICAgICAgICAgICAgdGhpcy5jb29yZHNbayArIDNdID0gYnk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGNyZWF0ZVRpbGUodGVycmFpbikgewogICAgICAgICAgICByZXR1cm4gbmV3IFRpbGUodGVycmFpbiwgdGhpcyk7CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIFRpbGUgewogICAgICAgIGNvbnN0cnVjdG9yKHRlcnJhaW4sIG1hcnRpbmkpIHsKICAgICAgICAgICAgY29uc3Qgc2l6ZSA9IG1hcnRpbmkuZ3JpZFNpemU7CiAgICAgICAgICAgIGlmICh0ZXJyYWluLmxlbmd0aCAhPT0gc2l6ZSAqIHNpemUpIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgICAgIGBFeHBlY3RlZCB0ZXJyYWluIGRhdGEgb2YgbGVuZ3RoICR7c2l6ZSAqIHNpemV9ICgke3NpemV9IHggJHtzaXplfSksIGdvdCAke3RlcnJhaW4ubGVuZ3RofS5gKTsKCiAgICAgICAgICAgIHRoaXMudGVycmFpbiA9IHRlcnJhaW47CiAgICAgICAgICAgIHRoaXMubWFydGluaSA9IG1hcnRpbmk7CiAgICAgICAgICAgIHRoaXMuZXJyb3JzID0gbmV3IEZsb2F0MzJBcnJheSh0ZXJyYWluLmxlbmd0aCk7CiAgICAgICAgICAgIHRoaXMudXBkYXRlKCk7CiAgICAgICAgfQoKICAgICAgICB1cGRhdGUoKSB7CiAgICAgICAgICAgIGNvbnN0IHtudW1UcmlhbmdsZXMsIG51bVBhcmVudFRyaWFuZ2xlcywgY29vcmRzLCBncmlkU2l6ZTogc2l6ZX0gPSB0aGlzLm1hcnRpbmk7CiAgICAgICAgICAgIGNvbnN0IHt0ZXJyYWluLCBlcnJvcnN9ID0gdGhpczsKCiAgICAgICAgICAgIC8vIGl0ZXJhdGUgb3ZlciBhbGwgcG9zc2libGUgdHJpYW5nbGVzLCBzdGFydGluZyBmcm9tIHRoZSBzbWFsbGVzdCBsZXZlbAogICAgICAgICAgICBmb3IgKGxldCBpID0gbnVtVHJpYW5nbGVzIC0gMTsgaSA+PSAwOyBpLS0pIHsKICAgICAgICAgICAgICAgIGNvbnN0IGsgPSBpICogNDsKICAgICAgICAgICAgICAgIGNvbnN0IGF4ID0gY29vcmRzW2sgKyAwXTsKICAgICAgICAgICAgICAgIGNvbnN0IGF5ID0gY29vcmRzW2sgKyAxXTsKICAgICAgICAgICAgICAgIGNvbnN0IGJ4ID0gY29vcmRzW2sgKyAyXTsKICAgICAgICAgICAgICAgIGNvbnN0IGJ5ID0gY29vcmRzW2sgKyAzXTsKICAgICAgICAgICAgICAgIGNvbnN0IG14ID0gKGF4ICsgYngpID4+IDE7CiAgICAgICAgICAgICAgICBjb25zdCBteSA9IChheSArIGJ5KSA+PiAxOwogICAgICAgICAgICAgICAgY29uc3QgY3ggPSBteCArIG15IC0gYXk7CiAgICAgICAgICAgICAgICBjb25zdCBjeSA9IG15ICsgYXggLSBteDsKCiAgICAgICAgICAgICAgICAvLyBjYWxjdWxhdGUgZXJyb3IgaW4gdGhlIG1pZGRsZSBvZiB0aGUgbG9uZyBlZGdlIG9mIHRoZSB0cmlhbmdsZQogICAgICAgICAgICAgICAgY29uc3QgaW50ZXJwb2xhdGVkSGVpZ2h0ID0gKHRlcnJhaW5bYXkgKiBzaXplICsgYXhdICsgdGVycmFpbltieSAqIHNpemUgKyBieF0pIC8gMjsKICAgICAgICAgICAgICAgIGNvbnN0IG1pZGRsZUluZGV4ID0gbXkgKiBzaXplICsgbXg7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRkbGVFcnJvciA9IE1hdGguYWJzKGludGVycG9sYXRlZEhlaWdodCAtIHRlcnJhaW5bbWlkZGxlSW5kZXhdKTsKCiAgICAgICAgICAgICAgICBlcnJvcnNbbWlkZGxlSW5kZXhdID0gTWF0aC5tYXgoZXJyb3JzW21pZGRsZUluZGV4XSwgbWlkZGxlRXJyb3IpOwoKICAgICAgICAgICAgICAgIGlmIChpIDwgbnVtUGFyZW50VHJpYW5nbGVzKSB7IC8vIGJpZ2dlciB0cmlhbmdsZXM7IGFjY3VtdWxhdGUgZXJyb3Igd2l0aCBjaGlsZHJlbgogICAgICAgICAgICAgICAgICAgIGNvbnN0IGxlZnRDaGlsZEluZGV4ID0gKChheSArIGN5KSA+PiAxKSAqIHNpemUgKyAoKGF4ICsgY3gpID4+IDEpOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHJpZ2h0Q2hpbGRJbmRleCA9ICgoYnkgKyBjeSkgPj4gMSkgKiBzaXplICsgKChieCArIGN4KSA+PiAxKTsKICAgICAgICAgICAgICAgICAgICBlcnJvcnNbbWlkZGxlSW5kZXhdID0gTWF0aC5tYXgoZXJyb3JzW21pZGRsZUluZGV4XSwgZXJyb3JzW2xlZnRDaGlsZEluZGV4XSwgZXJyb3JzW3JpZ2h0Q2hpbGRJbmRleF0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBnZXRNZXNoKG1heEVycm9yID0gMCkgewogICAgICAgICAgICBjb25zdCB7Z3JpZFNpemU6IHNpemUsIGluZGljZXN9ID0gdGhpcy5tYXJ0aW5pOwogICAgICAgICAgICBjb25zdCB7ZXJyb3JzfSA9IHRoaXM7CiAgICAgICAgICAgIGxldCBudW1WZXJ0aWNlcyA9IDA7CiAgICAgICAgICAgIGxldCBudW1UcmlhbmdsZXMgPSAwOwogICAgICAgICAgICBjb25zdCBtYXggPSBzaXplIC0gMTsKCiAgICAgICAgICAgIC8vIHVzZSBhbiBpbmRleCBncmlkIHRvIGtlZXAgdHJhY2sgb2YgdmVydGljZXMgdGhhdCB3ZXJlIGFscmVhZHkgdXNlZCB0byBhdm9pZCBkdXBsaWNhdGlvbgogICAgICAgICAgICBpbmRpY2VzLmZpbGwoMCk7CgogICAgICAgICAgICAvLyByZXRyaWV2ZSBtZXNoIGluIHR3byBzdGFnZXMgdGhhdCBib3RoIHRyYXZlcnNlIHRoZSBlcnJvciBtYXA6CiAgICAgICAgICAgIC8vIC0gY291bnRFbGVtZW50czogZmluZCB1c2VkIHZlcnRpY2VzIChhbmQgYXNzaWduIGVhY2ggYW4gaW5kZXgpLCBhbmQgY291bnQgdHJpYW5nbGVzIChmb3IgbWluaW11bSBhbGxvY2F0aW9uKQogICAgICAgICAgICAvLyAtIHByb2Nlc3NUcmlhbmdsZTogZmlsbCB0aGUgYWxsb2NhdGVkIHZlcnRpY2VzICYgdHJpYW5nbGVzIHR5cGVkIGFycmF5cwoKICAgICAgICAgICAgZnVuY3Rpb24gY291bnRFbGVtZW50cyhheCwgYXksIGJ4LCBieSwgY3gsIGN5KSB7CiAgICAgICAgICAgICAgICBjb25zdCBteCA9IChheCArIGJ4KSA+PiAxOwogICAgICAgICAgICAgICAgY29uc3QgbXkgPSAoYXkgKyBieSkgPj4gMTsKCiAgICAgICAgICAgICAgICBpZiAoTWF0aC5hYnMoYXggLSBjeCkgKyBNYXRoLmFicyhheSAtIGN5KSA+IDEgJiYgZXJyb3JzW215ICogc2l6ZSArIG14XSA+IG1heEVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgY291bnRFbGVtZW50cyhjeCwgY3ksIGF4LCBheSwgbXgsIG15KTsKICAgICAgICAgICAgICAgICAgICBjb3VudEVsZW1lbnRzKGJ4LCBieSwgY3gsIGN5LCBteCwgbXkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBpbmRpY2VzW2F5ICogc2l6ZSArIGF4XSA9IGluZGljZXNbYXkgKiBzaXplICsgYXhdIHx8ICsrbnVtVmVydGljZXM7CiAgICAgICAgICAgICAgICAgICAgaW5kaWNlc1tieSAqIHNpemUgKyBieF0gPSBpbmRpY2VzW2J5ICogc2l6ZSArIGJ4XSB8fCArK251bVZlcnRpY2VzOwogICAgICAgICAgICAgICAgICAgIGluZGljZXNbY3kgKiBzaXplICsgY3hdID0gaW5kaWNlc1tjeSAqIHNpemUgKyBjeF0gfHwgKytudW1WZXJ0aWNlczsKICAgICAgICAgICAgICAgICAgICBudW1UcmlhbmdsZXMrKzsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBjb3VudEVsZW1lbnRzKDAsIDAsIG1heCwgbWF4LCBtYXgsIDApOwogICAgICAgICAgICBjb3VudEVsZW1lbnRzKG1heCwgbWF4LCAwLCAwLCAwLCBtYXgpOwoKICAgICAgICAgICAgY29uc3QgdmVydGljZXMgPSBuZXcgVWludDE2QXJyYXkobnVtVmVydGljZXMgKiAyKTsKICAgICAgICAgICAgY29uc3QgdHJpYW5nbGVzID0gbmV3IFVpbnQzMkFycmF5KG51bVRyaWFuZ2xlcyAqIDMpOwogICAgICAgICAgICBsZXQgdHJpSW5kZXggPSAwOwoKICAgICAgICAgICAgZnVuY3Rpb24gcHJvY2Vzc1RyaWFuZ2xlKGF4LCBheSwgYngsIGJ5LCBjeCwgY3kpIHsKICAgICAgICAgICAgICAgIGNvbnN0IG14ID0gKGF4ICsgYngpID4+IDE7CiAgICAgICAgICAgICAgICBjb25zdCBteSA9IChheSArIGJ5KSA+PiAxOwoKICAgICAgICAgICAgICAgIGlmIChNYXRoLmFicyhheCAtIGN4KSArIE1hdGguYWJzKGF5IC0gY3kpID4gMSAmJiBlcnJvcnNbbXkgKiBzaXplICsgbXhdID4gbWF4RXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAvLyB0cmlhbmdsZSBkb2Vzbid0IGFwcHJveGltYXRlIHRoZSBzdXJmYWNlIHdlbGwgZW5vdWdoOyBkcmlsbCBkb3duIGZ1cnRoZXIKICAgICAgICAgICAgICAgICAgICBwcm9jZXNzVHJpYW5nbGUoY3gsIGN5LCBheCwgYXksIG14LCBteSk7CiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc1RyaWFuZ2xlKGJ4LCBieSwgY3gsIGN5LCBteCwgbXkpOwoKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgLy8gYWRkIGEgdHJpYW5nbGUKICAgICAgICAgICAgICAgICAgICBjb25zdCBhID0gaW5kaWNlc1theSAqIHNpemUgKyBheF0gLSAxOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IGIgPSBpbmRpY2VzW2J5ICogc2l6ZSArIGJ4XSAtIDE7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgYyA9IGluZGljZXNbY3kgKiBzaXplICsgY3hdIC0gMTsKCiAgICAgICAgICAgICAgICAgICAgdmVydGljZXNbMiAqIGFdID0gYXg7CiAgICAgICAgICAgICAgICAgICAgdmVydGljZXNbMiAqIGEgKyAxXSA9IGF5OwoKICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlc1syICogYl0gPSBieDsKICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlc1syICogYiArIDFdID0gYnk7CgogICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzWzIgKiBjXSA9IGN4OwogICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzWzIgKiBjICsgMV0gPSBjeTsKCiAgICAgICAgICAgICAgICAgICAgdHJpYW5nbGVzW3RyaUluZGV4KytdID0gYTsKICAgICAgICAgICAgICAgICAgICB0cmlhbmdsZXNbdHJpSW5kZXgrK10gPSBiOwogICAgICAgICAgICAgICAgICAgIHRyaWFuZ2xlc1t0cmlJbmRleCsrXSA9IGM7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcHJvY2Vzc1RyaWFuZ2xlKDAsIDAsIG1heCwgbWF4LCBtYXgsIDApOwogICAgICAgICAgICBwcm9jZXNzVHJpYW5nbGUobWF4LCBtYXgsIDAsIDAsIDAsIG1heCk7CgogICAgICAgICAgICByZXR1cm4ge3ZlcnRpY2VzLCB0cmlhbmdsZXN9OwogICAgICAgIH0KICAgIH0KCiAgICAvLyBsb2FkZXJzLmdsCiAgICAvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUCiAgICAvLyBDb3B5cmlnaHQgKGMpIHZpcy5nbCBjb250cmlidXRvcnMKICAgIC8vIElTQyBMaWNlbnNlCiAgICAvLyBDb3B5cmlnaHQoYykgMjAxOSwgTWljaGFlbCBGb2dsZW1hbiwgVmxhZGltaXIgQWdhZm9ua2luCiAgICAvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvYmFuLXRzLWNvbW1lbnQgKi8KICAgIC8vIEB0cy1ub2NoZWNrCiAgICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9iYW4tdHMtY29tbWVudCAqLwogICAgZnVuY3Rpb24gb3JpZW50KGF4LCBheSwgYngsIGJ5LCBjeCwgY3kpIHsKICAgICAgICByZXR1cm4gKGJ4IC0gY3gpICogKGF5IC0gY3kpIC0gKGJ5IC0gY3kpICogKGF4IC0gY3gpOwogICAgfQogICAgY2xhc3MgRGVsYXRpbiB7CiAgICAgICAgY29uc3RydWN0b3IoZGF0YSwgd2lkdGgsIGhlaWdodCA9IHdpZHRoKSB7CiAgICAgICAgICAgIHRoaXMuZGF0YSA9IGRhdGE7IC8vIGhlaWdodCBkYXRhCiAgICAgICAgICAgIHRoaXMud2lkdGggPSB3aWR0aDsKICAgICAgICAgICAgdGhpcy5oZWlnaHQgPSBoZWlnaHQ7CiAgICAgICAgICAgIHRoaXMuY29vcmRzID0gW107IC8vIHZlcnRleCBjb29yZGluYXRlcyAoeCwgeSkKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXMgPSBbXTsgLy8gbWVzaCB0cmlhbmdsZSBpbmRpY2VzCiAgICAgICAgICAgIC8vIGFkZGl0aW9uYWwgdHJpYW5nbGUgZGF0YQogICAgICAgICAgICB0aGlzLl9oYWxmZWRnZXMgPSBbXTsKICAgICAgICAgICAgdGhpcy5fY2FuZGlkYXRlcyA9IFtdOwogICAgICAgICAgICB0aGlzLl9xdWV1ZUluZGljZXMgPSBbXTsKICAgICAgICAgICAgdGhpcy5fcXVldWUgPSBbXTsgLy8gcXVldWUgb2YgYWRkZWQgdHJpYW5nbGVzCiAgICAgICAgICAgIHRoaXMuX2Vycm9ycyA9IFtdOwogICAgICAgICAgICB0aGlzLl9ybXMgPSBbXTsKICAgICAgICAgICAgdGhpcy5fcGVuZGluZyA9IFtdOyAvLyB0cmlhbmdsZXMgcGVuZGluZyBhZGRpdGlvbiB0byBxdWV1ZQogICAgICAgICAgICB0aGlzLl9wZW5kaW5nTGVuID0gMDsKICAgICAgICAgICAgdGhpcy5fcm1zU3VtID0gMDsKICAgICAgICAgICAgY29uc3QgeDEgPSB3aWR0aCAtIDE7CiAgICAgICAgICAgIGNvbnN0IHkxID0gaGVpZ2h0IC0gMTsKICAgICAgICAgICAgY29uc3QgcDAgPSB0aGlzLl9hZGRQb2ludCgwLCAwKTsKICAgICAgICAgICAgY29uc3QgcDEgPSB0aGlzLl9hZGRQb2ludCh4MSwgMCk7CiAgICAgICAgICAgIGNvbnN0IHAyID0gdGhpcy5fYWRkUG9pbnQoMCwgeTEpOwogICAgICAgICAgICBjb25zdCBwMyA9IHRoaXMuX2FkZFBvaW50KHgxLCB5MSk7CiAgICAgICAgICAgIC8vIGFkZCBpbml0aWFsIHR3byB0cmlhbmdsZXMKICAgICAgICAgICAgY29uc3QgdDAgPSB0aGlzLl9hZGRUcmlhbmdsZShwMywgcDAsIHAyLCAtMSwgLTEsIC0xKTsKICAgICAgICAgICAgdGhpcy5fYWRkVHJpYW5nbGUocDAsIHAzLCBwMSwgdDAsIC0xLCAtMSk7CiAgICAgICAgICAgIHRoaXMuX2ZsdXNoKCk7CiAgICAgICAgfQogICAgICAgIC8vIHJlZmluZSB0aGUgbWVzaCB1bnRpbCBpdHMgbWF4aW11bSBlcnJvciBnZXRzIGJlbG93IHRoZSBnaXZlbiBvbmUKICAgICAgICBydW4obWF4RXJyb3IgPSAxKSB7CiAgICAgICAgICAgIHdoaWxlICh0aGlzLmdldE1heEVycm9yKCkgPiBtYXhFcnJvcikgewogICAgICAgICAgICAgICAgdGhpcy5yZWZpbmUoKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAvLyByZWZpbmUgdGhlIG1lc2ggd2l0aCBhIHNpbmdsZSBwb2ludAogICAgICAgIHJlZmluZSgpIHsKICAgICAgICAgICAgdGhpcy5fc3RlcCgpOwogICAgICAgICAgICB0aGlzLl9mbHVzaCgpOwogICAgICAgIH0KICAgICAgICAvLyBtYXggZXJyb3Igb2YgdGhlIGN1cnJlbnQgbWVzaAogICAgICAgIGdldE1heEVycm9yKCkgewogICAgICAgICAgICByZXR1cm4gdGhpcy5fZXJyb3JzWzBdOwogICAgICAgIH0KICAgICAgICAvLyByb290LW1lYW4tc3F1YXJlIGRldmlhdGlvbiBvZiB0aGUgY3VycmVudCBtZXNoCiAgICAgICAgZ2V0Uk1TRCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Jtc1N1bSA+IDAgPyBNYXRoLnNxcnQodGhpcy5fcm1zU3VtIC8gKHRoaXMud2lkdGggKiB0aGlzLmhlaWdodCkpIDogMDsKICAgICAgICB9CiAgICAgICAgLy8gaGVpZ2h0IHZhbHVlIGF0IGEgZ2l2ZW4gcG9zaXRpb24KICAgICAgICBoZWlnaHRBdCh4LCB5KSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLmRhdGFbdGhpcy53aWR0aCAqIHkgKyB4XTsKICAgICAgICB9CiAgICAgICAgLy8gcmFzdGVyaXplIGFuZCBxdWV1ZSBhbGwgdHJpYW5nbGVzIHRoYXQgZ290IGFkZGVkIG9yIHVwZGF0ZWQgaW4gX3N0ZXAKICAgICAgICBfZmx1c2goKSB7CiAgICAgICAgICAgIGNvbnN0IHsgY29vcmRzIH0gPSB0aGlzOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuX3BlbmRpbmdMZW47IGkrKykgewogICAgICAgICAgICAgICAgY29uc3QgdCA9IHRoaXMuX3BlbmRpbmdbaV07CiAgICAgICAgICAgICAgICAvLyByYXN0ZXJpemUgdHJpYW5nbGUgdG8gZmluZCBtYXhpbXVtIHBpeGVsIGVycm9yCiAgICAgICAgICAgICAgICBjb25zdCBhID0gMiAqIHRoaXMudHJpYW5nbGVzW3QgKiAzICsgMF07CiAgICAgICAgICAgICAgICBjb25zdCBiID0gMiAqIHRoaXMudHJpYW5nbGVzW3QgKiAzICsgMV07CiAgICAgICAgICAgICAgICBjb25zdCBjID0gMiAqIHRoaXMudHJpYW5nbGVzW3QgKiAzICsgMl07CiAgICAgICAgICAgICAgICB0aGlzLl9maW5kQ2FuZGlkYXRlKGNvb3Jkc1thXSwgY29vcmRzW2EgKyAxXSwgY29vcmRzW2JdLCBjb29yZHNbYiArIDFdLCBjb29yZHNbY10sIGNvb3Jkc1tjICsgMV0sIHQpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuX3BlbmRpbmdMZW4gPSAwOwogICAgICAgIH0KICAgICAgICAvLyByYXN0ZXJpemUgYSB0cmlhbmdsZSwgZmluZCBpdHMgbWF4IGVycm9yLCBhbmQgcXVldWUgaXQgZm9yIHByb2Nlc3NpbmcKICAgICAgICBfZmluZENhbmRpZGF0ZShwMHgsIHAweSwgcDF4LCBwMXksIHAyeCwgcDJ5LCB0KSB7CiAgICAgICAgICAgIC8vIHRyaWFuZ2xlIGJvdW5kaW5nIGJveAogICAgICAgICAgICBjb25zdCBtaW5YID0gTWF0aC5taW4ocDB4LCBwMXgsIHAyeCk7CiAgICAgICAgICAgIGNvbnN0IG1pblkgPSBNYXRoLm1pbihwMHksIHAxeSwgcDJ5KTsKICAgICAgICAgICAgY29uc3QgbWF4WCA9IE1hdGgubWF4KHAweCwgcDF4LCBwMngpOwogICAgICAgICAgICBjb25zdCBtYXhZID0gTWF0aC5tYXgocDB5LCBwMXksIHAyeSk7CiAgICAgICAgICAgIC8vIGZvcndhcmQgZGlmZmVyZW5jaW5nIHZhcmlhYmxlcwogICAgICAgICAgICBsZXQgdzAwID0gb3JpZW50KHAxeCwgcDF5LCBwMngsIHAyeSwgbWluWCwgbWluWSk7CiAgICAgICAgICAgIGxldCB3MDEgPSBvcmllbnQocDJ4LCBwMnksIHAweCwgcDB5LCBtaW5YLCBtaW5ZKTsKICAgICAgICAgICAgbGV0IHcwMiA9IG9yaWVudChwMHgsIHAweSwgcDF4LCBwMXksIG1pblgsIG1pblkpOwogICAgICAgICAgICBjb25zdCBhMDEgPSBwMXkgLSBwMHk7CiAgICAgICAgICAgIGNvbnN0IGIwMSA9IHAweCAtIHAxeDsKICAgICAgICAgICAgY29uc3QgYTEyID0gcDJ5IC0gcDF5OwogICAgICAgICAgICBjb25zdCBiMTIgPSBwMXggLSBwMng7CiAgICAgICAgICAgIGNvbnN0IGEyMCA9IHAweSAtIHAyeTsKICAgICAgICAgICAgY29uc3QgYjIwID0gcDJ4IC0gcDB4OwogICAgICAgICAgICAvLyBwcmUtbXVsdGlwbGllZCB6IHZhbHVlcyBhdCB2ZXJ0aWNlcwogICAgICAgICAgICBjb25zdCBhID0gb3JpZW50KHAweCwgcDB5LCBwMXgsIHAxeSwgcDJ4LCBwMnkpOwogICAgICAgICAgICBjb25zdCB6MCA9IHRoaXMuaGVpZ2h0QXQocDB4LCBwMHkpIC8gYTsKICAgICAgICAgICAgY29uc3QgejEgPSB0aGlzLmhlaWdodEF0KHAxeCwgcDF5KSAvIGE7CiAgICAgICAgICAgIGNvbnN0IHoyID0gdGhpcy5oZWlnaHRBdChwMngsIHAyeSkgLyBhOwogICAgICAgICAgICAvLyBpdGVyYXRlIG92ZXIgcGl4ZWxzIGluIGJvdW5kaW5nIGJveAogICAgICAgICAgICBsZXQgbWF4RXJyb3IgPSAwOwogICAgICAgICAgICBsZXQgbXggPSAwOwogICAgICAgICAgICBsZXQgbXkgPSAwOwogICAgICAgICAgICBsZXQgcm1zID0gMDsKICAgICAgICAgICAgZm9yIChsZXQgeSA9IG1pblk7IHkgPD0gbWF4WTsgeSsrKSB7CiAgICAgICAgICAgICAgICAvLyBjb21wdXRlIHN0YXJ0aW5nIG9mZnNldAogICAgICAgICAgICAgICAgbGV0IGR4ID0gMDsKICAgICAgICAgICAgICAgIGlmICh3MDAgPCAwICYmIGExMiAhPT0gMCkgewogICAgICAgICAgICAgICAgICAgIGR4ID0gTWF0aC5tYXgoZHgsIE1hdGguZmxvb3IoLXcwMCAvIGExMikpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgKHcwMSA8IDAgJiYgYTIwICE9PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgZHggPSBNYXRoLm1heChkeCwgTWF0aC5mbG9vcigtdzAxIC8gYTIwKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAodzAyIDwgMCAmJiBhMDEgIT09IDApIHsKICAgICAgICAgICAgICAgICAgICBkeCA9IE1hdGgubWF4KGR4LCBNYXRoLmZsb29yKC13MDIgLyBhMDEpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGxldCB3MCA9IHcwMCArIGExMiAqIGR4OwogICAgICAgICAgICAgICAgbGV0IHcxID0gdzAxICsgYTIwICogZHg7CiAgICAgICAgICAgICAgICBsZXQgdzIgPSB3MDIgKyBhMDEgKiBkeDsKICAgICAgICAgICAgICAgIGxldCB3YXNJbnNpZGUgPSBmYWxzZTsKICAgICAgICAgICAgICAgIGZvciAobGV0IHggPSBtaW5YICsgZHg7IHggPD0gbWF4WDsgeCsrKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gY2hlY2sgaWYgaW5zaWRlIHRyaWFuZ2xlCiAgICAgICAgICAgICAgICAgICAgaWYgKHcwID49IDAgJiYgdzEgPj0gMCAmJiB3MiA+PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHdhc0luc2lkZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNvbXB1dGUgeiB1c2luZyBiYXJ5Y2VudHJpYyBjb29yZGluYXRlcwogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB6ID0gejAgKiB3MCArIHoxICogdzEgKyB6MiAqIHcyOwogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBkeiA9IE1hdGguYWJzKHogLSB0aGlzLmhlaWdodEF0KHgsIHkpKTsKICAgICAgICAgICAgICAgICAgICAgICAgcm1zICs9IGR6ICogZHo7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkeiA+IG1heEVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhFcnJvciA9IGR6OwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbXggPSB4OwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbXkgPSB5OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIGVsc2UgaWYgKHdhc0luc2lkZSkgewogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgdzAgKz0gYTEyOwogICAgICAgICAgICAgICAgICAgIHcxICs9IGEyMDsKICAgICAgICAgICAgICAgICAgICB3MiArPSBhMDE7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB3MDAgKz0gYjEyOwogICAgICAgICAgICAgICAgdzAxICs9IGIyMDsKICAgICAgICAgICAgICAgIHcwMiArPSBiMDE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKChteCA9PT0gcDB4ICYmIG15ID09PSBwMHkpIHx8IChteCA9PT0gcDF4ICYmIG15ID09PSBwMXkpIHx8IChteCA9PT0gcDJ4ICYmIG15ID09PSBwMnkpKSB7CiAgICAgICAgICAgICAgICBtYXhFcnJvciA9IDA7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gdXBkYXRlIHRyaWFuZ2xlIG1ldGFkYXRhCiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHRdID0gbXg7CiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHQgKyAxXSA9IG15OwogICAgICAgICAgICB0aGlzLl9ybXNbdF0gPSBybXM7CiAgICAgICAgICAgIC8vIGFkZCB0cmlhbmdsZSB0byBwcmlvcml0eSBxdWV1ZQogICAgICAgICAgICB0aGlzLl9xdWV1ZVB1c2godCwgbWF4RXJyb3IsIHJtcyk7CiAgICAgICAgfQogICAgICAgIC8vIHByb2Nlc3MgdGhlIG5leHQgdHJpYW5nbGUgaW4gdGhlIHF1ZXVlLCBzcGxpdHRpbmcgaXQgd2l0aCBhIG5ldyBwb2ludAogICAgICAgIF9zdGVwKCkgewogICAgICAgICAgICAvLyBwb3AgdHJpYW5nbGUgd2l0aCBoaWdoZXN0IGVycm9yIGZyb20gcHJpb3JpdHkgcXVldWUKICAgICAgICAgICAgY29uc3QgdCA9IHRoaXMuX3F1ZXVlUG9wKCk7CiAgICAgICAgICAgIGNvbnN0IGUwID0gdCAqIDMgKyAwOwogICAgICAgICAgICBjb25zdCBlMSA9IHQgKiAzICsgMTsKICAgICAgICAgICAgY29uc3QgZTIgPSB0ICogMyArIDI7CiAgICAgICAgICAgIGNvbnN0IHAwID0gdGhpcy50cmlhbmdsZXNbZTBdOwogICAgICAgICAgICBjb25zdCBwMSA9IHRoaXMudHJpYW5nbGVzW2UxXTsKICAgICAgICAgICAgY29uc3QgcDIgPSB0aGlzLnRyaWFuZ2xlc1tlMl07CiAgICAgICAgICAgIGNvbnN0IGF4ID0gdGhpcy5jb29yZHNbMiAqIHAwXTsKICAgICAgICAgICAgY29uc3QgYXkgPSB0aGlzLmNvb3Jkc1syICogcDAgKyAxXTsKICAgICAgICAgICAgY29uc3QgYnggPSB0aGlzLmNvb3Jkc1syICogcDFdOwogICAgICAgICAgICBjb25zdCBieSA9IHRoaXMuY29vcmRzWzIgKiBwMSArIDFdOwogICAgICAgICAgICBjb25zdCBjeCA9IHRoaXMuY29vcmRzWzIgKiBwMl07CiAgICAgICAgICAgIGNvbnN0IGN5ID0gdGhpcy5jb29yZHNbMiAqIHAyICsgMV07CiAgICAgICAgICAgIGNvbnN0IHB4ID0gdGhpcy5fY2FuZGlkYXRlc1syICogdF07CiAgICAgICAgICAgIGNvbnN0IHB5ID0gdGhpcy5fY2FuZGlkYXRlc1syICogdCArIDFdOwogICAgICAgICAgICBjb25zdCBwbiA9IHRoaXMuX2FkZFBvaW50KHB4LCBweSk7CiAgICAgICAgICAgIGlmIChvcmllbnQoYXgsIGF5LCBieCwgYnksIHB4LCBweSkgPT09IDApIHsKICAgICAgICAgICAgICAgIHRoaXMuX2hhbmRsZUNvbGxpbmVhcihwbiwgZTApOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsc2UgaWYgKG9yaWVudChieCwgYnksIGN4LCBjeSwgcHgsIHB5KSA9PT0gMCkgewogICAgICAgICAgICAgICAgdGhpcy5faGFuZGxlQ29sbGluZWFyKHBuLCBlMSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZSBpZiAob3JpZW50KGN4LCBjeSwgYXgsIGF5LCBweCwgcHkpID09PSAwKSB7CiAgICAgICAgICAgICAgICB0aGlzLl9oYW5kbGVDb2xsaW5lYXIocG4sIGUyKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgIGNvbnN0IGgwID0gdGhpcy5faGFsZmVkZ2VzW2UwXTsKICAgICAgICAgICAgICAgIGNvbnN0IGgxID0gdGhpcy5faGFsZmVkZ2VzW2UxXTsKICAgICAgICAgICAgICAgIGNvbnN0IGgyID0gdGhpcy5faGFsZmVkZ2VzW2UyXTsKICAgICAgICAgICAgICAgIGNvbnN0IHQwID0gdGhpcy5fYWRkVHJpYW5nbGUocDAsIHAxLCBwbiwgaDAsIC0xLCAtMSwgZTApOwogICAgICAgICAgICAgICAgY29uc3QgdDEgPSB0aGlzLl9hZGRUcmlhbmdsZShwMSwgcDIsIHBuLCBoMSwgLTEsIHQwICsgMSk7CiAgICAgICAgICAgICAgICBjb25zdCB0MiA9IHRoaXMuX2FkZFRyaWFuZ2xlKHAyLCBwMCwgcG4sIGgyLCB0MCArIDIsIHQxICsgMSk7CiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0MCk7CiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0MSk7CiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0Mik7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLy8gYWRkIGNvb3JkaW5hdGVzIGZvciBhIG5ldyB2ZXJ0ZXgKICAgICAgICBfYWRkUG9pbnQoeCwgeSkgewogICAgICAgICAgICBjb25zdCBpID0gdGhpcy5jb29yZHMubGVuZ3RoID4+IDE7CiAgICAgICAgICAgIHRoaXMuY29vcmRzLnB1c2goeCwgeSk7CiAgICAgICAgICAgIHJldHVybiBpOwogICAgICAgIH0KICAgICAgICAvLyBhZGQgb3IgdXBkYXRlIGEgdHJpYW5nbGUgaW4gdGhlIG1lc2gKICAgICAgICBfYWRkVHJpYW5nbGUoYSwgYiwgYywgYWIsIGJjLCBjYSwgZSA9IHRoaXMudHJpYW5nbGVzLmxlbmd0aCkgewogICAgICAgICAgICBjb25zdCB0ID0gZSAvIDM7IC8vIG5ldyB0cmlhbmdsZSBpbmRleAogICAgICAgICAgICAvLyBhZGQgdHJpYW5nbGUgdmVydGljZXMKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXNbZSArIDBdID0gYTsKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXNbZSArIDFdID0gYjsKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXNbZSArIDJdID0gYzsKICAgICAgICAgICAgLy8gYWRkIHRyaWFuZ2xlIGhhbGZlZGdlcwogICAgICAgICAgICB0aGlzLl9oYWxmZWRnZXNbZSArIDBdID0gYWI7CiAgICAgICAgICAgIHRoaXMuX2hhbGZlZGdlc1tlICsgMV0gPSBiYzsKICAgICAgICAgICAgdGhpcy5faGFsZmVkZ2VzW2UgKyAyXSA9IGNhOwogICAgICAgICAgICAvLyBsaW5rIG5laWdoYm9yaW5nIGhhbGZlZGdlcwogICAgICAgICAgICBpZiAoYWIgPj0gMCkgewogICAgICAgICAgICAgICAgdGhpcy5faGFsZmVkZ2VzW2FiXSA9IGUgKyAwOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChiYyA+PSAwKSB7CiAgICAgICAgICAgICAgICB0aGlzLl9oYWxmZWRnZXNbYmNdID0gZSArIDE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKGNhID49IDApIHsKICAgICAgICAgICAgICAgIHRoaXMuX2hhbGZlZGdlc1tjYV0gPSBlICsgMjsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBpbml0IHRyaWFuZ2xlIG1ldGFkYXRhCiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHQgKyAwXSA9IDA7CiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHQgKyAxXSA9IDA7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlSW5kaWNlc1t0XSA9IC0xOwogICAgICAgICAgICB0aGlzLl9ybXNbdF0gPSAwOwogICAgICAgICAgICAvLyBhZGQgdHJpYW5nbGUgdG8gcGVuZGluZyBxdWV1ZSBmb3IgbGF0ZXIgcmFzdGVyaXphdGlvbgogICAgICAgICAgICB0aGlzLl9wZW5kaW5nW3RoaXMuX3BlbmRpbmdMZW4rK10gPSB0OwogICAgICAgICAgICAvLyByZXR1cm4gZmlyc3QgaGFsZmVkZ2UgaW5kZXgKICAgICAgICAgICAgcmV0dXJuIGU7CiAgICAgICAgfQogICAgICAgIF9sZWdhbGl6ZShhKSB7CiAgICAgICAgICAgIC8vIGlmIHRoZSBwYWlyIG9mIHRyaWFuZ2xlcyBkb2Vzbid0IHNhdGlzZnkgdGhlIERlbGF1bmF5IGNvbmRpdGlvbgogICAgICAgICAgICAvLyAocDEgaXMgaW5zaWRlIHRoZSBjaXJjdW1jaXJjbGUgb2YgW3AwLCBwbCwgcHJdKSwgZmxpcCB0aGVtLAogICAgICAgICAgICAvLyB0aGVuIGRvIHRoZSBzYW1lIGNoZWNrL2ZsaXAgcmVjdXJzaXZlbHkgZm9yIHRoZSBuZXcgcGFpciBvZiB0cmlhbmdsZXMKICAgICAgICAgICAgLy8KICAgICAgICAgICAgLy8gICAgICAgICAgIHBsICAgICAgICAgICAgICAgICAgICBwbAogICAgICAgICAgICAvLyAgICAgICAgICAvfHxcICAgICAgICAgICAgICAgICAgLyAgXAogICAgICAgICAgICAvLyAgICAgICBhbC8gfHwgXGJsICAgICAgICAgICAgYWwvICAgIFxhCiAgICAgICAgICAgIC8vICAgICAgICAvICB8fCAgXCAgICAgICAgICAgICAgLyAgICAgIFwKICAgICAgICAgICAgLy8gICAgICAgLyAgYXx8YiAgXCAgICBmbGlwICAgIC9fX19hcl9fX1wKICAgICAgICAgICAgLy8gICAgIHAwXCAgIHx8ICAgL3AxICAgPT4gICBwMFwtLS1ibC0tLS9wMQogICAgICAgICAgICAvLyAgICAgICAgXCAgfHwgIC8gICAgICAgICAgICAgIFwgICAgICAvCiAgICAgICAgICAgIC8vICAgICAgIGFyXCB8fCAvYnIgICAgICAgICAgICAgYlwgICAgL2JyCiAgICAgICAgICAgIC8vICAgICAgICAgIFx8fC8gICAgICAgICAgICAgICAgICBcICAvCiAgICAgICAgICAgIC8vICAgICAgICAgICBwciAgICAgICAgICAgICAgICAgICAgcHIKICAgICAgICAgICAgY29uc3QgYiA9IHRoaXMuX2hhbGZlZGdlc1thXTsKICAgICAgICAgICAgaWYgKGIgPCAwKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgYTAgPSBhIC0gKGEgJSAzKTsKICAgICAgICAgICAgY29uc3QgYjAgPSBiIC0gKGIgJSAzKTsKICAgICAgICAgICAgY29uc3QgYWwgPSBhMCArICgoYSArIDEpICUgMyk7CiAgICAgICAgICAgIGNvbnN0IGFyID0gYTAgKyAoKGEgKyAyKSAlIDMpOwogICAgICAgICAgICBjb25zdCBibCA9IGIwICsgKChiICsgMikgJSAzKTsKICAgICAgICAgICAgY29uc3QgYnIgPSBiMCArICgoYiArIDEpICUgMyk7CiAgICAgICAgICAgIGNvbnN0IHAwID0gdGhpcy50cmlhbmdsZXNbYXJdOwogICAgICAgICAgICBjb25zdCBwciA9IHRoaXMudHJpYW5nbGVzW2FdOwogICAgICAgICAgICBjb25zdCBwbCA9IHRoaXMudHJpYW5nbGVzW2FsXTsKICAgICAgICAgICAgY29uc3QgcDEgPSB0aGlzLnRyaWFuZ2xlc1tibF07CiAgICAgICAgICAgIGNvbnN0IHsgY29vcmRzIH0gPSB0aGlzOwogICAgICAgICAgICBpZiAoIWluQ2lyY2xlKGNvb3Jkc1syICogcDBdLCBjb29yZHNbMiAqIHAwICsgMV0sIGNvb3Jkc1syICogcHJdLCBjb29yZHNbMiAqIHByICsgMV0sIGNvb3Jkc1syICogcGxdLCBjb29yZHNbMiAqIHBsICsgMV0sIGNvb3Jkc1syICogcDFdLCBjb29yZHNbMiAqIHAxICsgMV0pKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgaGFsID0gdGhpcy5faGFsZmVkZ2VzW2FsXTsKICAgICAgICAgICAgY29uc3QgaGFyID0gdGhpcy5faGFsZmVkZ2VzW2FyXTsKICAgICAgICAgICAgY29uc3QgaGJsID0gdGhpcy5faGFsZmVkZ2VzW2JsXTsKICAgICAgICAgICAgY29uc3QgaGJyID0gdGhpcy5faGFsZmVkZ2VzW2JyXTsKICAgICAgICAgICAgdGhpcy5fcXVldWVSZW1vdmUoYTAgLyAzKTsKICAgICAgICAgICAgdGhpcy5fcXVldWVSZW1vdmUoYjAgLyAzKTsKICAgICAgICAgICAgY29uc3QgdDAgPSB0aGlzLl9hZGRUcmlhbmdsZShwMCwgcDEsIHBsLCAtMSwgaGJsLCBoYWwsIGEwKTsKICAgICAgICAgICAgY29uc3QgdDEgPSB0aGlzLl9hZGRUcmlhbmdsZShwMSwgcDAsIHByLCB0MCwgaGFyLCBoYnIsIGIwKTsKICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDAgKyAxKTsKICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDEgKyAyKTsKICAgICAgICB9CiAgICAgICAgLy8gaGFuZGxlIGEgY2FzZSB3aGVyZSBuZXcgdmVydGV4IGlzIG9uIHRoZSBlZGdlIG9mIGEgdHJpYW5nbGUKICAgICAgICBfaGFuZGxlQ29sbGluZWFyKHBuLCBhKSB7CiAgICAgICAgICAgIGNvbnN0IGEwID0gYSAtIChhICUgMyk7CiAgICAgICAgICAgIGNvbnN0IGFsID0gYTAgKyAoKGEgKyAxKSAlIDMpOwogICAgICAgICAgICBjb25zdCBhciA9IGEwICsgKChhICsgMikgJSAzKTsKICAgICAgICAgICAgY29uc3QgcDAgPSB0aGlzLnRyaWFuZ2xlc1thcl07CiAgICAgICAgICAgIGNvbnN0IHByID0gdGhpcy50cmlhbmdsZXNbYV07CiAgICAgICAgICAgIGNvbnN0IHBsID0gdGhpcy50cmlhbmdsZXNbYWxdOwogICAgICAgICAgICBjb25zdCBoYWwgPSB0aGlzLl9oYWxmZWRnZXNbYWxdOwogICAgICAgICAgICBjb25zdCBoYXIgPSB0aGlzLl9oYWxmZWRnZXNbYXJdOwogICAgICAgICAgICBjb25zdCBiID0gdGhpcy5faGFsZmVkZ2VzW2FdOwogICAgICAgICAgICBpZiAoYiA8IDApIHsKICAgICAgICAgICAgICAgIGNvbnN0IHQwID0gdGhpcy5fYWRkVHJpYW5nbGUocG4sIHAwLCBwciwgLTEsIGhhciwgLTEsIGEwKTsKICAgICAgICAgICAgICAgIGNvbnN0IHQxID0gdGhpcy5fYWRkVHJpYW5nbGUocDAsIHBuLCBwbCwgdDAsIC0xLCBoYWwpOwogICAgICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDAgKyAxKTsKICAgICAgICAgICAgICAgIHRoaXMuX2xlZ2FsaXplKHQxICsgMik7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgYjAgPSBiIC0gKGIgJSAzKTsKICAgICAgICAgICAgY29uc3QgYmwgPSBiMCArICgoYiArIDIpICUgMyk7CiAgICAgICAgICAgIGNvbnN0IGJyID0gYjAgKyAoKGIgKyAxKSAlIDMpOwogICAgICAgICAgICBjb25zdCBwMSA9IHRoaXMudHJpYW5nbGVzW2JsXTsKICAgICAgICAgICAgY29uc3QgaGJsID0gdGhpcy5faGFsZmVkZ2VzW2JsXTsKICAgICAgICAgICAgY29uc3QgaGJyID0gdGhpcy5faGFsZmVkZ2VzW2JyXTsKICAgICAgICAgICAgdGhpcy5fcXVldWVSZW1vdmUoYjAgLyAzKTsKICAgICAgICAgICAgY29uc3QgdDAgPSB0aGlzLl9hZGRUcmlhbmdsZShwMCwgcHIsIHBuLCBoYXIsIC0xLCAtMSwgYTApOwogICAgICAgICAgICBjb25zdCB0MSA9IHRoaXMuX2FkZFRyaWFuZ2xlKHByLCBwMSwgcG4sIGhiciwgLTEsIHQwICsgMSwgYjApOwogICAgICAgICAgICBjb25zdCB0MiA9IHRoaXMuX2FkZFRyaWFuZ2xlKHAxLCBwbCwgcG4sIGhibCwgLTEsIHQxICsgMSk7CiAgICAgICAgICAgIGNvbnN0IHQzID0gdGhpcy5fYWRkVHJpYW5nbGUocGwsIHAwLCBwbiwgaGFsLCB0MCArIDIsIHQyICsgMSk7CiAgICAgICAgICAgIHRoaXMuX2xlZ2FsaXplKHQwKTsKICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDEpOwogICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0Mik7CiAgICAgICAgICAgIHRoaXMuX2xlZ2FsaXplKHQzKTsKICAgICAgICB9CiAgICAgICAgLy8gcHJpb3JpdHkgcXVldWUgbWV0aG9kcwogICAgICAgIF9xdWV1ZVB1c2godCwgZXJyb3IsIHJtcykgewogICAgICAgICAgICBjb25zdCBpID0gdGhpcy5fcXVldWUubGVuZ3RoOwogICAgICAgICAgICB0aGlzLl9xdWV1ZUluZGljZXNbdF0gPSBpOwogICAgICAgICAgICB0aGlzLl9xdWV1ZS5wdXNoKHQpOwogICAgICAgICAgICB0aGlzLl9lcnJvcnMucHVzaChlcnJvcik7CiAgICAgICAgICAgIHRoaXMuX3Jtc1N1bSArPSBybXM7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlVXAoaSk7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVBvcCgpIHsKICAgICAgICAgICAgY29uc3QgbiA9IHRoaXMuX3F1ZXVlLmxlbmd0aCAtIDE7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlU3dhcCgwLCBuKTsKICAgICAgICAgICAgdGhpcy5fcXVldWVEb3duKDAsIG4pOwogICAgICAgICAgICByZXR1cm4gdGhpcy5fcXVldWVQb3BCYWNrKCk7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVBvcEJhY2soKSB7CiAgICAgICAgICAgIGNvbnN0IHQgPSB0aGlzLl9xdWV1ZS5wb3AoKTsKICAgICAgICAgICAgdGhpcy5fZXJyb3JzLnBvcCgpOwogICAgICAgICAgICB0aGlzLl9ybXNTdW0gLT0gdGhpcy5fcm1zW3RdOwogICAgICAgICAgICB0aGlzLl9xdWV1ZUluZGljZXNbdF0gPSAtMTsKICAgICAgICAgICAgcmV0dXJuIHQ7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVJlbW92ZSh0KSB7CiAgICAgICAgICAgIGNvbnN0IGkgPSB0aGlzLl9xdWV1ZUluZGljZXNbdF07CiAgICAgICAgICAgIGlmIChpIDwgMCkgewogICAgICAgICAgICAgICAgY29uc3QgaXQgPSB0aGlzLl9wZW5kaW5nLmluZGV4T2YodCk7CiAgICAgICAgICAgICAgICBpZiAoaXQgIT09IC0xKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5fcGVuZGluZ1tpdF0gPSB0aGlzLl9wZW5kaW5nWy0tdGhpcy5fcGVuZGluZ0xlbl07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Jyb2tlbiB0cmlhbmd1bGF0aW9uIChzb21ldGhpbmcgd2VudCB3cm9uZykuJyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgbiA9IHRoaXMuX3F1ZXVlLmxlbmd0aCAtIDE7CiAgICAgICAgICAgIGlmIChuICE9PSBpKSB7CiAgICAgICAgICAgICAgICB0aGlzLl9xdWV1ZVN3YXAoaSwgbik7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuX3F1ZXVlRG93bihpLCBuKSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuX3F1ZXVlVXAoaSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5fcXVldWVQb3BCYWNrKCk7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZUxlc3MoaSwgaikgewogICAgICAgICAgICByZXR1cm4gdGhpcy5fZXJyb3JzW2ldID4gdGhpcy5fZXJyb3JzW2pdOwogICAgICAgIH0KICAgICAgICBfcXVldWVTd2FwKGksIGopIHsKICAgICAgICAgICAgY29uc3QgcGkgPSB0aGlzLl9xdWV1ZVtpXTsKICAgICAgICAgICAgY29uc3QgcGogPSB0aGlzLl9xdWV1ZVtqXTsKICAgICAgICAgICAgdGhpcy5fcXVldWVbaV0gPSBwajsKICAgICAgICAgICAgdGhpcy5fcXVldWVbal0gPSBwaTsKICAgICAgICAgICAgdGhpcy5fcXVldWVJbmRpY2VzW3BpXSA9IGo7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlSW5kaWNlc1twal0gPSBpOwogICAgICAgICAgICBjb25zdCBlID0gdGhpcy5fZXJyb3JzW2ldOwogICAgICAgICAgICB0aGlzLl9lcnJvcnNbaV0gPSB0aGlzLl9lcnJvcnNbal07CiAgICAgICAgICAgIHRoaXMuX2Vycm9yc1tqXSA9IGU7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVVwKGowKSB7CiAgICAgICAgICAgIGxldCBqID0gajA7CiAgICAgICAgICAgIHdoaWxlICh0cnVlKSB7CiAgICAgICAgICAgICAgICBjb25zdCBpID0gKGogLSAxKSA+PiAxOwogICAgICAgICAgICAgICAgaWYgKGkgPT09IGogfHwgIXRoaXMuX3F1ZXVlTGVzcyhqLCBpKSkgewogICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5fcXVldWVTd2FwKGksIGopOwogICAgICAgICAgICAgICAgaiA9IGk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgX3F1ZXVlRG93bihpMCwgbikgewogICAgICAgICAgICBsZXQgaSA9IGkwOwogICAgICAgICAgICB3aGlsZSAodHJ1ZSkgewogICAgICAgICAgICAgICAgY29uc3QgajEgPSAyICogaSArIDE7CiAgICAgICAgICAgICAgICBpZiAoajEgPj0gbiB8fCBqMSA8IDApIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnN0IGoyID0gajEgKyAxOwogICAgICAgICAgICAgICAgbGV0IGogPSBqMTsKICAgICAgICAgICAgICAgIGlmIChqMiA8IG4gJiYgdGhpcy5fcXVldWVMZXNzKGoyLCBqMSkpIHsKICAgICAgICAgICAgICAgICAgICBqID0gajI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuX3F1ZXVlTGVzcyhqLCBpKSkgewogICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5fcXVldWVTd2FwKGksIGopOwogICAgICAgICAgICAgICAgaSA9IGo7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIGkgPiBpMDsKICAgICAgICB9CiAgICB9CiAgICBmdW5jdGlvbiBpbkNpcmNsZShheCwgYXksIGJ4LCBieSwgY3gsIGN5LCBweCwgcHkpIHsKICAgICAgICBjb25zdCBkeCA9IGF4IC0gcHg7CiAgICAgICAgY29uc3QgZHkgPSBheSAtIHB5OwogICAgICAgIGNvbnN0IGV4ID0gYnggLSBweDsKICAgICAgICBjb25zdCBleSA9IGJ5IC0gcHk7CiAgICAgICAgY29uc3QgZnggPSBjeCAtIHB4OwogICAgICAgIGNvbnN0IGZ5ID0gY3kgLSBweTsKICAgICAgICBjb25zdCBhcCA9IGR4ICogZHggKyBkeSAqIGR5OwogICAgICAgIGNvbnN0IGJwID0gZXggKiBleCArIGV5ICogZXk7CiAgICAgICAgY29uc3QgY3AgPSBmeCAqIGZ4ICsgZnkgKiBmeTsKICAgICAgICByZXR1cm4gZHggKiAoZXkgKiBjcCAtIGJwICogZnkpIC0gZHkgKiAoZXggKiBjcCAtIGJwICogZngpICsgYXAgKiAoZXggKiBmeSAtIGV5ICogZngpIDwgMDsKICAgIH0KCiAgICAvKioKICAgICAqIFdlYiBXb3JrZXIgZm9yIHRlcnJhaW4gbWVzaCB0ZXNzZWxsYXRpb24uCiAgICAgKiBSdW5zIE1hcnRpbmkvRGVsYXRpbiBhbGdvcml0aG1zIG9uIGEgYmFja2dyb3VuZCB0aHJlYWQgdG8gYXZvaWQgYmxvY2tpbmcgdGhlIG1haW4gdGhyZWFkLgogICAgICovCiAgICAvLyBUcmFjayBhYm9ydGVkIHRhc2tzIHRvIGF2b2lkIHNlbmRpbmcgcmVzdWx0cyBmb3IgY2FuY2VsbGVkIHdvcmsKICAgIGNvbnN0IGFib3J0ZWRUYXNrcyA9IG5ldyBNYXAoKTsKICAgIHNlbGYub25tZXNzYWdlID0gKGUpID0+IHsKICAgICAgICBjb25zdCBkYXRhID0gZS5kYXRhOwogICAgICAgIGlmIChkYXRhLnR5cGUgPT09ICdhYm9ydCcpIHsKICAgICAgICAgICAgLy8gTWFyayB0YXNrIGFzIGFib3J0ZWQ7IGlmIGl0J3Mgc3RpbGwgY29tcHV0aW5nLCB3ZSdsbCBza2lwIHRoZSByZXNwb25zZQogICAgICAgICAgICBhYm9ydGVkVGFza3Muc2V0KGRhdGEudGFza0lkLCB0cnVlKTsKICAgICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICBpZiAoZGF0YS50eXBlID09PSAnY29tcHV0ZU1lc2gnKSB7CiAgICAgICAgICAgIGNvbnN0IHsgdGFza0lkLCB0ZXJyYWluLCBtZXNoTWF4RXJyb3IsIHRlc3NlbGF0b3IsIHdpZHRoLCBoZWlnaHQgfSA9IGRhdGE7CiAgICAgICAgICAgIGxldCBtZXNoOwogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgaWYgKHRlc3NlbGF0b3IgPT09ICdkZWxhdGluJykgewogICAgICAgICAgICAgICAgICAgIC8vIERlbGF0aW4gdGVzc2VsbGF0aW9uCiAgICAgICAgICAgICAgICAgICAgY29uc3QgdGluID0gbmV3IERlbGF0aW4odGVycmFpbiwgd2lkdGgsIGhlaWdodCk7CiAgICAgICAgICAgICAgICAgICAgdGluLnJ1bihtZXNoTWF4RXJyb3IpOwogICAgICAgICAgICAgICAgICAgIC8vIEB0cy1leHBlY3QtZXJyb3I6IERlbGF0aW4gaW5zdGFuY2UgcHJvcGVydGllcyAnY29vcmRzJyBhbmQgJ3RyaWFuZ2xlcycgYXJlIG5vdCBleHBsaWNpdGx5IHR5cGVkIGluIHRoZSBsaWJyYXJ5IHBvcnQKICAgICAgICAgICAgICAgICAgICBjb25zdCB7IGNvb3JkcywgdHJpYW5nbGVzIH0gPSB0aW47CiAgICAgICAgICAgICAgICAgICAgLy8gY29vcmRzIGlzIGEgcGxhaW4gYXJyYXkg4oCUIGNvbnZlcnQgdG8gRmxvYXQ2NEFycmF5IHNvIGl0IGhhcyAuYnVmZmVyIGZvciB0cmFuc2ZlcgogICAgICAgICAgICAgICAgICAgIGNvbnN0IHZlcnRpY2VzVHlwZWQgPSBGbG9hdDY0QXJyYXkuZnJvbShjb29yZHMpOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRyaWFuZ2xlc1R5cGVkID0gVWludDMyQXJyYXkuZnJvbSh0cmlhbmdsZXMpOwogICAgICAgICAgICAgICAgICAgIG1lc2ggPSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzOiB2ZXJ0aWNlc1R5cGVkLAogICAgICAgICAgICAgICAgICAgICAgICB0cmlhbmdsZXM6IHRyaWFuZ2xlc1R5cGVkLAogICAgICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvLyBNYXJ0aW5pIHRlc3NlbGxhdGlvbiAoZGVmYXVsdCkKICAgICAgICAgICAgICAgICAgICBjb25zdCBncmlkU2l6ZSA9IHdpZHRoID09PSAyNTcgPyAyNTcgOiB3aWR0aCArIDE7IC8vIE9ubHkgYWRkIDEgaWYgd2lkdGggaXMgbm90IGFscmVhZHkgMl5uKzEKICAgICAgICAgICAgICAgICAgICBjb25zdCBtYXJ0aW5pID0gbmV3IE1hcnRpbmkoZ3JpZFNpemUpOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbGUgPSBtYXJ0aW5pLmNyZWF0ZVRpbGUodGVycmFpbik7CiAgICAgICAgICAgICAgICAgICAgbWVzaCA9IHRpbGUuZ2V0TWVzaChtZXNoTWF4RXJyb3IpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgLy8gT25seSBzZW5kIHJlc3VsdCBpZiBub3QgYWJvcnRlZAogICAgICAgICAgICAgICAgaWYgKCFhYm9ydGVkVGFza3MuZ2V0KHRhc2tJZCkpIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IHsKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ21lc2hSZXN1bHQnLAogICAgICAgICAgICAgICAgICAgICAgICB0YXNrSWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdDogbWVzaCwKICAgICAgICAgICAgICAgICAgICAgICAgdGVycmFpbiwgLy8g4oaQIENSSVRJQ0FMOiBSZXR1cm4gdGVycmFpbiBidWZmZXIgdG8gbWFpbiB0aHJlYWQKICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgICAgIC8vIFRyYW5zZmVyIG93bmVyc2hpcCBvZiBBTEwgYnVmZmVycyBiYWNrIHRvIG1haW4gdGhyZWFkICh6ZXJvLWNvcHkgcm91bmR0cmlwKQogICAgICAgICAgICAgICAgICAgIC8vIFRoaXMgYXZvaWRzIG1lc2hUZXJyYWluLnNsaWNlKCkgYWxsb2NhdGlvbiBvbiBtYWluIHRocmVhZAogICAgICAgICAgICAgICAgICAgIHNlbGYucG9zdE1lc3NhZ2UocmVzcG9uc2UsIHsKICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmZXI6IFsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc2gudmVydGljZXMuYnVmZmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVzaC50cmlhbmdsZXMuYnVmZmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVycmFpbi5idWZmZXIsIC8vIOKGkCBUcmFuc2ZlciB0ZXJyYWluIGJhY2sKICAgICAgICAgICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgICAgIC8vIE9ubHkgcmVwb3J0IGVycm9ycyBmb3Igbm9uLWFib3J0ZWQgdGFza3MKICAgICAgICAgICAgICAgIGlmICghYWJvcnRlZFRhc2tzLmdldCh0YXNrSWQpKSB7CiAgICAgICAgICAgICAgICAgICAgc2VsZi5wb3N0TWVzc2FnZSh7CiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdlcnJvcicsCiAgICAgICAgICAgICAgICAgICAgICAgIHRhc2tJZCwKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSwKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBmaW5hbGx5IHsKICAgICAgICAgICAgICAgIC8vIENsZWFuIHVwIGFib3J0IHRyYWNraW5nCiAgICAgICAgICAgICAgICBhYm9ydGVkVGFza3MuZGVsZXRlKHRhc2tJZCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9OwogICAgLy8gU2lnbmFsIHRoYXQgd29ya2VyIGlzIHJlYWR5CiAgICBzZWxmLnBvc3RNZXNzYWdlKHsgdHlwZTogJ3JlYWR5JyB9KTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXRlcnJhaW4ud29ya2VyLmpzLm1hcAoK');
6784
+ /* eslint-enable */
6785
+
6786
+ /**
6787
+ * Pool of Web Workers for parallel terrain tessellation.
6788
+ * Distributes work across multiple workers based on CPU core count.
6789
+ *
6790
+ * SINGLETON PATTERN: One global pool is shared across all CogTiles instances
6791
+ * to avoid expensive worker creation/destruction during deck.gl layer recreations.
6792
+ */
6793
+ // @ts-expect-error - The import statement will be handled by both Rollup (web-worker: prefix)
6794
+ // and our Vite plugin (web-worker: → ?worker conversion)
6795
+ /**
6796
+ * Manages a pool of terrain tessellation workers.
6797
+ * Automatically scales to CPU core count (capped at 8 for memory safety).
6798
+ */
6799
+ class TerrainWorkerPool {
6800
+ workers = [];
6801
+ pendingTasks = new Map();
6802
+ taskCounter = 0;
6803
+ roundRobinIndex = 0;
6804
+ constructor(poolSize) {
6805
+ // Default to CPU core count, fallback to 4, cap at 8 to prevent memory exhaustion
6806
+ const defaultSize = Math.min(navigator.hardwareConcurrency || 4, 8);
6807
+ // On low-memory devices (<4GB), use only 2 workers to avoid OOM
6808
+ const memoryAdjustedSize = navigator.deviceMemory && navigator.deviceMemory < 4
6809
+ ? 2
6810
+ : defaultSize;
6811
+ const size = poolSize ?? memoryAdjustedSize;
6812
+ for (let i = 0; i < size; i++) {
6813
+ const worker = new WorkerFactory();
6814
+ worker.onmessage = this.handleWorkerMessage.bind(this);
6815
+ worker.onerror = this.handleWorkerError.bind(this);
6816
+ this.workers.push(worker);
6817
+ }
6818
+ }
6819
+ /**
6820
+ * Compute terrain mesh using the worker pool.
6821
+ * Returns a Promise that resolves with the mesh data.
6822
+ * Supports cancellation via AbortSignal.
6823
+ */
6824
+ async computeMesh(options) {
6825
+ const { terrain, meshMaxError, tesselator, width, height, signal } = options;
6826
+ // Check if already aborted
6827
+ if (signal?.aborted) {
6828
+ throw new DOMException('Aborted', 'AbortError');
6829
+ }
6830
+ const taskId = `task_${++this.taskCounter}`;
6831
+ return new Promise((resolve, reject) => {
6832
+ // Pick worker once — used for both dispatch and abort to ensure correct routing
6833
+ const worker = this.getNextWorker();
6834
+ const task = { resolve, reject, aborted: false, worker };
6835
+ this.pendingTasks.set(taskId, task);
6836
+ // Handle abort signal
6837
+ if (signal) {
6838
+ signal.addEventListener('abort', () => {
6839
+ // Guard against late aborts: only process if task still exists
6840
+ if (!this.pendingTasks.has(taskId)) {
6841
+ return;
6842
+ }
6843
+ task.aborted = true;
6844
+ this.pendingTasks.delete(taskId);
6845
+ // Send abort to the same worker that owns this task
6846
+ worker.postMessage({ type: 'abort', taskId });
6847
+ reject(new DOMException('Aborted', 'AbortError'));
6848
+ }, { once: true });
6849
+ }
6850
+ // Transfer terrain buffer ownership to avoid copy
6851
+ // NOTE: After this, `terrain` becomes detached on main thread
6852
+ worker.postMessage({
6853
+ type: 'computeMesh',
6854
+ taskId,
6855
+ terrain,
6856
+ meshMaxError,
6857
+ tesselator,
6858
+ width,
6859
+ height,
6860
+ }, [terrain.buffer] // ← Transferable
6861
+ );
6862
+ });
6863
+ }
6864
+ getNextWorker() {
6865
+ const worker = this.workers[this.roundRobinIndex];
6866
+ this.roundRobinIndex = (this.roundRobinIndex + 1) % this.workers.length;
6867
+ return worker;
6868
+ }
6869
+ handleWorkerMessage(e) {
6870
+ const { type, taskId, result, terrain, error } = e.data;
6871
+ if (type === 'ready') {
6872
+ // Worker initialization complete (can be ignored)
6873
+ return;
6874
+ }
6875
+ const task = this.pendingTasks.get(taskId);
6876
+ if (!task) {
6877
+ // Task was aborted or already resolved
6878
+ return;
6879
+ }
6880
+ this.pendingTasks.delete(taskId);
6881
+ if (task.aborted) {
6882
+ // Ignore result for aborted task
6883
+ return;
6884
+ }
6885
+ if (type === 'meshResult') {
6886
+ // Combine result with terrain that was transferred back
6887
+ task.resolve({
6888
+ vertices: result.vertices,
6889
+ triangles: result.triangles,
6890
+ terrain, // ← Include terrain from message
6891
+ });
6892
+ }
6893
+ else if (type === 'error') {
6894
+ task.reject(new Error(`Worker error: ${error}`));
6895
+ }
6896
+ }
6897
+ handleWorkerError(e) {
6898
+ // Find which worker failed (e.target is the Worker instance)
6899
+ const failedWorker = e.target;
6900
+ // Find all pending tasks assigned to this worker
6901
+ const failedTaskIds = [];
6902
+ this.pendingTasks.forEach((task, taskId) => {
6903
+ if (task.worker === failedWorker) {
6904
+ failedTaskIds.push(taskId);
6905
+ }
6906
+ });
6907
+ // Reject all tasks for the failed worker
6908
+ for (const taskId of failedTaskIds) {
6909
+ const task = this.pendingTasks.get(taskId);
6910
+ if (task) {
6911
+ this.pendingTasks.delete(taskId);
6912
+ task.reject(new Error(`Worker crashed: ${e.message || 'Unknown error'}`));
6913
+ }
6914
+ }
6915
+ // Respawn the failed worker to maintain pool capacity
6916
+ const workerIndex = this.workers.indexOf(failedWorker);
6917
+ if (workerIndex !== -1) {
6918
+ try {
6919
+ const newWorker = new WorkerFactory();
6920
+ newWorker.onmessage = this.handleWorkerMessage.bind(this);
6921
+ newWorker.onerror = this.handleWorkerError.bind(this);
6922
+ this.workers[workerIndex] = newWorker;
6923
+ }
6924
+ catch (spawnError) {
6925
+ // eslint-disable-next-line no-console
6926
+ console.error('[TerrainWorkerPool] Failed to respawn worker:', spawnError);
6927
+ }
6928
+ }
6929
+ }
6930
+ /**
6931
+ * Terminate all workers.
6932
+ * ⚠️ NOTE: Because this is a singleton, terminate() should only be called
6933
+ * on app shutdown, not on individual layer unmount.
6934
+ */
6935
+ terminate() {
6936
+ // Reject all pending tasks before terminating workers
6937
+ this.pendingTasks.forEach(task => {
6938
+ task.reject(new DOMException('Worker pool terminated', 'AbortError'));
6939
+ });
6940
+ this.pendingTasks.clear();
6941
+ this.workers.forEach(w => w.terminate());
6942
+ this.workers = [];
6943
+ }
6944
+ }
6945
+ // ─── SINGLETON INSTANCE ───
6946
+ // Lazily initialized on first use; shared across all CogTiles instances
6947
+ let globalWorkerPool = null;
6948
+ /**
6949
+ * Gets the global terrain worker pool, creating it on first use.
6950
+ * All CogTiles instances share this pool to avoid expensive worker churn
6951
+ * during deck.gl layer recreations.
6952
+ */
6953
+ function getGlobalTerrainWorkerPool() {
6954
+ if (!globalWorkerPool) {
6955
+ globalWorkerPool = new TerrainWorkerPool();
6956
+ }
6957
+ return globalWorkerPool;
6958
+ }
6959
+ /**
6960
+ * Terminates the global worker pool.
6961
+ * Only call this on app shutdown, not on layer unmount.
6962
+ */
6963
+ function terminateGlobalTerrainWorkerPool() {
6964
+ if (globalWorkerPool) {
6965
+ globalWorkerPool.terminate();
6966
+ globalWorkerPool = null;
6967
+ }
6968
+ }
6969
+
6739
6970
  const EARTH_CIRCUMFERENCE = 2 * Math.PI * 6378137;
6740
6971
  const EARTH_HALF_CIRCUMFERENCE = EARTH_CIRCUMFERENCE / 2;
6741
6972
  const webMercatorOrigin = [-20037508.342789244, 20037508.342789244];
@@ -7089,12 +7320,18 @@ class CogTiles {
7089
7320
  // future cache hits just await the already-resolved promise directly.
7090
7321
  cache = new TileCacheManager();
7091
7322
  tileReader;
7323
+ workerPool; // TerrainWorkerPool (for terrain tiles)
7092
7324
  // Store initialization promise to prevent concurrent duplicate initializations
7093
7325
  initializePromise;
7094
7326
  // Track the last successfully initialized URL to detect URL changes
7095
7327
  lastInitializedUrl;
7096
7328
  constructor(options) {
7097
7329
  this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
7330
+ // Get reference to global worker pool for terrain tiles
7331
+ // Do NOT create a new pool per instance — reuse the singleton
7332
+ if (options.type === 'terrain') {
7333
+ this.workerPool = getGlobalTerrainWorkerPool();
7334
+ }
7098
7335
  }
7099
7336
  async initializeCog(url) {
7100
7337
  // Reuse existing initialization while it is in progress, or when the same URL
@@ -7626,7 +7863,7 @@ class CogTiles {
7626
7863
  height: requiredSize,
7627
7864
  bounds: bounds ?? [0, 0, 0, 0],
7628
7865
  cellSizeMeters,
7629
- }, generatorOptions, resolvedMeshMaxError);
7866
+ }, generatorOptions, resolvedMeshMaxError, this.workerPool, controller.signal);
7630
7867
  })();
7631
7868
  const entry = {
7632
7869
  promise: pipeline,
@@ -7680,7 +7917,7 @@ class CogTiles {
7680
7917
  height: this.tileSize,
7681
7918
  bounds: bounds ?? [0, 0, 0, 0],
7682
7919
  cellSizeMeters,
7683
- }, this.options, meshMaxError ?? 4.0);
7920
+ }, this.options, meshMaxError ?? 4.0, this.workerPool, signal);
7684
7921
  }
7685
7922
  async getBitmapTile(x, y, z, bounds, cellSizeMeters, meshMaxError, signal) {
7686
7923
  const rasterKey = this.cache.getTileCacheKey(x, y, z);
@@ -7702,7 +7939,7 @@ class CogTiles {
7702
7939
  height: this.tileSize,
7703
7940
  bounds: bounds ?? [0, 0, 0, 0],
7704
7941
  cellSizeMeters,
7705
- }, this.options, meshMaxError ?? 4.0);
7942
+ }, this.options, meshMaxError ?? 4.0, this.workerPool);
7706
7943
  }
7707
7944
  async getTileAllBands(x, y, z, meshMaxError, signal, bounds) {
7708
7945
  if (!this.cog) {
@@ -7799,7 +8036,7 @@ class CogTiles {
7799
8036
  height: FETCH_SIZE,
7800
8037
  bounds: bounds ?? [0, 0, 0, 0],
7801
8038
  cellSizeMeters,
7802
- }, generatorOptions, resolvedMeshMaxError);
8039
+ }, generatorOptions, resolvedMeshMaxError, this.workerPool, signal);
7803
8040
  if (tileResult)
7804
8041
  results.push(tileResult);
7805
8042
  }
@@ -15679,4 +15916,5 @@ exports.GeoImage = GeoImage;
15679
15916
  exports.extractTerrainCoordinate = extractTerrainCoordinate;
15680
15917
  exports.sampleTerrainTileCoordinates = sampleTerrainTileCoordinates;
15681
15918
  exports.suppressGlobalAbortErrors = suppressGlobalAbortErrors;
15919
+ exports.terminateGlobalTerrainWorkerPool = terminateGlobalTerrainWorkerPool;
15682
15920
  //# sourceMappingURL=index.js.map