Many people start playing the lottery for fun, but for some, this activity can escalate into habit and addiction. Similar to other compulsive gamblers, lottery addicts may start using savings and loans to buy tickets. A mobile app is required which is intended to guide lottery addicts through exercises that will let them better estimate their chances of winning. The hope is that this app will help them realize that buying too many tickets will do little to improve their chances of winning. This code will form the logic for the app.

#Load libraries and the data
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
library (readr)
library(stringr)
library(ggplot2)
library(dplyr)
library(purrr)
library(tidyr)
library(magrittr)
library(ggmap)
library(mapproj)
library(viridis)
library(RColorBrewer)
library(grid)

setwd("C:/Users/Ana/Desktop/Data Analytics/CSV Files")

#import data
data <- read_csv("649.csv") 
Parsed with column specification:
cols(
  PRODUCT = col_double(),
  `DRAW NUMBER` = col_double(),
  `SEQUENCE NUMBER` = col_double(),
  `DRAW DATE` = col_character(),
  `NUMBER DRAWN 1` = col_double(),
  `NUMBER DRAWN 2` = col_double(),
  `NUMBER DRAWN 3` = col_double(),
  `NUMBER DRAWN 4` = col_double(),
  `NUMBER DRAWN 5` = col_double(),
  `NUMBER DRAWN 6` = col_double(),
  `BONUS NUMBER` = col_double()
)
#view beginning and end of dataset
head(data, 3)
tail(data, 3)

#return number of rows in dataset
print(nrow(data))
[1] 3665
#return number of columns in dataset
print(ncol(data))
[1] 11

#count how many unique entries there are for each column
colnames(data)
 [1] "PRODUCT"         "DRAW NUMBER"     "SEQUENCE NUMBER" "DRAW DATE"       "NUMBER DRAWN 1"  "NUMBER DRAWN 2" 
 [7] "NUMBER DRAWN 3"  "NUMBER DRAWN 4"  "NUMBER DRAWN 5"  "NUMBER DRAWN 6"  "BONUS NUMBER"   
for(i in colnames(data)){
  print(as.integer(count(unique(data[,i]))))
}
[1] 1
[1] 3591
[1] 4
[1] 3591
[1] 34
[1] 42
[1] 43
[1] 43
[1] 38
[1] 33
[1] 50
sort(unique(data$`NUMBER DRAWN 1`))
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 38

Next, calculate the probability of winning the draw with a single ticket.

#The order of the permutations doesn't matter in this instance, so we are looking for the total number of combinations. This is given by C = n!/((n-k)!k!) 
#Where:
#n = total number of numbers i.e. 49, k = number allowed i.e. 6.

#first, write a function which will calculate the factorial of any number.

factorial <- function(n){
  answer<-1
  for(i in 1:n) {
    answer<-answer*i
  }
  return(answer)
}

#now write a function to calculate the number of permutations when only a certain number of balls are drawn.

permutation <- function(n, k) {
  factorial(n)/factorial(n-k)
}

#now write a function to calculate the number of combinations when only a certain number of balls are drawn.

combinations <- function(n, k) {
  permutation(n, k)/factorial(k)
}

#the probability of winning the draw with a single ticket is therefore 1 divided by the number of combinations.

combinations(49,6)
[1] 13983816
p_win_1t <- 1/combinations(49,6)
p_win_1t
[1] 7.151124e-08

The probability of winning the lottery with one ticket is 1 in ~14million or 7e-8 or 0.00000007 or 0.000007%

The app wants the user to be able to enter 6 numbers i.e. their chosen numbers, and for the app to display the chance of them winning with those numbers. Although I’ve already calculated the chance, I’ll code to take a input of 6 numbers and return the chance of winning with those numbers in a user-friendly way.

#write a function that takes an input of 6 numbers and returns the probability of winning with those 6 numbers
#the actual numbers selected doesn't make any difference, so k equals the length of the vector i.e. 6.
#the number of combinations i.e. n_combs is calculated by passing k through the combinations function. n set to 49 as this isn't changing.
#to make it easy for the user to understand the chance, I have printed a sentence which presents the liklihood as 1 in X

one_ticket_probability <- function(a, b, c, d, e, f) {
  vector <- c(a, b, c, d, e, f)
  print(vector)
  k <- length(vector)
  n_combs <- combinations(49, k)
  output <- sprintf("The chance of winning the lottery with these numbers is 1 in %i", n_combs)
  print(output)
}

#here's some example user imput for the numbers
one_ticket_probability(2, 19, 36, 12, 11, 1)
[1]  2 19 36 12 11  1
[1] "The chance of winning the lottery with these numbers is 1 in 13983816"

Next we want to look at the probability of winning the lottery if we play more than one ticket. For instance, what is the probability of winning if we play 40 tickets?

