Redes Neuronales

Concepto

Una Red Neuronal Artificial (ANN) modela la relación entre un conjunto de entradas y una salida, resolviedno un problema de aprendizaje.

Ejemplos de aplicación de Redes Neuronales:
1. Recomendación de contenido de Netflix.
2.El feed de Instagram o Tiktok.
3.Determinar el número de letra escrito o a mano.

Ejemplo

1. Instalar paquetes y llamar librerias

#install.packages("neuralnet")
library(neuralnet)

2.Alimentar con ejemplos

examen <- c(20,10,30,20,80,30)
proyecto <- c(90,20,40,50,50,80)
estatus <- c(1,0,0,0,0,1)
df <- data.frame(examen, proyecto, estatus)

3.Generar la Red Neuronal

red_neuronal <- neuralnet(estatus~.,data = df)
plot(red_neuronal, rep="best")

## 4.Predecir con la Red Neuronal

prueba_examen <- c(30,40,85)
prueba_proyecto <- c(85,50,40)
prueba <-data.frame(prueba_examen,prueba_proyecto)
prediccion <- compute(red_neuronal,prueba)
prediccion$net.result
##           [,1]
## [1,] 0.3325338
## [2,] 0.3325338
## [3,] 0.3325338
probabilidad <- prediccion$net.result
resultado <- ifelse(probabilidad>0.5,1,0)
resultado
##      [,1]
## [1,]    0
## [2,]    0
## [3,]    0

Redes Neuronales Cáncer de Mama

1. Importar base de datos

df_mama <- read.csv("/Users/ingridzapatajuarez/Downloads/R/cancer_de_mama.csv")
df_mama$diagnosis <- ifelse(df_mama$diagnosis == "M",1,0)

2. General la Red Neuronal

red_neuronal <- neuralnet(diagnosis ~., data=df_mama)
plot(red_neuronal, rep="best")

prueba <- df_mama[c(19,20,21,22,23),]
prediccion <- compute(red_neuronal, prueba)
prediccion$net.result
##         [,1]
## 19 0.3725924
## 20 0.3725924
## 21 0.3725924
## 22 0.3725924
## 23 0.3725924
probabilidad <- prediccion$net.result

resultado <- ifelse(probabilidad>0.5,1,0)
resultado
##    [,1]
## 19    0
## 20    0
## 21    0
## 22    0
## 23    0

Series de Tiempo

Una serie de tiempo es una colección de observaciones sobre un determinado fenómeno efectuadas en momentos de tiempo sucesivos, usualmente equiespaciados.

Ejemplo de series de tiempo son: 1. Precio de acciones 2. Nivel de inventario 3. Rotación 4. Ventas.
5. PIB (GDP).

1. Instalar paquetes y llamar librerías

#install.packages("forecast")
library(forecast)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo

2. Crear la Serie de Tiempo

#Ejemplo: Los siguientes datos de producción trimestral
#inician en el primer trimestre de 2020.
#Se busca pronosticar la producción de los siguiente 5 trimestres.

produccion <- c(50,53,55,57,55,60)

serie_de_tiempo <- ts(data=produccion, start = c(2020,1), frequency = 4)

3. Crear el modelo ARIMA

ARIMA significa AutoReggresive Integrated Moving Average o Modelo AutoRegresivo Integrado de Promedio Móvil.

modelo <- auto.arima(serie_de_tiempo, D=1)
modelo
## Series: serie_de_tiempo 
## ARIMA(0,0,0)(0,1,0)[4] with drift 
## 
## Coefficients:
##        drift
##       1.5000
## s.e.  0.1768
## 
## sigma^2 = 2.01:  log likelihood = -2.84
## AIC=9.68   AICc=-2.32   BIC=7.06
summary(modelo)
## Series: serie_de_tiempo 
## ARIMA(0,0,0)(0,1,0)[4] with drift 
## 
## Coefficients:
##        drift
##       1.5000
## s.e.  0.1768
## 
## sigma^2 = 2.01:  log likelihood = -2.84
## AIC=9.68   AICc=-2.32   BIC=7.06
## 
## Training set error measures:
##                      ME      RMSE       MAE        MPE      MAPE       MASE
## Training set 0.03333332 0.5787923 0.3666667 0.03685269 0.6429133 0.06111111
##                    ACF1
## Training set -0.5073047

