This is a document to support our manuscript submission to International Journal of Epidemiology. In this page, we present the code used for estimating ICC values under exchangeable, nested/block exchangeable and exponential decay models for longitudinal cluster randomized trials. The same code can be used to analyzing data from longitudinal cluster randomized trials under different assumed correlation structures.

Cross-sectional design

Part 1: R code

Model fitting

# Load packages 
library(lme4)
library(glmmTMB)
# Load your dataset
data <- write.csv("replace it by the local path to your dataset")
# Convert Period, Cluster and Treatment to factors 
# (if they were coded as numerical variables in the dataset)
data[,c("Period", "Cluster", "Treatment")] <- 
  lapply(data[,c("Period", "Cluster", "Treatment")], factor)

# Fit three models
# Exchangeable model
fit_HH <- lmer(Outcome ~  Period + Treatment + (1|Cluster), data = data)
# Nested exchangeable model
fit_HG <- lmer(Outcome ~  Period + Treatment +(1|Cluster) + (1|Period:Cluster), data = data)
# Exponential decay model
fit_AR <- glmmTMB(Outcome ~ Period  +  Treatment + ar1(Period + 0 | Cluster), REML=T, 
                  data = data, family = gaussian)

ICC calculations

ICC can be calculated manually using the formula we provided in our manuscript or run the following code

# Extract variance component estimates
V1 <- as.data.frame(VarCorr(fit_HH))
V2 <- as.data.frame(VarCorr(fit_HG))
V3 <- VarCorr(fit_AR)

# Estimate within-period ICC for exchangeable model
wpicc_hh <- round(V1[,"vcov"][which(V1[,"grp"] == "Cluster")]/sum(V1[,"vcov"]),4)

# Estimate within-, between-period ICC and cac for nested exchangeable model
wpicc_hg <- round((V2[,"vcov"][which(V2[,"grp"] == "Cluster")] + V2[,"vcov"][which(V2[,"grp"] == "Period:Cluster")])/sum(V2[,"vcov"]),4)
bpicc_hg <- round((V2[,"vcov"][which(V2[,"grp"] == "Cluster")])/sum(V2[,"vcov"]),4)
cac_hg <- round(bpicc_hg/wpicc_hg,4)

# Estimate within-period ICC and decay rate for exponential decay model
wpicc_ar <- round(V3$cond$Cluster[1,1]/sum(V3$cond$Cluster[1,1],(attr(V3$cond,"sc"))^2),4)
ar1 <- round(attr(V3$cond$Cluster,"correlation")[1,2],4)

Part 2: SAS code

Model fitting

**Load your data properly in SAS;
**Exchangeable model;
PROC MIXED DATA=data ;
CLASS Cluster Period Treatment;   
MODEL Outcome =  Period Treatment/ SOLUTION;
RANDOM Cluster;
ods output covparms=HHcovparms fitstatistics=HHfitstatistics solutionf=HHsolutionf;
RUN;

**Nested exchangeable;
PROC MIXED DATA=data;
CLASS Cluster Period Treatment;  
MODEL Outcome =  Period Treatment/ SOLUTION;
RANDOM Cluster Period*Cluster ;
ods output covparms=NEcovparms fitstatistics=NEfitstatistics solutionf=NEsolutionf;
RUN;


**Exponential decay;
**PROC HPMIXED can be used to improve the computational speed;
PROC HPMIXED DATA=data;
CLASS Cluster Period Treatment;  
MODEL Outcome =  Period Treatment/ SOLUTION;
RANDOM Period / SUBJECT=Cluster  TYPE=AR(1);
ods output covparms=ARcovparms fitstatistics=ARfitstatistics solutionf=ARsolutionf;
RUN;

ICC calculations

**Estimate within-period ICC for exchangeable model;
PROC SQL; 
CREATE TABLE icchh AS 
SELECT sum(estimate*(covparm='Cluster')) / sum(estimate) AS wpicc 
FROM HHcovparms; 
QUIT;


**Estimate within-, between-period ICC and cac for nested exchangeable model;
PROC SQL; 
CREATE TABLE icchg AS 
SELECT
sum(estimate*(covparm ^='Residual'))/sum(estimate) AS wpicc,
sum(estimate*(covparm='Cluster')) / sum(estimate) AS bpicc,
sum(estimate*(covparm='Cluster'))/ sum(estimate*(covparm ^='Residual')) AS cac 
FROM Necovparms; 
QUIT;


**Estimate within-period ICC and decay rate for exponential decay model;
PROC SQL; 
CREATE TABLE iccar AS
SELECT 
sum(estimate*(covparm='Variance')) / sum(estimate*(covparm ^='AR(1)')) AS wpicc,
sum(estimate*(covparm='AR(1)')) as ar1 
FROM ARcovparms; 
QUIT;

Part 3: Stata code

# Load your dataset
import delimited "replace it by the local path to your dataset", clear
# Exchangeable model
mixed outcome i.period i.treatment_n || cluster:, reml
# Block exchangeable model
mixed outcome i.period i.treatment_n || cluster: || period:, reml
# ICC calculations
. estat icc

Cohort design

Part 1: R code

Model fitting

# Load packages 
library(lme4)
library(glmmTMB)
# Load your dataset
data <- write.csv("replace it by the local path to your dataset")
# Convert Period, Cluster and Treatment to factors 
# (if they were coded as numerical variables in the dataset)
data[,c("Period", "Cluster", "ID", "Treatment")] <- 
  lapply(data[,c("Period", "Cluster", "ID", "Treatment")], factor)
# Exchangeable model
fit_HH <- lmer(Outcome ~  Period + Treatment + (1|Cluster) + (1|ID), data = data)
# Block exchangeable model
fit_HG <- lmer(Outcome ~  Period + Treatment +(1|Cluster) + (1|Period:Cluster) + (1|ID), data = data)
# Exponential decay model
fit_AR <- glmmTMB(Outcome ~ Period  + Treatment + ar1(Period + 0 | Cluster) + (1|ID), REML=T, data = data, family = gaussian)

ICC calculations

# Extract variance component estimates        
V1 <- as.data.frame(VarCorr(fit_HH))
V2 <- as.data.frame(VarCorr(fit_HG))
V3 <- VarCorr(fit_AR)

# Estimate within-period ICC and IAC for exchangeable model
wpicc_hh <- round(V1[,"vcov"][which(V1[,"grp"] == "Cluster")]/sum(V1[,"vcov"]),4)
wiicc_hh <- round(sum(V1[,"vcov"][which(V1[,"grp"] == "Cluster")], V1[,"vcov"][which(V1[,"grp"] == "ID")])/sum(V1[,"vcov"]),4)

