Preguntas

¿Qué es la Inteligencia Locacional (Location Intelligence)? Es la forma en la que se logran realizar una vizualización y analisis de datos geoespaciales, con el fin de tomar decisiones estrategicas en base a la ubicación. Todo gracias a la capacidad de identificar patrones, por su capacidad de recopilar, procesar y analizar la información geografica

A partir del video “Why is Location Intelligence Vital for Successful Business?” (https://www.youtube.com/watch?v=7YAlv93nQiY) y las respuestas a las instrucciones 1) – 10) ¿Cómo contribuye el proceso de análisis de Inteligencia Locacional a la Inteligencia de Negocios? Al brindar datos geograficos al analisis de los datos, permite tomar mejores decisiones estrategicas, como mejores estrategias de marketing, mejor seleccion de localización de un negocio, identificar donde se encuentra el mayor flujo de clientela en cierta localización, entre otras.

Brevemente describir con tus propias palabras qué es la herramienta Análisis de Sentimientos / Sentiment Analysis. ¿Porqué la herramienta Sentiment Analysis es relevante en el desarrollo de la Inteligencia de Negocios (Business Intelligence)? Procesa el lenguaje natural para identificar y clasificar las opiniones en texto como reseñas o opiniones, y clasificarlos como positivas, negativas o neutrales.

Loading Required Data & Maps

Libraries

### LOADING REQUIRED LIBRARIES 
# data analysis 
library(dplyr)            # grammar of data manipulation helping to resolve data manipulation difficulties 
library(Hmisc)            # useful functions for data analysis and high - level graphics
library(foreign)          # read data stored by Minitab, SPSS, Stata
library(openxlsx)         # open, read, write,and edit xlsx files 
library(tidyverse)        # collection of R packages designed for data science

# spatial data analysis 
library(leaflet.extras)   # to provide extra functionality to the leaflet R package
library(sp)               # functions for plotting data as maps, spatial selection, methods for retrieving coordinates 
library(sf)               # encode spatial vector data 
library(maps)             # geographic maps
library(tmap)             # generate thematic maps
library(spdep)            # a collection of functions to create spatial weights matrices from polygon contiguities
library(terra)            # methods for spatial data analysis 
library(leaflet)          # interactive maps
library(mapproj)          # map projections 
library(mapsapi)          # 
library(spatialreg)       # spatial regression models 
library(sfdep)            # an interface to 'spdep' to integrate with 'sf' objects and the 'tidyverse
library(tidygeocoder)     # makes getting data from geocoding services easy
library(mapboxapi)        # 'Mapbox' Navigation APIs, including directions, isochrones, and route optimization.

# visualization 
library(ggmap)            # spatial data visualization 
library(rgeoda)           # spatial data analysis based on software GeoDa
library(ggplot2)          # Grammar of graphics. System for declarative creating graphics
library(corrplot)         # provides a visual exploratory tool on correlation matrix
library(RColorBrewer)     # offers several color palettes
library(leafsync)         # create small multiples of several leaflet web maps
library(htmltools)        # tools for HTML generation and output 

# others 
library(rlang)            # collection of frameworks and APIs for programming with R
library(classInt)         # methods for choosing univariate class intervals for mapping or other graphic purposes
library(gridExtra)        # to arrange and combine plots for easy comparison
library(knitr)            # integrates computing and reporting

### Getting access to distance, reviews, and ratings by using Google Maps
library(tm)               # a framework for text mining applications
library(wordcloud)        # functionality to create pretty word clouds
library(wordcloud2)       # 
library(googleway)        # provides a mechanism to access various Google Maps APIs, including plotting a Google Map from R and overlaying it with shapes and markers, and retrieving data from the places, directions, roads, distances, geocoding, elevation and timezone APIs
library(gmapsdistance)    # allows to calculate distances for a database through Google maps
# library(hereR)          # geocode and autocomplete addresses or reverse geocode POIs using the Geocoder API
library(osrm)             # enables the computation of routes, trips, isochrones and travel distances matrices (travel time and kilometric distance).

### Text Mining
library(tm)         # text mining functions 
library(syuzhet)    # includes four sentiment dictionaries and provides a method for accessing the robust, but computationally expensive, sentiment extraction tool developed in the NLP group at Stanford.
library(SnowballC)
library(wordcloud)
library(wordcloud2)

# library(remotes)
# library(openrouteservice)
# remotes::install_github("GIScience/openrouteservice-r")

GOOGLE MAPS API KEY

Monterrey Comida China Mapping

Mty <- leaflet() %>% 
  addTiles() %>% 
  setView(-100.31094, 25.66928, zoom = 15) %>% ### Mty downtown area
  addMarkers(-100.31094, 25.66928, popup = "Monterrey Downtown Area")
Mty
### Map coordinates of Parque Fundidora (Starting Point)
latitude  <- 25.67885
longitude <- -100.28418
r <- 15000
### Use the google_places function to make a call to the API and save the results
search_str <- google_places(search_string = 'restaurante chino', location=c(latitude,longitude), radius=r, key=gmaps_key)
### we can visualize the results (hospitals) got from google_places()
# search_str$results
### The page_token is the way to tell Google to return the next 20 results in the search instead of only the first 20
search_str_add_one <- google_places(search_string = 'restaurante chino', location=c(latitude,longitude), radius=r, key=gmaps_key, page_token = search_str$next_page_token)
### The page_token is the way to tell Google to return the next 20 results in the search instead of only the first 40
search_str_add_two <- google_places(search_string = 'restaurante chino', location=c(latitude,longitude), radius=r, key=gmaps_key, page_token = search_str_add_one$next_page_token)
### The data frame will give you a wealth of information about each place, including its address, latitude & longitude coordinates, price level, star rating, number of ratings, categories, and more.
business_name <- c(search_str$results$name, search_str_add_one$results$name, search_str_add_two$results$name)
business_rating <- c(search_str$results$rating, search_str_add_one$results$rating, search_str_add_two$results$rating)
user_ratings_total <- c(search_str$results$user_ratings_total, search_str_add_one$results$user_ratings_total,search_str_add_two$results$user_ratings_total)
place_id <- c(search_str$results$place_id, search_str_add_one$results$place_id, search_str_add_two$results$place_id)
lat <- c(search_str$results$geometry$location$lat, search_str_add_one$results$geometry$location$lat, search_str_add_two$results$geometry$location$lat)
lon <- c(search_str$results$geometry$location$lng, search_str_add_one$results$geometry$location$lng, search_str_add_two$results$geometry$location$lng)
# write.csv(data, "D:\\CD2001C_AD2024\\Power_BI_Data_AD2024\\data_location_intl.csv", row.names=TRUE)
data <- data.frame(business_name,business_rating,user_ratings_total,place_id,lat,lon)
# write.csv(data, "D:\\CD2001C_AD2024\\Power_BI_Data_AD2024\\data_location_intl.csv", row.names=TRUE)
data <- data %>% distinct(business_name, .keep_all = TRUE)
data_top_ratings <- data %>% slice_max(business_rating, n = 9)
data_low_ratings <- data %>% slice_min(business_rating, n = 9)
### Lets visualize the reviews information by coffee shop
top_ratings_plot <- ggplot(data_top_ratings, aes(x=reorder(business_name,business_rating), y=business_rating)) +
  geom_bar(stat="identity", fill="lightblue") + 
  labs(title="Restaurantes chinos - Top 10 Mejores Ratings", subtitle = "ZMM") + 
  coord_flip()
top_ratings_plot

low_ratings_plot <- ggplot(data_top_ratings, aes(x=reorder(business_name,user_ratings_total), y=user_ratings_total)) + 
  geom_bar(stat="identity", fill="lightblue") +
  labs(title="Restaurante Chino - Top 10 más visitados", subtitle = "ZMM") + 
  coord_flip()
low_ratings_plot

top_users_plot <- ggplot(data_low_ratings, aes(x=reorder(business_name,business_rating), y=business_rating)) + 
  geom_bar(stat="identity", fill="lightpink") + 
  labs(title="Restaurante Chino - Top 10 Peores Ratings", subtitle = "ZMM") + 
  coord_flip()
top_users_plot

low_users_plot <- ggplot(data_low_ratings, aes(x=reorder(business_name,user_ratings_total), y=user_ratings_total)) + 
  geom_bar(stat="identity", fill="lightpink")+ 
  labs(title="Restaurante Chino - Top 10 Menos visitados", subtitle = "ZMM") + 
  coord_flip()
low_users_plot

Heatmap of Top / Lowest Ratings Across ZMM

### Where in ZMM are located the hospitals characterized by the highest ratings?
register_google(key = gmaps_key)
ggmap(get_googlemap(center = c(lon = longitude, lat = latitude), zoom = 11)) +
        stat_density2d(data = data_top_ratings, aes(lon, lat, fill = after_stat(level)), 
                       geom = "polygon", alpha = 0.40) +
        scale_fill_gradient(low = "green", high = "red", guide = "none") +
  labs(x = '', y = '', title = "ZMM - Restaurantes Chinos con mejores ratings")

### Where in ZMM are located the Hospitals characterized by the lowest ratings?
ggmap(get_googlemap(center = c(lon = longitude, lat = latitude), zoom = 11)) +
  stat_density2d(data = data_low_ratings, aes(lon, lat, fill = after_stat(level)), 
                 geom = "polygon", alpha = 0.42) +
  scale_fill_gradient(low = "green", high = "red", guide = "none") +
  labs(x = '', y = '', title = "ZMM - Restaurantes Chinos con peor ratings")
## ℹ <https://maps.googleapis.com/maps/api/staticmap?center=25.67885,-100.28418&zoom=11&size=640x640&scale=2&maptype=terrain&key=xxx-TzTcERfzJ3o>

Text Analysis

## request more details about the comida china using google_place_details()
reviews_top_ratings <- google_place_details(place_id = data_top_ratings$place_id[8], key = gmaps_key)
reviews_low_ratings <- google_place_details(place_id = data_low_ratings$place_id[8], key = gmaps_key)
# reviews_top_ratings$result$reviews$text
# reviews_low_ratings$result$reviews$text
### Generate a vector containing only the text
top_ratings_text  <- reviews_top_ratings$result$reviews$text
top_ratings_doc   <- Corpus(VectorSource(top_ratings_text))
low_ratings_text  <- reviews_low_ratings$result$reviews$text
low_ratings_doc   <- Corpus(VectorSource(low_ratings_text))
### Clean the text data
options(warn=-1)
top_ratings_doc <- top_ratings_doc %>% tm_map(removeNumbers) %>% tm_map(removePunctuation) %>% tm_map(stripWhitespace)
top_ratings_doc <- tm_map(top_ratings_doc, content_transformer(tolower))
top_ratings_doc <- tm_map(top_ratings_doc, removeWords, stopwords("english"))
options(warn=-1)
low_ratings_doc <- low_ratings_doc %>% tm_map(removeNumbers) %>% tm_map(removePunctuation) %>% tm_map(stripWhitespace)
low_ratings_doc <- tm_map(low_ratings_doc, content_transformer(tolower))
low_ratings_doc <- tm_map(low_ratings_doc, removeWords, stopwords("english"))
### Lets create a dataframe containing each word in the first column and their frequency in the second column.
options(warn=-1)
dtm_top <- TermDocumentMatrix(top_ratings_doc) 
matrix_top <- as.matrix(dtm_top) 
words_top <- sort(rowSums(matrix_top),decreasing=TRUE) 
words_top_df <- data.frame(word = names(words_top),freq=words_top)
# write.csv(words_top_df, "D:\\CD2001C_AD2024\\Power_BI_Data_AD2024\\wordcloud_a.csv", row.names=TRUE)
options(warn=-1)
dtm_low <- TermDocumentMatrix(low_ratings_doc) 
matrix_low <- as.matrix(dtm_low) 
words_low <- sort(rowSums(matrix_low),decreasing=TRUE) 
words_low_df <- data.frame(word = names(words_low),freq=words_low)
### We can now generate the word cloud according to the top and low ratings reviews.
set.seed(1234) # for reproducibility 
### top ratings 
# top_raiting_wc<-wordcloud(words = words_top_df$word, freq = words_top_df$freq, min.freq = 1, max.words=200, random.order=FALSE, rot.per=0.35, colors=brewer.pal(8, "Dark2"))
top_rating_wc <- wordcloud2(data = words_top_df, color = "random-dark", size = 0.6, shape = "circle")
top_rating_wc
### low ratings
#low_raiting_wc<-wordcloud(words = words_low_df$word, freq = words_low_df$freq, min.freq = 1, max.words=200, random.order=FALSE, rot.per=0.35, colors=brewer.pal(8, "Dark2"))
low_rating_wc <- wordcloud2(data = words_low_df, color = "random-dark", size = 0.6, shape = "circle")
low_rating_wc

TEXY ANALYSIS & ANALYZE WORD FREQUENCIES

## request more details about the restaurant using google_place_details()
C_China_reviews <- google_place_details(place_id = data$place_id[15], language = "es", key = "AIzaSyDMtkIEvUpFccyUCGxLgXO-TzTcERfzJ3o")
C_China_reviews
## $html_attributions
## list()
## 
## $result
## $result$address_components
##                    long_name               short_name
## 1                        550                      550
## 2 Avenida Manuel L. Barragán   Av. Manuel L. Barragán
## 3           Valle de Anáhuac         Valle de Anáhuac
## 4   San Nicolás de los Garza San Nicolás de los Garza
## 5                 Nuevo León                     N.L.
## 6                     México                       MX
## 7                      66457                    66457
##                                         types
## 1                               street_number
## 2                                       route
## 3 sublocality_level_1, sublocality, political
## 4                         locality, political
## 5      administrative_area_level_1, political
## 6                          country, political
## 7                                 postal_code
## 
## $result$adr_address
## [1] "Local 110 Plaza La Joya, <span class=\"street-address\">Av. Manuel L. Barragán 550</span>, <span class=\"extended-address\">Valle de Anáhuac</span>, <span class=\"postal-code\">66457</span> <span class=\"locality\">San Nicolás de los Garza</span>, <span class=\"region\">N.L.</span>, <span class=\"country-name\">México</span>"
## 
## $result$business_status
## [1] "OPERATIONAL"
## 
## $result$current_opening_hours
## $result$current_opening_hours$open_now
## [1] TRUE
## 
## $result$current_opening_hours$periods
##   close.date close.day close.time  open.date open.day open.time
## 1 2025-03-16         0       2300 2025-03-16        0      1100
## 2 2025-03-17         1       2300 2025-03-17        1      1100
## 3 2025-03-18         2       2300 2025-03-18        2      1100
## 4 2025-03-12         3       2300 2025-03-12        3      1100
## 5 2025-03-13         4       2300 2025-03-13        4      1100
## 6 2025-03-15         6       0000 2025-03-14        5      1100
## 7 2025-03-16         0       0000 2025-03-15        6      1100
## 
## $result$current_opening_hours$weekday_text
## [1] "lunes: 11:00–23:00"     "martes: 11:00–23:00"    "miércoles: 11:00–23:00"
## [4] "jueves: 11:00–23:00"    "viernes: 11:00–24:00"   "sábado: 11:00–24:00"   
## [7] "domingo: 11:00–23:00"  
## 
## 
## $result$delivery
## [1] TRUE
## 
## $result$dine_in
## [1] TRUE
## 
## $result$editorial_summary
## $result$editorial_summary$language
## [1] "es-419"
## 
## $result$editorial_summary$overview
## [1] "Restaurante casual que ofrece un menú de fusión de varias cocinas asiáticas, como sushi y guisados al wok."
## 
## 
## $result$formatted_address
## [1] "Local 110 Plaza La Joya, Av. Manuel L. Barragán 550, Valle de Anáhuac, 66457 San Nicolás de los Garza, N.L., México"
## 
## $result$formatted_phone_number
## [1] "81 3233 2215"
## 
## $result$geometry
## $result$geometry$location
## $result$geometry$location$lat
## [1] 25.73768
## 
## $result$geometry$location$lng
## [1] -100.3147
## 
## 
## $result$geometry$viewport
## $result$geometry$viewport$northeast
## $result$geometry$viewport$northeast$lat
## [1] 25.73907
## 
## $result$geometry$viewport$northeast$lng
## [1] -100.3133
## 
## 
## $result$geometry$viewport$southwest
## $result$geometry$viewport$southwest$lat
## [1] 25.73637
## 
## $result$geometry$viewport$southwest$lng
## [1] -100.316
## 
## 
## 
## 
## $result$icon
## [1] "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/restaurant-71.png"
## 
## $result$icon_background_color
## [1] "#FF9E67"
## 
## $result$icon_mask_base_uri
## [1] "https://maps.gstatic.com/mapfiles/place_api/icons/v2/restaurant_pinlet"
## 
## $result$international_phone_number
## [1] "+52 81 3233 2215"
## 
## $result$name
## [1] "Oriental Wok Anáhuac"
## 
## $result$opening_hours
## $result$opening_hours$open_now
## [1] TRUE
## 
## $result$opening_hours$periods
##   close.day close.time open.day open.time
## 1         0       2300        0      1100
## 2         1       2300        1      1100
## 3         2       2300        2      1100
## 4         3       2300        3      1100
## 5         4       2300        4      1100
## 6         6       0000        5      1100
## 7         0       0000        6      1100
## 
## $result$opening_hours$weekday_text
## [1] "lunes: 11:00–23:00"     "martes: 11:00–23:00"    "miércoles: 11:00–23:00"
## [4] "jueves: 11:00–23:00"    "viernes: 11:00–24:00"   "sábado: 11:00–24:00"   
## [7] "domingo: 11:00–23:00"  
## 
## 
## $result$photos
##    height
## 1    4032
## 2    3024
## 3    3472
## 4    4032
## 5    4032
## 6    3024
## 7    1868
## 8    4608
## 9    2448
## 10   4624
##                                                                                   html_attributions
## 1              <a href="https://maps.google.com/maps/contrib/104595298535586425627">luis avelar</a>
## 2          <a href="https://maps.google.com/maps/contrib/109197942733752373678">Guadalupe Cantu</a>
## 3     <a href="https://maps.google.com/maps/contrib/115153180849401781964">Lupita Barrios Muñoz</a>
## 4            <a href="https://maps.google.com/maps/contrib/100128616113942673919">Dulce Rodarte</a>
## 5              <a href="https://maps.google.com/maps/contrib/111367996407708239408">Dalia Solís</a>
## 6                  <a href="https://maps.google.com/maps/contrib/109974487658740371237">Leon RM</a>
## 7        <a href="https://maps.google.com/maps/contrib/116328250776931654857">Iván López García</a>
## 8             <a href="https://maps.google.com/maps/contrib/117641540256688034014">Suzett Jasso</a>
## 9  <a href="https://maps.google.com/maps/contrib/114244301252710537367">Dann Hernandez (JD5094)</a>
## 10          <a href="https://maps.google.com/maps/contrib/109983946338487252057">Angeles Barrón</a>
##                                                                                                                                                                                                                                                                                                                                    photo_reference
## 1  AUy1YQ2eJTlJBff7FvPOWayfpgjY6VG3WoqVfVUPxgDjqW8ffQ3rmM77CNSTXl1Envxg7mq4pttkRwn0R_vaq3is5GgJXTXJW7lh0_AzuUWVw9cLUCLDWk-Hhoqps0gO1GU4xRxraljO54KVeqnKA2_Aw97oHoPzVmG3vZ5BHejWuHUjW-F-g26HDl12R_QWADGeadsg7Gl4Pe4XXGJjI2Os3r_dIvYKH5ZXqMekecVlY5J7hqTXIliPayF3Sm7mlkm3QlUkjzAKACZaqzJmcic6OxaKDcEDen6tu9IRaamsIao42p4H8ZTq0g5nbGN4CBIBoQEeN0TxL-4
## 2  AUy1YQ10YTwe8qLcH4kpM7jHo96dxzyjnwI0bGkJDKtGScxDTCw-f46RVh6DoSITfXNoWHI2rgYUt5WfZrx3C0EsrfkeOfurmND8H26ru_06umylGhK0bfwYqrIirMwkAZGe9rx8FT1xXC8CFAdoXYwZNLoZhstrMtbN0LAgnaFBnbP_36qK3R0pyzkzGRl6XKevHAcDKT41y3u1PlDr9tn8pBSpZLt7CfgqTjPctA9ZtQ37xmViu7ryBBVBD_5G5p8oVvNdsp02uXb8eZ_S87bH-VXPocPhcmOK-jtPXA1Z_VTSSDXy9fVL39N5NwR72K_ibhxT62JvkUQ
## 3  AUy1YQ25x8DqTgBitfgbrdjsowbacGRBu9q0ujP-o0VNivQStZ8Pqg52RjdCBS_fngH1g8lu6WcHYK5TiTb0Klxie1-MXuo8H8eVZauM-t_gJ0zlUX5nmQyZ8LIy0uFN-8--S7nssQaAZ6gAHtqO6BD6xIlm3hSpeKF_czQZHpg938PAHYIHsI5t11IR3hdo0yTcuLABT43DU474CFpT7d-BRB7xuxtmdbh3FZF9rTMC-Wwe_BNc0KKQK-cdePSrVx7-s4L1KMeYCFZEQ3H9pSpqbI8pQzMtpOXGvBC133oFx1JPKEuYwSq_xbs9DQZqC1_X5NmE7BJ-Bc8
## 4  AUy1YQ3rZcl1VDHnWV1zXJaGbqOa59KamxO8S-d1xE4gY27OOWiB0H1G_66UIJ2oAJfOdCQ9-vx3viEx1Z4l3uRIWLE1rO6Tbx3WBfgSDOr5NlcaiJpMjMnChh_pqxyJrA8Dl1QFgee5jA2i1C8riPCeXroqoqPT7KnCQ4dVNyHKmya0QE7DA3b6hK49212XYe2LeFmoWhJmhALC386IOnIDraKqYAagx6WFp3Jd-q1A423NuzcBM1oB97TdRPilu8gwmdKKgPsXr7E3LcLisNqg61icH_QZCeQ4rsagvrMo76C_pMsQvuHWPzMLgj7xya2g1F8bMX7fHzk
## 5  AUy1YQ3fSSlcWaS3NjM6mQNQMZxNx2PkIia8_bvqb1h-MheHrmEVuhr7Qd09K2DHqTxn6Y6NjwgvBVgrMfZOEMFoui4Va6WaCVIf7nFdzc2UU6Eej0lBkv2YJ8qslWOOCDPqoXyx0Isra1RyiB_h2r5l2wvgckGcLGa553Uci5d6fZqdvvHQQk04l_kzHIX3HmdDmpyLRtrb9MpMTwWwtXseiNPOB0afwnFYtGmx3PWLTCo6A9YVoWK-bLtVfONQg_zvjPtyNOkLtvlO5b8GG6xnfZqYJMfT6sM8WJb-9rtXkdzXoDKmC-WG_gCsZd_D3cTkNvoWEESeJuA
## 6  AUy1YQ0KRWlVTPKQH4lNQVl3bGiJ8mNktYZLl-sEBZ1eAbzkVcrFQmwWd8LQmGYDl8s--E5ia6ozdrCfVIYqUHJ05a2GiTLWu9owIpRvi_7fRjajvCtITGzet9XXvDv6DuhixLGPnKcd733oT9z6ky7-Eq9lBTRXpm7aQkQYj6rPqq7jlkAdwzVLnZnCqOnxOG6MdBS8SwQwNS8Cfy96G5rKrYa4nxxI_90SIwd1XgvOeRE2yr2Fdivs-Xh40i0ZvDSxyd9G4cAtnriyVL6a9zboISVTIwnSrsqX670B7Z0ZmrLFDhuqx2zcld7nIrG8iQLT_r98gGqLMtI
## 7  AUy1YQ2lk5ZaR9R1r1l2frulEmctNI9vhS6e4bLZyvxX4yLOlnZDzpnK5Z05wQh7OkXp9NY1m2no6kdZA0A9adiWFem7bSWwGJ9Zt-wInOqQtk1awk1lkX48pLxw-zx7mWqA1SsA6jWO9GyorW3YAHFplwtJN41agSEjTJa0iR4X8hrE98F2eRgGrHFToaHwEJrcd9Ggwr07-TSTnUC215TJkjUj0J-OVL5vGpph_FV8lc64lNj-Ey_MR47WTxmMgh56r4_id47wv50z1BP1ClpjvS3tDszOInnawf3P1vJgrKDasv1N4d6AVA7th41D9zykviU0lFBZj9s
## 8  AUy1YQ3NDozncv0ULLEor2DBqRBfXccZ551O5qBXWQkKj_OAMzgaXsfurekiHRcrnf2Ljnc964jKsb1QHzLShdFKc5AOdMTceB4Y74Mm9DjGSph1DCtJ_UxcleT3RIx4kT06XQaBf_sAYFFvxorvATwy2mjmC4VLkSGHhQfp3qQw9CWgP-HCR3COpWyo7LzkafQsSTuAzjRDPN1Nau1MEvD6iUQjsTqfac-sg2fkOif2s9JJ9_NLUtvxIarzMuJpyWREL2mdE3WMzFoGaD8tKk4J2y7QYHDNhctOpzIn3sgmPg4k10RCtgxOEDLKr0bYuWh1exbt9CxFvwo
## 9  AUy1YQ2wmc2ch7gtxBxja6xt9ghEBfQB0vwpUuM2uvEGy1NlrJAgqR905zc4REgj-rJ3G6apKrNjI55QaLTmMKRc5Fu9_5VN82WRVjq9ifQbcsCRQov5rHn85107v_KUsGmKsz6n7XTCjMtyTy0RytBjKjIQOifzqeP3C6wu4TV9QrTnEixmadDoZXkrfemXcHUN9LEAW5d12lh71tdlJ3NK3U8zGbh34jksDDR8ului6fQSxKark0N8bHfDd2WGxnFxswi2B6ALth9AV6O1OkKL5mP-kfJztk2Jd5eBbRyFse0znHTuGq7g7rVdnpkOPlYUF3tsxfpMRiQ
## 10 AUy1YQ3SlpLNkIsqE0aSjUBIz0XxQaTpBBvQfaiv8Rsxak-KcoizNBykPNWg4DUgLJWtzUDH1P0KSoyIyVks9MuZW-xV5NANmhuTHh46fsVbbWTDOzSL06Okxk_OLMZrbIfaZV6c4vf2UHcUeDIVAdmWhKm45NJyMP3Z6ak38wFhqV9VXdCA-qhPM5bTauHRgFAjJu8PGv-UdTY1LYogAyAhMlAdvf41uCX1seRRsQvo7OLQq9LvOaExV_m0GXgOBSqIkQqlY1oKzwOomMwIEPEzeqRDVbGKxJUaDt2_tgzohd_kgwf1AM5pfMY2-tKjqcnBMj9_tmxgrsI
##    width
## 1   3024
## 2   4032
## 3   4640
## 4   3024
## 5   3024
## 6   4032
## 7   4000
## 8   3456
## 9   3264
## 10  2608
## 
## $result$place_id
## [1] "ChIJUSIW7l2UYoYRfmWsmC4oTfw"
## 
## $result$plus_code
## $result$plus_code$compound_code
## [1] "PMQP+34 San Nicolás de los Garza, N.L., México"
## 
## $result$plus_code$global_code
## [1] "75QXPMQP+34"
## 
## 
## $result$price_level
## [1] 2
## 
## $result$rating
## [1] 4.3
## 
## $result$reference
## [1] "ChIJUSIW7l2UYoYRfmWsmC4oTfw"
## 
## $result$reservable
## [1] TRUE
## 
## $result$reviews
##           author_name
## 1 Martha M. Mendez A.
## 2    Debanny Martínez
## 3        bubulubii c:
## 4       Dulce Rodarte
## 5        Suzett Jasso
##                                                          author_url language
## 1 https://www.google.com/maps/contrib/104303907828966724052/reviews       es
## 2 https://www.google.com/maps/contrib/115637926877780804974/reviews       es
## 3 https://www.google.com/maps/contrib/116731230764560781696/reviews       es
## 4 https://www.google.com/maps/contrib/100128616113942673919/reviews       es
## 5 https://www.google.com/maps/contrib/117641540256688034014/reviews       es
##   original_language
## 1                es
## 2                es
## 3                es
## 4                es
## 5                es
##                                                                                                               profile_photo_url
## 1 https://lh3.googleusercontent.com/a-/ALV-UjXYPbFOW9Ne-tAATYx_0qzQnt6VvtEusCF854utBdF3qQcIhHlJ2A=s128-c0x00000000-cc-rp-mo-ba5
## 2     https://lh3.googleusercontent.com/a-/ALV-UjXeigQDNpNvTKbCr56bH2DB_-ykPMkMDm90Hk8BV0dPKyZX0HUkWQ=s128-c0x00000000-cc-rp-mo
## 3 https://lh3.googleusercontent.com/a-/ALV-UjUsYUPDo5SjKQieqX7TclPCSouBDUnv11RGmA_prqN34E95CSN7sw=s128-c0x00000000-cc-rp-mo-ba3
## 4       https://lh3.googleusercontent.com/a-/ALV-UjUqU6173pZlytL-eUHgHqHRcQCBA7Z7QvlVtjW_XLjbhd83Ruo_=s128-c0x00000000-cc-rp-mo
## 5   https://lh3.googleusercontent.com/a-/ALV-UjVFjaT_brl8U7L9-R0tW4iyyp7r0OoolQvDXR4mc8vxfhKMJnZ7=s128-c0x00000000-cc-rp-mo-ba2
##   rating relative_time_description
## 1      5              Hace 4 meses
## 2      5                Hace 1 mes
## 3      5                Hace 1 mes
## 4      5            Hace 2 semanas
## 5      5              Hace 9 meses
##                                                                                                                                                                                                                                                                 text
## 1 La comida está bien , los rollos estaban ricos y la sopa si nos gustó. La bebida era gin de frutos rojos y estaba rica.\nLa comida le doy 9 de 10\nEl mesero fue amable, pero se tardaron mucho en traer la comida. Fácil unos 40min ,lo bueno que no tenía prisa.
## 2                                                                                                                                       Buen ambiente familiar, la comida deliciosa, la puedes pedir a tu gusto sin problema alguno, el servicio, muy amables todos.
## 3                                                                                                                                                                                    Servicio muy bueno, meseros y gerente muy atentos. La comida estaba muy rica (:
## 4                                                                                                                                                                                                                               Muy rico y nos atendieron muy rápido
## 5                          Excelente lugar con un gran ambiente y una excelente atención \U{01faf6}🏻\nLa comida está deliciosa, pedí un ramen y un cosmo roll y un bonel roll todo super rico y no se diga las limonadas que venden \U{01faf6}🏻❤️\n100% recomendado
##         time translated
## 1 1730762921      FALSE
## 2 1738452707      FALSE
## 3 1738452671      FALSE
## 4 1740176741      FALSE
## 5 1718073228      FALSE
## 
## $result$serves_beer
## [1] TRUE
## 
## $result$serves_breakfast
## [1] FALSE
## 
## $result$serves_brunch
## [1] FALSE
## 
## $result$serves_dinner
## [1] TRUE
## 
## $result$serves_lunch
## [1] TRUE
## 
## $result$serves_vegetarian_food
## [1] TRUE
## 
## $result$takeout
## [1] TRUE
## 
## $result$types
## [1] "restaurant"        "food"              "point_of_interest"
## [4] "establishment"    
## 
## $result$url
## [1] "https://maps.google.com/?cid=18180231451359602046"
## 
## $result$user_ratings_total
## [1] 1512
## 
## $result$utc_offset
## [1] -360
## 
## $result$vicinity
## [1] "Local 110 Plaza La Joya, Avenida Manuel L. Barragán 550, Valle de Anáhuac, San Nicolás de los Garza"
## 
## $result$website
## [1] "http://orientalwok.mx/"
## 
## $result$wheelchair_accessible_entrance
## [1] TRUE
## 
## 
## $status
## [1] "OK"
### Based on google reviews lets generate a vector containing only the text.
### Corpus is a collection of written texts or a body of writing on a particular subject.
### Cleaning text data. 
C_China_reviews_text  <- C_China_reviews$result$reviews$text
C_China_reviews_doc   <- Corpus(VectorSource(C_China_reviews_text))   ### Tokenization: Break down the text into individual words or tokens. 
C_China_reviews_doc <- C_China_reviews_doc %>% tm_map(removeNumbers) %>% tm_map(removePunctuation) %>% tm_map(stripWhitespace)
C_China_reviews_doc <- tm_map(C_China_reviews_doc, content_transformer(tolower))
C_China_reviews_doc <- tm_map(C_China_reviews_doc, removeWords, stopwords("spanish"))
dtm_C_China_reviews <- TermDocumentMatrix(C_China_reviews_doc) 
matrix <- as.matrix(dtm_C_China_reviews) 
words <- sort(rowSums(matrix),decreasing=TRUE) 
words_df <- data.frame(word = names(words),freq=words)
### Word cloud of 50 hospitals around the starting point (Parque Fundidora)
### words with frequency below min.freq will not be plotted
# wordcloud(words = words_df$word, freq = words_df$freq, min.freq = 5, max.words=200, random.order=FALSE, rot.per=0.35, colors=brewer.pal(8, "Dark2"))
str(words_df)
## 'data.frame':    53 obs. of  2 variables:
##  $ word: chr  "comida" "bueno" "rica" "ambiente" ...
##  $ freq: num  6 2 2 2 2 2 2 2 2 1 ...
head(words_df)
##                word freq
## comida       comida    6
## bueno         bueno    2
## rica           rica    2
## ambiente   ambiente    2
## deliciosa deliciosa    2
## servicio   servicio    2
### Word clouds are a visual representation of text data where words are arranged in a cluster. The size of each word reflects its frequency or importance in the data set.
wordcloud2(data = words_df, color = "random-dark", size = 0.5, shape = "circle")
### term matrix is a table containing the frequency of the words.
tdm_sparse <- TermDocumentMatrix(C_China_reviews_doc, control = list(weighting = weightTfIdf))
tdm_m_sparse <- as.matrix(tdm_sparse)
# lets display frequency of words 
term_freq <- rowSums(tdm_m_sparse)
term_freq_sorted <- sort(term_freq, decreasing = TRUE)
tdm_d_sparse <- data.frame(word = names(term_freq_sorted), freq = term_freq_sorted)

# lets display the top 10 most frequent words
head(tdm_d_sparse, 10)
##                  word      freq
## atendieron atendieron 0.7739760
## rápido         rápido 0.7739760
## rico             rico 0.5007303
## atentos       atentos 0.3317040
## gerente       gerente 0.3317040
## meseros       meseros 0.3317040
## servicio     servicio 0.2990075
## bueno           bueno 0.2489345
## rica             rica 0.2489345
## excelente   excelente 0.2110844
# we create a word cloud to visualize the most frequent words in the google reviews
# wordcloud(words = tdm_d_sparse$word, freq = tdm_d_sparse$freq, min.freq = 4, max.words = 100, colors = brewer.pal(8, "Dark2"))
wordcloud2(data = tdm_d_sparse, color = "random-dark", size = 0.6, shape = "circle")

FOCUSING ON SENTIMENT ANALYSIS

# lets convert review column of dataframe to character vector so we can generate sentiment scores
text <- iconv(C_China_reviews_text)
# Syuzhet method: It is an algorithm used to extract emotions from a sentence. It is a sentiment lexicon that can be used with non-English texts.
syuzhet_vector <- get_sentiment(text, method = "syuzhet") ### get_sentiment() function is used to analyze sentiment. 

# See first row of vector
head(syuzhet_vector)
## [1] 0.00 1.05 0.00 0.00 0.50
# See summary statistics of vector
summary(syuzhet_vector)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00    0.00    0.00    0.31    0.50    1.05

SENTIMENT HISTOGRAM

# Using the full dataset for sentiment analysis might still 
text_sampled <- iconv(C_China_reviews_text)
syuzhet_vector_sampled <- get_sentiment(text_sampled, method = "syuzhet")
ggplot(data.frame(syuzhet_vector_sampled), aes(x = syuzhet_vector_sampled)) + 
  geom_histogram(binwidth = 0.1, fill = "blue", color = "black") + 
  labs(title = "Sentiment Distribution using Syuzhet Method (Sampled Data)", x = "Sentiment Score", y = "Frequency") + 
  theme_minimal()

BAR PLOT OF EMOTIONS

nrc_sampled <- get_nrc_sentiment(text_sampled)    ### sentiment dictionary to calculate the presence of eight different emotions and their corresponding valence in a text file
nrct_sampled <- data.frame(t(nrc_sampled))
nrcs_sampled <- data.frame(rowSums(nrct_sampled))
nrcs_sampled <- cbind("sentiment" = rownames(nrcs_sampled), nrcs_sampled)
rownames(nrcs_sampled) <- NULL
names(nrcs_sampled)[1] <- "sentiment"
names(nrcs_sampled)[2] <- "frequency"
nrcs_sampled <- nrcs_sampled %>% mutate(percent = frequency/sum(frequency))
nrcs2_sampled <- nrcs_sampled[1:8, ]
colnames(nrcs2_sampled)[1] <- "emotion"
### 1) The bar plot illustrating the distribution of emotions based on sentiment analysis using the NRC lexicon on the google reviews
### 2) Each bar represents a different emotion, and the height of the bar indicates the frequency of that emotion within the text data.
ggplot(nrcs2_sampled, aes(x = reorder(emotion, -frequency), y = frequency, fill = emotion)) + 
  geom_bar(stat = "identity") + 
  labs(title = "Emotion Distribution (Sampled Data)", x = "Emotion", y = "Frequency") + 
  theme_minimal() + 
  scale_fill_brewer(palette = "Set3")

PIE CHART OF SENTIMENT DISTRIBUTION

# lets create a data frame with sentiment and count
sentiment_df <- data.frame(sentiment = c("Positive", "Negative", "Neutral"), count = c(sum(syuzhet_vector_sampled > 0), sum(syuzhet_vector_sampled < 0), sum(syuzhet_vector_sampled == 0)))
# Create a pie chart
### 1) The output is a pie chart illustrating the distribution of sentiment categories within the dataset. 
### 2) Each segment of the pie chart represents a sentiment category (“Positive”, “Negative”, “Neutral”), and the size of each segment corresponds to the count of that sentiment category in the dataset. 
ggplot(sentiment_df, aes(x = "", y = count, fill = sentiment)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  labs(title = "Sentiment Distribution", x = "", y = "") +
  theme_minimal() +
  scale_fill_brewer(palette = "Set3")

Conclusión

Brevemente describir los 4 - 6 principales hallazgos identificados en el proceso de análisis de Google Maps Reviews y análisis de sentimientos (sentiment analysis). - El peor de todos los restaurantes es Fe Ling, la cual no tiene tantos visitantes, pero se podria considerar que el peor de - todos tomando en cuenta cantidad de reviews y ratings, seria Chino Jr. - Donde se encuentrar la concentración de los peores restaurantes chinos es en la zona de Valle Verde. - El mejor restaurante considerando cantidad de visitas y rating, sería Jia Fu Comida. - Los mejores restuaruantes se encuentrar en Zona Mitras y centro.

¿Cuáles son las unidades de negocio con los niveles de raiting más alto? ¿Cuál es la ubicación de las unidades de negocio con los mejores raitings? ¿Cuáles son las principales características locacionales, “reviews”, y de tipo de percepción por parte de los clientes? Yalin Chen, Jia Fu, Xiang Long, Ninety Nine, Ming Xing, La Gran muralla, Hui Xin, Chinese City, China House y China King Se encuentran en Zona Mitras y centro. Te tratan bien, esta limpio, el sabor de la comida como el pollo, y de muy buen precio.

¿Cuáles son las unidades de negocio con los niveles de raiting más bajo? ¿Cuál es la ubicación de las unidades de negocio con los menores raitings? ¿Cuáles son las principales características locacionale y “reviews” por parte de los clientes? Wang Wang, Chef Oriental, Fe Ling, Huixin, El Chino Jr, son restaurantes peor valorados, ubicados por la zona de Valle Verde. En estos lugares la queja más común es que las porciones de los platos no son tan bastas, para el costo. ¿Qué tipo de sentimiento describen los comentarios de Google Reviews de las unidades de negocio seleccionadas? Según el analisis de sentimientos, disgusto, miedo, enojo y tristeza son los sentimientos más presentes, debe ser por las criticas a los negocios.

LS0tCnRpdGxlOiAiQVBJIGdvb2dsZSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKICAgIHRoZW1lOiAiam91cm5hbCIKICAgIGhpZ2hsaWdodDogImVzcHJlc3NvIgpkYXRlOiAiMjAyNS0wMy0xMiIKLS0tCiFbXSgvVXNlcnMvZW1pbGlhbm8vRG93bmxvYWRzL2NvbWlkYSBjaGluYS5qcGVnKQoKIyAqKjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+UHJlZ3VudGFzPC9zcGFuPioqCgoqKsK/UXXDqSBlcyBsYSBJbnRlbGlnZW5jaWEgTG9jYWNpb25hbCAoTG9jYXRpb24gSW50ZWxsaWdlbmNlKT8qKgpFcyBsYSBmb3JtYSBlbiBsYSBxdWUgc2UgbG9ncmFuIHJlYWxpemFyIHVuYSB2aXp1YWxpemFjacOzbiB5IGFuYWxpc2lzIGRlIGRhdG9zIGdlb2VzcGFjaWFsZXMsIGNvbiBlbCBmaW4gZGUgdG9tYXIgZGVjaXNpb25lcyBlc3RyYXRlZ2ljYXMgZW4gYmFzZSBhIGxhIHViaWNhY2nDs24uIFRvZG8gZ3JhY2lhcyBhIGxhIGNhcGFjaWRhZCBkZSBpZGVudGlmaWNhciBwYXRyb25lcywgcG9yIHN1IGNhcGFjaWRhZCBkZSByZWNvcGlsYXIsIHByb2Nlc2FyIHkgYW5hbGl6YXIgbGEgaW5mb3JtYWNpw7NuIGdlb2dyYWZpY2EgCgoqKkEgcGFydGlyIGRlbCB2aWRlbyDigJxXaHkgaXMgTG9jYXRpb24gSW50ZWxsaWdlbmNlIFZpdGFsIGZvciBTdWNjZXNzZnVsIEJ1c2luZXNzP+KAnSAoaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj03WUFsdjkzblFpWSkgeSBsYXMgcmVzcHVlc3RhcyBhIGxhcyBpbnN0cnVjY2lvbmVzIDEpIOKAkyAxMCkgwr9Dw7NtbyBjb250cmlidXllIGVsIHByb2Nlc28gZGUgYW7DoWxpc2lzIGRlIEludGVsaWdlbmNpYSBMb2NhY2lvbmFsIGEgbGEgSW50ZWxpZ2VuY2lhIGRlIE5lZ29jaW9zPyoqCkFsIGJyaW5kYXIgZGF0b3MgZ2VvZ3JhZmljb3MgYWwgYW5hbGlzaXMgZGUgbG9zIGRhdG9zLCBwZXJtaXRlIHRvbWFyIG1lam9yZXMgZGVjaXNpb25lcyBlc3RyYXRlZ2ljYXMsIGNvbW8gbWVqb3JlcyBlc3RyYXRlZ2lhcyBkZSBtYXJrZXRpbmcsIG1lam9yIHNlbGVjY2lvbiBkZSBsb2NhbGl6YWNpw7NuIGRlIHVuIG5lZ29jaW8sIGlkZW50aWZpY2FyIGRvbmRlIHNlIGVuY3VlbnRyYSBlbCBtYXlvciBmbHVqbyBkZSBjbGllbnRlbGEgZW4gY2llcnRhIGxvY2FsaXphY2nDs24sIGVudHJlIG90cmFzLgoKKipCcmV2ZW1lbnRlIGRlc2NyaWJpciBjb24gdHVzIHByb3BpYXMgcGFsYWJyYXMgcXXDqSBlcyBsYSBoZXJyYW1pZW50YSBBbsOhbGlzaXMgZGUgU2VudGltaWVudG9zIC8gU2VudGltZW50IEFuYWx5c2lzLiDCv1BvcnF1w6kgbGEgaGVycmFtaWVudGEgU2VudGltZW50IEFuYWx5c2lzIGVzIHJlbGV2YW50ZSBlbiBlbCBkZXNhcnJvbGxvIGRlIGxhIEludGVsaWdlbmNpYSBkZSBOZWdvY2lvcyAoQnVzaW5lc3MgSW50ZWxsaWdlbmNlKT8qKgpQcm9jZXNhIGVsIGxlbmd1YWplIG5hdHVyYWwgcGFyYSBpZGVudGlmaWNhciB5IGNsYXNpZmljYXIgbGFzIG9waW5pb25lcyBlbiB0ZXh0byBjb21vIHJlc2XDsWFzIG8gb3BpbmlvbmVzLCB5IGNsYXNpZmljYXJsb3MgY29tbyBwb3NpdGl2YXMsIG5lZ2F0aXZhcyBvIG5ldXRyYWxlcy4KCiMgKipMb2FkaW5nIFJlcXVpcmVkIERhdGEgJiBNYXBzKioKCiMjIyAqKkxpYnJhcmllcyoqCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgTE9BRElORyBSRVFVSVJFRCBMSUJSQVJJRVMgCiMgZGF0YSBhbmFseXNpcyAKbGlicmFyeShkcGx5cikgICAgICAgICAgICAjIGdyYW1tYXIgb2YgZGF0YSBtYW5pcHVsYXRpb24gaGVscGluZyB0byByZXNvbHZlIGRhdGEgbWFuaXB1bGF0aW9uIGRpZmZpY3VsdGllcyAKbGlicmFyeShIbWlzYykgICAgICAgICAgICAjIHVzZWZ1bCBmdW5jdGlvbnMgZm9yIGRhdGEgYW5hbHlzaXMgYW5kIGhpZ2ggLSBsZXZlbCBncmFwaGljcwpsaWJyYXJ5KGZvcmVpZ24pICAgICAgICAgICMgcmVhZCBkYXRhIHN0b3JlZCBieSBNaW5pdGFiLCBTUFNTLCBTdGF0YQpsaWJyYXJ5KG9wZW54bHN4KSAgICAgICAgICMgb3BlbiwgcmVhZCwgd3JpdGUsYW5kIGVkaXQgeGxzeCBmaWxlcyAKbGlicmFyeSh0aWR5dmVyc2UpICAgICAgICAjIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyBkZXNpZ25lZCBmb3IgZGF0YSBzY2llbmNlCgojIHNwYXRpYWwgZGF0YSBhbmFseXNpcyAKbGlicmFyeShsZWFmbGV0LmV4dHJhcykgICAjIHRvIHByb3ZpZGUgZXh0cmEgZnVuY3Rpb25hbGl0eSB0byB0aGUgbGVhZmxldCBSIHBhY2thZ2UKbGlicmFyeShzcCkgICAgICAgICAgICAgICAjIGZ1bmN0aW9ucyBmb3IgcGxvdHRpbmcgZGF0YSBhcyBtYXBzLCBzcGF0aWFsIHNlbGVjdGlvbiwgbWV0aG9kcyBmb3IgcmV0cmlldmluZyBjb29yZGluYXRlcyAKbGlicmFyeShzZikgICAgICAgICAgICAgICAjIGVuY29kZSBzcGF0aWFsIHZlY3RvciBkYXRhIApsaWJyYXJ5KG1hcHMpICAgICAgICAgICAgICMgZ2VvZ3JhcGhpYyBtYXBzCmxpYnJhcnkodG1hcCkgICAgICAgICAgICAgIyBnZW5lcmF0ZSB0aGVtYXRpYyBtYXBzCmxpYnJhcnkoc3BkZXApICAgICAgICAgICAgIyBhIGNvbGxlY3Rpb24gb2YgZnVuY3Rpb25zIHRvIGNyZWF0ZSBzcGF0aWFsIHdlaWdodHMgbWF0cmljZXMgZnJvbSBwb2x5Z29uIGNvbnRpZ3VpdGllcwpsaWJyYXJ5KHRlcnJhKSAgICAgICAgICAgICMgbWV0aG9kcyBmb3Igc3BhdGlhbCBkYXRhIGFuYWx5c2lzIApsaWJyYXJ5KGxlYWZsZXQpICAgICAgICAgICMgaW50ZXJhY3RpdmUgbWFwcwpsaWJyYXJ5KG1hcHByb2opICAgICAgICAgICMgbWFwIHByb2plY3Rpb25zIApsaWJyYXJ5KG1hcHNhcGkpICAgICAgICAgICMgCmxpYnJhcnkoc3BhdGlhbHJlZykgICAgICAgIyBzcGF0aWFsIHJlZ3Jlc3Npb24gbW9kZWxzIApsaWJyYXJ5KHNmZGVwKSAgICAgICAgICAgICMgYW4gaW50ZXJmYWNlIHRvICdzcGRlcCcgdG8gaW50ZWdyYXRlIHdpdGggJ3NmJyBvYmplY3RzIGFuZCB0aGUgJ3RpZHl2ZXJzZQpsaWJyYXJ5KHRpZHlnZW9jb2RlcikgICAgICMgbWFrZXMgZ2V0dGluZyBkYXRhIGZyb20gZ2VvY29kaW5nIHNlcnZpY2VzIGVhc3kKbGlicmFyeShtYXBib3hhcGkpICAgICAgICAjICdNYXBib3gnIE5hdmlnYXRpb24gQVBJcywgaW5jbHVkaW5nIGRpcmVjdGlvbnMsIGlzb2Nocm9uZXMsIGFuZCByb3V0ZSBvcHRpbWl6YXRpb24uCgojIHZpc3VhbGl6YXRpb24gCmxpYnJhcnkoZ2dtYXApICAgICAgICAgICAgIyBzcGF0aWFsIGRhdGEgdmlzdWFsaXphdGlvbiAKbGlicmFyeShyZ2VvZGEpICAgICAgICAgICAjIHNwYXRpYWwgZGF0YSBhbmFseXNpcyBiYXNlZCBvbiBzb2Z0d2FyZSBHZW9EYQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICMgR3JhbW1hciBvZiBncmFwaGljcy4gU3lzdGVtIGZvciBkZWNsYXJhdGl2ZSBjcmVhdGluZyBncmFwaGljcwpsaWJyYXJ5KGNvcnJwbG90KSAgICAgICAgICMgcHJvdmlkZXMgYSB2aXN1YWwgZXhwbG9yYXRvcnkgdG9vbCBvbiBjb3JyZWxhdGlvbiBtYXRyaXgKbGlicmFyeShSQ29sb3JCcmV3ZXIpICAgICAjIG9mZmVycyBzZXZlcmFsIGNvbG9yIHBhbGV0dGVzCmxpYnJhcnkobGVhZnN5bmMpICAgICAgICAgIyBjcmVhdGUgc21hbGwgbXVsdGlwbGVzIG9mIHNldmVyYWwgbGVhZmxldCB3ZWIgbWFwcwpsaWJyYXJ5KGh0bWx0b29scykgICAgICAgICMgdG9vbHMgZm9yIEhUTUwgZ2VuZXJhdGlvbiBhbmQgb3V0cHV0IAoKIyBvdGhlcnMgCmxpYnJhcnkocmxhbmcpICAgICAgICAgICAgIyBjb2xsZWN0aW9uIG9mIGZyYW1ld29ya3MgYW5kIEFQSXMgZm9yIHByb2dyYW1taW5nIHdpdGggUgpsaWJyYXJ5KGNsYXNzSW50KSAgICAgICAgICMgbWV0aG9kcyBmb3IgY2hvb3NpbmcgdW5pdmFyaWF0ZSBjbGFzcyBpbnRlcnZhbHMgZm9yIG1hcHBpbmcgb3Igb3RoZXIgZ3JhcGhpYyBwdXJwb3NlcwpsaWJyYXJ5KGdyaWRFeHRyYSkgICAgICAgICMgdG8gYXJyYW5nZSBhbmQgY29tYmluZSBwbG90cyBmb3IgZWFzeSBjb21wYXJpc29uCmxpYnJhcnkoa25pdHIpICAgICAgICAgICAgIyBpbnRlZ3JhdGVzIGNvbXB1dGluZyBhbmQgcmVwb3J0aW5nCgojIyMgR2V0dGluZyBhY2Nlc3MgdG8gZGlzdGFuY2UsIHJldmlld3MsIGFuZCByYXRpbmdzIGJ5IHVzaW5nIEdvb2dsZSBNYXBzCmxpYnJhcnkodG0pICAgICAgICAgICAgICAgIyBhIGZyYW1ld29yayBmb3IgdGV4dCBtaW5pbmcgYXBwbGljYXRpb25zCmxpYnJhcnkod29yZGNsb3VkKSAgICAgICAgIyBmdW5jdGlvbmFsaXR5IHRvIGNyZWF0ZSBwcmV0dHkgd29yZCBjbG91ZHMKbGlicmFyeSh3b3JkY2xvdWQyKSAgICAgICAjIApsaWJyYXJ5KGdvb2dsZXdheSkgICAgICAgICMgcHJvdmlkZXMgYSBtZWNoYW5pc20gdG8gYWNjZXNzIHZhcmlvdXMgR29vZ2xlIE1hcHMgQVBJcywgaW5jbHVkaW5nIHBsb3R0aW5nIGEgR29vZ2xlIE1hcCBmcm9tIFIgYW5kIG92ZXJsYXlpbmcgaXQgd2l0aCBzaGFwZXMgYW5kIG1hcmtlcnMsIGFuZCByZXRyaWV2aW5nIGRhdGEgZnJvbSB0aGUgcGxhY2VzLCBkaXJlY3Rpb25zLCByb2FkcywgZGlzdGFuY2VzLCBnZW9jb2RpbmcsIGVsZXZhdGlvbiBhbmQgdGltZXpvbmUgQVBJcwpsaWJyYXJ5KGdtYXBzZGlzdGFuY2UpICAgICMgYWxsb3dzIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZXMgZm9yIGEgZGF0YWJhc2UgdGhyb3VnaCBHb29nbGUgbWFwcwojIGxpYnJhcnkoaGVyZVIpICAgICAgICAgICMgZ2VvY29kZSBhbmQgYXV0b2NvbXBsZXRlIGFkZHJlc3NlcyBvciByZXZlcnNlIGdlb2NvZGUgUE9JcyB1c2luZyB0aGUgR2VvY29kZXIgQVBJCmxpYnJhcnkob3NybSkgICAgICAgICAgICAgIyBlbmFibGVzIHRoZSBjb21wdXRhdGlvbiBvZiByb3V0ZXMsIHRyaXBzLCBpc29jaHJvbmVzIGFuZCB0cmF2ZWwgZGlzdGFuY2VzIG1hdHJpY2VzICh0cmF2ZWwgdGltZSBhbmQga2lsb21ldHJpYyBkaXN0YW5jZSkuCgojIyMgVGV4dCBNaW5pbmcKbGlicmFyeSh0bSkgICAgICAgICAjIHRleHQgbWluaW5nIGZ1bmN0aW9ucyAKbGlicmFyeShzeXV6aGV0KSAgICAjIGluY2x1ZGVzIGZvdXIgc2VudGltZW50IGRpY3Rpb25hcmllcyBhbmQgcHJvdmlkZXMgYSBtZXRob2QgZm9yIGFjY2Vzc2luZyB0aGUgcm9idXN0LCBidXQgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSwgc2VudGltZW50IGV4dHJhY3Rpb24gdG9vbCBkZXZlbG9wZWQgaW4gdGhlIE5MUCBncm91cCBhdCBTdGFuZm9yZC4KbGlicmFyeShTbm93YmFsbEMpCmxpYnJhcnkod29yZGNsb3VkKQpsaWJyYXJ5KHdvcmRjbG91ZDIpCgojIGxpYnJhcnkocmVtb3RlcykKIyBsaWJyYXJ5KG9wZW5yb3V0ZXNlcnZpY2UpCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoIkdJU2NpZW5jZS9vcGVucm91dGVzZXJ2aWNlLXIiKQpgYGAKCiMjIyAqKkdPT0dMRSBNQVBTIEFQSSBLRVkqKgoKYGBge3IsIHJlc3VsdHM9J2hpZGUnLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnbWFwc19rZXkgPC0gJ0FJemFTeURNdGtJRXZVcEZjY3lVQ0d4TGdYTy1UelRjRVJmekozbycKcmVnaXN0ZXJfZ29vZ2xlKGtleSA9IGdtYXBzX2tleSkgCnptbV9tdHkgPC0gZ2V0X21hcCgiTW9udGVycmV5LCBOdWV2byBMZcOzbiIsIHpvb20gPSAxMikKIyBnZ21hcCh6bW1fbXR5KSArIAojIGdlb21fcG9pbnQoZGF0YSA9IGhvc3BpdGFsc196bW1fbXR5LCBhZXMoeD1Mb25naXR1ZCx5PUxhdGl0dWQpLCBjb2xvcj0nb3JhbmdlJywgc2l6ZT00LGFscGhhPS43KQpgYGAKCiMgKipNb250ZXJyZXkgQ29taWRhIENoaW5hIE1hcHBpbmcqKgoKYGBge3J9Ck10eSA8LSBsZWFmbGV0KCkgJT4lIAogIGFkZFRpbGVzKCkgJT4lIAogIHNldFZpZXcoLTEwMC4zMTA5NCwgMjUuNjY5MjgsIHpvb20gPSAxNSkgJT4lICMjIyBNdHkgZG93bnRvd24gYXJlYQogIGFkZE1hcmtlcnMoLTEwMC4zMTA5NCwgMjUuNjY5MjgsIHBvcHVwID0gIk1vbnRlcnJleSBEb3dudG93biBBcmVhIikKTXR5CmBgYAoKYGBge3J9CiMjIyBNYXAgY29vcmRpbmF0ZXMgb2YgUGFycXVlIEZ1bmRpZG9yYSAoU3RhcnRpbmcgUG9pbnQpCmxhdGl0dWRlICA8LSAyNS42Nzg4NQpsb25naXR1ZGUgPC0gLTEwMC4yODQxOApyIDwtIDE1MDAwCmBgYAoKYGBge3J9CiMjIyBVc2UgdGhlIGdvb2dsZV9wbGFjZXMgZnVuY3Rpb24gdG8gbWFrZSBhIGNhbGwgdG8gdGhlIEFQSSBhbmQgc2F2ZSB0aGUgcmVzdWx0cwpzZWFyY2hfc3RyIDwtIGdvb2dsZV9wbGFjZXMoc2VhcmNoX3N0cmluZyA9ICdyZXN0YXVyYW50ZSBjaGlubycsIGxvY2F0aW9uPWMobGF0aXR1ZGUsbG9uZ2l0dWRlKSwgcmFkaXVzPXIsIGtleT1nbWFwc19rZXkpCmBgYAoKYGBge3J9IAojIyMgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgcmVzdWx0cyAoaG9zcGl0YWxzKSBnb3QgZnJvbSBnb29nbGVfcGxhY2VzKCkKIyBzZWFyY2hfc3RyJHJlc3VsdHMKYGBgCgpgYGB7cn0KIyMjIFRoZSBwYWdlX3Rva2VuIGlzIHRoZSB3YXkgdG8gdGVsbCBHb29nbGUgdG8gcmV0dXJuIHRoZSBuZXh0IDIwIHJlc3VsdHMgaW4gdGhlIHNlYXJjaCBpbnN0ZWFkIG9mIG9ubHkgdGhlIGZpcnN0IDIwCnNlYXJjaF9zdHJfYWRkX29uZSA8LSBnb29nbGVfcGxhY2VzKHNlYXJjaF9zdHJpbmcgPSAncmVzdGF1cmFudGUgY2hpbm8nLCBsb2NhdGlvbj1jKGxhdGl0dWRlLGxvbmdpdHVkZSksIHJhZGl1cz1yLCBrZXk9Z21hcHNfa2V5LCBwYWdlX3Rva2VuID0gc2VhcmNoX3N0ciRuZXh0X3BhZ2VfdG9rZW4pCmBgYAoKYGBge3J9CiMjIyBUaGUgcGFnZV90b2tlbiBpcyB0aGUgd2F5IHRvIHRlbGwgR29vZ2xlIHRvIHJldHVybiB0aGUgbmV4dCAyMCByZXN1bHRzIGluIHRoZSBzZWFyY2ggaW5zdGVhZCBvZiBvbmx5IHRoZSBmaXJzdCA0MApzZWFyY2hfc3RyX2FkZF90d28gPC0gZ29vZ2xlX3BsYWNlcyhzZWFyY2hfc3RyaW5nID0gJ3Jlc3RhdXJhbnRlIGNoaW5vJywgbG9jYXRpb249YyhsYXRpdHVkZSxsb25naXR1ZGUpLCByYWRpdXM9ciwga2V5PWdtYXBzX2tleSwgcGFnZV90b2tlbiA9IHNlYXJjaF9zdHJfYWRkX29uZSRuZXh0X3BhZ2VfdG9rZW4pCmBgYAoKYGBge3J9CiMjIyBUaGUgZGF0YSBmcmFtZSB3aWxsIGdpdmUgeW91IGEgd2VhbHRoIG9mIGluZm9ybWF0aW9uIGFib3V0IGVhY2ggcGxhY2UsIGluY2x1ZGluZyBpdHMgYWRkcmVzcywgbGF0aXR1ZGUgJiBsb25naXR1ZGUgY29vcmRpbmF0ZXMsIHByaWNlIGxldmVsLCBzdGFyIHJhdGluZywgbnVtYmVyIG9mIHJhdGluZ3MsIGNhdGVnb3JpZXMsIGFuZCBtb3JlLgpidXNpbmVzc19uYW1lIDwtIGMoc2VhcmNoX3N0ciRyZXN1bHRzJG5hbWUsIHNlYXJjaF9zdHJfYWRkX29uZSRyZXN1bHRzJG5hbWUsIHNlYXJjaF9zdHJfYWRkX3R3byRyZXN1bHRzJG5hbWUpCmJ1c2luZXNzX3JhdGluZyA8LSBjKHNlYXJjaF9zdHIkcmVzdWx0cyRyYXRpbmcsIHNlYXJjaF9zdHJfYWRkX29uZSRyZXN1bHRzJHJhdGluZywgc2VhcmNoX3N0cl9hZGRfdHdvJHJlc3VsdHMkcmF0aW5nKQp1c2VyX3JhdGluZ3NfdG90YWwgPC0gYyhzZWFyY2hfc3RyJHJlc3VsdHMkdXNlcl9yYXRpbmdzX3RvdGFsLCBzZWFyY2hfc3RyX2FkZF9vbmUkcmVzdWx0cyR1c2VyX3JhdGluZ3NfdG90YWwsc2VhcmNoX3N0cl9hZGRfdHdvJHJlc3VsdHMkdXNlcl9yYXRpbmdzX3RvdGFsKQpwbGFjZV9pZCA8LSBjKHNlYXJjaF9zdHIkcmVzdWx0cyRwbGFjZV9pZCwgc2VhcmNoX3N0cl9hZGRfb25lJHJlc3VsdHMkcGxhY2VfaWQsIHNlYXJjaF9zdHJfYWRkX3R3byRyZXN1bHRzJHBsYWNlX2lkKQpsYXQgPC0gYyhzZWFyY2hfc3RyJHJlc3VsdHMkZ2VvbWV0cnkkbG9jYXRpb24kbGF0LCBzZWFyY2hfc3RyX2FkZF9vbmUkcmVzdWx0cyRnZW9tZXRyeSRsb2NhdGlvbiRsYXQsIHNlYXJjaF9zdHJfYWRkX3R3byRyZXN1bHRzJGdlb21ldHJ5JGxvY2F0aW9uJGxhdCkKbG9uIDwtIGMoc2VhcmNoX3N0ciRyZXN1bHRzJGdlb21ldHJ5JGxvY2F0aW9uJGxuZywgc2VhcmNoX3N0cl9hZGRfb25lJHJlc3VsdHMkZ2VvbWV0cnkkbG9jYXRpb24kbG5nLCBzZWFyY2hfc3RyX2FkZF90d28kcmVzdWx0cyRnZW9tZXRyeSRsb2NhdGlvbiRsbmcpCiMgd3JpdGUuY3N2KGRhdGEsICJEOlxcQ0QyMDAxQ19BRDIwMjRcXFBvd2VyX0JJX0RhdGFfQUQyMDI0XFxkYXRhX2xvY2F0aW9uX2ludGwuY3N2Iiwgcm93Lm5hbWVzPVRSVUUpCmBgYAoKYGBge3J9CmRhdGEgPC0gZGF0YS5mcmFtZShidXNpbmVzc19uYW1lLGJ1c2luZXNzX3JhdGluZyx1c2VyX3JhdGluZ3NfdG90YWwscGxhY2VfaWQsbGF0LGxvbikKIyB3cml0ZS5jc3YoZGF0YSwgIkQ6XFxDRDIwMDFDX0FEMjAyNFxcUG93ZXJfQklfRGF0YV9BRDIwMjRcXGRhdGFfbG9jYXRpb25faW50bC5jc3YiLCByb3cubmFtZXM9VFJVRSkKYGBgCgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JSBkaXN0aW5jdChidXNpbmVzc19uYW1lLCAua2VlcF9hbGwgPSBUUlVFKQpgYGAKCmBgYHtyfQpkYXRhX3RvcF9yYXRpbmdzIDwtIGRhdGEgJT4lIHNsaWNlX21heChidXNpbmVzc19yYXRpbmcsIG4gPSA5KQpkYXRhX2xvd19yYXRpbmdzIDwtIGRhdGEgJT4lIHNsaWNlX21pbihidXNpbmVzc19yYXRpbmcsIG4gPSA5KQpgYGAKCgpgYGB7cn0KIyMjIExldHMgdmlzdWFsaXplIHRoZSByZXZpZXdzIGluZm9ybWF0aW9uIGJ5IGNvZmZlZSBzaG9wCnRvcF9yYXRpbmdzX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdG9wX3JhdGluZ3MsIGFlcyh4PXJlb3JkZXIoYnVzaW5lc3NfbmFtZSxidXNpbmVzc19yYXRpbmcpLCB5PWJ1c2luZXNzX3JhdGluZykpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9ImxpZ2h0Ymx1ZSIpICsgCiAgbGFicyh0aXRsZT0iUmVzdGF1cmFudGVzIGNoaW5vcyAtIFRvcCAxMCBNZWpvcmVzIFJhdGluZ3MiLCBzdWJ0aXRsZSA9ICJaTU0iKSArIAogIGNvb3JkX2ZsaXAoKQp0b3BfcmF0aW5nc19wbG90CmBgYAoKYGBge3J9Cmxvd19yYXRpbmdzX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdG9wX3JhdGluZ3MsIGFlcyh4PXJlb3JkZXIoYnVzaW5lc3NfbmFtZSx1c2VyX3JhdGluZ3NfdG90YWwpLCB5PXVzZXJfcmF0aW5nc190b3RhbCkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsPSJsaWdodGJsdWUiKSArCiAgbGFicyh0aXRsZT0iUmVzdGF1cmFudGUgQ2hpbm8gLSBUb3AgMTAgbcOhcyB2aXNpdGFkb3MiLCBzdWJ0aXRsZSA9ICJaTU0iKSArIAogIGNvb3JkX2ZsaXAoKQpsb3dfcmF0aW5nc19wbG90CmBgYAoKYGBge3J9CnRvcF91c2Vyc19wbG90IDwtIGdncGxvdChkYXRhX2xvd19yYXRpbmdzLCBhZXMoeD1yZW9yZGVyKGJ1c2luZXNzX25hbWUsYnVzaW5lc3NfcmF0aW5nKSwgeT1idXNpbmVzc19yYXRpbmcpKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0ibGlnaHRwaW5rIikgKyAKICBsYWJzKHRpdGxlPSJSZXN0YXVyYW50ZSBDaGlubyAtIFRvcCAxMCBQZW9yZXMgUmF0aW5ncyIsIHN1YnRpdGxlID0gIlpNTSIpICsgCiAgY29vcmRfZmxpcCgpCnRvcF91c2Vyc19wbG90CmBgYAoKYGBge3J9Cmxvd191c2Vyc19wbG90IDwtIGdncGxvdChkYXRhX2xvd19yYXRpbmdzLCBhZXMoeD1yZW9yZGVyKGJ1c2luZXNzX25hbWUsdXNlcl9yYXRpbmdzX3RvdGFsKSwgeT11c2VyX3JhdGluZ3NfdG90YWwpKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0ibGlnaHRwaW5rIikrIAogIGxhYnModGl0bGU9IlJlc3RhdXJhbnRlIENoaW5vIC0gVG9wIDEwIE1lbm9zIHZpc2l0YWRvcyIsIHN1YnRpdGxlID0gIlpNTSIpICsgCiAgY29vcmRfZmxpcCgpCmxvd191c2Vyc19wbG90CmBgYAoKIyMjICoqSGVhdG1hcCBvZiBUb3AgLyBMb3dlc3QgUmF0aW5ncyBBY3Jvc3MgWk1NKioKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgV2hlcmUgaW4gWk1NIGFyZSBsb2NhdGVkIHRoZSBob3NwaXRhbHMgY2hhcmFjdGVyaXplZCBieSB0aGUgaGlnaGVzdCByYXRpbmdzPwpyZWdpc3Rlcl9nb29nbGUoa2V5ID0gZ21hcHNfa2V5KQpnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlciA9IGMobG9uID0gbG9uZ2l0dWRlLCBsYXQgPSBsYXRpdHVkZSksIHpvb20gPSAxMSkpICsKICAgICAgICBzdGF0X2RlbnNpdHkyZChkYXRhID0gZGF0YV90b3BfcmF0aW5ncywgYWVzKGxvbiwgbGF0LCBmaWxsID0gYWZ0ZXJfc3RhdChsZXZlbCkpLCAKICAgICAgICAgICAgICAgICAgICAgICBnZW9tID0gInBvbHlnb24iLCBhbHBoYSA9IDAuNDApICsKICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJncmVlbiIsIGhpZ2ggPSAicmVkIiwgZ3VpZGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAnJywgeSA9ICcnLCB0aXRsZSA9ICJaTU0gLSBSZXN0YXVyYW50ZXMgQ2hpbm9zIGNvbiBtZWpvcmVzIHJhdGluZ3MiKQpgYGAKCmBgYHtyfQojIyMgV2hlcmUgaW4gWk1NIGFyZSBsb2NhdGVkIHRoZSBIb3NwaXRhbHMgY2hhcmFjdGVyaXplZCBieSB0aGUgbG93ZXN0IHJhdGluZ3M/CmdnbWFwKGdldF9nb29nbGVtYXAoY2VudGVyID0gYyhsb24gPSBsb25naXR1ZGUsIGxhdCA9IGxhdGl0dWRlKSwgem9vbSA9IDExKSkgKwogIHN0YXRfZGVuc2l0eTJkKGRhdGEgPSBkYXRhX2xvd19yYXRpbmdzLCBhZXMobG9uLCBsYXQsIGZpbGwgPSBhZnRlcl9zdGF0KGxldmVsKSksIAogICAgICAgICAgICAgICAgIGdlb20gPSAicG9seWdvbiIsIGFscGhhID0gMC40MikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImdyZWVuIiwgaGlnaCA9ICJyZWQiLCBndWlkZSA9ICJub25lIikgKwogIGxhYnMoeCA9ICcnLCB5ID0gJycsIHRpdGxlID0gIlpNTSAtIFJlc3RhdXJhbnRlcyBDaGlub3MgY29uIHBlb3IgcmF0aW5ncyIpCmBgYAoKIyMjICoqVGV4dCBBbmFseXNpcyoqCmBgYHtyfQojIyByZXF1ZXN0IG1vcmUgZGV0YWlscyBhYm91dCB0aGUgY29taWRhIGNoaW5hIHVzaW5nIGdvb2dsZV9wbGFjZV9kZXRhaWxzKCkKcmV2aWV3c190b3BfcmF0aW5ncyA8LSBnb29nbGVfcGxhY2VfZGV0YWlscyhwbGFjZV9pZCA9IGRhdGFfdG9wX3JhdGluZ3MkcGxhY2VfaWRbOF0sIGtleSA9IGdtYXBzX2tleSkKcmV2aWV3c19sb3dfcmF0aW5ncyA8LSBnb29nbGVfcGxhY2VfZGV0YWlscyhwbGFjZV9pZCA9IGRhdGFfbG93X3JhdGluZ3MkcGxhY2VfaWRbOF0sIGtleSA9IGdtYXBzX2tleSkKYGBgCgpgYGB7cn0KIyByZXZpZXdzX3RvcF9yYXRpbmdzJHJlc3VsdCRyZXZpZXdzJHRleHQKIyByZXZpZXdzX2xvd19yYXRpbmdzJHJlc3VsdCRyZXZpZXdzJHRleHQKYGBgCgpgYGB7cn0KIyMjIEdlbmVyYXRlIGEgdmVjdG9yIGNvbnRhaW5pbmcgb25seSB0aGUgdGV4dAp0b3BfcmF0aW5nc190ZXh0ICA8LSByZXZpZXdzX3RvcF9yYXRpbmdzJHJlc3VsdCRyZXZpZXdzJHRleHQKdG9wX3JhdGluZ3NfZG9jICAgPC0gQ29ycHVzKFZlY3RvclNvdXJjZSh0b3BfcmF0aW5nc190ZXh0KSkKbG93X3JhdGluZ3NfdGV4dCAgPC0gcmV2aWV3c19sb3dfcmF0aW5ncyRyZXN1bHQkcmV2aWV3cyR0ZXh0Cmxvd19yYXRpbmdzX2RvYyAgIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UobG93X3JhdGluZ3NfdGV4dCkpCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIyBDbGVhbiB0aGUgdGV4dCBkYXRhCm9wdGlvbnMod2Fybj0tMSkKdG9wX3JhdGluZ3NfZG9jIDwtIHRvcF9yYXRpbmdzX2RvYyAlPiUgdG1fbWFwKHJlbW92ZU51bWJlcnMpICU+JSB0bV9tYXAocmVtb3ZlUHVuY3R1YXRpb24pICU+JSB0bV9tYXAoc3RyaXBXaGl0ZXNwYWNlKQp0b3BfcmF0aW5nc19kb2MgPC0gdG1fbWFwKHRvcF9yYXRpbmdzX2RvYywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkKdG9wX3JhdGluZ3NfZG9jIDwtIHRtX21hcCh0b3BfcmF0aW5nc19kb2MsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kb3B0aW9ucyh3YXJuPS0xKQpsb3dfcmF0aW5nc19kb2MgPC0gbG93X3JhdGluZ3NfZG9jICU+JSB0bV9tYXAocmVtb3ZlTnVtYmVycykgJT4lIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lIHRtX21hcChzdHJpcFdoaXRlc3BhY2UpCmxvd19yYXRpbmdzX2RvYyA8LSB0bV9tYXAobG93X3JhdGluZ3NfZG9jLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQpsb3dfcmF0aW5nc19kb2MgPC0gdG1fbWFwKGxvd19yYXRpbmdzX2RvYywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKQpgYGAKCmBgYHtyfQojIyMgTGV0cyBjcmVhdGUgYSBkYXRhZnJhbWUgY29udGFpbmluZyBlYWNoIHdvcmQgaW4gdGhlIGZpcnN0IGNvbHVtbiBhbmQgdGhlaXIgZnJlcXVlbmN5IGluIHRoZSBzZWNvbmQgY29sdW1uLgpvcHRpb25zKHdhcm49LTEpCmR0bV90b3AgPC0gVGVybURvY3VtZW50TWF0cml4KHRvcF9yYXRpbmdzX2RvYykgCm1hdHJpeF90b3AgPC0gYXMubWF0cml4KGR0bV90b3ApIAp3b3Jkc190b3AgPC0gc29ydChyb3dTdW1zKG1hdHJpeF90b3ApLGRlY3JlYXNpbmc9VFJVRSkgCndvcmRzX3RvcF9kZiA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyh3b3Jkc190b3ApLGZyZXE9d29yZHNfdG9wKQojIHdyaXRlLmNzdih3b3Jkc190b3BfZGYsICJEOlxcQ0QyMDAxQ19BRDIwMjRcXFBvd2VyX0JJX0RhdGFfQUQyMDI0XFx3b3JkY2xvdWRfYS5jc3YiLCByb3cubmFtZXM9VFJVRSkKYGBgCgpgYGB7cn0Kb3B0aW9ucyh3YXJuPS0xKQpkdG1fbG93IDwtIFRlcm1Eb2N1bWVudE1hdHJpeChsb3dfcmF0aW5nc19kb2MpIAptYXRyaXhfbG93IDwtIGFzLm1hdHJpeChkdG1fbG93KSAKd29yZHNfbG93IDwtIHNvcnQocm93U3VtcyhtYXRyaXhfbG93KSxkZWNyZWFzaW5nPVRSVUUpIAp3b3Jkc19sb3dfZGYgPC0gZGF0YS5mcmFtZSh3b3JkID0gbmFtZXMod29yZHNfbG93KSxmcmVxPXdvcmRzX2xvdykKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMjIFdlIGNhbiBub3cgZ2VuZXJhdGUgdGhlIHdvcmQgY2xvdWQgYWNjb3JkaW5nIHRvIHRoZSB0b3AgYW5kIGxvdyByYXRpbmdzIHJldmlld3MuCnNldC5zZWVkKDEyMzQpICMgZm9yIHJlcHJvZHVjaWJpbGl0eSAKIyMjIHRvcCByYXRpbmdzIAojIHRvcF9yYWl0aW5nX3djPC13b3JkY2xvdWQod29yZHMgPSB3b3Jkc190b3BfZGYkd29yZCwgZnJlcSA9IHdvcmRzX3RvcF9kZiRmcmVxLCBtaW4uZnJlcSA9IDEsIG1heC53b3Jkcz0yMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkKdG9wX3JhdGluZ193YyA8LSB3b3JkY2xvdWQyKGRhdGEgPSB3b3Jkc190b3BfZGYsIGNvbG9yID0gInJhbmRvbS1kYXJrIiwgc2l6ZSA9IDAuNiwgc2hhcGUgPSAiY2lyY2xlIikKdG9wX3JhdGluZ193YwojIyMgbG93IHJhdGluZ3MKI2xvd19yYWl0aW5nX3djPC13b3JkY2xvdWQod29yZHMgPSB3b3Jkc19sb3dfZGYkd29yZCwgZnJlcSA9IHdvcmRzX2xvd19kZiRmcmVxLCBtaW4uZnJlcSA9IDEsIG1heC53b3Jkcz0yMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkKbG93X3JhdGluZ193YyA8LSB3b3JkY2xvdWQyKGRhdGEgPSB3b3Jkc19sb3dfZGYsIGNvbG9yID0gInJhbmRvbS1kYXJrIiwgc2l6ZSA9IDAuNiwgc2hhcGUgPSAiY2lyY2xlIikKbG93X3JhdGluZ193YwpgYGAKCgojIyAqKlRFWFkgQU5BTFlTSVMgJiBBTkFMWVpFIFdPUkQgRlJFUVVFTkNJRVMqKgoKYGBge3J9CiMjIHJlcXVlc3QgbW9yZSBkZXRhaWxzIGFib3V0IHRoZSByZXN0YXVyYW50IHVzaW5nIGdvb2dsZV9wbGFjZV9kZXRhaWxzKCkKQ19DaGluYV9yZXZpZXdzIDwtIGdvb2dsZV9wbGFjZV9kZXRhaWxzKHBsYWNlX2lkID0gZGF0YSRwbGFjZV9pZFsxNV0sIGxhbmd1YWdlID0gImVzIiwga2V5ID0gIkFJemFTeURNdGtJRXZVcEZjY3lVQ0d4TGdYTy1UelRjRVJmekozbyIpCkNfQ2hpbmFfcmV2aWV3cwpgYGAKCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyMjIEJhc2VkIG9uIGdvb2dsZSByZXZpZXdzIGxldHMgZ2VuZXJhdGUgYSB2ZWN0b3IgY29udGFpbmluZyBvbmx5IHRoZSB0ZXh0LgojIyMgQ29ycHVzIGlzIGEgY29sbGVjdGlvbiBvZiB3cml0dGVuIHRleHRzIG9yIGEgYm9keSBvZiB3cml0aW5nIG9uIGEgcGFydGljdWxhciBzdWJqZWN0LgojIyMgQ2xlYW5pbmcgdGV4dCBkYXRhLiAKQ19DaGluYV9yZXZpZXdzX3RleHQgIDwtIENfQ2hpbmFfcmV2aWV3cyRyZXN1bHQkcmV2aWV3cyR0ZXh0CkNfQ2hpbmFfcmV2aWV3c19kb2MgICA8LSBDb3JwdXMoVmVjdG9yU291cmNlKENfQ2hpbmFfcmV2aWV3c190ZXh0KSkgICAjIyMgVG9rZW5pemF0aW9uOiBCcmVhayBkb3duIHRoZSB0ZXh0IGludG8gaW5kaXZpZHVhbCB3b3JkcyBvciB0b2tlbnMuIApDX0NoaW5hX3Jldmlld3NfZG9jIDwtIENfQ2hpbmFfcmV2aWV3c19kb2MgJT4lIHRtX21hcChyZW1vdmVOdW1iZXJzKSAlPiUgdG1fbWFwKHJlbW92ZVB1bmN0dWF0aW9uKSAlPiUgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkKQ19DaGluYV9yZXZpZXdzX2RvYyA8LSB0bV9tYXAoQ19DaGluYV9yZXZpZXdzX2RvYywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkKQ19DaGluYV9yZXZpZXdzX2RvYyA8LSB0bV9tYXAoQ19DaGluYV9yZXZpZXdzX2RvYywgcmVtb3ZlV29yZHMsIHN0b3B3b3Jkcygic3BhbmlzaCIpKQpgYGAKCmBgYHtyfQpkdG1fQ19DaGluYV9yZXZpZXdzIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChDX0NoaW5hX3Jldmlld3NfZG9jKSAKbWF0cml4IDwtIGFzLm1hdHJpeChkdG1fQ19DaGluYV9yZXZpZXdzKSAKd29yZHMgPC0gc29ydChyb3dTdW1zKG1hdHJpeCksZGVjcmVhc2luZz1UUlVFKSAKd29yZHNfZGYgPC0gZGF0YS5mcmFtZSh3b3JkID0gbmFtZXMod29yZHMpLGZyZXE9d29yZHMpCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIyBXb3JkIGNsb3VkIG9mIDUwIGhvc3BpdGFscyBhcm91bmQgdGhlIHN0YXJ0aW5nIHBvaW50IChQYXJxdWUgRnVuZGlkb3JhKQojIyMgd29yZHMgd2l0aCBmcmVxdWVuY3kgYmVsb3cgbWluLmZyZXEgd2lsbCBub3QgYmUgcGxvdHRlZAojIHdvcmRjbG91ZCh3b3JkcyA9IHdvcmRzX2RmJHdvcmQsIGZyZXEgPSB3b3Jkc19kZiRmcmVxLCBtaW4uZnJlcSA9IDUsIG1heC53b3Jkcz0yMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkKYGBgCgpgYGB7cn0Kc3RyKHdvcmRzX2RmKQpoZWFkKHdvcmRzX2RmKQpgYGAKCmBgYHtyfQojIyMgV29yZCBjbG91ZHMgYXJlIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRleHQgZGF0YSB3aGVyZSB3b3JkcyBhcmUgYXJyYW5nZWQgaW4gYSBjbHVzdGVyLiBUaGUgc2l6ZSBvZiBlYWNoIHdvcmQgcmVmbGVjdHMgaXRzIGZyZXF1ZW5jeSBvciBpbXBvcnRhbmNlIGluIHRoZSBkYXRhIHNldC4Kd29yZGNsb3VkMihkYXRhID0gd29yZHNfZGYsIGNvbG9yID0gInJhbmRvbS1kYXJrIiwgc2l6ZSA9IDAuNSwgc2hhcGUgPSAiY2lyY2xlIikKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMjIHRlcm0gbWF0cml4IGlzIGEgdGFibGUgY29udGFpbmluZyB0aGUgZnJlcXVlbmN5IG9mIHRoZSB3b3Jkcy4KdGRtX3NwYXJzZSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoQ19DaGluYV9yZXZpZXdzX2RvYywgY29udHJvbCA9IGxpc3Qod2VpZ2h0aW5nID0gd2VpZ2h0VGZJZGYpKQp0ZG1fbV9zcGFyc2UgPC0gYXMubWF0cml4KHRkbV9zcGFyc2UpCmBgYAoKYGBge3J9CiMgbGV0cyBkaXNwbGF5IGZyZXF1ZW5jeSBvZiB3b3JkcyAKdGVybV9mcmVxIDwtIHJvd1N1bXModGRtX21fc3BhcnNlKQp0ZXJtX2ZyZXFfc29ydGVkIDwtIHNvcnQodGVybV9mcmVxLCBkZWNyZWFzaW5nID0gVFJVRSkKdGRtX2Rfc3BhcnNlIDwtIGRhdGEuZnJhbWUod29yZCA9IG5hbWVzKHRlcm1fZnJlcV9zb3J0ZWQpLCBmcmVxID0gdGVybV9mcmVxX3NvcnRlZCkKCiMgbGV0cyBkaXNwbGF5IHRoZSB0b3AgMTAgbW9zdCBmcmVxdWVudCB3b3JkcwpoZWFkKHRkbV9kX3NwYXJzZSwgMTApCmBgYAoKYGBge3J9CiMgd2UgY3JlYXRlIGEgd29yZCBjbG91ZCB0byB2aXN1YWxpemUgdGhlIG1vc3QgZnJlcXVlbnQgd29yZHMgaW4gdGhlIGdvb2dsZSByZXZpZXdzCiMgd29yZGNsb3VkKHdvcmRzID0gdGRtX2Rfc3BhcnNlJHdvcmQsIGZyZXEgPSB0ZG1fZF9zcGFyc2UkZnJlcSwgbWluLmZyZXEgPSA0LCBtYXgud29yZHMgPSAxMDAsIGNvbG9ycyA9IGJyZXdlci5wYWwoOCwgIkRhcmsyIikpCndvcmRjbG91ZDIoZGF0YSA9IHRkbV9kX3NwYXJzZSwgY29sb3IgPSAicmFuZG9tLWRhcmsiLCBzaXplID0gMC42LCBzaGFwZSA9ICJjaXJjbGUiKQpgYGAKCiMgKipGT0NVU0lORyBPTiBTRU5USU1FTlQgQU5BTFlTSVMqKgoKYGBge3J9CiMgbGV0cyBjb252ZXJ0IHJldmlldyBjb2x1bW4gb2YgZGF0YWZyYW1lIHRvIGNoYXJhY3RlciB2ZWN0b3Igc28gd2UgY2FuIGdlbmVyYXRlIHNlbnRpbWVudCBzY29yZXMKdGV4dCA8LSBpY29udihDX0NoaW5hX3Jldmlld3NfdGV4dCkKYGBgCgpgYGB7cn0KIyBTeXV6aGV0IG1ldGhvZDogSXQgaXMgYW4gYWxnb3JpdGhtIHVzZWQgdG8gZXh0cmFjdCBlbW90aW9ucyBmcm9tIGEgc2VudGVuY2UuIEl0IGlzIGEgc2VudGltZW50IGxleGljb24gdGhhdCBjYW4gYmUgdXNlZCB3aXRoIG5vbi1FbmdsaXNoIHRleHRzLgpzeXV6aGV0X3ZlY3RvciA8LSBnZXRfc2VudGltZW50KHRleHQsIG1ldGhvZCA9ICJzeXV6aGV0IikgIyMjIGdldF9zZW50aW1lbnQoKSBmdW5jdGlvbiBpcyB1c2VkIHRvIGFuYWx5emUgc2VudGltZW50LiAKCiMgU2VlIGZpcnN0IHJvdyBvZiB2ZWN0b3IKaGVhZChzeXV6aGV0X3ZlY3RvcikKCiMgU2VlIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB2ZWN0b3IKc3VtbWFyeShzeXV6aGV0X3ZlY3RvcikKYGBgCgojIyAqKlNFTlRJTUVOVCBISVNUT0dSQU0qKgoKYGBge3J9CiMgVXNpbmcgdGhlIGZ1bGwgZGF0YXNldCBmb3Igc2VudGltZW50IGFuYWx5c2lzIG1pZ2h0IHN0aWxsIAp0ZXh0X3NhbXBsZWQgPC0gaWNvbnYoQ19DaGluYV9yZXZpZXdzX3RleHQpCnN5dXpoZXRfdmVjdG9yX3NhbXBsZWQgPC0gZ2V0X3NlbnRpbWVudCh0ZXh0X3NhbXBsZWQsIG1ldGhvZCA9ICJzeXV6aGV0IikKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEuZnJhbWUoc3l1emhldF92ZWN0b3Jfc2FtcGxlZCksIGFlcyh4ID0gc3l1emhldF92ZWN0b3Jfc2FtcGxlZCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKyAKICBsYWJzKHRpdGxlID0gIlNlbnRpbWVudCBEaXN0cmlidXRpb24gdXNpbmcgU3l1emhldCBNZXRob2QgKFNhbXBsZWQgRGF0YSkiLCB4ID0gIlNlbnRpbWVudCBTY29yZSIsIHkgPSAiRnJlcXVlbmN5IikgKyAKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyAqKkJBUiBQTE9UIE9GIEVNT1RJT05TKioKCmBgYHtyfQpucmNfc2FtcGxlZCA8LSBnZXRfbnJjX3NlbnRpbWVudCh0ZXh0X3NhbXBsZWQpICAgICMjIyBzZW50aW1lbnQgZGljdGlvbmFyeSB0byBjYWxjdWxhdGUgdGhlIHByZXNlbmNlIG9mIGVpZ2h0IGRpZmZlcmVudCBlbW90aW9ucyBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyB2YWxlbmNlIGluIGEgdGV4dCBmaWxlCm5yY3Rfc2FtcGxlZCA8LSBkYXRhLmZyYW1lKHQobnJjX3NhbXBsZWQpKQpucmNzX3NhbXBsZWQgPC0gZGF0YS5mcmFtZShyb3dTdW1zKG5yY3Rfc2FtcGxlZCkpCm5yY3Nfc2FtcGxlZCA8LSBjYmluZCgic2VudGltZW50IiA9IHJvd25hbWVzKG5yY3Nfc2FtcGxlZCksIG5yY3Nfc2FtcGxlZCkKcm93bmFtZXMobnJjc19zYW1wbGVkKSA8LSBOVUxMCm5hbWVzKG5yY3Nfc2FtcGxlZClbMV0gPC0gInNlbnRpbWVudCIKbmFtZXMobnJjc19zYW1wbGVkKVsyXSA8LSAiZnJlcXVlbmN5IgpucmNzX3NhbXBsZWQgPC0gbnJjc19zYW1wbGVkICU+JSBtdXRhdGUocGVyY2VudCA9IGZyZXF1ZW5jeS9zdW0oZnJlcXVlbmN5KSkKbnJjczJfc2FtcGxlZCA8LSBucmNzX3NhbXBsZWRbMTo4LCBdCmNvbG5hbWVzKG5yY3MyX3NhbXBsZWQpWzFdIDwtICJlbW90aW9uIgpgYGAKCmBgYHtyfQojIyMgMSkgVGhlIGJhciBwbG90IGlsbHVzdHJhdGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGVtb3Rpb25zIGJhc2VkIG9uIHNlbnRpbWVudCBhbmFseXNpcyB1c2luZyB0aGUgTlJDIGxleGljb24gb24gdGhlIGdvb2dsZSByZXZpZXdzCiMjIyAyKSBFYWNoIGJhciByZXByZXNlbnRzIGEgZGlmZmVyZW50IGVtb3Rpb24sIGFuZCB0aGUgaGVpZ2h0IG9mIHRoZSBiYXIgaW5kaWNhdGVzIHRoZSBmcmVxdWVuY3kgb2YgdGhhdCBlbW90aW9uIHdpdGhpbiB0aGUgdGV4dCBkYXRhLgpnZ3Bsb3QobnJjczJfc2FtcGxlZCwgYWVzKHggPSByZW9yZGVyKGVtb3Rpb24sIC1mcmVxdWVuY3kpLCB5ID0gZnJlcXVlbmN5LCBmaWxsID0gZW1vdGlvbikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgbGFicyh0aXRsZSA9ICJFbW90aW9uIERpc3RyaWJ1dGlvbiAoU2FtcGxlZCBEYXRhKSIsIHggPSAiRW1vdGlvbiIsIHkgPSAiRnJlcXVlbmN5IikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKQpgYGAKCgojIyAqKkJBUiBQTE9UIE9GIE1PU1QgUE9QVUxBUiBXT1JEUyoqCgpgYGB7cn0KIyMjIDEpIFRoZSBvdXRwdXQgaXMgYSBob3Jpem9udGFsIGJhciBwbG90IGlsbHVzdHJhdGluZyB0aGUgZnJlcXVlbmN5IG9mIHRoZSB0b3AgMTAgbW9zdCBwb3B1bGFyIHdvcmRzIGluIHRoZSB0ZXh0IGRhdGEuCiMjIyAyKSBFYWNoIGJhciByZXByZXNlbnRzIGEgd29yZCwgYW5kIHRoZSBsZW5ndGggb2YgdGhlIGJhciBpbmRpY2F0ZXMgdGhlIGZyZXF1ZW5jeSBvZiB0aGF0IHdvcmQgaW4gdGhlIGRhdGFzZXQuIAp0ZG1fZF9zcGFyc2UgPC0gdGRtX2Rfc3BhcnNlWzE6MTAsIF0KdGRtX2Rfc3BhcnNlJHdvcmQgPC0gcmVvcmRlcih0ZG1fZF9zcGFyc2Ukd29yZCwgdGRtX2Rfc3BhcnNlJGZyZXEpCmdncGxvdCh0ZG1fZF9zcGFyc2UsIGFlcyh4ID0gd29yZCwgeSA9IGZyZXEsIGZpbGwgPSB3b3JkKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICBjb29yZF9mbGlwKCkgKyAKICBsYWJzKHRpdGxlID0gIk1vc3QgUG9wdWxhciBXb3JkcyIsIHggPSAiV29yZCIsIHkgPSAiRnJlcXVlbmN5IikgKyAKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKIyMgKipQSUUgQ0hBUlQgT0YgU0VOVElNRU5UIERJU1RSSUJVVElPTioqCgpgYGB7cn0KIyBsZXRzIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBzZW50aW1lbnQgYW5kIGNvdW50CnNlbnRpbWVudF9kZiA8LSBkYXRhLmZyYW1lKHNlbnRpbWVudCA9IGMoIlBvc2l0aXZlIiwgIk5lZ2F0aXZlIiwgIk5ldXRyYWwiKSwgY291bnQgPSBjKHN1bShzeXV6aGV0X3ZlY3Rvcl9zYW1wbGVkID4gMCksIHN1bShzeXV6aGV0X3ZlY3Rvcl9zYW1wbGVkIDwgMCksIHN1bShzeXV6aGV0X3ZlY3Rvcl9zYW1wbGVkID09IDApKSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBwaWUgY2hhcnQKIyMjIDEpIFRoZSBvdXRwdXQgaXMgYSBwaWUgY2hhcnQgaWxsdXN0cmF0aW5nIHRoZSBkaXN0cmlidXRpb24gb2Ygc2VudGltZW50IGNhdGVnb3JpZXMgd2l0aGluIHRoZSBkYXRhc2V0LiAKIyMjIDIpIEVhY2ggc2VnbWVudCBvZiB0aGUgcGllIGNoYXJ0IHJlcHJlc2VudHMgYSBzZW50aW1lbnQgY2F0ZWdvcnkgKOKAnFBvc2l0aXZl4oCdLCDigJxOZWdhdGl2ZeKAnSwg4oCcTmV1dHJhbOKAnSksIGFuZCB0aGUgc2l6ZSBvZiBlYWNoIHNlZ21lbnQgY29ycmVzcG9uZHMgdG8gdGhlIGNvdW50IG9mIHRoYXQgc2VudGltZW50IGNhdGVnb3J5IGluIHRoZSBkYXRhc2V0LiAKZ2dwbG90KHNlbnRpbWVudF9kZiwgYWVzKHggPSAiIiwgeSA9IGNvdW50LCBmaWxsID0gc2VudGltZW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogIGxhYnModGl0bGUgPSAiU2VudGltZW50IERpc3RyaWJ1dGlvbiIsIHggPSAiIiwgeSA9ICIiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKQpgYGAKCiMgKio8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPkNvbmNsdXNpw7NuPC9zcGFuPioqCgoqKkJyZXZlbWVudGUgZGVzY3JpYmlyIGxvcyA0IC0gNiBwcmluY2lwYWxlcyBoYWxsYXpnb3MgaWRlbnRpZmljYWRvcyBlbiBlbCBwcm9jZXNvIGRlIGFuw6FsaXNpcyBkZSBHb29nbGUgTWFwcyBSZXZpZXdzIHkgYW7DoWxpc2lzIGRlIHNlbnRpbWllbnRvcyAoc2VudGltZW50IGFuYWx5c2lzKS4qKgotIEVsIHBlb3IgZGUgdG9kb3MgbG9zIHJlc3RhdXJhbnRlcyBlcyBGZSBMaW5nLCBsYSBjdWFsIG5vIHRpZW5lIHRhbnRvcyB2aXNpdGFudGVzLCBwZXJvIHNlIHBvZHJpYSBjb25zaWRlcmFyIHF1ZSBlbCBwZW9yIGRlIC0gdG9kb3MgdG9tYW5kbyBlbiBjdWVudGEgY2FudGlkYWQgZGUgcmV2aWV3cyB5IHJhdGluZ3MsIHNlcmlhIENoaW5vIEpyLgotIERvbmRlIHNlIGVuY3VlbnRyYXIgbGEgY29uY2VudHJhY2nDs24gZGUgbG9zIHBlb3JlcyByZXN0YXVyYW50ZXMgY2hpbm9zIGVzIGVuIGxhIHpvbmEgZGUgVmFsbGUgVmVyZGUuCi0gRWwgbWVqb3IgcmVzdGF1cmFudGUgY29uc2lkZXJhbmRvIGNhbnRpZGFkIGRlIHZpc2l0YXMgeSByYXRpbmcsIHNlcsOtYSBKaWEgRnUgQ29taWRhLgotIExvcyBtZWpvcmVzIHJlc3R1YXJ1YW50ZXMgc2UgZW5jdWVudHJhciBlbiBab25hIE1pdHJhcyB5IGNlbnRyby4KCioqwr9DdcOhbGVzIHNvbiBsYXMgdW5pZGFkZXMgZGUgbmVnb2NpbyBjb24gbG9zIG5pdmVsZXMgZGUgcmFpdGluZyBtw6FzIGFsdG8/IMK/Q3XDoWwgZXMgbGEgdWJpY2FjacOzbiBkZSBsYXMgdW5pZGFkZXMgZGUgbmVnb2NpbyBjb24gbG9zIG1lam9yZXMgcmFpdGluZ3M/IMK/Q3XDoWxlcyBzb24gbGFzIHByaW5jaXBhbGVzIGNhcmFjdGVyw61zdGljYXMgbG9jYWNpb25hbGVzLCDigJxyZXZpZXdz4oCdLCB5IGRlIHRpcG8gZGUgcGVyY2VwY2nDs24gcG9yIHBhcnRlIGRlIGxvcyBjbGllbnRlcz8qKgpZYWxpbiBDaGVuLCBKaWEgRnUsIFhpYW5nIExvbmcsIE5pbmV0eSBOaW5lLCBNaW5nIFhpbmcsIExhIEdyYW4gbXVyYWxsYSwgSHVpIFhpbiwgQ2hpbmVzZSBDaXR5LCBDaGluYSBIb3VzZSB5IENoaW5hIEtpbmcKU2UgZW5jdWVudHJhbiBlbiBab25hIE1pdHJhcyB5IGNlbnRyby4KVGUgdHJhdGFuIGJpZW4sIGVzdGEgbGltcGlvLCBlbCBzYWJvciBkZSBsYSBjb21pZGEgY29tbyBlbCBwb2xsbywgeSBkZSBtdXkgYnVlbiBwcmVjaW8uCgoqKsK/Q3XDoWxlcyBzb24gbGFzIHVuaWRhZGVzIGRlIG5lZ29jaW8gY29uIGxvcyBuaXZlbGVzIGRlIHJhaXRpbmcgbcOhcyBiYWpvPyDCv0N1w6FsIGVzIGxhIHViaWNhY2nDs24gZGUgbGFzIHVuaWRhZGVzIGRlIG5lZ29jaW8gY29uIGxvcyBtZW5vcmVzIHJhaXRpbmdzPyDCv0N1w6FsZXMgc29uIGxhcyBwcmluY2lwYWxlcyBjYXJhY3RlcsOtc3RpY2FzIGxvY2FjaW9uYWxlIHkg4oCccmV2aWV3c+KAnSBwb3IgcGFydGUgZGUgbG9zIGNsaWVudGVzPyoqCldhbmcgV2FuZywgQ2hlZiBPcmllbnRhbCwgRmUgTGluZywgSHVpeGluLCBFbCBDaGlubyBKciwgc29uIHJlc3RhdXJhbnRlcyBwZW9yIHZhbG9yYWRvcywgdWJpY2Fkb3MgcG9yIGxhIHpvbmEgZGUgVmFsbGUgVmVyZGUuCkVuIGVzdG9zIGx1Z2FyZXMgbGEgcXVlamEgbcOhcyBjb23Dum4gZXMgcXVlIGxhcyBwb3JjaW9uZXMgZGUgbG9zIHBsYXRvcyBubyBzb24gdGFuIGJhc3RhcywgcGFyYSBlbCBjb3N0by4KKirCv1F1w6kgdGlwbyBkZSBzZW50aW1pZW50byBkZXNjcmliZW4gbG9zIGNvbWVudGFyaW9zIGRlIEdvb2dsZSBSZXZpZXdzIGRlIGxhcyB1bmlkYWRlcyBkZSBuZWdvY2lvIHNlbGVjY2lvbmFkYXM/KioKU2Vnw7puIGVsIGFuYWxpc2lzIGRlIHNlbnRpbWllbnRvcywgZGlzZ3VzdG8sIG1pZWRvLCBlbm9qbyB5IHRyaXN0ZXphIHNvbiBsb3Mgc2VudGltaWVudG9zIG3DoXMgcHJlc2VudGVzLCBkZWJlIHNlciBwb3IgbGFzIGNyaXRpY2FzIGEgbG9zIG5lZ29jaW9zLgoK