Load libraries

# install.packages(c("bioacoustics", "tuneR", "seewave", "dplyr", "tidyverse", "lubridate", "tools", "ggplot2", "ggpmisc", "plotly", "stringr", "monitoR", "pbapply", "foreach", "doParallel", "R.utils"), type="binary")
library(bioacoustics)
library(tuneR)
library(seewave)
library(dplyr)
library(tidyverse)
library(lubridate)
library(tools)
library(ggplot2)
library(ggpmisc)
library(plotly)
library(stringr)
library(foreach)
library(doParallel)
library(R.utils)
library(readxl)
library(pbapply)
library(monitoR)
ymd_hms = function(dt) {
  lubridate::ymd_hms(dt, tz="Pacific/Auckland")
}
dmy_hms = function(dt) {
  lubridate::dmy_hms(dt, tz="Pacific/Auckland")
}
options(stringsAsFactors = FALSE)

Load RFID log files

files = list.files("data/2019 RFID", pattern = "*.TXT$", recursive = TRUE, full.names=TRUE)
csvs = lapply(files, function(path) {
  csv = read.csv(path, header=FALSE, col.names = c("year", "month", "day", "hour","minute_sec", "id", "ext", "int", "msg"))
  csv$site = strsplit(strsplit(path, "/")[[1]][4], "_")[[1]][1]
  csv
})
rfid = do.call(rbind, csvs)
rm(csvs)
rfid$datetime = paste0(rfid$year, "-", rfid$month, "-", rfid$day, " ", rfid$hour, ":", rfid$minute_sec)
rfid$datetime = ymd_hms(rfid$datetime)
ggplot(rfid, aes(datetime)) +
  geom_freqpoly(binwidth = 60 * 60)

Reference spectrograms for playback files

html_tag_audio <- function(file, type = c("wav")) {
  type <- match.arg(type)
  htmltools::tags$h3(
    basename(file),
    htmltools::tags$audio(
      controls = "",
      htmltools::tags$source(
        src = file,
        type = glue::glue("audio/{type}", type = type)
      )
    )
  )
}
files = Sys.glob("./clips/PB*.wav")
PB_wavs = lapply(files, function(f) {
  wav = read_audio(f)
  wav_length = round(length(wav) / wav@samp.rate, 2)
  spectro(wav, main=paste0(basename(f), " ", wav_length, "s"), osc=TRUE)
  print(html_tag_audio(f))
  data.frame(filename=basename(f), wav_length)
})

PB0009zp.wav

PB0014zp.wav

PB0023.wav

PB0024.wav

PBB11up.wav

PB_wavs = do.call(rbind, PB_wavs)

Build a reference dataframe showing which wave files correspond to which times

read_BAR_metadata = function(files) {
  df = pblapply(files, function(f) {
    bits = strsplit(basename(f), "_")[[1]]
    start = ymd_hms(paste(bits[2], bits[3]))
    site = strsplit(f, "/")[[1]][4]
    try({
      wav_header = readWave(f, header=TRUE)
      duration = wav_header$samples / wav_header$sample.rate
      end = start + duration
      return(data.frame(filename = f, base_filename = basename(f), site = site, start = start, end = end, samples = wav_header$samples, sr = wav_header$sample.rate, duration=duration, interval = interval(start, end)))
    })
    NULL
  })
  df = do.call(rbind, df)
  df
}
if (file.exists("misc/BAR_timespans.csv")) {
  df = read.csv("misc/BAR_timespans.csv")
  df$start = ymd_hms(df$start)
  df$end = ymd_hms(df$end)
  df$interval = interval(df$start, df$end)
} else {
  files = list.files("data/2019 BAR recordings", pattern = "*.wav$", recursive = TRUE, full.names=TRUE)
  df = read_BAR_metadata(files)
  write.csv(df, "misc/BAR_timespans.csv", row.names = FALSE)
}
df[c("site", "interval")]

Select a few playback entries to check

PB_to_check = "PB0023.wav"
site_to_check = "NNK30"
filtered_rfid = filter(rfid, msg == PB_to_check & site == site_to_check)
filtered_df = filter(df, site == site_to_check)
filtered_rfid$corrected_datetime = filtered_rfid$datetime
filtered_rfid$match = lapply(1:nrow(filtered_rfid), function(i) {
  dt = filtered_rfid$corrected_datetime[i]
  site = filtered_rfid$site[i]
  indices = which(dt %within% filtered_df$interval)
  if (length(indices) == 0) indices = NA
  indices
})
print(paste("There are", nrow(filtered_rfid), PB_to_check, "playbacks, of which", sum(!is.na(filtered_rfid$match)), "have recordings"))
[1] "There are 307 PB0023.wav playbacks, of which 48 have recordings"
# A few randomly
datetimes_to_check = sort(sample(filtered_rfid$corrected_datetime[!is.na(filtered_rfid$match)], 5))
# Ones from the 13th
#datetimes_to_check = filtered_rfid$corrected_datetime[!is.na(filtered_rfid$match) & filtered_rfid$datetime > ymd_hms("2019-11-20 00:00:00") & filtered_rfid$datetime < ymd_hms("2019-11-20 23:59:59")]
#datetimes_to_check = filtered_rfid$corrected_datetime[!is.na(filtered_rfid$match) & filtered_rfid$corrected_datetime %within% interval(ymd_hms("2019-12-13 16:45:00"), ymd_hms("2019-12-13 17:13:00"))]
print(datetimes_to_check)
[1] "2019-12-13 15:04:16 NZDT" "2019-12-13 15:24:06 NZDT" "2019-12-13 16:38:35 NZDT" "2019-12-14 06:20:17 NZDT"
[5] "2019-12-14 06:39:53 NZDT"
#dt = ymd_hms("2019-12-13 16:45:04")
setWavPlayer("play") # OS dependent
checked_clips = lapply(datetimes_to_check, function(dt) {
  match = filtered_rfid$match[filtered_rfid$corrected_datetime == dt][[1]]
  f = filtered_df$filename[match]
  clip_start = time_length(dt - filtered_df$start[match], unit = "seconds")
  clip_end = clip_start + 1
  print(paste(f, dt, clip_start, clip_end))
  wav = read_audio(f, from = clip_start, to = clip_end)
  spectro(wav, main=dt, osc=TRUE)
  #play(wav)
  wav
})
[1] "data/2019 BAR recordings/NNK Kamahi nests/NNK30/NNK30_20191213_STUDY [-39.1072 176.8183]/NNK30_20191213_144041_SunriseToSunset [-39.1072 176.8183].wav 2019-12-13 15:04:16 1415 1416"
[1] "data/2019 BAR recordings/NNK Kamahi nests/NNK30/NNK30_20191213_STUDY [-39.1072 176.8183]/NNK30_20191213_144041_SunriseToSunset [-39.1072 176.8183].wav 2019-12-13 15:24:06 2605 2606"

[1] "data/2019 BAR recordings/NNK Kamahi nests/NNK30/NNK30_20191213_STUDY [-39.1072 176.8183]/NNK30_20191213_144041_SunriseToSunset [-39.1072 176.8183].wav 2019-12-13 16:38:35 7074 7075"

[1] "data/2019 BAR recordings/NNK Kamahi nests/NNK30/NNK30_20191214_STUDY [-39.1072 176.8183]/NNK30_20191214_044000_SunriseToSunset [-39.1072 176.8183].wav 2019-12-14 06:20:17 6017 6018"

[1] "data/2019 BAR recordings/NNK Kamahi nests/NNK30/NNK30_20191214_STUDY [-39.1072 176.8183]/NNK30_20191214_044000_SunriseToSunset [-39.1072 176.8183].wav 2019-12-14 06:39:53 7193 7194"

Write out a csv of approx positions of PB events per wave file

if (file.exists("misc/approx_offsets.csv")) {
  approx_offsets_guide = read.csv("misc/approx_offsets.csv")
} else {
  filtered_rfid = rfid[str_extract(rfid$msg, "^.{2}") == "PB",]
  approx_offsets = lapply(1:nrow(df), function(i) {
    matched_events = filtered_rfid[filtered_rfid$site == df$site[i] & filtered_rfid$datetime %within% df$interval[i],]
    if (nrow(matched_events) == 0) {
      return(NULL)
    }
    if (nrow(matched_events) > 10) {
      matched_events = sample_n(matched_events, 10)
    }
    offsets = time_length(matched_events$datetime - df$start[i])
    data.frame(filename = df$filename[i], offset = offsets, site = matched_events$site, pb = matched_events$msg, datetime = matched_events$datetime)
  })
  approx_offsets_guide = do.call(rbind, approx_offsets)
  write.csv(approx_offsets, "misc/approx_offsets.csv", row.names = FALSE)
}

Compare annotations with nearest RFID log

annotations = lapply(list.files("misc", "*.txt", full.names=TRUE), function(f) {
  if (str_detect(f, "ZOOM")) {
    return(NULL)
  } else if (str_detect(f, "selections")) {
    x = read.table(f, sep="\t", header=TRUE)
    x$datetime = ymd_hms(paste(x$Begin.Date, x$Begin.Clock.Time))
    x = x[c("Begin.Time..s.", "End.Time..s.", "Individual.ID", "Nest.ID", "datetime")]
  } else {
    x = read.table(f, col.names = c("Begin.Time..s.", "End.Time..s.", "Individual.ID"))[c(TRUE, FALSE),]
    x$Begin.Time..s. = as.numeric(x$Begin.Time..s.)
    x$End.Time..s. = as.numeric(x$Begin.Time..s.)
    x$Nest.ID = strsplit(basename(f), "_")[[1]][1]
    x$datetime = df$start[df$base_filename == str_replace(basename(f), ".txt", ".wav")] + x$Begin.Time..s.
  }
  x$filename = paste0(str_replace(basename(f), ".Band.Limited.Energy.Detector.selections.txt|.txt", ""), ".wav")
  x
})
annotations = do.call(rbind, annotations)
annotations$offset = rep(NA, nrow(annotations))
annotations$Individual.ID[annotations$Individual.ID == "PB0009" | annotations$Individual.ID == "PB009"] = "PB0009zp"
annotations$Nest.ID[annotations$Nest.ID == "NKN31"] = "NNK31"
for (i in 1:nrow(annotations)) {
  if (substring(annotations$Individual.ID[i], 0, 2) == "PB") {
    filtered_rfid = filter(rfid, annotations$Nest.ID[i] == site & msg == paste0(annotations$Individual.ID[i], ".wav"))
    matched_rec_start = df$start[df$site == annotations$Nest.ID[i] & annotations$datetime[i] %within% df$interval]
    diffs = time_length(filtered_rfid$datetime - annotations$datetime[i])
    min_i = which.min(abs(diffs))
    offset = diffs[min_i]
    if (length(offset) == 1) {
      annotations$offset[i] = offset
    }
  }
}

approx_offsets = read_excel("misc/approx_offsets.xlsx")
Expecting numeric in E3899 / R3899C5: got 'Wind'Expecting numeric in E3900 / R3900C5: got 'Wind'Expecting numeric in E3909 / R3909C5: got 'Wind'Expecting numeric in E3910 / R3910C5: got 'Wind'Expecting numeric in E3919 / R3919C5: got 'Wind'Expecting numeric in E3920 / R3920C5: got 'Wind'Expecting numeric in E3929 / R3929C5: got 'Wind'Expecting numeric in E3930 / R3930C5: got 'Wind'Expecting numeric in E3939 / R3939C5: got 'Wind'Expecting numeric in E3940 / R3940C5: got 'Wind'Expecting numeric in E3949 / R3949C5: got 'Wind'Expecting numeric in E3950 / R3950C5: got 'Wind'Expecting numeric in E3959 / R3959C5: got 'Wind'Expecting numeric in E3960 / R3960C5: got 'PB didn't play'Expecting numeric in E3969 / R3969C5: got 'PB didn't play'Expecting numeric in E4908 / R4908C5: got 'not found- in next rec'Expecting numeric in E4959 / R4959C5: got 'PB not playing'Expecting numeric in E4988 / R4988C5: got 'PB not playing'Expecting numeric in E4989 / R4989C5: got 'PB not playing'Expecting numeric in E4993 / R4993C5: got 'PB not playing'Expecting numeric in E4996 / R4996C5: got 'PB not playing'Expecting numeric in E4998 / R4998C5: got 'PB not playing'
names(approx_offsets) = c("filename", "guide_start", NA, NA, "Begin.Time..s.", "Nest.ID", "Individual.ID", "datetime", NA)
annotations = within(annotations, rm(End.Time..s.))
approx_offsets$offset = approx_offsets$guide_start - approx_offsets$Begin.Time..s.
approx_offsets$filename = basename(approx_offsets$filename)
approx_offsets$Individual.ID = str_replace(approx_offsets$Individual.ID, ".wav", "")
annotations = rbind(annotations, approx_offsets[names(annotations)])

annotations = na.omit(annotations)

filtered_annotations = filter(annotations, offset > -60 & Nest.ID == "NNS1")
ggplot(data = filtered_annotations, aes(x=Begin.Time..s., y=offset, color=substr(basename(filename), start = 6, stop = 20))) +
    geom_point() +
    geom_smooth(method = "lm") +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE) +
    labs(color='file datetime')