# Estimate within-, between-period ICC, IAC and CAC for nested exchangeable model
wpicc_hg <- round((V2[,"vcov"][which(V2[,"grp"] == "Cluster")] + V2[,"vcov"][which(V2[,"grp"] == "Period:Cluster")])/sum(V2[,"vcov"]),4)
bpicc_hg <- round((V2[,"vcov"][which(V2[,"grp"] == "Cluster")])/sum(V2[,"vcov"]),4)
wiicc_hg <- round(sum(V2[,"vcov"][which(V2[,"grp"] == "Cluster")],V2[,"vcov"][which(V2[,"grp"] == "ID")])/sum(V2[,"vcov"]),4)
cac_hg <- round((V2[,"vcov"][which(V2[,"grp"] == "Cluster")])/(V2[,"vcov"][which(V2[,"grp"] == "Cluster")] + V2[,"vcov"][which(V2[,"grp"] == "Period:Cluster")]),4)

# Estimate within-period ICC, IAC and decay rate(CAC) for exponential decay model
wpicc_ar <- round(V3$cond$Cluster[1,1]/((attr(V3$cond$ID,"stddev"))^2+ V3$cond$Cluster[1,1]+(attr(V3$cond,"sc"))^2),4)
ar1 <- round(attr(V3$cond$Cluster,"correlation")[1,2],4)
wiicc_ar <- round(((attr(V3$cond$ID,"stddev"))^2 + V3$cond$Cluster[1,1])/((attr(V3$cond$ID,"stddev"))^2+ V3$cond$Cluster[1,1]+(attr(V3$cond,"sc"))^2),3)

Part 2: SAS code

Model fitting

**Load your data properly in SAS;
**Exchangeable model;
**HH model;
PROC MIXED DATA=data;
CLASS Cluster Period ID Treatment;   
MODEL Outcome =  Period Treatment/ SOLUTION;
RANDOM int/SUBJECT=Cluster;
RANDOM int/SUBJECT=ID;
ods output covparms=HHcovparms fitstatistics=HHfitstatistics solutionf=HHsolutionf;
RUN;

**Hooper and Girling;
PROC MIXED DATA=data;
CLASS Cluster Period ID Treatment;  
MODEL Outcome =  Period Treatment/ SOLUTION;
RANDOM Cluster Period*Cluster;
RANDOM int/SUBJECT=ID;
ods output covparms=NEcovparms fitstatistics=NEfitstatistics solutionf=NEsolutionf;
RUN;


** Exponential decay;
** PROC HPMIXED can be used to improve the computational speed;
PROC MIXED DATA=data;
CLASS Cluster Period ID Treatment;  
MODEL Outcome =  Period Treatment/ SOLUTION;
RANDOM Period / SUBJECT=Cluster  TYPE=AR(1);
RANDOM int/SUBJECT=ID;
RUN;

ICC calculations

**ICC calculations based on the fitted models;
**Estimate within-period ICC for exchangeable model;
PROC SQL; 
CREATE TABLE icchh AS 
SELECT sum(estimate*(covparm='Cluster')) / sum(estimate) AS wpicc 
FROM HHcovparms; 
QUIT;


**Estimate within-, between-period ICC and cac for nested exchangeable model;
PROC SQL; 
CREATE TABLE icchg AS 
SELECT
sum(estimate*(covparm ^='Residual'))/sum(estimate) AS wpicc,
sum(estimate*(covparm='Cluster')) / sum(estimate) AS bpicc,
sum(estimate*(covparm='Cluster'))/ sum(estimate*(covparm ^='Residual')) AS cac 
FROM Necovparms; 
QUIT;


**Estimate within-period ICC and decay rate for exponential decay model;
PROC SQL; 
CREATE TABLE iccar AS
SELECT 
sum(estimate*(covparm='Variance')) / sum(estimate*(covparm ^='AR(1)')) AS wpicc,
sum(estimate*(covparm='AR(1)')) as ar1 
FROM ARcovparms; 
QUIT;


**Estimate within-period ICC for exchangeable model;
PROC SQL; 
CREATE TABLE icchh AS 
SELECT 
sum(estimate*(subject='Cluster')) / sum(estimate) AS wpicc, 
sum(estimate*(subject='Cluster') + estimate*(subject='ID')) / sum(estimate) AS wiicc 
FROM HHcovparms; 
QUIT;


**Estimate within-, between-period ICC and cac for nested exchangeable model;
PROC SQL; 
CREATE TABLE icchg AS 
SELECT
sum(estimate*(covparm='Cluster') + estimate*(covparm='Cluster*Period'))/sum(estimate) AS wpicc,
sum(estimate*(covparm='Cluster')) / sum(estimate) AS bpicc,
sum(estimate*(covparm='Cluster') + estimate*(covparm='Intercept')) / sum(estimate) AS wiicc,
sum(estimate*(covparm='Cluster'))/ sum(estimate*(covparm='Cluster') + estimate*(covparm='Cluster*Period')) AS cac 
FROM Necovparms; 
QUIT;


**Estimate within-period ICC and decay rate for exponential decay model;
PROC SQL; 
CREATE TABLE iccar AS
SELECT 
sum(estimate*(covparm='Variance')) / sum(estimate*(covparm ^='AR(1)')) AS wpicc,
sum(estimate*(covparm='Intercept') + estimate*(covparm='Variance')) / sum(estimate*(covparm ^='AR(1)')) AS wiicc,
sum(estimate*(covparm='AR(1)')) as ar1 
FROM ARcovparms; 
QUIT;

Part 3: Stata code

# Load your dataset
import delimited "replace it by the local path to your dataset", clear
# Exchangeable model
mixed outcome i.period_n i.treatment || cluster: || id:, reml
# Block exchangeable model
mixed outcome i.period_n treatment || cluster: || cluster: R.period_n || id:
# ICC calculations
. estat icc

Code used to create individual-level data with denominator and numerator information

R code

# First properly load the dataset following our template
# Create a column for number of non-successes 
data$non_suc <- data$Denominator-data$Numerator
# Create cluster ID for each individual
Cluster <- rep(data$Cluster, data$Denominator)
# Create period ID for each individual
Period <- rep(data$Period, data$Denominator)
# Create individual-level binary outcome
Outcome <- c()
for(i in 1:nrow(data)){
    temp <- c(rep(1, data$Numerator[i]),rep(0, data$non_suc[i]))
    Outcome <- c(Outcome, temp)
  }
# Form an analytic dataset
data_analytic <- data.frame(Outcome = Outcome, Cluster = Cluster, Period = Period)

SAS code

DATA data;
set data.analytic;
do i=1 to Numerator; 
y=1; 
output;
end;
do i= Numerator+1 to Denominator; 
y=0; 
output;
end;
n=1; /*for all, useful for counting # procedures per month per cluster later*/
run;

Stata code