#this function takes the usual inputs i.e. n = 49 and k = 6, and adds another input for the number of tickets bought, t. Since we are looking at whether the first ticket OR the second ticket OR the third ticket etc will win, we can add the probabilities. Since the probabilities of winning any ticket are the same, we can just multiply the probability of one ticket being the winning ticket by the number of tickets you buy. 

multi_ticket_probability <- function(t, n, k){
  p <- t/combinations(n, k)
  cat("The probability of winning if you buy", t, "tickets is", (p*100), "%", "\n")
}

num_tickets <- c(1, 10, 40, 100, 10000, 1000000, 6991908, 13983816)

for(i in num_tickets){
  multi_ticket_probability(i, 49, 6)
}
The probability of winning if you buy 1 tickets is 7.151124e-06 % 
The probability of winning if you buy 10 tickets is 7.151124e-05 % 
The probability of winning if you buy 40 tickets is 0.000286045 % 
The probability of winning if you buy 100 tickets is 0.0007151124 % 
The probability of winning if you buy 10000 tickets is 0.07151124 % 
The probability of winning if you buy 1e+06 tickets is 7.151124 % 
The probability of winning if you buy 6991908 tickets is 50 % 
The probability of winning if you buy 13983816 tickets is 100 % 

The probability of winning by buying 40 tickets is now 1 in ~350,000 or 0.000003 or 0.0003%. It is still an incredibly small chance.

Even if you buy 10,000 tickets, the chance you have a winning ticket is only 0.07%!

You’d need to buy a million tickets to have just a 7% chance of winning!

If you purchase 13983816 different tickets, you have a 100% chance of winning. This is because there are 13983816 combinations you can possibly have, so you must have the correct combination in there. Problem is that you’d have to spend £13983816 on buying the tickets, which is more than you’re likely to win.

Next, what is the probability of having at least five (or four, or three, or two) winning numbers on a single ticket? First, create a function which returns the probability of getting exactly 5 (or 4 or 3) winning numbers on a single ticket.

#the probability of picking 1 correct number is 6/49. 
#the probability of picking 2 correct numbers is 6/49*5/48
#the probability of picking all 6 correct answer is show below.

#(6/49)*(5/48)*(4/47)*(3/46)*(2/45)*(1/44)

#this can be written as P = k!/(n!/(n-k)!)
#P = 1/C where c = number of combinations 
#so, C = n!/((n-k)!k!) as already found above

#but there is another input needed for this question which is the number of matching balls, m
#for m = 2, then the formula is

#(6/49)*(5/48)

#i.e. (k!/(k-m)!)/(n!/(n-m)!)

#write a function to calculate the probability of getting m matching balls

matching_balls <- function(n, k, m){
  answer <- if_else(m!=6, (factorial(k)/factorial(k-m))/(factorial(n)/factorial(n-m)), 1/combinations(n, k))
}

matching_balls(49, 6, 1)

for (i in 1:6) {
  prob <- matching_balls(49, 6, i)
  cat("The probability of getting exactly", i, "matching numbers is", prob, "\n")
}
The probability of getting exactly 1 matching numbers is 0.122449 
The probability of getting exactly 2 matching numbers is 0.0127551 
The probability of getting exactly 3 matching numbers is 0.001085541 
The probability of getting exactly 4 matching numbers is 7.079613e-05 
The probability of getting exactly 5 matching numbers is 3.146494e-06 
The probability of getting exactly 6 matching numbers is 7.151124e-08 

Now the probability of getting AT LEAST 5 matching numbers is probability of getting 5 OR 6 matching numbers. Therefore, addition can be used to sum up the inidivical probabilities.

#write a function that calculated the probability of getting at least x number of matching balls. This uses the matching_balls function and then uses a for loop to iterate through x:6 to add up the probabilities. 

#the function takes three inputs - n = total number of balls, k = number of balls drawn, mx = number of matching numbers

at_least_x_matches <- function(n, k, mx){
  chance <- 0
  for(i in mx:6){
    chance<-chance+matching_balls(n, k, i)
  }
  cat("The probability of getting AT LEAST", mx, "matching numbers is", chance, "\n")
}

at_least_x_matches(49, 6, 1)
The probability of getting AT LEAST 1 matching numbers is 0.1363636 

Next create a list containing all the winning number combinations.

#extract the winning numbers as a vector. Each vector contains all the winning numbers by position. 

number_1 <- data$`NUMBER DRAWN 1`
number_2 <- data$`NUMBER DRAWN 2`
number_3 <- data$`NUMBER DRAWN 3`
number_4 <- data$`NUMBER DRAWN 4`
number_5 <- data$`NUMBER DRAWN 5`
number_6 <- data$`NUMBER DRAWN 6`

#create a list of all the winning numbers by position.

numbers_list <- list(number_1, number_2, number_3, number_4, number_5, number_6) 

#create vectors of each of the winning number combinations. 

winning_numbers <- pmap(numbers_list, function(a, b, c, d, e, f) {c(a,b,c,d,e,f)})

winning_numbers[[1]]
[1]  3 11 12 14 41 43

