library(readr)
library(dplyr)
library(lubridate)
library(ggplot2)
library(plotly)
library(ggmap)
rad2deg <- function(rad) {(rad * 180) / (pi)}
deg2rad <- function(deg) {(deg * pi) / (180)}
caprino_hours<-read.csv("data/caprino_hours.csv.gz",header = F)
caprino_imu_gyros<-read.csv("data/caprino_imu_gyro.csv.gz", header=F)
caprino_imu_acc<-read.csv("data/caprino_imu_acc.csv.gz", header=F)
caprino_gps<-read.csv("data/caprino_gps.csv",header=F)
caprino_imu_acc %>% nrow()
caprino_imu_gyros %>% nrow()
caprino_gps %>% nrow()
caprino_hours %>% nrow()
Datos de los archivos
caprino_imu_acc.csv, 4 columnas: tiempo UTC en microsegundos, aceleración X en m/s², aceleración Y en m/s², aceleración, Z en m/s². Muestreado a 200 muestras por segundo.
caprino_imu_gyro.csv, 4 columnas: tiempo UTC en microsegundos, velocidad angular X en rad/s, velocidad angular Y en rad/s, velocidad angular Z en rad/s. Muestreado a 200 muestras por segundo.
caprino_gps.csv, 4 columnas: tiempo UTC en microsegundos, latitud en radianes, longitud en radianes, altitud en metros. Muestreado a 5 muestras por segundo.
caprino_hours.csv: tiempo en horas, minutos y segundos. Muestreado a 5 muestras por segundo. La primera hora de este archivo coincide con el primer valor de tiempo UTC del archivo caprino_gps.csv.
Construccion del Dataset
caprino_data<-cbind(caprino_imu_acc,caprino_imu_gyros %>% select(-V1))
caprino_data<-cbind(caprino_data[seq(2,nrow(caprino_data),2),] %>% head(nrow(caprino_gps)-1), caprino_gps[2:nrow(caprino_gps),] )
names(caprino_data)<-c("imu_ts",
"accX",
"accY",
"accZ",
"gyroX",
"gyroY",
"gyroZ",
"gps_hour",
"gps_ts",
"lat",
"long",
"alt"
)
#caprino_data %>% mutate(diff=imu_ts-gps_ts) %>% select(diff)
caprino_data<-caprino_data %>% mutate(activity=ifelse(hms(gps_hour)>hms("8:56:00") & hms(gps_hour)<hms("10:38:00"),"Dco",
ifelse(hms(gps_hour)>=hms("10:38:00") & hms(gps_hour)<hms("11:38:00"),"C",
ifelse(hms(gps_hour)>=hms("11:38:00") & hms(gps_hour)<hms("12:05:00"),"P",
ifelse(hms(gps_hour)>=hms("12:05:00") & hms(gps_hour)<hms("15:48:00"),"Dca",
ifelse(hms(gps_hour)>=hms("17:30:00") & hms(gps_hour)<hms("20:33:00"),"P",
ifelse(hms(gps_hour)>=hms("20:50:00") & hms(gps_hour)<hms("21:31:00"),"C",
ifelse(hms(gps_hour)>=hms("21:33:00") & hms(gps_hour)<hms("21:50:00"),"Dco",NA)
)))))
)
)
caprino_data<-caprino_data %>% mutate(id=cut(as.POSIXct(hms(caprino_data$gps_hour),origin="1960-01-01",tz="GMT"),breaks="1 min"))
Numero de mediciones por tipo de actividad
NA hace referencia a aquellas mediciones que no fueron etiquetadas
caprino_data %>% group_by(activity) %>% summarise(n=n())
Grafica de la diferencia entre el tiempo de muestreo de la IMU y GPS.
ggplot(caprino_data%>% mutate(diff=imu_ts - gps_ts))+
geom_line(aes(y=diff,x=seq(1,nrow(caprino_data))),color='blue')+
theme_bw()

Grafica del trayecto georeferenciado discriminado por actividad

Grafica del trayecto georeferenciado discriminado por actividad
Las zonas con menor densidad de puntos indican un menor lapso de tiempo en esa zona.
caprino_data %>%
ggplot(aes(x=long,y=lat))+
geom_point(aes(color=activity),alpha=0.01,size=0.8)+
scale_color_brewer(palette="Spectral")+
theme_bw()

caprino_data_altitude_group<-caprino_data %>% mutate(altitude_group = cut(alt, breaks = c(200,378,525,527,530,650,800,900)))
ggplot(caprino_data_altitude_group,aes(x=long,y=lat))+
stat_density2d(aes(fill=altitude_group,alpha=..level..), geom="polygon", bins=1000) +
theme_minimal()
Distribución de los resultados de los sensores en cada una de las actividades (sin outliers)
- Se observan diferencias en la distribucion de la actividad P en comparacion a las otras actividades.
- Las actividades Dca y Dco presentan similtudes (lo cual es esperable).
- La activdad P presenta algunas similitudes con Dca y Dco, lo que puede dificultar su detection.
caprino_data_melted<-caprino_data %>% select(accX,accY,accZ,gyroX,gyroY,gyroZ,activity) %>% reshape2::melt()
Using activity as id variables
ggplot(caprino_data_melted)+
geom_boxplot(aes(x=variable,color=variable,y=value),outlier.size=0.1,outlier.shape = NA)+
scale_y_continuous(limits = c(-25,10))+
theme_bw()+
theme(axis.text.x = element_text(angle = 45, hjust = 1))+
facet_wrap(~activity)