*imported dataset contains variables:
*cluster: cluster ID
*denominator: number of observations in each cluster
*events: number of events in each cluster
*This code creates individual level data for the events

clear

import excel "Path to the original dataset", sheet("Sheet1") firstrow

gen none_events=denominator-events

expandcl events, cluster( cluster) generate(outcome)

replace outcome=1

tempfile ipd_events

save `ipd_events'

 

*This code creates individual level data for the no events

This code creates individual level data for the events

clear

import excel ""path to dataset\data.xlsx", sheet("Sheet1") firstrow

gen none_events=denominator-events

expandcl events, cluster( cluster) generate(outcome)

replace outcome=1

tempfile ipd_events

save `ipd_events'

 

*This code creates individual level data for the no events

clear

import excel "path to dataset\data.xlsx", sheet("Sheet1") firstrow

gen none_events=denominator-events

expandcl none_events, cluster( cluster) generate(outcome)

replace outcome=0

tempfile ipd_noevents

save `ipd_noevents'

append using "`ipd_events'"
LS0tDQp0aXRsZTogIkNvZGUgLSBlc3RpbWF0aW5nIGludHJhLWNsdXN0ZXIgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGZvciBwbGFubmluZyBsb25naXR1ZGluYWwgY2x1c3RlciByYW5kb21pemVkIHRyaWFsczogYSB0dXRvcmlhbCINCmF1dGhvcjogWW9uZ2RvbmcgT3V5YW5nDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIHRvYzogeWVzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj02MCksdGlkeT1UUlVFKQ0KYGBgDQpUaGlzIGlzIGEgZG9jdW1lbnQgdG8gc3VwcG9ydCBvdXIgbWFudXNjcmlwdCBzdWJtaXNzaW9uIHRvIEludGVybmF0aW9uYWwgSm91cm5hbCBvZiBFcGlkZW1pb2xvZ3kuIEluIHRoaXMgcGFnZSwgd2UgcHJlc2VudCB0aGUgY29kZSB1c2VkIGZvciBlc3RpbWF0aW5nIElDQyB2YWx1ZXMgdW5kZXIgZXhjaGFuZ2VhYmxlLCBuZXN0ZWQvYmxvY2sgZXhjaGFuZ2VhYmxlIGFuZCBleHBvbmVudGlhbCBkZWNheSBtb2RlbHMgZm9yIGxvbmdpdHVkaW5hbCBjbHVzdGVyIHJhbmRvbWl6ZWQgdHJpYWxzLiBUaGUgc2FtZSBjb2RlIGNhbiBiZSB1c2VkIHRvIGFuYWx5emluZyBkYXRhIGZyb20gbG9uZ2l0dWRpbmFsIGNsdXN0ZXIgcmFuZG9taXplZCB0cmlhbHMgdW5kZXIgZGlmZmVyZW50IGFzc3VtZWQgY29ycmVsYXRpb24gc3RydWN0dXJlcy4NCg0KDQojIENyb3NzLXNlY3Rpb25hbCBkZXNpZ24NCiMjIFBhcnQgMTogUiBjb2RlDQojIyMgTW9kZWwgZml0dGluZw0KYGBge3IsIGV2YWw9IEZ9DQojIExvYWQgcGFja2FnZXMgDQpsaWJyYXJ5KGxtZTQpDQpsaWJyYXJ5KGdsbW1UTUIpDQojIExvYWQgeW91ciBkYXRhc2V0DQpkYXRhIDwtIHdyaXRlLmNzdigicmVwbGFjZSBpdCBieSB0aGUgbG9jYWwgcGF0aCB0byB5b3VyIGRhdGFzZXQiKQ0KIyBDb252ZXJ0IFBlcmlvZCwgQ2x1c3RlciBhbmQgVHJlYXRtZW50IHRvIGZhY3RvcnMgDQojIChpZiB0aGV5IHdlcmUgY29kZWQgYXMgbnVtZXJpY2FsIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCkNCmRhdGFbLGMoIlBlcmlvZCIsICJDbHVzdGVyIiwgIlRyZWF0bWVudCIpXSA8LSANCiAgbGFwcGx5KGRhdGFbLGMoIlBlcmlvZCIsICJDbHVzdGVyIiwgIlRyZWF0bWVudCIpXSwgZmFjdG9yKQ0KDQojIEZpdCB0aHJlZSBtb2RlbHMNCiMgRXhjaGFuZ2VhYmxlIG1vZGVsDQpmaXRfSEggPC0gbG1lcihPdXRjb21lIH4gIFBlcmlvZCArIFRyZWF0bWVudCArICgxfENsdXN0ZXIpLCBkYXRhID0gZGF0YSkNCiMgTmVzdGVkIGV4Y2hhbmdlYWJsZSBtb2RlbA0KZml0X0hHIDwtIGxtZXIoT3V0Y29tZSB+ICBQZXJpb2QgKyBUcmVhdG1lbnQgKygxfENsdXN0ZXIpICsgKDF8UGVyaW9kOkNsdXN0ZXIpLCBkYXRhID0gZGF0YSkNCiMgRXhwb25lbnRpYWwgZGVjYXkgbW9kZWwNCmZpdF9BUiA8LSBnbG1tVE1CKE91dGNvbWUgfiBQZXJpb2QgICsgIFRyZWF0bWVudCArIGFyMShQZXJpb2QgKyAwIHwgQ2x1c3RlciksIFJFTUw9VCwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSwgZmFtaWx5ID0gZ2F1c3NpYW4pDQpgYGANCg0KIyMjIElDQyBjYWxjdWxhdGlvbnMNCg0KSUNDIGNhbiBiZSBjYWxjdWxhdGVkIG1hbnVhbGx5IHVzaW5nIHRoZSBmb3JtdWxhIHdlIHByb3ZpZGVkIGluIG91ciBtYW51c2NyaXB0IG9yIHJ1biB0aGUgZm9sbG93aW5nIGNvZGUNCmBgYHtyLCBldmFsPSBGfQ0KIyBFeHRyYWN0IHZhcmlhbmNlIGNvbXBvbmVudCBlc3RpbWF0ZXMNClYxIDwtIGFzLmRhdGEuZnJhbWUoVmFyQ29ycihmaXRfSEgpKQ0KVjIgPC0gYXMuZGF0YS5mcmFtZShWYXJDb3JyKGZpdF9IRykpDQpWMyA8LSBWYXJDb3JyKGZpdF9BUikNCg0KIyBFc3RpbWF0ZSB3aXRoaW4tcGVyaW9kIElDQyBmb3IgZXhjaGFuZ2VhYmxlIG1vZGVsDQp3cGljY19oaCA8LSByb3VuZChWMVssInZjb3YiXVt3aGljaChWMVssImdycCJdID09ICJDbHVzdGVyIildL3N1bShWMVssInZjb3YiXSksNCkNCg0KIyBFc3RpbWF0ZSB3aXRoaW4tLCBiZXR3ZWVuLXBlcmlvZCBJQ0MgYW5kIGNhYyBmb3IgbmVzdGVkIGV4Y2hhbmdlYWJsZSBtb2RlbA0Kd3BpY2NfaGcgPC0gcm91bmQoKFYyWywidmNvdiJdW3doaWNoKFYyWywiZ3JwIl0gPT0gIkNsdXN0ZXIiKV0gKyBWMlssInZjb3YiXVt3aGljaChWMlssImdycCJdID09ICJQZXJpb2Q6Q2x1c3RlciIpXSkvc3VtKFYyWywidmNvdiJdKSw0KQ0KYnBpY2NfaGcgPC0gcm91bmQoKFYyWywidmNvdiJdW3doaWNoKFYyWywiZ3JwIl0gPT0gIkNsdXN0ZXIiKV0pL3N1bShWMlssInZjb3YiXSksNCkNCmNhY19oZyA8LSByb3VuZChicGljY19oZy93cGljY19oZyw0KQ0KDQojIEVzdGltYXRlIHdpdGhpbi1wZXJpb2QgSUNDIGFuZCBkZWNheSByYXRlIGZvciBleHBvbmVudGlhbCBkZWNheSBtb2RlbA0Kd3BpY2NfYXIgPC0gcm91bmQoVjMkY29uZCRDbHVzdGVyWzEsMV0vc3VtKFYzJGNvbmQkQ2x1c3RlclsxLDFdLChhdHRyKFYzJGNvbmQsInNjIikpXjIpLDQpDQphcjEgPC0gcm91bmQoYXR0cihWMyRjb25kJENsdXN0ZXIsImNvcnJlbGF0aW9uIilbMSwyXSw0KQ0KYGBgDQoNCg0KIyMgUGFydCAyOiBTQVMgY29kZQ0KIyMjIE1vZGVsIGZpdHRpbmcNCmBgYHtzYXMsIGV2YWw9IEZ9DQoqKkxvYWQgeW91ciBkYXRhIHByb3Blcmx5IGluIFNBUzsNCioqRXhjaGFuZ2VhYmxlIG1vZGVsOw0KUFJPQyBNSVhFRCBEQVRBPWRhdGEgOw0KQ0xBU1MgQ2x1c3RlciBQZXJpb2QgVHJlYXRtZW50OyAgIA0KTU9ERUwgT3V0Y29tZSA9ICBQZXJpb2QgVHJlYXRtZW50LyBTT0xVVElPTjsNClJBTkRPTSBDbHVzdGVyOw0Kb2RzIG91dHB1dCBjb3ZwYXJtcz1ISGNvdnBhcm1zIGZpdHN0YXRpc3RpY3M9SEhmaXRzdGF0aXN0aWNzIHNvbHV0aW9uZj1ISHNvbHV0aW9uZjsNClJVTjsNCg0KKipOZXN0ZWQgZXhjaGFuZ2VhYmxlOw0KUFJPQyBNSVhFRCBEQVRBPWRhdGE7DQpDTEFTUyBDbHVzdGVyIFBlcmlvZCBUcmVhdG1lbnQ7ICANCk1PREVMIE91dGNvbWUgPSAgUGVyaW9kIFRyZWF0bWVudC8gU09MVVRJT047DQpSQU5ET00gQ2x1c3RlciBQZXJpb2QqQ2x1c3RlciA7DQpvZHMgb3V0cHV0IGNvdnBhcm1zPU5FY292cGFybXMgZml0c3RhdGlzdGljcz1ORWZpdHN0YXRpc3RpY3Mgc29sdXRpb25mPU5Fc29sdXRpb25mOw0KUlVOOw0KDQoNCioqRXhwb25lbnRpYWwgZGVjYXk7DQoqKlBST0MgSFBNSVhFRCBjYW4gYmUgdXNlZCB0byBpbXByb3ZlIHRoZSBjb21wdXRhdGlvbmFsIHNwZWVkOw0KUFJPQyBIUE1JWEVEIERBVEE9ZGF0YTsNCkNMQVNTIENsdXN0ZXIgUGVyaW9kIFRyZWF0bWVudDsgIA0KTU9ERUwgT3V0Y29tZSA9ICBQZXJpb2QgVHJlYXRtZW50LyBTT0xVVElPTjsNClJBTkRPTSBQZXJpb2QgLyBTVUJKRUNUPUNsdXN0ZXIgIFRZUEU9QVIoMSk7DQpvZHMgb3V0cHV0IGNvdnBhcm1zPUFSY292cGFybXMgZml0c3RhdGlzdGljcz1BUmZpdHN0YXRpc3RpY3Mgc29sdXRpb25mPUFSc29sdXRpb25mOw0KUlVOOw0KYGBgDQoNCg0KDQojIyMgSUNDIGNhbGN1bGF0aW9ucw0KYGBge3NhcywgZXZhbD0gRn0NCioqRXN0aW1hdGUgd2l0aGluLXBlcmlvZCBJQ0MgZm9yIGV4Y2hhbmdlYWJsZSBtb2RlbDsNClBST0MgU1FMOyANCkNSRUFURSBUQUJMRSBpY2NoaCBBUyANClNFTEVDVCBzdW0oZXN0aW1hdGUqKGNvdnBhcm09J0NsdXN0ZXInKSkgLyBzdW0oZXN0aW1hdGUpIEFTIHdwaWNjIA0KRlJPTSBISGNvdnBhcm1zOyANClFVSVQ7DQoNCg0KKipFc3RpbWF0ZSB3aXRoaW4tLCBiZXR3ZWVuLXBlcmlvZCBJQ0MgYW5kIGNhYyBmb3IgbmVzdGVkIGV4Y2hhbmdlYWJsZSBtb2RlbDsNClBST0MgU1FMOyANCkNSRUFURSBUQUJMRSBpY2NoZyBBUyANClNFTEVDVA0Kc3VtKGVzdGltYXRlKihjb3ZwYXJtIF49J1Jlc2lkdWFsJykpL3N1bShlc3RpbWF0ZSkgQVMgd3BpY2MsDQpzdW0oZXN0aW1hdGUqKGNvdnBhcm09J0NsdXN0ZXInKSkgLyBzdW0oZXN0aW1hdGUpIEFTIGJwaWNjLA0Kc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdDbHVzdGVyJykpLyBzdW0oZXN0aW1hdGUqKGNvdnBhcm0gXj0nUmVzaWR1YWwnKSkgQVMgY2FjIA0KRlJPTSBOZWNvdnBhcm1zOyANClFVSVQ7DQoNCg0KKipFc3RpbWF0ZSB3aXRoaW4tcGVyaW9kIElDQyBhbmQgZGVjYXkgcmF0ZSBmb3IgZXhwb25lbnRpYWwgZGVjYXkgbW9kZWw7DQpQUk9DIFNRTDsgDQpDUkVBVEUgVEFCTEUgaWNjYXIgQVMNClNFTEVDVCANCnN1bShlc3RpbWF0ZSooY292cGFybT0nVmFyaWFuY2UnKSkgLyBzdW0oZXN0aW1hdGUqKGNvdnBhcm0gXj0nQVIoMSknKSkgQVMgd3BpY2MsDQpzdW0oZXN0aW1hdGUqKGNvdnBhcm09J0FSKDEpJykpIGFzIGFyMSANCkZST00gQVJjb3ZwYXJtczsgDQpRVUlUOw0KYGBgDQoNCg0KIyMgUGFydCAzOiBTdGF0YSBjb2RlDQpgYGB7ciwgZXZhbD0gRn0NCiMgTG9hZCB5b3VyIGRhdGFzZXQNCmltcG9ydCBkZWxpbWl0ZWQgInJlcGxhY2UgaXQgYnkgdGhlIGxvY2FsIHBhdGggdG8geW91ciBkYXRhc2V0IiwgY2xlYXINCiMgRXhjaGFuZ2VhYmxlIG1vZGVsDQptaXhlZCBvdXRjb21lIGkucGVyaW9kIGkudHJlYXRtZW50X24gfHwgY2x1c3RlcjosIHJlbWwNCiMgQmxvY2sgZXhjaGFuZ2VhYmxlIG1vZGVsDQptaXhlZCBvdXRjb21lIGkucGVyaW9kIGkudHJlYXRtZW50X24gfHwgY2x1c3RlcjogfHwgcGVyaW9kOiwgcmVtbA0KIyBJQ0MgY2FsY3VsYXRpb25zDQouIGVzdGF0IGljYw0KYGBgDQoNCg0KDQojIENvaG9ydCBkZXNpZ24NCiMjIFBhcnQgMTogUiBjb2RlDQojIyMgTW9kZWwgZml0dGluZw0KYGBge3IsIGV2YWw9IEZ9DQojIExvYWQgcGFja2FnZXMgDQpsaWJyYXJ5KGxtZTQpDQpsaWJyYXJ5KGdsbW1UTUIpDQojIExvYWQgeW91ciBkYXRhc2V0DQpkYXRhIDwtIHdyaXRlLmNzdigicmVwbGFjZSBpdCBieSB0aGUgbG9jYWwgcGF0aCB0byB5b3VyIGRhdGFzZXQiKQ0KIyBDb252ZXJ0IFBlcmlvZCwgQ2x1c3RlciBhbmQgVHJlYXRtZW50IHRvIGZhY3RvcnMgDQojIChpZiB0aGV5IHdlcmUgY29kZWQgYXMgbnVtZXJpY2FsIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCkNCmRhdGFbLGMoIlBlcmlvZCIsICJDbHVzdGVyIiwgIklEIiwgIlRyZWF0bWVudCIpXSA8LSANCiAgbGFwcGx5KGRhdGFbLGMoIlBlcmlvZCIsICJDbHVzdGVyIiwgIklEIiwgIlRyZWF0bWVudCIpXSwgZmFjdG9yKQ0KIyBFeGNoYW5nZWFibGUgbW9kZWwNCmZpdF9ISCA8LSBsbWVyKE91dGNvbWUgfiAgUGVyaW9kICsgVHJlYXRtZW50ICsgKDF8Q2x1c3RlcikgKyAoMXxJRCksIGRhdGEgPSBkYXRhKQ0KIyBCbG9jayBleGNoYW5nZWFibGUgbW9kZWwNCmZpdF9IRyA8LSBsbWVyKE91dGNvbWUgfiAgUGVyaW9kICsgVHJlYXRtZW50ICsoMXxDbHVzdGVyKSArICgxfFBlcmlvZDpDbHVzdGVyKSArICgxfElEKSwgZGF0YSA9IGRhdGEpDQojIEV4cG9uZW50aWFsIGRlY2F5IG1vZGVsDQpmaXRfQVIgPC0gZ2xtbVRNQihPdXRjb21lIH4gUGVyaW9kICArIFRyZWF0bWVudCArIGFyMShQZXJpb2QgKyAwIHwgQ2x1c3RlcikgKyAoMXxJRCksIFJFTUw9VCwgZGF0YSA9IGRhdGEsIGZhbWlseSA9IGdhdXNzaWFuKQ0KDQpgYGANCg0KIyMjIElDQyBjYWxjdWxhdGlvbnMNCmBgYHtyLCBldmFsPSBGfQ0KIyBFeHRyYWN0IHZhcmlhbmNlIGNvbXBvbmVudCBlc3RpbWF0ZXMgICAgICAgIA0KVjEgPC0gYXMuZGF0YS5mcmFtZShWYXJDb3JyKGZpdF9ISCkpDQpWMiA8LSBhcy5kYXRhLmZyYW1lKFZhckNvcnIoZml0X0hHKSkNClYzIDwtIFZhckNvcnIoZml0X0FSKQ0KDQojIEVzdGltYXRlIHdpdGhpbi1wZXJpb2QgSUNDIGFuZCBJQUMgZm9yIGV4Y2hhbmdlYWJsZSBtb2RlbA0Kd3BpY2NfaGggPC0gcm91bmQoVjFbLCJ2Y292Il1bd2hpY2goVjFbLCJncnAiXSA9PSAiQ2x1c3RlciIpXS9zdW0oVjFbLCJ2Y292Il0pLDQpDQp3aWljY19oaCA8LSByb3VuZChzdW0oVjFbLCJ2Y292Il1bd2hpY2goVjFbLCJncnAiXSA9PSAiQ2x1c3RlciIpXSwgVjFbLCJ2Y292Il1bd2hpY2goVjFbLCJncnAiXSA9PSAiSUQiKV0pL3N1bShWMVssInZjb3YiXSksNCkNCg0KIyBFc3RpbWF0ZSB3aXRoaW4tLCBiZXR3ZWVuLXBlcmlvZCBJQ0MsIElBQyBhbmQgQ0FDIGZvciBuZXN0ZWQgZXhjaGFuZ2VhYmxlIG1vZGVsDQp3cGljY19oZyA8LSByb3VuZCgoVjJbLCJ2Y292Il1bd2hpY2goVjJbLCJncnAiXSA9PSAiQ2x1c3RlciIpXSArIFYyWywidmNvdiJdW3doaWNoKFYyWywiZ3JwIl0gPT0gIlBlcmlvZDpDbHVzdGVyIildKS9zdW0oVjJbLCJ2Y292Il0pLDQpDQpicGljY19oZyA8LSByb3VuZCgoVjJbLCJ2Y292Il1bd2hpY2goVjJbLCJncnAiXSA9PSAiQ2x1c3RlciIpXSkvc3VtKFYyWywidmNvdiJdKSw0KQ0Kd2lpY2NfaGcgPC0gcm91bmQoc3VtKFYyWywidmNvdiJdW3doaWNoKFYyWywiZ3JwIl0gPT0gIkNsdXN0ZXIiKV0sVjJbLCJ2Y292Il1bd2hpY2goVjJbLCJncnAiXSA9PSAiSUQiKV0pL3N1bShWMlssInZjb3YiXSksNCkNCmNhY19oZyA8LSByb3VuZCgoVjJbLCJ2Y292Il1bd2hpY2goVjJbLCJncnAiXSA9PSAiQ2x1c3RlciIpXSkvKFYyWywidmNvdiJdW3doaWNoKFYyWywiZ3JwIl0gPT0gIkNsdXN0ZXIiKV0gKyBWMlssInZjb3YiXVt3aGljaChWMlssImdycCJdID09ICJQZXJpb2Q6Q2x1c3RlciIpXSksNCkNCg0KIyBFc3RpbWF0ZSB3aXRoaW4tcGVyaW9kIElDQywgSUFDIGFuZCBkZWNheSByYXRlKENBQykgZm9yIGV4cG9uZW50aWFsIGRlY2F5IG1vZGVsDQp3cGljY19hciA8LSByb3VuZChWMyRjb25kJENsdXN0ZXJbMSwxXS8oKGF0dHIoVjMkY29uZCRJRCwic3RkZGV2IikpXjIrIFYzJGNvbmQkQ2x1c3RlclsxLDFdKyhhdHRyKFYzJGNvbmQsInNjIikpXjIpLDQpDQphcjEgPC0gcm91bmQoYXR0cihWMyRjb25kJENsdXN0ZXIsImNvcnJlbGF0aW9uIilbMSwyXSw0KQ0Kd2lpY2NfYXIgPC0gcm91bmQoKChhdHRyKFYzJGNvbmQkSUQsInN0ZGRldiIpKV4yICsgVjMkY29uZCRDbHVzdGVyWzEsMV0pLygoYXR0cihWMyRjb25kJElELCJzdGRkZXYiKSleMisgVjMkY29uZCRDbHVzdGVyWzEsMV0rKGF0dHIoVjMkY29uZCwic2MiKSleMiksMykNCiAgICAgICAgICANCmBgYA0KDQoNCiMjIFBhcnQgMjogU0FTIGNvZGUNCiMjIyBNb2RlbCBmaXR0aW5nDQpgYGB7c2FzLCBldmFsPSBGfQ0KKipMb2FkIHlvdXIgZGF0YSBwcm9wZXJseSBpbiBTQVM7DQoqKkV4Y2hhbmdlYWJsZSBtb2RlbDsNCioqSEggbW9kZWw7DQpQUk9DIE1JWEVEIERBVEE9ZGF0YTsNCkNMQVNTIENsdXN0ZXIgUGVyaW9kIElEIFRyZWF0bWVudDsgICANCk1PREVMIE91dGNvbWUgPSAgUGVyaW9kIFRyZWF0bWVudC8gU09MVVRJT047DQpSQU5ET00gaW50L1NVQkpFQ1Q9Q2x1c3RlcjsNClJBTkRPTSBpbnQvU1VCSkVDVD1JRDsNCm9kcyBvdXRwdXQgY292cGFybXM9SEhjb3ZwYXJtcyBmaXRzdGF0aXN0aWNzPUhIZml0c3RhdGlzdGljcyBzb2x1dGlvbmY9SEhzb2x1dGlvbmY7DQpSVU47DQoNCioqSG9vcGVyIGFuZCBHaXJsaW5nOw0KUFJPQyBNSVhFRCBEQVRBPWRhdGE7DQpDTEFTUyBDbHVzdGVyIFBlcmlvZCBJRCBUcmVhdG1lbnQ7ICANCk1PREVMIE91dGNvbWUgPSAgUGVyaW9kIFRyZWF0bWVudC8gU09MVVRJT047DQpSQU5ET00gQ2x1c3RlciBQZXJpb2QqQ2x1c3RlcjsNClJBTkRPTSBpbnQvU1VCSkVDVD1JRDsNCm9kcyBvdXRwdXQgY292cGFybXM9TkVjb3ZwYXJtcyBmaXRzdGF0aXN0aWNzPU5FZml0c3RhdGlzdGljcyBzb2x1dGlvbmY9TkVzb2x1dGlvbmY7DQpSVU47DQoNCg0KKiogRXhwb25lbnRpYWwgZGVjYXk7DQoqKiBQUk9DIEhQTUlYRUQgY2FuIGJlIHVzZWQgdG8gaW1wcm92ZSB0aGUgY29tcHV0YXRpb25hbCBzcGVlZDsNClBST0MgTUlYRUQgREFUQT1kYXRhOw0KQ0xBU1MgQ2x1c3RlciBQZXJpb2QgSUQgVHJlYXRtZW50OyAgDQpNT0RFTCBPdXRjb21lID0gIFBlcmlvZCBUcmVhdG1lbnQvIFNPTFVUSU9OOw0KUkFORE9NIFBlcmlvZCAvIFNVQkpFQ1Q9Q2x1c3RlciAgVFlQRT1BUigxKTsNClJBTkRPTSBpbnQvU1VCSkVDVD1JRDsNClJVTjsNCmBgYA0KDQoNCg0KIyMjIElDQyBjYWxjdWxhdGlvbnMNCmBgYHtzYXMsIGV2YWw9IEZ9DQoqKklDQyBjYWxjdWxhdGlvbnMgYmFzZWQgb24gdGhlIGZpdHRlZCBtb2RlbHM7DQoqKkVzdGltYXRlIHdpdGhpbi1wZXJpb2QgSUNDIGZvciBleGNoYW5nZWFibGUgbW9kZWw7DQpQUk9DIFNRTDsgDQpDUkVBVEUgVEFCTEUgaWNjaGggQVMgDQpTRUxFQ1Qgc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdDbHVzdGVyJykpIC8gc3VtKGVzdGltYXRlKSBBUyB3cGljYyANCkZST00gSEhjb3ZwYXJtczsgDQpRVUlUOw0KDQoNCioqRXN0aW1hdGUgd2l0aGluLSwgYmV0d2Vlbi1wZXJpb2QgSUNDIGFuZCBjYWMgZm9yIG5lc3RlZCBleGNoYW5nZWFibGUgbW9kZWw7DQpQUk9DIFNRTDsgDQpDUkVBVEUgVEFCTEUgaWNjaGcgQVMgDQpTRUxFQ1QNCnN1bShlc3RpbWF0ZSooY292cGFybSBePSdSZXNpZHVhbCcpKS9zdW0oZXN0aW1hdGUpIEFTIHdwaWNjLA0Kc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdDbHVzdGVyJykpIC8gc3VtKGVzdGltYXRlKSBBUyBicGljYywNCnN1bShlc3RpbWF0ZSooY292cGFybT0nQ2x1c3RlcicpKS8gc3VtKGVzdGltYXRlKihjb3ZwYXJtIF49J1Jlc2lkdWFsJykpIEFTIGNhYyANCkZST00gTmVjb3ZwYXJtczsgDQpRVUlUOw0KDQoNCioqRXN0aW1hdGUgd2l0aGluLXBlcmlvZCBJQ0MgYW5kIGRlY2F5IHJhdGUgZm9yIGV4cG9uZW50aWFsIGRlY2F5IG1vZGVsOw0KUFJPQyBTUUw7IA0KQ1JFQVRFIFRBQkxFIGljY2FyIEFTDQpTRUxFQ1QgDQpzdW0oZXN0aW1hdGUqKGNvdnBhcm09J1ZhcmlhbmNlJykpIC8gc3VtKGVzdGltYXRlKihjb3ZwYXJtIF49J0FSKDEpJykpIEFTIHdwaWNjLA0Kc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdBUigxKScpKSBhcyBhcjEgDQpGUk9NIEFSY292cGFybXM7IA0KUVVJVDsNCg0KDQoqKkVzdGltYXRlIHdpdGhpbi1wZXJpb2QgSUNDIGZvciBleGNoYW5nZWFibGUgbW9kZWw7DQpQUk9DIFNRTDsgDQpDUkVBVEUgVEFCTEUgaWNjaGggQVMgDQpTRUxFQ1QgDQpzdW0oZXN0aW1hdGUqKHN1YmplY3Q9J0NsdXN0ZXInKSkgLyBzdW0oZXN0aW1hdGUpIEFTIHdwaWNjLCANCnN1bShlc3RpbWF0ZSooc3ViamVjdD0nQ2x1c3RlcicpICsgZXN0aW1hdGUqKHN1YmplY3Q9J0lEJykpIC8gc3VtKGVzdGltYXRlKSBBUyB3aWljYyANCkZST00gSEhjb3ZwYXJtczsgDQpRVUlUOw0KDQoNCioqRXN0aW1hdGUgd2l0aGluLSwgYmV0d2Vlbi1wZXJpb2QgSUNDIGFuZCBjYWMgZm9yIG5lc3RlZCBleGNoYW5nZWFibGUgbW9kZWw7DQpQUk9DIFNRTDsgDQpDUkVBVEUgVEFCTEUgaWNjaGcgQVMgDQpTRUxFQ1QNCnN1bShlc3RpbWF0ZSooY292cGFybT0nQ2x1c3RlcicpICsgZXN0aW1hdGUqKGNvdnBhcm09J0NsdXN0ZXIqUGVyaW9kJykpL3N1bShlc3RpbWF0ZSkgQVMgd3BpY2MsDQpzdW0oZXN0aW1hdGUqKGNvdnBhcm09J0NsdXN0ZXInKSkgLyBzdW0oZXN0aW1hdGUpIEFTIGJwaWNjLA0Kc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdDbHVzdGVyJykgKyBlc3RpbWF0ZSooY292cGFybT0nSW50ZXJjZXB0JykpIC8gc3VtKGVzdGltYXRlKSBBUyB3aWljYywNCnN1bShlc3RpbWF0ZSooY292cGFybT0nQ2x1c3RlcicpKS8gc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdDbHVzdGVyJykgKyBlc3RpbWF0ZSooY292cGFybT0nQ2x1c3RlcipQZXJpb2QnKSkgQVMgY2FjIA0KRlJPTSBOZWNvdnBhcm1zOyANClFVSVQ7DQoNCg0KKipFc3RpbWF0ZSB3aXRoaW4tcGVyaW9kIElDQyBhbmQgZGVjYXkgcmF0ZSBmb3IgZXhwb25lbnRpYWwgZGVjYXkgbW9kZWw7DQpQUk9DIFNRTDsgDQpDUkVBVEUgVEFCTEUgaWNjYXIgQVMNClNFTEVDVCANCnN1bShlc3RpbWF0ZSooY292cGFybT0nVmFyaWFuY2UnKSkgLyBzdW0oZXN0aW1hdGUqKGNvdnBhcm0gXj0nQVIoMSknKSkgQVMgd3BpY2MsDQpzdW0oZXN0aW1hdGUqKGNvdnBhcm09J0ludGVyY2VwdCcpICsgZXN0aW1hdGUqKGNvdnBhcm09J1ZhcmlhbmNlJykpIC8gc3VtKGVzdGltYXRlKihjb3ZwYXJtIF49J0FSKDEpJykpIEFTIHdpaWNjLA0Kc3VtKGVzdGltYXRlKihjb3ZwYXJtPSdBUigxKScpKSBhcyBhcjEgDQpGUk9NIEFSY292cGFybXM7IA0KUVVJVDsNCmBgYA0KDQoNCiMjIFBhcnQgMzogU3RhdGEgY29kZQ0KYGBge3IsIGV2YWw9IEZ9DQojIExvYWQgeW91ciBkYXRhc2V0DQppbXBvcnQgZGVsaW1pdGVkICJyZXBsYWNlIGl0IGJ5IHRoZSBsb2NhbCBwYXRoIHRvIHlvdXIgZGF0YXNldCIsIGNsZWFyDQojIEV4Y2hhbmdlYWJsZSBtb2RlbA0KbWl4ZWQgb3V0Y29tZSBpLnBlcmlvZF9uIGkudHJlYXRtZW50IHx8IGNsdXN0ZXI6IHx8IGlkOiwgcmVtbA0KIyBCbG9jayBleGNoYW5nZWFibGUgbW9kZWwNCm1peGVkIG91dGNvbWUgaS5wZXJpb2RfbiB0cmVhdG1lbnQgfHwgY2x1c3RlcjogfHwgY2x1c3RlcjogUi5wZXJpb2RfbiB8fCBpZDoNCiMgSUNDIGNhbGN1bGF0aW9ucw0KLiBlc3RhdCBpY2MNCmBgYA0KDQoNCiMgQ29kZSB1c2VkIHRvIGNyZWF0ZSBpbmRpdmlkdWFsLWxldmVsIGRhdGEgd2l0aCBkZW5vbWluYXRvciBhbmQgbnVtZXJhdG9yIGluZm9ybWF0aW9uDQojIyBSIGNvZGUNCmBgYHtyLCBldmFsPSBGfQ0KIyBGaXJzdCBwcm9wZXJseSBsb2FkIHRoZSBkYXRhc2V0IGZvbGxvd2luZyBvdXIgdGVtcGxhdGUNCiMgQ3JlYXRlIGEgY29sdW1uIGZvciBudW1iZXIgb2Ygbm9uLXN1Y2Nlc3NlcyANCmRhdGEkbm9uX3N1YyA8LSBkYXRhJERlbm9taW5hdG9yLWRhdGEkTnVtZXJhdG9yDQojIENyZWF0ZSBjbHVzdGVyIElEIGZvciBlYWNoIGluZGl2aWR1YWwNCkNsdXN0ZXIgPC0gcmVwKGRhdGEkQ2x1c3RlciwgZGF0YSREZW5vbWluYXRvcikNCiMgQ3JlYXRlIHBlcmlvZCBJRCBmb3IgZWFjaCBpbmRpdmlkdWFsDQpQZXJpb2QgPC0gcmVwKGRhdGEkUGVyaW9kLCBkYXRhJERlbm9taW5hdG9yKQ0KIyBDcmVhdGUgaW5kaXZpZHVhbC1sZXZlbCBiaW5hcnkgb3V0Y29tZQ0KT3V0Y29tZSA8LSBjKCkNCmZvcihpIGluIDE6bnJvdyhkYXRhKSl7DQogICAgdGVtcCA8LSBjKHJlcCgxLCBkYXRhJE51bWVyYXRvcltpXSkscmVwKDAsIGRhdGEkbm9uX3N1Y1tpXSkpDQogICAgT3V0Y29tZSA8LSBjKE91dGNvbWUsIHRlbXApDQogIH0NCiMgRm9ybSBhbiBhbmFseXRpYyBkYXRhc2V0DQpkYXRhX2FuYWx5dGljIDwtIGRhdGEuZnJhbWUoT3V0Y29tZSA9IE91dGNvbWUsIENsdXN0ZXIgPSBDbHVzdGVyLCBQZXJpb2QgPSBQZXJpb2QpDQpgYGANCg0KIyMgU0FTIGNvZGUNCmBgYHtTQVMsIGV2YWw9IEZ9DQpEQVRBIGRhdGE7DQpzZXQgZGF0YS5hbmFseXRpYzsNCmRvIGk9MSB0byBOdW1lcmF0b3I7IA0KeT0xOyANCm91dHB1dDsNCmVuZDsNCmRvIGk9IE51bWVyYXRvcisxIHRvIERlbm9taW5hdG9yOyANCnk9MDsgDQpvdXRwdXQ7DQplbmQ7DQpuPTE7IC8qZm9yIGFsbCwgdXNlZnVsIGZvciBjb3VudGluZyAjIHByb2NlZHVyZXMgcGVyIG1vbnRoIHBlciBjbHVzdGVyIGxhdGVyKi8NCnJ1bjsNCmBgYA0KDQojIyBTdGF0YSBjb2RlDQpgYGB7U3RhdGEsIGV2YWw9IEZ9DQoqaW1wb3J0ZWQgZGF0YXNldCBjb250YWlucyB2YXJpYWJsZXM6DQoqY2x1c3RlcjogY2x1c3RlciBJRA0KKmRlbm9taW5hdG9yOiBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggY2x1c3Rlcg0KKmV2ZW50czogbnVtYmVyIG9mIGV2ZW50cyBpbiBlYWNoIGNsdXN0ZXINCipUaGlzIGNvZGUgY3JlYXRlcyBpbmRpdmlkdWFsIGxldmVsIGRhdGEgZm9yIHRoZSBldmVudHMNCg0KY2xlYXINCg0KaW1wb3J0IGV4Y2VsICJQYXRoIHRvIHRoZSBvcmlnaW5hbCBkYXRhc2V0Iiwgc2hlZXQoIlNoZWV0MSIpIGZpcnN0cm93DQoNCmdlbiBub25lX2V2ZW50cz1kZW5vbWluYXRvci1ldmVudHMNCg0KZXhwYW5kY2wgZXZlbnRzLCBjbHVzdGVyKCBjbHVzdGVyKSBnZW5lcmF0ZShvdXRjb21lKQ0KDQpyZXBsYWNlIG91dGNvbWU9MQ0KDQp0ZW1wZmlsZSBpcGRfZXZlbnRzDQoNCnNhdmUgYGlwZF9ldmVudHMnDQoNCiANCg0KKlRoaXMgY29kZSBjcmVhdGVzIGluZGl2aWR1YWwgbGV2ZWwgZGF0YSBmb3IgdGhlIG5vIGV2ZW50cw0KDQpUaGlzIGNvZGUgY3JlYXRlcyBpbmRpdmlkdWFsIGxldmVsIGRhdGEgZm9yIHRoZSBldmVudHMNCg0KY2xlYXINCg0KaW1wb3J0IGV4Y2VsICIicGF0aCB0byBkYXRhc2V0XGRhdGEueGxzeCIsIHNoZWV0KCJTaGVldDEiKSBmaXJzdHJvdw0KDQpnZW4gbm9uZV9ldmVudHM9ZGVub21pbmF0b3ItZXZlbnRzDQoNCmV4cGFuZGNsIGV2ZW50cywgY2x1c3RlciggY2x1c3RlcikgZ2VuZXJhdGUob3V0Y29tZSkNCg0KcmVwbGFjZSBvdXRjb21lPTENCg0KdGVtcGZpbGUgaXBkX2V2ZW50cw0KDQpzYXZlIGBpcGRfZXZlbnRzJw0KDQogDQoNCipUaGlzIGNvZGUgY3JlYXRlcyBpbmRpdmlkdWFsIGxldmVsIGRhdGEgZm9yIHRoZSBubyBldmVudHMNCg0KY2xlYXINCg0KaW1wb3J0IGV4Y2VsICJwYXRoIHRvIGRhdGFzZXRcZGF0YS54bHN4Iiwgc2hlZXQoIlNoZWV0MSIpIGZpcnN0cm93DQoNCmdlbiBub25lX2V2ZW50cz1kZW5vbWluYXRvci1ldmVudHMNCg0KZXhwYW5kY2wgbm9uZV9ldmVudHMsIGNsdXN0ZXIoIGNsdXN0ZXIpIGdlbmVyYXRlKG91dGNvbWUpDQoNCnJlcGxhY2Ugb3V0Y29tZT0wDQoNCnRlbXBmaWxlIGlwZF9ub2V2ZW50cw0KDQpzYXZlIGBpcGRfbm9ldmVudHMnDQoNCmFwcGVuZCB1c2luZyAiYGlwZF9ldmVudHMnIg0KDQpgYGANCg0K