Optimización del código
Una de las optimizaciones relativamente fáciles y disponibles siempre, es utilizar una versión actualizada de R. En general, R es muy conservador, por lo que el actualizar la versión no daña el código existente. Sin embargo, una nueva versión proporcionará a menudo aumentos de velocidad para funciones clave.
# Print the R version details using version
version
_
platform x86_64-apple-darwin15.6.0
arch x86_64
os darwin15.6.0
system x86_64, darwin15.6.0
status
major 3
minor 5.1
year 2018
month 07
day 02
svn rev 74947
language R
version.string R version 3.5.1 (2018-07-02)
nickname Feather Spray
# Assign the variable major to the major component
major <- version$major
major
[1] "3"
# Assign the variable minor to the minor component
minor <- version$minor
minor
[1] "5.1"
Tiempos de ejecución
Una de las tareas más comunes que realizamos es leer datos de archivos CSV. Sin embargo, para archivos CSV grandes, esto puede ser una tarea lenta. Un buen truco es leer los datos y guardarlos como un archivo binario R (rds) usando saveRDS(). Para leer en el archivo rds, usamos readRDS().
# How long does it take to read movies from CSV?
system.time(read.csv("movies.csv"))
user system elapsed
0.549 0.015 0.564
# How long does it take to read movies from RDS?
system.time(readRDS("movies.rds"))
user system elapsed
0.080 0.002 0.082
Usar system.time () es conveniente, pero tiene sus desventajas cuando se comparan llamadas de múltiples funciones. El paquete microbenchmark resuelve este problema con la función microbenchmark().
# Load the microbenchmark package
library(microbenchmark)
# Compare the two functions
compare <- microbenchmark(read.csv("movies.csv"),
readRDS("movies.rds"),
times = 10)
# Print compare
compare
Unit: milliseconds
Depende de la maquina …
Para muchos problemas, el tiempo es la parte cara. Si tener una computadora más rápida lo hace más productivo, puede ser rentable comprar uno. Sin embargo, antes de gastar nuevos juguetes para usted, su jefe/compañero puede querer ver algunos números para justificar el gasto. Medir el rendimiento de su computadora se llama evaluación comparativa, y puede hacerlo con el paquete benchmarkme.
# Load the benchmarkme package
library(benchmarkme)
# Assign the variable ram to the amount of RAM on this machine
ram <- get_ram()
ram
4.29 GB
# Assign the variable cpu to the cpu specs
cpu <- get_cpu()
cpu
$vendor_id
[1] "GenuineIntel"
$model_name
[1] "Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz"
$no_of_cores
[1] 4
El paquete benchmarkme le permite ejecutar un conjunto de puntos de referencia estandarizados y comparar sus resultados con otros usuarios. Un conjunto de pruebas de puntos de referencia es la velocidad de lectura y escritura.
# Load the package
library(benchmarkme)
See https://jumpingrivers.shinyapps.io/benchmarkme/ for a Shiny
interface to the benchmark data.
# Run the io benchmark
res <- benchmark_io(runs = 1, size = 5)
# IO benchmarks (2 tests) for size 5 MB:
Reading a csv with 625000 values: 0.912 (sec).
Writing a csv with 625000 values: 0.886 (sec).
# Plot the results
# plot(res)
Asignación de memoria
Si usted está programando en R, tenga en cuenta lo siguiente:
En R, la asignación de memoria ocurre automáticamente
R asigna memoria en RAM para almacenar variables
Minimice la asignación de variables para la velocidad
Considere el ejemplo1:
Welcome to R club!
- The first rule of R club never, ever grow a vector (Nunca incremente un vector)
Importancia de vectorizar tu código
Llamar a una función R finalmente lleva al código C o FORTRAN
Este código está muy optimizado
La meta es: Acceda al código subyacente de C o FORTRAN lo más rápido posible; cuantas menos funciones se llaman mejor.
El siguiente fragmento de código está escrito como el código tradicional C o Fortran. En lugar de usar la versión vectorizada de la multiplicación, usa un ciclo for.
x <- rnorm(10)
x2 <- numeric(length(x))
for(i in 1:10)
x2[i] <- x[i] * x[i]
La idea es evitar el uso extensivo de los ciclos for, debido a que R implementa la vectorización.
x <- rnorm(10)
x2 <- numeric(length(x))
x2_imp <- x^2
x2_imp
[1] 0.02951567 0.15724065 0.17687795 3.12364545 0.09472666 0.24533842
[7] 0.09361045 0.12474215 0.35690963 1.18789281
Una operación común en estadística es calcular la suma de los logaritmos de las probabilidades. El siguiente código calcula el log-sum (la suma de los logaritmos).
# Initial code
n <- 100
total <- 0
x <- runif(n)
for(i in 1:n)
total <- total + log(x[i])
total
[1] -108.7323
# Rewrite in a single line. Store the result in log_sum
log_sum <- sum(log(x))
log_sum
[1] -108.7323
Welcome to R club!
- La segunda regla del club R: use una solución vectorizada siempre que sea posible.
Matrices y Data Frame
Data Frame
Estructura de datos clave en R
Copiado en otros lenguajes
- Python: pandas data frame
Welcome to R club!
- La tercera regla del club R: use una matriz cuando sea apropiado.
Code profiling
profiling le ayuda a localizar los cuellos de botella en su código. Este apartado le enseña a visualizar los cuellos de botella utilizando el paquete profis.
La idea general es:
Ejecuta el código
Cada pocos milisegundos, registre lo que se está ejecutando actualmente
Rprof() viene con R y hace exactamente esto, Difícil de usar
Use profvis en su lugar
Pararrel Programing
CPU: cerebros de la computadora
La velocidad se estabilizó lentamente
_ Las CPU se estaban calentando demasiado
Multi-core CPUs
Pero R solo usa 1 núcleo :(
No todos los análisis pueden hacer uso de múltiples núcleos
Muchos algoritmos estadísticos solo pueden usar un solo núcleo
get_cpu()
$vendor_id
[1] "GenuineIntel"
$model_name
[1] "Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz"
$no_of_cores
[1] 4
LS0tCnRpdGxlOiAiRXNjcmliaWVuZG8gY8OzZGlnbyBlZmljaWVudGUgZW4gUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMjIE9wdGltaXphY2nDs24gZGVsIGPDs2RpZ28gCgpVbmEgZGUgbGFzIG9wdGltaXphY2lvbmVzIHJlbGF0aXZhbWVudGUgZsOhY2lsZXMgeSBkaXNwb25pYmxlcyBzaWVtcHJlLCBlcyB1dGlsaXphciB1bmEgdmVyc2nDs24gYWN0dWFsaXphZGEgZGUgUi4gRW4gZ2VuZXJhbCwgUiBlcyBtdXkgY29uc2VydmFkb3IsIHBvciBsbyBxdWUgZWwgYWN0dWFsaXphciBsYSB2ZXJzacOzbiBubyBkYcOxYSBlbCBjw7NkaWdvIGV4aXN0ZW50ZS4gU2luIGVtYmFyZ28sIHVuYSBudWV2YSB2ZXJzacOzbiBwcm9wb3JjaW9uYXLDoSBhIG1lbnVkbyBhdW1lbnRvcyBkZSB2ZWxvY2lkYWQgcGFyYSBmdW5jaW9uZXMgY2xhdmUuCgpgYGB7cn0KIyBQcmludCB0aGUgUiB2ZXJzaW9uIGRldGFpbHMgdXNpbmcgdmVyc2lvbgp2ZXJzaW9uCgojIEFzc2lnbiB0aGUgdmFyaWFibGUgbWFqb3IgdG8gdGhlIG1ham9yIGNvbXBvbmVudAptYWpvciA8LSB2ZXJzaW9uJG1ham9yCm1ham9yCiMgQXNzaWduIHRoZSB2YXJpYWJsZSBtaW5vciB0byB0aGUgbWlub3IgY29tcG9uZW50Cm1pbm9yIDwtIHZlcnNpb24kbWlub3IKbWlub3IKYGBgCgoKIyMgVGllbXBvcyBkZSBlamVjdWNpw7NuIAoKVW5hIGRlIGxhcyB0YXJlYXMgbcOhcyBjb211bmVzIHF1ZSByZWFsaXphbW9zIGVzIGxlZXIgZGF0b3MgZGUgYXJjaGl2b3MgQ1NWLiBTaW4gZW1iYXJnbywgcGFyYSBhcmNoaXZvcyBDU1YgZ3JhbmRlcywgZXN0byBwdWVkZSBzZXIgdW5hIHRhcmVhIGxlbnRhLiBVbiBidWVuIHRydWNvIGVzIGxlZXIgbG9zIGRhdG9zIHkgZ3VhcmRhcmxvcyBjb21vIHVuIGFyY2hpdm8gYmluYXJpbyBSIChyZHMpIHVzYW5kbyBzYXZlUkRTKCkuIFBhcmEgbGVlciBlbiBlbCBhcmNoaXZvIHJkcywgdXNhbW9zIHJlYWRSRFMoKS4KCgpgYGB7cn0KIyBIb3cgbG9uZyBkb2VzIGl0IHRha2UgdG8gcmVhZCBtb3ZpZXMgZnJvbSBDU1Y/CnN5c3RlbS50aW1lKHJlYWQuY3N2KCJtb3ZpZXMuY3N2IikpCgojIEhvdyBsb25nIGRvZXMgaXQgdGFrZSB0byByZWFkIG1vdmllcyBmcm9tIFJEUz8Kc3lzdGVtLnRpbWUocmVhZFJEUygibW92aWVzLnJkcyIpKQpgYGAKClVzYXIgc3lzdGVtLnRpbWUgKCkgZXMgY29udmVuaWVudGUsIHBlcm8gdGllbmUgc3VzIGRlc3ZlbnRhamFzIGN1YW5kbyBzZSBjb21wYXJhbiBsbGFtYWRhcyBkZSBtw7psdGlwbGVzIGZ1bmNpb25lcy4gRWwgcGFxdWV0ZSBtaWNyb2JlbmNobWFyayByZXN1ZWx2ZSBlc3RlIHByb2JsZW1hIGNvbiBsYSBmdW5jacOzbiBtaWNyb2JlbmNobWFyaygpLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZCB0aGUgbWljcm9iZW5jaG1hcmsgcGFja2FnZQpsaWJyYXJ5KG1pY3JvYmVuY2htYXJrKQoKIyBDb21wYXJlIHRoZSB0d28gZnVuY3Rpb25zCmNvbXBhcmUgPC0gbWljcm9iZW5jaG1hcmsocmVhZC5jc3YoIm1vdmllcy5jc3YiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZFJEUygibW92aWVzLnJkcyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDEwKQoKIyBQcmludCBjb21wYXJlCmNvbXBhcmUKYGBgCgojIyBEZXBlbmRlIGRlIGxhIG1hcXVpbmEgLi4uCgpQYXJhIG11Y2hvcyBwcm9ibGVtYXMsIGVsIHRpZW1wbyBlcyBsYSBwYXJ0ZSBjYXJhLiBTaSB0ZW5lciB1bmEgY29tcHV0YWRvcmEgbcOhcyByw6FwaWRhIGxvIGhhY2UgbcOhcyBwcm9kdWN0aXZvLCBwdWVkZSBzZXIgcmVudGFibGUgY29tcHJhciB1bm8uIFNpbiBlbWJhcmdvLCBhbnRlcyBkZSBnYXN0YXIgbnVldm9zIGp1Z3VldGVzIHBhcmEgdXN0ZWQsIHN1IGplZmUvY29tcGHDsWVybyBwdWVkZSBxdWVyZXIgdmVyIGFsZ3Vub3MgbsO6bWVyb3MgcGFyYSBqdXN0aWZpY2FyIGVsIGdhc3RvLiBNZWRpciBlbCByZW5kaW1pZW50byBkZSBzdSBjb21wdXRhZG9yYSBzZSBsbGFtYSBldmFsdWFjacOzbiBjb21wYXJhdGl2YSwgeSBwdWVkZSBoYWNlcmxvIGNvbiBlbCBwYXF1ZXRlIGJlbmNobWFya21lLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZCB0aGUgYmVuY2htYXJrbWUgcGFja2FnZQpsaWJyYXJ5KGJlbmNobWFya21lKQoKIyBBc3NpZ24gdGhlIHZhcmlhYmxlIHJhbSB0byB0aGUgYW1vdW50IG9mIFJBTSBvbiB0aGlzIG1hY2hpbmUKcmFtIDwtIGdldF9yYW0oKQpyYW0KCiMgQXNzaWduIHRoZSB2YXJpYWJsZSBjcHUgdG8gdGhlIGNwdSBzcGVjcwpjcHUgPC0gZ2V0X2NwdSgpCmNwdQpgYGAKCkVsIHBhcXVldGUgYmVuY2htYXJrbWUgbGUgcGVybWl0ZSBlamVjdXRhciB1biBjb25qdW50byBkZSBwdW50b3MgZGUgcmVmZXJlbmNpYSBlc3RhbmRhcml6YWRvcyB5IGNvbXBhcmFyIHN1cyByZXN1bHRhZG9zIGNvbiBvdHJvcyB1c3Vhcmlvcy4gVW4gY29uanVudG8gZGUgcHJ1ZWJhcyBkZSBwdW50b3MgZGUgcmVmZXJlbmNpYSBlcyBsYSB2ZWxvY2lkYWQgZGUgbGVjdHVyYSB5IGVzY3JpdHVyYS4KCgpgYGB7cn0KIyBMb2FkIHRoZSBwYWNrYWdlCmxpYnJhcnkoYmVuY2htYXJrbWUpCgojIFJ1biB0aGUgaW8gYmVuY2htYXJrCnJlcyA8LSBiZW5jaG1hcmtfaW8ocnVucyA9IDEsIHNpemUgPSA1KQoKIyBQbG90IHRoZSByZXN1bHRzCiMgcGxvdChyZXMpCmBgYAoKCgojIyBBc2lnbmFjacOzbiBkZSBtZW1vcmlhCgpTaSB1c3RlZCBlc3TDoSBwcm9ncmFtYW5kbyBlbiBSLCB0ZW5nYSBlbiBjdWVudGEgbG8gc2lndWllbnRlOgoKKiBFbiBSLCBsYSBhc2lnbmFjacOzbiBkZSBtZW1vcmlhIG9jdXJyZSBhdXRvbcOhdGljYW1lbnRlCgoqIFIgYXNpZ25hIG1lbW9yaWEgZW4gUkFNIHBhcmEgYWxtYWNlbmFyIHZhcmlhYmxlcwoKKiBNaW5pbWljZSBsYSBhc2lnbmFjacOzbiBkZSB2YXJpYWJsZXMgcGFyYSBsYSB2ZWxvY2lkYWQKCkNvbnNpZGVyZSBlbCBlamVtcGxvMToKCiMjIFdlbGNvbWUgdG8gUiBjbHViIQoKKiBfKipUaGUgZmlyc3QgcnVsZSBvZiBSIGNsdWIgbmV2ZXIsIGV2ZXIgZ3JvdyBhIHZlY3RvcioqXyAoTnVuY2EgaW5jcmVtZW50ZSB1biB2ZWN0b3IpCgoKICAgIAoKIyMgSW1wb3J0YW5jaWEgZGUgdmVjdG9yaXphciB0dSBjw7NkaWdvCgoqIExsYW1hciBhIHVuYSBmdW5jacOzbiBSIGZpbmFsbWVudGUgbGxldmEgYWwgY8OzZGlnbyBDIG8gRk9SVFJBTgoKKiBFc3RlIGPDs2RpZ28gZXN0w6EgbXV5IG9wdGltaXphZG8KCiogXyoqTGEgbWV0YSBlcyoqXzogQWNjZWRhIGFsIGPDs2RpZ28gc3VieWFjZW50ZSBkZSBDIG8gRk9SVFJBTiBsbyBtw6FzIHLDoXBpZG8gcG9zaWJsZTsgY3VhbnRhcyBtZW5vcyBmdW5jaW9uZXMgc2UgbGxhbWFuIG1lam9yLgoKCgpFbCBzaWd1aWVudGUgZnJhZ21lbnRvIGRlIGPDs2RpZ28gZXN0w6EgZXNjcml0byBjb21vIGVsIGPDs2RpZ28gdHJhZGljaW9uYWwgQyBvIEZvcnRyYW4uIEVuIGx1Z2FyIGRlIHVzYXIgbGEgdmVyc2nDs24gdmVjdG9yaXphZGEgZGUgbGEgbXVsdGlwbGljYWNpw7NuLCB1c2EgdW4gY2ljbG8gZm9yLgoKYGBge3J9CnggPC0gcm5vcm0oMTApCngyIDwtIG51bWVyaWMobGVuZ3RoKHgpKQpmb3IoaSBpbiAxOjEwKQogICAgeDJbaV0gPC0geFtpXSAqIHhbaV0KYGBgCgoKTGEgaWRlYSBlcyBldml0YXIgZWwgdXNvIGV4dGVuc2l2byBkZSBsb3MgY2ljbG9zIGZvciwgZGViaWRvIGEgcXVlIFIgaW1wbGVtZW50YSBsYSB2ZWN0b3JpemFjacOzbi4gCgpgYGB7cn0KeCA8LSBybm9ybSgxMCkKeDIgPC0gbnVtZXJpYyhsZW5ndGgoeCkpCngyX2ltcCA8LSB4XjIgCngyX2ltcApgYGAKCgpVbmEgb3BlcmFjacOzbiBjb23Dum4gZW4gZXN0YWTDrXN0aWNhIGVzIGNhbGN1bGFyIGxhIHN1bWEgZGUgbG9zIGxvZ2FyaXRtb3MgZGUgbGFzIHByb2JhYmlsaWRhZGVzLiBFbCBzaWd1aWVudGUgY8OzZGlnbyBjYWxjdWxhIGVsIGxvZy1zdW0gKGxhIHN1bWEgZGUgbG9zIGxvZ2FyaXRtb3MpLgoKYGBge3J9CiMgSW5pdGlhbCBjb2RlCm4gPC0gMTAwCnRvdGFsIDwtIDAKeCA8LSBydW5pZihuKQpmb3IoaSBpbiAxOm4pIAogICAgdG90YWwgPC0gdG90YWwgKyBsb2coeFtpXSkKdG90YWwKCiMgUmV3cml0ZSBpbiBhIHNpbmdsZSBsaW5lLiBTdG9yZSB0aGUgcmVzdWx0IGluIGxvZ19zdW0KbG9nX3N1bSA8LSBzdW0obG9nKHgpKQpsb2dfc3VtCmBgYAoKIyMgV2VsY29tZSB0byBSIGNsdWIhCgogICAgICAgIC0gTGEgc2VndW5kYSByZWdsYSBkZWwgY2x1YiBSOiB1c2UgdW5hIHNvbHVjacOzbiB2ZWN0b3JpemFkYSBzaWVtcHJlIHF1ZSBzZWEgcG9zaWJsZS4KCiMjIE1hdHJpY2VzIHkgRGF0YSBGcmFtZQoKIyMjIERhdGEgRnJhbWUgCgoqIEVzdHJ1Y3R1cmEgZGUgZGF0b3MgY2xhdmUgZW4gUgoKKiBDb3BpYWRvIGVuIG90cm9zIGxlbmd1YWplcyAKCiAgICAgICAgLSBQeXRob246IHBhbmRhcyBkYXRhIGZyYW1lCiAgICAgICAgCgojIyMgTWF0cmljZXMgCgoqIEVzIHVuYSBlc3RydWN0dXJhIGRlIGRhdG9zIHJlY3Rhbmd1bGFyLiAKCiogUHVlZGUgcmVhbGl6YXIgb3BlcmFjaW9uZXMgZGUgc3ViY29uanVudG8geSBleHRyYWNjacOzbiBoYWJpdHVhbGVzLgoKVmVyIEVqZW1wbG8gMi4KCiMjIFdlbGNvbWUgdG8gUiBjbHViIQoKICAgICAgICAtIExhIHRlcmNlcmEgcmVnbGEgZGVsIGNsdWIgUjogdXNlIHVuYSBtYXRyaXogY3VhbmRvIHNlYSBhcHJvcGlhZG8uCgojIyBDb2RlIHByb2ZpbGluZwoKcHJvZmlsaW5nIGxlIGF5dWRhIGEgbG9jYWxpemFyIGxvcyBfY3VlbGxvcyBkZSBib3RlbGxhXyBlbiBzdSBjw7NkaWdvLiBFc3RlIGFwYXJ0YWRvIGxlIGVuc2XDsWEgYSB2aXN1YWxpemFyIGxvcyBjdWVsbG9zIGRlIGJvdGVsbGEgdXRpbGl6YW5kbyBlbCBwYXF1ZXRlIGBwcm9maXNgLgoKCkxhIGlkZWEgZ2VuZXJhbCBlczoKCiogRWplY3V0YSBlbCBjw7NkaWdvCgoqIENhZGEgcG9jb3MgbWlsaXNlZ3VuZG9zLCByZWdpc3RyZSBsbyBxdWUgc2UgZXN0w6EgZWplY3V0YW5kbyBhY3R1YWxtZW50ZQoKKiBScHJvZigpIHZpZW5lIGNvbiBSIHkgaGFjZSBleGFjdGFtZW50ZSBlc3RvLCBEaWbDrWNpbCBkZSB1c2FyCgoqIFVzZSBwcm9mdmlzIGVuIHN1IGx1Z2FyCgoKIyMgUGFyYXJyZWwgUHJvZ3JhbWluZyAKCiogQ1BVOiBjZXJlYnJvcyBkZSBsYSBjb21wdXRhZG9yYQoKKiBMYSB2ZWxvY2lkYWQgc2UgZXN0YWJpbGl6w7MgbGVudGFtZW50ZQoKICAgICAgICBfIExhcyBDUFUgc2UgZXN0YWJhbiBjYWxlbnRhbmRvIGRlbWFzaWFkbwoKKiBNdWx0aS1jb3JlIENQVXMKCiogUGVybyBSIHNvbG8gdXNhIDEgbsO6Y2xlbyA6KAoKKiBObyB0b2RvcyBsb3MgYW7DoWxpc2lzIHB1ZWRlbiBoYWNlciB1c28gZGUgbcO6bHRpcGxlcyBuw7pjbGVvcwoKKiBNdWNob3MgYWxnb3JpdG1vcyBlc3RhZMOtc3RpY29zIHNvbG8gcHVlZGVuIHVzYXIgdW4gc29sbyBuw7pjbGVvCgoKCmBgYHtyfQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KGJlbmNobWFya21lKQoKZGV0ZWN0Q29yZXMoKQoKZ2V0X2NwdSgpCgpgYGAKCiMjIENvcnJpZW5kbyBlbiBwYXJhbGVsbwoKIyMjIMK/UXXDqSB0aXBvIGRlIHByb2JsZW1hcyBzZSBiZW5lZmljaWFuIGRlIGxhIGluZm9ybcOhdGljYSBwYXJhbGVsYT8KCkxhIHNpZ3VpZW50ZSBwaWV6YSBkZSBjw7NkaWdvIGltcGxlbWVudGEgdW4gc2ltcGxlIGp1ZWdvIGRlIGRhZG9zLiBFbCBqdWVnbyBlcyBlbCBzaWd1aWVudGU6CgpgYGB7cn0KdG90YWwgPC0gbm9fb2Zfcm9sbHMgPC0gMCAjIEluaXRpYWxpc2UKd2hpbGUodG90YWwgPCAxMCkgewogICAgICAgIHRvdGFsIDwtIHRvdGFsICsgc2FtcGxlKDE6NiwgMSkKICAgICAgICAKICAgICAgICBpZih0b3RhbCAlJSAyID09IDApIHRvdGFsIDwtIDAgICMgSWYgZXZlbi4gUmVzZXQgdG8gMAogICAgICAgIAogICAgICAgIG5vX29mX3JvbGxzIDwtIG5vX29mX3JvbGxzICsgMQp9Cm5vX29mX3JvbGxzCmBgYAoKwr9DcmVlcyBxdWUgZXN0ZSBhbGdvcml0bW8gc2UgcHVlZGUgKGbDoWNpbG1lbnRlKSBlamVjdXRhciBlbiBwYXJhbGVsbz8KCiogTm86IGVzIHVuIGFsZ29yaXRtbyBzZWN1ZW5jaWFsLiBFbCBpLcOpc2ltbyB2YWxvciBkZXBlbmRlIGRlbCB2YWxvciBhbnRlcmlvci4KCkhhcyBkaXNmcnV0YWRvIHRhbnRvIGRlbCBqdWVnbyBkZSBkYWRvcyBhbnRlcmlvciwgwqFxdWllcmVzIGp1Z2FybG8gbXVjaGFzIHZlY2VzIS4gVnVlbHZlIGVsIGPDs2RpZ28gb3JpZ2luYWwgZW4gdW5hIGZ1bmNpw7NuCgpgYGB7cn0KcGxheSA8LSBmdW5jdGlvbigpIHsKICB0b3RhbCA8LSBub19vZl9yb2xscyA8LSAwCiAgd2hpbGUodG90YWwgPCAxMCkgewogICAgdG90YWwgPC0gdG90YWwgKyBzYW1wbGUoMTo2LCAxKQoKICAgICMgSWYgZXZlbi4gUmVzZXQgdG8gMAogICAgaWYodG90YWwgJSUgMiA9PSAwKSB0b3RhbCA8LSAwIAogICAgbm9fb2Zfcm9sbHMgPC0gbm9fb2Zfcm9sbHMgKyAxCiAgfQogIG5vX29mX3JvbGxzCn0KYGBgCgp5IGNvbnN0cnV5ZSB1biBjaWNsbyBwYXJhIGp1Z2FyIGVsIGp1ZWdvOgoKYGBge3J9CnJlc3VsdHMgPC0gbnVtZXJpYygxMDApCmZvcihpIGluIHNlcV9hbG9uZyhyZXN1bHRzKSkgCiAgICByZXN1bHRzW2ldIDwtIHBsYXkoKQpgYGAKCgrCv0NyZWVzIHF1ZSBlc3RlIGNpY2xvIGZvciBzZSBwdWVkZSBlamVjdXRhciAoZsOhY2lsbWVudGUpICBlbiBwYXJhbGVsbz8KClPDrSwgZXN0byBlcyBfZW1iYXJyYXNzaW5nbHkgcGFyYWxsZWxfLiBQb2RlbW9zIHNpbXVsYXIgbG9zIGp1ZWdvcyBlbiBjdWFscXVpZXIgb3JkZW4KCgojIyBDb252ZXJ0aXIgYSBwYXJhbGVsbwoKKiBDYXJndWUgZWwgcGFxdWV0ZQoKKiBFc3BlY2lmaXF1ZSBsYSBjYW50aWRhZCBkZSBuw7pjbGVvcwoKKiBDcmVhciB1biBvYmpldG8gZGUgY2zDunN0ZXIKCiogQ2FtYmlhciBhIHBhckFwcGx5KCkKCiogRGV0ZW5lcgoKCg==