Update reticulate package

#update.packages("reticulate") 
#devtools::install_github("rstudio/reticulate")

tfruns: Track and Visualize Training Runs

The tfruns package provides a suite of tools for tracking, visualizing, and managing TensorFlow training runs and experiments from R:

Track the hyperparameters, metrics, output, and source code of every training run.

Compare hyperparmaeters and metrics across runs to find the best performing model.

Automatically generate reports to visualize individual training runs or comparisons between runs.

No changes to source code required (run data is automatically captured for all Keras and TF Estimator models).

#install.packages("tfruns")
library(tfruns)
#devtools::install_github("rstudio/tfestimators")
library(tensorflow)
library(tidyverse)
library(tfestimators)
library(data.table)
library(mlr)
library(reticulate)

The estimators availavle in tfestimator package is listed below.

library(knitr)
knitr::include_graphics('/Users/nanaakwasiabayieboateng/Documents/memphisclassesbooks/DataMiningscience/Deeplearning/tfestimator.png')

The data is available at the UCI machine learning repository here .

url<-"http://archive.ics.uci.edu/ml/machine-learning-databases/pima-indians-diabetes/pima-indians-diabetes.data"
data<-fread(url)
trying URL 'http://archive.ics.uci.edu/ml/machine-learning-databases/pima-indians-diabetes/pima-indians-diabetes.data'
Content type 'text/plain; charset=UTF-8' length 23279 bytes (22 KB)
==================================================
downloaded 22 KB
#read_csv(url)
colnames(data)<-c("preg","plas","pres","skin","test","mass","pedi","age","class")
data%>%glimpse()
Observations: 768
Variables: 9
$ preg  <int> 6, 1, 8, 1, 0, 5, 3, 10, 2, 8, 4, 10, 10, 1, 5, 7, 0...
$ plas  <int> 148, 85, 183, 89, 137, 116, 78, 115, 197, 125, 110, ...
$ pres  <int> 72, 66, 64, 66, 40, 74, 50, 0, 70, 96, 92, 74, 80, 6...
$ skin  <int> 35, 29, 0, 23, 35, 0, 32, 0, 45, 0, 0, 0, 0, 23, 19,...
$ test  <int> 0, 0, 0, 94, 168, 0, 88, 0, 543, 0, 0, 0, 0, 846, 17...
$ mass  <dbl> 33.6, 26.6, 23.3, 28.1, 43.1, 25.6, 31.0, 35.3, 30.5...
$ pedi  <dbl> 0.627, 0.351, 0.672, 0.167, 2.288, 0.201, 0.248, 0.1...
$ age   <int> 50, 31, 32, 21, 33, 30, 26, 29, 53, 54, 30, 34, 57, ...
$ class <int> 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1...

Description of Data

  1. Title: Pima Indians Diabetes Database

  2. Sources: National Institute of Diabetes and Digestive and Kidney Diseases

The diagnostic, binary-valued variable investigated is whether the patient shows signs of diabetes according to World Health Organization criteria (i.e., if the 2 hour post-load plasma glucose was at least 200 mg/dl at any survey examination or if found during routine medical care). The population lives near Phoenix, Arizona, USA.

Results: Their ADAP algorithm makes a real-valued prediction between 0 and 1. This was transformed into a binary decision using a cutoff of 0.448. Using 576 training instances, the sensitivity and specificity of their algorithm was 76% on the remaining 192 instances.

  1. Relevant Information: Several constraints were placed on the selection of these instances from a larger database. In particular, all patients here are females at least 21 years old of Pima Indian heritage. ADAP is an adaptive learning routine that generates and executes digital analogs of perceptron-like devices. It is a unique algorithm; see the paper for details.

  2. Number of Instances: 768

  3. Number of Attributes: 8 plus class

  4. For Each Attribute: (all numeric-valued)
  5. Number of times pregnant
  6. Plasma glucose concentration a 2 hours in an oral glucose tolerance test
  7. Diastolic blood pressure (mm Hg)
  8. Triceps skin fold thickness (mm)
  9. 2-Hour serum insulin (mu U/ml)
  10. Body mass index (weight in kg/(height in m)^2)
  11. Diabetes pedigree function
  12. Age (years)
  13. Class variable (0 or 1)

  14. Missing Attribute Values: Yes

  15. Class Distribution: (class value 1 is interpreted as “tested positive for diabetes”)

Class Value Number of instances 0 500 1 268

The dataset contains no missing values.

colSums(is.na(data)) 
 preg  plas  pres  skin  test  mass  pedi   age class 
    0     0     0     0     0     0     0     0     0 
summarizeColumns(data)

Split the data into training and test datasets.

# Split the data into training and test data sets
index <- sample(1:nrow(data), size = 0.80 * nrow(data))
train <- data[index, ]
test  <- data[-index, ]

INPUT FUNCTION

Estimators can accept data from arbitrary data sources through an ‘input function’. The input function selects feature and response columns from the input source as well as defines how data will be drawn (e.g. batch size, epochs, etc.). The tfestimators package provides the input_fn() helper function for generating input functions from common R data structures, e.g. R matrices and data frames.

