<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lưu trữ Pokemon TCG - vnGAME</title>
	<atom:link href="https://vngame.tv/tag/pokemon-tcg/feed/" rel="self" type="application/rss+xml" />
	<link>https://vngame.tv/tag/pokemon-tcg/</link>
	<description>TẢI GAME MIỄN PHÍ</description>
	<lastBuildDate>Sat, 10 Oct 2026 13:08:13 +0000</lastBuildDate>
	<language>vi</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://vngame.tv/wp-content/uploads/2020/12/favion-2020-70x70.png</url>
	<title>Lưu trữ Pokemon TCG - vnGAME</title>
	<link>https://vngame.tv/tag/pokemon-tcg/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">128554084</site>	<item>
		<title>Mở Pack Pokémon TCG Miễn Phí – Pokémon TCG Pack Opening Simulator</title>
		<link>https://vngame.tv/trai-nghiem-mo-the-pokemon-tcg-mien-phi/</link>
					<comments>https://vngame.tv/trai-nghiem-mo-the-pokemon-tcg-mien-phi/#respond</comments>
		
		<dc:creator><![CDATA[Cá]]></dc:creator>
		<pubDate>Tue, 15 Sep 2026 21:42:16 +0000</pubDate>
				<category><![CDATA[Mở Pack TCG Miễn Phí]]></category>
		<category><![CDATA[Pokemon Tools]]></category>
		<category><![CDATA[Hướng dẫn Pokémon TCG]]></category>
		<category><![CDATA[Mở thẻ Pokémon TCG]]></category>
		<category><![CDATA[Mở thẻ Pokémon TCG miễn phí]]></category>
		<category><![CDATA[Pokemon TCG]]></category>
		<category><![CDATA[Pokémon TCG miễn phí]]></category>
		<category><![CDATA[Pokémon TCG Pocket]]></category>
		<category><![CDATA[vngame]]></category>
		<guid isPermaLink="false">https://vngame.tv/?p=19159</guid>

					<description><![CDATA[<p>Bạn muốn trải nghiệm cảm giác xé pack Pokémon TCG nhưng không muốn phải mua booster pack thật? Với Pokémon TCG Pack Opening Simulator, bạn có thể mở pack Pokémon online hoàn toàn miễn phí, xem những lá bài mình nhận được và thử vận may với nhiều lựa chọn pack khác nhau. Không cần mua thẻ thật, không cần chuẩn bị bộ sưu tập và có thể bắt đầu ngay trên trình duyệt. Hãy chọn expansion, chọn số lượng pack muốn mở và xem liệu bạn có thể pull được những lá bài hiếm hay không. 📚 Bộ sưu [&#8230;]</p>
<p>Bài viết <a href="https://vngame.tv/trai-nghiem-mo-the-pokemon-tcg-mien-phi/" data-wpel-link="internal">Mở Pack Pokémon TCG Miễn Phí – Pokémon TCG Pack Opening Simulator</a> đã xuất hiện đầu tiên vào ngày <a href="https://vngame.tv" data-wpel-link="internal">vnGAME</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bạn muốn trải nghiệm cảm giác <strong>xé pack Pokémon TCG</strong> nhưng không muốn phải mua booster pack thật? Với <strong>Pokémon TCG Pack Opening Simulator</strong>, bạn có thể mở pack Pokémon online hoàn toàn miễn phí, xem những lá bài mình nhận được và thử vận may với nhiều lựa chọn pack khác nhau.</p>



<p class="wp-block-paragraph">Không cần mua thẻ thật, không cần chuẩn bị bộ sưu tập và có thể bắt đầu ngay trên trình duyệt. Hãy chọn expansion, chọn số lượng pack muốn mở và xem liệu bạn có thể pull được những lá bài hiếm hay không.</p>



<!-- ============================================================
     PokeHub Pack Opening — bản nhúng (Tiếng Việt, v20)
     Dán TOÀN BỘ khối này vào block "Custom HTML" của WordPress.
     Không có thẻ <html>/<head>/<body> nên sẽ không phá layout trang.

     Mới trong v23:
       - TRAINER NICKNAME: hỏi nickname trước khi bắt đầu; tên được lưu riêng trên trình duyệt
         để dùng cho thành tích Global Leaderboard.
       - GLOBAL LEADERBOARD: nút BXH nằm ngay cạnh Bộ sưu tập; khi có Firebase Realtime Database
         config, bảng xếp hạng theo tổng số pack mở được cập nhật realtime giữa nhiều người.
         Nếu chưa cấu hình, widget không lỗi — BXH chỉ báo trạng thái chưa kết nối.

     Mới trong v13:
       - UI TIẾN ĐỘ GỌN: màn hình mở gói chỉ còn một thanh tiến độ nhỏ dạng
         "94/188 lá · 50%", tránh chiếm chỗ của kết quả rút. Chi tiết bộ sưu tập
         và danh sách lá còn thiếu được chuyển vào modal "Bộ sưu tập".
       - BỘ SƯU TẬP CHI TIẾT: hiển thị tiến độ các bộ đã sưu tầm, và với bộ đang
         mở có danh sách đầy đủ các lá còn thiếu để bấm xem chi tiết.
       - NHÃN MỚI / TRÙNG vẫn giữ nguyên ngay khi lật lá, không phát sinh request API mới.

      Mới trong v12:
     Mang từ v11:
       - TỈ LỆ RÚT THEO TẦNG ĐỘ HIẾM: trước đây mỗi ID thẻ trong một bộ
         có xác suất bị rút bằng nhau, khiến một lá Crown Rare dễ trúng
         ngang một lá Common. Giờ khi chọn bộ, widget tải danh sách toàn
         bộ chuỗi độ hiếm mà TCGdex từng dùng (endpoint /rarities, tải
         một lần), xếp chúng vào 7 tầng 0–6, rồi với riêng bộ đang chọn,
         lọc /cards?set.id=...&rarity=eq:A|B|C cho từng tầng (tối đa 7
         request, chạy song song, không chặn nút mở gói). Kết quả là mỗi
         tầng có sẵn danh sách ID thật thuộc bộ đó; lúc rút bài, hệ thống
         chọn tầng theo trọng số giảm dần ~3 lần mỗi bậc rồi mới chọn đều
         trong tầng đó — mô phỏng cảm giác "hiếm hơn thì khó trúng hơn"
         gần với thực tế hơn nhiều. Nếu API không lọc được cho bộ nào đó
         (ví dụ bộ quá cũ/lạ), widget tự phát hiện và âm thầm quay lại
         kiểu rút đều như bản cũ — không bao giờ làm hỏng việc mở gói.

     Mang từ v10:
       - LƯU BỘ SƯU TẬP + THỐNG KÊ ROI: mọi lá bài mở ra được lưu vào
         localStorage của chính máy người xem (không cần server). Nút
         "Bộ sưu tập" mở bảng thống kê: số gói đã mở, tổng chi, tổng giá
         trị thu được, ROI %, số lá độc nhất, phân bổ độ hiếm, danh sách
         lá bài sắp theo giá. Có thể đổi giá 1 gói (mặc định $4.49),
         xuất JSON, hoặc xóa sạch dữ liệu.
       - CHIA SẺ ẢNH LÊN MXH: nút "Chia sẻ ảnh" vẽ một ảnh 1200x630 bằng
         canvas (tiêu đề, 5 lá đẹp nhất, các số liệu) rồi gọi Web Share
         API trên điện thoại; máy tính sẽ tải ảnh PNG về để đăng lên Facebook/X/Instagram.
         Nếu CDN ảnh thẻ chặn CORS, ảnh vẫn được tạo nhưng thay hình thẻ bằng tên thẻ
         (không bao giờ hỏng trắng).
       - MỞ CẢ HỘP: hàng chip 1 / 3 / 5 / 10 gói. Chế độ hộp tải thẻ song song
         (6 request cùng lúc) kèm thanh tiến trình, rồi hiện bảng tổng kết hộp:
         chi phí, giá trị, lời/lỗ, ROI, phân bổ độ hiếm và 12 lá đáng giá nhất
         (bấm vào để xem chi tiết).
     Mới trong v20:
       - Footer nhận diện/bản quyền vnGame.tv xuất hiện ở cả cuối danh sách bộ và cuối khu vực Mở pack, dùng chung số bộ đếm động.
       - Footer dùng font hệ thống có fallback tiếng Việt ổn định.
     ============================================================ -->

<div id="pokehub-app">
  <div class="ph-flash" id="ph-flash"></div>
  <canvas class="ph-confetti" id="ph-confetti" aria-hidden="true"></canvas>
  <div class="ph-toast" id="ph-toast" role="status" aria-live="polite"></div>

  <div class="ph-wrap">
    <div class="ph-tools">
      <button class="ph-tool" id="ph-col-btn" type="button">
        <span aria-hidden="true"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> Bộ sưu tập
        <span class="ph-col-count" id="ph-col-count" hidden=""></span>
      </button>
      <button class="ph-tool ph-leaderboard-tool" id="ph-leaderboard-btn" type="button" title="Bảng xếp hạng realtime">
        <span aria-hidden="true"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> BXH
        <span class="ph-lb-status" id="ph-leaderboard-live">Chưa cấu hình</span>
      </button>
      <button class="ph-tool" id="ph-shop-btn" type="button">
        <span aria-hidden="true"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f381.png" alt="🎁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> Cửa hàng
        <span class="ph-col-count" id="ph-shop-points-badge" hidden=""></span>
      </button>
      <button class="ph-tool" id="ph-challenge-btn" type="button">
        <span aria-hidden="true"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3af.png" alt="🎯" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> Thử thách
        <span class="ph-col-count ph-challenge-count" id="ph-challenge-count" hidden=""></span>
      </button>
      <button class="ph-tool" id="ph-daily-btn" type="button" title="Daily Mission, Streak và Daily Bonus Pack">
        <span aria-hidden="true"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3af.png" alt="🎯" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> Daily
        <span class="ph-col-count" id="ph-daily-badge">0/3</span>
      </button>
      <button class="ph-tool ph-mute" id="ph-mute-btn" type="button" aria-label="Bật/tắt âm thanh"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50a.png" alt="🔊" class="wp-smiley" style="height: 1em; max-height: 1em;" /></button>
    </div>

    <h1 class="ph-title">vnGame.tv Mở Gói Bài<span class="ph-dot" aria-hidden="true"></span></h1>
    <p class="ph-tagline">Chọn một bộ thẻ, xé lớp foil, và xem hôm nay bạn may mắn đến đâu — dữ liệu thẻ và giá thị trường lấy trực tiếp từ TCGdex.</p>

    <!-- màn hình 1: chọn bộ thẻ -->
    <div class="ph-screen ph-active" id="ph-screen-pick">
      <div id="ph-status">Đang tải danh sách bộ thẻ…</div>
      <div class="ph-exp-grid" id="ph-exp-grid"></div>

      <!-- nhận diện / bản quyền vnGame.tv -->
      <div class="ph-brand-footer" aria-label="Thông tin vnGame.tv">
        <div class="ph-brand-footer-main">vnGame.tv <span aria-hidden="true">·</span> Nơi mỗi pack là một cuộc phiêu lưu — săn hit hiếm, hoàn thiện collection và ghi tên trên BXH!</div>
        <div class="ph-brand-footer-meta">
          <span class="ph-brand-set-count">Đang kiểm tra danh sách bộ…</span>
          <span aria-hidden="true">·</span>
          <span>© 2026 vnGame.tv · All rights reserved.</span>
        </div>
      </div>
    </div>

    <!-- màn hình 2: mở gói -->
    <div class="ph-screen" id="ph-screen-open">
      <button class="ph-back" id="ph-back-btn">← Chọn bộ thẻ khác</button>

      <div class="ph-mini-progress" id="ph-set-progress" hidden="" aria-live="polite">
        <div class="ph-mini-progress-top">
          <div class="ph-mini-progress-left">
            <span class="ph-mini-progress-kicker">SƯU TẬP</span>
            <span class="ph-mini-progress-title" id="ph-set-progress-title">—</span>
            <span class="ph-mini-progress-meta" id="ph-set-progress-meta">0/0 lá</span>
          </div>
          <span class="ph-mini-progress-pct" id="ph-set-progress-pct">0%</span>
        </div>
        <div class="ph-mini-progress-track" aria-hidden="true">
          <span class="ph-mini-progress-bar" id="ph-set-progress-bar"></span>
        </div>
      </div>

      <div class="ph-stage" id="ph-stage">
        <div class="ph-pack" id="ph-pack" role="button" tabindex="0" aria-label="Mở gói bài">
          <div class="ph-pack-label" id="ph-pack-label">Genetic Apex</div>
        </div>
        <div class="ph-hint" id="ph-hint">Chạm vào gói bài để mở</div>
        <div class="ph-box-row" id="ph-box-row" hidden="">
          <span class="ph-box-lbl">Mở nhiều gói một lần:</span>
          <button class="ph-chip" type="button" data-packs="1">1 gói</button>
          <button class="ph-chip" type="button" data-packs="3">3 gói</button>
          <button class="ph-chip" type="button" data-packs="5">5 gói</button>
          <button class="ph-chip ph-chip-hot" type="button" data-packs="10">Cả hộp · 10 gói</button>
        </div>
        <div class="ph-err" id="ph-open-err" hidden=""></div>
      </div>

      <div class="ph-progress-panel" id="ph-progress" hidden="">
        <div class="ph-progress-text" id="ph-progress-text">Đang mở hộp…</div>
        <div class="ph-progress-track"><div class="ph-progress-bar" id="ph-progress-bar"></div></div>
        <div class="ph-progress-sub">Đang lấy dữ liệu và giá của từng lá bài từ TCGdex…</div>
      </div>

      <div class="ph-reveal" id="ph-reveal">
        <div class="ph-pull-heading">Kết quả rút bài</div>
        <div class="ph-card-row" id="ph-card-row"></div>
        <div class="ph-banner" id="ph-banner"></div>
        <div class="ph-luck" id="ph-luck" hidden="">Điểm may mắn <b id="ph-luck-score">—</b>/100</div>
        <div class="ph-total" id="ph-total" hidden="">Tổng giá trị gói bài: <b id="ph-total-val">—</b></div>
        <div class="ph-quip" id="ph-quip"></div>
        <div class="ph-saved" id="ph-saved"></div>
        <div class="ph-actions">
          <button class="ph-btn ph-primary" id="ph-again-btn">Mở gói khác</button>
          <button class="ph-btn" id="ph-share-btn">Chia sẻ ảnh</button>
          <button class="ph-btn" id="ph-change-btn">Đổi bộ thẻ</button>
        </div>
      </div>

      <div class="ph-reveal" id="ph-box-result">
        <div id="ph-box-body"></div>
        <div class="ph-actions">
          <button class="ph-btn ph-primary" id="ph-box-again">Mở hộp nữa</button>
          <button class="ph-btn" id="ph-box-share">Chia sẻ ảnh</button>
          <button class="ph-btn" id="ph-box-change">Đổi bộ thẻ</button>
        </div>
      </div>

      <!-- nhận diện / bản quyền vnGame.tv ở cuối khu vực mở pack -->
      <div class="ph-brand-footer" aria-label="Thông tin vnGame.tv">
        <div class="ph-brand-footer-main">vnGame.tv <span aria-hidden="true">·</span> Nơi mỗi pack là một cuộc phiêu lưu — săn hit hiếm, hoàn thiện collection và ghi tên trên BXH!</div>
        <div class="ph-brand-footer-meta">
          <span class="ph-brand-set-count">Đang kiểm tra danh sách bộ…</span>
          <span aria-hidden="true">·</span>
          <span>© 2026 vnGame.tv · All rights reserved.</span>
        </div>
      </div>

    </div>
  </div>

  <!-- modal chi tiết lá bài -->
  <div class="ph-modal" id="ph-modal" hidden="">
    <div class="ph-modal-backdrop" id="ph-modal-backdrop"></div>
    <div class="ph-modal-card" role="dialog" aria-modal="true" aria-label="Chi tiết lá bài">
      <button class="ph-modal-close" id="ph-modal-close" type="button" aria-label="Đóng">✕</button>
      <div class="ph-modal-body" id="ph-modal-body"></div>
    </div>
  </div>

  <!-- modal bộ sưu tập + ROI -->
  <div class="ph-modal" id="ph-col-modal" hidden="">
    <div class="ph-modal-backdrop" id="ph-col-backdrop"></div>
    <div class="ph-modal-card ph-modal-wide" role="dialog" aria-modal="true" aria-label="Bộ sưu tập">
      <button class="ph-modal-close" id="ph-col-close" type="button" aria-label="Đóng">✕</button>
      <div class="ph-modal-title" style="margin-bottom:14px;">Bộ sưu tập &amp; ROI</div>
      <div class="ph-modal-body" id="ph-col-body"></div>
    </div>
  </div>

  <!-- modal thử thách -->
  <div class="ph-modal" id="ph-challenge-modal" hidden="">
    <div class="ph-modal-backdrop" id="ph-challenge-backdrop"></div>
    <div class="ph-modal-card ph-modal-wide" role="dialog" aria-modal="true" aria-label="Thử thách">
      <button class="ph-modal-close" id="ph-challenge-close" type="button" aria-label="Đóng">✕</button>
      <div class="ph-modal-title" style="margin-bottom:6px;">Thử thách</div>
      <div class="ph-col-note" style="margin-bottom:14px;">Hoàn thành nhiệm vụ bằng cách mở pack và sưu tầm card. Tiến độ lưu trên chính trình duyệt này.</div>
      <div class="ph-modal-body" id="ph-challenge-body"></div>
    </div>
  </div>

  <!-- modal cửa hàng đổi thưởng -->
  <div class="ph-modal" id="ph-shop-modal" hidden="">
    <div class="ph-modal-backdrop" id="ph-shop-backdrop"></div>
    <div class="ph-modal-card ph-modal-wide" role="dialog" aria-modal="true" aria-label="Cửa hàng đổi thưởng">
      <button class="ph-modal-close" id="ph-shop-close" type="button" aria-label="Đóng">✕</button>
      <div class="ph-modal-title" style="margin-bottom:6px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f381.png" alt="🎁" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Cửa hàng đổi thưởng</div>
      <div class="ph-col-note" style="margin-bottom:14px;">Dùng <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /> điểm thưởng để đổi vật phẩm. Điểm không mất khi nhiệm vụ 30 phút được làm mới.</div>
      <div class="ph-modal-body" id="ph-shop-body"></div>
    </div>
  </div>


  <!-- modal bảng xếp hạng realtime -->
  <div class="ph-modal" id="ph-leaderboard-modal" hidden="">
    <div class="ph-modal-backdrop" id="ph-leaderboard-backdrop"></div>
    <div class="ph-modal-card ph-modal-wide" role="dialog" aria-modal="true" aria-label="Bảng xếp hạng realtime">
      <button class="ph-modal-close" id="ph-leaderboard-close" type="button" aria-label="Đóng">✕</button>
      <div class="ph-modal-title" style="margin-bottom:6px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Bảng xếp hạng</div>
      <div class="ph-col-note" style="margin-bottom:14px;">Xếp hạng theo tổng giá trị bộ sưu tập. Gói mở chỉ là chỉ số phụ; dữ liệu cập nhật realtime giữa những người đang dùng widget.</div>
      <div class="ph-modal-body" id="ph-leaderboard-body"></div>
    </div>
  </div>

  <!-- modal nhập nickname -->
  <div class="ph-modal ph-welcome-modal" id="ph-welcome-modal" hidden="">
    <div class="ph-modal-backdrop"></div>
    <div class="ph-welcome-card" role="dialog" aria-modal="true" aria-label="Chọn nickname trainer">
      <div class="ph-welcome-icon" aria-hidden="true"><span>◉</span></div>
      <div class="ph-welcome-title">WELCOME, TRAINER!</div>
      <div class="ph-welcome-sub">Chọn nickname của bạn trước khi bắt đầu.<br>Nickname này sẽ được dùng để lưu thành tích<br>trên Global Leaderboard.</div>
      <input id="ph-trainer-name" class="ph-welcome-input" type="text" maxlength="18" autocomplete="nickname" placeholder="Ví dụ: AshVN" aria-label="Nickname trainer">
      <div class="ph-welcome-error" id="ph-welcome-error" role="alert"></div>
      <button id="ph-welcome-start" class="ph-welcome-start" type="button"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Bắt đầu chơi</button>
    </div>
  </div>
</div>

<style>
/* mọi thứ được giới hạn trong #pokehub-app nên sẽ không đụng tới CSS của theme */
#pokehub-app{
  overflow-anchor:none;
  --ph-void:#0a0b16;
  --ph-panel:#12142a;
  --ph-panel-2:#181b38;
  --ph-line:#2a2e55;
  --ph-text:#f2f1fb;
  --ph-dim:#9497c4;
  --ph-gold:#ffd35c;
  --ph-c1:#4a7fff;
  --ph-c2:#6be8c0;
  --ph-t2:#6be8c0;
  --ph-t3:#4a7fff;
  --ph-t4:#c96bff;
  --ph-t5:#ff8fd0;
  --ph-t6:#ffd35c;

  background:
    radial-gradient(1200px 700px at 15% -10%, #1c1f45 0%, transparent 60%),
    radial-gradient(1000px 700px at 110% 10%, #24123f 0%, transparent 55%),
    var(--ph-void);
  color:var(--ph-text);
  font-family:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  padding:26px 18px 56px;
  border-radius:16px;
  position:relative;
  overflow:hidden;
  line-height:1.5;
}
#pokehub-app *,#pokehub-app *::before,#pokehub-app *::after{box-sizing:border-box;}
#pokehub-app p,#pokehub-app h1,#pokehub-app div{margin:0;}
#pokehub-app .ph-wrap,
#pokehub-app .ph-tagline,
#pokehub-app .ph-err{margin-left:auto;margin-right:auto;}

#pokehub-app .ph-wrap{max-width:920px;margin:0 auto;position:relative;}

.ph-tools{display:flex;justify-content:flex-end;gap:8px;margin-bottom:10px;flex-wrap:wrap;}
.ph-tool{
  background:var(--ph-panel);border:1px solid var(--ph-line);color:var(--ph-text);
  border-radius:10px;height:38px;padding:0 13px;cursor:pointer;font:inherit;font-size:13px;
  display:flex;align-items:center;gap:7px;transition:border-color .15s ease;
}
.ph-tool:hover{border-color:var(--ph-gold);}
.ph-mute{width:38px;padding:0;justify-content:center;font-size:16px;}
.ph-mute.ph-muted{opacity:.5;}
.ph-col-count{
  background:var(--ph-gold);color:#0a0b16;border-radius:20px;padding:1px 7px;
  font-size:11px;font-weight:700;font-variant-numeric:tabular-nums;
}
.ph-col-count[hidden]{display:none;}

.ph-title{
  font-family:'Space Grotesk',system-ui,sans-serif;
  font-weight:700;
  font-size:clamp(26px,4.5vw,40px);
  letter-spacing:-.01em;
  text-align:center;
  color:var(--ph-text);
  margin:0;
}
.ph-dot{
  display:inline-block;width:0.2em;height:0.2em;margin-left:3px;border-radius:50%;
  background:var(--ph-gold);vertical-align:baseline;position:relative;top:-0.06em;
}
#pokehub-app .ph-tagline{
  text-align:center;color:var(--ph-dim);
  margin:12px auto 36px;font-size:15px;
  max-width:54ch;
  text-wrap:balance;
  text-wrap:pretty;
  padding:0 8px;
}
@supports (text-wrap:balance){ #pokehub-app .ph-tagline{ text-wrap:balance; } }

.ph-screen{display:none;}
.ph-screen.ph-active{display:block;}

#ph-status{text-align:center;color:var(--ph-dim);padding:40px 0;font-size:14px;}
#ph-status button{
  display:inline-block;margin:14px 6px 0;background:var(--ph-panel);color:var(--ph-text);
  border:1px solid var(--ph-line);border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-size:13px;
}
#ph-status button:hover{border-color:var(--ph-gold);}
#ph-status .ph-status-row{display:block;margin-top:10px;}
#ph-status .ph-progress{font-size:12px;color:var(--ph-dim);opacity:.8;display:block;margin-top:6px;}

.ph-exp-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;max-height:640px;overflow-y:auto;padding-right:4px;}
.ph-exp-card{
  min-width:0;min-height:230px;background:var(--ph-panel);border:1px solid var(--ph-line);border-radius:14px;
  padding:0;cursor:pointer;overflow:hidden;text-align:left;color:inherit;font:inherit;
  transition:transform .18s ease,border-color .18s ease;
}
.ph-exp-card:hover{transform:translateY(-4px);border-color:var(--ph-gold);}
.ph-exp-card:focus-visible{outline:2px solid var(--ph-gold);outline-offset:2px;}
@media (max-width:760px){
  .ph-exp-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;}
}
@media (max-width:480px){
  .ph-exp-grid{grid-template-columns:1fr;}
}
.ph-art{
  height:120px;position:relative;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(150deg,var(--c1) 0%,var(--c2) 100%);
  clip-path:polygon(0 0,100% 0,100% 86%,50% 100%,0 86%);
  padding:14px 20px;
}
.ph-art::before{
  content:'';position:absolute;inset:0;z-index:1;mix-blend-mode:overlay;pointer-events:none;
  background:repeating-linear-gradient(115deg,rgba(255,255,255,.16) 0 2px,transparent 2px 14px);
}
.ph-art .ph-art-bg{
  position:absolute;inset:0;width:100%;height:100%;z-index:0;display:block;object-fit:cover;object-position:center 38%;
}
.ph-art .ph-art-glyph{
  font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:30px;color:rgba(10,11,22,.55);
  position:relative;z-index:2;transition:opacity .15s ease;
}
.ph-art .ph-art-glyph-hidden{opacity:0;}
.ph-art .ph-art-logo{
  position:relative;z-index:3;max-width:100%;max-height:100%;object-fit:contain;
  filter:drop-shadow(0 3px 10px rgba(10,11,22,.45));
}
.ph-meta{padding:12px 14px 16px;display:block;}
.ph-meta .ph-name{font-weight:600;font-size:14.5px;line-height:1.3;}
.ph-meta .ph-series{color:var(--ph-dim);font-size:11px;margin-top:2px;text-transform:uppercase;letter-spacing:.04em;}
.ph-meta .ph-count{color:var(--ph-dim);font-size:12px;margin-top:4px;}

.ph-exp-progress{display:block;visibility:visible;margin-top:8px;padding-top:0;}
.ph-exp-progress-meta{display:block;color:var(--ph-dim);font-size:10.5px;font-weight:600;margin-bottom:5px;font-variant-numeric:tabular-nums;line-height:1.2;}
.ph-exp-progress-track{display:block;width:100%;height:5px;background:#0d1020;border:1px solid var(--ph-line);border-radius:99px;overflow:hidden;}
.ph-exp-progress-fill{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));border-radius:inherit;transition:width .25s ease;}

.ph-mini-progress{
  margin:0 auto 10px;max-width:920px;padding:8px 12px 9px;
  background:rgba(18,20,42,.78);border:1px solid var(--ph-line);border-radius:10px;
}
.ph-mini-progress-top{display:flex;align-items:center;justify-content:space-between;gap:12px;}
.ph-mini-progress-left{display:flex;align-items:baseline;gap:8px;min-width:0;flex-wrap:wrap;}
.ph-mini-progress-kicker{font-size:9px;letter-spacing:.13em;color:var(--ph-dim);font-weight:700;}
.ph-mini-progress-title{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:42vw;}
.ph-mini-progress-meta{font-size:11px;color:var(--ph-dim);white-space:nowrap;}
.ph-mini-progress-pct{font-family:'Space Grotesk',sans-serif;font-size:16px;font-weight:700;color:var(--ph-gold);font-variant-numeric:tabular-nums;white-space:nowrap;}
.ph-mini-progress-track{height:4px;margin-top:6px;background:#0d1020;border-radius:99px;overflow:hidden;}
.ph-mini-progress-bar{display:block;height:100%;width:0;border-radius:inherit;background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));transition:width .35s ease;}
.ph-missing-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(76px,1fr));gap:8px;
  max-height:360px;overflow:auto;padding:2px;
}
.ph-missing-card{
  appearance:none;border:1px solid var(--ph-line);background:var(--ph-panel);
  border-radius:9px;padding:0 0 6px;color:var(--ph-text);cursor:pointer;text-align:left;min-width:0;
  transition:transform .15s ease,border-color .15s ease;
}
.ph-missing-card:hover{transform:translateY(-2px);border-color:var(--ph-gold);}
.ph-missing-img{display:flex;align-items:center;justify-content:center;aspect-ratio:245/337;background:var(--ph-panel-2);border-radius:8px 8px 0 0;overflow:hidden;color:var(--ph-dim);font-size:9px;text-align:center;padding:4px;}
.ph-missing-img img{width:100%;height:100%;object-fit:cover;display:block;}
.ph-missing-name{display:block;font-size:9.5px;color:var(--ph-dim);padding:5px 6px 0;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.ph-missing-empty{
  display:flex;align-items:center;justify-content:center;gap:8px;padding:18px 10px;
  color:var(--ph-t2);background:rgba(107,232,192,.06);border:1px solid rgba(107,232,192,.18);border-radius:10px;
  font-size:12px;
}
.ph-collection-set-detail{
  margin-top:14px;padding-top:16px;border-top:1px solid var(--ph-line);
}
.ph-collection-set-detail-head{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;margin-bottom:10px;}
.ph-collection-set-detail-title{font-family:'Space Grotesk',sans-serif;font-size:17px;font-weight:700;}
.ph-collection-set-detail-meta{font-size:11.5px;color:var(--ph-dim);margin-top:2px;}
.ph-collection-set-detail-pct{font-family:'Space Grotesk',sans-serif;font-size:20px;font-weight:700;color:var(--ph-gold);}
.ph-collection-set-detail-track{height:7px;background:#0d1020;border:1px solid var(--ph-line);border-radius:99px;overflow:hidden;margin-bottom:12px;}
.ph-collection-set-detail-fill{height:100%;width:0;background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));border-radius:inherit;}
.ph-collection-missing-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-bottom:8px;}
.ph-collection-missing-count{font-size:12.5px;font-weight:700;}
.ph-collection-missing-note{font-size:11px;color:var(--ph-dim);}
.ph-set-overview{margin-top:0;}
.ph-set-overview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px;max-height:330px;overflow:auto;padding-right:2px;}
.ph-set-overview-item{background:var(--ph-panel-2);border:1px solid var(--ph-line);border-radius:10px;padding:10px 11px;}
.ph-set-overview-top{display:flex;align-items:center;justify-content:space-between;gap:8px;}
.ph-set-overview-name{font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.ph-set-overview-pct{font-family:'Space Grotesk',sans-serif;font-size:13px;font-weight:700;color:var(--ph-gold);}
.ph-set-overview-meta{font-size:10.5px;color:var(--ph-dim);margin:3px 0 6px;}
.ph-current-chip{display:inline-block;margin-top:6px;padding:2px 7px;border-radius:99px;background:rgba(255,211,92,.1);border:1px solid rgba(255,211,92,.25);color:var(--ph-gold);font-size:9px;font-weight:700;}


.ph-novelty-badge{
  position:absolute;top:6px;left:6px;z-index:5;min-width:30px;height:22px;padding:0 7px;
  display:flex;align-items:center;justify-content:center;border-radius:7px;
  font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:800;letter-spacing:.02em;
  box-shadow:0 6px 16px rgba(0,0,0,.45);backdrop-filter:blur(5px);
  pointer-events:none;
}
.ph-new{background:rgba(107,232,192,.95);color:#08110f;border:1px solid rgba(255,255,255,.35);}
.ph-duplicate{background:rgba(255,211,92,.95);color:#171204;border:1px solid rgba(255,255,255,.35);}
.ph-shop-new-badge{left:auto;right:6px;top:6px;}


.ph-back{
  background:none;border:none;color:var(--ph-dim);cursor:pointer;font:inherit;font-size:13px;
  padding:0;margin-bottom:18px;
}
.ph-back:hover{color:var(--ph-text);}

.ph-stage{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:420px;}
.ph-pack{
  width:180px;height:250px;border-radius:14px;position:relative;cursor:pointer;
  background:linear-gradient(150deg,var(--ph-c1) 0%,var(--ph-c2) 100%);
  box-shadow:0 20px 50px -10px rgba(0,0,0,.6);
  display:flex;align-items:center;justify-content:center;
  transition:transform .2s ease;animation:ph-float 3.2s ease-in-out infinite;
}
.ph-pack::before{
  content:'';position:absolute;inset:0;border-radius:14px;mix-blend-mode:overlay;
  background:repeating-linear-gradient(115deg,rgba(255,255,255,.18) 0 2px,transparent 2px 16px);
}
.ph-pack::after{content:'';position:absolute;left:0;right:0;top:38%;height:2px;background:rgba(10,11,22,.35);}
.ph-pack-label{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:15px;color:rgba(10,11,22,.75);text-align:center;padding:0 18px;z-index:1;}
.ph-pack:hover{transform:scale(1.03);}
.ph-pack.ph-ripping{animation:ph-rip .55s ease forwards;}
.ph-pack.ph-busy{cursor:progress;opacity:.75;}
@keyframes ph-float{0%,100%{transform:translateY(0);}50%{transform:translateY(-8px);}}
@keyframes ph-rip{
  0%{transform:scale(1);}
  35%{transform:scale(1.08) rotate(-2deg);}
  70%{transform:scale(1.25) rotate(2deg);filter:brightness(2);}
  100%{transform:scale(1.6);opacity:0;filter:brightness(3);}
}
.ph-hint{margin-top:22px;color:var(--ph-dim);font-size:13px;}

.ph-box-row{
  display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap;
  margin-top:18px;
}
.ph-box-row[hidden]{display:none;}
.ph-box-lbl{color:var(--ph-dim);font-size:12.5px;}
.ph-chip{
  background:var(--ph-panel);border:1px solid var(--ph-line);color:var(--ph-text);
  border-radius:20px;padding:7px 14px;font:inherit;font-size:12.5px;cursor:pointer;
  transition:border-color .15s ease,transform .15s ease;
}
.ph-chip:hover{border-color:var(--ph-gold);transform:translateY(-2px);}
.ph-chip-hot{border-color:rgba(255,211,92,.5);color:var(--ph-gold);font-weight:600;}

.ph-progress-panel{padding:60px 10px;text-align:center;}
.ph-progress-panel[hidden]{display:none;}
.ph-progress-text{font-family:'Space Grotesk',sans-serif;font-weight:600;font-size:16px;}
.ph-progress-track{
  max-width:420px;margin:16px auto 0;height:8px;border-radius:99px;
  background:var(--ph-panel-2);border:1px solid var(--ph-line);overflow:hidden;
}
.ph-progress-bar{height:100%;width:0;background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));transition:width .2s ease;}
.ph-progress-sub{color:var(--ph-dim);font-size:12.5px;margin-top:12px;}

.ph-flash{position:absolute;inset:0;background:#fff;opacity:0;pointer-events:none !important;z-index:5;}
.ph-flash.ph-go{animation:ph-flash .5s ease forwards;}
@keyframes ph-flash{0%{opacity:0;}15%{opacity:.9;}100%{opacity:0;}}

.ph-confetti{position:absolute;inset:0;width:100%;height:100%;pointer-events:none !important;z-index:6;}

.ph-toast{
  position:absolute;left:50%;bottom:18px;transform:translate(-50%,14px);
  background:#1c1f45;border:1px solid var(--ph-line);color:var(--ph-text);
  padding:10px 16px;border-radius:10px;font-size:13px;max-width:min(92%,480px);
  text-align:center;opacity:0;pointer-events:none;z-index:20;
  transition:opacity .2s ease,transform .2s ease;box-shadow:0 14px 40px -12px rgba(0,0,0,.8);
}
.ph-toast.ph-show{opacity:1;transform:translate(-50%,0);}

.ph-reveal{display:none;}
.ph-reveal.ph-active{display:block;}
.ph-reveal.ph-extract-source{position:relative;}
.ph-reveal.ph-extract-source::before{
  content:'';position:absolute;left:50%;top:34px;width:82px;height:116px;transform:translateX(-50%);
  border-radius:10px 10px 8px 8px;pointer-events:none;opacity:.92;z-index:0;
  background:linear-gradient(145deg,var(--ph-c1),var(--ph-c2));
  border:1px solid rgba(255,255,255,.16);
  box-shadow:0 14px 28px rgba(0,0,0,.34),0 0 24px rgba(74,127,255,.16);
  clip-path:polygon(0 0,100% 0,88% 7%,100% 14%,88% 21%,100% 28%,88% 35%,100% 42%,88% 49%,100% 56%,88% 63%,100% 70%,88% 77%,100% 84%,88% 91%,100% 100%,0 100%,12% 91%,0 84%,12% 77%,0 70%,12% 63%,0 56%,12% 49%,0 42%,12% 35%,0 28%,12% 21%,0 14%,12% 7%);
}
.ph-reveal.ph-extract-source .ph-pull-heading,.ph-reveal.ph-extract-source .ph-card-row{position:relative;z-index:1;}
.ph-reveal.ph-extract-source .ph-pull-heading{z-index:2;}
.ph-pull-heading{text-align:center;font-family:'Space Grotesk',sans-serif;font-weight:600;font-size:14px;color:var(--ph-dim);margin-bottom:18px;}
.ph-card-row{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;perspective:1000px;position:relative;}
@media (max-width:640px){.ph-card-row{gap:6px;}}
.ph-slot{aspect-ratio:245/337;position:relative;cursor:pointer;}
.ph-inner{position:relative;width:100%;height:100%;transform-style:preserve-3d;transition:transform .6s cubic-bezier(.2,.8,.2,1);}
.ph-card-col{min-width:0;perspective:1000px;}
.ph-card-col.ph-extracting{
  opacity:0;
  transform:translateY(-92px) scale(.72) rotate(-4deg);
  animation:ph-card-extract .58s cubic-bezier(.2,.8,.2,1) forwards;
  animation-delay:var(--ph-extract-delay,0ms);
  position:relative;z-index:2;transform-origin:50% 100%;
}
@keyframes ph-card-extract{
  0%{opacity:0;transform:translateY(-92px) scale(.72) rotate(-4deg);}
  32%{opacity:1;transform:translateY(-38px) scale(.86) rotate(2deg);}
  62%{opacity:1;transform:translateY(7px) scale(1.03) rotate(-.8deg);}
  82%{opacity:1;transform:translateY(-2px) scale(.995) rotate(.25deg);}
  100%{opacity:1;transform:translateY(0) scale(1) rotate(0);}
}
.ph-slot.ph-tremble{animation:ph-tremble .22s ease-in-out 0s 3;}
.ph-slot.ph-tremble-strong{animation:ph-tremble-strong .24s ease-in-out 0s 4;}
.ph-slot.ph-last-anticipation{animation:ph-tremble-strong .24s ease-in-out 0s 3,ph-last-pulse .9s ease-in-out;}
@keyframes ph-tremble{
  0%,100%{transform:translateX(0) rotate(0);}
  25%{transform:translateX(-2px) rotate(-.6deg);}
  50%{transform:translateX(2px) rotate(.6deg);}
  75%{transform:translateX(-1px) rotate(-.35deg);}
}
@keyframes ph-tremble-strong{
  0%,100%{transform:translateX(0) rotate(0) scale(1);}
  20%{transform:translateX(-3px) rotate(-1deg) scale(1.008);}
  40%{transform:translateX(3px) rotate(1deg) scale(1.012);}
  60%{transform:translateX(-3px) rotate(-1deg) scale(1.008);}
  80%{transform:translateX(2px) rotate(.8deg) scale(1.01);}
}
@keyframes ph-last-pulse{0%,100%{filter:none;}45%{filter:brightness(1.2);}65%{filter:brightness(.95);}}
.ph-slot.ph-chase-6{animation:ph-chase-6 1.18s cubic-bezier(.22,.8,.24,1) both;}
@keyframes ph-chase-6{
  0%{transform:translateX(0) rotate(0) scale(1);filter:brightness(1);}
  17%{transform:translateX(-3px) rotate(-1deg) scale(1.01);filter:brightness(1.22);}
  28%{transform:translateX(0) rotate(0) scale(1);filter:brightness(1);}
  47%{transform:translateX(4px) rotate(1.1deg) scale(1.018);filter:brightness(1.32);}
  58%{transform:translateX(0) rotate(0) scale(1);filter:brightness(1.02);}
  74%{transform:translateX(-5px) rotate(-1.3deg) scale(1.03);filter:brightness(1.52);}
  82%{transform:translateX(0) rotate(0) scale(1.01);filter:brightness(1.12);}
  100%{transform:translateX(0) rotate(0) scale(1);filter:brightness(1);}
}
@media (prefers-reduced-motion:reduce){
  .ph-card-col.ph-extracting{animation:none;opacity:1;transform:none;}
  .ph-slot.ph-tremble,.ph-slot.ph-tremble-strong,.ph-slot.ph-last-anticipation,.ph-slot.ph-chase-6{animation:none;}
}
.ph-slot.ph-flipped .ph-inner{transform:rotateY(180deg);}
.ph-face{position:absolute;inset:0;border-radius:8px;backface-visibility:hidden;overflow:hidden;display:flex;align-items:center;justify-content:center;}
.ph-back-face{
  background:radial-gradient(circle at 50% 40%,#262a52 0%,#14162c 70%);
  border:1px solid var(--ph-line);color:var(--ph-dim);
  font-family:'Space Grotesk',sans-serif;font-size:26px;
}
.ph-front-face{transform:rotateY(180deg);background:var(--ph-panel-2);border:1px solid var(--ph-line);}
.ph-front-face img{width:100%;height:100%;object-fit:cover;display:block;}
.ph-image-fallback{font-size:11px;color:var(--ph-dim);padding:8px;text-align:center;line-height:1.3;}
.ph-slot[data-tier="2"] .ph-front-face{box-shadow:0 0 0 2px var(--ph-t2),0 0 18px -2px var(--ph-t2);}
.ph-slot[data-tier="3"] .ph-front-face{box-shadow:0 0 0 2px var(--ph-t3),0 0 22px -2px var(--ph-t3);}
.ph-slot[data-tier="4"] .ph-front-face{box-shadow:0 0 0 2px var(--ph-t4),0 0 26px 0 var(--ph-t4);}
.ph-slot[data-tier="5"] .ph-front-face{box-shadow:0 0 0 3px var(--ph-t5),0 0 32px 2px var(--ph-t5);}
.ph-slot[data-tier="6"] .ph-front-face{box-shadow:0 0 0 3px var(--ph-t6),0 0 44px 4px var(--ph-t6);}
.ph-slot[data-tier="4"].ph-flipped,.ph-slot[data-tier="5"].ph-flipped,.ph-slot[data-tier="6"].ph-flipped{z-index:2;}
.ph-slot[data-tier="4"].ph-flipped .ph-front-face::before,
.ph-slot[data-tier="5"].ph-flipped .ph-front-face::before,
.ph-slot[data-tier="6"].ph-flipped .ph-front-face::before{
  content:'';position:absolute;inset:0;mix-blend-mode:overlay;
  background:linear-gradient(115deg,transparent 40%,rgba(255,255,255,.55) 50%,transparent 60%);
  background-size:250% 250%;animation:ph-shine 1.8s ease-in-out infinite;
}
@keyframes ph-shine{0%{background-position:-40% -40%;}100%{background-position:140% 140%;}}
.ph-card-label{text-align:center;font-size:11.5px;color:var(--ph-dim);margin-top:8px;min-height:30px;line-height:1.4;}
.ph-card-price{color:var(--ph-gold);font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap;}
.ph-card-price.ph-card-noprice{color:var(--ph-dim);font-weight:400;}

.ph-banner{
  text-align:center;margin:30px 0 6px;min-height:34px;
  font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:20px;
  opacity:0;transform:translateY(6px);transition:opacity .4s ease,transform .4s ease;
}
.ph-banner.ph-show{opacity:1;transform:translateY(0);}
.ph-banner.ph-rare{color:var(--ph-t3);}
.ph-banner.ph-star{color:var(--ph-t4);}
.ph-banner.ph-shiny{color:var(--ph-t5);}
.ph-banner.ph-crown{color:var(--ph-gold);text-shadow:0 0 18px rgba(255,211,92,.6);}

.ph-luck{display:flex;align-items:center;justify-content:center;gap:8px;color:var(--ph-dim);font-size:14px;margin-top:10px;}
.ph-luck b{color:var(--ph-text);font-family:'Space Grotesk',sans-serif;font-size:16px;font-variant-numeric:tabular-nums;}
.ph-total{text-align:center;color:var(--ph-dim);font-size:14px;margin-top:6px;}
.ph-total b{color:var(--ph-gold);font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:16px;font-variant-numeric:tabular-nums;}
.ph-saved{text-align:center;color:var(--ph-dim);font-size:12.5px;margin-top:8px;}

.ph-quip{
  text-align:center;font-size:14px;font-weight:500;margin-top:12px;min-height:20px;
  opacity:0;transform:translateY(4px);transition:opacity .4s ease,transform .4s ease;
}
.ph-quip.ph-show{opacity:1;transform:translateY(0);}
.ph-quip-bad{color:var(--ph-dim);}
.ph-quip-meh{color:var(--ph-text);opacity:.8;}
.ph-quip-good{color:var(--ph-t5);}
.ph-quip-great{color:var(--ph-t5);text-shadow:0 0 16px rgba(255,143,208,.45);}

.ph-actions{display:flex;gap:12px;justify-content:center;margin-top:30px;flex-wrap:wrap;}
.ph-btn{
  font-family:'Space Grotesk',sans-serif;font-weight:600;font-size:14px;
  padding:12px 22px;border-radius:10px;border:1px solid var(--ph-line);
  background:var(--ph-panel);color:var(--ph-text);cursor:pointer;
  transition:border-color .15s ease,transform .15s ease;
}
.ph-btn:hover{transform:translateY(-2px);border-color:var(--ph-gold);}
.ph-btn.ph-small{padding:8px 14px;font-size:12.5px;}
.ph-btn.ph-danger{color:#ff9a9a;}
.ph-primary{background:linear-gradient(120deg,var(--ph-c1),var(--ph-c2));border:none;color:#0a0b16;}
#pokehub-app .ph-err{text-align:center;color:#ff9a9a;font-size:13px;margin:14px auto 0;max-width:46ch;}

/* ---------- nhận diện / bản quyền vnGame.tv ---------- */
#pokehub-app .ph-brand-footer{
  width:100%;
  max-width:920px;
  margin:26px auto 0;
  padding:15px 16px 13px;
  text-align:center;
  border-top:1px solid rgba(148,151,196,.18);
  background:linear-gradient(180deg,rgba(18,20,42,.30),rgba(18,20,42,.10));
  border-radius:0 0 12px 12px;
  color:var(--ph-dim);
  font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
}
#pokehub-app .ph-brand-footer-main{
  color:var(--ph-text);
  font-size:12.5px;
  line-height:1.55;
  font-weight:600;
  text-wrap:balance;
}
#pokehub-app .ph-brand-footer-main span,
#pokehub-app .ph-brand-footer-meta span[aria-hidden="true"]{
  color:var(--ph-gold);
}
#pokehub-app .ph-brand-footer-meta{
  display:flex;
  justify-content:center;
  align-items:center;
  gap:8px;
  flex-wrap:wrap;
  margin-top:6px;
  color:var(--ph-dim);
  font-size:10.5px;
  line-height:1.45;
  letter-spacing:.01em;
}
#pokehub-app .ph-brand-footer-meta span:last-child{
  color:rgba(242,241,251,.62);
}
@media (max-width:520px){
  #pokehub-app .ph-brand-footer{
    margin-top:22px;
    padding:13px 12px 12px;
  }
  #pokehub-app .ph-brand-footer-main{
    font-size:11.5px;
  }
  #pokehub-app .ph-brand-footer-meta{
    font-size:10px;
    gap:6px;
  }
}

/* bảng ROI của hộp: chỉ hiện sau khi lật hết 5 lá tinh hoa */
.ph-box-stats{
  opacity:0;transform:translateY(10px);pointer-events:none;
  transition:opacity .5s ease,transform .5s ease;
  margin-top:8px;padding-top:4px;
}
.ph-box-stats.ph-show{opacity:1;transform:translateY(0);pointer-events:auto;}

/* ---------- thống kê / bộ sưu tập ---------- */
.ph-stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin-bottom:14px;}
.ph-stat{background:var(--ph-panel-2);border:1px solid var(--ph-line);border-radius:12px;padding:12px 14px;}
.ph-stat-v{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:20px;font-variant-numeric:tabular-nums;}
.ph-stat-l{color:var(--ph-dim);font-size:11.5px;margin-top:2px;}
.ph-stat.ph-pos .ph-stat-v{color:var(--ph-t2);}
.ph-stat.ph-neg .ph-stat-v{color:#ff9a9a;}
.ph-col-note{color:var(--ph-dim);font-size:12.5px;margin-bottom:12px;}
.ph-col-ctl{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:6px;}
.ph-col-ctl label{color:var(--ph-dim);font-size:12.5px;display:flex;align-items:center;gap:8px;}
.ph-col-ctl input{
  width:92px;background:var(--ph-panel-2);border:1px solid var(--ph-line);color:var(--ph-text);
  border-radius:8px;padding:7px 9px;font:inherit;font-size:13px;
}

.ph-bar-row{display:flex;align-items:center;gap:10px;margin-bottom:6px;font-size:12.5px;}
.ph-bar-l{color:var(--ph-dim);flex:0 0 118px;}
.ph-bar-w{flex:1;height:8px;background:var(--ph-panel-2);border-radius:99px;overflow:hidden;}
.ph-bar-f{display:block;height:100%;background:var(--ph-dim);}
.ph-bar-f[data-tier="2"]{background:var(--ph-t2);}
.ph-bar-f[data-tier="3"]{background:var(--ph-t3);}
.ph-bar-f[data-tier="4"]{background:var(--ph-t4);}
.ph-bar-f[data-tier="5"]{background:var(--ph-t5);}
.ph-bar-f[data-tier="6"]{background:var(--ph-t6);}
.ph-bar-n{flex:0 0 42px;text-align:right;color:var(--ph-text);font-variant-numeric:tabular-nums;}

.ph-thumb-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:12px;}
.ph-thumb.ph-clickable{cursor:pointer;}
.ph-thumb-img{
  aspect-ratio:245/337;border-radius:8px;overflow:hidden;background:var(--ph-panel-2);
  position:relative;
  border:1px solid var(--ph-line);display:flex;align-items:center;justify-content:center;
  font-size:11px;color:var(--ph-dim);text-align:center;padding:6px;
}
.ph-thumb-img img{width:100%;height:100%;object-fit:cover;display:block;}
.ph-thumb-img[data-tier="4"]{box-shadow:0 0 0 2px var(--ph-t4);}
.ph-thumb-img[data-tier="5"]{box-shadow:0 0 0 2px var(--ph-t5);}
.ph-thumb-img[data-tier="6"]{box-shadow:0 0 0 2px var(--ph-t6);}
.ph-thumb-name{font-size:11.5px;margin-top:6px;line-height:1.35;}
.ph-thumb-price{font-size:12px;color:var(--ph-gold);font-weight:600;font-variant-numeric:tabular-nums;}
.ph-thumb-price.ph-card-noprice{color:var(--ph-dim);font-weight:400;font-size:11px;}

.ph-set-overview-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:9px;}
.ph-set-overview-item{background:var(--ph-panel-2);border:1px solid var(--ph-line);border-radius:10px;padding:11px 12px;position:relative;}
.ph-set-overview-top{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;}
.ph-set-overview-name{font-size:12.5px;font-weight:600;line-height:1.3;}
.ph-set-overview-pct{font-family:'Space Grotesk',sans-serif;font-size:13px;font-weight:700;color:var(--ph-gold);white-space:nowrap;}
.ph-set-overview-meta{color:var(--ph-dim);font-size:10.5px;margin:4px 0 7px;}
.ph-current-chip{position:absolute;right:10px;bottom:9px;font-size:9px;padding:2px 6px;border-radius:99px;background:rgba(255,211,92,.12);color:var(--ph-gold);border:1px solid rgba(255,211,92,.25);}

/* ---------- thử thách ---------- */
.ph-challenge-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:10px;}
.ph-challenge-item{background:var(--ph-panel-2);border:1px solid var(--ph-line);border-radius:12px;padding:13px 14px;position:relative;overflow:hidden;}
.ph-challenge-item.ph-done{border-color:rgba(107,232,192,.45);box-shadow:0 0 0 1px rgba(107,232,192,.08) inset;}
.ph-challenge-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;}
.ph-challenge-name{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:14px;line-height:1.3;}
.ph-challenge-desc{font-size:11.5px;color:var(--ph-dim);margin-top:4px;line-height:1.45;}
.ph-challenge-status{flex:none;font-size:10px;font-weight:800;padding:3px 7px;border-radius:99px;background:rgba(255,211,92,.10);border:1px solid rgba(255,211,92,.25);color:var(--ph-gold);}
.ph-challenge-done{background:rgba(107,232,192,.10);border-color:rgba(107,232,192,.28);color:var(--ph-t2);}
.ph-challenge-progress{display:flex;align-items:center;gap:8px;margin-top:11px;}
.ph-challenge-track{flex:1;height:7px;background:#0d1020;border:1px solid var(--ph-line);border-radius:99px;overflow:hidden;}
.ph-challenge-fill{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));border-radius:inherit;transition:width .3s ease;}
.ph-challenge-meta{flex:0 0 auto;font-size:10.5px;color:var(--ph-text);font-variant-numeric:tabular-nums;}
.ph-challenge-foot{margin-top:8px;font-size:10.5px;color:var(--ph-dim);}
.ph-challenge-hero{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px;}
.ph-challenge-hero .ph-stat{padding:10px 11px;}
@media (max-width:560px){.ph-challenge-hero{grid-template-columns:1fr;}}

/* ---------- modal ---------- */
.ph-modal{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:20px;}
.ph-modal[hidden]{display:none;}
.ph-modal-backdrop{position:absolute;inset:0;background:rgba(6,7,16,.72);backdrop-filter:blur(2px);}
.ph-modal-card{
  position:relative;z-index:1;width:100%;max-width:640px;max-height:88vh;overflow-y:auto;
  background:var(--ph-panel);border:1px solid var(--ph-line);border-radius:16px;
  padding:26px 24px;box-shadow:0 30px 80px -20px rgba(0,0,0,.7);
  animation:ph-modal-in .22s cubic-bezier(.2,.8,.2,1);
  color:var(--ph-text);font-family:'Inter',system-ui,sans-serif;
}
.ph-modal-wide{max-width:860px;}
@keyframes ph-modal-in{from{opacity:0;transform:translateY(10px) scale(.98);}to{opacity:1;transform:translateY(0) scale(1);}}
.ph-modal-close{
  position:absolute;top:14px;right:14px;width:32px;height:32px;border-radius:9px;
  background:var(--ph-panel-2);border:1px solid var(--ph-line);color:var(--ph-text);
  cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;
  transition:border-color .15s ease;z-index:2;
}
.ph-modal-close:hover{border-color:var(--ph-gold);}
.ph-modal-head{display:flex;gap:22px;align-items:flex-start;}
.ph-modal-imgwrap{flex:0 0 180px;width:180px;aspect-ratio:245/337;border-radius:10px;overflow:hidden;background:var(--ph-panel-2);border:1px solid var(--ph-line);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ph-dim);text-align:center;padding:8px;}
.ph-modal-imgwrap img{width:100%;height:100%;object-fit:cover;display:block;}
.ph-modal-info{flex:1;min-width:0;}
.ph-modal-title{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:20px;line-height:1.25;}
.ph-modal-sub{color:var(--ph-dim);font-size:12.5px;margin-top:4px;text-transform:uppercase;letter-spacing:.03em;}
.ph-modal-price{color:var(--ph-gold);font-weight:700;font-size:17px;margin-top:12px;font-variant-numeric:tabular-nums;}
.ph-modal-price.ph-card-noprice{color:var(--ph-dim);font-weight:500;font-size:13px;}
.ph-modal-rows{margin-top:14px;display:flex;flex-direction:column;gap:6px;}
.ph-modal-row{display:flex;gap:10px;font-size:13px;justify-content:space-between;}
.ph-modal-k{color:var(--ph-dim);}
.ph-modal-v{color:var(--ph-text);text-align:right;}
.ph-modal-section{margin-top:20px;padding-top:16px;border-top:1px solid var(--ph-line);}
.ph-modal-section-title{font-family:'Space Grotesk',sans-serif;font-weight:600;font-size:13px;color:var(--ph-dim);text-transform:uppercase;letter-spacing:.04em;margin-bottom:10px;}
.ph-modal-attack + .ph-modal-attack{margin-top:10px;}
.ph-modal-attack-line{font-size:14px;font-weight:600;}
.ph-modal-attack-effect{font-size:13px;color:var(--ph-dim);margin-top:2px;line-height:1.5;}
.ph-modal-flavor{font-size:13.5px;color:var(--ph-dim);line-height:1.6;}
@media (max-width:520px){
  .ph-mini-progress-title{max-width:52vw;}
  .ph-mini-progress-left{gap:6px;}
  .ph-mini-progress-meta{font-size:10.5px;}
  .ph-mini-progress-pct{font-size:15px;}
  .ph-missing-grid{grid-template-columns:repeat(auto-fill,minmax(68px,1fr));max-height:300px;}
  .ph-modal-head{flex-direction:column;align-items:center;text-align:center;}
  .ph-modal-imgwrap{flex:0 0 auto;width:150px;}
  .ph-modal-row{justify-content:center;gap:6px;}
  .ph-modal-rows{align-items:center;}
}

@media (prefers-reduced-motion:reduce){
  #pokehub-app *{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;}
  .ph-confetti{display:none;}
  .ph-modal-card{animation:none;}
}

/* ============================================================
   ADD-ON: giao diện thưởng + đếm ngược nhiệm vụ 30 phút + cửa hàng đổi thưởng + sửa scroll mở pack
   ============================================================ */
.ph-challenge-refresh{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:12px;padding:11px 13px;border-radius:11px;
  background:linear-gradient(90deg,rgba(74,127,255,.10),rgba(107,232,192,.08));
  border:1px solid rgba(107,232,192,.20);color:var(--ph-text);
  font-size:12px;font-weight:600;
}
.ph-challenge-refresh b{
  color:var(--ph-gold);font-family:'Space Grotesk',sans-serif;
  font-size:15px;font-variant-numeric:tabular-nums;white-space:nowrap;
}
.ph-challenge-cycle-note{
  color:var(--ph-dim);font-size:11px;margin:-6px 0 12px;
  text-align:center;
}
.ph-challenge-reward{
  color:var(--ph-gold);font-weight:700;
}
.ph-challenge-complete-time{
  color:var(--ph-t2);font-size:10px;
}
.ph-challenge-item.ph-done .ph-challenge-reward{
  text-shadow:0 0 10px rgba(255,211,92,.22);
}
@media (max-width:560px){
  .ph-challenge-refresh{align-items:flex-start;flex-direction:column;gap:5px;}
}



/* ---------- cửa hàng đổi thưởng ---------- */
.ph-shop-section{margin-top:18px;padding-top:16px;border-top:1px solid var(--ph-line);}
.ph-shop-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:6px;}
.ph-shop-head .ph-modal-section-title{margin-bottom:0;}
.ph-shop-points{font-family:'Space Grotesk',sans-serif;color:var(--ph-gold);font-weight:800;font-size:15px;white-space:nowrap;}
.ph-shop-note{color:var(--ph-dim);font-size:11.5px;line-height:1.45;margin-bottom:10px;}
.ph-shop-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:9px;}
.ph-shop-item{display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--ph-panel-2);border:1px solid var(--ph-line);border-radius:11px;padding:11px 12px;}
.ph-shop-main{min-width:0;flex:1;}
.ph-shop-name{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:13px;line-height:1.3;}
.ph-shop-desc{color:var(--ph-dim);font-size:10.5px;line-height:1.4;margin-top:3px;}
.ph-shop-stock{color:var(--ph-t2);font-size:10px;font-weight:700;margin-top:5px;}
.ph-shop-side{display:flex;flex-direction:column;align-items:flex-end;gap:5px;flex:none;}
.ph-shop-price{color:var(--ph-gold);font-size:11px;font-weight:800;white-space:nowrap;}
.ph-shop-buy{white-space:nowrap;min-width:54px;}
.ph-shop-inventory-title{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:12px;margin:14px 0 7px;}
.ph-shop-inventory{display:flex;flex-wrap:wrap;gap:7px;}
.ph-shop-inventory span{background:rgba(255,211,92,.07);border:1px solid rgba(255,211,92,.18);color:var(--ph-text);border-radius:999px;padding:4px 8px;font-size:10.5px;}
@media (max-width:560px){.ph-shop-item{align-items:flex-start;}.ph-shop-side{min-width:72px;}.ph-shop-grid{grid-template-columns:1fr;}}

/* ---------- cửa hàng bên ngoài ---------- */
.ph-shop-outside-balance{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:12px;padding:12px 14px;border-radius:11px;
  background:linear-gradient(90deg,rgba(255,211,92,.10),rgba(107,232,192,.08));
  border:1px solid rgba(255,211,92,.22);
}
.ph-shop-outside-balance b{
  color:var(--ph-gold);font-family:'Space Grotesk',sans-serif;font-size:18px;font-variant-numeric:tabular-nums;
}


/* ---------- patch: modal mobile + toast trên modal ---------- */
#pokehub-app .ph-toast{
  position:fixed;
  left:50%;
  bottom:18px;
  z-index:2001;
  pointer-events:none;
}

#pokehub-app .ph-modal-card{
  display:flex;
  flex-direction:column;
  overflow:hidden;
  min-height:0;
}

#pokehub-app .ph-modal-body{
  min-height:0;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding-right:2px;
}

@media (max-width:560px){
  #pokehub-app .ph-modal{
    align-items:flex-start;
    padding:10px;
  }
  #pokehub-app .ph-modal-card{
    width:100%;
    max-width:none;
    max-height:calc(100dvh - 20px);
    padding:54px 14px 14px;
    border-radius:14px;
  }
  #pokehub-app .ph-modal-close{
    position:absolute;
    top:10px;
    right:10px;
    width:36px;
    height:36px;
    z-index:10;
  }
  #pokehub-app .ph-modal-body{
    max-height:none;
    overflow-y:auto;
  }
}


/* ---------- patch cuối: modal thoát khỏi overflow của widget + nút X luôn nổi ---------- */
#pokehub-app:has(.ph-modal:not([hidden])){
  overflow:visible !important;
}
#pokehub-app .ph-modal{
  position:fixed !important;
  inset:0 !important;
  z-index:2147483000 !important;
  overflow:visible !important;
}
#pokehub-app .ph-modal-card{
  position:relative !important;
  z-index:2147483001 !important;
}
#pokehub-app .ph-modal-close{
  position:absolute !important;
  top:auto !important;
  right:auto !important;
  left:50% !important;
  bottom:10px !important;
  transform:translateX(-50%) !important;
  width:42px !important;
  height:42px !important;
  z-index:2147483647 !important;
  display:flex !important;
  align-items:center !important;
  justify-content:center !important;
  pointer-events:auto !important;
  opacity:1 !important;
  visibility:visible !important;
}
@media (max-width:560px){
  #pokehub-app .ph-modal-card{
    width:calc(100vw - 20px) !important;
    max-width:none !important;
    max-height:calc(100dvh - 20px) !important;
    margin-top:10px !important;
    margin-bottom:10px !important;
    padding-bottom:68px !important;
  }
  #pokehub-app .ph-modal-body{
    max-height:calc(100dvh - 120px) !important;
    overflow-y:auto !important;
  }
}
/* ---------- patch: nút X trên PC ---------- */
@media (min-width:561px){
  #pokehub-app .ph-modal-close{
    top:14px !important;
    right:14px !important;
    left:auto !important;
    bottom:auto !important;
    transform:none !important;
    width:36px !important;
    height:36px !important;
    z-index:2147483647 !important;
    display:flex !important;
    align-items:center !important;
    justify-content:center !important;
    pointer-events:auto !important;
    opacity:1 !important;
    visibility:visible !important;
  }
}

/* ---------- patch: giãn khoảng cách phần kết quả hộp cho dễ đọc ---------- */
#pokehub-app #ph-box-result .ph-box-stats .ph-btn.ph-small{
  margin-top:14px !important;
  margin-bottom:18px;
}
#pokehub-app #ph-box-result .ph-box-stats .ph-quip{
  margin-top:14px;
  line-height:1.55;
}
#pokehub-app #ph-box-result > .ph-actions{
  margin-top:24px;
}

/* ---------- patch: khóa AdSense trong widget để không phá layout ---------- */
#pokehub-app .adsbygoogle,
#pokehub-app ins.adsbygoogle,
#pokehub-app .google-auto-placed,
#pokehub-app [id^="google_ads_iframe_"],
#pokehub-app iframe[src*="googleads"],
#pokehub-app iframe[src*="doubleclick"]{
  display:none !important;
  width:0 !important;
  height:0 !important;
  min-height:0 !important;
  max-height:0 !important;
  margin:0 !important;
  padding:0 !important;
  border:0 !important;
  overflow:hidden !important;
}




/* ---------- Trainer nickname + global realtime leaderboard ---------- */
.ph-leaderboard-tool{min-width:84px;}
.ph-lb-status{font-size:9px;font-weight:800;letter-spacing:.02em;color:var(--ph-dim);padding:2px 6px;border-radius:999px;border:1px solid var(--ph-line);background:rgba(148,151,196,.06);white-space:nowrap;}
.ph-lb-online{color:var(--ph-t2);border-color:rgba(107,232,192,.3);background:rgba(107,232,192,.08);}
.ph-lb-note{font-size:12px;color:var(--ph-dim);line-height:1.5;margin-bottom:12px;text-align:center;}
.ph-lb-rules{margin:0 0 12px;padding:9px 11px;border:1px solid rgba(107,232,192,.18);background:rgba(107,232,192,.05);border-radius:10px;color:var(--ph-dim);font-size:11px;line-height:1.5;text-align:center;}
.ph-lb-rules b{color:var(--ph-text);}
.ph-lb-hero{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-bottom:12px;}
.ph-lb-hero .ph-stat{padding:10px 11px;overflow:hidden;}
.ph-lb-hero .ph-stat-v{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:18px;}
.ph-lb-actions{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;margin-bottom:12px;}
.ph-lb-table-wrap{overflow:auto;border:1px solid var(--ph-line);border-radius:11px;background:var(--ph-panel-2);}
.ph-lb-table{width:100%;border-collapse:collapse;min-width:980px;font-size:11.5px;}
.ph-lb-table th,.ph-lb-table td{padding:9px 8px;border-bottom:1px solid var(--ph-line);text-align:left;vertical-align:middle;}
.ph-lb-table th{color:var(--ph-dim);font-size:9.5px;text-transform:uppercase;letter-spacing:.045em;position:sticky;top:0;background:var(--ph-panel-2);white-space:nowrap;}
.ph-lb-table td:first-child,.ph-lb-table th:first-child{width:38px;text-align:center;}
.ph-lb-table td.ph-lb-center{text-align:center;font-variant-numeric:tabular-nums;}
.ph-lb-table td.ph-lb-money,.ph-lb-table td.ph-lb-roi,.ph-lb-table td.ph-lb-luck{white-space:nowrap;font-variant-numeric:tabular-nums;}
.ph-lb-table td.ph-lb-money{color:var(--ph-gold);font-weight:800;}
.ph-lb-table td.ph-lb-luck{color:var(--ph-t5);font-weight:800;}
.ph-lb-roi-pos{color:var(--ph-t2);font-weight:800;}
.ph-lb-roi-neg{color:#ff9a9a;font-weight:800;}
.ph-lb-table tbody tr:last-child td{border-bottom:none;}
.ph-lb-table .ph-lb-me td{background:rgba(255,211,92,.07);color:var(--ph-text);font-weight:700;}
.ph-lb-table tbody tr:nth-child(1) td:first-child{color:var(--ph-gold);font-weight:900;}
.ph-lb-table tbody tr:nth-child(2) td:first-child{color:var(--ph-text);font-weight:900;}
.ph-lb-table tbody tr:nth-child(3) td:first-child{color:var(--ph-t5);font-weight:900;}
@media (max-width:760px){.ph-lb-hero{grid-template-columns:repeat(2,minmax(0,1fr));}}
@media (max-width:520px){.ph-lb-hero{grid-template-columns:1fr 1fr;}}
.ph-welcome-modal{align-items:center;justify-content:center;padding:14px;}
.ph-welcome-card{position:relative;z-index:1;width:min(100%,390px);padding:30px 24px 26px;border-radius:18px;background:var(--ph-panel);border:1px solid var(--ph-line);box-shadow:0 30px 80px -20px rgba(0,0,0,.82);text-align:center;color:var(--ph-text);animation:ph-modal-in .22s cubic-bezier(.2,.8,.2,1);}
.ph-welcome-icon{width:44px;height:44px;margin:0 auto 13px;border-radius:8px;display:flex;align-items:center;justify-content:center;border:2px solid #ff4f9c;background:linear-gradient(145deg,#ff4f9c,#ff7dc0);box-shadow:0 0 18px rgba(255,79,156,.22);}
.ph-welcome-icon span{width:20px;height:20px;border-radius:50%;background:#fff;color:#ff4f9c;font-size:15px;line-height:20px;font-weight:900;}
.ph-welcome-title{font-family:'Space Grotesk',system-ui,sans-serif;font-weight:800;font-size:26px;line-height:1.15;letter-spacing:.01em;}
.ph-welcome-sub{color:var(--ph-dim);font-size:12.5px;line-height:1.6;margin:10px 0 16px;}
.ph-welcome-input{display:block;width:100%;height:38px;padding:0 12px;border-radius:10px;background:#0e1020;color:var(--ph-text);border:1px solid var(--ph-gold);outline:none;font:inherit;font-size:13px;text-align:left;}
.ph-welcome-input:focus{box-shadow:0 0 0 2px rgba(255,211,92,.14);}
.ph-welcome-error{min-height:18px;color:#ff9a9a;font-size:11px;margin-top:6px;}
.ph-welcome-start{width:100%;height:44px;border:0;border-radius:10px;cursor:pointer;font:inherit;font-weight:800;font-size:14px;color:#0a0b16;background:linear-gradient(120deg,var(--ph-c1),var(--ph-c2));transition:transform .15s ease,filter .15s ease;}
.ph-welcome-start:hover{transform:translateY(-1px);filter:brightness(1.04);}
@media (max-width:560px){.ph-lb-hero{grid-template-columns:1fr;}.ph-welcome-card{padding:26px 18px 22px;}.ph-welcome-title{font-size:22px;}}



/* ============================================================
   ADD-ON CSS: Daily Hub
   ============================================================ */
/* ============================================================
     PokeHub Daily Hub Add-on v1
     Chỉ thêm giao diện + logic Daily, không sửa code gốc.
     ============================================================ */
  #ph-daily-btn{
    position:relative;
  }
  .ph-daily-badge{
    background:var(--ph-gold);
    color:#0a0b16;
    border-radius:20px;
    padding:1px 7px;
    font-size:11px;
    font-weight:800;
    font-variant-numeric:tabular-nums;
  }
  .ph-daily-badge.ph-daily-hot{
    background:linear-gradient(120deg,var(--ph-gold),#fff1a8);
    box-shadow:0 0 14px rgba(255,211,92,.25);
  }
  .ph-daily-wrap{
    display:flex;
    flex-direction:column;
    gap:14px;
  }
  .ph-daily-hero{
    display:grid;
    grid-template-columns:1.15fr .85fr;
    gap:10px;
  }
  .ph-daily-card{
    background:var(--ph-panel-2);
    border:1px solid var(--ph-line);
    border-radius:13px;
    padding:14px;
  }
  .ph-daily-kicker{
    color:var(--ph-dim);
    font-size:10px;
    font-weight:800;
    letter-spacing:.12em;
    text-transform:uppercase;
  }
  .ph-daily-title{
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-size:19px;
    font-weight:800;
    line-height:1.2;
    margin-top:4px;
  }
  .ph-daily-sub{
    color:var(--ph-dim);
    font-size:11.5px;
    margin-top:4px;
    line-height:1.45;
  }
  .ph-daily-streak{
    display:flex;
    align-items:flex-end;
    justify-content:space-between;
    gap:10px;
  }
  .ph-daily-streak-num{
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-size:30px;
    font-weight:800;
    line-height:1;
    color:var(--ph-gold);
  }
  .ph-daily-streak-label{
    color:var(--ph-text);
    font-size:12px;
    font-weight:700;
    margin-top:4px;
  }
  .ph-daily-streak-bar{
    height:7px;
    border-radius:99px;
    overflow:hidden;
    background:#0d1020;
    border:1px solid var(--ph-line);
    margin-top:11px;
  }
  .ph-daily-streak-fill{
    height:100%;
    width:0;
    background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));
    border-radius:inherit;
    transition:width .3s ease;
  }
  .ph-daily-reset{
    color:var(--ph-dim);
    font-size:10.5px;
    margin-top:7px;
    font-variant-numeric:tabular-nums;
  }
  .ph-daily-pack{
    position:relative;
    overflow:hidden;
    background:
      radial-gradient(260px 120px at 0% 0%,rgba(74,127,255,.15),transparent 65%),
      radial-gradient(220px 120px at 100% 100%,rgba(107,232,192,.11),transparent 70%),
      var(--ph-panel-2);
    border:1px solid rgba(255,211,92,.28);
  }
  .ph-daily-pack::after{
    content:'';
    position:absolute;
    inset:auto -30px -55px auto;
    width:140px;
    height:140px;
    border-radius:50%;
    background:rgba(255,211,92,.06);
    pointer-events:none;
  }
  .ph-daily-pack-top{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
  }
  .ph-daily-pack-name{
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-size:17px;
    font-weight:800;
  }
  .ph-daily-pack-status{
    font-size:10.5px;
    font-weight:800;
    padding:4px 8px;
    border-radius:99px;
    color:var(--ph-gold);
    background:rgba(255,211,92,.09);
    border:1px solid rgba(255,211,92,.22);
  }
  .ph-daily-pack-status.ph-claimed{
    color:var(--ph-t2);
    background:rgba(107,232,192,.08);
    border-color:rgba(107,232,192,.20);
  }
  .ph-daily-pack-desc{
    color:var(--ph-dim);
    font-size:11.5px;
    line-height:1.5;
    margin-top:5px;
  }
  .ph-daily-pack-action{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    margin-top:13px;
  }
  .ph-daily-pack-countdown{
    color:var(--ph-dim);
    font-size:10.5px;
    font-variant-numeric:tabular-nums;
  }
  .ph-daily-pack-btn{
    min-width:164px;
    border:none;
    border-radius:10px;
    padding:10px 14px;
    background:linear-gradient(120deg,var(--ph-c1),var(--ph-c2));
    color:#0a0b16;
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-size:12.5px;
    font-weight:800;
    cursor:pointer;
    transition:transform .15s ease,filter .15s ease,opacity .15s ease;
  }
  .ph-daily-pack-btn:hover{transform:translateY(-2px);filter:brightness(1.05);}
  .ph-daily-pack-btn:disabled{opacity:.48;cursor:not-allowed;transform:none;filter:none;}
  .ph-daily-mission-head{
    display:flex;
    align-items:flex-end;
    justify-content:space-between;
    gap:10px;
    margin-bottom:9px;
  }
  .ph-daily-mission-title{
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-size:16px;
    font-weight:800;
  }
  .ph-daily-mission-meta{
    color:var(--ph-dim);
    font-size:10.5px;
  }
  .ph-daily-mission-list{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:9px;
  }
  .ph-daily-mission{
    background:var(--ph-panel-2);
    border:1px solid var(--ph-line);
    border-radius:11px;
    padding:12px;
  }
  .ph-daily-mission.ph-done{
    border-color:rgba(107,232,192,.35);
    box-shadow:0 0 0 1px rgba(107,232,192,.06) inset;
  }
  .ph-daily-mission-top{
    display:flex;
    align-items:flex-start;
    justify-content:space-between;
    gap:8px;
  }
  .ph-daily-mission-name{
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-weight:750;
    font-size:12.5px;
    line-height:1.35;
  }
  .ph-daily-mission-reward{
    color:var(--ph-gold);
    font-size:10px;
    font-weight:800;
    white-space:nowrap;
  }
  .ph-daily-mission-desc{
    color:var(--ph-dim);
    font-size:10.5px;
    line-height:1.45;
    margin-top:4px;
    min-height:31px;
  }
  .ph-daily-mission-progress{
    display:flex;
    align-items:center;
    gap:7px;
    margin-top:10px;
  }
  .ph-daily-mission-track{
    flex:1;
    height:6px;
    background:#0d1020;
    border:1px solid var(--ph-line);
    border-radius:99px;
    overflow:hidden;
  }
  .ph-daily-mission-fill{
    display:block;
    height:100%;
    width:0;
    background:linear-gradient(90deg,var(--ph-c1),var(--ph-c2));
    border-radius:inherit;
    transition:width .25s ease;
  }
  .ph-daily-mission.ph-done .ph-daily-mission-fill{
    background:var(--ph-t2);
  }
  .ph-daily-mission-value{
    flex:0 0 auto;
    color:var(--ph-text);
    font-size:10px;
    font-variant-numeric:tabular-nums;
  }
  .ph-daily-mission-status{
    color:var(--ph-dim);
    font-size:10px;
    margin-top:7px;
  }
  .ph-daily-mission.ph-done .ph-daily-mission-status{
    color:var(--ph-t2);
    font-weight:700;
  }
  .ph-daily-bottom{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    flex-wrap:wrap;
  }
  .ph-daily-points{
    color:var(--ph-dim);
    font-size:11px;
  }
  .ph-daily-points b{
    color:var(--ph-gold);
    font-family:'Space Grotesk',system-ui,sans-serif;
    font-size:15px;
  }
  .ph-daily-note{
    color:var(--ph-dim);
    font-size:10.5px;
    line-height:1.45;
    text-align:right;
  }
  .ph-daily-alert{
    padding:9px 11px;
    border-radius:10px;
    background:rgba(255,211,92,.07);
    border:1px solid rgba(255,211,92,.18);
    color:var(--ph-gold);
    font-size:11px;
    line-height:1.45;
  }
  .ph-daily-alert.ph-ok{
    background:rgba(107,232,192,.07);
    border-color:rgba(107,232,192,.18);
    color:var(--ph-t2);
  }
  @media (max-width:700px){
    .ph-daily-hero{grid-template-columns:1fr;}
    .ph-daily-mission-list{grid-template-columns:1fr;}
    .ph-daily-pack-action{align-items:stretch;flex-direction:column;}
    .ph-daily-pack-btn{width:100%;}
    .ph-daily-note{text-align:left;}
  }
</style>

<script type="text/plain" id="phsrc">KGZ1bmN0aW9uKCl7CiAgInVzZSBzdHJpY3QiOwogIHZhciByb290ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bva2VodWItYXBwJyk7CiAgaWYgKCFyb290IHx8IHJvb3QuZGF0YXNldC5ib290ZWQpIHJldHVybjsKICByb290LmRhdGFzZXQuYm9vdGVkID0gJzEnOwoKICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignZXJyb3InLCBmdW5jdGlvbihlKXsKICAgIHZhciBzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3BoLXN0YXR1cycpOwogICAgaWYgKHMgJiYgIXMuZGF0YXNldC5waEVycm9yU2hvd24pewogICAgICBzLmRhdGFzZXQucGhFcnJvclNob3duID0gJzEnOwogICAgICBzLmhpZGRlbiA9IGZhbHNlOwogICAgICBzLnRleHRDb250ZW50ID0gJ0zhu5dpIHThuq1wIGzhu4duaCBj4bunYSB3aWRnZXQ6ICcgKyAoZS5tZXNzYWdlIHx8ICdraMO0bmcgcsO1IGzhu5dpJykgKyAnLiBIw6N5IG3hu58gY29uc29sZSBj4bunYSB0csOsbmggZHV54buHdCDEkeG7gyB4ZW0gY2hpIHRp4bq/dC4nOwogICAgfQogIH0pOwoKICB2YXIgQVBJID0gImh0dHBzOi8vYXBpLnRjZ2RleC5uZXQvdjIvZW4iOwogIHZhciBRVUFMSVRZID0gImhpZ2gud2VicCI7CiAgdmFyIFNFUklFU19JRFMgPSBbIm1lIiwgInRjZ3AiLCAic3YiLCAiYmFzZSIsICJneW0iLCAibmVvIiwgImVjYXJkIiwgImV4IiwgImRwIiwgInBsIiwgImhnc3MiLCAiYnciLCAieHkiLCAic20iLCAic3dzaCJdOwogIHZhciBTRVJJRVNfUFJJT1JJVFkgPSBbIm1lIiwgInN2IiwgInRjZ3AiLCAic3dzaCIsICJzbSIsICJ4eSIsICJidyIsICJoZ3NzIiwgInBsIiwgImRwIiwgImV4IiwgImVjYXJkIiwgIm5lbyIsICJneW0iLCAiYmFzZSJdOwogIC8vIMavdSB0acOqbiBwYWNrIMSRYW5nIG7Ds25nIC8gbeG7m2kgdHLDqm4gxJHhuqd1LCBuaMawbmcgY2jhu4kgZMO5bmcgY8OhYyBi4buZIGPDsyBk4buvIGxp4buHdQogIC8vIHbDoCDhuqNuaCDhu5VuIMSR4buLbmggdHJvbmcgVENHZGV4LiBNRVAgQmxhY2sgU3RhciBQcm9tb3MgYuG7iyBsb+G6oWkga2jhu49pIGRhbmggc8OhY2gKICAvLyB2w6wgYXNzZXQgbG9nbyBraMO0bmcgY8OzIHRyw6puIFRDR2RleC4gNCBwYWNrIC8gaMOgbmcgduG6q24gZ2nhu68gbmd1ecOqbi4KICB2YXIgRElTUExBWV9TRVRfT1JERVIgPSBbCiAgICAiMzB0aCBDZWxlYnJhdGlvbiIsCiAgICAiUG9rw6ltb24gR08iLAogICAgIlBoYW50YXNtYWwgRmxhbWVzIiwKICAgICJQaXRjaCBCbGFjayIsCiAgICAiQ2hhb3MgUmlzaW5nIiwKICAgICJNZWdhIEV2b2x1dGlvbiIsCiAgICAiQXNjZW5kZWQgSGVyb2VzIiwKICAgICJXaGl0ZSBGbGFyZSIsCiAgICAiQmxhY2sgQm9sdCIKICBdOwogIHZhciBGRUFUVVJFRF9TRVRfSURTID0gWyJtNmEiLCAibWUwNSIsICJzd3NoMTAiLCAic3YxMXciLCAic3YxMWIiXTsgLy8gUGl0Y2ggQmxhY2ssIFBva8OpbW9uIEdPLCBXaGl0ZSBGbGFyZSwgQmxhY2sgQm9sdAogIHZhciBFVVJfVVNEID0gMS4wODsKICB2YXIgREVGQVVMVF9QQUNLX1BSSUNFID0gNC40OTsKCiAgLy8gLS0tLS0tLS0tLSBUw4pOIFRSQUlORVIgKyBCWEggUkVBTFRJTUUgLS0tLS0tLS0tLQogIC8vIETDoW4gRmlyZWJhc2UgUmVhbHRpbWUgRGF0YWJhc2UgY29uZmlnIGPhu6dhIGLhuqFuIHbDoG8gxJHDonkgxJHhu4MgYuG6rXQgQlhIIGdp4buvYSBOSEnhu4BVIE5Hxq/hu5xJLgogIC8vIMSQ4buDIHRy4buRbmcgdGjDrCBwYWNrIHbhuqtuIGNo4bqheSBiw6xuaCB0aMaw4budbmc7IEJYSCBjaOG7iSBoaeG7h24gdHLhuqFuZyB0aMOhaSBjaMawYSBj4bqldSBow6xuaC4KICB2YXIgUEhfTEVBREVSQk9BUkRfQ09ORklHID0gewogICAgYXBpS2V5OiAnJywKICAgIGF1dGhEb21haW46ICcnLAogICAgZGF0YWJhc2VVUkw6ICcnLAogICAgcHJvamVjdElkOiAnJywKICAgIGFwcElkOiAnJwogIH07CiAgdmFyIFBIX1RSQUlORVJfS0VZID0gJ3Bva2VodWIudHJhaW5lci5uYW1lLnYxJzsKICB2YXIgUEhfTEJfVUlEX0tFWSA9ICdwb2tlaHViLmxlYWRlcmJvYXJkLnVpZC52MSc7CiAgdmFyIFBIX0xCX1BBVEggPSAncG9rZWh1YkxlYWRlcmJvYXJkL3YxL3BsYXllcnMnOwogIHZhciBwaExiID0gewogICAgZmlyZWJhc2VBcHA6bnVsbCwgZGI6bnVsbCwgcGxheWVyc1JlZjpudWxsLCBjb25uZWN0ZWRSZWY6bnVsbCwKICAgIGNvbm5lY3RlZDpmYWxzZSwgcmVhZHk6ZmFsc2UsIGxvYWRpbmc6ZmFsc2UsIHVuc3ViOm51bGwsIHVpZDpudWxsLAogICAgX3NuYXBzaG90Om51bGwsIF9zZGtQcm9taXNlOm51bGwKICB9OwoKICBmdW5jdGlvbiBzZXJpZXNQcmlvcml0eVJhbmsoc2VyaWVzSWQpewogICAgdmFyIGlkeCA9IFNFUklFU19QUklPUklUWS5pbmRleE9mKHNlcmllc0lkKTsKICAgIHJldHVybiBpZHggPT09IC0xID8gU0VSSUVTX1BSSU9SSVRZLmxlbmd0aCA6IGlkeDsKICB9CgogIHZhciBQQUxFVFRFUyA9IFsKICAgIFsiI2ZmNmI0YSIsIiM0YTdmZmYiXSxbIiM2YmU4YzAiLCIjYjc5Y2ZmIl0sWyIjNmM4Y2ZmIiwiI2ZmN2ZjNCJdLFsiI2ZmYjg0YSIsIiNmZjZiYjAiXSwKICAgIFsiIzRhZDBmZiIsIiM4ZGZmNmIiXSxbIiNjOTZiZmYiLCIjZmY5YTRhIl0sWyIjNGFmZmIwIiwiIzRhN2ZmZiJdLFsiI2ZmNGE3YSIsIiNmZmQzNWMiXQogIF07CiAgZnVuY3Rpb24gcGFsZXR0ZUZvcihpZCl7CiAgICB2YXIgaCA9IDA7CiAgICBmb3IgKHZhciBpPTA7aTxpZC5sZW5ndGg7aSsrKSBoID0gKGgqMzEgKyBpZC5jaGFyQ29kZUF0KGkpKSA+Pj4gMDsKICAgIHJldHVybiBQQUxFVFRFU1toICUgUEFMRVRURVMubGVuZ3RoXTsKICB9CgogIC8vIFRDR2RleCBoaeG7h24ga2jDtG5nIGPDsyBhc3NldCBsb2dvIGNobyBNRVAgQmxhY2sgU3RhciBQcm9tb3MuIETDuW5nIGxvZ28gU1ZHCiAgLy8gbuG7mWkgdHV54bq/biBsw6BtIGZhbGxiYWNrIMSR4buDIGNhcmQgduG6q24gY8OzIGjDrG5oIG5o4bqtbiBkaeG7h24gdGhheSB2w6wgY2jhu4kgaGnhu4duIGNo4buvIE0uCiAgdmFyIEZBTExCQUNLX1NFVF9MT0dPUyA9IHsKICAgIC8vIExvZ28gMzB0aCBDZWxlYnJhdGlvbiAoUE5HIHRyb25nIHN14buRdCkg4oCUIGTDuW5nIHRy4buxYyB0aeG6v3AgdOG7qyBCdWxiYWdhcmRlbiBBcmNoaXZlcwogICAgLy8gdsOsIGFzc2V0IGxvZ28gY+G7p2EgVENHZGV4IGtow7RuZyBwaOG6o2kgbMO6YyBuw6BvIGPFqW5nIGPDsyBVUkwgY8O0bmcga2hhaSDhu5VuIMSR4buLbmguCiAgICBtNmE6ICdodHRwczovL2FyY2hpdmVzLmJ1bGJhZ2FyZGVuLm5ldC9tZWRpYS91cGxvYWQvZC9kOS8zMHRoX0NlbGVicmF0aW9uX0xvZ29fRU4ucG5nJywKICAgIG1lcDogJ2RhdGE6aW1hZ2Uvc3ZnK3htbDtjaGFyc2V0PVVURi04LCcgKyBlbmNvZGVVUklDb21wb25lbnQoCiAgICAgICc8c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiB2aWV3Qm94PVwiMCAwIDUyMCAxODBcIj4nICsKICAgICAgJzxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD1cImdcIiB4MT1cIjBcIiB5MT1cIjBcIiB4Mj1cIjFcIiB5Mj1cIjFcIj48c3RvcCBzdG9wLWNvbG9yPVwiI2ZmOGE2NVwiLz48c3RvcCBvZmZzZXQ9XCIxXCIgc3RvcC1jb2xvcj1cIiM3ZDdjZmZcIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+JyArCiAgICAgICc8cmVjdCB4PVwiOFwiIHk9XCI4XCIgd2lkdGg9XCI1MDRcIiBoZWlnaHQ9XCIxNjRcIiByeD1cIjM4XCIgZmlsbD1cInVybCgjZylcIiBzdHJva2U9XCIjMTExMzI2XCIgc3Ryb2tlLXdpZHRoPVwiMTZcIi8+JyArCiAgICAgICc8dGV4dCB4PVwiMjYwXCIgeT1cIjExM1wiIHRleHQtYW5jaG9yPVwibWlkZGxlXCIgZm9udC1mYW1pbHk9XCJBcmlhbCwgc2Fucy1zZXJpZlwiIGZvbnQtc2l6ZT1cIjgyXCIgZm9udC13ZWlnaHQ9XCI5MDBcIiBmb250LXN0eWxlPVwiaXRhbGljXCIgZmlsbD1cIiNmZmZcIiBzdHJva2U9XCIjMTExMzI2XCIgc3Ryb2tlLXdpZHRoPVwiMTRcIiBwYWludC1vcmRlcj1cInN0cm9rZVwiPk1FUDwvdGV4dD4nICsKICAgICAgJzwvc3ZnPicKICAgICkKICB9OwoKICAvLyDhuqJuaCBu4buBbiAvIOG6o25oIGLDrGEgcmnDqm5nIGNobyAzMHRoIENlbGVicmF0aW9uLgogIC8vIETDuW5nIGtleSB2aXN1YWwgY2jDrW5oIHRo4bupYzsgbmjhuq1uIGRp4buHbiB0aGVvIGPhuqMgSUQgbOG6q24gdMOqbiDEkeG7gyBraMO0bmcgcGjhu6UgdGh14buZYwogIC8vIHZp4buHYyBBUEkvc2VyaWVzIHRy4bqjIHbhu4EgYmnhur9uIHRo4buDIElEIG7DoG8gY2hvIHNldCBt4bubaS4KICB2YXIgVEhJUlRJRVRIX0JHID0gJ2h0dHBzOi8vd3d3LjMwdGgucG9rZW1vbi1jYXJkLmNvbS9pbWFnZXMvbTZhL202YV9rdl8xZTI2YzgwMi53ZWJwP3Y9MjAyNjEwMDQnOwogIHZhciBGQUxMQkFDS19TRVRfQkFDS0dST1VORFMgPSB7CiAgICBtNmE6IFRISVJUSUVUSF9CRwogIH07CgogIGZ1bmN0aW9uIGlzMzB0aENlbGVicmF0aW9uU2V0KHNldCl7CiAgICBpZiAoIXNldCkgcmV0dXJuIGZhbHNlOwogICAgdmFyIGlkID0gU3RyaW5nKHNldC5pZCB8fCAnJykudHJpbSgpLnRvTG93ZXJDYXNlKCk7CiAgICB2YXIgbmFtZSA9IFN0cmluZyhzZXQubmFtZSB8fCAnJykudHJpbSgpLnRvTG93ZXJDYXNlKCk7CiAgICByZXR1cm4gaWQgPT09ICdtNmEnIHx8IG5hbWUgPT09ICczMHRoIGNlbGVicmF0aW9uJyB8fCBuYW1lLmluZGV4T2YoJzMwdGggY2VsZWJyYXRpb24nKSAhPT0gLTE7CiAgfQoKICBmdW5jdGlvbiBzZXRMb2dvVXJsKHNldCl7CiAgICBpZiAoIXNldCkgcmV0dXJuICcnOwogICAgLy8gS2V5IHZpc3VhbCDEkcOjIGPDsyBz4bq1biBsb2dvIDMwdGg7IGtow7RuZyBjaOG7k25nIHRow6ptIGxvZ28gbmdvw6BpLgogICAgaWYgKGlzMzB0aENlbGVicmF0aW9uU2V0KHNldCkpIHJldHVybiAnJzsKICAgIGlmIChGQUxMQkFDS19TRVRfTE9HT1NbU3RyaW5nKHNldC5pZCB8fCAnJykudG9Mb3dlckNhc2UoKV0pewogICAgICByZXR1cm4gRkFMTEJBQ0tfU0VUX0xPR09TW1N0cmluZyhzZXQuaWQgfHwgJycpLnRvTG93ZXJDYXNlKCldOwogICAgfQogICAgaWYgKHNldC5sb2dvKSByZXR1cm4gc2V0LmxvZ28gKyAnLndlYnAnOwogICAgcmV0dXJuICcnOwogIH0KCiAgZnVuY3Rpb24gc2V0QmFja2dyb3VuZFVybChzZXQpewogICAgaWYgKCFzZXQpIHJldHVybiAnJzsKICAgIGlmIChpczMwdGhDZWxlYnJhdGlvblNldChzZXQpKSByZXR1cm4gVEhJUlRJRVRIX0JHOwogICAgdmFyIGlkID0gU3RyaW5nKHNldC5pZCB8fCAnJykudHJpbSgpLnRvTG93ZXJDYXNlKCk7CiAgICBpZiAoRkFMTEJBQ0tfU0VUX0JBQ0tHUk9VTkRTW2lkXSkgcmV0dXJuIEZBTExCQUNLX1NFVF9CQUNLR1JPVU5EU1tpZF07CiAgICByZXR1cm4gJyc7CiAgfQoKICBmdW5jdGlvbiBzZXRQYWNrQXJ0VXJsKHNldCwgZnVsbFNldCl7CiAgICBpZiAoIXNldCAmJiAhZnVsbFNldCkgcmV0dXJuICcnOwogICAgdmFyIHMgPSBmdWxsU2V0IHx8IHNldCB8fCB7fTsKICAgIGlmIChpczMwdGhDZWxlYnJhdGlvblNldChzKSB8fCBpczMwdGhDZWxlYnJhdGlvblNldChzZXQpKSByZXR1cm4gVEhJUlRJRVRIX0JHOwogICAgdmFyIGNhcmRzID0gQXJyYXkuaXNBcnJheShzLmNhcmRzKSA/IHMuY2FyZHMgOiBbXTsKICAgIGZvciAodmFyIGk9MDtpPGNhcmRzLmxlbmd0aDtpKyspewogICAgICBpZiAoY2FyZHNbaV0gJiYgY2FyZHNbaV0uaW1hZ2UpIHJldHVybiBjYXJkc1tpXS5pbWFnZSArICcvJyArIFFVQUxJVFk7CiAgICB9CiAgICBpZiAocy5sb2dvKSByZXR1cm4gU3RyaW5nKHMubG9nbykucmVwbGFjZSgvXC53ZWJwJC9pLCcnKSArICcud2VicCc7CiAgICBpZiAoc2V0ICYmIHNldC5sb2dvKSByZXR1cm4gU3RyaW5nKHNldC5sb2dvKS5yZXBsYWNlKC9cLndlYnAkL2ksJycpICsgJy53ZWJwJzsKICAgIHJldHVybiBzZXRCYWNrZ3JvdW5kVXJsKHMpIHx8ICcnOwogIH0KCiAgZnVuY3Rpb24gc2V0UGFja0xvZ29Vcmwoc2V0LCBmdWxsU2V0KXsKICAgIHZhciBzID0gZnVsbFNldCB8fCBzZXQgfHwge307CiAgICBpZiAoaXMzMHRoQ2VsZWJyYXRpb25TZXQocykgfHwgaXMzMHRoQ2VsZWJyYXRpb25TZXQoc2V0KSkgcmV0dXJuICcnOwogICAgdmFyIGlkID0gU3RyaW5nKHMuaWQgfHwgKHNldCAmJiBzZXQuaWQpIHx8ICcnKS50b0xvd2VyQ2FzZSgpOwogICAgaWYgKEZBTExCQUNLX1NFVF9MT0dPU1tpZF0pIHJldHVybiBGQUxMQkFDS19TRVRfTE9HT1NbaWRdOwogICAgaWYgKHMubG9nbykgcmV0dXJuIFN0cmluZyhzLmxvZ28pLnJlcGxhY2UoL1wud2VicCQvaSwnJykgKyAnLndlYnAnOwogICAgaWYgKHNldCAmJiBzZXQubG9nbykgcmV0dXJuIFN0cmluZyhzZXQubG9nbykucmVwbGFjZSgvXC53ZWJwJC9pLCcnKSArICcud2VicCc7CiAgICByZXR1cm4gJyc7CiAgfQoKICBmdW5jdGlvbiBlbnN1cmVQYWNrVmlzdWFsKCl7CiAgICBpZiAoIWVscy5wYWNrKSByZXR1cm47CiAgICBpZiAoZWxzLnBhY2tBcnQgJiYgZWxzLnBhY2tBcnQucGFyZW50Tm9kZT09PWVscy5wYWNrKSByZXR1cm47CgogICAgdmFyIGFydD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTsKICAgIGFydC5jbGFzc05hbWU9J3BoLXBhY2stYXJ0JzsKICAgIGFydC5hbHQ9Jyc7CiAgICBhcnQuZGVjb2Rpbmc9J2FzeW5jJzsKICAgIGFydC5sb2FkaW5nPSdlYWdlcic7CiAgICBhcnQuc2V0QXR0cmlidXRlKCdhcmlhLWhpZGRlbicsJ3RydWUnKTsKCiAgICB2YXIgc2hhZGU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOwogICAgc2hhZGUuY2xhc3NOYW1lPSdwaC1wYWNrLWFydC1zaGFkZSc7CiAgICBzaGFkZS5zZXRBdHRyaWJ1dGUoJ2FyaWEtaGlkZGVuJywndHJ1ZScpOwoKICAgIHZhciBsb2dvPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpOwogICAgbG9nby5jbGFzc05hbWU9J3BoLXBhY2stbG9nbyc7CiAgICBsb2dvLmFsdD0nJzsKICAgIGxvZ28uZGVjb2Rpbmc9J2FzeW5jJzsKICAgIGxvZ28uc2V0QXR0cmlidXRlKCdhcmlhLWhpZGRlbicsJ3RydWUnKTsKCiAgICB2YXIgc2VhbD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICBzZWFsLmNsYXNzTmFtZT0ncGgtcGFjay1zZWFsJzsKICAgIHNlYWwuaW5uZXJIVE1MPSc8c3Bhbj5QT0vDiU1PTiBUQ0c8L3NwYW4+PGI+Qk9PU1RFUiBQQUNLPC9iPic7CiAgICBzZWFsLnNldEF0dHJpYnV0ZSgnYXJpYS1oaWRkZW4nLCd0cnVlJyk7CgogICAgdmFyIHNlcmllcz1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICBzZXJpZXMuY2xhc3NOYW1lPSdwaC1wYWNrLXNlcmllcyc7CiAgICBzZXJpZXMudGV4dENvbnRlbnQ9J1RSQURJTkcgQ0FSRCBHQU1FJzsKICAgIHNlcmllcy5zZXRBdHRyaWJ1dGUoJ2FyaWEtaGlkZGVuJywndHJ1ZScpOwoKICAgIHZhciBzaGluZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICBzaGluZS5jbGFzc05hbWU9J3BoLXBhY2stc2hpbmUnOwogICAgc2hpbmUuc2V0QXR0cmlidXRlKCdhcmlhLWhpZGRlbicsJ3RydWUnKTsKCiAgICBlbHMucGFjay5pbnNlcnRCZWZvcmUoYXJ0LCBlbHMucGFjay5maXJzdENoaWxkKTsKICAgIGVscy5wYWNrLmFwcGVuZENoaWxkKHNoYWRlKTsKICAgIGVscy5wYWNrLmFwcGVuZENoaWxkKGxvZ28pOwogICAgZWxzLnBhY2suYXBwZW5kQ2hpbGQoc2VhbCk7CiAgICBlbHMucGFjay5hcHBlbmRDaGlsZChzZXJpZXMpOwogICAgZWxzLnBhY2suYXBwZW5kQ2hpbGQoc2hpbmUpOwogICAgZWxzLnBhY2tBcnQ9YXJ0OyBlbHMucGFja0xvZ289bG9nbzsgZWxzLnBhY2tTZWFsPXNlYWw7IGVscy5wYWNrU2VyaWVzPXNlcmllczsKICB9CgogIGZ1bmN0aW9uIGFwcGx5UGFja1Zpc3VhbChzZXQsIGZ1bGxTZXQpewogICAgZW5zdXJlUGFja1Zpc3VhbCgpOwogICAgaWYgKCFlbHMucGFjaykgcmV0dXJuOwogICAgdmFyIGFydFVybD1zZXRQYWNrQXJ0VXJsKHNldCwgZnVsbFNldCk7CiAgICB2YXIgbG9nb1VybD1zZXRQYWNrTG9nb1VybChzZXQsIGZ1bGxTZXQpOwogICAgdmFyIG5hbWU9KGZ1bGxTZXQgJiYgZnVsbFNldC5uYW1lKSB8fCAoc2V0ICYmIHNldC5uYW1lKSB8fCAnUG9rw6ltb24gVENHJzsKICAgIHZhciBpczMwPWlzMzB0aENlbGVicmF0aW9uU2V0KGZ1bGxTZXQgfHwgc2V0KTsKCiAgICBlbHMucGFjay5kYXRhc2V0LnBhY2tTZXQ9U3RyaW5nKChmdWxsU2V0ICYmIGZ1bGxTZXQuaWQpIHx8IChzZXQgJiYgc2V0LmlkKSB8fCAnJyk7CiAgICBlbHMucGFjay5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1waC1wYWNrLWFjY2VudCcsIGlzMzAgPyAnI2ZmZDY1YScgOiAncmdiYSg5MCwxOTAsMjU1LC45NSknKTsKICAgIGVscy5wYWNrTGFiZWwudGV4dENvbnRlbnQ9bmFtZTsKICAgIGVscy5wYWNrU2VyaWVzLnRleHRDb250ZW50PWlzMzAgPyAnMzBUSCBBTk5JVkVSU0FSWSDigKIgQk9PU1RFUiBQQUNLJyA6ICdUUkFESU5HIENBUkQgR0FNRSDigKIgQk9PU1RFUiBQQUNLJzsKCiAgICBlbHMucGFja0FydC5yZW1vdmVBdHRyaWJ1dGUoJ3NyYycpOwogICAgZWxzLnBhY2tBcnQuc3R5bGUuZGlzcGxheT0nbm9uZSc7CiAgICBpZiAoYXJ0VXJsKXsKICAgICAgZWxzLnBhY2tBcnQuc3JjPWFydFVybDsKICAgICAgZWxzLnBhY2tBcnQuc3R5bGUuZGlzcGxheT0nYmxvY2snOwogICAgICBlbHMucGFja0FydC5vbmVycm9yPWZ1bmN0aW9uKCl7CiAgICAgICAgaWYgKGVscy5wYWNrQXJ0KSBlbHMucGFja0FydC5zdHlsZS5kaXNwbGF5PSdub25lJzsKICAgICAgfTsKICAgIH0KCiAgICBlbHMucGFja0xvZ28ucmVtb3ZlQXR0cmlidXRlKCdzcmMnKTsKICAgIGVscy5wYWNrTG9nby5zdHlsZS5kaXNwbGF5PSdub25lJzsKICAgIGlmIChsb2dvVXJsICYmICFpczMwKXsKICAgICAgZWxzLnBhY2tMb2dvLnNyYz1sb2dvVXJsOwogICAgICBlbHMucGFja0xvZ28uc3R5bGUuZGlzcGxheT0nYmxvY2snOwogICAgICBlbHMucGFja0xvZ28ub25lcnJvcj1mdW5jdGlvbigpeyBpZiAoZWxzLnBhY2tMb2dvKSBlbHMucGFja0xvZ28uc3R5bGUuZGlzcGxheT0nbm9uZSc7IH07CiAgICB9CgogICAgZWxzLnBhY2suY2xhc3NMaXN0LnRvZ2dsZSgncGgtcGFjay1oYXMtYXJ0JywgISFhcnRVcmwpOwogICAgZWxzLnBhY2suY2xhc3NMaXN0LnRvZ2dsZSgncGgtcGFjay1pcy0zMHRoJywgaXMzMCk7CiAgfQoKCiAgLy8gLS0tLS0tLS0tLSB04buJIGzhu4cgcsO6dCB0aGVvIHThuqduZyDEkeG7mSBoaeG6v20gLS0tLS0tLS0tLQogIC8vIFRy4buNbmcgc+G7kSBUxq/GoE5HIMSQ4buQSSBjaG8gbeG7l2kgdOG6p25nICgwID0gdGjGsOG7nW5nIC4uIDYgPSBjaGFzZSkuIMSQw6J5IGNo4buJIGzDoAogIC8vIHjhuqVwIHjhu4kgdGhlbyBj4bqjbSBnacOhYyB0aOG7sWMgdOG6vyBt4bufIGfDs2kgKG3hu5dpIHThuqduZyBoaeG6v20gaMahbiB04bqnbmcgdHLGsOG7m2MKICAvLyBjaOG7q25nIDIuNeKAkzMuNSBs4bqnbiksIEtIw5RORyBwaOG6o2kgdOG7iSBs4buHIGluIOG6pW4gY2jDrW5oIHRo4bupYyBj4bunYSBQb2vDqW1vbiBUQ0cKICAvLyAoc+G7kSDEkcOzIGtow7RuZyBjw7RuZyBraGFpIHbDoCBraMOhYyBuaGF1IHRoZW8gdOG7q25nIGLhu5kpLiBN4bulYyB0acOqdSBsw6AgxJHhu4MKICAvLyBt4buZdCBsw6EgYuG6rWMgNiBraMO0bmcgY8OybiBjw7MgY8ahIGjhu5lpIHRyw7puZyBuZ2FuZyBi4bqxbmcgbeG7mXQgbMOhIGLhuq1jIDAgY2jhu4kKICAvLyB2w6wgY+G6oyBoYWkgxJHhu4F1IGzDoCAibeG7mXQgSUQgdGjhursiIOKAlCB24bqlbiDEkeG7gSBj4bunYSBi4bqjbiBjxakuCiAgdmFyIFRJRVJfV0VJR0hUUyA9IFsxMDAsIDQyLCAxNCwgNC4yLCAxLjMsIDAuNDIsIDAuMTRdOwogIHZhciBSQVJJVFlfVElFUl9HUk9VUFMgPSBudWxsOyAgIC8vIFsgW2NodeG7l2kgxJHhu5kgaGnhur9tIHThuqduZyAwXSwgW3ThuqduZyAxXSwgLi4uIF0KICB2YXIgcmFyaXR5VGF4b25vbXlQcm9taXNlID0gbnVsbDsKCiAgdmFyIHN0YXRlID0geyBjdXJyZW50Om51bGwsIHBvb2w6W10sIHRpZXJQb29sczpudWxsLCBjYWNoZTpuZXcgTWFwKCksIG9wZW5pbmc6ZmFsc2UsIG11dGVkOmZhbHNlLCBsYXN0UmVzdWx0Om51bGwsIGV4cGFuc2lvbnM6W10sIG9wZW5TY3JvbGxZOm51bGwgfTsKICB2YXIgJCA9IGZ1bmN0aW9uKGlkKXsgcmV0dXJuIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKTsgfTsKICB2YXIgZWxzID0gewogICAgc3RhdHVzOiQoJ3BoLXN0YXR1cycpLCBncmlkOiQoJ3BoLWV4cC1ncmlkJyksCiAgICBwaWNrOiQoJ3BoLXNjcmVlbi1waWNrJyksIG9wZW46JCgncGgtc2NyZWVuLW9wZW4nKSwKICAgIGJhY2s6JCgncGgtYmFjay1idG4nKSwgcGFjazokKCdwaC1wYWNrJyksIHBhY2tMYWJlbDokKCdwaC1wYWNrLWxhYmVsJyksCiAgICBwYWNrQXJ0Om51bGwsIHBhY2tMb2dvOm51bGwsIHBhY2tTZWFsOm51bGwsIHBhY2tTZXJpZXM6bnVsbCwKICAgIGhpbnQ6JCgncGgtaGludCcpLCBlcnI6JCgncGgtb3Blbi1lcnInKSwgc3RhZ2U6JCgncGgtc3RhZ2UnKSwKICAgIGJveFJvdzokKCdwaC1ib3gtcm93JyksCiAgICBwcm9ncmVzczokKCdwaC1wcm9ncmVzcycpLCBwcm9ncmVzc0JhcjokKCdwaC1wcm9ncmVzcy1iYXInKSwgcHJvZ3Jlc3NUZXh0OiQoJ3BoLXByb2dyZXNzLXRleHQnKSwKICAgIHJldmVhbDokKCdwaC1yZXZlYWwnKSwgcm93OiQoJ3BoLWNhcmQtcm93JyksIGJhbm5lcjokKCdwaC1iYW5uZXInKSwKICAgIGx1Y2s6JCgncGgtbHVjaycpLCBsdWNrU2NvcmU6JCgncGgtbHVjay1zY29yZScpLAogICAgdG90YWw6JCgncGgtdG90YWwnKSwgdG90YWxWYWw6JCgncGgtdG90YWwtdmFsJyksIHF1aXA6JCgncGgtcXVpcCcpLAogICAgc2F2ZWQ6JCgncGgtc2F2ZWQnKSwKICAgIGNvbmZldHRpOiQoJ3BoLWNvbmZldHRpJyksCiAgICBtb2RhbDokKCdwaC1tb2RhbCcpLCBtb2RhbEJhY2tkcm9wOiQoJ3BoLW1vZGFsLWJhY2tkcm9wJyksCiAgICBtb2RhbENsb3NlOiQoJ3BoLW1vZGFsLWNsb3NlJyksIG1vZGFsQm9keTokKCdwaC1tb2RhbC1ib2R5JyksCiAgICBhZ2FpbjokKCdwaC1hZ2Fpbi1idG4nKSwgY2hhbmdlOiQoJ3BoLWNoYW5nZS1idG4nKSwgZmxhc2g6JCgncGgtZmxhc2gnKSwKICAgIHNoYXJlOiQoJ3BoLXNoYXJlLWJ0bicpLAogICAgbXV0ZTokKCdwaC1tdXRlLWJ0bicpLCBjb2xCdG46JCgncGgtY29sLWJ0bicpLCBjb2xDb3VudDokKCdwaC1jb2wtY291bnQnKSwKICAgIGNvbE1vZGFsOiQoJ3BoLWNvbC1tb2RhbCcpLCBjb2xCYWNrZHJvcDokKCdwaC1jb2wtYmFja2Ryb3AnKSwKICAgIGNvbENsb3NlOiQoJ3BoLWNvbC1jbG9zZScpLCBjb2xCb2R5OiQoJ3BoLWNvbC1ib2R5JyksCiAgICBjaGFsbGVuZ2VCdG46JCgncGgtY2hhbGxlbmdlLWJ0bicpLCBjaGFsbGVuZ2VDb3VudDokKCdwaC1jaGFsbGVuZ2UtY291bnQnKSwKICAgIGNoYWxsZW5nZU1vZGFsOiQoJ3BoLWNoYWxsZW5nZS1tb2RhbCcpLCBjaGFsbGVuZ2VCYWNrZHJvcDokKCdwaC1jaGFsbGVuZ2UtYmFja2Ryb3AnKSwKICAgIGNoYWxsZW5nZUNsb3NlOiQoJ3BoLWNoYWxsZW5nZS1jbG9zZScpLCBjaGFsbGVuZ2VCb2R5OiQoJ3BoLWNoYWxsZW5nZS1ib2R5JyksCiAgICBzaG9wQnRuOiQoJ3BoLXNob3AtYnRuJyksIHNob3BQb2ludHNCYWRnZTokKCdwaC1zaG9wLXBvaW50cy1iYWRnZScpLAogICAgc2hvcE1vZGFsOiQoJ3BoLXNob3AtbW9kYWwnKSwgc2hvcEJhY2tkcm9wOiQoJ3BoLXNob3AtYmFja2Ryb3AnKSwKICAgIHNob3BDbG9zZTokKCdwaC1zaG9wLWNsb3NlJyksIHNob3BCb2R5OiQoJ3BoLXNob3AtYm9keScpLAogICAgc2V0UHJvZ3Jlc3M6JCgncGgtc2V0LXByb2dyZXNzJyksIHNldFByb2dyZXNzVGl0bGU6JCgncGgtc2V0LXByb2dyZXNzLXRpdGxlJyksCiAgICBzZXRQcm9ncmVzc01ldGE6JCgncGgtc2V0LXByb2dyZXNzLW1ldGEnKSwgc2V0UHJvZ3Jlc3NCYXI6JCgncGgtc2V0LXByb2dyZXNzLWJhcicpLAogICAgc2V0UHJvZ3Jlc3NQY3Q6JCgncGgtc2V0LXByb2dyZXNzLXBjdCcpLAogICAgYm94OiQoJ3BoLWJveC1yZXN1bHQnKSwgYm94Qm9keTokKCdwaC1ib3gtYm9keScpLAogICAgYm94U2hhcmU6JCgncGgtYm94LXNoYXJlJyksIGJveEFnYWluOiQoJ3BoLWJveC1hZ2FpbicpLCBib3hDaGFuZ2U6JCgncGgtYm94LWNoYW5nZScpLAogICAgdG9hc3Q6JCgncGgtdG9hc3QnKSwKICAgIHdlbGNvbWVNb2RhbDokKCdwaC13ZWxjb21lLW1vZGFsJyksIHdlbGNvbWVJbnB1dDokKCdwaC10cmFpbmVyLW5hbWUnKSwKICAgIHdlbGNvbWVTdGFydDokKCdwaC13ZWxjb21lLXN0YXJ0JyksIHdlbGNvbWVFcnJvcjokKCdwaC13ZWxjb21lLWVycm9yJyksCiAgICBsYkJ0bjokKCdwaC1sZWFkZXJib2FyZC1idG4nKSwgbGJMaXZlOiQoJ3BoLWxlYWRlcmJvYXJkLWxpdmUnKSwKICAgIGxiTW9kYWw6JCgncGgtbGVhZGVyYm9hcmQtbW9kYWwnKSwgbGJCYWNrZHJvcDokKCdwaC1sZWFkZXJib2FyZC1iYWNrZHJvcCcpLAogICAgbGJDbG9zZTokKCdwaC1sZWFkZXJib2FyZC1jbG9zZScpLCBsYkJvZHk6JCgncGgtbGVhZGVyYm9hcmQtYm9keScpCiAgfTsKCiAgLy8gLS0tLS0tLS0tLSB0b2FzdCAtLS0tLS0tLS0tCiAgdmFyIHRvYXN0VGltZXIgPSAwOwogIGZ1bmN0aW9uIHRvYXN0KG1zZyl7CiAgICBlbHMudG9hc3QudGV4dENvbnRlbnQgPSBtc2c7CiAgICBlbHMudG9hc3QuY2xhc3NMaXN0LmFkZCgncGgtc2hvdycpOwogICAgY2xlYXJUaW1lb3V0KHRvYXN0VGltZXIpOwogICAgdG9hc3RUaW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24oKXsgZWxzLnRvYXN0LmNsYXNzTGlzdC5yZW1vdmUoJ3BoLXNob3cnKTsgfSwgMzIwMCk7CiAgfQoKICAvLyAtLS0tLS0tLS0tIFRSQUlORVIgTkFNRSArIEdMT0JBTCBMRUFERVJCT0FSRCAtLS0tLS0tLS0tCiAgZnVuY3Rpb24gcGhMb2FkVHJhaW5lck5hbWUoKXsKICAgIHZhciBuYW1lID0gJyc7CiAgICB0cnkgeyBuYW1lID0gd2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKFBIX1RSQUlORVJfS0VZKSB8fCAnJzsgfSBjYXRjaChlKXt9CiAgICByZXR1cm4gU3RyaW5nKG5hbWUpLnRyaW0oKTsKICB9CgogIGZ1bmN0aW9uIHBoU2FmZVRyYWluZXJOYW1lKG5hbWUpewogICAgdmFyIHMgPSBTdHJpbmcobmFtZSA9PSBudWxsID8gJycgOiBuYW1lKS5yZXBsYWNlKC9bPD5dL2csJycpLnJlcGxhY2UoL1xzKy9nLCcgJykudHJpbSgpOwogICAgaWYgKHMubGVuZ3RoID4gMTgpIHMgPSBzLnNsaWNlKDAsMTgpLnRyaW0oKTsKICAgIHJldHVybiBzOwogIH0KCiAgZnVuY3Rpb24gcGhTYXZlVHJhaW5lck5hbWUobmFtZSl7CiAgICB2YXIgcyA9IHBoU2FmZVRyYWluZXJOYW1lKG5hbWUpOwogICAgaWYgKCFzIHx8IHMubGVuZ3RoIDwgMikgcmV0dXJuIGZhbHNlOwogICAgdHJ5IHsgd2luZG93LmxvY2FsU3RvcmFnZS5zZXRJdGVtKFBIX1RSQUlORVJfS0VZLCBzKTsgfSBjYXRjaChlKXsgcmV0dXJuIGZhbHNlOyB9CiAgICByZXR1cm4gdHJ1ZTsKICB9CgogIGZ1bmN0aW9uIHBoR2V0TGJVaWQoKXsKICAgIGlmIChwaExiLnVpZCkgcmV0dXJuIHBoTGIudWlkOwogICAgdmFyIHVpZCA9ICcnOwogICAgdHJ5IHsgdWlkID0gd2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKFBIX0xCX1VJRF9LRVkpIHx8ICcnOyB9IGNhdGNoKGUpe30KICAgIGlmICghdWlkKXsKICAgICAgaWYgKHdpbmRvdy5jcnlwdG8gJiYgd2luZG93LmNyeXB0by5yYW5kb21VVUlEKSB1aWQgPSB3aW5kb3cuY3J5cHRvLnJhbmRvbVVVSUQoKTsKICAgICAgZWxzZSB1aWQgPSAncF8nICsgTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMikgKyAnXycgKyBEYXRlLm5vdygpLnRvU3RyaW5nKDM2KTsKICAgICAgdHJ5IHsgd2luZG93LmxvY2FsU3RvcmFnZS5zZXRJdGVtKFBIX0xCX1VJRF9LRVksIHVpZCk7IH0gY2F0Y2goZSl7fQogICAgfQogICAgcGhMYi51aWQgPSB1aWQ7CiAgICByZXR1cm4gdWlkOwogIH0KCiAgZnVuY3Rpb24gcGhMYklzQ29uZmlndXJlZCgpewogICAgcmV0dXJuICEhKFBIX0xFQURFUkJPQVJEX0NPTkZJRyAmJiBQSF9MRUFERVJCT0FSRF9DT05GSUcuZGF0YWJhc2VVUkwgJiYKICAgICAgU3RyaW5nKFBIX0xFQURFUkJPQVJEX0NPTkZJRy5kYXRhYmFzZVVSTCkuaW5kZXhPZignaHR0cCcpID09PSAwICYmCiAgICAgIFN0cmluZyhQSF9MRUFERVJCT0FSRF9DT05GSUcuZGF0YWJhc2VVUkwpLmluZGV4T2YoJ1lPVVJfJykgPT09IC0xKTsKICB9CgogIGZ1bmN0aW9uIHBoTGJTdGF0dXNUZXh0KCl7CiAgICBpZiAoIXBoTGJJc0NvbmZpZ3VyZWQoKSkgcmV0dXJuICdDaMawYSBj4bqldSBow6xuaCc7CiAgICBpZiAocGhMYi5sb2FkaW5nICYmICFwaExiLnJlYWR5KSByZXR1cm4gJ8SQYW5nIGvhur90IG7hu5Fp4oCmJzsKICAgIGlmIChwaExiLmNvbm5lY3RlZCkgcmV0dXJuICfil48gTElWRSc7CiAgICBpZiAocGhMYi5yZWFkeSkgcmV0dXJuICdN4bqldCBr4bq/dCBu4buRaSDCtyB0aOG7rSBs4bqhaSc7CiAgICByZXR1cm4gJ8SQYW5nIGvhur90IG7hu5Fp4oCmJzsKICB9CgogIGZ1bmN0aW9uIHBoTGJVcGRhdGVMaXZlQmFkZ2UoKXsKICAgIGlmICghZWxzLmxiTGl2ZSkgcmV0dXJuOwogICAgZWxzLmxiTGl2ZS50ZXh0Q29udGVudCA9IHBoTGJTdGF0dXNUZXh0KCk7CiAgICBlbHMubGJMaXZlLmNsYXNzTmFtZSA9ICdwaC1sYi1zdGF0dXMnICsgKHBoTGIuY29ubmVjdGVkID8gJyBwaC1sYi1vbmxpbmUnIDogJycpOwogICAgZWxzLmxiTGl2ZS50aXRsZSA9IHBoTGJTdGF0dXNUZXh0KCk7CiAgfQoKICBmdW5jdGlvbiBwaExiU2NyaXB0KHNyYyl7CiAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSxyZWplY3QpewogICAgICB2YXIgcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpOwogICAgICBzLnNyYyA9IHNyYzsgcy5hc3luYyA9IHRydWU7CiAgICAgIHMub25sb2FkID0gcmVzb2x2ZTsKICAgICAgcy5vbmVycm9yID0gZnVuY3Rpb24oKXsgcmVqZWN0KG5ldyBFcnJvcignS2jDtG5nIHThuqNpIMSRxrDhu6NjIHRoxrAgdmnhu4duIHJlYWx0aW1lJykpOyB9OwogICAgICBkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHMpOwogICAgfSk7CiAgfQoKICBmdW5jdGlvbiBwaExiRW5zdXJlRmlyZWJhc2UoKXsKICAgIGlmICghcGhMYklzQ29uZmlndXJlZCgpKSByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKCdDaMawYSBj4bqldSBow6xuaCBGaXJlYmFzZSBSZWFsdGltZSBEYXRhYmFzZScpKTsKICAgIGlmICh3aW5kb3cuZmlyZWJhc2UgJiYgd2luZG93LmZpcmViYXNlLmRhdGFiYXNlKSByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7CiAgICBpZiAocGhMYi5fc2RrUHJvbWlzZSkgcmV0dXJuIHBoTGIuX3Nka1Byb21pc2U7CiAgICBwaExiLmxvYWRpbmcgPSB0cnVlOwogICAgcGhMYlVwZGF0ZUxpdmVCYWRnZSgpOwogICAgcGhMYi5fc2RrUHJvbWlzZSA9IHBoTGJTY3JpcHQoJ2h0dHBzOi8vd3d3LmdzdGF0aWMuY29tL2ZpcmViYXNlanMvOC4xMC4xL2ZpcmViYXNlLWFwcC5qcycpCiAgICAgIC50aGVuKGZ1bmN0aW9uKCl7IHJldHVybiBwaExiU2NyaXB0KCdodHRwczovL3d3dy5nc3RhdGljLmNvbS9maXJlYmFzZWpzLzguMTAuMS9maXJlYmFzZS1kYXRhYmFzZS5qcycpOyB9KQogICAgICAudGhlbihmdW5jdGlvbigpeyBwaExiLmxvYWRpbmcgPSBmYWxzZTsgfSkKICAgICAgLmNhdGNoKGZ1bmN0aW9uKGVycil7IHBoTGIubG9hZGluZyA9IGZhbHNlOyB0aHJvdyBlcnI7IH0pOwogICAgcmV0dXJuIHBoTGIuX3Nka1Byb21pc2U7CiAgfQoKICBmdW5jdGlvbiBwaExiSW5pdCgpewogICAgaWYgKCFwaExiSXNDb25maWd1cmVkKCkpewogICAgICBwaExiVXBkYXRlTGl2ZUJhZGdlKCk7CiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZmFsc2UpOwogICAgfQogICAgcmV0dXJuIHBoTGJFbnN1cmVGaXJlYmFzZSgpLnRoZW4oZnVuY3Rpb24oKXsKICAgICAgaWYgKCF3aW5kb3cuZmlyZWJhc2UgfHwgIXdpbmRvdy5maXJlYmFzZS5kYXRhYmFzZSkgdGhyb3cgbmV3IEVycm9yKCdGaXJlYmFzZSBjaMawYSBz4bq1biBzw6BuZycpOwogICAgICBpZiAoIXBoTGIuZmlyZWJhc2VBcHApewogICAgICAgIHZhciBhcHBzID0gd2luZG93LmZpcmViYXNlLmFwcHMgfHwgW107CiAgICAgICAgaWYgKGFwcHMubGVuZ3RoKSBwaExiLmZpcmViYXNlQXBwID0gYXBwc1swXTsKICAgICAgICBlbHNlIHBoTGIuZmlyZWJhc2VBcHAgPSB3aW5kb3cuZmlyZWJhc2UuaW5pdGlhbGl6ZUFwcChQSF9MRUFERVJCT0FSRF9DT05GSUcpOwogICAgICB9CiAgICAgIGlmICghcGhMYi5kYikgcGhMYi5kYiA9IHBoTGIuZmlyZWJhc2VBcHAuZGF0YWJhc2UoKTsKICAgICAgaWYgKCFwaExiLnBsYXllcnNSZWYpIHBoTGIucGxheWVyc1JlZiA9IHBoTGIuZGIucmVmKFBIX0xCX1BBVEgpOwogICAgICBpZiAoIXBoTGIuY29ubmVjdGVkUmVmKXsKICAgICAgICBwaExiLmNvbm5lY3RlZFJlZiA9IHBoTGIuZGIucmVmKCcuaW5mby9jb25uZWN0ZWQnKTsKICAgICAgICBwaExiLmNvbm5lY3RlZFJlZi5vbigndmFsdWUnLCBmdW5jdGlvbihzbmFwKXsKICAgICAgICAgIHBoTGIuY29ubmVjdGVkID0gc25hcC52YWwoKSA9PT0gdHJ1ZTsKICAgICAgICAgIHBoTGJVcGRhdGVMaXZlQmFkZ2UoKTsKICAgICAgICAgIGlmIChlbHMubGJNb2RhbCAmJiAhZWxzLmxiTW9kYWwuaGlkZGVuKSBwaFJlbmRlckxlYWRlcmJvYXJkKCk7CiAgICAgICAgfSk7CiAgICAgIH0KICAgICAgcGhMYi5yZWFkeSA9IHRydWU7CiAgICAgIHBoTGJVcGRhdGVMaXZlQmFkZ2UoKTsKICAgICAgcmV0dXJuIHRydWU7CiAgICB9KS5jYXRjaChmdW5jdGlvbigpewogICAgICBwaExiLnJlYWR5ID0gZmFsc2U7CiAgICAgIHBoTGJVcGRhdGVMaXZlQmFkZ2UoKTsKICAgICAgcmV0dXJuIGZhbHNlOwogICAgfSk7CiAgfQoKICBmdW5jdGlvbiBwaExiQ3VycmVudFNuYXBzaG90KCl7CiAgICB2YXIgZCA9IGxvYWREQigpLCBzID0gY29sbGVjdGlvblN0YXRzKCk7CiAgICB2YXIgYmVzdFRpZXIgPSAwLCBiZXN0VmFsdWUgPSAwOwogICAgT2JqZWN0LmtleXMoZC5jYXJkcyB8fCB7fSkuZm9yRWFjaChmdW5jdGlvbihrKXsKICAgICAgdmFyIGUgPSBkLmNhcmRzW2tdOwogICAgICBiZXN0VGllciA9IE1hdGgubWF4KGJlc3RUaWVyLCBOdW1iZXIoZS50aWVyKSB8fCAwKTsKICAgICAgaWYgKHR5cGVvZiBlLnByaWNlID09PSAnbnVtYmVyJykgYmVzdFZhbHVlID0gTWF0aC5tYXgoYmVzdFZhbHVlLCBlLnByaWNlKTsKICAgIH0pOwogICAgcmV0dXJuIHsKICAgICAgbmFtZTogcGhMb2FkVHJhaW5lck5hbWUoKSB8fCAnVHJhaW5lcicsCiAgICAgIHBhY2tzOiBOdW1iZXIocy5wYWNrcykgfHwgMCwKICAgICAgY2FyZHM6IE51bWJlcihzLmNhcmRzKSB8fCAwLAogICAgICB1bmlxdWU6IE51bWJlcihzLnVuaXF1ZSkgfHwgMCwKICAgICAgYmVzdFRpZXI6IGJlc3RUaWVyLAogICAgICBiZXN0VmFsdWU6IE51bWJlcihiZXN0VmFsdWUudG9GaXhlZCgyKSksCiAgICAgIHVwZGF0ZWRBdDogd2luZG93LmZpcmViYXNlICYmIHdpbmRvdy5maXJlYmFzZS5kYXRhYmFzZSAmJiB3aW5kb3cuZmlyZWJhc2UuZGF0YWJhc2UuU2VydmVyVmFsdWUKICAgICAgICA/IHdpbmRvdy5maXJlYmFzZS5kYXRhYmFzZS5TZXJ2ZXJWYWx1ZS5USU1FU1RBTVAgOiBEYXRlLm5vdygpCiAgICB9OwogIH0KCiAgZnVuY3Rpb24gcGhMYlN5bmNTdGF0cygpewogICAgaWYgKCFwaExiSXNDb25maWd1cmVkKCkgfHwgIXBoTGIucGxheWVyc1JlZikgcmV0dXJuOwogICAgdmFyIG5hbWUgPSBwaExvYWRUcmFpbmVyTmFtZSgpOwogICAgaWYgKCFuYW1lKSByZXR1cm47CiAgICB2YXIgdWlkID0gcGhHZXRMYlVpZCgpOwogICAgdmFyIGRhdGEgPSBwaExiQ3VycmVudFNuYXBzaG90KCk7CiAgICBkYXRhLnVpZCA9IHVpZDsKICAgIHBoTGIucGxheWVyc1JlZi5jaGlsZCh1aWQpLnNldChkYXRhKS5jYXRjaChmdW5jdGlvbigpe30pOwogIH0KCiAgZnVuY3Rpb24gcGhMYlN0YXJ0TGlzdGVuZXIoKXsKICAgIGlmICghcGhMYi5wbGF5ZXJzUmVmIHx8IHBoTGIudW5zdWIpIHJldHVybjsKICAgIHBoTGIudW5zdWIgPSBmdW5jdGlvbigpe307CiAgICBwaExiLnBsYXllcnNSZWYub24oJ3ZhbHVlJywgZnVuY3Rpb24oc25hcHNob3QpewogICAgICBwaExiLl9zbmFwc2hvdCA9IHNuYXBzaG90OwogICAgICBpZiAoZWxzLmxiTW9kYWwgJiYgIWVscy5sYk1vZGFsLmhpZGRlbikgcGhSZW5kZXJMZWFkZXJib2FyZCgpOwogICAgfSwgZnVuY3Rpb24oKXsKICAgICAgcGhMYi5fc25hcHNob3QgPSBudWxsOwogICAgICBpZiAoZWxzLmxiTW9kYWwgJiYgIWVscy5sYk1vZGFsLmhpZGRlbikgcGhSZW5kZXJMZWFkZXJib2FyZCgpOwogICAgfSk7CiAgfQoKICBmdW5jdGlvbiBwaExiQm9vdCgpewogICAgaWYgKCFwaExiSXNDb25maWd1cmVkKCkpewogICAgICBwaExiVXBkYXRlTGl2ZUJhZGdlKCk7CiAgICAgIHJldHVybjsKICAgIH0KICAgIHBoTGJJbml0KCkudGhlbihmdW5jdGlvbihvayl7CiAgICAgIGlmICghb2spIHJldHVybjsKICAgICAgcGhMYlN0YXJ0TGlzdGVuZXIoKTsKICAgICAgaWYgKHBoTG9hZFRyYWluZXJOYW1lKCkpIHBoTGJTeW5jU3RhdHMoKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gcGhMZWFkZXJib2FyZFJvd3MoKXsKICAgIHZhciBvdXQgPSBbXSwgc25hcCA9IHBoTGIuX3NuYXBzaG90OwogICAgaWYgKHNuYXAgJiYgc25hcC5mb3JFYWNoKXsKICAgICAgc25hcC5mb3JFYWNoKGZ1bmN0aW9uKGNoaWxkKXsKICAgICAgICB2YXIgdiA9IGNoaWxkLnZhbCgpIHx8IHt9OwogICAgICAgIGlmICghdi5uYW1lKSByZXR1cm47CiAgICAgICAgb3V0LnB1c2goewogICAgICAgICAgdWlkOiBjaGlsZC5rZXksCiAgICAgICAgICBuYW1lOiBTdHJpbmcodi5uYW1lKS5zbGljZSgwLDE4KSwKICAgICAgICAgIHBhY2tzOiBOdW1iZXIodi5wYWNrcyl8fDAsCiAgICAgICAgICBjYXJkczogTnVtYmVyKHYuY2FyZHMpfHwwLAogICAgICAgICAgdW5pcXVlOiBOdW1iZXIodi51bmlxdWUpfHwwLAogICAgICAgICAgYmVzdFRpZXI6IE51bWJlcih2LmJlc3RUaWVyKXx8MCwKICAgICAgICAgIGJlc3RWYWx1ZTogTnVtYmVyKHYuYmVzdFZhbHVlKXx8MCwKICAgICAgICAgIHVwZGF0ZWRBdDogTnVtYmVyKHYudXBkYXRlZEF0KXx8MAogICAgICAgIH0pOwogICAgICB9KTsKICAgIH0KICAgIG91dC5zb3J0KGZ1bmN0aW9uKGEsYil7CiAgICAgIGlmIChiLnBhY2tzICE9PSBhLnBhY2tzKSByZXR1cm4gYi5wYWNrcyAtIGEucGFja3M7CiAgICAgIGlmIChiLmJlc3RUaWVyICE9PSBhLmJlc3RUaWVyKSByZXR1cm4gYi5iZXN0VGllciAtIGEuYmVzdFRpZXI7CiAgICAgIGlmIChiLmJlc3RWYWx1ZSAhPT0gYS5iZXN0VmFsdWUpIHJldHVybiBiLmJlc3RWYWx1ZSAtIGEuYmVzdFZhbHVlOwogICAgICBpZiAoYi51bmlxdWUgIT09IGEudW5pcXVlKSByZXR1cm4gYi51bmlxdWUgLSBhLnVuaXF1ZTsKICAgICAgcmV0dXJuIGIudXBkYXRlZEF0IC0gYS51cGRhdGVkQXQ7CiAgICB9KTsKICAgIHJldHVybiBvdXQ7CiAgfQoKICBmdW5jdGlvbiBwaFJlbmRlckxlYWRlcmJvYXJkKCl7CiAgICBpZiAoIWVscy5sYkJvZHkpIHJldHVybjsKICAgIGVscy5sYkJvZHkudGV4dENvbnRlbnQgPSAnJzsKICAgIHZhciBub3RlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBub3RlLmNsYXNzTmFtZSA9ICdwaC1sYi1ub3RlJzsKICAgIG5vdGUudGV4dENvbnRlbnQgPSBwaExiSXNDb25maWd1cmVkKCkKICAgICAgPyAn8J+PhiBY4bq/cCBo4bqhbmcgdGhlbyB04buVbmcgc+G7kSBnw7NpIMSRw6MgbeG7nyDCtyAnICsgcGhMYlN0YXR1c1RleHQoKQogICAgICA6ICfwn4+GIEJYSCDEkcOjIHPhurVuIGdpYW8gZGnhu4duIG5oxrBuZyBjaMawYSBr4bq/dCBu4buRaSByZWFsdGltZS4gRMOhbiBGaXJlYmFzZSBjb25maWcgdsOgbyBjb2RlIMSR4buDIGLhuq10IG5oaeG7gXUgbmfGsOG7nWkuJzsKICAgIGVscy5sYkJvZHkuYXBwZW5kQ2hpbGQobm90ZSk7CgogICAgdmFyIGN1cnJlbnROYW1lID0gcGhMb2FkVHJhaW5lck5hbWUoKSB8fCAnVHJhaW5lcic7CiAgICB2YXIgcm93cyA9IHBoTGVhZGVyYm9hcmRSb3dzKCk7CiAgICB2YXIgbXlVaWQgPSBwaEdldExiVWlkKCksIG15ID0gbnVsbCwgbXlSYW5rID0gJ+KAlCc7CiAgICByb3dzLmZvckVhY2goZnVuY3Rpb24ocixpKXsgaWYgKHIudWlkID09PSBteVVpZCl7IG15PXI7IG15UmFuaz1TdHJpbmcoaSsxKTsgfSB9KTsKCiAgICB2YXIgaGVybyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBoZXJvLmNsYXNzTmFtZT0ncGgtbGItaGVybyc7CiAgICBoZXJvLmFwcGVuZENoaWxkKHN0YXRCb3goJ0jhuqFuZyBoaeG7h24gdOG6oWknLCBteVJhbmspKTsKICAgIGhlcm8uYXBwZW5kQ2hpbGQoc3RhdEJveCgnR8OzaSDEkcOjIG3hu58nLCBteSA/IFN0cmluZyhteS5wYWNrcykgOiBTdHJpbmcoY29sbGVjdGlvblN0YXRzKCkucGFja3MpKSk7CiAgICBoZXJvLmFwcGVuZENoaWxkKHN0YXRCb3goJ1RyYWluZXInLCBjdXJyZW50TmFtZSkpOwogICAgZWxzLmxiQm9keS5hcHBlbmRDaGlsZChoZXJvKTsKCiAgICB2YXIgYWN0aW9ucz1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgYWN0aW9ucy5jbGFzc05hbWU9J3BoLWxiLWFjdGlvbnMnOwogICAgdmFyIHJlbmFtZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsgcmVuYW1lLnR5cGU9J2J1dHRvbic7IHJlbmFtZS5jbGFzc05hbWU9J3BoLWJ0biBwaC1zbWFsbCc7IHJlbmFtZS50ZXh0Q29udGVudD0n4pyP77iPIMSQ4buVaSB0w6puJzsKICAgIHJlbmFtZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsZnVuY3Rpb24oKXsgcGhMYkNsb3NlKCk7IHBoU2hvd1dlbGNvbWUodHJ1ZSk7IH0pOwogICAgYWN0aW9ucy5hcHBlbmRDaGlsZChyZW5hbWUpOyBlbHMubGJCb2R5LmFwcGVuZENoaWxkKGFjdGlvbnMpOwoKICAgIGlmICghcm93cy5sZW5ndGgpewogICAgICB2YXIgZW1wdHk9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGVtcHR5LmNsYXNzTmFtZT0ncGgtY29sLW5vdGUnOwogICAgICBlbXB0eS50ZXh0Q29udGVudD1waExiSXNDb25maWd1cmVkKCkgPyAnQ2jGsGEgY8OzIHRyYWluZXIgbsOgbyB0csOqbiBCWEguIE3hu58gcGFjayDEkeG6p3UgdGnDqm4gxJHhu4MgbMOqbiBi4bqjbmchJyA6ICdDaMawYSBjw7MgZOG7ryBsaeG7h3UgcmVhbHRpbWUgxJHhu4MgaGnhu4NuIHRo4buLLic7CiAgICAgIGVscy5sYkJvZHkuYXBwZW5kQ2hpbGQoZW1wdHkpOyByZXR1cm47CiAgICB9CgogICAgdmFyIHRhYmxlV3JhcD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgdGFibGVXcmFwLmNsYXNzTmFtZT0ncGgtbGItdGFibGUtd3JhcCc7CiAgICB2YXIgdGFibGU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGFibGUnKTsgdGFibGUuY2xhc3NOYW1lPSdwaC1sYi10YWJsZSc7CiAgICB2YXIgdGhlYWQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGhlYWQnKSwgdHJoPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3RyJyk7CiAgICBbJyMnLCdUcmFpbmVyJywnR8OzaScsJ0zDoScsJ0Jlc3QnXS5mb3JFYWNoKGZ1bmN0aW9uKHQpeyB2YXIgdGg9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGgnKTsgdGgudGV4dENvbnRlbnQ9dDsgdHJoLmFwcGVuZENoaWxkKHRoKTsgfSk7CiAgICB0aGVhZC5hcHBlbmRDaGlsZCh0cmgpOyB0YWJsZS5hcHBlbmRDaGlsZCh0aGVhZCk7CiAgICB2YXIgdGJvZHk9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGJvZHknKTsKICAgIHJvd3Muc2xpY2UoMCwyMCkuZm9yRWFjaChmdW5jdGlvbihyLGkpewogICAgICB2YXIgdHI9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndHInKTsgaWYgKHIudWlkPT09bXlVaWQpIHRyLmNsYXNzTmFtZT0ncGgtbGItbWUnOwogICAgICB2YXIgdGRSYW5rPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3RkJyk7IHRkUmFuay50ZXh0Q29udGVudD1TdHJpbmcoaSsxKTsKICAgICAgdmFyIHRkTmFtZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZCcpOyB0ZE5hbWUudGV4dENvbnRlbnQ9ci5uYW1lKyhyLnVpZD09PW15VWlkPycgwrcgQuG6oW4nOicnKTsKICAgICAgdmFyIHRkUD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZCcpOyB0ZFAudGV4dENvbnRlbnQ9U3RyaW5nKHIucGFja3MpOwogICAgICB2YXIgdGRDPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3RkJyk7IHRkQy50ZXh0Q29udGVudD1TdHJpbmcoci5jYXJkcyk7CiAgICAgIHZhciB0ZEJlc3Q9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGQnKTsgdGRCZXN0LnRleHRDb250ZW50PXRpZXJMYWJlbFRleHQoci5iZXN0VGllcikrKHIuYmVzdFZhbHVlPjA/JyDCtyAnK21vbmV5KHIuYmVzdFZhbHVlKTonJyk7CiAgICAgIHRyLmFwcGVuZENoaWxkKHRkUmFuayk7IHRyLmFwcGVuZENoaWxkKHRkTmFtZSk7IHRyLmFwcGVuZENoaWxkKHRkUCk7IHRyLmFwcGVuZENoaWxkKHRkQyk7IHRyLmFwcGVuZENoaWxkKHRkQmVzdCk7CiAgICAgIHRib2R5LmFwcGVuZENoaWxkKHRyKTsKICAgIH0pOwogICAgdGFibGUuYXBwZW5kQ2hpbGQodGJvZHkpOyB0YWJsZVdyYXAuYXBwZW5kQ2hpbGQodGFibGUpOyBlbHMubGJCb2R5LmFwcGVuZENoaWxkKHRhYmxlV3JhcCk7CgogICAgdmFyIGZvb3Q9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGZvb3QuY2xhc3NOYW1lPSdwaC1jb2wtbm90ZSc7CiAgICBmb290LnRleHRDb250ZW50PXJvd3MubGVuZ3RoPjIwID8gJ8SQYW5nIGhp4buHbiB0b3AgMjAgwrcgQ8OzICcrcm93cy5sZW5ndGgrJyB0cmFpbmVyIHRyw6puIEJYSC4nIDogJ0Phuq1wIG5o4bqtdCB04buxIMSR4buZbmcga2hpIGPDsyBuZ8aw4budaSBt4bufIHBhY2suJzsKICAgIGVscy5sYkJvZHkuYXBwZW5kQ2hpbGQoZm9vdCk7CiAgfQoKICBmdW5jdGlvbiBwaExiT3BlbigpewogICAgaWYgKCFwaExvYWRUcmFpbmVyTmFtZSgpKXsgcGhTaG93V2VsY29tZShmYWxzZSk7IHJldHVybjsgfQogICAgcGhSZW5kZXJMZWFkZXJib2FyZCgpOwogICAgZWxzLmxiTW9kYWwuaGlkZGVuPWZhbHNlOwogICAgc2V0TW9kYWxWaWV3cG9ydE9wZW4odHJ1ZSk7CiAgICBwaExiQm9vdCgpOwogIH0KCiAgZnVuY3Rpb24gcGhMYkNsb3NlKCl7CiAgICBpZiAoIWVscy5sYk1vZGFsKSByZXR1cm47CiAgICBlbHMubGJNb2RhbC5oaWRkZW49dHJ1ZTsgZWxzLmxiQm9keS50ZXh0Q29udGVudD0nJzsgc2V0TW9kYWxWaWV3cG9ydE9wZW4oZmFsc2UpOwogIH0KCiAgZnVuY3Rpb24gcGhTaG93V2VsY29tZShmb3JjZUVkaXQpewogICAgaWYgKCFlbHMud2VsY29tZU1vZGFsKSByZXR1cm47CiAgICB2YXIgc2F2ZWQ9cGhMb2FkVHJhaW5lck5hbWUoKTsKICAgIGVscy53ZWxjb21lSW5wdXQudmFsdWU9c2F2ZWR8fCcnOwogICAgZWxzLndlbGNvbWVFcnJvci50ZXh0Q29udGVudD0nJzsKICAgIGVscy53ZWxjb21lU3RhcnQudGV4dENvbnRlbnQ9KHNhdmVkfHxmb3JjZUVkaXQpPyfwn5K+IEzGsHUgdMOqbic6J/CfmoAgQuG6r3QgxJHhuqd1IGNoxqFpJzsKICAgIGVscy53ZWxjb21lTW9kYWwuaGlkZGVuPWZhbHNlOwogICAgc2V0TW9kYWxWaWV3cG9ydE9wZW4odHJ1ZSk7CiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7IGVscy53ZWxjb21lSW5wdXQuZm9jdXMoKTsgZWxzLndlbGNvbWVJbnB1dC5zZWxlY3QoKTsgfSw0MCk7CiAgfQoKICBmdW5jdGlvbiBwaFN1Ym1pdFdlbGNvbWUoKXsKICAgIHZhciBuYW1lPXBoU2FmZVRyYWluZXJOYW1lKGVscy53ZWxjb21lSW5wdXQudmFsdWUpOwogICAgaWYgKCFuYW1lIHx8IG5hbWUubGVuZ3RoPDIpeyBlbHMud2VsY29tZUVycm9yLnRleHRDb250ZW50PSdOaWNrbmFtZSB04buRaSB0aGnhu4N1IDIga8O9IHThu7EuJzsgZWxzLndlbGNvbWVJbnB1dC5mb2N1cygpOyByZXR1cm47IH0KICAgIGlmICghcGhTYXZlVHJhaW5lck5hbWUobmFtZSkpeyBlbHMud2VsY29tZUVycm9yLnRleHRDb250ZW50PSdLaMO0bmcgdGjhu4MgbMawdSBuaWNrbmFtZSB0csOqbiB0csOsbmggZHV54buHdCBuw6B5Lic7IHJldHVybjsgfQogICAgcGhMYkJvb3QoKTsKICAgIGVscy53ZWxjb21lTW9kYWwuaGlkZGVuPXRydWU7IGVscy53ZWxjb21lRXJyb3IudGV4dENvbnRlbnQ9Jyc7IHNldE1vZGFsVmlld3BvcnRPcGVuKGZhbHNlKTsKICAgIHRvYXN0KCfwn5GLIENow6BvICcrbmFtZSsnISBT4bq1biBzw6BuZyBt4bufIHBhY2sgcuG7k2kuJyk7CiAgfQoKICBmdW5jdGlvbiBwaFNldHVwVHJhaW5lckFuZExlYWRlcmJvYXJkKCl7CiAgICBpZiAoZWxzLndlbGNvbWVTdGFydCkgZWxzLndlbGNvbWVTdGFydC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycscGhTdWJtaXRXZWxjb21lKTsKICAgIGlmIChlbHMud2VsY29tZUlucHV0KSBlbHMud2VsY29tZUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLGZ1bmN0aW9uKGUpeyBpZihlLmtleT09PSdFbnRlcicpe2UucHJldmVudERlZmF1bHQoKTtwaFN1Ym1pdFdlbGNvbWUoKTt9IH0pOwogICAgaWYgKGVscy5sYkJ0bikgZWxzLmxiQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJyxwaExiT3Blbik7CiAgICBpZiAoZWxzLmxiQ2xvc2UpIGVscy5sYkNsb3NlLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJyxwaExiQ2xvc2UpOwogICAgaWYgKGVscy5sYkJhY2tkcm9wKSBlbHMubGJCYWNrZHJvcC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycscGhMYkNsb3NlKTsKICAgIGlmICghcGhMb2FkVHJhaW5lck5hbWUoKSkgcGhTaG93V2VsY29tZShmYWxzZSk7IGVsc2UgcGhMYkJvb3QoKTsKICAgIHBoTGJVcGRhdGVMaXZlQmFkZ2UoKTsKICB9CgogIC8vIC0tLS0tLS0tLS0gc291bmQgZW5naW5lIC0tLS0tLS0tLS0KICB2YXIgYXVkaW9DdHggPSBudWxsOwogIGZ1bmN0aW9uIGVuc3VyZUF1ZGlvKCl7CiAgICBpZiAoc3RhdGUubXV0ZWQpIHJldHVybiBudWxsOwogICAgaWYgKCFhdWRpb0N0eCl7CiAgICAgIHZhciBDdHggPSB3aW5kb3cuQXVkaW9Db250ZXh0IHx8IHdpbmRvdy53ZWJraXRBdWRpb0NvbnRleHQ7CiAgICAgIGlmICghQ3R4KSByZXR1cm4gbnVsbDsKICAgICAgYXVkaW9DdHggPSBuZXcgQ3R4KCk7CiAgICB9CiAgICBpZiAoYXVkaW9DdHguc3RhdGUgPT09ICdzdXNwZW5kZWQnKSBhdWRpb0N0eC5yZXN1bWUoKTsKICAgIHJldHVybiBhdWRpb0N0eDsKICB9CgogIGZ1bmN0aW9uIHRvbmUoY3R4LCB3aGVuLCBmcmVxLCBkdXIsIG9wdHMpewogICAgb3B0cyA9IG9wdHMgfHwge307CiAgICB2YXIgb3NjID0gY3R4LmNyZWF0ZU9zY2lsbGF0b3IoKTsKICAgIHZhciBnYWluID0gY3R4LmNyZWF0ZUdhaW4oKTsKICAgIG9zYy50eXBlID0gb3B0cy50eXBlIHx8ICdzaW5lJzsKICAgIG9zYy5mcmVxdWVuY3kuc2V0VmFsdWVBdFRpbWUoZnJlcSwgd2hlbik7CiAgICBpZiAob3B0cy5zbGlkZVRvKSBvc2MuZnJlcXVlbmN5LmV4cG9uZW50aWFsUmFtcFRvVmFsdWVBdFRpbWUob3B0cy5zbGlkZVRvLCB3aGVuICsgZHVyKTsKICAgIHZhciBwZWFrID0gb3B0cy5nYWluICE9IG51bGwgPyBvcHRzLmdhaW4gOiAwLjE2OwogICAgZ2Fpbi5nYWluLnNldFZhbHVlQXRUaW1lKDAuMDAwMSwgd2hlbik7CiAgICBnYWluLmdhaW4uZXhwb25lbnRpYWxSYW1wVG9WYWx1ZUF0VGltZShwZWFrLCB3aGVuICsgTWF0aC5taW4oMC4wMiwgZHVyKjAuMykpOwogICAgZ2Fpbi5nYWluLmV4cG9uZW50aWFsUmFtcFRvVmFsdWVBdFRpbWUoMC4wMDAxLCB3aGVuICsgZHVyKTsKICAgIG9zYy5jb25uZWN0KGdhaW4pLmNvbm5lY3QoY3R4LmRlc3RpbmF0aW9uKTsKICAgIG9zYy5zdGFydCh3aGVuKTsKICAgIG9zYy5zdG9wKHdoZW4gKyBkdXIgKyAwLjAyKTsKICB9CgogIGZ1bmN0aW9uIHBsYXlSaXAoKXsKICAgIHZhciBjdHggPSBlbnN1cmVBdWRpbygpOwogICAgaWYgKCFjdHgpIHJldHVybjsKICAgIHZhciBkdXIgPSAwLjU7CiAgICB2YXIgYnVmZmVyU2l6ZSA9IE1hdGguZmxvb3IoY3R4LnNhbXBsZVJhdGUgKiBkdXIpOwogICAgdmFyIGJ1ZmZlciA9IGN0eC5jcmVhdGVCdWZmZXIoMSwgYnVmZmVyU2l6ZSwgY3R4LnNhbXBsZVJhdGUpOwogICAgdmFyIGRhdGEgPSBidWZmZXIuZ2V0Q2hhbm5lbERhdGEoMCk7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJ1ZmZlclNpemU7IGkrKykgZGF0YVtpXSA9IChNYXRoLnJhbmRvbSgpKjIgLSAxKSAqICgxIC0gaS9idWZmZXJTaXplKTsKICAgIHZhciBub2lzZSA9IGN0eC5jcmVhdGVCdWZmZXJTb3VyY2UoKTsKICAgIG5vaXNlLmJ1ZmZlciA9IGJ1ZmZlcjsKICAgIHZhciBmaWx0ZXIgPSBjdHguY3JlYXRlQmlxdWFkRmlsdGVyKCk7CiAgICBmaWx0ZXIudHlwZSA9ICdiYW5kcGFzcyc7CiAgICBmaWx0ZXIuUS52YWx1ZSA9IDAuNzsKICAgIHZhciBub3cgPSBjdHguY3VycmVudFRpbWU7CiAgICBmaWx0ZXIuZnJlcXVlbmN5LnNldFZhbHVlQXRUaW1lKDcwMCwgbm93KTsKICAgIGZpbHRlci5mcmVxdWVuY3kuZXhwb25lbnRpYWxSYW1wVG9WYWx1ZUF0VGltZSg0MjAwLCBub3cgKyBkdXIpOwogICAgdmFyIGdhaW4gPSBjdHguY3JlYXRlR2FpbigpOwogICAgZ2Fpbi5nYWluLnNldFZhbHVlQXRUaW1lKDAuMzUsIG5vdyk7CiAgICBnYWluLmdhaW4uZXhwb25lbnRpYWxSYW1wVG9WYWx1ZUF0VGltZSgwLjAwMSwgbm93ICsgZHVyKTsKICAgIG5vaXNlLmNvbm5lY3QoZmlsdGVyKS5jb25uZWN0KGdhaW4pLmNvbm5lY3QoY3R4LmRlc3RpbmF0aW9uKTsKICAgIG5vaXNlLnN0YXJ0KG5vdyk7CiAgICBub2lzZS5zdG9wKG5vdyArIGR1cik7CiAgICB0b25lKGN0eCwgbm93ICsgZHVyKjAuODUsIDIyMCwgMC4xMiwge3R5cGU6J3RyaWFuZ2xlJywgZ2FpbjowLjE4LCBzbGlkZVRvOjkwfSk7CiAgfQoKICB2YXIgU0NBTEUgPSBbNTIzLjI1LCA1ODcuMzMsIDY1OS4yNSwgNzgzLjk5LCA4ODAuMDAsIDEwNDYuNTBdOwogIGZ1bmN0aW9uIHBsYXlGbGlwKHRpZXIpewogICAgdmFyIGN0eCA9IGVuc3VyZUF1ZGlvKCk7CiAgICBpZiAoIWN0eCkgcmV0dXJuOwogICAgdmFyIG5vdyA9IGN0eC5jdXJyZW50VGltZTsKICAgIGlmICh0aWVyIDw9IDEpewogICAgICB0b25lKGN0eCwgbm93LCA0NDAsIDAuMTIsIHt0eXBlOidzaW5lJywgZ2FpbjowLjEyfSk7CiAgICAgIHJldHVybjsKICAgIH0KICAgIHZhciBub3RlcyA9IE1hdGgubWluKDIgKyB0aWVyLCBTQ0FMRS5sZW5ndGgpOwogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBub3RlczsgaSsrKXsKICAgICAgdG9uZShjdHgsIG5vdyArIGkqMC4wNDUsIFNDQUxFW2kgJSBTQ0FMRS5sZW5ndGhdICogKDEgKyB0aWVyKjAuMDIpLCAwLjIyLCB7CiAgICAgICAgdHlwZTogdGllciA+PSA1ID8gJ3RyaWFuZ2xlJyA6ICdzaW5lJywKICAgICAgICBnYWluOiAwLjEwICsgdGllciowLjAyCiAgICAgIH0pOwogICAgfQogIH0KCiAgZnVuY3Rpb24gcGxheUZhbmZhcmUoKXsKICAgIHZhciBjdHggPSBlbnN1cmVBdWRpbygpOwogICAgaWYgKCFjdHgpIHJldHVybjsKICAgIHZhciBub3cgPSBjdHguY3VycmVudFRpbWU7CiAgICB2YXIgY2hvcmQgPSBbNTIzLjI1LCA2NTkuMjUsIDc4My45OSwgMTA0Ni41MF07CiAgICBjaG9yZC5mb3JFYWNoKGZ1bmN0aW9uKGYsIGkpewogICAgICB0b25lKGN0eCwgbm93ICsgaSowLjAyLCBmLCAwLjksIHt0eXBlOidzYXd0b290aCcsIGdhaW46MC4wN30pOwogICAgICB0b25lKGN0eCwgbm93ICsgaSowLjAyLCBmLzIsIDAuOSwge3R5cGU6J3NpbmUnLCBnYWluOjAuMTB9KTsKICAgIH0pOwogICAgWzExNzQuNjYsIDEzMTguNTEsIDE1NjcuOThdLmZvckVhY2goZnVuY3Rpb24oZiwgaSl7CiAgICAgIHRvbmUoY3R4LCBub3cgKyAwLjM1ICsgaSowLjEyLCBmLCAwLjM1LCB7dHlwZTondHJpYW5nbGUnLCBnYWluOjAuMTJ9KTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gdXBkYXRlTXV0ZUJ0bigpewogICAgZWxzLm11dGUudGV4dENvbnRlbnQgPSBzdGF0ZS5tdXRlZCA/ICdcdUQ4M0RcdUREMDcnIDogJ1x1RDgzRFx1REQwQSc7CiAgICBlbHMubXV0ZS5jbGFzc0xpc3QudG9nZ2xlKCdwaC1tdXRlZCcsIHN0YXRlLm11dGVkKTsKICB9CiAgZWxzLm11dGUuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpewogICAgc3RhdGUubXV0ZWQgPSAhc3RhdGUubXV0ZWQ7CiAgICB1cGRhdGVNdXRlQnRuKCk7CiAgfSk7CiAgdXBkYXRlTXV0ZUJ0bigpOwoKICAvLyAtLS0tLS0tLS0tIGRhdGEgbG9hZGluZyAtLS0tLS0tLS0tCiAgZnVuY3Rpb24gZ2V0SlNPTih1cmwsIHRpbWVvdXRNcyl7CiAgICB2YXIgY3RybCA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTsKICAgIHZhciB0ID0gc2V0VGltZW91dChmdW5jdGlvbigpeyBjdHJsLmFib3J0KCk7IH0sIHRpbWVvdXRNcyB8fCA5MDAwKTsKICAgIHJldHVybiBmZXRjaCh1cmwsIHtzaWduYWw6Y3RybC5zaWduYWx9KQogICAgICAudGhlbihmdW5jdGlvbihyKXsKICAgICAgICBpZiAoIXIub2spIHRocm93IG5ldyBFcnJvcignSFRUUCAnICsgci5zdGF0dXMpOwogICAgICAgIHJldHVybiByLmpzb24oKTsKICAgICAgfSkKICAgICAgLmZpbmFsbHkoZnVuY3Rpb24oKXsgY2xlYXJUaW1lb3V0KHQpOyB9KTsKICB9CgogIGZ1bmN0aW9uIHNldEFjY2VudChjMSxjMil7CiAgICByb290LnN0eWxlLnNldFByb3BlcnR5KCctLXBoLWMxJywgYzEpOwogICAgcm9vdC5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1waC1jMicsIGMyKTsKICB9CgogIGZ1bmN0aW9uIHJhcml0eVRpZXIoc3RyKXsKICAgIHZhciByID0gKHN0ciB8fCAnJykudG9Mb3dlckNhc2UoKTsKICAgIGlmIChyLmluZGV4T2YoJ2Nyb3duJykgPiAtMSkgICAgICAgICByZXR1cm4ge3RpZXI6NiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3NwZWNpYWwgYXJ0JykgPiAtMSkgICByZXR1cm4ge3RpZXI6NiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2h5cGVyJykgPiAtMSkgICAgICAgICByZXR1cm4ge3RpZXI6NiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3JhaW5ib3cnKSA+IC0xKSAgICAgICByZXR1cm4ge3RpZXI6NiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3NlY3JldCcpID4gLTEpICAgICAgICByZXR1cm4ge3RpZXI6NiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2dvbGQnKSA+IC0xKSAgICAgICAgICByZXR1cm4ge3RpZXI6NiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2RvdWJsZSBzaGlueScpID4gLTEpICByZXR1cm4ge3RpZXI6NSwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3RocmVlIHN0YXInKSA+IC0xKSAgICByZXR1cm4ge3RpZXI6NSwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2lsbHVzdHJhdGlvbicpID4gLTEpICByZXR1cm4ge3RpZXI6NSwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3NoaW55JykgPiAtMSkgICAgICAgICByZXR1cm4ge3RpZXI6NCwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3N0YXInKSA+IC0xKSAgICAgICAgICByZXR1cm4ge3RpZXI6NCwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2Z1bGwgYXJ0JykgPiAtMSkgICAgICByZXR1cm4ge3RpZXI6NCwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2V4JykgPiAtMSAmJiByLmluZGV4T2YoJ3JhcmUnKSA+IC0xKSByZXR1cm4ge3RpZXI6NCwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2ZvdXIgZGlhbW9uZCcpID4gLTEpICByZXR1cm4ge3RpZXI6MywgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2RvdWJsZSByYXJlJykgPiAtMSkgICByZXR1cm4ge3RpZXI6MywgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3VsdHJhJykgPiAtMSkgICAgICAgICByZXR1cm4ge3RpZXI6MywgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3RocmVlIGRpYW1vbmQnKSA+IC0xKSByZXR1cm4ge3RpZXI6MiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2hvbG8nKSA+IC0xKSAgICAgICAgICByZXR1cm4ge3RpZXI6MiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3R3byBkaWFtb25kJykgPiAtMSkgICByZXR1cm4ge3RpZXI6MSwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ2FtYXppbmcnKSA+IC0xKSAgICAgICByZXR1cm4ge3RpZXI6MywgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3Byb21vJykgPiAtMSkgICAgICAgICByZXR1cm4ge3RpZXI6MiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3JhcmUnKSA+IC0xKSAgICAgICAgICByZXR1cm4ge3RpZXI6MiwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ29uZSBkaWFtb25kJykgPiAtMSkgICByZXR1cm4ge3RpZXI6MCwgbGFiZWw6c3RyfTsKICAgIGlmIChyLmluZGV4T2YoJ3VuY29tbW9uJykgPiAtMSkgICAgICByZXR1cm4ge3RpZXI6MSwgbGFiZWw6c3RyfTsKICAgIHJldHVybiB7dGllcjowLCBsYWJlbDogc3RyIHx8ICdDb21tb24nfTsKICB9CgogIC8vIFThuqNpIG3hu5l0IGzhuqduIGRhbmggc8OhY2ggTeG7jEkgY2h14buXaSDEkeG7mSBoaeG6v20gbcOgIFRDR2RleCB04burbmcgZMO5bmcgKGVuZHBvaW50CiAgLy8gL3Jhcml0aWVzKSwgcuG7k2kgeOG6v3AgdOG7q25nIGNodeG7l2kgdsOgbyB04bqnbmcgMC02IGLhurFuZyBjaMOtbmggcmFyaXR5VGllcigpLgogIC8vIE5o4budIHbhuq15IHBo4bqnbiBs4buNYyB0aGVvIGLhu5kg4bufIGTGsOG7m2kga2jDtG5nIGPhuqduIGxp4buHdCBrw6ogdGF5IHThu6tuZyBjaHXhu5dpIGNobwogIC8vIHThu6tuZyB0aOG7nWkga+G7syAoQmFzZSwgWFksIFN3b3JkICYgU2hpZWxkLCBQb2NrZXQgVENH4oCmKS4KICBmdW5jdGlvbiBsb2FkUmFyaXR5VGF4b25vbXkoKXsKICAgIGlmIChyYXJpdHlUYXhvbm9teVByb21pc2UpIHJldHVybiByYXJpdHlUYXhvbm9teVByb21pc2U7CiAgICByYXJpdHlUYXhvbm9teVByb21pc2UgPSBnZXRKU09OKEFQSSArICcvcmFyaXRpZXMnLCAxMDAwMCkudGhlbihmdW5jdGlvbihkYXRhKXsKICAgICAgdmFyIGxpc3QgPSBBcnJheS5pc0FycmF5KGRhdGEpID8gZGF0YQogICAgICAgIDogKGRhdGEgJiYgQXJyYXkuaXNBcnJheShkYXRhLnJhcml0aWVzKSkgPyBkYXRhLnJhcml0aWVzCiAgICAgICAgOiAoZGF0YSAmJiBBcnJheS5pc0FycmF5KGRhdGEuZGF0YSkpID8gZGF0YS5kYXRhCiAgICAgICAgOiBbXTsKICAgICAgdmFyIGdyb3VwcyA9IFtbXSxbXSxbXSxbXSxbXSxbXSxbXV07CiAgICAgIHZhciBzZWVuID0ge307CiAgICAgIGxpc3QuZm9yRWFjaChmdW5jdGlvbihzdHIpewogICAgICAgIGlmICh0eXBlb2Ygc3RyICE9PSAnc3RyaW5nJyB8fCBzZWVuW3N0cl0pIHJldHVybjsKICAgICAgICBzZWVuW3N0cl0gPSB0cnVlOwogICAgICAgIGdyb3Vwc1tyYXJpdHlUaWVyKHN0cikudGllcl0ucHVzaChzdHIpOwogICAgICB9KTsKICAgICAgUkFSSVRZX1RJRVJfR1JPVVBTID0gZ3JvdXBzOwogICAgICByZXR1cm4gZ3JvdXBzOwogICAgfSkuY2F0Y2goZnVuY3Rpb24oKXsKICAgICAgUkFSSVRZX1RJRVJfR1JPVVBTID0gbnVsbDsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9KTsKICAgIHJldHVybiByYXJpdHlUYXhvbm9teVByb21pc2U7CiAgfQoKICAvLyBW4bubaSBi4buZIHRo4bq7IMSRYW5nIGNo4buNbiwgZOG7sW5nICJo4buZcCIgY2jhu6lhIGPDoWMgbMOhIMSQw4MgxJHGsOG7o2MgZ+G6r24gc+G6tW4gdOG6p25nIMSR4buZCiAgLy8gaGnhur9tIHRo4bqtdCAoMC02KSBi4bqxbmcgY8OhY2ggbOG7jWMgL2NhcmRzP3NldC5pZD0uLi4mcmFyaXR5PWVxOkF8QnxDIGNobwogIC8vIHThu6tuZyB04bqnbmcg4oCUIG3hu5dpIHThuqduZyBjaOG7iSB04buRbiAxIHJlcXVlc3QsIGtow7RuZyBj4bqnbiB04bqjaSB04burbmcgbMOhIG3hu5l0LgogIC8vIE7hur91IEFQSSBraMO0bmcgdHLhuqMgduG7gSBr4bq/dCBxdeG6oyDEkcOhbmcgdGluICh2w60gZOG7pSBi4buZIHF1w6EgY8WpL2zhuqEga2hp4bq/biBs4buNYwogIC8vIGtow7RuZyBraOG7m3ApLCBow6BtIHThu7EgYuG7jyBxdWEgdsOgIHRvw6BuIGLhu5kgaOG7hyB0aOG7kW5nIHF1YXkgbOG6oWkga2nhu4N1IHLDunQgxJHhu4F1CiAgLy8gbmjGsCB0csaw4bubYyDigJQga2jDtG5nIGJhbyBnaeG7nSBsw6BtIGjhu49uZyB2aeG7h2MgbeG7nyBnw7NpLgogIGZ1bmN0aW9uIGJ1aWxkVGllclBvb2xzKHNldElkLCB0b3RhbElkcyl7CiAgICBsb2FkUmFyaXR5VGF4b25vbXkoKS50aGVuKGZ1bmN0aW9uKGdyb3Vwcyl7CiAgICAgIGlmICghZ3JvdXBzKSByZXR1cm47CiAgICAgIHZhciB0aWVycyA9IFtdLCBqb2JzID0gW107CiAgICAgIGZvciAodmFyIHQgPSAwOyB0IDw9IDY7IHQrKyl7CiAgICAgICAgaWYgKGdyb3Vwc1t0XSAmJiBncm91cHNbdF0ubGVuZ3RoKXsKICAgICAgICAgIHZhciB2YWx1ZSA9ICdlcTonICsgZ3JvdXBzW3RdLm1hcChmdW5jdGlvbihzKXsgcmV0dXJuIGVuY29kZVVSSUNvbXBvbmVudChzKTsgfSkuam9pbignfCcpOwogICAgICAgICAgdmFyIHVybCA9IEFQSSArICcvY2FyZHM/c2V0LmlkPScgKyBlbmNvZGVVUklDb21wb25lbnQoc2V0SWQpICsgJyZyYXJpdHk9JyArIHZhbHVlOwogICAgICAgICAgdGllcnMucHVzaCh0KTsKICAgICAgICAgIGpvYnMucHVzaChnZXRKU09OKHVybCwgMTAwMDApLmNhdGNoKGZ1bmN0aW9uKCl7IHJldHVybiBbXTsgfSkpOwogICAgICAgIH0KICAgICAgfQogICAgICBpZiAoIWpvYnMubGVuZ3RoKSByZXR1cm47CiAgICAgIFByb21pc2UuYWxsKGpvYnMpLnRoZW4oZnVuY3Rpb24ocmVzdWx0cyl7CiAgICAgICAgLy8gYuG7mSDEkcOjIGLhu4sgxJHhu5VpIHRyb25nIGzDumMgxJFhbmcgdOG6o2kgdGjDrCBi4buPIGvhur90IHF14bqjIG7DoHksIGto4buPaSBnaGkgbmjhuqdtCiAgICAgICAgaWYgKCFzdGF0ZS5jdXJyZW50IHx8IHN0YXRlLmN1cnJlbnQuaWQgIT09IHNldElkKSByZXR1cm47CiAgICAgICAgdmFyIHBvb2xzID0gW1tdLFtdLFtdLFtdLFtdLFtdLFtdXTsKICAgICAgICB2YXIgbWF0Y2hlZCA9IHt9OwogICAgICAgIHJlc3VsdHMuZm9yRWFjaChmdW5jdGlvbihhcnIsIGlkeCl7CiAgICAgICAgICB2YXIgdCA9IHRpZXJzW2lkeF07CiAgICAgICAgICAoYXJyIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uKGMpewogICAgICAgICAgICBpZiAoIWMgfHwgIWMuaWQgfHwgbWF0Y2hlZFtjLmlkXSkgcmV0dXJuOwogICAgICAgICAgICBtYXRjaGVkW2MuaWRdID0gdHJ1ZTsKICAgICAgICAgICAgcG9vbHNbdF0ucHVzaCh7IGlkOmMuaWQsIG5hbWU6Yy5uYW1lLCBpbWFnZTpjLmltYWdlIH0pOwogICAgICAgICAgfSk7CiAgICAgICAgfSk7CiAgICAgICAgdmFyIGhpZ2hlclRpZXJDb3VudCA9IDA7CiAgICAgICAgZm9yICh2YXIgdDIgPSAxOyB0MiA8PSA2OyB0MisrKSBoaWdoZXJUaWVyQ291bnQgKz0gcG9vbHNbdDJdLmxlbmd0aDsKICAgICAgICBpZiAoIWhpZ2hlclRpZXJDb3VudCl7CiAgICAgICAgICAvLyBLaMO0bmcgbOG7jWMgxJHGsOG7o2MgZ8OsIOG7nyB04bqnbmcgPjAgY2hvIGLhu5kgbsOgeSAtPiBraMO0bmcgxJHhu6cgdGluIGPhuq15LAogICAgICAgICAgLy8gZ2nhu68gbmd1ecOqbiBraeG7g3UgcsO6dCDEkeG7gXUgY8WpIHRoYXkgdsOsIGTDuW5nIGThu68gbGnhu4d1IG7hu61hIHbhu51pLgogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICAodG90YWxJZHMgfHwgW10pLmZvckVhY2goZnVuY3Rpb24oaWQpewogICAgICAgICAgaWYgKCFtYXRjaGVkW2lkXSkgcG9vbHNbMF0ucHVzaCh7IGlkOmlkIH0pOwogICAgICAgIH0pOwogICAgICAgIHN0YXRlLnRpZXJQb29scyA9IHBvb2xzOwogICAgICB9KS5jYXRjaChmdW5jdGlvbigpe30pOwogICAgfSk7CiAgfQoKICAvLyBSw7p0IG3hu5l0IElEIHRo4bq7IHRoZW8gdHLhu41uZyBz4buRIHThuqduZyDEkeG7mSBoaeG6v20gbuG6v3UgxJHDoyBjw7MgZOG7ryBsaeG7h3U7IG7hur91CiAgLy8gY2jGsGEgY8OzICjEkWFuZyB04bqjaSwgaG/hurdjIEFQSSBraMO0bmcgaOG7lyB0cuG7oyBjaG8gYuG7mSBuw6B5KSB0aMOsIHLDunQgxJHhu4F1CiAgLy8gdHJvbmcgdG/DoG4gYuG7mSBwb29sIG5oxrAgY8OhY2ggY8WpIOKAlCBsdcO0biBjw7MgbeG7mXQgbOG7sWEgY2jhu41uIGjhu6NwIGzhu4cgxJHhu4MgdHLhuqMgduG7gS4KICBmdW5jdGlvbiBwaWNrQ2FyZElkKGJvb3N0KXsKICAgIHZhciB0cCA9IHN0YXRlLnRpZXJQb29sczsKICAgIGlmICh0cCl7CiAgICAgIHZhciBhdmFpbCA9IFtdLCB0b3RhbCA9IDA7CiAgICAgIGZvciAodmFyIHQgPSAwOyB0IDw9IDY7IHQrKyl7CiAgICAgICAgaWYgKHRwW3RdICYmIHRwW3RdLmxlbmd0aCl7CiAgICAgICAgICB2YXIgd2VpZ2h0ID0gVElFUl9XRUlHSFRTW3RdOwogICAgICAgICAgaWYgKGJvb3N0ICYmIGJvb3N0Lmx1Y2t5ICYmIHQgPj0gMykgd2VpZ2h0ICo9IDM7CiAgICAgICAgICBpZiAoYm9vc3QgJiYgYm9vc3QucmFyZSAmJiB0ID49IDMpIHdlaWdodCAqPSAyOwogICAgICAgICAgYXZhaWwucHVzaCh7IHQ6dCwgdzp3ZWlnaHQgfSk7CiAgICAgICAgICB0b3RhbCArPSB3ZWlnaHQ7CiAgICAgICAgfQogICAgICB9CiAgICAgIGlmIChhdmFpbC5sZW5ndGgpewogICAgICAgIHZhciByID0gTWF0aC5yYW5kb20oKSAqIHRvdGFsLCBhY2MgPSAwLCBjaG9zZW4gPSBhdmFpbFthdmFpbC5sZW5ndGggLSAxXS50OwogICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYXZhaWwubGVuZ3RoOyBpKyspewogICAgICAgICAgYWNjICs9IGF2YWlsW2ldLnc7CiAgICAgICAgICBpZiAociA8PSBhY2MpeyBjaG9zZW4gPSBhdmFpbFtpXS50OyBicmVhazsgfQogICAgICAgIH0KICAgICAgICB2YXIgYnVja2V0ID0gdHBbY2hvc2VuXTsKICAgICAgICByZXR1cm4gYnVja2V0W01hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGJ1Y2tldC5sZW5ndGgpXS5pZDsKICAgICAgfQogICAgfQogICAgcmV0dXJuIHBpY2tSYW5kb20oc3RhdGUucG9vbCkuaWQ7CiAgfQoKICBmdW5jdGlvbiBwaWNrR3VhcmFudGVlZEhpZ2hJZCgpewogICAgdmFyIHRwID0gc3RhdGUudGllclBvb2xzOwogICAgaWYgKHRwKXsKICAgICAgdmFyIGNob2ljZXMgPSBbXTsKICAgICAgZm9yICh2YXIgdCA9IDQ7IHQgPD0gNjsgdCsrKSBpZiAodHBbdF0gJiYgdHBbdF0ubGVuZ3RoKSBjaG9pY2VzID0gY2hvaWNlcy5jb25jYXQodHBbdF0pOwogICAgICBpZiAoY2hvaWNlcy5sZW5ndGgpIHJldHVybiBwaWNrUmFuZG9tKGNob2ljZXMpLmlkOwogICAgfQogICAgcmV0dXJuIG51bGw7CiAgfQoKICBmdW5jdGlvbiBwcmljZVVTRChjYXJkKXsKICAgIHZhciBwID0gY2FyZCAmJiBjYXJkLnByaWNpbmc7CiAgICBpZiAoIXApIHJldHVybiBudWxsOwogICAgdmFyIGJlc3QgPSBudWxsLCBrLCB2LCBtOwogICAgaWYgKHAudGNncGxheWVyKXsKICAgICAgZm9yIChrIGluIHAudGNncGxheWVyKXsKICAgICAgICB2ID0gcC50Y2dwbGF5ZXJba107CiAgICAgICAgaWYgKCF2IHx8IHR5cGVvZiB2ICE9PSAnb2JqZWN0JykgY29udGludWU7CiAgICAgICAgbSA9IHYubWFya2V0UHJpY2UgIT0gbnVsbCA/IHYubWFya2V0UHJpY2UKICAgICAgICAgIDogKHYubWlkUHJpY2UgIT0gbnVsbCA/IHYubWlkUHJpY2UgOiB2Lmxvd1ByaWNlKTsKICAgICAgICBpZiAodHlwZW9mIG0gPT09ICdudW1iZXInICYmIG0gPiAwICYmIChiZXN0ID09PSBudWxsIHx8IG0gPiBiZXN0KSkgYmVzdCA9IG07CiAgICAgIH0KICAgICAgaWYgKGJlc3QgIT09IG51bGwpIHJldHVybiBiZXN0OwogICAgfQogICAgaWYgKHAuY2FyZG1hcmtldCl7CiAgICAgIHZhciBjbSA9IHAuY2FyZG1hcmtldDsKICAgICAgdmFyIGNhbmRpZGF0ZXMgPSBbY20udHJlbmQsIGNtLmF2ZywgY20uYXZnNywgY20uYXZnMzAsIGNtLmF2ZzEsIGNtLmxvd107CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FuZGlkYXRlcy5sZW5ndGg7IGkrKyl7CiAgICAgICAgaWYgKHR5cGVvZiBjYW5kaWRhdGVzW2ldID09PSAnbnVtYmVyJyAmJiBjYW5kaWRhdGVzW2ldID4gMCkgcmV0dXJuIGNhbmRpZGF0ZXNbaV0gKiBFVVJfVVNEOwogICAgICB9CiAgICB9CiAgICByZXR1cm4gbnVsbDsKICB9CgogIGZ1bmN0aW9uIG1vbmV5KG4peyByZXR1cm4gJyQnICsgbi50b0ZpeGVkKDIpOyB9CiAgZnVuY3Rpb24gcGN0KG4peyByZXR1cm4gKG4gPj0gMCA/ICcrJyA6ICcnKSArIG4udG9GaXhlZCgwKSArICclJzsgfQoKICAvLyAtLS0tLS0tLS0tIEzGr1UgQuG7mCBTxq9VIFThuqxQIChsb2NhbFN0b3JhZ2UpIC0tLS0tLS0tLS0KICB2YXIgTFNfS0VZID0gJ3Bva2VodWIuY29sbGVjdGlvbi52Mic7CiAgdmFyIGRiID0gbnVsbDsKICBmdW5jdGlvbiBsb2FkREIoKXsKICAgIGlmIChkYikgcmV0dXJuIGRiOwogICAgdmFyIHJhdyA9IG51bGw7CiAgICB0cnkgeyByYXcgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmdldEl0ZW0oTFNfS0VZKTsgfSBjYXRjaChlKXt9CiAgICBpZiAocmF3KXsgdHJ5IHsgZGIgPSBKU09OLnBhcnNlKHJhdyk7IH0gY2F0Y2goZSl7IGRiID0gbnVsbDsgfSB9CiAgICBpZiAoIWRiIHx8IHR5cGVvZiBkYiAhPT0gJ29iamVjdCcpIGRiID0ge307CiAgICBpZiAoIWRiLmNhcmRzIHx8IHR5cGVvZiBkYi5jYXJkcyAhPT0gJ29iamVjdCcpIGRiLmNhcmRzID0ge307CiAgICBpZiAoIWRiLmNoYWxsZW5nZXMgfHwgdHlwZW9mIGRiLmNoYWxsZW5nZXMgIT09ICdvYmplY3QnKSBkYi5jaGFsbGVuZ2VzID0ge307CiAgICBpZiAodHlwZW9mIGRiLnBhY2tzICE9PSAnbnVtYmVyJykgZGIucGFja3MgPSAwOwogICAgaWYgKHR5cGVvZiBkYi5zcGVudCAhPT0gJ251bWJlcicpIGRiLnNwZW50ID0gMDsKICAgIGlmICh0eXBlb2YgZGIucGFja1ByaWNlICE9PSAnbnVtYmVyJykgZGIucGFja1ByaWNlID0gREVGQVVMVF9QQUNLX1BSSUNFOwogICAgaWYgKHR5cGVvZiBkYi5iZXN0THVjayAhPT0gJ251bWJlcicpIGRiLmJlc3RMdWNrID0gMDsKICAgIHJldHVybiBkYjsKICB9CiAgZnVuY3Rpb24gc2F2ZURCKCl7CiAgICB0cnkgeyB3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0oTFNfS0VZLCBKU09OLnN0cmluZ2lmeShkYikpOyByZXR1cm4gdHJ1ZTsgfQogICAgY2F0Y2goZSl7IHJldHVybiBmYWxzZTsgfQogIH0KCiAgZnVuY3Rpb24gcmVjb3JkUGFja3MoY2FyZHMsIHBhY2tzKXsKICAgIHZhciBkID0gbG9hZERCKCk7CiAgICBkLnBhY2tzICs9IHBhY2tzOwogICAgZC5zcGVudCArPSBwYWNrcyAqIGQucGFja1ByaWNlOwogICAgY2FyZHMuZm9yRWFjaChmdW5jdGlvbihjKXsKICAgICAgaWYgKCFjIHx8ICFjLmlkKSByZXR1cm47CiAgICAgIHZhciB1c2QgPSBwcmljZVVTRChjKTsKICAgICAgdmFyIGUgPSBkLmNhcmRzW2MuaWRdOwogICAgICB2YXIgc2V0SWQgPSAoYy5zZXQgJiYgYy5zZXQuaWQpIHx8IChzdGF0ZS5jdXJyZW50ICYmIHN0YXRlLmN1cnJlbnQuaWQpIHx8ICcnOwogICAgICB2YXIgc2V0TmFtZSA9IChjLnNldCAmJiBjLnNldC5uYW1lKSB8fCAoc3RhdGUuY3VycmVudCAmJiBzdGF0ZS5jdXJyZW50Lm5hbWUpIHx8ICcnOwogICAgICB2YXIgbG9jYWxJZCA9IGMubG9jYWxJZCAhPSBudWxsID8gU3RyaW5nKGMubG9jYWxJZCkgOiAnJzsKCiAgICAgIGlmIChlKXsKICAgICAgICBlLm4rKzsKICAgICAgICBpZiAodXNkICE9IG51bGwpIGUucHJpY2UgPSB1c2Q7CiAgICAgICAgaWYgKHNldElkICYmICFlLnNldElkKSBlLnNldElkID0gc2V0SWQ7CiAgICAgICAgaWYgKHNldE5hbWUgJiYgIWUuc2V0KSBlLnNldCA9IHNldE5hbWU7CiAgICAgICAgaWYgKGxvY2FsSWQgJiYgIWUubG9jYWxJZCkgZS5sb2NhbElkID0gbG9jYWxJZDsKICAgICAgICBpZiAoYy5pbWFnZSAmJiAhZS5pbWcpIGUuaW1nID0gYy5pbWFnZTsKICAgICAgICBpZiAoYy5yYXJpdHkgJiYgIWUucmFyaXR5KSBlLnJhcml0eSA9IGMucmFyaXR5OwogICAgICAgIGlmIChlLnRpZXIgPT0gbnVsbCkgZS50aWVyID0gcmFyaXR5VGllcihjLnJhcml0eSkudGllcjsKICAgICAgfSBlbHNlIHsKICAgICAgICBkLmNhcmRzW2MuaWRdID0gewogICAgICAgICAgaWQ6IGMuaWQsIG5hbWU6IGMubmFtZSB8fCAnJywgaW1nOiBjLmltYWdlIHx8ICcnLAogICAgICAgICAgcmFyaXR5OiBjLnJhcml0eSB8fCAnJywgdGllcjogcmFyaXR5VGllcihjLnJhcml0eSkudGllciwKICAgICAgICAgIHByaWNlOiB1c2QsIG46IDEsIHNldDogc2V0TmFtZSwgc2V0SWQ6IHNldElkLCBsb2NhbElkOiBsb2NhbElkLAogICAgICAgICAgdDogRGF0ZS5ub3coKQogICAgICAgIH07CiAgICAgIH0KICAgIH0pOwogICAgc2F2ZURCKCk7CiAgICBwaExiU3luY1N0YXRzKCk7CiAgICB1cGRhdGVDb2xCYWRnZSgpOwogICAgdXBkYXRlQ3VycmVudFNldFByb2dyZXNzKCk7CiAgICBpZiAoc3RhdGUuZXhwYW5zaW9ucyAmJiBzdGF0ZS5leHBhbnNpb25zLmxlbmd0aCl7CiAgICAgIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoZWxzLmdyaWQucXVlcnlTZWxlY3RvckFsbCgnLnBoLWV4cC1jYXJkJyksIGZ1bmN0aW9uKGNhcmRFbCl7CiAgICAgICAgdmFyIGlkID0gY2FyZEVsLmRhdGFzZXQuc2V0SWQ7CiAgICAgICAgaWYgKCFpZCkgcmV0dXJuOwogICAgICAgIHZhciBmb3VuZCA9IHN0YXRlLmV4cGFuc2lvbnMuZmluZChmdW5jdGlvbih4KXsgcmV0dXJuIHguc2V0ICYmIFN0cmluZyh4LnNldC5pZCkgPT09IFN0cmluZyhpZCk7IH0pOwogICAgICAgIGlmIChmb3VuZCkgdXBkYXRlRXhwYW5zaW9uUHJvZ3Jlc3NDYXJkKGNhcmRFbCwgZm91bmQuc2V0KTsKICAgICAgfSk7CiAgICB9CiAgICB1cGRhdGVDaGFsbGVuZ2VCYWRnZSgpOwogIH0KCiAgZnVuY3Rpb24gZW50cnlCZWxvbmdzVG9TZXQoZSwgc2V0KXsKICAgIGlmICghZSB8fCAhc2V0KSByZXR1cm4gZmFsc2U7CiAgICBpZiAoZS5zZXRJZCAmJiBTdHJpbmcoZS5zZXRJZCkgPT09IFN0cmluZyhzZXQuaWQpKSByZXR1cm4gdHJ1ZTsKICAgIHJldHVybiAhIShlLnNldCAmJiBzZXQubmFtZSAmJiBlLnNldCA9PT0gc2V0Lm5hbWUpOwogIH0KCiAgZnVuY3Rpb24gc2V0UHJvZ3Jlc3NEYXRhKHNldCwgcG9vbCl7CiAgICB2YXIgZCA9IGxvYWREQigpLCBvd25lZCA9IHt9LCBlbnRyaWVzID0gW107CiAgICBPYmplY3Qua2V5cyhkLmNhcmRzKS5mb3JFYWNoKGZ1bmN0aW9uKGlkKXsKICAgICAgdmFyIGUgPSBkLmNhcmRzW2lkXTsKICAgICAgaWYgKGVudHJ5QmVsb25nc1RvU2V0KGUsIHNldCkpewogICAgICAgIG93bmVkW2lkXSA9IGU7CiAgICAgICAgZW50cmllcy5wdXNoKGUpOwogICAgICB9CiAgICB9KTsKCiAgICB2YXIgdG90YWwgPSBOdW1iZXIoc2V0ICYmIHNldC5jYXJkQ291bnQgJiYgc2V0LmNhcmRDb3VudC50b3RhbCkgfHwgKHBvb2wgPyBwb29sLmxlbmd0aCA6IDApOwogICAgdmFyIHVuaXF1ZSA9IE9iamVjdC5rZXlzKG93bmVkKS5sZW5ndGg7CiAgICB2YXIgcGN0RG9uZSA9IHRvdGFsID4gMCA/IE1hdGgucm91bmQodW5pcXVlIC8gdG90YWwgKiAxMDApIDogMDsKCiAgICB2YXIgbWlzc2luZyA9IFtdOwogICAgaWYgKEFycmF5LmlzQXJyYXkocG9vbCkgJiYgcG9vbC5sZW5ndGgpewogICAgICBwb29sLmZvckVhY2goZnVuY3Rpb24oYyl7CiAgICAgICAgaWYgKGMgJiYgYy5pZCAmJiAhb3duZWRbYy5pZF0pIG1pc3NpbmcucHVzaChjKTsKICAgICAgfSk7CiAgICB9CiAgICByZXR1cm4ge3RvdGFsOnRvdGFsLCB1bmlxdWU6dW5pcXVlLCBwY3Q6cGN0RG9uZSwgbWlzc2luZzptaXNzaW5nLCBlbnRyaWVzOmVudHJpZXN9OwogIH0KCiAgZnVuY3Rpb24gdXBkYXRlRXhwYW5zaW9uUHJvZ3Jlc3NDYXJkKGNhcmRFbCwgc2V0KXsKICAgIGlmICghY2FyZEVsIHx8ICFzZXQpIHJldHVybjsKICAgIHZhciBkID0gc2V0UHJvZ3Jlc3NEYXRhKHNldCk7CiAgICB2YXIgcHJvZyA9IGNhcmRFbC5xdWVyeVNlbGVjdG9yKCcucGgtZXhwLXByb2dyZXNzJyk7CiAgICB2YXIgZmlsbCA9IGNhcmRFbC5xdWVyeVNlbGVjdG9yKCcucGgtZXhwLXByb2dyZXNzLWZpbGwnKTsKICAgIHZhciBtZXRhID0gY2FyZEVsLnF1ZXJ5U2VsZWN0b3IoJy5waC1leHAtcHJvZ3Jlc3MtbWV0YScpOwogICAgaWYgKCFwcm9nIHx8ICFmaWxsIHx8ICFtZXRhKSByZXR1cm47CiAgICBwcm9nLmhpZGRlbiA9IGZhbHNlOwogICAgcHJvZy5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKICAgIGZpbGwuc3R5bGUud2lkdGggPSBNYXRoLm1pbigxMDAsIGQucGN0KSArICclJzsKICAgIG1ldGEudGV4dENvbnRlbnQgPSBkLnVuaXF1ZSArICcvJyArIGQudG90YWwgKyAnIGzDoSDCtyAnICsgZC5wY3QgKyAnJSc7CiAgfQoKICBmdW5jdGlvbiB1cGRhdGVDdXJyZW50U2V0UHJvZ3Jlc3MoKXsKICAgIGlmICghZWxzLnNldFByb2dyZXNzIHx8ICFzdGF0ZS5jdXJyZW50KSByZXR1cm47CiAgICB2YXIgZCA9IHNldFByb2dyZXNzRGF0YShzdGF0ZS5jdXJyZW50LCBzdGF0ZS5wb29sKTsKICAgIGVscy5zZXRQcm9ncmVzcy5oaWRkZW4gPSBmYWxzZTsKICAgIGVscy5zZXRQcm9ncmVzc1RpdGxlLnRleHRDb250ZW50ID0gc3RhdGUuY3VycmVudC5uYW1lIHx8ICdC4buZIHRo4bq7IGhp4buHbiB04bqhaSc7CiAgICBlbHMuc2V0UHJvZ3Jlc3NNZXRhLnRleHRDb250ZW50ID0gZC51bmlxdWUgKyAnLycgKyBkLnRvdGFsICsgJyBsw6EnOwogICAgZWxzLnNldFByb2dyZXNzQmFyLnN0eWxlLndpZHRoID0gTWF0aC5taW4oMTAwLCBkLnBjdCkgKyAnJSc7CiAgICBlbHMuc2V0UHJvZ3Jlc3NQY3QudGV4dENvbnRlbnQgPSBkLnBjdCArICclJzsKICB9CgogIGZ1bmN0aW9uIHJlbmRlckN1cnJlbnRTZXRNaXNzaW5nSW5Db2xsZWN0aW9uKCl7CiAgICBpZiAoIXN0YXRlLmN1cnJlbnQgfHwgIXN0YXRlLnBvb2wgfHwgIXN0YXRlLnBvb2wubGVuZ3RoKSByZXR1cm4gbnVsbDsKICAgIHZhciBkID0gc2V0UHJvZ3Jlc3NEYXRhKHN0YXRlLmN1cnJlbnQsIHN0YXRlLnBvb2wpOwogICAgdmFyIHdyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHdyYXAuY2xhc3NOYW1lID0gJ3BoLWNvbGxlY3Rpb24tc2V0LWRldGFpbCc7CgogICAgdmFyIGhlYWQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGhlYWQuY2xhc3NOYW1lID0gJ3BoLWNvbGxlY3Rpb24tc2V0LWRldGFpbC1oZWFkJzsKICAgIHZhciBsZWZ0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICB2YXIgdGl0bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHRpdGxlLmNsYXNzTmFtZSA9ICdwaC1jb2xsZWN0aW9uLXNldC1kZXRhaWwtdGl0bGUnOwogICAgdGl0bGUudGV4dENvbnRlbnQgPSBzdGF0ZS5jdXJyZW50Lm5hbWUgfHwgJ0Lhu5kgxJFhbmcgbeG7nyc7CiAgICB2YXIgbWV0YSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbWV0YS5jbGFzc05hbWUgPSAncGgtY29sbGVjdGlvbi1zZXQtZGV0YWlsLW1ldGEnOwogICAgbWV0YS50ZXh0Q29udGVudCA9IGQudW5pcXVlICsgJy8nICsgZC50b3RhbCArICcgbMOhIMSRw6MgY8OzJzsKICAgIGxlZnQuYXBwZW5kQ2hpbGQodGl0bGUpOwogICAgbGVmdC5hcHBlbmRDaGlsZChtZXRhKTsKICAgIHZhciBwY3RFbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgcGN0RWwuY2xhc3NOYW1lID0gJ3BoLWNvbGxlY3Rpb24tc2V0LWRldGFpbC1wY3QnOwogICAgcGN0RWwudGV4dENvbnRlbnQgPSBkLnBjdCArICclJzsKICAgIGhlYWQuYXBwZW5kQ2hpbGQobGVmdCk7CiAgICBoZWFkLmFwcGVuZENoaWxkKHBjdEVsKTsKICAgIHdyYXAuYXBwZW5kQ2hpbGQoaGVhZCk7CgogICAgdmFyIHRyYWNrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICB0cmFjay5jbGFzc05hbWUgPSAncGgtY29sbGVjdGlvbi1zZXQtZGV0YWlsLXRyYWNrJzsKICAgIHZhciBmaWxsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBmaWxsLmNsYXNzTmFtZSA9ICdwaC1jb2xsZWN0aW9uLXNldC1kZXRhaWwtZmlsbCc7CiAgICBmaWxsLnN0eWxlLndpZHRoID0gTWF0aC5taW4oMTAwLCBkLnBjdCkgKyAnJSc7CiAgICB0cmFjay5hcHBlbmRDaGlsZChmaWxsKTsKICAgIHdyYXAuYXBwZW5kQ2hpbGQodHJhY2spOwoKICAgIHZhciBtaCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbWguY2xhc3NOYW1lID0gJ3BoLWNvbGxlY3Rpb24tbWlzc2luZy1oZWFkJzsKICAgIHZhciBtYyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgIG1jLmNsYXNzTmFtZSA9ICdwaC1jb2xsZWN0aW9uLW1pc3NpbmctY291bnQnOwogICAgbWMudGV4dENvbnRlbnQgPSBkLm1pc3NpbmcubGVuZ3RoID8gJ0PDsm4gdGhp4bq/dSAnICsgZC5taXNzaW5nLmxlbmd0aCArICcgbMOhJyA6ICfEkMOjIGhvw6BuIHRoaeG7h24gMTAwJSc7CiAgICB2YXIgbW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICBtbi5jbGFzc05hbWUgPSAncGgtY29sbGVjdGlvbi1taXNzaW5nLW5vdGUnOwogICAgbW4udGV4dENvbnRlbnQgPSAnQuG6pW0gdsOgbyBsw6EgxJHhu4MgeGVtIGNoaSB0aeG6v3QnOwogICAgbWguYXBwZW5kQ2hpbGQobWMpOwogICAgbWguYXBwZW5kQ2hpbGQobW4pOwogICAgd3JhcC5hcHBlbmRDaGlsZChtaCk7CgogICAgaWYgKCFkLm1pc3NpbmcubGVuZ3RoKXsKICAgICAgdmFyIGRvbmUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgZG9uZS5jbGFzc05hbWUgPSAncGgtbWlzc2luZy1lbXB0eSc7CiAgICAgIGRvbmUuaW5uZXJIVE1MID0gJzxzcGFuPuKckzwvc3Bhbj4gQuG6oW4gxJHDoyBzxrB1IHThuqdtIMSR4bunIHRvw6BuIGLhu5kgbMOhIHRyb25nIGLhu5kgbsOgeS4nOwogICAgICB3cmFwLmFwcGVuZENoaWxkKGRvbmUpOwogICAgICByZXR1cm4gd3JhcDsKICAgIH0KCiAgICB2YXIgZ3JpZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgZ3JpZC5jbGFzc05hbWUgPSAncGgtbWlzc2luZy1ncmlkJzsKICAgIGQubWlzc2luZy5mb3JFYWNoKGZ1bmN0aW9uKGMpewogICAgICB2YXIgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2J1dHRvbicpOwogICAgICBpdGVtLnR5cGUgPSAnYnV0dG9uJzsKICAgICAgaXRlbS5jbGFzc05hbWUgPSAncGgtbWlzc2luZy1jYXJkJzsKICAgICAgaXRlbS50aXRsZSA9ICdDaMawYSBjw7MgwrcgJyArIChjLm5hbWUgfHwgJycpOwogICAgICB2YXIgaW1nV3JhcCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgaW1nV3JhcC5jbGFzc05hbWUgPSAncGgtbWlzc2luZy1pbWcnOwogICAgICBpZiAoYy5pbWFnZSl7CiAgICAgICAgdmFyIGltZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpOwogICAgICAgIGltZy5sb2FkaW5nID0gJ2xhenknOwogICAgICAgIGltZy5hbHQgPSBjLm5hbWUgfHwgJyc7CiAgICAgICAgaW1nLnNyYyA9IGMuaW1hZ2UgKyAnLycgKyBRVUFMSVRZOwogICAgICAgIGltZy5vbmVycm9yID0gZnVuY3Rpb24oKXsgaW1nLnJlbW92ZSgpOyBpbWdXcmFwLnRleHRDb250ZW50ID0gYy5uYW1lIHx8ICc/JzsgfTsKICAgICAgICBpbWdXcmFwLmFwcGVuZENoaWxkKGltZyk7CiAgICAgIH0gZWxzZSBpbWdXcmFwLnRleHRDb250ZW50ID0gYy5uYW1lIHx8ICc/JzsKICAgICAgdmFyIHR4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgdHh0LmNsYXNzTmFtZSA9ICdwaC1taXNzaW5nLW5hbWUnOwogICAgICB0eHQudGV4dENvbnRlbnQgPSBbYy5sb2NhbElkIHx8ICcnLCBjLm5hbWUgfHwgJ0NoxrBhIHLDtSB0w6puJ10uZmlsdGVyKEJvb2xlYW4pLmpvaW4oJyDCtyAnKTsKICAgICAgaXRlbS5hcHBlbmRDaGlsZChpbWdXcmFwKTsKICAgICAgaXRlbS5hcHBlbmRDaGlsZCh0eHQpOwogICAgICBpdGVtLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZnVuY3Rpb24oKXsKICAgICAgICBvcGVuQ2FyZE1vZGFsKGMsIHJhcml0eVRpZXIoYy5yYXJpdHkpLCBwcmljZVVTRChjKSk7CiAgICAgIH0pOwogICAgICBncmlkLmFwcGVuZENoaWxkKGl0ZW0pOwogICAgfSk7CiAgICB3cmFwLmFwcGVuZENoaWxkKGdyaWQpOwogICAgcmV0dXJuIHdyYXA7CiAgfQoKICBmdW5jdGlvbiBub3ZlbHR5TGFiZWwoY2FyZCwgYmVmb3JlREIpewogICAgdmFyIGQgPSBiZWZvcmVEQiB8fCBsb2FkREIoKTsKICAgIHZhciBlID0gZC5jYXJkc1tjYXJkICYmIGNhcmQuaWRdOwogICAgaWYgKCFlKSByZXR1cm4ge3RleHQ6J03hu5tpIScsIGNsczoncGgtbmV3J307CiAgICByZXR1cm4ge3RleHQ6J8OXJyArICgoTnVtYmVyKGUubikgfHwgMCkgKyAxKSwgY2xzOidwaC1kdXBsaWNhdGUnfTsKICB9CgogIGZ1bmN0aW9uIGFkZE5vdmVsdHlCYWRnZShmcm9udCwgbm92ZWx0eSl7CiAgICBpZiAoIWZyb250IHx8ICFub3ZlbHR5KSByZXR1cm47CiAgICB2YXIgYmFkZ2UgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICBiYWRnZS5jbGFzc05hbWUgPSAncGgtbm92ZWx0eS1iYWRnZSAnICsgbm92ZWx0eS5jbHM7CiAgICBiYWRnZS50ZXh0Q29udGVudCA9IG5vdmVsdHkudGV4dDsKICAgIGZyb250LmFwcGVuZENoaWxkKGJhZGdlKTsKICB9CgogIGZ1bmN0aW9uIGNvbGxlY3Rpb25TdGF0cygpewogICAgdmFyIGQgPSBsb2FkREIoKSwgaWRzID0gT2JqZWN0LmtleXMoZC5jYXJkcyk7CiAgICB2YXIgdmFsID0gMCwgY250ID0gMCwgcHJpY2VkID0gMCwgYmVzdCA9IG51bGwsIHRpZXJzID0gWzAsMCwwLDAsMCwwLDBdOwogICAgaWRzLmZvckVhY2goZnVuY3Rpb24oayl7CiAgICAgIHZhciBlID0gZC5jYXJkc1trXTsKICAgICAgY250ICs9IGUubjsKICAgICAgdGllcnNbTWF0aC5tYXgoMCwgTWF0aC5taW4oNiwgZS50aWVyIHx8IDApKV0gKz0gZS5uOwogICAgICBpZiAodHlwZW9mIGUucHJpY2UgPT09ICdudW1iZXInKXsKICAgICAgICB2YWwgKz0gZS5wcmljZSAqIGUubjsKICAgICAgICBwcmljZWQgKz0gZS5uOwogICAgICAgIGlmICghYmVzdCB8fCBlLnByaWNlID4gYmVzdC5wcmljZSkgYmVzdCA9IGU7CiAgICAgIH0KICAgIH0pOwogICAgcmV0dXJuIHsKICAgICAgdW5pcXVlOiBpZHMubGVuZ3RoLCBjYXJkczogY250LCB2YWx1ZTogdmFsLCBwcmljZWQ6IHByaWNlZCwKICAgICAgc3BlbnQ6IGQuc3BlbnQsIHBhY2tzOiBkLnBhY2tzLCBwYWNrUHJpY2U6IGQucGFja1ByaWNlLAogICAgICByb2k6IGQuc3BlbnQgPiAwID8gKHZhbCAtIGQuc3BlbnQpIC8gZC5zcGVudCAqIDEwMCA6IG51bGwsCiAgICAgIGJlc3Q6IGJlc3QsIHRpZXJzOiB0aWVycwogICAgfTsKICB9CgogIGZ1bmN0aW9uIHVwZGF0ZUNvbEJhZGdlKCl7CiAgICB2YXIgcyA9IGNvbGxlY3Rpb25TdGF0cygpOwogICAgZWxzLmNvbENvdW50LnRleHRDb250ZW50ID0gcy5jYXJkcyA/IFN0cmluZyhzLmNhcmRzKSA6ICcnOwogICAgZWxzLmNvbENvdW50LmhpZGRlbiA9ICFzLmNhcmRzOwogIH0KCiAgdmFyIFRJRVJfTkFNRVMgPSBbJ1RoxrDhu51uZycsJ1VuY29tbW9uJywnSGnhur9tJywnVWx0cmEnLCdGdWxsIEFydCAvIFN0YXInLCfEkOG6t2MgYmnhu4d0JywnQ2hhc2UnXTsKICBmdW5jdGlvbiB0aWVyTGFiZWxUZXh0KHRpZXIpeyByZXR1cm4gVElFUl9OQU1FU1tNYXRoLm1heCgwLCBNYXRoLm1pbig2LCB0aWVyKSldOyB9CgogIGZ1bmN0aW9uIHN0YXRCb3gobGFiZWwsIHZhbHVlLCBjbHMpewogICAgdmFyIGIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGIuY2xhc3NOYW1lID0gJ3BoLXN0YXQnICsgKGNscyA/ICcgJyArIGNscyA6ICcnKTsKICAgIHZhciB2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IHYuY2xhc3NOYW1lID0gJ3BoLXN0YXQtdic7IHYudGV4dENvbnRlbnQgPSB2YWx1ZTsKICAgIHZhciBsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGwuY2xhc3NOYW1lID0gJ3BoLXN0YXQtbCc7IGwudGV4dENvbnRlbnQgPSBsYWJlbDsKICAgIGIuYXBwZW5kQ2hpbGQodik7IGIuYXBwZW5kQ2hpbGQobCk7CiAgICByZXR1cm4gYjsKICB9CgogIGZ1bmN0aW9uIHJvaUNsYXNzKHJvaSl7CiAgICBpZiAocm9pID09IG51bGwpIHJldHVybiAnJzsKICAgIHJldHVybiByb2kgPj0gMCA/ICdwaC1wb3MnIDogJ3BoLW5lZyc7CiAgfQoKICBmdW5jdGlvbiBjYXJkVGh1bWIoZW50cnksIG9uQ2xpY2spewogICAgdmFyIGNvbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgY29sLmNsYXNzTmFtZSA9ICdwaC10aHVtYic7CiAgICB2YXIgYm94ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBib3guY2xhc3NOYW1lID0gJ3BoLXRodW1iLWltZyc7CiAgICBib3guZGF0YXNldC50aWVyID0gU3RyaW5nKGVudHJ5LnRpZXIgfHwgMCk7CiAgICBpZiAoZW50cnkuaW1nKXsKICAgICAgdmFyIGltID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7CiAgICAgIGltLmxvYWRpbmcgPSAnbGF6eSc7CiAgICAgIGltLmFsdCA9IGVudHJ5Lm5hbWUgfHwgJyc7CiAgICAgIGltLnNyYyA9IGVudHJ5LmltZyArICcvJyArIFFVQUxJVFk7CiAgICAgIGltLm9uZXJyb3IgPSBmdW5jdGlvbigpeyBib3gudGV4dENvbnRlbnQgPSBlbnRyeS5uYW1lIHx8ICc/JzsgfTsKICAgICAgYm94LmFwcGVuZENoaWxkKGltKTsKICAgIH0gZWxzZSB7CiAgICAgIGJveC50ZXh0Q29udGVudCA9IGVudHJ5Lm5hbWUgfHwgJz8nOwogICAgfQogICAgaWYgKGVudHJ5Lm5ld0Zyb21TaG9wKXsKICAgICAgdmFyIHNob3BOZXcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICAgIHNob3BOZXcuY2xhc3NOYW1lID0gJ3BoLW5vdmVsdHktYmFkZ2UgcGgtbmV3IHBoLXNob3AtbmV3LWJhZGdlJzsKICAgICAgc2hvcE5ldy50ZXh0Q29udGVudCA9ICdN4bubaSc7CiAgICAgIGJveC5hcHBlbmRDaGlsZChzaG9wTmV3KTsKICAgIH0KICAgIHZhciBubSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbm0uY2xhc3NOYW1lID0gJ3BoLXRodW1iLW5hbWUnOwogICAgbm0udGV4dENvbnRlbnQgPSAoZW50cnkubmFtZSB8fCAnJykgKyAoZW50cnkubiA+IDEgPyAnIFx1MDBkNycgKyBlbnRyeS5uIDogJycpOwogICAgdmFyIHByID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBwci5jbGFzc05hbWUgPSAncGgtdGh1bWItcHJpY2UnICsgKHR5cGVvZiBlbnRyeS5wcmljZSA9PT0gJ251bWJlcicgPyAnJyA6ICcgcGgtY2FyZC1ub3ByaWNlJyk7CiAgICBwci50ZXh0Q29udGVudCA9IHR5cGVvZiBlbnRyeS5wcmljZSA9PT0gJ251bWJlcicgPyBtb25leShlbnRyeS5wcmljZSkgOiAnY2jGsGEgY8OzIGdpw6EnOwogICAgY29sLmFwcGVuZENoaWxkKGJveCk7IGNvbC5hcHBlbmRDaGlsZChubSk7IGNvbC5hcHBlbmRDaGlsZChwcik7CiAgICBpZiAob25DbGljayl7CiAgICAgIGNvbC5jbGFzc0xpc3QuYWRkKCdwaC1jbGlja2FibGUnKTsKICAgICAgY29sLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgb25DbGljayk7CiAgICB9CiAgICByZXR1cm4gY29sOwogIH0KCgogIGZ1bmN0aW9uIHJlbmRlclNldFByb2dyZXNzT3ZlcnZpZXcoKXsKICAgIGlmICghc3RhdGUuZXhwYW5zaW9ucyB8fCAhc3RhdGUuZXhwYW5zaW9ucy5sZW5ndGgpIHJldHVybiBudWxsOwogICAgdmFyIHdyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHdyYXAuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXNlY3Rpb24gcGgtc2V0LW92ZXJ2aWV3JzsKCiAgICB2YXIgdGl0bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHRpdGxlLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1zZWN0aW9uLXRpdGxlJzsKICAgIHRpdGxlLnRleHRDb250ZW50ID0gJ1Rp4bq/biDEkeG7mSB04burbmcgYuG7mSc7CiAgICB3cmFwLmFwcGVuZENoaWxkKHRpdGxlKTsKCiAgICB2YXIgYWN0aXZlID0gc3RhdGUuZXhwYW5zaW9ucy5maWx0ZXIoZnVuY3Rpb24oZW50cnkpewogICAgICB2YXIgZCA9IHNldFByb2dyZXNzRGF0YShlbnRyeS5zZXQpOwogICAgICByZXR1cm4gZC51bmlxdWUgPiAwIHx8IChzdGF0ZS5jdXJyZW50ICYmIFN0cmluZyhzdGF0ZS5jdXJyZW50LmlkKSA9PT0gU3RyaW5nKGVudHJ5LnNldC5pZCkpOwogICAgfSk7CiAgICBpZiAoIWFjdGl2ZS5sZW5ndGgpIHJldHVybiBudWxsOwoKICAgIHZhciBub3RlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBub3RlLmNsYXNzTmFtZSA9ICdwaC1jb2wtbm90ZSc7CiAgICBub3RlLnRleHRDb250ZW50ID0gJ0No4buJIGhp4buHbiBjw6FjIGLhu5kgYuG6oW4gxJHDoyBt4bufIGhv4bq3YyDEkWFuZyBt4bufOyBi4buZIGNoxrBhIGNo4bqhbSB04bubaSB24bqrbiBjaMawYSBjaGnhur9tIGNo4buXIHRyb25nIGRhbmggc8OhY2guJzsKICAgIHdyYXAuYXBwZW5kQ2hpbGQobm90ZSk7CgogICAgdmFyIGdyaWQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGdyaWQuY2xhc3NOYW1lID0gJ3BoLXNldC1vdmVydmlldy1ncmlkJzsKCiAgICBhY3RpdmUuc29ydChmdW5jdGlvbihhLGIpewogICAgICB2YXIgZGEgPSBzZXRQcm9ncmVzc0RhdGEoYS5zZXQpLCBkYmIgPSBzZXRQcm9ncmVzc0RhdGEoYi5zZXQpOwogICAgICBpZiAoZGJiLnBjdCAhPT0gZGEucGN0KSByZXR1cm4gZGJiLnBjdCAtIGRhLnBjdDsKICAgICAgcmV0dXJuIChhLnNldC5uYW1lIHx8ICcnKS5sb2NhbGVDb21wYXJlKGIuc2V0Lm5hbWUgfHwgJycpOwogICAgfSkuZm9yRWFjaChmdW5jdGlvbihlbnRyeSl7CiAgICAgIHZhciBzID0gZW50cnkuc2V0OwogICAgICB2YXIgZCA9IHNldFByb2dyZXNzRGF0YShzKTsKICAgICAgdmFyIGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgaXRlbS5jbGFzc05hbWUgPSAncGgtc2V0LW92ZXJ2aWV3LWl0ZW0nOwoKICAgICAgdmFyIHRvcCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICB0b3AuY2xhc3NOYW1lID0gJ3BoLXNldC1vdmVydmlldy10b3AnOwogICAgICB2YXIgbmFtZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBuYW1lLmNsYXNzTmFtZSA9ICdwaC1zZXQtb3ZlcnZpZXctbmFtZSc7CiAgICAgIG5hbWUudGV4dENvbnRlbnQgPSBzLm5hbWUgfHwgJ0Lhu5kga2jDtG5nIHTDqm4nOwogICAgICB2YXIgcGN0RWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgcGN0RWwuY2xhc3NOYW1lID0gJ3BoLXNldC1vdmVydmlldy1wY3QnOwogICAgICBwY3RFbC50ZXh0Q29udGVudCA9IGQucGN0ICsgJyUnOwogICAgICB0b3AuYXBwZW5kQ2hpbGQobmFtZSk7CiAgICAgIHRvcC5hcHBlbmRDaGlsZChwY3RFbCk7CgogICAgICB2YXIgbWV0YSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBtZXRhLmNsYXNzTmFtZSA9ICdwaC1zZXQtb3ZlcnZpZXctbWV0YSc7CiAgICAgIG1ldGEudGV4dENvbnRlbnQgPSBkLnVuaXF1ZSArICcvJyArIGQudG90YWwgKyAnIGzDoSDCtyAnICsgKGQubWlzc2luZy5sZW5ndGggPyAndGhp4bq/dSAnICsgZC5taXNzaW5nLmxlbmd0aCA6ICfEkeG7pyBi4buZJyk7CgogICAgICB2YXIgdHJhY2sgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgdHJhY2suY2xhc3NOYW1lID0gJ3BoLWV4cC1wcm9ncmVzcy10cmFjayc7CiAgICAgIHZhciBmaWxsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOwogICAgICBmaWxsLmNsYXNzTmFtZSA9ICdwaC1leHAtcHJvZ3Jlc3MtZmlsbCc7CiAgICAgIGZpbGwuc3R5bGUud2lkdGggPSBNYXRoLm1pbigxMDAsIGQucGN0KSArICclJzsKICAgICAgdHJhY2suYXBwZW5kQ2hpbGQoZmlsbCk7CgogICAgICBpdGVtLmFwcGVuZENoaWxkKHRvcCk7CiAgICAgIGl0ZW0uYXBwZW5kQ2hpbGQobWV0YSk7CiAgICAgIGl0ZW0uYXBwZW5kQ2hpbGQodHJhY2spOwoKICAgICAgaWYgKHN0YXRlLmN1cnJlbnQgJiYgU3RyaW5nKHN0YXRlLmN1cnJlbnQuaWQpID09PSBTdHJpbmcocy5pZCkpewogICAgICAgIHZhciBjdXJyZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOwogICAgICAgIGN1cnJlbnQuY2xhc3NOYW1lID0gJ3BoLWN1cnJlbnQtY2hpcCc7CiAgICAgICAgY3VycmVudC50ZXh0Q29udGVudCA9ICfEkGFuZyBt4bufJzsKICAgICAgICBpdGVtLmFwcGVuZENoaWxkKGN1cnJlbnQpOwogICAgICB9CiAgICAgIGdyaWQuYXBwZW5kQ2hpbGQoaXRlbSk7CiAgICB9KTsKICAgIHdyYXAuYXBwZW5kQ2hpbGQoZ3JpZCk7CiAgICByZXR1cm4gd3JhcDsKICB9CgogIGZ1bmN0aW9uIHJlbmRlckNvbGxlY3Rpb24oKXsKICAgIHZhciBzID0gY29sbGVjdGlvblN0YXRzKCk7CiAgICB2YXIgZCA9IGxvYWREQigpOwogICAgZWxzLmNvbEJvZHkudGV4dENvbnRlbnQgPSAnJzsKCiAgICB2YXIgb3ZlcnZpZXdCbG9jayA9IHJlbmRlclNldFByb2dyZXNzT3ZlcnZpZXcoKTsKICAgIGlmIChvdmVydmlld0Jsb2NrKSBlbHMuY29sQm9keS5hcHBlbmRDaGlsZChvdmVydmlld0Jsb2NrKTsKCiAgICB2YXIgY3VycmVudFNldERldGFpbCA9IHJlbmRlckN1cnJlbnRTZXRNaXNzaW5nSW5Db2xsZWN0aW9uKCk7CiAgICBpZiAoY3VycmVudFNldERldGFpbCkgZWxzLmNvbEJvZHkuYXBwZW5kQ2hpbGQoY3VycmVudFNldERldGFpbCk7CgogICAgdmFyIHN0YXRzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBzdGF0cy5jbGFzc05hbWUgPSAncGgtc3RhdC1ncmlkJzsKICAgIHN0YXRzLmFwcGVuZENoaWxkKHN0YXRCb3goJ0fDs2kgxJHDoyBt4bufJywgU3RyaW5nKHMucGFja3MpKSk7CiAgICBzdGF0cy5hcHBlbmRDaGlsZChzdGF0Qm94KCfEkMOjIGNoaScsIG1vbmV5KHMuc3BlbnQpKSk7CiAgICBzdGF0cy5hcHBlbmRDaGlsZChzdGF0Qm94KCdHacOhIHRy4buLIHRodSDEkcaw4bujYycsIG1vbmV5KHMudmFsdWUpKSk7CiAgICBzdGF0cy5hcHBlbmRDaGlsZChzdGF0Qm94KCdST0knLCBzLnJvaSA9PSBudWxsID8gJ+KAlCcgOiBwY3Qocy5yb2kpLCByb2lDbGFzcyhzLnJvaSkpKTsKICAgIHN0YXRzLmFwcGVuZENoaWxkKHN0YXRCb3goJ0zDoSDEkeG7mWMgbmjhuqV0JywgU3RyaW5nKHMudW5pcXVlKSkpOwogICAgc3RhdHMuYXBwZW5kQ2hpbGQoc3RhdEJveCgnVOG7lW5nIHPhu5EgbMOhJywgU3RyaW5nKHMuY2FyZHMpKSk7CiAgICBlbHMuY29sQm9keS5hcHBlbmRDaGlsZChzdGF0cyk7CgogICAgdmFyIG5vdGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIG5vdGUuY2xhc3NOYW1lID0gJ3BoLWNvbC1ub3RlJzsKICAgIG5vdGUudGV4dENvbnRlbnQgPSBzLnByaWNlZCA8IHMuY2FyZHMKICAgICAgPyAnQ8OzICcgKyAocy5jYXJkcyAtIHMucHJpY2VkKSArICcgbMOhIGNoxrBhIGPDsyBk4buvIGxp4buHdSBnacOhIG7Dqm4ga2jDtG5nIMSRxrDhu6NjIHTDrW5oIHbDoG8gdOG7lW5nIGdpw6EgdHLhu4suJwogICAgICA6ICdHacOhIGzhuqV5IHThu6sgVENHZGV4ICjGsHUgdGnDqm4gVENHcGxheWVyIHRoZW8gVVNEKS4nOwogICAgZWxzLmNvbEJvZHkuYXBwZW5kQ2hpbGQobm90ZSk7CgogICAgLy8gZ2nDoSBt4buXaSBnw7NpCiAgICB2YXIgY3RsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBjdGwuY2xhc3NOYW1lID0gJ3BoLWNvbC1jdGwnOwogICAgdmFyIGxhYiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyk7CiAgICBsYWIudGV4dENvbnRlbnQgPSAnR2nDoSAxIGfDs2kgKFVTRCknOwogICAgdmFyIGlucCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7CiAgICBpbnAudHlwZSA9ICdudW1iZXInOyBpbnAubWluID0gJzAnOyBpbnAuc3RlcCA9ICcwLjAxJzsKICAgIGlucC52YWx1ZSA9IFN0cmluZyhkLnBhY2tQcmljZSk7CiAgICBpbnAuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgZnVuY3Rpb24oKXsKICAgICAgdmFyIHYgPSBwYXJzZUZsb2F0KGlucC52YWx1ZSk7CiAgICAgIGlmIChpc05hTih2KSB8fCB2IDwgMCkgcmV0dXJuOwogICAgICBkLnBhY2tQcmljZSA9IHY7IHNhdmVEQigpOwogICAgICB0b2FzdCgnxJDDoyDEkeG6t3QgZ2nDoSBnw7NpOiAnICsgbW9uZXkodikgKyAnICjDoXAgZOG7pW5nIGNobyBjw6FjIGfDs2kgbeG7nyBzYXUpJyk7CiAgICAgIHJlbmRlckNvbGxlY3Rpb24oKTsKICAgIH0pOwogICAgbGFiLmFwcGVuZENoaWxkKGlucCk7CiAgICBjdGwuYXBwZW5kQ2hpbGQobGFiKTsKCiAgICB2YXIgZXhwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYnV0dG9uJyk7CiAgICBleHAudHlwZSA9ICdidXR0b24nOyBleHAuY2xhc3NOYW1lID0gJ3BoLWJ0biBwaC1zbWFsbCc7CiAgICBleHAudGV4dENvbnRlbnQgPSAnWHXhuqV0IEpTT04nOwogICAgZXhwLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZnVuY3Rpb24oKXsKICAgICAgdmFyIGJsb2IgPSBuZXcgQmxvYihbSlNPTi5zdHJpbmdpZnkoZCwgbnVsbCwgMildLCB7dHlwZTonYXBwbGljYXRpb24vanNvbid9KTsKICAgICAgZG93bmxvYWRCbG9iKGJsb2IsICdwb2tlaHViLWJvLXN1dS10YXAuanNvbicpOwogICAgfSk7CiAgICBjdGwuYXBwZW5kQ2hpbGQoZXhwKTsKCiAgICB2YXIgY2xyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYnV0dG9uJyk7CiAgICBjbHIudHlwZSA9ICdidXR0b24nOyBjbHIuY2xhc3NOYW1lID0gJ3BoLWJ0biBwaC1zbWFsbCBwaC1kYW5nZXInOwogICAgY2xyLnRleHRDb250ZW50ID0gJ1jDs2EgYuG7mSBzxrB1IHThuq1wJzsKICAgIGNsci5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7CiAgICAgIGlmICghd2luZG93LmNvbmZpcm0oJ1jDs2EgdG/DoG4gYuG7mSBi4buZIHPGsHUgdOG6rXAgdsOgIHPhu5EgbGnhu4d1IFJPST8nKSkgcmV0dXJuOwogICAgICBkYiA9IHsgY2FyZHM6e30sIGNoYWxsZW5nZXM6e30sIHBhY2tzOjAsIHNwZW50OjAsIHBhY2tQcmljZTpkLnBhY2tQcmljZSwgYmVzdEx1Y2s6MCB9OwogICAgICBzYXZlREIoKTsgcGhMYlN5bmNTdGF0cygpOyB1cGRhdGVDb2xCYWRnZSgpOyByZW5kZXJDb2xsZWN0aW9uKCk7CiAgICAgIHRvYXN0KCfEkMOjIHjDs2EgYuG7mSBzxrB1IHThuq1wJyk7CiAgICB9KTsKICAgIGN0bC5hcHBlbmRDaGlsZChjbHIpOwogICAgZWxzLmNvbEJvZHkuYXBwZW5kQ2hpbGQoY3RsKTsKCiAgICBpZiAoIXMuY2FyZHMpewogICAgICB2YXIgZW1wdHkgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgZW1wdHkuY2xhc3NOYW1lID0gJ3BoLWNvbC1ub3RlJzsKICAgICAgZW1wdHkudGV4dENvbnRlbnQgPSAnQ2jGsGEgY8OzIGzDoSBuw6BvLiBN4bufIG3hu5l0IGfDs2kgYsOgaSB2w6AgbeG7jWkgdGjhu6kgc+G6vSB04buxIMSR4buZbmcgxJHGsOG7o2MgbMawdSBs4bqhaSB0csOqbiBjaMOtbmggbcOheSBi4bqhbi4nOwogICAgICBlbHMuY29sQm9keS5hcHBlbmRDaGlsZChlbXB0eSk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICAvLyBwaGFuIGJvIGRvIGhpZW0KICAgIHZhciBicmsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGJyay5jbGFzc05hbWUgPSAncGgtbW9kYWwtc2VjdGlvbic7CiAgICB2YXIgYnJrVCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgYnJrVC5jbGFzc05hbWUgPSAncGgtbW9kYWwtc2VjdGlvbi10aXRsZSc7CiAgICBicmtULnRleHRDb250ZW50ID0gJ1Bow6JuIGLhu5UgxJHhu5kgaGnhur9tJzsKICAgIGJyay5hcHBlbmRDaGlsZChicmtUKTsKICAgIHZhciBtYXhUID0gTWF0aC5tYXguYXBwbHkobnVsbCwgcy50aWVycykgfHwgMTsKICAgIGZvciAodmFyIHQgPSA2OyB0ID49IDA7IHQtLSl7CiAgICAgIGlmICghcy50aWVyc1t0XSkgY29udGludWU7CiAgICAgIHZhciByb3diID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIHJvd2IuY2xhc3NOYW1lID0gJ3BoLWJhci1yb3cnOwogICAgICB2YXIgYmwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IGJsLmNsYXNzTmFtZSA9ICdwaC1iYXItbCc7IGJsLnRleHRDb250ZW50ID0gdGllckxhYmVsVGV4dCh0KTsKICAgICAgdmFyIGJ3ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyBidy5jbGFzc05hbWUgPSAncGgtYmFyLXcnOwogICAgICB2YXIgYmYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IGJmLmNsYXNzTmFtZSA9ICdwaC1iYXItZic7IGJmLmRhdGFzZXQudGllciA9IFN0cmluZyh0KTsKICAgICAgYmYuc3R5bGUud2lkdGggPSBNYXRoLm1heCg0LCBzLnRpZXJzW3RdIC8gbWF4VCAqIDEwMCkgKyAnJSc7CiAgICAgIGJ3LmFwcGVuZENoaWxkKGJmKTsKICAgICAgdmFyIGJuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyBibi5jbGFzc05hbWUgPSAncGgtYmFyLW4nOyBibi50ZXh0Q29udGVudCA9IFN0cmluZyhzLnRpZXJzW3RdKTsKICAgICAgcm93Yi5hcHBlbmRDaGlsZChibCk7IHJvd2IuYXBwZW5kQ2hpbGQoYncpOyByb3diLmFwcGVuZENoaWxkKGJuKTsKICAgICAgYnJrLmFwcGVuZENoaWxkKHJvd2IpOwogICAgfQogICAgZWxzLmNvbEJvZHkuYXBwZW5kQ2hpbGQoYnJrKTsKCiAgICAvLyBkYW5oIHNhY2ggbGEgYmFpCiAgICB2YXIgbGlzdFdyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGxpc3RXcmFwLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1zZWN0aW9uJzsKICAgIHZhciBsdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbHQuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXNlY3Rpb24tdGl0bGUnOwogICAgbHQudGV4dENvbnRlbnQgPSAnTMOhIGLDoGkgxJHDoyBzxrB1IHThuqdtIChnacOhIGNhbyBuaOG6pXQgdHLGsOG7m2MpJzsKICAgIGxpc3RXcmFwLmFwcGVuZENoaWxkKGx0KTsKICAgIHZhciBncmlkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBncmlkLmNsYXNzTmFtZSA9ICdwaC10aHVtYi1ncmlkJzsKICAgIHZhciBlbnRyaWVzID0gT2JqZWN0LmtleXMoZC5jYXJkcykubWFwKGZ1bmN0aW9uKGspeyByZXR1cm4gZC5jYXJkc1trXTsgfSkuc29ydChmdW5jdGlvbihhLGIpewogICAgICB2YXIgcGEgPSB0eXBlb2YgYS5wcmljZSA9PT0gJ251bWJlcicgPyBhLnByaWNlIDogLTE7CiAgICAgIHZhciBwYiA9IHR5cGVvZiBiLnByaWNlID09PSAnbnVtYmVyJyA/IGIucHJpY2UgOiAtMTsKICAgICAgaWYgKHBiICE9PSBwYSkgcmV0dXJuIHBiIC0gcGE7CiAgICAgIHJldHVybiAoYi50aWVyfHwwKSAtIChhLnRpZXJ8fDApOwogICAgfSk7CiAgICBlbnRyaWVzLnNsaWNlKDAsIDMwMCkuZm9yRWFjaChmdW5jdGlvbihlKXsKICAgICAgZ3JpZC5hcHBlbmRDaGlsZChjYXJkVGh1bWIoZSwgZnVuY3Rpb24oKXsgb3BlblN0b3JlZENhcmQoZSk7IH0pKTsKICAgIH0pOwogICAgbGlzdFdyYXAuYXBwZW5kQ2hpbGQoZ3JpZCk7CiAgICBpZiAoZW50cmllcy5sZW5ndGggPiAzMDApewogICAgICB2YXIgbW9yZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBtb3JlLmNsYXNzTmFtZSA9ICdwaC1jb2wtbm90ZSc7CiAgICAgIG1vcmUudGV4dENvbnRlbnQgPSAnxJBhbmcgaGnhu4duIDMwMC8nICsgZW50cmllcy5sZW5ndGggKyAnIGzDoSDEkeG6p3UgdGnDqm4uJzsKICAgICAgbGlzdFdyYXAuYXBwZW5kQ2hpbGQobW9yZSk7CiAgICB9CiAgICBlbHMuY29sQm9keS5hcHBlbmRDaGlsZChsaXN0V3JhcCk7CgogICAgdmFyIHNoYXJlQ29sID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBzaGFyZUNvbC5jbGFzc05hbWUgPSAncGgtYWN0aW9ucyc7CiAgICB2YXIgc2IgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsKICAgIHNiLnR5cGUgPSAnYnV0dG9uJzsgc2IuY2xhc3NOYW1lID0gJ3BoLWJ0biBwaC1wcmltYXJ5JzsKICAgIHNiLnRleHRDb250ZW50ID0gJ0NoaWEgc+G6uyDhuqNuaCBi4buZIHPGsHUgdOG6rXAnOwogICAgc2IuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpewogICAgICB2YXIgdG9wID0gZW50cmllcy5zbGljZSgwLDUpLm1hcChmdW5jdGlvbihlKXsKICAgICAgICByZXR1cm4geyBuYW1lOmUubmFtZSwgaW1hZ2U6ZS5pbWcsIHRpZXI6ZS50aWVyfHwwLCBwcmljZTplLnByaWNlIH07CiAgICAgIH0pOwogICAgICBtYWtlQW5kU2hhcmUoewogICAgICAgIHRpdGxlOiAnQuG7mSBzxrB1IHThuq1wIFBva2VIdWInLAogICAgICAgIHN1Yjogcy5wYWNrcyArICcgZ8OzaSDEkcOjIG3hu58gXHUwMGI3ICcgKyBzLnVuaXF1ZSArICcgbMOhIMSR4buZYyBuaOG6pXQnLAogICAgICAgIGNhcmRzOiB0b3AsCiAgICAgICAgc3RhdHM6IFsKICAgICAgICAgIFsnxJDDoyBjaGknLCBtb25leShzLnNwZW50KV0sCiAgICAgICAgICBbJ0dpw6EgdHLhu4snLCBtb25leShzLnZhbHVlKV0sCiAgICAgICAgICBbJ1JPSScsIHMucm9pID09IG51bGwgPyAn4oCUJyA6IHBjdChzLnJvaSldLAogICAgICAgICAgWydT4buRIGzDoScsIFN0cmluZyhzLmNhcmRzKV0KICAgICAgICBdLAogICAgICAgIHRleHQ6ICdC4buZIHPGsHUgdOG6rXAgUG9rZUh1YiBj4bunYSB0w7RpOiAnICsgcy5wYWNrcyArICcgZ8OzaSwgZ2nDoSB0cuG7iyAnICsgbW9uZXkocy52YWx1ZSkgKyAnLCBST0kgJyArIChzLnJvaSA9PSBudWxsID8gJ+KAlCcgOiBwY3Qocy5yb2kpKSArICcuJwogICAgICB9KTsKICAgIH0pOwogICAgc2hhcmVDb2wuYXBwZW5kQ2hpbGQoc2IpOwogICAgZWxzLmNvbEJvZHkuYXBwZW5kQ2hpbGQoc2hhcmVDb2wpOwogIH0KCiAgLy8gLS0tLS0tLS0tLSBUSOG7rCBUSMOBQ0ggLS0tLS0tLS0tLQogIC8vIENo4buJIGTDuW5nIGThu68gbGnhu4d1IMSRw6MgY8OzIHRyb25nIGNvbGxlY3Rpb247IGtow7RuZyBn4buNaSBBUEkgbeG7m2kuCiAgdmFyIENIQUxMRU5HRVMgPSBbCiAgICB7IGlkOidmaXJzdC1wYWNrJywgaWNvbjon8J+OtCcsIG5hbWU6J03hu58gZ8OzaSDEkeG6p3UgdGnDqm4nLCBkZXNjOidN4bufIMOtdCBuaOG6pXQgMSBnw7NpIGLDoGkuJywgZ29hbDoxLCB2YWx1ZTpmdW5jdGlvbihzKXsgcmV0dXJuIHMucGFja3M7IH0sIGZtdDpmdW5jdGlvbih2LGcpeyByZXR1cm4gTWF0aC5taW4odixnKSArICcvJyArIGc7IH0gfSwKICAgIHsgaWQ6J2NvbGxlY3Rvci01JywgaWNvbjon8J+TmicsIG5hbWU6J05ow6Agc8awdSB04bqnbScsIGRlc2M6J1PGsHUgdOG6p20gNSBsw6EgxJHhu5ljIG5o4bqldC4nLCBnb2FsOjUsIHZhbHVlOmZ1bmN0aW9uKHMpeyByZXR1cm4gcy51bmlxdWU7IH0sIGZtdDpmdW5jdGlvbih2LGcpeyByZXR1cm4gTWF0aC5taW4odixnKSArICcvJyArIGc7IH0gfSwKICAgIHsgaWQ6J2NvbGxlY3Rvci0yMCcsIGljb246J/Cfl4LvuI8nLCBuYW1lOidC4buZIHPGsHUgdOG6rXAgbeG7nyBy4buZbmcnLCBkZXNjOidTxrB1IHThuqdtIDIwIGzDoSDEkeG7mWMgbmjhuqV0LicsIGdvYWw6MjAsIHZhbHVlOmZ1bmN0aW9uKHMpeyByZXR1cm4gcy51bmlxdWU7IH0sIGZtdDpmdW5jdGlvbih2LGcpeyByZXR1cm4gTWF0aC5taW4odixnKSArICcvJyArIGc7IH0gfSwKICAgIHsgaWQ6J3JhcmUtcHVsbCcsIGljb246J+KcqCcsIG5hbWU6J1PEg24gbMOhIGhp4bq/bScsIGRlc2M6J1B1bGwgw610IG5o4bqldCAxIGzDoSB04burIHRpZXIgVWx0cmEgdHLhu58gbMOqbi4nLCBnb2FsOjEsIHZhbHVlOmZ1bmN0aW9uKHMpeyByZXR1cm4gcy5oaWdoVGllcjsgfSwgZm10OmZ1bmN0aW9uKHYsZyl7IHJldHVybiBNYXRoLm1pbih2LGcpICsgJy8nICsgZzsgfSB9LAogICAgeyBpZDonY2hhc2UtcHVsbCcsIGljb246J/CfkZEnLCBuYW1lOidDaGFzZSBIdW50ZXInLCBkZXNjOidQdWxsIMOtdCBuaOG6pXQgMSBsw6EgQ2hhc2UuJywgZ29hbDoxLCB2YWx1ZTpmdW5jdGlvbihzKXsgcmV0dXJuIHMuY2hhc2U7IH0sIGZtdDpmdW5jdGlvbih2LGcpeyByZXR1cm4gTWF0aC5taW4odixnKSArICcvJyArIGc7IH0gfSwKICAgIHsgaWQ6J3Rlbi1wYWNrcycsIGljb246J/Cfk6YnLCBuYW1lOidN4bufIDEwIGfDs2knLCBkZXNjOidN4bufIHThu5VuZyBj4buZbmcgMTAgZ8OzaSBiw6BpLicsIGdvYWw6MTAsIHZhbHVlOmZ1bmN0aW9uKHMpeyByZXR1cm4gcy5wYWNrczsgfSwgZm10OmZ1bmN0aW9uKHYsZyl7IHJldHVybiBNYXRoLm1pbih2LGcpICsgJy8nICsgZzsgfSB9LAogICAgeyBpZDonaHVuZHJlZC1jYXJkcycsIGljb246J/Cfkq8nLCBuYW1lOicxMDAgbMOhJywgZGVzYzonUHVsbCB04buVbmcgY+G7mW5nIDEwMCBsw6EgYsOgaS4nLCBnb2FsOjEwMCwgdmFsdWU6ZnVuY3Rpb24ocyl7IHJldHVybiBzLmNhcmRzOyB9LCBmbXQ6ZnVuY3Rpb24odixnKXsgcmV0dXJuIE1hdGgubWluKHYsZykgKyAnLycgKyBnOyB9IH0KICBdOwoKICBmdW5jdGlvbiBjaGFsbGVuZ2VTdGF0cygpewogICAgdmFyIHMgPSBjb2xsZWN0aW9uU3RhdHMoKSwgZCA9IGxvYWREQigpLCBoaWdoVGllciA9IDAsIHRpZXI0ID0gMCwgdGllcjUgPSAwLCBjaGFzZSA9IDA7CiAgICBPYmplY3Qua2V5cyhkLmNhcmRzKS5mb3JFYWNoKGZ1bmN0aW9uKGspewogICAgICB2YXIgZSA9IGQuY2FyZHNba10sIHQgPSBOdW1iZXIoZS50aWVyKSB8fCAwLCBuID0gTnVtYmVyKGUubikgfHwgMDsKICAgICAgaWYgKHQgPj0gMykgaGlnaFRpZXIgKz0gbjsKICAgICAgaWYgKHQgPj0gNCkgdGllcjQgKz0gbjsKICAgICAgaWYgKHQgPj0gNSkgdGllcjUgKz0gbjsKICAgICAgaWYgKHQgPj0gNikgY2hhc2UgKz0gbjsKICAgIH0pOwogICAgcy5oaWdoVGllciA9IGhpZ2hUaWVyOyBzLnRpZXI0ID0gdGllcjQ7IHMudGllcjUgPSB0aWVyNTsgcy5jaGFzZSA9IGNoYXNlOwogICAgcmV0dXJuIHM7CiAgfQoKICBmdW5jdGlvbiBjaGFsbGVuZ2VDb21wbGV0ZWQoZGVmLCBzKXsgcmV0dXJuIGRlZi52YWx1ZShzKSA+PSBkZWYuZ29hbDsgfQoKICBmdW5jdGlvbiB1cGRhdGVDaGFsbGVuZ2VCYWRnZSgpewogICAgaWYgKCFlbHMuY2hhbGxlbmdlQ291bnQgfHwgIWVscy5jaGFsbGVuZ2VCdG4pIHJldHVybjsKICAgIHZhciBzID0gY2hhbGxlbmdlU3RhdHMoKSwgZCA9IGxvYWREQigpLCBkb25lID0gMDsKICAgIENIQUxMRU5HRVMuZm9yRWFjaChmdW5jdGlvbihkZWYpewogICAgICBpZiAoY2hhbGxlbmdlQ29tcGxldGVkKGRlZiwgcykpewogICAgICAgIGRvbmUrKzsKICAgICAgICBpZiAoIWQuY2hhbGxlbmdlc1tkZWYuaWRdKSBkLmNoYWxsZW5nZXNbZGVmLmlkXSA9IERhdGUubm93KCk7CiAgICAgIH0KICAgIH0pOwogICAgc2F2ZURCKCk7CiAgICBlbHMuY2hhbGxlbmdlQ291bnQudGV4dENvbnRlbnQgPSBkb25lICsgJy8nICsgQ0hBTExFTkdFUy5sZW5ndGg7CiAgICBlbHMuY2hhbGxlbmdlQ291bnQuaGlkZGVuID0gZmFsc2U7CiAgfQoKICBmdW5jdGlvbiBjaGFsbGVuZ2VDYXJkKGRlZiwgcywgZCl7CiAgICB2YXIgdmFsdWUgPSBNYXRoLm1heCgwLCBOdW1iZXIoZGVmLnZhbHVlKHMpKSB8fCAwKSwgZG9uZSA9IGNoYWxsZW5nZUNvbXBsZXRlZChkZWYsIHMpOwogICAgdmFyIHBlcmNlbnQgPSBkZWYuZ29hbCA+IDAgPyBNYXRoLm1pbigxMDAsIE1hdGgucm91bmQodmFsdWUgLyBkZWYuZ29hbCAqIDEwMCkpIDogMTAwOwogICAgdmFyIGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgaXRlbS5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWl0ZW0nICsgKGRvbmUgPyAnIHBoLWRvbmUnIDogJycpOwogICAgdmFyIGhlYWQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgaGVhZC5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWhlYWQnOwogICAgdmFyIGxlZnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHZhciBuYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IG5hbWUuY2xhc3NOYW1lID0gJ3BoLWNoYWxsZW5nZS1uYW1lJzsgbmFtZS50ZXh0Q29udGVudCA9IGRlZi5pY29uICsgJyAnICsgZGVmLm5hbWU7CiAgICB2YXIgZGVzYyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBkZXNjLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtZGVzYyc7IGRlc2MudGV4dENvbnRlbnQgPSBkZWYuZGVzYzsKICAgIGxlZnQuYXBwZW5kQ2hpbGQobmFtZSk7IGxlZnQuYXBwZW5kQ2hpbGQoZGVzYyk7CiAgICB2YXIgYmFkZ2UgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IGJhZGdlLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2Utc3RhdHVzJyArIChkb25lID8gJyBwaC1jaGFsbGVuZ2UtZG9uZScgOiAnJyk7IGJhZGdlLnRleHRDb250ZW50ID0gZG9uZSA/ICfinJMgSG/DoG4gdGjDoG5oJyA6ICfEkGFuZyBsw6BtJzsKICAgIGhlYWQuYXBwZW5kQ2hpbGQobGVmdCk7IGhlYWQuYXBwZW5kQ2hpbGQoYmFkZ2UpOyBpdGVtLmFwcGVuZENoaWxkKGhlYWQpOwogICAgdmFyIHByb2cgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgcHJvZy5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLXByb2dyZXNzJzsKICAgIHZhciB0cmFjayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgdHJhY2suY2xhc3NOYW1lID0gJ3BoLWNoYWxsZW5nZS10cmFjayc7CiAgICB2YXIgZmlsbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgZmlsbC5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWZpbGwnOyBmaWxsLnN0eWxlLndpZHRoID0gcGVyY2VudCArICclJzsgdHJhY2suYXBwZW5kQ2hpbGQoZmlsbCk7CiAgICB2YXIgbWV0YSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgbWV0YS5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLW1ldGEnOyBtZXRhLnRleHRDb250ZW50ID0gZGVmLmZtdCh2YWx1ZSwgZGVmLmdvYWwpOwogICAgcHJvZy5hcHBlbmRDaGlsZCh0cmFjayk7IHByb2cuYXBwZW5kQ2hpbGQobWV0YSk7IGl0ZW0uYXBwZW5kQ2hpbGQocHJvZyk7CiAgICB2YXIgZm9vdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBmb290LmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtZm9vdCc7CiAgICBpZiAoZG9uZSl7CiAgICAgIHZhciB3aGVuID0gZC5jaGFsbGVuZ2VzW2RlZi5pZF0gPyBuZXcgRGF0ZShkLmNoYWxsZW5nZXNbZGVmLmlkXSkgOiBudWxsOwogICAgICBmb290LnRleHRDb250ZW50ID0gd2hlbiA/ICdIb8OgbiB0aMOgbmggbmfDoHkgJyArIHdoZW4udG9Mb2NhbGVEYXRlU3RyaW5nKCd2aS1WTicpIDogJ8SQw6MgaG/DoG4gdGjDoG5oJzsKICAgIH0gZWxzZSBmb290LnRleHRDb250ZW50ID0gJ1Rp4bq/biDEkeG7mTogJyArIHBlcmNlbnQgKyAnJSc7CiAgICBpdGVtLmFwcGVuZENoaWxkKGZvb3QpOwogICAgcmV0dXJuIGl0ZW07CiAgfQoKICBmdW5jdGlvbiByZW5kZXJDaGFsbGVuZ2VzKCl7CiAgICBpZiAoIWVscy5jaGFsbGVuZ2VCb2R5KSByZXR1cm47CiAgICB2YXIgcyA9IGNoYWxsZW5nZVN0YXRzKCksIGQgPSBsb2FkREIoKTsKICAgIGVscy5jaGFsbGVuZ2VCb2R5LnRleHRDb250ZW50ID0gJyc7CiAgICB2YXIgZG9uZSA9IENIQUxMRU5HRVMuZmlsdGVyKGZ1bmN0aW9uKGRlZil7IHJldHVybiBjaGFsbGVuZ2VDb21wbGV0ZWQoZGVmLCBzKTsgfSkubGVuZ3RoOwogICAgdmFyIGhlcm8gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgaGVyby5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWhlcm8nOwogICAgaGVyby5hcHBlbmRDaGlsZChzdGF0Qm94KCfEkMOjIGhvw6BuIHRow6BuaCcsIGRvbmUgKyAnLzUnKSk7CiAgICBoZXJvLmFwcGVuZENoaWxkKHN0YXRCb3goJ0fDs2kgxJHDoyBt4bufJywgU3RyaW5nKHMucGFja3MpKSk7CiAgICBoZXJvLmFwcGVuZENoaWxkKHN0YXRCb3goJ0zDoSDEkeG7mWMgbmjhuqV0JywgU3RyaW5nKHMudW5pcXVlKSkpOwogICAgZWxzLmNoYWxsZW5nZUJvZHkuYXBwZW5kQ2hpbGQoaGVybyk7CiAgICB2YXIgbGlzdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBsaXN0LmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtbGlzdCc7CiAgICBDSEFMTEVOR0VTLmZvckVhY2goZnVuY3Rpb24oZGVmKXsgbGlzdC5hcHBlbmRDaGlsZChjaGFsbGVuZ2VDYXJkKGRlZiwgcywgZCkpOyB9KTsKICAgIGVscy5jaGFsbGVuZ2VCb2R5LmFwcGVuZENoaWxkKGxpc3QpOwogIH0KCiAgZnVuY3Rpb24gb3BlbkNoYWxsZW5nZU1vZGFsKCl7IHJlbmRlckNoYWxsZW5nZXMoKTsgZWxzLmNoYWxsZW5nZU1vZGFsLmhpZGRlbiA9IGZhbHNlOyBzZXRNb2RhbFZpZXdwb3J0T3Blbih0cnVlKTsgfQogIGZ1bmN0aW9uIGNsb3NlQ2hhbGxlbmdlTW9kYWwoKXsgZWxzLmNoYWxsZW5nZU1vZGFsLmhpZGRlbiA9IHRydWU7IGVscy5jaGFsbGVuZ2VCb2R5LnRleHRDb250ZW50ID0gJyc7IHNldE1vZGFsVmlld3BvcnRPcGVuKGZhbHNlKTsgfQogIGVscy5jaGFsbGVuZ2VCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBvcGVuQ2hhbGxlbmdlTW9kYWwpOwogIGVscy5jaGFsbGVuZ2VDbG9zZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGNsb3NlQ2hhbGxlbmdlTW9kYWwpOwogIGVscy5jaGFsbGVuZ2VCYWNrZHJvcC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGNsb3NlQ2hhbGxlbmdlTW9kYWwpOwoKICBmdW5jdGlvbiBtYXJrU3RvcmVkQ2FyZFNlZW4oZSl7CiAgICBpZiAoIWUgfHwgIWUuaWQgfHwgIWUubmV3RnJvbVNob3ApIHJldHVybjsKICAgIHZhciBkID0gbG9hZERCKCk7CiAgICBpZiAoZC5jYXJkcyAmJiBkLmNhcmRzW2UuaWRdICYmIGQuY2FyZHNbZS5pZF0ubmV3RnJvbVNob3ApewogICAgICBkLmNhcmRzW2UuaWRdLm5ld0Zyb21TaG9wID0gZmFsc2U7CiAgICAgIGRlbGV0ZSBkLmNhcmRzW2UuaWRdLmxhc3RTaG9wUmVkZWVtZWRBdDsKICAgICAgc2F2ZURCKCk7CiAgICAgIGUubmV3RnJvbVNob3AgPSBmYWxzZTsKICAgICAgdXBkYXRlQ29sQmFkZ2UoKTsKICAgICAgaWYgKGVscy5jb2xNb2RhbCAmJiAhZWxzLmNvbE1vZGFsLmhpZGRlbikgcmVuZGVyQ29sbGVjdGlvbigpOwogICAgfQogIH0KCiAgZnVuY3Rpb24gb3BlblN0b3JlZENhcmQoZSl7CiAgICAvLyBYZW0gdGjhursgPSDEkcOjIMSR4buNYyB0aMO0bmcgYsOhbyBN4bubaS4KICAgIG1hcmtTdG9yZWRDYXJkU2VlbihlKTsKICAgIC8vIEzhuqV5IGThu68gbGnhu4d1IMSR4bqneSDEkeG7pyBu4bq/dSBjw7MgdHJvbmcgY2FjaGUsIG7hur91IGtow7RuZyB0aMOsIGThu7FuZyB04burIGThu68gbGnhu4d1IMSRw6MgbMawdS4KICAgIHZhciBjYWNoZWQgPSBzdGF0ZS5jYWNoZS5nZXQoZS5pZCk7CiAgICBpZiAoY2FjaGVkKXsgb3BlbkNhcmRNb2RhbChjYWNoZWQsIHJhcml0eVRpZXIoY2FjaGVkLnJhcml0eSksIHByaWNlVVNEKGNhY2hlZCkpOyByZXR1cm47IH0KICAgIGdldEpTT04oQVBJICsgJy9jYXJkcy8nICsgZS5pZCkudGhlbihmdW5jdGlvbihjKXsKICAgICAgc3RhdGUuY2FjaGUuc2V0KGUuaWQsIGMpOwogICAgICBvcGVuQ2FyZE1vZGFsKGMsIHJhcml0eVRpZXIoYy5yYXJpdHkpLCBwcmljZVVTRChjKSk7CiAgICB9KS5jYXRjaChmdW5jdGlvbigpewogICAgICBvcGVuQ2FyZE1vZGFsKHsgaWQ6ZS5pZCwgbmFtZTplLm5hbWUsIGltYWdlOmUuaW1nLCByYXJpdHk6ZS5yYXJpdHkgfSwKICAgICAgICB7IHRpZXI6IGUudGllcnx8MCwgbGFiZWw6IGUucmFyaXR5IHx8IHRpZXJMYWJlbFRleHQoZS50aWVyfHwwKSB9LAogICAgICAgIHR5cGVvZiBlLnByaWNlID09PSAnbnVtYmVyJyA/IGUucHJpY2UgOiBudWxsKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gb3BlbkNvbE1vZGFsKCl7CiAgICByZW5kZXJDb2xsZWN0aW9uKCk7CiAgICBlbHMuY29sTW9kYWwuaGlkZGVuID0gZmFsc2U7CiAgICBzZXRNb2RhbFZpZXdwb3J0T3Blbih0cnVlKTsKICB9CiAgZnVuY3Rpb24gY2xvc2VDb2xNb2RhbCgpewogICAgZWxzLmNvbE1vZGFsLmhpZGRlbiA9IHRydWU7CiAgICBlbHMuY29sQm9keS50ZXh0Q29udGVudCA9ICcnOwogICAgc2V0TW9kYWxWaWV3cG9ydE9wZW4oZmFsc2UpOwogIH0KICBlbHMuY29sQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgb3BlbkNvbE1vZGFsKTsKICBlbHMuY29sQ2xvc2UuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBjbG9zZUNvbE1vZGFsKTsKICBlbHMuY29sQmFja2Ryb3AuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBjbG9zZUNvbE1vZGFsKTsKCiAgLy8gLS0tLS0tLS0tLSBDSElBIFPhurog4bqiTkggLS0tLS0tLS0tLQogIGZ1bmN0aW9uIGRvd25sb2FkQmxvYihibG9iLCBuYW1lKXsKICAgIHZhciB1cmwgPSBVUkwuY3JlYXRlT2JqZWN0VVJMKGJsb2IpOwogICAgdmFyIGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7CiAgICBhLmhyZWYgPSB1cmw7IGEuZG93bmxvYWQgPSBuYW1lOwogICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChhKTsgYS5jbGljaygpOyBhLnJlbW92ZSgpOwogICAgc2V0VGltZW91dChmdW5jdGlvbigpeyBVUkwucmV2b2tlT2JqZWN0VVJMKHVybCk7IH0sIDQwMDApOwogIH0KCiAgZnVuY3Rpb24gbG9hZEltZ0NPUlMoc3JjKXsKICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXMpewogICAgICB2YXIgaW0gPSBuZXcgSW1hZ2UoKTsKICAgICAgaW0uY3Jvc3NPcmlnaW4gPSAnYW5vbnltb3VzJzsKICAgICAgaW0ub25sb2FkID0gZnVuY3Rpb24oKXsgcmVzKGltKTsgfTsKICAgICAgaW0ub25lcnJvciA9IGZ1bmN0aW9uKCl7IHJlcyhudWxsKTsgfTsKICAgICAgaW0uc3JjID0gc3JjOwogICAgfSk7CiAgfQoKICBmdW5jdGlvbiByb3VuZFJlY3QoZywgeCwgeSwgdywgaCwgcil7CiAgICBnLmJlZ2luUGF0aCgpOwogICAgZy5tb3ZlVG8oeCtyLCB5KTsKICAgIGcubGluZVRvKHgrdy1yLCB5KTsgZy5xdWFkcmF0aWNDdXJ2ZVRvKHgrdywgeSwgeCt3LCB5K3IpOwogICAgZy5saW5lVG8oeCt3LCB5K2gtcik7IGcucXVhZHJhdGljQ3VydmVUbyh4K3csIHkraCwgeCt3LXIsIHkraCk7CiAgICBnLmxpbmVUbyh4K3IsIHkraCk7IGcucXVhZHJhdGljQ3VydmVUbyh4LCB5K2gsIHgsIHkraC1yKTsKICAgIGcubGluZVRvKHgsIHkrcik7IGcucXVhZHJhdGljQ3VydmVUbyh4LCB5LCB4K3IsIHkpOwogICAgZy5jbG9zZVBhdGgoKTsKICB9CgogIHZhciBUSUVSX0NPTE9SUyA9IFsnIzNhM2Y2YicsJyM2YmU4YzAnLCcjNmJlOGMwJywnIzRhN2ZmZicsJyNjOTZiZmYnLCcjZmY4ZmQwJywnI2ZmZDM1YyddOwoKICBmdW5jdGlvbiBmaXRUZXh0KGcsIHRleHQsIG1heCl7CiAgICB2YXIgdCA9IHRleHQgfHwgJyc7CiAgICB3aGlsZSAodC5sZW5ndGggPiA0ICYmIGcubWVhc3VyZVRleHQodCkud2lkdGggPiBtYXgpIHQgPSB0LnNsaWNlKDAsIC0yKTsKICAgIHJldHVybiB0ID09PSB0ZXh0ID8gdCA6IHQgKyAn4oCmJzsKICB9CgogIGZ1bmN0aW9uIGJ1aWxkU2hhcmVDYW52YXMob3B0cywgbm9JbWFnZXMpewogICAgdmFyIFcgPSAxMjAwLCBIID0gNjMwOwogICAgdmFyIGN2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7CiAgICBjdi53aWR0aCA9IFc7IGN2LmhlaWdodCA9IEg7CiAgICB2YXIgZyA9IGN2LmdldENvbnRleHQoJzJkJyk7CgogICAgdmFyIGJnID0gZy5jcmVhdGVMaW5lYXJHcmFkaWVudCgwLCAwLCBXLCBIKTsKICAgIGJnLmFkZENvbG9yU3RvcCgwLCAnIzEyMTQyYScpOwogICAgYmcuYWRkQ29sb3JTdG9wKDAuNSwgJyMwYTBiMTYnKTsKICAgIGJnLmFkZENvbG9yU3RvcCgxLCAnIzFiMTAzOCcpOwogICAgZy5maWxsU3R5bGUgPSBiZzsgZy5maWxsUmVjdCgwLCAwLCBXLCBIKTsKICAgIGcuZmlsbFN0eWxlID0gJ3JnYmEoMjU1LDI1NSwyNTUsLjAzNSknOwogICAgZm9yICh2YXIgaSA9IC1IOyBpIDwgVzsgaSArPSAyNikgeyBnLnNhdmUoKTsgZy50cmFuc2xhdGUoaSwgMCk7IGcucm90YXRlKDAuMjUpOyBnLmZpbGxSZWN0KDAsIC0yMDAsIDgsIEgqMik7IGcucmVzdG9yZSgpOyB9CgogICAgZy5maWxsU3R5bGUgPSAnI2YyZjFmYic7CiAgICBnLmZvbnQgPSAnNzAwIDQ2cHggIlNwYWNlIEdyb3Rlc2siLCBzeXN0ZW0tdWksIHNhbnMtc2VyaWYnOwogICAgZy5maWxsVGV4dChmaXRUZXh0KGcsIG9wdHMudGl0bGUsIDkwMCksIDYwLCA4Nik7CiAgICBnLmZpbGxTdHlsZSA9ICcjOTQ5N2M0JzsKICAgIGcuZm9udCA9ICc0MDAgMjRweCBJbnRlciwgc3lzdGVtLXVpLCBzYW5zLXNlcmlmJzsKICAgIGcuZmlsbFRleHQoZml0VGV4dChnLCBvcHRzLnN1YiB8fCAnJywgOTUwKSwgNjAsIDEyNik7CiAgICBnLmZpbGxTdHlsZSA9ICcjZmZkMzVjJzsKICAgIGcuYmVnaW5QYXRoKCk7IGcuYXJjKFcgLSA3MCwgNzQsIDEwLCAwLCBNYXRoLlBJKjIpOyBnLmZpbGwoKTsKCiAgICB2YXIgY2FyZHMgPSAob3B0cy5jYXJkcyB8fCBbXSkuc2xpY2UoMCwgNSk7CiAgICB2YXIgc2xvdFcgPSAxNzgsIHNsb3RIID0gMjQ1LCBnYXAgPSAyMjsKICAgIHZhciB0b3RhbFcgPSBjYXJkcy5sZW5ndGggKiBzbG90VyArIE1hdGgubWF4KDAsIGNhcmRzLmxlbmd0aCAtIDEpICogZ2FwOwogICAgdmFyIHgwID0gKFcgLSB0b3RhbFcpIC8gMiwgeTAgPSAxOTA7CgogICAgcmV0dXJuIFByb21pc2UuYWxsKGNhcmRzLm1hcChmdW5jdGlvbihjKXsKICAgICAgcmV0dXJuIChub0ltYWdlcyB8fCAhYy5pbWFnZSkgPyBQcm9taXNlLnJlc29sdmUobnVsbCkgOiBsb2FkSW1nQ09SUyhjLmltYWdlICsgJy8nICsgUVVBTElUWSk7CiAgICB9KSkudGhlbihmdW5jdGlvbihpbWdzKXsKICAgICAgY2FyZHMuZm9yRWFjaChmdW5jdGlvbihjLCBpZHgpewogICAgICAgIHZhciB4ID0geDAgKyBpZHggKiAoc2xvdFcgKyBnYXApOwogICAgICAgIHZhciBjb2wgPSBUSUVSX0NPTE9SU1tNYXRoLm1heCgwLCBNYXRoLm1pbig2LCBjLnRpZXIgfHwgMCkpXTsKICAgICAgICBnLnNhdmUoKTsKICAgICAgICBnLnNoYWRvd0NvbG9yID0gY29sOyBnLnNoYWRvd0JsdXIgPSAoYy50aWVyIHx8IDApID49IDQgPyAyNiA6IDg7CiAgICAgICAgcm91bmRSZWN0KGcsIHgsIHkwLCBzbG90Vywgc2xvdEgsIDEyKTsKICAgICAgICBnLmZpbGxTdHlsZSA9ICcjMTgxYjM4JzsgZy5maWxsKCk7CiAgICAgICAgZy5yZXN0b3JlKCk7CiAgICAgICAgdmFyIGltID0gaW1nc1tpZHhdOwogICAgICAgIGlmIChpbSl7CiAgICAgICAgICBnLnNhdmUoKTsKICAgICAgICAgIHJvdW5kUmVjdChnLCB4ICsgMywgeTAgKyAzLCBzbG90VyAtIDYsIHNsb3RIIC0gNiwgMTApOwogICAgICAgICAgZy5jbGlwKCk7CiAgICAgICAgICBnLmRyYXdJbWFnZShpbSwgeCArIDMsIHkwICsgMywgc2xvdFcgLSA2LCBzbG90SCAtIDYpOwogICAgICAgICAgZy5yZXN0b3JlKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGcuZmlsbFN0eWxlID0gJyM5NDk3YzQnOwogICAgICAgICAgZy5mb250ID0gJzYwMCAxNXB4IEludGVyLCBzeXN0ZW0tdWksIHNhbnMtc2VyaWYnOwogICAgICAgICAgdmFyIG5tID0gZml0VGV4dChnLCBjLm5hbWUgfHwgJz8nLCBzbG90VyAtIDI0KTsKICAgICAgICAgIGcuZmlsbFRleHQobm0sIHggKyAxMiwgeTAgKyBzbG90SC8yKTsKICAgICAgICB9CiAgICAgICAgZy5zdHJva2VTdHlsZSA9IGNvbDsgZy5saW5lV2lkdGggPSAoYy50aWVyIHx8IDApID49IDQgPyAzIDogMS41OwogICAgICAgIHJvdW5kUmVjdChnLCB4LCB5MCwgc2xvdFcsIHNsb3RILCAxMik7IGcuc3Ryb2tlKCk7CgogICAgICAgIGcuZmlsbFN0eWxlID0gdHlwZW9mIGMucHJpY2UgPT09ICdudW1iZXInID8gJyNmZmQzNWMnIDogJyM2ZjcyYTAnOwogICAgICAgIGcuZm9udCA9ICc3MDAgMTdweCAiU3BhY2UgR3JvdGVzayIsIHN5c3RlbS11aSwgc2Fucy1zZXJpZic7CiAgICAgICAgdmFyIHB0ID0gdHlwZW9mIGMucHJpY2UgPT09ICdudW1iZXInID8gbW9uZXkoYy5wcmljZSkgOiAn4oCUJzsKICAgICAgICBnLmZpbGxUZXh0KHB0LCB4ICsgMiwgeTAgKyBzbG90SCArIDI2KTsKICAgICAgICBnLmZpbGxTdHlsZSA9ICcjOTQ5N2M0JzsKICAgICAgICBnLmZvbnQgPSAnNDAwIDEzcHggSW50ZXIsIHN5c3RlbS11aSwgc2Fucy1zZXJpZic7CiAgICAgICAgZy5maWxsVGV4dChmaXRUZXh0KGcsIGMubmFtZSB8fCAnJywgc2xvdFcgLSA0KSwgeCArIDIsIHkwICsgc2xvdEggKyA0Nik7CiAgICAgIH0pOwoKICAgICAgdmFyIHN0YXRzID0gb3B0cy5zdGF0cyB8fCBbXTsKICAgICAgdmFyIHN5ID0gNTIwLCBzdyA9IDI2MCwgc3ggPSA2MDsKICAgICAgc3RhdHMuc2xpY2UoMCwgNCkuZm9yRWFjaChmdW5jdGlvbihzdCwgaWR4KXsKICAgICAgICB2YXIgeCA9IHN4ICsgaWR4ICogc3c7CiAgICAgICAgZy5maWxsU3R5bGUgPSAncmdiYSgyNTUsMjU1LDI1NSwuMDUpJzsKICAgICAgICByb3VuZFJlY3QoZywgeCwgc3ksIHN3IC0gMjIsIDc0LCAxMik7IGcuZmlsbCgpOwogICAgICAgIGcuZmlsbFN0eWxlID0gc3RbMl0gPT09ICduZWcnID8gJyNmZjlhOWEnIDogKHN0WzJdID09PSAncG9zJyA/ICcjNmJlOGMwJyA6ICcjZjJmMWZiJyk7CiAgICAgICAgZy5mb250ID0gJzcwMCAyOHB4ICJTcGFjZSBHcm90ZXNrIiwgc3lzdGVtLXVpLCBzYW5zLXNlcmlmJzsKICAgICAgICBnLmZpbGxUZXh0KGZpdFRleHQoZywgc3RbMV0sIHN3IC0gNDYpLCB4ICsgMTYsIHN5ICsgMzgpOwogICAgICAgIGcuZmlsbFN0eWxlID0gJyM5NDk3YzQnOwogICAgICAgIGcuZm9udCA9ICc0MDAgMTRweCBJbnRlciwgc3lzdGVtLXVpLCBzYW5zLXNlcmlmJzsKICAgICAgICBnLmZpbGxUZXh0KGZpdFRleHQoZywgc3RbMF0sIHN3IC0gNDYpLCB4ICsgMTYsIHN5ICsgNjApOwogICAgICB9KTsKCiAgICAgIGcuZmlsbFN0eWxlID0gJyM2ZjcyYTAnOwogICAgICBnLmZvbnQgPSAnNTAwIDE1cHggSW50ZXIsIHN5c3RlbS11aSwgc2Fucy1zZXJpZic7CiAgICAgIGcuZmlsbFRleHQoJ1Bva2VIdWIgXHUwMGI3IGThu68gbGnhu4d1ICYgZ2nDoSB04burIFRDR2RleCcsIDYwLCA2MTIpOwogICAgICByZXR1cm4gY3Y7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIGV4cG9ydENhbnZhcyhjdiwgdGV4dCwgb25GYWlsKXsKICAgIHZhciBvayA9IHRydWU7CiAgICB0cnkgewogICAgICBjdi50b0Jsb2IoZnVuY3Rpb24oYmxvYil7CiAgICAgICAgaWYgKCFibG9iKXsgaWYgKG9uRmFpbCkgb25GYWlsKCk7IHJldHVybjsgfQogICAgICAgIHZhciBuYW1lID0gJ3Bva2VodWItJyArIERhdGUubm93KCkgKyAnLnBuZyc7CiAgICAgICAgdmFyIGZpbGUgPSBudWxsOwogICAgICAgIHRyeSB7IGZpbGUgPSBuZXcgRmlsZShbYmxvYl0sIG5hbWUsIHt0eXBlOidpbWFnZS9wbmcnfSk7IH0gY2F0Y2goZSl7fQogICAgICAgIGlmIChmaWxlICYmIG5hdmlnYXRvci5jYW5TaGFyZSAmJiBuYXZpZ2F0b3Iuc2hhcmUgJiYgbmF2aWdhdG9yLmNhblNoYXJlKHtmaWxlczpbZmlsZV19KSl7CiAgICAgICAgICBuYXZpZ2F0b3Iuc2hhcmUoeyBmaWxlczpbZmlsZV0sIHRleHQ6dGV4dCB8fCAnJywgdGl0bGU6J1Bva2VIdWInIH0pCiAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKCl7IHRvYXN0KCfEkMOjIG3hu58gYuG6o25nIGNoaWEgc+G6uycpOyB9KQogICAgICAgICAgICAuY2F0Y2goZnVuY3Rpb24oKXsgZG93bmxvYWRCbG9iKGJsb2IsIG5hbWUpOyB0b2FzdCgnxJDDoyB04bqjaSDhuqNuaCB24buBIG3DoXknKTsgfSk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGRvd25sb2FkQmxvYihibG9iLCBuYW1lKTsKICAgICAgICAgIHRvYXN0KCfEkMOjIHThuqNpIOG6o25oIHbhu4EgbcOheSDigJQgxJHEg25nIGzDqm4gRmFjZWJvb2svWC9JbnN0YWdyYW0gbMOgIHhvbmcnKTsKICAgICAgICB9CiAgICAgIH0sICdpbWFnZS9wbmcnKTsKICAgIH0gY2F0Y2goZSl7CiAgICAgIG9rID0gZmFsc2U7CiAgICAgIGlmIChvbkZhaWwpIG9uRmFpbCgpOwogICAgfQogICAgcmV0dXJuIG9rOwogIH0KCiAgZnVuY3Rpb24gbWFrZUFuZFNoYXJlKG9wdHMpewogICAgdG9hc3QoJ8SQYW5nIHThuqFvIOG6o25o4oCmJyk7CiAgICBidWlsZFNoYXJlQ2FudmFzKG9wdHMsIGZhbHNlKS50aGVuKGZ1bmN0aW9uKGN2KXsKICAgICAgZXhwb3J0Q2FudmFzKGN2LCBvcHRzLnRleHQsIGZ1bmN0aW9uKCl7CiAgICAgICAgLy8gYW5oIHRoZSBiaSBjaGFuIENPUlMgLT4gdmUgbGFpIGtob25nIGNvIGFuaCB0aGUKICAgICAgICBidWlsZFNoYXJlQ2FudmFzKG9wdHMsIHRydWUpLnRoZW4oZnVuY3Rpb24oY3YyKXsKICAgICAgICAgIGV4cG9ydENhbnZhcyhjdjIsIG9wdHMudGV4dCwgZnVuY3Rpb24oKXsKICAgICAgICAgICAgdG9hc3QoJ1Ryw6xuaCBkdXnhu4d0IGtow7RuZyBjaG8gcGjDqXAgeHXhuqV0IOG6o25oIOG7nyB0cmFuZyBuw6B5LicpOwogICAgICAgICAgfSk7CiAgICAgICAgfSk7CiAgICAgIH0pOwogICAgfSk7CiAgfQoKICAvLyAtLS0tLS0tLS0tIHBow6FvIGhvYSAvIGNvbmZldHRpIC0tLS0tLS0tLS0KICB2YXIgcmVkdWNlTW90aW9uID0gd2luZG93Lm1hdGNoTWVkaWEgJiYgd2luZG93Lm1hdGNoTWVkaWEoJyhwcmVmZXJzLXJlZHVjZWQtbW90aW9uOiByZWR1Y2UpJykubWF0Y2hlczsKICB2YXIgY29uZiA9IHsgY3R4Om51bGwsIHBhcnRzOltdLCByYWY6MCwgdzowLCBoOjAsIGRwcjoxIH07CiAgdmFyIENPTkZfQ09MT1JTID0gWycjZmZkMzVjJywnI2ZmOGZkMCcsJyNjOTZiZmYnLCcjNGE3ZmZmJywnIzZiZThjMCcsJyNmZmZmZmYnXTsKCiAgZnVuY3Rpb24gY29uZlNpemUoKXsKICAgIHZhciByID0gcm9vdC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsKICAgIGNvbmYuZHByID0gTWF0aC5taW4od2luZG93LmRldmljZVBpeGVsUmF0aW8gfHwgMSwgMik7CiAgICBjb25mLncgPSByLndpZHRoOyBjb25mLmggPSByLmhlaWdodDsKICAgIGVscy5jb25mZXR0aS53aWR0aCA9IE1hdGgucm91bmQoY29uZi53ICogY29uZi5kcHIpOwogICAgZWxzLmNvbmZldHRpLmhlaWdodCA9IE1hdGgucm91bmQoY29uZi5oICogY29uZi5kcHIpOwogICAgY29uZi5jdHguc2V0VHJhbnNmb3JtKGNvbmYuZHByLCAwLCAwLCBjb25mLmRwciwgMCwgMCk7CiAgfQoKICBmdW5jdGlvbiBjb25mUmVhZHkoKXsKICAgIGlmICghZWxzLmNvbmZldHRpIHx8IHJlZHVjZU1vdGlvbikgcmV0dXJuIGZhbHNlOwogICAgaWYgKCFjb25mLmN0eCl7CiAgICAgIGNvbmYuY3R4ID0gZWxzLmNvbmZldHRpLmdldENvbnRleHQoJzJkJyk7CiAgICAgIGlmICghY29uZi5jdHgpIHJldHVybiBmYWxzZTsKICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIGZ1bmN0aW9uKCl7IGlmIChjb25mLmN0eCkgY29uZlNpemUoKTsgfSk7CiAgICB9CiAgICBjb25mU2l6ZSgpOwogICAgcmV0dXJuIHRydWU7CiAgfQoKICBmdW5jdGlvbiBjb25mVGljaygpewogICAgdmFyIGN0eCA9IGNvbmYuY3R4OwogICAgY3R4LmNsZWFyUmVjdCgwLCAwLCBjb25mLncsIGNvbmYuaCk7CiAgICB2YXIgYWxpdmUgPSAwOwogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb25mLnBhcnRzLmxlbmd0aDsgaSsrKXsKICAgICAgdmFyIHAgPSBjb25mLnBhcnRzW2ldOwogICAgICBpZiAocC5kZWFkKSBjb250aW51ZTsKICAgICAgcC52eSArPSAwLjI2OwogICAgICBwLnZ4ICo9IDAuOTk7IHAudnkgKj0gMC45OTsKICAgICAgcC54ICs9IHAudng7IHAueSArPSBwLnZ5OwogICAgICBwLnJvdCArPSBwLnZyOwogICAgICBwLmxpZmUgLT0gMTsKICAgICAgaWYgKHAueSA+IGNvbmYuaCArIDMwIHx8IHAubGlmZSA8PSAwKXsgcC5kZWFkID0gdHJ1ZTsgY29udGludWU7IH0KICAgICAgYWxpdmUrKzsKICAgICAgY3R4LnNhdmUoKTsKICAgICAgY3R4LnRyYW5zbGF0ZShwLngsIHAueSk7CiAgICAgIGN0eC5yb3RhdGUocC5yb3QpOwogICAgICBjdHguZ2xvYmFsQWxwaGEgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBwLmxpZmUgLyA0MCkpOwogICAgICBjdHguZmlsbFN0eWxlID0gcC5jb2xvcjsKICAgICAgY3R4LmZpbGxSZWN0KC1wLncgLyAyLCAtcC5oIC8gMiwgcC53LCBwLmggKiAoMC40ICsgMC42ICogTWF0aC5hYnMoTWF0aC5jb3MocC5yb3QpKSkpOwogICAgICBjdHgucmVzdG9yZSgpOwogICAgfQogICAgaWYgKGFsaXZlKXsKICAgICAgY29uZi5yYWYgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoY29uZlRpY2spOwogICAgfSBlbHNlIHsKICAgICAgY29uZi5wYXJ0cyA9IFtdOwogICAgICBjb25mLnJhZiA9IDA7CiAgICAgIGN0eC5jbGVhclJlY3QoMCwgMCwgY29uZi53LCBjb25mLmgpOwogICAgfQogIH0KCiAgZnVuY3Rpb24gY29uZkJ1cnN0KHgsIHksIGNvdW50LCBwb3dlcil7CiAgICBpZiAoIWNvbmZSZWFkeSgpKSByZXR1cm47CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNvdW50OyBpKyspewogICAgICB2YXIgYW5nID0gTWF0aC5yYW5kb20oKSAqIE1hdGguUEkgKiAyOwogICAgICB2YXIgc3BkID0gKDAuNCArIE1hdGgucmFuZG9tKCkpICogcG93ZXI7CiAgICAgIGNvbmYucGFydHMucHVzaCh7CiAgICAgICAgeDogeCwgeTogeSwKICAgICAgICB2eDogTWF0aC5jb3MoYW5nKSAqIHNwZCwKICAgICAgICB2eTogTWF0aC5zaW4oYW5nKSAqIHNwZCAtIHBvd2VyICogMC40NSwKICAgICAgICByb3Q6IE1hdGgucmFuZG9tKCkgKiBNYXRoLlBJLAogICAgICAgIHZyOiAoTWF0aC5yYW5kb20oKSAtIDAuNSkgKiAwLjMsCiAgICAgICAgdzogNSArIE1hdGgucmFuZG9tKCkgKiA1LAogICAgICAgIGg6IDggKyBNYXRoLnJhbmRvbSgpICogNywKICAgICAgICBjb2xvcjogQ09ORl9DT0xPUlNbKE1hdGgucmFuZG9tKCkgKiBDT05GX0NPTE9SUy5sZW5ndGgpIHwgMF0sCiAgICAgICAgbGlmZTogOTAgKyBNYXRoLnJhbmRvbSgpICogNjAKICAgICAgfSk7CiAgICB9CiAgICBpZiAoY29uZi5wYXJ0cy5sZW5ndGggPiA0MjApIGNvbmYucGFydHMuc3BsaWNlKDAsIGNvbmYucGFydHMubGVuZ3RoIC0gNDIwKTsKICAgIGlmICghY29uZi5yYWYpIGNvbmYucmFmID0gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGNvbmZUaWNrKTsKICB9CgogIGZ1bmN0aW9uIGNvbmZDZWxlYnJhdGUoYmlnKXsKICAgIGlmICghY29uZlJlYWR5KCkpIHJldHVybjsKICAgIHZhciBuID0gYmlnID8gOTAgOiA1NSwgcCA9IGJpZyA/IDEzIDogMTA7CiAgICBjb25mQnVyc3QoY29uZi53ICogMC4xMiwgY29uZi5oICogMC45MiwgbiwgcCk7CiAgICBjb25mQnVyc3QoY29uZi53ICogMC44OCwgY29uZi5oICogMC45MiwgbiwgcCk7CiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7IGNvbmZCdXJzdChjb25mLncgKiAwLjUsIGNvbmYuaCAqIDAuODUsIG4sIHApOyB9LCAyMjApOwogICAgaWYgKGJpZykgc2V0VGltZW91dChmdW5jdGlvbigpewogICAgICBjb25mQnVyc3QoY29uZi53ICogMC4zLCBjb25mLmggKiAwLjksIG4sIHApOwogICAgICBjb25mQnVyc3QoY29uZi53ICogMC43LCBjb25mLmggKiAwLjksIG4sIHApOwogICAgfSwgNDYwKTsKICB9CgogIGZ1bmN0aW9uIGNvbmZBdEVsZW1lbnQoZWwsIHRpZXIpewogICAgaWYgKCFjb25mUmVhZHkoKSkgcmV0dXJuOwogICAgdmFyIHIgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSwgcnIgPSByb290LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogICAgY29uZkJ1cnN0KHIubGVmdCAtIHJyLmxlZnQgKyByLndpZHRoIC8gMiwgci50b3AgLSByci50b3AgKyByLmhlaWdodCAvIDIsCiAgICAgICAgICAgICAgdGllciA+PSA2ID8gNjAgOiAzNiwgdGllciA+PSA2ID8gMTEgOiA4KTsKICB9CgogIGZ1bmN0aW9uIGNsZWFyQ29uZmV0dGkoKXsKICAgIGlmIChjb25mLnJhZikgY2FuY2VsQW5pbWF0aW9uRnJhbWUoY29uZi5yYWYpOwogICAgY29uZi5yYWYgPSAwOyBjb25mLnBhcnRzID0gW107CiAgICBpZiAoY29uZi5jdHgpIGNvbmYuY3R4LmNsZWFyUmVjdCgwLCAwLCBjb25mLncsIGNvbmYuaCk7CiAgfQoKICAvLyAtLS0tLS0tLS0tIGNoaSB0aeG6v3QgbMOhIGLDoGkgKG1vZGFsKSAtLS0tLS0tLS0tCiAgdmFyIG1vZGFsT3BlbiA9IGZhbHNlOwogIHZhciBsYXN0Rm9jdXNlZCA9IG51bGw7CgogIGZ1bmN0aW9uIHNldE1vZGFsVmlld3BvcnRPcGVuKG9wZW4pewogICAgcm9vdC5zdHlsZS5vdmVyZmxvdyA9IG9wZW4gPyAndmlzaWJsZScgOiAnaGlkZGVuJzsKICB9CgogIGZ1bmN0aW9uIGNsb3NlQ2FyZE1vZGFsKCl7CiAgICBpZiAoIW1vZGFsT3BlbikgcmV0dXJuOwogICAgbW9kYWxPcGVuID0gZmFsc2U7CiAgICBlbHMubW9kYWwuaGlkZGVuID0gdHJ1ZTsKICAgIHNldE1vZGFsVmlld3BvcnRPcGVuKGZhbHNlKTsKICAgIGVscy5tb2RhbEJvZHkudGV4dENvbnRlbnQgPSAnJzsKICAgIGlmIChsYXN0Rm9jdXNlZCAmJiBsYXN0Rm9jdXNlZC5mb2N1cykgbGFzdEZvY3VzZWQuZm9jdXMoKTsKICB9CgogIGZ1bmN0aW9uIG1ldGFSb3cobGFiZWwsIHZhbHVlKXsKICAgIHZhciByb3cgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHJvdy5jbGFzc05hbWUgPSAncGgtbW9kYWwtcm93JzsKICAgIHZhciBrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyBrLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1rJzsgay50ZXh0Q29udGVudCA9IGxhYmVsOwogICAgdmFyIHYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IHYuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXYnOyB2LnRleHRDb250ZW50ID0gdmFsdWU7CiAgICByb3cuYXBwZW5kQ2hpbGQoayk7IHJvdy5hcHBlbmRDaGlsZCh2KTsKICAgIHJldHVybiByb3c7CiAgfQoKICBmdW5jdGlvbiBvcGVuQ2FyZE1vZGFsKGNhcmQsIGluZm8sIHVzZCl7CiAgICBsYXN0Rm9jdXNlZCA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7CiAgICBlbHMubW9kYWxCb2R5LnRleHRDb250ZW50ID0gJyc7CgogICAgdmFyIGhlYWQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGhlYWQuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLWhlYWQnOwoKICAgIHZhciBpbWdXcmFwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBpbWdXcmFwLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1pbWd3cmFwJzsKICAgIGlmIChjYXJkLmltYWdlKXsKICAgICAgdmFyIGltZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpOwogICAgICBpbWcuYWx0ID0gY2FyZC5uYW1lIHx8ICcnOwogICAgICBpbWcuc3JjID0gY2FyZC5pbWFnZSArICcvJyArIFFVQUxJVFk7CiAgICAgIGltZy5vbmVycm9yID0gZnVuY3Rpb24oKXsgaW1nV3JhcC50ZXh0Q29udGVudCA9IGNhcmQubmFtZSB8fCAnPyc7IH07CiAgICAgIGltZ1dyYXAuYXBwZW5kQ2hpbGQoaW1nKTsKICAgIH0gZWxzZSBpbWdXcmFwLnRleHRDb250ZW50ID0gY2FyZC5uYW1lIHx8ICc/JzsKCiAgICB2YXIgaW5mbzEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGluZm8xLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1pbmZvJzsKCiAgICB2YXIgdGl0bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHRpdGxlLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC10aXRsZSc7CiAgICB0aXRsZS50ZXh0Q29udGVudCA9IGNhcmQubmFtZSB8fCAnS2jDtG5nIHLDtSB0w6puJzsKICAgIGluZm8xLmFwcGVuZENoaWxkKHRpdGxlKTsKCiAgICB2YXIgc3ViID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBzdWIuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXN1Yic7CiAgICB2YXIgc2V0TmFtZSA9IGNhcmQuc2V0ICYmIGNhcmQuc2V0Lm5hbWUgPyBjYXJkLnNldC5uYW1lIDogKHN0YXRlLmN1cnJlbnQgJiYgc3RhdGUuY3VycmVudC5uYW1lKSB8fCAnJzsKICAgIHZhciBudW0gPSBjYXJkLmxvY2FsSWQgIT0gbnVsbAogICAgICA/IChjYXJkLmxvY2FsSWQgKyAoY2FyZC5zZXQgJiYgY2FyZC5zZXQuY2FyZENvdW50ICYmIGNhcmQuc2V0LmNhcmRDb3VudC50b3RhbCA/ICcvJyArIGNhcmQuc2V0LmNhcmRDb3VudC50b3RhbCA6ICcnKSkKICAgICAgOiAnJzsKICAgIHN1Yi50ZXh0Q29udGVudCA9IFtzZXROYW1lLCBudW1dLmZpbHRlcihCb29sZWFuKS5qb2luKCcgXHUwMGI3ICcpOwogICAgaW5mbzEuYXBwZW5kQ2hpbGQoc3ViKTsKCiAgICB2YXIgcHJpY2VMaW5lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBwcmljZUxpbmUuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXByaWNlJyArICh1c2QgPT0gbnVsbCA/ICcgcGgtY2FyZC1ub3ByaWNlJyA6ICcnKTsKICAgIHByaWNlTGluZS50ZXh0Q29udGVudCA9IHVzZCAhPSBudWxsID8gbW9uZXkodXNkKSA6ICdDaMawYSBjw7MgZ2nDoSB0aOG7iyB0csaw4budbmcnOwogICAgaW5mbzEuYXBwZW5kQ2hpbGQocHJpY2VMaW5lKTsKCiAgICB2YXIgcm93cyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgcm93cy5jbGFzc05hbWUgPSAncGgtbW9kYWwtcm93cyc7CiAgICByb3dzLmFwcGVuZENoaWxkKG1ldGFSb3coJ8SQ4buZIGhp4bq/bScsIChpbmZvLmxhYmVsIHx8IHRpZXJMYWJlbFRleHQoaW5mby50aWVyKSkgKyAnIChi4bqtYyAnICsgaW5mby50aWVyICsgJy82KScpKTsKICAgIGlmIChjYXJkLmNhdGVnb3J5KSByb3dzLmFwcGVuZENoaWxkKG1ldGFSb3coJ0xv4bqhaScsIGNhcmQuY2F0ZWdvcnkpKTsKICAgIGlmIChjYXJkLmhwKSByb3dzLmFwcGVuZENoaWxkKG1ldGFSb3coJ0hQJywgU3RyaW5nKGNhcmQuaHApKSk7CiAgICBpZiAoQXJyYXkuaXNBcnJheShjYXJkLnR5cGVzKSAmJiBjYXJkLnR5cGVzLmxlbmd0aCkgcm93cy5hcHBlbmRDaGlsZChtZXRhUm93KCdI4buHJywgY2FyZC50eXBlcy5qb2luKCcsICcpKSk7CiAgICBpZiAoY2FyZC5ldm9sdmVGcm9tKSByb3dzLmFwcGVuZENoaWxkKG1ldGFSb3coJ1Rp4bq/biBow7NhIHThu6snLCBjYXJkLmV2b2x2ZUZyb20pKTsKICAgIGlmIChjYXJkLmlsbHVzdHJhdG9yKSByb3dzLmFwcGVuZENoaWxkKG1ldGFSb3coJ0jhu41hIHPEqScsIGNhcmQuaWxsdXN0cmF0b3IpKTsKICAgIGlmIChjYXJkLnJldHJlYXQgIT0gbnVsbCkgcm93cy5hcHBlbmRDaGlsZChtZXRhUm93KCdQaMOtIHLDunQgbHVpJywgU3RyaW5nKGNhcmQucmV0cmVhdCkpKTsKICAgIGluZm8xLmFwcGVuZENoaWxkKHJvd3MpOwoKICAgIGhlYWQuYXBwZW5kQ2hpbGQoaW1nV3JhcCk7IGhlYWQuYXBwZW5kQ2hpbGQoaW5mbzEpOwogICAgZWxzLm1vZGFsQm9keS5hcHBlbmRDaGlsZChoZWFkKTsKCiAgICBpZiAoQXJyYXkuaXNBcnJheShjYXJkLmF0dGFja3MpICYmIGNhcmQuYXR0YWNrcy5sZW5ndGgpewogICAgICB2YXIgYXRrV3JhcCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBhdGtXcmFwLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1zZWN0aW9uJzsKICAgICAgdmFyIGF0a1RpdGxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIGF0a1RpdGxlLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1zZWN0aW9uLXRpdGxlJzsKICAgICAgYXRrVGl0bGUudGV4dENvbnRlbnQgPSAnQ2hpw6p1IHRo4bupYyc7CiAgICAgIGF0a1dyYXAuYXBwZW5kQ2hpbGQoYXRrVGl0bGUpOwogICAgICBjYXJkLmF0dGFja3MuZm9yRWFjaChmdW5jdGlvbihhKXsKICAgICAgICB2YXIgYXRrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgICAgYXRrLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1hdHRhY2snOwogICAgICAgIHZhciBsaW5lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgICAgbGluZS5jbGFzc05hbWUgPSAncGgtbW9kYWwtYXR0YWNrLWxpbmUnOwogICAgICAgIHZhciBjb3N0ID0gQXJyYXkuaXNBcnJheShhLmNvc3QpICYmIGEuY29zdC5sZW5ndGggPyAnWycgKyBhLmNvc3Quam9pbignLCAnKSArICddICcgOiAnJzsKICAgICAgICBsaW5lLnRleHRDb250ZW50ID0gY29zdCArIChhLm5hbWUgfHwgJycpICsgKGEuZGFtYWdlID8gJyBcdTIwMTQgJyArIGEuZGFtYWdlIDogJycpOwogICAgICAgIGF0ay5hcHBlbmRDaGlsZChsaW5lKTsKICAgICAgICBpZiAoYS5lZmZlY3QpewogICAgICAgICAgdmFyIGVmZiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICAgICAgZWZmLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1hdHRhY2stZWZmZWN0JzsKICAgICAgICAgIGVmZi50ZXh0Q29udGVudCA9IGEuZWZmZWN0OwogICAgICAgICAgYXRrLmFwcGVuZENoaWxkKGVmZik7CiAgICAgICAgfQogICAgICAgIGF0a1dyYXAuYXBwZW5kQ2hpbGQoYXRrKTsKICAgICAgfSk7CiAgICAgIGVscy5tb2RhbEJvZHkuYXBwZW5kQ2hpbGQoYXRrV3JhcCk7CiAgICB9CgogICAgaWYgKGNhcmQuZWZmZWN0KXsKICAgICAgdmFyIGVmZldyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgZWZmV3JhcC5jbGFzc05hbWUgPSAncGgtbW9kYWwtc2VjdGlvbic7CiAgICAgIHZhciBlZmZUaXRsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBlZmZUaXRsZS5jbGFzc05hbWUgPSAncGgtbW9kYWwtc2VjdGlvbi10aXRsZSc7CiAgICAgIGVmZlRpdGxlLnRleHRDb250ZW50ID0gJ0hp4buHdSDhu6luZyc7CiAgICAgIGVmZldyYXAuYXBwZW5kQ2hpbGQoZWZmVGl0bGUpOwogICAgICB2YXIgZWZmVGV4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBlZmZUZXh0LmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1mbGF2b3InOwogICAgICBlZmZUZXh0LnRleHRDb250ZW50ID0gY2FyZC5lZmZlY3Q7CiAgICAgIGVmZldyYXAuYXBwZW5kQ2hpbGQoZWZmVGV4dCk7CiAgICAgIGVscy5tb2RhbEJvZHkuYXBwZW5kQ2hpbGQoZWZmV3JhcCk7CiAgICB9CgogICAgaWYgKEFycmF5LmlzQXJyYXkoY2FyZC53ZWFrbmVzc2VzKSAmJiBjYXJkLndlYWtuZXNzZXMubGVuZ3RoKXsKICAgICAgdmFyIHdUZXh0ID0gY2FyZC53ZWFrbmVzc2VzLm1hcChmdW5jdGlvbih3KXsgcmV0dXJuIHcudHlwZSArICh3LnZhbHVlID8gJyAnICsgdy52YWx1ZSA6ICcnKTsgfSkuam9pbignLCAnKTsKICAgICAgZWxzLm1vZGFsQm9keS5hcHBlbmRDaGlsZChtZXRhUm93KCfEkGnhu4NtIHnhur91Jywgd1RleHQpKTsKICAgIH0KCiAgICBpZiAoY2FyZC5kZXNjcmlwdGlvbil7CiAgICAgIHZhciBmbGF2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIGZsYXYuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXNlY3Rpb24nOwogICAgICB2YXIgZmxhdlRpdGxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIGZsYXZUaXRsZS5jbGFzc05hbWUgPSAncGgtbW9kYWwtc2VjdGlvbi10aXRsZSc7CiAgICAgIGZsYXZUaXRsZS50ZXh0Q29udGVudCA9ICdNw7QgdOG6oyc7CiAgICAgIGZsYXYuYXBwZW5kQ2hpbGQoZmxhdlRpdGxlKTsKICAgICAgdmFyIGZsYXZUZXh0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIGZsYXZUZXh0LmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1mbGF2b3InOwogICAgICBmbGF2VGV4dC50ZXh0Q29udGVudCA9IGNhcmQuZGVzY3JpcHRpb247CiAgICAgIGZsYXYuYXBwZW5kQ2hpbGQoZmxhdlRleHQpOwogICAgICBlbHMubW9kYWxCb2R5LmFwcGVuZENoaWxkKGZsYXYpOwogICAgfQoKICAgIG1vZGFsT3BlbiA9IHRydWU7CiAgICBlbHMubW9kYWwuaGlkZGVuID0gZmFsc2U7CiAgICBzZXRNb2RhbFZpZXdwb3J0T3Blbih0cnVlKTsKICAgIGVscy5tb2RhbENsb3NlLmZvY3VzKCk7CiAgfQoKICBlbHMubW9kYWxCYWNrZHJvcC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGNsb3NlQ2FyZE1vZGFsKTsKICBlbHMubW9kYWxDbG9zZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGNsb3NlQ2FyZE1vZGFsKTsKICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgZnVuY3Rpb24oZSl7CiAgICBpZiAoZS5rZXkgIT09ICdFc2NhcGUnKSByZXR1cm47CiAgICBpZiAoZWxzLndlbGNvbWVNb2RhbCAmJiAhZWxzLndlbGNvbWVNb2RhbC5oaWRkZW4pIHJldHVybjsKICAgIGlmIChtb2RhbE9wZW4peyBjbG9zZUNhcmRNb2RhbCgpOyByZXR1cm47IH0KICAgIGlmIChlbHMubGJNb2RhbCAmJiAhZWxzLmxiTW9kYWwuaGlkZGVuKXsgcGhMYkNsb3NlKCk7IHJldHVybjsgfQogICAgaWYgKGVscy5jb2xNb2RhbCAmJiAhZWxzLmNvbE1vZGFsLmhpZGRlbil7IGNsb3NlQ29sTW9kYWwoKTsgcmV0dXJuOyB9CiAgICBpZiAoZWxzLmNoYWxsZW5nZU1vZGFsICYmICFlbHMuY2hhbGxlbmdlTW9kYWwuaGlkZGVuKXsKICAgICAgaWYgKHR5cGVvZiBwaENoYWxsZW5nZUNsb3NlRW5oYW5jZWQgPT09ICdmdW5jdGlvbicpIHBoQ2hhbGxlbmdlQ2xvc2VFbmhhbmNlZCgpOwogICAgICBlbHNlIGNsb3NlQ2hhbGxlbmdlTW9kYWwoKTsKICAgICAgcmV0dXJuOwogICAgfQogICAgaWYgKGVscy5zaG9wTW9kYWwgJiYgIWVscy5zaG9wTW9kYWwuaGlkZGVuKXsKICAgICAgaWYgKHR5cGVvZiBwaFNob3BDbG9zZUV4dGVybmFsID09PSAnZnVuY3Rpb24nKSBwaFNob3BDbG9zZUV4dGVybmFsKCk7CiAgICAgIHJldHVybjsKICAgIH0KICB9KTsKCiAgLy8gLS0tLS0tLS0tLSBkaWFnbm9zdGljcyAtLS0tLS0tLS0tCiAgdmFyIGRpYWcgPSB7IGNoZWNrZWQ6MCwgb2s6MCwgZmFpbGVkOjAsIHRvdGFsOlNFUklFU19JRFMubGVuZ3RoLCByZW5kZXJlZEFueTpmYWxzZSwgc2FmZXR5RmlyZWQ6ZmFsc2UgfTsKCiAgZnVuY3Rpb24gdXBkYXRlU3RhdHVzTGluZSgpewogICAgaWYgKGRpYWcucmVuZGVyZWRBbnkpIHsgZWxzLnN0YXR1cy5oaWRkZW4gPSB0cnVlOyByZXR1cm47IH0KICAgIGVscy5zdGF0dXMuaGlkZGVuID0gZmFsc2U7CiAgICB2YXIgbGluZSA9ICfEkGFuZyB04bqjaSBkYW5oIHPDoWNoIGLhu5kgdGjhurvigKYgKMSRw6Mga2nhu4NtIHRyYSAnICsgZGlhZy5jaGVja2VkICsgJy8nICsgZGlhZy50b3RhbCArICcgbmd14buTbiwgJyArIGRpYWcub2sgKyAnIHRow6BuaCBjw7RuZywgJyArIGRpYWcuZmFpbGVkICsgJyBs4buXaSknOwogICAgZWxzLnN0YXR1cy5pbm5lckhUTUwgPSAnJzsKICAgIHZhciBwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBwLnRleHRDb250ZW50ID0gbGluZTsKICAgIGVscy5zdGF0dXMuYXBwZW5kQ2hpbGQocCk7CiAgICB2YXIgcHJvZ3Jlc3MgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHByb2dyZXNzLmNsYXNzTmFtZSA9ICdwaC1wcm9ncmVzcyc7CiAgICBwcm9ncmVzcy50ZXh0Q29udGVudCA9IGRpYWcuZmFpbGVkID4gMAogICAgICA/ICdN4buZdCBz4buRIG5ndeG7k24ga2jDtG5nIHBo4bqjbiBo4buTaSDigJQgbuG6v3UgZGFuaCBzw6FjaCBuw6B5IHbhuqtuIHRy4buRbmcgc2F1IHbDoGkgZ2nDonksIGPDsyB0aOG7gyBr4bq/dCBu4buRaSBj4bunYSBi4bqhbiDEkWFuZyBjaOG6t24gYXBpLnRjZ2RleC5uZXQuJwogICAgICA6ICfEkGFuZyBr4bq/dCBu4buRaSB04bubaSBhcGkudGNnZGV4Lm5ldOKApic7CiAgICBlbHMuc3RhdHVzLmFwcGVuZENoaWxkKHByb2dyZXNzKTsKICAgIGVscy5zdGF0dXMuYXBwZW5kQ2hpbGQobWFrZVRlc3RMaW5rKCkpOwogIH0KCiAgZnVuY3Rpb24gbWFrZVRlc3RMaW5rKCl7CiAgICB2YXIgcm93ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICByb3cuY2xhc3NOYW1lID0gJ3BoLXN0YXR1cy1yb3cnOwogICAgdmFyIGJ0biA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2J1dHRvbicpOwogICAgYnRuLnR5cGUgPSAnYnV0dG9uJzsKICAgIGJ0bi50ZXh0Q29udGVudCA9ICdLaeG7g20gdHJhIGvhur90IG7hu5FpIEFQSSBcdTIxOTcnOwogICAgYnRuLm9uY2xpY2sgPSBmdW5jdGlvbigpewogICAgICB3aW5kb3cub3BlbignaHR0cHM6Ly9hcGkudGNnZGV4Lm5ldC92Mi9lbi9zZXJpZXMvYmFzZScsICdfYmxhbmsnLCAnbm9vcGVuZXInKTsKICAgIH07CiAgICByb3cuYXBwZW5kQ2hpbGQoYnRuKTsKICAgIHJldHVybiByb3c7CiAgfQoKICBmdW5jdGlvbiBzaG93SGFyZEZhaWx1cmUoKXsKICAgIGVscy5zdGF0dXMuaGlkZGVuID0gZmFsc2U7CiAgICBlbHMuc3RhdHVzLmlubmVySFRNTCA9ICcnOwogICAgdmFyIHAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHAudGV4dENvbnRlbnQgPSAnS2jDtG5nIHRo4buDIHThuqNpIMSRxrDhu6NjIGLhu5kgdGjhursgbsOgby4gVG/DoG4gYuG7mSAnICsgZGlhZy50b3RhbCArICcgbmd14buTbiDEkeG7gXUgbOG7l2kgaG/hurdjIHF1w6EgdGjhu51pIGdpYW4gY2jhu50uJzsKICAgIGVscy5zdGF0dXMuYXBwZW5kQ2hpbGQocCk7CiAgICB2YXIgcDIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHAyLmNsYXNzTmFtZSA9ICdwaC1wcm9ncmVzcyc7CiAgICBwMi50ZXh0Q29udGVudCA9ICdIw6N5IGLhuqVtICJLaeG7g20gdHJhIGvhur90IG7hu5FpIEFQSSIgYsOqbiBkxrDhu5tpLiBO4bq/dSBsacOqbiBr4bq/dCDEkcOzIGPFqW5nIGtow7RuZyB04bqjaSDEkcaw4bujYyBk4buvIGxp4buHdSBKU09OIHRyb25nIHRhYiBt4bubaSwgdGjDrCBt4bqhbmcsIHTGsOG7nW5nIGzhu61hLCBob+G6t2MgbeG7mXQgY8OgaSDEkeG6t3QgYuG6o28gbeG6rXQvQ0ROIHRyw6puIHRyYW5nIG7DoHkgxJFhbmcgY2jhurduIGFwaS50Y2dkZXgubmV0Lic7CiAgICBlbHMuc3RhdHVzLmFwcGVuZENoaWxkKHAyKTsKICAgIHZhciByb3cgPSBtYWtlVGVzdExpbmsoKTsKICAgIHJvdy5zdHlsZS5tYXJnaW5Ub3AgPSAnMTBweCc7CiAgICB2YXIgcmV0cnkgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsKICAgIHJldHJ5LnR5cGUgPSAnYnV0dG9uJzsKICAgIHJldHJ5LnRleHRDb250ZW50ID0gJ1Ro4butIGzhuqFpJzsKICAgIHJldHJ5Lm9uY2xpY2sgPSBsb2FkRXhwYW5zaW9uczsKICAgIHJvdy5hcHBlbmRDaGlsZChyZXRyeSk7CiAgICBlbHMuc3RhdHVzLmFwcGVuZENoaWxkKHJvdyk7CiAgfQoKICBmdW5jdGlvbiBsb2FkRXhwYW5zaW9ucygpewogICAgZGlhZyA9IHsgY2hlY2tlZDowLCBvazowLCBmYWlsZWQ6MCwgdG90YWw6U0VSSUVTX0lEUy5sZW5ndGgsIHJlbmRlcmVkQW55OmZhbHNlLCBzYWZldHlGaXJlZDpmYWxzZSB9OwogICAgZWxzLmdyaWQudGV4dENvbnRlbnQgPSAnJzsKICAgIHVwZGF0ZVN0YXR1c0xpbmUoKTsKCiAgICB2YXIgc2VlbiA9IG5ldyBNYXAoKTsKCiAgICBmdW5jdGlvbiByZW5kZXJTb3J0ZWQoKXsKICAgICAgaWYgKCFzZWVuLnNpemUpIHJldHVybjsKICAgICAgZGlhZy5yZW5kZXJlZEFueSA9IHRydWU7CiAgICAgIHZhciBzb3J0ZWQgPSBBcnJheS5mcm9tKHNlZW4udmFsdWVzKCkpLnNvcnQoZnVuY3Rpb24oYSwgYil7CiAgICAgICAgdmFyIG5hID0gRElTUExBWV9TRVRfT1JERVIuaW5kZXhPZihTdHJpbmcoYS5zZXQubmFtZSB8fCAnJykpOwogICAgICAgIHZhciBuYiA9IERJU1BMQVlfU0VUX09SREVSLmluZGV4T2YoU3RyaW5nKGIuc2V0Lm5hbWUgfHwgJycpKTsKICAgICAgICBpZiAobmEgIT09IC0xIHx8IG5iICE9PSAtMSl7CiAgICAgICAgICBpZiAobmEgPT09IC0xKSByZXR1cm4gMTsKICAgICAgICAgIGlmIChuYiA9PT0gLTEpIHJldHVybiAtMTsKICAgICAgICAgIGlmIChuYSAhPT0gbmIpIHJldHVybiBuYSAtIG5iOwogICAgICAgIH0KICAgICAgICB2YXIgcmEgPSBzZXJpZXNQcmlvcml0eVJhbmsoYS5zZXJpZXNJZCksIHJiID0gc2VyaWVzUHJpb3JpdHlSYW5rKGIuc2VyaWVzSWQpOwogICAgICAgIGlmIChyYSAhPT0gcmIpIHJldHVybiByYSAtIHJiOwogICAgICAgIHZhciBkYSA9IGEuc2V0LnJlbGVhc2VEYXRlIHx8ICcnLCBkYiA9IGIuc2V0LnJlbGVhc2VEYXRlIHx8ICcnOwogICAgICAgIGlmIChkYSAhPT0gZGIpIHJldHVybiBkYi5sb2NhbGVDb21wYXJlKGRhKTsKICAgICAgICByZXR1cm4gU3RyaW5nKGEuc2V0Lm5hbWUgfHwgJycpLmxvY2FsZUNvbXBhcmUoU3RyaW5nKGIuc2V0Lm5hbWUgfHwgJycpKTsKICAgICAgfSk7CiAgICAgIHJlbmRlckV4cGFuc2lvbnMoc29ydGVkKTsKICAgIH0KCiAgICB2YXIgc2FmZXR5VGltZXIgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7CiAgICAgIGRpYWcuc2FmZXR5RmlyZWQgPSB0cnVlOwogICAgICBpZiAoIWRpYWcucmVuZGVyZWRBbnkpIHNob3dIYXJkRmFpbHVyZSgpOwogICAgfSwgMTUwMDApOwoKICAgIHZhciByZXF1ZXN0cyA9IFNFUklFU19JRFMubWFwKGZ1bmN0aW9uKGlkKXsKICAgICAgcmV0dXJuIGdldEpTT04oQVBJICsgJy9zZXJpZXMvJyArIGlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpewogICAgICAgIGRpYWcuY2hlY2tlZCsrOyBkaWFnLm9rKys7CiAgICAgICAgaWYgKGRhdGEgJiYgZGF0YS5zZXRzKXsKICAgICAgICAgIGRhdGEuc2V0cy5mb3JFYWNoKGZ1bmN0aW9uKHMpewogICAgICAgICAgICB2YXIgdG90YWwgPSAocy5jYXJkQ291bnQgJiYgcy5jYXJkQ291bnQudG90YWwpIHx8IDA7CiAgICAgICAgICAgIGlmICh0b3RhbCA8IDIwKSByZXR1cm47CiAgICAgICAgICAgIGlmICghc2Vlbi5oYXMocy5pZCkpIHNlZW4uc2V0KHMuaWQsIHsgc2V0OiBzLCBzZXJpZXM6IGRhdGEubmFtZSB8fCBkYXRhLmlkLCBzZXJpZXNJZDogaWQgfSk7CiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgaWYgKHNlZW4uc2l6ZSkgcmVuZGVyU29ydGVkKCk7CiAgICAgICAgZWxzZSB1cGRhdGVTdGF0dXNMaW5lKCk7CiAgICAgIH0pLmNhdGNoKGZ1bmN0aW9uKCl7CiAgICAgICAgZGlhZy5jaGVja2VkKys7IGRpYWcuZmFpbGVkKys7CiAgICAgICAgaWYgKCFkaWFnLnJlbmRlcmVkQW55KSB1cGRhdGVTdGF0dXNMaW5lKCk7CiAgICAgIH0pOwogICAgfSk7CgogICAgLy8gTeG7mXQgc+G7kSBzZXQgbeG7m2kgeHXhuqV0IGhp4buHbiB0csOqbiBUQ0dkZXggdHLGsOG7m2Mga2hpIHRo4bupIHThu7EgL3NlcmllcyDEkcaw4bujYyBj4bqtcCBuaOG6rXQuCiAgICAvLyBU4bqjaSB0cuG7sWMgdGnhur9wIGPDoWMgc2V0IG7hu5VpIGLhuq10IMSRw6MgeMOhYyBuaOG6rW47IG7hur91IEFQSSBjaMawYSBjw7MgdGjDrCBi4buPIHF1YSBhbiB0b8Ogbi4KICAgIHZhciBmZWF0dXJlZFJlcXVlc3RzID0gRkVBVFVSRURfU0VUX0lEUy5tYXAoZnVuY3Rpb24oaWQpewogICAgICByZXR1cm4gZ2V0SlNPTihBUEkgKyAnL3NldHMvJyArIGlkLCAxMDAwMCkudGhlbihmdW5jdGlvbihkYXRhKXsKICAgICAgICBpZiAoIWRhdGEgfHwgIWRhdGEuaWQpIHJldHVybjsKICAgICAgICB2YXIgdG90YWwgPSAoZGF0YS5jYXJkQ291bnQgJiYgZGF0YS5jYXJkQ291bnQudG90YWwpIHx8IDA7CiAgICAgICAgaWYgKHRvdGFsIDwgMjApIHJldHVybjsKICAgICAgICBpZiAoIXNlZW4uaGFzKGRhdGEuaWQpKXsKICAgICAgICAgIHZhciBzZXJpZSA9IGRhdGEuc2VyaWUgfHwge307CiAgICAgICAgICBzZWVuLnNldChkYXRhLmlkLCB7CiAgICAgICAgICAgIHNldDogZGF0YSwKICAgICAgICAgICAgc2VyaWVzOiBzZXJpZS5uYW1lIHx8IHNlcmllLmlkIHx8ICdNZWdhIEV2b2x1dGlvbicsCiAgICAgICAgICAgIHNlcmllc0lkOiBzZXJpZS5pZCB8fCAnbWUnCiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmVuZGVyU29ydGVkKCk7CiAgICAgIH0pLmNhdGNoKGZ1bmN0aW9uKCl7IC8qIEFQSSBjaMawYSBjw7Mgc2V0IG7DoHkgdGjDrCBraMO0bmcgbMOgbSBo4buPbmcgZGFuaCBzw6FjaCAqLyB9KTsKICAgIH0pOwoKICAgIFByb21pc2UuYWxsKHJlcXVlc3RzLmNvbmNhdChmZWF0dXJlZFJlcXVlc3RzKSkudGhlbihmdW5jdGlvbigpewogICAgICBjbGVhclRpbWVvdXQoc2FmZXR5VGltZXIpOwogICAgICBpZiAoIXNlZW4uc2l6ZSAmJiAhZGlhZy5zYWZldHlGaXJlZCkgc2hvd0hhcmRGYWlsdXJlKCk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHJlbmRlckV4cGFuc2lvbnMoZW50cmllcyl7CiAgICBzdGF0ZS5leHBhbnNpb25zID0gZW50cmllcyB8fCBbXTsKICAgIGVscy5zdGF0dXMuaGlkZGVuID0gdHJ1ZTsKICAgIGVscy5ncmlkLnRleHRDb250ZW50ID0gJyc7CiAgICBlbnRyaWVzLmZvckVhY2goZnVuY3Rpb24oZW50cnkpewogICAgICB2YXIgcyA9IGVudHJ5LnNldDsKICAgICAgdmFyIHBhaXIgPSBwYWxldHRlRm9yKHMuaWQpLCBjMSA9IHBhaXJbMF0sIGMyID0gcGFpclsxXTsKICAgICAgdmFyIGNhcmQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsKICAgICAgY2FyZC5jbGFzc05hbWUgPSAncGgtZXhwLWNhcmQnOwogICAgICBjYXJkLnR5cGUgPSAnYnV0dG9uJzsKICAgICAgY2FyZC5kYXRhc2V0LnNldElkID0gU3RyaW5nKHMuaWQpOwoKICAgICAgdmFyIGFydCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBhcnQuY2xhc3NOYW1lID0gJ3BoLWFydCc7CiAgICAgIGFydC5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1jMScsIGMxKTsKICAgICAgYXJ0LnN0eWxlLnNldFByb3BlcnR5KCctLWMyJywgYzIpOwoKICAgICAgdmFyIGJnU3JjID0gKHR5cGVvZiBzZXRCYWNrZ3JvdW5kVXJsID09PSAnZnVuY3Rpb24nKSA/IHNldEJhY2tncm91bmRVcmwocykgOiAnJzsKICAgICAgdmFyIGJnSW1nID0gbnVsbDsKCiAgICAgIHZhciBnbHlwaCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgZ2x5cGguY2xhc3NOYW1lID0gJ3BoLWFydC1nbHlwaCc7CiAgICAgIGdseXBoLnRleHRDb250ZW50ID0gKHMubmFtZSB8fCAnPycpLnNsaWNlKDAsMSk7CgogICAgICBpZiAoYmdTcmMpewogICAgICAgIC8vIETDuW5nIGPhuqMgPGltZz4gdGjhuq10IGzhuqtuIGJhY2tncm91bmQgQ1NTIMSR4buDIOG6o25oIGNo4bqvYyBjaOG6r24gxJHGsOG7o2MgcmVuZGVyLgogICAgICAgIGFydC5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2UgPSAnbGluZWFyLWdyYWRpZW50KDE4MGRlZyxyZ2JhKDgsMTAsMjQsLjA4KSAwJSxyZ2JhKDgsMTAsMjQsLjM0KSAxMDAlKSx1cmwoIicgKyBiZ1NyYyArICciKSc7CiAgICAgICAgYXJ0LnN0eWxlLmJhY2tncm91bmRTaXplID0gJ2NvdmVyJzsKICAgICAgICBhcnQuc3R5bGUuYmFja2dyb3VuZFBvc2l0aW9uID0gJ2NlbnRlciAzOCUnOwogICAgICAgIGFydC5zdHlsZS5iYWNrZ3JvdW5kUmVwZWF0ID0gJ25vLXJlcGVhdCc7CgogICAgICAgIGJnSW1nID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7CiAgICAgICAgYmdJbWcuY2xhc3NOYW1lID0gJ3BoLWFydC1iZyc7CiAgICAgICAgYmdJbWcuYWx0ID0gJyc7CiAgICAgICAgYmdJbWcuZGVjb2RpbmcgPSAnYXN5bmMnOwogICAgICAgIGJnSW1nLmxvYWRpbmcgPSAnZWFnZXInOwogICAgICAgIGJnSW1nLnNyYyA9IGJnU3JjOwogICAgICAgIGJnSW1nLm9ubG9hZCA9IGZ1bmN0aW9uKCl7IGdseXBoLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7IH07CiAgICAgICAgYmdJbWcub25lcnJvciA9IGZ1bmN0aW9uKCl7CiAgICAgICAgICBpZiAoYmdJbWcucGFyZW50Tm9kZSkgYmdJbWcucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChiZ0ltZyk7CiAgICAgICAgICBhcnQuc3R5bGUuYmFja2dyb3VuZEltYWdlID0gJ2xpbmVhci1ncmFkaWVudCgxNTBkZWcsdmFyKC0tYzEpIDAlLHZhcigtLWMyKSAxMDAlKSc7CiAgICAgICAgICBnbHlwaC5zdHlsZS5kaXNwbGF5ID0gJyc7CiAgICAgICAgfTsKICAgICAgICBhcnQuYXBwZW5kQ2hpbGQoYmdJbWcpOwogICAgICB9CgogICAgICBhcnQuYXBwZW5kQ2hpbGQoZ2x5cGgpOwoKICAgICAgdmFyIGxvZ29TcmMgPSBzZXRMb2dvVXJsKHMpOwogICAgICBpZiAobG9nb1NyYyl7CiAgICAgICAgdmFyIGxvZ28gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTsKICAgICAgICBsb2dvLmNsYXNzTmFtZSA9ICdwaC1hcnQtbG9nbyc7CiAgICAgICAgbG9nby5sb2FkaW5nID0gJ2xhenknOwogICAgICAgIGxvZ28uYWx0ID0gcy5uYW1lIHx8ICcnOwogICAgICAgIGxvZ28uc3JjID0gbG9nb1NyYzsKICAgICAgICBsb2dvLm9ubG9hZCA9IGZ1bmN0aW9uKCl7IGdseXBoLmNsYXNzTGlzdC5hZGQoJ3BoLWFydC1nbHlwaC1oaWRkZW4nKTsgfTsKICAgICAgICBsb2dvLm9uZXJyb3IgPSBmdW5jdGlvbigpewogICAgICAgICAgaWYgKGxvZ28ucGFyZW50Tm9kZSkgbG9nby5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGxvZ28pOwogICAgICAgIH07CiAgICAgICAgYXJ0LmFwcGVuZENoaWxkKGxvZ28pOwogICAgICB9CgogICAgICB2YXIgbWV0YSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBtZXRhLmNsYXNzTmFtZSA9ICdwaC1tZXRhJzsKICAgICAgdmFyIG5hbWUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgbmFtZS5jbGFzc05hbWUgPSAncGgtbmFtZSc7CiAgICAgIG5hbWUudGV4dENvbnRlbnQgPSBzLm5hbWU7CiAgICAgIHZhciBzZXJpZXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgc2VyaWVzLmNsYXNzTmFtZSA9ICdwaC1zZXJpZXMnOwogICAgICBzZXJpZXMudGV4dENvbnRlbnQgPSBlbnRyeS5zZXJpZXMgfHwgJyc7CiAgICAgIHZhciBjb3VudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBjb3VudC5jbGFzc05hbWUgPSAncGgtY291bnQnOwogICAgICBjb3VudC50ZXh0Q29udGVudCA9ICgocy5jYXJkQ291bnQgJiYgcy5jYXJkQ291bnQudG90YWwpIHx8ICc/JykgKyAnIGzDoSBiw6BpJzsKICAgICAgbWV0YS5hcHBlbmRDaGlsZChuYW1lKTsgbWV0YS5hcHBlbmRDaGlsZChzZXJpZXMpOyBtZXRhLmFwcGVuZENoaWxkKGNvdW50KTsKCiAgICAgIHZhciBwcm9nID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIHByb2cuY2xhc3NOYW1lID0gJ3BoLWV4cC1wcm9ncmVzcyc7CiAgICAgIHZhciBwcm9nTWV0YSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBwcm9nTWV0YS5jbGFzc05hbWUgPSAncGgtZXhwLXByb2dyZXNzLW1ldGEnOwogICAgICB2YXIgaW5pdGlhbFRvdGFsID0gTnVtYmVyKHMgJiYgcy5jYXJkQ291bnQgJiYgcy5jYXJkQ291bnQudG90YWwpIHx8IDA7CiAgICAgIHByb2dNZXRhLnRleHRDb250ZW50ID0gJzAvJyArIGluaXRpYWxUb3RhbCArICcgbMOhIMK3IDAlJzsKICAgICAgdmFyIHByb2dUcmFjayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgcHJvZ1RyYWNrLmNsYXNzTmFtZSA9ICdwaC1leHAtcHJvZ3Jlc3MtdHJhY2snOwogICAgICB2YXIgcHJvZ0ZpbGwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICAgIHByb2dGaWxsLmNsYXNzTmFtZSA9ICdwaC1leHAtcHJvZ3Jlc3MtZmlsbCc7CiAgICAgIHByb2dUcmFjay5hcHBlbmRDaGlsZChwcm9nRmlsbCk7CiAgICAgIHByb2cuYXBwZW5kQ2hpbGQocHJvZ01ldGEpOwogICAgICBwcm9nLmFwcGVuZENoaWxkKHByb2dUcmFjayk7CiAgICAgIG1ldGEuYXBwZW5kQ2hpbGQocHJvZyk7CgogICAgICBjYXJkLmFwcGVuZENoaWxkKGFydCk7IGNhcmQuYXBwZW5kQ2hpbGQobWV0YSk7CiAgICAgIHVwZGF0ZUV4cGFuc2lvblByb2dyZXNzQ2FyZChjYXJkLCBzKTsKICAgICAgY2FyZC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7IHNlbGVjdEV4cGFuc2lvbihzLCBjMSwgYzIpOyB9KTsKICAgICAgZWxzLmdyaWQuYXBwZW5kQ2hpbGQoY2FyZCk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHNlbGVjdEV4cGFuc2lvbihzZXQsIGMxLCBjMil7CiAgICBzdGF0ZS5jdXJyZW50ID0gc2V0OwogICAgc3RhdGUucG9vbCA9IFtdOwogICAgc3RhdGUudGllclBvb2xzID0gbnVsbDsKICAgIHN0YXRlLmNhY2hlLmNsZWFyKCk7CiAgICBzZXRBY2NlbnQoYzEsIGMyKTsKICAgIGFwcGx5UGFja1Zpc3VhbChzZXQsIG51bGwpOwogICAgZWxzLnBpY2suY2xhc3NMaXN0LnJlbW92ZSgncGgtYWN0aXZlJyk7CiAgICBlbHMub3Blbi5jbGFzc0xpc3QuYWRkKCdwaC1hY3RpdmUnKTsKICAgIHN0YXRlLm9wZW5TY3JvbGxZID0gTWF0aC5tYXgoMCwgTWF0aC5yb3VuZCh3aW5kb3cucGFnZVlPZmZzZXQgfHwgd2luZG93LnNjcm9sbFkgfHwgMCkpOwogICAgcmVzZXRTdGFnZSgpOwogICAgZWxzLmVyci5oaWRkZW4gPSB0cnVlOwogICAgZWxzLnBhY2suY2xhc3NMaXN0LmFkZCgncGgtYnVzeScpOwogICAgZWxzLmJveFJvdy5oaWRkZW4gPSB0cnVlOwogICAgZWxzLmhpbnQudGV4dENvbnRlbnQgPSAnxJBhbmcgdOG6o2kgdGjhursgYsOgaSBjaG8gYuG7mSBuw6B54oCmJzsKCiAgICBnZXRKU09OKEFQSSArICcvc2V0cy8nICsgc2V0LmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpewogICAgICAvLyBN4buZdCBz4buRIGLhu5kgcHJvbW8gY+G7p2EgVENHZGV4ICjEkWnhu4NuIGjDrG5oIE1FUCkgY8OzIGNhcmQuaW1hZ2UgPSBudWxsIGTDuQogICAgICAvLyBjYXJkIElEIC8gZOG7ryBsaeG7h3UgbMOhIHbhuqtuIHThu5NuIHThuqFpLiBLaMO0bmcgxJHGsOG7o2MgZMO5bmcgaW1hZ2UgbMOgbSDEkWnhu4F1IGtp4buHbgogICAgICAvLyDEkeG7gyBxdXnhur90IMSR4buLbmggYuG7mSBjw7MgbeG7nyDEkcaw4bujYyBoYXkga2jDtG5nLiDhuqJuaCBz4bq9IGZhbGxiYWNrIHNhbmcgdMOqbiBsw6Ega2hpCiAgICAgIC8vIENETiBjaMawYSBjw7Mgc2Nhbi4KICAgICAgc3RhdGUucG9vbCA9IEFycmF5LmlzQXJyYXkoZGF0YS5jYXJkcykgPyBkYXRhLmNhcmRzLmZpbHRlcihmdW5jdGlvbihjKXsgcmV0dXJuIGMgJiYgYy5pZDsgfSkgOiBbXTsKICAgICAgaWYgKCFzdGF0ZS5wb29sLmxlbmd0aCkgdGhyb3cgbmV3IEVycm9yKCdBUEkga2jDtG5nIHRy4bqjIHbhu4EgbMOhIGLDoGkgY2hvIGLhu5kgbsOgeScpOwogICAgICBhcHBseVBhY2tWaXN1YWwoc2V0LCBkYXRhKTsKICAgICAgZWxzLnBhY2suY2xhc3NMaXN0LnJlbW92ZSgncGgtYnVzeScpOwogICAgICBlbHMuaGludC50ZXh0Q29udGVudCA9ICdDaOG6oW0gdsOgbyBnw7NpIGLDoGkgxJHhu4MgbeG7nyc7CiAgICAgIGVscy5ib3hSb3cuaGlkZGVuID0gZmFsc2U7CiAgICAgIHVwZGF0ZUN1cnJlbnRTZXRQcm9ncmVzcygpOwogICAgICAvLyBE4buxbmcgdOG7iSBs4buHIHLDunQgdGhlbyDEkeG7mSBoaeG6v20gY2hvIGLhu5kgbsOgeSDhu58gbuG7gW4g4oCUIGtow7RuZyBjaOG6t24gbsO6dCBt4bufCiAgICAgIC8vIGfDs2k6IG7hur91IGNoxrBhIHhvbmcgaG/hurdjIHRo4bqldCBi4bqhaSwgbeG7nyBnw7NpIHbhuqtuIGTDuW5nIGtp4buDdSByw7p0IMSR4buBdSBjxakuCiAgICAgIGJ1aWxkVGllclBvb2xzKHNldC5pZCwgc3RhdGUucG9vbC5tYXAoZnVuY3Rpb24oYyl7IHJldHVybiBjLmlkOyB9KSk7CiAgICB9KS5jYXRjaChmdW5jdGlvbihlcnIpewogICAgICBlbHMucGFjay5jbGFzc0xpc3QucmVtb3ZlKCdwaC1idXN5Jyk7CiAgICAgIGVscy5oaW50LnRleHRDb250ZW50ID0gJyc7CiAgICAgIGVscy5lcnIudGV4dENvbnRlbnQgPSAnS2jDtG5nIHRo4buDIHThuqNpIHRo4bq7IGLDoGkgY2hvIGLhu5kgbsOgeSAoJyArIGVyci5tZXNzYWdlICsgJykuIEjDo3kgY2jhu41uIGLhu5kga2jDoWMgaG/hurdjIHThuqNpIGzhuqFpIHRyYW5nLic7CiAgICAgIGVscy5lcnIuaGlkZGVuID0gZmFsc2U7CiAgICB9KTsKICB9CgogIGVscy5iYWNrLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZnVuY3Rpb24oKXsKICAgIGVscy5vcGVuLmNsYXNzTGlzdC5yZW1vdmUoJ3BoLWFjdGl2ZScpOwogICAgZWxzLnBpY2suY2xhc3NMaXN0LmFkZCgncGgtYWN0aXZlJyk7CiAgfSk7CiAgZWxzLmNoYW5nZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7IGVscy5iYWNrLmNsaWNrKCk7IH0pOwogIGVscy5ib3hDaGFuZ2UuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpeyBlbHMuYmFjay5jbGljaygpOyB9KTsKCiAgLy8gR2nhu68gduG7iyB0csOtIHRoYW8gdMOhYyDhu5VuIMSR4buLbmggdHLDqm4gUEM6IHNhdSBraGkga+G6v3QgcXXhuqMgMy81LzEwIGfDs2kgZMOgaSDEkcaw4bujYyDhuqluLAogIC8vIHRyYW5nIGPDsyB0aOG7gyBjbyBs4bqhaSB2w6AgYnJvd3NlciB04buxIGFuY2hvciB2w6BvIG3hu5l0IHbhu4sgdHLDrSBraMOhYy4gTHXDtG4gxJHGsGEgdsO5bmcgcGFjawogIC8vIHbhu4EgxJHDum5nIG3hu5l0IG3hu5FjIGPhu5EgxJHhu4tuaCDEkeG7gyBuw7p0ICJN4bufIGfDs2kiIGx1w7RuIG7hurFtIOG7nyBjw7luZyBraHUgduG7sWMgbcOgbiBow6xuaC4KICBmdW5jdGlvbiBzY3JvbGxUb09wZW5BbmNob3IoYmVoYXZpb3IpewogICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uKCl7CiAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShmdW5jdGlvbigpewogICAgICAgIGlmICghZWxzLnN0YWdlIHx8IGVscy5vcGVuLmNsYXNzTGlzdC5jb250YWlucygncGgtYWN0aXZlJykgPT09IGZhbHNlKSByZXR1cm47CiAgICAgICAgdmFyIHRvcDsKICAgICAgICBpZiAoTnVtYmVyLmlzRmluaXRlKE51bWJlcihzdGF0ZS5vcGVuU2Nyb2xsWSkpKSB0b3AgPSBNYXRoLm1heCgwLCBNYXRoLnJvdW5kKE51bWJlcihzdGF0ZS5vcGVuU2Nyb2xsWSkpKTsKICAgICAgICBlbHNlIHsKICAgICAgICAgIHZhciByZWN0ID0gZWxzLnN0YWdlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogICAgICAgICAgdG9wID0gTWF0aC5tYXgoMCwgTWF0aC5yb3VuZCh3aW5kb3cucGFnZVlPZmZzZXQgKyByZWN0LnRvcCAtIDI4KSk7CiAgICAgICAgfQogICAgICAgIHRyeXsgd2luZG93LnNjcm9sbFRvKHt0b3A6dG9wLCBiZWhhdmlvcjpiZWhhdmlvciB8fCAnYXV0byd9KTsgfQogICAgICAgIGNhdGNoKGUpeyB3aW5kb3cuc2Nyb2xsVG8oMCwgdG9wKTsgfQogICAgICB9KTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gcmVzZXRTdGFnZSgpewogICAgc3RhdGUub3BlbmluZyA9IGZhbHNlOwogICAgZWxzLnN0YWdlLnN0eWxlLmRpc3BsYXkgPSAnZmxleCc7CiAgICBlbHMucHJvZ3Jlc3MuaGlkZGVuID0gdHJ1ZTsKICAgIGVscy5ib3guY2xhc3NMaXN0LnJlbW92ZSgncGgtYWN0aXZlJyk7CiAgICBlbHMucmV2ZWFsLmNsYXNzTGlzdC5yZW1vdmUoJ3BoLWFjdGl2ZScsJ3BoLWV4dHJhY3Qtc291cmNlJyk7CiAgICBlbHMucGFjay5jbGFzc0xpc3QucmVtb3ZlKCdwaC1yaXBwaW5nJyk7CiAgICBlbHMuaGludC50ZXh0Q29udGVudCA9ICdDaOG6oW0gdsOgbyBnw7NpIGLDoGkgxJHhu4MgbeG7nyc7CiAgICBlbHMuYm94Um93LmhpZGRlbiA9ICFzdGF0ZS5wb29sLmxlbmd0aDsKICAgIGVscy5iYW5uZXIuY2xhc3NOYW1lID0gJ3BoLWJhbm5lcic7CiAgICBlbHMuYmFubmVyLnRleHRDb250ZW50ID0gJyc7CiAgICBlbHMubHVjay5oaWRkZW4gPSB0cnVlOwogICAgZWxzLnRvdGFsLmhpZGRlbiA9IHRydWU7CiAgICBlbHMuc2F2ZWQudGV4dENvbnRlbnQgPSAnJzsKICAgIGVscy5xdWlwLnRleHRDb250ZW50ID0gJyc7CiAgICBlbHMucXVpcC5jbGFzc05hbWUgPSAncGgtcXVpcCc7CiAgICBlbHMucm93LnRleHRDb250ZW50ID0gJyc7CiAgICBjbGVhckNvbmZldHRpKCk7CiAgICBjbG9zZUNhcmRNb2RhbCgpOwogICAgdXBkYXRlQ3VycmVudFNldFByb2dyZXNzKCk7CiAgICBzY3JvbGxUb09wZW5BbmNob3IoJ2F1dG8nKTsKICB9CgogIGZ1bmN0aW9uIHBpY2tSYW5kb20oYSl7IHJldHVybiBhW01hdGguZmxvb3IoTWF0aC5yYW5kb20oKSphLmxlbmd0aCldOyB9CgogIGZ1bmN0aW9uIGZ1bGxDYXJkKGlkKXsKICAgIGlmIChzdGF0ZS5jYWNoZS5oYXMoaWQpKSByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHN0YXRlLmNhY2hlLmdldChpZCkpOwogICAgcmV0dXJuIGdldEpTT04oQVBJICsgJy9jYXJkcy8nICsgaWQpLnRoZW4oZnVuY3Rpb24oYyl7CiAgICAgIHN0YXRlLmNhY2hlLnNldChpZCwgYyk7CiAgICAgIHJldHVybiBjOwogICAgfSk7CiAgfQoKICBmdW5jdGlvbiBwaFNob3BMb2FkKGQpewogICAgaWYgKCFkLmNoYWxsZW5nZUludmVudG9yeSB8fCB0eXBlb2YgZC5jaGFsbGVuZ2VJbnZlbnRvcnkgIT09ICdvYmplY3QnKSBkLmNoYWxsZW5nZUludmVudG9yeSA9IHt9OwogICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoTnVtYmVyKGQuY2hhbGxlbmdlSW52ZW50b3J5Lmx1Y2t5KSkpIGQuY2hhbGxlbmdlSW52ZW50b3J5Lmx1Y2t5ID0gMDsKICAgIGlmICghTnVtYmVyLmlzRmluaXRlKE51bWJlcihkLmNoYWxsZW5nZUludmVudG9yeS5yYXJlKSkpIGQuY2hhbGxlbmdlSW52ZW50b3J5LnJhcmUgPSAwOwogICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoTnVtYmVyKGQuY2hhbGxlbmdlSW52ZW50b3J5LmNyb3duKSkpIGQuY2hhbGxlbmdlSW52ZW50b3J5LmNyb3duID0gMDsKICAgIHJldHVybiBkLmNoYWxsZW5nZUludmVudG9yeTsKICB9CgogIGZ1bmN0aW9uIHBoU2hvcFRha2VCb29zdCgpewogICAgdmFyIGQgPSBsb2FkREIoKSwgaW52ID0gcGhTaG9wTG9hZChkKTsKICAgIHZhciBib29zdCA9IHsgbHVja3k6ZmFsc2UsIHJhcmU6ZmFsc2UsIGNyb3duOmZhbHNlIH07CiAgICBpZiAoaW52LmNyb3duID4gMCl7IGludi5jcm93bi0tOyBib29zdC5jcm93biA9IHRydWU7IH0KICAgIGlmIChpbnYucmFyZSA+IDApeyBpbnYucmFyZS0tOyBib29zdC5yYXJlID0gdHJ1ZTsgfQogICAgaWYgKGludi5sdWNreSA+IDApeyBpbnYubHVja3ktLTsgYm9vc3QubHVja3kgPSB0cnVlOyB9CiAgICBzYXZlREIoKTsKICAgIHJldHVybiBib29zdDsKICB9CgogIGZ1bmN0aW9uIHBoU2hvcFJlZnVuZEJvb3N0KGJvb3N0KXsKICAgIGlmICghYm9vc3QpIHJldHVybjsKICAgIHZhciBkID0gbG9hZERCKCksIGludiA9IHBoU2hvcExvYWQoZCk7CiAgICBpZiAoYm9vc3QubHVja3kpIGludi5sdWNreSsrOwogICAgaWYgKGJvb3N0LnJhcmUpIGludi5yYXJlKys7CiAgICBpZiAoYm9vc3QuY3Jvd24pIGludi5jcm93bisrOwogICAgc2F2ZURCKCk7CiAgfQoKICBmdW5jdGlvbiByZWNvcmRTaG9wQ2FyZChjYXJkKXsKICAgIGlmICghY2FyZCB8fCAhY2FyZC5pZCkgcmV0dXJuIGZhbHNlOwogICAgdmFyIGQgPSBsb2FkREIoKTsKICAgIHZhciB1c2QgPSBwcmljZVVTRChjYXJkKTsKICAgIHZhciBlID0gZC5jYXJkc1tjYXJkLmlkXTsKICAgIHZhciBzZXRJZCA9IChjYXJkLnNldCAmJiBjYXJkLnNldC5pZCkgfHwgKHN0YXRlLmN1cnJlbnQgJiYgc3RhdGUuY3VycmVudC5pZCkgfHwgJyc7CiAgICB2YXIgc2V0TmFtZSA9IChjYXJkLnNldCAmJiBjYXJkLnNldC5uYW1lKSB8fCAoc3RhdGUuY3VycmVudCAmJiBzdGF0ZS5jdXJyZW50Lm5hbWUpIHx8ICcnOwogICAgdmFyIGxvY2FsSWQgPSBjYXJkLmxvY2FsSWQgIT0gbnVsbCA/IFN0cmluZyhjYXJkLmxvY2FsSWQpIDogJyc7CgogICAgaWYgKGUpewogICAgICBlLm4gPSBNYXRoLm1heCgwLCBOdW1iZXIoZS5uKSB8fCAwKSArIDE7CiAgICAgIGlmICh1c2QgIT0gbnVsbCkgZS5wcmljZSA9IHVzZDsKICAgICAgaWYgKHNldElkICYmICFlLnNldElkKSBlLnNldElkID0gc2V0SWQ7CiAgICAgIGlmIChzZXROYW1lICYmICFlLnNldCkgZS5zZXQgPSBzZXROYW1lOwogICAgICBpZiAobG9jYWxJZCAmJiAhZS5sb2NhbElkKSBlLmxvY2FsSWQgPSBsb2NhbElkOwogICAgICBpZiAoY2FyZC5pbWFnZSAmJiAhZS5pbWcpIGUuaW1nID0gY2FyZC5pbWFnZTsKICAgICAgaWYgKGNhcmQucmFyaXR5ICYmICFlLnJhcml0eSkgZS5yYXJpdHkgPSBjYXJkLnJhcml0eTsKICAgICAgaWYgKGUudGllciA9PSBudWxsKSBlLnRpZXIgPSByYXJpdHlUaWVyKGNhcmQucmFyaXR5KS50aWVyOwogICAgICBlLm5ld0Zyb21TaG9wID0gdHJ1ZTsKICAgICAgZS5sYXN0U2hvcFJlZGVlbWVkQXQgPSBEYXRlLm5vdygpOwogICAgfSBlbHNlIHsKICAgICAgZC5jYXJkc1tjYXJkLmlkXSA9IHsKICAgICAgICBpZDogY2FyZC5pZCwKICAgICAgICBuYW1lOiBjYXJkLm5hbWUgfHwgJycsCiAgICAgICAgaW1nOiBjYXJkLmltYWdlIHx8ICcnLAogICAgICAgIHJhcml0eTogY2FyZC5yYXJpdHkgfHwgJycsCiAgICAgICAgdGllcjogcmFyaXR5VGllcihjYXJkLnJhcml0eSkudGllciwKICAgICAgICBwcmljZTogdXNkLAogICAgICAgIG46IDEsCiAgICAgICAgc2V0OiBzZXROYW1lLAogICAgICAgIHNldElkOiBzZXRJZCwKICAgICAgICBsb2NhbElkOiBsb2NhbElkLAogICAgICAgIHQ6IERhdGUubm93KCksCiAgICAgICAgbmV3RnJvbVNob3A6IHRydWUsCiAgICAgICAgbGFzdFNob3BSZWRlZW1lZEF0OiBEYXRlLm5vdygpCiAgICAgIH07CiAgICB9CgogICAgc2F2ZURCKCk7CiAgICB1cGRhdGVDb2xCYWRnZSgpOwogICAgdXBkYXRlQ3VycmVudFNldFByb2dyZXNzKCk7CiAgICBpZiAoc3RhdGUuZXhwYW5zaW9ucyAmJiBzdGF0ZS5leHBhbnNpb25zLmxlbmd0aCl7CiAgICAgIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoZWxzLmdyaWQucXVlcnlTZWxlY3RvckFsbCgnLnBoLWV4cC1jYXJkJyksIGZ1bmN0aW9uKGNhcmRFbCl7CiAgICAgICAgdmFyIGlkID0gY2FyZEVsLmRhdGFzZXQuc2V0SWQ7CiAgICAgICAgaWYgKCFpZCkgcmV0dXJuOwogICAgICAgIHZhciBmb3VuZCA9IHN0YXRlLmV4cGFuc2lvbnMuZmluZChmdW5jdGlvbih4KXsgcmV0dXJuIHguc2V0ICYmIFN0cmluZyh4LnNldC5pZCkgPT09IFN0cmluZyhpZCk7IH0pOwogICAgICAgIGlmIChmb3VuZCkgdXBkYXRlRXhwYW5zaW9uUHJvZ3Jlc3NDYXJkKGNhcmRFbCwgZm91bmQuc2V0KTsKICAgICAgfSk7CiAgICB9CiAgICByZXR1cm4gdHJ1ZTsKICB9CgogIGZ1bmN0aW9uIHBoU2hvcEJ1eUNhcmQoY29zdCwgbGFiZWwpewogICAgdmFyIGQgPSBsb2FkREIoKTsKICAgIHBoU2hvcExvYWQoZCk7CiAgICB2YXIgcG9pbnRzID0gTWF0aC5tYXgoMCwgTnVtYmVyKGQuY2hhbGxlbmdlUG9pbnRzKSB8fCAwKTsKICAgIGlmIChwb2ludHMgPCBjb3N0KXsKICAgICAgdG9hc3QoJ+KtkCBLaMO0bmcgxJHhu6cgxJFp4buDbSDEkeG7gyDEkeG7lWkgJyArIGxhYmVsICsgJy4nKTsKICAgICAgcmV0dXJuIGZhbHNlOwogICAgfQogICAgaWYgKCFzdGF0ZS5wb29sIHx8ICFzdGF0ZS5wb29sLmxlbmd0aCl7CiAgICAgIHRvYXN0KCfimqDvuI8gSMOjeSBjaOG7jW4gbeG7mXQgYuG7mSB0aOG6uyB0csaw4bubYyBraGkgxJHhu5VpIHRo4bq7LicpOwogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgdmFyIGNhcmRJZDsKICAgIHRyeXsKICAgICAgY2FyZElkID0gcGlja0NhcmRJZCh7fSk7CiAgICB9Y2F0Y2goZSl7CiAgICAgIHRvYXN0KCfimqDvuI8gQ2jGsGEgdGjhu4MgY2jhu41uIHRo4bq7IMSR4buDIMSR4buVaS4gVnVpIGzDsm5nIHRo4butIGzhuqFpLicpOwogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgcmV0dXJuIGZ1bGxDYXJkKGNhcmRJZCkudGhlbihmdW5jdGlvbihjYXJkKXsKICAgICAgaWYgKCFjYXJkIHx8ICFjYXJkLmlkKSB0aHJvdyBuZXcgRXJyb3IoJ0tow7RuZyBs4bqleSDEkcaw4bujYyBk4buvIGxp4buHdSBsw6EgYsOgaScpOwogICAgICB2YXIgbGF0ZXN0ID0gbG9hZERCKCk7CiAgICAgIHZhciBsYXRlc3RQb2ludHMgPSBNYXRoLm1heCgwLCBOdW1iZXIobGF0ZXN0LmNoYWxsZW5nZVBvaW50cykgfHwgMCk7CiAgICAgIHBoU2hvcExvYWQobGF0ZXN0KTsKICAgICAgaWYgKGxhdGVzdFBvaW50cyA8IGNvc3QpewogICAgICAgIHRvYXN0KCfirZAgS2jDtG5nIMSR4bunIMSRaeG7g20gxJHhu4MgxJHhu5VpICcgKyBsYWJlbCArICcuJyk7CiAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICB9CiAgICAgIGxhdGVzdC5jaGFsbGVuZ2VQb2ludHMgPSBsYXRlc3RQb2ludHMgLSBjb3N0OwogICAgICBzYXZlREIoKTsKICAgICAgcmVjb3JkU2hvcENhcmQoY2FyZCk7CiAgICAgIHVwZGF0ZUNoYWxsZW5nZUJhZGdlKCk7CiAgICAgIHVwZGF0ZVNob3BCYWRnZSgpOwogICAgICBpZiAoZWxzLnNob3BNb2RhbCAmJiAhZWxzLnNob3BNb2RhbC5oaWRkZW4pIHJlbmRlckV4dGVybmFsU2hvcCgpOwogICAgICBpZiAoZWxzLmNvbE1vZGFsICYmICFlbHMuY29sTW9kYWwuaGlkZGVuKSByZW5kZXJDb2xsZWN0aW9uKCk7CiAgICAgIHRvYXN0KCfwn460IMSQw6MgxJHhu5VpICcgKyAoY2FyZC5uYW1lIHx8IGxhYmVsKSArICcuIFRo4bq7IMSRw6MgdsOgbyBC4buZIHPGsHUgdOG6rXAgdsOgIMSRxrDhu6NjIMSRw6FuaCBk4bqldSBN4bubaS4nKTsKICAgICAgcmV0dXJuIHRydWU7CiAgICB9KS5jYXRjaChmdW5jdGlvbigpewogICAgICB0b2FzdCgn4pqg77iPIMSQ4buVaSB0aOG6uyB0aOG6pXQgYuG6oWkuIMSQaeG7g20gY+G7p2EgYuG6oW4ga2jDtG5nIGLhu4sgdHLhu6suJyk7CiAgICAgIHJldHVybiBmYWxzZTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gcGhTaG9wQnV5KGtleSwgY29zdCwgbGFiZWwpewogICAgdmFyIGQgPSBsb2FkREIoKTsKICAgIHBoU2hvcExvYWQoZCk7CiAgICB2YXIgcG9pbnRzID0gTWF0aC5tYXgoMCwgTnVtYmVyKGQuY2hhbGxlbmdlUG9pbnRzKSB8fCAwKTsKICAgIGlmIChwb2ludHMgPCBjb3N0KXsgdG9hc3QoJ+KtkCBLaMO0bmcgxJHhu6cgxJFp4buDbSDEkeG7gyDEkeG7lWkgJyArIGxhYmVsICsgJy4nKTsgcmV0dXJuIGZhbHNlOyB9CiAgICBkLmNoYWxsZW5nZVBvaW50cyA9IHBvaW50cyAtIGNvc3Q7CiAgICBkLmNoYWxsZW5nZUludmVudG9yeVtrZXldKys7CiAgICBzYXZlREIoKTsKICAgIHRvYXN0KCfwn46BIMSQw6MgxJHhu5VpICcgKyBsYWJlbCArICcgdsOgIHRow6ptIHbDoG8gS2hvIScpOwogICAgcmVuZGVyQ2hhbGxlbmdlcygpOwogICAgdXBkYXRlU2hvcEJhZGdlKCk7CiAgICBpZiAoZWxzLnNob3BNb2RhbCAmJiAhZWxzLnNob3BNb2RhbC5oaWRkZW4pIHJlbmRlckV4dGVybmFsU2hvcCgpOwogICAgcmV0dXJuIHRydWU7CiAgfQoKICBmdW5jdGlvbiBkcmF3T25lKGJvb3N0KXsgcmV0dXJuIGZ1bGxDYXJkKHBpY2tDYXJkSWQoYm9vc3QpKTsgfQoKICAvLyAtLS0tLS0tLS0tIG3hu58gMSBnw7NpIC0tLS0tLS0tLS0KICBmdW5jdGlvbiBvcGVuUGFjaygpewogICAgaWYgKHN0YXRlLm9wZW5pbmcgfHwgIXN0YXRlLnBvb2wubGVuZ3RoKSByZXR1cm47CiAgICBzdGF0ZS5vcGVuaW5nID0gdHJ1ZTsKICAgIHN0YXRlLm9wZW5TY3JvbGxZID0gTWF0aC5tYXgoMCwgTWF0aC5yb3VuZCh3aW5kb3cucGFnZVlPZmZzZXQgfHwgd2luZG93LnNjcm9sbFkgfHwgMCkpOwogICAgdmFyIHNob3BCb29zdCA9IHBoU2hvcFRha2VCb29zdCgpOwogICAgZW5zdXJlQXVkaW8oKTsKICAgIGVscy5lcnIuaGlkZGVuID0gdHJ1ZTsKICAgIGVscy5oaW50LnRleHRDb250ZW50ID0gJyc7CiAgICBlbHMuYm94Um93LmhpZGRlbiA9IHRydWU7CiAgICBlbHMucGFjay5jbGFzc0xpc3QuYWRkKCdwaC1yaXBwaW5nJyk7CiAgICBlbHMuZmxhc2guY2xhc3NMaXN0LnJlbW92ZSgncGgtZ28nKTsKICAgIHZvaWQgZWxzLmZsYXNoLm9mZnNldFdpZHRoOwogICAgZWxzLmZsYXNoLmNsYXNzTGlzdC5hZGQoJ3BoLWdvJyk7CiAgICBwbGF5UmlwKCk7CgogICAgdmFyIGpvYnMgPSBbXTsKICAgIHZhciBndWFyYW50ZWVkID0gc2hvcEJvb3N0LmNyb3duID8gcGlja0d1YXJhbnRlZWRIaWdoSWQoKSA6IG51bGw7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IDEyOyBpKyspIGpvYnMucHVzaChmdWxsQ2FyZChpID09PSAwICYmIGd1YXJhbnRlZWQgPyBndWFyYW50ZWVkIDogcGlja0NhcmRJZChzaG9wQm9vc3QpKSk7CgogICAgUHJvbWlzZS5hbGwoam9icykudGhlbihmdW5jdGlvbihhbGwpewogICAgICB2YXIgY2FyZHMgPSBhbGwuc2xpY2UoMCw1KTsKICAgICAgdmFyIGV4dHJhcyA9IGFsbC5zbGljZSg1KTsKICAgICAgdmFyIGxhc3QgPSBjYXJkc1s0XTsKICAgICAgZXh0cmFzLmZvckVhY2goZnVuY3Rpb24oYyl7CiAgICAgICAgaWYgKHJhcml0eVRpZXIoYy5yYXJpdHkpLnRpZXIgPiByYXJpdHlUaWVyKGxhc3QucmFyaXR5KS50aWVyKSBsYXN0ID0gYzsKICAgICAgfSk7CiAgICAgIGNhcmRzWzRdID0gbGFzdDsKICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpewogICAgICAgIGVscy5zdGFnZS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgICAgIHJlbmRlclJldmVhbChjYXJkcyk7CiAgICAgICAgc2Nyb2xsVG9PcGVuQW5jaG9yKCdhdXRvJyk7CiAgICAgIH0sIDQ4MCk7CiAgICB9KS5jYXRjaChmdW5jdGlvbihlcnIpewogICAgICBwaFNob3BSZWZ1bmRCb29zdChzaG9wQm9vc3QpOwogICAgICBzdGF0ZS5vcGVuaW5nID0gZmFsc2U7CiAgICAgIGVscy5wYWNrLmNsYXNzTGlzdC5yZW1vdmUoJ3BoLXJpcHBpbmcnKTsKICAgICAgZWxzLmhpbnQudGV4dENvbnRlbnQgPSAnQ2jhuqFtIHbDoG8gZ8OzaSBiw6BpIMSR4buDIG3hu58nOwogICAgICBlbHMuYm94Um93LmhpZGRlbiA9IGZhbHNlOwogICAgICBlbHMuZXJyLnRleHRDb250ZW50ID0gJ03hu58gZ8OzaSBiw6BpIHRo4bqldCBi4bqhaSAoJyArIGVyci5tZXNzYWdlICsgJykuIFZ1aSBsw7JuZyB0aOG7rSBs4bqhaS4nOwogICAgICBlbHMuZXJyLmhpZGRlbiA9IGZhbHNlOwogICAgfSk7CiAgfQoKICBlbHMucGFjay5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIG9wZW5QYWNrKTsKICBlbHMucGFjay5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgZnVuY3Rpb24oZSl7CiAgICBpZiAoZS5rZXkgPT09ICdFbnRlcicgfHwgZS5rZXkgPT09ICcgJyl7IGUucHJldmVudERlZmF1bHQoKTsgb3BlblBhY2soKTsgfQogIH0pOwoKICBmdW5jdGlvbiByZW5kZXJSZXZlYWwoY2FyZHMpewogICAgZWxzLnJldmVhbC5jbGFzc0xpc3QuYWRkKCdwaC1hY3RpdmUnLCdwaC1leHRyYWN0LXNvdXJjZScpOwogICAgZWxzLnJvdy50ZXh0Q29udGVudCA9ICcnOwogICAgZWxzLmJhbm5lci5jbGFzc05hbWUgPSAncGgtYmFubmVyJzsKICAgIGVscy5iYW5uZXIudGV4dENvbnRlbnQgPSAnJzsKICAgIGVscy5sdWNrLmhpZGRlbiA9IHRydWU7CiAgICBlbHMudG90YWwuaGlkZGVuID0gdHJ1ZTsKICAgIGVscy5xdWlwLnRleHRDb250ZW50ID0gJyc7CiAgICBlbHMucXVpcC5jbGFzc05hbWUgPSAncGgtcXVpcCc7CiAgICBlbHMuc2F2ZWQudGV4dENvbnRlbnQgPSAnJzsKICAgIGNsZWFyQ29uZmV0dGkoKTsKCiAgICB2YXIgYmVmb3JlREIgPSBsb2FkREIoKTsKICAgIHZhciBub3ZlbHR5QnlJZCA9IHt9OwogICAgY2FyZHMuZm9yRWFjaChmdW5jdGlvbihjYXJkKXsKICAgICAgaWYgKCFjYXJkIHx8ICFjYXJkLmlkIHx8IG5vdmVsdHlCeUlkW2NhcmQuaWRdKSByZXR1cm47CiAgICAgIG5vdmVsdHlCeUlkW2NhcmQuaWRdID0gbm92ZWx0eUxhYmVsKGNhcmQsIGJlZm9yZURCKTsKICAgIH0pOwoKICAgIHZhciBtYXhUaWVyID0gMDsKCiAgICBjYXJkcy5mb3JFYWNoKGZ1bmN0aW9uKGNhcmQsIGkpewogICAgICB2YXIgaW5mbyA9IHJhcml0eVRpZXIoY2FyZC5yYXJpdHkpOwogICAgICBpZiAoaW5mby50aWVyID4gbWF4VGllcikgbWF4VGllciA9IGluZm8udGllcjsKCiAgICAgIHZhciBjb2wgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgY29sLmNsYXNzTmFtZSA9ICdwaC1jYXJkLWNvbCBwaC1leHRyYWN0aW5nJzsKICAgICAgY29sLnN0eWxlLnNldFByb3BlcnR5KCctLXBoLWV4dHJhY3QtZGVsYXknLCAoaSAqIDk1KSArICdtcycpOwogICAgICB2YXIgc2xvdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBzbG90LmNsYXNzTmFtZSA9ICdwaC1zbG90JzsKICAgICAgc2xvdC5kYXRhc2V0LnRpZXIgPSBTdHJpbmcoaW5mby50aWVyKTsKCiAgICAgIHZhciBpbm5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBpbm5lci5jbGFzc05hbWUgPSAncGgtaW5uZXInOwogICAgICB2YXIgYmFjayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBiYWNrLmNsYXNzTmFtZSA9ICdwaC1mYWNlIHBoLWJhY2stZmFjZSc7IGJhY2sudGV4dENvbnRlbnQgPSAnPyc7CiAgICAgIHZhciBmcm9udCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBmcm9udC5jbGFzc05hbWUgPSAncGgtZmFjZSBwaC1mcm9udC1mYWNlJzsKICAgICAgaWYgKGNhcmQuaW1hZ2UpewogICAgICAgIHZhciBpbWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTsKICAgICAgICBpbWcubG9hZGluZyA9ICdsYXp5JzsKICAgICAgICBpbWcuYWx0ID0gY2FyZC5uYW1lIHx8ICcnOwogICAgICAgIGltZy5zcmMgPSBjYXJkLmltYWdlICsgJy8nICsgUVVBTElUWTsKICAgICAgICBpbWcub25lcnJvciA9IGZ1bmN0aW9uKCl7CiAgICAgICAgICBpbWcucmVtb3ZlKCk7CiAgICAgICAgICBpZiAoIWZyb250LnF1ZXJ5U2VsZWN0b3IoJy5waC1pbWFnZS1mYWxsYmFjaycpKXsKICAgICAgICAgICAgdmFyIGZiID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOwogICAgICAgICAgICBmYi5jbGFzc05hbWUgPSAncGgtaW1hZ2UtZmFsbGJhY2snOwogICAgICAgICAgICBmYi50ZXh0Q29udGVudCA9IGNhcmQubmFtZSB8fCAnPyc7CiAgICAgICAgICAgIGZyb250LmFwcGVuZENoaWxkKGZiKTsKICAgICAgICAgIH0KICAgICAgICB9OwogICAgICAgIGZyb250LmFwcGVuZENoaWxkKGltZyk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgdmFyIGZiMCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgICBmYjAuY2xhc3NOYW1lID0gJ3BoLWltYWdlLWZhbGxiYWNrJzsKICAgICAgICBmYjAudGV4dENvbnRlbnQgPSBjYXJkLm5hbWUgfHwgJz8nOwogICAgICAgIGZyb250LmFwcGVuZENoaWxkKGZiMCk7CiAgICAgIH0KICAgICAgaW5uZXIuYXBwZW5kQ2hpbGQoYmFjayk7IGlubmVyLmFwcGVuZENoaWxkKGZyb250KTsKICAgICAgc2xvdC5hcHBlbmRDaGlsZChpbm5lcik7CgogICAgICB2YXIgbGFiZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgbGFiZWwuY2xhc3NOYW1lID0gJ3BoLWNhcmQtbGFiZWwnOwoKICAgICAgY29sLmFwcGVuZENoaWxkKHNsb3QpOyBjb2wuYXBwZW5kQ2hpbGQobGFiZWwpOwogICAgICBlbHMucm93LmFwcGVuZENoaWxkKGNvbCk7CgogICAgICB2YXIgdXNkID0gcHJpY2VVU0QoY2FyZCk7CiAgICAgIHZhciByZXZlYWxTdGFydGVkID0gZmFsc2U7CiAgICAgIHZhciBpc0xhc3QgPSBpID09PSBjYXJkcy5sZW5ndGggLSAxOwoKICAgICAgZnVuY3Rpb24gcmV2ZWFsKCl7CiAgICAgICAgaWYgKHNsb3QuY2xhc3NMaXN0LmNvbnRhaW5zKCdwaC1mbGlwcGVkJykpewogICAgICAgICAgb3BlbkNhcmRNb2RhbChjYXJkLCBpbmZvLCB1c2QpOwogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICBpZiAocmV2ZWFsU3RhcnRlZCkgcmV0dXJuOwogICAgICAgIHJldmVhbFN0YXJ0ZWQgPSB0cnVlOwogICAgICAgIHBoUHJlcGFyZVJldmVhbChzbG90LCBpbmZvLnRpZXIsIGlzTGFzdCk7CiAgICAgICAgdmFyIGRlbGF5ID0gcGhSZXZlYWxEZWxheShpbmZvLnRpZXIsIGlzTGFzdCk7CiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpewogICAgICAgICAgcGhDbGVhclJldmVhbEZ4KHNsb3QpOwogICAgICAgICAgc2xvdC5jbGFzc0xpc3QuYWRkKCdwaC1mbGlwcGVkJyk7CiAgICAgICAgICBzbG90LnRpdGxlID0gJ0LhuqVtIMSR4buDIHhlbSBjaGkgdGnhur90IGzDoSBiw6BpJzsKICAgICAgICBsYWJlbC50ZXh0Q29udGVudCA9ICcnOwogICAgICAgIHZhciBubSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgICBubS50ZXh0Q29udGVudCA9IGNhcmQubmFtZSArICcgXHUwMGI3ICcgKyBpbmZvLmxhYmVsICsgJyBcdTAwYjcgJzsKICAgICAgICB2YXIgcHIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICAgICAgcHIuY2xhc3NOYW1lID0gdXNkICE9IG51bGwgPyAncGgtY2FyZC1wcmljZScgOiAncGgtY2FyZC1wcmljZSBwaC1jYXJkLW5vcHJpY2UnOwogICAgICAgIHByLnRleHRDb250ZW50ID0gdXNkICE9IG51bGwgPyBtb25leSh1c2QpIDogJ1x1MjAxNCc7CiAgICAgICAgbGFiZWwuYXBwZW5kQ2hpbGQobm0pOyBsYWJlbC5hcHBlbmRDaGlsZChwcik7CiAgICAgICAgcGxheUZsaXAoaW5mby50aWVyKTsKICAgICAgICAgIGFkZE5vdmVsdHlCYWRnZShmcm9udCwgbm92ZWx0eUJ5SWRbY2FyZC5pZF0pOwogICAgICAgICAgaWYgKGluZm8udGllciA+PSA1KXsKICAgICAgICAgICAgZWxzLmZsYXNoLmNsYXNzTGlzdC5yZW1vdmUoJ3BoLWdvJyk7CiAgICAgICAgICAgIHZvaWQgZWxzLmZsYXNoLm9mZnNldFdpZHRoOwogICAgICAgICAgICBlbHMuZmxhc2guY2xhc3NMaXN0LmFkZCgncGgtZ28nKTsKICAgICAgICAgIH0KICAgICAgICAgIGlmIChpbmZvLnRpZXIgPj0gNCkgY29uZkF0RWxlbWVudChzbG90LCBpbmZvLnRpZXIpOwogICAgICAgIH0sIGRlbGF5KTsKICAgICAgfQoKICAgICAgc2xvdC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHJldmVhbCk7CiAgICAgIHNldFRpbWVvdXQocmV2ZWFsLCAzNTAgKyBpICogMjYwKTsKICAgIH0pOwoKICAgIC8vIGx1dSB2YW8gYm8gc3V1IHRhcCBuZ2F5IGtoaSBtbwogICAgcmVjb3JkUGFja3MoY2FyZHMsIDEpOwogICAgc3RhdGUubGFzdFJlc3VsdCA9IHsgY2FyZHM6IGNhcmRzLCBwYWNrczogMSB9OwoKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKXsKICAgICAgZWxzLnJldmVhbC5jbGFzc0xpc3QucmVtb3ZlKCdwaC1leHRyYWN0LXNvdXJjZScpOwogICAgICBzaG93QmFubmVyKG1heFRpZXIsIGNhcmRzKTsKICAgIH0sIDM1MCArIGNhcmRzLmxlbmd0aCAqIDI2MCArIDMwMCk7CiAgfQoKICBmdW5jdGlvbiBzdW1UaWVycyhjYXJkcyl7CiAgICByZXR1cm4gY2FyZHMucmVkdWNlKGZ1bmN0aW9uKHMsYyl7IHJldHVybiBzICsgcmFyaXR5VGllcihjLnJhcml0eSkudGllcjsgfSwgMCk7CiAgfQoKICBmdW5jdGlvbiBwYWNrVmFsdWUoY2FyZHMpewogICAgdmFyIHByaWNlZCA9IDAsIHRvdGFsID0gMDsKICAgIGNhcmRzLmZvckVhY2goZnVuY3Rpb24oYyl7CiAgICAgIHZhciB2ID0gcHJpY2VVU0QoYyk7CiAgICAgIGlmICh2ICE9IG51bGwpeyB0b3RhbCArPSB2OyBwcmljZWQrKzsgfQogICAgfSk7CiAgICByZXR1cm4geyB0b3RhbDogdG90YWwsIHByaWNlZDogcHJpY2VkIH07CiAgfQoKICBmdW5jdGlvbiBzaG93QmFubmVyKG1heFRpZXIsIGNhcmRzKXsKICAgIHZhciB0ZXh0ID0gJycsIGNscyA9ICcnOwogICAgaWYgKG1heFRpZXIgPj0gNil7IHRleHQgPSAnVGjhursgY2hhc2UhIEPhu7FjIHBo4bqpbSEnOyBjbHMgPSAncGgtY3Jvd24nOyBwbGF5RmFuZmFyZSgpOyB9CiAgICBlbHNlIGlmIChtYXhUaWVyID09PSA1KXsgdGV4dCA9ICdSw7p0IMSRxrDhu6NjIHRo4bq7IG1pbmggaOG7jWEgxJHhurdjIGJp4buHdCEnOyBjbHMgPSAncGgtc2hpbnknOyB9CiAgICBlbHNlIGlmIChtYXhUaWVyID09PSA0KXsgdGV4dCA9ICdSw7p0IMSRxrDhu6NjIHRo4bq7IFN0YXIhJzsgY2xzID0gJ3BoLXN0YXInOyB9CiAgICBlbHNlIGlmIChtYXhUaWVyID09PSAzKXsgdGV4dCA9ICdSw7p0IMSRxrDhu6NjIHRo4bq7IGhp4bq/bSEnOyBjbHMgPSAncGgtcmFyZSc7IH0KCiAgICBpZiAodGV4dCl7CiAgICAgIGVscy5iYW5uZXIudGV4dENvbnRlbnQgPSB0ZXh0OwogICAgICBlbHMuYmFubmVyLmNsYXNzTmFtZSA9ICdwaC1iYW5uZXIgJyArIGNsczsKICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uKCl7IGVscy5iYW5uZXIuY2xhc3NMaXN0LmFkZCgncGgtc2hvdycpOyB9KTsKICAgIH0KCiAgICB2YXIgcHYgPSBwYWNrVmFsdWUoY2FyZHMpOwogICAgdmFyIHRvdGFsVVNEID0gcHYudG90YWwsIHByaWNlZCA9IHB2LnByaWNlZDsKCiAgICB2YXIgcmFyaXR5UGFydCA9IE1hdGgubWluKDEwMCwgc3VtVGllcnMoY2FyZHMpIC8gKGNhcmRzLmxlbmd0aCAqIDYpICogMTAwICsgKG1heFRpZXIgPj0gNiA/IDIwIDogMCkpOwogICAgdmFyIHZhbHVlUGFydCA9IHByaWNlZCA/IE1hdGgubWluKDEwMCwgTWF0aC5sb2cxMCgxICsgdG90YWxVU0QpIC8gTWF0aC5sb2cxMCgxMDEpICogMTAwKSA6IG51bGw7CiAgICB2YXIgbHVjayA9IHZhbHVlUGFydCA9PSBudWxsCiAgICAgID8gTWF0aC5yb3VuZChyYXJpdHlQYXJ0KQogICAgICA6IE1hdGgucm91bmQocmFyaXR5UGFydCAqIDAuNTUgKyB2YWx1ZVBhcnQgKiAwLjQ1KTsKICAgIGx1Y2sgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxMDAsIGx1Y2spKTsKCiAgICB2YXIgZCA9IGxvYWREQigpOwogICAgaWYgKGx1Y2sgPiAoTnVtYmVyKGQuYmVzdEx1Y2spIHx8IDApKXsKICAgICAgZC5iZXN0THVjayA9IGx1Y2s7CiAgICAgIHNhdmVEQigpOwogICAgfQoKICAgIGVscy5sdWNrU2NvcmUudGV4dENvbnRlbnQgPSBsdWNrOwogICAgZWxzLmx1Y2suaGlkZGVuID0gZmFsc2U7CiAgICBpZiAocHJpY2VkKXsKICAgICAgZWxzLnRvdGFsVmFsLnRleHRDb250ZW50ID0gbW9uZXkodG90YWxVU0QpICsgKHByaWNlZCA8IGNhcmRzLmxlbmd0aCA/ICcgKCcgKyBwcmljZWQgKyAnLycgKyBjYXJkcy5sZW5ndGggKyAnIGzDoSBjw7MgZ2nDoSknIDogJycpOwogICAgICBlbHMudG90YWwuaGlkZGVuID0gZmFsc2U7CiAgICB9IGVsc2UgewogICAgICBlbHMudG90YWwuaGlkZGVuID0gdHJ1ZTsKICAgIH0KCiAgICB2YXIgZGlmZiA9IHRvdGFsVVNEIC0gZC5wYWNrUHJpY2U7CiAgICBlbHMuc2F2ZWQudGV4dENvbnRlbnQgPSAnxJDDoyBsxrB1IHbDoG8gYuG7mSBzxrB1IHThuq1wIFx1MDBiNyBnw7NpIG7DoHkgJyArCiAgICAgIChwcmljZWQgPyAoZGlmZiA+PSAwID8gJ2zhu51pICcgKyBtb25leShkaWZmKSA6ICds4buXICcgKyBtb25leSgtZGlmZikpICsgJyBzbyB24bubaSBnacOhIGfDs2kgJyArIG1vbmV5KGQucGFja1ByaWNlKSA6ICdjaMawYSDEkeG7pyBk4buvIGxp4buHdSBnacOhIMSR4buDIHTDrW5oIGzhu51pL2zhu5cnKTsKCiAgICBzaG93UXVpcChsdWNrLCBtYXhUaWVyLCB0b3RhbFVTRCwgcHJpY2VkKTsKCiAgICBpZiAobWF4VGllciA+PSA2KSBjb25mQ2VsZWJyYXRlKHRydWUpOwogICAgZWxzZSBpZiAobWF4VGllciA+PSA0KSBjb25mQ2VsZWJyYXRlKGZhbHNlKTsKICB9CgogIHZhciBRVUlQUyA9IHsKICAgIGJhZDogWydIw7RtIG5heSDEkWVuIHF1w6EsIG3hu58gdGnhur9wIMSRaeKApicsCiAgICAgICAgICAnVG/DoG4gdGjhursgdGjGsOG7nW5nLCBn4buhIGfhuqFjIHRow6ptIG3hu5l0IGfDs2kgbuG7r2EgdGjDtGkhJywKICAgICAgICAgICdHw7NpIG7DoHkgbmjhurkgdMO6aSB0aOG6rXTigKYgdGjhu60gduG6rW4gbOG6p24gbuG7r2EgbsOgby4nXSwKICAgIG1laDogWydU4bqhbSDhu5VuLCBjaMawYSBjw7MgZ8OsIMSR4buDIGtob2UgY+G6oy4nLAogICAgICAgICAgJ0NoxrBhIGLDuW5nIG7hu5UsIG5oxrBuZyBjxaluZyBraMO0bmcgbOG7ly4nLAogICAgICAgICAgJ03hu5l0IGfDs2kgYsOsbmggdGjGsOG7nW5nIFx1MjAxNCBt4bufIHRow6ptIHhlbSBzYW8uJ10sCiAgICBnb29kOiBbJ0tow7RuZyB04buHIGNow7p0IG7DoG8sIHRodSBob+G6oWNoIGtow6EgdOG7kXQhJywKICAgICAgICAgICAnR8OzaSBuw6B5IG5nb24gxJHhuqV5LCBnaeG7ryBwaG9uZyDEkeG7mSBuaMOpIScsCiAgICAgICAgICAgJ0PDsyBow6BuZyBy4buTaSBcdTIwMTQgdGF5IGLhuqFuIMSRYW5nIHRoxqFtIMSRw7MuJ10sCiAgICBncmVhdDogWydNYXkgbeG6r24gY+G7sWMgxJHhu4luaCwgc+G7kSBi4bqhbiDEkWFuZyDEkeG7jyBy4buxYyEnLAogICAgICAgICAgICAnUXXDoSBsw6NpISBIw7RtIG5heSBuw6puIG11YSB2w6kgc+G7kS4nLAogICAgICAgICAgICAnR8OzaSDEkeG7iW5oIG7Ds2MgXHUyMDE0IGNo4bulcCBtw6BuIGjDrG5oIGtob2UgbmdheSDEkWkhJ10KICB9OwogIGZ1bmN0aW9uIHBpY2tRdWlwKGxpc3QpeyByZXR1cm4gbGlzdFtNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBsaXN0Lmxlbmd0aCldOyB9CgogIGZ1bmN0aW9uIHNob3dRdWlwKGx1Y2ssIG1heFRpZXIsIHRvdGFsVVNELCBwcmljZWQpewogICAgdmFyIGJhbmQsIGNsczsKICAgIGlmIChtYXhUaWVyID49IDYgfHwgbHVjayA+PSA3MCB8fCAocHJpY2VkICYmIHRvdGFsVVNEID49IDI1KSl7IGJhbmQgPSBRVUlQUy5ncmVhdDsgY2xzID0gJ3BoLXF1aXAtZ3JlYXQnOyB9CiAgICBlbHNlIGlmIChtYXhUaWVyID49IDQgfHwgbHVjayA+PSA0NSB8fCAocHJpY2VkICYmIHRvdGFsVVNEID49IDgpKXsgYmFuZCA9IFFVSVBTLmdvb2Q7IGNscyA9ICdwaC1xdWlwLWdvb2QnOyB9CiAgICBlbHNlIGlmIChsdWNrID49IDIyKXsgYmFuZCA9IFFVSVBTLm1laDsgY2xzID0gJ3BoLXF1aXAtbWVoJzsgfQogICAgZWxzZSB7IGJhbmQgPSBRVUlQUy5iYWQ7IGNscyA9ICdwaC1xdWlwLWJhZCc7IH0KICAgIGVscy5xdWlwLnRleHRDb250ZW50ID0gcGlja1F1aXAoYmFuZCk7CiAgICBlbHMucXVpcC5jbGFzc05hbWUgPSAncGgtcXVpcCAnICsgY2xzOwogICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uKCl7IGVscy5xdWlwLmNsYXNzTGlzdC5hZGQoJ3BoLXNob3cnKTsgfSk7CiAgfQoKICBlbHMuYWdhaW4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCByZXNldFN0YWdlKTsKICBlbHMuYm94QWdhaW4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCByZXNldFN0YWdlKTsKCiAgLy8gY2hpYSBzZSBhbmgga2V0IHF1YSAxIGdvaQogIGVscy5zaGFyZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7CiAgICB2YXIgciA9IHN0YXRlLmxhc3RSZXN1bHQ7CiAgICBpZiAoIXIpIHJldHVybjsKICAgIHZhciBjYXJkcyA9IHIuY2FyZHMubWFwKGZ1bmN0aW9uKGMpewogICAgICByZXR1cm4geyBuYW1lOmMubmFtZSwgaW1hZ2U6Yy5pbWFnZSwgdGllcjpyYXJpdHlUaWVyKGMucmFyaXR5KS50aWVyLCBwcmljZTpwcmljZVVTRChjKSB9OwogICAgfSk7CiAgICB2YXIgcHYgPSBwYWNrVmFsdWUoci5jYXJkcyk7CiAgICB2YXIgZCA9IGxvYWREQigpOwogICAgdmFyIGRpZmYgPSBwdi50b3RhbCAtIGQucGFja1ByaWNlICogci5wYWNrczsKICAgIG1ha2VBbmRTaGFyZSh7CiAgICAgIHRpdGxlOiAnTeG7nyBnw7NpICcgKyAoc3RhdGUuY3VycmVudCA/IHN0YXRlLmN1cnJlbnQubmFtZSA6ICcnKSwKICAgICAgc3ViOiByLnBhY2tzICsgJyBnw7NpIFx1MDBiNyDEkWnhu4NtIG1heSBt4bqvbiAnICsgZWxzLmx1Y2tTY29yZS50ZXh0Q29udGVudCArICcvMTAwJywKICAgICAgY2FyZHM6IGNhcmRzLAogICAgICBzdGF0czogWwogICAgICAgIFsnR2nDoSB0cuG7iyBnw7NpJywgbW9uZXkocHYudG90YWwpXSwKICAgICAgICBbJ0dpw6EgZ8OzaScsIG1vbmV5KGQucGFja1ByaWNlICogci5wYWNrcyldLAogICAgICAgIFsnTOG7nWkvbOG7lycsIChkaWZmID49IDAgPyAnKycgOiAnLScpICsgbW9uZXkoTWF0aC5hYnMoZGlmZikpLCBkaWZmID49IDAgPyAncG9zJyA6ICduZWcnXSwKICAgICAgICBbJ01heSBt4bqvbicsIGVscy5sdWNrU2NvcmUudGV4dENvbnRlbnQgKyAnLzEwMCddCiAgICAgIF0sCiAgICAgIHRleHQ6ICdNw6xuaCB24burYSBt4bufIGfDs2kgJyArIChzdGF0ZS5jdXJyZW50ID8gc3RhdGUuY3VycmVudC5uYW1lIDogJycpICsgJyB0csOqbiBQb2tlSHViLCBnacOhIHRy4buLICcgKyBtb25leShwdi50b3RhbCkgKyAnIScKICAgIH0pOwogIH0pOwoKICAvLyAtLS0tLS0tLS0tIE3hu54gQ+G6oiBI4buYUCAtLS0tLS0tLS0tCiAgZnVuY3Rpb24gc2hvd1Byb2dyZXNzKGRvbmUsIHRvdGFsLCBwYWNrc0RvbmUsIHBhY2tzKXsKICAgIGVscy5wcm9ncmVzcy5oaWRkZW4gPSBmYWxzZTsKICAgIGVscy5zdGFnZS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgdmFyIHAgPSB0b3RhbCA/IE1hdGgucm91bmQoZG9uZSAvIHRvdGFsICogMTAwKSA6IDA7CiAgICBlbHMucHJvZ3Jlc3NCYXIuc3R5bGUud2lkdGggPSBwICsgJyUnOwogICAgZWxzLnByb2dyZXNzVGV4dC50ZXh0Q29udGVudCA9ICfEkGFuZyBt4bufIGjhu5lw4oCmICcgKyBwYWNrc0RvbmUgKyAnLycgKyBwYWNrcyArICcgZ8OzaSAoJyArIHAgKyAnJSknOwogIH0KCiAgZnVuY3Rpb24gZmV0Y2hNYW55KGlkcywgb25Qcm9nKXsKICAgIHZhciBvdXQgPSBuZXcgQXJyYXkoaWRzLmxlbmd0aCksIGkgPSAwLCBhY3RpdmUgPSAwLCBkb25lID0gMCwgTElNSVQgPSA2OwogICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUpewogICAgICBmdW5jdGlvbiBwdW1wKCl7CiAgICAgICAgd2hpbGUgKGFjdGl2ZSA8IExJTUlUICYmIGkgPCBpZHMubGVuZ3RoKXsKICAgICAgICAgIChmdW5jdGlvbihpZHgpewogICAgICAgICAgICBhY3RpdmUrKzsgaSsrOwogICAgICAgICAgICBmdWxsQ2FyZChpZHNbaWR4XSkudGhlbihmdW5jdGlvbihjKXsgb3V0W2lkeF0gPSBjOyB9LCBmdW5jdGlvbigpeyBvdXRbaWR4XSA9IG51bGw7IH0pCiAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24oKXsKICAgICAgICAgICAgICAgIGFjdGl2ZS0tOyBkb25lKys7CiAgICAgICAgICAgICAgICBpZiAob25Qcm9nKSBvblByb2coZG9uZSk7CiAgICAgICAgICAgICAgICBpZiAoZG9uZSA9PT0gaWRzLmxlbmd0aCkgcmVzb2x2ZShvdXQpOwogICAgICAgICAgICAgICAgZWxzZSBwdW1wKCk7CiAgICAgICAgICAgICAgfSk7CiAgICAgICAgICB9KShpKTsKICAgICAgICB9CiAgICAgIH0KICAgICAgaWYgKCFpZHMubGVuZ3RoKSByZXNvbHZlKG91dCk7IGVsc2UgcHVtcCgpOwogICAgfSk7CiAgfQoKICBmdW5jdGlvbiBvcGVuQm94KHBhY2tzKXsKICAgIGlmIChzdGF0ZS5vcGVuaW5nIHx8ICFzdGF0ZS5wb29sLmxlbmd0aCkgcmV0dXJuOwogICAgc3RhdGUub3BlbmluZyA9IHRydWU7CiAgICBzdGF0ZS5vcGVuU2Nyb2xsWSA9IE1hdGgubWF4KDAsIE1hdGgucm91bmQod2luZG93LnBhZ2VZT2Zmc2V0IHx8IHdpbmRvdy5zY3JvbGxZIHx8IDApKTsKICAgIHZhciBzaG9wQm9vc3QgPSBwaFNob3BUYWtlQm9vc3QoKTsKICAgIGVuc3VyZUF1ZGlvKCk7CiAgICBwbGF5UmlwKCk7CiAgICBlbHMuZXJyLmhpZGRlbiA9IHRydWU7CiAgICBlbHMuYm94Um93LmhpZGRlbiA9IHRydWU7CiAgICBlbHMucmV2ZWFsLmNsYXNzTGlzdC5yZW1vdmUoJ3BoLWFjdGl2ZScpOwogICAgZWxzLmJveC5jbGFzc0xpc3QucmVtb3ZlKCdwaC1hY3RpdmUnKTsKICAgIGNsZWFyQ29uZmV0dGkoKTsKCiAgICB2YXIgcGVyID0gNjsgLy8gNSBsw6EgKyAxIGzDoSBk4buxIHBow7JuZyBjaG8gw7QgY3Xhu5FpCiAgICB2YXIgaWRzID0gW107CiAgICB2YXIgZ3VhcmFudGVlZCA9IHNob3BCb29zdC5jcm93biA/IHBpY2tHdWFyYW50ZWVkSGlnaElkKCkgOiBudWxsOwogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYWNrcyAqIHBlcjsgaSsrKSBpZHMucHVzaChpID09PSAwICYmIGd1YXJhbnRlZWQgPyBndWFyYW50ZWVkIDogcGlja0NhcmRJZChzaG9wQm9vc3QpKTsKICAgIHNob3dQcm9ncmVzcygwLCBpZHMubGVuZ3RoLCAwLCBwYWNrcyk7CgogICAgZmV0Y2hNYW55KGlkcywgZnVuY3Rpb24oZG9uZSl7CiAgICAgIHNob3dQcm9ncmVzcyhkb25lLCBpZHMubGVuZ3RoLCBNYXRoLm1pbihwYWNrcywgTWF0aC5mbG9vcihkb25lIC8gcGVyKSksIHBhY2tzKTsKICAgIH0pLnRoZW4oZnVuY3Rpb24oYWxsKXsKICAgICAgdmFyIGNhcmRzID0gYWxsLmZpbHRlcihCb29sZWFuKTsKICAgICAgaWYgKCFjYXJkcy5sZW5ndGgpewogICAgICAgIHBoU2hvcFJlZnVuZEJvb3N0KHNob3BCb29zdCk7CiAgICAgICAgc3RhdGUub3BlbmluZyA9IGZhbHNlOwogICAgICAgIGVscy5wcm9ncmVzcy5oaWRkZW4gPSB0cnVlOwogICAgICAgIGVscy5zdGFnZS5zdHlsZS5kaXNwbGF5ID0gJ2ZsZXgnOwogICAgICAgIGVscy5ib3hSb3cuaGlkZGVuID0gZmFsc2U7CiAgICAgICAgZWxzLmVyci50ZXh0Q29udGVudCA9ICdLaMO0bmcgdOG6o2kgxJHGsOG7o2MgbMOhIGLDoGkgbsOgbyBjaG8gaOG7mXAgbsOgeS4gVGjhu60gbOG6oWkgaG/hurdjIGNo4buNbiBi4buZIGtow6FjLic7CiAgICAgICAgZWxzLmVyci5oaWRkZW4gPSBmYWxzZTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgdmFyIHBhY2tzQXJyID0gW10sIGsgPSAwOwogICAgICBmb3IgKHZhciBwID0gMDsgcCA8IHBhY2tzICYmIGsgPCBjYXJkcy5sZW5ndGg7IHArKyl7CiAgICAgICAgdmFyIHNsb3RzID0gW107CiAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCA1ICYmIGsgPCBjYXJkcy5sZW5ndGg7IGorKyl7IHNsb3RzLnB1c2goY2FyZHNba10pOyBrKys7IH0KICAgICAgICB2YXIgcmVzZXJ2ZSA9IGNhcmRzW2tdOyBrKys7CiAgICAgICAgaWYgKHJlc2VydmUgJiYgc2xvdHMubGVuZ3RoICYmCiAgICAgICAgICAgIHJhcml0eVRpZXIocmVzZXJ2ZS5yYXJpdHkpLnRpZXIgPiByYXJpdHlUaWVyKHNsb3RzW3Nsb3RzLmxlbmd0aC0xXS5yYXJpdHkpLnRpZXIpewogICAgICAgICAgc2xvdHNbc2xvdHMubGVuZ3RoLTFdID0gcmVzZXJ2ZTsKICAgICAgICB9CiAgICAgICAgaWYgKHNsb3RzLmxlbmd0aCkgcGFja3NBcnIucHVzaChzbG90cyk7CiAgICAgIH0KICAgICAgZmluaXNoQm94KHBhY2tzQXJyKTsKICAgICAgc2Nyb2xsVG9PcGVuQW5jaG9yKCdhdXRvJyk7CiAgICB9KS5jYXRjaChmdW5jdGlvbihlcnIpewogICAgICBwaFNob3BSZWZ1bmRCb29zdChzaG9wQm9vc3QpOwogICAgICBzdGF0ZS5vcGVuaW5nID0gZmFsc2U7CiAgICAgIGVscy5wcm9ncmVzcy5oaWRkZW4gPSB0cnVlOwogICAgICBlbHMuc3RhZ2Uuc3R5bGUuZGlzcGxheSA9ICdmbGV4JzsKICAgICAgZWxzLmJveFJvdy5oaWRkZW4gPSBmYWxzZTsKICAgICAgZWxzLmVyci50ZXh0Q29udGVudCA9ICdN4bufIGjhu5lwIHRo4bqldCBi4bqhaSAoJyArIGVyci5tZXNzYWdlICsgJykuIFZ1aSBsw7JuZyB0aOG7rSBs4bqhaS4nOwogICAgICBlbHMuZXJyLmhpZGRlbiA9IGZhbHNlOwogICAgICBzY3JvbGxUb09wZW5BbmNob3IoJ2F1dG8nKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gcGhSZXZlYWxEZWxheSh0aWVyLCBpc0xhc3QpewogICAgdmFyIGJhc2UgPSB0aWVyID49IDYgPyAxMTgwIDogdGllciA+PSA1ID8gNzIwIDogdGllciA+PSA0ID8gNTYwIDogdGllciA+PSAzID8gNDAwIDogdGllciA+PSAyID8gMjYwIDogMDsKICAgIGlmIChpc0xhc3QpIGJhc2UgKz0gdGllciA+PSA0ID8gMjYwIDogMTYwOwogICAgcmV0dXJuIGJhc2U7CiAgfQoKICBmdW5jdGlvbiBwaFByZXBhcmVSZXZlYWwoc2xvdCwgdGllciwgaXNMYXN0KXsKICAgIGlmIChpc0xhc3QpIHNsb3QuY2xhc3NMaXN0LmFkZCgncGgtbGFzdC1hbnRpY2lwYXRpb24nKTsKICAgIGlmICh0aWVyID49IDYpeyBzbG90LmNsYXNzTGlzdC5hZGQoJ3BoLWNoYXNlLTYnKTsgfQogICAgZWxzZSBpZiAodGllciA+PSA1KXsgc2xvdC5jbGFzc0xpc3QuYWRkKCdwaC10cmVtYmxlLXN0cm9uZycpOyB9CiAgICBlbHNlIGlmICh0aWVyID49IDIpeyBzbG90LmNsYXNzTGlzdC5hZGQoJ3BoLXRyZW1ibGUnKTsgfQogIH0KCiAgZnVuY3Rpb24gcGhDbGVhclJldmVhbEZ4KHNsb3QpewogICAgc2xvdC5jbGFzc0xpc3QucmVtb3ZlKCdwaC10cmVtYmxlJywncGgtdHJlbWJsZS1zdHJvbmcnLCdwaC1sYXN0LWFudGljaXBhdGlvbicsJ3BoLWNoYXNlLTYnKTsKICB9CgogIC8vIEzhuq10IG3hu5l0IGzDoSB0aGVvIGtp4buDdSBnw7NpIGLDoGkgKGTDuW5nIGNobyBtw6BuICJ0aW5oIGhvYSBj4bqjIGjhu5lwIikuCiAgZnVuY3Rpb24gYnVpbGRTbG90KGNhcmQsIGluZm8sIHVzZCwgdGFyZ2V0Um93LCBub3ZlbHR5KXsKICAgIHZhciBjb2wgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHZhciBzbG90ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBzbG90LmNsYXNzTmFtZSA9ICdwaC1zbG90JzsKICAgIHNsb3QuZGF0YXNldC50aWVyID0gU3RyaW5nKGluZm8udGllcik7CgogICAgdmFyIGlubmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGlubmVyLmNsYXNzTmFtZSA9ICdwaC1pbm5lcic7CiAgICB2YXIgYmFjayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBiYWNrLmNsYXNzTmFtZSA9ICdwaC1mYWNlIHBoLWJhY2stZmFjZSc7IGJhY2sudGV4dENvbnRlbnQgPSAnPyc7CiAgICB2YXIgZnJvbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgZnJvbnQuY2xhc3NOYW1lID0gJ3BoLWZhY2UgcGgtZnJvbnQtZmFjZSc7CiAgICBpZiAoY2FyZC5pbWFnZSl7CiAgICAgIHZhciBpbWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTsKICAgICAgaW1nLmxvYWRpbmcgPSAnbGF6eSc7CiAgICAgIGltZy5hbHQgPSBjYXJkLm5hbWUgfHwgJyc7CiAgICAgIGltZy5zcmMgPSBjYXJkLmltYWdlICsgJy8nICsgUVVBTElUWTsKICAgICAgaW1nLm9uZXJyb3IgPSBmdW5jdGlvbigpeyBpbWcucmVtb3ZlKCk7IGlmICghZnJvbnQucXVlcnlTZWxlY3RvcignLnBoLWltYWdlLWZhbGxiYWNrJykpIHsgdmFyIGZiID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyBmYi5jbGFzc05hbWU9J3BoLWltYWdlLWZhbGxiYWNrJzsgZmIudGV4dENvbnRlbnQgPSBjYXJkLm5hbWUgfHwgJz8nOyBmcm9udC5hcHBlbmRDaGlsZChmYik7IH0gfTsKICAgICAgZnJvbnQuYXBwZW5kQ2hpbGQoaW1nKTsKICAgIH0gZWxzZSBmcm9udC50ZXh0Q29udGVudCA9IGNhcmQubmFtZSB8fCAnPyc7CiAgICBpbm5lci5hcHBlbmRDaGlsZChiYWNrKTsgaW5uZXIuYXBwZW5kQ2hpbGQoZnJvbnQpOwogICAgc2xvdC5hcHBlbmRDaGlsZChpbm5lcik7CgogICAgdmFyIGxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBsYWJlbC5jbGFzc05hbWUgPSAncGgtY2FyZC1sYWJlbCc7CgogICAgY29sLmNsYXNzTmFtZSA9ICdwaC1jYXJkLWNvbCBwaC1leHRyYWN0aW5nJzsKICAgIGNvbC5hcHBlbmRDaGlsZChzbG90KTsgY29sLmFwcGVuZENoaWxkKGxhYmVsKTsKICAgIHRhcmdldFJvdy5hcHBlbmRDaGlsZChjb2wpOwogICAgY29sLnN0eWxlLnNldFByb3BlcnR5KCctLXBoLWV4dHJhY3QtZGVsYXknLCAoKHRhcmdldFJvdy5jaGlsZHJlbi5sZW5ndGggLSAxKSAqIDk1KSArICdtcycpOwoKICAgIHZhciByZXZlYWxTdGFydGVkID0gZmFsc2U7CiAgICB2YXIgcmV2ZWFsSW5kZXggPSB0YXJnZXRSb3cuY2hpbGRyZW4ubGVuZ3RoIC0gMTsKCiAgICBmdW5jdGlvbiByZXZlYWwoKXsKICAgICAgaWYgKHNsb3QuY2xhc3NMaXN0LmNvbnRhaW5zKCdwaC1mbGlwcGVkJykpewogICAgICAgIG9wZW5DYXJkTW9kYWwoY2FyZCwgaW5mbywgdXNkKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgaWYgKHJldmVhbFN0YXJ0ZWQpIHJldHVybjsKICAgICAgcmV2ZWFsU3RhcnRlZCA9IHRydWU7CiAgICAgIHZhciBpc0xhc3QgPSByZXZlYWxJbmRleCA9PT0gdGFyZ2V0Um93LmNoaWxkcmVuLmxlbmd0aCAtIDE7CiAgICAgIHBoUHJlcGFyZVJldmVhbChzbG90LCBpbmZvLnRpZXIsIGlzTGFzdCk7CiAgICAgIHZhciBkZWxheSA9IHBoUmV2ZWFsRGVsYXkoaW5mby50aWVyLCBpc0xhc3QpOwogICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7CiAgICAgICAgcGhDbGVhclJldmVhbEZ4KHNsb3QpOwogICAgICAgIHNsb3QuY2xhc3NMaXN0LmFkZCgncGgtZmxpcHBlZCcpOwogICAgICAgIHNsb3QudGl0bGUgPSAnQuG6pW0gxJHhu4MgeGVtIGNoaSB0aeG6v3QgbMOhIGLDoGknOwogICAgICBsYWJlbC50ZXh0Q29udGVudCA9ICcnOwogICAgICB2YXIgbm0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7CiAgICAgIG5tLnRleHRDb250ZW50ID0gKGNhcmQubmFtZSB8fCAnJykgKyAnIFx1MDBiNyAnICsgKGluZm8ubGFiZWwgfHwgdGllckxhYmVsVGV4dChpbmZvLnRpZXIpKSArICcgXHUwMGI3ICc7CiAgICAgIHZhciBwciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgICAgcHIuY2xhc3NOYW1lID0gdXNkICE9IG51bGwgPyAncGgtY2FyZC1wcmljZScgOiAncGgtY2FyZC1wcmljZSBwaC1jYXJkLW5vcHJpY2UnOwogICAgICBwci50ZXh0Q29udGVudCA9IHVzZCAhPSBudWxsID8gbW9uZXkodXNkKSA6ICdcdTIwMTQnOwogICAgICBsYWJlbC5hcHBlbmRDaGlsZChubSk7IGxhYmVsLmFwcGVuZENoaWxkKHByKTsKICAgICAgcGxheUZsaXAoaW5mby50aWVyKTsKICAgICAgYWRkTm92ZWx0eUJhZGdlKGZyb250LCBub3ZlbHR5KTsKICAgICAgICBpZiAoaW5mby50aWVyID49IDUpewogICAgICAgICAgZWxzLmZsYXNoLmNsYXNzTGlzdC5yZW1vdmUoJ3BoLWdvJyk7CiAgICAgICAgICB2b2lkIGVscy5mbGFzaC5vZmZzZXRXaWR0aDsKICAgICAgICAgIGVscy5mbGFzaC5jbGFzc0xpc3QuYWRkKCdwaC1nbycpOwogICAgICAgIH0KICAgICAgICBpZiAoaW5mby50aWVyID49IDQpIGNvbmZBdEVsZW1lbnQoc2xvdCwgaW5mby50aWVyKTsKICAgICAgfSwgZGVsYXkpOwogICAgfQoKICAgIHNsb3QuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCByZXZlYWwpOwogICAgcmV0dXJuIHJldmVhbDsKICB9CgogIGZ1bmN0aW9uIGZpbmlzaEJveChwYWNrc0Fycil7CiAgICBlbHMucHJvZ3Jlc3MuaGlkZGVuID0gdHJ1ZTsKICAgIHZhciBmbGF0ID0gW107CiAgICBwYWNrc0Fyci5mb3JFYWNoKGZ1bmN0aW9uKHApeyBmbGF0ID0gZmxhdC5jb25jYXQocCk7IH0pOwoKICAgIHZhciBiZWZvcmVEQiA9IGxvYWREQigpOwogICAgdmFyIG5vdmVsdHlCeUlkID0ge307CiAgICBmbGF0LmZvckVhY2goZnVuY3Rpb24oY2FyZCl7CiAgICAgIGlmICghY2FyZCB8fCAhY2FyZC5pZCB8fCBub3ZlbHR5QnlJZFtjYXJkLmlkXSkgcmV0dXJuOwogICAgICBub3ZlbHR5QnlJZFtjYXJkLmlkXSA9IG5vdmVsdHlMYWJlbChjYXJkLCBiZWZvcmVEQik7CiAgICB9KTsKCiAgICByZWNvcmRQYWNrcyhmbGF0LCBwYWNrc0Fyci5sZW5ndGgpOwogICAgc3RhdGUubGFzdFJlc3VsdCA9IHsgY2FyZHM6IGZsYXQsIHBhY2tzOiBwYWNrc0Fyci5sZW5ndGggfTsKCiAgICB2YXIgZCA9IGxvYWREQigpOwogICAgdmFyIHB2ID0gcGFja1ZhbHVlKGZsYXQpOwogICAgdmFyIHNwZW50ID0gZC5wYWNrUHJpY2UgKiBwYWNrc0Fyci5sZW5ndGg7CiAgICB2YXIgcm9pID0gc3BlbnQgPiAwID8gKHB2LnRvdGFsIC0gc3BlbnQpIC8gc3BlbnQgKiAxMDAgOiBudWxsOwogICAgdmFyIG1heFRpZXIgPSAwLCB0aWVycyA9IFswLDAsMCwwLDAsMCwwXTsKICAgIGZsYXQuZm9yRWFjaChmdW5jdGlvbihjKXsKICAgICAgdmFyIHQgPSByYXJpdHlUaWVyKGMucmFyaXR5KS50aWVyOwogICAgICB0aWVyc1t0XSsrOwogICAgICBpZiAodCA+IG1heFRpZXIpIG1heFRpZXIgPSB0OwogICAgfSk7CgogICAgdmFyIHNvcnRlZCA9IGZsYXQuc2xpY2UoKS5zb3J0KGZ1bmN0aW9uKGEsYil7CiAgICAgIHZhciBwYSA9IHByaWNlVVNEKGEpLCBwYiA9IHByaWNlVVNEKGIpOwogICAgICBwYSA9IHBhID09IG51bGwgPyAtMSA6IHBhOyBwYiA9IHBiID09IG51bGwgPyAtMSA6IHBiOwogICAgICBpZiAocGIgIT09IHBhKSByZXR1cm4gcGIgLSBwYTsKICAgICAgcmV0dXJuIHJhcml0eVRpZXIoYi5yYXJpdHkpLnRpZXIgLSByYXJpdHlUaWVyKGEucmFyaXR5KS50aWVyOwogICAgfSk7CiAgICB2YXIgaGVybyA9IHNvcnRlZC5zbGljZSgwLCA1KTsKCiAgICBlbHMuYm94Qm9keS50ZXh0Q29udGVudCA9ICcnOwoKICAgIC8vIC0tLS0tIG3DoG4gImfDs2kgdGluaCBob2EiOiA1IGzDoSDEkcOhbmcgZ2nDoSBuaOG6pXQgY+G7p2EgY+G6oyBo4buZcCAtLS0tLQogICAgdmFyIGggPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGguY2xhc3NOYW1lID0gJ3BoLXB1bGwtaGVhZGluZyc7CiAgICBoLnRleHRDb250ZW50ID0gJ1RpbmggaG9hIGPhuqMgaOG7mXAgXHUwMGI3ICcgKyBwYWNrc0Fyci5sZW5ndGggKyAnIGfDs2kgXHUwMGI3ICcgKyBmbGF0Lmxlbmd0aCArICcgbMOhJzsKICAgIGVscy5ib3hCb2R5LmFwcGVuZENoaWxkKGgpOwoKICAgIHZhciByb3cgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHJvdy5jbGFzc05hbWUgPSAncGgtY2FyZC1yb3cnOwogICAgZWxzLmJveEJvZHkuYXBwZW5kQ2hpbGQocm93KTsKCiAgICB2YXIgYmFubmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBiYW5uZXIuY2xhc3NOYW1lID0gJ3BoLWJhbm5lcic7CiAgICBlbHMuYm94Qm9keS5hcHBlbmRDaGlsZChiYW5uZXIpOwoKICAgIC8vIC0tLS0tIGLhuqNuZyBST0ksIGhp4buHbiBzYXUga2hpIGzhuq10IHhvbmcgLS0tLS0KICAgIHZhciB3cmFwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICB3cmFwLmNsYXNzTmFtZSA9ICdwaC1ib3gtc3RhdHMnOwoKICAgIHZhciBzdGF0cyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgc3RhdHMuY2xhc3NOYW1lID0gJ3BoLXN0YXQtZ3JpZCc7CiAgICBzdGF0cy5hcHBlbmRDaGlsZChzdGF0Qm94KCdDaGkgcGjDrSBo4buZcCcsIG1vbmV5KHNwZW50KSkpOwogICAgc3RhdHMuYXBwZW5kQ2hpbGQoc3RhdEJveCgnR2nDoSB0cuG7iyB0aHUgxJHGsOG7o2MnLCBtb25leShwdi50b3RhbCkpKTsKICAgIHN0YXRzLmFwcGVuZENoaWxkKHN0YXRCb3goJ0zhu51pL2zhu5cnLCAocHYudG90YWwgLSBzcGVudCA+PSAwID8gJysnIDogJy0nKSArIG1vbmV5KE1hdGguYWJzKHB2LnRvdGFsIC0gc3BlbnQpKSwgcHYudG90YWwgLSBzcGVudCA+PSAwID8gJ3BoLXBvcycgOiAncGgtbmVnJykpOwogICAgc3RhdHMuYXBwZW5kQ2hpbGQoc3RhdEJveCgnUk9JJywgcm9pID09IG51bGwgPyAn4oCUJyA6IHBjdChyb2kpLCByb2lDbGFzcyhyb2kpKSk7CiAgICB3cmFwLmFwcGVuZENoaWxkKHN0YXRzKTsKCiAgICB2YXIgbm90ZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbm90ZS5jbGFzc05hbWUgPSAncGgtY29sLW5vdGUnOwogICAgbm90ZS50ZXh0Q29udGVudCA9IHB2LnByaWNlZCA8IGZsYXQubGVuZ3RoCiAgICAgID8gcHYucHJpY2VkICsgJy8nICsgZmxhdC5sZW5ndGggKyAnIGzDoSBjw7MgZOG7ryBsaeG7h3UgZ2nDoS4gVG/DoG4gYuG7mSBz4buRIGzDoSDEkcOjIMSRxrDhu6NjIGzGsHUgdsOgbyBi4buZIHPGsHUgdOG6rXAuJwogICAgICA6ICdUb8OgbiBi4buZIHPhu5EgbMOhIMSRw6MgxJHGsOG7o2MgbMawdSB2w6BvIGLhu5kgc8awdSB04bqtcC4nOwogICAgd3JhcC5hcHBlbmRDaGlsZChub3RlKTsKCiAgICB2YXIgYnJrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBicmsuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXNlY3Rpb24nOwogICAgdmFyIGJya1QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGJya1QuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXNlY3Rpb24tdGl0bGUnOwogICAgYnJrVC50ZXh0Q29udGVudCA9ICdQaMOibiBi4buVIMSR4buZIGhp4bq/bSc7CiAgICBicmsuYXBwZW5kQ2hpbGQoYnJrVCk7CiAgICB2YXIgbWF4TiA9IE1hdGgubWF4LmFwcGx5KG51bGwsIHRpZXJzKSB8fCAxOwogICAgZm9yICh2YXIgdCA9IDY7IHQgPj0gMDsgdC0tKXsKICAgICAgaWYgKCF0aWVyc1t0XSkgY29udGludWU7CiAgICAgIHZhciByb3diID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIHJvd2IuY2xhc3NOYW1lID0gJ3BoLWJhci1yb3cnOwogICAgICB2YXIgYmwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IGJsLmNsYXNzTmFtZSA9ICdwaC1iYXItbCc7IGJsLnRleHRDb250ZW50ID0gdGllckxhYmVsVGV4dCh0KTsKICAgICAgdmFyIGJ3ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyBidy5jbGFzc05hbWUgPSAncGgtYmFyLXcnOwogICAgICB2YXIgYmYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IGJmLmNsYXNzTmFtZSA9ICdwaC1iYXItZic7IGJmLmRhdGFzZXQudGllciA9IFN0cmluZyh0KTsKICAgICAgYmYuc3R5bGUud2lkdGggPSBNYXRoLm1heCg0LCB0aWVyc1t0XSAvIG1heE4gKiAxMDApICsgJyUnOwogICAgICBidy5hcHBlbmRDaGlsZChiZik7CiAgICAgIHZhciBibiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgYm4uY2xhc3NOYW1lID0gJ3BoLWJhci1uJzsgYm4udGV4dENvbnRlbnQgPSBTdHJpbmcodGllcnNbdF0pOwogICAgICByb3diLmFwcGVuZENoaWxkKGJsKTsgcm93Yi5hcHBlbmRDaGlsZChidyk7IHJvd2IuYXBwZW5kQ2hpbGQoYm4pOwogICAgICBicmsuYXBwZW5kQ2hpbGQocm93Yik7CiAgICB9CiAgICB3cmFwLmFwcGVuZENoaWxkKGJyayk7CgogICAgdmFyIHJlc3QgPSBzb3J0ZWQuc2xpY2UoNSwgMTcpOwogICAgaWYgKHJlc3QubGVuZ3RoKXsKICAgICAgdmFyIG1vcmVCdG4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsKICAgICAgbW9yZUJ0bi50eXBlID0gJ2J1dHRvbic7CiAgICAgIG1vcmVCdG4uY2xhc3NOYW1lID0gJ3BoLWJ0biBwaC1zbWFsbCc7CiAgICAgIG1vcmVCdG4udGV4dENvbnRlbnQgPSAnWGVtIGPDoWMgbMOhIMSRw6FuZyBnacOhIHRp4bq/cCB0aGVvJzsKICAgICAgbW9yZUJ0bi5zZXRBdHRyaWJ1dGUoJ2FyaWEtZXhwYW5kZWQnLCAnZmFsc2UnKTsKICAgICAgbW9yZUJ0bi5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKICAgICAgbW9yZUJ0bi5zdHlsZS5tYXJnaW4gPSAnMTRweCBhdXRvIDAnOwoKICAgICAgdmFyIHRvcFdyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgdG9wV3JhcC5jbGFzc05hbWUgPSAncGgtbW9kYWwtc2VjdGlvbic7CiAgICAgIHRvcFdyYXAuc3R5bGUuZGlzcGxheSA9ICdub25lJzsKCiAgICAgIHZhciB0b3BUID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIHRvcFQuY2xhc3NOYW1lID0gJ3BoLW1vZGFsLXNlY3Rpb24tdGl0bGUnOwogICAgICB0b3BULnRleHRDb250ZW50ID0gJ0PDoWMgbMOhIMSRw6FuZyBnacOhIHRp4bq/cCB0aGVvJzsKICAgICAgdG9wV3JhcC5hcHBlbmRDaGlsZCh0b3BUKTsKCiAgICAgIHZhciBncmlkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgIGdyaWQuY2xhc3NOYW1lID0gJ3BoLXRodW1iLWdyaWQnOwogICAgICByZXN0LmZvckVhY2goZnVuY3Rpb24oYyl7CiAgICAgICAgdmFyIGluZm8gPSByYXJpdHlUaWVyKGMucmFyaXR5KTsKICAgICAgICB2YXIgdXNkID0gcHJpY2VVU0QoYyk7CiAgICAgICAgZ3JpZC5hcHBlbmRDaGlsZChjYXJkVGh1bWIoCiAgICAgICAgICB7IG5hbWU6Yy5uYW1lLCBpbWc6Yy5pbWFnZSwgdGllcjppbmZvLnRpZXIsIHByaWNlOnVzZCwgbjoxIH0sCiAgICAgICAgICBmdW5jdGlvbigpeyBvcGVuQ2FyZE1vZGFsKGMsIGluZm8sIHVzZCk7IH0KICAgICAgICApKTsKICAgICAgfSk7CiAgICAgIHRvcFdyYXAuYXBwZW5kQ2hpbGQoZ3JpZCk7CgogICAgICBtb3JlQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZnVuY3Rpb24oKXsKICAgICAgICB2YXIgc2hvdyA9IHRvcFdyYXAuc3R5bGUuZGlzcGxheSA9PT0gJ25vbmUnOwogICAgICAgIHRvcFdyYXAuc3R5bGUuZGlzcGxheSA9IHNob3cgPyAnJyA6ICdub25lJzsKICAgICAgICBtb3JlQnRuLnNldEF0dHJpYnV0ZSgnYXJpYS1leHBhbmRlZCcsIHNob3cgPyAndHJ1ZScgOiAnZmFsc2UnKTsKICAgICAgICBtb3JlQnRuLnRleHRDb250ZW50ID0gc2hvdyA/ICfhuqhuIGPDoWMgbMOhIMSRw6FuZyBnacOhIHRp4bq/cCB0aGVvJyA6ICdYZW0gY8OhYyBsw6EgxJHDoW5nIGdpw6EgdGnhur9wIHRoZW8nOwogICAgICB9KTsKCiAgICAgIHdyYXAuYXBwZW5kQ2hpbGQobW9yZUJ0bik7CiAgICAgIHdyYXAuYXBwZW5kQ2hpbGQodG9wV3JhcCk7CiAgICB9CgogICAgdmFyIGx1Y2sgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxMDAsIE1hdGgucm91bmQoCiAgICAgIHN1bVRpZXJzKGZsYXQpIC8gKGZsYXQubGVuZ3RoICogNikgKiAxMDAgKiAwLjU1ICsKICAgICAgKHB2LnByaWNlZCA/IE1hdGgubWluKDEwMCwgTWF0aC5sb2cxMCgxICsgcHYudG90YWwgLyBNYXRoLm1heCgxLCBwYWNrc0Fyci5sZW5ndGgpKSAvIE1hdGgubG9nMTAoMTAxKSAqIDEwMCkgOiAwKSAqIDAuNDUKICAgICkpKTsKICAgIHZhciBiYW5kLCBjbHM7CiAgICBpZiAocm9pICE9IG51bGwgJiYgcm9pID49IDQwKXsgYmFuZCA9IFFVSVBTLmdyZWF0OyBjbHMgPSAncGgtcXVpcC1ncmVhdCc7IH0KICAgIGVsc2UgaWYgKHJvaSAhPSBudWxsICYmIHJvaSA+PSAwKXsgYmFuZCA9IFFVSVBTLmdvb2Q7IGNscyA9ICdwaC1xdWlwLWdvb2QnOyB9CiAgICBlbHNlIGlmIChyb2kgIT0gbnVsbCAmJiByb2kgPj0gLTM1KXsgYmFuZCA9IFFVSVBTLm1laDsgY2xzID0gJ3BoLXF1aXAtbWVoJzsgfQogICAgZWxzZSB7IGJhbmQgPSBRVUlQUy5iYWQ7IGNscyA9ICdwaC1xdWlwLWJhZCc7IH0KICAgIHZhciBxdWlwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBxdWlwLmNsYXNzTmFtZSA9ICdwaC1xdWlwIHBoLXNob3cgJyArIGNsczsKICAgIHF1aXAudGV4dENvbnRlbnQgPSBwaWNrUXVpcChiYW5kKSArICcgKMSRaeG7g20gbWF5IG3huq9uIHRydW5nIGLDrG5oICcgKyBsdWNrICsgJy8xMDApJzsKICAgIHdyYXAuYXBwZW5kQ2hpbGQocXVpcCk7CgogICAgZWxzLmJveEJvZHkuYXBwZW5kQ2hpbGQod3JhcCk7CiAgICBlbHMuYm94LmNsYXNzTGlzdC5hZGQoJ3BoLWFjdGl2ZScpOwoKICAgIC8vIC0tLS0tIGzhuq10IGzhuqduIGzGsOG7o3QsIGNo4bqtbSBoxqFuIDEgZ8OzaSB0aMaw4budbmcgxJHhu4MgY8OzIGNhbyB0csOgbyAtLS0tLQogICAgdmFyIHJldmVhbHMgPSBoZXJvLm1hcChmdW5jdGlvbihjKXsKICAgICAgcmV0dXJuIGJ1aWxkU2xvdChjLCByYXJpdHlUaWVyKGMucmFyaXR5KSwgcHJpY2VVU0QoYyksIHJvdywgbm92ZWx0eUJ5SWRbYy5pZF0pOwogICAgfSk7CiAgICByZXZlYWxzLmZvckVhY2goZnVuY3Rpb24oZm4sIGkpeyBzZXRUaW1lb3V0KGZuLCA1MDAgKyBpICogNDIwKTsgfSk7CgogICAgdmFyIGVuZEF0ID0gNTAwICsgcmV2ZWFscy5sZW5ndGggKiA0MjAgKyAzMjA7CiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7CiAgICAgIHZhciB0ZXh0ID0gJycsIGJjbHMgPSAnJzsKICAgICAgaWYgKG1heFRpZXIgPj0gNil7IHRleHQgPSAnVGjhursgY2hhc2UgdHJvbmcgaOG7mXAhIEPhu7FjIHBo4bqpbSEnOyBiY2xzID0gJ3BoLWNyb3duJzsgcGxheUZhbmZhcmUoKTsgY29uZkNlbGVicmF0ZSh0cnVlKTsgfQogICAgICBlbHNlIGlmIChtYXhUaWVyID09PSA1KXsgdGV4dCA9ICdI4buZcCBuw6B5IGPDsyB0aOG6uyBtaW5oIGjhu41hIMSR4bq3YyBiaeG7h3QhJzsgYmNscyA9ICdwaC1zaGlueSc7IGNvbmZDZWxlYnJhdGUoZmFsc2UpOyB9CiAgICAgIGVsc2UgaWYgKG1heFRpZXIgPT09IDQpeyB0ZXh0ID0gJ0jhu5lwIG7DoHkgY8OzIHRo4bq7IFN0YXIhJzsgYmNscyA9ICdwaC1zdGFyJzsgY29uZkNlbGVicmF0ZShmYWxzZSk7IH0KICAgICAgZWxzZSBpZiAobWF4VGllciA9PT0gMyl7IHRleHQgPSAnSOG7mXAgbsOgeSBjw7MgdGjhursgaGnhur9tISc7IGJjbHMgPSAncGgtcmFyZSc7IH0KICAgICAgaWYgKHRleHQpewogICAgICAgIGJhbm5lci50ZXh0Q29udGVudCA9IHRleHQ7CiAgICAgICAgYmFubmVyLmNsYXNzTmFtZSA9ICdwaC1iYW5uZXIgJyArIGJjbHM7CiAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uKCl7IGJhbm5lci5jbGFzc0xpc3QuYWRkKCdwaC1zaG93Jyk7IH0pOwogICAgICB9CiAgICAgIHdyYXAuY2xhc3NMaXN0LmFkZCgncGgtc2hvdycpOwogICAgICBzdGF0ZS5vcGVuaW5nID0gZmFsc2U7CiAgICB9LCBlbmRBdCk7CgogICAgZWxzLmJveFNoYXJlLm9uY2xpY2sgPSBmdW5jdGlvbigpewogICAgICB2YXIgY2FyZHMgPSBoZXJvLm1hcChmdW5jdGlvbihjKXsKICAgICAgICByZXR1cm4geyBuYW1lOmMubmFtZSwgaW1hZ2U6Yy5pbWFnZSwgdGllcjpyYXJpdHlUaWVyKGMucmFyaXR5KS50aWVyLCBwcmljZTpwcmljZVVTRChjKSB9OwogICAgICB9KTsKICAgICAgbWFrZUFuZFNoYXJlKHsKICAgICAgICB0aXRsZTogJ03hu58gaOG7mXAgJyArIChzdGF0ZS5jdXJyZW50ID8gc3RhdGUuY3VycmVudC5uYW1lIDogJycpLAogICAgICAgIHN1YjogcGFja3NBcnIubGVuZ3RoICsgJyBnw7NpIFx1MDBiNyAnICsgZmxhdC5sZW5ndGggKyAnIGzDoSBcdTAwYjcgUk9JICcgKyAocm9pID09IG51bGwgPyAn4oCUJyA6IHBjdChyb2kpKSwKICAgICAgICBjYXJkczogY2FyZHMsCiAgICAgICAgc3RhdHM6IFsKICAgICAgICAgIFsnQ2hpIHBow60nLCBtb25leShzcGVudCldLAogICAgICAgICAgWydHacOhIHRy4buLJywgbW9uZXkocHYudG90YWwpXSwKICAgICAgICAgIFsnTOG7nWkvbOG7lycsIChwdi50b3RhbCAtIHNwZW50ID49IDAgPyAnKycgOiAnLScpICsgbW9uZXkoTWF0aC5hYnMocHYudG90YWwgLSBzcGVudCkpLCBwdi50b3RhbCAtIHNwZW50ID49IDAgPyAncG9zJyA6ICduZWcnXSwKICAgICAgICAgIFsnUk9JJywgcm9pID09IG51bGwgPyAn4oCUJyA6IHBjdChyb2kpXQogICAgICAgIF0sCiAgICAgICAgdGV4dDogJ03DrG5oIHbhu6thIG3hu58gJyArIHBhY2tzQXJyLmxlbmd0aCArICcgZ8OzaSAnICsgKHN0YXRlLmN1cnJlbnQgPyBzdGF0ZS5jdXJyZW50Lm5hbWUgOiAnJykgKyAnIHRyw6puIFBva2VIdWI6IGNoaSAnICsgbW9uZXkoc3BlbnQpICsgJywgdGh1IHbhu4EgJyArIG1vbmV5KHB2LnRvdGFsKSArICcgKFJPSSAnICsgKHJvaSA9PSBudWxsID8gJ+KAlCcgOiBwY3Qocm9pKSkgKyAnKS4nCiAgICAgIH0pOwogICAgfTsKICB9CgogIC8vIGNoaXAgY2hvbiBzbyBnb2kKICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKGVscy5ib3hSb3cucXVlcnlTZWxlY3RvckFsbCgnW2RhdGEtcGFja3NdJyksIGZ1bmN0aW9uKGJ0bil7CiAgICBidG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpewogICAgICB2YXIgbiA9IHBhcnNlSW50KGJ0bi5kYXRhc2V0LnBhY2tzLCAxMCkgfHwgMTsKICAgICAgaWYgKG4gPT09IDEpIG9wZW5QYWNrKCk7IGVsc2Ugb3BlbkJveChuKTsKICAgICAgc2Nyb2xsVG9PcGVuQW5jaG9yKCdhdXRvJyk7CiAgICB9KTsKICB9KTsKCiAgdXBkYXRlQ29sQmFkZ2UoKTsKCgogIC8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQogICAgIEFERC1PTjogVEjhu6wgVEjDgUNIIE7Dgk5HIENBTyArIFRIxq/hu55ORyBM4buaTiArIEzDgE0gTeG7mkkgMzAgUEjDmlQKICAgICBDaOG7iSBi4buVIHN1bmcgbG9naWMsIGdp4buvIG5ndXnDqm4gaOG7hyB0aOG7kW5nIGPFqS4KICAgICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi8KICB2YXIgcGhDaGFsbGVuZ2VSZWZyZXNoVGltZXIgPSBudWxsOwogIHZhciBQSF9DSEFMTEVOR0VfQ1lDTEVfTVMgPSAzMCAqIDYwICogMTAwMDsKICB2YXIgUEhfQUNUSVZFX0NIQUxMRU5HRV9DT1VOVCA9IDY7CgogIGZ1bmN0aW9uIHBoQ2hhbGxlbmdlQ3ljbGVLZXkobm93KXsKICAgIC8vIENodSBr4buzIGzDoCByb2xsaW5nIDMwIHBow7p0LCBi4bqvdCDEkeG6p3UgdOG7qyBs4bqnbiDEkeG6p3UgaOG7hyB0aOG7kW5nIHThuqFvIGNodSBr4buzIGNobyBuZ8aw4budaSBkw7luZy4KICAgIHZhciBkID0gbG9hZERCKCk7CiAgICB2YXIgc3RhcnQgPSBOdW1iZXIoZC5jaGFsbGVuZ2VDeWNsZVN0YXJ0ZWRBdCkgfHwgMDsKICAgIGlmICghc3RhcnQpIHJldHVybiAwOwogICAgcmV0dXJuIE1hdGguZmxvb3Ioc3RhcnQgLyBQSF9DSEFMTEVOR0VfQ1lDTEVfTVMpOwogIH0KCiAgZnVuY3Rpb24gcGhDaGFsbGVuZ2VDdXJyZW50Q3ljbGVFbmRzQXQobm93KXsKICAgIHZhciBkID0gbG9hZERCKCk7CiAgICB2YXIgc3RhcnQgPSBOdW1iZXIoZC5jaGFsbGVuZ2VDeWNsZVN0YXJ0ZWRBdCkgfHwgMDsKICAgIGlmICghc3RhcnQpIHJldHVybiAobm93IHx8IERhdGUubm93KCkpICsgUEhfQ0hBTExFTkdFX0NZQ0xFX01TOwogICAgcmV0dXJuIHN0YXJ0ICsgUEhfQ0hBTExFTkdFX0NZQ0xFX01TOwogIH0KCiAgZnVuY3Rpb24gcGhDaGFsbGVuZ2VTbmFwc2hvdChzKXsKICAgIHJldHVybiB7CiAgICAgIHBhY2tzOiBOdW1iZXIocy5wYWNrcykgfHwgMCwKICAgICAgdW5pcXVlOiBOdW1iZXIocy51bmlxdWUpIHx8IDAsCiAgICAgIGNhcmRzOiBOdW1iZXIocy5jYXJkcykgfHwgMCwKICAgICAgaGlnaFRpZXI6IE51bWJlcihzLmhpZ2hUaWVyKSB8fCAwLAogICAgICB0aWVyNDogTnVtYmVyKHMudGllcjQpIHx8IDAsCiAgICAgIHRpZXI1OiBOdW1iZXIocy50aWVyNSkgfHwgMCwKICAgICAgY2hhc2U6IE51bWJlcihzLmNoYXNlKSB8fCAwLAogICAgICB2YWx1ZTogTnVtYmVyKHMudmFsdWUpIHx8IDAKICAgIH07CiAgfQoKICBmdW5jdGlvbiBwaENoYWxsZW5nZUVuc3VyZUN5Y2xlKCl7CiAgICB2YXIgZCA9IGxvYWREQigpOwogICAgdmFyIG5vdyA9IERhdGUubm93KCk7CiAgICB2YXIgc3RhcnQgPSBOdW1iZXIoZC5jaGFsbGVuZ2VDeWNsZVN0YXJ0ZWRBdCkgfHwgMDsKICAgIHZhciBleHBpcmVkID0gIXN0YXJ0IHx8IChub3cgLSBzdGFydCA+PSBQSF9DSEFMTEVOR0VfQ1lDTEVfTVMpOwogICAgdmFyIGN1cnJlbnQgPSBjaGFsbGVuZ2VTdGF0cygpOwogICAgaWYgKGV4cGlyZWQgfHwgIWQuY2hhbGxlbmdlQmFzZWxpbmUpewogICAgICBkLmNoYWxsZW5nZUN5Y2xlU3RhcnRlZEF0ID0gbm93OwogICAgICBkLmNoYWxsZW5nZUN5Y2xlID0gbm93OwogICAgICBkLmNoYWxsZW5nZUJhc2VsaW5lID0gcGhDaGFsbGVuZ2VTbmFwc2hvdChjdXJyZW50KTsKICAgICAgZC5jaGFsbGVuZ2VDbGFpbXMgPSB7fTsKICAgICAgcGhDaGFsbGVuZ2VQaWNrQWN0aXZlKGQpOwogICAgICBzYXZlREIoKTsKICAgIH0KICAgIGlmICghQXJyYXkuaXNBcnJheShkLmNoYWxsZW5nZUFjdGl2ZUlkcykgfHwgZC5jaGFsbGVuZ2VBY3RpdmVJZHMubGVuZ3RoICE9PSBQSF9BQ1RJVkVfQ0hBTExFTkdFX0NPVU5UKXsKICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZC5jaGFsbGVuZ2VBY3RpdmVJZHMpICYmIGQuY2hhbGxlbmdlQWN0aXZlSWRzLmxlbmd0aCA+IFBIX0FDVElWRV9DSEFMTEVOR0VfQ09VTlQpIGQuY2hhbGxlbmdlQWN0aXZlSWRzID0gW107CiAgICAgIHBoQ2hhbGxlbmdlUGlja0FjdGl2ZShkKTsKICAgICAgZC5jaGFsbGVuZ2VDbGFpbXMgPSB7fTsKICAgICAgc2F2ZURCKCk7CiAgICB9CiAgICBpZiAoIWQuY2hhbGxlbmdlQ2xhaW1zIHx8IHR5cGVvZiBkLmNoYWxsZW5nZUNsYWltcyAhPT0gJ29iamVjdCcpIGQuY2hhbGxlbmdlQ2xhaW1zID0ge307CiAgICBpZiAoIWQuY2hhbGxlbmdlQmFzZWxpbmUgfHwgdHlwZW9mIGQuY2hhbGxlbmdlQmFzZWxpbmUgIT09ICdvYmplY3QnKXsKICAgICAgZC5jaGFsbGVuZ2VCYXNlbGluZSA9IHBoQ2hhbGxlbmdlU25hcHNob3QoY3VycmVudCk7CiAgICAgIHNhdmVEQigpOwogICAgfQogICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoTnVtYmVyKGQuY2hhbGxlbmdlUG9pbnRzKSkpIGQuY2hhbGxlbmdlUG9pbnRzID0gMDsKICAgIGlmICghTnVtYmVyLmlzRmluaXRlKE51bWJlcihkLmNoYWxsZW5nZVJld2FyZHNUb3RhbCkpKSBkLmNoYWxsZW5nZVJld2FyZHNUb3RhbCA9IDA7CiAgICBwaFNob3BMb2FkKGQpOwogICAgcmV0dXJuIGQ7CiAgfQoKICBmdW5jdGlvbiBwaENoYWxsZW5nZUN5Y2xlU3RhdHMoKXsKICAgIHZhciBkID0gcGhDaGFsbGVuZ2VFbnN1cmVDeWNsZSgpOwogICAgdmFyIG5vdyA9IGNoYWxsZW5nZVN0YXRzKCk7CiAgICB2YXIgYiA9IGQuY2hhbGxlbmdlQmFzZWxpbmUgfHwge307CiAgICByZXR1cm4gewogICAgICBwYWNrczogTWF0aC5tYXgoMCwgKE51bWJlcihub3cucGFja3MpfHwwKSAtIChOdW1iZXIoYi5wYWNrcyl8fDApKSwKICAgICAgdW5pcXVlOiBNYXRoLm1heCgwLCAoTnVtYmVyKG5vdy51bmlxdWUpfHwwKSAtIChOdW1iZXIoYi51bmlxdWUpfHwwKSksCiAgICAgIGNhcmRzOiBNYXRoLm1heCgwLCAoTnVtYmVyKG5vdy5jYXJkcyl8fDApIC0gKE51bWJlcihiLmNhcmRzKXx8MCkpLAogICAgICBoaWdoVGllcjogTWF0aC5tYXgoMCwgKE51bWJlcihub3cuaGlnaFRpZXIpfHwwKSAtIChOdW1iZXIoYi5oaWdoVGllcil8fDApKSwKICAgICAgdGllcjQ6IE1hdGgubWF4KDAsIChOdW1iZXIobm93LnRpZXI0KXx8MCkgLSAoTnVtYmVyKGIudGllcjQpfHwwKSksCiAgICAgIHRpZXI1OiBNYXRoLm1heCgwLCAoTnVtYmVyKG5vdy50aWVyNSl8fDApIC0gKE51bWJlcihiLnRpZXI1KXx8MCkpLAogICAgICBjaGFzZTogTWF0aC5tYXgoMCwgKE51bWJlcihub3cuY2hhc2UpfHwwKSAtIChOdW1iZXIoYi5jaGFzZSl8fDApKSwKICAgICAgdmFsdWU6IE1hdGgubWF4KDAsIChOdW1iZXIobm93LnZhbHVlKXx8MCkgLSAoTnVtYmVyKGIudmFsdWUpfHwwKSkKICAgIH07CiAgfQoKICAvKiBOaGnhu4F1IG5oaeG7h20gduG7pSBoxqFuIHbDoCBraMOzIGjGoW47IG3hu5dpIG5oaeG7h20gduG7pSBjw7MgdGjGsOG7n25nIMSRaeG7g20gbOG7m24uICovCiAgQ0hBTExFTkdFUyA9IFsKICAgIHsgaWQ6J2N5Y2xlLTUtcGFja3MnLCBpY29uOifwn5OmJywgbmFtZTonS2jhu59pIMSR4buZbmcgdOG7kWMgxJHhu5kgY2FvJywgZGVzYzonTeG7nyA1IGfDs2kgdHJvbmcgY2h1IGvhu7MgMzAgcGjDunQuJywgZ29hbDo1LCByZXdhcmQ6NTAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5wYWNrczt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCiAgICB7IGlkOidjeWNsZS0xNS1wYWNrcycsIGljb246J/CfmoAnLCBuYW1lOidDw6B5IDE1IGfDs2knLCBkZXNjOidN4bufIDE1IGfDs2kgdHJvbmcgbeG7mXQgY2h1IGvhu7MuJywgZ29hbDoxNSwgcmV3YXJkOjE1MDAsIHZhbHVlOmZ1bmN0aW9uKHMpe3JldHVybiBzLnBhY2tzO30sIGZtdDpmdW5jdGlvbih2LGcpe3JldHVybiBNYXRoLm1pbih2LGcpKycvJytnO30gfSwKICAgIHsgaWQ6J2N5Y2xlLTMwLXBhY2tzJywgaWNvbjon8J+UpScsIG5hbWU6J01hcmF0aG9uIDMwIGfDs2knLCBkZXNjOidN4bufIDMwIGfDs2kgdHJvbmcgY2h1IGvhu7MgMzAgcGjDunQuJywgZ29hbDozMCwgcmV3YXJkOjQwMDAsIHZhbHVlOmZ1bmN0aW9uKHMpe3JldHVybiBzLnBhY2tzO30sIGZtdDpmdW5jdGlvbih2LGcpe3JldHVybiBNYXRoLm1pbih2LGcpKycvJytnO30gfSwKICAgIHsgaWQ6J2N5Y2xlLTUwLXBhY2tzJywgaWNvbjon8J+PhicsIG5hbWU6J0Lhuq1jIHRo4bqneSBt4bufIHBhY2snLCBkZXNjOidN4bufIDUwIGfDs2kgdHLGsOG7m2Mga2hpIGNodSBr4buzIGvhur90IHRow7pjLicsIGdvYWw6NTAsIHJld2FyZDo4MDAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5wYWNrczt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCgogICAgeyBpZDonY3ljbGUtMjAtdW5pcXVlJywgaWNvbjon8J+TmicsIG5hbWU6J1PGsHUgdOG6p20gMjAgbMOhIG3hu5tpJywgZGVzYzonTmjhuq1uIDIwIGzDoSDEkeG7mWMgbmjhuqV0IHRyb25nIGNodSBr4buzLicsIGdvYWw6MjAsIHJld2FyZDoxMjAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy51bmlxdWU7fSwgZm10OmZ1bmN0aW9uKHYsZyl7cmV0dXJuIE1hdGgubWluKHYsZykrJy8nK2c7fSB9LAogICAgeyBpZDonY3ljbGUtNTAtdW5pcXVlJywgaWNvbjon8J+Xg++4jycsIG5hbWU6J0tobyBiw6F1IDUwIGzDoSBt4bubaScsIGRlc2M6J05o4bqtbiA1MCBsw6EgxJHhu5ljIG5o4bqldCB0cm9uZyBjaHUga+G7sy4nLCBnb2FsOjUwLCByZXdhcmQ6MzUwMCwgdmFsdWU6ZnVuY3Rpb24ocyl7cmV0dXJuIHMudW5pcXVlO30sIGZtdDpmdW5jdGlvbih2LGcpe3JldHVybiBNYXRoLm1pbih2LGcpKycvJytnO30gfSwKICAgIHsgaWQ6J2N5Y2xlLTEwMC11bmlxdWUnLCBpY29uOifwn5KOJywgbmFtZTonQuG7mSBzxrB1IHThuq1wIGto4buVbmcgbOG7kycsIGRlc2M6J05o4bqtbiAxMDAgbMOhIMSR4buZYyBuaOG6pXQgdHJvbmcgY2h1IGvhu7MuJywgZ29hbDoxMDAsIHJld2FyZDo5MDAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy51bmlxdWU7fSwgZm10OmZ1bmN0aW9uKHYsZyl7cmV0dXJuIE1hdGgubWluKHYsZykrJy8nK2c7fSB9LAoKICAgIHsgaWQ6J2N5Y2xlLTUtaGlnaCcsIGljb246J+KcqCcsIG5hbWU6J1PEg24gVWx0cmEgbGnDqm4gdOG7pWMnLCBkZXNjOidQdWxsIDUgbMOhIHThu6sgVWx0cmEgdHLhu58gbMOqbiB0cm9uZyBjaHUga+G7sy4nLCBnb2FsOjUsIHJld2FyZDoxODAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5oaWdoVGllcjt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCiAgICB7IGlkOidjeWNsZS0xNS1oaWdoJywgaWNvbjon8J+MoCcsIG5hbWU6J1Ro4bujIHPEg24gY+G7sWMgaGnhur9tJywgZGVzYzonUHVsbCAxNSBsw6EgdOG7qyBVbHRyYSB0cuG7nyBsw6puIHRyb25nIGNodSBr4buzLicsIGdvYWw6MTUsIHJld2FyZDo1MDAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5oaWdoVGllcjt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCiAgICB7IGlkOidjeWNsZS0yLWNoYXNlJywgaWNvbjon8J+RkScsIG5hbWU6J0RvdWJsZSBDaGFzZSBIdW50ZXInLCBkZXNjOidQdWxsIDIgbMOhIENoYXNlIHRyb25nIGPDuW5nIGNodSBr4buzLicsIGdvYWw6MiwgcmV3YXJkOjcwMDAsIHZhbHVlOmZ1bmN0aW9uKHMpe3JldHVybiBzLmNoYXNlO30sIGZtdDpmdW5jdGlvbih2LGcpe3JldHVybiBNYXRoLm1pbih2LGcpKycvJytnO30gfSwKCiAgICB7IGlkOidjeWNsZS0xMDAtY2FyZHMnLCBpY29uOifwn5KvJywgbmFtZTonQsOjbyB0aOG6uycsIGRlc2M6J1B1bGwgMTAwIGzDoSB0cm9uZyBjaHUga+G7syAzMCBwaMO6dC4nLCBnb2FsOjEwMCwgcmV3YXJkOjE1MDAsIHZhbHVlOmZ1bmN0aW9uKHMpe3JldHVybiBzLmNhcmRzO30sIGZtdDpmdW5jdGlvbih2LGcpe3JldHVybiBNYXRoLm1pbih2LGcpKycvJytnO30gfSwKICAgIHsgaWQ6J2N5Y2xlLTI1MC1jYXJkcycsIGljb246J+KaoScsIG5hbWU6J0Phu5cgbcOheSBt4bufIHBhY2snLCBkZXNjOidQdWxsIDI1MCBsw6EgdHJvbmcgbeG7mXQgY2h1IGvhu7MuJywgZ29hbDoyNTAsIHJld2FyZDo0NTAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5jYXJkczt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCiAgICB7IGlkOidjeWNsZS01MDAtY2FyZHMnLCBpY29uOifwn5G5JywgbmFtZTonUXXDoWkgduG6rXQgc8awdSB04bqnbScsIGRlc2M6J1B1bGwgNTAwIGzDoSB0cm9uZyBt4buZdCBjaHUga+G7sy4nLCBnb2FsOjUwMCwgcmV3YXJkOjEwMDAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5jYXJkczt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCgogICAgeyBpZDonY3ljbGUtdmFsdWUtMjUnLCBpY29uOifwn5KwJywgbmFtZTonR2nDoSB0cuG7iyBwYWNrIGLDuW5nIG7hu5UnLCBkZXNjOidUaHUgduG7gSDDrXQgbmjhuqV0ICQyNSBnacOhIHRy4buLIHRo4bq7IHRyb25nIGNodSBr4buzLicsIGdvYWw6MjUsIHJld2FyZDoxODAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy52YWx1ZTt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gJyQnK01hdGgubWluKHYsZykudG9GaXhlZCgyKSsnIC8gJCcrZzt9IH0sCiAgICB7IGlkOidjeWNsZS12YWx1ZS03NScsIGljb246J/CfkrAnLCBuYW1lOidLaG8gYsOhdSAkNzUnLCBkZXNjOidUaHUgduG7gSDDrXQgbmjhuqV0ICQ3NSBnacOhIHRy4buLIHRo4bq7IHRyb25nIGNodSBr4buzLicsIGdvYWw6NzUsIHJld2FyZDo1NTAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy52YWx1ZTt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gJyQnK01hdGgubWluKHYsZykudG9GaXhlZCgyKSsnIC8gJCcrZzt9IH0sCiAgICB7IGlkOidjeWNsZS12YWx1ZS0xNTAnLCBpY29uOifwn5KwJywgbmFtZTonSmFja3BvdCAkMTUwJywgZGVzYzonVGh1IHbhu4Egw610IG5o4bqldCAkMTUwIGdpw6EgdHLhu4sgdGjhursgdHJvbmcgY2h1IGvhu7MuJywgZ29hbDoxNTAsIHJld2FyZDoxMjAwMCwgdmFsdWU6ZnVuY3Rpb24ocyl7cmV0dXJuIHMudmFsdWU7fSwgZm10OmZ1bmN0aW9uKHYsZyl7cmV0dXJuICckJytNYXRoLm1pbih2LGcpLnRvRml4ZWQoMikrJyAvICQnK2c7fSB9LAoKICAgIHsgaWQ6J2N5Y2xlLTMtdGllcjQnLCBpY29uOifwn4yfJywgbmFtZTonQmEgcGjDoXQgc2nDqnUgaGnhur9tJywgZGVzYzonUHVsbCAzIGzDoSBUaWVyIDQgdHLhu58gbMOqbiB0cm9uZyBjaHUga+G7sy4nLCBnb2FsOjMsIHJld2FyZDozMDAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy50aWVyNDt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCiAgICB7IGlkOidjeWNsZS0yLXRpZXI1JywgaWNvbjon8J+SnCcsIG5hbWU6J0hhaSBsw6EgaHV54buBbiB0aG/huqFpJywgZGVzYzonUHVsbCAyIGzDoSBUaWVyIDUgdHLhu58gbMOqbiB0cm9uZyBjaHUga+G7sy4nLCBnb2FsOjIsIHJld2FyZDo1MDAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy50aWVyNTt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0sCiAgICB7IGlkOidjeWNsZS0xMC10aWVyNCcsIGljb246J/CfkqUnLCBuYW1lOidDxqFuIGLDo28gVGllciA0JywgZGVzYzonUHVsbCAxMCBsw6EgVGllciA0IHRy4bufIGzDqm4gdHJvbmcgY2h1IGvhu7MuJywgZ29hbDoxMCwgcmV3YXJkOjc1MDAsIHZhbHVlOmZ1bmN0aW9uKHMpe3JldHVybiBzLnRpZXI0O30sIGZtdDpmdW5jdGlvbih2LGcpe3JldHVybiBNYXRoLm1pbih2LGcpKycvJytnO30gfSwKICAgIHsgaWQ6J2N5Y2xlLTEtY2hhc2UnLCBpY29uOifwn4OPJywgbmFtZTonU8SDbiBDaGFzZScsIGRlc2M6J1B1bGwgw610IG5o4bqldCAxIGzDoSBDaGFzZSB0cm9uZyBjaHUga+G7sy4nLCBnb2FsOjEsIHJld2FyZDozNTAwLCB2YWx1ZTpmdW5jdGlvbihzKXtyZXR1cm4gcy5jaGFzZTt9LCBmbXQ6ZnVuY3Rpb24odixnKXtyZXR1cm4gTWF0aC5taW4odixnKSsnLycrZzt9IH0KICBdOwogIGNoYWxsZW5nZVN0YXRzID0gZnVuY3Rpb24oKXsKICAgIHZhciBzID0gY29sbGVjdGlvblN0YXRzKCksIGQgPSBsb2FkREIoKSwgaGlnaFRpZXIgPSAwLCBjaGFzZSA9IDA7CiAgICBPYmplY3Qua2V5cyhkLmNhcmRzIHx8IHt9KS5mb3JFYWNoKGZ1bmN0aW9uKGspewogICAgICB2YXIgZSA9IGQuY2FyZHNba10sIHQgPSBOdW1iZXIoZS50aWVyKSB8fCAwLCBuID0gTnVtYmVyKGUubikgfHwgMDsKICAgICAgaWYgKHQgPj0gMykgaGlnaFRpZXIgKz0gbjsKICAgICAgaWYgKHQgPj0gNikgY2hhc2UgKz0gbjsKICAgIH0pOwogICAgcy5oaWdoVGllciA9IGhpZ2hUaWVyOwogICAgcy5jaGFzZSA9IGNoYXNlOwogICAgcmV0dXJuIHM7CiAgfTsKCiAgZnVuY3Rpb24gcGhDaGFsbGVuZ2VSZXdhcmRUZXh0KG4pewogICAgcmV0dXJuICcrJyArIE51bWJlcihuIHx8IDApLnRvTG9jYWxlU3RyaW5nKCd2aS1WTicpICsgJyDirZAnOwogIH0KCiAgZnVuY3Rpb24gcGhDaGFsbGVuZ2VGb3JtYXRNb25leSh2KXsKICAgIHJldHVybiAnJCcgKyBOdW1iZXIodiB8fCAwKS50b0ZpeGVkKDIpOwogIH0KCiAgZnVuY3Rpb24gcGhDaGFsbGVuZ2VHZXRBY3RpdmVEZWZzKGQpewogICAgdmFyIGlkcyA9IEFycmF5LmlzQXJyYXkoZC5jaGFsbGVuZ2VBY3RpdmVJZHMpID8gZC5jaGFsbGVuZ2VBY3RpdmVJZHMgOiBbXTsKICAgIHZhciBieUlkID0ge307CiAgICBDSEFMTEVOR0VTLmZvckVhY2goZnVuY3Rpb24oZGVmKXsgYnlJZFtkZWYuaWRdID0gZGVmOyB9KTsKICAgIHJldHVybiBpZHMubWFwKGZ1bmN0aW9uKGlkKXsgcmV0dXJuIGJ5SWRbaWRdOyB9KS5maWx0ZXIoQm9vbGVhbikuc2xpY2UoMCxQSF9BQ1RJVkVfQ0hBTExFTkdFX0NPVU5UKTsKICB9CgogIGZ1bmN0aW9uIHBoQ2hhbGxlbmdlUGlja0FjdGl2ZShkKXsKICAgIHZhciBwcmV2aW91cyA9IEFycmF5LmlzQXJyYXkoZC5jaGFsbGVuZ2VBY3RpdmVJZHMpID8gZC5jaGFsbGVuZ2VBY3RpdmVJZHMuc2xpY2UoKSA6IFtdOwogICAgdmFyIHBpY2tlZCA9IFtdOwoKICAgIC8vIENo4buNbiAxIG5oaeG7h20gduG7pSB04burIG3hu5dpIG5ow7NtIMSR4buDIDYgw7QgY8OzIMSR4buZIMSRYSBk4bqhbmc6IG3hu58gcGFjaywgc8awdSB04bqnbSwKICAgIC8vIFVsdHJhL0NoYXNlLCBz4buRIGzDoSwgZ2nDoSB0cuG7iyB2w6AgVGllciA0LzUuCiAgICB2YXIgZ3JvdXBzID0gWwogICAgICAvXmN5Y2xlLVxkKy1wYWNrcyQvLAogICAgICAvXmN5Y2xlLVxkKy11bmlxdWUkLywKICAgICAgL14oY3ljbGUtXGQrLWhpZ2h8Y3ljbGUtXGQrLWNoYXNlKSQvLAogICAgICAvXmN5Y2xlLVxkKy1jYXJkcyQvLAogICAgICAvXmN5Y2xlLXZhbHVlLS8sCiAgICAgIC9eKGN5Y2xlLVxkKy10aWVyNHxjeWNsZS1cZCstdGllcjUpJC8KICAgIF07CgogICAgZ3JvdXBzLmZvckVhY2goZnVuY3Rpb24ocmUpewogICAgICB2YXIgY2FuZGlkYXRlcyA9IENIQUxMRU5HRVMuZmlsdGVyKGZ1bmN0aW9uKGRlZil7CiAgICAgICAgcmV0dXJuIHJlLnRlc3QoZGVmLmlkKSAmJiBwcmV2aW91cy5pbmRleE9mKGRlZi5pZCkgPT09IC0xICYmIHBpY2tlZC5ldmVyeShmdW5jdGlvbih4KXsgcmV0dXJuIHguaWQgIT09IGRlZi5pZDsgfSk7CiAgICAgIH0pOwogICAgICBpZiAoIWNhbmRpZGF0ZXMubGVuZ3RoKXsKICAgICAgICBjYW5kaWRhdGVzID0gQ0hBTExFTkdFUy5maWx0ZXIoZnVuY3Rpb24oZGVmKXsKICAgICAgICAgIHJldHVybiByZS50ZXN0KGRlZi5pZCkgJiYgcGlja2VkLmV2ZXJ5KGZ1bmN0aW9uKHgpeyByZXR1cm4geC5pZCAhPT0gZGVmLmlkOyB9KTsKICAgICAgICB9KTsKICAgICAgfQogICAgICBpZiAoY2FuZGlkYXRlcy5sZW5ndGgpewogICAgICAgIHBpY2tlZC5wdXNoKGNhbmRpZGF0ZXNbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogY2FuZGlkYXRlcy5sZW5ndGgpXSk7CiAgICAgIH0KICAgIH0pOwoKICAgIC8vIE7hur91IGRhbmggc8OhY2ggbmhp4buHbSB24bulIHNhdSBuw6B5IHRoYXkgxJHhu5VpLCB24bqrbiBsdcO0biDEkeG7pyA2IMO0LgogICAgdmFyIGZhbGxiYWNrID0gQ0hBTExFTkdFUy5maWx0ZXIoZnVuY3Rpb24oZGVmKXsKICAgICAgcmV0dXJuIHBpY2tlZC5ldmVyeShmdW5jdGlvbih4KXsgcmV0dXJuIHguaWQgIT09IGRlZi5pZDsgfSk7CiAgICB9KTsKICAgIHdoaWxlIChmYWxsYmFjay5sZW5ndGggJiYgcGlja2VkLmxlbmd0aCA8IFBIX0FDVElWRV9DSEFMTEVOR0VfQ09VTlQpewogICAgICB2YXIgaiA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGZhbGxiYWNrLmxlbmd0aCk7CiAgICAgIHBpY2tlZC5wdXNoKGZhbGxiYWNrLnNwbGljZShqLDEpWzBdKTsKICAgIH0KCiAgICBkLmNoYWxsZW5nZVByZXZpb3VzSWRzID0gcHJldmlvdXM7CiAgICBkLmNoYWxsZW5nZUFjdGl2ZUlkcyA9IHBpY2tlZC5tYXAoZnVuY3Rpb24oZGVmKXsgcmV0dXJuIGRlZi5pZDsgfSkuc2xpY2UoMCwgUEhfQUNUSVZFX0NIQUxMRU5HRV9DT1VOVCk7CiAgfQoKICBmdW5jdGlvbiBwaENoYWxsZW5nZVN5bmNSZXdhcmRzKCl7CiAgICB2YXIgZCA9IHBoQ2hhbGxlbmdlRW5zdXJlQ3ljbGUoKTsKICAgIHZhciBzID0gcGhDaGFsbGVuZ2VDeWNsZVN0YXRzKCk7CiAgICB2YXIgZWFybmVkTm93ID0gMDsKICAgIHBoQ2hhbGxlbmdlR2V0QWN0aXZlRGVmcyhkKS5mb3JFYWNoKGZ1bmN0aW9uKGRlZil7CiAgICAgIHZhciBkb25lID0gY2hhbGxlbmdlQ29tcGxldGVkKGRlZiwgcyk7CiAgICAgIGlmIChkb25lICYmICFkLmNoYWxsZW5nZUNsYWltc1tkZWYuaWRdKXsKICAgICAgICBkLmNoYWxsZW5nZUNsYWltc1tkZWYuaWRdID0gRGF0ZS5ub3coKTsKICAgICAgICBkLmNoYWxsZW5nZXNbZGVmLmlkXSA9IERhdGUubm93KCk7CiAgICAgICAgdmFyIHJld2FyZCA9IE1hdGgubWF4KDAsIE51bWJlcihkZWYucmV3YXJkKSB8fCAwKTsKICAgICAgICBkLmNoYWxsZW5nZVBvaW50cyArPSByZXdhcmQ7CiAgICAgICAgZC5jaGFsbGVuZ2VSZXdhcmRzVG90YWwgKz0gcmV3YXJkOwogICAgICAgIGVhcm5lZE5vdyArPSByZXdhcmQ7CiAgICAgIH0KICAgIH0pOwogICAgaWYgKGVhcm5lZE5vdyA+IDApewogICAgICBzYXZlREIoKTsKICAgICAgaWYgKGVscy50b2FzdCkgdG9hc3QoJ/CfjokgSG/DoG4gdGjDoG5oIG5oaeG7h20gduG7pSEgKycgKyBlYXJuZWROb3cudG9Mb2NhbGVTdHJpbmcoJ3ZpLVZOJykgKyAnIOKtkCDEkWnhu4NtIHRoxrDhu59uZycpOwogICAgfSBlbHNlIHsKICAgICAgc2F2ZURCKCk7CiAgICB9CiAgICByZXR1cm4gZDsKICB9CgogIGZ1bmN0aW9uIHVwZGF0ZUNoYWxsZW5nZUJhZGdlKCl7CiAgICBpZiAoIWVscy5jaGFsbGVuZ2VDb3VudCB8fCAhZWxzLmNoYWxsZW5nZUJ0bikgcmV0dXJuOwogICAgdmFyIGQgPSBwaENoYWxsZW5nZVN5bmNSZXdhcmRzKCk7CiAgICB2YXIgcyA9IHBoQ2hhbGxlbmdlQ3ljbGVTdGF0cygpLCBkb25lID0gMDsKICAgIHBoQ2hhbGxlbmdlR2V0QWN0aXZlRGVmcyhkKS5mb3JFYWNoKGZ1bmN0aW9uKGRlZil7CiAgICAgIGlmIChjaGFsbGVuZ2VDb21wbGV0ZWQoZGVmLCBzKSkgZG9uZSsrOwogICAgfSk7CiAgICBlbHMuY2hhbGxlbmdlQ291bnQudGV4dENvbnRlbnQgPSBkb25lICsgJy8nICsgUEhfQUNUSVZFX0NIQUxMRU5HRV9DT1VOVDsKICAgIGVscy5jaGFsbGVuZ2VDb3VudC5oaWRkZW4gPSBmYWxzZTsKICAgIGlmIChlbHMuY2hhbGxlbmdlQnRuKSBlbHMuY2hhbGxlbmdlQnRuLnRpdGxlID0gJ05oaeG7h20gduG7pSBt4bubaSBzYXUgJyArIE1hdGgubWF4KDAsIE1hdGguY2VpbCgocGhDaGFsbGVuZ2VDdXJyZW50Q3ljbGVFbmRzQXQoKSAtIERhdGUubm93KCkpLzEwMDApKSArICcgZ2nDonknOwogICAgdXBkYXRlU2hvcEJhZGdlKCk7CiAgfQoKICBmdW5jdGlvbiB1cGRhdGVTaG9wQmFkZ2UoKXsKICAgIGlmICghZWxzLnNob3BQb2ludHNCYWRnZSB8fCAhZWxzLnNob3BCdG4pIHJldHVybjsKICAgIHZhciBkID0gbG9hZERCKCk7CiAgICB2YXIgcHRzID0gTWF0aC5tYXgoMCwgTnVtYmVyKGQuY2hhbGxlbmdlUG9pbnRzKSB8fCAwKTsKICAgIGVscy5zaG9wUG9pbnRzQmFkZ2UudGV4dENvbnRlbnQgPSBwdHMudG9Mb2NhbGVTdHJpbmcoJ3ZpLVZOJyk7CiAgICBlbHMuc2hvcFBvaW50c0JhZGdlLmhpZGRlbiA9IGZhbHNlOwogICAgZWxzLnNob3BCdG4udGl0bGUgPSAnQuG6oW4gY8OzICcgKyBwdHMudG9Mb2NhbGVTdHJpbmcoJ3ZpLVZOJykgKyAnIOKtkCDEkWnhu4NtIHRoxrDhu59uZyc7CiAgfQoKICBmdW5jdGlvbiBwaENoYWxsZW5nZUNvdW50ZG93blRleHQoKXsKICAgIHZhciBsZWZ0ID0gTWF0aC5tYXgoMCwgcGhDaGFsbGVuZ2VDdXJyZW50Q3ljbGVFbmRzQXQoKSAtIERhdGUubm93KCkpOwogICAgdmFyIHRvdGFsU2VjID0gTWF0aC5mbG9vcihsZWZ0IC8gMTAwMCk7CiAgICB2YXIgbWluID0gTWF0aC5mbG9vcih0b3RhbFNlYyAvIDYwKTsKICAgIHZhciBzZWMgPSB0b3RhbFNlYyAlIDYwOwogICAgcmV0dXJuIFN0cmluZyhtaW4pLnBhZFN0YXJ0KDIsJzAnKSArICc6JyArIFN0cmluZyhzZWMpLnBhZFN0YXJ0KDIsJzAnKTsKICB9CgogIGZ1bmN0aW9uIHBoQ2hhbGxlbmdlU3RhcnRDb3VudGRvd24oKXsKICAgIGlmIChwaENoYWxsZW5nZVJlZnJlc2hUaW1lcikgY2xlYXJJbnRlcnZhbChwaENoYWxsZW5nZVJlZnJlc2hUaW1lcik7CiAgICBwaENoYWxsZW5nZVJlZnJlc2hUaW1lciA9IHNldEludGVydmFsKGZ1bmN0aW9uKCl7CiAgICAgIHZhciBub2RlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3BoLWNoYWxsZW5nZS1jb3VudGRvd24nKTsKICAgICAgaWYgKG5vZGUpIG5vZGUudGV4dENvbnRlbnQgPSBwaENoYWxsZW5nZUNvdW50ZG93blRleHQoKTsKICAgICAgdmFyIGVuZCA9IHBoQ2hhbGxlbmdlQ3VycmVudEN5Y2xlRW5kc0F0KCk7CiAgICAgIGlmIChEYXRlLm5vdygpID49IGVuZCl7CiAgICAgICAgcGhDaGFsbGVuZ2VFbnN1cmVDeWNsZSgpOwogICAgICAgIHVwZGF0ZUNoYWxsZW5nZUJhZGdlKCk7CiAgICAgICAgaWYgKGVscy5jaGFsbGVuZ2VNb2RhbCAmJiAhZWxzLmNoYWxsZW5nZU1vZGFsLmhpZGRlbikgcmVuZGVyQ2hhbGxlbmdlcygpOwogICAgICB9CiAgICB9LCAxMDAwKTsKICB9CgogIGZ1bmN0aW9uIHBoQ2hhbGxlbmdlU3RvcENvdW50ZG93bigpewogICAgaWYgKHBoQ2hhbGxlbmdlUmVmcmVzaFRpbWVyKXsKICAgICAgY2xlYXJJbnRlcnZhbChwaENoYWxsZW5nZVJlZnJlc2hUaW1lcik7CiAgICAgIHBoQ2hhbGxlbmdlUmVmcmVzaFRpbWVyID0gbnVsbDsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGNoYWxsZW5nZUNhcmQoZGVmLCBzLCBkKXsKICAgIHZhciB2YWx1ZSA9IE1hdGgubWF4KDAsIE51bWJlcihkZWYudmFsdWUocykpIHx8IDApOwogICAgdmFyIGRvbmUgPSBjaGFsbGVuZ2VDb21wbGV0ZWQoZGVmLCBzKTsKICAgIHZhciBwZXJjZW50ID0gZGVmLmdvYWwgPiAwID8gTWF0aC5taW4oMTAwLCBNYXRoLnJvdW5kKHZhbHVlIC8gZGVmLmdvYWwgKiAxMDApKSA6IDEwMDsKCiAgICB2YXIgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgaXRlbS5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWl0ZW0nICsgKGRvbmUgPyAnIHBoLWRvbmUnIDogJycpOwoKICAgIHZhciBoZWFkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGhlYWQuY2xhc3NOYW1lID0gJ3BoLWNoYWxsZW5nZS1oZWFkJzsKICAgIHZhciBsZWZ0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICB2YXIgbmFtZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBuYW1lLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtbmFtZSc7IG5hbWUudGV4dENvbnRlbnQgPSBkZWYuaWNvbiArICcgJyArIGRlZi5uYW1lOwogICAgdmFyIGRlc2MgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgZGVzYy5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWRlc2MnOyBkZXNjLnRleHRDb250ZW50ID0gZGVmLmRlc2M7CiAgICBsZWZ0LmFwcGVuZENoaWxkKG5hbWUpOyBsZWZ0LmFwcGVuZENoaWxkKGRlc2MpOwoKICAgIHZhciBiYWRnZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsKICAgIGJhZGdlLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2Utc3RhdHVzJyArIChkb25lID8gJyBwaC1jaGFsbGVuZ2UtZG9uZScgOiAnJyk7CiAgICBiYWRnZS50ZXh0Q29udGVudCA9IGRvbmUgPyAn4pyTIEhvw6BuIHRow6BuaCcgOiAnxJBhbmcgbMOgbSc7CiAgICBoZWFkLmFwcGVuZENoaWxkKGxlZnQpOyBoZWFkLmFwcGVuZENoaWxkKGJhZGdlKTsgaXRlbS5hcHBlbmRDaGlsZChoZWFkKTsKCiAgICB2YXIgcHJvZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBwcm9nLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtcHJvZ3Jlc3MnOwogICAgdmFyIHRyYWNrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyB0cmFjay5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLXRyYWNrJzsKICAgIHZhciBmaWxsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOyBmaWxsLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtZmlsbCc7CiAgICBmaWxsLnN0eWxlLndpZHRoID0gcGVyY2VudCArICclJzsgdHJhY2suYXBwZW5kQ2hpbGQoZmlsbCk7CiAgICB2YXIgbWV0YSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgbWV0YS5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLW1ldGEnOyBtZXRhLnRleHRDb250ZW50ID0gZGVmLmZtdCh2YWx1ZSwgZGVmLmdvYWwpOwogICAgcHJvZy5hcHBlbmRDaGlsZCh0cmFjayk7IHByb2cuYXBwZW5kQ2hpbGQobWV0YSk7IGl0ZW0uYXBwZW5kQ2hpbGQocHJvZyk7CgogICAgdmFyIGZvb3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgZm9vdC5jbGFzc05hbWUgPSAncGgtY2hhbGxlbmdlLWZvb3QnOwogICAgdmFyIHJld2FyZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgcmV3YXJkLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtcmV3YXJkJzsKICAgIHJld2FyZC50ZXh0Q29udGVudCA9ICfwn46BIFRoxrDhu59uZzogJyArIHBoQ2hhbGxlbmdlUmV3YXJkVGV4dChkZWYucmV3YXJkKTsKICAgIGZvb3QuYXBwZW5kQ2hpbGQocmV3YXJkKTsKCiAgICBpZiAoZG9uZSl7CiAgICAgIHZhciB3aGVuID0gZC5jaGFsbGVuZ2VDbGFpbXMgJiYgZC5jaGFsbGVuZ2VDbGFpbXNbZGVmLmlkXSA/IG5ldyBEYXRlKGQuY2hhbGxlbmdlQ2xhaW1zW2RlZi5pZF0pIDogbnVsbDsKICAgICAgdmFyIHN0YXR1cyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTsgc3RhdHVzLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtY29tcGxldGUtdGltZSc7CiAgICAgIHN0YXR1cy50ZXh0Q29udGVudCA9IHdoZW4gPyAnIMK3IE5o4bqtbiBsw7pjICcgKyB3aGVuLnRvTG9jYWxlVGltZVN0cmluZygndmktVk4nLCB7aG91cjonMi1kaWdpdCcsIG1pbnV0ZTonMi1kaWdpdCd9KSA6ICcnOwogICAgICBmb290LmFwcGVuZENoaWxkKHN0YXR1cyk7CiAgICB9IGVsc2UgewogICAgICB2YXIgcHJvZ3Jlc3NUeHQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IHByb2dyZXNzVHh0LnRleHRDb250ZW50ID0gJyDCtyBUaeG6v24gxJHhu5k6ICcgKyBwZXJjZW50ICsgJyUnOwogICAgICBmb290LmFwcGVuZENoaWxkKHByb2dyZXNzVHh0KTsKICAgIH0KICAgIGl0ZW0uYXBwZW5kQ2hpbGQoZm9vdCk7CgogICAgcmV0dXJuIGl0ZW07CiAgfQoKICBmdW5jdGlvbiBwaFNob3BJdGVtKGljb24sIG5hbWUsIGRlc2MsIGtleSwgY29zdCl7CiAgICB2YXIgZCA9IGxvYWREQigpLCBpbnYgPSBwaFNob3BMb2FkKGQpOwogICAgdmFyIGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgaXRlbS5jbGFzc05hbWUgPSAncGgtc2hvcC1pdGVtJzsKICAgIHZhciBtYWluID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IG1haW4uY2xhc3NOYW1lID0gJ3BoLXNob3AtbWFpbic7CiAgICB2YXIgdGl0bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgdGl0bGUuY2xhc3NOYW1lID0gJ3BoLXNob3AtbmFtZSc7IHRpdGxlLnRleHRDb250ZW50ID0gaWNvbiArICcgJyArIG5hbWU7CiAgICB2YXIgdGV4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyB0ZXh0LmNsYXNzTmFtZSA9ICdwaC1zaG9wLWRlc2MnOyB0ZXh0LnRleHRDb250ZW50ID0gZGVzYzsKICAgIHZhciBzdG9jayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBzdG9jay5jbGFzc05hbWUgPSAncGgtc2hvcC1zdG9jayc7IHN0b2NrLnRleHRDb250ZW50ID0gJ1Ryb25nIGtobzogJyArIE51bWJlcihpbnZba2V5XSB8fCAwKTsKICAgIG1haW4uYXBwZW5kQ2hpbGQodGl0bGUpOyBtYWluLmFwcGVuZENoaWxkKHRleHQpOyBtYWluLmFwcGVuZENoaWxkKHN0b2NrKTsKICAgIHZhciBzaWRlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IHNpZGUuY2xhc3NOYW1lID0gJ3BoLXNob3Atc2lkZSc7CiAgICB2YXIgcHJpY2UgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgcHJpY2UuY2xhc3NOYW1lID0gJ3BoLXNob3AtcHJpY2UnOyBwcmljZS50ZXh0Q29udGVudCA9IE51bWJlcihjb3N0KS50b0xvY2FsZVN0cmluZygndmktVk4nKSArICcg4q2QJzsKICAgIHZhciBidG4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsgYnRuLnR5cGUgPSAnYnV0dG9uJzsgYnRuLmNsYXNzTmFtZSA9ICdwaC1idG4gcGgtc21hbGwgcGgtc2hvcC1idXknOyBidG4udGV4dENvbnRlbnQgPSAnxJDhu5VpJzsKICAgIGJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7IHBoU2hvcEJ1eShrZXksIGNvc3QsIG5hbWUpOyB9KTsKICAgIHNpZGUuYXBwZW5kQ2hpbGQocHJpY2UpOyBzaWRlLmFwcGVuZENoaWxkKGJ0bik7CiAgICBpdGVtLmFwcGVuZENoaWxkKG1haW4pOyBpdGVtLmFwcGVuZENoaWxkKHNpZGUpOwogICAgcmV0dXJuIGl0ZW07CiAgfQoKICBmdW5jdGlvbiByZW5kZXJTaG9wKGQpewogICAgdmFyIHNlYyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBzZWMuY2xhc3NOYW1lID0gJ3BoLXNob3Atc2VjdGlvbic7CiAgICB2YXIgaGVhZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBoZWFkLmNsYXNzTmFtZSA9ICdwaC1zaG9wLWhlYWQnOwogICAgdmFyIHRpdGxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IHRpdGxlLmNsYXNzTmFtZSA9ICdwaC1tb2RhbC1zZWN0aW9uLXRpdGxlJzsgdGl0bGUudGV4dENvbnRlbnQgPSAn8J+OgSBD4butYSBow6BuZyDEkeG7lWkgdGjGsOG7n25nJzsKICAgIHZhciBwdHMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IHB0cy5jbGFzc05hbWUgPSAncGgtc2hvcC1wb2ludHMnOyBwdHMudGV4dENvbnRlbnQgPSBOdW1iZXIoZC5jaGFsbGVuZ2VQb2ludHMgfHwgMCkudG9Mb2NhbGVTdHJpbmcoJ3ZpLVZOJykgKyAnIOKtkCc7CiAgICBoZWFkLmFwcGVuZENoaWxkKHRpdGxlKTsgaGVhZC5hcHBlbmRDaGlsZChwdHMpOyBzZWMuYXBwZW5kQ2hpbGQoaGVhZCk7CiAgICB2YXIgbm90ZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBub3RlLmNsYXNzTmFtZSA9ICdwaC1zaG9wLW5vdGUnOyBub3RlLnRleHRDb250ZW50ID0gJ8SQ4buVaSDirZAgbOG6pXkgduG6rXQgcGjhuqltLiBW4bqtdCBwaOG6qW0gdHJvbmcgS2hvIHPhur0gdOG7sSDEkeG7mW5nIMSRxrDhu6NjIGTDuW5nIGNobyBow6BuaCDEkeG7mW5nIG3hu58gcGFjayB0aeG6v3AgdGhlby4nOyBzZWMuYXBwZW5kQ2hpbGQobm90ZSk7CiAgICB2YXIgZ3JpZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBncmlkLmNsYXNzTmFtZSA9ICdwaC1zaG9wLWdyaWQnOwogICAgZ3JpZC5hcHBlbmRDaGlsZChwaFNob3BJdGVtKCfwn46f77iPJywnTHVja3kgVGlja2V0JywnVMSDbmcgeDMgdHLhu41uZyBz4buRIGPDoWMgdGllciAzKyBjaG8gbOG6p24gbeG7nyBwYWNrIHRp4bq/cCB0aGVvLicsJ2x1Y2t5JywxMDAwKSk7CiAgICBncmlkLmFwcGVuZENoaWxkKHBoU2hvcEl0ZW0oJ/Cfko4nLCdSYXJlIEJvb3N0ZXInLCdUxINuZyB4MiB0cuG7jW5nIHPhu5EgY8OhYyB0aWVyIDMrIGNobyBs4bqnbiBt4bufIHBhY2sgdGnhur9wIHRoZW8uJywncmFyZScsMzAwMCkpOwogICAgZ3JpZC5hcHBlbmRDaGlsZChwaFNob3BJdGVtKCfwn5GRJywnQ3Jvd24gVGlja2V0JywnQ+G7kSBn4bqvbmcgYuG6o28gxJHhuqNtIDEgbMOhIFRpZXIgNCsgdHJvbmcgbOG6p24gbeG7nyBwYWNrIHRp4bq/cCB0aGVvLicsJ2Nyb3duJyw4MDAwKSk7CgogICAgdmFyIGNhcmRJdGVtID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGNhcmRJdGVtLmNsYXNzTmFtZSA9ICdwaC1zaG9wLWl0ZW0nOwogICAgdmFyIGNhcmRNYWluID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGNhcmRNYWluLmNsYXNzTmFtZSA9ICdwaC1zaG9wLW1haW4nOwogICAgdmFyIGNhcmRUaXRsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBjYXJkVGl0bGUuY2xhc3NOYW1lID0gJ3BoLXNob3AtbmFtZSc7IGNhcmRUaXRsZS50ZXh0Q29udGVudCA9ICfwn460IMSQ4buVaSAxIGzDoSBiw6BpJzsKICAgIHZhciBjYXJkRGVzYyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBjYXJkRGVzYy5jbGFzc05hbWUgPSAncGgtc2hvcC1kZXNjJzsgY2FyZERlc2MudGV4dENvbnRlbnQgPSAnxJDhu5VpIOKtkCDEkeG7gyBuaOG6rW4gbmfhuqt1IG5oacOqbiAxIGzDoSB0cm9uZyBi4buZIHRo4bq7IMSRYW5nIGNo4buNbi4gTMOhIG5o4bqtbiDEkcaw4bujYyBz4bq9IHbDoG8gQuG7mSBzxrB1IHThuq1wLic7CiAgICB2YXIgY2FyZFN0b2NrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGNhcmRTdG9jay5jbGFzc05hbWUgPSAncGgtc2hvcC1zdG9jayc7IGNhcmRTdG9jay50ZXh0Q29udGVudCA9ICdOaOG6rW4gdHLhu7FjIHRp4bq/cCDCtyBraMO0bmcgcXVhIEtobyc7CiAgICBjYXJkTWFpbi5hcHBlbmRDaGlsZChjYXJkVGl0bGUpOyBjYXJkTWFpbi5hcHBlbmRDaGlsZChjYXJkRGVzYyk7IGNhcmRNYWluLmFwcGVuZENoaWxkKGNhcmRTdG9jayk7CgogICAgdmFyIGNhcmRTaWRlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGNhcmRTaWRlLmNsYXNzTmFtZSA9ICdwaC1zaG9wLXNpZGUnOwogICAgdmFyIGNhcmRQcmljZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBjYXJkUHJpY2UuY2xhc3NOYW1lID0gJ3BoLXNob3AtcHJpY2UnOyBjYXJkUHJpY2UudGV4dENvbnRlbnQgPSAnMzAwMCDirZAnOwogICAgdmFyIGNhcmRCdG4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTsgY2FyZEJ0bi50eXBlID0gJ2J1dHRvbic7IGNhcmRCdG4uY2xhc3NOYW1lID0gJ3BoLWJ0biBwaC1zbWFsbCBwaC1zaG9wLWJ1eSc7IGNhcmRCdG4udGV4dENvbnRlbnQgPSAnxJDhu5VpJzsKICAgIGNhcmRCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpewogICAgICBjYXJkQnRuLmRpc2FibGVkID0gdHJ1ZTsKICAgICAgdmFyIHJlc3VsdCA9IHBoU2hvcEJ1eUNhcmQoMzAwMCwgJzEgbMOhIGLDoGknKTsKICAgICAgaWYgKHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0LnRoZW4gPT09ICdmdW5jdGlvbicpewogICAgICAgIHJlc3VsdC50aGVuKGZ1bmN0aW9uKCl7IGNhcmRCdG4uZGlzYWJsZWQgPSBmYWxzZTsgfSk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgY2FyZEJ0bi5kaXNhYmxlZCA9IGZhbHNlOwogICAgICB9CiAgICB9KTsKICAgIGNhcmRTaWRlLmFwcGVuZENoaWxkKGNhcmRQcmljZSk7IGNhcmRTaWRlLmFwcGVuZENoaWxkKGNhcmRCdG4pOwogICAgY2FyZEl0ZW0uYXBwZW5kQ2hpbGQoY2FyZE1haW4pOyBjYXJkSXRlbS5hcHBlbmRDaGlsZChjYXJkU2lkZSk7CiAgICBncmlkLmFwcGVuZENoaWxkKGNhcmRJdGVtKTsKICAgIHNlYy5hcHBlbmRDaGlsZChncmlkKTsKICAgIHZhciBpbnZUaXRsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBpbnZUaXRsZS5jbGFzc05hbWUgPSAncGgtc2hvcC1pbnZlbnRvcnktdGl0bGUnOyBpbnZUaXRsZS50ZXh0Q29udGVudCA9ICfwn5OmIEtobyB24bqtdCBwaOG6qW0nOyBzZWMuYXBwZW5kQ2hpbGQoaW52VGl0bGUpOwogICAgdmFyIGludiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBpbnYuY2xhc3NOYW1lID0gJ3BoLXNob3AtaW52ZW50b3J5JzsKICAgIHZhciBpbnZlbnRvcnkgPSBwaFNob3BMb2FkKGQpOwogICAgaW52LmlubmVySFRNTCA9ICc8c3Bhbj7wn46f77iPIEx1Y2t5IFRpY2tldCDDlyAnICsgTnVtYmVyKGludmVudG9yeS5sdWNreXx8MCkgKyAnPC9zcGFuPjxzcGFuPvCfko4gUmFyZSBCb29zdGVyIMOXICcgKyBOdW1iZXIoaW52ZW50b3J5LnJhcmV8fDApICsgJzwvc3Bhbj48c3Bhbj7wn5GRIENyb3duIFRpY2tldCDDlyAnICsgTnVtYmVyKGludmVudG9yeS5jcm93bnx8MCkgKyAnPC9zcGFuPic7CiAgICBzZWMuYXBwZW5kQ2hpbGQoaW52KTsKICAgIHJldHVybiBzZWM7CiAgfQoKICByZW5kZXJDaGFsbGVuZ2VzID0gZnVuY3Rpb24oKXsKICAgIGlmICghZWxzLmNoYWxsZW5nZUJvZHkpIHJldHVybjsKICAgIHZhciBkID0gcGhDaGFsbGVuZ2VTeW5jUmV3YXJkcygpOwogICAgdmFyIHMgPSBwaENoYWxsZW5nZUN5Y2xlU3RhdHMoKTsKICAgIHZhciBhY3RpdmUgPSBwaENoYWxsZW5nZUdldEFjdGl2ZURlZnMoZCk7CiAgICBlbHMuY2hhbGxlbmdlQm9keS50ZXh0Q29udGVudCA9ICcnOwoKICAgIHZhciByZWZyZXNoID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICByZWZyZXNoLmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtcmVmcmVzaCc7CiAgICByZWZyZXNoLmlubmVySFRNTCA9ICc8c3Bhbj7wn5SEIE5oaeG7h20gduG7pSB04buxIGzDoG0gbeG7m2kgbeG7l2kgMzAgcGjDunQ8L3NwYW4+PGIgaWQ9InBoLWNoYWxsZW5nZS1jb3VudGRvd24iPicgKyBwaENoYWxsZW5nZUNvdW50ZG93blRleHQoKSArICc8L2I+JzsKICAgIGVscy5jaGFsbGVuZ2VCb2R5LmFwcGVuZENoaWxkKHJlZnJlc2gpOwoKICAgIHZhciBoZXJvID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IGhlcm8uY2xhc3NOYW1lID0gJ3BoLWNoYWxsZW5nZS1oZXJvJzsKICAgIGhlcm8uYXBwZW5kQ2hpbGQoc3RhdEJveCgnR8OzaSDEkcOjIG3hu58nLCBTdHJpbmcocy5wYWNrcykpKTsKICAgIGhlcm8uYXBwZW5kQ2hpbGQoc3RhdEJveCgnTMOhIMSR4buZYyBuaOG6pXQnLCBTdHJpbmcocy51bmlxdWUpKSk7CiAgICBoZXJvLmFwcGVuZENoaWxkKHN0YXRCb3goJ+KtkCDEkGnhu4NtIHRoxrDhu59uZycsIE51bWJlcihkLmNoYWxsZW5nZVBvaW50cyB8fCAwKS50b0xvY2FsZVN0cmluZygndmktVk4nKSkpOwogICAgZWxzLmNoYWxsZW5nZUJvZHkuYXBwZW5kQ2hpbGQoaGVybyk7CgogICAgdmFyIG5vdGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIG5vdGUuY2xhc3NOYW1lID0gJ3BoLWNoYWxsZW5nZS1jeWNsZS1ub3RlJzsKICAgIG5vdGUudGV4dENvbnRlbnQgPSAnQ2h1IGvhu7MgaGnhu4duIHThuqFpOiAnICsgbmV3IERhdGUoTnVtYmVyKGQuY2hhbGxlbmdlQ3ljbGVTdGFydGVkQXQgfHwgRGF0ZS5ub3coKSkpLnRvTG9jYWxlVGltZVN0cmluZygndmktVk4nLCB7aG91cjonMi1kaWdpdCcsIG1pbnV0ZTonMi1kaWdpdCd9KSArICcg4oaSICcgKyBuZXcgRGF0ZShwaENoYWxsZW5nZUN1cnJlbnRDeWNsZUVuZHNBdCgpKS50b0xvY2FsZVRpbWVTdHJpbmcoJ3ZpLVZOJywge2hvdXI6JzItZGlnaXQnLCBtaW51dGU6JzItZGlnaXQnfSk7CiAgICBlbHMuY2hhbGxlbmdlQm9keS5hcHBlbmRDaGlsZChub3RlKTsKCiAgICB2YXIgbGlzdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBsaXN0LmNsYXNzTmFtZSA9ICdwaC1jaGFsbGVuZ2UtbGlzdCc7CiAgICBhY3RpdmUuZm9yRWFjaChmdW5jdGlvbihkZWYpeyBsaXN0LmFwcGVuZENoaWxkKGNoYWxsZW5nZUNhcmQoZGVmLCBzLCBkKSk7IH0pOwogICAgZWxzLmNoYWxsZW5nZUJvZHkuYXBwZW5kQ2hpbGQobGlzdCk7CgogICAgcGhDaGFsbGVuZ2VTdGFydENvdW50ZG93bigpOwogIH07CgogIGZ1bmN0aW9uIHBoQ2hhbGxlbmdlT3BlbkVuaGFuY2VkKCl7CiAgICByZW5kZXJDaGFsbGVuZ2VzKCk7CiAgICBlbHMuY2hhbGxlbmdlTW9kYWwuaGlkZGVuID0gZmFsc2U7CiAgICBzZXRNb2RhbFZpZXdwb3J0T3Blbih0cnVlKTsKICAgIHBoQ2hhbGxlbmdlU3RhcnRDb3VudGRvd24oKTsKICB9CgogIGZ1bmN0aW9uIHBoQ2hhbGxlbmdlQ2xvc2VFbmhhbmNlZCgpewogICAgZWxzLmNoYWxsZW5nZU1vZGFsLmhpZGRlbiA9IHRydWU7CiAgICBlbHMuY2hhbGxlbmdlQm9keS50ZXh0Q29udGVudCA9ICcnOwogICAgc2V0TW9kYWxWaWV3cG9ydE9wZW4oZmFsc2UpOwogICAgcGhDaGFsbGVuZ2VTdG9wQ291bnRkb3duKCk7CiAgfQoKICBmdW5jdGlvbiByZW5kZXJFeHRlcm5hbFNob3AoKXsKICAgIGlmICghZWxzLnNob3BCb2R5KSByZXR1cm47CiAgICB2YXIgZCA9IHBoQ2hhbGxlbmdlU3luY1Jld2FyZHMoKTsKICAgIGVscy5zaG9wQm9keS50ZXh0Q29udGVudCA9ICcnOwoKICAgIHZhciBiYWxhbmNlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBiYWxhbmNlLmNsYXNzTmFtZSA9ICdwaC1zaG9wLW91dHNpZGUtYmFsYW5jZSc7CiAgICBiYWxhbmNlLmlubmVySFRNTCA9ICc8c3Bhbj7irZAgxJBp4buDbSB0aMaw4bufbmcgaGnhu4duIGPDszwvc3Bhbj48Yj4nICsgTnVtYmVyKGQuY2hhbGxlbmdlUG9pbnRzIHx8IDApLnRvTG9jYWxlU3RyaW5nKCd2aS1WTicpICsgJzwvYj4nOwogICAgZWxzLnNob3BCb2R5LmFwcGVuZENoaWxkKGJhbGFuY2UpOwogICAgZWxzLnNob3BCb2R5LmFwcGVuZENoaWxkKHJlbmRlclNob3AoZCkpOwogIH0KCiAgZnVuY3Rpb24gcGhTaG9wT3BlbkV4dGVybmFsKCl7CiAgICByZW5kZXJFeHRlcm5hbFNob3AoKTsKICAgIGVscy5zaG9wTW9kYWwuaGlkZGVuID0gZmFsc2U7CiAgICBzZXRNb2RhbFZpZXdwb3J0T3Blbih0cnVlKTsKICB9CgogIGZ1bmN0aW9uIHBoU2hvcENsb3NlRXh0ZXJuYWwoKXsKICAgIGVscy5zaG9wTW9kYWwuaGlkZGVuID0gdHJ1ZTsKICAgIGVscy5zaG9wQm9keS50ZXh0Q29udGVudCA9ICcnOwogICAgc2V0TW9kYWxWaWV3cG9ydE9wZW4oZmFsc2UpOwogIH0KCiAgZWxzLmNoYWxsZW5nZUJ0bi5yZW1vdmVFdmVudExpc3RlbmVyKCdjbGljaycsIG9wZW5DaGFsbGVuZ2VNb2RhbCk7CiAgZWxzLmNoYWxsZW5nZUNsb3NlLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgY2xvc2VDaGFsbGVuZ2VNb2RhbCk7CiAgZWxzLmNoYWxsZW5nZUJhY2tkcm9wLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgY2xvc2VDaGFsbGVuZ2VNb2RhbCk7CiAgZWxzLmNoYWxsZW5nZUJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHBoQ2hhbGxlbmdlT3BlbkVuaGFuY2VkKTsKICBlbHMuY2hhbGxlbmdlQ2xvc2UuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBwaENoYWxsZW5nZUNsb3NlRW5oYW5jZWQpOwogIGVscy5jaGFsbGVuZ2VCYWNrZHJvcC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHBoQ2hhbGxlbmdlQ2xvc2VFbmhhbmNlZCk7CgogIGVscy5zaG9wQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcGhTaG9wT3BlbkV4dGVybmFsKTsKICBlbHMuc2hvcENsb3NlLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcGhTaG9wQ2xvc2VFeHRlcm5hbCk7CiAgZWxzLnNob3BCYWNrZHJvcC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHBoU2hvcENsb3NlRXh0ZXJuYWwpOwoKICAvKiBLaOG7n2kgdOG6oW8gY2h1IGvhu7MgbmdheSBraGkgd2lkZ2V0IGNo4bqheTsga2jDtG5nIGzDoG0gdGhheSDEkeG7lWkgZOG7ryBsaeG7h3UgY29sbGVjdGlvbiBjxakuICovCiAgcGhDaGFsbGVuZ2VFbnN1cmVDeWNsZSgpOwogIHBoQ2hhbGxlbmdlU3luY1Jld2FyZHMoKTsKCiAgdXBkYXRlQ2hhbGxlbmdlQmFkZ2UoKTsKICB1cGRhdGVTaG9wQmFkZ2UoKTsKICBwaFNldHVwVHJhaW5lckFuZExlYWRlcmJvYXJkKCk7CiAgZW5zdXJlUGFja1Zpc3VhbCgpOwogIGxvYWRFeHBhbnNpb25zKCk7Cn0pKCk7CgovKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KICAgQURELU9OOiBEYWlseSBNaXNzaW9uICsgNy1EYXkgU3RyZWFrICsgRGFpbHkgQm9udXMgUGFjawogICBBcHBlbmRlZCBhZnRlciB0aGUgb3JpZ2luYWwgY29kZTsgb3JpZ2luYWwgZnVuY3Rpb25zIHJlbWFpbiB1bnRvdWNoZWQuCiAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqLwooZnVuY3Rpb24oKXsKICAndXNlIHN0cmljdCc7CgogIC8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQogICAgIFBva2VIdWIgRGFpbHkgSHViIEFkZC1vbiB2MQogICAgIC0gS2jDtG5nIHRoYXkgxJHhu5VpIGjDoG0gZ+G7kWMuCiAgICAgLSBEw7luZyBsb2NhbFN0b3JhZ2Uga2V5IHJpw6puZzogcG9rZWh1Yi5kYWlseS52MQogICAgIC0gVGhlbyBkw7VpIGhv4bqhdCDEkeG7mW5nIHBhY2sgcXVhIERCICsgRE9NIGhp4buHbiBjw7MuCiAgICAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICovCgogIHZhciBEQUlMWV9LRVkgPSAncG9rZWh1Yi5kYWlseS52MSc7CiAgdmFyIEJBU0VfREJfS0VZID0gJ3Bva2VodWIuY29sbGVjdGlvbi52Mic7CiAgdmFyIGRhaWx5VGltZXIgPSBudWxsOwogIHZhciBhY3Rpdml0eVRpbWVyID0gbnVsbDsKICB2YXIgYWN0aXZpdHlPYnNlcnZlciA9IG51bGw7CiAgdmFyIGRhaWx5QnVzeSA9IGZhbHNlOwoKICB2YXIgTUlTU0lPTl9QT09MID0gewogICAgcGFja3M6IHsKICAgICAgaWQ6J2RhaWx5LXBhY2tzJywKICAgICAgaWNvbjon8J+OtCcsCiAgICAgIG5hbWU6J03hu58gMyBwYWNrJywKICAgICAgZGVzYzonTeG7nyDDrXQgbmjhuqV0IDMgZ8OzaSB0cm9uZyBow7RtIG5heS4nLAogICAgICBnb2FsOjMsCiAgICAgIHJld2FyZDoxMDAsCiAgICAgIHR5cGU6J3BhY2tzJwogICAgfSwKICAgIHJhcmU6IHsKICAgICAgaWQ6J2RhaWx5LXJhcmUnLAogICAgICBpY29uOifirZAnLAogICAgICBuYW1lOidQdWxsIDEgUmFyZSsnLAogICAgICBkZXNjOidSw7p0IMOtdCBuaOG6pXQgMSBsw6EgUmFyZSBob+G6t2MgaGnhur9tIGjGoW4uJywKICAgICAgZ29hbDoxLAogICAgICByZXdhcmQ6MTAwLAogICAgICB0eXBlOidyYXJlJwogICAgfSwKICAgIHZhbHVlOiB7CiAgICAgIGlkOidkYWlseS12YWx1ZScsCiAgICAgIGljb246J/CfkrAnLAogICAgICBuYW1lOidDaOG6oW0gJDEwIGdpw6EgdHLhu4snLAogICAgICBkZXNjOidU4buVbmcgZ2nDoSB0cuG7iyBjYXJkIGjDtG0gbmF5IMSR4bqhdCDDrXQgbmjhuqV0ICQxMC4nLAogICAgICBnb2FsOjEwLAogICAgICByZXdhcmQ6MTI1LAogICAgICB0eXBlOid2YWx1ZScKICAgIH0sCiAgICBsdWNrOiB7CiAgICAgIGlkOidkYWlseS1sdWNrJywKICAgICAgaWNvbjon8J+UpScsCiAgICAgIG5hbWU6J0x1Y2sgU2NvcmUg4omlIDcwJywKICAgICAgZGVzYzonxJDhuqF0IEx1Y2sgU2NvcmUgdOG7qyA3MCB0cuG7nyBsw6puIOG7nyBt4buZdCBsxrDhu6N0IG3hu58uJywKICAgICAgZ29hbDoxLAogICAgICByZXdhcmQ6MTI1LAogICAgICB0eXBlOidsdWNrJwogICAgfSwKICAgIGlyOiB7CiAgICAgIGlkOidkYWlseS1pcicsCiAgICAgIGljb246J/Cfko4nLAogICAgICBuYW1lOidTxINuIElSIC8gU0lSJywKICAgICAgZGVzYzonUsO6dCDDrXQgbmjhuqV0IDEgbMOhIElsbHVzdHJhdGlvbiBSYXJlIC8gU3BlY2lhbCBBcnQuJywKICAgICAgZ29hbDoxLAogICAgICByZXdhcmQ6MTUwLAogICAgICB0eXBlOidpcicKICAgIH0sCiAgICB1bmlxdWU6IHsKICAgICAgaWQ6J2RhaWx5LXVuaXF1ZScsCiAgICAgIGljb246J/Cfk5onLAogICAgICBuYW1lOidUaMOqbSA1IGNhcmQgbeG7m2knLAogICAgICBkZXNjOidC4buVIHN1bmcgNSBsw6EgY2jGsGEgdOG7q25nIGPDsyB2w6BvIELhu5kgc8awdSB04bqtcCBow7RtIG5heS4nLAogICAgICBnb2FsOjUsCiAgICAgIHJld2FyZDoxMjUsCiAgICAgIHR5cGU6J3VuaXF1ZScKICAgIH0KICB9OwoKICB2YXIgUVVBTElUWV9JRFMgPSBbJ3JhcmUnLCdsdWNrJywnaXInXTsKICB2YXIgR1JJTkRfSURTID0gWyd2YWx1ZScsJ3VuaXF1ZSddOwoKICBmdW5jdGlvbiBieUlkKGlkKXsgcmV0dXJuIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKTsgfQoKICBmdW5jdGlvbiByZWFkREIoKXsKICAgIHZhciByYXcgPSBudWxsLCBkID0ge307CiAgICB0cnkgeyByYXcgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmdldEl0ZW0oQkFTRV9EQl9LRVkpOyB9IGNhdGNoKGUpe30KICAgIGlmIChyYXcpewogICAgICB0cnkgeyBkID0gSlNPTi5wYXJzZShyYXcpIHx8IHt9OyB9IGNhdGNoKGUpeyBkID0ge307IH0KICAgIH0KICAgIGlmICghZCB8fCB0eXBlb2YgZCAhPT0gJ29iamVjdCcpIGQgPSB7fTsKICAgIGlmICghZC5jYXJkcyB8fCB0eXBlb2YgZC5jYXJkcyAhPT0gJ29iamVjdCcpIGQuY2FyZHMgPSB7fTsKICAgIGlmICh0eXBlb2YgZC5wYWNrcyAhPT0gJ251bWJlcicpIGQucGFja3MgPSBOdW1iZXIoZC5wYWNrcykgfHwgMDsKICAgIGlmICh0eXBlb2YgZC5zcGVudCAhPT0gJ251bWJlcicpIGQuc3BlbnQgPSBOdW1iZXIoZC5zcGVudCkgfHwgMDsKICAgIGlmICh0eXBlb2YgZC5wYWNrUHJpY2UgIT09ICdudW1iZXInKSBkLnBhY2tQcmljZSA9IE51bWJlcihkLnBhY2tQcmljZSkgfHwgNC40OTsKICAgIHJldHVybiBkOwogIH0KCiAgZnVuY3Rpb24gd3JpdGVEQihkKXsKICAgIHRyeSB7CiAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2Uuc2V0SXRlbShCQVNFX0RCX0tFWSwgSlNPTi5zdHJpbmdpZnkoZCkpOwogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0gY2F0Y2goZSl7IHJldHVybiBmYWxzZTsgfQogIH0KCiAgZnVuY3Rpb24gdW5pcXVlQ291bnQoZCl7CiAgICByZXR1cm4gT2JqZWN0LmtleXMoKGQgJiYgZC5jYXJkcykgfHwge30pLmxlbmd0aDsKICB9CgogIGZ1bmN0aW9uIGRhdGVLZXkoZGF0ZSl7CiAgICB2YXIgZCA9IGRhdGUgfHwgbmV3IERhdGUoKTsKICAgIHZhciB5ID0gZC5nZXRGdWxsWWVhcigpOwogICAgdmFyIG0gPSBTdHJpbmcoZC5nZXRNb250aCgpKzEpLnBhZFN0YXJ0KDIsJzAnKTsKICAgIHZhciBkYXkgPSBTdHJpbmcoZC5nZXREYXRlKCkpLnBhZFN0YXJ0KDIsJzAnKTsKICAgIHJldHVybiB5ICsgJy0nICsgbSArICctJyArIGRheTsKICB9CgogIGZ1bmN0aW9uIHByZXZpb3VzRGF0ZUtleSgpewogICAgdmFyIGQgPSBuZXcgRGF0ZSgpOwogICAgZC5zZXREYXRlKGQuZ2V0RGF0ZSgpIC0gMSk7CiAgICByZXR1cm4gZGF0ZUtleShkKTsKICB9CgogIGZ1bmN0aW9uIHRvbW9ycm93TWlkbmlnaHQoKXsKICAgIHZhciBkID0gbmV3IERhdGUoKTsKICAgIGQuc2V0SG91cnMoMjQsMCwwLDApOwogICAgcmV0dXJuIGQuZ2V0VGltZSgpOwogIH0KCiAgZnVuY3Rpb24gcmVhZERhaWx5KCl7CiAgICB2YXIgcmF3ID0gbnVsbCwgZCA9IHt9OwogICAgdHJ5IHsgcmF3ID0gd2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKERBSUxZX0tFWSk7IH0gY2F0Y2goZSl7fQogICAgaWYgKHJhdyl7CiAgICAgIHRyeSB7IGQgPSBKU09OLnBhcnNlKHJhdykgfHwge307IH0gY2F0Y2goZSl7IGQgPSB7fTsgfQogICAgfQogICAgaWYgKCFkIHx8IHR5cGVvZiBkICE9PSAnb2JqZWN0JykgZCA9IHt9OwogICAgcmV0dXJuIGQ7CiAgfQoKICBmdW5jdGlvbiB3cml0ZURhaWx5KGQpewogICAgdHJ5IHsKICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5zZXRJdGVtKERBSUxZX0tFWSwgSlNPTi5zdHJpbmdpZnkoZCkpOwogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0gY2F0Y2goZSl7IHJldHVybiBmYWxzZTsgfQogIH0KCiAgZnVuY3Rpb24gc2VlZGVkTnVtYmVyKHNlZWQpewogICAgdmFyIGggPSAyMTY2MTM2MjYxOwogICAgZm9yICh2YXIgaT0wOyBpPHNlZWQubGVuZ3RoOyBpKyspewogICAgICBoIF49IHNlZWQuY2hhckNvZGVBdChpKTsKICAgICAgaCArPSAoaCA8PCAxKSArIChoIDw8IDQpICsgKGggPDwgNykgKyAoaCA8PCA4KSArIChoIDw8IDI0KTsKICAgIH0KICAgIHJldHVybiAoaCA+Pj4gMCkgLyA0Mjk0OTY3Mjk2OwogIH0KCiAgZnVuY3Rpb24gcGlja01pc3Npb24oc2VlZCwgcG9vbCwgZXhjbHVkZWQpewogICAgdmFyIGNhbmRpZGF0ZXMgPSBwb29sLmZpbHRlcihmdW5jdGlvbihpZCl7IHJldHVybiBleGNsdWRlZC5pbmRleE9mKGlkKSA9PT0gLTE7IH0pOwogICAgaWYgKCFjYW5kaWRhdGVzLmxlbmd0aCkgcmV0dXJuIG51bGw7CiAgICB2YXIgbiA9IE1hdGguZmxvb3Ioc2VlZGVkTnVtYmVyKHNlZWQgKyAnOicgKyBleGNsdWRlZC5qb2luKCcsJykpICogY2FuZGlkYXRlcy5sZW5ndGgpOwogICAgcmV0dXJuIGNhbmRpZGF0ZXNbTWF0aC5taW4oY2FuZGlkYXRlcy5sZW5ndGggLSAxLG4pXTsKICB9CgogIGZ1bmN0aW9uIGJ1aWxkTWlzc2lvbnMoZGF5KXsKICAgIHZhciBzZWxlY3RlZCA9IFsncGFja3MnXTsKICAgIHZhciBxdWFsaXR5ID0gcGlja01pc3Npb24oZGF5ICsgJzpxdWFsaXR5JywgUVVBTElUWV9JRFMsIHNlbGVjdGVkKTsKICAgIGlmIChxdWFsaXR5KSBzZWxlY3RlZC5wdXNoKHF1YWxpdHkpOwogICAgdmFyIGdyaW5kID0gcGlja01pc3Npb24oZGF5ICsgJzpncmluZCcsIEdSSU5EX0lEUywgc2VsZWN0ZWQpOwogICAgaWYgKGdyaW5kKSBzZWxlY3RlZC5wdXNoKGdyaW5kKTsKCiAgICB2YXIgZmFsbGJhY2sgPSBPYmplY3Qua2V5cyhNSVNTSU9OX1BPT0wpOwogICAgd2hpbGUgKHNlbGVjdGVkLmxlbmd0aCA8IDMpewogICAgICB2YXIgbmV4dCA9IHBpY2tNaXNzaW9uKGRheSArICc6ZmFsbGJhY2s6JyArIHNlbGVjdGVkLmxlbmd0aCwgZmFsbGJhY2ssIHNlbGVjdGVkKTsKICAgICAgaWYgKCFuZXh0KSBicmVhazsKICAgICAgc2VsZWN0ZWQucHVzaChuZXh0KTsKICAgIH0KICAgIHJldHVybiBzZWxlY3RlZC5zbGljZSgwLDMpOwogIH0KCiAgZnVuY3Rpb24gbWlzc2lvbkRlZihpZCl7CiAgICByZXR1cm4gTUlTU0lPTl9QT09MW2lkXSB8fCBudWxsOwogIH0KCiAgZnVuY3Rpb24gZW5zdXJlRGFpbHkoKXsKICAgIHZhciBkID0gcmVhZERhaWx5KCk7CiAgICB2YXIgdG9kYXkgPSBkYXRlS2V5KCk7CiAgICBpZiAoZC5kYXRlICE9PSB0b2RheSl7CiAgICAgIHZhciBiYXNlID0gcmVhZERCKCk7CiAgICAgIHZhciB3YXNZZXN0ZXJkYXkgPSBkLmRhdGUgPT09IHByZXZpb3VzRGF0ZUtleSgpOwogICAgICB2YXIgc3RyZWFrID0gd2FzWWVzdGVyZGF5ID8gTWF0aC5tYXgoMCwgTnVtYmVyKGQuc3RyZWFrKSB8fCAwKSArIDEgOiAxOwoKICAgICAgZCA9IHsKICAgICAgICB2ZXJzaW9uOjEsCiAgICAgICAgZGF0ZTp0b2RheSwKICAgICAgICBzdHJlYWs6c3RyZWFrLAogICAgICAgIGJhc2VQYWNrczpOdW1iZXIoYmFzZS5wYWNrcyl8fDAsCiAgICAgICAgYmFzZVVuaXF1ZTp1bmlxdWVDb3VudChiYXNlKSwKICAgICAgICBkYWlseVBhY2tzOjAsCiAgICAgICAgZGFpbHlSYXJlOjAsCiAgICAgICAgZGFpbHlJUjowLAogICAgICAgIGRhaWx5VmFsdWU6MCwKICAgICAgICBkYWlseUx1Y2tIaXQ6ZmFsc2UsCiAgICAgICAgbWlzc2lvbnM6YnVpbGRNaXNzaW9ucyh0b2RheSksCiAgICAgICAgY2xhaW1zOnt9LAogICAgICAgIGFsbEJvbnVzQ2xhaW1lZDpmYWxzZSwKICAgICAgICBmcmVlUGFja3NDbGFpbWVkOjAsCiAgICAgICAgc3RyZWFrQm9udXNDbGFpbWVkOmZhbHNlLAogICAgICAgIGxhc3RQcm9jZXNzZWRQYWNrczpOdW1iZXIoYmFzZS5wYWNrcyl8fDAKICAgICAgfTsKICAgICAgd3JpdGVEYWlseShkKTsKICAgIH0KCiAgICBpZiAoIUFycmF5LmlzQXJyYXkoZC5taXNzaW9ucykgfHwgZC5taXNzaW9ucy5sZW5ndGggIT09IDMpewogICAgICBkLm1pc3Npb25zID0gYnVpbGRNaXNzaW9ucyh0b2RheSk7CiAgICB9CiAgICBpZiAoIWQuY2xhaW1zIHx8IHR5cGVvZiBkLmNsYWltcyAhPT0gJ29iamVjdCcpIGQuY2xhaW1zID0ge307CiAgICBpZiAodHlwZW9mIGQuZGFpbHlQYWNrcyAhPT0gJ251bWJlcicpIGQuZGFpbHlQYWNrcyA9IE51bWJlcihkLmRhaWx5UGFja3MpfHwwOwogICAgaWYgKHR5cGVvZiBkLmRhaWx5UmFyZSAhPT0gJ251bWJlcicpIGQuZGFpbHlSYXJlID0gTnVtYmVyKGQuZGFpbHlSYXJlKXx8MDsKICAgIGlmICh0eXBlb2YgZC5kYWlseUlSICE9PSAnbnVtYmVyJykgZC5kYWlseUlSID0gTnVtYmVyKGQuZGFpbHlJUil8fDA7CiAgICBpZiAodHlwZW9mIGQuZGFpbHlWYWx1ZSAhPT0gJ251bWJlcicpIGQuZGFpbHlWYWx1ZSA9IE51bWJlcihkLmRhaWx5VmFsdWUpfHwwOwogICAgaWYgKHR5cGVvZiBkLmRhaWx5THVja0hpdCAhPT0gJ2Jvb2xlYW4nKSBkLmRhaWx5THVja0hpdCA9ICEhZC5kYWlseUx1Y2tIaXQ7CiAgICBpZiAodHlwZW9mIGQuZnJlZVBhY2tzQ2xhaW1lZCAhPT0gJ251bWJlcicpIGQuZnJlZVBhY2tzQ2xhaW1lZCA9IE51bWJlcihkLmZyZWVQYWNrc0NsYWltZWQpfHwwOwogICAgaWYgKHR5cGVvZiBkLnN0cmVha0JvbnVzQ2xhaW1lZCAhPT0gJ2Jvb2xlYW4nKSBkLnN0cmVha0JvbnVzQ2xhaW1lZCA9ICEhZC5zdHJlYWtCb251c0NsYWltZWQ7CiAgICBpZiAodHlwZW9mIGQubGFzdFByb2Nlc3NlZFBhY2tzICE9PSAnbnVtYmVyJykgZC5sYXN0UHJvY2Vzc2VkUGFja3MgPSBOdW1iZXIocmVhZERCKCkucGFja3MpfHwwOwogICAgd3JpdGVEYWlseShkKTsKICAgIHJldHVybiBkOwogIH0KCiAgZnVuY3Rpb24gc3RyZWFrUmV3YXJkUGFja3Moc3RyZWFrKXsKICAgIHJldHVybiBOdW1iZXIoc3RyZWFrKSA+IDAgJiYgTnVtYmVyKHN0cmVhaykgJSA3ID09PSAwID8gMiA6IDE7CiAgfQoKICBmdW5jdGlvbiBwcm9ncmVzc0ZvcihkZWYsIGQpewogICAgaWYgKCFkZWYpIHJldHVybiAwOwogICAgaWYgKGRlZi50eXBlID09PSAncGFja3MnKSByZXR1cm4gTnVtYmVyKGQuZGFpbHlQYWNrcyl8fDA7CiAgICBpZiAoZGVmLnR5cGUgPT09ICdyYXJlJykgcmV0dXJuIE51bWJlcihkLmRhaWx5UmFyZSl8fDA7CiAgICBpZiAoZGVmLnR5cGUgPT09ICd2YWx1ZScpIHJldHVybiBOdW1iZXIoZC5kYWlseVZhbHVlKXx8MDsKICAgIGlmIChkZWYudHlwZSA9PT0gJ2x1Y2snKSByZXR1cm4gZC5kYWlseUx1Y2tIaXQgPyAxIDogMDsKICAgIGlmIChkZWYudHlwZSA9PT0gJ2lyJykgcmV0dXJuIE51bWJlcihkLmRhaWx5SVIpfHwwOwogICAgaWYgKGRlZi50eXBlID09PSAndW5pcXVlJyl7CiAgICAgIHZhciBiYXNlID0gTnVtYmVyKGQuYmFzZVVuaXF1ZSl8fDAsIGN1cnJlbnQgPSB1bmlxdWVDb3VudChyZWFkREIoKSk7CiAgICAgIHJldHVybiBNYXRoLm1heCgwLCBjdXJyZW50IC0gYmFzZSk7CiAgICB9CiAgICByZXR1cm4gMDsKICB9CgogIGZ1bmN0aW9uIGZvcm1hdE1pc3Npb25WYWx1ZShkZWYsIHZhbHVlKXsKICAgIGlmICghZGVmKSByZXR1cm4gJzAnOwogICAgaWYgKGRlZi50eXBlID09PSAndmFsdWUnKXsKICAgICAgcmV0dXJuICckJyArIE1hdGgubWluKE51bWJlcih2YWx1ZSl8fDAsIGRlZi5nb2FsKS50b0ZpeGVkKDIpICsgJyAvICQnICsgZGVmLmdvYWw7CiAgICB9CiAgICBpZiAoZGVmLnR5cGUgPT09ICdsdWNrJyl7CiAgICAgIHJldHVybiB2YWx1ZSA+PSAxID8gJzcwKyDinJMnIDogJzAvMSc7CiAgICB9CiAgICByZXR1cm4gTWF0aC5taW4oTnVtYmVyKHZhbHVlKXx8MCwgZGVmLmdvYWwpICsgJy8nICsgZGVmLmdvYWw7CiAgfQoKICBmdW5jdGlvbiBtaXNzaW9uRG9uZShkZWYsIGQpewogICAgcmV0dXJuIHByb2dyZXNzRm9yKGRlZixkKSA+PSBkZWYuZ29hbDsKICB9CgogIGZ1bmN0aW9uIGFkZFBvaW50cyhhbW91bnQpewogICAgdmFyIGQgPSByZWFkREIoKTsKICAgIHZhciBwdHMgPSBNYXRoLm1heCgwLCBOdW1iZXIoZC5jaGFsbGVuZ2VQb2ludHMpfHwwKTsKICAgIGQuY2hhbGxlbmdlUG9pbnRzID0gcHRzICsgTWF0aC5tYXgoMCwgTnVtYmVyKGFtb3VudCl8fDApOwogICAgd3JpdGVEQihkKTsKICB9CgogIGZ1bmN0aW9uIGN1cnJlbnRQb2ludHMoKXsKICAgIHZhciBkID0gcmVhZERCKCk7CiAgICByZXR1cm4gTWF0aC5tYXgoMCwgTnVtYmVyKGQuY2hhbGxlbmdlUG9pbnRzKXx8MCk7CiAgfQoKICBmdW5jdGlvbiBzeW5jU2hvcEJhZGdlKCl7CiAgICB2YXIgYmFkZ2UgPSBieUlkKCdwaC1zaG9wLXBvaW50cy1iYWRnZScpOwogICAgdmFyIGJ0biA9IGJ5SWQoJ3BoLXNob3AtYnRuJyk7CiAgICBpZiAoIWJhZGdlKSByZXR1cm47CiAgICB2YXIgcHRzID0gY3VycmVudFBvaW50cygpOwogICAgYmFkZ2UudGV4dENvbnRlbnQgPSBwdHMudG9Mb2NhbGVTdHJpbmcoJ3ZpLVZOJyk7CiAgICBiYWRnZS5oaWRkZW4gPSBmYWxzZTsKICAgIGlmIChidG4pIGJ0bi50aXRsZSA9ICdC4bqhbiBjw7MgJyArIHB0cy50b0xvY2FsZVN0cmluZygndmktVk4nKSArICcg4q2QIMSRaeG7g20gdGjGsOG7n25nJzsKICB9CgogIGZ1bmN0aW9uIGNsYWltQ29tcGxldGVkTWlzc2lvbnMoKXsKICAgIHZhciBkID0gZW5zdXJlRGFpbHkoKTsKICAgIHZhciBlYXJuZWQgPSAwOwogICAgZC5taXNzaW9ucy5mb3JFYWNoKGZ1bmN0aW9uKGlkKXsKICAgICAgdmFyIGRlZiA9IG1pc3Npb25EZWYoaWQpOwogICAgICBpZiAoIWRlZikgcmV0dXJuOwogICAgICBpZiAobWlzc2lvbkRvbmUoZGVmLGQpICYmICFkLmNsYWltc1tpZF0pewogICAgICAgIGQuY2xhaW1zW2lkXSA9IERhdGUubm93KCk7CiAgICAgICAgZWFybmVkICs9IE51bWJlcihkZWYucmV3YXJkKXx8MDsKICAgICAgfQogICAgfSk7CgogICAgaWYgKGVhcm5lZCA+IDApewogICAgICB2YXIgZGIgPSByZWFkREIoKTsKICAgICAgZGIuY2hhbGxlbmdlUG9pbnRzID0gTWF0aC5tYXgoMCwgTnVtYmVyKGRiLmNoYWxsZW5nZVBvaW50cyl8fDApICsgZWFybmVkOwogICAgICB3cml0ZURCKGRiKTsKICAgIH0KCiAgICB2YXIgYWxsRG9uZSA9IGQubWlzc2lvbnMuZXZlcnkoZnVuY3Rpb24oaWQpewogICAgICB2YXIgZGVmID0gbWlzc2lvbkRlZihpZCk7CiAgICAgIHJldHVybiBkZWYgJiYgbWlzc2lvbkRvbmUoZGVmLGQpOwogICAgfSk7CgogICAgaWYgKGFsbERvbmUgJiYgIWQuYWxsQm9udXNDbGFpbWVkKXsKICAgICAgdmFyIGRiMiA9IHJlYWREQigpOwogICAgICBkYjIuY2hhbGxlbmdlUG9pbnRzID0gTWF0aC5tYXgoMCwgTnVtYmVyKGRiMi5jaGFsbGVuZ2VQb2ludHMpfHwwKSArIDI1MDsKICAgICAgd3JpdGVEQihkYjIpOwogICAgICBkLmFsbEJvbnVzQ2xhaW1lZCA9IHRydWU7CiAgICAgIGVhcm5lZCArPSAyNTA7CiAgICB9CgogICAgaWYgKGVhcm5lZCA+IDApIHdyaXRlRGFpbHkoZCk7CiAgICBzeW5jU2hvcEJhZGdlKCk7CiAgICByZXR1cm4gZWFybmVkOwogIH0KCiAgZnVuY3Rpb24gcmVtb3ZlRnJlZVBhY2tDb3N0KCl7CiAgICB2YXIgZCA9IHJlYWREQigpOwogICAgdmFyIHByaWNlID0gTWF0aC5tYXgoMCwgTnVtYmVyKGQucGFja1ByaWNlKXx8MCk7CiAgICBkLnNwZW50ID0gTWF0aC5tYXgoMCwgTnVtYmVyKGQuc3BlbnQpfHwwKSAtIHByaWNlOwogICAgd3JpdGVEQihkKTsKICB9CgogIGZ1bmN0aW9uIHBhcnNlTW9uZXkodGV4dCl7CiAgICB2YXIgbSA9IFN0cmluZyh0ZXh0IHx8ICcnKS5tYXRjaCgvXCQoWzAtOV0rKD86XC5bMC05XSspPykvKTsKICAgIHJldHVybiBtID8gKHBhcnNlRmxvYXQobVsxXSkgfHwgMCkgOiAwOwogIH0KCiAgZnVuY3Rpb24gcGFyc2VJbnRUZXh0KHRleHQpewogICAgdmFyIG0gPSBTdHJpbmcodGV4dCB8fCAnJykubWF0Y2goLyhcZCspLyk7CiAgICByZXR1cm4gbSA/IHBhcnNlSW50KG1bMV0sMTApIDogMDsKICB9CgogIGZ1bmN0aW9uIGluc3BlY3RTaW5nbGUoKXsKICAgIHZhciByZXZlYWwgPSBieUlkKCdwaC1yZXZlYWwnKTsKICAgIGlmICghcmV2ZWFsIHx8ICFyZXZlYWwuY2xhc3NMaXN0LmNvbnRhaW5zKCdwaC1hY3RpdmUnKSkgcmV0dXJuIG51bGw7CgogICAgdmFyIHNsb3RzID0gcmV2ZWFsLnF1ZXJ5U2VsZWN0b3JBbGwoJy5waC1zbG90W2RhdGEtdGllcl0nKTsKICAgIGlmIChzbG90cy5sZW5ndGggPCA1KSByZXR1cm4gbnVsbDsKCiAgICB2YXIgcmFyZSA9IDAsIGlyID0gMDsKICAgIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoc2xvdHMsIGZ1bmN0aW9uKHNsb3QpewogICAgICB2YXIgdCA9IE51bWJlcihzbG90LmRhdGFzZXQudGllcil8fDA7CiAgICAgIGlmICh0ID49IDIpIHJhcmUrKzsKICAgICAgaWYgKHQgPj0gNSkgaXIrKzsKICAgIH0pOwoKICAgIHZhciBsdWNrTm9kZSA9IGJ5SWQoJ3BoLWx1Y2stc2NvcmUnKTsKICAgIHZhciBsdWNrID0gbHVja05vZGUgPyBwYXJzZUludChsdWNrTm9kZS50ZXh0Q29udGVudCwxMCkgOiAwOwoKICAgIHZhciB0b3RhbE5vZGUgPSBieUlkKCdwaC10b3RhbC12YWwnKTsKICAgIHZhciB2YWx1ZSA9IHRvdGFsTm9kZSAmJiAhYnlJZCgncGgtdG90YWwnKS5oaWRkZW4gPyBwYXJzZU1vbmV5KHRvdGFsTm9kZS50ZXh0Q29udGVudCkgOiAwOwoKICAgIC8qIHNob3dCYW5uZXIgY2jhuqF5IHNhdSByZXZlYWw7IGNo4budIHThu5tpIGtoaSBMdWNrL3RvdGFsIGPDsyBk4buvIGxp4buHdSBsw6Ag4buVbi4gKi8KICAgIGlmICghbHVja05vZGUgfHwgIWx1Y2tOb2RlLnRleHRDb250ZW50IHx8IHNsb3RzLmxlbmd0aCA8IDUpIHJldHVybiBudWxsOwoKICAgIHJldHVybiB7IHBhY2tzOjEsIHJhcmU6cmFyZSwgaXI6aXIsIHZhbHVlOnZhbHVlLCBsdWNrOmx1Y2ssIHJlYWR5OnRydWUgfTsKICB9CgogIGZ1bmN0aW9uIGluc3BlY3RCb3goKXsKICAgIHZhciByZXN1bHQgPSBieUlkKCdwaC1ib3gtcmVzdWx0Jyk7CiAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LmNsYXNzTGlzdC5jb250YWlucygncGgtYWN0aXZlJykpIHJldHVybiBudWxsOwoKICAgIHZhciByb3dzID0gcmVzdWx0LnF1ZXJ5U2VsZWN0b3JBbGwoJy5waC1iYXItcm93Jyk7CiAgICBpZiAoIXJvd3MubGVuZ3RoKSByZXR1cm4gbnVsbDsKCiAgICB2YXIgcmFyZSA9IDAsIGlyID0gMDsKICAgIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwocm93cywgZnVuY3Rpb24ocm93KXsKICAgICAgdmFyIGZpbGwgPSByb3cucXVlcnlTZWxlY3RvcignLnBoLWJhci1mW2RhdGEtdGllcl0nKTsKICAgICAgdmFyIGNvdW50ID0gcm93LnF1ZXJ5U2VsZWN0b3IoJy5waC1iYXItbicpOwogICAgICBpZiAoIWZpbGwgfHwgIWNvdW50KSByZXR1cm47CiAgICAgIHZhciB0aWVyID0gTnVtYmVyKGZpbGwuZGF0YXNldC50aWVyKXx8MDsKICAgICAgdmFyIG4gPSBwYXJzZUludFRleHQoY291bnQudGV4dENvbnRlbnQpOwogICAgICBpZiAodGllciA+PSAyKSByYXJlICs9IG47CiAgICAgIGlmICh0aWVyID49IDUpIGlyICs9IG47CiAgICB9KTsKCiAgICB2YXIgdmFsdWUgPSAwOwogICAgQXJyYXkucHJvdG90eXBlLmZvckVhY2guY2FsbChyZXN1bHQucXVlcnlTZWxlY3RvckFsbCgnLnBoLXN0YXQnKSwgZnVuY3Rpb24oc3RhdCl7CiAgICAgIHZhciBsYWJlbCA9IHN0YXQucXVlcnlTZWxlY3RvcignLnBoLXN0YXQtbCcpOwogICAgICB2YXIgdmFsID0gc3RhdC5xdWVyeVNlbGVjdG9yKCcucGgtc3RhdC12Jyk7CiAgICAgIGlmIChsYWJlbCAmJiB2YWwgJiYgbGFiZWwudGV4dENvbnRlbnQudHJpbSgpID09PSAnR2nDoSB0cuG7iyB0aHUgxJHGsOG7o2MnKXsKICAgICAgICB2YWx1ZSA9IHBhcnNlTW9uZXkodmFsLnRleHRDb250ZW50KTsKICAgICAgfQogICAgfSk7CgogICAgdmFyIHRleHQgPSByZXN1bHQudGV4dENvbnRlbnQgfHwgJyc7CiAgICB2YXIgbG0gPSB0ZXh0Lm1hdGNoKC/EkWnhu4NtIG1heSBt4bqvbiB0cnVuZyBiw6xuaFxzKyhcZCspL2kpOwogICAgdmFyIGx1Y2sgPSBsbSA/IHBhcnNlSW50KGxtWzFdLDEwKSA6IDA7CgogICAgcmV0dXJuIHsgcGFja3M6MCwgcmFyZTpyYXJlLCBpcjppciwgdmFsdWU6dmFsdWUsIGx1Y2s6bHVjaywgcmVhZHk6dHJ1ZSB9OwogIH0KCiAgZnVuY3Rpb24gaW5zcGVjdExhdGVzdFJlc3VsdCgpewogICAgdmFyIGJveCA9IGluc3BlY3RCb3goKTsKICAgIGlmIChib3gpIHJldHVybiBib3g7CiAgICByZXR1cm4gaW5zcGVjdFNpbmdsZSgpOwogIH0KCiAgZnVuY3Rpb24gdXBkYXRlRGFpbHlNZXRyaWNzKGFjdGl2aXR5KXsKICAgIGlmICghYWN0aXZpdHkgfHwgIWFjdGl2aXR5LnJlYWR5KSByZXR1cm47CiAgICB2YXIgZCA9IGVuc3VyZURhaWx5KCk7CiAgICB2YXIgY3VycmVudERCID0gcmVhZERCKCk7CiAgICB2YXIgY3VycmVudFBhY2tzID0gTnVtYmVyKGN1cnJlbnREQi5wYWNrcyl8fDA7CiAgICB2YXIgZGVsdGFQYWNrcyA9IE1hdGgubWF4KDAsIGN1cnJlbnRQYWNrcyAtIChOdW1iZXIoZC5sYXN0UHJvY2Vzc2VkUGFja3MpfHwwKSk7CgogICAgaWYgKCFkZWx0YVBhY2tzKSByZXR1cm4gZmFsc2U7CgogICAgZC5kYWlseVBhY2tzICs9IGRlbHRhUGFja3M7CiAgICBkLmRhaWx5UmFyZSArPSBOdW1iZXIoYWN0aXZpdHkucmFyZSl8fDA7CiAgICBkLmRhaWx5SVIgKz0gTnVtYmVyKGFjdGl2aXR5LmlyKXx8MDsKICAgIGQuZGFpbHlWYWx1ZSArPSBOdW1iZXIoYWN0aXZpdHkudmFsdWUpfHwwOwogICAgaWYgKChOdW1iZXIoYWN0aXZpdHkubHVjayl8fDApID49IDcwKSBkLmRhaWx5THVja0hpdCA9IHRydWU7CiAgICBkLmxhc3RQcm9jZXNzZWRQYWNrcyA9IGN1cnJlbnRQYWNrczsKICAgIHdyaXRlRGFpbHkoZCk7CgogICAgdmFyIGVhcm5lZCA9IGNsYWltQ29tcGxldGVkTWlzc2lvbnMoKTsKICAgIHJlbmRlckRhaWx5KCk7CiAgICBpZiAoZWFybmVkID4gMCkgc2hvd0RhaWx5TWVzc2FnZSgn8J+OiSBOaGnhu4dtIHbhu6UgaG/DoG4gdGjDoG5oISArJyArIGVhcm5lZC50b0xvY2FsZVN0cmluZygndmktVk4nKSArICcg4q2QIMSRaeG7g20gdGjGsOG7n25nJywgdHJ1ZSk7CiAgICByZXR1cm4gdHJ1ZTsKICB9CgogIGZ1bmN0aW9uIHNjaGVkdWxlQWN0aXZpdHlDaGVjaygpewogICAgaWYgKGFjdGl2aXR5VGltZXIpIGNsZWFyVGltZW91dChhY3Rpdml0eVRpbWVyKTsKCiAgICB2YXIgdHJpZXMgPSAwOwogICAgZnVuY3Rpb24gYXR0ZW1wdCgpewogICAgICB0cmllcysrOwogICAgICB2YXIgb2sgPSB1cGRhdGVEYWlseU1ldHJpY3MoaW5zcGVjdExhdGVzdFJlc3VsdCgpKTsKICAgICAgaWYgKCFvayAmJiB0cmllcyA8IDEyKXsKICAgICAgICBhY3Rpdml0eVRpbWVyID0gc2V0VGltZW91dChhdHRlbXB0LCA2NTApOwogICAgICB9CiAgICB9CiAgICBhY3Rpdml0eVRpbWVyID0gc2V0VGltZW91dChhdHRlbXB0LCA5MDApOwogIH0KCiAgZnVuY3Rpb24gb2JzZXJ2ZVBhY2tBY3Rpdml0eSgpewogICAgdmFyIG9wZW5TY3JlZW4gPSBieUlkKCdwaC1zY3JlZW4tb3BlbicpOwogICAgaWYgKCFvcGVuU2NyZWVuIHx8IHR5cGVvZiBNdXRhdGlvbk9ic2VydmVyID09PSAndW5kZWZpbmVkJykgcmV0dXJuOwoKICAgIGFjdGl2aXR5T2JzZXJ2ZXIgPSBuZXcgTXV0YXRpb25PYnNlcnZlcihmdW5jdGlvbigpewogICAgICB2YXIgZGIgPSByZWFkREIoKSwgZCA9IGVuc3VyZURhaWx5KCk7CiAgICAgIGlmICgoTnVtYmVyKGRiLnBhY2tzKXx8MCkgPiAoTnVtYmVyKGQubGFzdFByb2Nlc3NlZFBhY2tzKXx8MCkpewogICAgICAgIHNjaGVkdWxlQWN0aXZpdHlDaGVjaygpOwogICAgICB9CiAgICB9KTsKCiAgICBhY3Rpdml0eU9ic2VydmVyLm9ic2VydmUob3BlblNjcmVlbiwge2NoaWxkTGlzdDp0cnVlLHN1YnRyZWU6dHJ1ZSxhdHRyaWJ1dGVzOnRydWV9KTsKICB9CgogIGZ1bmN0aW9uIGNvdW50ZG93blRleHQoKXsKICAgIHZhciBsZWZ0ID0gTWF0aC5tYXgoMCwgdG9tb3Jyb3dNaWRuaWdodCgpIC0gRGF0ZS5ub3coKSk7CiAgICB2YXIgc2VjID0gTWF0aC5mbG9vcihsZWZ0LzEwMDApOwogICAgdmFyIGggPSBNYXRoLmZsb29yKHNlYy8zNjAwKTsKICAgIHZhciBtID0gTWF0aC5mbG9vcigoc2VjJTM2MDApLzYwKTsKICAgIHZhciBzID0gc2VjJTYwOwogICAgcmV0dXJuIFN0cmluZyhoKS5wYWRTdGFydCgyLCcwJykgKyAnOicgKyBTdHJpbmcobSkucGFkU3RhcnQoMiwnMCcpICsgJzonICsgU3RyaW5nKHMpLnBhZFN0YXJ0KDIsJzAnKTsKICB9CgogIGZ1bmN0aW9uIGRhaWx5QnV0dG9uKCl7CiAgICB2YXIgZXhpc3RpbmcgPSBieUlkKCdwaC1kYWlseS1idG4nKTsKICAgIGlmIChleGlzdGluZyl7CiAgICAgIGV4aXN0aW5nLm9uY2xpY2sgPSBvcGVuRGFpbHk7CiAgICAgIHJldHVybiBleGlzdGluZzsKICAgIH0KCiAgICB2YXIgdG9vbHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjcG9rZWh1Yi1hcHAgLnBoLXRvb2xzJyk7CiAgICBpZiAoIXRvb2xzKSByZXR1cm4gbnVsbDsKCiAgICB2YXIgYnRuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYnV0dG9uJyk7CiAgICBidG4uY2xhc3NOYW1lID0gJ3BoLXRvb2wnOwogICAgYnRuLmlkID0gJ3BoLWRhaWx5LWJ0bic7CiAgICBidG4udHlwZSA9ICdidXR0b24nOwogICAgYnRuLnRpdGxlID0gJ0RhaWx5IE1pc3Npb24sIFN0cmVhayB2w6AgRGFpbHkgQm9udXMgUGFjayc7CiAgICBidG4uaW5uZXJIVE1MID0gJzxzcGFuIGFyaWEtaGlkZGVuPSJ0cnVlIj7wn46vPC9zcGFuPiBEYWlseSA8c3BhbiBjbGFzcz0icGgtZGFpbHktYmFkZ2UiIGlkPSJwaC1kYWlseS1iYWRnZSI+MC8zPC9zcGFuPic7CiAgICB0b29scy5pbnNlcnRCZWZvcmUoYnRuLCBieUlkKCdwaC1tdXRlLWJ0bicpIHx8IG51bGwpOwogICAgYnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgb3BlbkRhaWx5KTsKICAgIHJldHVybiBidG47CiAgfQoKICBmdW5jdGlvbiBjcmVhdGVNb2RhbCgpewogICAgaWYgKGJ5SWQoJ3BoLWRhaWx5LW1vZGFsJykpIHJldHVybjsKCiAgICB2YXIgbW9kYWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIG1vZGFsLmNsYXNzTmFtZSA9ICdwaC1tb2RhbCc7CiAgICBtb2RhbC5pZCA9ICdwaC1kYWlseS1tb2RhbCc7CiAgICBtb2RhbC5oaWRkZW4gPSB0cnVlOwogICAgbW9kYWwuaW5uZXJIVE1MID0KICAgICAgJzxkaXYgY2xhc3M9InBoLW1vZGFsLWJhY2tkcm9wIiBpZD0icGgtZGFpbHktYmFja2Ryb3AiPjwvZGl2PicgKwogICAgICAnPGRpdiBjbGFzcz0icGgtbW9kYWwtY2FyZCBwaC1tb2RhbC13aWRlIiByb2xlPSJkaWFsb2ciIGFyaWEtbW9kYWw9InRydWUiIGFyaWEtbGFiZWw9IkRhaWx5IEh1YiI+JyArCiAgICAgICAgJzxidXR0b24gY2xhc3M9InBoLW1vZGFsLWNsb3NlIiBpZD0icGgtZGFpbHktY2xvc2UiIHR5cGU9ImJ1dHRvbiIgYXJpYS1sYWJlbD0ixJDDs25nIj7inJU8L2J1dHRvbj4nICsKICAgICAgICAnPGRpdiBjbGFzcz0icGgtbW9kYWwtdGl0bGUiIHN0eWxlPSJtYXJnaW4tYm90dG9tOjEycHg7Ij7wn46vIERhaWx5IEh1YjwvZGl2PicgKwogICAgICAgICc8ZGl2IGNsYXNzPSJwaC1tb2RhbC1ib2R5IiBpZD0icGgtZGFpbHktYm9keSI+PC9kaXY+JyArCiAgICAgICc8L2Rpdj4nOwoKICAgIHZhciBhcHAgPSBieUlkKCdwb2tlaHViLWFwcCcpOwogICAgaWYgKGFwcCkgYXBwLmFwcGVuZENoaWxkKG1vZGFsKTsKCiAgICBieUlkKCdwaC1kYWlseS1jbG9zZScpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgY2xvc2VEYWlseSk7CiAgICBieUlkKCdwaC1kYWlseS1iYWNrZHJvcCcpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgY2xvc2VEYWlseSk7CiAgfQoKICBmdW5jdGlvbiBzaG93RGFpbHlNZXNzYWdlKG1zZywgb2spewogICAgdmFyIG5vZGUgPSBieUlkKCdwaC1kYWlseS1hbGVydCcpOwogICAgaWYgKCFub2RlKSByZXR1cm47CiAgICBub2RlLnRleHRDb250ZW50ID0gbXNnOwogICAgbm9kZS5jbGFzc05hbWUgPSAncGgtZGFpbHktYWxlcnQnICsgKG9rID8gJyBwaC1vaycgOiAnJyk7CiAgICBub2RlLmhpZGRlbiA9IGZhbHNlOwogICAgY2xlYXJUaW1lb3V0KG5vZGUuX3RpbWVyKTsKICAgIG5vZGUuX3RpbWVyID0gc2V0VGltZW91dChmdW5jdGlvbigpewogICAgICBub2RlLmhpZGRlbiA9IHRydWU7CiAgICB9LCA0MjAwKTsKICB9CgogIGZ1bmN0aW9uIHJlbmRlckRhaWx5KCl7CiAgICB2YXIgZCA9IGVuc3VyZURhaWx5KCk7CiAgICB2YXIgYm9keSA9IGJ5SWQoJ3BoLWRhaWx5LWJvZHknKTsKICAgIHZhciBiYWRnZSA9IGJ5SWQoJ3BoLWRhaWx5LWJhZGdlJyk7CiAgICBpZiAoIWJvZHkpIHJldHVybjsKCiAgICB2YXIgZG9uZSA9IDA7CiAgICBkLm1pc3Npb25zLmZvckVhY2goZnVuY3Rpb24oaWQpewogICAgICBpZiAobWlzc2lvbkRvbmUobWlzc2lvbkRlZihpZCksIGQpKSBkb25lKys7CiAgICB9KTsKCiAgICBpZiAoYmFkZ2UpewogICAgICBiYWRnZS50ZXh0Q29udGVudCA9IGRvbmUgKyAnLzMnOwogICAgICBiYWRnZS5jbGFzc05hbWUgPSAncGgtZGFpbHktYmFkZ2UnICsgKChkb25lID09PSAzIHx8IGQuc3RyZWFrICUgNyA9PT0gMCkgPyAnIHBoLWRhaWx5LWhvdCcgOiAnJyk7CiAgICB9CgogICAgdmFyIHJld2FyZFBhY2tzID0gc3RyZWFrUmV3YXJkUGFja3MoZC5zdHJlYWspOwogICAgdmFyIGNhbkNsYWltID0gZC5mcmVlUGFja3NDbGFpbWVkIDwgcmV3YXJkUGFja3M7CgogICAgdmFyIGhlcm8gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGhlcm8uY2xhc3NOYW1lID0gJ3BoLWRhaWx5LWhlcm8nOwoKICAgIHZhciBzdHJlYWsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHN0cmVhay5jbGFzc05hbWUgPSAncGgtZGFpbHktY2FyZCc7CiAgICBzdHJlYWsuaW5uZXJIVE1MID0KICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LWtpY2tlciI+U1RSRUFLPC9kaXY+JyArCiAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1zdHJlYWsiPicgKwogICAgICAgICc8ZGl2PicgKwogICAgICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LXN0cmVhay1udW0iPvCflKUgJyArIE51bWJlcihkLnN0cmVha3x8MSkgKyAnPC9kaXY+JyArCiAgICAgICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktc3RyZWFrLWxhYmVsIj5uZ8OgeSBsacOqbiB0aeG6v3A8L2Rpdj4nICsKICAgICAgICAnPC9kaXY+JyArCiAgICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LW5vdGUiPicgKwogICAgICAgICAgKGQuc3RyZWFrICUgNyA9PT0gMCA/ICfwn5SlIERBWSA3IEJPTlVTIScgOiAnTeG7kWMgdGjGsOG7n25nIGvhur8gdGnhur9wOiBEYXkgJyArIChNYXRoLmZsb29yKChOdW1iZXIoZC5zdHJlYWspfHwxKS83KSo3ICsgNykpICsKICAgICAgICAnPC9kaXY+JyArCiAgICAgICc8L2Rpdj4nICsKICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LXN0cmVhay1iYXIiPjxzcGFuIGNsYXNzPSJwaC1kYWlseS1zdHJlYWstZmlsbCIgc3R5bGU9IndpZHRoOicgKwogICAgICAgIChkLnN0cmVhayAlIDcgPT09IDAgPyAxMDAgOiAoKGQuc3RyZWFrICUgNykgLyA3ICogMTAwKSkgKwogICAgICAnJSI+PC9zcGFuPjwvZGl2PicgKwogICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktcmVzZXQiPkRhaWx5IHJlc2V0IHNhdSA8c3BhbiBpZD0icGgtZGFpbHktY291bnRkb3duIj4tLTotLTotLTwvc3Bhbj48L2Rpdj4nOwoKICAgIHZhciBwYWNrID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBwYWNrLmNsYXNzTmFtZSA9ICdwaC1kYWlseS1jYXJkIHBoLWRhaWx5LXBhY2snOwogICAgcGFjay5pbm5lckhUTUwgPQogICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktcGFjay10b3AiPicgKwogICAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1wYWNrLW5hbWUiPvCfjoEgRGFpbHkgQm9udXMgUGFjazwvZGl2PicgKwogICAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1wYWNrLXN0YXR1cyAnICsgKGNhbkNsYWltID8gJycgOiAncGgtY2xhaW1lZCcpICsgJyI+JyArCiAgICAgICAgICAoY2FuQ2xhaW0gPyAoJ1JFQURZIMK3ICcgKyByZXdhcmRQYWNrcyArICcgUEFDSycgKyAocmV3YXJkUGFja3MgPiAxID8gJ1MnIDogJycpKSA6ICdDTEFJTUVEJykgKwogICAgICAgICc8L2Rpdj4nICsKICAgICAgJzwvZGl2PicgKwogICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktcGFjay1kZXNjIj4nICsKICAgICAgICAoZC5zdHJlYWsgJSA3ID09PSAwCiAgICAgICAgICA/ICfwn5SlIDctZGF5IHN0cmVhayEgSMO0bSBuYXkgbmjhuq1uIDxiPjIgRnJlZSBQYWNrczwvYj4gKyBib251cyDEkWnhu4NtLicKICAgICAgICAgIDogJ03hu5dpIG5nw6B5IG5o4bqtbiAxIEZyZWUgUGFjay4gUGFjayDEkcaw4bujYyBt4bufIGLhurFuZyBi4buZIHRo4bq7IGhp4buHbiB04bqhaSB2w6Aga2jDtG5nIHTDrW5oIGNoaSBwaMOtIFJPSS4nKSArCiAgICAgICc8L2Rpdj4nICsKICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LXBhY2stYWN0aW9uIj4nICsKICAgICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktcGFjay1jb3VudGRvd24iPk5leHQgZGFpbHkgcGFjayDCtyA8c3BhbiBpZD0icGgtZGFpbHktcGFjay1jb3VudGRvd24iPi0tOi0tOi0tPC9zcGFuPjwvZGl2PicgKwogICAgICAgICc8YnV0dG9uIGNsYXNzPSJwaC1kYWlseS1wYWNrLWJ0biIgaWQ9InBoLWRhaWx5LXBhY2stYnRuIiB0eXBlPSJidXR0b24iPicgKwogICAgICAgICAgKGNhbkNsYWltID8gKCfwn46BIE3hu58gJyArIHJld2FyZFBhY2tzICsgJyBGcmVlIFBhY2snICsgKHJld2FyZFBhY2tzID4gMSA/ICdzJyA6ICcnKSkgOiAn4pyTIMSQw6Mgbmjhuq1uIGjDtG0gbmF5JykgKwogICAgICAgICc8L2J1dHRvbj4nICsKICAgICAgJzwvZGl2Pic7CgogICAgaGVyby5hcHBlbmRDaGlsZChzdHJlYWspOwogICAgaGVyby5hcHBlbmRDaGlsZChwYWNrKTsKICAgIGJvZHkudGV4dENvbnRlbnQgPSAnJzsKICAgIGJvZHkuYXBwZW5kQ2hpbGQoaGVybyk7CgogICAgdmFyIGFsZXJ0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBhbGVydC5jbGFzc05hbWUgPSAncGgtZGFpbHktYWxlcnQnOwogICAgYWxlcnQuaWQgPSAncGgtZGFpbHktYWxlcnQnOwogICAgYWxlcnQuaGlkZGVuID0gdHJ1ZTsKICAgIGJvZHkuYXBwZW5kQ2hpbGQoYWxlcnQpOwoKICAgIHZhciBtaXNzaW9uc1dyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHZhciBoZWFkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBoZWFkLmNsYXNzTmFtZSA9ICdwaC1kYWlseS1taXNzaW9uLWhlYWQnOwogICAgaGVhZC5pbm5lckhUTUwgPQogICAgICAnPGRpdj4nICsKICAgICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHkta2lja2VyIj5UT0RBWTwvZGl2PicgKwogICAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1taXNzaW9uLXRpdGxlIj7wn46vIDMgRGFpbHkgTWlzc2lvbnM8L2Rpdj4nICsKICAgICAgJzwvZGl2PicgKwogICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktbWlzc2lvbi1tZXRhIj4nICsgZG9uZSArICcvMyBob8OgbiB0aMOgbmg8L2Rpdj4nOwogICAgbWlzc2lvbnNXcmFwLmFwcGVuZENoaWxkKGhlYWQpOwoKICAgIHZhciBsaXN0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICBsaXN0LmNsYXNzTmFtZSA9ICdwaC1kYWlseS1taXNzaW9uLWxpc3QnOwoKICAgIGQubWlzc2lvbnMuZm9yRWFjaChmdW5jdGlvbihpZCl7CiAgICAgIHZhciBkZWYgPSBtaXNzaW9uRGVmKGlkKTsKICAgICAgaWYgKCFkZWYpIHJldHVybjsKCiAgICAgIHZhciB2YWx1ZSA9IHByb2dyZXNzRm9yKGRlZixkKTsKICAgICAgdmFyIGNvbXBsZXRlZCA9IG1pc3Npb25Eb25lKGRlZixkKTsKICAgICAgdmFyIHBjdCA9IGRlZi5nb2FsID4gMCA/IE1hdGgubWluKDEwMCwgTWF0aC5yb3VuZCh2YWx1ZSAvIGRlZi5nb2FsICogMTAwKSkgOiAxMDA7CgogICAgICB2YXIgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICBpdGVtLmNsYXNzTmFtZSA9ICdwaC1kYWlseS1taXNzaW9uJyArIChjb21wbGV0ZWQgPyAnIHBoLWRvbmUnIDogJycpOwogICAgICBpdGVtLmlubmVySFRNTCA9CiAgICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LW1pc3Npb24tdG9wIj4nICsKICAgICAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1taXNzaW9uLW5hbWUiPicgKyBkZWYuaWNvbiArICcgJyArIGRlZi5uYW1lICsgJzwvZGl2PicgKwogICAgICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LW1pc3Npb24tcmV3YXJkIj4rJyArIGRlZi5yZXdhcmQudG9Mb2NhbGVTdHJpbmcoJ3ZpLVZOJykgKyAnIOKtkDwvZGl2PicgKwogICAgICAgICc8L2Rpdj4nICsKICAgICAgICAnPGRpdiBjbGFzcz0icGgtZGFpbHktbWlzc2lvbi1kZXNjIj4nICsgZGVmLmRlc2MgKyAnPC9kaXY+JyArCiAgICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LW1pc3Npb24tcHJvZ3Jlc3MiPicgKwogICAgICAgICAgJzxkaXYgY2xhc3M9InBoLWRhaWx5LW1pc3Npb24tdHJhY2siPjxzcGFuIGNsYXNzPSJwaC1kYWlseS1taXNzaW9uLWZpbGwiIHN0eWxlPSJ3aWR0aDonICsgcGN0ICsgJyUiPjwvc3Bhbj48L2Rpdj4nICsKICAgICAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1taXNzaW9uLXZhbHVlIj4nICsgZm9ybWF0TWlzc2lvblZhbHVlKGRlZix2YWx1ZSkgKyAnPC9kaXY+JyArCiAgICAgICAgJzwvZGl2PicgKwogICAgICAgICc8ZGl2IGNsYXNzPSJwaC1kYWlseS1taXNzaW9uLXN0YXR1cyI+JyArCiAgICAgICAgICAoY29tcGxldGVkID8gJ+KckyBIb8OgbiB0aMOgbmgnIDogJ8SQYW5nIGzDoG0nKSArCiAgICAgICAgICAoZC5jbGFpbXNbaWRdID8gJyDCtyDEkMOjIG5o4bqtbiB0aMaw4bufbmcnIDogJycpICsKICAgICAgICAnPC9kaXY+JzsKCiAgICAgIGxpc3QuYXBwZW5kQ2hpbGQoaXRlbSk7CiAgICB9KTsKCiAgICBtaXNzaW9uc1dyYXAuYXBwZW5kQ2hpbGQobGlzdCk7CiAgICBib2R5LmFwcGVuZENoaWxkKG1pc3Npb25zV3JhcCk7CgogICAgdmFyIGJvdHRvbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgYm90dG9tLmNsYXNzTmFtZSA9ICdwaC1kYWlseS1ib3R0b20nOwoKICAgIHZhciBwb2ludHMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIHBvaW50cy5jbGFzc05hbWUgPSAncGgtZGFpbHktcG9pbnRzJzsKICAgIHBvaW50cy5pbm5lckhUTUwgPSAn4q2QIMSQaeG7g20gaGnhu4duIGPDszogPGI+JyArIGN1cnJlbnRQb2ludHMoKS50b0xvY2FsZVN0cmluZygndmktVk4nKSArICc8L2I+JzsKCiAgICB2YXIgbm90ZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbm90ZS5jbGFzc05hbWUgPSAncGgtZGFpbHktbm90ZSc7CiAgICBub3RlLmlubmVySFRNTCA9CiAgICAgIChkb25lID09PSAzCiAgICAgICAgPyAn8J+OiSBIb8OgbiB0aMOgbmggY+G6oyAzIOKGkiA8Yj4rMjUwIOKtkCBib251czwvYj4uJwogICAgICAgIDogJ0hvw6BuIHRow6BuaCBj4bqjIDMgbmhp4buHbSB24bulIMSR4buDIG5o4bqtbiB0aMOqbSA8Yj4yNTAg4q2QPC9iPi4nKTsKCiAgICBib3R0b20uYXBwZW5kQ2hpbGQocG9pbnRzKTsKICAgIGJvdHRvbS5hcHBlbmRDaGlsZChub3RlKTsKICAgIGJvZHkuYXBwZW5kQ2hpbGQoYm90dG9tKTsKCiAgICB1cGRhdGVDb3VudGRvd25Ob2RlcygpOwoKICAgIHZhciBwYWNrQnRuID0gYnlJZCgncGgtZGFpbHktcGFjay1idG4nKTsKICAgIGlmIChwYWNrQnRuKXsKICAgICAgcGFja0J0bi5kaXNhYmxlZCA9ICFjYW5DbGFpbSB8fCBkYWlseUJ1c3k7CiAgICAgIHBhY2tCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBjbGFpbURhaWx5UGFja3MpOwogICAgfQogIH0KCiAgZnVuY3Rpb24gdXBkYXRlQ291bnRkb3duTm9kZXMoKXsKICAgIHZhciB0ID0gY291bnRkb3duVGV4dCgpOwogICAgdmFyIGEgPSBieUlkKCdwaC1kYWlseS1jb3VudGRvd24nKTsKICAgIHZhciBiID0gYnlJZCgncGgtZGFpbHktcGFjay1jb3VudGRvd24nKTsKICAgIGlmIChhKSBhLnRleHRDb250ZW50ID0gdDsKICAgIGlmIChiKSBiLnRleHRDb250ZW50ID0gdDsKICB9CgogIGZ1bmN0aW9uIHVwZGF0ZURhaWx5VGltZXIoKXsKICAgIHZhciBkID0gZW5zdXJlRGFpbHkoKTsKICAgIHZhciB0b2RheSA9IGRhdGVLZXkoKTsKICAgIGlmIChkLmRhdGUgIT09IHRvZGF5KXsKICAgICAgZW5zdXJlRGFpbHkoKTsKICAgICAgcmVuZGVyRGFpbHkoKTsKICAgIH0KICAgIHVwZGF0ZUNvdW50ZG93bk5vZGVzKCk7CiAgICBkYWlseVRpbWVyID0gc2V0VGltZW91dCh1cGRhdGVEYWlseVRpbWVyLCAxMDAwKTsKICB9CgogIGZ1bmN0aW9uIGNhbk9wZW5DdXJyZW50UGFjaygpewogICAgdmFyIHNjcmVlbiA9IGJ5SWQoJ3BoLXNjcmVlbi1vcGVuJyk7CiAgICB2YXIgcGFjayA9IGJ5SWQoJ3BoLXBhY2snKTsKICAgIGlmICghc2NyZWVuIHx8ICFwYWNrIHx8ICFzY3JlZW4uY2xhc3NMaXN0LmNvbnRhaW5zKCdwaC1hY3RpdmUnKSkgcmV0dXJuIGZhbHNlOwogICAgdmFyIHN0eWxlID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUocGFjayk7CiAgICByZXR1cm4gc3R5bGUuZGlzcGxheSAhPT0gJ25vbmUnICYmIHN0eWxlLnZpc2liaWxpdHkgIT09ICdoaWRkZW4nICYmICFwYWNrLmNsYXNzTGlzdC5jb250YWlucygncGgtYnVzeScpOwogIH0KCiAgZnVuY3Rpb24gc2xlZXAobXMpewogICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUpeyBzZXRUaW1lb3V0KHJlc29sdmUsbXMpOyB9KTsKICB9CgogIGZ1bmN0aW9uIHdhaXRGb3IoZm4sIHRpbWVvdXQsIGludGVydmFsKXsKICAgIHRpbWVvdXQgPSBOdW1iZXIodGltZW91dCl8fDE1MDAwOwogICAgaW50ZXJ2YWwgPSBOdW1iZXIoaW50ZXJ2YWwpfHwyNTA7CiAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSxyZWplY3QpewogICAgICB2YXIgc3RhcnRlZCA9IERhdGUubm93KCk7CiAgICAgIGZ1bmN0aW9uIHRpY2soKXsKICAgICAgICB0cnkgewogICAgICAgICAgaWYgKGZuKCkpIHJldHVybiByZXNvbHZlKHRydWUpOwogICAgICAgIH0gY2F0Y2goZSl7fQogICAgICAgIGlmIChEYXRlLm5vdygpIC0gc3RhcnRlZCA+PSB0aW1lb3V0KSByZXR1cm4gcmVqZWN0KG5ldyBFcnJvcigndGltZW91dCcpKTsKICAgICAgICBzZXRUaW1lb3V0KHRpY2ssIGludGVydmFsKTsKICAgICAgfQogICAgICB0aWNrKCk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIG9wZW5PbmVEYWlseVBhY2soKXsKICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLHJlamVjdCl7CiAgICAgIGlmICghY2FuT3BlbkN1cnJlbnRQYWNrKCkpIHJldHVybiByZWplY3QobmV3IEVycm9yKCduby1wYWNrJykpOwogICAgICB2YXIgYmVmb3JlID0gTnVtYmVyKHJlYWREQigpLnBhY2tzKXx8MDsKICAgICAgdmFyIHBhY2sgPSBieUlkKCdwaC1wYWNrJyk7CiAgICAgIHBhY2suY2xpY2soKTsKCiAgICAgIHdhaXRGb3IoZnVuY3Rpb24oKXsKICAgICAgICByZXR1cm4gKE51bWJlcihyZWFkREIoKS5wYWNrcyl8fDApID4gYmVmb3JlOwogICAgICB9LCAxNjAwMCwgMjUwKS50aGVuKGZ1bmN0aW9uKCl7CiAgICAgICAgLyogxJDhu6NpIHJldmVhbC9zaG93QmFubmVyIGhvw6BuIHThuqV0IHRyxrDhu5tjIGtoaSByZXNldCDEkeG7gyBnw7NpIHRo4bupIDIga2jDtG5nIGNo4buTbmcgYW5pbWF0aW9uLiAqLwogICAgICAgIHJldHVybiBzbGVlcCgzMjAwKTsKICAgICAgfSkudGhlbihmdW5jdGlvbigpewogICAgICAgIHJlbW92ZUZyZWVQYWNrQ29zdCgpOwogICAgICAgIHJldHVybiByZXNvbHZlKHRydWUpOwogICAgICB9KS5jYXRjaChyZWplY3QpOwogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBjbGFpbURhaWx5UGFja3MoKXsKICAgIGlmIChkYWlseUJ1c3kpIHJldHVybjsKICAgIHZhciBkID0gZW5zdXJlRGFpbHkoKTsKICAgIHZhciByZXdhcmRQYWNrcyA9IHN0cmVha1Jld2FyZFBhY2tzKGQuc3RyZWFrKTsKCiAgICBpZiAoZC5mcmVlUGFja3NDbGFpbWVkID49IHJld2FyZFBhY2tzKXsKICAgICAgcmVuZGVyRGFpbHkoKTsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGlmICghY2FuT3BlbkN1cnJlbnRQYWNrKCkpewogICAgICBzaG93RGFpbHlNZXNzYWdlKCfimqDvuI8gSMOjeSBjaOG7jW4gbeG7mXQgYuG7mSB0aOG6uyB0csaw4bubYywgcuG7k2kgbeG7nyBEYWlseSBCb251cyBQYWNrLicsIGZhbHNlKTsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGRhaWx5QnVzeSA9IHRydWU7CiAgICByZW5kZXJEYWlseSgpOwoKICAgIHZhciBvcGVuZWQgPSAwOwogICAgdHJ5IHsKICAgICAgd2hpbGUgKG9wZW5lZCA8IHJld2FyZFBhY2tzKXsKICAgICAgICBpZiAoIWNhbk9wZW5DdXJyZW50UGFjaygpKXsKICAgICAgICAgIC8qIFNhdSBsxrDhu6N0IHRyxrDhu5tjLCBi4bqlbSAiTeG7nyBnw7NpIGtow6FjIiDEkeG7gyB0cuG7nyBs4bqhaSBtw6BuIHBhY2suICovCiAgICAgICAgICB2YXIgYWdhaW4gPSBieUlkKCdwaC1hZ2Fpbi1idG4nKTsKICAgICAgICAgIGlmIChhZ2FpbikgYWdhaW4uY2xpY2soKTsKICAgICAgICAgIGF3YWl0IHdhaXRGb3IoY2FuT3BlbkN1cnJlbnRQYWNrLCA3MDAwLCAyMDApOwogICAgICAgICAgYXdhaXQgc2xlZXAoMjUwKTsKICAgICAgICB9CgogICAgICAgIGF3YWl0IG9wZW5PbmVEYWlseVBhY2soKTsKICAgICAgICBvcGVuZWQrKzsKICAgICAgICBkID0gZW5zdXJlRGFpbHkoKTsKICAgICAgICBkLmZyZWVQYWNrc0NsYWltZWQgPSBvcGVuZWQ7CiAgICAgICAgaWYgKG9wZW5lZCA9PT0gcmV3YXJkUGFja3MgJiYgZC5zdHJlYWsgJSA3ID09PSAwICYmICFkLnN0cmVha0JvbnVzQ2xhaW1lZCl7CiAgICAgICAgICB2YXIgZGIgPSByZWFkREIoKTsKICAgICAgICAgIGRiLmNoYWxsZW5nZVBvaW50cyA9IE1hdGgubWF4KDAsIE51bWJlcihkYi5jaGFsbGVuZ2VQb2ludHMpfHwwKSArIDUwMDsKICAgICAgICAgIHdyaXRlREIoZGIpOwogICAgICAgICAgZC5zdHJlYWtCb251c0NsYWltZWQgPSB0cnVlOwogICAgICAgIH0KICAgICAgICB3cml0ZURhaWx5KGQpOwogICAgICAgIGF3YWl0IHNsZWVwKDUwMCk7CiAgICAgIH0KCiAgICAgIGNsYWltQ29tcGxldGVkTWlzc2lvbnMoKTsKICAgICAgcmVuZGVyRGFpbHkoKTsKICAgICAgc2hvd0RhaWx5TWVzc2FnZSgKICAgICAgICByZXdhcmRQYWNrcyA+IDEKICAgICAgICAgID8gJ/CflKUgRGF5IDcgQm9udXMhIMSQw6MgbeG7nyDEkeG7pyAyIEZyZWUgUGFja3MgdsOgIG5o4bqtbiArNTAwIOKtkC4nCiAgICAgICAgICA6ICfwn46BIERhaWx5IEJvbnVzIFBhY2sgxJHDoyBuaOG6rW4uIEjhurluIGfhurdwIGzhuqFpIG5nw6B5IG1haSEnLAogICAgICAgIHRydWUKICAgICAgKTsKICAgIH0gY2F0Y2goZXJyKXsKICAgICAgZCA9IGVuc3VyZURhaWx5KCk7CiAgICAgIGQuZnJlZVBhY2tzQ2xhaW1lZCA9IG9wZW5lZDsKICAgICAgd3JpdGVEYWlseShkKTsKICAgICAgcmVuZGVyRGFpbHkoKTsKCiAgICAgIGlmIChlcnIgJiYgZXJyLm1lc3NhZ2UgPT09ICduby1wYWNrJyl7CiAgICAgICAgc2hvd0RhaWx5TWVzc2FnZSgn4pqg77iPIEjDo3kgY2jhu41uIG3hu5l0IGLhu5kgdGjhursgdHLGsOG7m2Mga2hpIG5o4bqtbiBEYWlseSBQYWNrLicsIGZhbHNlKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBzaG93RGFpbHlNZXNzYWdlKCfimqDvuI8gRGFpbHkgUGFjayBjaMawYSBob8OgbiB04bqldC4gS2jDtG5nIHTDrW5oIGzGsOG7o3QgY2jGsGEgbeG7nyB0aMOgbmggY8O0bmcuJywgZmFsc2UpOwogICAgICB9CiAgICB9IGZpbmFsbHkgewogICAgICBkYWlseUJ1c3kgPSBmYWxzZTsKICAgICAgcmVuZGVyRGFpbHkoKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIG9wZW5EYWlseSgpewogICAgY3JlYXRlTW9kYWwoKTsKICAgIHZhciBtb2RhbCA9IGJ5SWQoJ3BoLWRhaWx5LW1vZGFsJyk7CiAgICBpZiAoIW1vZGFsKSByZXR1cm47CiAgICBlbnN1cmVEYWlseSgpOwogICAgY2xhaW1Db21wbGV0ZWRNaXNzaW9ucygpOwogICAgcmVuZGVyRGFpbHkoKTsKICAgIG1vZGFsLmhpZGRlbiA9IGZhbHNlOwogICAgdmFyIGNsb3NlID0gYnlJZCgncGgtZGFpbHktY2xvc2UnKTsKICAgIGlmIChjbG9zZSkgc2V0VGltZW91dChmdW5jdGlvbigpeyBjbG9zZS5mb2N1cygpOyB9LCA0MCk7CiAgfQoKICBmdW5jdGlvbiBjbG9zZURhaWx5KCl7CiAgICB2YXIgbW9kYWwgPSBieUlkKCdwaC1kYWlseS1tb2RhbCcpOwogICAgaWYgKG1vZGFsKSBtb2RhbC5oaWRkZW4gPSB0cnVlOwogIH0KCiAgZnVuY3Rpb24gaW5pdCgpewogICAgZW5zdXJlRGFpbHkoKTsKICAgIGRhaWx5QnV0dG9uKCk7CiAgICBjcmVhdGVNb2RhbCgpOwogICAgY2xhaW1Db21wbGV0ZWRNaXNzaW9ucygpOwogICAgcmVuZGVyRGFpbHkoKTsKICAgIG9ic2VydmVQYWNrQWN0aXZpdHkoKTsKCiAgICBpZiAoZGFpbHlUaW1lcikgY2xlYXJUaW1lb3V0KGRhaWx5VGltZXIpOwogICAgdXBkYXRlRGFpbHlUaW1lcigpOwoKICAgIC8qIFRoZW8gZMO1aSBj4bqjIHRyxrDhu51uZyBo4bujcCBET00ga2jDtG5nIG11dGF0aW9uIMSRw7puZyBsw7pjIG5oxrBuZyBEQiDEkcOjIHTEg25nLiAqLwogICAgc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKXsKICAgICAgdmFyIGQgPSBlbnN1cmVEYWlseSgpOwogICAgICB2YXIgYmFzZVBhY2tzID0gTnVtYmVyKGQubGFzdFByb2Nlc3NlZFBhY2tzKXx8MDsKICAgICAgdmFyIGN1cnJlbnRQYWNrcyA9IE51bWJlcihyZWFkREIoKS5wYWNrcyl8fDA7CiAgICAgIGlmIChjdXJyZW50UGFja3MgPiBiYXNlUGFja3MpIHNjaGVkdWxlQWN0aXZpdHlDaGVjaygpOwogICAgfSwgMTAwMCk7CgogICAgLyogTuG6v3UgbmfGsOG7nWkgZMO5bmcgbeG7nyBEYWlseSBzYXUga2hpIG3hu58gcGFjaywgY+G6rXAgbmjhuq10IG5nYXkgVUkuICovCiAgICBzZXRJbnRlcnZhbChmdW5jdGlvbigpewogICAgICBpZiAoYnlJZCgncGgtZGFpbHktbW9kYWwnKSAmJiAhYnlJZCgncGgtZGFpbHktbW9kYWwnKS5oaWRkZW4pIHJlbmRlckRhaWx5KCk7CiAgICAgIHN5bmNTaG9wQmFkZ2UoKTsKICAgIH0sIDI1MDApOwogIH0KCiAgaWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyl7CiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgaW5pdCk7CiAgfSBlbHNlIHsKICAgIGluaXQoKTsKICB9Cgp9KSgpOwo=</script>
<script>eval(decodeURIComponent(escape(atob(document.currentScript.previousElementSibling.textContent))))</script>

<!-- ============================================================
     ADD-ON: SUPABASE GLOBAL LEADERBOARD
     Không sửa code gốc phía trên.
     Bảng Supabase mặc định: public.leaderboard
     Cột dùng:
       uid text/uuid UNIQUE PRIMARY KEY
       name text
       packs integer
       cards integer
       unique_cards integer
       best_tier integer
       best_value numeric
       updated_at timestamptz
     ============================================================ -->

<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<script>
(function(){
  "use strict";

  var SUPABASE_URL = "https://gopkuuxnhopcgzhvxpbi.supabase.co";
  var SUPABASE_PUBLISHABLE_KEY = "sb_publishable_OneQUOJaMHQW3Oz8yOJcdQ_YCBG6GtR";
  var SUPABASE_TABLE = "leaderboard";

  var app = document.getElementById("pokehub-app");
  if (!app) return;

  var els = {
    lbBtn: document.getElementById("ph-leaderboard-btn"),
    lbLive: document.getElementById("ph-leaderboard-live"),
    lbModal: document.getElementById("ph-leaderboard-modal"),
    lbBackdrop: document.getElementById("ph-leaderboard-backdrop"),
    lbClose: document.getElementById("ph-leaderboard-close"),
    lbBody: document.getElementById("ph-leaderboard-body"),
    welcomeModal: document.getElementById("ph-welcome-modal"),
    welcomeInput: document.getElementById("ph-trainer-name")
  };

  var client = null;
  var channel = null;
  var rowsCache = [];
  var lastFingerprint = "";
  var syncing = false;
  var started = false;

  function setStatus(text, online){
    if (!els.lbLive) return;
    els.lbLive.textContent = text;
    els.lbLive.className = "ph-lb-status" + (online ? " ph-lb-online" : "");
    els.lbLive.title = text;
  }

  function trainerName(){
    try {
      return String(localStorage.getItem("pokehub.trainer.name.v1") || "")
        .replace(/[<>]/g, "")
        .replace(/\s+/g, " ")
        .trim()
        .slice(0, 18);
    } catch(e){ return ""; }
  }

  function trainerUid(){
    var key = "pokehub.leaderboard.uid.v1";
    var uid = "";
    try { uid = localStorage.getItem(key) || ""; } catch(e) {}
    if (!uid) {
      if (window.crypto && window.crypto.randomUUID) {
        uid = window.crypto.randomUUID();
      } else {
        uid = "p_" + Math.random().toString(36).slice(2) + "_" + Date.now().toString(36);
      }
      try { localStorage.setItem(key, uid); } catch(e) {}
    }
    return uid;
  }

  function readCollection(){
    var empty = {
      packs:0, cards:0, unique_cards:0,
      total_value:0, spent:0, roi_pct:0,
      sets_collected:0, rare_plus:0, chase_cards:0,
      best_tier:0, best_value:0, best_luck:0
    };

    try {
      var raw = localStorage.getItem("pokehub.collection.v2");
      if (!raw) return empty;

      var db = JSON.parse(raw);
      if (!db || typeof db !== "object") return empty;

      var cards = db.cards && typeof db.cards === "object" ? db.cards : {};
      var keys = Object.keys(cards);
      var totalCards = 0;
      var totalValue = 0;
      var bestTier = 0;
      var bestValue = 0;
      var rarePlus = 0;
      var chaseCards = 0;
      var sets = {};

      keys.forEach(function(id){
        var e = cards[id] || {};
        var n = Number(e.n) || 0;
        var tier = Number(e.tier) || 0;
        var price = Number(e.price);

        totalCards += n;
        bestTier = Math.max(bestTier, tier);

        if (Number.isFinite(price) && price > 0){
          totalValue += price * n;
          bestValue = Math.max(bestValue, price);
        }
        if (tier >= 2) rarePlus += n;
        if (tier >= 4) chaseCards += n;

        var sid = e.setId ? String(e.setId) : "";
        if (sid) sets[sid] = true;
      });

      var packs = Number(db.packs) || 0;
      var spent = Number(db.spent) || 0;
      var roi = spent > 0 ? ((totalValue - spent) / spent) * 100 : 0;

      return {
        packs:packs,
        cards:totalCards,
        unique_cards:keys.length,
        total_value:Number(totalValue.toFixed(2)),
        spent:Number(spent.toFixed(2)),
        roi_pct:Number(roi.toFixed(2)),
        sets_collected:Object.keys(sets).length,
        rare_plus:rarePlus,
        chase_cards:chaseCards,
        best_tier:bestTier,
        best_value:Number(bestValue.toFixed(2)),
        best_luck:Math.max(0, Math.min(100, Number(db.bestLuck) || 0))
      };
    } catch(e){
      return empty;
    }
  }

  function currentRow(){
    var c = readCollection();
    return {
      uid:trainerUid(),
      name:trainerName() || "Trainer",
      packs:c.packs,
      cards:c.cards,
      unique_cards:c.unique_cards,
      total_value:c.total_value,
      spent:c.spent,
      roi_pct:c.roi_pct,
      sets_collected:c.sets_collected,
      rare_plus:c.rare_plus,
      chase_cards:c.chase_cards,
      best_tier:c.best_tier,
      best_value:c.best_value,
      best_luck:c.best_luck,
      updated_at:new Date().toISOString()
    };
  }

  function rowFingerprint(row){
    return [
      row.uid,row.name,row.packs,row.cards,row.unique_cards,
      row.total_value,row.spent,row.roi_pct,row.sets_collected,
      row.rare_plus,row.chase_cards,row.best_tier,row.best_value,
      row.best_luck
    ].join("|");
  }

  function normalizeRow(row){
    row = row || {};

    var bestValue = Number(
      row.best_value != null ? row.best_value :
      (row.bestValue != null ? row.bestValue : row.value)
    ) || 0;

    var totalValue = Number(
      row.total_value != null ? row.total_value :
      (row.totalValue != null ? row.totalValue : 0)
    ) || 0;

    return {
      uid:String(row.uid || row.id || ""),
      name:String(row.name || row.trainer || "Trainer").slice(0,18),
      packs:Number(row.packs)||0,
      cards:Number(row.cards)||0,
      unique:Number(
        row.unique_cards != null ? row.unique_cards :
        (row.unique != null ? row.unique : row.uniqueCards)
      )||0,
      totalValue:totalValue,
      spent:Number(row.spent)||0,
      roiPct:Number(
        row.roi_pct != null ? row.roi_pct :
        (row.roiPct != null ? row.roiPct : 0)
      )||0,
      setsCollected:Number(
        row.sets_collected != null ? row.sets_collected :
        (row.setsCollected != null ? row.setsCollected : row.sets)
      )||0,
      rarePlus:Number(
        row.rare_plus != null ? row.rare_plus :
        (row.rarePlus != null ? row.rarePlus : 0)
      )||0,
      chaseCards:Number(
        row.chase_cards != null ? row.chase_cards :
        (row.chaseCards != null ? row.chaseCards : 0)
      )||0,
      bestTier:Number(
        row.best_tier != null ? row.best_tier :
        (row.bestTier != null ? row.bestTier : row.tier)
      )||0,
      bestValue:bestValue,
      bestLuck:Number(
        row.best_luck != null ? row.best_luck :
        (row.bestLuck != null ? row.bestLuck : 0)
      )||0,
      updatedAt:row.updated_at || row.updatedAt || 0
    };
  }

  function sortRows(rows){
    return rows.map(normalizeRow).filter(function(r){ return !!r.uid; }).sort(function(a,b){
      if (Math.abs(b.totalValue - a.totalValue) > 0.01) return b.totalValue - a.totalValue;
      if (Math.abs(b.roiPct - a.roiPct) > 0.01) return b.roiPct - a.roiPct;
      if (b.unique !== a.unique) return b.unique - a.unique;
      if (b.setsCollected !== a.setsCollected) return b.setsCollected - a.setsCollected;
      if (b.chaseCards !== a.chaseCards) return b.chaseCards - a.chaseCards;
      if (b.rarePlus !== a.rarePlus) return b.rarePlus - a.rarePlus;
      if (b.bestTier !== a.bestTier) return b.bestTier - a.bestTier;
      if (b.bestLuck !== a.bestLuck) return b.bestLuck - a.bestLuck;
      if (Math.abs(b.bestValue - a.bestValue) > 0.01) return b.bestValue - a.bestValue;
      if (a.packs !== b.packs) return a.packs - b.packs;
      return String(a.updatedAt).localeCompare(String(b.updatedAt));
    });
  }

  function tierLabel(tier){
    var labels = {
      0:"Common",1:"Uncommon",2:"Rare",3:"Double Rare",
      4:"Ultra Rare",5:"Special",6:"Crown Rare"
    };
    return labels[tier] || "—";
  }

  function money(value){ return "$" + Number(value || 0).toFixed(2); }

  function signedPct(value){
    var n = Number(value || 0);
    return (n >= 0 ? "+" : "") + n.toFixed(1) + "%";
  }

  function statBox(label, value){
    var box=document.createElement("div");
    box.className="ph-stat";
    var v=document.createElement("div");
    v.className="ph-stat-v";
    v.textContent=value;
    var l=document.createElement("div");
    l.className="ph-stat-l";
    l.textContent=label;
    box.appendChild(v); box.appendChild(l);
    return box;
  }

  async function loadRows(){
    if (!client) return [];
    var result = await client.from(SUPABASE_TABLE).select("*");
    if (result.error) throw result.error;
    rowsCache = sortRows(result.data || []);
    return rowsCache;
  }

  async function syncMe(force){
    if (!client || syncing) return;
    var name=trainerName();
    if (!name) return;

    var row=currentRow();
    var fp=rowFingerprint(row);
    if (!force && fp===lastFingerprint) return;

    syncing=true;
    try{
      var result=await client.from(SUPABASE_TABLE).upsert(row,{onConflict:"uid"});
      if (result.error){
        console.error("[BXH] Không ghi được leaderboard:",result.error);
        setStatus("Lỗi BXH",false);
        return;
      }
      lastFingerprint=fp;
      setStatus("● LIVE",true);
    }catch(err){
      console.error("[BXH] Sync error:",err);
      setStatus("Lỗi BXH",false);
    }finally{
      syncing=false;
    }
  }

  function renderLeaderboard(){
    if (!els.lbBody) return;

    els.lbBody.textContent="";

    var note=document.createElement("div");
    note.className="ph-lb-note";
    note.textContent="&#x1f3c6; BXH theo tổng giá trị bộ sưu tập · " + (client ? "● LIVE" : "đang kết nối…");
    els.lbBody.appendChild(note);

    var rules=document.createElement("div");
    rules.className="ph-lb-rules";
    rules.innerHTML="<b>Ưu tiên:</b> Tổng giá trị → ROI → Unique → Sets → Chase → Rare+ → Best → Luck → Giá card cao nhất. Số gói chỉ dùng khi mọi chỉ số bằng nhau.";
    els.lbBody.appendChild(rules);

    var currentName=trainerName() || "Trainer";
    var rows=rowsCache.slice();
    var myUid=trainerUid(), my=null, myRank="—";

    rows.forEach(function(r,i){
      if (r.uid===myUid){ my=r; myRank=String(i+1); }
    });

    var local=readCollection();

    var hero=document.createElement("div");
    hero.className="ph-lb-hero";
    hero.appendChild(statBox("Hạng hiện tại",myRank));
    hero.appendChild(statBox("Tổng giá trị",money(my ? my.totalValue : local.total_value)));
    hero.appendChild(statBox("ROI",signedPct(my ? my.roiPct : local.roi_pct)));
    hero.appendChild(statBox("Best Luck",String(my ? my.bestLuck : local.best_luck)+"/100"));
    els.lbBody.appendChild(hero);

    var actions=document.createElement("div");
    actions.className="ph-lb-actions";

    var rename=document.createElement("button");
    rename.type="button";
    rename.className="ph-btn ph-small";
    rename.textContent="&#x270f; Đổi tên";
    rename.addEventListener("click",function(){
      if (els.lbModal) els.lbModal.hidden=true;
      if (els.welcomeModal){
        els.welcomeModal.hidden=false;
        if (els.welcomeInput){
          els.welcomeInput.value=currentName;
          setTimeout(function(){
            els.welcomeInput.focus();
            els.welcomeInput.select();
          },40);
        }
      }
    });
    actions.appendChild(rename);
    els.lbBody.appendChild(actions);

    if (!rows.length){
      var empty=document.createElement("div");
      empty.className="ph-col-note";
      empty.textContent="Chưa có trainer nào trên BXH. Mở pack đầu tiên để lên bảng!";
      els.lbBody.appendChild(empty);
      return;
    }

    var tableWrap=document.createElement("div");
    tableWrap.className="ph-lb-table-wrap";

    var table=document.createElement("table");
    table.className="ph-lb-table";

    var thead=document.createElement("thead");
    var trh=document.createElement("tr");

    ["#","Trainer","Tổng giá trị","ROI","Unique","Sets","Rare+","Chase","Best","Luck","Gói"].forEach(function(title){
      var th=document.createElement("th");
      th.textContent=title;
      trh.appendChild(th);
    });

    thead.appendChild(trh);
    table.appendChild(thead);

    var tbody=document.createElement("tbody");

    rows.slice(0,20).forEach(function(r,i){
      var tr=document.createElement("tr");
      if (r.uid===myUid) tr.className="ph-lb-me";

      var tdRank=document.createElement("td");
      tdRank.className="ph-lb-center";
      tdRank.textContent=String(i+1);

      var tdName=document.createElement("td");
      tdName.textContent=r.name+(r.uid===myUid ? " · Bạn" : "");

      var tdValue=document.createElement("td");
      tdValue.className="ph-lb-money";
      tdValue.textContent=money(r.totalValue);

      var tdRoi=document.createElement("td");
      tdRoi.className="ph-lb-roi " + (r.roiPct>=0 ? "ph-lb-roi-pos" : "ph-lb-roi-neg");
      tdRoi.textContent=signedPct(r.roiPct);

      var tdUnique=document.createElement("td");
      tdUnique.className="ph-lb-center";
      tdUnique.textContent=String(r.unique);

      var tdSets=document.createElement("td");
      tdSets.className="ph-lb-center";
      tdSets.textContent=String(r.setsCollected);

      var tdRare=document.createElement("td");
      tdRare.className="ph-lb-center";
      tdRare.textContent=String(r.rarePlus);

      var tdChase=document.createElement("td");
      tdChase.className="ph-lb-center";
      tdChase.textContent=String(r.chaseCards);

      var tdBest=document.createElement("td");
      tdBest.textContent=tierLabel(r.bestTier)+(r.bestValue>0 ? " · "+money(r.bestValue) : "");

      var tdLuck=document.createElement("td");
      tdLuck.className="ph-lb-luck";
      tdLuck.textContent=String(r.bestLuck)+"/100";

      var tdPacks=document.createElement("td");
      tdPacks.className="ph-lb-center";
      tdPacks.textContent=String(r.packs);

      [tdRank,tdName,tdValue,tdRoi,tdUnique,tdSets,tdRare,tdChase,tdBest,tdLuck,tdPacks].forEach(function(td){
        tr.appendChild(td);
      });

      tbody.appendChild(tr);
    });

    table.appendChild(tbody);
    tableWrap.appendChild(table);
    els.lbBody.appendChild(tableWrap);

    var foot=document.createElement("div");
    foot.className="ph-col-note";
    foot.style.marginTop="10px";
    foot.textContent=rows.length>20
      ? "Đang hiện top 20 · Có "+rows.length+" trainer trên BXH."
      : "Realtime · Tổng giá trị và ROI lấy trực tiếp từ bộ sưu tập.";
    els.lbBody.appendChild(foot);
  }

  async function refreshLeaderboard(){
    try{
      await loadRows();
      renderLeaderboard();
      setStatus("● LIVE",true);
    }catch(err){
      console.error("[BXH] Không đọc được leaderboard:",err);
      setStatus("Lỗi BXH",false);
      if (els.lbBody){
        els.lbBody.textContent="Không đọc được bảng xếp hạng. Kiểm tra cấu hình dữ liệu và kết nối.";
      }
    }
  }

  function openLeaderboard(e){
    if (e){
      e.preventDefault();
      e.stopImmediatePropagation();
    }

    if (!trainerName()){
      if (els.welcomeModal) els.welcomeModal.hidden=false;
      if (els.welcomeInput){
        setTimeout(function(){ els.welcomeInput.focus(); },40);
      }
      return;
    }

    if (els.lbModal) els.lbModal.hidden=false;
    refreshLeaderboard();
    syncMe(true);
  }

  async function start(){
    if (started) return;
    started=true;
    setStatus("Đang kết nối…",false);

    try{
      if (!window.supabase || !window.supabase.createClient){
        throw new Error("Không tải được supabase-js");
      }

      client=window.supabase.createClient(SUPABASE_URL,SUPABASE_PUBLISHABLE_KEY);

      await syncMe(true);
      await refreshLeaderboard();

      channel=client
        .channel("ph-leaderboard-realtime")
        .on("postgres_changes",{
          event:"*",
          schema:"public",
          table:SUPABASE_TABLE
        },function(){
          refreshLeaderboard();
        })
        .subscribe(function(status){
          if (status==="SUBSCRIBED"){
            setStatus("● LIVE",true);
          }else if(status==="CHANNEL_ERROR" || status==="TIMED_OUT"){
            setStatus("Mất kết nối",false);
          }
        });

      setInterval(function(){ syncMe(false); },1500);

    }catch(err){
      console.error("[BXH] Init error:",err);
      setStatus("Chưa kết nối",false);
    }
  }

  /* GIỮ NGUYÊN CƠ CHẾ CLICK ĐANG CHẠY TỐT CỦA FILE NÀY */
  if (els.lbBtn){
    els.lbBtn.addEventListener("click",openLeaderboard,true);
  }

  if (els.welcomeInput){
    els.welcomeInput.addEventListener("change",function(){
      setTimeout(function(){ syncMe(true); },300);
    });
  }

  start();

})();
</script>

<!-- vnGame.tv: đếm động số bộ đang thực sự được render -->
<script>
(function(){
  "use strict";
  var countNodes = document.querySelectorAll(".ph-brand-set-count");
  var grid = document.getElementById("ph-exp-grid");
  if (!countNodes.length || !grid) return;

  function updateSetCount(){
    var count = grid.querySelectorAll(":scope > .ph-exp-card").length;
    if (count > 0){
      countNodes.forEach(function(node){
        node.textContent = String(count) + " bộ đang khả dụng";
      });
    }
  }

  updateSetCount();
  if (window.MutationObserver){
    var observer = new MutationObserver(updateSetCount);
    observer.observe(grid, {childList:true});
  }
})();
</script>

<style>
/* ============================================================
   PokeHub REAL BOOSTER PACK — Vietnamese UI
   Visual-only layer. Existing JS/API/pull/collection logic stays intact.
   The pack uses real set artwork already available to the widget:
   30th key visual for M6A, then the selected set's first card artwork,
   with the real set logo layered on top when available.
   ============================================================ */
#pokehub-app .ph-stage{min-height:500px;position:relative;}
#pokehub-app .ph-pack{
  width:214px;height:322px;border-radius:18px;position:relative;overflow:hidden;
  cursor:pointer;isolation:isolate;
  background:
    radial-gradient(130px 180px at 50% 50%,rgba(65,155,255,.16),transparent 70%),
    linear-gradient(145deg,#283650 0%,#0d1424 47%,#0a1020 100%);
  border:1px solid rgba(210,228,255,.58);
  box-shadow:
    0 28px 55px rgba(0,0,0,.58),
    0 0 0 1px rgba(255,214,102,.22),
    0 0 42px rgba(36,145,255,.15);
  transform:translateZ(0);
  animation:ph-real-float 3.2s ease-in-out infinite;
  transition:transform .22s ease,filter .22s ease,box-shadow .22s ease;
}
#pokehub-app .ph-pack:hover{
  transform:translateY(-5px) scale(1.02);
  box-shadow:0 34px 64px rgba(0,0,0,.64),0 0 0 1px rgba(255,214,102,.38),0 0 58px rgba(36,145,255,.22);
}
#pokehub-app .ph-pack::before{
  content:'';position:absolute;left:7px;right:7px;top:8px;height:24px;z-index:8;
  border-radius:9px 9px 7px 7px;
  background:
    repeating-linear-gradient(90deg,rgba(255,255,255,.42) 0 2px,rgba(155,171,193,.18) 2px 5px),
    linear-gradient(180deg,#eef4f9,#9eaaba 44%,#737f90 55%,#dbe4eb);
  border:1px solid rgba(255,255,255,.68);
  box-shadow:inset 0 2px 3px rgba(255,255,255,.58),0 3px 8px rgba(0,0,0,.32);
  pointer-events:none;
}
#pokehub-app .ph-pack::after{
  content:'';position:absolute;inset:0;z-index:7;pointer-events:none;border-radius:inherit;
  background:
    linear-gradient(115deg,transparent 31%,rgba(255,255,255,.18) 43%,transparent 55%),
    repeating-linear-gradient(118deg,rgba(255,255,255,.06) 0 1px,transparent 1px 12px),
    linear-gradient(180deg,transparent 67%,rgba(0,0,0,.36) 100%);
  mix-blend-mode:screen;
}
#pokehub-app .ph-pack-art{
  position:absolute;left:9px;right:9px;top:34px;bottom:45px;z-index:1;
  width:calc(100% - 18px);height:calc(100% - 79px);
  object-fit:cover;object-position:center 38%;display:none;
  border-radius:13px 13px 9px 9px;
  filter:saturate(1.08) contrast(1.02) brightness(.93);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.2),0 5px 18px rgba(0,0,0,.32);
  pointer-events:none;
}
#pokehub-app .ph-pack-art-shade{
  position:absolute;left:9px;right:9px;top:34px;bottom:45px;z-index:2;
  border-radius:13px 13px 9px 9px;pointer-events:none;
  background:
    linear-gradient(180deg,rgba(3,7,18,.06),rgba(3,7,18,.02) 34%,rgba(3,7,18,.64) 100%),
    radial-gradient(90px 90px at 50% 52%,transparent 0,rgba(3,7,18,.13) 74%,rgba(3,7,18,.38) 100%);
}
#pokehub-app .ph-pack-logo{
  position:absolute;left:50%;top:44%;transform:translate(-50%,-50%);z-index:4;
  max-width:82%;max-height:84px;width:auto;height:auto;object-fit:contain;
  filter:drop-shadow(0 5px 10px rgba(0,0,0,.65));display:none;
  pointer-events:none;
}
#pokehub-app .ph-pack-label{
  position:absolute;left:18px;right:18px;bottom:38px;z-index:5;
  display:flex;align-items:center;justify-content:center;min-height:48px;padding:8px 10px;
  border:1px solid rgba(255,255,255,.25);border-radius:11px;
  background:linear-gradient(180deg,rgba(6,11,22,.22),rgba(4,8,17,.84));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.08),0 7px 18px rgba(0,0,0,.24);
  color:#fff;font-family:'Space Grotesk',system-ui,sans-serif;font-weight:900;font-size:16px;
  line-height:1.15;text-align:center;text-shadow:0 2px 7px rgba(0,0,0,.8);
  pointer-events:none;
}
#pokehub-app .ph-pack-series{
  position:absolute;left:14px;right:14px;bottom:10px;z-index:6;
  color:rgba(245,248,255,.84);font-size:8px;font-weight:800;letter-spacing:.18em;
  text-align:center;text-transform:uppercase;text-shadow:0 1px 4px rgba(0,0,0,.8);
  pointer-events:none;
}
#pokehub-app .ph-pack-seal{display:none;}
#pokehub-app .ph-pack-shine{
  position:absolute;inset:-15%;z-index:9;pointer-events:none;opacity:.12;
  background:linear-gradient(115deg,transparent 35%,rgba(255,255,255,.86) 50%,transparent 65%);
  transform:translateX(-70%) rotate(8deg);animation:ph-pack-shine-idle 4.2s ease-in-out infinite;
}
#pokehub-app .ph-pack.ph-pack-is-30th .ph-pack-art{object-position:center 42%;filter:saturate(1.04) contrast(1.04) brightness(.92);}
#pokehub-app .ph-pack.ph-pack-is-30th .ph-pack-label{
  border-color:rgba(255,214,102,.48);
  background:linear-gradient(180deg,rgba(31,24,7,.18),rgba(8,10,16,.86));
}
#pokehub-app .ph-pack.ph-ripping{
  animation:ph-real-rip .62s cubic-bezier(.18,.78,.2,1) forwards;
}
#pokehub-app .ph-pack.ph-ripping .ph-pack-shine{animation:ph-pack-open-shine .5s ease-out forwards;}
@keyframes ph-real-float{0%,100%{transform:translateY(0) rotate(0deg);}50%{transform:translateY(-7px) rotate(.35deg);}}
@keyframes ph-pack-shine-idle{0%,72%,100%{transform:translateX(-70%) rotate(8deg);opacity:0;}80%{transform:translateX(35%) rotate(8deg);opacity:.13;}90%{transform:translateX(115%) rotate(8deg);opacity:0;}}
@keyframes ph-pack-open-shine{0%{transform:translateX(-70%) rotate(8deg);opacity:0;}38%{transform:translateX(-10%) rotate(8deg);opacity:.85;}100%{transform:translateX(120%) rotate(8deg);opacity:0;}}
@keyframes ph-real-rip{
  0%{transform:translateY(0) scale(1) rotate(0deg);filter:brightness(1);}
  18%{transform:translateY(-2px) scale(1.015) rotate(-1deg);filter:brightness(1.08);}
  34%{transform:translateY(1px) scale(1.03) rotate(1.2deg);filter:brightness(1.16);}
  52%{transform:translateY(-3px) scale(1.08) rotate(-1.5deg);filter:brightness(1.45);}
  72%{transform:translateY(-5px) scale(1.15) rotate(1.2deg);filter:brightness(1.85) saturate(1.12);}
  100%{transform:translateY(-10px) scale(1.48) rotate(-3deg);opacity:0;filter:brightness(2.7) saturate(1.18);}
}
#pokehub-app .ph-hint{margin-top:30px;font-size:13px;color:#9fb4d6;}
#pokehub-app .ph-box-row{margin-top:20px;}
#pokehub-app .ph-chip{background:rgba(8,16,31,.84);border-color:rgba(117,164,227,.34);box-shadow:inset 0 1px 0 rgba(255,255,255,.04);}
#pokehub-app .ph-chip-hot{border-color:rgba(255,214,102,.7);box-shadow:0 0 18px rgba(255,214,102,.1);}
@media (max-width:520px){
  #pokehub-app .ph-pack{width:184px;height:276px;}
  #pokehub-app .ph-pack-art{top:31px;bottom:42px;height:calc(100% - 73px);}
  #pokehub-app .ph-pack-art-shade{top:31px;bottom:42px;}
  #pokehub-app .ph-pack-label{left:14px;right:14px;bottom:34px;font-size:13px;min-height:42px;}
  #pokehub-app .ph-pack-series{font-size:7px;bottom:9px;}
}
</style>


<style>
/* ============================================================
   PATCH: REAL BOOSTER COVER — KHÔNG DÙNG HÌNH POKEMON CARD LÀM MẶT PACK
   Giữ nguyên logic/API/click/pull; chỉ thay lớp hình ảnh mặt pack.
   Pack dùng chính TÊN BỘ làm cover, giống thẻ set ở màn hình ngoài.
   ============================================================ */
#pokehub-app .ph-pack-art,
#pokehub-app .ph-pack-art-shade,
#pokehub-app .ph-pack-logo{
  display:none !important;
}

#pokehub-app .ph-pack{
  background:
    radial-gradient(180px 145px at 50% 42%,color-mix(in srgb,var(--ph-c1) 30%,transparent),transparent 72%),
    radial-gradient(150px 130px at 78% 78%,color-mix(in srgb,var(--ph-c2) 22%,transparent),transparent 70%),
    linear-gradient(145deg,#2a3850 0%,#101827 48%,#09101d 100%);
}

#pokehub-app .ph-pack-label{
  left:10px !important;
  right:10px !important;
  top:72px !important;
  bottom:42px !important;
  min-height:0 !important;
  padding:72px 18px 62px !important;
  border:1px solid rgba(255,255,255,.30) !important;
  border-radius:15px !important;
  background:
    linear-gradient(135deg,rgba(17,28,48,.96),rgba(7,13,25,.95)),
    repeating-linear-gradient(118deg,rgba(255,255,255,.05) 0 2px,transparent 2px 13px) !important;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.10),
    inset 0 0 0 1px rgba(76,184,255,.08),
    0 10px 22px rgba(0,0,0,.28) !important;
  color:#fff !important;
  font-size:24px !important;
  font-weight:900 !important;
  letter-spacing:-.02em !important;
  line-height:1.06 !important;
  text-align:center !important;
  text-shadow:0 3px 10px rgba(0,0,0,.88),0 0 18px rgba(91,202,255,.18) !important;
  overflow:hidden !important;
  pointer-events:none !important;
}

#pokehub-app .ph-pack-label::before{
  content:'POKÉMON TRADING CARD GAME' !important;
  position:absolute;
  top:16px;
  left:14px;
  right:14px;
  padding-bottom:10px;
  border-bottom:1px solid rgba(255,255,255,.14);
  color:rgba(244,248,255,.72);
  font-family:system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:8px;
  font-weight:900;
  letter-spacing:.20em;
  line-height:1.2;
  text-align:center;
  white-space:nowrap;
}

#pokehub-app .ph-pack-label::after{
  content:'BOOSTER PACK' !important;
  position:absolute;
  left:14px;
  right:14px;
  bottom:16px;
  padding-top:9px;
  border-top:1px solid rgba(255,255,255,.14);
  color:#ffd866;
  font-family:system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:8px;
  font-weight:900;
  letter-spacing:.22em;
  line-height:1.2;
  text-align:center;
}

#pokehub-app .ph-pack-series{
  bottom:24px !important;
  font-size:7px !important;
  letter-spacing:.16em !important;
  color:rgba(245,248,255,.58) !important;
}

#pokehub-app .ph-pack.ph-pack-is-30th .ph-pack-label{
  background:
    radial-gradient(150px 100px at 50% 16%,rgba(255,215,90,.16),transparent 72%),
    linear-gradient(135deg,rgba(34,29,12,.97),rgba(9,11,17,.96)) !important;
  border-color:rgba(255,214,102,.46) !important;
}

@media (max-width:520px){
  #pokehub-app .ph-pack-label{
    top:58px !important;
    bottom:40px !important;
    padding:62px 14px 55px !important;
    font-size:19px !important;
  }
}
</style>


<!-- ============================================================
     PATCH: dùng ĐÚNG ảnh bìa đã hiển thị ở thẻ bộ bên ngoài
     cho pack mở bên trong. Không thay logic/API/pull.
     ============================================================ -->
<style>
#pokehub-app .ph-pack-cover-clone{
  position:absolute !important;
  inset:0 !important;
  width:100% !important;
  height:100% !important;
  margin:0 !important;
  padding:0 !important;
  clip-path:none !important;
  border-radius:inherit !important;
  overflow:hidden !important;
  z-index:1 !important;
  background:#0a1020 !important;
}
#pokehub-app .ph-pack-cover-clone::before,
#pokehub-app .ph-pack-cover-clone::after{
  display:none !important;
}
#pokehub-app .ph-pack-cover-clone .ph-art-bg{
  position:absolute !important;
  inset:0 !important;
  width:100% !important;
  height:100% !important;
  object-fit:cover !important;
  object-position:center !important;
  display:block !important;
  filter:none !important;
  border:0 !important;
  border-radius:inherit !important;
}
#pokehub-app .ph-pack-cover-clone .ph-art-logo{
  position:absolute !important;
  left:50% !important;
  top:48% !important;
  transform:translate(-50%,-50%) !important;
  z-index:3 !important;
  max-width:84% !important;
  max-height:38% !important;
  width:auto !important;
  height:auto !important;
  object-fit:contain !important;
  display:block !important;
  filter:drop-shadow(0 5px 10px rgba(0,0,0,.48)) !important;
}
#pokehub-app .ph-pack-cover-clone .ph-art-glyph{display:none !important;}
#pokehub-app .ph-pack .ph-pack-art,
#pokehub-app .ph-pack .ph-pack-logo,
#pokehub-app .ph-pack .ph-pack-label,
#pokehub-app .ph-pack .ph-pack-series,
#pokehub-app .ph-pack .ph-pack-art-shade{
  display:none !important;
}
#pokehub-app .ph-pack::after{
  z-index:8 !important;
  opacity:.22 !important;
}
#pokehub-app .ph-pack::before{z-index:9 !important;}
#pokehub-app .ph-pack-shine{z-index:10 !important;}
#pokehub-app .ph-pack.ph-ripping .ph-pack-cover-clone{
  animation:ph-cover-flash .62s cubic-bezier(.18,.78,.2,1) forwards;
}
@keyframes ph-cover-flash{
  0%{filter:brightness(1) saturate(1);transform:scale(1);}
  36%{filter:brightness(1.08) saturate(1.03);transform:scale(1.015) rotate(-.4deg);}
  70%{filter:brightness(1.34) saturate(1.08);transform:scale(1.08) rotate(.8deg);}
  100%{filter:brightness(2.1) saturate(1.12);transform:scale(1.34) rotate(-2deg);opacity:0;}
}
@media (max-width:520px){
  #pokehub-app .ph-pack-cover-clone .ph-art-logo{max-width:86% !important;max-height:34% !important;}
}
</style>
<script>
(function(){
  "use strict";
  var app=document.getElementById("pokehub-app");
  if(!app) return;
  var pack=document.getElementById("ph-pack");
  var label=document.getElementById("ph-pack-label");
  var grid=document.getElementById("ph-exp-grid");
  var screenOpen=document.getElementById("ph-screen-open");
  if(!pack || !label || !grid) return;

  function norm(s){
    return String(s||"")
      .replace(/\s+/g," ")
      .trim()
      .toLowerCase();
  }

  function findCard(){
    var wanted=norm(label.textContent);
    if(!wanted) return null;
    var cards=grid.querySelectorAll(".ph-exp-card");
    var fallback=null;
    for(var i=0;i<cards.length;i++){
      var nameEl=cards[i].querySelector(".ph-name");
      if(!nameEl) continue;
      var name=norm(nameEl.textContent);
      if(name===wanted) return cards[i];
      if(!fallback &#038;&#038; (name.indexOf(wanted)!==-1 || wanted.indexOf(name)!==-1)) fallback=cards[i];
    }
    return fallback;
  }

  function buildClone(){
    var card=findCard();
    if(!card) return false;
    var art=card.querySelector(".ph-art");
    if(!art) return false;

    var old=pack.querySelector(".ph-pack-cover-clone");
    if(old) old.remove();

    var clone=art.cloneNode(true);
    clone.className="ph-art ph-pack-cover-clone";

    // GIỮ NGUYÊN style/background của cover bên ngoài.
    // Bản trước xoá style ở đây nên ảnh bìa bị mất khi clone sang pack.
    var originalStyle=art.getAttribute("style") || "";
    if (originalStyle) clone.setAttribute("style", originalStyle);

    // Fallback chắc chắn: lấy background đã render thực tế của cover ngoài.
    try{
      var cs=window.getComputedStyle(art);
      clone.style.backgroundImage=cs.backgroundImage;
      clone.style.backgroundSize=cs.backgroundSize;
      clone.style.backgroundPosition=cs.backgroundPosition;
      clone.style.backgroundRepeat=cs.backgroundRepeat;
    }catch(e){}

    var bg=clone.querySelector(".ph-art-bg");
    if(bg){
      bg.style.display="block";
      bg.removeAttribute("loading");
    }

    var glyph=clone.querySelector(".ph-art-glyph");
    if(glyph) glyph.remove();

    pack.insertBefore(clone,pack.firstChild);
    pack.dataset.coverSource="outer-set-card";
    return true;
  }

  var lastLabel="";
  function sync(){
    var now=norm(label.textContent);
    if(!now &#038;&#038; lastLabel) return;
    if(now!==lastLabel || !pack.querySelector(".ph-pack-cover-clone")){
      if(buildClone()) lastLabel=now;
    }
  }

  sync();
  if(window.MutationObserver){
    var mo=new MutationObserver(function(){
      setTimeout(sync,0);
    });
    mo.observe(label,{childList:true,characterData:true,subtree:true});
    mo.observe(grid,{childList:true,subtree:true});
    if(screenOpen) mo.observe(screenOpen,{attributes:true,attributeFilter:["class","hidden"]});
  }
  setInterval(sync,800);
})();
</script>

<!-- ============================================================
     FINAL PATCH v7: lấy ĐÚNG COVER ĐANG HIỂN THỊ Ở CARD BÊN NGOÀI
     Không clone DOM nữa. Copy trực tiếp background + logo thực tế.
     Giữ nguyên toàn bộ logic mở pack/API/pull/collection.
     ============================================================ -->
<style>
#pokehub-app .ph-pack-cover-clone{display:none !important;}
#pokehub-app .ph-pack-exact-logo{
  position:absolute !important;
  left:50% !important;
  top:50% !important;
  transform:translate(-50%,-50%) !important;
  z-index:6 !important;
  display:block !important;
  width:auto !important;
  height:auto !important;
  max-width:88% !important;
  max-height:58% !important;
  object-fit:contain !important;
  pointer-events:none !important;
  filter:drop-shadow(0 4px 9px rgba(0,0,0,.55)) !important;
}
#pokehub-app .ph-pack.ph-exact-cover-ready{
  background-repeat:no-repeat !important;
  background-size:cover !important;
  background-position:center center !important;
}
#pokehub-app .ph-pack.ph-ripping .ph-pack-exact-logo{
  animation:ph-exact-cover-logo-rip .62s cubic-bezier(.18,.78,.2,1) forwards;
}
@keyframes ph-exact-cover-logo-rip{
  0%{transform:translate(-50%,-50%) scale(1);opacity:1;filter:drop-shadow(0 4px 9px rgba(0,0,0,.55));}
  36%{transform:translate(-50%,-50%) scale(1.015) rotate(-.4deg);opacity:1;}
  70%{transform:translate(-50%,-50%) scale(1.08) rotate(.8deg);opacity:1;filter:brightness(1.35) saturate(1.08) drop-shadow(0 6px 12px rgba(0,0,0,.6));}
  100%{transform:translate(-50%,-50%) scale(1.28) rotate(-2deg);opacity:0;filter:brightness(2) saturate(1.1) drop-shadow(0 8px 18px rgba(0,0,0,.7));}
}
</style>
<script>
(function(){
  "use strict";
  var app=document.getElementById("pokehub-app");
  if(!app) return;
  var pack=document.getElementById("ph-pack");
  var label=document.getElementById("ph-pack-label");
  var grid=document.getElementById("ph-exp-grid");
  if(!pack || !label || !grid) return;

  function norm(s){
    return String(s||"").replace(/\s+/g," ").trim().toLowerCase();
  }

  function findCard(){
    var wanted=norm(label.textContent);
    if(!wanted) return null;
    var cards=grid.querySelectorAll(".ph-exp-card");
    var fallback=null;
    for(var i=0;i<cards.length;i++){
      var nameEl=cards[i].querySelector(".ph-name");
      if(!nameEl) continue;
      var name=norm(nameEl.textContent);
      if(name===wanted) return cards[i];
      if(!fallback &#038;&#038; (name.indexOf(wanted)!==-1 || wanted.indexOf(name)!==-1)) fallback=cards[i];
    }
    return fallback;
  }

  function ensureLogo(){
    var logo=pack.querySelector(".ph-pack-exact-logo");
    if(!logo){
      logo=document.createElement("img");
      logo.className="ph-pack-exact-logo";
      logo.alt="";
      logo.decoding="async";
      logo.loading="eager";
      logo.setAttribute("aria-hidden","true");
      pack.appendChild(logo);
    }
    return logo;
  }

  function applyOuterCover(){
    var card=findCard();
    if(!card) return false;
    var art=card.querySelector(".ph-art");
    if(!art) return false;

    var cs;
    try{ cs=window.getComputedStyle(art); }catch(e){ cs=null; }
    if(!cs) return false;

    /* Xóa lớp clone cũ gây blank */
    var clones=pack.querySelectorAll(".ph-pack-cover-clone");
    for(var i=0;i<clones.length;i++) clones[i].remove();

    /* Copy CHÍNH background đang render ở card ngoài */
    var bgImage=cs.backgroundImage;
    var bgSize=cs.backgroundSize;
    var bgPos=cs.backgroundPosition;
    var bgRepeat=cs.backgroundRepeat;
    var bgColor=cs.backgroundColor;

    /* Nếu card ngoài có <img> cover mà computed background không có URL,
       dùng luôn src của ảnh đó làm fallback. */
    var bgImg=art.querySelector(".ph-art-bg");
    if((!bgImage || bgImage==="none") && bgImg && bgImg.src){
      bgImage='url("'+bgImg.src.replace(/"/g,'\\"')+'")';
    }

    if(bgImage && bgImage!=="none") pack.style.setProperty("background-image",bgImage,"important");
    else pack.style.setProperty("background-image",cs.backgroundImage||"none","important");
    pack.style.setProperty("background-size",bgSize||"cover","important");
    pack.style.setProperty("background-position",bgPos||"center center","important");
    pack.style.setProperty("background-repeat",bgRepeat||"no-repeat","important");
    if(bgColor) pack.style.setProperty("background-color",bgColor,"important");

    /* Copy đúng logo mà card bên ngoài đang dùng */
    var srcLogo=art.querySelector(".ph-art-logo");
    var logo=ensureLogo();
    if(srcLogo && srcLogo.src){
      logo.src=srcLogo.src;
      logo.style.display="block";
    }else{
      logo.removeAttribute("src");
      logo.style.display="none";
    }

    /* Nếu cover ngoài dùng ảnh 30th làm nền, không chồng logo khác lên */
    var is30=/30th\s*celebration/i.test(label.textContent||"");
    if(is30){
      logo.removeAttribute("src");
      logo.style.display="none";
    }

    /* Giữ accent của card ngoài nếu có */
    var c1=art.style.getPropertyValue("--c1");
    var c2=art.style.getPropertyValue("--c2");
    if(c1) pack.style.setProperty("--c1",c1);
    if(c2) pack.style.setProperty("--c2",c2);

    pack.classList.add("ph-exact-cover-ready");
    pack.dataset.coverSource="exact-outer-card";
    return true;
  }

  var last="";
  function sync(){
    var now=norm(label.textContent);
    if(!now) return;
    if(now!==last || !pack.classList.contains("ph-exact-cover-ready")){
      if(applyOuterCover()) last=now;
    }
  }

  sync();

  if(window.MutationObserver){
    var mo=new MutationObserver(function(){ setTimeout(sync,0); });
    mo.observe(label,{childList:true,characterData:true,subtree:true});
    mo.observe(grid,{childList:true,subtree:true});
  }

  /* Tự đồng bộ nhẹ để chịu được Ceris/WordPress thay DOM sau khi chọn set. */
  setInterval(sync,700);
})();
</script>

<!-- ============================================================
     FINAL COVER PATCH v8 — BẮT TRỰC TIẾP COVER CỦA CARD NGOÀI
     Không dò theo tên, không clone DOM, không đụng logic pull/API.
     Khi người dùng click một set, patch này chụp chính background + logo
     của card đó trước khi code gốc xử lý click, rồi đưa nguyên cover vào pack.
     ============================================================ -->
<style>
#pokehub-app .ph-pack-real-cover{
  position:absolute !important;
  inset:0 !important;
  width:100% !important;
  height:100% !important;
  display:block !important;
  z-index:2 !important;
  border-radius:inherit !important;
  background-color:#09101d !important;
  background-repeat:no-repeat !important;
  background-size:cover !important;
  background-position:center center !important;
  pointer-events:none !important;
  overflow:hidden !important;
}
#pokehub-app .ph-pack-real-logo{
  position:absolute !important;
  left:50% !important;
  top:48% !important;
  transform:translate(-50%,-50%) !important;
  width:auto !important;
  height:auto !important;
  max-width:84% !important;
  max-height:42% !important;
  display:block !important;
  z-index:4 !important;
  object-fit:contain !important;
  pointer-events:none !important;
  filter:drop-shadow(0 5px 10px rgba(0,0,0,.58)) !important;
}
#pokehub-app .ph-pack-real-cover[hidden],
#pokehub-app .ph-pack-real-logo[hidden]{display:none !important;}
#pokehub-app .ph-pack-real-cover.ph-cover-ripping{
  animation:ph-real-cover-rip .62s cubic-bezier(.18,.78,.2,1) forwards;
}
#pokehub-app .ph-pack-real-logo.ph-cover-ripping{
  animation:ph-real-logo-rip .62s cubic-bezier(.18,.78,.2,1) forwards;
}
@keyframes ph-real-cover-rip{
  0%{transform:scale(1) rotate(0deg);filter:brightness(1) saturate(1);opacity:1;}
  35%{transform:scale(1.015) rotate(-.35deg);filter:brightness(1.08) saturate(1.03);opacity:1;}
  70%{transform:scale(1.08) rotate(.8deg);filter:brightness(1.34) saturate(1.08);opacity:1;}
  100%{transform:scale(1.34) rotate(-2deg);filter:brightness(2.1) saturate(1.12);opacity:0;}
}
@keyframes ph-real-logo-rip{
  0%{transform:translate(-50%,-50%) scale(1) rotate(0deg);filter:drop-shadow(0 5px 10px rgba(0,0,0,.58));opacity:1;}
  35%{transform:translate(-50%,-50%) scale(1.015) rotate(-.35deg);opacity:1;}
  70%{transform:translate(-50%,-50%) scale(1.08) rotate(.8deg);filter:brightness(1.35) saturate(1.08) drop-shadow(0 7px 14px rgba(0,0,0,.62));opacity:1;}
  100%{transform:translate(-50%,-50%) scale(1.28) rotate(-2deg);filter:brightness(2) saturate(1.12) drop-shadow(0 9px 18px rgba(0,0,0,.7));opacity:0;}
}
@media (max-width:520px){
  #pokehub-app .ph-pack-real-logo{max-width:88% !important;max-height:36% !important;}
}
</style>
<script>
(function(){
  "use strict";
  var app=document.getElementById("pokehub-app");
  if(!app) return;
  var pack=document.getElementById("ph-pack");
  var grid=document.getElementById("ph-exp-grid");
  var openScreen=document.getElementById("ph-screen-open");
  if(!pack || !grid) return;

  var chosen={bg:"",size:"cover",position:"center center",repeat:"no-repeat",color:"#09101d",logo:"",c1:"",c2:""};

  function ensureCover(){
    var cover=pack.querySelector(".ph-pack-real-cover");
    if(!cover){
      cover=document.createElement("div");
      cover.className="ph-pack-real-cover";
      cover.setAttribute("aria-hidden","true");
      pack.insertBefore(cover,pack.firstChild);
    }
    return cover;
  }

  function ensureLogo(){
    var logo=pack.querySelector(".ph-pack-real-logo");
    if(!logo){
      logo=document.createElement("img");
      logo.className="ph-pack-real-logo";
      logo.alt="";
      logo.decoding="async";
      logo.loading="eager";
      logo.setAttribute("aria-hidden","true");
      pack.appendChild(logo);
    }
    return logo;
  }

  function readCardCover(card){
    if(!card) return false;
    var art=card.querySelector(".ph-art");
    if(!art) return false;

    var cs;
    try{ cs=window.getComputedStyle(art); }catch(e){ cs=null; }
    if(!cs) return false;

    var bg=cs.backgroundImage;
    var bgImg=art.querySelector(".ph-art-bg");
    if((!bg || bg==="none") && bgImg && (bgImg.currentSrc || bgImg.src)){
      var src=bgImg.currentSrc || bgImg.src;
      bg='url("'+String(src).replace(/"/g,'\\"')+'")';
    }

    chosen.bg=bg && bg!=="none" ? bg : "";
    chosen.size=cs.backgroundSize || "cover";
    chosen.position=cs.backgroundPosition || "center center";
    chosen.repeat=cs.backgroundRepeat || "no-repeat";
    chosen.color=cs.backgroundColor || "#09101d";
    chosen.c1=art.style.getPropertyValue("--c1") || "";
    chosen.c2=art.style.getPropertyValue("--c2") || "";

    var srcLogo=art.querySelector(".ph-art-logo");
    chosen.logo=(srcLogo && (srcLogo.currentSrc || srcLogo.src)) ? (srcLogo.currentSrc || srcLogo.src) : "";
    return !!(chosen.bg || chosen.logo);
  }

  function findCardFromEvent(target){
    if(!target) return null;
    var el=target.closest ? target.closest(".ph-exp-card") : null;
    return el && grid.contains(el) ? el : null;
  }

  function captureCard(card){
    if(!readCardCover(card)) return;
    try{ sessionStorage.setItem("pokehub.last.cover.v8",JSON.stringify(chosen)); }catch(e){}
    setTimeout(applyCover,0);
    setTimeout(applyCover,40);
    setTimeout(applyCover,180);
  }

  function applyCover(){
    var cover=ensureCover();
    if(chosen.bg){
      cover.style.backgroundImage=chosen.bg;
      cover.style.backgroundSize=chosen.size;
      cover.style.backgroundPosition=chosen.position;
      cover.style.backgroundRepeat=chosen.repeat;
      cover.style.backgroundColor=chosen.color;
      cover.hidden=false;
    }

    var logo=ensureLogo();
    if(chosen.logo){
      logo.src=chosen.logo;
      logo.hidden=false;
    }else{
      logo.removeAttribute("src");
      logo.hidden=true;
    }

    if(chosen.c1) pack.style.setProperty("--c1",chosen.c1);
    if(chosen.c2) pack.style.setProperty("--c2",chosen.c2);
    pack.classList.add("ph-v8-cover-ready");
  }

  function restoreSaved(){
    try{
      var raw=sessionStorage.getItem("pokehub.last.cover.v8");
      if(raw){
        var x=JSON.parse(raw);
        if(x && typeof x==="object"){
          chosen=Object.assign(chosen,x);
          applyCover();
        }
      }
    }catch(e){}
  }

  /* Capture trước listener gốc: chắc chắn lấy đúng card mà người dùng vừa chọn. */
  grid.addEventListener("click",function(e){
    var card=findCardFromEvent(e.target);
    if(card) captureCard(card);
  },true);

  grid.addEventListener("pointerdown",function(e){
    var card=findCardFromEvent(e.target);
    if(card) captureCard(card);
  },true);

  grid.addEventListener("keydown",function(e){
    if(e.key!=="Enter" && e.key!==" ") return;
    var card=findCardFromEvent(e.target);
    if(card) captureCard(card);
  },true);

  if(window.MutationObserver && openScreen){
    var mo=new MutationObserver(function(){
      if(openScreen.classList.contains("ph-active")) applyCover();
    });
    mo.observe(openScreen,{attributes:true,attributeFilter:["class"]});
  }

  function resetCoverVisual(){
    var c=pack.querySelector(".ph-pack-real-cover");
    var l=pack.querySelector(".ph-pack-real-logo");

    if(c){
      c.classList.remove("ph-cover-ripping");
      c.hidden=false;
      c.style.opacity="1";
      c.style.transform="none";
      c.style.filter="none";
      c.style.animation="none";
      void c.offsetWidth;
      c.style.animation="";
    }

    if(l){
      l.classList.remove("ph-cover-ripping");
      l.hidden = !chosen.logo;
      l.style.opacity = chosen.logo ? "1" : "";
      l.style.transform="translate(-50%,-50%)";
      l.style.filter="drop-shadow(0 5px 10px rgba(0,0,0,.58))";
      l.style.animation="none";
      void l.offsetWidth;
      l.style.animation="";
    }
  }

  var lastRipping=false;
  var lastOpenActive=false;
  setInterval(function(){
    if(!chosen.bg && !chosen.logo) return;

    var active=!!(openScreen && openScreen.classList.contains("ph-active"));
    var ripping=pack.classList.contains("ph-ripping");

    /* Khi quay lại màn hình pack hoặc bắt đầu lượt mở mới, luôn phục hồi cover
       từ trạng thái đã chọn; không để animation lần trước giữ opacity:0. */
    if(active && !lastOpenActive && !ripping){
      resetCoverVisual();
      applyCover();
    }

    if(active && !ripping){
      resetCoverVisual();
      applyCover();
    }

    if(ripping && !lastRipping){
      var c=pack.querySelector(".ph-pack-real-cover");
      var l=pack.querySelector(".ph-pack-real-logo");
      resetCoverVisual();
      if(c){ c.classList.remove("ph-cover-ripping"); void c.offsetWidth; c.classList.add("ph-cover-ripping"); }
      if(l && !l.hidden){ l.classList.remove("ph-cover-ripping"); void l.offsetWidth; l.classList.add("ph-cover-ripping"); }
    }

    /* Sau khi rip kết thúc, chuẩn bị sẵn cover cho lượt mở tiếp theo. */
    if(lastRipping && !ripping){
      resetCoverVisual();
      if(active) applyCover();
    }

    lastRipping=ripping;
    lastOpenActive=active;
  },220);

  restoreSaved();
})();
</script>



<style>
/* ============================================================
   ĐÔNG SƠN — TRỐNG ĐỒNG CHIM LẠC BACKDROP — VISUAL ONLY
   Chỉ thay hiệu ứng nền phía sau pack; không đụng gameplay.
   ============================================================ */
#pokehub-app .ph-stage{
  isolation:isolate;
  position:relative;
}

/* Mặt trống đồng tối: 3 vòng thưa + mặt trời trung tâm + chim Lạc */
#pokehub-app .ph-stage::before{
  content:"";
  position:absolute;
  left:50%;
  top:50%;
  width:min(520px,84vw);
  height:min(520px,84vw);
  transform:translate(-50%,-50%);
  border-radius:50%;
  z-index:0;
  pointer-events:none;
  background:
    /* tâm mặt trời Đông Sơn */
    radial-gradient(circle at 50% 50%,
      rgba(11,19,28,.98) 0 7.5%,
      rgba(171,126,40,.28) 7.8% 8.5%,
      transparent 8.9% 12%),
    /* 3 vòng thưa */
    radial-gradient(circle at 50% 50%,
      transparent 0 22%,
      rgba(210,171,67,.30) 22.25% 22.7%,
      transparent 23.1% 43%),
    radial-gradient(circle at 50% 50%,
      transparent 0 44%,
      rgba(210,171,67,.22) 44.25% 44.65%,
      transparent 45.05% 64%),
    radial-gradient(circle at 50% 50%,
      transparent 0 65%,
      rgba(210,171,67,.18) 65.25% 65.65%,
      transparent 66.05% 100%),
    /* tia mặt trời rất thưa */
    repeating-conic-gradient(from 0deg at 50% 50%,
      rgba(225,190,83,.12) 0deg 3deg,
      transparent 3deg 30deg),
    /* vòng chim Lạc cách xa tâm */
    radial-gradient(circle at 50% 50%,
      transparent 0 73%,
      rgba(225,190,83,.10) 73.25% 73.55%,
      transparent 73.9% 100%),
    /* nền đồng xanh đen */
    radial-gradient(circle at 50% 44%,
      rgba(91,76,35,.14),
      rgba(19,29,38,.18) 36%,
      rgba(4,9,15,.86) 73%,
      rgba(2,5,10,.98) 100%);
  border:1px solid rgba(210,171,67,.16);
  box-shadow:
    0 0 70px rgba(204,157,43,.08),
    inset 0 0 90px rgba(0,0,0,.80);
  opacity:.82;
  animation:ph-dongson-drum 32s linear infinite;
}

/* Vòng chim Lạc: 4 chim cách xa nhau, mô phỏng họa tiết khắc quanh mặt trống */
#pokehub-app .ph-stage::after{
  content:"";
  position:absolute;
  left:50%;
  top:50%;
  width:min(375px,64vw);
  height:min(375px,64vw);
  transform:translate(-50%,-50%);
  border-radius:50%;
  z-index:1;
  pointer-events:none;
  background:
    url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjAgNDIwIj4KPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDJhYjQzIiBzdHJva2Utd2lkdGg9IjIiIG9wYWNpdHk9IjAuNTIiPgo8Y2lyY2xlIGN4PSIyMTAiIGN5PSIyMTAiIHI9IjE4NCIvPgo8L2c+CjxnIGZpbGw9IiNkMmFiNDMiIG9wYWNpdHk9IjAuNTgiPgogIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxMCA1OCkiPgogICAgPHBhdGggZD0iTTAgOCBDMTggLTggNDIgLTQgNTQgOCBDNDAgNiAyOSA5IDIwIDE5IEMxMiAyOCAtMSAyOSAtMTAgMTggQy0zIDE3IDAgMTQgMCA4WiIvPgogICAgPHBhdGggZD0iTTIwIDE5IEMzMCAxMiA0MiA4IDYyIDEyIEM0OCAxOSAzOSAyOCAzMyA0MSBDMjcgMzIgMjMgMjYgMjAgMTlaIi8+CiAgICA8cGF0aCBkPSJNLTEgMTcgTC0xOCAxMCBMLTkgMjIgWiIvPgogIDwvZz4KICA8ZyB0cmFuc2Zvcm09InJvdGF0ZSg5MCAyMTAgMjEwKSI+CiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTAgNTgpIj4KICAgICAgPHBhdGggZD0iTTAgOCBDMTggLTggNDIgLTQgNTQgOCBDNDAgNiAyOSA5IDIwIDE5IEMxMiAyOCAtMSAyOSAtMTAgMTggQy0zIDE3IDAgMTQgMCA4WiIvPgogICAgICA8cGF0aCBkPSJNMjAgMTkgQzMwIDEyIDQyIDggNjIgMTIgQzQ4IDE5IDM5IDI4IDMzIDQxIEMyNyAzMiAyMyAyNiAyMCAxOVoiLz4KICAgICAgPHBhdGggZD0iTS0xIDE3IEwtMTggMTAgTC05IDIyIFoiLz4KICAgIDwvZz4KICA8L2c+CiAgPGcgdHJhbnNmb3JtPSJyb3RhdGUoMTgwIDIxMCAyMTApIj4KICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxMCA1OCkiPgogICAgICA8cGF0aCBkPSJNMCA4IEMxOCAtOCA0MiAtNCA1NCA4IEM0MCA2IDI5IDkgMjAgMTkgQzEyIDI4IC0xIDI5IC0xMCAxOCBDLTMgMTcgMCAxNCAwIDhaIi8+CiAgICAgIDxwYXRoIGQ9Ik0yMCAxOSBDMzAgMTIgNDIgOCA2MiAxMiBDNDggMTkgMzkgMjggMzMgNDEgQzI3IDMyIDIzIDI2IDIwIDE5WiIvPgogICAgICA8cGF0aCBkPSJNLTEgMTcgTC0xOCAxMCBMLTkgMjIgWiIvPgogICAgPC9nPgogIDwvZz4KICA8ZyB0cmFuc2Zvcm09InJvdGF0ZSgyNzAgMjEwIDIxMCkiPgogICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjEwIDU4KSI+CiAgICAgIDxwYXRoIGQ9Ik0wIDggQzE4IC04IDQyIC00IDU0IDggQzQwIDYgMjkgOSAyMCAxOSBDMTIgMjggLTEgMjkgLTEwIDE4IEMtMyAxNyAwIDE0IDAgOFoiLz4KICAgICAgPHBhdGggZD0iTTIwIDE5IEMzMCAxMiA0MiA4IDYyIDEyIEM0OCAxOSAzOSAyOCAzMyA0MSBDMjcgMzIgMjMgMjYgMjAgMTlaIi8+CiAgICAgIDxwYXRoIGQ9Ik0tMSAxNyBMLTE4IDEwIEwtOSAyMiBaIi8+CiAgICA8L2c+CiAgPC9nPgo8L2c+CjxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyYWI0MyIgc3Ryb2tlLXdpZHRoPSIyIiBvcGFjaXR5PSIwLjI2Ij4KICA8Y2lyY2xlIGN4PSIyMTAiIGN5PSIyMTAiIHI9IjMyIi8+CiAgPGNpcmNsZSBjeD0iMjEwIiBjeT0iMjEwIiByPSIxNCIvPgogIDxwYXRoIGQ9Ik0yMTAgMTgxIEwyMTYgMTk2IEwyMzIgMjAwIEwyMTggMjEwIEwyMjAgMjI2IEwyMTAgMjE4IEwyMDAgMjI2IEwyMDIgMjEwIEwxODggMjAwIEwyMDQgMTk2IFoiLz4KPC9nPgo8L3N2Zz4=") center/contain no-repeat,
    radial-gradient(circle at 50% 50%,
      rgba(6,10,15,.78) 0 14%,
      rgba(210,171,67,.08) 14.2% 21%,
      transparent 22% 100%);
  opacity:.64;
  filter:drop-shadow(0 0 10px rgba(210,171,67,.08));
}

#pokehub-app .ph-pack{
  position:relative;
  z-index:3;
}

/* Giữ họa tiết gọn trong đúng khu vực mở pack, không tràn lên thanh sưu tập hay footer. */
#pokehub-app .ph-stage{
  overflow:hidden;
}

@media (max-width:760px){
  #pokehub-app .ph-stage::before{
    top:52%;
    width:min(350px,88vw);
    height:min(350px,88vw);
  }
  #pokehub-app .ph-stage::after{
    top:52%;
    width:min(255px,66vw);
    height:min(255px,66vw);
  }
}

@media (max-width:480px){
  #pokehub-app .ph-stage::before{
    top:52%;
    width:min(305px,92vw);
    height:min(305px,92vw);
    opacity:.72;
  }
  #pokehub-app .ph-stage::after{
    top:52%;
    width:min(220px,68vw);
    height:min(220px,68vw);
    opacity:.56;
  }
}

@keyframes ph-dongson-drum{
  from{transform:translate(-50%,-50%) rotate(0deg);}
  to{transform:translate(-50%,-50%) rotate(360deg);}
}

@media (prefers-reduced-motion:reduce){
  #pokehub-app .ph-stage::before{animation:none;}
}


<style>
/* ============================================================
   V13: LÀM NỔI HỌA TIẾT TRỐNG ĐỒNG, NHƯNG KHÔNG ĐÈ LÊN TEXT/NÚT
   Chỉ chỉnh visual của backdrop.
   ============================================================ */
#pokehub-app .ph-stage::before{
  opacity: .92 !important;
  filter:
    drop-shadow(0 0 10px rgba(210,171,67,.16))
    drop-shadow(0 0 34px rgba(210,171,67,.07)) !important;
  z-index:0 !important;
}

#pokehub-app .ph-stage::after{
  opacity: .84 !important;
  filter:
    drop-shadow(0 0 9px rgba(210,171,67,.14)) !important;
  z-index:1 !important;
}

/* Ring và chim Lạc vẫn nổi, nhưng text/nút luôn nằm phía trên */
#pokehub-app .ph-stage > .ph-hint,
#pokehub-app .ph-stage > .ph-box-row{
  position:relative !important;
  z-index:6 !important;
}

#pokehub-app .ph-stage > .ph-hint{
  text-shadow:
    0 2px 8px rgba(0,0,0,.92),
    0 0 10px rgba(8,12,22,.96) !important;
}

#pokehub-app .ph-stage > .ph-box-row{
  isolation:isolate;
}

#pokehub-app .ph-stage > .ph-box-row .ph-box-lbl,
#pokehub-app .ph-stage > .ph-box-row .ph-chip{
  position:relative !important;
  z-index:7 !important;
  text-shadow:0 1px 6px rgba(0,0,0,.7);
}

/* Các vòng đồng có độ tương phản rõ hơn ở desktop */
@media (min-width:761px){
  #pokehub-app .ph-stage::before{
    background-blend-mode:screen,normal,normal,normal,screen,normal,normal !important;
  }
}

/* Mobile: nổi vừa đủ, tránh vòng ăn vào nút */
@media (max-width:760px){
  #pokehub-app .ph-stage::before{
    opacity:.80 !important;
    filter:drop-shadow(0 0 8px rgba(210,171,67,.14)) !important;
  }
  #pokehub-app .ph-stage::after{
    opacity:.70 !important;
    filter:drop-shadow(0 0 7px rgba(210,171,67,.12)) !important;
  }
}
</style>

<style>
/* ============================================================
   PATCH: ĐƯA TEXT + NÚT MỞ GÓI LÊN TRÊN HỌA TIẾT TRỐNG ĐỒNG
   Chỉ sửa thứ tự hiển thị; không đổi gameplay.
   ============================================================ */
#pokehub-app .ph-stage > .ph-hint,
#pokehub-app .ph-stage > .ph-box-row{
  position:relative !important;
  z-index:20 !important;
}

#pokehub-app .ph-box-row{
  padding:5px 8px !important;
  border-radius:14px !important;
  background:rgba(5,9,18,.28) !important;
  backdrop-filter:blur(2px);
  -webkit-backdrop-filter:blur(2px);
}

#pokehub-app .ph-box-lbl,
#pokehub-app .ph-chip{
  position:relative !important;
  z-index:21 !important;
}

/* Giữ pack vẫn cao nhất, còn họa tiết chỉ nằm phía sau */
#pokehub-app .ph-pack{
  position:relative !important;
  z-index:30 !important;
}

/* Vòng đồng rõ hơn nhưng không che chữ/nút */
#pokehub-app .ph-stage::before{
  opacity:.90 !important;
  filter:drop-shadow(0 0 16px rgba(210,171,67,.12)) !important;
}
#pokehub-app .ph-stage::after{
  opacity:.74 !important;
  filter:drop-shadow(0 0 12px rgba(210,171,67,.10)) !important;
}

@media (max-width:520px){
  #pokehub-app .ph-box-row{
    padding:4px 6px !important;
    background:rgba(5,9,18,.34) !important;
  }
}
</style>

<style>
/* ============================================================
   V14: mobile ring center-lock
   Luôn căn tâm họa tiết Đông Sơn theo TÂM PACK thực tế.
   Chỉ sửa vị trí backdrop; không sửa gameplay.
   ============================================================ */
#pokehub-app .ph-stage::before,
#pokehub-app .ph-stage::after{
  left:var(--ph-ring-center-x,50%) !important;
  top:var(--ph-ring-center-y,50%) !important;
}

@media (max-width:760px){
  /* Vòng nhỏ hơn để không lấn sang hai bên nhưng vẫn ôm đủ pack. */
  #pokehub-app .ph-stage::before{
    width:min(390px,94vw) !important;
    height:min(390px,94vw) !important;
  }
  #pokehub-app .ph-stage::after{
    width:min(270px,66vw) !important;
    height:min(270px,66vw) !important;
  }
}

@media (max-width:480px){
  #pokehub-app .ph-stage::before{
    width:min(340px,90vw) !important;
    height:min(340px,90vw) !important;
  }
  #pokehub-app .ph-stage::after{
    width:min(230px,64vw) !important;
    height:min(230px,64vw) !important;
  }
}
</style>

<script>
(function(){
  "use strict";
  var app=document.getElementById("pokehub-app");
  if(!app) return;
  var stage=app.querySelector(".ph-stage");
  var pack=document.getElementById("ph-pack");
  if(!stage || !pack) return;

  function lockRingToPack(){
    var sr=stage.getBoundingClientRect();
    var pr=pack.getBoundingClientRect();
    var x=(pr.left - sr.left) + (pr.width/2);
    var y=(pr.top - sr.top) + (pr.height/2);
    if(!isFinite(x) || !isFinite(y)) return;
    stage.style.setProperty("--ph-ring-center-x", x + "px");
    stage.style.setProperty("--ph-ring-center-y", y + "px");
  }

  lockRingToPack();

  if(window.ResizeObserver){
    var ro=new ResizeObserver(lockRingToPack);
    ro.observe(stage);
    ro.observe(pack);
  }

  window.addEventListener("resize",lockRingToPack,{passive:true});
  window.addEventListener("orientationchange",function(){ setTimeout(lockRingToPack,60); },{passive:true});
  setInterval(lockRingToPack,500);
})();
</script>

<style>
/* ============================================================
   FINAL DESKTOP PATCH — TRỐNG ĐỒNG GỌN GIỐNG MOBILE
   Chỉ chỉnh kích thước desktop. Mobile giữ nguyên.
   Không đụng pack/gameplay/API.
   ============================================================ */
@media (min-width:761px){
  #pokehub-app .ph-stage::before{
    width:min(390px,60vw) !important;
    height:min(390px,60vw) !important;
  }

  #pokehub-app .ph-stage::after{
    width:min(270px,42vw) !important;
    height:min(270px,42vw) !important;
  }
}
</style>



<details style="
  margin: 30px auto 10px;
  max-width: 100%;
  background: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  overflow: hidden;
  box-shadow: 0 2px 5px rgba(0,0,0,0.03);
">
  <summary style="
    padding: 12px 16px;
    font-size: 13px;
    font-weight: 600;
    color: #4a5568;
    cursor: pointer;
    user-select: none;
    background: #f8fafc;
    display: flex;
    align-items: center;
    justify-content: space-between;
    outline: none;
  ">
    <span><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2696.png" alt="⚖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Tuyên bố miễn trừ trách nhiệm / Disclaimer</span>
    <span style="font-size: 11px; color: #94a3b8; font-weight: normal;">(Bấm để xem)</span>
  </summary>

  <div style="
    padding: 16px;
    border-top: 1px solid #e2e8f0;
    font-size: 12px;
    line-height: 1.6;
    color: #475569;
    background: #ffffff;
    text-align: justify;
  ">
    <!-- KHỐI TIẾNG VIỆT -->
    <div style="margin-bottom: 16px;">
      <div style="font-weight: 700; color: #1e293b; margin-bottom: 8px; display: flex; align-items: center; gap: 6px;">
        <span style="background: #e2e8f0; color: #334155; padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: bold;">VIE</span>
        Tiếng Việt
      </div>
      <div style="margin: 0 0 8px 0;">Công cụ này là một dự án fan-made/unofficial được tạo nhằm mục đích giải trí và trải nghiệm mô phỏng mở pack trực tuyến. Công cụ không bán, phân phối hoặc cung cấp sản phẩm thẻ bài vật lý và không thu bất kỳ khoản phí nào để sử dụng tính năng mô phỏng.</div>
      <div style="margin: 0 0 8px 0;">Tên thương hiệu, tên sản phẩm, nhân vật, hình ảnh, artwork và các tài sản trí tuệ liên quan đến Pokémon, Yu-Gi-Oh!, One Piece hoặc các bên thứ ba tương ứng thuộc về chủ sở hữu quyền của chúng. VNGame không tuyên bố quyền sở hữu đối với các tài sản trí tuệ đó và không đại diện cho, không được tài trợ bởi và không có liên kết chính thức với các chủ sở hữu thương hiệu tương ứng.</div>
      <div style="margin: 0;">Nội dung trên trang được cung cấp cho mục đích thông tin và giải trí. Nếu bạn là chủ sở hữu quyền và cho rằng bất kỳ nội dung nào trên trang vi phạm quyền của bạn, vui lòng liên hệ với VNGame để yêu cầu xem xét và xử lý nội dung liên quan.</div>
    </div>

    <hr style="border: 0; border-top: 1px dashed #cbd5e1; margin: 16px 0;">

    <!-- KHỐI TIẾNG ANH -->
    <div>
      <div style="font-weight: 700; color: #1e293b; margin-bottom: 8px; display: flex; align-items: center; gap: 6px;">
        <span style="background: #3b82f6; color: #ffffff; padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: bold;">ENG</span>
        English
      </div>
      <div style="margin: 0 0 8px 0;">This tool is an unofficial, fan-made project created solely for entertainment and online simulation purposes. It does not sell, distribute, or provide physical trading cards, nor does it charge any fees for using the simulation features.</div>
      <div style="margin: 0 0 8px 0;">All brand names, product titles, characters, images, artwork, and related intellectual property regarding Pokémon, Yu-Gi-Oh!, One Piece, or other respective third parties belong to their respective copyright and trademark owners. VNGame claims no ownership over these intellectual assets and is not affiliated with, endorsed, or sponsored by the brand owners.</div>
      <div style="margin: 0;">Content on this page is provided for informational and entertainment purposes only. If you are a copyright owner and believe that any content on this page infringes upon your rights, please contact VNGame directly for prompt review and removal.</div>
    </div>
  </div>
</details>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Pokémon TCG Pack Opening Simulator là gì?</mark></h3>



<p class="wp-block-paragraph">Pokémon TCG Pack Opening Simulator là công cụ mô phỏng quá trình <strong>mở booster pack Pokémon TCG</strong> trên nền tảng online. Thay vì phải mua một gói bài vật lý, bạn có thể trải nghiệm việc mở pack trực tiếp trên website.</p>



<p class="wp-block-paragraph">Mỗi lần mở pack, hệ thống sẽ hiển thị các lá bài tương ứng với expansion mà bạn lựa chọn. Bạn cũng có thể xem độ hiếm của từng card và giá trị tham khảo của các lá bài trong pack.</p>



<p class="wp-block-paragraph">Đây là một trải nghiệm phù hợp với người chơi Pokémon TCG, người sưu tầm card hoặc đơn giản là những ai thích cảm giác hồi hộp khi mở pack.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Lưu ý:</strong> Đây là simulator mang tính giải trí. Các lá bài nhận được trong simulator không phải thẻ Pokémon TCG vật lý và không được gửi đến người chơi.</p>
</blockquote>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Cách mở pack Pokémon TCG miễn phí</mark></h3>



<p class="wp-block-paragraph">Việc mở pack khá đơn giản và không yêu cầu cài đặt phần mềm.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Bước 1: Chọn Expansion</mark></h4>



<p class="wp-block-paragraph">Chọn bộ Pokémon TCG mà bạn muốn mở từ danh sách expansion có sẵn.</p>



<p class="wp-block-paragraph">Mỗi expansion có một danh sách card và pool bài riêng, vì vậy việc lựa chọn expansion sẽ ảnh hưởng đến những lá bài bạn có thể pull được.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Bước 2: Chọn số lượng pack</mark></h4>



<p class="wp-block-paragraph">Bạn có thể lựa chọn số pack muốn mở trong một lần, chẳng hạn <strong>1, 3, 5 hoặc 10 pack</strong>.</p>



<p class="wp-block-paragraph">Nếu muốn trải nghiệm cảm giác mở từng gói, hãy bắt đầu với 1 pack. Còn nếu muốn thử vận may nhanh hơn, bạn có thể mở nhiều pack cùng lúc.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Bước 3: Mở pack và xem kết quả</mark></h4>



<p class="wp-block-paragraph">Nhấn nút mở pack để bắt đầu.</p>



<p class="wp-block-paragraph">Các lá bài sẽ được hiển thị sau khi pack được mở. Hãy kiểm tra rarity, card và giá trị tham khảo để xem lượt mở của bạn may mắn đến đâu.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Các Expansion Pokémon TCG có thể mở</mark></h3>



<p class="wp-block-paragraph">Simulator hỗ trợ các expansion Pokémon TCG được tích hợp trong hệ thống. Mỗi bộ có những card, rarity và danh sách bài khác nhau.</p>



<p class="wp-block-paragraph">Một trong những expansion có thể trải nghiệm là <strong>Genetic Apex</strong>. Bạn có thể chọn expansion trực tiếp trong simulator để xem danh sách card và bắt đầu mở pack.</p>



<p class="wp-block-paragraph">Khi có thêm expansion mới, danh sách có thể được cập nhật để mang đến nhiều lựa chọn hơn cho người chơi.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Rarity trong Pokémon TCG</mark></h3>



<p class="wp-block-paragraph">Một trong những điều thú vị nhất khi mở pack Pokémon TCG là không biết mình sẽ nhận được lá bài nào.</p>



<p class="wp-block-paragraph">Các card có nhiều cấp độ độ hiếm khác nhau. Những card phổ biến thường xuất hiện thường xuyên hơn, trong khi các card có rarity cao thường khó pull hơn.</p>



<p class="wp-block-paragraph">Một số nhóm rarity mà người chơi có thể gặp gồm:</p>



<ul class="wp-block-list">
<li><strong>Common</strong> – những lá bài phổ biến.</li>



<li><strong>Uncommon</strong> – độ hiếm cao hơn Common.</li>



<li><strong>Rare</strong> – những card có độ hiếm cao hơn trong pack.</li>



<li><strong>Double Rare</strong> – nhóm card hiếm với thiết kế đặc biệt.</li>



<li><strong>Illustration Rare</strong> – các card nổi bật với artwork đặc biệt.</li>



<li><strong>Special Illustration Rare</strong> – những card có artwork đặc biệt và thường được người sưu tầm quan tâm.</li>
</ul>



<p class="wp-block-paragraph">Tùy từng expansion, hệ thống rarity và danh sách card có thể khác nhau.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Luck Score – Lượt mở của bạn may mắn đến đâu?</mark></h3>



<p class="wp-block-paragraph">Sau khi mở pack, <strong>Luck Score</strong> giúp bạn đánh giá mức độ may mắn của lượt pull.</p>



<p class="wp-block-paragraph">Điểm số được dựa trên chất lượng và độ hiếm của những card xuất hiện trong pack. Nếu bạn pull được nhiều card có rarity cao, Luck Score sẽ phản ánh một lượt mở đáng chú ý hơn.</p>



<p class="wp-block-paragraph">Bạn có thể sử dụng Luck Score như một cách vui để so sánh các lượt mở pack của mình và thử xem lần tiếp theo có thể phá kỷ lục hay không.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Xem giá trị của Pack</mark></h3>



<p class="wp-block-paragraph">Ngoài rarity, simulator còn giúp bạn xem <strong>giá trị tham khảo của những card đã pull được</strong>.</p>



<p class="wp-block-paragraph">Tổng giá trị của pack được tính dựa trên giá trị tham khảo của các card xuất hiện trong lượt mở. Đây là một cách thú vị để xem pack vừa mở có những card đáng chú ý nào.</p>



<p class="wp-block-paragraph">Tuy nhiên, giá card Pokémon TCG có thể thay đổi theo thị trường và nguồn dữ liệu. Giá hiển thị trong simulator chỉ nên được xem là <strong>giá trị tham khảo</strong>, không phải mức giá mua hoặc bán được đảm bảo.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Collection và lịch sử mở Pack</mark></h3>



<p class="wp-block-paragraph">Nếu bạn thường xuyên sử dụng simulator, việc theo dõi những card đã mở sẽ giúp trải nghiệm thú vị hơn.</p>



<p class="wp-block-paragraph">Bạn có thể quan tâm đến:</p>



<ul class="wp-block-list">
<li>Những card hiếm nhất đã pull được.</li>



<li>Expansion đã mở.</li>



<li>Luck Score cao nhất.</li>



<li>Tổng số pack đã mở.</li>



<li>Tổng giá trị tham khảo của các card.</li>



<li>Những lượt pull đặc biệt mà bạn muốn chia sẻ với bạn bè.</li>
</ul>



<p class="wp-block-paragraph">Thay vì chỉ mở một pack rồi rời đi, bạn có thể thử nhiều expansion khác nhau và xem vận may của mình thay đổi như thế nào.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Chia sẻ kết quả mở Pack</mark></h3>



<p class="wp-block-paragraph">Vừa pull được một card cực hiếm?</p>



<p class="wp-block-paragraph">Sau khi hoàn thành lượt mở pack, bạn có thể sử dụng tính năng <strong>chia sẻ ảnh kết quả</strong> để lưu lại thành quả và gửi cho bạn bè hoặc chia sẻ lên mạng xã hội.</p>



<p class="wp-block-paragraph">Đây cũng là một cách thú vị để tạo thử thách:</p>



<p class="wp-block-paragraph"><strong>Ai có Luck Score cao hơn? Ai pull được card hiếm hơn?</strong></p>



<p class="wp-block-paragraph">Hãy thử mở cùng một expansion và so sánh kết quả với bạn bè.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Pokémon TCG Pack Opening Simulator có miễn phí không?</mark></h3>



<p class="wp-block-paragraph">Có. Bạn có thể sử dụng simulator để trải nghiệm <strong>mở pack Pokémon TCG miễn phí</strong> mà không cần mua booster pack vật lý.</p>



<p class="wp-block-paragraph">Simulator được tạo ra nhằm mang đến trải nghiệm pack opening trực tuyến nhanh chóng và thuận tiện.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Có nhận được card Pokémon thật không?</mark></h3>



<p class="wp-block-paragraph">Không.</p>



<p class="wp-block-paragraph">Các card xuất hiện trong simulator chỉ là <strong>card kỹ thuật số được sử dụng cho mục đích mô phỏng</strong>. Việc mở pack trên website không tạo ra hoặc gửi cho bạn bất kỳ thẻ Pokémon TCG vật lý nào.</p>



<p class="wp-block-paragraph">Nếu bạn muốn sở hữu card thật, bạn vẫn cần mua hoặc trao đổi các sản phẩm Pokémon TCG vật lý từ những nguồn phù hợp.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">FAQ – Câu hỏi thường gặp</mark></h3>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Pokémon TCG Pack Opening Simulator có mất phí không?</mark></h4>



<p class="wp-block-paragraph">Không. Bạn có thể mở pack online miễn phí và không cần mua booster pack vật lý.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Tôi có cần đăng ký tài khoản không?</mark></h4>



<p class="wp-block-paragraph">Bạn có thể bắt đầu trải nghiệm trực tiếp trên trang simulator mà không cần thực hiện quy trình mua pack.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Simulator có phải pack Pokémon TCG thật không?</mark></h4>



<p class="wp-block-paragraph">Không. Đây là công cụ mô phỏng việc mở pack Pokémon TCG. Card nhận được trên simulator không phải card vật lý.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Tỷ lệ pull có giống pack thật không?</mark></h4>



<p class="wp-block-paragraph">Simulator sử dụng hệ thống dữ liệu và xác suất được thiết lập cho trải nghiệm mô phỏng. Vì vậy, không nên xem kết quả simulator là cam kết về tỷ lệ pull của booster pack vật lý.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Giá card được lấy từ đâu?</mark></h4>



<p class="wp-block-paragraph">Giá trị card được lấy từ nguồn dữ liệu được tích hợp vào hệ thống và được sử dụng làm giá trị tham khảo. Giá thị trường thực tế có thể thay đổi theo thời gian.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Có thể mở nhiều pack cùng lúc không?</mark></h4>



<p class="wp-block-paragraph">Có. Simulator hỗ trợ nhiều lựa chọn số lượng pack, giúp bạn có thể mở nhanh nhiều pack trong một lượt.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Bắt đầu mở Pack Pokémon TCG miễn phí</mark></h3>



<p class="wp-block-paragraph">Không cần mua booster pack và cũng không cần chuẩn bị bộ sưu tập card. Chỉ cần chọn expansion, chọn số lượng pack và bắt đầu thử vận may.</p>



<p class="wp-block-paragraph"><strong>Bạn sẽ pull được card nào trong lần mở tiếp theo?</strong></p>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f449.png" alt="👉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Mở Pack Pokémon TCG miễn phí ngay và xem kết quả của bạn!</strong></p>
<p>Bài viết <a href="https://vngame.tv/trai-nghiem-mo-the-pokemon-tcg-mien-phi/" data-wpel-link="internal">Mở Pack Pokémon TCG Miễn Phí – Pokémon TCG Pack Opening Simulator</a> đã xuất hiện đầu tiên vào ngày <a href="https://vngame.tv" data-wpel-link="internal">vnGAME</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vngame.tv/trai-nghiem-mo-the-pokemon-tcg-mien-phi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">19159</post-id>	</item>
		<item>
		<title>(Pokémon TCG) Hướng Dẫn Build Deck Charizard ex: Kẻ Hủy Diệt Meta 2026</title>
		<link>https://vngame.tv/huong-dan-build-deck-charizard-ex/</link>
					<comments>https://vngame.tv/huong-dan-build-deck-charizard-ex/#respond</comments>
		
		<dc:creator><![CDATA[Cá]]></dc:creator>
		<pubDate>Wed, 25 Mar 2026 13:24:01 +0000</pubDate>
				<category><![CDATA[TCG (Trading Card Game)]]></category>
		<category><![CDATA[Build Deck Pokemon]]></category>
		<category><![CDATA[Charizard ex]]></category>
		<category><![CDATA[Decklist Pokemon]]></category>
		<category><![CDATA[Hướng dẫn chơi Pokemon TCG]]></category>
		<category><![CDATA[Pokemon TCG]]></category>
		<category><![CDATA[Stellar Crown]]></category>
		<guid isPermaLink="false">https://vngame.tv/?p=16119</guid>

					<description><![CDATA[<p>[lwptoc float=&#8221;left&#8221; width=&#8221;250px&#8221;] Dù đã trải qua nhiều mùa giải, Charizard ex vẫn luôn là một trong những thế lực đáng sợ nhất trong đấu trường Pokémon TCG. Với khả năng lội ngược dòng ngoạn mục và sự hỗ trợ từ các dòng Pokemon mới như Dusknoir, Charizard ex không chỉ là một biểu tượng mà còn là chìa khóa dẫn đến chiến thắng tại các giải đấu lớn. ✔ ĐIỂM MẠNH (PROS) Sát thương bùng nổ: Mạnh dần theo số Prize đối thủ đã lấy. Khả năng tìm bài: Pidgeot ex giúp lấy mọi thứ bạn cần mỗi lượt. [&#8230;]</p>
<p>Bài viết <a href="https://vngame.tv/huong-dan-build-deck-charizard-ex/" data-wpel-link="internal">(Pokémon TCG) Hướng Dẫn Build Deck Charizard ex: Kẻ Hủy Diệt Meta 2026</a> đã xuất hiện đầu tiên vào ngày <a href="https://vngame.tv" data-wpel-link="internal">vnGAME</a>.</p>
]]></description>
										<content:encoded><![CDATA[
[lwptoc float=&#8221;left&#8221; width=&#8221;250px&#8221;]



<p class="wp-block-paragraph">Dù đã trải qua nhiều mùa giải, <strong>Charizard ex</strong> vẫn luôn là một trong những thế lực đáng sợ nhất trong đấu trường Pokémon TCG. Với khả năng lội ngược dòng ngoạn mục và sự hỗ trợ từ các dòng Pokemon mới như Dusknoir, Charizard ex không chỉ là một biểu tượng mà còn là chìa khóa dẫn đến chiến thắng tại các giải đấu lớn.</p>



<div class="vngame-analysis-box" style="margin: 30px 0; display: flex; flex-wrap: wrap; gap: 20px; font-family: sans-serif;">
    
    <div style="flex: 1; min-width: 280px; background: #f6fff6; border: 1px solid #c3e6cb; border-radius: 12px; padding: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.03);">
        <h4 style="margin: 0 0 15px 0; color: #28a745; display: flex; align-items: center; gap: 10px; font-size: 18px;">
            <span style="background: #28a745; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 14px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> 
            ĐIỂM MẠNH (PROS)
        </h4>
        <ul style="margin: 0; padding-left: 20px; color: #155724; font-size: 14px; line-height: 1.8;">
            <li><b>Sát thương bùng nổ:</b> Mạnh dần theo số Prize đối thủ đã lấy.</li>
            <li><b>Khả năng tìm bài:</b> Pidgeot ex giúp lấy mọi thứ bạn cần mỗi lượt.</li>
            <li><b>Độ bền cao:</b> Charizard ex sở hữu lượng HP cực khủng (330 HP).</li>
            <li><b>Lội ngược dòng:</b> Rất mạnh trong các kèo đấu cần sự kiên nhẫn.</li>
        </ul>
    </div>

    <div style="flex: 1; min-width: 280px; background: #fff5f5; border: 1px solid #f5c6cb; border-radius: 12px; padding: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.03);">
        <h4 style="margin: 0 0 15px 0; color: #dc3545; display: flex; align-items: center; gap: 10px; font-size: 18px;">
            <span style="background: #dc3545; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 14px;">✘</span> 
            ĐIỂM YẾU (CONS)
        </h4>
        <ul style="margin: 0; padding-left: 20px; color: #721c24; font-size: 14px; line-height: 1.8;">
            <li><b>Sợ hệ Hệ Cỏ (Grass):</b> Nhận gấp đôi sát thương từ các Deck hệ Cỏ.</li>
            <li><b>Phụ thuộc vào Ability:</b> Dễ bị &#8220;tịt ngòi&#8221; nếu đối thủ khóa Ability.</li>
            <li><b>Cần thiết lập:</b> Mất thời gian để tiến hóa Stage 2 (cần Rare Candy).</li>
            <li><b>Bị nhắm mục tiêu:</b> Pidgey mỏng manh thường bị bắn tỉa sớm.</li>
        </ul>
    </div>

</div>



<p class="wp-block-paragraph">Trong bài viết này, chúng ta sẽ cùng phân tích chi tiết danh sách bài (decklist), chiến thuật vận hành và cách đối đầu với các đối thủ sừng sỏ trong meta hiện tại.</p>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">1. Danh sách bài (Decklist) Charizard ex</mark></h3>



<p class="wp-block-paragraph">Dưới đây là danh sách 60 lá bài tối ưu, kết hợp giữa sức mạnh công phá của Charizard và khả năng kiểm soát bàn đấu từ Pidgeot ex và Dusknoir.</p>



<div class="decklist-container" style="margin: 25px 0; padding: 20px; background: #f9f9f9; border: 1px solid #e0e0e0; border-left: 6px solid #e3350d; border-radius: 8px; font-family: sans-serif;">
    <p style="margin: 0 0 15px 0; font-weight: bold; color: #333; font-size: 16px;">Sử dụng danh sách này để chơi Online (PTCG Live):</p>
    
    <button id="copyDeckBtn" onclick="copyDecklist()" style="
        background-color: #e3350d; 
        color: #ffffff !important; 
        border: none; 
        padding: 14px 28px; 
        border-radius: 6px; 
        cursor: pointer; 
        font-weight: bold; 
        font-size: 15px;
        display: inline-flex; 
        align-items: center; 
        justify-content: center;
        gap: 10px; 
        width: auto;
        min-width: 280px;
        line-height: 1;
        text-transform: uppercase;
        box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        transition: all 0.3s ease;
    ">
        <span class="dashicons dashicons-clipboard" style="font-size: 20px; width: 20px; height: 20px;"></span> 
        <span id="btnText">SAO CHÉP DECKLIST (IMPORT GAME)</span>
    </button>
    
    <p id="copySuccess" style="display: none; color: #28a745; margin-top: 12px; font-weight: bold; font-size: 14px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Đã sao chép danh sách Charizard ex thành công!</p>
</div>

<textarea id="decklistText" style="display: none;">
Pokémon: 14
2 Charizard ex OBF 125
2 Pidgeot ex OBF 164
2 Pidgey OBF 162
1 Pidgeotto OBF 163
3 Charmander PAF 007
1 Charmeleon PAF 008
1 Charmeleon MEW 005
2 Duskull PRE 018
1 Dusclops PRE 019
1 Dusknoir PRE 020
1 Fezandipiti ex SFA 038
1 Klefki SVI 096
1 Tatsugiri TWM 131
1 Chi-Yu PAR 029

Trainer: 14
4 Buddy-Buddy Poffin TEF 144
4 Ultra Ball SVI 196
4 Rare Candy SVI 191
2 Super Rod PAL 188
2 Counter Catcher PAR 160
4 Lillie&#8217;s Determination MEG 125
2 Arven OBF 186
2 Iono PAL 185
2 Boss&#8217;s Orders BRS 132
1 Briar PRE 120
1 Maximum Belt TEF 154
2 Technical Machine: Evolution PAR 178
1 Town Store OBF 196
1 Artazon PAL 171

Energy: 2
6 Basic {R} Energy SVE 002
2 Jet Energy PAL 190

Total Cards: 60
</textarea>

<script>
function copyDecklist() {
    var copyText = document.getElementById("decklistText");
    const el = document.createElement('textarea');
    el.value = copyText.value;
    document.body.appendChild(el);
    el.select();
    document.execCommand('copy');
    document.body.removeChild(el);

    var successMsg = document.getElementById("copySuccess");
    var btn = document.getElementById("copyDeckBtn");
    var btnText = document.getElementById("btnText");
    
    successMsg.style.display = "block";
    btn.style.backgroundColor = "#28a745"; // Đổi sang màu xanh khi thành công
    btnText.innerText = "ĐÃ SAO CHÉP THÀNH CÔNG!";
    
    setTimeout(function() {
        successMsg.style.display = "none";
        btn.style.backgroundColor = "#e3350d"; // Trở về màu đỏ Charizard
        btnText.innerText = "SAO CHÉP DECKLIST (IMPORT GAME)";
    }, 3000);
}
</script>



<div style="margin-top: 10px; padding: 10px; background-color: #e9ecef; border-radius: 6px; font-size: 14px; color: #495057; display: flex; align-items: flex-start; gap: 8px;">
    <span class="dashicons dashicons-info" style="color: #007bff; margin-top: 2px;"></span>
    <span>
        <strong>Cách dùng:</strong> Sau khi bấm <strong>&#8220;Sao chép&#8221;</strong>, bạn mở game <strong>Pokémon TCG Live</strong>, vào mục <strong>Decks</strong> -> chọn <strong>Create a Deck</strong> -> chọn <strong>Import</strong> để nhận bộ bài ngay lập tức!
    </span>
</div>



<div class="vngame-article-promo" style="margin: 35px 0; padding: 0; background: #ffffff; border: 1px solid #e0e0e0; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.08); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; transition: transform 0.3s ease;" onmouseover="this.style.transform='translateY(-5px)';" onmouseout="this.style.transform='translateY(0) ';">
    
    <div style="display: flex; flex-wrap: wrap;">
        <div style="flex: 1; min-width: 200px; background: #007bff; display: flex; align-items: center; justify-content: center; min-height: 150px; color: white;">
             <span class="dashicons dashicons-format-video" style="font-size: 60px; width: 60px; height: 60px; opacity: 0.8;"></span>
        </div>

        <div style="flex: 2; padding: 25px; min-width: 250px; display: flex; flex-direction: column; justify-content: center;">
            <h4 style="margin: 0 0 10px 0; color: #007bff; font-size: 14px; text-transform: uppercase; letter-spacing: 1px; font-weight: bold;">Hướng dẫn hữu ích</h4>
            <h3 style="margin: 0 0 15px 0; color: #333; font-size: 20px; line-height: 1.4;">Cách Import Decklist vào Pokémon TCG Live chỉ với 1 Click</h3>
            <p style="margin: 0 0 20px 0; color: #666; font-size: 14px; line-height: 1.6;">
                Bạn đã có danh sách bài nhưng không biết cách đưa vào game? Xem ngay hướng dẫn chi tiết để tiết kiệm thời gian thiết lập Deck!
            </p>
            
            <a href="https://vngame.tv/huong-dan-cach-import-decklist-vao-pokemon-tcg-live-chi-voi-1-click/" target="_blank" rel="noopener noreferrer" style="
                display: inline-block;
                width: fit-content;
                background-color: #007bff;
                color: #ffffff !important;
                text-decoration: none !important;
                padding: 10px 25px;
                border-radius: 50px;
                font-weight: bold;
                font-size: 14px;
                text-transform: uppercase;
                transition: background 0.3s;
            " onmouseover="this.style.backgroundColor=&#039;#0056b3&#039;;" onmouseout="this.style.backgroundColor=&#039;#007bff&#039;;" data-wpel-link="internal">
                Đọc bài hướng dẫn ngay →
            </a>
        </div>
    </div>
</div>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Danh Sách Pokémon</mark></h3>



<ul class="wp-block-list">
<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Charizard ex (OBF-125):</mark></strong> Chủ lực gánh kèo với đòn <em>Burning Darkness</em> cực mạnh về cuối trận.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Pidgeot ex (OBF-164):</mark></strong> Công cụ tìm kiếm bài tối thượng, cho phép lấy bất kỳ lá bài nào mỗi lượt.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Pidgey (OBF-162) &amp; 1x Pidgeotto (OBF-163):</mark></strong> Dàn dự bị quan trọng để thiết lập chuỗi tiến hóa Pidgeot.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">3x Charmander (PAF-007):</mark></strong> Những quân bài cơ bản nhất để bắt đầu chuỗi tấn công hệ Lửa.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Charmeleon (PAF-008) &amp; 1x Charmeleon (MEW-005):</mark></strong> Các bước tiến hóa trung gian linh hoạt tùy theo tình huống.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Dusknoir (PRE):</mark></strong> Công cụ gây sát thương đột biến bằng cách hy sinh để ép đối thủ lấy Prize.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Duskull (PRE) &amp; 1x Dusclops (PRE):</mark></strong> Nguyên liệu cần thiết để chuẩn bị cho các pha lật kèo với Dusknoir.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Fezandipiti ex (SFA-038):</mark></strong> Hỗ trợ bắt Bench đối thủ và giúp rút bài ổn định khi bị KO.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Klefki (SVI-096):</mark></strong> Chốt chặn khó chịu giúp khóa các Ability quan trọng của đối thủ.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Tatsugiri (TWM-131):</mark></strong> &#8220;Máy lọc&#8221; hỗ trợ tìm kiếm Supporter để duy trì mạch trận.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Chi-Yu (PAR-029):</mark></strong> Vũ khí bí mật khắc chế các hệ Thép hoặc dọn dẹp các Pokemon 1 Prize.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Danh Sách Trainer &amp; Energy</mark></h3>



<ul class="wp-block-list">
<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">4x</mark> <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Buddy-Buddy Poffin (TEF-144)</mark>:</strong> Tìm kiếm nhanh các Pokemon cơ bản để dàn quân lên sân sớm.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">4x Ultra Ball (SVI-196)</mark>:</strong> Công cụ lọc bài và tìm kiếm các Pokemon tiến hóa quan trọng.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">4x Rare Candy (SVI-191)</mark>:</strong> “Phím tắt” giúp tiến hóa thẳng lên Stage 2 như Charizard hay Pidgeot.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Super Rod (PAL-188) &amp; 2x Counter Catcher (PAR-160)</mark>:</strong> Hồi tài nguyên và bắt bài các mục tiêu yếu trên Bench đối thủ.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">4x Lillie’s Determination (MEG-125)</mark>:</strong> Supporter chính giúp ổn định lượng bài trên tay trong mọi thời điểm.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Arven (OBF-186)</mark>:</strong> Chuyên gia tìm kiếm các Item và Tool (như TM: Evolution) đúng lúc cần thiết.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Iono (PAL-185) &amp; 2x Boss’s Orders (BRS-132)</mark>:</strong> Phá bài trên tay đối thủ và ép các mục tiêu quan trọng ra trận.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Briar (PRE):</mark></strong> Lá bài chiến thuật giúp lấy thêm Prize để kết thúc trận đấu nhanh chóng.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">1x Maximum Belt (TEF-154) [ACE SPEC]</mark>:</strong> Gia tăng sát thương để thực hiện những cú One-hit KO cực nặng.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">2x Technical Machine: Evolution, 1x Town Store (OBF-196) &amp; 1x Artazon (PAL-171)</mark>:</strong> Hệ thống Stadium và Tool tối ưu hóa tốc độ tiến hóa.</li>



<li><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">6x Basic Fire Energy (SVE-002) &amp; 2x Jet Energy (PAL-190)</mark>:</strong> Nguồn năng lượng dồi dào và khả năng linh hoạt vị trí.</li>
</ul>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">2. Chiến Thuật Vận Hành Deck Charizard ex</mark></h3>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Giai đoạn đầu trận: Thiết lập bàn đấu</mark></h4>



<p class="wp-block-paragraph">Mục tiêu quan trọng nhất là đưa được <strong>Pidgeot ex (OBF-164)</strong> lên sân sớm nhất có thể. Khả năng <em>Quick Search</em> cho phép bạn lấy bất kỳ lá bài nào mỗi lượt, giúp việc tiến hóa Charizard ex trở nên cực kỳ đơn giản. Sử dụng <strong> <a href="https://vngame.tv/glossary/arven-obf-186/" class="local-link" data-wpel-link="internal">Arven (OBF-186)</a></strong> để tìm <em>Technical Machine: Evolution</em> giúp tiến hóa hàng loạt Pokemon trên Bench ngay từ lượt 1 hoặc 2.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Giai đoạn giữa trận: Sức mạnh từ sự hy sinh</mark></h4>



<p class="wp-block-paragraph">Điểm độc đáo của biến thể này là dòng <strong>Dusknoir (PRE)</strong>. Bằng cách sử dụng Ability <em>Cursed Blast</em>, bạn có thể tự hủy Dusknoir để đặt sát thương lên Pokemon đối thủ. Việc này vô tình giúp đối thủ lấy Prize nhanh hơn, từ đó kích hoạt tối đa sức mạnh đòn <em>Burning Darkness</em> của Charizard ex (gây thêm sát thương cho mỗi thẻ Prize đối thủ đã lấy).</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Giai đoạn kết thúc: Combo Briar và Maximum Belt</mark></h4>



<p class="wp-block-paragraph">Với <strong><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Maximum Belt (TEF-154)</mark></strong></strong>, Charizard ex có thể thực hiện những cú One-hit KO (OHKO) lên các Pokemon ex hệ lớn như Gholdengo ex hay Dragapult ex. Đặc biệt, lá bài <strong>Briar (PRE)</strong> cho phép bạn lấy thêm một thẻ Prize nếu Charizard ex hạ gục đối thủ khi họ còn đúng 2 thẻ Prize, tạo ra những pha &#8220;lật kèo&#8221; không tưởng.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-purple-color">Mẹo Chiến Thuật: Tối Ưu Sát Thương Với Maximum Belt (TEF-154)</mark></h4>



<p class="wp-block-paragraph">Trong Meta hiện tại, các Pokémon ex Stage 2 thường có lượng HP cực lớn (từ 310 đến 330 HP), khiến đòn <em>Burning Darkness</em> của Charizard ex đôi khi không thể kết liễu mục tiêu chỉ trong một lượt nếu đối thủ chưa lấy đủ Prize. Đây chính là lúc <strong><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Maximum Belt (TEF-154)</mark></strong></strong> trở thành &#8220;chìa khóa&#8221; giải quyết trận đấu.</p>



<ul class="wp-block-list">
<li><strong>Phá vỡ giới hạn sát thương:</strong> <strong><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Maximum Belt (TEF-154)</mark></strong></strong> cộng thêm <strong>50 sát thương</strong> trực tiếp khi tấn công vào Pokémon ex của đối thủ.</li>



<li><strong>Cú Knock-out bất ngờ:</strong> * Thông thường, nếu đối thủ mới lấy <strong>1 Prize</strong>, Charizard ex chỉ gây được <strong>210 sát thương</strong> (180 gốc + 30).
<ul class="wp-block-list">
<li>Khi gắn thêm <strong><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Maximum Belt (TEF-154)</mark></strong></strong>, con số này vọt lên <strong>260 sát thương</strong>, đủ để tiễn các Pokémon Basic ex như <em>Miraidon ex</em> hay <em>Iron Hands ex</em> lên bảng đếm số ngay lập tức.</li>
</ul>
</li>



<li><strong>Dứt điểm Mirror Match (Kèo đấu Charizard):</strong> Trong những trận đấu đối đầu với chính Charizard ex, việc sở hữu Maximum Belt giúp bạn có thể OHKO (One-hit KO) đối thủ sớm hơn một lượt Prize so với thông thường, tạo lợi thế cực lớn về nhịp độ trận đấu.</li>
</ul>
</blockquote>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-purple-color">Mẹo Nâng Cao: Vô hiệu hóa đối thủ với Klefki (SVI-096)</mark></h4>



<p class="wp-block-paragraph">Trong meta hiện tại, nhiều bộ bài dựa dẫm hoàn toàn vào Ability của các Pokémon cơ bản (Basic Pokémon) như <em>Comfey</em> hay <em>Psyduck</em>. Đây là lúc <strong>Klefki (SVI-096)</strong> tỏa sáng với kỹ năng <em>Mischievous Lock</em>.</p>



<ul class="wp-block-list">
<li><strong>Cách vận hành với Jet Energy:</strong> Thông thường, việc đưa Klefki lên vị trí Active (chiến đấu) sẽ làm tốn lượt gắn Energy để rút lui sau này. Tuy nhiên, với <strong>Jet Energy</strong>, bạn có thể:
<ul class="wp-block-list">
<li>Gắn <strong>Jet Energy</strong> vào Klefki khi nó đang ở trên Bench để đưa nó lên vị trí Active ngay lập tức mà không mất lượt Switch.</li>



<li>Khóa chặt mọi Ability của đối thủ ngay trong lượt của họ.</li>



<li>Đến lượt mình, nhờ Ability <em>Infernal Reign</em> của Charizard ex có thể tự cấp năng lượng Lửa, bạn có thể thoải mái gắn năng lượng cho chủ lực rồi rút lui Klefki về để tấn công.</li>
</ul>
</li>



<li><strong>Khắc chế cứng Gardevoir ex:</strong> Klefki cực kỳ hiệu quả khi đối đầu với Gardevoir. Nó ngăn chặn đối thủ sử dụng <em>Adrena-Brain</em>, giúp bảo vệ dàn <strong>Duskull</strong> mỏng manh trên Bench của bạn để chờ thời cơ tiến hóa.</li>
</ul>
</blockquote>
</div>
</div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">3. Phân tích kèo đấu (Matchups)</mark></h3>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Charizard ex vs. Gardevoir ex (Cân bằng)</mark></h4>



<p class="wp-block-paragraph">Đây là kèo đấu kỹ năng. <strong>Klefki (SVI-096)</strong> là &#8220;ngôi sao&#8221; ở đây khi nó khóa khả năng của đối thủ, ngăn chặn họ sử dụng các chiến thuật buff năng lượng. Hãy ưu tiên hạ gục Gardevoir ex sớm để cắt đứt nguồn cung năng lượng <em>Psychic Embrace</em>.</p>



<h4 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Charizard ex vs. Gholdengo ex (Cân bằng)</mark></h4>



<p class="wp-block-paragraph">Đây là một cuộc đua về tốc độ lấy Prize. Sử dụng <strong>Chi-Yu (PAR-029)</strong> để hạ gục Gholdengo ex chỉ với một đòn đánh hệ Lửa đơn giản. Nếu đối thủ chiếm ưu thế sớm, hãy dùng combo Dusknoir + Briar để rút ngắn khoảng cách Prize và kết thúc trận đấu.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">4. Tại sao bạn nên chơi Charizard ex trong mùa giải này?</mark></h3>



<ol start="1" class="wp-block-list">
<li><strong>Độ ổn định cao:</strong> Nhờ Pidgeot ex và hệ thống Trainer tối ưu.</li>



<li><strong>Khả năng lội ngược dòng:</strong> Càng thua sâu, Charizard ex càng mạnh.</li>



<li><strong>Linh hoạt:</strong> Có thể thích nghi với nhiều dạng Meta khác nhau nhờ dàn Pokemon hỗ trợ đa dạng (Fezandipiti ex, Klefki).</li>
</ol>



<p class="wp-block-paragraph"><strong>Kết luận:</strong> Charizard ex không chỉ dành cho những người yêu thích sự hoài niệm mà còn là lựa chọn hàng đầu cho những ai muốn leo hạng (rank) hoặc tham gia các giải đấu Pokémon TCG chuyên nghiệp.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Hy vọng bài viết này giúp bạn làm chủ được Deck Charizard ex. Chúc các bạn có những trận đấu bùng nổ!</p>
<p>Bài viết <a href="https://vngame.tv/huong-dan-build-deck-charizard-ex/" data-wpel-link="internal">(Pokémon TCG) Hướng Dẫn Build Deck Charizard ex: Kẻ Hủy Diệt Meta 2026</a> đã xuất hiện đầu tiên vào ngày <a href="https://vngame.tv" data-wpel-link="internal">vnGAME</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vngame.tv/huong-dan-build-deck-charizard-ex/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">16119</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: vngame.tv @ 2026-10-16 10:55:23 by W3 Total Cache
-->