NA
Tomamos muestras aleatorias de 1 minuto de duracion para sobre las cuales despues querremos predecir el comportamiento
set.seed(21092019)
minute_slice<-as.vector(t(caprino_data %>% group_by(id) %>% summarise(n=n()) %>% select(id)))
trainidx<-sample(1:length(minute_slice),(length(minute_slice)*70)/100.0)
data_train<-caprino_data %>% filter( id %in% minute_slice[trainidx])
data_test<-caprino_data %>% filter( id %in% (minute_slice[-trainidx]))
data_test%>% group_by(id) %>% summarise(n=n())
Aplicamos Bag-of-features.
presupuesto
Consideramos cada muestra como un vector de 5 dimensiones. La idea es aplicar un algoritmo de clustering sobre estos vectores (accX,accY,accZ,GyroX,gyroY). Una vez encontrado los centroides, para cada muestra de 1 se construye un histograma de los vectores que han caido en cada uno de estos clusters. Este histograma es el que se utiliza para entregar el algoritmo.
La hipotesis de aplicar este metodo es que aquellas muestras que pertenecen a una misma actividad tendran valores similares. Los cuales han sido representado mediantes estos histogramas.
Aplicamos k-means con 12 centroides sobre el conjunto de entrenamiento.
cluster_results<-kmeans(x = data_train%>% select(accX,accY,accZ,gyroX,gyroY), centers = 24, nstart = 5)
did not converge in 10 iterationsQuick-TRANSfer stage steps exceeded maximum (= 8090550)Quick-TRANSfer stage steps exceeded maximum (= 8090550)Quick-TRANSfer stage steps exceeded maximum (= 8090550)Quick-TRANSfer stage steps exceeded maximum (= 8090550)
data_train_cluster<-cbind(data_train,cluster=cluster_results$cluster) %>% group_by(activity,cluster)
data_train_cluster %>% summarise(n=n()) %>%
ggplot()+
geom_col(aes(x=cluster,y=n),fill='skyblue')+
facet_wrap(~activity)+
theme_bw()

NA
Aplicamos k-means sobre el conjunto de test. Se usan los mismos centroides encontrados en training
cluster_results_test<-kmeans(x = data_test%>% select(accX,accY,accZ,gyroX,gyroY), centers = cluster_results$centers)
data_test_cluster<-cbind(data_test,cluster=cluster_results_test$cluster) %>% group_by(activity,cluster)
data_test_cluster %>% summarise(n=n()) %>%
ggplot()+
geom_col(aes(x=cluster,y=n),fill='orange')+
facet_wrap(~activity)+
theme_bw()

Preparando el dataset para aplicar un clasificador
Tomamos intervalos de 1m y creamos los atributos usando bag-of-features
sample_minute_slice<-minute_slice[sample(1:length(minute_slice),40)]
data_train_cluster %>% filter (id %in% sample_minute_slice) %>% group_by(activity,id,cluster) %>% summarise(n=n()) %>%
ggplot()+
geom_col(aes(x=cluster,y=n,fill=activity))+
facet_wrap(~id)+
theme_bw()

data_train_cluster_labeled<-data_train_cluster %>% group_by(id,cluster) %>% summarise(n=n()) %>% reshape2::dcast(id~cluster,fill = 0) %>% inner_join(data_train_cluster%>% ungroup() %>% select(id,activity) %>% unique() ,by="id") %>% filter(!is.na(activity))
Using n as value column: use value.var to override.
data_test_cluster_labeled<-data_test_cluster %>% group_by(id,cluster) %>% summarise(n=n()) %>% reshape2::dcast(id~cluster,fill = 0) %>% inner_join(data_test_cluster%>% ungroup() %>% select(id,activity) %>% unique() ,by="id")
Using n as value column: use value.var to override.
Probamos Random Forest
Aplicamos 2x5 CV para elegir la mejor version del modelo
library(caret)
library(doMC)
registerDoMC(cores=6)
ctrl_fast <- trainControl(method="repeatedcv",
repeats=2,
number=5,
#summaryFunction=twoClassSummary,
verboseIter=F,
#preProcOptions = list(pcaComp = 10),
classProbs=TRUE,
allowParallel = T)
nnGrid <- expand.grid(decay = c(0), size=c(10,20,50,100,200,300))
svmGrid <- expand.grid(sigma = c(0.001,0.00001), C=c(0.1,0.001,0.5))
kerasGrid <- expand.grid(
size=c(20,50,2000,5000),
lambda=c(0),
batch_size=c(1024),
decay=c(0),
activation=c('tanh','sigmoid','relu'),
lr=c(0.001), rho=c(0.9)
)
train_formula=formula("activity~.")
#ctrl_fast$sampling<-"smote"
rfFit <- caret::train(train_formula,
data = data_train_cluster_labeled %>% select(-id),
#metric="ROC",
#preProcess=c("pca"),
#method = "mlpWeightDecay",
#method = "svmRadial",
#method ="mlpKerasDecay",
method = "rf",
#tuneGrid=kerasGrid,
tuneLength=11,
verbose=0,
#epochs=100,
trControl = ctrl_fast)
modelos obtenidos
rfFit
Random Forest
459 samples
24 predictors
4 classes: 'C', 'Dca', 'Dco', 'P'
No pre-processing
Resampling: Cross-Validated (5 fold, repeated 2 times)
Summary of sample sizes: 366, 368, 368, 368, 366, 368, ...
Resampling results across tuning parameters:
mtry Accuracy Kappa
2 0.7840887 0.7014212
4 0.7951013 0.7175083
6 0.7983624 0.7220231
8 0.7907174 0.7116646
10 0.7896777 0.7099344
13 0.7830726 0.7005419
15 0.7853416 0.7040351
17 0.7831435 0.7006733
19 0.7841949 0.7021027
21 0.7831435 0.7010262
24 0.7754748 0.6901564
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 6.
Evaluacion sobre el conjunto de test
Matriz de Confusion
data_test_prediction<-predict(rfFit,data_test_cluster_labeled %>% select(-id))#,type="prob")
caret::confusionMatrix(data_test_prediction,as.factor(data_test_cluster_labeled$activity))
Confusion Matrix and Statistics
Reference
Prediction C Dca Dco P
C 25 0 1 1
Dca 0 65 5 9
Dco 0 1 27 4
P 1 7 4 44
Overall Statistics
Accuracy : 0.8299
95% CI : (0.7695, 0.8799)
No Information Rate : 0.3763
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7605
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: C Class: Dca Class: Dco Class: P
Sensitivity 0.9615 0.8904 0.7297 0.7586
Specificity 0.9881 0.8843 0.9682 0.9118
Pos Pred Value 0.9259 0.8228 0.8438 0.7857
Neg Pred Value 0.9940 0.9304 0.9383 0.8986
Prevalence 0.1340 0.3763 0.1907 0.2990
Detection Rate 0.1289 0.3351 0.1392 0.2268
Detection Prevalence 0.1392 0.4072 0.1649 0.2887
Balanced Accuracy 0.9748 0.8874 0.8489 0.8352