# return an input_fn for a given subset of data
data_input_fn <- function(data, num_epochs = 1) {
  input_fn(data, 
           features = c("preg","plas","pres","skin","test","mass","pedi","age"), 
           response = "class",
           batch_size = 32,
           num_epochs = num_epochs)
}

FEATURE COLUMNS

Next, we define the feature columns for our model. Feature columns are mappings of raw input data to the data that we’ll actually feed into our training, evaluation, and prediction steps. We can transform variables to categorical variables for the analysis. We can do this by using thetfestimators::feature_columns() function to get the data into the shape expected for an input Tensor. Category levels are set by passing a list to the vocabulary_list argument.For example to convert variable with two levels we can use column_categorical_with_vocabulary_list(“variable”, vocabulary_list = list(“level 1”, “level 2”))

cols <- feature_columns(
  
  column_numeric("preg","plas","pres","skin","test","mass","pedi","age")
)

ESTIMATOR

Next, we create the estimator by calling the linear_classifier() function and passing it a set of feature columns:

model <- linear_classifier(feature_columns = cols)

We train the model afterwards.

# train the model
model %>% train(data_input_fn(train, num_epochs = 10))

[-] Training -- loss: 22.18, step: 1
[\] Training -- loss: 898.94, step: 2
                                                                       
[|] Training -- loss: 26.33, step: 3
[/] Training -- loss: 112.03, step: 4
[-] Training -- loss: 238.68, step: 5
[\] Training -- loss: 549.02, step: 6
[|] Training -- loss: 255.83, step: 7
[/] Training -- loss: 127.88, step: 8
                                                                       
[-] Training -- loss: 90.98, step: 9
[\] Training -- loss: 177.39, step: 10
[|] Training -- loss: 263.25, step: 11
[/] Training -- loss: 181.84, step: 12
[-] Training -- loss: 116.20, step: 13
[\] Training -- loss: 272.25, step: 14
                                                                       
[|] Training -- loss: 29.15, step: 15
[/] Training -- loss: 45.41, step: 16
[-] Training -- loss: 117.46, step: 17
                                                                       
[\] Training -- loss: 41.48, step: 18
[|] Training -- loss: 159.80, step: 19
[/] Training -- loss: 113.04, step: 20
[-] Training -- loss: 142.92, step: 21
[\] Training -- loss: 248.46, step: 22
                                                                       
[|] Training -- loss: 85.69, step: 23
[/] Training -- loss: 216.56, step: 24
                                                                       
[-] Training -- loss: 68.48, step: 25
[\] Training -- loss: 52.60, step: 26
[|] Training -- loss: 82.04, step: 27
[/] Training -- loss: 184.77, step: 28
                                                                       
[-] Training -- loss: 84.66, step: 29
[\] Training -- loss: 56.09, step: 30
[|] Training -- loss: 62.31, step: 31
[/] Training -- loss: 94.20, step: 32
[-] Training -- loss: 51.16, step: 33
[\] Training -- loss: 58.38, step: 34
[|] Training -- loss: 112.49, step: 35
[/] Training -- loss: 138.90, step: 36
                                                                       
[-] Training -- loss: 48.63, step: 37
[\] Training -- loss: 52.38, step: 38
[|] Training -- loss: 23.78, step: 39
[/] Training -- loss: 81.91, step: 40
[-] Training -- loss: 117.72, step: 41
                                                                       
[\] Training -- loss: 34.53, step: 42
[|] Training -- loss: 52.25, step: 43
[/] Training -- loss: 103.42, step: 44
                                                                       
[-] Training -- loss: 64.43, step: 45
[\] Training -- loss: 40.75, step: 46
[|] Training -- loss: 73.24, step: 47
[/] Training -- loss: 72.88, step: 48
[-] Training -- loss: 141.48, step: 49
                                                                       
[\] Training -- loss: 69.24, step: 50
[|] Training -- loss: 81.84, step: 51
[/] Training -- loss: 76.14, step: 52
[-] Training -- loss: 50.69, step: 53
[\] Training -- loss: 26.84, step: 54
[|] Training -- loss: 80.43, step: 55
[/] Training -- loss: 111.80, step: 56
[-] Training -- loss: 114.20, step: 57
                                                                       
[\] Training -- loss: 49.81, step: 58
[|] Training -- loss: 40.30, step: 59
[/] Training -- loss: 47.10, step: 60
[-] Training -- loss: 79.76, step: 61
[\] Training -- loss: 118.48, step: 62
                                                                       
