Hypothesis, if the crowd drives the odds pricing, how able is the crowd to determine a wining horse (the horse with the shortest odds i.e. 2 to 1)?

Assumptions

  1. The crowd being the bookmaker punters i.e. those placing bets using bookmaker
  2. The favourite horse is that with the shortest odds (smallest odds). e.g. 2 to 1
  3. The data is the last price captured.

Created by Clive Bird January 2022.

bookmaker <- "Bet365"
racecourse <- "Chelmsford City"
odds <- "at 2.0 to 1"
model_paramter_tile <- "Wins"

# data vector represents all the race venue i.e horse race course.
# 1 = Win, 0 = Not Win
data = c(0,0,0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1)
# Actual race data shown below.
#Racecourse EventDateTime   HorseName   Odds    Outcome WinBoolean  FinalUnixTime
#Chelmsford City    2020-06-08 14:30:00.000 MARIA ROSA  2   LOSE    0   1591626600
#Chelmsford City    2020-06-17 17:50:00.000 NIGHT MOMENT    2   PLACED  0   1592416200
#Chelmsford City    2020-08-22 17:55:00.000 BRANWELL    2   LOSE    0   1598118900
#Chelmsford City    2020-10-08 19:30:00.000 INDIGO TIMES    2   WIN 1   1602185400
#Chelmsford City    2020-10-10 16:08:00.000 HIGHFIELD PRINCESS  2   WIN 1   1602346080
#Chelmsford City    2020-10-10 17:25:00.000 BULLACE 2   WIN 1   1602350700
#Chelmsford City    2020-11-12 17:00:00.000 TRUMBLE 2   WIN 1   1605200400
#Chelmsford City    2020-11-12 18:30:00.000 ABSOLUTE SCENES 2   WIN 1   1605205800
#Chelmsford City    2020-11-23 16:45:00.000 ORIENTALISM 2   PLACED  0   1606149900
#Chelmsford City    2020-11-27 19:45:00.000 FORTUNE FINDER  2   WIN 1   1606506300
#Chelmsford City    2020-12-17 16:55:00.000 MELODY OF LIFE  2   PLACED  0   1608224100
#Chelmsford City    2021-02-04 18:30:00.000 ELECTRIC BLUE   2   WIN 1   1612463400
#Chelmsford City    2021-02-18 20:30:00.000 COZONE  2   PLACED  0   1613680200
#Chelmsford City    2021-03-04 19:50:00.000 SERGEANT MAJOR  2   WIN 1   1614887400
#Chelmsford City    2021-03-18 17:55:00.000 SHOW ME A SUNSET    2   LOSE    0   1616090100
#Chelmsford City    2021-03-18 19:55:00.000 BELLISSIME  2   WIN 1   1616097300
#Chelmsford City    2021-08-10 20:20:00.000 MIDFIELD    2   LOSE    0   1628626800
#Chelmsford City    2021-08-21 21:00:00.000 MAHANAKHON POWER    2   WIN 1   1629579600
#Chelmsford City    2021-09-02 19:55:00.000 MOUNT MARCY 2   LOSE    0   1630612500
#Chelmsford City    2021-09-02 20:25:00.000 THE VEGAS RAIDER    2   WIN 1   1630614300
#Chelmsford City    2021-09-09 17:30:00.000 HARB    2   WIN 1   1631208600
#Chelmsford City    2021-09-09 17:30:00.000 IKHTIRAAQ   2   PLACED  0   1631208600
#Chelmsford City    2021-09-25 20:30:00.000 LA ROCA DEL FUEGO   2   WIN 1   1632601800
#Chelmsford City    2021-10-07 18:30:00.000 FLORA FINCH 2   PLACED  0   1633631400
#Chelmsford City    2021-10-07 20:30:00.000 LINDWALL    2   WIN 1   1633638600