filtered_annotations = filter(annotations, Nest.ID == "NNBB12")
ggplot(data = filtered_annotations, aes(x=Begin.Time..s., y=offset)) +
    geom_point() +
    geom_smooth(method = "lm") +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE)

ggplot(data = annotations, aes(x=Begin.Time..s., y=offset, color=Nest.ID)) +
    geom_point() +
    geom_smooth(method = "lm") +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE)

Something is wrong with the RFID chip clock - it’s drifting significantly. More data points will help - let’s autodetect PB in the audio files

monitoR seems too unreliable for this task, unfortunately

The reason for the drift is SD card write speed. BAR logfiles indicate the true time packed, so if we read the BAR logfiles we can determine the true sampling rate, and correct it. In some cases, it’ll be just under 44.1KHz.

files = list.files("data/2019 BAR recordings", pattern = "logfile.*txt$", recursive = TRUE, full.names=TRUE)
sites = sapply(strsplit(files, "/"), "[[", 4)
df$true_length = rep(NA, nrow(df))
for (site in unique(sites)) {
  files_for_site = files[site == sites]
  lines = unlist(lapply(files_for_site, readLines))
  rec = str_subset(lines, "Recording")
  dt = dmy_hms(str_extract(rec, "^.{19}"))
  rec = rec[order(dt)]
  dt = sort(dt)
  for (i in 1:(length(rec) - 1)) {
    if (str_detect(rec[i], "started") && str_detect(rec[i + 1], "stopped")) {
      filtered_df = df[df$site == site,]
      diffs = time_length(filtered_df$start - dt[i])
      min_i = which.min(abs(diffs))
      min_i = which(df$filename == filtered_df$filename[min_i])
      df$true_length[min_i] = time_length(dt[i+1] - dt[i])
    }
  }
}
ggplot(df[df$site != "KAKA Aviary_20191101_20191113_BAR",], aes(x=start, y=true_length - duration, color = site)) +
  geom_point() + 
  scale_y_continuous(breaks = scales::pretty_breaks(n = 10))

Write out waves with corrected sampling rates

The problem with that though, is that sampling rates in wave files are integers - there’s a loss of precision there. It should be possible to multiply a desired second offset by the true sampling rate and slice a wav file that way.

df$true_sr = df$samples / df$true_length
df$true_sr[!is.na(df$true_sr) & df$true_sr > 44000] = 44100
write.csv(df, "misc/BAR_timespans.csv", row.names = FALSE)
ggplot(df[df$duration > 100,], aes(x=true_sr)) +
  geom_histogram(binwidth=5)

annotations$corrected_begin = sapply(1:nrow(annotations), function(i) {
  true_sr = df$true_sr[str_detect(df$base_filename, fixed(annotations$filename[i]))]
  annotations$Begin.Time..s.[i] * 44100 / true_sr
})
annotations$offset = NA
for (i in 1:nrow(annotations)) {
  if (substring(annotations$Individual.ID[i], 0, 2) == "PB") {
    filtered_rfid = filter(rfid, annotations$Nest.ID[i] == site & msg == paste0(annotations$Individual.ID[i], ".wav"))
    matched_rec_start = df$start[str_detect(df$base_filename, fixed(annotations$filename[i]))]
    rfid_offset = time_length(filtered_rfid$datetime - matched_rec_start)
    diffs = time_length(annotations$corrected_begin[i] - rfid_offset)
    min_i = which.min(abs(diffs))
    offset = diffs[min_i]
    if (length(offset) == 1) {
      annotations$offset[i] = offset
    }
  }
}
filtered_annotations = filter(annotations, Nest.ID == "NNS1" & abs(offset) < 60)
ggplot(data = filtered_annotations, aes(x=corrected_begin, y=offset, color=substr(basename(filename), start = 6, stop = 20))) +
    geom_point() +
    geom_smooth(method = "lm") +
    scale_y_continuous(breaks = scales::pretty_breaks(n = 10)) +
    labs(color='file datetime') +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE) +
    ggtitle("Drift per file at NNS1")


ggplot(data = filtered_annotations, aes(x=datetime, y=offset)) +
    geom_point() +
    geom_smooth(method = "lm") +
    scale_y_continuous(breaks = scales::pretty_breaks(n = 10)) +
    labs(color='file datetime') +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE) + 
    ggtitle("Drift per day at NNS1")


ggplot(data = annotations, aes(x=corrected_begin, y=offset, color=Nest.ID)) +
    geom_point() +
    geom_smooth(method = "lm") +
    scale_y_continuous(breaks = scales::pretty_breaks(n = 10))


filtered_annotations = filter(annotations, Nest.ID == "NNBB12")
ggplot(data = filtered_annotations, aes(x=datetime, y=offset)) +
    geom_point() +
    geom_smooth(method = "lm") +
    scale_y_continuous(breaks = scales::pretty_breaks(n = 10)) +
    labs(color='file datetime') +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE) + 
    ggtitle("Drift per day at NNBB12")

filtered_annotations = filter(annotations, Nest.ID == "NNK34")
ggplot(data = filtered_annotations, aes(x=datetime, y=offset)) +
    geom_point() +
    geom_smooth(method = "lm") +
    scale_y_continuous(breaks = scales::pretty_breaks(n = 10)) +
    labs(color='file datetime') +
    stat_poly_eq(formula = y ~ x,
                   aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                   parse = TRUE) + 
    ggtitle("Drift per day at NNK34")