[|] Training -- loss: 73.83, step: 63
[/] Training -- loss: 20.61, step: 64
[-] Training -- loss: 32.32, step: 65
[\] Training -- loss: 76.05, step: 66
[|] Training -- loss: 38.57, step: 67
[/] Training -- loss: 67.98, step: 68
[-] Training -- loss: 30.62, step: 69
[\] Training -- loss: 30.51, step: 70
[|] Training -- loss: 25.79, step: 71
[/] Training -- loss: 21.09, step: 72
[-] Training -- loss: 39.48, step: 73
[\] Training -- loss: 38.96, step: 74
[|] Training -- loss: 30.10, step: 75
[/] Training -- loss: 33.58, step: 76
[-] Training -- loss: 44.39, step: 77
[\] Training -- loss: 121.28, step: 78
                                                                       
[|] Training -- loss: 89.70, step: 79
[/] Training -- loss: 59.57, step: 80
[-] Training -- loss: 62.29, step: 81
[\] Training -- loss: 30.87, step: 82
[|] Training -- loss: 56.52, step: 83
[/] Training -- loss: 32.06, step: 84
[-] Training -- loss: 60.52, step: 85
[\] Training -- loss: 99.41, step: 86
[|] Training -- loss: 27.82, step: 87
[/] Training -- loss: 52.77, step: 88
[-] Training -- loss: 49.96, step: 89
[\] Training -- loss: 72.59, step: 90
[|] Training -- loss: 67.14, step: 91
[/] Training -- loss: 33.29, step: 92
[-] Training -- loss: 27.95, step: 93
[\] Training -- loss: 17.61, step: 94
[|] Training -- loss: 20.99, step: 95
[/] Training -- loss: 27.35, step: 96
[-] Training -- loss: 19.51, step: 97
[\] Training -- loss: 39.69, step: 98
[|] Training -- loss: 25.91, step: 99
[/] Training -- loss: 25.87, step: 100
[-] Training -- loss: 23.38, step: 101
[\] Training -- loss: 25.72, step: 102
[|] Training -- loss: 49.72, step: 103
[/] Training -- loss: 19.75, step: 104
[-] Training -- loss: 19.75, step: 105
[\] Training -- loss: 37.84, step: 106
[|] Training -- loss: 83.40, step: 107
[/] Training -- loss: 104.30, step: 108
                                                                       
[-] Training -- loss: 59.79, step: 109
[\] Training -- loss: 24.61, step: 110
[|] Training -- loss: 28.32, step: 111
[/] Training -- loss: 35.22, step: 112
[-] Training -- loss: 66.99, step: 113
[\] Training -- loss: 49.32, step: 114
[|] Training -- loss: 27.45, step: 115
[/] Training -- loss: 34.46, step: 116
[-] Training -- loss: 53.99, step: 117
[\] Training -- loss: 30.21, step: 118
[|] Training -- loss: 59.44, step: 119
[/] Training -- loss: 23.67, step: 120
[-] Training -- loss: 17.16, step: 121
[\] Training -- loss: 15.45, step: 122
[|] Training -- loss: 43.75, step: 123
[/] Training -- loss: 135.51, step: 124
                                                                       
[-] Training -- loss: 58.39, step: 125
[\] Training -- loss: 35.61, step: 126
[|] Training -- loss: 22.55, step: 127
[/] Training -- loss: 25.20, step: 128
[-] Training -- loss: 35.48, step: 129
[\] Training -- loss: 27.58, step: 130
[|] Training -- loss: 58.30, step: 131
[/] Training -- loss: 49.04, step: 132
[-] Training -- loss: 28.12, step: 133
[\] Training -- loss: 40.11, step: 134
[|] Training -- loss: 29.50, step: 135
[/] Training -- loss: 31.44, step: 136
[-] Training -- loss: 43.53, step: 137
[\] Training -- loss: 51.67, step: 138
[|] Training -- loss: 42.87, step: 139
[/] Training -- loss: 34.58, step: 140
[-] Training -- loss: 39.75, step: 141
[\] Training -- loss: 31.81, step: 142
[|] Training -- loss: 24.74, step: 143
[/] Training -- loss: 25.95, step: 144
[-] Training -- loss: 21.55, step: 145
[\] Training -- loss: 25.45, step: 146
[|] Training -- loss: 39.19, step: 147
[/] Training -- loss: 23.43, step: 148
[-] Training -- loss: 47.57, step: 149
[\] Training -- loss: 50.52, step: 150
[|] Training -- loss: 94.98, step: 151
[/] Training -- loss: 40.20, step: 152
[-] Training -- loss: 20.37, step: 153
[\] Training -- loss: 21.75, step: 154
[|] Training -- loss: 30.24, step: 155
[/] Training -- loss: 42.65, step: 156
[-] Training -- loss: 23.87, step: 157
[\] Training -- loss: 26.00, step: 158
[|] Training -- loss: 25.41, step: 159
[/] Training -- loss: 52.50, step: 160
[-] Training -- loss: 24.05, step: 161
[\] Training -- loss: 18.57, step: 162
[|] Training -- loss: 27.64, step: 163
[/] Training -- loss: 35.61, step: 164
[-] Training -- loss: 19.14, step: 165
[\] Training -- loss: 20.32, step: 166
[|] Training -- loss: 19.01, step: 167
[/] Training -- loss: 24.10, step: 168
[-] Training -- loss: 33.67, step: 169
[\] Training -- loss: 59.86, step: 170
[|] Training -- loss: 70.62, step: 171
[/] Training -- loss: 29.50, step: 172
[-] Training -- loss: 29.61, step: 173
[\] Training -- loss: 25.99, step: 174
[|] Training -- loss: 28.93, step: 175
[/] Training -- loss: 62.65, step: 176
[-] Training -- loss: 53.62, step: 177
[\] Training -- loss: 35.48, step: 178
[|] Training -- loss: 17.98, step: 179
[/] Training -- loss: 28.27, step: 180
[-] Training -- loss: 34.04, step: 181
[\] Training -- loss: 27.57, step: 182
[|] Training -- loss: 26.55, step: 183
[/] Training -- loss: 21.56, step: 184
[-] Training -- loss: 26.21, step: 185
[\] Training -- loss: 27.46, step: 186
[|] Training -- loss: 27.01, step: 187
[/] Training -- loss: 35.04, step: 188
[-] Training -- loss: 32.12, step: 189
[\] Training -- loss: 67.88, step: 190
[|] Training -- loss: 19.98, step: 191
[/] Training -- loss: 26.74, step: 192

