Initializing the Yeast dataset from UCI.
yeast <- read.csv("https://archive.ics.uci.edu/ml/machine-learning-databases/yeast/yeast.data",
sep="",
header=F,
col.names=c("name", "mcg", "gvh", "alm", "mit", "erl", "pox", "vac", "nuc", "class"))
yeast
Lets plot a matrix for paramters comparison and correlation checking:

Lets now load the data-set in python
import numpy as np
import sklearn
import sklearn.model_selection
classes = {
"CYT": 0,
"NUC": 1,
"MIT": 2,
"ME3": 3,
"ME2": 4,
"ME1": 5,
"EXC": 6,
"VAC": 7,
"POX": 8,
"ERL": 9
}
data = np.genfromtxt("https://archive.ics.uci.edu/ml/machine-learning-databases/yeast/yeast.data", dtype="|U5", autostrip=True)
X = np.asarray(data[:, 1:-1], dtype=np.float64)
y = [classes[k] for k in data[:, -1]]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, random_state=1)
In this Python snippet we create a classifier using auto machine learning with auto-sklearn:
import autosklearn.classification
import autosklearn.metrics
import sklearn.datasets
import sklearn.metrics
from sklearn.externals import joblib
from time import gmtime, strftime
automl = autosklearn.classification.AutoSklearnClassifier(
time_left_for_this_task=60 * 60,
per_run_time_limit=60 * 6)
automl.fit(X_train, y_train)
joblib.dump(automl, './automl-model.pkl')
Lets load the trained model:
import scipy
from sklearn.externals import joblib
import autosklearn
import numpy as np
import sklearn
import sklearn.model_selection
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import itertools
from scipy import interp
import pandas as pd
classes = {
"CYT": 0,
"NUC": 1,
"MIT": 2,
"ME3": 3,
"ME2": 4,
"ME1": 5,
"EXC": 6,
"VAC": 7,
"POX": 8,
"ERL": 9
}
attrs = ["name", "mcg", "gvh", "alm", "mit", "erl", "pox", "vac", "nuc", "class"]
def plot_confusion_matrix(cm, classes,
normalize=False,
title='Confusion matrix',
cmap=plt.cm.Blues):
"""
This function prints and plots the confusion matrix.
Normalization can be applied by setting `normalize=True`.
"""
if normalize:
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
print("Normalized confusion matrix")
else:
print('Confusion matrix, without normalization')
print(cm)
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=45)
plt.yticks(tick_marks, classes)
fmt = '.2f' if normalize else 'd'
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, format(cm[i, j], fmt),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.tight_layout()
plt.ylabel('Classe real')
plt.xlabel('Classe predita')
to_use = ["mcg", "gvh", "alm", "mit", "erl", "pox", "vac", "nuc", "class"]
data = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/yeast/yeast.data", sep="\s+", usecols=to_use, names=attrs, converters={
9: lambda c: classes[c]
})
X = data[to_use[:-1]]
y = data[to_use[-1]]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, random_state=1)
automl_csf = joblib.load('/Users/mateus/dev/automl/projeto-mineracao/automl-2017-08-14 18:38:46.pkl')
y_test_pred = automl_csf.predict(X_test)
y_test_pred_prob = automl_csf.predict_proba(X_test)
y_pred = automl_csf.predict(X)
y_pred_prob = automl_csf.predict_proba(X)
# Compute ROC curve and ROC area for each class
fpr = dict()
tpr = dict()
roc_auc = dict()
n_classes = len(classes)
for c, i in classes.items():
fpr[i], tpr[i], _ = sklearn.metrics.roc_curve(y_test, y_test_pred_prob[:, i], pos_label=i)
roc_auc[i] = sklearn.metrics.auc(fpr[i], tpr[i])
# Compute macro-average ROC curve and ROC area
# First aggregate all false positive rates
all_fpr = np.unique(np.concatenate([fpr[i] for i in range(n_classes)]))
# Then interpolate all ROC curves at this points
mean_tpr = np.zeros_like(all_fpr)
for c, i in classes.items():
mean_tpr += interp(all_fpr, fpr[i], tpr[i])
# Finally average it and compute AUC
mean_tpr /= n_classes
fpr["macro"] = all_fpr
tpr["macro"] = mean_tpr
roc_auc["macro"] = sklearn.metrics.auc(fpr["macro"], tpr["macro"])
# Plot all ROC curves
plt.figure()
plt.plot(fpr["macro"], tpr["macro"],
label='Curva ROC da macro-média (area = {0:0.2f})'
''.format(roc_auc["macro"]),
color='navy', linestyle=':', linewidth=4)
lw = 2
for c, i in classes.items():
plt.plot(fpr[i], tpr[i], lw=lw,
label='Curva ROC da classe {0} (area = {1:0.2f})'
''.format(c, roc_auc[i]))
plt.plot([0, 1], [0, 1], 'k--', lw=lw)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('Taxa de falso positivos')
plt.ylabel('Taxa de verdadeiros positivos')
plt.title('Curva ROC multiclasse')
plt.legend(loc="lower right")
plt.savefig('roc.png', bbox_inches='tight')
print(y_test_pred_prob)
print(automl_csf.show_models())
print("Accuracy score", sklearn.metrics.accuracy_score(y_test, y_test_pred))
print("Kappa Score", sklearn.metrics.cohen_kappa_score(y_test, y_test_pred))
print("F-score", sklearn.metrics.f1_score(y_test, y_test_pred, average="macro"))
plt.figure()
plot_confusion_matrix(confusion_matrix(y_test, y_test_pred), classes=classes.keys(), normalize=True,
title='Matriz de confusão para a base de testes.')
plt.savefig('confusion_matrix.png', bbox_inches='tight')
plt.figure()
plot_confusion_matrix(confusion_matrix(y, y_pred), classes=classes.keys(), normalize=True,
title='Matriz de confusão para toda a base de dados.')
plt.savefig('confusion_matrix_all.png', bbox_inches='tight')
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpJbml0aWFsaXppbmcgdGhlIFllYXN0IGRhdGFzZXQgZnJvbSBVQ0kuCgpgYGB7cn0KeWVhc3QgPC0gcmVhZC5jc3YoImh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9tYWNoaW5lLWxlYXJuaW5nLWRhdGFiYXNlcy95ZWFzdC95ZWFzdC5kYXRhIiwKICAgICAgICAgICAgICAgICAgc2VwPSIiLAogICAgICAgICAgICAgICAgICBoZWFkZXI9RiwKICAgICAgICAgICAgICAgICAgY29sLm5hbWVzPWMoIm5hbWUiLCAibWNnIiwgImd2aCIsICJhbG0iLCAibWl0IiwgImVybCIsICJwb3giLCAidmFjIiwgIm51YyIsICJjbGFzcyIpKQp5ZWFzdApgYGAKCkxldHMgcGxvdCBhIG1hdHJpeCBmb3IgcGFyYW10ZXJzIGNvbXBhcmlzb24gYW5kIGNvcnJlbGF0aW9uIGNoZWNraW5nOgoKYGBge3IgbXlwbG90LGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9OSxkcGk9MzAwLG91dC53aWR0aD0iMTkyMHB4IixvdXQuaGVpZ2h0PSIyMTYwcHgiLGVjaG89RkFMU0UscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCdHR2FsbHknKQoKbXlfYmluIDwtIGZ1bmN0aW9uKGRhdGEsIG1hcHBpbmcsIC4uLiwgbG93ID0gIiMxMzJCNDMiLCBoaWdoID0gIiM1NkIxRjciKSB7CiAgZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gbWFwcGluZykgKwogICAgZ2VvbV9iaW4yZCguLi4pICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gbG93LCBoaWdoID0gaGlnaCkKfQoKZ2dwYWlycyh5ZWFzdCwgY29sdW1ucz0yOjEwLCBsb3dlcj1saXN0KGNvbnRpbnVvdXM9bXlfYmluKSwgYmlud2lkdGg9MTApICsgdGhlbWUoCiAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwKICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwKICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSBOQSksCiAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTk1IikpCmBgYApMZXRzIG5vdyBsb2FkIHRoZSBkYXRhLXNldCBpbiBweXRob24KYGBge3B5dGhvbn0KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBza2xlYXJuCmltcG9ydCBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbgoKY2xhc3NlcyA9IHsKICAgICJDWVQiOiAwLAogICAgIk5VQyI6IDEsCiAgICAiTUlUIjogMiwKICAgICJNRTMiOiAzLAogICAgIk1FMiI6IDQsCiAgICAiTUUxIjogNSwKICAgICJFWEMiOiA2LAogICAgIlZBQyI6IDcsCiAgICAiUE9YIjogOCwKICAgICJFUkwiOiA5Cn0KCmRhdGEgPSBucC5nZW5mcm9tdHh0KCJodHRwczovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMveWVhc3QveWVhc3QuZGF0YSIsIGR0eXBlPSJ8VTUiLCBhdXRvc3RyaXA9VHJ1ZSkKWCA9IG5wLmFzYXJyYXkoZGF0YVs6LCAxOi0xXSwgZHR5cGU9bnAuZmxvYXQ2NCkKeSA9IFtjbGFzc2VzW2tdIGZvciBrIGluIGRhdGFbOiwgLTFdXQoKWF90cmFpbiwgWF90ZXN0LCB5X3RyYWluLCB5X3Rlc3QgPSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbi50cmFpbl90ZXN0X3NwbGl0KFgsIHksIHJhbmRvbV9zdGF0ZT0xKQpgYGAKCkluIHRoaXMgUHl0aG9uIHNuaXBwZXQgd2UgY3JlYXRlIGEgY2xhc3NpZmllciB1c2luZyBhdXRvIG1hY2hpbmUgbGVhcm5pbmcgd2l0aCBhdXRvLXNrbGVhcm46CgpgYGB7cHl0aG9ufQppbXBvcnQgYXV0b3NrbGVhcm4uY2xhc3NpZmljYXRpb24KaW1wb3J0IGF1dG9za2xlYXJuLm1ldHJpY3MKaW1wb3J0IHNrbGVhcm4uZGF0YXNldHMKaW1wb3J0IHNrbGVhcm4ubWV0cmljcwoKZnJvbSBza2xlYXJuLmV4dGVybmFscyBpbXBvcnQgam9ibGliCmZyb20gdGltZSBpbXBvcnQgZ210aW1lLCBzdHJmdGltZQphdXRvbWwgPSBhdXRvc2tsZWFybi5jbGFzc2lmaWNhdGlvbi5BdXRvU2tsZWFybkNsYXNzaWZpZXIoCiAgICB0aW1lX2xlZnRfZm9yX3RoaXNfdGFzaz02MCAqIDYwLAogICAgcGVyX3J1bl90aW1lX2xpbWl0PTYwICogNikgCiAgICAKYXV0b21sLmZpdChYX3RyYWluLCB5X3RyYWluKQpqb2JsaWIuZHVtcChhdXRvbWwsICcuL2F1dG9tbC1tb2RlbC5wa2wnKQpgYGAKCkxldHMgbG9hZCB0aGUgdHJhaW5lZCBtb2RlbDoKCmBgYHtweXRob259CmltcG9ydCBzY2lweQpmcm9tIHNrbGVhcm4uZXh0ZXJuYWxzIGltcG9ydCBqb2JsaWIKaW1wb3J0IGF1dG9za2xlYXJuCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgc2tsZWFybgppbXBvcnQgc2tsZWFybi5tb2RlbF9zZWxlY3Rpb24KZnJvbSBza2xlYXJuLm1ldHJpY3MgaW1wb3J0IGNvbmZ1c2lvbl9tYXRyaXgKaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdAppbXBvcnQgaXRlcnRvb2xzCmZyb20gc2NpcHkgaW1wb3J0IGludGVycAppbXBvcnQgcGFuZGFzIGFzIHBkCgpjbGFzc2VzID0gewogICAgIkNZVCI6IDAsCiAgICAiTlVDIjogMSwKICAgICJNSVQiOiAyLAogICAgIk1FMyI6IDMsCiAgICAiTUUyIjogNCwKICAgICJNRTEiOiA1LAogICAgIkVYQyI6IDYsCiAgICAiVkFDIjogNywKICAgICJQT1giOiA4LAogICAgIkVSTCI6IDkKfQoKYXR0cnMgPSBbIm5hbWUiLCAibWNnIiwgImd2aCIsICJhbG0iLCAibWl0IiwgImVybCIsICJwb3giLCAidmFjIiwgIm51YyIsICJjbGFzcyJdCgpkZWYgcGxvdF9jb25mdXNpb25fbWF0cml4KGNtLCBjbGFzc2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZT1GYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT0nQ29uZnVzaW9uIG1hdHJpeCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY21hcD1wbHQuY20uQmx1ZXMpOgogICAgIiIiCiAgICBUaGlzIGZ1bmN0aW9uIHByaW50cyBhbmQgcGxvdHMgdGhlIGNvbmZ1c2lvbiBtYXRyaXguCiAgICBOb3JtYWxpemF0aW9uIGNhbiBiZSBhcHBsaWVkIGJ5IHNldHRpbmcgYG5vcm1hbGl6ZT1UcnVlYC4KICAgICIiIgogICAgaWYgbm9ybWFsaXplOgogICAgICAgIGNtID0gY20uYXN0eXBlKCdmbG9hdCcpIC8gY20uc3VtKGF4aXM9MSlbOiwgbnAubmV3YXhpc10KICAgICAgICBwcmludCgiTm9ybWFsaXplZCBjb25mdXNpb24gbWF0cml4IikKICAgIGVsc2U6CiAgICAgICAgcHJpbnQoJ0NvbmZ1c2lvbiBtYXRyaXgsIHdpdGhvdXQgbm9ybWFsaXphdGlvbicpCgogICAgcHJpbnQoY20pCgogICAgcGx0Lmltc2hvdyhjbSwgaW50ZXJwb2xhdGlvbj0nbmVhcmVzdCcsIGNtYXA9Y21hcCkKICAgIHBsdC50aXRsZSh0aXRsZSkKICAgIHBsdC5jb2xvcmJhcigpCiAgICB0aWNrX21hcmtzID0gbnAuYXJhbmdlKGxlbihjbGFzc2VzKSkKICAgIHBsdC54dGlja3ModGlja19tYXJrcywgY2xhc3Nlcywgcm90YXRpb249NDUpCiAgICBwbHQueXRpY2tzKHRpY2tfbWFya3MsIGNsYXNzZXMpCgogICAgZm10ID0gJy4yZicgaWYgbm9ybWFsaXplIGVsc2UgJ2QnCiAgICB0aHJlc2ggPSBjbS5tYXgoKSAvIDIuCiAgICBmb3IgaSwgaiBpbiBpdGVydG9vbHMucHJvZHVjdChyYW5nZShjbS5zaGFwZVswXSksIHJhbmdlKGNtLnNoYXBlWzFdKSk6CiAgICAgICAgcGx0LnRleHQoaiwgaSwgZm9ybWF0KGNtW2ksIGpdLCBmbXQpLAogICAgICAgICAgICAgICAgIGhvcml6b250YWxhbGlnbm1lbnQ9ImNlbnRlciIsCiAgICAgICAgICAgICAgICAgY29sb3I9IndoaXRlIiBpZiBjbVtpLCBqXSA+IHRocmVzaCBlbHNlICJibGFjayIpCgogICAgcGx0LnRpZ2h0X2xheW91dCgpCiAgICBwbHQueWxhYmVsKCdDbGFzc2UgcmVhbCcpCiAgICBwbHQueGxhYmVsKCdDbGFzc2UgcHJlZGl0YScpCgp0b191c2UgPSBbIm1jZyIsICJndmgiLCAiYWxtIiwgIm1pdCIsICJlcmwiLCAicG94IiwgInZhYyIsICJudWMiLCAiY2xhc3MiXQoKZGF0YSA9IHBkLnJlYWRfY3N2KCJodHRwczovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMveWVhc3QveWVhc3QuZGF0YSIsIHNlcD0iXHMrIiwgdXNlY29scz10b191c2UsIG5hbWVzPWF0dHJzLCBjb252ZXJ0ZXJzPXsKICAgIDk6IGxhbWJkYSBjOiBjbGFzc2VzW2NdCn0pCgpYID0gZGF0YVt0b191c2VbOi0xXV0KeSA9IGRhdGFbdG9fdXNlWy0xXV0KClhfdHJhaW4sIFhfdGVzdCwgeV90cmFpbiwgeV90ZXN0ID0gc2tsZWFybi5tb2RlbF9zZWxlY3Rpb24udHJhaW5fdGVzdF9zcGxpdChYLCB5LCByYW5kb21fc3RhdGU9MSkKYXV0b21sX2NzZiA9IGpvYmxpYi5sb2FkKCcvVXNlcnMvbWF0ZXVzL2Rldi9hdXRvbWwvcHJvamV0by1taW5lcmFjYW8vYXV0b21sLTIwMTctMDgtMTQgMTg6Mzg6NDYucGtsJykKCnlfdGVzdF9wcmVkID0gYXV0b21sX2NzZi5wcmVkaWN0KFhfdGVzdCkKeV90ZXN0X3ByZWRfcHJvYiA9IGF1dG9tbF9jc2YucHJlZGljdF9wcm9iYShYX3Rlc3QpCnlfcHJlZCA9IGF1dG9tbF9jc2YucHJlZGljdChYKQp5X3ByZWRfcHJvYiA9IGF1dG9tbF9jc2YucHJlZGljdF9wcm9iYShYKQoKIyBDb21wdXRlIFJPQyBjdXJ2ZSBhbmQgUk9DIGFyZWEgZm9yIGVhY2ggY2xhc3MKZnByID0gZGljdCgpCnRwciA9IGRpY3QoKQpyb2NfYXVjID0gZGljdCgpCm5fY2xhc3NlcyA9IGxlbihjbGFzc2VzKQpmb3IgYywgaSBpbiBjbGFzc2VzLml0ZW1zKCk6CiAgICBmcHJbaV0sIHRwcltpXSwgXyA9IHNrbGVhcm4ubWV0cmljcy5yb2NfY3VydmUoeV90ZXN0LCB5X3Rlc3RfcHJlZF9wcm9iWzosIGldLCBwb3NfbGFiZWw9aSkKICAgIHJvY19hdWNbaV0gPSBza2xlYXJuLm1ldHJpY3MuYXVjKGZwcltpXSwgdHByW2ldKQoKIyBDb21wdXRlIG1hY3JvLWF2ZXJhZ2UgUk9DIGN1cnZlIGFuZCBST0MgYXJlYQoKIyBGaXJzdCBhZ2dyZWdhdGUgYWxsIGZhbHNlIHBvc2l0aXZlIHJhdGVzCmFsbF9mcHIgPSBucC51bmlxdWUobnAuY29uY2F0ZW5hdGUoW2ZwcltpXSBmb3IgaSBpbiByYW5nZShuX2NsYXNzZXMpXSkpCgojIFRoZW4gaW50ZXJwb2xhdGUgYWxsIFJPQyBjdXJ2ZXMgYXQgdGhpcyBwb2ludHMKbWVhbl90cHIgPSBucC56ZXJvc19saWtlKGFsbF9mcHIpCmZvciBjLCBpIGluIGNsYXNzZXMuaXRlbXMoKToKICAgIG1lYW5fdHByICs9IGludGVycChhbGxfZnByLCBmcHJbaV0sIHRwcltpXSkKCiMgRmluYWxseSBhdmVyYWdlIGl0IGFuZCBjb21wdXRlIEFVQwptZWFuX3RwciAvPSBuX2NsYXNzZXMKCmZwclsibWFjcm8iXSA9IGFsbF9mcHIKdHByWyJtYWNybyJdID0gbWVhbl90cHIKcm9jX2F1Y1sibWFjcm8iXSA9IHNrbGVhcm4ubWV0cmljcy5hdWMoZnByWyJtYWNybyJdLCB0cHJbIm1hY3JvIl0pCgojIFBsb3QgYWxsIFJPQyBjdXJ2ZXMKcGx0LmZpZ3VyZSgpCnBsdC5wbG90KGZwclsibWFjcm8iXSwgdHByWyJtYWNybyJdLAogICAgICAgICBsYWJlbD0nQ3VydmEgUk9DIGRhIG1hY3JvLW3DqWRpYSAoYXJlYSA9IHswOjAuMmZ9KScKICAgICAgICAgICAgICAgJycuZm9ybWF0KHJvY19hdWNbIm1hY3JvIl0pLAogICAgICAgICBjb2xvcj0nbmF2eScsIGxpbmVzdHlsZT0nOicsIGxpbmV3aWR0aD00KQpsdyA9IDIKZm9yIGMsIGkgaW4gY2xhc3Nlcy5pdGVtcygpOgogICAgcGx0LnBsb3QoZnByW2ldLCB0cHJbaV0sIGx3PWx3LAogICAgICAgICAgICAgbGFiZWw9J0N1cnZhIFJPQyBkYSBjbGFzc2UgezB9IChhcmVhID0gezE6MC4yZn0pJwogICAgICAgICAgICAgICAgICAgJycuZm9ybWF0KGMsIHJvY19hdWNbaV0pKQoKcGx0LnBsb3QoWzAsIDFdLCBbMCwgMV0sICdrLS0nLCBsdz1sdykKcGx0LnhsaW0oWzAuMCwgMS4wXSkKcGx0LnlsaW0oWzAuMCwgMS4wNV0pCnBsdC54bGFiZWwoJ1RheGEgZGUgZmFsc28gcG9zaXRpdm9zJykKcGx0LnlsYWJlbCgnVGF4YSBkZSB2ZXJkYWRlaXJvcyBwb3NpdGl2b3MnKQpwbHQudGl0bGUoJ0N1cnZhIFJPQyBtdWx0aWNsYXNzZScpCnBsdC5sZWdlbmQobG9jPSJsb3dlciByaWdodCIpCnBsdC5zYXZlZmlnKCdyb2MucG5nJywgYmJveF9pbmNoZXM9J3RpZ2h0JykKCnByaW50KHlfdGVzdF9wcmVkX3Byb2IpCgpwcmludChhdXRvbWxfY3NmLnNob3dfbW9kZWxzKCkpCnByaW50KCJBY2N1cmFjeSBzY29yZSIsIHNrbGVhcm4ubWV0cmljcy5hY2N1cmFjeV9zY29yZSh5X3Rlc3QsIHlfdGVzdF9wcmVkKSkKcHJpbnQoIkthcHBhIFNjb3JlIiwgc2tsZWFybi5tZXRyaWNzLmNvaGVuX2thcHBhX3Njb3JlKHlfdGVzdCwgeV90ZXN0X3ByZWQpKQpwcmludCgiRi1zY29yZSIsIHNrbGVhcm4ubWV0cmljcy5mMV9zY29yZSh5X3Rlc3QsIHlfdGVzdF9wcmVkLCBhdmVyYWdlPSJtYWNybyIpKQoKcGx0LmZpZ3VyZSgpCnBsb3RfY29uZnVzaW9uX21hdHJpeChjb25mdXNpb25fbWF0cml4KHlfdGVzdCwgeV90ZXN0X3ByZWQpLCBjbGFzc2VzPWNsYXNzZXMua2V5cygpLCBub3JtYWxpemU9VHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPSdNYXRyaXogZGUgY29uZnVzw6NvIHBhcmEgYSBiYXNlIGRlIHRlc3Rlcy4nKQpwbHQuc2F2ZWZpZygnY29uZnVzaW9uX21hdHJpeC5wbmcnLCBiYm94X2luY2hlcz0ndGlnaHQnKQoKcGx0LmZpZ3VyZSgpCnBsb3RfY29uZnVzaW9uX21hdHJpeChjb25mdXNpb25fbWF0cml4KHksIHlfcHJlZCksIGNsYXNzZXM9Y2xhc3Nlcy5rZXlzKCksIG5vcm1hbGl6ZT1UcnVlLAogICAgICAgICAgICAgICAgICAgICAgdGl0bGU9J01hdHJpeiBkZSBjb25mdXPDo28gcGFyYSB0b2RhIGEgYmFzZSBkZSBkYWRvcy4nKQpwbHQuc2F2ZWZpZygnY29uZnVzaW9uX21hdHJpeF9hbGwucG5nJywgYmJveF9pbmNoZXM9J3RpZ2h0JykKYGBgCgohW10ocm9jLnBuZykKCiFbXShjb25mdXNpb25fbWF0cml4LnBuZykKCiFbXShjb25mdXNpb25fbWF0cml4X2FsbC5wbmcp