Next write a function that takes any 6 numbers and returns the number of times those numbers have been the winning numbers in the past. Then tell the user how likely it is that those numbers will win the draw next time.

#write function that takes 6 numbers as an input and returns the number of times those numbers have been the winning numbers in the past. 

nw <- length(data$`NUMBER DRAWN 1`) #to find out how many interations are required in the for loop in the function.

check_historical_occurance <- function(a, b, c, d, e, f) {
  user_numbers <- c(a, b, c, d, e, f)
  user_sorted <- sort(user_numbers)
  won_before <- 0
  for(i in 1:nw){
    won_before <- if_else(setequal(user_sorted, winning_numbers[[i]])==TRUE, won_before+1, won_before)
  }
  cat("These numbers have been the winning numbers", won_before, "time(s) before", "\n", "The liklihood of winning with these numbers in the next draw is approximately 1 in 14 million")
}

#run the function with some random numbers
check_historical_occurance(2, 3, 4, 5, 6, 7)
These numbers have been the winning numbers 0 time(s) before 
 The liklihood of winning with these numbers in the next draw is approximately 1 in 14 million
#run the function again with some numbers we know have previously won the draw
check_historical_occurance(3, 11, 12, 14, 41, 43)
These numbers have been the winning numbers 1 time(s) before 
 The liklihood of winning with these numbers in the next draw is approximately 1 in 14 million