EVALUATION

We can evaluate the model’s accuracy using the evaluate() function, using our ‘test’ data set for validation.

model %>% evaluate(data_input_fn(test), simplify = TRUE)
WARNING:tensorflow:Casting <dtype: 'float32'> labels to bool.
WARNING:tensorflow:Casting <dtype: 'float32'> labels to bool.

[-] Evaluating -- loss: 36.51, step: 1
[\] Evaluating -- loss: 37.55, step: 2
[|] Evaluating -- loss: 29.75, step: 3
[/] Evaluating -- loss: 34.83, step: 4
[-] Evaluating -- loss: 31.47, step: 5

PREDICTION

After we’ve finished training our model, we can use it to generate predictions from new data.

#model %>% predict(data_input_fn(test), simplify = FALSE)
predictions=model %>% predict(data_input_fn(test), simplify = TRUE)
predictions%>%head()
glimpse(predictions)
Observations: 154
Variables: 5
$ logits        <list> [0.1383194, 3.272619, 1.162019, 6.597319, 6...
$ logistic      <list> [0.5345248, 0.9634774, 0.7616994, 0.9986379...
$ probabilities <list> [<0.4654752, 0.5345248>, <0.03652255, 0.963...
$ class_ids     <list> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, ...
$ classes       <list> ["1", "1", "1", "1", "1", "1", "1", "1", "1...
#simplify_fn <- function(predictions) {
 # lapply(predictions, function(x) x$probabilities)
#}
#model %>% predict(data_input_fn(test), simplify = simplify_fn)

MODEL PERSISTENCE

Models created via tfestimators are persisted on disk. To obtain the location of where the model artifacts are stored, we can call model_dir():

saved_model_dir <- model_dir(model)

And subsequently load the saved model (in a new session) by passing the directory to the model_dir argument of the model constructor and use it for prediction or continue training:

cols <- feature_columns( 
  column_numeric("preg","plas","pres","skin","test","mass","pedi","age")
)
loaded_model <- linear_classifier(feature_columns = cols,
                                 model_dir = saved_model_dir)
loaded_model
A TensorFlow classifier [<tensorflow.python.estimator.canned.linear.LinearClassifier>]
Model Directory: /var/folders/mj/w1gxzjcd0qx2cw_0690z7y640000gn/T/tmpnl5hwxo3
Model has been trained for 192 steps.
LS0tCnRpdGxlOiAiQnVpbGRpbmcgTGluZWFyIENsYXNzaWZpZXIgd2l0aCB0ZmVzdGltYXRvciAiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IE5hbmEgQm9hdGVuZwpkZl9wcmludDogcGFnZWQKVGltZTogJ2ByIFN5cy50aW1lKClgJwpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCi0tLQoKCgoKYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0KCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIG91dC53aWR0aCA9IjEwMCUiLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2RlZmF1bHQnLCAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICBmaWcuY2FwID0iRmlnLiAzMCIsIAogICAgICAgICAgICAgICAgICAgICAgb3V0LndpZHRoPSIxMDAlIikKCm9wdGlvbnMocmVwci5wbG90LmhlaWdodCA9IDUsIHJlcHIucGxvdC53aWR0aCA9IDYpCm9wdGlvbnModGlkeXZlcnNlLnF1aWV0ID0gVFJVRSkKCmBgYAoKCgoKClVwZGF0ZSByZXRpY3VsYXRlIHBhY2thZ2UKCmBgYHtyfQojdXBkYXRlLnBhY2thZ2VzKCJyZXRpY3VsYXRlIikgCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInJzdHVkaW8vcmV0aWN1bGF0ZSIpCmBgYAoKCiMjIyMgdGZydW5zOiBUcmFjayBhbmQgVmlzdWFsaXplIFRyYWluaW5nIFJ1bnMKCgpUaGUgdGZydW5zIHBhY2thZ2UgcHJvdmlkZXMgYSBzdWl0ZSBvZiB0b29scyBmb3IgdHJhY2tpbmcsIHZpc3VhbGl6aW5nLCBhbmQgbWFuYWdpbmcgVGVuc29yRmxvdyB0cmFpbmluZyBydW5zIGFuZCBleHBlcmltZW50cyBmcm9tIFI6CgpUcmFjayB0aGUgaHlwZXJwYXJhbWV0ZXJzLCBtZXRyaWNzLCBvdXRwdXQsIGFuZCBzb3VyY2UgY29kZSBvZiBldmVyeSB0cmFpbmluZyBydW4uCgpDb21wYXJlIGh5cGVycGFybWFldGVycyBhbmQgbWV0cmljcyBhY3Jvc3MgcnVucyB0byBmaW5kIHRoZSBiZXN0IHBlcmZvcm1pbmcgbW9kZWwuCgpBdXRvbWF0aWNhbGx5IGdlbmVyYXRlIHJlcG9ydHMgdG8gdmlzdWFsaXplIGluZGl2aWR1YWwgdHJhaW5pbmcgcnVucyBvciBjb21wYXJpc29ucyBiZXR3ZWVuIHJ1bnMuCgpObyBjaGFuZ2VzIHRvIHNvdXJjZSBjb2RlIHJlcXVpcmVkIChydW4gZGF0YSBpcyBhdXRvbWF0aWNhbGx5IGNhcHR1cmVkIGZvciBhbGwgS2VyYXMgYW5kIFRGIEVzdGltYXRvciBtb2RlbHMpLgoKCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoInRmcnVucyIpCmxpYnJhcnkodGZydW5zKQpgYGAKCgoKCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigicnN0dWRpby90ZmVzdGltYXRvcnMiKQpsaWJyYXJ5KHRlbnNvcmZsb3cpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRmZXN0aW1hdG9ycykKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KG1scikKbGlicmFyeShyZXRpY3VsYXRlKQpgYGAKClRoZSBlc3RpbWF0b3JzIGF2YWlsYXZsZSBpbiB0ZmVzdGltYXRvciBwYWNrYWdlIGlzIGxpc3RlZCBiZWxvdy4KCmBgYHtyfQpsaWJyYXJ5KGtuaXRyKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnL1VzZXJzL25hbmFha3dhc2lhYmF5aWVib2F0ZW5nL0RvY3VtZW50cy9tZW1waGlzY2xhc3Nlc2Jvb2tzL0RhdGFNaW5pbmdzY2llbmNlL0RlZXBsZWFybmluZy90ZmVzdGltYXRvci5wbmcnKQpgYGAKClRoZSBkYXRhIGlzIGF2YWlsYWJsZSBhdCB0aGUgVUNJIG1hY2hpbmUgbGVhcm5pbmcgcmVwb3NpdG9yeSBbaGVyZV0oaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvcGltYS1pbmRpYW5zLWRpYWJldGVzL3BpbWEtaW5kaWFucy1kaWFiZXRlcy5kYXRhKSAuCgpgYGB7cn0KdXJsPC0iaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvcGltYS1pbmRpYW5zLWRpYWJldGVzL3BpbWEtaW5kaWFucy1kaWFiZXRlcy5kYXRhIgoKZGF0YTwtZnJlYWQodXJsKQojcmVhZF9jc3YodXJsKQoKY29sbmFtZXMoZGF0YSk8LWMoInByZWciLCJwbGFzIiwicHJlcyIsInNraW4iLCJ0ZXN0IiwibWFzcyIsInBlZGkiLCJhZ2UiLCJjbGFzcyIpCgpkYXRhJT4lZ2xpbXBzZSgpCgpgYGAKCiMjIyMgRGVzY3JpcHRpb24gIG9mIERhdGEKCjEuIFRpdGxlOiBQaW1hIEluZGlhbnMgRGlhYmV0ZXMgRGF0YWJhc2UKCjIuIFNvdXJjZXM6IE5hdGlvbmFsIEluc3RpdHV0ZSBvZiBEaWFiZXRlcyBhbmQgRGlnZXN0aXZlIGFuZAogICAgICAgICAgICAgICAgICAgICAgICBLaWRuZXkgRGlzZWFzZXMKClRoZSBkaWFnbm9zdGljLCBiaW5hcnktdmFsdWVkIHZhcmlhYmxlIGludmVzdGlnYXRlZCBpcyB3aGV0aGVyIHRoZQpwYXRpZW50IHNob3dzIHNpZ25zIG9mIGRpYWJldGVzIGFjY29yZGluZyB0byBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIGNyaXRlcmlhIChpLmUuLCBpZiB0aGUgMiBob3VyIHBvc3QtbG9hZCBwbGFzbWEgZ2x1Y29zZSB3YXMgYXQgbGVhc3QgCjIwMCBtZy9kbCBhdCBhbnkgc3VydmV5ICBleGFtaW5hdGlvbiBvciBpZiBmb3VuZCBkdXJpbmcgcm91dGluZSBtZWRpY2FsIGNhcmUpLiAgIFRoZSBwb3B1bGF0aW9uIGxpdmVzIG5lYXIgUGhvZW5peCwgQXJpem9uYSwgVVNBLgoKUmVzdWx0czogVGhlaXIgQURBUCBhbGdvcml0aG0gbWFrZXMgYSByZWFsLXZhbHVlZCBwcmVkaWN0aW9uIGJldHdlZW4KIDAgYW5kIDEuICBUaGlzIHdhcyB0cmFuc2Zvcm1lZCBpbnRvIGEgYmluYXJ5IGRlY2lzaW9uIHVzaW5nIGEgY3V0b2ZmIG9mIAogMC40NDguICBVc2luZyA1NzYgdHJhaW5pbmcgaW5zdGFuY2VzLCB0aGUgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5Cm9mIHRoZWlyIGFsZ29yaXRobSB3YXMgNzYlIG9uIHRoZSByZW1haW5pbmcgMTkyIGluc3RhbmNlcy4KCjQuIFJlbGV2YW50IEluZm9ybWF0aW9uOgogICAgICBTZXZlcmFsIGNvbnN0cmFpbnRzIHdlcmUgcGxhY2VkIG9uIHRoZSBzZWxlY3Rpb24gb2YgdGhlc2UgaW5zdGFuY2VzIGZyb20gYSBsYXJnZXIgZGF0YWJhc2UuICBJbiBwYXJ0aWN1bGFyLCBhbGwgcGF0aWVudHMgaGVyZSBhcmUgZmVtYWxlcyBhdApsZWFzdCAyMSB5ZWFycyBvbGQgb2YgUGltYSBJbmRpYW4gaGVyaXRhZ2UuICBBREFQIGlzIGFuIGFkYXB0aXZlIGxlYXJuaW5nCnJvdXRpbmUgdGhhdCBnZW5lcmF0ZXMgYW5kIGV4ZWN1dGVzIGRpZ2l0YWwgYW5hbG9ncyBvZiBwZXJjZXB0cm9uLWxpa2UKZGV2aWNlcy4gIEl0IGlzIGEgdW5pcXVlIGFsZ29yaXRobTsgc2VlIHRoZSBwYXBlciBmb3IgZGV0YWlscy4KCjUuIE51bWJlciBvZiBJbnN0YW5jZXM6IDc2OAoKNi4gTnVtYmVyIG9mIEF0dHJpYnV0ZXM6IDggcGx1cyBjbGFzcyAKCjcuIEZvciBFYWNoIEF0dHJpYnV0ZTogKGFsbCBudW1lcmljLXZhbHVlZCkKICAgMS4gTnVtYmVyIG9mIHRpbWVzIHByZWduYW50CiAgIDIuIFBsYXNtYSBnbHVjb3NlIGNvbmNlbnRyYXRpb24gYSAyIGhvdXJzIGluIGFuIG9yYWwgZ2x1Y29zZSB0b2xlcmFuY2UgdGVzdAogICAzLiBEaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tIEhnKQogICA0LiBUcmljZXBzIHNraW4gZm9sZCB0aGlja25lc3MgKG1tKQogICA1LiAyLUhvdXIgc2VydW0gaW5zdWxpbiAobXUgVS9tbCkKICAgNi4gQm9keSBtYXNzIGluZGV4ICh3ZWlnaHQgaW4ga2cvKGhlaWdodCBpbiBtKV4yKQogICA3LiBEaWFiZXRlcyBwZWRpZ3JlZSBmdW5jdGlvbgogICA4LiBBZ2UgKHllYXJzKQogICA5LiBDbGFzcyB2YXJpYWJsZSAoMCBvciAxKQoKOC4gTWlzc2luZyBBdHRyaWJ1dGUgVmFsdWVzOiBZZXMKCjkuIENsYXNzIERpc3RyaWJ1dGlvbjogKGNsYXNzIHZhbHVlIDEgaXMgaW50ZXJwcmV0ZWQgYXMgInRlc3RlZCBwb3NpdGl2ZSBmb3IKICAgZGlhYmV0ZXMiKQoKICAgQ2xhc3MgVmFsdWUgIE51bWJlciBvZiBpbnN0YW5jZXMKICAgMCAgICAgICAgICAgIDUwMAogICAxICAgICAgICAgICAgMjY4CgoKClRoZSBkYXRhc2V0IGNvbnRhaW5zIG5vIG1pc3NpbmcgdmFsdWVzLgpgYGB7cn0KY29sU3Vtcyhpcy5uYShkYXRhKSkgCgoKc3VtbWFyaXplQ29sdW1ucyhkYXRhKQpgYGAKClNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YXNldHMuCmBgYHtyfQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzCmluZGV4IDwtIHNhbXBsZSgxOm5yb3coZGF0YSksIHNpemUgPSAwLjgwICogbnJvdyhkYXRhKSkKdHJhaW4gPC0gZGF0YVtpbmRleCwgXQp0ZXN0ICA8LSBkYXRhWy1pbmRleCwgXQpgYGAKCgogCiMjIyMgSU5QVVQgRlVOQ1RJT04KCkVzdGltYXRvcnMgY2FuIGFjY2VwdCBkYXRhIGZyb20gYXJiaXRyYXJ5IGRhdGEgc291cmNlcyB0aHJvdWdoIGFuIOKAmGlucHV0IGZ1bmN0aW9u4oCZLiBUaGUgaW5wdXQgZnVuY3Rpb24gc2VsZWN0cyBmZWF0dXJlIGFuZCByZXNwb25zZSBjb2x1bW5zIGZyb20gdGhlIGlucHV0IHNvdXJjZSBhcyB3ZWxsIGFzIGRlZmluZXMgaG93IGRhdGEgd2lsbCBiZSBkcmF3biAoZS5nLiBiYXRjaCBzaXplLCBlcG9jaHMsIGV0Yy4pLiBUaGUgdGZlc3RpbWF0b3JzIHBhY2thZ2UgcHJvdmlkZXMgdGhlIGlucHV0X2ZuKCkgaGVscGVyIGZ1bmN0aW9uIGZvciBnZW5lcmF0aW5nIGlucHV0IGZ1bmN0aW9ucyBmcm9tIGNvbW1vbiBSIGRhdGEgc3RydWN0dXJlcywgZS5nLiBSIG1hdHJpY2VzIGFuZCBkYXRhIGZyYW1lcy4KCgoKYGBge3J9CiMgcmV0dXJuIGFuIGlucHV0X2ZuIGZvciBhIGdpdmVuIHN1YnNldCBvZiBkYXRhCmRhdGFfaW5wdXRfZm4gPC0gZnVuY3Rpb24oZGF0YSwgbnVtX2Vwb2NocyA9IDEpIHsKICBpbnB1dF9mbihkYXRhLCAKICAgICAgICAgICBmZWF0dXJlcyA9IGMoInByZWciLCJwbGFzIiwicHJlcyIsInNraW4iLCJ0ZXN0IiwibWFzcyIsInBlZGkiLCJhZ2UiKSwgCiAgICAgICAgICAgcmVzcG9uc2UgPSAiY2xhc3MiLAogICAgICAgICAgIGJhdGNoX3NpemUgPSAzMiwKICAgICAgICAgICBudW1fZXBvY2hzID0gbnVtX2Vwb2NocykKfQpgYGAKCgojIyMjIEZFQVRVUkUgQ09MVU1OUwoKTmV4dCwgd2UgZGVmaW5lIHRoZSBmZWF0dXJlIGNvbHVtbnMgZm9yIG91ciBtb2RlbC4gRmVhdHVyZSBjb2x1bW5zIGFyZSBtYXBwaW5ncyBvZiByYXcgaW5wdXQgZGF0YSB0byB0aGUgZGF0YSB0aGF0IHdl4oCZbGwgYWN0dWFsbHkgZmVlZCBpbnRvIG91ciB0cmFpbmluZywgZXZhbHVhdGlvbiwgYW5kIHByZWRpY3Rpb24gc3RlcHMuIApXZSBjYW4gdHJhbnNmb3JtIHZhcmlhYmxlcyB0byAgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGZvciB0aGUgYW5hbHlzaXMuIFdlIGNhbiBkbyB0aGlzICBieSB1c2luZyB0aGV0ZmVzdGltYXRvcnM6OmZlYXR1cmVfY29sdW1ucygpIGZ1bmN0aW9uIHRvIGdldCB0aGUgZGF0YSBpbnRvIHRoZSBzaGFwZSBleHBlY3RlZCBmb3IgYW4gaW5wdXQgVGVuc29yLiBDYXRlZ29yeSBsZXZlbHMgYXJlIHNldCBieSBwYXNzaW5nIGEgbGlzdCB0byB0aGUgdm9jYWJ1bGFyeV9saXN0IGFyZ3VtZW50LkZvciBleGFtcGxlIHRvIGNvbnZlcnQgdmFyaWFibGUgd2l0aCB0d28gbGV2ZWxzIHdlIGNhbiB1c2UgIGNvbHVtbl9jYXRlZ29yaWNhbF93aXRoX3ZvY2FidWxhcnlfbGlzdCgidmFyaWFibGUiLCB2b2NhYnVsYXJ5X2xpc3QgPSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KCJsZXZlbCAxIiwgImxldmVsIDIiKSkKCgpgYGB7cn0KY29scyA8LSBmZWF0dXJlX2NvbHVtbnMoCiAgCiAgY29sdW1uX251bWVyaWMoInByZWciLCJwbGFzIiwicHJlcyIsInNraW4iLCJ0ZXN0IiwibWFzcyIsInBlZGkiLCJhZ2UiKQopCmBgYAoKCiMjIyMgRVNUSU1BVE9SCgpOZXh0LCB3ZSBjcmVhdGUgdGhlIGVzdGltYXRvciBieSBjYWxsaW5nIHRoZSBsaW5lYXJfY2xhc3NpZmllcigpIGZ1bmN0aW9uIGFuZCBwYXNzaW5nIGl0IGEgc2V0IG9mIGZlYXR1cmUgY29sdW1uczoKCmBgYHtyfQptb2RlbCA8LSBsaW5lYXJfY2xhc3NpZmllcihmZWF0dXJlX2NvbHVtbnMgPSBjb2xzKQpgYGAKCldlIHRyYWluIHRoZSBtb2RlbCBhZnRlcndhcmRzLgoKYGBge3J9CiMgdHJhaW4gdGhlIG1vZGVsCm1vZGVsICU+JSB0cmFpbihkYXRhX2lucHV0X2ZuKHRyYWluLCBudW1fZXBvY2hzID0gMTApKQpgYGAKCgojIyMjIEVWQUxVQVRJT04KCldlIGNhbiBldmFsdWF0ZSB0aGUgbW9kZWzigJlzIGFjY3VyYWN5IHVzaW5nIHRoZSBldmFsdWF0ZSgpIGZ1bmN0aW9uLCB1c2luZyBvdXIg4oCYdGVzdOKAmSBkYXRhIHNldCBmb3IgdmFsaWRhdGlvbi4KCmBgYHtyfQptb2RlbCAlPiUgZXZhbHVhdGUoZGF0YV9pbnB1dF9mbih0ZXN0KSwgc2ltcGxpZnkgPSBUUlVFKQpgYGAKCgoKCgoKIyMjIyBQUkVESUNUSU9OCgpBZnRlciB3ZeKAmXZlIGZpbmlzaGVkIHRyYWluaW5nIG91ciBtb2RlbCwgd2UgY2FuIHVzZSBpdCB0byBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmcm9tIG5ldyBkYXRhLgoKYGBge3J9CiNtb2RlbCAlPiUgcHJlZGljdChkYXRhX2lucHV0X2ZuKHRlc3QpLCBzaW1wbGlmeSA9IEZBTFNFKQoKcHJlZGljdGlvbnM9bW9kZWwgJT4lIHByZWRpY3QoZGF0YV9pbnB1dF9mbih0ZXN0KSwgc2ltcGxpZnkgPSBUUlVFKQoKcHJlZGljdGlvbnMlPiVoZWFkKCkKCmBgYAoKCmBgYHtyfQpnbGltcHNlKHByZWRpY3Rpb25zKQpgYGAKCgoKYGBge3J9CiNzaW1wbGlmeV9mbiA8LSBmdW5jdGlvbihwcmVkaWN0aW9ucykgewogIyBsYXBwbHkocHJlZGljdGlvbnMsIGZ1bmN0aW9uKHgpIHgkcHJvYmFiaWxpdGllcykKI30KCiNtb2RlbCAlPiUgcHJlZGljdChkYXRhX2lucHV0X2ZuKHRlc3QpLCBzaW1wbGlmeSA9IHNpbXBsaWZ5X2ZuKQpgYGAKCgojIyMjIE1PREVMIFBFUlNJU1RFTkNFCgpNb2RlbHMgY3JlYXRlZCB2aWEgdGZlc3RpbWF0b3JzIGFyZSBwZXJzaXN0ZWQgb24gZGlzay4gVG8gb2J0YWluIHRoZSBsb2NhdGlvbiBvZiB3aGVyZSB0aGUgbW9kZWwgYXJ0aWZhY3RzIGFyZSBzdG9yZWQsIHdlIGNhbiBjYWxsIG1vZGVsX2RpcigpOgoKYGBge3J9CnNhdmVkX21vZGVsX2RpciA8LSBtb2RlbF9kaXIobW9kZWwpCmBgYAoKCkFuZCBzdWJzZXF1ZW50bHkgbG9hZCB0aGUgc2F2ZWQgbW9kZWwgKGluIGEgbmV3IHNlc3Npb24pIGJ5IHBhc3NpbmcgdGhlIGRpcmVjdG9yeSB0byB0aGUgbW9kZWxfZGlyIGFyZ3VtZW50IG9mIHRoZSBtb2RlbCBjb25zdHJ1Y3RvciBhbmQgdXNlIGl0IGZvciBwcmVkaWN0aW9uIG9yIGNvbnRpbnVlIHRyYWluaW5nOgoKYGBge3J9CmNvbHMgPC0gZmVhdHVyZV9jb2x1bW5zKCAKICBjb2x1bW5fbnVtZXJpYygicHJlZyIsInBsYXMiLCJwcmVzIiwic2tpbiIsInRlc3QiLCJtYXNzIiwicGVkaSIsImFnZSIpCikKbG9hZGVkX21vZGVsIDwtIGxpbmVhcl9jbGFzc2lmaWVyKGZlYXR1cmVfY29sdW1ucyA9IGNvbHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2RpciA9IHNhdmVkX21vZGVsX2RpcikKbG9hZGVkX21vZGVsCmBgYAoKCgoK