library(tidyverse)
library(arrow)
library(gt)
library(ggrepel)
library(scales)
options(cfb.lib_only = TRUE)
source("../R/02_team_game_metrics.R")   # classify_plays(), NEEDED
source("../R/04_storyline_engine.R")
source("../R/05_viz_style.R")

TEAM <- "Syracuse"; OPP <- "Pittsburgh"
SEASON <- params$season; WEEK <- params$week
FIG_DIR <- sprintf("figures/%d_wk%02d_story", SEASON, WEEK)
SCOPE   <- sprintf("%d Week %d | %s at %s", SEASON, WEEK, TEAM, OPP)
hist <- read_parquet("~/cfb-data/processed/team_game_metrics.parquet")
cur  <- read_parquet("~/cfb-data/processed/team_game_metrics_2026.parquet")
fbs  <- readRDS("~/cfb-data/fbs_teams_2026.rds")

load_week <- function(w) {
  p <- read_parquet(sprintf("~/cfb-data/raw/pbp_inseason/pbp_%d_wk%02d.parquet", SEASON, w))
  # Raw week files are cached BEFORE harmonisation, so divisions are rebuilt
  # here from the cached FBS team list.
  p$home_team_division <- ifelse(p$home %in% fbs, "fbs", "fcs")
  p$away_team_division <- ifelse(p$away %in% fbs, "fbs", "fcs")
  p$wk <- w
  p
}
raw <- map(1:WEEK, load_week)

su_plays <- map(raw, function(p) {
  p |> select(all_of(intersect(NEEDED, names(p))), wk) |>
    filter(pos_team == TEAM) |> classify_plays() |> filter(is_scrimmage)
}) |> list_rbind() |>
  mutate(type = ifelse(pass == 1, "Pass", "Rush"),
         zone = ifelse(yards_to_goal <= 40, "Inside opponent 40", "Outside the 40"))

peers <- cur |> filter(team %in% fbs, !low_volume, is_fbs_matchup, week == WEEK)
su3   <- cur |> filter(team == TEAM, week == WEEK)

Exhibit 1 — The passing game collapses in scoring range

# Weeks 2-3 only: both FBS opponents, so the comparison is clean. Week 1's FCS
# game is excluded here rather than averaged in.
zone_split <- su_plays |>
  filter(wk >= 2) |>
  group_by(zone, type) |>
  summarise(plays = n(), sr = mean(success, na.rm = TRUE),
            epa = mean(EPA, na.rm = TRUE), .groups = "drop") |>
  mutate(zone = factor(zone, levels = c("Outside the 40", "Inside opponent 40")))

p_zone <- ggplot(zone_split, aes(x = zone, y = epa, fill = type)) +
  geom_hline(yintercept = 0, color = SU$muted, linewidth = 0.4) +
  geom_col(position = position_dodge(width = 0.72), width = 0.62) +
  geom_text(aes(label = sprintf("%+.3f\n%.0f%% success\n%d plays", epa, 100*sr, plays),
                vjust = ifelse(epa >= 0, -0.25, 1.15)),
            position = position_dodge(width = 0.72),
            size = 3.1, fontface = "bold", lineheight = 0.95, color = SU$ink) +
  scale_fill_manual(values = c(Pass = SU$orange, Rush = SU$blue)) +
  scale_y_continuous(expand = expansion(mult = c(0.28, 0.28))) +
  labs(
    title = sprintf("%s throws well until the field gets short, then it falls apart", TEAM),
    subtitle = "EPA per play by field zone | Weeks 2-3 only, both FBS opponents | Higher is better",
    x = NULL, y = "EPA per play", fill = NULL,
    caption = su_caption("2026 Weeks 2-3 | vs California and Pittsburgh")
  ) +
  theme_su(legend_pos = "top")

save_fig(p_zone, "01_pass_collapse_in_scoring_range", height = 5)

Outside the 40, Syracuse’s passing game is above average. Inside it, the success rate is cut roughly in half and EPA per play falls by more than eight tenths of a point. The run game declines in scoring range too — every offense does, as the defense gains a shorter field to defend — but nothing like this.

Exhibit 2 — Three weeks, three steps backward

by_week <- su_plays |>
  group_by(wk, type) |>
  summarise(epa = mean(EPA, na.rm = TRUE), sr = mean(success, na.rm = TRUE),
            .groups = "drop")

ends <- by_week |> filter(wk == WEEK)

p_trend <- ggplot(by_week, aes(wk, epa, color = type)) +
  annotate("rect", xmin = 0.6, xmax = 1.4, ymin = -Inf, ymax = Inf,
           fill = SU$muted, alpha = 0.10) +
  annotate("text", x = 1, y = Inf, vjust = 1.6, label = "FCS opponent",
           size = 3, fontface = "italic", color = SU$ink2) +
  geom_hline(yintercept = 0, color = SU$muted, linewidth = 0.4) +
  geom_line(linewidth = 1) +
  geom_point(size = 3) +
  geom_text_repel(data = ends, aes(label = sprintf("%s %+.3f", type, epa)),
                  nudge_x = 0.18, size = 3.4, fontface = "bold",
                  segment.color = NA, force = 0) +
  scale_color_manual(values = c(Pass = SU$orange, Rush = SU$blue), guide = "none") +
  scale_x_continuous(breaks = 1:WEEK, labels = c("Wk 1\nNew Hampshire",
                                                 "Wk 2\nCalifornia",
                                                 "Wk 3\nPittsburgh")[1:WEEK],
                     expand = expansion(mult = c(0.05, 0.22))) +
  labs(
    title = "The run game recovered against Pitt. The passing game did not.",
    subtitle = sprintf(
      "EPA per play by game | Pass: %+.3f to %+.3f to %+.3f | Week 1 came against an FCS defense and is shaded accordingly",
      by_week$epa[by_week$type=="Pass" & by_week$wk==1],
      by_week$epa[by_week$type=="Pass" & by_week$wk==2],
      by_week$epa[by_week$type=="Pass" & by_week$wk==3]),
    x = NULL, y = "EPA per play",
    caption = su_caption("2026 Weeks 1-3")
  ) +
  theme_su(legend_pos = "none")

