Reading datasets and grouping observations
library(data.table)
library(plyr) #join
library(ggplot2) #fortify, ggplot
library(scales) #scale_fill_distiller
library(sp) #used by rgdal
library(rgdal) #readOGR
library(ggmap) #theme_nothing
library(rgeos) #gCentroids
library(forecast) #auto.arima, forecast
Read .csv containing monthly TESS Ontario works cases for January 2004 to November 2014. The size of the csv file is 2.68 GB.
CSVFILE <- "opendata_tess_ow.csv"
tess_dt <- fread(CSVFILE) #data.table
|--------------------------------------------------|
|==================================================|
Structure of datatable:
str(tess_dt)
Classes ‘data.table’ and 'data.frame': 9536341 obs. of 21 variables:
$ YEAR_NUM : int 2004 2004 2004 2004 2004 2004 2004 2004 2004 2004 ...
$ MNTH : int 20040101 20040101 20040101 20040101 20040101 20040101 20040101 20040101 20040101 20040101 ...
$ PROGRAM_NM : chr "Ontario Works" "Ontario Works" "Ontario Works" "Ontario Works" ...
$ OFFICE : chr "Application Centre" "Application Centre" "Application Centre" "Application Centre" ...
$ FAMILY_TYP_NM : chr "Families" "Families" "Families" "Families" ...
$ FAMILY_SIZE : chr "2" "2" "2" "2" ...
$ AGE : chr "18 to 29 yrs old" "18 to 29 yrs old" "18 to 29 yrs old" "30 to 39 yrs old" ...
$ EDUCATION_LEVEL : chr "High School Incomplete" "High School Incomplete" "Post Secondary" "High School Complete" ...
$ EARNINGS : chr "" "" "" "Earnings from employment" ...
$ IMMIGRATION_STATUS : chr "Permanent Resident" "Permanent Resident" "Canadian Citizen" "Canadian Citizen" ...
$ TIMES_ON_ASSISTANCE : chr "1" "4+" "3" "1" ...
$ MONTHS_ON_ASSISTANCE : chr "1 to 6 months" "1 to 6 months" "1 to 6 months" "1 to 6 months" ...
$ MONTHS_OFF_ASSISTANCE : chr "1 to 6 months" "1 to 6 months" "1 to 6 months" "7 to 24 months" ...
$ GENDER : chr "F" "F" "F" "F" ...
$ SHELTER_COSTS : chr "$600 to $999" "$400 to $599" "$200 to $399" "$400 to $599" ...
$ YOUNGEST_DEP_AGE_RANGE: chr "less than 5 yrs old" "less than 5 yrs old" "less than 5 yrs old" "5 to 10 yrs old" ...
$ WARD_SCODE : int 2 2 8 8 6 2 1 2 8 6 ...
$ CENSUS_NEIGH_SCODE : int 4 4 24 27 19 4 2 4 27 19 ...
$ NEW_CASES : int 0 0 0 0 0 0 0 0 0 0 ...
$ EXITS : int 1 0 0 0 1 0 0 0 0 0 ...
$ CASES : int 1 1 1 1 1 1 1 1 1 1 ...
- attr(*, ".internal.selfref")=<externalptr>
Select columns from tess_dt
ow_cases <- tess_dt[,.(YEAR_NUM, MNTH, WARD_SCODE, CENSUS_NEIGH_SCODE, CASES)]
Drop rows where at least one of CENSUS_NEIGH_SCODE and WARD_SCODE is missing
ow_cases_dropna <- na.omit(ow_cases, cols=c("CENSUS_NEIGH_SCODE", "WARD_SCODE"))
Calculate mean number of cases in 2010 by month and ward
ow_wards_2010 <- ow_cases_dropna[YEAR_NUM==2010,.(TOTAL_CASES=sum(CASES)),by=.(MNTH, WARD_SCODE)]
ow_wards_2010_months <- ow_wards_2010[,.(MONTHLY_CASES=sum(TOTAL_CASES)),by=.(MNTH, WARD_SCODE)]
ow_wards_2010_monthly <- ow_wards_2010_months[,.(MEAN_CASES_PER_MONTH=mean(MONTHLY_CASES)),by=.(WARD_SCODE)]
Add “id” column
ow_wards_2010_monthly$id <- ow_wards_2010_monthly$WARD_SCODE
Calculate mean number of cases in 2013 by month and ward
ow_wards_2013 <- ow_cases_dropna[YEAR_NUM==2013,.(TOTAL_CASES=sum(CASES)),by=.(MNTH, WARD_SCODE)]
ow_wards_2013_months <- ow_wards_2013[,.(MONTHLY_CASES=sum(TOTAL_CASES)),by=.(MNTH, WARD_SCODE)]
ow_wards_2013_monthly <- ow_wards_2013_months[,.(MEAN_CASES_PER_MONTH=mean(MONTHLY_CASES)),by=.(WARD_SCODE)]
Add “id” column
ow_wards_2013_monthly$id <- ow_wards_2013_monthly$WARD_SCODE
Plotting data by wards
City Wards
https://www.toronto.ca/city-government/data-research-maps/open-data/open-data-catalogue/#29b6fadf-0bd6-2af9-4a8c-8c41da285ad7
Owner: City Clerk’s Office
Currency: August 2018
44 Ward Model - May 2010 (WGS84 - Latitude / Longitude)
http://opendata.toronto.ca/gcc/wards_may2010_wgs84.zip
Shapefile: icitw_wgs84
readme.txt:
Wards: There are a total of 44 electoral wards in the City of Toronto
* GEO_ID = unique geographic identifier
* NAME = Name of the Ward with corresponding ward number
* SCODE_NAME = Ward Number
* LCODE_NAME = Ward Number and the community council area it is in (N,S, E or W)
* TYPE_DESC = Ward
* TYPE_CODE = City Ward
We shall input wards shapefile, create centroids of wards, and process shapefile as dataframe. First we input the shapefile
wards.sh <- readOGR("C:/Users/14165/Desktop/ArcGIS/SHAPEFILES/May2010_WGS84", "icitw_wgs84")
OGR data source with driver: ESRI Shapefile
Source: "C:\Users\14165\Desktop\ArcGIS\SHAPEFILES\May2010_WGS84", layer: "icitw_wgs84"
with 44 features
It has 10 fields
Add “id” column
wards.sh@data$id <- as.integer(wards.sh@data$SCODE_NAME)
Make centroids of each ward, for placing labels when plotting
wards.sh.centroids <- as.data.frame(gCentroid(wards.sh, byid = TRUE))
Add “id” column
wards.sh.centroids$id <- wards.sh@data$id
Shapefile processing
wards.sh.points = fortify(wards.sh, region="id")
wards.sh.df = join(wards.sh.points, wards.sh@data, by="id")
Merge ward shapefile and ow_wards_2010_monthly dataframe
wards.sh.ow_2010 <- merge(wards.sh.df, ow_wards_2010_monthly, by = "id")
Make graphics object for wards.sh.ow_2010
p.wards_2010 <- ggplot() +
geom_polygon(data = wards.sh.ow_2010,
aes(x = long, y = lat, group = group, fill = MEAN_CASES_PER_MONTH),
color = "black", size = 0.25) +
coord_map() +
scale_fill_distiller(name="Cases", palette = "YlOrBr", trans = "reverse", breaks = pretty_breaks(n = 8)) +
theme_nothing(legend = TRUE) +
labs(title="Mean monthly number of Ontario Works cases by ward in Toronto in 2010") +
geom_text(aes(x=x, y=y, group=NULL, label=id), data = wards.sh.centroids, size = 2)
Plot graphics object
p.wards_2010 + guides(fill = guide_legend(reverse = TRUE)) #ggplot2
Calculate mean number of cases in 2013 by month and ward
ow_wards_2013 <- ow_cases_dropna[YEAR_NUM==2013,.(TOTAL_CASES=sum(CASES)),by=.(MNTH, WARD_SCODE)]
ow_wards_2013_months <- ow_wards_2013[,.(MONTHLY_CASES=sum(TOTAL_CASES)),by=.(MNTH, WARD_SCODE)]
ow_wards_2013_monthly <- ow_wards_2013_months[,.(MEAN_CASES_PER_MONTH=mean(MONTHLY_CASES)),by=.(WARD_SCODE)]
Add “id” column
ow_wards_2013_monthly$id <- ow_wards_2013_monthly$WARD_SCODE
Merge ward shapefile and ow_wards_2013_monthly dataframe
wards.sh.ow_2013 <- merge(wards.sh.df, ow_wards_2013_monthly, by = "id")
Make graphics object
p.wards_2013 <- ggplot() +
geom_polygon(data = wards.sh.ow_2013,
aes(x = long, y = lat, group = group, fill = MEAN_CASES_PER_MONTH),
color = "black", size = 0.25) +
coord_map() +
scale_fill_distiller(name="Cases", palette = "YlOrBr", trans = "reverse", breaks = pretty_breaks(n = 8)) +
theme_nothing(legend = TRUE) +
labs(title="Mean monthly number of Ontario Works cases by ward in Toronto in 2013") +
geom_text(aes(x=x,y=y, group=NULL, label=id), data = wards.sh.centroids, size=2)
Plot graphics object
p.wards_2013 + guides(fill = guide_legend(reverse = TRUE)) #ggplot2
Calculate mean number of cases in 2013 by month and neighbourhood
ow_nbds_2013 <- ow_cases_dropna[YEAR_NUM==2013,.(TOTAL_CASES=sum(CASES)),by=.(MNTH, CENSUS_NEIGH_SCODE)]
ow_nbds_2013_months <- ow_nbds_2013[,.(MONTHLY_CASES=sum(TOTAL_CASES)),by=.(MNTH, CENSUS_NEIGH_SCODE)]
ow_nbds_2013_monthly <- ow_nbds_2013_months[,.(MEAN_CASES_PER_MONTH=mean(MONTHLY_CASES)),by=.(CENSUS_NEIGH_SCODE)]
Add “id” column
ow_nbds_2013_monthly$id <- ow_nbds_2013_monthly$CENSUS_NEIGH_SCODE
Doing finer aggregation: mean monthly cases who are single (without dependents)
ow_singles <- tess_dt[FAMILY_TYP_NM=="Singles",.(YEAR_NUM, MNTH, WARD_SCODE, CENSUS_NEIGH_SCODE, CASES)]
ow_singles_dropna <- na.omit(ow_singles, cols=c("CENSUS_NEIGH_SCODE", "WARD_SCODE"))
ow_singles_wards_2013 <- ow_singles_dropna[YEAR_NUM==2013,.(TOTAL_CASES=sum(CASES)),by=.(MNTH, WARD_SCODE)]
ow_singles_wards_2013_months <- ow_singles_wards_2013[,.(MONTHLY_CASES=sum(TOTAL_CASES)),by=.(MNTH, WARD_SCODE)]
ow_singles_wards_2013_monthly <- ow_singles_wards_2013_months[,.(MEAN_CASES_PER_MONTH=mean(MONTHLY_CASES)),by=.(WARD_SCODE)]
Add “id” column
ow_singles_wards_2013_monthly$id <- ow_singles_wards_2013_monthly$WARD_SCODE
Merge ward shapefile and Ontario Works dataframe
wards.sh.ow_singles_2013 <- merge(wards.sh.df, ow_singles_wards_2013_monthly, by = "id")
We shall plot wards.sh.ow_singles_2013. Make graphics object
p.wards_singles_2013 <- ggplot() +
geom_polygon(data = wards.sh.ow_singles_2013,
aes(x = long, y = lat, group = group, fill = MEAN_CASES_PER_MONTH),
color = "black", size = 0.25) +
coord_map() +
scale_fill_distiller(name="Cases", palette = "PuRd", trans = "reverse", breaks = pretty_breaks(n = 8)) +
theme_nothing(legend = TRUE) +
labs(title="Mean monthly number of Ontario Works cases (singles) by ward in Toronto in 2013") +
geom_text(aes(x=x,y=y, group=NULL, label=id), data = wards.sh.centroids, size = 2)
Plot graphics object
p.wards_singles_2013 + guides(fill = guide_legend(reverse = TRUE))
Plotting data by neighbourhoods
Neighbourhoods shapefile
https://www.toronto.ca/city-government/data-research-maps/open-data/open-data-catalogue/#a45bd45a-ede8-730e-1abc-93105b2c439f
http://opendata.toronto.ca/gcc/neighbourhoods_planning_areas_wgs84.zip
Owner: Social Development, Finance & Administration
Currency: June 2014
Neighbourhoods (WGS84)
Shapefile: NEIGHBORHOODS_WGS84
NEIGHBORHOODS_WGS84_readme.txt:
NEIGHBORHOODS_WGS84_readme
* Column name (Description)
* AREA_S_CD = AREA_SHORT_CODE
* AREA_NAME = AREA_NAME
Input shapefile
nbds.sh <- readOGR("C:/Users/14165/Desktop/ArcGIS/SHAPEFILES/neighbourhoods_planning_areas_wgs84", "NEIGHBORHOODS_WGS84")
OGR data source with driver: ESRI Shapefile
Source: "C:\Users\14165\Desktop\ArcGIS\SHAPEFILES\neighbourhoods_planning_areas_wgs84", layer: "NEIGHBORHOODS_WGS84"
with 140 features
It has 2 fields
Add “id” column
nbds.sh@data$id <- as.integer(nbds.sh@data$AREA_S_CD)
Make centroids of each neighbourhood, for placing labels when plotting
nbds.sh.centroids <- as.data.frame(gCentroid(nbds.sh, byid = TRUE))
Add “id” column
nbds.sh.centroids$id <- nbds.sh@data$id
Shapefile processing
nbds.sh.points = fortify(nbds.sh, region = "id")
nbds.sh.df = join(nbds.sh.points, nbds.sh@data, by = "id")
Merge neighbourhood shapefile and Ontario Works dataframe
nbds.sh.ow_2013 <- merge(nbds.sh.df, ow_nbds_2013_monthly, by = "id")
Make graphics object
p.nbds_2013 <- ggplot() +
geom_polygon(data = nbds.sh.ow_2013,
aes(x = long, y = lat, group = group, fill = MEAN_CASES_PER_MONTH),
color = "black", size = 0.2) +
coord_map() +
scale_fill_distiller(name="Cases", palette = "YlOrBr", trans = "reverse", breaks = pretty_breaks(n = 8)) +
theme_nothing(legend = TRUE) +
labs(title="Mean monthly number of Ontario Works cases by neighbourhood in Toronto in 2013") +
geom_text(aes(x=x,y=y, group=NULL, label=id), data = nbds.sh.centroids, size = 2)
Plot graphics object
p.nbds_2013+guides(fill = guide_legend(reverse = TRUE))
Time series
Avril Coghlan, Using R for Time Series Analysis
Vincent Zoonekynd, Time series
TESS Ontario Works data manipulation
Group by month and create time series:
ow_cases_months <- tess_dt[,.(MNTH, CASES, NEW_CASES, EXITS)]
ow_cases_monthly <- ow_cases_months[,.(CASES=sum(CASES), NEW_CASES=sum(NEW_CASES), EXITS=sum(EXITS)),by=.(MNTH)]
ow_cases_monthly$MNTH <- NULL
head(ow_cases_monthly)
ow_ts <- ts(ow_cases_monthly, start = c(2004,1), frequency = 12)
head(ow_ts)
CASES NEW_CASES EXITS
Jan 2004 70169 3597 3788
Feb 2004 70327 3407 3977
Mar 2004 70929 5042 4430
Apr 2004 70040 5044 3917
May 2004 69999 4567 4146
Jun 2004 69976 4806 4318
TESS Ontario Works plots
Plot time series
options(scipen=999) #do not use scientific notation
plot(ow_ts)
Make time series of CASES
CASES <- ts(ow_cases_monthly$CASES, start = c(2004,1), frequency = 12)
plot(CASES)
Make time series of NEW_CASES
NEW_CASES <- ts(ow_cases_monthly$NEW_CASES, start = c(2004,1), frequency = 12)
plot(NEW_CASES)
Decomposing time series into trend, seasonal, and remainder terms
Now we use stats::decompose and stats::stl to separate time series as a sum of trend, seasonal, and remainder terms.
CASES_components <- decompose(CASES)
autoplot(CASES_components)
NEW_CASES_components <- decompose(NEW_CASES)
autoplot(NEW_CASES_components)
CASES.stl <- stl(CASES, s.window = "periodic")
autoplot(CASES.stl)
NEW_CASES.stl <- stl(NEW_CASES, s.window = "periodic")
autoplot(NEW_CASES.stl)
Holt-Winters exponential smoothing
CASES.HW <- HoltWinters(CASES)
CASES.HW
Holt-Winters exponential smoothing with trend and additive seasonal component.
Call:
HoltWinters(x = CASES)
Smoothing parameters:
alpha: 0.9535295
beta : 0.1484578
gamma: 1
Coefficients:
[,1]
a 84371.18862
b -445.73297
s1 -1132.80119
s2 -1618.51347
s3 -369.32564
s4 -3.67365
s5 626.57812
s6 466.48438
s7 853.77241
s8 638.32040
s9 352.15093
s10 473.25854
s11 298.98513
s12 -793.18862
plot(CASES.HW)
CASES.HW.forecast <- forecast(CASES.HW, h = 24)
plot(CASES.HW.forecast)
ARIMA
CASES.arima <- auto.arima(CASES)
CASES.arima
Series: CASES
ARIMA(0,2,1)(0,0,2)[12]
Coefficients:
ma1 sma1 sma2
-0.8806 0.1925 0.2403
s.e. 0.0497 0.0852 0.0827
sigma^2 estimated as 825250: log likelihood=-1053.56
AIC=2115.11 AICc=2115.44 BIC=2126.52
op <- par(mfrow=c(2,1), mar=c(2,4,1,2)+.1)
acf(CASES, main="")
pacf(CASES, main="")
par(op)
CASES.forecast <- forecast(CASES, level = c(95), h = 22)
plot(CASES.forecast)
CASES.arima.forecast <- forecast(CASES.arima, level = c(95), h = 22)
plot(CASES.arima.forecast)
Ontario Monthly Social Assistance Caseloads 1969-2018
SA <- fread("historical_sa_recipients_dataset_q2_2018_19.csv")
SA$Beneficiaries <- NULL
Make time series
SA_ts <- ts(SA$Cases, start = c(1969,1), frequency = 12)
plot(SA_ts)
SA_ts_1997 <- window(SA_ts, c(1997,1))
plot(SA_ts_1997)
SA_ts_1997.components <- decompose(SA_ts_1997)
autoplot(SA_ts_1997.components)
SA_ts_1997.stl <- stl(SA_ts_1997, s.window = "periodic")
autoplot(SA_ts_1997.stl)
Maytree Social Assistance Summaries
Maytree_ON <- fread("ON.csv")
str(Maytree_ON)
Classes ‘data.table’ and 'data.frame': 25 obs. of 7 variables:
$ Year : chr "1997-98" "1998-99" "1999-00" "2000-01" ...
$ Ontario Works Cases : int 362334 310493 262439 215618 196596 195137 192096 191723 198377 199242 ...
$ Ontario Works Beneficiaries: int 796109 690608 577620 469494 419493 404067 389754 380670 386801 383068 ...
$ ODSP Cases : int 185479 189392 189536 191885 192048 194140 200087 205880 212058 221718 ...
$ ODSP Beneficiaries : int 261737 268159 268286 271144 270558 271740 278393 285231 292622 305202 ...
$ Total Cases : int 547813 499884 451975 407503 388644 389277 392183 397603 410435 420960 ...
$ Total Beneficiaries : int 1057846 958767 845907 740637 690051 675807 668148 665901 679423 688270 ...
- attr(*, ".internal.selfref")=<externalptr>
Maytree_OW.ts <- ts(Maytree_ON$`Ontario Works Cases`, start = 1997, frequency = 1)
Maytree_ODSP.ts <- ts(Maytree_ON$`ODSP Cases`, start = 1997, frequency = 1)
plot(Maytree_OW.ts)
plot(Maytree_ODSP.ts)
plot(Maytree_ODSP.ts+Maytree_OW.ts)
Statistics Canada Table 11-10-0239-01
T1110023901 <- fread("1110023901-noSymbol.csv")
str(T1110023901)
Classes ‘data.table’ and 'data.frame': 42 obs. of 3 variables:
$ Reference period : int 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 ...
$ Number of persons : int 1830 2182 2266 2281 2300 2500 2512 2760 2604 2636 ...
$ Number with income: int 52 81 54 93 76 91 112 126 127 111 ...
- attr(*, ".internal.selfref")=<externalptr>
T1110023901.ts <- ts(T1110023901$`Number of persons`, start = 1976, frequency = 1)
plot(T1110023901.ts, ylab = "Number of persons", main = "Persons 16 years and older in Toronto with social assistance income" )
T1110023901.ts_1997 <- window(T1110023901.ts, c(1997,1))
plot(T1110023901.ts_1997)
op <- par(mfrow=c(2,1), mar=c(2,4,1,2)+.1)
acf(T1110023901.ts_1997, main="")
pacf(T1110023901.ts_1997, main="")
par(op)
T1110023901.ts_1997.forecast <- forecast(T1110023901.ts_1997, level = c(95), h = 12)
plot(T1110023901.ts_1997.forecast)
T1110023901.ts_1997.arima <- auto.arima(T1110023901.ts_1997)
T1110023901.ts_1997.arima
Series: T1110023901.ts_1997
ARIMA(0,1,1) with drift
Coefficients:
ma1 drift
-0.6140 97.5973
s.e. 0.3226 10.4978
sigma^2 estimated as 11017: log likelihood=-120.63
AIC=247.27 AICc=248.77 BIC=250.25
T1110023901.ts_1997.arima.forecast <- forecast(T1110023901.ts_1997.arima, level = c(95), h = 5)
plot(T1110023901.ts_1997.arima.forecast)
LS0tDQp0aXRsZTogIlRvcm9udG8gRW1wbG95bWVudCBhbmQgU29jaWFsIFNlcnZpY2VzIE9udGFyaW8gV29ya3MgRGF0YXNldCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIHRvYzogeWVzDQotLS0NCg0KIyBBdXRob3IgYW5kIGRhdGUNCg0KKiBKb3JkYW4gQmVsbA0KKiBKdWx5IDEsIDIwMTkNCiogPGh0dHBzOi8vam9yZGFuYmVsbDIzNTcuZ2l0aHViLmlvL1RFU1NfT1cubmIuaHRtbD4NCg0KIyBTb3VyY2VzIGFuZCBkZXNjcmlwdGlvbnMgb2YgZGF0YXNldHMNCg0KVGhpcyBSIG5vdGVib29rIGV4cGxvcmVzIHRoZSBPbnRhcmlvIFdvcmtzIChzb2NpYWwgYXNzaXN0YW5jZSBmb3IgcGVvcGxlIHdpdGhvdXQgaW5jb21lKSBjYXNlIG51bWJlcnMgaW4gdGhlDQpkYXRhc2V0ICoqSGVhZCBvZiBIb3VzZWhvbGQqKiBmcm9tIFRvcm9udG8gRW1wbG95bWVudCBhbmQgU29jaWFsIFNlcnZpY2VzLg0KV2UgY29tcHV0ZSBtZWFuICBtb250aGx5IHJhdGVzIGZvciBhIGdpdmVuIHllYXIgKDIwMTMsIHRoZSBsYXRlc3QgeWVhciB3aXRoIGRhdGEgZm9yIGFsbCBtb250aHMpLA0KYW5kIHBsb3QgdGhpcyBkYXRhIGFzIG1hcHMgb2YgVG9yb250byBkaXZpZGVkIGludG8gd2FyZHMgKDQ0IHdhcmRzKSBhbmQgbmVpZ2hib3VyaG9vZHMgKDE0MCBuZWlnaGJvdXJob29kcykuDQoNCg0KPGh0dHBzOi8vd3d3LnRvcm9udG8uY2EvY2l0eS1nb3Zlcm5tZW50L2RhdGEtcmVzZWFyY2gtbWFwcy9vcGVuLWRhdGEvb3Blbi1kYXRhLWNhdGFsb2d1ZS9jb21tdW5pdHktc2VydmljZXMvIzE1NTgwZDcxLTMxY2EtMDEwZS0zODk1LTZiNzdiNDlkOTY2Zj4NCg0KDQo+VGhpcyBkYXRhc2V0IGNvbnRhaW5zIHRoZSBIZWFkIG9mIEhvdXNlaG9sZCB0cmVuZGluZyBkYXRhIGZyb20gMjAwNCB0byBwcmVzZW50LiBJdCBwcm92aWRlcyBpbmZvcm1hdGlvbiBvbiBob3VzZWhvbGRzIGJ5IG5laWdoYm91cmhvb2Qgd2l0aCByZWZlcmVuY2UgdG8gdGhlIE9udGFyaW8gV29ya3Mgc29jaWFsIGFzc2lzdGFuY2UgcHJvZ3JhbS4gVGhlIGluZm9ybWF0aW9uIGluIHRoaXMgZGF0YXNldCB3aWxsIGluZm9ybSB0aGUgZGVsaXZlcnkgb2Ygc2VydmljZXMgYnkgcHJvdmlkaW5nIHRyZW5kaW5nIGluZm9ybWF0aW9uIG9uIHNvY2lhbCBhc3Npc3RhbmNlLg0KPg0KPkN1cnJlbmN5OiBNYXJjaCAyMDE1DQoNCjxodHRwOi8vb3BlbmRhdGEudG9yb250by5jYS9lbXBsb3ltZW50LnNvY2lhbC9oZWFkLmhvdXNlaG9sZC9vcGVuZGF0YV90ZXNzX293LnppcD4NCg0KQ2hpbGRyZW4sIENvbW11bml0eSBhbmQgU29jaWFsIFNlcnZpY2VzLCBPbnRhcmlvLCBbU29jaWFsIEFzc2lzdGFuY2UgQ2FzZWxvYWRzXShodHRwczovL3d3dy5vbnRhcmlvLmNhL2RhdGEvc29jaWFsLWFzc2lzdGFuY2UtY2FzZWxvYWRzKQ0KDQo+IFRvdGFsIG51bWJlcnMgb2Ygc29jaWFsIGFzc2lzdGFuY2UgcmVjaXBpZW50cyBhbmQgYmVuZWZpY2lhcmllcyBlYWNoIG1vbnRoIHNpbmNlIEphbnVhcnkgMTk2OS4NCg0KPGh0dHBzOi8vZmlsZXMub250YXJpby5jYS9vcGVuZGF0YS9oaXN0b3JpY2FsX3NhX3JlY2lwaWVudHNfZGF0YXNldF9xMl8yMDE4XzE5Lnhsc3g+DQoNCk1heXRyZWUsIFtTb2NpYWwgQXNzaXN0YW5jZSBTdW1tYXJpZXNdKGh0dHBzOi8vbWF5dHJlZS5jb20vc29jaWFsLWFzc2lzdGFuY2Utc3VtbWFyaWVzL29udGFyaW8vKToNCg0KPiBUaGUgU29jaWFsIEFzc2lzdGFuY2UgU3VtbWFyaWVzIHNlcmllcyB0cmFja3MgdGhlIG51bWJlciBvZiByZWNpcGllbnRzIG9mIHNvY2lhbCBhc3Npc3RhbmNlICh3ZWxmYXJlIHBheW1lbnRzKSBpbiBlYWNoIHByb3ZpbmNlIGFuZCB0ZXJyaXRvcnkuDQoNCjxodHRwczovL21heXRyZWUuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy9PTi54bHN4Pg0KDQpbSW5jb21lIG9mIGluZGl2aWR1YWxzIGJ5IGFnZSBncm91cCwgc2V4IGFuZCBpbmNvbWUgc291cmNlLCBDYW5hZGEsIHByb3ZpbmNlcyBhbmQgc2VsZWN0ZWQgY2Vuc3VzIG1ldHJvcG9saXRhbiBhcmVhc10oaHR0cHM6Ly93d3cxNTAuc3RhdGNhbi5nYy5jYS90MS90YmwxL2VuL2N2IXJlY3JlYXRlLmFjdGlvbj9waWQ9MTExMDAyMzkwMSZzZWxlY3RlZE5vZGVJZHM9MUQxNyw0RDE0LDVEMSw1RDImY2hlY2tlZExldmVscz0xRDEsMkQxJnJlZlBlcmlvZHM9MTk3NjAxMDEsMjAxNzAxMDEmZGltZW5zaW9uTGF5b3V0cz1sYXlvdXQzLGxheW91dDMsbGF5b3V0MyxsYXlvdXQzLGxheW91dDIsbGF5b3V0MyZ2ZWN0b3JEaXNwbGF5PWZhbHNlKQ0KDQo+IFRhYmxlOiAxMS0xMC0wMjM5LTAxIChmb3JtZXJseSBDQU5TSU0gMjA2LTAwNTIpDQoNCiIxMTEwMDIzOTAxLW5vU3ltYm9sLmNzdiINCg0KIk51bWJlciBvZiBwZXJzb25zIiA9IG51bWJlciBvZiBwZXJzb25zIHdob3NlIGFnZSBpcyAkXGdlcSAxNiQuIA0KDQoiTnVtYmVyIHdpdGggaW5jb21lIiA9IG51bWJlciBvZiBwZXJzb25zIHdpdGggaW5jb21lIGZyb20gc29jaWFsIGFzc2lzdGFuY2UNCg0KDQojIFJlYWRpbmcgZGF0YXNldHMgYW5kIGdyb3VwaW5nIG9ic2VydmF0aW9ucw0KDQpgYGB7cn0NCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkocGx5cikgI2pvaW4NCmxpYnJhcnkoZ2dwbG90MikgI2ZvcnRpZnksIGdncGxvdA0KbGlicmFyeShzY2FsZXMpICNzY2FsZV9maWxsX2Rpc3RpbGxlcg0KbGlicmFyeShzcCkgI3VzZWQgYnkgcmdkYWwNCmxpYnJhcnkocmdkYWwpICNyZWFkT0dSDQpsaWJyYXJ5KGdnbWFwKSAjdGhlbWVfbm90aGluZw0KbGlicmFyeShyZ2VvcykgI2dDZW50cm9pZHMNCmxpYnJhcnkoZm9yZWNhc3QpICNhdXRvLmFyaW1hLCBmb3JlY2FzdA0KYGBgDQpSZWFkIC5jc3YgY29udGFpbmluZyBtb250aGx5IFRFU1MgT250YXJpbyB3b3JrcyBjYXNlcyBmb3IgSmFudWFyeSAyMDA0IHRvIE5vdmVtYmVyIDIwMTQuDQpUaGUgc2l6ZSBvZiB0aGUgY3N2IGZpbGUgaXMgMi42OCBHQi4NCg0KYGBge3J9DQpDU1ZGSUxFIDwtICJvcGVuZGF0YV90ZXNzX293LmNzdiINCnRlc3NfZHQgPC0gZnJlYWQoQ1NWRklMRSkgI2RhdGEudGFibGUNCmBgYA0KU3RydWN0dXJlIG9mIGRhdGF0YWJsZToNCmBgYHtyfQ0Kc3RyKHRlc3NfZHQpDQpgYGANCg0KU2VsZWN0IGNvbHVtbnMgZnJvbSB0ZXNzX2R0DQpgYGB7cn0NCm93X2Nhc2VzIDwtIHRlc3NfZHRbLC4oWUVBUl9OVU0sIE1OVEgsIFdBUkRfU0NPREUsIENFTlNVU19ORUlHSF9TQ09ERSwgQ0FTRVMpXQ0KYGBgDQoNCkRyb3Agcm93cyB3aGVyZSBhdCBsZWFzdCBvbmUgb2YgQ0VOU1VTX05FSUdIX1NDT0RFIGFuZCBXQVJEX1NDT0RFIGlzIG1pc3NpbmcNCmBgYHtyfQ0Kb3dfY2FzZXNfZHJvcG5hIDwtIG5hLm9taXQob3dfY2FzZXMsIGNvbHM9YygiQ0VOU1VTX05FSUdIX1NDT0RFIiwgIldBUkRfU0NPREUiKSkNCmBgYA0KQ2FsY3VsYXRlIG1lYW4gbnVtYmVyIG9mIGNhc2VzIGluIDIwMTAgYnkgbW9udGggYW5kIHdhcmQNCmBgYHtyfQ0Kb3dfd2FyZHNfMjAxMCA8LSBvd19jYXNlc19kcm9wbmFbWUVBUl9OVU09PTIwMTAsLihUT1RBTF9DQVNFUz1zdW0oQ0FTRVMpKSxieT0uKE1OVEgsIFdBUkRfU0NPREUpXQ0KDQpvd193YXJkc18yMDEwX21vbnRocyA8LSBvd193YXJkc18yMDEwWywuKE1PTlRITFlfQ0FTRVM9c3VtKFRPVEFMX0NBU0VTKSksYnk9LihNTlRILCBXQVJEX1NDT0RFKV0NCg0Kb3dfd2FyZHNfMjAxMF9tb250aGx5IDwtIG93X3dhcmRzXzIwMTBfbW9udGhzWywuKE1FQU5fQ0FTRVNfUEVSX01PTlRIPW1lYW4oTU9OVEhMWV9DQVNFUykpLGJ5PS4oV0FSRF9TQ09ERSldDQpgYGANCkFkZCAiaWQiIGNvbHVtbg0KYGBge3J9DQpvd193YXJkc18yMDEwX21vbnRobHkkaWQgPC0gb3dfd2FyZHNfMjAxMF9tb250aGx5JFdBUkRfU0NPREUNCmBgYA0KQ2FsY3VsYXRlIG1lYW4gbnVtYmVyIG9mIGNhc2VzIGluIDIwMTMgYnkgbW9udGggYW5kIHdhcmQNCmBgYHtyfQ0Kb3dfd2FyZHNfMjAxMyA8LSBvd19jYXNlc19kcm9wbmFbWUVBUl9OVU09PTIwMTMsLihUT1RBTF9DQVNFUz1zdW0oQ0FTRVMpKSxieT0uKE1OVEgsIFdBUkRfU0NPREUpXQ0KDQpvd193YXJkc18yMDEzX21vbnRocyA8LSBvd193YXJkc18yMDEzWywuKE1PTlRITFlfQ0FTRVM9c3VtKFRPVEFMX0NBU0VTKSksYnk9LihNTlRILCBXQVJEX1NDT0RFKV0NCg0Kb3dfd2FyZHNfMjAxM19tb250aGx5IDwtIG93X3dhcmRzXzIwMTNfbW9udGhzWywuKE1FQU5fQ0FTRVNfUEVSX01PTlRIPW1lYW4oTU9OVEhMWV9DQVNFUykpLGJ5PS4oV0FSRF9TQ09ERSldDQpgYGANCkFkZCAiaWQiIGNvbHVtbg0KYGBge3J9DQpvd193YXJkc18yMDEzX21vbnRobHkkaWQgPC0gb3dfd2FyZHNfMjAxM19tb250aGx5JFdBUkRfU0NPREUNCmBgYA0KDQojIFBsb3R0aW5nIGRhdGEgYnkgd2FyZHMNCg0KKipDaXR5IFdhcmRzKioNCg0KPGh0dHBzOi8vd3d3LnRvcm9udG8uY2EvY2l0eS1nb3Zlcm5tZW50L2RhdGEtcmVzZWFyY2gtbWFwcy9vcGVuLWRhdGEvb3Blbi1kYXRhLWNhdGFsb2d1ZS8jMjliNmZhZGYtMGJkNi0yYWY5LTRhOGMtOGM0MWRhMjg1YWQ3Pg0KDQo+IE93bmVyOiBDaXR5IENsZXJrJ3MgT2ZmaWNlDQo+DQo+IEN1cnJlbmN5OiBBdWd1c3QgMjAxOA0KDQoqKjQ0IFdhcmQgTW9kZWwgLSBNYXkgMjAxMCAoV0dTODQgLSBMYXRpdHVkZSAvIExvbmdpdHVkZSkqKg0KDQo8aHR0cDovL29wZW5kYXRhLnRvcm9udG8uY2EvZ2NjL3dhcmRzX21heTIwMTBfd2dzODQuemlwPg0KDQpTaGFwZWZpbGU6IGljaXR3X3dnczg0IA0KDQpyZWFkbWUudHh0Og0KDQo+IFdhcmRzOiBUaGVyZSBhcmUgYSB0b3RhbCBvZiA0NCBlbGVjdG9yYWwgd2FyZHMgaW4gdGhlIENpdHkgb2YgVG9yb250byAgDQo+ICogR0VPX0lEID0gdW5pcXVlIGdlb2dyYXBoaWMgaWRlbnRpZmllciAgICANCj4gKiBOQU1FID0gTmFtZSBvZiB0aGUgV2FyZCB3aXRoIGNvcnJlc3BvbmRpbmcgd2FyZCBudW1iZXIgICAgDQo+ICogU0NPREVfTkFNRSA9IFdhcmQgTnVtYmVyICAgIA0KPiAqIExDT0RFX05BTUUgPSBXYXJkIE51bWJlciBhbmQgdGhlIGNvbW11bml0eSBjb3VuY2lsIGFyZWEgaXQgaXMgaW4gKE4sUywgRSBvciBXKSAgICANCj4gKiBUWVBFX0RFU0MgPSBXYXJkICAgIA0KPiAqIFRZUEVfQ09ERSA9IENpdHkgV2FyZCAgICANCg0KV2Ugc2hhbGwgaW5wdXQgd2FyZHMgc2hhcGVmaWxlLCBjcmVhdGUgY2VudHJvaWRzIG9mIHdhcmRzLCBhbmQgcHJvY2VzcyBzaGFwZWZpbGUgYXMgZGF0YWZyYW1lLiBGaXJzdA0Kd2UgaW5wdXQgdGhlIHNoYXBlZmlsZQ0KYGBge3J9DQp3YXJkcy5zaCA8LSByZWFkT0dSKCJDOi9Vc2Vycy8xNDE2NS9EZXNrdG9wL0FyY0dJUy9TSEFQRUZJTEVTL01heTIwMTBfV0dTODQiLCAiaWNpdHdfd2dzODQiKQ0KYGBgDQpBZGQgImlkIiBjb2x1bW4NCmBgYHtyfQ0Kd2FyZHMuc2hAZGF0YSRpZCA8LSBhcy5pbnRlZ2VyKHdhcmRzLnNoQGRhdGEkU0NPREVfTkFNRSkNCmBgYA0KTWFrZSBjZW50cm9pZHMgb2YgZWFjaCB3YXJkLCBmb3IgcGxhY2luZyBsYWJlbHMgd2hlbiBwbG90dGluZw0KYGBge3J9DQp3YXJkcy5zaC5jZW50cm9pZHMgIDwtIGFzLmRhdGEuZnJhbWUoZ0NlbnRyb2lkKHdhcmRzLnNoLCBieWlkID0gVFJVRSkpDQpgYGANCkFkZCAiaWQiIGNvbHVtbg0KYGBge3J9DQp3YXJkcy5zaC5jZW50cm9pZHMkaWQgPC0gd2FyZHMuc2hAZGF0YSRpZA0KYGBgDQpTaGFwZWZpbGUgcHJvY2Vzc2luZw0KYGBge3J9DQp3YXJkcy5zaC5wb2ludHMgPSBmb3J0aWZ5KHdhcmRzLnNoLCByZWdpb249ImlkIikNCndhcmRzLnNoLmRmID0gam9pbih3YXJkcy5zaC5wb2ludHMsIHdhcmRzLnNoQGRhdGEsIGJ5PSJpZCIpDQpgYGANCk1lcmdlIHdhcmQgc2hhcGVmaWxlIGFuZCBvd193YXJkc18yMDEwX21vbnRobHkgZGF0YWZyYW1lDQpgYGB7cn0NCndhcmRzLnNoLm93XzIwMTAgPC0gbWVyZ2Uod2FyZHMuc2guZGYsIG93X3dhcmRzXzIwMTBfbW9udGhseSwgYnkgPSAiaWQiKQ0KYGBgDQpNYWtlIGdyYXBoaWNzIG9iamVjdCBmb3Igd2FyZHMuc2gub3dfMjAxMA0KYGBge3J9DQpwLndhcmRzXzIwMTAgPC0gZ2dwbG90KCkgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3YXJkcy5zaC5vd18yMDEwLCANCiAgICAgICAgICAgICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IE1FQU5fQ0FTRVNfUEVSX01PTlRIKSwNCiAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUpICsgIA0KICBjb29yZF9tYXAoKSArIA0KICBzY2FsZV9maWxsX2Rpc3RpbGxlcihuYW1lPSJDYXNlcyIsIHBhbGV0dGUgPSAiWWxPckJyIiwgdHJhbnMgPSAicmV2ZXJzZSIsIGJyZWFrcyA9IHByZXR0eV9icmVha3MobiA9IDgpKSArIA0KICB0aGVtZV9ub3RoaW5nKGxlZ2VuZCA9IFRSVUUpICsgDQogIGxhYnModGl0bGU9Ik1lYW4gbW9udGhseSBudW1iZXIgb2YgT250YXJpbyBXb3JrcyBjYXNlcyBieSB3YXJkIGluIFRvcm9udG8gaW4gMjAxMCIpICsgDQogIGdlb21fdGV4dChhZXMoeD14LCB5PXksIGdyb3VwPU5VTEwsIGxhYmVsPWlkKSwgZGF0YSA9IHdhcmRzLnNoLmNlbnRyb2lkcywgc2l6ZSA9IDIpDQpgYGANClBsb3QgZ3JhcGhpY3Mgb2JqZWN0DQpgYGB7cn0NCnAud2FyZHNfMjAxMCArIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgI2dncGxvdDINCmBgYA0KQ2FsY3VsYXRlIG1lYW4gbnVtYmVyIG9mIGNhc2VzIGluIDIwMTMgYnkgbW9udGggYW5kIHdhcmQNCmBgYHtyfQ0Kb3dfd2FyZHNfMjAxMyA8LSBvd19jYXNlc19kcm9wbmFbWUVBUl9OVU09PTIwMTMsLihUT1RBTF9DQVNFUz1zdW0oQ0FTRVMpKSxieT0uKE1OVEgsIFdBUkRfU0NPREUpXQ0KDQpvd193YXJkc18yMDEzX21vbnRocyA8LSBvd193YXJkc18yMDEzWywuKE1PTlRITFlfQ0FTRVM9c3VtKFRPVEFMX0NBU0VTKSksYnk9LihNTlRILCBXQVJEX1NDT0RFKV0NCg0Kb3dfd2FyZHNfMjAxM19tb250aGx5IDwtIG93X3dhcmRzXzIwMTNfbW9udGhzWywuKE1FQU5fQ0FTRVNfUEVSX01PTlRIPW1lYW4oTU9OVEhMWV9DQVNFUykpLGJ5PS4oV0FSRF9TQ09ERSldIA0KYGBgDQpBZGQgImlkIiBjb2x1bW4NCmBgYHtyfQ0Kb3dfd2FyZHNfMjAxM19tb250aGx5JGlkIDwtIG93X3dhcmRzXzIwMTNfbW9udGhseSRXQVJEX1NDT0RFDQpgYGANCk1lcmdlIHdhcmQgc2hhcGVmaWxlIGFuZCBvd193YXJkc18yMDEzX21vbnRobHkgZGF0YWZyYW1lDQpgYGB7cn0NCndhcmRzLnNoLm93XzIwMTMgPC0gbWVyZ2Uod2FyZHMuc2guZGYsIG93X3dhcmRzXzIwMTNfbW9udGhseSwgYnkgPSAiaWQiKQ0KYGBgDQpNYWtlIGdyYXBoaWNzIG9iamVjdA0KYGBge3J9DQpwLndhcmRzXzIwMTMgPC0gZ2dwbG90KCkgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3YXJkcy5zaC5vd18yMDEzLCANCiAgICAgICAgICAgICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IE1FQU5fQ0FTRVNfUEVSX01PTlRIKSwgDQogICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAwLjI1KSArIA0KICBjb29yZF9tYXAoKSArIA0KICBzY2FsZV9maWxsX2Rpc3RpbGxlcihuYW1lPSJDYXNlcyIsIHBhbGV0dGUgPSAiWWxPckJyIiwgdHJhbnMgPSAicmV2ZXJzZSIsIGJyZWFrcyA9IHByZXR0eV9icmVha3MobiA9IDgpKSArIA0KICB0aGVtZV9ub3RoaW5nKGxlZ2VuZCA9IFRSVUUpICsgDQogIGxhYnModGl0bGU9Ik1lYW4gbW9udGhseSBudW1iZXIgb2YgT250YXJpbyBXb3JrcyBjYXNlcyBieSB3YXJkIGluIFRvcm9udG8gaW4gMjAxMyIpICsgDQogIGdlb21fdGV4dChhZXMoeD14LHk9eSwgZ3JvdXA9TlVMTCwgbGFiZWw9aWQpLCBkYXRhID0gd2FyZHMuc2guY2VudHJvaWRzLCBzaXplPTIpDQpgYGANClBsb3QgZ3JhcGhpY3Mgb2JqZWN0DQpgYGB7cn0NCnAud2FyZHNfMjAxMyArIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgI2dncGxvdDINCmBgYA0KQ2FsY3VsYXRlIG1lYW4gbnVtYmVyIG9mIGNhc2VzIGluIDIwMTMgYnkgbW9udGggYW5kIG5laWdoYm91cmhvb2QNCmBgYHtyfQ0Kb3dfbmJkc18yMDEzIDwtIG93X2Nhc2VzX2Ryb3BuYVtZRUFSX05VTT09MjAxMywuKFRPVEFMX0NBU0VTPXN1bShDQVNFUykpLGJ5PS4oTU5USCwgQ0VOU1VTX05FSUdIX1NDT0RFKV0NCg0Kb3dfbmJkc18yMDEzX21vbnRocyA8LSBvd19uYmRzXzIwMTNbLC4oTU9OVEhMWV9DQVNFUz1zdW0oVE9UQUxfQ0FTRVMpKSxieT0uKE1OVEgsIENFTlNVU19ORUlHSF9TQ09ERSldDQoNCm93X25iZHNfMjAxM19tb250aGx5IDwtIG93X25iZHNfMjAxM19tb250aHNbLC4oTUVBTl9DQVNFU19QRVJfTU9OVEg9bWVhbihNT05USExZX0NBU0VTKSksYnk9LihDRU5TVVNfTkVJR0hfU0NPREUpXQ0KYGBgDQpBZGQgImlkIiBjb2x1bW4NCmBgYHtyfQ0Kb3dfbmJkc18yMDEzX21vbnRobHkkaWQgPC0gb3dfbmJkc18yMDEzX21vbnRobHkkQ0VOU1VTX05FSUdIX1NDT0RFDQpgYGANCkRvaW5nIGZpbmVyIGFnZ3JlZ2F0aW9uOiBtZWFuIG1vbnRobHkgY2FzZXMgd2hvIGFyZSBzaW5nbGUgKHdpdGhvdXQgZGVwZW5kZW50cykNCmBgYHtyfQ0Kb3dfc2luZ2xlcyA8LSB0ZXNzX2R0W0ZBTUlMWV9UWVBfTk09PSJTaW5nbGVzIiwuKFlFQVJfTlVNLCBNTlRILCBXQVJEX1NDT0RFLCBDRU5TVVNfTkVJR0hfU0NPREUsIENBU0VTKV0NCg0Kb3dfc2luZ2xlc19kcm9wbmEgPC0gbmEub21pdChvd19zaW5nbGVzLCBjb2xzPWMoIkNFTlNVU19ORUlHSF9TQ09ERSIsICJXQVJEX1NDT0RFIikpDQoNCm93X3NpbmdsZXNfd2FyZHNfMjAxMyA8LSBvd19zaW5nbGVzX2Ryb3BuYVtZRUFSX05VTT09MjAxMywuKFRPVEFMX0NBU0VTPXN1bShDQVNFUykpLGJ5PS4oTU5USCwgV0FSRF9TQ09ERSldDQoNCm93X3NpbmdsZXNfd2FyZHNfMjAxM19tb250aHMgPC0gb3dfc2luZ2xlc193YXJkc18yMDEzWywuKE1PTlRITFlfQ0FTRVM9c3VtKFRPVEFMX0NBU0VTKSksYnk9LihNTlRILCBXQVJEX1NDT0RFKV0NCg0Kb3dfc2luZ2xlc193YXJkc18yMDEzX21vbnRobHkgPC0gb3dfc2luZ2xlc193YXJkc18yMDEzX21vbnRoc1ssLihNRUFOX0NBU0VTX1BFUl9NT05USD1tZWFuKE1PTlRITFlfQ0FTRVMpKSxieT0uKFdBUkRfU0NPREUpXQ0KYGBgDQpBZGQgImlkIiBjb2x1bW4NCmBgYHtyfQ0Kb3dfc2luZ2xlc193YXJkc18yMDEzX21vbnRobHkkaWQgPC0gb3dfc2luZ2xlc193YXJkc18yMDEzX21vbnRobHkkV0FSRF9TQ09ERQ0KYGBgDQpNZXJnZSB3YXJkIHNoYXBlZmlsZSBhbmQgT250YXJpbyBXb3JrcyBkYXRhZnJhbWUNCmBgYHtyfQ0Kd2FyZHMuc2gub3dfc2luZ2xlc18yMDEzIDwtIG1lcmdlKHdhcmRzLnNoLmRmLCBvd19zaW5nbGVzX3dhcmRzXzIwMTNfbW9udGhseSwgYnkgPSAiaWQiKQ0KYGBgDQpXZSBzaGFsbCBwbG90IHdhcmRzLnNoLm93X3NpbmdsZXNfMjAxMy4gTWFrZSBncmFwaGljcyBvYmplY3QNCmBgYHtyfQ0KcC53YXJkc19zaW5nbGVzXzIwMTMgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3BvbHlnb24oZGF0YSA9IHdhcmRzLnNoLm93X3NpbmdsZXNfMjAxMywgDQogICAgICAgICAgICAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBNRUFOX0NBU0VTX1BFUl9NT05USCksIA0KICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yNSkgKyANCiAgY29vcmRfbWFwKCkgKyANCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIobmFtZT0iQ2FzZXMiLCBwYWxldHRlID0gIlB1UmQiLCB0cmFucyA9ICJyZXZlcnNlIiwgYnJlYWtzID0gcHJldHR5X2JyZWFrcyhuID0gOCkpICsgDQogIHRoZW1lX25vdGhpbmcobGVnZW5kID0gVFJVRSkgKyANCiAgbGFicyh0aXRsZT0iTWVhbiBtb250aGx5IG51bWJlciBvZiBPbnRhcmlvIFdvcmtzIGNhc2VzIChzaW5nbGVzKSBieSB3YXJkIGluIFRvcm9udG8gaW4gMjAxMyIpICsgDQogIGdlb21fdGV4dChhZXMoeD14LHk9eSwgZ3JvdXA9TlVMTCwgbGFiZWw9aWQpLCBkYXRhID0gd2FyZHMuc2guY2VudHJvaWRzLCBzaXplID0gMikNCmBgYA0KUGxvdCBncmFwaGljcyBvYmplY3QNCmBgYHtyfQ0KcC53YXJkc19zaW5nbGVzXzIwMTMgKyBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpDQpgYGANCg0KIyBQbG90dGluZyBkYXRhIGJ5IG5laWdoYm91cmhvb2RzDQoNCioqTmVpZ2hib3VyaG9vZHMgc2hhcGVmaWxlKioNCg0KPGh0dHBzOi8vd3d3LnRvcm9udG8uY2EvY2l0eS1nb3Zlcm5tZW50L2RhdGEtcmVzZWFyY2gtbWFwcy9vcGVuLWRhdGEvb3Blbi1kYXRhLWNhdGFsb2d1ZS8jYTQ1YmQ0NWEtZWRlOC03MzBlLTFhYmMtOTMxMDViMmM0MzlmPg0KDQo8aHR0cDovL29wZW5kYXRhLnRvcm9udG8uY2EvZ2NjL25laWdoYm91cmhvb2RzX3BsYW5uaW5nX2FyZWFzX3dnczg0LnppcD4NCg0KPiBPd25lcjogU29jaWFsIERldmVsb3BtZW50LCBGaW5hbmNlICYgQWRtaW5pc3RyYXRpb24NCj4NCj4gQ3VycmVuY3k6IEp1bmUgMjAxNA0KDQoqKk5laWdoYm91cmhvb2RzIChXR1M4NCkqKg0KDQpTaGFwZWZpbGU6IE5FSUdIQk9SSE9PRFNfV0dTODQNCg0KTkVJR0hCT1JIT09EU19XR1M4NF9yZWFkbWUudHh0Og0KDQo+IE5FSUdIQk9SSE9PRFNfV0dTODRfcmVhZG1lICANCj4gKiBDb2x1bW4gbmFtZSAgKERlc2NyaXB0aW9uKSAgICANCj4gKiBBUkVBX1NfQ0QgPSBBUkVBX1NIT1JUX0NPREUgICAgDQo+ICogQVJFQV9OQU1FID0gQVJFQV9OQU1FICAgIA0KDQpJbnB1dCBzaGFwZWZpbGUNCmBgYHtyfQ0KbmJkcy5zaCA8LSByZWFkT0dSKCJDOi9Vc2Vycy8xNDE2NS9EZXNrdG9wL0FyY0dJUy9TSEFQRUZJTEVTL25laWdoYm91cmhvb2RzX3BsYW5uaW5nX2FyZWFzX3dnczg0IiwgIk5FSUdIQk9SSE9PRFNfV0dTODQiKQ0KYGBgDQpBZGQgImlkIiBjb2x1bW4NCmBgYHtyfQ0KbmJkcy5zaEBkYXRhJGlkIDwtIGFzLmludGVnZXIobmJkcy5zaEBkYXRhJEFSRUFfU19DRCkNCmBgYA0KTWFrZSBjZW50cm9pZHMgb2YgZWFjaCBuZWlnaGJvdXJob29kLCBmb3IgcGxhY2luZyBsYWJlbHMgd2hlbiBwbG90dGluZw0KYGBge3J9DQpuYmRzLnNoLmNlbnRyb2lkcyAgPC0gYXMuZGF0YS5mcmFtZShnQ2VudHJvaWQobmJkcy5zaCwgYnlpZCA9IFRSVUUpKQ0KYGBgDQpBZGQgImlkIiBjb2x1bW4NCmBgYHtyfQ0KbmJkcy5zaC5jZW50cm9pZHMkaWQgPC0gbmJkcy5zaEBkYXRhJGlkDQpgYGANClNoYXBlZmlsZSBwcm9jZXNzaW5nDQpgYGB7cn0NCm5iZHMuc2gucG9pbnRzID0gZm9ydGlmeShuYmRzLnNoLCByZWdpb24gPSAiaWQiKQ0KbmJkcy5zaC5kZiA9IGpvaW4obmJkcy5zaC5wb2ludHMsIG5iZHMuc2hAZGF0YSwgYnkgPSAiaWQiKQ0KYGBgDQpNZXJnZSBuZWlnaGJvdXJob29kIHNoYXBlZmlsZSBhbmQgT250YXJpbyBXb3JrcyBkYXRhZnJhbWUNCmBgYHtyfQ0KbmJkcy5zaC5vd18yMDEzIDwtIG1lcmdlKG5iZHMuc2guZGYsIG93X25iZHNfMjAxM19tb250aGx5LCBieSA9ICJpZCIpDQpgYGANCk1ha2UgZ3JhcGhpY3Mgb2JqZWN0DQpgYGB7cn0NCnAubmJkc18yMDEzIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBuYmRzLnNoLm93XzIwMTMsIA0KICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gTUVBTl9DQVNFU19QRVJfTU9OVEgpLCANCiAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMikgKyANCiAgY29vcmRfbWFwKCkgKyANCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIobmFtZT0iQ2FzZXMiLCBwYWxldHRlID0gIllsT3JCciIsIHRyYW5zID0gInJldmVyc2UiLCBicmVha3MgPSBwcmV0dHlfYnJlYWtzKG4gPSA4KSkgKyANCiAgdGhlbWVfbm90aGluZyhsZWdlbmQgPSBUUlVFKSArIA0KICBsYWJzKHRpdGxlPSJNZWFuIG1vbnRobHkgbnVtYmVyIG9mIE9udGFyaW8gV29ya3MgY2FzZXMgYnkgbmVpZ2hib3VyaG9vZCBpbiBUb3JvbnRvIGluIDIwMTMiKSArIA0KICBnZW9tX3RleHQoYWVzKHg9eCx5PXksIGdyb3VwPU5VTEwsIGxhYmVsPWlkKSwgZGF0YSA9IG5iZHMuc2guY2VudHJvaWRzLCBzaXplID0gMikNCmBgYA0KUGxvdCBncmFwaGljcyBvYmplY3QNCmBgYHtyfQ0KcC5uYmRzXzIwMTMrZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKQ0KYGBgDQoNCg0KDQojIFRpbWUgc2VyaWVzDQoNCkF2cmlsIENvZ2hsYW4sIFtVc2luZyBSIGZvciBUaW1lIFNlcmllcyBBbmFseXNpc10oaHR0cHM6Ly9hLWxpdHRsZS1ib29rLW9mLXItZm9yLXRpbWUtc2VyaWVzLnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC9zcmMvdGltZXNlcmllcy5odG1sKQ0KDQpWaW5jZW50IFpvb25la3luZCwgW1RpbWUgc2VyaWVzXShodHRwOi8vem9vbmVrMi5mcmVlLmZyL1VOSVgvNDhfUi8xNS5odG1sKQ0KDQoNCg0KIyMgVEVTUyBPbnRhcmlvIFdvcmtzIGRhdGEgbWFuaXB1bGF0aW9uDQoNCkdyb3VwIGJ5IG1vbnRoIGFuZCBjcmVhdGUgdGltZSBzZXJpZXM6DQpgYGB7cn0NCm93X2Nhc2VzX21vbnRocyA8LSB0ZXNzX2R0WywuKE1OVEgsIENBU0VTLCBORVdfQ0FTRVMsIEVYSVRTKV0NCg0Kb3dfY2FzZXNfbW9udGhseSA8LSBvd19jYXNlc19tb250aHNbLC4oQ0FTRVM9c3VtKENBU0VTKSwgTkVXX0NBU0VTPXN1bShORVdfQ0FTRVMpLCBFWElUUz1zdW0oRVhJVFMpKSxieT0uKE1OVEgpXQ0KDQpvd19jYXNlc19tb250aGx5JE1OVEggPC0gTlVMTA0KDQpoZWFkKG93X2Nhc2VzX21vbnRobHkpDQpgYGANCg0KYGBge3J9DQpvd190cyA8LSB0cyhvd19jYXNlc19tb250aGx5LCBzdGFydCA9IGMoMjAwNCwxKSwgZnJlcXVlbmN5ID0gMTIpDQoNCmhlYWQob3dfdHMpDQpgYGANCg0KIyMgVEVTUyBPbnRhcmlvIFdvcmtzIHBsb3RzDQoNClBsb3QgdGltZSBzZXJpZXMNCmBgYHtyfQ0Kb3B0aW9ucyhzY2lwZW49OTk5KSAjZG8gbm90IHVzZSBzY2llbnRpZmljIG5vdGF0aW9uDQoNCnBsb3Qob3dfdHMpDQpgYGANCg0KTWFrZSB0aW1lIHNlcmllcyBvZiBDQVNFUw0KDQpgYGB7cn0NCkNBU0VTIDwtIHRzKG93X2Nhc2VzX21vbnRobHkkQ0FTRVMsIHN0YXJ0ID0gYygyMDA0LDEpLCBmcmVxdWVuY3kgPSAxMikNCg0KcGxvdChDQVNFUykNCmBgYA0KDQpNYWtlIHRpbWUgc2VyaWVzIG9mIE5FV19DQVNFUw0KDQpgYGB7cn0NCk5FV19DQVNFUyA8LSB0cyhvd19jYXNlc19tb250aGx5JE5FV19DQVNFUywgc3RhcnQgPSBjKDIwMDQsMSksIGZyZXF1ZW5jeSA9IDEyKQ0KDQpwbG90KE5FV19DQVNFUykNCmBgYA0KDQoNCiMjIERlY29tcG9zaW5nIHRpbWUgc2VyaWVzIGludG8gdHJlbmQsIHNlYXNvbmFsLCBhbmQgcmVtYWluZGVyIHRlcm1zDQoNCk5vdyB3ZSB1c2Ugc3RhdHM6OmRlY29tcG9zZSBhbmQgc3RhdHM6OnN0bCB0byBzZXBhcmF0ZSB0aW1lIHNlcmllcyBhcyBhIHN1bSBvZiANCnRyZW5kLCBzZWFzb25hbCwgYW5kIHJlbWFpbmRlciB0ZXJtcy4NCg0KYGBge3J9DQpDQVNFU19jb21wb25lbnRzIDwtIGRlY29tcG9zZShDQVNFUykNCmBgYA0KDQpgYGB7cn0NCmF1dG9wbG90KENBU0VTX2NvbXBvbmVudHMpDQpgYGANCg0KYGBge3J9DQpORVdfQ0FTRVNfY29tcG9uZW50cyA8LSBkZWNvbXBvc2UoTkVXX0NBU0VTKQ0KYGBgDQoNCmBgYHtyfQ0KYXV0b3Bsb3QoTkVXX0NBU0VTX2NvbXBvbmVudHMpDQpgYGANCg0KYGBge3J9DQpDQVNFUy5zdGwgPC0gc3RsKENBU0VTLCBzLndpbmRvdyA9ICJwZXJpb2RpYyIpDQpgYGANCg0KYGBge3J9DQphdXRvcGxvdChDQVNFUy5zdGwpDQpgYGANCg0KYGBge3J9DQpORVdfQ0FTRVMuc3RsIDwtIHN0bChORVdfQ0FTRVMsIHMud2luZG93ID0gInBlcmlvZGljIikNCmBgYA0KDQpgYGB7cn0NCmF1dG9wbG90KE5FV19DQVNFUy5zdGwpDQpgYGANCg0KDQojIyBIb2x0LVdpbnRlcnMgZXhwb25lbnRpYWwgc21vb3RoaW5nDQoNCmBgYHtyfQ0KQ0FTRVMuSFcgPC0gSG9sdFdpbnRlcnMoQ0FTRVMpDQoNCkNBU0VTLkhXDQpgYGANCg0KYGBge3J9DQpwbG90KENBU0VTLkhXKQ0KYGBgDQoNCmBgYHtyfQ0KQ0FTRVMuSFcuZm9yZWNhc3QgPC0gZm9yZWNhc3QoQ0FTRVMuSFcsIGggPSAyNCkNCg0KcGxvdChDQVNFUy5IVy5mb3JlY2FzdCkNCmBgYA0KDQoNCiMjIEFSSU1BDQoNCmBgYHtyfQ0KQ0FTRVMuYXJpbWEgPC0gYXV0by5hcmltYShDQVNFUykNCmBgYA0KDQpgYGB7cn0NCkNBU0VTLmFyaW1hDQpgYGANCg0KDQpgYGB7cn0NCm9wIDwtIHBhcihtZnJvdz1jKDIsMSksIG1hcj1jKDIsNCwxLDIpKy4xKQ0KYWNmKENBU0VTLCAgbWFpbj0iIikNCnBhY2YoQ0FTRVMsIG1haW49IiIpDQpwYXIob3ApDQpgYGANCg0KDQpgYGB7cn0NCkNBU0VTLmZvcmVjYXN0IDwtIGZvcmVjYXN0KENBU0VTLCBsZXZlbCA9IGMoOTUpLCBoID0gMjIpDQoNCnBsb3QoQ0FTRVMuZm9yZWNhc3QpDQpgYGANCg0KYGBge3J9DQpDQVNFUy5hcmltYS5mb3JlY2FzdCA8LSBmb3JlY2FzdChDQVNFUy5hcmltYSwgbGV2ZWwgPSBjKDk1KSwgaCA9IDIyKQ0KDQpwbG90KENBU0VTLmFyaW1hLmZvcmVjYXN0KQ0KYGBgDQoNCg0KDQojIyBPbnRhcmlvIE1vbnRobHkgU29jaWFsIEFzc2lzdGFuY2UgQ2FzZWxvYWRzIDE5NjktMjAxOA0KDQpgYGB7cn0NClNBIDwtIGZyZWFkKCJoaXN0b3JpY2FsX3NhX3JlY2lwaWVudHNfZGF0YXNldF9xMl8yMDE4XzE5LmNzdiIpDQoNClNBJEJlbmVmaWNpYXJpZXMgPC0gTlVMTA0KYGBgDQoNCk1ha2UgdGltZSBzZXJpZXMNCg0KYGBge3J9DQpTQV90cyA8LSB0cyhTQSRDYXNlcywgc3RhcnQgPSBjKDE5NjksMSksIGZyZXF1ZW5jeSA9IDEyKQ0KYGBgDQoNCg0KYGBge3J9DQpwbG90KFNBX3RzKQ0KYGBgDQoNCg0KYGBge3J9DQpTQV90c18xOTk3IDwtIHdpbmRvdyhTQV90cywgYygxOTk3LDEpKQ0KDQpwbG90KFNBX3RzXzE5OTcpDQpgYGANCg0KYGBge3J9DQpTQV90c18xOTk3LmNvbXBvbmVudHMgPC0gZGVjb21wb3NlKFNBX3RzXzE5OTcpDQoNCmF1dG9wbG90KFNBX3RzXzE5OTcuY29tcG9uZW50cykNCmBgYA0KDQoNCmBgYHtyfQ0KU0FfdHNfMTk5Ny5zdGwgPC0gc3RsKFNBX3RzXzE5OTcsIHMud2luZG93ID0gInBlcmlvZGljIikNCg0KYXV0b3Bsb3QoU0FfdHNfMTk5Ny5zdGwpDQpgYGANCg0KDQojIyBNYXl0cmVlIFNvY2lhbCBBc3Npc3RhbmNlIFN1bW1hcmllcw0KDQpgYGB7cn0NCk1heXRyZWVfT04gPC0gZnJlYWQoIk9OLmNzdiIpDQpgYGANCg0KYGBge3J9DQpzdHIoTWF5dHJlZV9PTikNCmBgYA0KDQoNCmBgYHtyfQ0KTWF5dHJlZV9PVy50cyA8LSB0cyhNYXl0cmVlX09OJGBPbnRhcmlvIFdvcmtzIENhc2VzYCwgc3RhcnQgPSAxOTk3LCBmcmVxdWVuY3kgPSAxKQ0KDQpNYXl0cmVlX09EU1AudHMgPC0gdHMoTWF5dHJlZV9PTiRgT0RTUCBDYXNlc2AsIHN0YXJ0ID0gMTk5NywgZnJlcXVlbmN5ID0gMSkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoTWF5dHJlZV9PVy50cykNCmBgYA0KDQpgYGB7cn0NCnBsb3QoTWF5dHJlZV9PRFNQLnRzKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChNYXl0cmVlX09EU1AudHMrTWF5dHJlZV9PVy50cykNCmBgYA0KDQojIyBTdGF0aXN0aWNzIENhbmFkYSBUYWJsZSAxMS0xMC0wMjM5LTAxDQoNCmBgYHtyfQ0KVDExMTAwMjM5MDEgPC0gZnJlYWQoIjExMTAwMjM5MDEtbm9TeW1ib2wuY3N2IikNCmBgYA0KDQpgYGB7cn0NCnN0cihUMTExMDAyMzkwMSkNCmBgYA0KDQoNCmBgYHtyfQ0KVDExMTAwMjM5MDEudHMgPC0gdHMoVDExMTAwMjM5MDEkYE51bWJlciBvZiBwZXJzb25zYCwgc3RhcnQgPSAxOTc2LCBmcmVxdWVuY3kgPSAxKQ0KDQpwbG90KFQxMTEwMDIzOTAxLnRzLCB5bGFiID0gIk51bWJlciBvZiBwZXJzb25zIiwgbWFpbiA9ICJQZXJzb25zIDE2IHllYXJzIGFuZCBvbGRlciBpbiBUb3JvbnRvIHdpdGggc29jaWFsIGFzc2lzdGFuY2UgaW5jb21lIiApDQpgYGANCg0KYGBge3J9DQpUMTExMDAyMzkwMS50c18xOTk3IDwtIHdpbmRvdyhUMTExMDAyMzkwMS50cywgYygxOTk3LDEpKQ0KDQpwbG90KFQxMTEwMDIzOTAxLnRzXzE5OTcpDQpgYGANCg0KDQpgYGB7cn0NCm9wIDwtIHBhcihtZnJvdz1jKDIsMSksIG1hcj1jKDIsNCwxLDIpKy4xKQ0KYWNmKFQxMTEwMDIzOTAxLnRzXzE5OTcsICBtYWluPSIiKQ0KcGFjZihUMTExMDAyMzkwMS50c18xOTk3LCBtYWluPSIiKQ0KcGFyKG9wKQ0KYGBgDQoNCmBgYHtyfQ0KVDExMTAwMjM5MDEudHNfMTk5Ny5mb3JlY2FzdCA8LSBmb3JlY2FzdChUMTExMDAyMzkwMS50c18xOTk3LCBsZXZlbCA9IGMoOTUpLCBoID0gMTIpDQoNCnBsb3QoVDExMTAwMjM5MDEudHNfMTk5Ny5mb3JlY2FzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KVDExMTAwMjM5MDEudHNfMTk5Ny5hcmltYSA8LSBhdXRvLmFyaW1hKFQxMTEwMDIzOTAxLnRzXzE5OTcpDQoNClQxMTEwMDIzOTAxLnRzXzE5OTcuYXJpbWENCmBgYA0KDQpgYGB7cn0NClQxMTEwMDIzOTAxLnRzXzE5OTcuYXJpbWEuZm9yZWNhc3QgPC0gZm9yZWNhc3QoVDExMTAwMjM5MDEudHNfMTk5Ny5hcmltYSwgbGV2ZWwgPSBjKDk1KSwgaCA9IDUpDQoNCnBsb3QoVDExMTAwMjM5MDEudHNfMTk5Ny5hcmltYS5mb3JlY2FzdCkNCmBgYA==