LS0tDQp0aXRsZTogIkd1aWRlZCBQcm9qZWN0OiBNb2JpbGUgQXBwIGZvciBMb3R0ZXJ5IEFkZGljdGlvbiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCk1hbnkgcGVvcGxlIHN0YXJ0IHBsYXlpbmcgdGhlIGxvdHRlcnkgZm9yIGZ1biwgYnV0IGZvciBzb21lLCB0aGlzIGFjdGl2aXR5IGNhbiBlc2NhbGF0ZSBpbnRvIGhhYml0IGFuZCBhZGRpY3Rpb24uIFNpbWlsYXIgdG8gb3RoZXIgY29tcHVsc2l2ZSBnYW1ibGVycywgbG90dGVyeSBhZGRpY3RzIG1heSBzdGFydCB1c2luZyBzYXZpbmdzIGFuZCBsb2FucyB0byBidXkgdGlja2V0cy4gDQpBIG1vYmlsZSBhcHAgaXMgcmVxdWlyZWQgd2hpY2ggaXMgaW50ZW5kZWQgdG8gZ3VpZGUgbG90dGVyeSBhZGRpY3RzIHRocm91Z2ggZXhlcmNpc2VzIHRoYXQgd2lsbCBsZXQgdGhlbSBiZXR0ZXIgZXN0aW1hdGUgdGhlaXIgY2hhbmNlcyBvZiB3aW5uaW5nLiBUaGUgaG9wZSBpcyB0aGF0IHRoaXMgYXBwIHdpbGwgaGVscCB0aGVtIHJlYWxpemUgdGhhdCBidXlpbmcgdG9vIG1hbnkgdGlja2V0cyB3aWxsIGRvIGxpdHRsZSB0byBpbXByb3ZlIHRoZWlyIGNoYW5jZXMgb2Ygd2lubmluZy4NClRoaXMgY29kZSB3aWxsIGZvcm0gdGhlIGxvZ2ljIGZvciB0aGUgYXBwLg0KYGBge3J9DQojTG9hZCBsaWJyYXJpZXMgYW5kIHRoZSBkYXRhDQoNCmxpYnJhcnkgKHJlYWRyKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkoZ2dtYXApDQpsaWJyYXJ5KG1hcHByb2opDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ3JpZCkNCg0Kc2V0d2QoIkM6L1VzZXJzL0FuYS9EZXNrdG9wL0RhdGEgQW5hbHl0aWNzL0NTViBGaWxlcyIpDQoNCiNpbXBvcnQgZGF0YQ0KZGF0YSA8LSByZWFkX2NzdigiNjQ5LmNzdiIpIA0KDQojdmlldyBiZWdpbm5pbmcgYW5kIGVuZCBvZiBkYXRhc2V0DQpoZWFkKGRhdGEsIDMpDQp0YWlsKGRhdGEsIDMpDQoNCiNyZXR1cm4gbnVtYmVyIG9mIHJvd3MgaW4gZGF0YXNldA0KcHJpbnQobnJvdyhkYXRhKSkNCg0KI3JldHVybiBudW1iZXIgb2YgY29sdW1ucyBpbiBkYXRhc2V0DQpwcmludChuY29sKGRhdGEpKQ0KYGBgDQpgYGB7cn0NCg0KI2NvdW50IGhvdyBtYW55IHVuaXF1ZSBlbnRyaWVzIHRoZXJlIGFyZSBmb3IgZWFjaCBjb2x1bW4NCmNvbG5hbWVzKGRhdGEpDQoNCmZvcihpIGluIGNvbG5hbWVzKGRhdGEpKXsNCiAgcHJpbnQoYXMuaW50ZWdlcihjb3VudCh1bmlxdWUoZGF0YVssaV0pKSkpDQp9DQoNCnNvcnQodW5pcXVlKGRhdGEkYE5VTUJFUiBEUkFXTiAxYCkpDQpgYGANCk5leHQsIGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgb2Ygd2lubmluZyB0aGUgZHJhdyB3aXRoIGEgc2luZ2xlIHRpY2tldC4NCmBgYHtyfQ0KI1RoZSBvcmRlciBvZiB0aGUgcGVybXV0YXRpb25zIGRvZXNuJ3QgbWF0dGVyIGluIHRoaXMgaW5zdGFuY2UsIHNvIHdlIGFyZSBsb29raW5nIGZvciB0aGUgdG90YWwgbnVtYmVyIG9mIGNvbWJpbmF0aW9ucy4gVGhpcyBpcyBnaXZlbiBieSBDID0gbiEvKChuLWspIWshKSANCiNXaGVyZToNCiNuID0gdG90YWwgbnVtYmVyIG9mIG51bWJlcnMgaS5lLiA0OSwgayA9IG51bWJlciBhbGxvd2VkIGkuZS4gNi4NCg0KI2ZpcnN0LCB3cml0ZSBhIGZ1bmN0aW9uIHdoaWNoIHdpbGwgY2FsY3VsYXRlIHRoZSBmYWN0b3JpYWwgb2YgYW55IG51bWJlci4NCg0KZmFjdG9yaWFsIDwtIGZ1bmN0aW9uKG4pew0KICBhbnN3ZXI8LTENCiAgZm9yKGkgaW4gMTpuKSB7DQogICAgYW5zd2VyPC1hbnN3ZXIqaQ0KICB9DQogIHJldHVybihhbnN3ZXIpDQp9DQoNCiNub3cgd3JpdGUgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBwZXJtdXRhdGlvbnMgd2hlbiBvbmx5IGEgY2VydGFpbiBudW1iZXIgb2YgYmFsbHMgYXJlIGRyYXduLg0KDQpwZXJtdXRhdGlvbiA8LSBmdW5jdGlvbihuLCBrKSB7DQogIGZhY3RvcmlhbChuKS9mYWN0b3JpYWwobi1rKQ0KfQ0KDQojbm93IHdyaXRlIGEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgY29tYmluYXRpb25zIHdoZW4gb25seSBhIGNlcnRhaW4gbnVtYmVyIG9mIGJhbGxzIGFyZSBkcmF3bi4NCg0KY29tYmluYXRpb25zIDwtIGZ1bmN0aW9uKG4sIGspIHsNCiAgcGVybXV0YXRpb24obiwgaykvZmFjdG9yaWFsKGspDQp9DQoNCiN0aGUgcHJvYmFiaWxpdHkgb2Ygd2lubmluZyB0aGUgZHJhdyB3aXRoIGEgc2luZ2xlIHRpY2tldCBpcyB0aGVyZWZvcmUgMSBkaXZpZGVkIGJ5IHRoZSBudW1iZXIgb2YgY29tYmluYXRpb25zLg0KDQpjb21iaW5hdGlvbnMoNDksNikNCnBfd2luXzF0IDwtIDEvY29tYmluYXRpb25zKDQ5LDYpDQpwX3dpbl8xdA0KDQpgYGANClRoZSBwcm9iYWJpbGl0eSBvZiB3aW5uaW5nIHRoZSBsb3R0ZXJ5IHdpdGggb25lIHRpY2tldCBpcyAxIGluIH4xNG1pbGxpb24gb3IgN2UtOCBvciAwLjAwMDAwMDA3IG9yIDAuMDAwMDA3JQ0KDQpUaGUgYXBwIHdhbnRzIHRoZSB1c2VyIHRvIGJlIGFibGUgdG8gZW50ZXIgNiBudW1iZXJzIGkuZS4gdGhlaXIgY2hvc2VuIG51bWJlcnMsIGFuZCBmb3IgdGhlIGFwcCB0byBkaXNwbGF5IHRoZSBjaGFuY2Ugb2YgdGhlbSB3aW5uaW5nIHdpdGggdGhvc2UgbnVtYmVycy4gQWx0aG91Z2ggSSd2ZSBhbHJlYWR5IGNhbGN1bGF0ZWQgdGhlIGNoYW5jZSwgSSdsbCBjb2RlIHRvIHRha2UgYSBpbnB1dCBvZiA2IG51bWJlcnMgYW5kIHJldHVybiB0aGUgY2hhbmNlIG9mIHdpbm5pbmcgd2l0aCB0aG9zZSBudW1iZXJzIGluIGEgdXNlci1mcmllbmRseSB3YXkuDQoNCmBgYHtyfQ0KI3dyaXRlIGEgZnVuY3Rpb24gdGhhdCB0YWtlcyBhbiBpbnB1dCBvZiA2IG51bWJlcnMgYW5kIHJldHVybnMgdGhlIHByb2JhYmlsaXR5IG9mIHdpbm5pbmcgd2l0aCB0aG9zZSA2IG51bWJlcnMNCiN0aGUgYWN0dWFsIG51bWJlcnMgc2VsZWN0ZWQgZG9lc24ndCBtYWtlIGFueSBkaWZmZXJlbmNlLCBzbyBrIGVxdWFscyB0aGUgbGVuZ3RoIG9mIHRoZSB2ZWN0b3IgaS5lLiA2Lg0KI3RoZSBudW1iZXIgb2YgY29tYmluYXRpb25zIGkuZS4gbl9jb21icyBpcyBjYWxjdWxhdGVkIGJ5IHBhc3NpbmcgayB0aHJvdWdoIHRoZSBjb21iaW5hdGlvbnMgZnVuY3Rpb24uIG4gc2V0IHRvIDQ5IGFzIHRoaXMgaXNuJ3QgY2hhbmdpbmcuDQojdG8gbWFrZSBpdCBlYXN5IGZvciB0aGUgdXNlciB0byB1bmRlcnN0YW5kIHRoZSBjaGFuY2UsIEkgaGF2ZSBwcmludGVkIGEgc2VudGVuY2Ugd2hpY2ggcHJlc2VudHMgdGhlIGxpa2xpaG9vZCBhcyAxIGluIFgNCg0Kb25lX3RpY2tldF9wcm9iYWJpbGl0eSA8LSBmdW5jdGlvbihhLCBiLCBjLCBkLCBlLCBmKSB7DQogIHZlY3RvciA8LSBjKGEsIGIsIGMsIGQsIGUsIGYpDQogIHByaW50KHZlY3RvcikNCiAgayA8LSBsZW5ndGgodmVjdG9yKQ0KICBuX2NvbWJzIDwtIGNvbWJpbmF0aW9ucyg0OSwgaykNCiAgb3V0cHV0IDwtIHNwcmludGYoIlRoZSBjaGFuY2Ugb2Ygd2lubmluZyB0aGUgbG90dGVyeSB3aXRoIHRoZXNlIG51bWJlcnMgaXMgMSBpbiAlaSIsIG5fY29tYnMpDQogIHByaW50KG91dHB1dCkNCn0NCg0KI2hlcmUncyBzb21lIGV4YW1wbGUgdXNlciBpbXB1dCBmb3IgdGhlIG51bWJlcnMNCm9uZV90aWNrZXRfcHJvYmFiaWxpdHkoMiwgMTksIDM2LCAxMiwgMTEsIDEpDQoNCmBgYA0KTmV4dCB3ZSB3YW50IHRvIGxvb2sgYXQgdGhlIHByb2JhYmlsaXR5IG9mIHdpbm5pbmcgdGhlIGxvdHRlcnkgaWYgd2UgcGxheSBtb3JlIHRoYW4gb25lIHRpY2tldC4gRm9yIGluc3RhbmNlLCB3aGF0IGlzIHRoZSBwcm9iYWJpbGl0eSBvZiB3aW5uaW5nIGlmIHdlIHBsYXkgNDAgdGlja2V0cz8NCmBgYHtyfQ0KI3RoaXMgZnVuY3Rpb24gdGFrZXMgdGhlIHVzdWFsIGlucHV0cyBpLmUuIG4gPSA0OSBhbmQgayA9IDYsIGFuZCBhZGRzIGFub3RoZXIgaW5wdXQgZm9yIHRoZSBudW1iZXIgb2YgdGlja2V0cyBib3VnaHQsIHQuIFNpbmNlIHdlIGFyZSBsb29raW5nIGF0IHdoZXRoZXIgdGhlIGZpcnN0IHRpY2tldCBPUiB0aGUgc2Vjb25kIHRpY2tldCBPUiB0aGUgdGhpcmQgdGlja2V0IGV0YyB3aWxsIHdpbiwgd2UgY2FuIGFkZCB0aGUgcHJvYmFiaWxpdGllcy4gU2luY2UgdGhlIHByb2JhYmlsaXRpZXMgb2Ygd2lubmluZyBhbnkgdGlja2V0IGFyZSB0aGUgc2FtZSwgd2UgY2FuIGp1c3QgbXVsdGlwbHkgdGhlIHByb2JhYmlsaXR5IG9mIG9uZSB0aWNrZXQgYmVpbmcgdGhlIHdpbm5pbmcgdGlja2V0IGJ5IHRoZSBudW1iZXIgb2YgdGlja2V0cyB5b3UgYnV5LiANCg0KbXVsdGlfdGlja2V0X3Byb2JhYmlsaXR5IDwtIGZ1bmN0aW9uKHQsIG4sIGspew0KICBwIDwtIHQvY29tYmluYXRpb25zKG4sIGspDQogIGNhdCgiVGhlIHByb2JhYmlsaXR5IG9mIHdpbm5pbmcgaWYgeW91IGJ1eSIsIHQsICJ0aWNrZXRzIGlzIiwgKHAqMTAwKSwgIiUiLCAiXG4iKQ0KfQ0KDQpudW1fdGlja2V0cyA8LSBjKDEsIDEwLCA0MCwgMTAwLCAxMDAwMCwgMTAwMDAwMCwgNjk5MTkwOCwgMTM5ODM4MTYpDQoNCmZvcihpIGluIG51bV90aWNrZXRzKXsNCiAgbXVsdGlfdGlja2V0X3Byb2JhYmlsaXR5KGksIDQ5LCA2KQ0KfQ0KDQpgYGANClRoZSBwcm9iYWJpbGl0eSBvZiB3aW5uaW5nIGJ5IGJ1eWluZyA0MCB0aWNrZXRzIGlzIG5vdyAxIGluIH4zNTAsMDAwIG9yIDAuMDAwMDAzIG9yIDAuMDAwMyUuIEl0IGlzIHN0aWxsIGFuIGluY3JlZGlibHkgc21hbGwgY2hhbmNlLg0KDQpFdmVuIGlmIHlvdSBidXkgMTAsMDAwIHRpY2tldHMsIHRoZSBjaGFuY2UgeW91IGhhdmUgYSB3aW5uaW5nIHRpY2tldCBpcyBvbmx5IDAuMDclIQ0KDQpZb3UnZCBuZWVkIHRvIGJ1eSBhIG1pbGxpb24gdGlja2V0cyB0byBoYXZlIGp1c3QgYSA3JSBjaGFuY2Ugb2Ygd2lubmluZyENCg0KSWYgeW91IHB1cmNoYXNlIDEzOTgzODE2IGRpZmZlcmVudCB0aWNrZXRzLCB5b3UgaGF2ZSBhIDEwMCUgY2hhbmNlIG9mIHdpbm5pbmcuIFRoaXMgaXMgYmVjYXVzZSB0aGVyZSBhcmUgMTM5ODM4MTYgY29tYmluYXRpb25zIHlvdSBjYW4gcG9zc2libHkgaGF2ZSwgc28geW91IG11c3QgaGF2ZSB0aGUgY29ycmVjdCBjb21iaW5hdGlvbiBpbiB0aGVyZS4gUHJvYmxlbSBpcyB0aGF0IHlvdSdkIGhhdmUgdG8gc3BlbmQgwqMxMzk4MzgxNiBvbiBidXlpbmcgdGhlIHRpY2tldHMsIHdoaWNoIGlzIG1vcmUgdGhhbiB5b3UncmUgbGlrZWx5IHRvIHdpbi4gDQoNCk5leHQsIHdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIGhhdmluZyBhdCBsZWFzdCBmaXZlIChvciBmb3VyLCBvciB0aHJlZSwgb3IgdHdvKSB3aW5uaW5nIG51bWJlcnMgb24gYSBzaW5nbGUgdGlja2V0Pw0KRmlyc3QsIGNyZWF0ZSBhIGZ1bmN0aW9uIHdoaWNoIHJldHVybnMgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgZXhhY3RseSA1IChvciA0IG9yIDMpIHdpbm5pbmcgbnVtYmVycyBvbiBhIHNpbmdsZSB0aWNrZXQuDQoNCmBgYHtyfQ0KI3RoZSBwcm9iYWJpbGl0eSBvZiBwaWNraW5nIDEgY29ycmVjdCBudW1iZXIgaXMgNi80OS4gDQojdGhlIHByb2JhYmlsaXR5IG9mIHBpY2tpbmcgMiBjb3JyZWN0IG51bWJlcnMgaXMgNi80OSo1LzQ4DQojdGhlIHByb2JhYmlsaXR5IG9mIHBpY2tpbmcgYWxsIDYgY29ycmVjdCBhbnN3ZXIgaXMgc2hvdyBiZWxvdy4NCg0KIyg2LzQ5KSooNS80OCkqKDQvNDcpKigzLzQ2KSooMi80NSkqKDEvNDQpDQoNCiN0aGlzIGNhbiBiZSB3cml0dGVuIGFzIFAgPSBrIS8obiEvKG4taykhKQ0KI1AgPSAxL0Mgd2hlcmUgYyA9IG51bWJlciBvZiBjb21iaW5hdGlvbnMgDQojc28sIEMgPSBuIS8oKG4taykhayEpIGFzIGFscmVhZHkgZm91bmQgYWJvdmUNCg0KI2J1dCB0aGVyZSBpcyBhbm90aGVyIGlucHV0IG5lZWRlZCBmb3IgdGhpcyBxdWVzdGlvbiB3aGljaCBpcyB0aGUgbnVtYmVyIG9mIG1hdGNoaW5nIGJhbGxzLCBtDQojZm9yIG0gPSAyLCB0aGVuIHRoZSBmb3JtdWxhIGlzDQoNCiMoNi80OSkqKDUvNDgpDQoNCiNpLmUuIChrIS8oay1tKSEpLyhuIS8obi1tKSEpDQoNCiN3cml0ZSBhIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgb2YgZ2V0dGluZyBtIG1hdGNoaW5nIGJhbGxzDQoNCm1hdGNoaW5nX2JhbGxzIDwtIGZ1bmN0aW9uKG4sIGssIG0pew0KICBhbnN3ZXIgPC0gaWZfZWxzZShtIT02LCAoZmFjdG9yaWFsKGspL2ZhY3RvcmlhbChrLW0pKS8oZmFjdG9yaWFsKG4pL2ZhY3RvcmlhbChuLW0pKSwgMS9jb21iaW5hdGlvbnMobiwgaykpDQp9DQoNCm1hdGNoaW5nX2JhbGxzKDQ5LCA2LCAxKQ0KDQpmb3IgKGkgaW4gMTo2KSB7DQogIHByb2IgPC0gbWF0Y2hpbmdfYmFsbHMoNDksIDYsIGkpDQogIGNhdCgiVGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgZXhhY3RseSIsIGksICJtYXRjaGluZyBudW1iZXJzIGlzIiwgcHJvYiwgIlxuIikNCn0NCmBgYA0KTm93IHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIEFUIExFQVNUIDUgbWF0Y2hpbmcgbnVtYmVycyBpcyBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIDUgT1IgNiBtYXRjaGluZyBudW1iZXJzLiBUaGVyZWZvcmUsIGFkZGl0aW9uIGNhbiBiZSB1c2VkIHRvIHN1bSB1cCB0aGUgaW5pZGl2aWNhbCBwcm9iYWJpbGl0aWVzLg0KYGBge3J9DQojd3JpdGUgYSBmdW5jdGlvbiB0aGF0IGNhbGN1bGF0ZWQgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgYXQgbGVhc3QgeCBudW1iZXIgb2YgbWF0Y2hpbmcgYmFsbHMuIFRoaXMgdXNlcyB0aGUgbWF0Y2hpbmdfYmFsbHMgZnVuY3Rpb24gYW5kIHRoZW4gdXNlcyBhIGZvciBsb29wIHRvIGl0ZXJhdGUgdGhyb3VnaCB4OjYgdG8gYWRkIHVwIHRoZSBwcm9iYWJpbGl0aWVzLiANCg0KI3RoZSBmdW5jdGlvbiB0YWtlcyB0aHJlZSBpbnB1dHMgLSBuID0gdG90YWwgbnVtYmVyIG9mIGJhbGxzLCBrID0gbnVtYmVyIG9mIGJhbGxzIGRyYXduLCBteCA9IG51bWJlciBvZiBtYXRjaGluZyBudW1iZXJzDQoNCmF0X2xlYXN0X3hfbWF0Y2hlcyA8LSBmdW5jdGlvbihuLCBrLCBteCl7DQogIGNoYW5jZSA8LSAwDQogIGZvcihpIGluIG14OjYpew0KICAgIGNoYW5jZTwtY2hhbmNlK21hdGNoaW5nX2JhbGxzKG4sIGssIGkpDQogIH0NCiAgY2F0KCJUaGUgcHJvYmFiaWxpdHkgb2YgZ2V0dGluZyBBVCBMRUFTVCIsIG14LCAibWF0Y2hpbmcgbnVtYmVycyBpcyIsIGNoYW5jZSwgIlxuIikNCn0NCg0KYXRfbGVhc3RfeF9tYXRjaGVzKDQ5LCA2LCAxKQ0KDQpgYGANCk5leHQgY3JlYXRlIGEgbGlzdCBjb250YWluaW5nIGFsbCB0aGUgd2lubmluZyBudW1iZXIgY29tYmluYXRpb25zLg0KDQpgYGB7cn0NCiNleHRyYWN0IHRoZSB3aW5uaW5nIG51bWJlcnMgYXMgYSB2ZWN0b3IuIEVhY2ggdmVjdG9yIGNvbnRhaW5zIGFsbCB0aGUgd2lubmluZyBudW1iZXJzIGJ5IHBvc2l0aW9uLiANCg0KbnVtYmVyXzEgPC0gZGF0YSRgTlVNQkVSIERSQVdOIDFgDQpudW1iZXJfMiA8LSBkYXRhJGBOVU1CRVIgRFJBV04gMmANCm51bWJlcl8zIDwtIGRhdGEkYE5VTUJFUiBEUkFXTiAzYA0KbnVtYmVyXzQgPC0gZGF0YSRgTlVNQkVSIERSQVdOIDRgDQpudW1iZXJfNSA8LSBkYXRhJGBOVU1CRVIgRFJBV04gNWANCm51bWJlcl82IDwtIGRhdGEkYE5VTUJFUiBEUkFXTiA2YA0KDQojY3JlYXRlIGEgbGlzdCBvZiBhbGwgdGhlIHdpbm5pbmcgbnVtYmVycyBieSBwb3NpdGlvbi4NCg0KbnVtYmVyc19saXN0IDwtIGxpc3QobnVtYmVyXzEsIG51bWJlcl8yLCBudW1iZXJfMywgbnVtYmVyXzQsIG51bWJlcl81LCBudW1iZXJfNikgDQoNCiNjcmVhdGUgdmVjdG9ycyBvZiBlYWNoIG9mIHRoZSB3aW5uaW5nIG51bWJlciBjb21iaW5hdGlvbnMuIA0KDQp3aW5uaW5nX251bWJlcnMgPC0gcG1hcChudW1iZXJzX2xpc3QsIGZ1bmN0aW9uKGEsIGIsIGMsIGQsIGUsIGYpIHtjKGEsYixjLGQsZSxmKX0pDQoNCndpbm5pbmdfbnVtYmVyc1tbMV1dDQoNCmBgYA0KTmV4dCB3cml0ZSBhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYW55IDYgbnVtYmVycyBhbmQgcmV0dXJucyB0aGUgbnVtYmVyIG9mIHRpbWVzIHRob3NlIG51bWJlcnMgaGF2ZSBiZWVuIHRoZSB3aW5uaW5nIG51bWJlcnMgaW4gdGhlIHBhc3QuIFRoZW4gdGVsbCB0aGUgdXNlciBob3cgbGlrZWx5IGl0IGlzIHRoYXQgdGhvc2UgbnVtYmVycyB3aWxsIHdpbiB0aGUgZHJhdyBuZXh0IHRpbWUuIA0KDQpgYGB7cn0NCiN3cml0ZSBmdW5jdGlvbiB0aGF0IHRha2VzIDYgbnVtYmVycyBhcyBhbiBpbnB1dCBhbmQgcmV0dXJucyB0aGUgbnVtYmVyIG9mIHRpbWVzIHRob3NlIG51bWJlcnMgaGF2ZSBiZWVuIHRoZSB3aW5uaW5nIG51bWJlcnMgaW4gdGhlIHBhc3QuIA0KDQpudyA8LSBsZW5ndGgoZGF0YSRgTlVNQkVSIERSQVdOIDFgKSAjdG8gZmluZCBvdXQgaG93IG1hbnkgaW50ZXJhdGlvbnMgYXJlIHJlcXVpcmVkIGluIHRoZSBmb3IgbG9vcCBpbiB0aGUgZnVuY3Rpb24uDQoNCmNoZWNrX2hpc3RvcmljYWxfb2NjdXJhbmNlIDwtIGZ1bmN0aW9uKGEsIGIsIGMsIGQsIGUsIGYpIHsNCiAgdXNlcl9udW1iZXJzIDwtIGMoYSwgYiwgYywgZCwgZSwgZikNCiAgdXNlcl9zb3J0ZWQgPC0gc29ydCh1c2VyX251bWJlcnMpDQogIHdvbl9iZWZvcmUgPC0gMA0KICBmb3IoaSBpbiAxOm53KXsNCiAgICB3b25fYmVmb3JlIDwtIGlmX2Vsc2Uoc2V0ZXF1YWwodXNlcl9zb3J0ZWQsIHdpbm5pbmdfbnVtYmVyc1tbaV1dKT09VFJVRSwgd29uX2JlZm9yZSsxLCB3b25fYmVmb3JlKQ0KICB9DQogIGNhdCgiVGhlc2UgbnVtYmVycyBoYXZlIGJlZW4gdGhlIHdpbm5pbmcgbnVtYmVycyIsIHdvbl9iZWZvcmUsICJ0aW1lKHMpIGJlZm9yZSIsICJcbiIsICJUaGUgbGlrbGlob29kIG9mIHdpbm5pbmcgd2l0aCB0aGVzZSBudW1iZXJzIGluIHRoZSBuZXh0IGRyYXcgaXMgYXBwcm94aW1hdGVseSAxIGluIDE0IG1pbGxpb24iKQ0KfQ0KDQojcnVuIHRoZSBmdW5jdGlvbiB3aXRoIHNvbWUgcmFuZG9tIG51bWJlcnMNCmNoZWNrX2hpc3RvcmljYWxfb2NjdXJhbmNlKDIsIDMsIDQsIDUsIDYsIDcpDQoNCiNydW4gdGhlIGZ1bmN0aW9uIGFnYWluIHdpdGggc29tZSBudW1iZXJzIHdlIGtub3cgaGF2ZSBwcmV2aW91c2x5IHdvbiB0aGUgZHJhdw0KY2hlY2tfaGlzdG9yaWNhbF9vY2N1cmFuY2UoMywgMTEsIDEyLCAxNCwgNDEsIDQzKQ0KYGBgDQoNCg==