save_fig(p_trend, "02_pass_vs_rush_by_week", height = 5)

by_week |>
  pivot_wider(names_from = type, values_from = c(epa, sr)) |>
  left_join(su_plays |> filter(pass == 1) |> group_by(wk) |>
              summarise(sack_rate = mean(sack == 1, na.rm = TRUE), .groups = "drop"),
            by = "wk") |>
  transmute(Game = c("Wk 1 — New Hampshire (FCS)", "Wk 2 — California",
                     "Wk 3 — Pittsburgh")[wk],
            `Pass EPA` = epa_Pass, `Pass success` = sr_Pass,
            `Rush EPA` = epa_Rush, `Rush success` = sr_Rush,
            `Sack rate allowed` = sack_rate) |>
  gt() |>
  tab_header(
    title = md("**The decline is not a protection problem**"),
    subtitle = md("Sack rate allowed held steady while passing efficiency fell — the line is not the story")
  ) |>
  fmt_number(columns = c(`Pass EPA`, `Rush EPA`), decimals = 3) |>
  fmt_percent(columns = c(`Pass success`, `Rush success`, `Sack rate allowed`), decimals = 1) |>
  cols_align("center", columns = -Game) |>
  tab_style(style = cell_text(weight = "bold"), locations = cells_body(columns = Game)) |>
  tab_source_note(md(paste0("**", su_caption("2026 Weeks 1-3"), "**"))) |>
  gt_theme_su()
The decline is not a protection problem
Sack rate allowed held steady while passing efficiency fell — the line is not the story
Game Pass EPA Pass success Rush EPA Rush success Sack rate allowed
Wk 1 — New Hampshire (FCS) −0.112 51.4% 0.035 55.0% 8.1%
Wk 2 — California −0.159 33.3% −0.093 37.5% 5.6%
Wk 3 — Pittsburgh −0.204 31.4% 0.130 41.7% 5.7%
Data: CollegeFootballData via cfbfastR | 2026 Weeks 1-3

Exhibit 3 — Where that ranks nationally

lg <- raw[[WEEK]] |>
  select(all_of(intersect(NEEDED, names(raw[[WEEK]]))),
         home_team_division, away_team_division) |>
  filter(home_team_division == "fbs", away_team_division == "fbs") |>
  classify_plays() |> filter(is_scrimmage, pass == 1) |>
  group_by(team = pos_team) |>
  summarise(n = n(), pass_epa = mean(EPA, na.rm = TRUE), .groups = "drop") |>
  filter(n >= 15) |>
  mutate(is_su = team == TEAM)

su_pass_rank <- as.integer(rank(-lg$pass_epa)[lg$team == TEAM])

lg <- lg |> arrange(desc(pass_epa)) |> mutate(team = factor(team, levels = team))

p_rank <- ggplot(lg, aes(x = pass_epa, y = team, color = is_su)) +
  geom_vline(xintercept = median(lg$pass_epa), linetype = "dashed",
             color = SU$muted, linewidth = 0.4) +
  geom_point(aes(size = is_su)) +
  geom_text_repel(data = filter(lg, is_su),
    aes(label = sprintf("%s — %+.3f EPA per dropback\n%s of %d",
                        team, pass_epa, scales::ordinal(su_pass_rank), nrow(lg))),
    nudge_x = -0.33, size = 3.4, fontface = "bold", segment.color = NA,
    lineheight = 0.95, force = 0) +
  scale_color_manual(values = c(`FALSE` = SU$muted, `TRUE` = SU$orange), guide = "none") +
  scale_size_manual(values = c(`FALSE` = 1.6, `TRUE` = 3.6), guide = "none") +
  labs(
    title = sprintf("%s ranked %s of %d in passing efficiency in Week 3",
                    TEAM, scales::ordinal(su_pass_rank), nrow(lg)),
    subtitle = sprintf("EPA per dropback | FBS teams with 15+ dropbacks against FBS opponents | League median %+.3f | Dashed line = median",
                       median(lg$pass_epa)),
    x = "EPA per dropback", y = NULL,
    caption = su_caption(SCOPE)
  ) +
  theme_su(legend_pos = "none") +
  theme(axis.text.y = element_text(size = 5.5))

save_fig(p_rank, "03_pass_epa_vs_peers", height = 8)

Exhibit 4 — What it cost

drives <- raw[[WEEK]] |>
  filter(pos_team == TEAM) |>
  group_by(drive_id) |>
  summarise(start = first(drive_start_yards_to_goal),
            closest = min(yards_to_goal, na.rm = TRUE),
            pts = first(drive_pts), result = first(drive_result),
            plays = n(), .groups = "drop") |>
  filter(!is.na(start), is.finite(closest))

opps <- drives |> filter(closest <= 40) |> arrange(closest)

pr_pts <- {
  v <- peers$off_pts_per_opp
  as.integer(round(rank(-v, na.last = "keep")[peers$team == TEAM]))
}