f = "NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav"
offset = median(annotations$offset[annotations$filename == f], na.rm=TRUE) # offset for this file
matched_df = df[df$base_filename == f,]
matched_event_offsets = time_length(rfid$datetime[rfid$msg == "PB0023.wav" & rfid$datetime %within% matched_df$interval & rfid$site == matched_df$site] - matched_df$start)
for (begin_time in matched_event_offsets) {
  sr = matched_df$true_sr
  clip_start = round((begin_time + offset) * sr)
  clip_end = round(clip_start + (sr * 2))
  print(paste(matched_df$filename, matched_df$true_sr, clip_start, clip_end))
  wav = readWave(filename = matched_df$filename, from = clip_start, to = clip_end, units="samples")
  wav@samp.rate = sr
  spectro(wav, main=begin_time, osc=TRUE)
  #play(wav)
}
[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 18528754 18616403"
[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 35751704 35839353"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 53193776 53281425"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 71074091 71161740"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 89129703 89217352"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 106527950 106615599"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 144961862 145049511"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 162579231 162666880"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 179714533 179802182"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 197375726 197463375"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 215518987 215606636"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 232566640 232654289"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 270781430 270869079"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 288092029 288179678"

[1] "data/2019 BAR recordings/NNS DOC Station nests/NNS1/NNS1_20191203_STUDY [-39.0986 176.8061]/NNS1_20191203_064040_SunriseToSunset [-39.0986 176.8061].wav 43824.3009169214 305139682 305227331"

Not bad.

Let’s rename these RFID codes with something a bit more readable. We’ll need to maintain a way of converting between.

unique_rfids = unique(rfid$msg[str_extract(rfid$msg, "^.{2}") == "RF"])
#human_readable_rfids = paste0("bird", 1:length(unique_rfids))
human_readable_rfids = c("alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", "juliet", "kilo", "lima", "mike", "november")
human_readable_rfids = setNames(human_readable_rfids, unique_rfids)
unique_rfids = setNames(unique_rfids, human_readable_rfids)

processed_rfid = rfid
mask = str_extract(processed_rfid$msg, "^.{2}") == "RF"
processed_rfid$msg[mask] = human_readable_rfids[processed_rfid$msg[mask]]
ggplot(processed_rfid[mask,], aes(x = datetime, color = msg)) +
  geom_freqpoly(binwidth = 60 * 60 * 24) + 
  ggtitle("RFID detections per day")

Try detect entry / exit events by looking at light gate changes

if (file.exists("misc/processed_rfid.csv")) {
  processed_rfid2 = read.csv("misc/processed_rfid.csv")
  processed_rfid2$datetime = ymd_hms(processed_rfid2$datetime)
} else {
  time_threshold = 2
  LG_threshold = 1
  processed_rfid2 = pblapply(1:sum(mask), function(i) {
    dt = processed_rfid$datetime[mask][i]
    this_site = processed_rfid$site[mask][i]
    bird = processed_rfid$msg[mask][i]
    LG = filter(processed_rfid, site == this_site & abs(datetime - dt) < time_threshold & msg == "Light Gate Change")
    if (nrow(LG) > 2) {
      event_type = NULL
      ext_g = mean(diff(LG$ext))
      int_g = mean(diff(LG$int))
      if (ext_g < 0 & int_g > 0) {
        event_type = "entry"
      } else if (int_g < 0 & ext_g > 0) {
        event_type = "exit"
      }
      if (length(event_type) == 1) {
        print(paste(i, bird, event_type, this_site, dt))
        return(data.frame(i = i, bird = bird, event_type = event_type, ext_g = ext_g, int_g = int_g, site = this_site, datetime = dt))
      }
    }
  })
  processed_rfid2 = do.call(rbind, processed_rfid)
  write.csv(processed_rfid2, "misc/processed_rfid.csv", row.names=FALSE)
}
ggplot(processed_rfid2, aes(x = datetime, y = bird, color = event_type)) + geom_point()

ggplot(filter(processed_rfid2, datetime > ymd("2019-12-28") & datetime < ymd("2019-12-29")), aes(x = datetime, y = bird, color = event_type)) + geom_point()

Seems a bit too unreliable - the light gates might be too sensitive. Looks like we’ll have to just slice around RFID passes.

Build a dataframe of datetime intervals to slice - intelligently overlapping for same bird id, and removing intervals where there might be confusion (a playback, or another bird)

time_threshold = 5
events_to_ignore = c("Power-on Reset", "Low-voltage Detect Reset", "Light Gate Change", 
  "Watchdog(COP) Reset", "Playback Error! Restarting", "Software Reset"
)

if (file.exists("misc/dts_to_slice_deduplicated.csv")) {
  dts_to_slice_deduplicated = read.csv("misc/dts_to_slice_deduplicated.csv")
  dts_to_slice_deduplicated$start = ymd_hms(dts_to_slice_deduplicated$start)
  dts_to_slice_deduplicated$end = ymd_hms(dts_to_slice_deduplicated$end)
} else {
  dts_to_slice = pblapply(1:sum(mask), function(i) {
      dt = processed_rfid$datetime[mask][i]
      this_site = processed_rfid$site[mask][i]
      bird = processed_rfid$msg[mask][i]
      this_id = processed_rfid$id[mask][i]
      
      start = dt - time_threshold
      end = dt + time_threshold
      nearby_events = filter(processed_rfid, id != this_id & site == this_site & abs(time_length(datetime - dt)) < time_threshold & !(msg %in% events_to_ignore))
      other_birds = human_readable_rfids[!human_readable_rfids == bird]
      if (any(other_birds %in% nearby_events$msg)) {
        print(paste(i, "another bird within threshold"))
        return(NULL)
      }
      if (any(str_extract(nearby_events$msg, "^.{2}") == "PB")) {
        print(paste(i, "playback within threshold"))
        return(NULL)
      }
      data.frame(i = i, site = this_site, bird = bird, start = start, end = end, interval = interval(start, end))
  })
  dts_to_slice = do.call(rbind, dts_to_slice)
  write.csv(dts_to_slice, "misc/dts_to_slice.csv", row.names = FALSE)
  
  dts_to_slice_deduplicated = dts_to_slice %>% group_by(site, bird) %>% arrange(start) %>% 
                        mutate(indx = c(0, cumsum(as.numeric(lead(start)) >
                                cummax(as.numeric(end)))[-n()])) %>%
                        group_by(site, bird, indx) %>%
                        summarise(start = min(start), end = max(end)) %>%
                        select(-indx)
  dts_to_slice_deduplicated$duration = time_length(dts_to_slice_deduplicated$end - dts_to_slice_deduplicated$start)
  write.csv(dts_to_slice_deduplicated, "misc/dts_to_slice_deduplicated.csv", row.names = FALSE)
}
ggplot(dts_to_slice_deduplicated, aes(duration)) + geom_histogram(binwidth = 1)

Put median offset per file in df

For each interval around a RFID pass, output a wave clip, corrected for offset and SR

df$offset = sapply(df$base_filename, function(f) {
  median(annotations$offset[paste0(annotations$filename) == f], na.rm = TRUE)
})
invisible(pblapply(1:nrow(dts_to_slice_deduplicated), function(i) {
  start = dts_to_slice_deduplicated$start[i]
  duration = dts_to_slice_deduplicated$duration[i]
  bird = dts_to_slice_deduplicated$bird[i]
  site = dts_to_slice_deduplicated$site[i]
  formatted_date = format(start, "%Y-%m-%d_%H%M%S_NZDT")
  output_filename = paste0("data/2019 BAR recordings - individual bird clips/", bird, "/", site, "_", formatted_date, ".wav")
  if (file.exists(output_filename)) {
    return(NULL)
  }
  matched_df = df[df$site == site & start %within% df$interval & !is.na(df$offset) & !is.na(df$true_sr),]
  if (nrow(matched_df) == 1) {
    sr = matched_df$true_sr
    clip_start_s = time_length(start - matched_df$start) + matched_df$offset
    clip_start_samples = round(clip_start_s * sr)
    clip_end_samples = round(clip_start_samples + (sr * duration))
    #print(paste(i, bird, site, start, duration, matched_df$filename, matched_df$true_sr, clip_start_samples, clip_end_samples))
    try({
      wav = readWave(filename = matched_df$filename, from = clip_start_samples, to = clip_end_samples, units="samples")
      wav@samp.rate = sr
      if (sr != 44100) {
        wav = resamp(wav, g = 44100, output = "Wave")
      }
      wav@left = round(wav@left)
      mkdirs(dirname(output_filename))
      writeWave(wav, filename = output_filename, extensible = F)
    })
  }
}))

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~01m 13s      
  |+                                                 | 2 % ~01m 16s      
  |++                                                | 3 % ~01m 10s      
  |++                                                | 4 % ~01m 06s      
  |+++                                               | 5 % ~01m 10s      
  |+++                                               | 6 % ~01m 07s      
  |++++                                              | 7 % ~01m 08s      
  |++++                                              | 8 % ~01m 06s      
  |+++++                                             | 9 % ~01m 04s      
  |+++++                                             | 10% ~01m 05s      
  |++++++                                            | 11% ~01m 04s      
  |++++++                                            | 12% ~01m 05s      
  |+++++++                                           | 13% ~01m 03s      
  |+++++++                                           | 14% ~01m 02s      
  |++++++++                                          | 15% ~01m 02s      
  |++++++++                                          | 16% ~01m 01s      
  |+++++++++                                         | 17% ~60s          
  |+++++++++                                         | 18% ~60s          
  |++++++++++                                        | 19% ~59s          
  |++++++++++                                        | 20% ~59s          
  |+++++++++++                                       | 21% ~58s          
  |+++++++++++                                       | 22% ~57s          
  |++++++++++++                                      | 23% ~56s          
  |++++++++++++                                      | 24% ~56s          
  |+++++++++++++                                     | 25% ~55s          
  |+++++++++++++                                     | 26% ~54s          
  |++++++++++++++                                    | 27% ~53s          
  |++++++++++++++                                    | 28% ~52s          
  |+++++++++++++++                                   | 29% ~51s          
  |+++++++++++++++                                   | 30% ~51s          
  |++++++++++++++++                                  | 31% ~50s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++                                  | 32% ~49s          
  |+++++++++++++++++                                 | 33% ~50s          
  |+++++++++++++++++                                 | 34% ~49s          
  |++++++++++++++++++                                | 35% ~48s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++                                | 36% ~47s          
  |+++++++++++++++++++                               | 37% ~47s          
  |+++++++++++++++++++                               | 38% ~46s          
  |++++++++++++++++++++                              | 39% ~45s          
  |++++++++++++++++++++                              | 40% ~45s          
  |+++++++++++++++++++++                             | 41% ~44s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++                             | 42% ~43s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++                            | 43% ~43s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++                            | 44% ~42s          
  |+++++++++++++++++++++++                           | 45% ~41s          
  |+++++++++++++++++++++++                           | 46% ~40s          
  |++++++++++++++++++++++++                          | 47% ~40s          
  |++++++++++++++++++++++++                          | 48% ~39s          
  |+++++++++++++++++++++++++                         | 49% ~38s          
  |+++++++++++++++++++++++++                         | 50% ~37s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++++++                        | 51% ~36s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++++++                        | 52% ~36s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++                       | 53% ~35s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++                       | 54% ~35s          
  |++++++++++++++++++++++++++++                      | 55% ~34s          
  |++++++++++++++++++++++++++++                      | 56% ~33s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++                     | 57% ~32s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++                     | 58% ~32s          
  |++++++++++++++++++++++++++++++                    | 59% ~31s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++++++++++                    | 60% ~30s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++++                   | 61% ~29s          
  |+++++++++++++++++++++++++++++++                   | 62% ~29s          
  |++++++++++++++++++++++++++++++++                  | 63% ~28s          
  |++++++++++++++++++++++++++++++++                  | 64% ~27s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++++++                 | 65% ~26s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++++++                 | 66% ~26s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++++++++++++++                | 67% ~25s          
  |++++++++++++++++++++++++++++++++++                | 68% ~24s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~23s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~23s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~22s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++++++++++++++++              | 72% ~21s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~20s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~20s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~19s          
  |++++++++++++++++++++++++++++++++++++++            | 76% ~18s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~17s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~17s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |++++++++++++++++++++++++++++++++++++++++          | 79% ~16s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~15s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~14s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~14s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~13s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~12s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~11s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~11s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~10s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~09s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~08s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~08s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~07s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~06s          
Error in readBin(con, int, n = N, size = bytes, signed = (bytes != 1),  : 
  invalid 'n' argument

  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~05s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~05s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~04s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~03s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~02s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~02s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=01m 15s

Use monitoR to remove silence in sliced clips

files = list.files("data/Sound templates", pattern = "*.txt$", full.names=TRUE)
f = files[1]
selection_table = read.table(f, header=TRUE, sep="\t")
freq_lim = c(selection_table$Low.Freq..Hz., selection_table$High.Freq..Hz.)/1000
wave_filename = str_replace(f, ".Table.1.selections.txt", ".wav")
template_id = substr(basename(f), 1, 8)
template_wav = readWave(wave_filename, from = selection_table$Begin.Time..s., to = selection_table$End.Time..s., units="seconds")
template_wav = resamp(template_wav, g = 44100, output = "Wave")
template_duration = length(template_wav@left) / 44100
processed_template_wave_filename = paste0("misc/", basename(wave_filename))
savewav(template_wav, filename = processed_template_wave_filename)
ctemps = makeCorTemplate(processed_template_wave_filename, wl = 300, frq.lim = freq_lim, name = template_id)

Automatic point selection.

Done.
templateCutoff(ctemps) = c(default=.5)

buffer = 0

for (bird in human_readable_rfids) {
  files_for_bird = Sys.glob(paste0("data/2019 BAR recordings - individual bird clips/", bird, "/*.wav"))
  clips = c()
  if (length(files_for_bird) == 0) {
    next
  }
  for (i in 1:length(files_for_bird)) {
    f = files_for_bird[i]
    cscores = corMatch(f, ctemps, quiet=TRUE)
    cdetects = findPeaks(cscores)
    detects = getDetections(cdetects)
    if (i == 1) {
      plot(cdetects)
    }
    if (nrow(detects) > 1) {
      for (j in 1:nrow(detects)) {
        start_time = detects$time[j]
        clip = readWave(f, from = start_time - buffer, to = start_time + template_duration + buffer, units = "seconds")@left
        clips = c(clips, clip)
      }
    }
  }
  if (length(clips) > 10) {
    wav = Wave(clips, samp.rate = 44100, bit = 16)
    output_filename = paste0("data/2019 BAR recordings - individual bird clips/", bird, ".wav")
    writeWave(wav, output_filename, extensible = F)
  }
}

Done with  ZOOM0003
Done
0 to 10.000022675737 seconds

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done

Done with  ZOOM0003
Done
LS0tCnRpdGxlOiAiVGltZSBzeW5jIGNoZWNrIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBMb2FkIGxpYnJhcmllcwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoImJpb2Fjb3VzdGljcyIsICJ0dW5lUiIsICJzZWV3YXZlIiwgImRwbHlyIiwgInRpZHl2ZXJzZSIsICJsdWJyaWRhdGUiLCAidG9vbHMiLCAiZ2dwbG90MiIsICJnZ3BtaXNjIiwgInBsb3RseSIsICJzdHJpbmdyIiwgIm1vbml0b1IiLCAicGJhcHBseSIsICJmb3JlYWNoIiwgImRvUGFyYWxsZWwiLCAiUi51dGlscyIpLCB0eXBlPSJiaW5hcnkiKQpsaWJyYXJ5KGJpb2Fjb3VzdGljcykKbGlicmFyeSh0dW5lUikKbGlicmFyeShzZWV3YXZlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkodG9vbHMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3BtaXNjKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGZvcmVhY2gpCmxpYnJhcnkoZG9QYXJhbGxlbCkKbGlicmFyeShSLnV0aWxzKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShwYmFwcGx5KQpsaWJyYXJ5KG1vbml0b1IpCnltZF9obXMgPSBmdW5jdGlvbihkdCkgewogIGx1YnJpZGF0ZTo6eW1kX2htcyhkdCwgdHo9IlBhY2lmaWMvQXVja2xhbmQiKQp9CmRteV9obXMgPSBmdW5jdGlvbihkdCkgewogIGx1YnJpZGF0ZTo6ZG15X2htcyhkdCwgdHo9IlBhY2lmaWMvQXVja2xhbmQiKQp9Cm9wdGlvbnMoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMjIExvYWQgUkZJRCBsb2cgZmlsZXMKCmBgYHtyfQpmaWxlcyA9IGxpc3QuZmlsZXMoImRhdGEvMjAxOSBSRklEIiwgcGF0dGVybiA9ICIqLlRYVCQiLCByZWN1cnNpdmUgPSBUUlVFLCBmdWxsLm5hbWVzPVRSVUUpCmNzdnMgPSBsYXBwbHkoZmlsZXMsIGZ1bmN0aW9uKHBhdGgpIHsKICBjc3YgPSByZWFkLmNzdihwYXRoLCBoZWFkZXI9RkFMU0UsIGNvbC5uYW1lcyA9IGMoInllYXIiLCAibW9udGgiLCAiZGF5IiwgImhvdXIiLCJtaW51dGVfc2VjIiwgImlkIiwgImV4dCIsICJpbnQiLCAibXNnIikpCiAgY3N2JHNpdGUgPSBzdHJzcGxpdChzdHJzcGxpdChwYXRoLCAiLyIpW1sxXV1bNF0sICJfIilbWzFdXVsxXQogIGNzdgp9KQpyZmlkID0gZG8uY2FsbChyYmluZCwgY3N2cykKcm0oY3N2cykKcmZpZCRkYXRldGltZSA9IHBhc3RlMChyZmlkJHllYXIsICItIiwgcmZpZCRtb250aCwgIi0iLCByZmlkJGRheSwgIiAiLCByZmlkJGhvdXIsICI6IiwgcmZpZCRtaW51dGVfc2VjKQpyZmlkJGRhdGV0aW1lID0geW1kX2htcyhyZmlkJGRhdGV0aW1lKQpnZ3Bsb3QocmZpZCwgYWVzKGRhdGV0aW1lKSkgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSA2MCAqIDYwKQpgYGAKIyMjIFJlZmVyZW5jZSBzcGVjdHJvZ3JhbXMgZm9yIHBsYXliYWNrIGZpbGVzCmBgYHtyfQpodG1sX3RhZ19hdWRpbyA8LSBmdW5jdGlvbihmaWxlLCB0eXBlID0gYygid2F2IikpIHsKICB0eXBlIDwtIG1hdGNoLmFyZyh0eXBlKQogIGh0bWx0b29sczo6dGFncyRoMygKICAgIGJhc2VuYW1lKGZpbGUpLAogICAgaHRtbHRvb2xzOjp0YWdzJGF1ZGlvKAogICAgICBjb250cm9scyA9ICIiLAogICAgICBodG1sdG9vbHM6OnRhZ3Mkc291cmNlKAogICAgICAgIHNyYyA9IGZpbGUsCiAgICAgICAgdHlwZSA9IGdsdWU6OmdsdWUoImF1ZGlvL3t0eXBlfSIsIHR5cGUgPSB0eXBlKQogICAgICApCiAgICApCiAgKQp9CmZpbGVzID0gU3lzLmdsb2IoIi4vY2xpcHMvUEIqLndhdiIpClBCX3dhdnMgPSBsYXBwbHkoZmlsZXMsIGZ1bmN0aW9uKGYpIHsKICB3YXYgPSByZWFkX2F1ZGlvKGYpCiAgd2F2X2xlbmd0aCA9IHJvdW5kKGxlbmd0aCh3YXYpIC8gd2F2QHNhbXAucmF0ZSwgMikKICBzcGVjdHJvKHdhdiwgbWFpbj1wYXN0ZTAoYmFzZW5hbWUoZiksICIgIiwgd2F2X2xlbmd0aCwgInMiKSwgb3NjPVRSVUUpCiAgcHJpbnQoaHRtbF90YWdfYXVkaW8oZikpCiAgZGF0YS5mcmFtZShmaWxlbmFtZT1iYXNlbmFtZShmKSwgd2F2X2xlbmd0aCkKfSkKUEJfd2F2cyA9IGRvLmNhbGwocmJpbmQsIFBCX3dhdnMpCmBgYAoKIyMjIEJ1aWxkIGEgcmVmZXJlbmNlIGRhdGFmcmFtZSBzaG93aW5nIHdoaWNoIHdhdmUgZmlsZXMgY29ycmVzcG9uZCB0byB3aGljaCB0aW1lcwoKYGBge3J9CnJlYWRfQkFSX21ldGFkYXRhID0gZnVuY3Rpb24oZmlsZXMpIHsKICBkZiA9IHBibGFwcGx5KGZpbGVzLCBmdW5jdGlvbihmKSB7CiAgICBiaXRzID0gc3Ryc3BsaXQoYmFzZW5hbWUoZiksICJfIilbWzFdXQogICAgc3RhcnQgPSB5bWRfaG1zKHBhc3RlKGJpdHNbMl0sIGJpdHNbM10pKQogICAgc2l0ZSA9IHN0cnNwbGl0KGYsICIvIilbWzFdXVs0XQogICAgdHJ5KHsKICAgICAgd2F2X2hlYWRlciA9IHJlYWRXYXZlKGYsIGhlYWRlcj1UUlVFKQogICAgICBkdXJhdGlvbiA9IHdhdl9oZWFkZXIkc2FtcGxlcyAvIHdhdl9oZWFkZXIkc2FtcGxlLnJhdGUKICAgICAgZW5kID0gc3RhcnQgKyBkdXJhdGlvbgogICAgICByZXR1cm4oZGF0YS5mcmFtZShmaWxlbmFtZSA9IGYsIGJhc2VfZmlsZW5hbWUgPSBiYXNlbmFtZShmKSwgc2l0ZSA9IHNpdGUsIHN0YXJ0ID0gc3RhcnQsIGVuZCA9IGVuZCwgc2FtcGxlcyA9IHdhdl9oZWFkZXIkc2FtcGxlcywgc3IgPSB3YXZfaGVhZGVyJHNhbXBsZS5yYXRlLCBkdXJhdGlvbj1kdXJhdGlvbiwgaW50ZXJ2YWwgPSBpbnRlcnZhbChzdGFydCwgZW5kKSkpCiAgICB9KQogICAgTlVMTAogIH0pCiAgZGYgPSBkby5jYWxsKHJiaW5kLCBkZikKICBkZgp9CmlmIChmaWxlLmV4aXN0cygibWlzYy9CQVJfdGltZXNwYW5zLmNzdiIpKSB7CiAgZGYgPSByZWFkLmNzdigibWlzYy9CQVJfdGltZXNwYW5zLmNzdiIpCiAgZGYkc3RhcnQgPSB5bWRfaG1zKGRmJHN0YXJ0KQogIGRmJGVuZCA9IHltZF9obXMoZGYkZW5kKQogIGRmJGludGVydmFsID0gaW50ZXJ2YWwoZGYkc3RhcnQsIGRmJGVuZCkKfSBlbHNlIHsKICBmaWxlcyA9IGxpc3QuZmlsZXMoImRhdGEvMjAxOSBCQVIgcmVjb3JkaW5ncyIsIHBhdHRlcm4gPSAiKi53YXYkIiwgcmVjdXJzaXZlID0gVFJVRSwgZnVsbC5uYW1lcz1UUlVFKQogIGRmID0gcmVhZF9CQVJfbWV0YWRhdGEoZmlsZXMpCiAgd3JpdGUuY3N2KGRmLCAibWlzYy9CQVJfdGltZXNwYW5zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQp9CmRmW2MoInNpdGUiLCAiaW50ZXJ2YWwiKV0KYGBgCgoKIyMjIFNlbGVjdCBhIGZldyBwbGF5YmFjayBlbnRyaWVzIHRvIGNoZWNrCgpgYGB7cn0KUEJfdG9fY2hlY2sgPSAiUEIwMDIzLndhdiIKc2l0ZV90b19jaGVjayA9ICJOTkszMCIKZmlsdGVyZWRfcmZpZCA9IGZpbHRlcihyZmlkLCBtc2cgPT0gUEJfdG9fY2hlY2sgJiBzaXRlID09IHNpdGVfdG9fY2hlY2spCmZpbHRlcmVkX2RmID0gZmlsdGVyKGRmLCBzaXRlID09IHNpdGVfdG9fY2hlY2spCmZpbHRlcmVkX3JmaWQkY29ycmVjdGVkX2RhdGV0aW1lID0gZmlsdGVyZWRfcmZpZCRkYXRldGltZQpmaWx0ZXJlZF9yZmlkJG1hdGNoID0gbGFwcGx5KDE6bnJvdyhmaWx0ZXJlZF9yZmlkKSwgZnVuY3Rpb24oaSkgewogIGR0ID0gZmlsdGVyZWRfcmZpZCRjb3JyZWN0ZWRfZGF0ZXRpbWVbaV0KICBzaXRlID0gZmlsdGVyZWRfcmZpZCRzaXRlW2ldCiAgaW5kaWNlcyA9IHdoaWNoKGR0ICV3aXRoaW4lIGZpbHRlcmVkX2RmJGludGVydmFsKQogIGlmIChsZW5ndGgoaW5kaWNlcykgPT0gMCkgaW5kaWNlcyA9IE5BCiAgaW5kaWNlcwp9KQpwcmludChwYXN0ZSgiVGhlcmUgYXJlIiwgbnJvdyhmaWx0ZXJlZF9yZmlkKSwgUEJfdG9fY2hlY2ssICJwbGF5YmFja3MsIG9mIHdoaWNoIiwgc3VtKCFpcy5uYShmaWx0ZXJlZF9yZmlkJG1hdGNoKSksICJoYXZlIHJlY29yZGluZ3MiKSkKIyBBIGZldyByYW5kb21seQpkYXRldGltZXNfdG9fY2hlY2sgPSBzb3J0KHNhbXBsZShmaWx0ZXJlZF9yZmlkJGNvcnJlY3RlZF9kYXRldGltZVshaXMubmEoZmlsdGVyZWRfcmZpZCRtYXRjaCldLCA1KSkKIyBPbmVzIGZyb20gdGhlIDEzdGgKI2RhdGV0aW1lc190b19jaGVjayA9IGZpbHRlcmVkX3JmaWQkY29ycmVjdGVkX2RhdGV0aW1lWyFpcy5uYShmaWx0ZXJlZF9yZmlkJG1hdGNoKSAmIGZpbHRlcmVkX3JmaWQkZGF0ZXRpbWUgPiB5bWRfaG1zKCIyMDE5LTExLTIwIDAwOjAwOjAwIikgJiBmaWx0ZXJlZF9yZmlkJGRhdGV0aW1lIDwgeW1kX2htcygiMjAxOS0xMS0yMCAyMzo1OTo1OSIpXQojZGF0ZXRpbWVzX3RvX2NoZWNrID0gZmlsdGVyZWRfcmZpZCRjb3JyZWN0ZWRfZGF0ZXRpbWVbIWlzLm5hKGZpbHRlcmVkX3JmaWQkbWF0Y2gpICYgZmlsdGVyZWRfcmZpZCRjb3JyZWN0ZWRfZGF0ZXRpbWUgJXdpdGhpbiUgaW50ZXJ2YWwoeW1kX2htcygiMjAxOS0xMi0xMyAxNjo0NTowMCIpLCB5bWRfaG1zKCIyMDE5LTEyLTEzIDE3OjEzOjAwIikpXQpwcmludChkYXRldGltZXNfdG9fY2hlY2spCmBgYAoKYGBge3J9CiNkdCA9IHltZF9obXMoIjIwMTktMTItMTMgMTY6NDU6MDQiKQpzZXRXYXZQbGF5ZXIoInBsYXkiKSAjIE9TIGRlcGVuZGVudApjaGVja2VkX2NsaXBzID0gbGFwcGx5KGRhdGV0aW1lc190b19jaGVjaywgZnVuY3Rpb24oZHQpIHsKICBtYXRjaCA9IGZpbHRlcmVkX3JmaWQkbWF0Y2hbZmlsdGVyZWRfcmZpZCRjb3JyZWN0ZWRfZGF0ZXRpbWUgPT0gZHRdW1sxXV0KICBmID0gZmlsdGVyZWRfZGYkZmlsZW5hbWVbbWF0Y2hdCiAgY2xpcF9zdGFydCA9IHRpbWVfbGVuZ3RoKGR0IC0gZmlsdGVyZWRfZGYkc3RhcnRbbWF0Y2hdLCB1bml0ID0gInNlY29uZHMiKQogIGNsaXBfZW5kID0gY2xpcF9zdGFydCArIDEKICBwcmludChwYXN0ZShmLCBkdCwgY2xpcF9zdGFydCwgY2xpcF9lbmQpKQogIHdhdiA9IHJlYWRfYXVkaW8oZiwgZnJvbSA9IGNsaXBfc3RhcnQsIHRvID0gY2xpcF9lbmQpCiAgc3BlY3Rybyh3YXYsIG1haW49ZHQsIG9zYz1UUlVFKQogICNwbGF5KHdhdikKICB3YXYKfSkKYGBgCgojIyBXcml0ZSBvdXQgYSBjc3Ygb2YgYXBwcm94IHBvc2l0aW9ucyBvZiBQQiBldmVudHMgcGVyIHdhdmUgZmlsZQoKYGBge3J9CmlmIChmaWxlLmV4aXN0cygibWlzYy9hcHByb3hfb2Zmc2V0cy5jc3YiKSkgewogIGFwcHJveF9vZmZzZXRzX2d1aWRlID0gcmVhZC5jc3YoIm1pc2MvYXBwcm94X29mZnNldHMuY3N2IikKfSBlbHNlIHsKICBmaWx0ZXJlZF9yZmlkID0gcmZpZFtzdHJfZXh0cmFjdChyZmlkJG1zZywgIl4uezJ9IikgPT0gIlBCIixdCiAgYXBwcm94X29mZnNldHMgPSBsYXBwbHkoMTpucm93KGRmKSwgZnVuY3Rpb24oaSkgewogICAgbWF0Y2hlZF9ldmVudHMgPSBmaWx0ZXJlZF9yZmlkW2ZpbHRlcmVkX3JmaWQkc2l0ZSA9PSBkZiRzaXRlW2ldICYgZmlsdGVyZWRfcmZpZCRkYXRldGltZSAld2l0aGluJSBkZiRpbnRlcnZhbFtpXSxdCiAgICBpZiAobnJvdyhtYXRjaGVkX2V2ZW50cykgPT0gMCkgewogICAgICByZXR1cm4oTlVMTCkKICAgIH0KICAgIGlmIChucm93KG1hdGNoZWRfZXZlbnRzKSA+IDEwKSB7CiAgICAgIG1hdGNoZWRfZXZlbnRzID0gc2FtcGxlX24obWF0Y2hlZF9ldmVudHMsIDEwKQogICAgfQogICAgb2Zmc2V0cyA9IHRpbWVfbGVuZ3RoKG1hdGNoZWRfZXZlbnRzJGRhdGV0aW1lIC0gZGYkc3RhcnRbaV0pCiAgICBkYXRhLmZyYW1lKGZpbGVuYW1lID0gZGYkZmlsZW5hbWVbaV0sIG9mZnNldCA9IG9mZnNldHMsIHNpdGUgPSBtYXRjaGVkX2V2ZW50cyRzaXRlLCBwYiA9IG1hdGNoZWRfZXZlbnRzJG1zZywgZGF0ZXRpbWUgPSBtYXRjaGVkX2V2ZW50cyRkYXRldGltZSkKICB9KQogIGFwcHJveF9vZmZzZXRzX2d1aWRlID0gZG8uY2FsbChyYmluZCwgYXBwcm94X29mZnNldHMpCiAgd3JpdGUuY3N2KGFwcHJveF9vZmZzZXRzLCAibWlzYy9hcHByb3hfb2Zmc2V0cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKfQpgYGAKCiMjIENvbXBhcmUgYW5ub3RhdGlvbnMgd2l0aCBuZWFyZXN0IFJGSUQgbG9nCgpgYGB7cn0KYW5ub3RhdGlvbnMgPSBsYXBwbHkobGlzdC5maWxlcygibWlzYyIsICIqLnR4dCIsIGZ1bGwubmFtZXM9VFJVRSksIGZ1bmN0aW9uKGYpIHsKICBpZiAoc3RyX2RldGVjdChmLCAiWk9PTSIpKSB7CiAgICByZXR1cm4oTlVMTCkKICB9IGVsc2UgaWYgKHN0cl9kZXRlY3QoZiwgInNlbGVjdGlvbnMiKSkgewogICAgeCA9IHJlYWQudGFibGUoZiwgc2VwPSJcdCIsIGhlYWRlcj1UUlVFKQogICAgeCRkYXRldGltZSA9IHltZF9obXMocGFzdGUoeCRCZWdpbi5EYXRlLCB4JEJlZ2luLkNsb2NrLlRpbWUpKQogICAgeCA9IHhbYygiQmVnaW4uVGltZS4ucy4iLCAiRW5kLlRpbWUuLnMuIiwgIkluZGl2aWR1YWwuSUQiLCAiTmVzdC5JRCIsICJkYXRldGltZSIpXQogIH0gZWxzZSB7CiAgICB4ID0gcmVhZC50YWJsZShmLCBjb2wubmFtZXMgPSBjKCJCZWdpbi5UaW1lLi5zLiIsICJFbmQuVGltZS4ucy4iLCAiSW5kaXZpZHVhbC5JRCIpKVtjKFRSVUUsIEZBTFNFKSxdCiAgICB4JEJlZ2luLlRpbWUuLnMuID0gYXMubnVtZXJpYyh4JEJlZ2luLlRpbWUuLnMuKQogICAgeCRFbmQuVGltZS4ucy4gPSBhcy5udW1lcmljKHgkQmVnaW4uVGltZS4ucy4pCiAgICB4JE5lc3QuSUQgPSBzdHJzcGxpdChiYXNlbmFtZShmKSwgIl8iKVtbMV1dWzFdCiAgICB4JGRhdGV0aW1lID0gZGYkc3RhcnRbZGYkYmFzZV9maWxlbmFtZSA9PSBzdHJfcmVwbGFjZShiYXNlbmFtZShmKSwgIi50eHQiLCAiLndhdiIpXSArIHgkQmVnaW4uVGltZS4ucy4KICB9CiAgeCRmaWxlbmFtZSA9IHBhc3RlMChzdHJfcmVwbGFjZShiYXNlbmFtZShmKSwgIi5CYW5kLkxpbWl0ZWQuRW5lcmd5LkRldGVjdG9yLnNlbGVjdGlvbnMudHh0fC50eHQiLCAiIiksICIud2F2IikKICB4Cn0pCmFubm90YXRpb25zID0gZG8uY2FsbChyYmluZCwgYW5ub3RhdGlvbnMpCmFubm90YXRpb25zJG9mZnNldCA9IHJlcChOQSwgbnJvdyhhbm5vdGF0aW9ucykpCmFubm90YXRpb25zJEluZGl2aWR1YWwuSURbYW5ub3RhdGlvbnMkSW5kaXZpZHVhbC5JRCA9PSAiUEIwMDA5IiB8IGFubm90YXRpb25zJEluZGl2aWR1YWwuSUQgPT0gIlBCMDA5Il0gPSAiUEIwMDA5enAiCmFubm90YXRpb25zJE5lc3QuSURbYW5ub3RhdGlvbnMkTmVzdC5JRCA9PSAiTktOMzEiXSA9ICJOTkszMSIKZm9yIChpIGluIDE6bnJvdyhhbm5vdGF0aW9ucykpIHsKICBpZiAoc3Vic3RyaW5nKGFubm90YXRpb25zJEluZGl2aWR1YWwuSURbaV0sIDAsIDIpID09ICJQQiIpIHsKICAgIGZpbHRlcmVkX3JmaWQgPSBmaWx0ZXIocmZpZCwgYW5ub3RhdGlvbnMkTmVzdC5JRFtpXSA9PSBzaXRlICYgbXNnID09IHBhc3RlMChhbm5vdGF0aW9ucyRJbmRpdmlkdWFsLklEW2ldLCAiLndhdiIpKQogICAgbWF0Y2hlZF9yZWNfc3RhcnQgPSBkZiRzdGFydFtkZiRzaXRlID09IGFubm90YXRpb25zJE5lc3QuSURbaV0gJiBhbm5vdGF0aW9ucyRkYXRldGltZVtpXSAld2l0aGluJSBkZiRpbnRlcnZhbF0KICAgIGRpZmZzID0gdGltZV9sZW5ndGgoZmlsdGVyZWRfcmZpZCRkYXRldGltZSAtIGFubm90YXRpb25zJGRhdGV0aW1lW2ldKQogICAgbWluX2kgPSB3aGljaC5taW4oYWJzKGRpZmZzKSkKICAgIG9mZnNldCA9IGRpZmZzW21pbl9pXQogICAgaWYgKGxlbmd0aChvZmZzZXQpID09IDEpIHsKICAgICAgYW5ub3RhdGlvbnMkb2Zmc2V0W2ldID0gb2Zmc2V0CiAgICB9CiAgfQp9CgphcHByb3hfb2Zmc2V0cyA9IHJlYWRfZXhjZWwoIm1pc2MvYXBwcm94X29mZnNldHMueGxzeCIpCm5hbWVzKGFwcHJveF9vZmZzZXRzKSA9IGMoImZpbGVuYW1lIiwgImd1aWRlX3N0YXJ0IiwgTkEsIE5BLCAiQmVnaW4uVGltZS4ucy4iLCAiTmVzdC5JRCIsICJJbmRpdmlkdWFsLklEIiwgImRhdGV0aW1lIiwgTkEpCmFubm90YXRpb25zID0gd2l0aGluKGFubm90YXRpb25zLCBybShFbmQuVGltZS4ucy4pKQphcHByb3hfb2Zmc2V0cyRvZmZzZXQgPSBhcHByb3hfb2Zmc2V0cyRndWlkZV9zdGFydCAtIGFwcHJveF9vZmZzZXRzJEJlZ2luLlRpbWUuLnMuCmFwcHJveF9vZmZzZXRzJGZpbGVuYW1lID0gYmFzZW5hbWUoYXBwcm94X29mZnNldHMkZmlsZW5hbWUpCmFwcHJveF9vZmZzZXRzJEluZGl2aWR1YWwuSUQgPSBzdHJfcmVwbGFjZShhcHByb3hfb2Zmc2V0cyRJbmRpdmlkdWFsLklELCAiLndhdiIsICIiKQphbm5vdGF0aW9ucyA9IHJiaW5kKGFubm90YXRpb25zLCBhcHByb3hfb2Zmc2V0c1tuYW1lcyhhbm5vdGF0aW9ucyldKQoKYW5ub3RhdGlvbnMgPSBuYS5vbWl0KGFubm90YXRpb25zKQoKZmlsdGVyZWRfYW5ub3RhdGlvbnMgPSBmaWx0ZXIoYW5ub3RhdGlvbnMsIG9mZnNldCA+IC02MCAmIE5lc3QuSUQgPT0gIk5OUzEiKQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkX2Fubm90YXRpb25zLCBhZXMoeD1CZWdpbi5UaW1lLi5zLiwgeT1vZmZzZXQsIGNvbG9yPXN1YnN0cihiYXNlbmFtZShmaWxlbmFtZSksIHN0YXJ0ID0gNiwgc3RvcCA9IDIwKSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgICBzdGF0X3BvbHlfZXEoZm9ybXVsYSA9IHkgfiB4LAogICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gcGFzdGUoLi5lcS5sYWJlbC4uLCAuLnJyLmxhYmVsLi4sIHNlcCA9ICJ+fn4iKSksIAogICAgICAgICAgICAgICAgICAgcGFyc2UgPSBUUlVFKSArCiAgICBsYWJzKGNvbG9yPSdmaWxlIGRhdGV0aW1lJykKZmlsdGVyZWRfYW5ub3RhdGlvbnMgPSBmaWx0ZXIoYW5ub3RhdGlvbnMsIE5lc3QuSUQgPT0gIk5OQkIxMiIpCmdncGxvdChkYXRhID0gZmlsdGVyZWRfYW5ub3RhdGlvbnMsIGFlcyh4PUJlZ2luLlRpbWUuLnMuLCB5PW9mZnNldCkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgICBzdGF0X3BvbHlfZXEoZm9ybXVsYSA9IHkgfiB4LAogICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gcGFzdGUoLi5lcS5sYWJlbC4uLCAuLnJyLmxhYmVsLi4sIHNlcCA9ICJ+fn4iKSksIAogICAgICAgICAgICAgICAgICAgcGFyc2UgPSBUUlVFKQpnZ3Bsb3QoZGF0YSA9IGFubm90YXRpb25zLCBhZXMoeD1CZWdpbi5UaW1lLi5zLiwgeT1vZmZzZXQsIGNvbG9yPU5lc3QuSUQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgc3RhdF9wb2x5X2VxKGZvcm11bGEgPSB5IH4geCwKICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlKC4uZXEubGFiZWwuLiwgLi5yci5sYWJlbC4uLCBzZXAgPSAifn5+IikpLCAKICAgICAgICAgICAgICAgICAgIHBhcnNlID0gVFJVRSkKYGBgCgpTb21ldGhpbmcgaXMgd3Jvbmcgd2l0aCB0aGUgUkZJRCBjaGlwIGNsb2NrIC0gaXQncyBkcmlmdGluZyBzaWduaWZpY2FudGx5LiBNb3JlIGRhdGEgcG9pbnRzIHdpbGwgaGVscCAtIGxldCdzIGF1dG9kZXRlY3QgUEIgaW4gdGhlIGF1ZGlvIGZpbGVzCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpmaWx0ZXJlZF9hbm5vdGF0aW9ucyA9IGFubm90YXRpb25zW2Fubm90YXRpb25zJEluZGl2aWR1YWwuSUQgPT0gIlBCMDAyMyIgJiBhbm5vdGF0aW9ucyROZXN0LklEID09ICJOTkszMCIsXQpwcmludChmaWx0ZXJlZF9hbm5vdGF0aW9uc1sxLF0pClBCMjMgPSBtYWtlQ29yVGVtcGxhdGUoImNsaXBzL1BCMDAyMy53YXYiLCBmcnEubGltID0gYygzLDEwKSwgbmFtZT0iUEIwMDIzIikKUEIyNCA9IG1ha2VDb3JUZW1wbGF0ZSgiY2xpcHMvUEIwMDI0LndhdiIsIGZycS5saW0gPSBjKDMsMTApLCBuYW1lPSJQQjAwMjQiKQpjdGVtcHMgPSBjb21iaW5lQ29yVGVtcGxhdGVzKFBCMjMsIFBCMjQpCndhdiA9IHJlYWRfYXVkaW8oZmlsdGVyZWRfYW5ub3RhdGlvbnMkQmVnaW4uUGF0aFsxXSwgZnJvbSA9IDMwMCwgdG8gPSA4MDApCnNhdmV3YXYod2F2LCBmaWxlbmFtZT0idGVzdC53YXYiKQojc3BlY3Rybyh3YXYsIG9zYz1UUlVFKQojcGxheSh3YXYpCmNzY29yZXMgPC0gY29yTWF0Y2goInRlc3Qud2F2IiwgY3RlbXBzLCBwYXJhbGxlbCA9IFRSVUUsIHNob3cucHJvZyA9IFRSVUUpCmNkZXRlY3RzIDwtIGZpbmRQZWFrcyhjc2NvcmVzKQpnZXREZXRlY3Rpb25zKGNkZXRlY3RzKQpgYGAKbW9uaXRvUiBzZWVtcyB0b28gdW5yZWxpYWJsZSBmb3IgdGhpcyB0YXNrLCB1bmZvcnR1bmF0ZWx5CgoKVGhlIHJlYXNvbiBmb3IgdGhlIGRyaWZ0IGlzIFNEIGNhcmQgd3JpdGUgc3BlZWQuIEJBUiBsb2dmaWxlcyBpbmRpY2F0ZSB0aGUgdHJ1ZSB0aW1lIHBhY2tlZCwgc28gaWYgd2UgcmVhZCB0aGUgQkFSIGxvZ2ZpbGVzIHdlIGNhbiBkZXRlcm1pbmUgdGhlIHRydWUgc2FtcGxpbmcgcmF0ZSwgYW5kIGNvcnJlY3QgaXQuIEluIHNvbWUgY2FzZXMsIGl0J2xsIGJlIGp1c3QgdW5kZXIgNDQuMUtIei4KCmBgYHtyfQpmaWxlcyA9IGxpc3QuZmlsZXMoImRhdGEvMjAxOSBCQVIgcmVjb3JkaW5ncyIsIHBhdHRlcm4gPSAibG9nZmlsZS4qdHh0JCIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXM9VFJVRSkKc2l0ZXMgPSBzYXBwbHkoc3Ryc3BsaXQoZmlsZXMsICIvIiksICJbWyIsIDQpCmRmJHRydWVfbGVuZ3RoID0gcmVwKE5BLCBucm93KGRmKSkKZm9yIChzaXRlIGluIHVuaXF1ZShzaXRlcykpIHsKICBmaWxlc19mb3Jfc2l0ZSA9IGZpbGVzW3NpdGUgPT0gc2l0ZXNdCiAgbGluZXMgPSB1bmxpc3QobGFwcGx5KGZpbGVzX2Zvcl9zaXRlLCByZWFkTGluZXMpKQogIHJlYyA9IHN0cl9zdWJzZXQobGluZXMsICJSZWNvcmRpbmciKQogIGR0ID0gZG15X2htcyhzdHJfZXh0cmFjdChyZWMsICJeLnsxOX0iKSkKICByZWMgPSByZWNbb3JkZXIoZHQpXQogIGR0ID0gc29ydChkdCkKICBmb3IgKGkgaW4gMToobGVuZ3RoKHJlYykgLSAxKSkgewogICAgaWYgKHN0cl9kZXRlY3QocmVjW2ldLCAic3RhcnRlZCIpICYmIHN0cl9kZXRlY3QocmVjW2kgKyAxXSwgInN0b3BwZWQiKSkgewogICAgICBmaWx0ZXJlZF9kZiA9IGRmW2RmJHNpdGUgPT0gc2l0ZSxdCiAgICAgIGRpZmZzID0gdGltZV9sZW5ndGgoZmlsdGVyZWRfZGYkc3RhcnQgLSBkdFtpXSkKICAgICAgbWluX2kgPSB3aGljaC5taW4oYWJzKGRpZmZzKSkKICAgICAgbWluX2kgPSB3aGljaChkZiRmaWxlbmFtZSA9PSBmaWx0ZXJlZF9kZiRmaWxlbmFtZVttaW5faV0pCiAgICAgIGRmJHRydWVfbGVuZ3RoW21pbl9pXSA9IHRpbWVfbGVuZ3RoKGR0W2krMV0gLSBkdFtpXSkKICAgIH0KICB9Cn0KZ2dwbG90KGRmW2RmJHNpdGUgIT0gIktBS0EgQXZpYXJ5XzIwMTkxMTAxXzIwMTkxMTEzX0JBUiIsXSwgYWVzKHg9c3RhcnQsIHk9dHJ1ZV9sZW5ndGggLSBkdXJhdGlvbiwgY29sb3IgPSBzaXRlKSkgKwogIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDEwKSkKYGBgCgojIyBXcml0ZSBvdXQgd2F2ZXMgd2l0aCBjb3JyZWN0ZWQgc2FtcGxpbmcgcmF0ZXMKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnJlZ2lzdGVyRG9QYXJhbGxlbChkZXRlY3RDb3JlcygpIC0gMSkKZmlsdGVyZWRfZGYgPSBkZlshaXMubmEoZGYkdHJ1ZV9sZW5ndGgpICYgZGYkdHJ1ZV9sZW5ndGggPiAxMDAgJiBkZiRzaXRlICVpbiUgdW5pcXVlKHJmaWQkc2l0ZSksXQpmb3JlYWNoKGkgPSAxOm5yb3coZmlsdGVyZWRfZGYpLCAucGFja2FnZXMgPSAidHVuZVIiKSAgJWRvcGFyJSB7CiAgb3V0cHV0X2ZpbGVuYW1lID0gc3RyX3JlcGxhY2UoZmlsdGVyZWRfZGYkZmlsZW5hbWVbaV0sICIyMDE5IEJBUiByZWNvcmRpbmdzIiwgIjIwMTkgQkFSIHJlY29yZGluZ3MgLSBmaXhlZCBTUiIpCiAgaWYgKCFmaWxlLmV4aXN0cyhvdXRwdXRfZmlsZW5hbWUpKSB7CiAgICB3YXYgPSByZWFkV2F2ZShmaWx0ZXJlZF9kZiRmaWxlbmFtZVtpXSkKICAgIHdhdkBzYW1wLnJhdGUgPSByb3VuZChsZW5ndGgod2F2KSAvIGZpbHRlcmVkX2RmJHRydWVfbGVuZ3RoW2ldKQogICAgbWtkaXJzKGRpcm5hbWUob3V0cHV0X2ZpbGVuYW1lKSkKICAgIHdyaXRlV2F2ZSh3YXYsIGZpbGVuYW1lID0gb3V0cHV0X2ZpbGVuYW1lKQogIH0KfQpgYGAKClRoZSBwcm9ibGVtIHdpdGggdGhhdCB0aG91Z2gsIGlzIHRoYXQgc2FtcGxpbmcgcmF0ZXMgaW4gd2F2ZSBmaWxlcyBhcmUgaW50ZWdlcnMgLSB0aGVyZSdzIGEgbG9zcyBvZiBwcmVjaXNpb24gdGhlcmUuIEl0IHNob3VsZCBiZSBwb3NzaWJsZSB0byBtdWx0aXBseSBhIGRlc2lyZWQgc2Vjb25kIG9mZnNldCBieSB0aGUgdHJ1ZSBzYW1wbGluZyByYXRlIGFuZCBzbGljZSBhIHdhdiBmaWxlIHRoYXQgd2F5LgoKYGBge3J9CmRmJHRydWVfc3IgPSBkZiRzYW1wbGVzIC8gZGYkdHJ1ZV9sZW5ndGgKZGYkdHJ1ZV9zclshaXMubmEoZGYkdHJ1ZV9zcikgJiBkZiR0cnVlX3NyID4gNDQwMDBdID0gNDQxMDAKd3JpdGUuY3N2KGRmLCAibWlzYy9CQVJfdGltZXNwYW5zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpnZ3Bsb3QoZGZbZGYkZHVyYXRpb24gPiAxMDAsXSwgYWVzKHg9dHJ1ZV9zcikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD01KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KYXBwcm94X29mZnNldHMkY29ycmVjdGVkX29mZnNldCA9IHNhcHBseSgxOm5yb3coYXBwcm94X29mZnNldHMpLCBmdW5jdGlvbihpKSB7CiAgYXBwcm94X29mZnNldHMkb2Zmc2V0W2ldICogNDQxMDAgLyBkZiR0cnVlX3NyW2FwcHJveF9vZmZzZXRzJGZpbGVuYW1lW2ldID09IGRmJGZpbGVuYW1lXQp9KQp3cml0ZS5jc3YoYXBwcm94X29mZnNldHMsICJtaXNjL2FwcHJveF9vZmZzZXRzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpmaWx0ZXJlZF9vZmZzZXRzID0gYXBwcm94X29mZnNldHNbYXBwcm94X29mZnNldHMkc2l0ZSA9PSAiTk5TMSIgJiBhcHByb3hfb2Zmc2V0cyRmaWxlbmFtZSAlaW4lIGRmJGZpbGVuYW1lWyFpcy5uYShkZiR0cnVlX3NyKV0sXQpmb3IgKGkgaW4gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9vZmZzZXRzKSwgMTApKSB7CiAgZiA9IGZpbHRlcmVkX29mZnNldHMkZmlsZW5hbWVbaV0KICB0cnVlX3NyX2Zvcl9mID0gZGYkdHJ1ZV9zcltkZiRmaWxlbmFtZSA9PSBmXQogIGNsaXBfc3RhcnQgPSByb3VuZChmaWx0ZXJlZF9vZmZzZXRzJG9mZnNldFtpXSAqIHRydWVfc3JfZm9yX2YpCiAgY2xpcF9lbmQgPSByb3VuZChjbGlwX3N0YXJ0ICsgdHJ1ZV9zcl9mb3JfZikKICBwcmludChwYXN0ZShmLCB0cnVlX3NyX2Zvcl9mLCBjbGlwX3N0YXJ0LCBjbGlwX2VuZCkpCiAgd2F2ID0gcmVhZFdhdmUoZmlsZW5hbWUgPSBmLCBmcm9tID0gY2xpcF9zdGFydCwgdG8gPSBjbGlwX2VuZCwgdW5pdHM9InNhbXBsZXMiKQogIHdhdkBzYW1wLnJhdGUgPSB0cnVlX3NyX2Zvcl9mCiAgc3BlY3Rybyh3YXYsIG1haW49YmFzZW5hbWUoZiksIG9zYz1UUlVFKQogICNwbGF5KHdhdikKfQpgYGAKCmBgYHtyfQphbm5vdGF0aW9ucyRjb3JyZWN0ZWRfYmVnaW4gPSBzYXBwbHkoMTpucm93KGFubm90YXRpb25zKSwgZnVuY3Rpb24oaSkgewogIHRydWVfc3IgPSBkZiR0cnVlX3NyW3N0cl9kZXRlY3QoZGYkYmFzZV9maWxlbmFtZSwgZml4ZWQoYW5ub3RhdGlvbnMkZmlsZW5hbWVbaV0pKV0KICBhbm5vdGF0aW9ucyRCZWdpbi5UaW1lLi5zLltpXSAqIDQ0MTAwIC8gdHJ1ZV9zcgp9KQphbm5vdGF0aW9ucyRvZmZzZXQgPSBOQQpmb3IgKGkgaW4gMTpucm93KGFubm90YXRpb25zKSkgewogIGlmIChzdWJzdHJpbmcoYW5ub3RhdGlvbnMkSW5kaXZpZHVhbC5JRFtpXSwgMCwgMikgPT0gIlBCIikgewogICAgZmlsdGVyZWRfcmZpZCA9IGZpbHRlcihyZmlkLCBhbm5vdGF0aW9ucyROZXN0LklEW2ldID09IHNpdGUgJiBtc2cgPT0gcGFzdGUwKGFubm90YXRpb25zJEluZGl2aWR1YWwuSURbaV0sICIud2F2IikpCiAgICBtYXRjaGVkX3JlY19zdGFydCA9IGRmJHN0YXJ0W3N0cl9kZXRlY3QoZGYkYmFzZV9maWxlbmFtZSwgZml4ZWQoYW5ub3RhdGlvbnMkZmlsZW5hbWVbaV0pKV0KICAgIHJmaWRfb2Zmc2V0ID0gdGltZV9sZW5ndGgoZmlsdGVyZWRfcmZpZCRkYXRldGltZSAtIG1hdGNoZWRfcmVjX3N0YXJ0KQogICAgZGlmZnMgPSB0aW1lX2xlbmd0aChhbm5vdGF0aW9ucyRjb3JyZWN0ZWRfYmVnaW5baV0gLSByZmlkX29mZnNldCkKICAgIG1pbl9pID0gd2hpY2gubWluKGFicyhkaWZmcykpCiAgICBvZmZzZXQgPSBkaWZmc1ttaW5faV0KICAgIGlmIChsZW5ndGgob2Zmc2V0KSA9PSAxKSB7CiAgICAgIGFubm90YXRpb25zJG9mZnNldFtpXSA9IG9mZnNldAogICAgfQogIH0KfQpmaWx0ZXJlZF9hbm5vdGF0aW9ucyA9IGZpbHRlcihhbm5vdGF0aW9ucywgTmVzdC5JRCA9PSAiTk5TMSIgJiBhYnMob2Zmc2V0KSA8IDYwKQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkX2Fubm90YXRpb25zLCBhZXMoeD1jb3JyZWN0ZWRfYmVnaW4sIHk9b2Zmc2V0LCBjb2xvcj1zdWJzdHIoYmFzZW5hbWUoZmlsZW5hbWUpLCBzdGFydCA9IDYsIHN0b3AgPSAyMCkpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuID0gMTApKSArCiAgICBsYWJzKGNvbG9yPSdmaWxlIGRhdGV0aW1lJykgKwogICAgc3RhdF9wb2x5X2VxKGZvcm11bGEgPSB5IH4geCwKICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlKC4uZXEubGFiZWwuLiwgLi5yci5sYWJlbC4uLCBzZXAgPSAifn5+IikpLCAKICAgICAgICAgICAgICAgICAgIHBhcnNlID0gVFJVRSkgKwogICAgZ2d0aXRsZSgiRHJpZnQgcGVyIGZpbGUgYXQgTk5TMSIpCgpnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkX2Fubm90YXRpb25zLCBhZXMoeD1kYXRldGltZSwgeT1vZmZzZXQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuID0gMTApKSArCiAgICBsYWJzKGNvbG9yPSdmaWxlIGRhdGV0aW1lJykgKwogICAgc3RhdF9wb2x5X2VxKGZvcm11bGEgPSB5IH4geCwKICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlKC4uZXEubGFiZWwuLiwgLi5yci5sYWJlbC4uLCBzZXAgPSAifn5+IikpLCAKICAgICAgICAgICAgICAgICAgIHBhcnNlID0gVFJVRSkgKyAKICAgIGdndGl0bGUoIkRyaWZ0IHBlciBkYXkgYXQgTk5TMSIpCgpnZ3Bsb3QoZGF0YSA9IGFubm90YXRpb25zLCBhZXMoeD1jb3JyZWN0ZWRfYmVnaW4sIHk9b2Zmc2V0LCBjb2xvcj1OZXN0LklEKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDEwKSkKCmZpbHRlcmVkX2Fubm90YXRpb25zID0gZmlsdGVyKGFubm90YXRpb25zLCBOZXN0LklEID09ICJOTkJCMTIiKQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkX2Fubm90YXRpb25zLCBhZXMoeD1kYXRldGltZSwgeT1vZmZzZXQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuID0gMTApKSArCiAgICBsYWJzKGNvbG9yPSdmaWxlIGRhdGV0aW1lJykgKwogICAgc3RhdF9wb2x5X2VxKGZvcm11bGEgPSB5IH4geCwKICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlKC4uZXEubGFiZWwuLiwgLi5yci5sYWJlbC4uLCBzZXAgPSAifn5+IikpLCAKICAgICAgICAgICAgICAgICAgIHBhcnNlID0gVFJVRSkgKyAKICAgIGdndGl0bGUoIkRyaWZ0IHBlciBkYXkgYXQgTk5CQjEyIikKZmlsdGVyZWRfYW5ub3RhdGlvbnMgPSBmaWx0ZXIoYW5ub3RhdGlvbnMsIE5lc3QuSUQgPT0gIk5OSzM0IikKZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZF9hbm5vdGF0aW9ucywgYWVzKHg9ZGF0ZXRpbWUsIHk9b2Zmc2V0KSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDEwKSkgKwogICAgbGFicyhjb2xvcj0nZmlsZSBkYXRldGltZScpICsKICAgIHN0YXRfcG9seV9lcShmb3JtdWxhID0geSB+IHgsCiAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBwYXN0ZSguLmVxLmxhYmVsLi4sIC4ucnIubGFiZWwuLiwgc2VwID0gIn5+fiIpKSwgCiAgICAgICAgICAgICAgICAgICBwYXJzZSA9IFRSVUUpICsgCiAgICBnZ3RpdGxlKCJEcmlmdCBwZXIgZGF5IGF0IE5OSzM0IikKYGBgCgpgYGB7cn0KZiA9ICJOTlMxXzIwMTkxMjAzXzA2NDA0MF9TdW5yaXNlVG9TdW5zZXQgWy0zOS4wOTg2IDE3Ni44MDYxXS53YXYiCm9mZnNldCA9IG1lZGlhbihhbm5vdGF0aW9ucyRvZmZzZXRbYW5ub3RhdGlvbnMkZmlsZW5hbWUgPT0gZl0sIG5hLnJtPVRSVUUpICMgb2Zmc2V0IGZvciB0aGlzIGZpbGUKbWF0Y2hlZF9kZiA9IGRmW2RmJGJhc2VfZmlsZW5hbWUgPT0gZixdCm1hdGNoZWRfZXZlbnRfb2Zmc2V0cyA9IHRpbWVfbGVuZ3RoKHJmaWQkZGF0ZXRpbWVbcmZpZCRtc2cgPT0gIlBCMDAyMy53YXYiICYgcmZpZCRkYXRldGltZSAld2l0aGluJSBtYXRjaGVkX2RmJGludGVydmFsICYgcmZpZCRzaXRlID09IG1hdGNoZWRfZGYkc2l0ZV0gLSBtYXRjaGVkX2RmJHN0YXJ0KQpmb3IgKGJlZ2luX3RpbWUgaW4gbWF0Y2hlZF9ldmVudF9vZmZzZXRzKSB7CiAgc3IgPSBtYXRjaGVkX2RmJHRydWVfc3IKICBjbGlwX3N0YXJ0ID0gcm91bmQoKGJlZ2luX3RpbWUgKyBvZmZzZXQpICogc3IpCiAgY2xpcF9lbmQgPSByb3VuZChjbGlwX3N0YXJ0ICsgKHNyICogMikpCiAgcHJpbnQocGFzdGUobWF0Y2hlZF9kZiRmaWxlbmFtZSwgbWF0Y2hlZF9kZiR0cnVlX3NyLCBjbGlwX3N0YXJ0LCBjbGlwX2VuZCkpCiAgd2F2ID0gcmVhZFdhdmUoZmlsZW5hbWUgPSBtYXRjaGVkX2RmJGZpbGVuYW1lLCBmcm9tID0gY2xpcF9zdGFydCwgdG8gPSBjbGlwX2VuZCwgdW5pdHM9InNhbXBsZXMiKQogIHdhdkBzYW1wLnJhdGUgPSBzcgogIHNwZWN0cm8od2F2LCBtYWluPWJlZ2luX3RpbWUsIG9zYz1UUlVFKQogICNwbGF5KHdhdikKfQpgYGAKCk5vdCBiYWQuCgojIyBMZXQncyByZW5hbWUgdGhlc2UgUkZJRCBjb2RlcyB3aXRoIHNvbWV0aGluZyBhIGJpdCBtb3JlIHJlYWRhYmxlLiBXZSdsbCBuZWVkIHRvIG1haW50YWluIGEgd2F5IG9mIGNvbnZlcnRpbmcgYmV0d2Vlbi4KCgpgYGB7cn0KdW5pcXVlX3JmaWRzID0gdW5pcXVlKHJmaWQkbXNnW3N0cl9leHRyYWN0KHJmaWQkbXNnLCAiXi57Mn0iKSA9PSAiUkYiXSkKI2h1bWFuX3JlYWRhYmxlX3JmaWRzID0gcGFzdGUwKCJiaXJkIiwgMTpsZW5ndGgodW5pcXVlX3JmaWRzKSkKaHVtYW5fcmVhZGFibGVfcmZpZHMgPSBjKCJhbHBoYSIsICJicmF2byIsICJjaGFybGllIiwgImRlbHRhIiwgImVjaG8iLCAiZm94dHJvdCIsICJnb2xmIiwgImhvdGVsIiwgImluZGlhIiwgImp1bGlldCIsICJraWxvIiwgImxpbWEiLCAibWlrZSIsICJub3ZlbWJlciIpCmh1bWFuX3JlYWRhYmxlX3JmaWRzID0gc2V0TmFtZXMoaHVtYW5fcmVhZGFibGVfcmZpZHMsIHVuaXF1ZV9yZmlkcykKdW5pcXVlX3JmaWRzID0gc2V0TmFtZXModW5pcXVlX3JmaWRzLCBodW1hbl9yZWFkYWJsZV9yZmlkcykKCnByb2Nlc3NlZF9yZmlkID0gcmZpZAptYXNrID0gc3RyX2V4dHJhY3QocHJvY2Vzc2VkX3JmaWQkbXNnLCAiXi57Mn0iKSA9PSAiUkYiCnByb2Nlc3NlZF9yZmlkJG1zZ1ttYXNrXSA9IGh1bWFuX3JlYWRhYmxlX3JmaWRzW3Byb2Nlc3NlZF9yZmlkJG1zZ1ttYXNrXV0KZ2dwbG90KHByb2Nlc3NlZF9yZmlkW21hc2ssXSwgYWVzKHggPSBkYXRldGltZSwgY29sb3IgPSBtc2cpKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDYwICogNjAgKiAyNCkgKyAKICBnZ3RpdGxlKCJSRklEIGRldGVjdGlvbnMgcGVyIGRheSIpCmBgYAoKIyMgVHJ5IGRldGVjdCBlbnRyeSAvIGV4aXQgZXZlbnRzIGJ5IGxvb2tpbmcgYXQgbGlnaHQgZ2F0ZSBjaGFuZ2VzCgpgYGB7cn0KaWYgKGZpbGUuZXhpc3RzKCJtaXNjL3Byb2Nlc3NlZF9yZmlkLmNzdiIpKSB7CiAgcHJvY2Vzc2VkX3JmaWQyID0gcmVhZC5jc3YoIm1pc2MvcHJvY2Vzc2VkX3JmaWQuY3N2IikKICBwcm9jZXNzZWRfcmZpZDIkZGF0ZXRpbWUgPSB5bWRfaG1zKHByb2Nlc3NlZF9yZmlkMiRkYXRldGltZSkKfSBlbHNlIHsKICB0aW1lX3RocmVzaG9sZCA9IDIKICBMR190aHJlc2hvbGQgPSAxCiAgcHJvY2Vzc2VkX3JmaWQyID0gcGJsYXBwbHkoMTpzdW0obWFzayksIGZ1bmN0aW9uKGkpIHsKICAgIGR0ID0gcHJvY2Vzc2VkX3JmaWQkZGF0ZXRpbWVbbWFza11baV0KICAgIHRoaXNfc2l0ZSA9IHByb2Nlc3NlZF9yZmlkJHNpdGVbbWFza11baV0KICAgIGJpcmQgPSBwcm9jZXNzZWRfcmZpZCRtc2dbbWFza11baV0KICAgIExHID0gZmlsdGVyKHByb2Nlc3NlZF9yZmlkLCBzaXRlID09IHRoaXNfc2l0ZSAmIGFicyhkYXRldGltZSAtIGR0KSA8IHRpbWVfdGhyZXNob2xkICYgbXNnID09ICJMaWdodCBHYXRlIENoYW5nZSIpCiAgICBpZiAobnJvdyhMRykgPiAyKSB7CiAgICAgIGV2ZW50X3R5cGUgPSBOVUxMCiAgICAgIGV4dF9nID0gbWVhbihkaWZmKExHJGV4dCkpCiAgICAgIGludF9nID0gbWVhbihkaWZmKExHJGludCkpCiAgICAgIGlmIChleHRfZyA8IDAgJiBpbnRfZyA+IDApIHsKICAgICAgICBldmVudF90eXBlID0gImVudHJ5IgogICAgICB9IGVsc2UgaWYgKGludF9nIDwgMCAmIGV4dF9nID4gMCkgewogICAgICAgIGV2ZW50X3R5cGUgPSAiZXhpdCIKICAgICAgfQogICAgICBpZiAobGVuZ3RoKGV2ZW50X3R5cGUpID09IDEpIHsKICAgICAgICBwcmludChwYXN0ZShpLCBiaXJkLCBldmVudF90eXBlLCB0aGlzX3NpdGUsIGR0KSkKICAgICAgICByZXR1cm4oZGF0YS5mcmFtZShpID0gaSwgYmlyZCA9IGJpcmQsIGV2ZW50X3R5cGUgPSBldmVudF90eXBlLCBleHRfZyA9IGV4dF9nLCBpbnRfZyA9IGludF9nLCBzaXRlID0gdGhpc19zaXRlLCBkYXRldGltZSA9IGR0KSkKICAgICAgfQogICAgfQogIH0pCiAgcHJvY2Vzc2VkX3JmaWQyID0gZG8uY2FsbChyYmluZCwgcHJvY2Vzc2VkX3JmaWQpCiAgd3JpdGUuY3N2KHByb2Nlc3NlZF9yZmlkMiwgIm1pc2MvcHJvY2Vzc2VkX3JmaWQuY3N2Iiwgcm93Lm5hbWVzPUZBTFNFKQp9CmdncGxvdChwcm9jZXNzZWRfcmZpZDIsIGFlcyh4ID0gZGF0ZXRpbWUsIHkgPSBiaXJkLCBjb2xvciA9IGV2ZW50X3R5cGUpKSArIGdlb21fcG9pbnQoKQpnZ3Bsb3QoZmlsdGVyKHByb2Nlc3NlZF9yZmlkMiwgZGF0ZXRpbWUgPiB5bWQoIjIwMTktMTItMjgiKSAmIGRhdGV0aW1lIDwgeW1kKCIyMDE5LTEyLTI5IikpLCBhZXMoeCA9IGRhdGV0aW1lLCB5ID0gYmlyZCwgY29sb3IgPSBldmVudF90eXBlKSkgKyBnZW9tX3BvaW50KCkKYGBgClNlZW1zIGEgYml0IHRvbyB1bnJlbGlhYmxlIC0gdGhlIGxpZ2h0IGdhdGVzIG1pZ2h0IGJlIHRvbyBzZW5zaXRpdmUuIExvb2tzIGxpa2Ugd2UnbGwgaGF2ZSB0byBqdXN0IHNsaWNlIGFyb3VuZCBSRklEIHBhc3Nlcy4KCiMjIEJ1aWxkIGEgZGF0YWZyYW1lIG9mIGRhdGV0aW1lIGludGVydmFscyB0byBzbGljZSAtIGludGVsbGlnZW50bHkgb3ZlcmxhcHBpbmcgZm9yIHNhbWUgYmlyZCBpZCwgYW5kIHJlbW92aW5nIGludGVydmFscyB3aGVyZSB0aGVyZSBtaWdodCBiZSBjb25mdXNpb24gKGEgcGxheWJhY2ssIG9yIGFub3RoZXIgYmlyZCkKCmBgYHtyfQp0aW1lX3RocmVzaG9sZCA9IDUKZXZlbnRzX3RvX2lnbm9yZSA9IGMoIlBvd2VyLW9uIFJlc2V0IiwgIkxvdy12b2x0YWdlIERldGVjdCBSZXNldCIsICJMaWdodCBHYXRlIENoYW5nZSIsIAogICJXYXRjaGRvZyhDT1ApIFJlc2V0IiwgIlBsYXliYWNrIEVycm9yISBSZXN0YXJ0aW5nIiwgIlNvZnR3YXJlIFJlc2V0IgopCgppZiAoZmlsZS5leGlzdHMoIm1pc2MvZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZC5jc3YiKSkgewogIGR0c190b19zbGljZV9kZWR1cGxpY2F0ZWQgPSByZWFkLmNzdigibWlzYy9kdHNfdG9fc2xpY2VfZGVkdXBsaWNhdGVkLmNzdiIpCiAgZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCRzdGFydCA9IHltZF9obXMoZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCRzdGFydCkKICBkdHNfdG9fc2xpY2VfZGVkdXBsaWNhdGVkJGVuZCA9IHltZF9obXMoZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCRlbmQpCn0gZWxzZSB7CiAgZHRzX3RvX3NsaWNlID0gcGJsYXBwbHkoMTpzdW0obWFzayksIGZ1bmN0aW9uKGkpIHsKICAgICAgZHQgPSBwcm9jZXNzZWRfcmZpZCRkYXRldGltZVttYXNrXVtpXQogICAgICB0aGlzX3NpdGUgPSBwcm9jZXNzZWRfcmZpZCRzaXRlW21hc2tdW2ldCiAgICAgIGJpcmQgPSBwcm9jZXNzZWRfcmZpZCRtc2dbbWFza11baV0KICAgICAgdGhpc19pZCA9IHByb2Nlc3NlZF9yZmlkJGlkW21hc2tdW2ldCiAgICAgIAogICAgICBzdGFydCA9IGR0IC0gdGltZV90aHJlc2hvbGQKICAgICAgZW5kID0gZHQgKyB0aW1lX3RocmVzaG9sZAogICAgICBuZWFyYnlfZXZlbnRzID0gZmlsdGVyKHByb2Nlc3NlZF9yZmlkLCBpZCAhPSB0aGlzX2lkICYgc2l0ZSA9PSB0aGlzX3NpdGUgJiBhYnModGltZV9sZW5ndGgoZGF0ZXRpbWUgLSBkdCkpIDwgdGltZV90aHJlc2hvbGQgJiAhKG1zZyAlaW4lIGV2ZW50c190b19pZ25vcmUpKQogICAgICBvdGhlcl9iaXJkcyA9IGh1bWFuX3JlYWRhYmxlX3JmaWRzWyFodW1hbl9yZWFkYWJsZV9yZmlkcyA9PSBiaXJkXQogICAgICBpZiAoYW55KG90aGVyX2JpcmRzICVpbiUgbmVhcmJ5X2V2ZW50cyRtc2cpKSB7CiAgICAgICAgcHJpbnQocGFzdGUoaSwgImFub3RoZXIgYmlyZCB3aXRoaW4gdGhyZXNob2xkIikpCiAgICAgICAgcmV0dXJuKE5VTEwpCiAgICAgIH0KICAgICAgaWYgKGFueShzdHJfZXh0cmFjdChuZWFyYnlfZXZlbnRzJG1zZywgIl4uezJ9IikgPT0gIlBCIikpIHsKICAgICAgICBwcmludChwYXN0ZShpLCAicGxheWJhY2sgd2l0aGluIHRocmVzaG9sZCIpKQogICAgICAgIHJldHVybihOVUxMKQogICAgICB9CiAgICAgIGRhdGEuZnJhbWUoaSA9IGksIHNpdGUgPSB0aGlzX3NpdGUsIGJpcmQgPSBiaXJkLCBzdGFydCA9IHN0YXJ0LCBlbmQgPSBlbmQsIGludGVydmFsID0gaW50ZXJ2YWwoc3RhcnQsIGVuZCkpCiAgfSkKICBkdHNfdG9fc2xpY2UgPSBkby5jYWxsKHJiaW5kLCBkdHNfdG9fc2xpY2UpCiAgd3JpdGUuY3N2KGR0c190b19zbGljZSwgIm1pc2MvZHRzX3RvX3NsaWNlLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQogIAogIGR0c190b19zbGljZV9kZWR1cGxpY2F0ZWQgPSBkdHNfdG9fc2xpY2UgJT4lIGdyb3VwX2J5KHNpdGUsIGJpcmQpICU+JSBhcnJhbmdlKHN0YXJ0KSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShpbmR4ID0gYygwLCBjdW1zdW0oYXMubnVtZXJpYyhsZWFkKHN0YXJ0KSkgPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1bW1heChhcy5udW1lcmljKGVuZCkpKVstbigpXSkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShzaXRlLCBiaXJkLCBpbmR4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHN0YXJ0ID0gbWluKHN0YXJ0KSwgZW5kID0gbWF4KGVuZCkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWluZHgpCiAgZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCRkdXJhdGlvbiA9IHRpbWVfbGVuZ3RoKGR0c190b19zbGljZV9kZWR1cGxpY2F0ZWQkZW5kIC0gZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCRzdGFydCkKICB3cml0ZS5jc3YoZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCwgIm1pc2MvZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKfQpnZ3Bsb3QoZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCwgYWVzKGR1cmF0aW9uKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpCmBgYAoKIyMgUHV0IG1lZGlhbiBvZmZzZXQgcGVyIGZpbGUgaW4gZGYKIyMgRm9yIGVhY2ggaW50ZXJ2YWwgYXJvdW5kIGEgUkZJRCBwYXNzLCBvdXRwdXQgYSB3YXZlIGNsaXAsIGNvcnJlY3RlZCBmb3Igb2Zmc2V0IGFuZCBTUgoKYGBge3J9CmRmJG9mZnNldCA9IHNhcHBseShkZiRiYXNlX2ZpbGVuYW1lLCBmdW5jdGlvbihmKSB7CiAgbWVkaWFuKGFubm90YXRpb25zJG9mZnNldFtwYXN0ZTAoYW5ub3RhdGlvbnMkZmlsZW5hbWUpID09IGZdLCBuYS5ybSA9IFRSVUUpCn0pCmludmlzaWJsZShwYmxhcHBseSgxOm5yb3coZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCksIGZ1bmN0aW9uKGkpIHsKICBzdGFydCA9IGR0c190b19zbGljZV9kZWR1cGxpY2F0ZWQkc3RhcnRbaV0KICBkdXJhdGlvbiA9IGR0c190b19zbGljZV9kZWR1cGxpY2F0ZWQkZHVyYXRpb25baV0KICBiaXJkID0gZHRzX3RvX3NsaWNlX2RlZHVwbGljYXRlZCRiaXJkW2ldCiAgc2l0ZSA9IGR0c190b19zbGljZV9kZWR1cGxpY2F0ZWQkc2l0ZVtpXQogIGZvcm1hdHRlZF9kYXRlID0gZm9ybWF0KHN0YXJ0LCAiJVktJW0tJWRfJUglTSVTX05aRFQiKQogIG91dHB1dF9maWxlbmFtZSA9IHBhc3RlMCgiZGF0YS8yMDE5IEJBUiByZWNvcmRpbmdzIC0gaW5kaXZpZHVhbCBiaXJkIGNsaXBzLyIsIGJpcmQsICIvIiwgc2l0ZSwgIl8iLCBmb3JtYXR0ZWRfZGF0ZSwgIi53YXYiKQogIGlmIChmaWxlLmV4aXN0cyhvdXRwdXRfZmlsZW5hbWUpKSB7CiAgICByZXR1cm4oTlVMTCkKICB9CiAgbWF0Y2hlZF9kZiA9IGRmW2RmJHNpdGUgPT0gc2l0ZSAmIHN0YXJ0ICV3aXRoaW4lIGRmJGludGVydmFsICYgIWlzLm5hKGRmJG9mZnNldCkgJiAhaXMubmEoZGYkdHJ1ZV9zciksXQogIGlmIChucm93KG1hdGNoZWRfZGYpID09IDEpIHsKICAgIHNyID0gbWF0Y2hlZF9kZiR0cnVlX3NyCiAgICBjbGlwX3N0YXJ0X3MgPSB0aW1lX2xlbmd0aChzdGFydCAtIG1hdGNoZWRfZGYkc3RhcnQpICsgbWF0Y2hlZF9kZiRvZmZzZXQKICAgIGNsaXBfc3RhcnRfc2FtcGxlcyA9IHJvdW5kKGNsaXBfc3RhcnRfcyAqIHNyKQogICAgY2xpcF9lbmRfc2FtcGxlcyA9IHJvdW5kKGNsaXBfc3RhcnRfc2FtcGxlcyArIChzciAqIGR1cmF0aW9uKSkKICAgICNwcmludChwYXN0ZShpLCBiaXJkLCBzaXRlLCBzdGFydCwgZHVyYXRpb24sIG1hdGNoZWRfZGYkZmlsZW5hbWUsIG1hdGNoZWRfZGYkdHJ1ZV9zciwgY2xpcF9zdGFydF9zYW1wbGVzLCBjbGlwX2VuZF9zYW1wbGVzKSkKICAgIHRyeSh7CiAgICAgIHdhdiA9IHJlYWRXYXZlKGZpbGVuYW1lID0gbWF0Y2hlZF9kZiRmaWxlbmFtZSwgZnJvbSA9IGNsaXBfc3RhcnRfc2FtcGxlcywgdG8gPSBjbGlwX2VuZF9zYW1wbGVzLCB1bml0cz0ic2FtcGxlcyIpCiAgICAgIHdhdkBzYW1wLnJhdGUgPSBzcgogICAgICBpZiAoc3IgIT0gNDQxMDApIHsKICAgICAgICB3YXYgPSByZXNhbXAod2F2LCBnID0gNDQxMDAsIG91dHB1dCA9ICJXYXZlIikKICAgICAgfQogICAgICB3YXZAbGVmdCA9IHJvdW5kKHdhdkBsZWZ0KQogICAgICBta2RpcnMoZGlybmFtZShvdXRwdXRfZmlsZW5hbWUpKQogICAgICB3cml0ZVdhdmUod2F2LCBmaWxlbmFtZSA9IG91dHB1dF9maWxlbmFtZSwgZXh0ZW5zaWJsZSA9IEYpCiAgICB9KQogIH0KfSkpCmBgYAoKIyMgVXNlIG1vbml0b1IgdG8gcmVtb3ZlIHNpbGVuY2UgaW4gc2xpY2VkIGNsaXBzCgpgYGB7cn0KZmlsZXMgPSBsaXN0LmZpbGVzKCJkYXRhL1NvdW5kIHRlbXBsYXRlcyIsIHBhdHRlcm4gPSAiKi50eHQkIiwgZnVsbC5uYW1lcz1UUlVFKQpmID0gZmlsZXNbMV0Kc2VsZWN0aW9uX3RhYmxlID0gcmVhZC50YWJsZShmLCBoZWFkZXI9VFJVRSwgc2VwPSJcdCIpCmZyZXFfbGltID0gYyhzZWxlY3Rpb25fdGFibGUkTG93LkZyZXEuLkh6Liwgc2VsZWN0aW9uX3RhYmxlJEhpZ2guRnJlcS4uSHouKS8xMDAwCndhdmVfZmlsZW5hbWUgPSBzdHJfcmVwbGFjZShmLCAiLlRhYmxlLjEuc2VsZWN0aW9ucy50eHQiLCAiLndhdiIpCnRlbXBsYXRlX2lkID0gc3Vic3RyKGJhc2VuYW1lKGYpLCAxLCA4KQp0ZW1wbGF0ZV93YXYgPSByZWFkV2F2ZSh3YXZlX2ZpbGVuYW1lLCBmcm9tID0gc2VsZWN0aW9uX3RhYmxlJEJlZ2luLlRpbWUuLnMuLCB0byA9IHNlbGVjdGlvbl90YWJsZSRFbmQuVGltZS4ucy4sIHVuaXRzPSJzZWNvbmRzIikKdGVtcGxhdGVfd2F2ID0gcmVzYW1wKHRlbXBsYXRlX3dhdiwgZyA9IDQ0MTAwLCBvdXRwdXQgPSAiV2F2ZSIpCnRlbXBsYXRlX2R1cmF0aW9uID0gbGVuZ3RoKHRlbXBsYXRlX3dhdkBsZWZ0KSAvIDQ0MTAwCnByb2Nlc3NlZF90ZW1wbGF0ZV93YXZlX2ZpbGVuYW1lID0gcGFzdGUwKCJtaXNjLyIsIGJhc2VuYW1lKHdhdmVfZmlsZW5hbWUpKQpzYXZld2F2KHRlbXBsYXRlX3dhdiwgZmlsZW5hbWUgPSBwcm9jZXNzZWRfdGVtcGxhdGVfd2F2ZV9maWxlbmFtZSkKY3RlbXBzID0gbWFrZUNvclRlbXBsYXRlKHByb2Nlc3NlZF90ZW1wbGF0ZV93YXZlX2ZpbGVuYW1lLCB3bCA9IDMwMCwgZnJxLmxpbSA9IGZyZXFfbGltLCBuYW1lID0gdGVtcGxhdGVfaWQpCnRlbXBsYXRlQ3V0b2ZmKGN0ZW1wcykgPSBjKGRlZmF1bHQ9LjUpCgpidWZmZXIgPSAwCgpmb3IgKGJpcmQgaW4gaHVtYW5fcmVhZGFibGVfcmZpZHMpIHsKICBmaWxlc19mb3JfYmlyZCA9IFN5cy5nbG9iKHBhc3RlMCgiZGF0YS8yMDE5IEJBUiByZWNvcmRpbmdzIC0gaW5kaXZpZHVhbCBiaXJkIGNsaXBzLyIsIGJpcmQsICIvKi53YXYiKSkKICBjbGlwcyA9IGMoKQogIGlmIChsZW5ndGgoZmlsZXNfZm9yX2JpcmQpID09IDApIHsKICAgIG5leHQKICB9CiAgZm9yIChpIGluIDE6bGVuZ3RoKGZpbGVzX2Zvcl9iaXJkKSkgewogICAgZiA9IGZpbGVzX2Zvcl9iaXJkW2ldCiAgICBjc2NvcmVzID0gY29yTWF0Y2goZiwgY3RlbXBzLCBxdWlldD1UUlVFKQogICAgY2RldGVjdHMgPSBmaW5kUGVha3MoY3Njb3JlcykKICAgIGRldGVjdHMgPSBnZXREZXRlY3Rpb25zKGNkZXRlY3RzKQogICAgaWYgKGkgPT0gMSkgewogICAgICBwbG90KGNkZXRlY3RzKQogICAgfQogICAgaWYgKG5yb3coZGV0ZWN0cykgPiAxKSB7CiAgICAgIGZvciAoaiBpbiAxOm5yb3coZGV0ZWN0cykpIHsKICAgICAgICBzdGFydF90aW1lID0gZGV0ZWN0cyR0aW1lW2pdCiAgICAgICAgY2xpcCA9IHJlYWRXYXZlKGYsIGZyb20gPSBzdGFydF90aW1lIC0gYnVmZmVyLCB0byA9IHN0YXJ0X3RpbWUgKyB0ZW1wbGF0ZV9kdXJhdGlvbiArIGJ1ZmZlciwgdW5pdHMgPSAic2Vjb25kcyIpQGxlZnQKICAgICAgICBjbGlwcyA9IGMoY2xpcHMsIGNsaXApCiAgICAgIH0KICAgIH0KICB9CiAgaWYgKGxlbmd0aChjbGlwcykgPiAxMCkgewogICAgd2F2ID0gV2F2ZShjbGlwcywgc2FtcC5yYXRlID0gNDQxMDAsIGJpdCA9IDE2KQogICAgb3V0cHV0X2ZpbGVuYW1lID0gcGFzdGUwKCJkYXRhLzIwMTkgQkFSIHJlY29yZGluZ3MgLSBpbmRpdmlkdWFsIGJpcmQgY2xpcHMvIiwgYmlyZCwgIi53YXYiKQogICAgd3JpdGVXYXZlKHdhdiwgb3V0cHV0X2ZpbGVuYW1lLCBleHRlbnNpYmxlID0gRikKICB9Cn0KYGBgCgo=