install.packages('dplyr')
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.1/dplyr_1.0.7.zip'
Content type 'application/zip' length 1345624 bytes (1.3 MB)
downloaded 1.3 MB
package ‘dplyr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Administrator\AppData\Local\Temp\2\Rtmp4sIJS4\downloaded_packages
install.packages('gridExtra')
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.1/gridExtra_2.3.zip'
Content type 'application/zip' length 1109502 bytes (1.1 MB)
downloaded 1.1 MB
package ‘gridExtra’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Administrator\AppData\Local\Temp\2\Rtmp4sIJS4\downloaded_packages
install.packages('tidyverse')
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.1/tidyverse_1.3.1.zip'
Content type 'application/zip' length 430268 bytes (420 KB)
downloaded 420 KB
package ‘tidyverse’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Administrator\AppData\Local\Temp\2\Rtmp4sIJS4\downloaded_packages
install.packages('ggridges')
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.1/ggridges_0.5.3.zip'
Content type 'application/zip' length 2259262 bytes (2.2 MB)
downloaded 2.2 MB
package ‘ggridges’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Administrator\AppData\Local\Temp\2\Rtmp4sIJS4\downloaded_packages
install.packages('ggExtra')
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.1/ggExtra_0.9.zip'
Content type 'application/zip' length 381276 bytes (372 KB)
downloaded 372 KB
package ‘ggExtra’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Administrator\AppData\Local\Temp\2\Rtmp4sIJS4\downloaded_packages
library( dplyr )

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library( ggplot2 )
library( gridExtra )

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
library( tidyverse )
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ------------------------------------ tidyverse 1.3.1 --
v tibble  3.1.6     v purrr   0.3.4
v tidyr   1.1.4     v stringr 1.4.0
v readr   2.1.1     v forcats 0.5.1
-- Conflicts --------------------------------------- tidyverse_conflicts() --
x gridExtra::combine() masks dplyr::combine()
x dplyr::filter()      masks stats::filter()
x dplyr::lag()         masks stats::lag()
library( ggridges )
library( ggExtra )
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
library( lattice )
library( RColorBrewer )

chart_title <- paste(racecourse, " using ", sep=" ")
chart_title <- paste(chart_title, bookmaker, sep=" ")
chart_title <- paste(chart_title, odds, sep=" ")

#The prop_model function

# This function takes a number of successes and failure coded as a TRUE/FALSE
# or 0/1 vector. This should be given as the data argument.
# The result is a visualization of the how a Beta-Binomial
# model gradually learns the underlying proportion of successes 
# using this data. The function also returns a sample from the
# posterior distribution that can be further manipulated and inspected.
# The default prior is a Beta(1,1) distribution, but this can be set using the
# prior_prop argument.

# Make sure the packages tidyverse and ggridges are installed, otherwise run:
# install.packages(c("tidyverse", "ggridges"))

# Example usage:
# data <- c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE)
# prop_model(data)
prop_model <- function(data = c(), 
                       prior_prop = c(1, 1), 
                       n_draws = 1000000,
                       gr_name="Proportion graph",
                       model_paramter_tile,
                       chart_title
                       ) {
  
  data <- as.logical(data)
  # data_indices decides what densities to plot between the prior and the posterior
  # For 20 datapoints and less we're plotting all of them.
  data_indices <- round(seq(0, length(data), length.out = min(length(data) + 1, 40)))
  
  # dens_curves will be a data frame with the x & y coordinates for the 
  # denities to plot where x = proportion_success and y = probability
  proportion_success <- c(0, seq(0, 1, length.out = 100), 1)
  dens_curves <- map_dfr(data_indices, function(i) {
    value <- ifelse(i == 0, "Prior", ifelse(data[i], "Win", "Not Win"))
    label <- paste0("n=", i)
    probability <- dbeta(proportion_success,
                         prior_prop[1] + sum(data[seq_len(i)]),
                         prior_prop[2] + sum(!data[seq_len(i)]))
    probability <- probability / max(probability)
    data_frame(value, label, proportion_success, probability)
  })
  # Turning label and value into factors with the right ordering for the plot
  dens_curves$label <- fct_rev(factor(dens_curves$label, levels =  paste0("n=", data_indices )))
  dens_curves$value <- factor(dens_curves$value, levels = c("Prior", "Win", "Not Win"))
  
  graph_label <- paste("Prior likelihood distribution Beta(a =", 
                       as.character(prior_prop[1]),", b =",
                                    as.character(prior_prop[2]),")") 
  
  p <- ggplot(dens_curves, aes(x = proportion_success, y = label,
                               height = probability, fill = value)) +
    ggridges::geom_density_ridges(stat="identity", color = "white", alpha = 0.8,
                                  panel_scaling = TRUE, size = 1) +
    scale_y_discrete("", expand = c(0.01, 0)) +
    scale_x_continuous(model_paramter_tile) +
    scale_fill_manual(values = hcl(120 * 2:0 + 15, 100, 65), name = "", drop = FALSE,
                      labels =  c("Prior   ", "Win   ", "Not Win   ")) +
    ggtitle(paste0(gr_name, ": ", sum(data),  " Win, ", sum(!data), " Not Win"),
            subtitle = graph_label) +
    labs(caption = chart_title) +
    theme_light() +
    theme(legend.position = "top")
  print(p)
  
  # Returning a sample from the posterior distribution that can be further 
  # manipulated and inspected
  posterior_sample <- rbeta(n_draws, prior_prop[1] + sum(data), prior_prop[2] + sum(!data))
  invisible(posterior_sample)
}