CONCLUSIONES
LS0tCnRpdGxlOiAiRGF0YXNldCBHYW5hZG8gQ2FwcmlubyA3LzkvMTgiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShnZ21hcCkKCnJhZDJkZWcgPC0gZnVuY3Rpb24ocmFkKSB7KHJhZCAqIDE4MCkgLyAocGkpfQpkZWcycmFkIDwtIGZ1bmN0aW9uKGRlZykgeyhkZWcgKiBwaSkgLyAoMTgwKX0KCgoKY2Fwcmlub19ob3VyczwtcmVhZC5jc3YoImRhdGEvY2Fwcmlub19ob3Vycy5jc3YuZ3oiLGhlYWRlciA9IEYpCmNhcHJpbm9faW11X2d5cm9zPC1yZWFkLmNzdigiZGF0YS9jYXByaW5vX2ltdV9neXJvLmNzdi5neiIsIGhlYWRlcj1GKQpjYXByaW5vX2ltdV9hY2M8LXJlYWQuY3N2KCJkYXRhL2NhcHJpbm9faW11X2FjYy5jc3YuZ3oiLCBoZWFkZXI9RikKY2Fwcmlub19ncHM8LXJlYWQuY3N2KCJkYXRhL2NhcHJpbm9fZ3BzLmNzdiIsaGVhZGVyPUYpCgpjYXByaW5vX2ltdV9hY2MgJT4lIG5yb3coKQpjYXByaW5vX2ltdV9neXJvcyAlPiUgbnJvdygpCmNhcHJpbm9fZ3BzICU+JSBucm93KCkKY2Fwcmlub19ob3VycyAlPiUgbnJvdygpCgoKYGBgCgoKIyMjIERhdG9zIGRlIGxvcyBhcmNoaXZvcyAKKiBjYXByaW5vX2ltdV9hY2MuY3N2LCA0IGNvbHVtbmFzOiB0aWVtcG8gVVRDIGVuIG1pY3Jvc2VndW5kb3MsCmFjZWxlcmFjacOzbiBYIGVuIG0vc8KyLCBhY2VsZXJhY2nDs24gWSBlbiBtL3PCsiwgYWNlbGVyYWNpw7NuLCBaIGVuIG0vc8KyLgpNdWVzdHJlYWRvIGEgMjAwIG11ZXN0cmFzIHBvciBzZWd1bmRvLgoKKiBjYXByaW5vX2ltdV9neXJvLmNzdiwgNCBjb2x1bW5hczogdGllbXBvIFVUQyBlbiBtaWNyb3NlZ3VuZG9zLAp2ZWxvY2lkYWQgYW5ndWxhciBYIGVuIHJhZC9zLCB2ZWxvY2lkYWQgYW5ndWxhciBZIGVuIHJhZC9zLCB2ZWxvY2lkYWQKYW5ndWxhciBaIGVuIHJhZC9zLiBNdWVzdHJlYWRvIGEgMjAwIG11ZXN0cmFzIHBvciBzZWd1bmRvLgoKKiBjYXByaW5vX2dwcy5jc3YsIDQgY29sdW1uYXM6IHRpZW1wbyBVVEMgZW4gbWljcm9zZWd1bmRvcywgbGF0aXR1ZCBlbgpyYWRpYW5lcywgbG9uZ2l0dWQgZW4gcmFkaWFuZXMsIGFsdGl0dWQgZW4gbWV0cm9zLiBNdWVzdHJlYWRvIGEgNQptdWVzdHJhcyBwb3Igc2VndW5kby4KCiogY2Fwcmlub19ob3Vycy5jc3Y6IHRpZW1wbyBlbiBob3JhcywgbWludXRvcyB5IHNlZ3VuZG9zLiBNdWVzdHJlYWRvIGEKNSBtdWVzdHJhcyBwb3Igc2VndW5kby4gTGEgcHJpbWVyYSBob3JhIGRlIGVzdGUgYXJjaGl2byBjb2luY2lkZSBjb24KZWwgcHJpbWVyIHZhbG9yIGRlIHRpZW1wbyBVVEMgZGVsIGFyY2hpdm8gY2Fwcmlub19ncHMuY3N2LgoKCgojIyBDb25zdHJ1Y2Npb24gZGVsIERhdGFzZXQKCmBgYHtyfQpjYXByaW5vX2RhdGE8LWNiaW5kKGNhcHJpbm9faW11X2FjYyxjYXByaW5vX2ltdV9neXJvcyAlPiUgc2VsZWN0KC1WMSkpCgpjYXByaW5vX2RhdGE8LWNiaW5kKGNhcHJpbm9fZGF0YVtzZXEoMixucm93KGNhcHJpbm9fZGF0YSksMiksXSAlPiUgaGVhZChucm93KGNhcHJpbm9fZ3BzKS0xKSwgIGNhcHJpbm9fZ3BzWzI6bnJvdyhjYXByaW5vX2dwcyksXSApIApuYW1lcyhjYXByaW5vX2RhdGEpPC1jKCJpbXVfdHMiLAogICAgICAgICAgICAgICAgICAgICAgICJhY2NYIiwKICAgICAgICAgICAgICAgICAgICAgICAiYWNjWSIsCiAgICAgICAgICAgICAgICAgICAgICAgImFjY1oiLAogICAgICAgICAgICAgICAgICAgICAgICJneXJvWCIsCiAgICAgICAgICAgICAgICAgICAgICAgImd5cm9ZIiwKICAgICAgICAgICAgICAgICAgICAgICAiZ3lyb1oiLAogICAgICAgICAgICAgICAgICAgICAgICJncHNfaG91ciIsCiAgICAgICAgICAgICAgICAgICAgICAgImdwc190cyIsCiAgICAgICAgICAgICAgICAgICAgICAgImxhdCIsCiAgICAgICAgICAgICAgICAgICAgICAgImxvbmciLAogICAgICAgICAgICAgICAgICAgICAgICJhbHQiCiAgICAgICAgICAgICAgICAgICAgICAgICkKI2NhcHJpbm9fZGF0YSAlPiUgbXV0YXRlKGRpZmY9aW11X3RzLWdwc190cykgJT4lIHNlbGVjdChkaWZmKQpjYXByaW5vX2RhdGE8LWNhcHJpbm9fZGF0YSAlPiUgbXV0YXRlKGFjdGl2aXR5PWlmZWxzZShobXMoZ3BzX2hvdXIpPmhtcygiODo1NjowMCIpICYgaG1zKGdwc19ob3VyKTxobXMoIjEwOjM4OjAwIiksIkRjbyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGhtcyhncHNfaG91cik+PWhtcygiMTA6Mzg6MDAiKSAmIGhtcyhncHNfaG91cik8aG1zKCIxMTozODowMCIpLCJDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShobXMoZ3BzX2hvdXIpPj1obXMoIjExOjM4OjAwIikgJiBobXMoZ3BzX2hvdXIpPGhtcygiMTI6MDU6MDAiKSwiUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaG1zKGdwc19ob3VyKT49aG1zKCIxMjowNTowMCIpICYgaG1zKGdwc19ob3VyKTxobXMoIjE1OjQ4OjAwIiksIkRjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaG1zKGdwc19ob3VyKT49aG1zKCIxNzozMDowMCIpICYgaG1zKGdwc19ob3VyKTxobXMoIjIwOjMzOjAwIiksIlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGhtcyhncHNfaG91cik+PWhtcygiMjA6NTA6MDAiKSAmIGhtcyhncHNfaG91cik8aG1zKCIyMTozMTowMCIpLCJDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShobXMoZ3BzX2hvdXIpPj1obXMoIjIxOjMzOjAwIikgJiBobXMoZ3BzX2hvdXIpPGhtcygiMjE6NTA6MDAiKSwiRGNvIixOQSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICApCgpjYXByaW5vX2RhdGE8LWNhcHJpbm9fZGF0YSAlPiUgbXV0YXRlKGlkPWN1dChhcy5QT1NJWGN0KGhtcyhjYXByaW5vX2RhdGEkZ3BzX2hvdXIpLG9yaWdpbj0iMTk2MC0wMS0wMSIsdHo9IkdNVCIpLGJyZWFrcz0iMSBtaW4iKSkgCgpgYGAKICAKIyNOdW1lcm8gZGUgbWVkaWNpb25lcyBwb3IgdGlwbyBkZSBhY3RpdmlkYWQgIApOQSBoYWNlIHJlZmVyZW5jaWEgYSBhcXVlbGxhcyBtZWRpY2lvbmVzIHF1ZSBubyBmdWVyb24gZXRpcXVldGFkYXMKYGBge3J9CmNhcHJpbm9fZGF0YSAlPiUgZ3JvdXBfYnkoYWN0aXZpdHkpICU+JSBzdW1tYXJpc2Uobj1uKCkpCgpgYGAKCiMjIEdyYWZpY2EgZGUgbGEgZGlmZXJlbmNpYSBlbnRyZSBlbCB0aWVtcG8gZGUgbXVlc3RyZW8gZGUgbGEgSU1VIHkgR1BTLgpgYGB7cn0KICBnZ3Bsb3QoY2Fwcmlub19kYXRhJT4lIG11dGF0ZShkaWZmPWltdV90cyAtIGdwc190cykpKwogIGdlb21fbGluZShhZXMoeT1kaWZmLHg9c2VxKDEsbnJvdyhjYXByaW5vX2RhdGEpKSksY29sb3I9J2JsdWUnKSsKICB0aGVtZV9idygpCmBgYAoKIyMgR3JhZmljYSBkZWwgdHJheWVjdG8gZ2VvcmVmZXJlbmNpYWRvICBkaXNjcmltaW5hZG8gcG9yIGFjdGl2aWRhZCAKYGBge3IgZmlnLndpZHRoPTEyfQptemFfbWFwIDwtIGdldF9tYXAobG9jYXRpb24gPSBjKGxhdD0tMzIuMzQsIGxvbj0tNjcuOTA3NDcpLCB6b29tID0gMTQsIG1hcHR5cGUgPSAidGVycmFpbiIsIGNvbG9yID0gJ2J3JykKCmdnbWFwKG16YV9tYXApKyAKICAjc3RhdF9kZW5zaXR5MmQoYWVzKHk9IHJhZDJkZWcobGF0KSwgeD1yYWQyZGVnKGxvbmcpLGZpbGw9YWx0aXR1ZGVfZ3JvdXAsYWxwaGE9Li5sZXZlbC4uKSwgZ2VvbT0icG9seWdvbiIsICBiaW5zPTEwMDAsZGF0YT1jYXByaW5vX2RhdGFfYWx0aXR1ZGVfZ3JvdXApKwogCiAgZ2VvbV9wb2ludChhZXMoeT0gcmFkMmRlZyhsYXQpLCB4PXJhZDJkZWcobG9uZyksc2hhcGU9YWN0aXZpdHksY29sb3I9YWN0aXZpdHkpLHNpemU9MixkYXRhPWNhcHJpbm9fZGF0YSU+JSBzYW1wbGVfbigxMDAwMDApLGZpbGw9J2JsYWNrJykrCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDIiKQogIAoKI3FtcGxvdCh4ID1yYWQyZGVnKGxvbmcpLCB5PXJhZDJkZWcobGF0KSwgZGF0YSA9IGNhcHJpbm9fZGF0YSwgbWFwdHlwZSA9ICJ0b25lciIsIGNvbG9yID0gSSgicmVkIikpCgogIAogIAojbXphIDwtIGMobGVmdCA9IC02Ny45MjUsIGJvdHRvbSA9IC0zMi40MCwgcmlnaHQgPSAtNjcuOSwgdG9wID0tMzIuMjUpCiNtYXAgPC0gZ2V0X3N0YW1lbm1hcChiYm94PW16YSwgem9vbSA9IDE1LCBtYXB0eXBlID0gInRvbmVyLWxpdGUiLGNyb3AgPSBUKQojZ2dtYXAobWFwKSAgKwojICBnZW9tX3BvaW50KGFlcyh5PSByYWQyZGVnKGxhdCksIHg9cmFkMmRlZyhsb25nKSxzaGFwZT1hY3Rpdml0eSxjb2xvcj1hY3Rpdml0eSksc2l6ZT0yLGRhdGE9Y2Fwcmlub19kYXRhJT4lIHNhbXBsZV9uKDEwMDAwMCksZmlsbD0nYmxhY2snKSsKIyMgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikKIz9nZXRfc3RhbWVubWFwCiAgICAKYGBgCiMjIEdyYWZpY2EgZGVsIHRyYXllY3RvIGdlb3JlZmVyZW5jaWFkbyAgZGlzY3JpbWluYWRvIHBvciBhY3RpdmlkYWQKTGFzIHpvbmFzIGNvbiBtZW5vciBkZW5zaWRhZCBkZSBwdW50b3MgaW5kaWNhbiB1biBtZW5vciBsYXBzbyBkZSB0aWVtcG8gZW4gZXNhIHpvbmEuCmBgYHtyfQoKY2Fwcmlub19kYXRhICAgJT4lCmdncGxvdChhZXMoeD1sb25nLHk9bGF0KSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9YWN0aXZpdHkpLGFscGhhPTAuMDEsc2l6ZT0wLjgpKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTcGVjdHJhbCIpKwogIHRoZW1lX2J3KCkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTF9CmNhcHJpbm9fZGF0YV9hbHRpdHVkZV9ncm91cDwtY2Fwcmlub19kYXRhICU+JSBtdXRhdGUoYWx0aXR1ZGVfZ3JvdXAgPSBjdXQoYWx0LCBicmVha3MgPSBjKDIwMCwzNzgsNTI1LDUyNyw1MzAsNjUwLDgwMCw5MDApKSkgCgpnZ3Bsb3QoY2Fwcmlub19kYXRhX2FsdGl0dWRlX2dyb3VwLGFlcyh4PWxvbmcseT1sYXQpKSsKICBzdGF0X2RlbnNpdHkyZChhZXMoZmlsbD1hbHRpdHVkZV9ncm91cCxhbHBoYT0uLmxldmVsLi4pLCBnZW9tPSJwb2x5Z29uIiwgIGJpbnM9MTAwMCkgKwogIHRoZW1lX21pbmltYWwoKQogIApgYGAKCgojIyBEaXN0cmlidWNpw7NuIGRlIGxvcyByZXN1bHRhZG9zIGRlIGxvcyBzZW5zb3JlcyBlbiBjYWRhIHVuYSBkZSBsYXMgYWN0aXZpZGFkZXMgKHNpbiBvdXRsaWVycykKKiBTZSBvYnNlcnZhbiBkaWZlcmVuY2lhcyBlbiBsYSBkaXN0cmlidWNpb24gZGUgbGEgYWN0aXZpZGFkIFAgZW4gY29tcGFyYWNpb24gYSBsYXMgb3RyYXMgYWN0aXZpZGFkZXMuCiogTGFzIGFjdGl2aWRhZGVzICoqRGNhKiogeSAqKkRjbyoqIHByZXNlbnRhbiBzaW1pbHR1ZGVzIChsbyBjdWFsIGVzIGVzcGVyYWJsZSkuCiogTGEgYWN0aXZkYWQgKipQKiogcHJlc2VudGEgYWxndW5hcyBzaW1pbGl0dWRlcyBjb24gKipEY2EqKiB5ICoqRGNvKiosIGxvIHF1ZSBwdWVkZSBkaWZpY3VsdGFyIHN1IGRldGVjdGlvbi4KCmBgYHtyfQpjYXByaW5vX2RhdGFfbWVsdGVkPC1jYXByaW5vX2RhdGEgJT4lIHNlbGVjdChhY2NYLGFjY1ksYWNjWixneXJvWCxneXJvWSxneXJvWixhY3Rpdml0eSkgJT4lIHJlc2hhcGUyOjptZWx0KCkgCiAgZ2dwbG90KGNhcHJpbm9fZGF0YV9tZWx0ZWQpKwogIGdlb21fYm94cGxvdChhZXMoeD12YXJpYWJsZSxjb2xvcj12YXJpYWJsZSx5PXZhbHVlKSxvdXRsaWVyLnNpemU9MC4xLG91dGxpZXIuc2hhcGUgPSBOQSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTI1LDEwKSkrCiAgdGhlbWVfYncoKSsKdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkrCiAgZmFjZXRfd3JhcCh+YWN0aXZpdHkpIAogIApgYGAKIyBUb21hbW9zIG11ZXN0cmFzIGFsZWF0b3JpYXMgZGUgMSBtaW51dG8gZGUgZHVyYWNpb24gcGFyYSBzb2JyZSBsYXMgY3VhbGVzIGRlc3B1ZXMgcXVlcnJlbW9zIHByZWRlY2lyIGVsIGNvbXBvcnRhbWllbnRvCgpgYGB7cn0Kc2V0LnNlZWQoMjEwOTIwMTkpCm1pbnV0ZV9zbGljZTwtYXMudmVjdG9yKHQoY2Fwcmlub19kYXRhICU+JSBncm91cF9ieShpZCkgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lIHNlbGVjdChpZCkpKQp0cmFpbmlkeDwtc2FtcGxlKDE6bGVuZ3RoKG1pbnV0ZV9zbGljZSksKGxlbmd0aChtaW51dGVfc2xpY2UpKjcwKS8xMDAuMCkKZGF0YV90cmFpbjwtY2Fwcmlub19kYXRhICU+JSBmaWx0ZXIoIGlkICVpbiUgbWludXRlX3NsaWNlW3RyYWluaWR4XSkKZGF0YV90ZXN0PC1jYXByaW5vX2RhdGEgJT4lIGZpbHRlciggaWQgJWluJSAobWludXRlX3NsaWNlWy10cmFpbmlkeF0pKQpkYXRhX3Rlc3QlPiUgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2Uobj1uKCkpCmBgYAoKIyBBcGxpY2Ftb3MgQmFnLW9mLWZlYXR1cmVzLgojIyBwcmVzdXB1ZXN0bwoKIENvbnNpZGVyYW1vcyBjYWRhIG11ZXN0cmEgY29tbyB1biB2ZWN0b3IgZGUgNSBkaW1lbnNpb25lcy4gTGEgaWRlYSBlcyBhcGxpY2FyIHVuIGFsZ29yaXRtbyBkZSBjbHVzdGVyaW5nIHNvYnJlIGVzdG9zIHZlY3RvcmVzIChhY2NYLGFjY1ksYWNjWixHeXJvWCxneXJvWSkuIFVuYSB2ZXogZW5jb250cmFkbyBsb3MgY2VudHJvaWRlcywgcGFyYSBjYWRhIG11ZXN0cmEgZGUgMSBzZSBjb25zdHJ1eWUgdW4gaGlzdG9ncmFtYSBkZSBsb3MgdmVjdG9yZXMgcXVlIGhhbiBjYWlkbyBlbiBjYWRhIHVubyBkZSBlc3RvcyBjbHVzdGVycy4gRXN0ZSBoaXN0b2dyYW1hIGVzIGVsIHF1ZSBzZSB1dGlsaXphIHBhcmEgZW50cmVnYXIgZWwgYWxnb3JpdG1vLgoKTGEgaGlwb3Rlc2lzIGRlIGFwbGljYXIgZXN0ZSBtZXRvZG8gZXMgcXVlIGFxdWVsbGFzIG11ZXN0cmFzIHF1ZSBwZXJ0ZW5lY2VuIGEgdW5hIG1pc21hIGFjdGl2aWRhZCB0ZW5kcmFuIHZhbG9yZXMgc2ltaWxhcmVzLiBMb3MgY3VhbGVzIGhhbiBzaWRvIHJlcHJlc2VudGFkbyBtZWRpYW50ZXMgZXN0b3MgaGlzdG9ncmFtYXMuIAoKIyMgQXBsaWNhbW9zIGstbWVhbnMgY29uIDEyIGNlbnRyb2lkZXMgc29icmUgZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50by4KCmBgYHtyfQoKY2x1c3Rlcl9yZXN1bHRzPC1rbWVhbnMoeCA9IGRhdGFfdHJhaW4lPiUgc2VsZWN0KGFjY1gsYWNjWSxhY2NaLGd5cm9YLGd5cm9ZKSwgY2VudGVycyA9IDI0LCBuc3RhcnQgPSA1KQpkYXRhX3RyYWluX2NsdXN0ZXI8LWNiaW5kKGRhdGFfdHJhaW4sY2x1c3Rlcj1jbHVzdGVyX3Jlc3VsdHMkY2x1c3RlcikgJT4lIGdyb3VwX2J5KGFjdGl2aXR5LGNsdXN0ZXIpCmRhdGFfdHJhaW5fY2x1c3RlciAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbChhZXMoeD1jbHVzdGVyLHk9biksZmlsbD0nc2t5Ymx1ZScpKwogIGZhY2V0X3dyYXAofmFjdGl2aXR5KSsKICB0aGVtZV9idygpCiAgCmBgYAoKIyMgQXBsaWNhbW9zIGstbWVhbnMgc29icmUgZWwgY29uanVudG8gZGUgdGVzdC4gU2UgdXNhbiBsb3MgbWlzbW9zIGNlbnRyb2lkZXMgZW5jb250cmFkb3MgZW4gdHJhaW5pbmcKYGBge3J9CmNsdXN0ZXJfcmVzdWx0c190ZXN0PC1rbWVhbnMoeCA9IGRhdGFfdGVzdCU+JSBzZWxlY3QoYWNjWCxhY2NZLGFjY1osZ3lyb1gsZ3lyb1kpLCBjZW50ZXJzID0gY2x1c3Rlcl9yZXN1bHRzJGNlbnRlcnMpCmRhdGFfdGVzdF9jbHVzdGVyPC1jYmluZChkYXRhX3Rlc3QsY2x1c3Rlcj1jbHVzdGVyX3Jlc3VsdHNfdGVzdCRjbHVzdGVyKSAlPiUgZ3JvdXBfYnkoYWN0aXZpdHksY2x1c3RlcikKZGF0YV90ZXN0X2NsdXN0ZXIgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgZ2dwbG90KCkrCiAgZ2VvbV9jb2woYWVzKHg9Y2x1c3Rlcix5PW4pLGZpbGw9J29yYW5nZScpKwogIGZhY2V0X3dyYXAofmFjdGl2aXR5KSsKICB0aGVtZV9idygpCgpgYGAKCiMjIFByZXBhcmFuZG8gZWwgZGF0YXNldCBwYXJhIGFwbGljYXIgdW4gY2xhc2lmaWNhZG9yClRvbWFtb3MgaW50ZXJ2YWxvcyBkZSAxbSB5IGNyZWFtb3MgbG9zIGF0cmlidXRvcyB1c2FuZG8gYmFnLW9mLWZlYXR1cmVzCmBgYHtyIGZpZy5oZWlnaHQ9MTJ9CgoKc2FtcGxlX21pbnV0ZV9zbGljZTwtbWludXRlX3NsaWNlW3NhbXBsZSgxOmxlbmd0aChtaW51dGVfc2xpY2UpLDQwKV0KZGF0YV90cmFpbl9jbHVzdGVyICU+JSBmaWx0ZXIgKGlkICVpbiUgc2FtcGxlX21pbnV0ZV9zbGljZSkgICU+JSBncm91cF9ieShhY3Rpdml0eSxpZCxjbHVzdGVyKSAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbChhZXMoeD1jbHVzdGVyLHk9bixmaWxsPWFjdGl2aXR5KSkrCiAgZmFjZXRfd3JhcCh+aWQpKwogIHRoZW1lX2J3KCkKCmBgYApgYGB7cn0KCmRhdGFfdHJhaW5fY2x1c3Rlcl9sYWJlbGVkPC1kYXRhX3RyYWluX2NsdXN0ZXIgICAlPiUgZ3JvdXBfYnkoaWQsY2x1c3RlcikgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lIHJlc2hhcGUyOjpkY2FzdChpZH5jbHVzdGVyLGZpbGwgPSAwKSAlPiUgaW5uZXJfam9pbihkYXRhX3RyYWluX2NsdXN0ZXIlPiUgdW5ncm91cCgpICU+JSBzZWxlY3QoaWQsYWN0aXZpdHkpICAlPiUgdW5pcXVlKCkgICxieT0iaWQiKSAlPiUgZmlsdGVyKCFpcy5uYShhY3Rpdml0eSkpCgoKCmRhdGFfdGVzdF9jbHVzdGVyX2xhYmVsZWQ8LWRhdGFfdGVzdF9jbHVzdGVyICAgJT4lIGdyb3VwX2J5KGlkLGNsdXN0ZXIpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSByZXNoYXBlMjo6ZGNhc3QoaWR+Y2x1c3RlcixmaWxsID0gMCkgJT4lIGlubmVyX2pvaW4oZGF0YV90ZXN0X2NsdXN0ZXIlPiUgdW5ncm91cCgpICU+JSBzZWxlY3QoaWQsYWN0aXZpdHkpICAlPiUgdW5pcXVlKCkgLGJ5PSJpZCIpIApgYGAKIyMgUHJvYmFtb3MgUmFuZG9tIEZvcmVzdApBcGxpY2Ftb3MgMng1IENWIHBhcmEgZWxlZ2lyIGxhIG1lam9yIHZlcnNpb24gZGVsIG1vZGVsbwpgYGB7cn0KbGlicmFyeShjYXJldCkKbGlicmFyeShkb01DKQpyZWdpc3RlckRvTUMoY29yZXM9NikKY3RybF9mYXN0IDwtIHRyYWluQ29udHJvbChtZXRob2Q9InJlcGVhdGVkY3YiLCAKICAgICAgICAgICAgICAgICAgICAgcmVwZWF0cz0yLAogICAgICAgICAgICAgICAgICAgICBudW1iZXI9NSwgCiAgICAgICAgICAgICAgICAgICAgICNzdW1tYXJ5RnVuY3Rpb249dHdvQ2xhc3NTdW1tYXJ5LAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlSXRlcj1GLAogICAgICAgICAgICAgICAgICAgICAjcHJlUHJvY09wdGlvbnMgPSBsaXN0KHBjYUNvbXAgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnM9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgYWxsb3dQYXJhbGxlbCA9IFQpICAKCm5uR3JpZCA8LSAgZXhwYW5kLmdyaWQoZGVjYXkgPSBjKDApLCBzaXplPWMoMTAsMjAsNTAsMTAwLDIwMCwzMDApKSAKc3ZtR3JpZCA8LSAgZXhwYW5kLmdyaWQoc2lnbWEgPSBjKDAuMDAxLDAuMDAwMDEpLCBDPWMoMC4xLDAuMDAxLDAuNSkpIAoKa2VyYXNHcmlkIDwtIGV4cGFuZC5ncmlkKAogIHNpemU9YygyMCw1MCwyMDAwLDUwMDApLAogIGxhbWJkYT1jKDApLAogIGJhdGNoX3NpemU9YygxMDI0KSwKICBkZWNheT1jKDApLAogIGFjdGl2YXRpb249YygndGFuaCcsJ3NpZ21vaWQnLCdyZWx1JyksCiAgbHI9YygwLjAwMSksIHJobz1jKDAuOSkKKQp0cmFpbl9mb3JtdWxhPWZvcm11bGEoImFjdGl2aXR5fi4iKQojY3RybF9mYXN0JHNhbXBsaW5nPC0ic21vdGUiCnJmRml0IDwtIGNhcmV0Ojp0cmFpbih0cmFpbl9mb3JtdWxhLAogICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbl9jbHVzdGVyX2xhYmVsZWQgJT4lIHNlbGVjdCgtaWQpLAogICAgICAgICAgICAgICAjbWV0cmljPSJST0MiLAogICAgICAgICAgICAgICAjcHJlUHJvY2Vzcz1jKCJwY2EiKSwKICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICNtZXRob2QgPSAibWxwV2VpZ2h0RGVjYXkiLAogICAgICAgICAgICAgICAjbWV0aG9kID0gInN2bVJhZGlhbCIsCiAgICAgICAgICAgICAgICNtZXRob2QgPSJtbHBLZXJhc0RlY2F5IiwKICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIiwKICAgICAgICAgICAgICAgI3R1bmVHcmlkPWtlcmFzR3JpZCwKICAgICAgICAgICAgICAgdHVuZUxlbmd0aD0xMSwKICAgICAgICAgICAgICAgdmVyYm9zZT0wLAogICAgICAgICAgICAgICAjZXBvY2hzPTEwMCwKICAgICAgICAgICAgICAgdHJDb250cm9sID0gY3RybF9mYXN0KQoKYGBgCiMjIG1vZGVsb3Mgb2J0ZW5pZG9zCmBgYHtyfQpyZkZpdApgYGAKCiMjIEV2YWx1YWNpb24gc29icmUgZWwgY29uanVudG8gZGUgdGVzdApNYXRyaXogZGUgQ29uZnVzaW9uCmBgYHtyfQpkYXRhX3Rlc3RfcHJlZGljdGlvbjwtcHJlZGljdChyZkZpdCxkYXRhX3Rlc3RfY2x1c3Rlcl9sYWJlbGVkICU+JSBzZWxlY3QoLWlkKSkjLHR5cGU9InByb2IiKQpjYXJldDo6Y29uZnVzaW9uTWF0cml4KGRhdGFfdGVzdF9wcmVkaWN0aW9uLGFzLmZhY3RvcihkYXRhX3Rlc3RfY2x1c3Rlcl9sYWJlbGVkJGFjdGl2aXR5KSkKYGBgCgpgYGB7cn0KCnRhYmxlKGRhdGFfdGVzdF9wcmVkaWN0aW9uLGFzLmZhY3RvcihkYXRhX3Rlc3RfY2x1c3Rlcl9sYWJlbGVkJGFjdGl2aXR5KSkKCnJlc2hhcGUyOjptZWx0KHRhYmxlKGRhdGFfdGVzdF9wcmVkaWN0aW9uLGFzLmZhY3RvcihkYXRhX3Rlc3RfY2x1c3Rlcl9sYWJlbGVkJGFjdGl2aXR5KSkpICU+JQogIGdncGxvdChhZXMoeD1kYXRhX3Rlc3RfcHJlZGljdGlvbix5PVZhcjIpKSsKICBnZW9tX3RpbGUoYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXIgPSAid2hpdGUiKSArIAogICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJTEuMGYiLCB2YWx1ZSkpLCB2anVzdCA9IDEpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIpKwogIHhsYWIoIiBQcmVkaWN0ZWQgQWN0aXZpdHkgIikreWxhYigiIEFjdHVhbCBBY3Rpdml0eSIpKwogIHRoZW1lX2J3KCkrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojQ09OQ0xVU0lPTkVTCg==