opps |>
  transmute(`Drive started` = sprintf("own %.0f", 100 - start),
            `Got as close as` = ifelse(closest == 0, "the goal line",
                                       sprintf("the %s %.0f", OPP, closest)),
            Plays = plays, Result = result, Points = pts) |>
  gt() |>
  tab_header(
    title = md(sprintf("**Seven trips inside the %s 40 produced 13 points**", OPP)),
    subtitle = md(sprintf(
      "%.2f points per scoring opportunity — %s of %d nationally, and the third straight weekly decline",
      su3$off_pts_per_opp, scales::ordinal(pr_pts), nrow(peers)))
  ) |>
  cols_align("center", columns = c(Plays, Points)) |>
  cols_align("left", columns = c(`Drive started`, `Got as close as`, Result)) |>
  data_color(columns = Points, method = "numeric", palette = SU_SEQ) |>
  tab_style(style = list(cell_fill(color = "#FFF3E6"), cell_text(weight = "bold")),
            locations = cells_body(rows = Points == 0)) |>
  tab_source_note(md(paste0("**", su_caption(SCOPE),
    "** — a scoring opportunity is any drive reaching the opponent's 40."))) |>
  gt_theme_su()
Seven trips inside the Pittsburgh 40 produced 13 points
1.86 points per scoring opportunity — 100th of 114 nationally, and the third straight weekly decline
Drive started Got as close as Plays Result Points
own 17 the goal line 14 FG 3
own 35 the Pittsburgh 1 13 TD 7
own 25 the Pittsburgh 10 7 MISSED FG 0
own 41 the Pittsburgh 16 11 FG 3
own 16 the Pittsburgh 28 15 PUNT 0
own 20 the Pittsburgh 37 12 DOWNS 0
own 25 the Pittsburgh 40 6 INT 0
Data: CollegeFootballData via cfbfastR | 2026 Week 3 | Syracuse at Pittsburgh — a scoring opportunity is any drive reaching the opponent’s 40.

Figures written to disk