# Extract and explore the posterior
posterior <- prop_model(data, model_paramter_tile=model_paramter_tile, chart_title=chart_title)
Warning: `data_frame()` was deprecated in tibble 1.1.0.
Please use `tibble()` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

head(posterior)
[1] 0.5471468 0.6108983 0.6465365 0.6157755 0.5881064 0.6000661
hist(posterior, breaks = 30, xlim = c(0, 1), col = "gold")


# Inspect the posterior distribution model's parameters of interest.
# Median is the 'Best guess' point estimate.
# 90% & 95% credible interval (CI)

# Median is the 'Best guess' point estimate.
summary(posterior)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.1312  0.4916  0.5571  0.5556  0.6210  0.9041 
# Measure the credible interval. I.e. the probability expressed a %, that the model's posterior parameter of interest value will fall between this probability range.

# A 90% probability that the parameter of interest value is between this range. 90% credible interval
quantile(posterior, c(0.05, 0.95))
       5%       95% 
0.3983478 0.7080927 
# A 95% probability that the parameter of interest value is between this range. 95% credible interval
quantile(posterior, c(0.05 - 0.025, 0.95 + 0.025))
     2.5%     97.5% 
0.3691813 0.7344128 
# Calculate the probability that the 'win to not win' ratio is greater than or equal to more than half (50%).
# I.e. The probability I will win more than I loose if I bet on all like odds driven by the crowd using this book maker at this race course and odds combination.