4. Realizar el pronóstico

pronostico <- forecast(modelo, level=c(95), h=5)
pronostico
##         Point Forecast    Lo 95    Hi 95
## 2021 Q3             61 58.22127 63.77873
## 2021 Q4             63 60.22127 65.77873
## 2022 Q1             61 58.22127 63.77873
## 2022 Q2             66 63.22127 68.77873
## 2022 Q3             67 63.07028 70.92972
plot(pronostico)

Análisis de sentimientos y emociones Himno

library(syuzhet)
library(RColorBrewer)
texto_cadena <- scan(file = "/Users/ingridzapatajuarez/Downloads/R/himnonacional.txt", fileEncoding = "UTF-8", what = character(), sep = "\n", allowEscapes = T)
texto_palabras <- get_tokens(texto_cadena)
head(texto_palabras)
## [1] "mexicanos" "al"        "grito"     "de"        "guerra"    "el"
length(texto_palabras)
## [1] 496
oraciones_vector <- get_sentences(texto_cadena)
length(oraciones_vector)
## [1] 92
sentimientos_df <- get_nrc_sentiment(texto_palabras, lang="spanish")
summary(sentimientos_df)
##      anger         anticipation        disgust             fear       
##  Min.   :0.0000   Min.   :0.00000   Min.   :0.00000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.0000  
##  Median :0.0000   Median :0.00000   Median :0.00000   Median :0.0000  
##  Mean   :0.0625   Mean   :0.04234   Mean   :0.02218   Mean   :0.0746  
##  3rd Qu.:0.0000   3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.0000  
##  Max.   :3.0000   Max.   :2.00000   Max.   :2.00000   Max.   :2.0000  
##       joy             sadness           surprise           trust        
##  Min.   :0.00000   Min.   :0.00000   Min.   :0.00000   Min.   :0.00000  
##  1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.00000  
##  Median :0.00000   Median :0.00000   Median :0.00000   Median :0.00000  
##  Mean   :0.04839   Mean   :0.04234   Mean   :0.02621   Mean   :0.04032  
##  3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.00000  
##  Max.   :2.00000   Max.   :2.00000   Max.   :2.00000   Max.   :2.00000  
##     negative          positive     
##  Min.   :0.00000   Min.   :0.0000  
##  1st Qu.:0.00000   1st Qu.:0.0000  
##  Median :0.00000   Median :0.0000  
##  Mean   :0.08266   Mean   :0.1008  
##  3rd Qu.:0.00000   3rd Qu.:0.0000  
##  Max.   :3.00000   Max.   :3.0000
head(sentimientos_df)
##   anger anticipation disgust fear joy sadness surprise trust negative positive
## 1     0            0       0    0   0       0        0     0        0        0
## 2     0            0       0    0   0       0        0     0        0        0
## 3     2            0       0    1   0       1        2     0        2        0
## 4     0            0       0    0   0       0        0     0        0        0
## 5     1            0       0    2   0       1        0     0        2        0
## 6     0            0       0    0   0       0        0     0        0        0
barplot(
  colSums(prop.table(sentimientos_df[,1:8])),
  spce= 0.2,
  horiz = FALSE,
  las = 1, 
  cex.names = 0.7,
  col = brewer.pal(n=8, name= "Set3"),
  main = "Analisis de Himno Nacional Mexicano",
  xlab = "emociones"
)
## Warning in plot.window(xlim, ylim, log = log, ...): "spce" is not a graphical
## parameter
## Warning in axis(if (horiz) 2 else 1, at = at.l, labels = names.arg, lty =
## axis.lty, : "spce" is not a graphical parameter
## Warning in title(main = main, sub = sub, xlab = xlab, ylab = ylab, ...): "spce"
## is not a graphical parameter
## Warning in axis(if (horiz) 1 else 2, cex.axis = cex.axis, ...): "spce" is not a
## graphical parameter