figs <- list.files(FIG_DIR, pattern = "\\.png$", full.names = TRUE)
if (length(figs) == 0) cat("No figures written.\n") else {
  cat("300-dpi PNGs ready for the piece:\n\n")
  for (f in figs) cat("  ", f, "  (", round(file.info(f)$size/1024), " KB)\n", sep = "")
  cat("\nCopy the gt tables from the browser into Google Docs — they paste as\n")
  cat("native editable tables.\n")
}
## 300-dpi PNGs ready for the piece:
## 
##   figures/2026_wk03_story/01_pass_collapse_in_scoring_range.png  (127 KB)
##   figures/2026_wk03_story/02_pass_vs_rush_by_week.png  (151 KB)
##   figures/2026_wk03_story/03_pass_epa_vs_peers.png  (387 KB)
## 
## Copy the gt tables from the browser into Google Docs — they paste as
## native editable tables.
LS0tCnRpdGxlOiAiU3lyYWN1c2UgQ2FuIFRocm93IEl0IFVudGlsIHRoZSBGaWVsZCBHZXRzIFNob3J0IgpzdWJ0aXRsZTogIldlZWsgMyAyMDI2IOKAlCBTeXJhY3VzZSAxMyBhdCBQaXR0c2J1cmdoIDI3IOKAlCBleGhpYml0cyBmb3IgcHVibGljYXRpb24iCmF1dGhvcjogIkNocmlzIEppbWVuZXoiCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCIKcGFyYW1zOgogIHNlYXNvbjogMjAyNgogIHdlZWs6IDMKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIEVOQ09ESU5HOiBhIHNoZWxsIHdpdGggbm8gTEFORyBzZXQgc3RhcnRzIFIgaW4gdGhlIEMgLyBVUy1BU0NJSSBsb2NhbGUsIGFuZAojIGd0IGFuZCBnZ3Bsb3QgdGhlbiBFU0NBUEUgZXZlcnkgbm9uLUFTQ0lJIGNoYXJhY3Rlci4gRm9yY2UgVVRGLTggc28gZW0gZGFzaGVzCiMgc3Vydml2ZSBpbnRvIHB1YmxpY2F0aW9uLXJlYWR5IG91dHB1dC4KZm9yIChsb2MgaW4gYygiZW5fVVMuVVRGLTgiLCAiQy5VVEYtOCIsICJVVEYtOCIpKSB7CiAgaWYgKG56Y2hhcihzdXBwcmVzc1dhcm5pbmdzKFN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsIGxvYykpKSkgYnJlYWsKfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2LCBkcGkgPSAxNTApCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShhcnJvdykKbGlicmFyeShndCkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKYGBgCgpgYGB7ciBzdHlsZX0Kb3B0aW9ucyhjZmIubGliX29ubHkgPSBUUlVFKQpzb3VyY2UoIi4uL1IvMDJfdGVhbV9nYW1lX21ldHJpY3MuUiIpICAgIyBjbGFzc2lmeV9wbGF5cygpLCBORUVERUQKc291cmNlKCIuLi9SLzA0X3N0b3J5bGluZV9lbmdpbmUuUiIpCnNvdXJjZSgiLi4vUi8wNV92aXpfc3R5bGUuUiIpCgpURUFNIDwtICJTeXJhY3VzZSI7IE9QUCA8LSAiUGl0dHNidXJnaCIKU0VBU09OIDwtIHBhcmFtcyRzZWFzb247IFdFRUsgPC0gcGFyYW1zJHdlZWsKRklHX0RJUiA8LSBzcHJpbnRmKCJmaWd1cmVzLyVkX3drJTAyZF9zdG9yeSIsIFNFQVNPTiwgV0VFSykKU0NPUEUgICA8LSBzcHJpbnRmKCIlZCBXZWVrICVkIHwgJXMgYXQgJXMiLCBTRUFTT04sIFdFRUssIFRFQU0sIE9QUCkKYGBgCgpgYGB7ciBkYXRhfQpoaXN0IDwtIHJlYWRfcGFycXVldCgifi9jZmItZGF0YS9wcm9jZXNzZWQvdGVhbV9nYW1lX21ldHJpY3MucGFycXVldCIpCmN1ciAgPC0gcmVhZF9wYXJxdWV0KCJ+L2NmYi1kYXRhL3Byb2Nlc3NlZC90ZWFtX2dhbWVfbWV0cmljc18yMDI2LnBhcnF1ZXQiKQpmYnMgIDwtIHJlYWRSRFMoIn4vY2ZiLWRhdGEvZmJzX3RlYW1zXzIwMjYucmRzIikKCmxvYWRfd2VlayA8LSBmdW5jdGlvbih3KSB7CiAgcCA8LSByZWFkX3BhcnF1ZXQoc3ByaW50Zigifi9jZmItZGF0YS9yYXcvcGJwX2luc2Vhc29uL3BicF8lZF93ayUwMmQucGFycXVldCIsIFNFQVNPTiwgdykpCiAgIyBSYXcgd2VlayBmaWxlcyBhcmUgY2FjaGVkIEJFRk9SRSBoYXJtb25pc2F0aW9uLCBzbyBkaXZpc2lvbnMgYXJlIHJlYnVpbHQKICAjIGhlcmUgZnJvbSB0aGUgY2FjaGVkIEZCUyB0ZWFtIGxpc3QuCiAgcCRob21lX3RlYW1fZGl2aXNpb24gPC0gaWZlbHNlKHAkaG9tZSAlaW4lIGZicywgImZicyIsICJmY3MiKQogIHAkYXdheV90ZWFtX2RpdmlzaW9uIDwtIGlmZWxzZShwJGF3YXkgJWluJSBmYnMsICJmYnMiLCAiZmNzIikKICBwJHdrIDwtIHcKICBwCn0KcmF3IDwtIG1hcCgxOldFRUssIGxvYWRfd2VlaykKCnN1X3BsYXlzIDwtIG1hcChyYXcsIGZ1bmN0aW9uKHApIHsKICBwIHw+IHNlbGVjdChhbGxfb2YoaW50ZXJzZWN0KE5FRURFRCwgbmFtZXMocCkpKSwgd2spIHw+CiAgICBmaWx0ZXIocG9zX3RlYW0gPT0gVEVBTSkgfD4gY2xhc3NpZnlfcGxheXMoKSB8PiBmaWx0ZXIoaXNfc2NyaW1tYWdlKQp9KSB8PiBsaXN0X3JiaW5kKCkgfD4KICBtdXRhdGUodHlwZSA9IGlmZWxzZShwYXNzID09IDEsICJQYXNzIiwgIlJ1c2giKSwKICAgICAgICAgem9uZSA9IGlmZWxzZSh5YXJkc190b19nb2FsIDw9IDQwLCAiSW5zaWRlIG9wcG9uZW50IDQwIiwgIk91dHNpZGUgdGhlIDQwIikpCgpwZWVycyA8LSBjdXIgfD4gZmlsdGVyKHRlYW0gJWluJSBmYnMsICFsb3dfdm9sdW1lLCBpc19mYnNfbWF0Y2h1cCwgd2VlayA9PSBXRUVLKQpzdTMgICA8LSBjdXIgfD4gZmlsdGVyKHRlYW0gPT0gVEVBTSwgd2VlayA9PSBXRUVLKQpgYGAKCiMjIEV4aGliaXQgMSDigJQgVGhlIHBhc3NpbmcgZ2FtZSBjb2xsYXBzZXMgaW4gc2NvcmluZyByYW5nZQoKYGBge3IgZXhoaWJpdC0xLCBmaWcuYXNwID0gMC41fQojIFdlZWtzIDItMyBvbmx5OiBib3RoIEZCUyBvcHBvbmVudHMsIHNvIHRoZSBjb21wYXJpc29uIGlzIGNsZWFuLiBXZWVrIDEncyBGQ1MKIyBnYW1lIGlzIGV4Y2x1ZGVkIGhlcmUgcmF0aGVyIHRoYW4gYXZlcmFnZWQgaW4uCnpvbmVfc3BsaXQgPC0gc3VfcGxheXMgfD4KICBmaWx0ZXIod2sgPj0gMikgfD4KICBncm91cF9ieSh6b25lLCB0eXBlKSB8PgogIHN1bW1hcmlzZShwbGF5cyA9IG4oKSwgc3IgPSBtZWFuKHN1Y2Nlc3MsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGVwYSA9IG1lYW4oRVBBLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKSB8PgogIG11dGF0ZSh6b25lID0gZmFjdG9yKHpvbmUsIGxldmVscyA9IGMoIk91dHNpZGUgdGhlIDQwIiwgIkluc2lkZSBvcHBvbmVudCA0MCIpKSkKCnBfem9uZSA8LSBnZ3Bsb3Qoem9uZV9zcGxpdCwgYWVzKHggPSB6b25lLCB5ID0gZXBhLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9IFNVJG11dGVkLCBsaW5ld2lkdGggPSAwLjQpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43MiksIHdpZHRoID0gMC42MikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlKy4zZlxuJS4wZiUlIHN1Y2Nlc3NcbiVkIHBsYXlzIiwgZXBhLCAxMDAqc3IsIHBsYXlzKSwKICAgICAgICAgICAgICAgIHZqdXN0ID0gaWZlbHNlKGVwYSA+PSAwLCAtMC4yNSwgMS4xNSkpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43MiksCiAgICAgICAgICAgIHNpemUgPSAzLjEsIGZvbnRmYWNlID0gImJvbGQiLCBsaW5laGVpZ2h0ID0gMC45NSwgY29sb3IgPSBTVSRpbmspICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKFBhc3MgPSBTVSRvcmFuZ2UsIFJ1c2ggPSBTVSRibHVlKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4yOCwgMC4yOCkpKSArCiAgbGFicygKICAgIHRpdGxlID0gc3ByaW50ZigiJXMgdGhyb3dzIHdlbGwgdW50aWwgdGhlIGZpZWxkIGdldHMgc2hvcnQsIHRoZW4gaXQgZmFsbHMgYXBhcnQiLCBURUFNKSwKICAgIHN1YnRpdGxlID0gIkVQQSBwZXIgcGxheSBieSBmaWVsZCB6b25lIHwgV2Vla3MgMi0zIG9ubHksIGJvdGggRkJTIG9wcG9uZW50cyB8IEhpZ2hlciBpcyBiZXR0ZXIiLAogICAgeCA9IE5VTEwsIHkgPSAiRVBBIHBlciBwbGF5IiwgZmlsbCA9IE5VTEwsCiAgICBjYXB0aW9uID0gc3VfY2FwdGlvbigiMjAyNiBXZWVrcyAyLTMgfCB2cyBDYWxpZm9ybmlhIGFuZCBQaXR0c2J1cmdoIikKICApICsKICB0aGVtZV9zdShsZWdlbmRfcG9zID0gInRvcCIpCgpzYXZlX2ZpZyhwX3pvbmUsICIwMV9wYXNzX2NvbGxhcHNlX2luX3Njb3JpbmdfcmFuZ2UiLCBoZWlnaHQgPSA1KQpgYGAKCk91dHNpZGUgdGhlIDQwLCBTeXJhY3VzZSdzIHBhc3NpbmcgZ2FtZSBpcyBhYm92ZSBhdmVyYWdlLiBJbnNpZGUgaXQsIHRoZQpzdWNjZXNzIHJhdGUgaXMgY3V0IHJvdWdobHkgaW4gaGFsZiBhbmQgRVBBIHBlciBwbGF5IGZhbGxzIGJ5IG1vcmUgdGhhbgplaWdodCB0ZW50aHMgb2YgYSBwb2ludC4gVGhlIHJ1biBnYW1lIGRlY2xpbmVzIGluIHNjb3JpbmcgcmFuZ2UgdG9vIOKAlCBldmVyeQpvZmZlbnNlIGRvZXMsIGFzIHRoZSBkZWZlbnNlIGdhaW5zIGEgc2hvcnRlciBmaWVsZCB0byBkZWZlbmQg4oCUIGJ1dCBub3RoaW5nCmxpa2UgdGhpcy4KCiMjIEV4aGliaXQgMiDigJQgVGhyZWUgd2Vla3MsIHRocmVlIHN0ZXBzIGJhY2t3YXJkCgpgYGB7ciBleGhpYml0LTIsIGZpZy5hc3AgPSAwLjV9CmJ5X3dlZWsgPC0gc3VfcGxheXMgfD4KICBncm91cF9ieSh3aywgdHlwZSkgfD4KICBzdW1tYXJpc2UoZXBhID0gbWVhbihFUEEsIG5hLnJtID0gVFJVRSksIHNyID0gbWVhbihzdWNjZXNzLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiKQoKZW5kcyA8LSBieV93ZWVrIHw+IGZpbHRlcih3ayA9PSBXRUVLKQoKcF90cmVuZCA8LSBnZ3Bsb3QoYnlfd2VlaywgYWVzKHdrLCBlcGEsIGNvbG9yID0gdHlwZSkpICsKICBhbm5vdGF0ZSgicmVjdCIsIHhtaW4gPSAwLjYsIHhtYXggPSAxLjQsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mLAogICAgICAgICAgIGZpbGwgPSBTVSRtdXRlZCwgYWxwaGEgPSAwLjEwKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IEluZiwgdmp1c3QgPSAxLjYsIGxhYmVsID0gIkZDUyBvcHBvbmVudCIsCiAgICAgICAgICAgc2l6ZSA9IDMsIGZvbnRmYWNlID0gIml0YWxpYyIsIGNvbG9yID0gU1UkaW5rMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gU1UkbXV0ZWQsIGxpbmV3aWR0aCA9IDAuNCkgKwogIGdlb21fbGluZShsaW5ld2lkdGggPSAxKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZW5kcywgYWVzKGxhYmVsID0gc3ByaW50ZigiJXMgJSsuM2YiLCB0eXBlLCBlcGEpKSwKICAgICAgICAgICAgICAgICAgbnVkZ2VfeCA9IDAuMTgsIHNpemUgPSAzLjQsIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gTkEsIGZvcmNlID0gMCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKFBhc3MgPSBTVSRvcmFuZ2UsIFJ1c2ggPSBTVSRibHVlKSwgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTpXRUVLLCBsYWJlbHMgPSBjKCJXayAxXG5OZXcgSGFtcHNoaXJlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXayAyXG5DYWxpZm9ybmlhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXayAzXG5QaXR0c2J1cmdoIilbMTpXRUVLXSwKICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMDUsIDAuMjIpKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJUaGUgcnVuIGdhbWUgcmVjb3ZlcmVkIGFnYWluc3QgUGl0dC4gVGhlIHBhc3NpbmcgZ2FtZSBkaWQgbm90LiIsCiAgICBzdWJ0aXRsZSA9IHNwcmludGYoCiAgICAgICJFUEEgcGVyIHBsYXkgYnkgZ2FtZSB8IFBhc3M6ICUrLjNmIHRvICUrLjNmIHRvICUrLjNmIHwgV2VlayAxIGNhbWUgYWdhaW5zdCBhbiBGQ1MgZGVmZW5zZSBhbmQgaXMgc2hhZGVkIGFjY29yZGluZ2x5IiwKICAgICAgYnlfd2VlayRlcGFbYnlfd2VlayR0eXBlPT0iUGFzcyIgJiBieV93ZWVrJHdrPT0xXSwKICAgICAgYnlfd2VlayRlcGFbYnlfd2VlayR0eXBlPT0iUGFzcyIgJiBieV93ZWVrJHdrPT0yXSwKICAgICAgYnlfd2VlayRlcGFbYnlfd2VlayR0eXBlPT0iUGFzcyIgJiBieV93ZWVrJHdrPT0zXSksCiAgICB4ID0gTlVMTCwgeSA9ICJFUEEgcGVyIHBsYXkiLAogICAgY2FwdGlvbiA9IHN1X2NhcHRpb24oIjIwMjYgV2Vla3MgMS0zIikKICApICsKICB0aGVtZV9zdShsZWdlbmRfcG9zID0gIm5vbmUiKQoKc2F2ZV9maWcocF90cmVuZCwgIjAyX3Bhc3NfdnNfcnVzaF9ieV93ZWVrIiwgaGVpZ2h0ID0gNSkKYGBgCgpgYGB7ciBleGhpYml0LTJifQpieV93ZWVrIHw+CiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHR5cGUsIHZhbHVlc19mcm9tID0gYyhlcGEsIHNyKSkgfD4KICBsZWZ0X2pvaW4oc3VfcGxheXMgfD4gZmlsdGVyKHBhc3MgPT0gMSkgfD4gZ3JvdXBfYnkod2spIHw+CiAgICAgICAgICAgICAgc3VtbWFyaXNlKHNhY2tfcmF0ZSA9IG1lYW4oc2FjayA9PSAxLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKSwKICAgICAgICAgICAgYnkgPSAid2siKSB8PgogIHRyYW5zbXV0ZShHYW1lID0gYygiV2sgMSDigJQgTmV3IEhhbXBzaGlyZSAoRkNTKSIsICJXayAyIOKAlCBDYWxpZm9ybmlhIiwKICAgICAgICAgICAgICAgICAgICAgIldrIDMg4oCUIFBpdHRzYnVyZ2giKVt3a10sCiAgICAgICAgICAgIGBQYXNzIEVQQWAgPSBlcGFfUGFzcywgYFBhc3Mgc3VjY2Vzc2AgPSBzcl9QYXNzLAogICAgICAgICAgICBgUnVzaCBFUEFgID0gZXBhX1J1c2gsIGBSdXNoIHN1Y2Nlc3NgID0gc3JfUnVzaCwKICAgICAgICAgICAgYFNhY2sgcmF0ZSBhbGxvd2VkYCA9IHNhY2tfcmF0ZSkgfD4KICBndCgpIHw+CiAgdGFiX2hlYWRlcigKICAgIHRpdGxlID0gbWQoIioqVGhlIGRlY2xpbmUgaXMgbm90IGEgcHJvdGVjdGlvbiBwcm9ibGVtKioiKSwKICAgIHN1YnRpdGxlID0gbWQoIlNhY2sgcmF0ZSBhbGxvd2VkIGhlbGQgc3RlYWR5IHdoaWxlIHBhc3NpbmcgZWZmaWNpZW5jeSBmZWxsIOKAlCB0aGUgbGluZSBpcyBub3QgdGhlIHN0b3J5IikKICApIHw+CiAgZm10X251bWJlcihjb2x1bW5zID0gYyhgUGFzcyBFUEFgLCBgUnVzaCBFUEFgKSwgZGVjaW1hbHMgPSAzKSB8PgogIGZtdF9wZXJjZW50KGNvbHVtbnMgPSBjKGBQYXNzIHN1Y2Nlc3NgLCBgUnVzaCBzdWNjZXNzYCwgYFNhY2sgcmF0ZSBhbGxvd2VkYCksIGRlY2ltYWxzID0gMSkgfD4KICBjb2xzX2FsaWduKCJjZW50ZXIiLCBjb2x1bW5zID0gLUdhbWUpIHw+CiAgdGFiX3N0eWxlKHN0eWxlID0gY2VsbF90ZXh0KHdlaWdodCA9ICJib2xkIiksIGxvY2F0aW9ucyA9IGNlbGxzX2JvZHkoY29sdW1ucyA9IEdhbWUpKSB8PgogIHRhYl9zb3VyY2Vfbm90ZShtZChwYXN0ZTAoIioqIiwgc3VfY2FwdGlvbigiMjAyNiBXZWVrcyAxLTMiKSwgIioqIikpKSB8PgogIGd0X3RoZW1lX3N1KCkKYGBgCgojIyBFeGhpYml0IDMg4oCUIFdoZXJlIHRoYXQgcmFua3MgbmF0aW9uYWxseQoKYGBge3IgZXhoaWJpdC0zLCBmaWcuYXNwID0gMC44NX0KbGcgPC0gcmF3W1tXRUVLXV0gfD4KICBzZWxlY3QoYWxsX29mKGludGVyc2VjdChORUVERUQsIG5hbWVzKHJhd1tbV0VFS11dKSkpLAogICAgICAgICBob21lX3RlYW1fZGl2aXNpb24sIGF3YXlfdGVhbV9kaXZpc2lvbikgfD4KICBmaWx0ZXIoaG9tZV90ZWFtX2RpdmlzaW9uID09ICJmYnMiLCBhd2F5X3RlYW1fZGl2aXNpb24gPT0gImZicyIpIHw+CiAgY2xhc3NpZnlfcGxheXMoKSB8PiBmaWx0ZXIoaXNfc2NyaW1tYWdlLCBwYXNzID09IDEpIHw+CiAgZ3JvdXBfYnkodGVhbSA9IHBvc190ZWFtKSB8PgogIHN1bW1hcmlzZShuID0gbigpLCBwYXNzX2VwYSA9IG1lYW4oRVBBLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKSB8PgogIGZpbHRlcihuID49IDE1KSB8PgogIG11dGF0ZShpc19zdSA9IHRlYW0gPT0gVEVBTSkKCnN1X3Bhc3NfcmFuayA8LSBhcy5pbnRlZ2VyKHJhbmsoLWxnJHBhc3NfZXBhKVtsZyR0ZWFtID09IFRFQU1dKQoKbGcgPC0gbGcgfD4gYXJyYW5nZShkZXNjKHBhc3NfZXBhKSkgfD4gbXV0YXRlKHRlYW0gPSBmYWN0b3IodGVhbSwgbGV2ZWxzID0gdGVhbSkpCgpwX3JhbmsgPC0gZ2dwbG90KGxnLCBhZXMoeCA9IHBhc3NfZXBhLCB5ID0gdGVhbSwgY29sb3IgPSBpc19zdSkpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWRpYW4obGckcGFzc19lcGEpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLAogICAgICAgICAgICAgY29sb3IgPSBTVSRtdXRlZCwgbGluZXdpZHRoID0gMC40KSArCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGlzX3N1KSkgKwogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZmlsdGVyKGxnLCBpc19zdSksCiAgICBhZXMobGFiZWwgPSBzcHJpbnRmKCIlcyDigJQgJSsuM2YgRVBBIHBlciBkcm9wYmFja1xuJXMgb2YgJWQiLAogICAgICAgICAgICAgICAgICAgICAgICB0ZWFtLCBwYXNzX2VwYSwgc2NhbGVzOjpvcmRpbmFsKHN1X3Bhc3NfcmFuayksIG5yb3cobGcpKSksCiAgICBudWRnZV94ID0gLTAuMzMsIHNpemUgPSAzLjQsIGZvbnRmYWNlID0gImJvbGQiLCBzZWdtZW50LmNvbG9yID0gTkEsCiAgICBsaW5laGVpZ2h0ID0gMC45NSwgZm9yY2UgPSAwKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZBTFNFYCA9IFNVJG11dGVkLCBgVFJVRWAgPSBTVSRvcmFuZ2UpLCBndWlkZSA9ICJub25lIikgKwogIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMoYEZBTFNFYCA9IDEuNiwgYFRSVUVgID0gMy42KSwgZ3VpZGUgPSAibm9uZSIpICsKICBsYWJzKAogICAgdGl0bGUgPSBzcHJpbnRmKCIlcyByYW5rZWQgJXMgb2YgJWQgaW4gcGFzc2luZyBlZmZpY2llbmN5IGluIFdlZWsgMyIsCiAgICAgICAgICAgICAgICAgICAgVEVBTSwgc2NhbGVzOjpvcmRpbmFsKHN1X3Bhc3NfcmFuayksIG5yb3cobGcpKSwKICAgIHN1YnRpdGxlID0gc3ByaW50ZigiRVBBIHBlciBkcm9wYmFjayB8IEZCUyB0ZWFtcyB3aXRoIDE1KyBkcm9wYmFja3MgYWdhaW5zdCBGQlMgb3Bwb25lbnRzIHwgTGVhZ3VlIG1lZGlhbiAlKy4zZiB8IERhc2hlZCBsaW5lID0gbWVkaWFuIiwKICAgICAgICAgICAgICAgICAgICAgICBtZWRpYW4obGckcGFzc19lcGEpKSwKICAgIHggPSAiRVBBIHBlciBkcm9wYmFjayIsIHkgPSBOVUxMLAogICAgY2FwdGlvbiA9IHN1X2NhcHRpb24oU0NPUEUpCiAgKSArCiAgdGhlbWVfc3UobGVnZW5kX3BvcyA9ICJub25lIikgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1LjUpKQoKc2F2ZV9maWcocF9yYW5rLCAiMDNfcGFzc19lcGFfdnNfcGVlcnMiLCBoZWlnaHQgPSA4KQpgYGAKCiMjIEV4aGliaXQgNCDigJQgV2hhdCBpdCBjb3N0CgpgYGB7ciBleGhpYml0LTR9CmRyaXZlcyA8LSByYXdbW1dFRUtdXSB8PgogIGZpbHRlcihwb3NfdGVhbSA9PSBURUFNKSB8PgogIGdyb3VwX2J5KGRyaXZlX2lkKSB8PgogIHN1bW1hcmlzZShzdGFydCA9IGZpcnN0KGRyaXZlX3N0YXJ0X3lhcmRzX3RvX2dvYWwpLAogICAgICAgICAgICBjbG9zZXN0ID0gbWluKHlhcmRzX3RvX2dvYWwsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHB0cyA9IGZpcnN0KGRyaXZlX3B0cyksIHJlc3VsdCA9IGZpcnN0KGRyaXZlX3Jlc3VsdCksCiAgICAgICAgICAgIHBsYXlzID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSB8PgogIGZpbHRlcighaXMubmEoc3RhcnQpLCBpcy5maW5pdGUoY2xvc2VzdCkpCgpvcHBzIDwtIGRyaXZlcyB8PiBmaWx0ZXIoY2xvc2VzdCA8PSA0MCkgfD4gYXJyYW5nZShjbG9zZXN0KQoKcHJfcHRzIDwtIHsKICB2IDwtIHBlZXJzJG9mZl9wdHNfcGVyX29wcAogIGFzLmludGVnZXIocm91bmQocmFuaygtdiwgbmEubGFzdCA9ICJrZWVwIilbcGVlcnMkdGVhbSA9PSBURUFNXSkpCn0KCm9wcHMgfD4KICB0cmFuc211dGUoYERyaXZlIHN0YXJ0ZWRgID0gc3ByaW50Zigib3duICUuMGYiLCAxMDAgLSBzdGFydCksCiAgICAgICAgICAgIGBHb3QgYXMgY2xvc2UgYXNgID0gaWZlbHNlKGNsb3Nlc3QgPT0gMCwgInRoZSBnb2FsIGxpbmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCJ0aGUgJXMgJS4wZiIsIE9QUCwgY2xvc2VzdCkpLAogICAgICAgICAgICBQbGF5cyA9IHBsYXlzLCBSZXN1bHQgPSByZXN1bHQsIFBvaW50cyA9IHB0cykgfD4KICBndCgpIHw+CiAgdGFiX2hlYWRlcigKICAgIHRpdGxlID0gbWQoc3ByaW50ZigiKipTZXZlbiB0cmlwcyBpbnNpZGUgdGhlICVzIDQwIHByb2R1Y2VkIDEzIHBvaW50cyoqIiwgT1BQKSksCiAgICBzdWJ0aXRsZSA9IG1kKHNwcmludGYoCiAgICAgICIlLjJmIHBvaW50cyBwZXIgc2NvcmluZyBvcHBvcnR1bml0eSDigJQgJXMgb2YgJWQgbmF0aW9uYWxseSwgYW5kIHRoZSB0aGlyZCBzdHJhaWdodCB3ZWVrbHkgZGVjbGluZSIsCiAgICAgIHN1MyRvZmZfcHRzX3Blcl9vcHAsIHNjYWxlczo6b3JkaW5hbChwcl9wdHMpLCBucm93KHBlZXJzKSkpCiAgKSB8PgogIGNvbHNfYWxpZ24oImNlbnRlciIsIGNvbHVtbnMgPSBjKFBsYXlzLCBQb2ludHMpKSB8PgogIGNvbHNfYWxpZ24oImxlZnQiLCBjb2x1bW5zID0gYyhgRHJpdmUgc3RhcnRlZGAsIGBHb3QgYXMgY2xvc2UgYXNgLCBSZXN1bHQpKSB8PgogIGRhdGFfY29sb3IoY29sdW1ucyA9IFBvaW50cywgbWV0aG9kID0gIm51bWVyaWMiLCBwYWxldHRlID0gU1VfU0VRKSB8PgogIHRhYl9zdHlsZShzdHlsZSA9IGxpc3QoY2VsbF9maWxsKGNvbG9yID0gIiNGRkYzRTYiKSwgY2VsbF90ZXh0KHdlaWdodCA9ICJib2xkIikpLAogICAgICAgICAgICBsb2NhdGlvbnMgPSBjZWxsc19ib2R5KHJvd3MgPSBQb2ludHMgPT0gMCkpIHw+CiAgdGFiX3NvdXJjZV9ub3RlKG1kKHBhc3RlMCgiKioiLCBzdV9jYXB0aW9uKFNDT1BFKSwKICAgICIqKiDigJQgYSBzY29yaW5nIG9wcG9ydHVuaXR5IGlzIGFueSBkcml2ZSByZWFjaGluZyB0aGUgb3Bwb25lbnQncyA0MC4iKSkpIHw+CiAgZ3RfdGhlbWVfc3UoKQpgYGAKCiMjIEZpZ3VyZXMgd3JpdHRlbiB0byBkaXNrCgpgYGB7ciBtYW5pZmVzdH0KZmlncyA8LSBsaXN0LmZpbGVzKEZJR19ESVIsIHBhdHRlcm4gPSAiXFwucG5nJCIsIGZ1bGwubmFtZXMgPSBUUlVFKQppZiAobGVuZ3RoKGZpZ3MpID09IDApIGNhdCgiTm8gZmlndXJlcyB3cml0dGVuLlxuIikgZWxzZSB7CiAgY2F0KCIzMDAtZHBpIFBOR3MgcmVhZHkgZm9yIHRoZSBwaWVjZTpcblxuIikKICBmb3IgKGYgaW4gZmlncykgY2F0KCIgICIsIGYsICIgICgiLCByb3VuZChmaWxlLmluZm8oZikkc2l6ZS8xMDI0KSwgIiBLQilcbiIsIHNlcCA9ICIiKQogIGNhdCgiXG5Db3B5IHRoZSBndCB0YWJsZXMgZnJvbSB0aGUgYnJvd3NlciBpbnRvIEdvb2dsZSBEb2NzIOKAlCB0aGV5IHBhc3RlIGFzXG4iKQogIGNhdCgibmF0aXZlIGVkaXRhYmxlIHRhYmxlcy5cbiIpCn0KYGBgCg==