cat("Probability that the 'win to not win' ratio is greater than or equal to 50% is ", sum( posterior >= 0.5 )/length(posterior) ) 
Probability that the 'win to not win' ratio is greater than or equal to 50% is  0.721641
LS0tDQp0aXRsZTogIlByb2plY3QgRXF1b3J1bSBSIE5vdGVib29rIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KICBIeXBvdGhlc2lzLCBpZiB0aGUgY3Jvd2QgZHJpdmVzIHRoZSBvZGRzIHByaWNpbmcsIGhvdyBhYmxlIGlzIHRoZSBjcm93ZCB0byBkZXRlcm1pbmUgYSB3aW5pbmcgaG9yc2UgKHRoZSBob3JzZSB3aXRoIHRoZSBzaG9ydGVzdCBvZGRzIGkuZS4gMiB0byAxKT8NCiAgDQogIEFzc3VtcHRpb25zDQoNCjEuIFRoZSBjcm93ZCBiZWluZyB0aGUgYm9va21ha2VyIHB1bnRlcnMgaS5lLiB0aG9zZSBwbGFjaW5nIGJldHMgdXNpbmcgYm9va21ha2VyDQoyLiBUaGUgZmF2b3VyaXRlIGhvcnNlIGlzIHRoYXQgd2l0aCB0aGUgc2hvcnRlc3Qgb2RkcyAoc21hbGxlc3Qgb2RkcykuIGUuZy4gMiB0byAxDQozLiBUaGUgZGF0YSBpcyB0aGUgbGFzdCBwcmljZSBjYXB0dXJlZC4NCg0KQ3JlYXRlZCBieSBDbGl2ZSBCaXJkIEphbnVhcnkgMjAyMi4NCg0KYGBge3J9DQpib29rbWFrZXIgPC0gIkJldDM2NSINCnJhY2Vjb3Vyc2UgPC0gIkNoZWxtc2ZvcmQgQ2l0eSINCm9kZHMgPC0gImF0IDIuMCB0byAxIg0KbW9kZWxfcGFyYW10ZXJfdGlsZSA8LSAiV2lucyINCg0KIyBkYXRhIHZlY3RvciByZXByZXNlbnRzIGFsbCB0aGUgcmFjZSB2ZW51ZSBpLmUgaG9yc2UgcmFjZSBjb3Vyc2UuDQojIDEgPSBXaW4sIDAgPSBOb3QgV2luDQpkYXRhID0gYygwLDAsMCwxLDEsMSwxLDEsMCwxLDAsMSwwLDEsMCwxLDAsMSwwLDEsMSwwLDEsMCwxKQ0KIyBBY3R1YWwgcmFjZSBkYXRhIHNob3duIGJlbG93Lg0KI1JhY2Vjb3Vyc2UJRXZlbnREYXRlVGltZQlIb3JzZU5hbWUJT2RkcwlPdXRjb21lCVdpbkJvb2xlYW4JRmluYWxVbml4VGltZQ0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIwLTA2LTA4IDE0OjMwOjAwLjAwMAlNQVJJQSBST1NBCTIJTE9TRQkwCTE1OTE2MjY2MDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMC0wNi0xNyAxNzo1MDowMC4wMDAJTklHSFQgTU9NRU5UCTIJUExBQ0VECTAJMTU5MjQxNjIwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIwLTA4LTIyIDE3OjU1OjAwLjAwMAlCUkFOV0VMTAkyCUxPU0UJMAkxNTk4MTE4OTAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjAtMTAtMDggMTk6MzA6MDAuMDAwCUlORElHTyBUSU1FUwkyCVdJTgkxCTE2MDIxODU0MDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMC0xMC0xMCAxNjowODowMC4wMDAJSElHSEZJRUxEIFBSSU5DRVNTCTIJV0lOCTEJMTYwMjM0NjA4MA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIwLTEwLTEwIDE3OjI1OjAwLjAwMAlCVUxMQUNFCTIJV0lOCTEJMTYwMjM1MDcwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIwLTExLTEyIDE3OjAwOjAwLjAwMAlUUlVNQkxFCTIJV0lOCTEJMTYwNTIwMDQwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIwLTExLTEyIDE4OjMwOjAwLjAwMAlBQlNPTFVURSBTQ0VORVMJMglXSU4JMQkxNjA1MjA1ODAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjAtMTEtMjMgMTY6NDU6MDAuMDAwCU9SSUVOVEFMSVNNCTIJUExBQ0VECTAJMTYwNjE0OTkwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIwLTExLTI3IDE5OjQ1OjAwLjAwMAlGT1JUVU5FIEZJTkRFUgkyCVdJTgkxCTE2MDY1MDYzMDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMC0xMi0xNyAxNjo1NTowMC4wMDAJTUVMT0RZIE9GIExJRkUJMglQTEFDRUQJMAkxNjA4MjI0MTAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjEtMDItMDQgMTg6MzA6MDAuMDAwCUVMRUNUUklDIEJMVUUJMglXSU4JMQkxNjEyNDYzNDAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjEtMDItMTggMjA6MzA6MDAuMDAwCUNPWk9ORQkyCVBMQUNFRAkwCTE2MTM2ODAyMDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMS0wMy0wNCAxOTo1MDowMC4wMDAJU0VSR0VBTlQgTUFKT1IJMglXSU4JMQkxNjE0ODg3NDAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjEtMDMtMTggMTc6NTU6MDAuMDAwCVNIT1cgTUUgQSBTVU5TRVQJMglMT1NFCTAJMTYxNjA5MDEwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIxLTAzLTE4IDE5OjU1OjAwLjAwMAlCRUxMSVNTSU1FCTIJV0lOCTEJMTYxNjA5NzMwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIxLTA4LTEwIDIwOjIwOjAwLjAwMAlNSURGSUVMRAkyCUxPU0UJMAkxNjI4NjI2ODAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjEtMDgtMjEgMjE6MDA6MDAuMDAwCU1BSEFOQUtIT04gUE9XRVIJMglXSU4JMQkxNjI5NTc5NjAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjEtMDktMDIgMTk6NTU6MDAuMDAwCU1PVU5UIE1BUkNZCTIJTE9TRQkwCTE2MzA2MTI1MDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMS0wOS0wMiAyMDoyNTowMC4wMDAJVEhFIFZFR0FTIFJBSURFUgkyCVdJTgkxCTE2MzA2MTQzMDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMS0wOS0wOSAxNzozMDowMC4wMDAJSEFSQgkyCVdJTgkxCTE2MzEyMDg2MDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMS0wOS0wOSAxNzozMDowMC4wMDAJSUtIVElSQUFRCTIJUExBQ0VECTAJMTYzMTIwODYwMA0KI0NoZWxtc2ZvcmQgQ2l0eQkyMDIxLTA5LTI1IDIwOjMwOjAwLjAwMAlMQSBST0NBIERFTCBGVUVHTwkyCVdJTgkxCTE2MzI2MDE4MDANCiNDaGVsbXNmb3JkIENpdHkJMjAyMS0xMC0wNyAxODozMDowMC4wMDAJRkxPUkEgRklOQ0gJMglQTEFDRUQJMAkxNjMzNjMxNDAwDQojQ2hlbG1zZm9yZCBDaXR5CTIwMjEtMTAtMDcgMjA6MzA6MDAuMDAwCUxJTkRXQUxMCTIJV0lOCTEJMTYzMzYzODYwMA0KDQoNCmluc3RhbGwucGFja2FnZXMoJ2RwbHlyJykNCmluc3RhbGwucGFja2FnZXMoJ2dyaWRFeHRyYScpDQppbnN0YWxsLnBhY2thZ2VzKCd0aWR5dmVyc2UnKQ0KaW5zdGFsbC5wYWNrYWdlcygnZ2dyaWRnZXMnKQ0KaW5zdGFsbC5wYWNrYWdlcygnZ2dFeHRyYScpDQoNCmxpYnJhcnkoIGRwbHlyICkNCmxpYnJhcnkoIGdncGxvdDIgKQ0KbGlicmFyeSggZ3JpZEV4dHJhICkNCmxpYnJhcnkoIHRpZHl2ZXJzZSApDQpsaWJyYXJ5KCBnZ3JpZGdlcyApDQpsaWJyYXJ5KCBnZ0V4dHJhICkNCmxpYnJhcnkoIGxhdHRpY2UgKQ0KbGlicmFyeSggUkNvbG9yQnJld2VyICkNCg0KY2hhcnRfdGl0bGUgPC0gcGFzdGUocmFjZWNvdXJzZSwgIiB1c2luZyAiLCBzZXA9IiAiKQ0KY2hhcnRfdGl0bGUgPC0gcGFzdGUoY2hhcnRfdGl0bGUsIGJvb2ttYWtlciwgc2VwPSIgIikNCmNoYXJ0X3RpdGxlIDwtIHBhc3RlKGNoYXJ0X3RpdGxlLCBvZGRzLCBzZXA9IiAiKQ0KDQojVGhlIHByb3BfbW9kZWwgZnVuY3Rpb24NCg0KIyBUaGlzIGZ1bmN0aW9uIHRha2VzIGEgbnVtYmVyIG9mIHN1Y2Nlc3NlcyBhbmQgZmFpbHVyZSBjb2RlZCBhcyBhIFRSVUUvRkFMU0UNCiMgb3IgMC8xIHZlY3Rvci4gVGhpcyBzaG91bGQgYmUgZ2l2ZW4gYXMgdGhlIGRhdGEgYXJndW1lbnQuDQojIFRoZSByZXN1bHQgaXMgYSB2aXN1YWxpemF0aW9uIG9mIHRoZSBob3cgYSBCZXRhLUJpbm9taWFsDQojIG1vZGVsIGdyYWR1YWxseSBsZWFybnMgdGhlIHVuZGVybHlpbmcgcHJvcG9ydGlvbiBvZiBzdWNjZXNzZXMgDQojIHVzaW5nIHRoaXMgZGF0YS4gVGhlIGZ1bmN0aW9uIGFsc28gcmV0dXJucyBhIHNhbXBsZSBmcm9tIHRoZQ0KIyBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIHRoYXQgY2FuIGJlIGZ1cnRoZXIgbWFuaXB1bGF0ZWQgYW5kIGluc3BlY3RlZC4NCiMgVGhlIGRlZmF1bHQgcHJpb3IgaXMgYSBCZXRhKDEsMSkgZGlzdHJpYnV0aW9uLCBidXQgdGhpcyBjYW4gYmUgc2V0IHVzaW5nIHRoZQ0KIyBwcmlvcl9wcm9wIGFyZ3VtZW50Lg0KDQojIE1ha2Ugc3VyZSB0aGUgcGFja2FnZXMgdGlkeXZlcnNlIGFuZCBnZ3JpZGdlcyBhcmUgaW5zdGFsbGVkLCBvdGhlcndpc2UgcnVuOg0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIsICJnZ3JpZGdlcyIpKQ0KDQojIEV4YW1wbGUgdXNhZ2U6DQojIGRhdGEgPC0gYyhUUlVFLCBGQUxTRSwgVFJVRSwgVFJVRSwgRkFMU0UsIFRSVUUsIFRSVUUpDQojIHByb3BfbW9kZWwoZGF0YSkNCnByb3BfbW9kZWwgPC0gZnVuY3Rpb24oZGF0YSA9IGMoKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHByaW9yX3Byb3AgPSBjKDEsIDEpLCANCiAgICAgICAgICAgICAgICAgICAgICAgbl9kcmF3cyA9IDEwMDAwMDAsDQogICAgICAgICAgICAgICAgICAgICAgIGdyX25hbWU9IlByb3BvcnRpb24gZ3JhcGgiLA0KICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9wYXJhbXRlcl90aWxlLA0KICAgICAgICAgICAgICAgICAgICAgICBjaGFydF90aXRsZQ0KICAgICAgICAgICAgICAgICAgICAgICApIHsNCiAgDQogIGRhdGEgPC0gYXMubG9naWNhbChkYXRhKQ0KICAjIGRhdGFfaW5kaWNlcyBkZWNpZGVzIHdoYXQgZGVuc2l0aWVzIHRvIHBsb3QgYmV0d2VlbiB0aGUgcHJpb3IgYW5kIHRoZSBwb3N0ZXJpb3INCiAgIyBGb3IgMjAgZGF0YXBvaW50cyBhbmQgbGVzcyB3ZSdyZSBwbG90dGluZyBhbGwgb2YgdGhlbS4NCiAgZGF0YV9pbmRpY2VzIDwtIHJvdW5kKHNlcSgwLCBsZW5ndGgoZGF0YSksIGxlbmd0aC5vdXQgPSBtaW4obGVuZ3RoKGRhdGEpICsgMSwgNDApKSkNCiAgDQogICMgZGVuc19jdXJ2ZXMgd2lsbCBiZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgeCAmIHkgY29vcmRpbmF0ZXMgZm9yIHRoZSANCiAgIyBkZW5pdGllcyB0byBwbG90IHdoZXJlIHggPSBwcm9wb3J0aW9uX3N1Y2Nlc3MgYW5kIHkgPSBwcm9iYWJpbGl0eQ0KICBwcm9wb3J0aW9uX3N1Y2Nlc3MgPC0gYygwLCBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDEwMCksIDEpDQogIGRlbnNfY3VydmVzIDwtIG1hcF9kZnIoZGF0YV9pbmRpY2VzLCBmdW5jdGlvbihpKSB7DQogICAgdmFsdWUgPC0gaWZlbHNlKGkgPT0gMCwgIlByaW9yIiwgaWZlbHNlKGRhdGFbaV0sICJXaW4iLCAiTm90IFdpbiIpKQ0KICAgIGxhYmVsIDwtIHBhc3RlMCgibj0iLCBpKQ0KICAgIHByb2JhYmlsaXR5IDwtIGRiZXRhKHByb3BvcnRpb25fc3VjY2VzcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcl9wcm9wWzFdICsgc3VtKGRhdGFbc2VxX2xlbihpKV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yX3Byb3BbMl0gKyBzdW0oIWRhdGFbc2VxX2xlbihpKV0pKQ0KICAgIHByb2JhYmlsaXR5IDwtIHByb2JhYmlsaXR5IC8gbWF4KHByb2JhYmlsaXR5KQ0KICAgIGRhdGFfZnJhbWUodmFsdWUsIGxhYmVsLCBwcm9wb3J0aW9uX3N1Y2Nlc3MsIHByb2JhYmlsaXR5KQ0KICB9KQ0KICAjIFR1cm5pbmcgbGFiZWwgYW5kIHZhbHVlIGludG8gZmFjdG9ycyB3aXRoIHRoZSByaWdodCBvcmRlcmluZyBmb3IgdGhlIHBsb3QNCiAgZGVuc19jdXJ2ZXMkbGFiZWwgPC0gZmN0X3JldihmYWN0b3IoZGVuc19jdXJ2ZXMkbGFiZWwsIGxldmVscyA9ICBwYXN0ZTAoIm49IiwgZGF0YV9pbmRpY2VzICkpKQ0KICBkZW5zX2N1cnZlcyR2YWx1ZSA8LSBmYWN0b3IoZGVuc19jdXJ2ZXMkdmFsdWUsIGxldmVscyA9IGMoIlByaW9yIiwgIldpbiIsICJOb3QgV2luIikpDQogIA0KICBncmFwaF9sYWJlbCA8LSBwYXN0ZSgiUHJpb3IgbGlrZWxpaG9vZCBkaXN0cmlidXRpb24gQmV0YShhID0iLCANCiAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHByaW9yX3Byb3BbMV0pLCIsIGIgPSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocHJpb3JfcHJvcFsyXSksIikiKSANCiAgDQogIHAgPC0gZ2dwbG90KGRlbnNfY3VydmVzLCBhZXMoeCA9IHByb3BvcnRpb25fc3VjY2VzcywgeSA9IGxhYmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IHByb2JhYmlsaXR5LCBmaWxsID0gdmFsdWUpKSArDQogICAgZ2dyaWRnZXM6Omdlb21fZGVuc2l0eV9yaWRnZXMoc3RhdD0iaWRlbnRpdHkiLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbmVsX3NjYWxpbmcgPSBUUlVFLCBzaXplID0gMSkgKw0KICAgIHNjYWxlX3lfZGlzY3JldGUoIiIsIGV4cGFuZCA9IGMoMC4wMSwgMCkpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobW9kZWxfcGFyYW10ZXJfdGlsZSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGhjbCgxMjAgKiAyOjAgKyAxNSwgMTAwLCA2NSksIG5hbWUgPSAiIiwgZHJvcCA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9ICBjKCJQcmlvciAgICIsICJXaW4gICAiLCAiTm90IFdpbiAgICIpKSArDQogICAgZ2d0aXRsZShwYXN0ZTAoZ3JfbmFtZSwgIjogIiwgc3VtKGRhdGEpLCAgIiBXaW4sICIsIHN1bSghZGF0YSksICIgTm90IFdpbiIpLA0KICAgICAgICAgICAgc3VidGl0bGUgPSBncmFwaF9sYWJlbCkgKw0KICAgIGxhYnMoY2FwdGlvbiA9IGNoYXJ0X3RpdGxlKSArDQogICAgdGhlbWVfbGlnaHQoKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQogIHByaW50KHApDQogIA0KICAjIFJldHVybmluZyBhIHNhbXBsZSBmcm9tIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIHRoYXQgY2FuIGJlIGZ1cnRoZXIgDQogICMgbWFuaXB1bGF0ZWQgYW5kIGluc3BlY3RlZA0KICBwb3N0ZXJpb3Jfc2FtcGxlIDwtIHJiZXRhKG5fZHJhd3MsIHByaW9yX3Byb3BbMV0gKyBzdW0oZGF0YSksIHByaW9yX3Byb3BbMl0gKyBzdW0oIWRhdGEpKQ0KICBpbnZpc2libGUocG9zdGVyaW9yX3NhbXBsZSkNCn0NCg0KDQojIEV4dHJhY3QgYW5kIGV4cGxvcmUgdGhlIHBvc3Rlcmlvcg0KcG9zdGVyaW9yIDwtIHByb3BfbW9kZWwoZGF0YSwgbW9kZWxfcGFyYW10ZXJfdGlsZT1tb2RlbF9wYXJhbXRlcl90aWxlLCBjaGFydF90aXRsZT1jaGFydF90aXRsZSkNCmhlYWQocG9zdGVyaW9yKQ0KDQoNCmhpc3QocG9zdGVyaW9yLCBicmVha3MgPSAzMCwgeGxpbSA9IGMoMCwgMSksIGNvbCA9ICJnb2xkIikNCg0KIyBJbnNwZWN0IHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG1vZGVsJ3MgcGFyYW1ldGVycyBvZiBpbnRlcmVzdC4NCiMgTWVkaWFuIGlzIHRoZSAnQmVzdCBndWVzcycgcG9pbnQgZXN0aW1hdGUuDQojIDkwJSAmIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCAoQ0kpDQoNCiMgTWVkaWFuIGlzIHRoZSAnQmVzdCBndWVzcycgcG9pbnQgZXN0aW1hdGUuDQpzdW1tYXJ5KHBvc3RlcmlvcikNCg0KIyBNZWFzdXJlIHRoZSBjcmVkaWJsZSBpbnRlcnZhbC4gSS5lLiB0aGUgcHJvYmFiaWxpdHkgZXhwcmVzc2VkIGEgJSwgdGhhdCB0aGUgbW9kZWwncyBwb3N0ZXJpb3IgcGFyYW1ldGVyIG9mIGludGVyZXN0IHZhbHVlIHdpbGwgZmFsbCBiZXR3ZWVuIHRoaXMgcHJvYmFiaWxpdHkgcmFuZ2UuDQoNCiMgQSA5MCUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgcGFyYW1ldGVyIG9mIGludGVyZXN0IHZhbHVlIGlzIGJldHdlZW4gdGhpcyByYW5nZS4gOTAlIGNyZWRpYmxlIGludGVydmFsDQpxdWFudGlsZShwb3N0ZXJpb3IsIGMoMC4wNSwgMC45NSkpDQoNCiMgQSA5NSUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgcGFyYW1ldGVyIG9mIGludGVyZXN0IHZhbHVlIGlzIGJldHdlZW4gdGhpcyByYW5nZS4gOTUlIGNyZWRpYmxlIGludGVydmFsDQpxdWFudGlsZShwb3N0ZXJpb3IsIGMoMC4wNSAtIDAuMDI1LCAwLjk1ICsgMC4wMjUpKQ0KDQojIENhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgJ3dpbiB0byBub3Qgd2luJyByYXRpbyBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gbW9yZSB0aGFuIGhhbGYgKDUwJSkuDQojIEkuZS4gVGhlIHByb2JhYmlsaXR5IEkgd2lsbCB3aW4gbW9yZSB0aGFuIEkgbG9vc2UgaWYgSSBiZXQgb24gYWxsIGxpa2Ugb2RkcyBkcml2ZW4gYnkgdGhlIGNyb3dkIHVzaW5nIHRoaXMgYm9vayBtYWtlciBhdCB0aGlzIHJhY2UgY291cnNlIGFuZCBvZGRzIGNvbWJpbmF0aW9uLg0KDQpjYXQoIlByb2JhYmlsaXR5IHRoYXQgdGhlICd3aW4gdG8gbm90IHdpbicgcmF0aW8gaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDUwJSBpcyAiLCBzdW0oIHBvc3RlcmlvciA+PSAwLjUgKS9sZW5ndGgocG9zdGVyaW9yKSApIA0KDQoNCmBgYA0KDQoNCg0K