{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  },
  "accelerator": "GPU",
  "colab": {
   "provenance": [],
   "gpuType": "T4"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Scan3D — MASt3R + Cloudflare\n",
    "\n",
    "1. **Runtime → Modifier le type d'exécution → T4 GPU**\n",
    "2. **Runtime → Tout exécuter**\n",
    "3. Attends le lien `https://….trycloudflare.com`\n",
    "4. Ouvre ce lien (ou colle-le sur https://scan3d.pages.dev/)\n",
    "\n",
    "La cellule 2 **écrit les fichiers localement** (plus de téléchargement Pages).\n",
    "\n",
    "⚠️ Garde cet onglet Colab ouvert pendant le scan.\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Vérif GPU\n",
    "import torch\n",
    "print('CUDA:', torch.cuda.is_available())\n",
    "if torch.cuda.is_available():\n",
    "    print('GPU:', torch.cuda.get_device_name(0))\n",
    "else:\n",
    "    print(\"⚠️ Active un GPU: Runtime → Modifier le type d'exécution → T4 GPU\")\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Écrit les fichiers Scan3D (intégrés au notebook — AUCUN téléchargement Pages)\n",
    "import base64\n",
    "import pathlib\n",
    "\n",
    "ROOT = pathlib.Path(\"/content/scan3d\")\n",
    "ROOT.mkdir(parents=True, exist_ok=True)\n",
    "\n",
    "APP_B64 = \"\"\"IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiIKU2NhbjNEIOKAlCBpbnRlcmZhY2UgR3JhZGlvIHByb3ByZSBwb3VyIHJlY29uc3RydWN0aW9uIDNEIE1BU3QzUiAoQ29sYWIgKyBDbG91ZGZsYXJlKS4KVXBsb2FkIHBob3RvcyBvdSB2aWTDqW8g4oaSIG51YWdlIGRlIHBvaW50cyBkZW5zZSAoLnBseSkuCiIiIgpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgppbXBvcnQgZ2MKaW1wb3J0IG9zCmltcG9ydCBzaHV0aWwKaW1wb3J0IHN1YnByb2Nlc3MKaW1wb3J0IHN5cwppbXBvcnQgdGVtcGZpbGUKaW1wb3J0IHRpbWUKaW1wb3J0IHV1aWQKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgppbXBvcnQgZ3JhZGlvIGFzIGdyCmltcG9ydCBudW1weSBhcyBucAoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBQYXRocyAocmVwbyBtYXN0M3IgbXVzdCBiZSBvbiBQWVRIT05QQVRIIG9yIG5leHQgdG8gdGhpcyBmaWxlKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpST09UID0gUGF0aChfX2ZpbGVfXykucmVzb2x2ZSgpLnBhcmVudApNQVNUM1JfUk9PVCA9IFBhdGgob3MuZW52aXJvbi5nZXQoIk1BU1QzUl9ST09UIiwgUk9PVCAvICJtYXN0M3IiKSkucmVzb2x2ZSgpCldPUksgPSBQYXRoKG9zLmVudmlyb24uZ2V0KCJTQ0FOM0RfV09SSyIsIFJPT1QgLyAid29yayIpKS5yZXNvbHZlKCkKV09SSy5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCgpmb3IgcCBpbiAoc3RyKE1BU1QzUl9ST09UKSwgc3RyKE1BU1QzUl9ST09UIC8gImR1c3QzciIpKToKICAgIGlmIHAgbm90IGluIHN5cy5wYXRoOgogICAgICAgIHN5cy5wYXRoLmluc2VydCgwLCBwKQoKREVWSUNFID0gImN1ZGEiIGlmIG9zLmVudmlyb24uZ2V0KCJDVURBX1ZJU0lCTEVfREVWSUNFUyIsICIwIikgIT0gIiIgZWxzZSAiY3B1IgpNT0RFTF9OQU1FID0gb3MuZW52aXJvbi5nZXQoCiAgICAiTUFTVDNSX01PREVMIiwKICAgICJuYXZlci9NQVN0M1JfVmlUTGFyZ2VfQmFzZURlY29kZXJfNTEyX2NhdG1scGRwdF9tZXRyaWMiLAopCklNQUdFX1NJWkUgPSBpbnQob3MuZW52aXJvbi5nZXQoIk1BU1QzUl9JTUFHRV9TSVpFIiwgIjUxMiIpKQpNQVhfRlJBTUVTID0gaW50KG9zLmVudmlyb24uZ2V0KCJTQ0FOM0RfTUFYX0ZSQU1FUyIsICIxMiIpKQoKX21vZGVsID0gTm9uZQpfc3RhdHVzID0gIk1vZMOobGUgbm9uIGNoYXJnw6kiCgoKZGVmIGdldF9kZXZpY2UoKSAtPiBzdHI6CiAgICB0cnk6CiAgICAgICAgaW1wb3J0IHRvcmNoCgogICAgICAgIHJldHVybiAiY3VkYSIgaWYgdG9yY2guY3VkYS5pc19hdmFpbGFibGUoKSBlbHNlICJjcHUiCiAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgIHJldHVybiAiY3B1IgoKCmRlZiBsb2FkX21vZGVsKCk6CiAgICBnbG9iYWwgX21vZGVsLCBfc3RhdHVzCiAgICBpZiBfbW9kZWwgaXMgbm90IE5vbmU6CiAgICAgICAgcmV0dXJuIF9tb2RlbAoKICAgIGltcG9ydCB0b3JjaAogICAgZnJvbSBtYXN0M3IubW9kZWwgaW1wb3J0IEFzeW1tZXRyaWNNQVN0M1IKCiAgICBkZXZpY2UgPSBnZXRfZGV2aWNlKCkKICAgIF9zdGF0dXMgPSBmIkNoYXJnZW1lbnQgTUFTdDNSIHN1ciB7ZGV2aWNlfeKApiIKICAgIHByaW50KF9zdGF0dXMsIGZsdXNoPVRydWUpCiAgICBtb2RlbCA9IEFzeW1tZXRyaWNNQVN0M1IuZnJvbV9wcmV0cmFpbmVkKE1PREVMX05BTUUpLnRvKGRldmljZSkKICAgIG1vZGVsLmV2YWwoKQogICAgX21vZGVsID0gbW9kZWwKICAgIF9zdGF0dXMgPSBmIlByw6p0ICh7ZGV2aWNlfSkiCiAgICBwcmludChfc3RhdHVzLCBmbHVzaD1UcnVlKQogICAgcmV0dXJuIF9tb2RlbAoKCmRlZiBleHRyYWN0X2ZyYW1lc19mcm9tX3ZpZGVvKHZpZGVvX3BhdGg6IHN0ciwgbWF4X2ZyYW1lczogaW50ID0gTUFYX0ZSQU1FUykgLT4gbGlzdFtzdHJdOgogICAgIiIiRXh0cmFjdCBldmVubHkgc3BhY2VkIGZyYW1lcyBmcm9tIGEgdmlkZW8gd2l0aCBPcGVuQ1Ygb3IgZmZtcGVnLiIiIgogICAgb3V0X2RpciA9IFdPUksgLyBmImZyYW1lc197dXVpZC51dWlkNCgpLmhleFs6OF19IgogICAgb3V0X2Rpci5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCgogICAgdHJ5OgogICAgICAgIGltcG9ydCBjdjIKCiAgICAgICAgY2FwID0gY3YyLlZpZGVvQ2FwdHVyZSh2aWRlb19wYXRoKQogICAgICAgIGlmIG5vdCBjYXAuaXNPcGVuZWQoKToKICAgICAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKCJJbXBvc3NpYmxlIGQnb3V2cmlyIGxhIHZpZMOpbyIpCiAgICAgICAgdG90YWwgPSBpbnQoY2FwLmdldChjdjIuQ0FQX1BST1BfRlJBTUVfQ09VTlQpIG9yIDApCiAgICAgICAgaWYgdG90YWwgPD0gMDoKICAgICAgICAgICAgdG90YWwgPSBtYXhfZnJhbWVzICogNQogICAgICAgIGluZGljZXMgPSBucC5saW5zcGFjZSgwLCBtYXgodG90YWwgLSAxLCAwKSwgbnVtPW1pbihtYXhfZnJhbWVzLCBtYXgodG90YWwsIDEpKSkKICAgICAgICBpbmRpY2VzID0gc29ydGVkKHNldChpbnQoaSkgZm9yIGkgaW4gaW5kaWNlcykpCiAgICAgICAgcGF0aHM6IGxpc3Rbc3RyXSA9IFtdCiAgICAgICAgaWR4X3NldCA9IHNldChpbmRpY2VzKQogICAgICAgIGZpID0gMAogICAgICAgIHdoaWxlIFRydWU6CiAgICAgICAgICAgIG9rLCBmcmFtZSA9IGNhcC5yZWFkKCkKICAgICAgICAgICAgaWYgbm90IG9rOgogICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgaWYgZmkgaW4gaWR4X3NldDoKICAgICAgICAgICAgICAgIHAgPSBvdXRfZGlyIC8gZiJmcmFtZV97bGVuKHBhdGhzKTowM2R9LmpwZyIKICAgICAgICAgICAgICAgIGN2Mi5pbXdyaXRlKHN0cihwKSwgZnJhbWUsIFtpbnQoY3YyLklNV1JJVEVfSlBFR19RVUFMSVRZKSwgOTJdKQogICAgICAgICAgICAgICAgcGF0aHMuYXBwZW5kKHN0cihwKSkKICAgICAgICAgICAgZmkgKz0gMQogICAgICAgICAgICBpZiBsZW4ocGF0aHMpID49IG1heF9mcmFtZXM6CiAgICAgICAgICAgICAgICBicmVhawogICAgICAgIGNhcC5yZWxlYXNlKCkKICAgICAgICBpZiBwYXRoczoKICAgICAgICAgICAgcmV0dXJuIHBhdGhzCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiJPcGVuQ1YgZmFsbGJhY2sg4oaSIGZmbXBlZzoge2V9IiwgZmx1c2g9VHJ1ZSkKCiAgICAjIGZmbXBlZyBmYWxsYmFjawogICAgcGF0dGVybiA9IHN0cihvdXRfZGlyIC8gImZyYW1lXyUwM2QuanBnIikKICAgICMgc2FtcGxlIH5tYXhfZnJhbWVzIG92ZXIgZHVyYXRpb24KICAgIGNtZCA9IFsKICAgICAgICAiZmZtcGVnIiwKICAgICAgICAiLXkiLAogICAgICAgICItaSIsCiAgICAgICAgdmlkZW9fcGF0aCwKICAgICAgICAiLXZmIiwKICAgICAgICBmImZwcz0xL3ttYXgoMSwgMil9IiwKICAgICAgICAiLWZyYW1lczp2IiwKICAgICAgICBzdHIobWF4X2ZyYW1lcyksCiAgICAgICAgIi1xOnYiLAogICAgICAgICIyIiwKICAgICAgICBwYXR0ZXJuLAogICAgXQogICAgc3VicHJvY2Vzcy5ydW4oY21kLCBjaGVjaz1GYWxzZSwgY2FwdHVyZV9vdXRwdXQ9VHJ1ZSkKICAgIHBhdGhzID0gc29ydGVkKHN0cihwKSBmb3IgcCBpbiBvdXRfZGlyLmdsb2IoImZyYW1lXyouanBnIikpCiAgICBpZiBub3QgcGF0aHM6CiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKCJBdWN1bmUgZnJhbWUgZXh0cmFpdGUgZGUgbGEgdmlkw6lvIikKICAgIHJldHVybiBwYXRoc1s6bWF4X2ZyYW1lc10KCgpkZWYgY29sbGVjdF9pbWFnZV9wYXRocyhpbWFnZXMsIHZpZGVvKSAtPiBsaXN0W3N0cl06CiAgICBwYXRoczogbGlzdFtzdHJdID0gW10KICAgIGlmIGltYWdlczoKICAgICAgICBmb3IgaXRlbSBpbiBpbWFnZXM6CiAgICAgICAgICAgIGlmIGl0ZW0gaXMgTm9uZToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICMgR3JhZGlvIEZpbGUgLyBJbWFnZSBjYW4gYmUgcGF0aCBzdHIsIGRpY3QsIG9yIG51bXB5CiAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoaXRlbSwgZGljdCk6CiAgICAgICAgICAgICAgICBwID0gaXRlbS5nZXQoIm5hbWUiKSBvciBpdGVtLmdldCgicGF0aCIpCiAgICAgICAgICAgIGVsaWYgaXNpbnN0YW5jZShpdGVtLCAoc3RyLCBQYXRoKSk6CiAgICAgICAgICAgICAgICBwID0gc3RyKGl0ZW0pCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAjIG51bXB5IGltYWdlIGZyb20gZ3IuSW1hZ2Ug4oCUIHNhdmUgdGVtcAogICAgICAgICAgICAgICAgcCA9IHN0cihXT1JLIC8gZiJpbWdfe3V1aWQudXVpZDQoKS5oZXhbOjhdfS5qcGciKQogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGZyb20gUElMIGltcG9ydCBJbWFnZQoKICAgICAgICAgICAgICAgICAgICBpZiBoYXNhdHRyKGl0ZW0sICJzYXZlIik6CiAgICAgICAgICAgICAgICAgICAgICAgIGl0ZW0uc2F2ZShwKQogICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgIEltYWdlLmZyb21hcnJheShucC5hc2FycmF5KGl0ZW0pLmFzdHlwZShucC51aW50OCkpLnNhdmUocCkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgaWYgcCBhbmQgUGF0aChwKS5leGlzdHMoKToKICAgICAgICAgICAgICAgIHBhdGhzLmFwcGVuZChwKQoKICAgIGlmIHZpZGVvOgogICAgICAgIHZwYXRoID0gdmlkZW8gaWYgaXNpbnN0YW5jZSh2aWRlbywgc3RyKSBlbHNlIGdldGF0dHIodmlkZW8sICJuYW1lIiwgTm9uZSkgb3Igc3RyKHZpZGVvKQogICAgICAgIGlmIHZwYXRoIGFuZCBQYXRoKHZwYXRoKS5leGlzdHMoKToKICAgICAgICAgICAgcGF0aHMuZXh0ZW5kKGV4dHJhY3RfZnJhbWVzX2Zyb21fdmlkZW8odnBhdGgpKQoKICAgICMgdW5pcXVlIHByZXNlcnZlIG9yZGVyCiAgICBzZWVuID0gc2V0KCkKICAgIG91dCA9IFtdCiAgICBmb3IgcCBpbiBwYXRoczoKICAgICAgICBpZiBwIG5vdCBpbiBzZWVuIGFuZCBQYXRoKHApLmV4aXN0cygpOgogICAgICAgICAgICBzZWVuLmFkZChwKQogICAgICAgICAgICBvdXQuYXBwZW5kKHApCiAgICByZXR1cm4gb3V0CgoKZGVmIHJlY29uc3RydWN0KGltYWdlX3BhdGhzOiBsaXN0W3N0cl0sIG5fZnJhbWVzOiBpbnQsIHNjZW5lX2dyYXBoOiBzdHIpIC0+IHR1cGxlW3N0ciwgc3RyLCBzdHJdOgogICAgIiIiCiAgICBSdW4gTUFTdDNSIHNwYXJzZSBnbG9iYWwgYWxpZ25tZW50IChzYW1lIHBhdGggYXMgb2ZmaWNpYWwgZGVtbykg4oaSIEdMQiArIFBMWS4KICAgIFJldHVybnMgKGdsYl9wYXRoLCBzdGF0dXNfbXNnLCBwcmV2aWV3X25vdGUpLgogICAgIiIiCiAgICBpbXBvcnQgY29weQogICAgaW1wb3J0IHRvcmNoCiAgICBpbXBvcnQgdHJpbWVzaAogICAgaW1wb3J0IG1hc3Qzci51dGlscy5wYXRoX3RvX2R1c3QzciAgIyBub3FhOiBGNDAxCiAgICBmcm9tIGR1c3Qzci51dGlscy5kZXZpY2UgaW1wb3J0IHRvX251bXB5CiAgICBmcm9tIGR1c3Qzci51dGlscy5pbWFnZSBpbXBvcnQgbG9hZF9pbWFnZXMKICAgIGZyb20gbWFzdDNyLmNsb3VkX29wdC5zcGFyc2VfZ2EgaW1wb3J0IHNwYXJzZV9nbG9iYWxfYWxpZ25tZW50CiAgICBmcm9tIG1hc3Qzci5pbWFnZV9wYWlycyBpbXBvcnQgbWFrZV9wYWlycwoKICAgIGlmIGxlbihpbWFnZV9wYXRocykgPCAxOgogICAgICAgIHJhaXNlIGdyLkVycm9yKCJBam91dGUgZGVzIGltYWdlcyBvdSB1bmUgdmlkw6lvLiIpCgogICAgaW1hZ2VfcGF0aHMgPSBpbWFnZV9wYXRoc1s6IG1heCgyLCBpbnQobl9mcmFtZXMpKV0KICAgIGRldmljZSA9IGdldF9kZXZpY2UoKQogICAgbW9kZWwgPSBsb2FkX21vZGVsKCkKCiAgICBqb2IgPSBXT1JLIC8gZiJqb2Jfe3V1aWQudXVpZDQoKS5oZXhbOjEwXX0iCiAgICBqb2IubWtkaXIocGFyZW50cz1UcnVlLCBleGlzdF9vaz1UcnVlKQoKICAgIGxvY2FsX2ltZ3MgPSBbXQogICAgZm9yIGksIHNyYyBpbiBlbnVtZXJhdGUoaW1hZ2VfcGF0aHMpOgogICAgICAgIGRzdCA9IGpvYiAvIGYiaW5wdXRfe2k6MDNkfXtQYXRoKHNyYykuc3VmZml4Lmxvd2VyKCkgb3IgJy5qcGcnfSIKICAgICAgICBzaHV0aWwuY29weTIoc3JjLCBkc3QpCiAgICAgICAgbG9jYWxfaW1ncy5hcHBlbmQoc3RyKGRzdCkpCgogICAgaW1ncyA9IGxvYWRfaW1hZ2VzKGxvY2FsX2ltZ3MsIHNpemU9SU1BR0VfU0laRSwgdmVyYm9zZT1UcnVlKQogICAgZmlsZWxpc3QgPSBsaXN0KGxvY2FsX2ltZ3MpCiAgICAjIE9mZmljaWFsIGRlbW86IGR1cGxpY2F0ZSBzaW5nbGUgaW1hZ2Ugc28gcGlwZWxpbmUgc3RpbGwgcnVucwogICAgaWYgbGVuKGltZ3MpID09IDE6CiAgICAgICAgaW1ncyA9IFtpbWdzWzBdLCBjb3B5LmRlZXBjb3B5KGltZ3NbMF0pXQogICAgICAgIGltZ3NbMV1bImlkeCJdID0gMQogICAgICAgIGZpbGVsaXN0ID0gW2ZpbGVsaXN0WzBdLCBmaWxlbGlzdFswXSArICJfMiJdCgogICAgcGFpcnMgPSBtYWtlX3BhaXJzKGltZ3MsIHNjZW5lX2dyYXBoPXNjZW5lX2dyYXBoLCBwcmVmaWx0ZXI9Tm9uZSwgc3ltbWV0cml6ZT1UcnVlKQoKICAgIGNhY2hlX2RpciA9IHN0cihqb2IgLyAiY2FjaGUiKQogICAgb3MubWFrZWRpcnMoY2FjaGVfZGlyLCBleGlzdF9vaz1UcnVlKQoKICAgICMgQWxpZ24gd2l0aCBvZmZpY2lhbCBtYXN0M3IvZGVtby5weSBkZWZhdWx0cyAobGlnaHRlciBuaXRlciBmb3IgQ29sYWIgVDQpCiAgICBzY2VuZSA9IHNwYXJzZV9nbG9iYWxfYWxpZ25tZW50KAogICAgICAgIGZpbGVsaXN0LAogICAgICAgIHBhaXJzLAogICAgICAgIGNhY2hlX2RpciwKICAgICAgICBtb2RlbCwKICAgICAgICBscjE9MC4wNywKICAgICAgICBuaXRlcjE9MjAwLAogICAgICAgIGxyMj0wLjAxLAogICAgICAgIG5pdGVyMj0yMDAsCiAgICAgICAgZGV2aWNlPWRldmljZSwKICAgICAgICBvcHRfZGVwdGg9VHJ1ZSwKICAgICAgICBzaGFyZWRfaW50cmluc2ljcz1GYWxzZSwKICAgICAgICBtYXRjaGluZ19jb25mX3Rocj0wLjAsCiAgICApCgogICAgbWluX2NvbmZfdGhyID0gMS41CiAgICBwdHMzZCwgXywgY29uZnMgPSB0b19udW1weShzY2VuZS5nZXRfZGVuc2VfcHRzM2QoY2xlYW5fZGVwdGg9VHJ1ZSkpCiAgICBtc2sgPSB0b19udW1weShbYyA+IG1pbl9jb25mX3RociBmb3IgYyBpbiBjb25mc10pCiAgICByZ2JpbWcgPSBzY2VuZS5pbWdzCgogICAgIyBCdWlsZCBjb2xvcmVkIHBvaW50IGNsb3VkIHdpdGggdHJpbWVzaCDihpIgR0xCIChiZXN0IGZvciBHcmFkaW8gTW9kZWwzRCkKICAgIHB0cyA9IG5wLmNvbmNhdGVuYXRlKFtwW20ucmF2ZWwoKV0gZm9yIHAsIG0gaW4gemlwKHB0czNkLCBtc2spXSkucmVzaGFwZSgtMSwgMykKICAgIGNvbCA9IG5wLmNvbmNhdGVuYXRlKFtwW21dIGZvciBwLCBtIGluIHppcChyZ2JpbWcsIG1zayldKS5yZXNoYXBlKC0xLCAzKQogICAgdmFsaWQgPSBucC5pc2Zpbml0ZShwdHMuc3VtKGF4aXM9MSkpCiAgICBwdHMsIGNvbCA9IHB0c1t2YWxpZF0sIGNvbFt2YWxpZF0KICAgIGlmIGNvbC5tYXgoKSA8PSAxLjAxOgogICAgICAgIGNvbCA9IChjb2wgKiAyNTUpLmNsaXAoMCwgMjU1KQogICAgY29sID0gY29sLmFzdHlwZShucC51aW50OCkKCiAgICBpZiBsZW4ocHRzKSA+IDkwMF8wMDA6CiAgICAgICAgc2VsID0gbnAucmFuZG9tLmNob2ljZShsZW4ocHRzKSwgOTAwXzAwMCwgcmVwbGFjZT1GYWxzZSkKICAgICAgICBwdHMsIGNvbCA9IHB0c1tzZWxdLCBjb2xbc2VsXQoKICAgIGlmIGxlbihwdHMpIDwgNTA6CiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKCJUcm9wIHBldSBkZSBwb2ludHMgM0Qg4oCUIGFqb3V0ZSBwbHVzIGRlIHZ1ZXMgLyBtZWlsbGV1ciBjaGV2YXVjaGVtZW50LiIpCgogICAgZ2xiX3BhdGggPSBzdHIoam9iIC8gInNjYW4uZ2xiIikKICAgIHBseV9wYXRoID0gc3RyKGpvYiAvICJzY2FuLnBseSIpCiAgICBwYyA9IHRyaW1lc2guUG9pbnRDbG91ZCh2ZXJ0aWNlcz1wdHMsIGNvbG9ycz1jb2wpCiAgICBzY2VuZV90ID0gdHJpbWVzaC5TY2VuZShwYykKICAgIHNjZW5lX3QuZXhwb3J0KGdsYl9wYXRoKQogICAgcGMuZXhwb3J0KHBseV9wYXRoKQoKICAgIG1zZyA9ICgKICAgICAgICBmIuKchSBSZWNvbnN0cnVjdGlvbiB0ZXJtaW7DqWUg4oCUIHtsZW4obG9jYWxfaW1ncyl9IHZ1ZXMsICIKICAgICAgICBmIntsZW4ocHRzKTosfSBwb2ludHMsIGRldmljZT17ZGV2aWNlfS4iCiAgICApCiAgICBub3RlID0gKAogICAgICAgICJBcGVyw6d1IEdMQiBjaS1kZXNzdXMuIFR1IHBldXggYXVzc2kgdMOpbMOpY2hhcmdlciBsZSBgLnBseWAgcG91ciBNZXNoTGFiIC8gQmxlbmRlci4gIgogICAgICAgICJUb3VybmUgYXV0b3VyIGRlIGzigJlvYmpldCBhdmVjIH42MOKAkzcwICUgZGUgY2hldmF1Y2hlbWVudCBlbnRyZSBwaG90b3MuIgogICAgKQoKICAgIGlmIGRldmljZSA9PSAiY3VkYSI6CiAgICAgICAgdG9yY2guY3VkYS5lbXB0eV9jYWNoZSgpCiAgICBnYy5jb2xsZWN0KCkKCiAgICByZXR1cm4gZ2xiX3BhdGgsIHBseV9wYXRoLCBtc2csIG5vdGUKCgpkZWYgcnVuX3NjYW4oaW1hZ2VzLCB2aWRlbywgbl9mcmFtZXMsIHNjZW5lX2dyYXBoLCBwcm9ncmVzcz1nci5Qcm9ncmVzcyh0cmFja190cWRtPVRydWUpKToKICAgIHQwID0gdGltZS50aW1lKCkKICAgIHByb2dyZXNzKDAuMDUsIGRlc2M9IkNvbGxlY3RlIGRlcyBpbWFnZXPigKYiKQogICAgdHJ5OgogICAgICAgIHBhdGhzID0gY29sbGVjdF9pbWFnZV9wYXRocyhpbWFnZXMsIHZpZGVvKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHJhaXNlIGdyLkVycm9yKGYiRW50csOpZSBpbnZhbGlkZSA6IHtlfSIpIGZyb20gZQoKICAgIGlmIGxlbihwYXRocykgPCAyOgogICAgICAgIHJhaXNlIGdyLkVycm9yKAogICAgICAgICAgICAiQWpvdXRlIGF1IG1vaW5zIDIgcGhvdG9zLCBvdSB1bmUgdmlkw6lvIGFzc2V6IGxvbmd1ZSBwb3VyIGVuIGV4dHJhaXJlIDIrIGZyYW1lcy4iCiAgICAgICAgKQoKICAgIHByb2dyZXNzKDAuMTUsIGRlc2M9ZiJNQVN0M1Ig4oCUIHtsZW4ocGF0aHMpfSB2dWVzIChwYXRpZW5jZSBzdXIgVDQp4oCmIikKICAgIHRyeToKICAgICAgICBnbGIsIHBseSwgbXNnLCBub3RlID0gcmVjb25zdHJ1Y3QocGF0aHMsIGludChuX2ZyYW1lcyksIHNjZW5lX2dyYXBoKQogICAgZXhjZXB0IGdyLkVycm9yOgogICAgICAgIHJhaXNlCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgaW1wb3J0IHRyYWNlYmFjawoKICAgICAgICB0cmFjZWJhY2sucHJpbnRfZXhjKCkKICAgICAgICByYWlzZSBnci5FcnJvcihmIsOJY2hlYyByZWNvbnN0cnVjdGlvbiA6IHtlfSIpIGZyb20gZQoKICAgIGVsYXBzZWQgPSB0aW1lLnRpbWUoKSAtIHQwCiAgICBtc2cgPSBmInttc2d9ICDCtyAge2VsYXBzZWQ6LjFmfXMiCiAgICBwcm9ncmVzcygxLjAsIGRlc2M9IlRlcm1pbsOpIikKICAgIHJldHVybiBnbGIsIHBseSwgbXNnLCBub3RlCgoKQ1VTVE9NX0NTUyA9ICIiIgpAaW1wb3J0IHVybCgnaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1ETStTYW5zOml0YWwsb3Bzeix3Z2h0QDAsOS4uNDAsNDAwOzAsOS4uNDAsNTAwOzAsOS4uNDAsNjAwOzAsOS4uNDAsNzAwOzEsOS4uNDAsNDAwJmZhbWlseT1KZXRCcmFpbnMrTW9ubzp3Z2h0QDQwMDs1MDAmZGlzcGxheT1zd2FwJyk7Cgo6cm9vdCB7CiAgLS1iZzA6ICMwNzA5MGY7CiAgLS1iZzE6ICMwZTEyMWI7CiAgLS1iZzI6ICMxNTFiMjg7CiAgLS1saW5lOiByZ2JhKDI1NSwyNTUsMjU1LDAuMDgpOwogIC0tdGV4dDogI2VlZjJmZjsKICAtLW11dGVkOiAjOTRhM2I4OwogIC0tYWNjZW50OiAjMzhiZGY4OwogIC0tYWNjZW50MjogI2E3OGJmYTsKICAtLW9rOiAjMzRkMzk5OwogIC0tcmFkaXVzOiAxNnB4Owp9CgouZ3JhZGlvLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxMTAwcHggIWltcG9ydGFudDsKICBtYXJnaW46IDAgYXV0byAhaW1wb3J0YW50OwogIGZvbnQtZmFtaWx5OiAnRE0gU2FucycsIHN5c3RlbS11aSwgc2Fucy1zZXJpZiAhaW1wb3J0YW50OwogIGNvbG9yOiB2YXIoLS10ZXh0KSAhaW1wb3J0YW50OwogIGJhY2tncm91bmQ6IHJhZGlhbC1ncmFkaWVudCgxMjAwcHggNjAwcHggYXQgMTAlIC0xMCUsIHJnYmEoNTYsMTg5LDI0OCwwLjEyKSwgdHJhbnNwYXJlbnQgNTAlKSwKICAgICAgICAgICAgICByYWRpYWwtZ3JhZGllbnQoOTAwcHggNTAwcHggYXQgMTAwJSAwJSwgcmdiYSgxNjcsMTM5LDI1MCwwLjEwKSwgdHJhbnNwYXJlbnQgNDUlKSwKICAgICAgICAgICAgICB2YXIoLS1iZzApICFpbXBvcnRhbnQ7Cn0KCi5zY2FuLWhlcm8gewogIHBhZGRpbmc6IDI4cHggOHB4IDhweDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KLnNjYW4taGVybyBoMSB7CiAgZm9udC1zaXplOiAycmVtOwogIGZvbnQtd2VpZ2h0OiA3MDA7CiAgbGV0dGVyLXNwYWNpbmc6IC0wLjAzZW07CiAgbWFyZ2luOiAwIDAgOHB4OwogIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMjBkZWcsICNmZmYgMCUsICM3ZGQzZmMgNDUlLCAjYzRiNWZkIDEwMCUpOwogIC13ZWJraXQtYmFja2dyb3VuZC1jbGlwOiB0ZXh0OwogIC13ZWJraXQtdGV4dC1maWxsLWNvbG9yOiB0cmFuc3BhcmVudDsKICBiYWNrZ3JvdW5kLWNsaXA6IHRleHQ7Cn0KLnNjYW4taGVybyBwIHsKICBjb2xvcjogdmFyKC0tbXV0ZWQpOwogIGZvbnQtc2l6ZTogMS4wMnJlbTsKICBtYXJnaW46IDAgYXV0bzsKICBtYXgtd2lkdGg6IDU2MHB4OwogIGxpbmUtaGVpZ2h0OiAxLjU7Cn0KLnNjYW4tYmFkZ2UgewogIGRpc3BsYXk6IGlubGluZS1mbGV4OwogIGdhcDogOHB4OwogIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgbWFyZ2luLXRvcDogMTRweDsKICBwYWRkaW5nOiA2cHggMTJweDsKICBib3JkZXItcmFkaXVzOiA5OTlweDsKICBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1saW5lKTsKICBiYWNrZ3JvdW5kOiByZ2JhKDI1NSwyNTUsMjU1LDAuMDMpOwogIGNvbG9yOiB2YXIoLS1tdXRlZCk7CiAgZm9udC1zaXplOiAwLjgycmVtOwogIGZvbnQtZmFtaWx5OiAnSmV0QnJhaW5zIE1vbm8nLCBtb25vc3BhY2U7Cn0KLnNjYW4tYmFkZ2Ugc3Ryb25nIHsgY29sb3I6IHZhcigtLW9rKTsgZm9udC13ZWlnaHQ6IDUwMDsgfQoKZm9vdGVyLCAuZm9vdGVyIHsgZGlzcGxheTogbm9uZSAhaW1wb3J0YW50OyB9CgoucGFuZWwgewogIGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpICFpbXBvcnRhbnQ7CiAgYmFja2dyb3VuZDogcmdiYSgxNCwxOCwyNywwLjg1KSAhaW1wb3J0YW50OwogIGJvcmRlci1yYWRpdXM6IHZhcigtLXJhZGl1cykgIWltcG9ydGFudDsKICBiYWNrZHJvcC1maWx0ZXI6IGJsdXIoOHB4KTsKfQoKYnV0dG9uLnByaW1hcnksIC5wcmltYXJ5IHsKICBiYWNrZ3JvdW5kOiBsaW5lYXItZ3JhZGllbnQoMTM1ZGVnLCAjMGVhNWU5LCAjNjM2NmYxKSAhaW1wb3J0YW50OwogIGJvcmRlcjogbm9uZSAhaW1wb3J0YW50OwogIGNvbG9yOiB3aGl0ZSAhaW1wb3J0YW50OwogIGZvbnQtd2VpZ2h0OiA2MDAgIWltcG9ydGFudDsKICBib3JkZXItcmFkaXVzOiAxMnB4ICFpbXBvcnRhbnQ7CiAgYm94LXNoYWRvdzogMCA4cHggMjRweCByZ2JhKDE0LDE2NSwyMzMsMC4yNSk7Cn0KIiIiCgpUSEVNRSA9IGdyLnRoZW1lcy5Tb2Z0KAogICAgcHJpbWFyeV9odWU9InNreSIsCiAgICBzZWNvbmRhcnlfaHVlPSJ2aW9sZXQiLAogICAgbmV1dHJhbF9odWU9InNsYXRlIiwKICAgIGZvbnQ9Z3IudGhlbWVzLkdvb2dsZUZvbnQoIkRNIFNhbnMiKSwKKS5zZXQoCiAgICBib2R5X2JhY2tncm91bmRfZmlsbD0iIzA3MDkwZiIsCiAgICBib2R5X2JhY2tncm91bmRfZmlsbF9kYXJrPSIjMDcwOTBmIiwKICAgIGJsb2NrX2JhY2tncm91bmRfZmlsbD0iIzBlMTIxYiIsCiAgICBibG9ja19iYWNrZ3JvdW5kX2ZpbGxfZGFyaz0iIzBlMTIxYiIsCiAgICBib3JkZXJfY29sb3JfcHJpbWFyeT0icmdiYSgyNTUsMjU1LDI1NSwwLjA4KSIsCiAgICBibG9ja19ib3JkZXJfd2lkdGg9IjFweCIsCiAgICBibG9ja19yYWRpdXM9IjE2cHgiLAogICAgYnV0dG9uX3ByaW1hcnlfYmFja2dyb3VuZF9maWxsPSJsaW5lYXItZ3JhZGllbnQoMTM1ZGVnLCAjMGVhNWU5LCAjNjM2NmYxKSIsCiAgICBidXR0b25fcHJpbWFyeV90ZXh0X2NvbG9yPSJ3aGl0ZSIsCikKCgpkZWYgYnVpbGRfdWkoKToKICAgIHdpdGggZ3IuQmxvY2tzKHRpdGxlPSJTY2FuM0Qg4oCUIE1BU3QzUiIsIHRoZW1lPVRIRU1FLCBjc3M9Q1VTVE9NX0NTUykgYXMgZGVtbzoKICAgICAgICBnci5IVE1MKAogICAgICAgICAgICAiIiIKICAgICAgICAgICAgPGRpdiBjbGFzcz0ic2Nhbi1oZXJvIj4KICAgICAgICAgICAgICA8aDE+U2NhbjNEPC9oMT4KICAgICAgICAgICAgICA8cD5TY2FubmUgdW4gb2JqZXQgb3UgdW5lIHBpw6hjZSDDoCBwYXJ0aXIgZGUgcGhvdG9zIC8gdmlkw6lvIOKAlAogICAgICAgICAgICAgIHJlY29uc3RydWN0aW9uIGRlbnNlIG9wZW4gc291cmNlIDxiPk1BU3QzUjwvYj4sIHNhbnMgTGlEQVIuPC9wPgogICAgICAgICAgICAgIDxkaXYgY2xhc3M9InNjYW4tYmFkZ2UiPkdQVSBDb2xhYiDCtyB0dW5uZWwgQ2xvdWRmbGFyZSDCtyA8c3Ryb25nPmhhdXRlIHF1YWxpdMOpPC9zdHJvbmc+PC9kaXY+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAiIiIKICAgICAgICApCgogICAgICAgIHdpdGggZ3IuUm93KCk6CiAgICAgICAgICAgIHdpdGggZ3IuQ29sdW1uKHNjYWxlPTUpOgogICAgICAgICAgICAgICAgaW1hZ2VzID0gZ3IuRmlsZSgKICAgICAgICAgICAgICAgICAgICBsYWJlbD0iUGhvdG9zICgyKyB2dWVzLCBhbmdsZXMgZGlmZsOpcmVudHMpIiwKICAgICAgICAgICAgICAgICAgICBmaWxlX2NvdW50PSJtdWx0aXBsZSIsCiAgICAgICAgICAgICAgICAgICAgZmlsZV90eXBlcz1bImltYWdlIl0sCiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0PTE0MCwKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHZpZGVvID0gZ3IuVmlkZW8oCiAgICAgICAgICAgICAgICAgICAgbGFiZWw9Ik91IHVuZSBjb3VydGUgdmlkw6lvIChvbiBleHRyYWl0IGRlcyBmcmFtZXMpIiwKICAgICAgICAgICAgICAgICAgICBoZWlnaHQ9MjIwLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgd2l0aCBnci5BY2NvcmRpb24oIk9wdGlvbnMgYXZhbmPDqWVzIiwgb3Blbj1GYWxzZSk6CiAgICAgICAgICAgICAgICAgICAgbl9mcmFtZXMgPSBnci5TbGlkZXIoCiAgICAgICAgICAgICAgICAgICAgICAgIDIsCiAgICAgICAgICAgICAgICAgICAgICAgIDIwLAogICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZT04LAogICAgICAgICAgICAgICAgICAgICAgICBzdGVwPTEsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPSJOb21icmUgbWF4IGRlIHZ1ZXMgdXRpbGlzw6llcyIsCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgIHNjZW5lX2dyYXBoID0gZ3IuRHJvcGRvd24oCiAgICAgICAgICAgICAgICAgICAgICAgIGNob2ljZXM9WyJjb21wbGV0ZSIsICJzd2luIiwgImxvZ3dpbiIsICJvbmVyZWYiXSwKICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9ImNvbXBsZXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9IkdyYXBoZSBkZSBwYWlyZXMgKGNvbXBsZXRlID0gbWF4IHF1YWxpdMOpLCBwbHVzIGxlbnQpIiwKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBidG4gPSBnci5CdXR0b24oIkxhbmNlciBsZSBzY2FuIDNEIiwgdmFyaWFudD0icHJpbWFyeSIsIHNpemU9ImxnIikKICAgICAgICAgICAgICAgIHN0YXR1cyA9IGdyLlRleHRib3gobGFiZWw9IlN0YXR1dCIsIGludGVyYWN0aXZlPUZhbHNlLCBsaW5lcz0yKQogICAgICAgICAgICAgICAgdGlwcyA9IGdyLk1hcmtkb3duKAogICAgICAgICAgICAgICAgICAgICLwn5KhICoqQ29uc2VpbHMqKiA6IGJvbiDDqWNsYWlyYWdlLCBjaGV2YXVjaGVtZW50IH42MOKAkzcwICUgZW50cmUgdnVlcywgIgogICAgICAgICAgICAgICAgICAgICLDqXZpdGUgbGVzIG11cnMgdW5pcyAvIGZsb3UuIFBvdXIgdW5lIHBpw6hjZSA6IDjigJMxMiBmcmFtZXMgc3VmZmlzZW50IHN1ciBDb2xhYiBmcmVlLiIKICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgIHdpdGggZ3IuQ29sdW1uKHNjYWxlPTUpOgogICAgICAgICAgICAgICAgbW9kZWwzZCA9IGdyLk1vZGVsM0QoCiAgICAgICAgICAgICAgICAgICAgbGFiZWw9IkFwZXLDp3UgM0QiLAogICAgICAgICAgICAgICAgICAgIGNsZWFyX2NvbG9yPVswLjAzLCAwLjA0LCAwLjA3LCAxLjBdLAogICAgICAgICAgICAgICAgICAgIGhlaWdodD00MjAsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBwbHlfZmlsZSA9IGdyLkZpbGUobGFiZWw9IlTDqWzDqWNoYXJnZXIgKC5wbHkgb3UgLmdsYikiKQogICAgICAgICAgICAgICAgbm90ZSA9IGdyLk1hcmtkb3duKCIiKQoKICAgICAgICBidG4uY2xpY2soCiAgICAgICAgICAgIGZuPXJ1bl9zY2FuLAogICAgICAgICAgICBpbnB1dHM9W2ltYWdlcywgdmlkZW8sIG5fZnJhbWVzLCBzY2VuZV9ncmFwaF0sCiAgICAgICAgICAgIG91dHB1dHM9W21vZGVsM2QsIHBseV9maWxlLCBzdGF0dXMsIG5vdGVdLAogICAgICAgICkKCiAgICAgICAgZ3IuTWFya2Rvd24oCiAgICAgICAgICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOmNlbnRlcjtjb2xvcjojNjQ3NDhiO2ZvbnQtc2l6ZTowLjg1cmVtO21hcmdpbi10b3A6MXJlbSc+IgogICAgICAgICAgICAiTW9kw6hsZSA6IE1BU3QzUiAoTmF2ZXIgTGFicykgwrcgbGljZW5jZSBub24tY29tbWVyY2lhbGUgQ0MgQlktTkMtU0EgwrcgIgogICAgICAgICAgICAic2Vzc2lvbiBDb2xhYiArIENsb3VkZmxhcmUgVHVubmVsPC9kaXY+IgogICAgICAgICkKICAgIHJldHVybiBkZW1vCgoKZGVmIG1haW4oKToKICAgIHNlcnZlcl9uYW1lID0gb3MuZW52aXJvbi5nZXQoIkdSQURJT19TRVJWRVJfTkFNRSIsICIwLjAuMC4wIikKICAgIHNlcnZlcl9wb3J0ID0gaW50KG9zLmVudmlyb24uZ2V0KCJHUkFESU9fU0VSVkVSX1BPUlQiLCAiNzg2MCIpKQogICAgIyBzaGFyZT1UcnVlIHBhciBkw6lmYXV0IHN1ciBDb2xhYiAobGllbiBwdWJsaWMgZmlhYmxlKS4gRMOpc2FjdGl2ZXI6IEdSQURJT19TSEFSRT0wCiAgICBzaGFyZSA9IG9zLmVudmlyb24uZ2V0KCJHUkFESU9fU0hBUkUiLCAiMSIpIG5vdCBpbiAoIjAiLCAiZmFsc2UiLCAiRmFsc2UiKQogICAgcHJpbnQoZiJEw6ltYXJyYWdlIEdyYWRpbyBzdXIge3NlcnZlcl9uYW1lfTp7c2VydmVyX3BvcnR9IHNoYXJlPXtzaGFyZX3igKYiLCBmbHVzaD1UcnVlKQogICAgcHJpbnQoIk1BU3QzUiBzZSBjaGFyZ2UgYXUgMWVyIHNjYW4sIHBhcyBhdSBib290LiIsIGZsdXNoPVRydWUpCiAgICBkZW1vID0gYnVpbGRfdWkoKQogICAgIyBHcmFkaW8gNCAmIDUgOiBxdWV1ZSBBUEkgbMOpZ8OocmVtZW50IGRpZmbDqXJlbnRlCiAgICB0cnk6CiAgICAgICAgYXBwID0gZGVtby5xdWV1ZShkZWZhdWx0X2NvbmN1cnJlbmN5X2xpbWl0PTEpCiAgICBleGNlcHQgVHlwZUVycm9yOgogICAgICAgIGFwcCA9IGRlbW8ucXVldWUoKQogICAgYXBwLmxhdW5jaCgKICAgICAgICBzZXJ2ZXJfbmFtZT1zZXJ2ZXJfbmFtZSwKICAgICAgICBzZXJ2ZXJfcG9ydD1zZXJ2ZXJfcG9ydCwKICAgICAgICBzaGFyZT1zaGFyZSwKICAgICAgICBzaG93X2Vycm9yPVRydWUsCiAgICAgICAgcXVpZXQ9RmFsc2UsCiAgICAgICAgaW5icm93c2VyPUZhbHNlLAogICAgKQoKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBtYWluKCkK\"\"\"\n",
    "SETUP_B64 = \"\"\"IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiIKSW5zdGFsbGUgTUFTdDNSICsgbGFuY2UgU2NhbjNEIHN1ciBHb29nbGUgQ29sYWIuCk1vZGUgZmlhYmxlIDogR3JhZGlvIHNoYXJlPVRydWUgKGxpZW4gKi5ncmFkaW8ubGl2ZSkg4oCUIHBsdXMgc3RhYmxlIHF1ZSBzdWJwcm9jZXNzICsgY2xvdWRmbGFyZWQuCiIiIgpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgppbXBvcnQgb3MKaW1wb3J0IHJlCmltcG9ydCBzaHV0aWwKaW1wb3J0IHN1YnByb2Nlc3MKaW1wb3J0IHN5cwppbXBvcnQgdGhyZWFkaW5nCmltcG9ydCB0aW1lCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAoKSE9NRSA9IFBhdGgoIi9jb250ZW50IikgaWYgUGF0aCgiL2NvbnRlbnQiKS5leGlzdHMoKSBlbHNlIFBhdGguY3dkKCkKUk9PVCA9IEhPTUUgLyAic2NhbjNkIgpNQVNUM1IgPSBST09UIC8gIm1hc3QzciIKUE9SVCA9IGludChvcy5lbnZpcm9uLmdldCgiR1JBRElPX1NFUlZFUl9QT1JUIiwgIjc4NjAiKSkKCgpkZWYgc2goY21kOiBzdHIgfCBsaXN0W3N0cl0sIGN3ZDogUGF0aCB8IE5vbmUgPSBOb25lLCBjaGVjazogYm9vbCA9IFRydWUpOgogICAgcHJpbnQoZiJcbiQge2NtZCBpZiBpc2luc3RhbmNlKGNtZCwgc3RyKSBlbHNlICcgJy5qb2luKGNtZCl9IiwgZmx1c2g9VHJ1ZSkKICAgIGlmIGlzaW5zdGFuY2UoY21kLCBzdHIpOgogICAgICAgIHJldHVybiBzdWJwcm9jZXNzLnJ1bihjbWQsIHNoZWxsPVRydWUsIGN3ZD1zdHIoY3dkKSBpZiBjd2QgZWxzZSBOb25lLCBjaGVjaz1jaGVjaykKICAgIHJldHVybiBzdWJwcm9jZXNzLnJ1bihjbWQsIGN3ZD1zdHIoY3dkKSBpZiBjd2QgZWxzZSBOb25lLCBjaGVjaz1jaGVjaykKCgpkZWYgZW5zdXJlX2dwdSgpOgogICAgdHJ5OgogICAgICAgIGltcG9ydCB0b3JjaAoKICAgICAgICBvayA9IHRvcmNoLmN1ZGEuaXNfYXZhaWxhYmxlKCkKICAgICAgICBuYW1lID0gdG9yY2guY3VkYS5nZXRfZGV2aWNlX25hbWUoMCkgaWYgb2sgZWxzZSAiQ1BVIgogICAgICAgIHByaW50KGYiUHlUb3JjaCB7dG9yY2guX192ZXJzaW9uX199IMK3IGRldmljZSA9IHtuYW1lfSIsIGZsdXNoPVRydWUpCiAgICAgICAgaWYgbm90IG9rOgogICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgICLimqDvuI8gIFBhcyBkZSBHUFUuIFJ1bnRpbWUg4oaSIE1vZGlmaWVyIGxlIHR5cGUgZCdleMOpY3V0aW9uIOKGkiBUNCBHUFUuIiwKICAgICAgICAgICAgICAgIGZsdXNoPVRydWUsCiAgICAgICAgICAgICkKICAgICAgICByZXR1cm4gb2sKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICBwcmludChmInRvcmNoIGNoZWNrOiB7ZX0iLCBmbHVzaD1UcnVlKQogICAgICAgIHJldHVybiBGYWxzZQoKCmRlZiBfcGlwKGFyZ3M6IHN0ciwgY2hlY2s6IGJvb2wgPSBGYWxzZSk6CiAgICAiIiJwaXAgaW5zdGFsbCBlbiBtb2RlIHNpbGVuY2lldXggOyBsZXMgJ0VSUk9SOiBkZXBlbmRlbmN5IHJlc29sdmVyJyBzb250IGRlcyB3YXJuaW5ncy4iIiIKICAgIGVudiA9IG9zLmVudmlyb24uY29weSgpCiAgICBlbnZbIlBJUF9ESVNBQkxFX1BJUF9WRVJTSU9OX0NIRUNLIl0gPSAiMSIKICAgIGVudlsiUElQX1JPT1RfVVNFUl9BQ1RJT04iXSA9ICJpZ25vcmUiCiAgICBjbWQgPSBmIntzeXMuZXhlY3V0YWJsZX0gLW0gcGlwIGluc3RhbGwgLXEge2FyZ3N9IgogICAgcHJpbnQoZiJcbiQge2NtZH0iLCBmbHVzaD1UcnVlKQogICAgIyBPbiBmaWx0cmUgbGUgYnJ1aXQgZHUgcmVzb2x2ZXIgcG91ciBuZSBwYXMgYWZmb2xlcgogICAgciA9IHN1YnByb2Nlc3MucnVuKAogICAgICAgIGNtZCwKICAgICAgICBzaGVsbD1UcnVlLAogICAgICAgIGN3ZD1zdHIoUk9PVCksCiAgICAgICAgZW52PWVudiwKICAgICAgICBjYXB0dXJlX291dHB1dD1UcnVlLAogICAgICAgIHRleHQ9VHJ1ZSwKICAgICkKICAgIG91dCA9IChyLnN0ZG91dCBvciAiIikgKyAoci5zdGRlcnIgb3IgIiIpCiAgICAjIE4nYWZmaWNoZSBxdWUgbGVzIHZyYWllcyBlcnJldXJzIChwYXMgbGUgcmFwcG9ydCBkZSBjb25mbGl0cykKICAgIGZvciBsaW5lIGluIG91dC5zcGxpdGxpbmVzKCk6CiAgICAgICAgbG93ID0gbGluZS5sb3dlcigpCiAgICAgICAgaWYgImRlcGVuZGVuY3kgY29uZmxpY3RzIiBpbiBsb3cgb3IgImRlcGVuZGVuY3kgcmVzb2x2ZXIiIGluIGxvdzoKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiBsaW5lLnN0cmlwKCkuc3RhcnRzd2l0aCgiRVJST1I6IikgYW5kICJkZXBlbmRlbmN5IHJlc29sdmVyIiBpbiBsb3c6CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgaWYgInJlcXVpcmVzIiBpbiBsb3cgYW5kICJ3aGljaCBpcyBpbmNvbXBhdGlibGUiIGluIGxvdzoKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiAic3VjY2Vzc2Z1bGx5IGluc3RhbGxlZCIgaW4gbG93IG9yICJhbHJlYWR5IHNhdGlzZmllZCIgaW4gbG93OgogICAgICAgICAgICBjb250aW51ZQogICAgICAgICMgZ2FyZGVyIHRyYWNlYmFjayAvIEVSUk9SIHLDqWVscwogICAgICAgIGlmIGFueSgKICAgICAgICAgICAgayBpbiBsaW5lCiAgICAgICAgICAgIGZvciBrIGluICgKICAgICAgICAgICAgICAgICJFUlJPUiIsCiAgICAgICAgICAgICAgICAiZXJyb3I6IiwKICAgICAgICAgICAgICAgICJUcmFjZWJhY2siLAogICAgICAgICAgICAgICAgIkZhaWxlZCIsCiAgICAgICAgICAgICAgICAiTm8gbWF0Y2hpbmciLAogICAgICAgICAgICAgICAgIkNvdWxkIG5vdCIsCiAgICAgICAgICAgICkKICAgICAgICApOgogICAgICAgICAgICBpZiAiZGVwZW5kZW5jeSByZXNvbHZlciIgbm90IGluIGxvdyBhbmQgImluY29tcGF0aWJsZSIgbm90IGluIGxvdzoKICAgICAgICAgICAgICAgIHByaW50KGxpbmUsIGZsdXNoPVRydWUpCiAgICBpZiByLnJldHVybmNvZGUgIT0gMCBhbmQgY2hlY2s6CiAgICAgICAgcHJpbnQob3V0Wy0yMDAwOl0sIGZsdXNoPVRydWUpCiAgICAgICAgcmFpc2Ugc3VicHJvY2Vzcy5DYWxsZWRQcm9jZXNzRXJyb3Ioci5yZXR1cm5jb2RlLCBjbWQpCiAgICByZXR1cm4gcgoKCmRlZiBpbnN0YWxsX2RlcHMoKToKICAgIFJPT1QubWtkaXIocGFyZW50cz1UcnVlLCBleGlzdF9vaz1UcnVlKQogICAgb3MuY2hkaXIoUk9PVCkKCiAgICBzaCgiYXB0LWdldCB1cGRhdGUgLXFxICYmIGFwdC1nZXQgaW5zdGFsbCAteSAtcXEgZmZtcGVnIGxpYmdsMSA+IC9kZXYvbnVsbCIsIGNoZWNrPUZhbHNlKQoKICAgIHByaW50KAogICAgICAgICJJbnN0YWxsIFNjYW4zROKAplxuIgogICAgICAgICIgIChsZXMgbWVzc2FnZXMgcGlwICdkZXBlbmRlbmN5IGNvbmZsaWN0cycgc29udCBNQVNRVcOJUyDigJQgY2UgbmUgc29udCBwYXMgZGVzIGNyYXNocykiLAogICAgICAgIGZsdXNoPVRydWUsCiAgICApCgogICAgIyAxKSBiYXNlIENvbGFiLXNhZmUgKHBhcyBkZSBHcmFkaW8gZW5jb3JlKQogICAgX3BpcCgnInNldHVwdG9vbHM9PTgxLjAuMCIgd2hlZWwgImplZGk9PTAuMTkuMiInKQogICAgX3BpcCgKICAgICAgICAib3BlbmN2LXB5dGhvbi1oZWFkbGVzcyB0cmltZXNoIGVpbm9wcyByb21hIHRxZG0gIgogICAgICAgICJtYXRwbG90bGliIHNjaWtpdC1sZWFybiBzY2lweSBwaWxsb3ciCiAgICApCgogICAgIyAyKSBjbG9uZSBtYXN0M3IKICAgIGlmIG5vdCBNQVNUM1IuZXhpc3RzKCk6CiAgICAgICAgc2goCiAgICAgICAgICAgICJnaXQgY2xvbmUgLS1yZWN1cnNpdmUgLS1kZXB0aCAxIGh0dHBzOi8vZ2l0aHViLmNvbS9uYXZlci9tYXN0M3IuZ2l0IiwKICAgICAgICAgICAgY3dkPVJPT1QsCiAgICAgICAgKQogICAgZWxzZToKICAgICAgICBwcmludCgibWFzdDNyIGTDqWrDoCBjbG9uw6kiLCBmbHVzaD1UcnVlKQogICAgICAgIHNoKCJnaXQgc3VibW9kdWxlIHVwZGF0ZSAtLWluaXQgLS1yZWN1cnNpdmUiLCBjd2Q9TUFTVDNSLCBjaGVjaz1GYWxzZSkKCiAgICAjIDMpIGRlcHMgbWFzdDNyL2R1c3QzciAocGV1dmVudCByZWRlc2NlbmRyZSB3ZWJzb2NrZXRzIOKGkiBvbiBjb3JyaWdlIGFwcsOocykKICAgIGZvciByIGluIChNQVNUM1IgLyAicmVxdWlyZW1lbnRzLnR4dCIsIE1BU1QzUiAvICJkdXN0M3IiIC8gInJlcXVpcmVtZW50cy50eHQiKToKICAgICAgICBpZiByLmV4aXN0cygpOgogICAgICAgICAgICBfcGlwKGYiLXIge3J9IC0tdXBncmFkZS1zdHJhdGVneSBvbmx5LWlmLW5lZWRlZCIpCgogICAgX3BpcCgidG9yY2h2aXNpb24gLS11cGdyYWRlLXN0cmF0ZWd5IG9ubHktaWYtbmVlZGVkIikKCiAgICBjdXJvcGUgPSBNQVNUM1IgLyAiZHVzdDNyIiAvICJjcm9jbyIgLyAibW9kZWxzIiAvICJjdXJvcGUiCiAgICBpZiBjdXJvcGUuZXhpc3RzKCk6CiAgICAgICAgc2goZiJ7c3lzLmV4ZWN1dGFibGV9IHNldHVwLnB5IGJ1aWxkX2V4dCAtLWlucGxhY2UiLCBjd2Q9Y3Vyb3BlLCBjaGVjaz1GYWxzZSkKCiAgICAjIDQpIEdyYWRpbyA1IEVOIERFUk5JRVIg4oCUIGNvbXBhdGlibGUgd2Vic29ja2V0cyAxNSArIMOpY29zeXN0w6htZSBDb2xhYiAyMDI2CiAgICAjICAgIChHcmFkaW8gNCBmb3LDp2FpdCB3ZWJzb2NrZXRzPDEzIOKGkiBjb25mbGl0cyB5ZmluYW5jZS9nb29nbGUtZ2VuYWkvbGFuZ3NtaXRoKQogICAgcHJpbnQoIkluc3RhbGwgR3JhZGlvIDUgKyB3ZWJzb2NrZXRzIDE1IChhbGlnbmVtZW50IENvbGFiKeKApiIsIGZsdXNoPVRydWUpCiAgICBfcGlwKCciZ3JhZGlvPj01LjAsPDYiJykKICAgIF9waXAoJyJzZXR1cHRvb2xzPT04MS4wLjAiICJ3ZWJzb2NrZXRzPT0xNS4wLjEiICJqZWRpPT0wLjE5LjIiJykKCiAgICAjIFNhbml0eSBjaGVjawogICAgc2goCiAgICAgICAgZiJ7c3lzLmV4ZWN1dGFibGV9IC1jIFwiIgogICAgICAgICJpbXBvcnQgZ3JhZGlvLCB0b3JjaCwgd2Vic29ja2V0cywgc2V0dXB0b29sczsgIgogICAgICAgICJwcmludCgnT0sgZ3JhZGlvJywgZ3JhZGlvLl9fdmVyc2lvbl9fKTsgIgogICAgICAgICJwcmludCgnT0sgdG9yY2gnLCB0b3JjaC5fX3ZlcnNpb25fXywgJ2N1ZGEnLCB0b3JjaC5jdWRhLmlzX2F2YWlsYWJsZSgpKTsgIgogICAgICAgICJwcmludCgnT0sgd2Vic29ja2V0cycsIHdlYnNvY2tldHMuX192ZXJzaW9uX18pOyAiCiAgICAgICAgInByaW50KCdPSyBzZXR1cHRvb2xzJywgc2V0dXB0b29scy5fX3ZlcnNpb25fXykiCiAgICAgICAgIlwiIiwKICAgICAgICBjaGVjaz1GYWxzZSwKICAgICkKICAgIHByaW50KCLinIUgRMOpcGVuZGFuY2VzIGluc3RhbGzDqWVzIOKAlCBzdWl0ZSA6IHNtb2tlIHRlc3QgKyBsaWVuIHB1YmxpYyBHcmFkaW8uIiwgZmx1c2g9VHJ1ZSkKCgpkZWYgZW5zdXJlX2FwcF9weSgpOgogICAgdGFyZ2V0ID0gUk9PVCAvICJhcHAucHkiCiAgICBpZiB0YXJnZXQuZXhpc3RzKCkgYW5kIHRhcmdldC5zdGF0KCkuc3Rfc2l6ZSA+IDEwMDA6CiAgICAgICAgcHJpbnQoZiJhcHAucHkgT0sgKHt0YXJnZXQuc3RhdCgpLnN0X3NpemV9IGJ5dGVzKSIsIGZsdXNoPVRydWUpCiAgICAgICAgcmV0dXJuCiAgICBjYW5kaWRhdGVzID0gWwogICAgICAgIFBhdGgoX19maWxlX18pLnJlc29sdmUoKS5wYXJlbnQucGFyZW50IC8gImFwcC5weSIsCiAgICAgICAgUGF0aCgiL2NvbnRlbnQvYXBwLnB5IiksCiAgICBdCiAgICBmb3IgYyBpbiBjYW5kaWRhdGVzOgogICAgICAgIGlmIGMuZXhpc3RzKCk6CiAgICAgICAgICAgIHNodXRpbC5jb3B5MihjLCB0YXJnZXQpCiAgICAgICAgICAgIHByaW50KGYiYXBwLnB5IGNvcGnDqSBkZXB1aXMge2N9IiwgZmx1c2g9VHJ1ZSkKICAgICAgICAgICAgcmV0dXJuCiAgICByYWlzZSBGaWxlTm90Rm91bmRFcnJvcigiYXBwLnB5IG1hbnF1YW50IGRhbnMgL2NvbnRlbnQvc2NhbjNkLyIpCgoKZGVmIHNtb2tlX3Rlc3RfaW1wb3J0KCk6CiAgICAiIiJWw6lyaWZpZSBxdWUgR3JhZGlvICsgYXBwIGltcG9ydGVudCBhdmFudCBsZSBsYXVuY2ggKGVycmV1cnMgdmlzaWJsZXMpLiIiIgogICAgcHJpbnQoIlxuLS0tIHNtb2tlIHRlc3QgaW1wb3J0IC0tLSIsIGZsdXNoPVRydWUpCiAgICBlbnYgPSBvcy5lbnZpcm9uLmNvcHkoKQogICAgZW52WyJNQVNUM1JfUk9PVCJdID0gc3RyKE1BU1QzUikKICAgIGVudlsiUFlUSE9OUEFUSCJdID0gc3RyKFJPT1QpICsgb3MucGF0aHNlcCArIGVudi5nZXQoIlBZVEhPTlBBVEgiLCAiIikKICAgIGNvZGUgPSByIiIiCmltcG9ydCBzeXMKc3lzLnBhdGguaW5zZXJ0KDAsIHIiJXMiKQppbXBvcnQgZ3JhZGlvIGFzIGdyCnByaW50KCJncmFkaW8iLCBnci5fX3ZlcnNpb25fXykKaW1wb3J0IGFwcApwcmludCgiYXBwLmJ1aWxkX3VpIiwgY2FsbGFibGUoYXBwLmJ1aWxkX3VpKSkKZGVtbyA9IGFwcC5idWlsZF91aSgpCnByaW50KCJVSSBjb25zdHJ1aXRlIE9LIikKIiIiICUgc3RyKFJPT1QpLnJlcGxhY2UoIlxcIiwgIlxcXFwiKQogICAgciA9IHN1YnByb2Nlc3MucnVuKAogICAgICAgIFtzeXMuZXhlY3V0YWJsZSwgIi1jIiwgY29kZV0sCiAgICAgICAgY3dkPXN0cihST09UKSwKICAgICAgICBlbnY9ZW52LAogICAgICAgIGNhcHR1cmVfb3V0cHV0PVRydWUsCiAgICAgICAgdGV4dD1UcnVlLAogICAgKQogICAgcHJpbnQoci5zdGRvdXQsIGZsdXNoPVRydWUpCiAgICBpZiByLnJldHVybmNvZGUgIT0gMDoKICAgICAgICBwcmludChyLnN0ZGVyciwgZmx1c2g9VHJ1ZSkKICAgICAgICByYWlzZSBTeXN0ZW1FeGl0KAogICAgICAgICAgICAiw4ljaGVjIGltcG9ydCBhcHAvR3JhZGlvIOKAlCBjb3JyaWdlIGwnZXJyZXVyIGNpLWRlc3N1cyBhdmFudCBkZSByZWxhbmNlci4iCiAgICAgICAgKQogICAgcHJpbnQoIi0tLSBzbW9rZSB0ZXN0IE9LIC0tLVxuIiwgZmx1c2g9VHJ1ZSkKCgpkZWYgc3RhcnRfY2xvdWRmbGFyZWRfYmFja2dyb3VuZChwb3J0OiBpbnQpIC0+IE5vbmU6CiAgICAiIiJPcHRpb25uZWwgOiB0dW5uZWwgQ0YgZW4gcGx1cyBkdSBzaGFyZSBHcmFkaW8uIiIiCiAgICB0cnk6CiAgICAgICAgbG9jYWwgPSBST09UIC8gImNsb3VkZmxhcmVkIgogICAgICAgIGlmIG5vdCBsb2NhbC5leGlzdHMoKToKICAgICAgICAgICAgdXJsID0gImh0dHBzOi8vZ2l0aHViLmNvbS9jbG91ZGZsYXJlL2Nsb3VkZmxhcmVkL3JlbGVhc2VzL2xhdGVzdC9kb3dubG9hZC9jbG91ZGZsYXJlZC1saW51eC1hbWQ2NCIKICAgICAgICAgICAgc2goZiJ3Z2V0IC1xIC1PIHtsb2NhbH0ge3VybH0gJiYgY2htb2QgK3gge2xvY2FsfSIsIGNoZWNrPUZhbHNlKQogICAgICAgIGlmIG5vdCBsb2NhbC5leGlzdHMoKToKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgbG9nX3BhdGggPSBST09UIC8gImNsb3VkZmxhcmVkLmxvZyIKICAgICAgICBsb2cgPSBvcGVuKGxvZ19wYXRoLCAidyIsIGVuY29kaW5nPSJ1dGYtOCIpCiAgICAgICAgc3VicHJvY2Vzcy5Qb3BlbigKICAgICAgICAgICAgW3N0cihsb2NhbCksICJ0dW5uZWwiLCAiLS11cmwiLCBmImh0dHA6Ly8xMjcuMC4wLjE6e3BvcnR9IiwgIi0tbm8tYXV0b3VwZGF0ZSJdLAogICAgICAgICAgICBzdGRvdXQ9bG9nLAogICAgICAgICAgICBzdGRlcnI9c3VicHJvY2Vzcy5TVERPVVQsCiAgICAgICAgKQoKICAgICAgICBkZWYgd2F0Y2goKToKICAgICAgICAgICAgcGF0ID0gcmUuY29tcGlsZShyImh0dHBzOi8vW2EtekEtWjAtOS1dK1wudHJ5Y2xvdWRmbGFyZVwuY29tIikKICAgICAgICAgICAgZm9yIF8gaW4gcmFuZ2UoNjApOgogICAgICAgICAgICAgICAgdGltZS5zbGVlcCgxKQogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIHRleHQgPSBsb2dfcGF0aC5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IiwgZXJyb3JzPSJpZ25vcmUiKQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICAgICAgZm91bmQgPSBwYXQuZmluZGFsbCh0ZXh0KQogICAgICAgICAgICAgICAgaWYgZm91bmQ6CiAgICAgICAgICAgICAgICAgICAgdXJsID0gZm91bmRbLTFdCiAgICAgICAgICAgICAgICAgICAgcHJpbnQoZiJcbvCfjI0gQ2xvdWRmbGFyZSAob3B0aW9ubmVsKSA6IHt1cmx9XG4iLCBmbHVzaD1UcnVlKQogICAgICAgICAgICAgICAgICAgIChST09UIC8gInB1YmxpY191cmxfY2YudHh0Iikud3JpdGVfdGV4dCh1cmwsIGVuY29kaW5nPSJ1dGYtOCIpCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuCgogICAgICAgIHRocmVhZGluZy5UaHJlYWQodGFyZ2V0PXdhdGNoLCBkYWVtb249VHJ1ZSkuc3RhcnQoKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHByaW50KGYiY2xvdWRmbGFyZWQgb3B0aW9ubmVsIGlnbm9yw6k6IHtlfSIsIGZsdXNoPVRydWUpCgoKZGVmIGxhdW5jaF9ncmFkaW9faW5wcm9jZXNzKHBvcnQ6IGludCk6CiAgICAiIiIKICAgIExhbmNlIEdyYWRpbyBkYW5zIENFIHByb2Nlc3MgKGxlcyBlcnJldXJzIHMnYWZmaWNoZW50IGRhbnMgQ29sYWIpLgogICAgc2hhcmU9VHJ1ZSDihpIgbGllbiBwdWJsaWMgaHR0cHM6Ly94eHh4LmdyYWRpby5saXZlIChmaWFibGUgc3VyIENvbGFiKS4KICAgICIiIgogICAgb3MuZW52aXJvblsiTUFTVDNSX1JPT1QiXSA9IHN0cihNQVNUM1IpCiAgICBvcy5lbnZpcm9uWyJTQ0FOM0RfV09SSyJdID0gc3RyKFJPT1QgLyAid29yayIpCiAgICBvcy5lbnZpcm9uWyJHUkFESU9fQU5BTFlUSUNTX0VOQUJMRUQiXSA9ICJGYWxzZSIKICAgIG9zLmVudmlyb25bIkhGX0hVQl9ESVNBQkxFX1RFTEVNRVRSWSJdID0gIjEiCiAgICBvcy5lbnZpcm9uLnNldGRlZmF1bHQoIlNDQU4zRF9NQVhfRlJBTUVTIiwgIjEwIikKICAgIG9zLmVudmlyb24uc2V0ZGVmYXVsdCgiTUFTVDNSX0lNQUdFX1NJWkUiLCAiNTEyIikKICAgIG9zLmVudmlyb25bIkdSQURJT19TRVJWRVJfUE9SVCJdID0gc3RyKHBvcnQpCgogICAgIyByZWNoYXJnZXIgYXBwIHNpIGTDqWrDoCBpbXBvcnTDqQogICAgZm9yIG1vZCBpbiBsaXN0KHN5cy5tb2R1bGVzKToKICAgICAgICBpZiBtb2QgPT0gImFwcCIgb3IgbW9kLnN0YXJ0c3dpdGgoImFwcC4iKToKICAgICAgICAgICAgZGVsIHN5cy5tb2R1bGVzW21vZF0KCiAgICBpZiBzdHIoUk9PVCkgbm90IGluIHN5cy5wYXRoOgogICAgICAgIHN5cy5wYXRoLmluc2VydCgwLCBzdHIoUk9PVCkpCiAgICBpZiBzdHIoTUFTVDNSKSBub3QgaW4gc3lzLnBhdGg6CiAgICAgICAgc3lzLnBhdGguaW5zZXJ0KDAsIHN0cihNQVNUM1IpKQogICAgaWYgc3RyKE1BU1QzUiAvICJkdXN0M3IiKSBub3QgaW4gc3lzLnBhdGg6CiAgICAgICAgc3lzLnBhdGguaW5zZXJ0KDAsIHN0cihNQVNUM1IgLyAiZHVzdDNyIikpCgogICAgb3MuY2hkaXIoUk9PVCkKCiAgICBpbXBvcnQgYXBwCgogICAgcHJpbnQoIkNvbnN0cnVjdGlvbiBkZSBsJ1VJ4oCmIiwgZmx1c2g9VHJ1ZSkKICAgIGRlbW8gPSBhcHAuYnVpbGRfdWkoKQoKICAgIHN0YXJ0X2Nsb3VkZmxhcmVkX2JhY2tncm91bmQocG9ydCkKCiAgICBwcmludCgiXG4iICsgIj0iICogNjAsIGZsdXNoPVRydWUpCiAgICBwcmludCgiICBMYW5jZW1lbnQgR3JhZGlvIChzaGFyZT1UcnVlKeKApiIsIGZsdXNoPVRydWUpCiAgICBwcmludCgiICBVbiBsaWVuIGh0dHBzOi8v4oCmLmdyYWRpby5saXZlIHZhIGFwcGFyYcOudHJlLiIsIGZsdXNoPVRydWUpCiAgICBwcmludCgiICBMZSBtb2TDqGxlIE1BU3QzUiBzZSBjaGFyZ2UgYXUgUFJFTUlFUiBzY2FuIChwYXMgYXUgYm9vdCkuIiwgZmx1c2g9VHJ1ZSkKICAgIHByaW50KCI9IiAqIDYwICsgIlxuIiwgZmx1c2g9VHJ1ZSkKCiAgICB0cnk6CiAgICAgICAgcSA9IGRlbW8ucXVldWUoZGVmYXVsdF9jb25jdXJyZW5jeV9saW1pdD0xKQogICAgZXhjZXB0IFR5cGVFcnJvcjoKICAgICAgICBxID0gZGVtby5xdWV1ZSgpCiAgICBxLmxhdW5jaCgKICAgICAgICBzZXJ2ZXJfbmFtZT0iMC4wLjAuMCIsCiAgICAgICAgc2VydmVyX3BvcnQ9cG9ydCwKICAgICAgICBzaGFyZT1UcnVlLAogICAgICAgIHNob3dfZXJyb3I9VHJ1ZSwKICAgICAgICBpbmJyb3dzZXI9RmFsc2UsCiAgICAgICAgcXVpZXQ9RmFsc2UsCiAgICApCgoKZGVmIGZyZWVfcG9ydChwb3J0OiBpbnQpOgogICAgc2goZiJmdXNlciAtayB7cG9ydH0vdGNwIDI+L2Rldi9udWxsIHx8IHRydWUiLCBjaGVjaz1GYWxzZSkKICAgIHNoKAogICAgICAgIGYiYmFzaCAtbGMgJ3NzIC1scHRuIFwic3BvcnQgPSA6e3BvcnR9XCIgMj4vZGV2L251bGwgfCBhd2sgXCJOUj4xe3twcmludH19XCIgfHwgdHJ1ZSciLAogICAgICAgIGNoZWNrPUZhbHNlLAogICAgKQogICAgdGltZS5zbGVlcCgxKQoKCmRlZiBtYWluKCk6CiAgICBwcmludCgiPSIgKiA2MCkKICAgIHByaW50KCIgIFNjYW4zRCDCtyBNQVN0M1IgwrcgR29vZ2xlIENvbGFiIikKICAgIHByaW50KCI9IiAqIDYwKQoKICAgIGVuc3VyZV9ncHUoKQogICAgaW5zdGFsbF9kZXBzKCkKICAgIGVuc3VyZV9hcHBfcHkoKQogICAgZnJlZV9wb3J0KFBPUlQpCgogICAgdHJ5OgogICAgICAgIHNtb2tlX3Rlc3RfaW1wb3J0KCkKICAgIGV4Y2VwdCBTeXN0ZW1FeGl0OgogICAgICAgIHJhaXNlCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiJzbW9rZSB0ZXN0IGV4Y2VwdGlvbjoge2V9IiwgZmx1c2g9VHJ1ZSkKCiAgICBwb3J0ID0gUE9SVAogICAgdHJ5OgogICAgICAgIGxhdW5jaF9ncmFkaW9faW5wcm9jZXNzKHBvcnQpCiAgICBleGNlcHQgT1NFcnJvciBhcyBlOgogICAgICAgIGlmICJhZGRyZXNzIGFscmVhZHkgaW4gdXNlIiBpbiBzdHIoZSkubG93ZXIoKSBvciAiMTAwNDgiIGluIHN0cihlKToKICAgICAgICAgICAgcG9ydCA9IDc4NjEKICAgICAgICAgICAgcHJpbnQoZiJQb3J0IG9jY3Vww6ksIGVzc2FpIHtwb3J0feKApiIsIGZsdXNoPVRydWUpCiAgICAgICAgICAgIGZyZWVfcG9ydChwb3J0KQogICAgICAgICAgICBsYXVuY2hfZ3JhZGlvX2lucHJvY2Vzcyhwb3J0KQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJhaXNlCgoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIG1haW4oKQo=\"\"\"\n",
    "\n",
    "(ROOT / \"app.py\").write_bytes(base64.b64decode(APP_B64))\n",
    "(ROOT / \"setup_and_run.py\").write_bytes(base64.b64decode(SETUP_B64))\n",
    "\n",
    "print(\"OK app.py\", (ROOT / \"app.py\").stat().st_size, \"bytes\")\n",
    "print(\"OK setup_and_run.py\", (ROOT / \"setup_and_run.py\").stat().st_size, \"bytes\")\n",
    "print(\"Dossier:\", ROOT)\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Install MASt3R + Gradio + Cloudflare Tunnel (5–15 min la 1re fois)\n",
    "%cd /content/scan3d\n",
    "!python setup_and_run.py\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Affiche le lien public\n",
    "from pathlib import Path\n",
    "from IPython.display import Markdown, display\n",
    "p = Path('/content/scan3d/public_url.txt')\n",
    "if p.exists():\n",
    "    url = p.read_text().strip()\n",
    "    display(Markdown(f'## 🌍 Lien public\\n\\n### [{url}]({url})\\n\\nColle-le aussi sur https://scan3d.pages.dev/'))\n",
    "else:\n",
    "    print('Pas encore d URL — regarde la sortie de la cellule précédente')\n",
    "    log = Path('/content/scan3d/cloudflared.log')\n",
    "    if log.exists():\n",
    "        print(log.read_text()[-2500:])\n"
   ]
  }
 ]
}