palabras_miedo <- texto_palabras[sentimientos_df$fear >0]
palabras_miedo_orden <- sort(table(unlist(palabras_miedo)), decreasing=TRUE)
head(palabras_miedo_orden, n = 10)
## 
##    guerra    sangre     golpe  guerrero  metralla   destino      dios   enemigo 
##         8         4         2         2         2         1         1         1 
##     grito impotente 
##         1         1
secuencia_sentimientos <- (sentimientos_df$negative*-1) + sentimientos_df$positive
simple_plot(secuencia_sentimientos)

Shiny App análisis de sentimientos, Himno y Miau

Nota Importante

Profesor, la verdad no sé que le haya pasado a mi shiny pero tengo entendido que tengo todo bien pero el análisis de sentimientos no me sale en la Shiny App. Comoquiera dejé el código para ver si no existía algún error. Gracias.

link

LS0tCnRpdGxlOiAiQWN0aXZpZGFkIDQuMiBJbnRlZ3JhZG9yYSIKYXV0aG9yOiAiSW5ncmlkIFphcGF0YSIKZGF0ZTogIjIwMjMtMTAtMDkiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKICAgIHRoZW1lOiAic2ltcGxleCIKICAgIGhpZ2hsaWdodDogIm1vbm9jaHJvbWUiCi0tLQoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5SZWRlcyBOZXVyb25hbGVzPC9zcGFuPiAKCgohW10oL1VzZXJzL2luZ3JpZHphcGF0YWp1YXJlei9Eb3dubG9hZHMvUi9yZWRlc25ldXJvbmFsZXMuZ2lmKQoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5Db25jZXB0bzwvc3Bhbj4gIAoKVW5hIFJlZCBOZXVyb25hbCBBcnRpZmljaWFsIChBTk4pIG1vZGVsYSBsYSByZWxhY2nDs24gZW50cmUgdW4gY29uanVudG8gZGUgZW50cmFkYXMgeSB1bmEgc2FsaWRhLCByZXNvbHZpZWRubyB1biBwcm9ibGVtYSBkZSBhcHJlbmRpemFqZS4gIAoKRWplbXBsb3MgZGUgYXBsaWNhY2nDs24gZGUgUmVkZXMgTmV1cm9uYWxlczogIAoxLiBSZWNvbWVuZGFjacOzbiBkZSBjb250ZW5pZG8gZGUgTmV0ZmxpeC4gIAoyLkVsIGZlZWQgZGUgSW5zdGFncmFtIG8gVGlrdG9rLiAgCjMuRGV0ZXJtaW5hciBlbCBuw7ptZXJvIGRlIGxldHJhIGVzY3JpdG8gbyBhIG1hbm8uICAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+RWplbXBsbzwvc3Bhbj4gIAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+MS4gSW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyaWFzPC9zcGFuPiAKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygibmV1cmFsbmV0IikKbGlicmFyeShuZXVyYWxuZXQpCmBgYAojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij4yLkFsaW1lbnRhciBjb24gZWplbXBsb3M8L3NwYW4+IAoKYGBge3J9CmV4YW1lbiA8LSBjKDIwLDEwLDMwLDIwLDgwLDMwKQpwcm95ZWN0byA8LSBjKDkwLDIwLDQwLDUwLDUwLDgwKQplc3RhdHVzIDwtIGMoMSwwLDAsMCwwLDEpCmRmIDwtIGRhdGEuZnJhbWUoZXhhbWVuLCBwcm95ZWN0bywgZXN0YXR1cykKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij4zLkdlbmVyYXIgbGEgUmVkIE5ldXJvbmFsPC9zcGFuPiAKCmBgYHtyfQpyZWRfbmV1cm9uYWwgPC0gbmV1cmFsbmV0KGVzdGF0dXN+LixkYXRhID0gZGYpCnBsb3QocmVkX25ldXJvbmFsLCByZXA9ImJlc3QiKQpgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+NC5QcmVkZWNpciBjb24gbGEgUmVkIE5ldXJvbmFsPC9zcGFuPiAKCmBgYHtyfQpwcnVlYmFfZXhhbWVuIDwtIGMoMzAsNDAsODUpCnBydWViYV9wcm95ZWN0byA8LSBjKDg1LDUwLDQwKQpwcnVlYmEgPC1kYXRhLmZyYW1lKHBydWViYV9leGFtZW4scHJ1ZWJhX3Byb3llY3RvKQpwcmVkaWNjaW9uIDwtIGNvbXB1dGUocmVkX25ldXJvbmFsLHBydWViYSkKcHJlZGljY2lvbiRuZXQucmVzdWx0CnByb2JhYmlsaWRhZCA8LSBwcmVkaWNjaW9uJG5ldC5yZXN1bHQKcmVzdWx0YWRvIDwtIGlmZWxzZShwcm9iYWJpbGlkYWQ+MC41LDEsMCkKcmVzdWx0YWRvCmBgYAoKPGNlbnRlcj4KCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+UmVkZXMgTmV1cm9uYWxlcyBDw6FuY2VyIGRlIE1hbWE8L3NwYW4+IAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij4xLiBJbXBvcnRhciBiYXNlIGRlIGRhdG9zPC9zcGFuPiAKCmBgYHtyfQpkZl9tYW1hIDwtIHJlYWQuY3N2KCIvVXNlcnMvaW5ncmlkemFwYXRhanVhcmV6L0Rvd25sb2Fkcy9SL2NhbmNlcl9kZV9tYW1hLmNzdiIpCmRmX21hbWEkZGlhZ25vc2lzIDwtIGlmZWxzZShkZl9tYW1hJGRpYWdub3NpcyA9PSAiTSIsMSwwKQpgYGAKCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiID4yLiBHZW5lcmFsIGxhIFJlZCBOZXVyb25hbDwvc3Bhbj4KYGBge3J9CnJlZF9uZXVyb25hbCA8LSBuZXVyYWxuZXQoZGlhZ25vc2lzIH4uLCBkYXRhPWRmX21hbWEpCnBsb3QocmVkX25ldXJvbmFsLCByZXA9ImJlc3QiKQpgYGAKCmBgYHtyfQpwcnVlYmEgPC0gZGZfbWFtYVtjKDE5LDIwLDIxLDIyLDIzKSxdCnByZWRpY2Npb24gPC0gY29tcHV0ZShyZWRfbmV1cm9uYWwsIHBydWViYSkKcHJlZGljY2lvbiRuZXQucmVzdWx0CnByb2JhYmlsaWRhZCA8LSBwcmVkaWNjaW9uJG5ldC5yZXN1bHQKCnJlc3VsdGFkbyA8LSBpZmVsc2UocHJvYmFiaWxpZGFkPjAuNSwxLDApCnJlc3VsdGFkbwpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+U2VyaWVzIGRlIFRpZW1wbzwvc3Bhbj4gCgohW10oL1VzZXJzL2luZ3JpZHphcGF0YWp1YXJlei9Eb3dubG9hZHMvUi90aW1lc2VyaWVzLmdpZikKClVuYSAqc2VyaWUgZGUgdGllbXBvKiBlcyB1bmEgY29sZWNjacOzbiBkZSBvYnNlcnZhY2lvbmVzIHNvYnJlIHVuIGRldGVybWluYWRvIGZlbsOzbWVubyBlZmVjdHVhZGFzIGVuIG1vbWVudG9zIGRlIHRpZW1wbyBzdWNlc2l2b3MsIHVzdWFsbWVudGUgZXF1aWVzcGFjaWFkb3MuCgogIEVqZW1wbG8gZGUgc2VyaWVzIGRlIHRpZW1wbyBzb246CiAgMS4gUHJlY2lvIGRlIGFjY2lvbmVzCiAgMi4gTml2ZWwgZGUgaW52ZW50YXJpbwogIDMuIFJvdGFjacOzbgogIDQuIFZlbnRhcy4gICAKICA1LiBQSUIgKEdEUCkuICAgCgogIAojIyA8c3BhbiBzdHlsZT0iY29sb3I6IG9yYW5nZTsiPjEuIEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcsOtYXM8L3NwYW4+CgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImZvcmVjYXN0IikKbGlicmFyeShmb3JlY2FzdCkKYGBgCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogb3JhbmdlOyI+Mi4gQ3JlYXIgbGEgU2VyaWUgZGUgVGllbXBvPC9zcGFuPgpgYGB7cn0KI0VqZW1wbG86IExvcyBzaWd1aWVudGVzIGRhdG9zIGRlIHByb2R1Y2Npw7NuIHRyaW1lc3RyYWwKI2luaWNpYW4gZW4gZWwgcHJpbWVyIHRyaW1lc3RyZSBkZSAyMDIwLgojU2UgYnVzY2EgcHJvbm9zdGljYXIgbGEgcHJvZHVjY2nDs24gZGUgbG9zIHNpZ3VpZW50ZSA1IHRyaW1lc3RyZXMuCgpwcm9kdWNjaW9uIDwtIGMoNTAsNTMsNTUsNTcsNTUsNjApCgpzZXJpZV9kZV90aWVtcG8gPC0gdHMoZGF0YT1wcm9kdWNjaW9uLCBzdGFydCA9IGMoMjAyMCwxKSwgZnJlcXVlbmN5ID0gNCkKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IG9yYW5nZTsiPjMuIENyZWFyIGVsIG1vZGVsbyBBUklNQTwvc3Bhbj4KCioqQVJJTUEqKiBzaWduaWZpY2EgKkF1dG9SZWdncmVzaXZlIEludGVncmF0ZWQgTW92aW5nIEF2ZXJhZ2UqIG8gKk1vZGVsbyAKQXV0b1JlZ3Jlc2l2byBJbnRlZ3JhZG8gZGUgUHJvbWVkaW8gTcOzdmlsKi4KCmBgYHtyfQptb2RlbG8gPC0gYXV0by5hcmltYShzZXJpZV9kZV90aWVtcG8sIEQ9MSkKbW9kZWxvCnN1bW1hcnkobW9kZWxvKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogb3JhbmdlOyI+NC4gUmVhbGl6YXIgZWwgcHJvbsOzc3RpY288L3NwYW4+CmBgYHtyfQpwcm9ub3N0aWNvIDwtIGZvcmVjYXN0KG1vZGVsbywgbGV2ZWw9Yyg5NSksIGg9NSkKcHJvbm9zdGljbwpwbG90KHByb25vc3RpY28pCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5BbsOhbGlzaXMgZGUgc2VudGltaWVudG9zIHkgZW1vY2lvbmVzIEhpbW5vPC9zcGFuPiAKCmBgYHtyfQpsaWJyYXJ5KHN5dXpoZXQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCmBgYHtyfQp0ZXh0b19jYWRlbmEgPC0gc2NhbihmaWxlID0gIi9Vc2Vycy9pbmdyaWR6YXBhdGFqdWFyZXovRG93bmxvYWRzL1IvaGltbm9uYWNpb25hbC50eHQiLCBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiLCB3aGF0ID0gY2hhcmFjdGVyKCksIHNlcCA9ICJcbiIsIGFsbG93RXNjYXBlcyA9IFQpCmBgYAoKYGBge3J9CnRleHRvX3BhbGFicmFzIDwtIGdldF90b2tlbnModGV4dG9fY2FkZW5hKQpoZWFkKHRleHRvX3BhbGFicmFzKQpsZW5ndGgodGV4dG9fcGFsYWJyYXMpCm9yYWNpb25lc192ZWN0b3IgPC0gZ2V0X3NlbnRlbmNlcyh0ZXh0b19jYWRlbmEpCmxlbmd0aChvcmFjaW9uZXNfdmVjdG9yKQpgYGAKCmBgYHtyfQpzZW50aW1pZW50b3NfZGYgPC0gZ2V0X25yY19zZW50aW1lbnQodGV4dG9fcGFsYWJyYXMsIGxhbmc9InNwYW5pc2giKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KHNlbnRpbWllbnRvc19kZikKaGVhZChzZW50aW1pZW50b3NfZGYpCmBgYAoKYGBge3J9CmJhcnBsb3QoCiAgY29sU3Vtcyhwcm9wLnRhYmxlKHNlbnRpbWllbnRvc19kZlssMTo4XSkpLAogIHNwY2U9IDAuMiwKICBob3JpeiA9IEZBTFNFLAogIGxhcyA9IDEsIAogIGNleC5uYW1lcyA9IDAuNywKICBjb2wgPSBicmV3ZXIucGFsKG49OCwgbmFtZT0gIlNldDMiKSwKICBtYWluID0gIkFuYWxpc2lzIGRlIEhpbW5vIE5hY2lvbmFsIE1leGljYW5vIiwKICB4bGFiID0gImVtb2Npb25lcyIKKQpgYGAKCmBgYHtyfQpwYWxhYnJhc19taWVkbyA8LSB0ZXh0b19wYWxhYnJhc1tzZW50aW1pZW50b3NfZGYkZmVhciA+MF0KcGFsYWJyYXNfbWllZG9fb3JkZW4gPC0gc29ydCh0YWJsZSh1bmxpc3QocGFsYWJyYXNfbWllZG8pKSwgZGVjcmVhc2luZz1UUlVFKQpoZWFkKHBhbGFicmFzX21pZWRvX29yZGVuLCBuID0gMTApCnNlY3VlbmNpYV9zZW50aW1pZW50b3MgPC0gKHNlbnRpbWllbnRvc19kZiRuZWdhdGl2ZSotMSkgKyBzZW50aW1pZW50b3NfZGYkcG9zaXRpdmUKc2ltcGxlX3Bsb3Qoc2VjdWVuY2lhX3NlbnRpbWllbnRvcykKYGBgCgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5TaGlueSBBcHAgYW7DoWxpc2lzIGRlIHNlbnRpbWllbnRvcywgSGltbm8geSBNaWF1PC9zcGFuPiAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPk5vdGEgSW1wb3J0YW50ZTwvc3Bhbj4gCgpQcm9mZXNvciwgbGEgdmVyZGFkIG5vIHPDqSBxdWUgbGUgaGF5YSBwYXNhZG8gYSBtaSBzaGlueSBwZXJvIHRlbmdvIGVudGVuZGlkbyBxdWUgdGVuZ28gdG9kbyBiaWVuIHBlcm8gZWwgYW7DoWxpc2lzIGRlIHNlbnRpbWllbnRvcyBubyBtZSBzYWxlIGVuIGxhIFNoaW55IEFwcC4gQ29tb3F1aWVyYSBkZWrDqSBlbCBjw7NkaWdvIHBhcmEgdmVyIHNpIG5vIGV4aXN0w61hIGFsZ8O6biBlcnJvci4gR3JhY2lhcy4KCltsaW5rXShodHRwczovL2luZ3JpZHphcGF0YS5zaGlueWFwcHMuaW8vQVBQNS8p