Required packages

library(readr)
library(tidyr)
library(dplyr)
library(ggplot2)
library(caret)
library(editrules)
library(forecast)
library(stringr)
library(outliers)
library(lubridate)

Executive Summary

  1. To fulfill all the requirements required in datasets, I chose the datasets of crimes in boston and the crime codes for the following offenses from the source https://www.kaggle.com/AnalyzeBoston/crimes-in-boston.

  2. After that, I merged the datasets by performing leftjoin operation on the common attribute.

  3. Then, I inspected all the variables required for the analysis and segregated the non-necessary or redundent once.

  4. Longitude and Latitude of the crimes were deriving a meaningful variable “LOCATION”, which was derived using the ‘Haversine’ formula from the source, https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula/23095329#23095329.

  5. After that,I used is.na() function to find all the NA values and then scanned all the missing accordingly.

  6. Next, we looked for the outliers present by deducing boxplot and then removed them accordingly.

  7. Then, by taking the “LOCATION” variable and deducing a histogram to check the normality trend and then transformed it using Box-Cox transformation to attain normality and min-max normalization.

Data

I have taken the datasets of crimes in boston by the source https://www.kaggle.com/AnalyzeBoston/crimes-in-boston. It states all the crimes occured in boston with all the linked descriptions and time recorded by the Boston Police. Then renamed it as bostcrime.csv & crimecodes.csv accordingly.

Following attributes hold in boscrime.csv - DATASET A: 1) INCIDENT_NUMBER 2) OFFENSE_CODE 3) OFFENSE_CODE_GROUP 4) OFFENSE_DESCRIPTION 5) DISTRICT 6) REPORTING_AREA 7) SHOOTING 8) OCCURRED_ON_DATE 9) 10 YEAR 10) MONTH 11) DAY_OF_WEEK 12) HOUR 13) UCR_PART 14) STREET 15) Lat 16) Long

Attributes for crimecodes.csv- DATASET B: 1) CODE 2) NAME

## DATASET 1:

bostcrime <- read.csv("/Users/preritmiglani/Desktop/bostcrime.csv",stringsAsFactors = FALSE)

## DATASET 2:

crimecodes<- read.csv("/Users/preritmiglani/Desktop/crime codes.csv",stringsAsFactors = FALSE)

head(bostcrime)

## Head for crimecodes.csv

head(crimecodes)

## To check the number of attributes and records for the datasets

dim(bostcrime)
[1] 319073     17
dim(crimecodes)
[1] 576   2
## Now,I changed the column name of bostcrime from 'offense_code' to 'code' as in crimecodes dataset to merge

colnames(bostcrime)[colnames(bostcrime) == 'OFFENSE_CODE'] <- 'CODE'

## Now merging both the datasets

crime_comb <- bostcrime %>% left_join(crimecodes, by=c('CODE'))
dim(crime_comb)
[1] 577880     18
head(crime_comb)

## As,we could see too many records in the merged dataset,so we will get the distinct values

crimecodes <- crimecodes %>% distinct(CODE, .keep_all= TRUE)

## Again,we would apply merge function and check

crime_comb <- bostcrime %>% left_join(crimecodes, by=c('CODE'))
dim(crime_comb)
[1] 319073     18
## checking for the head
head(crime_comb)
NA

Understand

Now,as while importing, we have taken StringsasFactors=False, so now we will check the structure of the merged dataset and would observe that ‘UCR_PART’ relates to the severity of the crime, so this a categorical variable and we label it accordingly. Also,we would convert ‘DAY_OF_WEEK’ to factors. Also ,we could observe that the ‘OCCURRED_ON_DATA’ variable contains the year,day,month and time which is already given, so we would remove the attribute as it is redundant and also ‘DISTRICT’, ‘REPORTING AREA’,‘SHOOTING’ were removed as they have no role in the analysis.


## Checking for the structure of merged datset

str(crime_comb)
'data.frame':   319073 obs. of  18 variables:
 $ INCIDENT_NUMBER    : chr  "I182070945" "I182070943" "I182070941" "I182070940" ...
 $ CODE               : int  619 1402 3410 3114 3114 3820 724 3301 301 3301 ...
 $ OFFENSE_CODE_GROUP : chr  "Larceny" "Vandalism" "Towed" "Investigate Property" ...
 $ OFFENSE_DESCRIPTION: chr  "LARCENY ALL OTHERS" "VANDALISM" "TOWED MOTOR VEHICLE" "INVESTIGATE PROPERTY" ...
 $ DISTRICT           : chr  "D14" "C11" "D4" "D4" ...
 $ REPORTING_AREA     : int  808 347 151 272 421 398 330 584 177 364 ...
 $ SHOOTING           : chr  "" "" "" "" ...
 $ OCCURRED_ON_DATE   : chr  "2018-09-02 13:00:00" "2018-08-21 00:00:00" "2018-09-03 19:27:00" "2018-09-03 21:16:00" ...
 $ YEAR               : int  2018 2018 2018 2018 2018 2018 2018 2018 2018 2018 ...
 $ MONTH              : int  9 8 9 9 9 9 9 9 9 9 ...
 $ DAY_OF_WEEK        : chr  "Sunday" "Tuesday" "Monday" "Monday" ...
 $ HOUR               : int  13 0 19 21 21 21 21 20 20 20 ...
 $ UCR_PART           : chr  "Part One" "Part Two" "Part Three" "Part Three" ...
 $ STREET             : chr  "LINCOLN ST" "HECLA ST" "CAZENOVE ST" "NEWCOMB ST" ...
 $ Lat                : num  42.4 42.3 42.3 42.3 42.3 ...
 $ Long               : num  -71.1 -71.1 -71.1 -71.1 -71.1 ...
 $ Location           : chr  "(42.35779134, -71.13937053)" "(42.30682138, -71.06030035)" "(42.34658879, -71.07242943)" "(42.33418175, -71.07866441)" ...
 $ NAME               : chr  "LARCENY ALL OTHERS" "VANDALISM" "TOWED MOTOR VEHICLE" "INVESTIGATE PROPERTY" ...
## Removing the attributes which are not necessary

crime_comb<-crime_comb %>% select(-c("SHOOTING","OCCURRED_ON_DATE","DISTRICT","REPORTING_AREA"))

## Converting to factors and giving labels according to the severity of crime

crime_comb$UCR_PART<-factor(crime_comb$UCR_PART,levels = c("Part Three", "Part Two", "Part One"), 
                            labels = c("Severe", "Mild", "Low"), 
                            ordered = TRUE )

## Converting to factor

crime_comb$DAY_OF_WEEK <- factor(crime_comb$DAY_OF_WEEK)

## Checking the structure again

str(crime_comb)
'data.frame':   319073 obs. of  14 variables:
 $ INCIDENT_NUMBER    : chr  "I182070945" "I182070943" "I182070941" "I182070940" ...
 $ CODE               : int  619 1402 3410 3114 3114 3820 724 3301 301 3301 ...
 $ OFFENSE_CODE_GROUP : chr  "Larceny" "Vandalism" "Towed" "Investigate Property" ...
 $ OFFENSE_DESCRIPTION: chr  "LARCENY ALL OTHERS" "VANDALISM" "TOWED MOTOR VEHICLE" "INVESTIGATE PROPERTY" ...
 $ YEAR               : int  2018 2018 2018 2018 2018 2018 2018 2018 2018 2018 ...
 $ MONTH              : int  9 8 9 9 9 9 9 9 9 9 ...
 $ DAY_OF_WEEK        : Factor w/ 7 levels "Friday","Monday",..: 4 6 2 2 2 2 2 2 2 2 ...
 $ HOUR               : int  13 0 19 21 21 21 21 20 20 20 ...
 $ UCR_PART           : Ord.factor w/ 3 levels "Severe"<"Mild"<..: 3 2 1 1 1 1 3 1 3 1 ...
 $ STREET             : chr  "LINCOLN ST" "HECLA ST" "CAZENOVE ST" "NEWCOMB ST" ...
 $ Lat                : num  42.4 42.3 42.3 42.3 42.3 ...
 $ Long               : num  -71.1 -71.1 -71.1 -71.1 -71.1 ...
 $ Location           : chr  "(42.35779134, -71.13937053)" "(42.30682138, -71.06030035)" "(42.34658879, -71.07242943)" "(42.33418175, -71.07866441)" ...
 $ NAME               : chr  "LARCENY ALL OTHERS" "VANDALISM" "TOWED MOTOR VEHICLE" "INVESTIGATE PROPERTY" ...

Tidy & Manipulate Data I

We can say that the dataset is in tidy condition as each variable in the dataset is placed in its own location and each row has some observation. Also (Longitude,Latitude) gives us location of the crime which is infered furthur and also we could say that dates are placed in separate column with time details , so we removed the combined attribute OCCURRED_ON_DATE from the dataset.

Tidy & Manipulate Data II

First , we see that LAT and LONG gives us the location of the crime. So, we will add a new column Location in our dataset for the analysis.To find the centroid of the Long and Lat , we will do so my deducing the mean of both the variables.


## As, the values of Lat and long are not matched , so first we will do pattern matching of the columns and calculate the right mean by using clustered centre

## Take the mean of Lat by using the clustered centre

centroid_lat <- crime_comb %>% filter(str_detect(Lat, regex("4[0-9]{1,}\\.[0-9]{4,}"))) %>% summarise(mean(Lat))

## Take the mean of Long by using the clustered centre

centroid_long <- crime_comb %>% filter(str_detect(Long, regex("-7[0-9]{1,}\\.[0-9]{4,}"))) %>% summarise(mean(Long))

## Now, to calculate the distance between two points, we will use the Haversine formula from the source https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula/23095329#23095329

hav.location <- function(long1, lat1, long2, lat2) {
  R <- 6371
  diff.long <- (long2 - long1)
  diff.lat <- (lat2 - lat1)
  a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
  b <- 2 * asin(pmin(1, sqrt(a))) 
  d = R * b
  return(d)
}

## Add the LOCATION column 

crime_comb <- crime_comb %>% mutate(LOCATION = hav.location(centroid_long[[1]], centroid_lat[[1]], Long, Lat ))

## Head after mutating

head(crime_comb %>% select(Lat,Long,LOCATION))
NA

Scan I

We, will check for the missing values in this section.


## First,we will check total missing values.

colSums(is.na(crime_comb))
    INCIDENT_NUMBER                CODE  OFFENSE_CODE_GROUP OFFENSE_DESCRIPTION 
                  0                   0                   0                   0 
               YEAR               MONTH         DAY_OF_WEEK                HOUR 
                  0                   0                   0                   0 
           UCR_PART              STREET                 Lat                Long 
               1322                   0               19999               19999 
           Location                NAME            LOCATION 
                  0                   0               19999 
## We decided to apply pattern matching for Lat and Long values
## Replace the values with NA

crime_comb$Lat[str_detect(crime_comb$Lat, regex("4[0-9]{1,}\\.[0-9]{4,}")) == FALSE] <- NA

crime_comb$Long[str_detect(crime_comb$Long, regex("-7[0-9]{1,}\\.[0-9]{4,}")) == FALSE] <- NA


## Again checking for the missing values

colSums(is.na(crime_comb))
    INCIDENT_NUMBER                CODE  OFFENSE_CODE_GROUP OFFENSE_DESCRIPTION 
                  0                   0                   0                   0 
               YEAR               MONTH         DAY_OF_WEEK                HOUR 
                  0                   0                   0                   0 
           UCR_PART              STREET                 Lat                Long 
               1322                   0               20744               20756 
           Location                NAME            LOCATION 
                  0                   0               19999 
## Now, we will remove the records with missing values.

crime_comb <- na.omit(crime_comb)

## Checking the missing values

colSums(is.na(crime_comb))
    INCIDENT_NUMBER                CODE  OFFENSE_CODE_GROUP OFFENSE_DESCRIPTION 
                  0                   0                   0                   0 
               YEAR               MONTH         DAY_OF_WEEK                HOUR 
                  0                   0                   0                   0 
           UCR_PART              STREET                 Lat                Long 
                  0                   0                   0                   0 
           Location                NAME            LOCATION 
                  0                   0                   0 
## We will now create a function to check for the infinite number and Nan in the dataset

is.special_value <- function(x){
  if (is.numeric(x)) (is.infinite(x) | is.nan(x))
}

## Apply the function to the dataset

colSums(sapply(crime_comb[, c("Lat", "Long", "LOCATION")], is.special_value))
     Lat     Long LOCATION 
       0        0        0 
## Checking the dimensions
dim(crime_comb)
[1] 297076     15

Scan II

First we will create a boxplot for the univariate variable to check for the outliers and then a histogram to check for z-scores but require normality for that and we can see that normality is not attained after making histogram, so we cannot check the z-score for the dataset.


## We will create a box plot

boxplot(crime_comb$LOCATION, main="CRIME IN BOSTON", ylab="LOCATION",col="grey")


## Create a histogram to check for normality check

hist(crime_comb$LOCATION)


## Create a function to cap the outliers values

cap <- function(x) {
  qtiles <- quantile(x, c(0.05, 0.25, 0.75, 0.75, 0.95))
  x[ x > qtiles[3] + 1.5 * IQR(x) ] <- qtiles[4]
  x
}

## Apply the function to cap outliers

crime_comb$LOCATION <- crime_comb$LOCATION %>% cap()

## Now, lets check again to see the outliers

boxplot(crime_comb$LOCATION, main="CRIME IN BOSTON", ylab="LOCATION",col="grey")

Transform

We will use LOCATION variable and try to scale the values according to the skewness of the histogram. As, here I have used BOX-COX as it will be perfect for total transformation into the normality.


## Now, check for histogram of LOCATION variable with the crime frequency

hist(crime_comb$LOCATION,
     xlab = "LOCATION",
     col = "grey",
     ylab = "CRIME FREQUENCY")


## Using Box-cox for transformation

boxcox_crime<- BoxCox(crime_comb$LOCATION,lambda = "auto")

## Again we will make a histogram to check for the transformation

hist(boxcox_crime,xlab = "LOCATION",
     col = "green",
     ylab = "CRIME FREQUENCY")

NA
NA
LS0tCnRpdGxlOiAiTUFUSDIzNDkgU0VNRVNURVIgMSwyMDIwIgphdXRob3I6ICJQUkVSSVQgTUlHTEFOSSBTMzgxNTgzOCIKc3VidGl0bGU6IEFzc2lnbm1lbnQgMgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCiMjIFJlcXVpcmVkIHBhY2thZ2VzIAoKCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZWRpdHJ1bGVzKQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkob3V0bGllcnMpCmxpYnJhcnkobHVicmlkYXRlKQpgYGAKCgojIyBFeGVjdXRpdmUgU3VtbWFyeSAKCjEpIFRvIGZ1bGZpbGwgYWxsIHRoZSByZXF1aXJlbWVudHMgcmVxdWlyZWQgaW4gZGF0YXNldHMsIEkgY2hvc2UgdGhlIGRhdGFzZXRzIG9mIGNyaW1lcyBpbiBib3N0b24gYW5kIHRoZSBjcmltZSBjb2RlcyBmb3IgdGhlIGZvbGxvd2luZyBvZmZlbnNlcyBmcm9tIHRoZSBzb3VyY2UgIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vQW5hbHl6ZUJvc3Rvbi9jcmltZXMtaW4tYm9zdG9uLgoKMikgQWZ0ZXIgdGhhdCwgSSBtZXJnZWQgdGhlIGRhdGFzZXRzIGJ5IHBlcmZvcm1pbmcgbGVmdGpvaW4gb3BlcmF0aW9uIG9uIHRoZSBjb21tb24gYXR0cmlidXRlLgoKMykgVGhlbiwgSSBpbnNwZWN0ZWQgYWxsIHRoZSB2YXJpYWJsZXMgcmVxdWlyZWQgZm9yIHRoZSBhbmFseXNpcyBhbmQgc2VncmVnYXRlZCB0aGUgbm9uLW5lY2Vzc2FyeSBvciByZWR1bmRlbnQgb25jZS4KCjQpIExvbmdpdHVkZSBhbmQgTGF0aXR1ZGUgb2YgdGhlIGNyaW1lcyB3ZXJlIGRlcml2aW5nIGEgbWVhbmluZ2Z1bCB2YXJpYWJsZSAiTE9DQVRJT04iLCB3aGljaCB3YXMgZGVyaXZlZCB1c2luZyB0aGUgJ0hhdmVyc2luZeKAmSBmb3JtdWxhIGZyb20gdGhlIHNvdXJjZSwgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjc5MjgvY2FsY3VsYXRlLWRpc3RhbmNlLWJldHdlZW4tdHdvLWxhdGl0dWRlLWxvbmdpdHVkZS1wb2ludHMtaGF2ZXJzaW5lLWZvcm11bGEvMjMwOTUzMjkjMjMwOTUzMjkuCgo1KSBBZnRlciB0aGF0LEkgdXNlZCBpcy5uYSgpIGZ1bmN0aW9uIHRvIGZpbmQgYWxsIHRoZSBOQSB2YWx1ZXMgYW5kIHRoZW4gc2Nhbm5lZCBhbGwgdGhlIG1pc3NpbmcgYWNjb3JkaW5nbHkuCgo2KSBOZXh0LCB3ZSBsb29rZWQgZm9yIHRoZSBvdXRsaWVycyBwcmVzZW50IGJ5IGRlZHVjaW5nIGJveHBsb3QgYW5kIHRoZW4gcmVtb3ZlZCB0aGVtIGFjY29yZGluZ2x5LgoKNykgVGhlbiwgYnkgdGFraW5nIHRoZSAiTE9DQVRJT04iIHZhcmlhYmxlIGFuZCBkZWR1Y2luZyBhIGhpc3RvZ3JhbSB0byBjaGVjayB0aGUgbm9ybWFsaXR5IHRyZW5kIGFuZCB0aGVuIHRyYW5zZm9ybWVkIGl0IHVzaW5nIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gdG8gYXR0YWluIG5vcm1hbGl0eSBhbmQgbWluLW1heCBub3JtYWxpemF0aW9uLgoKCiMjIERhdGEgCgpJIGhhdmUgdGFrZW4gdGhlIGRhdGFzZXRzIG9mIGNyaW1lcyBpbiBib3N0b24gYnkgdGhlIHNvdXJjZSBodHRwczovL3d3dy5rYWdnbGUuY29tL0FuYWx5emVCb3N0b24vY3JpbWVzLWluLWJvc3Rvbi4KSXQgc3RhdGVzIGFsbCB0aGUgY3JpbWVzIG9jY3VyZWQgaW4gYm9zdG9uIHdpdGggYWxsIHRoZSBsaW5rZWQgZGVzY3JpcHRpb25zIGFuZCB0aW1lIHJlY29yZGVkIGJ5IHRoZSBCb3N0b24gUG9saWNlLgpUaGVuIHJlbmFtZWQgaXQgYXMgYm9zdGNyaW1lLmNzdiAmIGNyaW1lY29kZXMuY3N2IGFjY29yZGluZ2x5LgoKRm9sbG93aW5nIGF0dHJpYnV0ZXMgaG9sZCBpbiBib3NjcmltZS5jc3YgLSBEQVRBU0VUIEE6CjEpIElOQ0lERU5UX05VTUJFUiAKMikgT0ZGRU5TRV9DT0RFIAozKSBPRkZFTlNFX0NPREVfR1JPVVAgCjQpIE9GRkVOU0VfREVTQ1JJUFRJT04gCjUpIERJU1RSSUNUIAo2KSBSRVBPUlRJTkdfQVJFQSAKNykgU0hPT1RJTkcgCjgpIE9DQ1VSUkVEX09OX0RBVEUgCjkpIDEwIFlFQVIgCjEwKSBNT05USCAKMTEpIERBWV9PRl9XRUVLIAoxMikgSE9VUiAKMTMpIFVDUl9QQVJUCjE0KSBTVFJFRVQgCjE1KSBMYXQKMTYpIExvbmcKCkF0dHJpYnV0ZXMgZm9yIGNyaW1lY29kZXMuY3N2LSBEQVRBU0VUIEI6CjEpIENPREUKMikgTkFNRQoKCmBgYHtyfQojIyBEQVRBU0VUIDE6Cgpib3N0Y3JpbWUgPC0gcmVhZC5jc3YoIi9Vc2Vycy9wcmVyaXRtaWdsYW5pL0Rlc2t0b3AvYm9zdGNyaW1lLmNzdiIsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyMgREFUQVNFVCAyOgoKY3JpbWVjb2RlczwtIHJlYWQuY3N2KCIvVXNlcnMvcHJlcml0bWlnbGFuaS9EZXNrdG9wL2NyaW1lIGNvZGVzLmNzdiIsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKaGVhZChib3N0Y3JpbWUpCgojIyBIZWFkIGZvciBjcmltZWNvZGVzLmNzdgoKaGVhZChjcmltZWNvZGVzKQoKIyMgVG8gY2hlY2sgdGhlIG51bWJlciBvZiBhdHRyaWJ1dGVzIGFuZCByZWNvcmRzIGZvciB0aGUgZGF0YXNldHMKCmRpbShib3N0Y3JpbWUpCmRpbShjcmltZWNvZGVzKQoKIyMgTm93LEkgY2hhbmdlZCB0aGUgY29sdW1uIG5hbWUgb2YgYm9zdGNyaW1lIGZyb20gJ29mZmVuc2VfY29kZScgdG8gJ2NvZGUnIGFzIGluIGNyaW1lY29kZXMgZGF0YXNldCB0byBtZXJnZQoKY29sbmFtZXMoYm9zdGNyaW1lKVtjb2xuYW1lcyhib3N0Y3JpbWUpID09ICdPRkZFTlNFX0NPREUnXSA8LSAnQ09ERScKCiMjIE5vdyBtZXJnaW5nIGJvdGggdGhlIGRhdGFzZXRzCgpjcmltZV9jb21iIDwtIGJvc3RjcmltZSAlPiUgbGVmdF9qb2luKGNyaW1lY29kZXMsIGJ5PWMoJ0NPREUnKSkKZGltKGNyaW1lX2NvbWIpCgpoZWFkKGNyaW1lX2NvbWIpCgojIyBBcyx3ZSBjb3VsZCBzZWUgdG9vIG1hbnkgcmVjb3JkcyBpbiB0aGUgbWVyZ2VkIGRhdGFzZXQsc28gd2Ugd2lsbCBnZXQgdGhlIGRpc3RpbmN0IHZhbHVlcwoKY3JpbWVjb2RlcyA8LSBjcmltZWNvZGVzICU+JSBkaXN0aW5jdChDT0RFLCAua2VlcF9hbGw9IFRSVUUpCgojIyBBZ2Fpbix3ZSB3b3VsZCBhcHBseSBtZXJnZSBmdW5jdGlvbiBhbmQgY2hlY2sKCmNyaW1lX2NvbWIgPC0gYm9zdGNyaW1lICU+JSBsZWZ0X2pvaW4oY3JpbWVjb2RlcywgYnk9YygnQ09ERScpKQpkaW0oY3JpbWVfY29tYikKCiMjIGNoZWNraW5nIGZvciB0aGUgaGVhZApoZWFkKGNyaW1lX2NvbWIpCgpgYGAKCiMjIFVuZGVyc3RhbmQgCgpOb3csYXMgd2hpbGUgaW1wb3J0aW5nLCB3ZSBoYXZlIHRha2VuIFN0cmluZ3Nhc0ZhY3RvcnM9RmFsc2UsIHNvIG5vdyB3ZSB3aWxsIGNoZWNrIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIG1lcmdlZCBkYXRhc2V0IGFuZCB3b3VsZCBvYnNlcnZlIHRoYXQgJ1VDUl9QQVJUJyByZWxhdGVzIHRvIHRoZSBzZXZlcml0eSBvZiB0aGUgY3JpbWUsIHNvIHRoaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhbmQgd2UgbGFiZWwgaXQgYWNjb3JkaW5nbHkuIEFsc28sd2Ugd291bGQgY29udmVydCAnREFZX09GX1dFRUsnIHRvIGZhY3RvcnMuCkFsc28gLHdlIGNvdWxkIG9ic2VydmUgdGhhdCB0aGUgJ09DQ1VSUkVEX09OX0RBVEEnIHZhcmlhYmxlIGNvbnRhaW5zIHRoZSB5ZWFyLGRheSxtb250aCBhbmQgdGltZSB3aGljaCBpcyBhbHJlYWR5IGdpdmVuLCBzbyB3ZSB3b3VsZCByZW1vdmUgdGhlIGF0dHJpYnV0ZSBhcyBpdCBpcyByZWR1bmRhbnQgYW5kIGFsc28gJ0RJU1RSSUNUJywKJ1JFUE9SVElORyBBUkVBJywnU0hPT1RJTkcnIHdlcmUgcmVtb3ZlZCBhcyB0aGV5IGhhdmUgbm8gcm9sZSBpbiB0aGUgYW5hbHlzaXMuCgpgYGB7cn0KCiMjIENoZWNraW5nIGZvciB0aGUgc3RydWN0dXJlIG9mIG1lcmdlZCBkYXRzZXQKCnN0cihjcmltZV9jb21iKQoKIyMgUmVtb3ZpbmcgdGhlIGF0dHJpYnV0ZXMgd2hpY2ggYXJlIG5vdCBuZWNlc3NhcnkKCmNyaW1lX2NvbWI8LWNyaW1lX2NvbWIgJT4lIHNlbGVjdCgtYygiU0hPT1RJTkciLCJPQ0NVUlJFRF9PTl9EQVRFIiwiRElTVFJJQ1QiLCJSRVBPUlRJTkdfQVJFQSIpKQoKIyMgQ29udmVydGluZyB0byBmYWN0b3JzIGFuZCBnaXZpbmcgbGFiZWxzIGFjY29yZGluZyB0byB0aGUgc2V2ZXJpdHkgb2YgY3JpbWUKCmNyaW1lX2NvbWIkVUNSX1BBUlQ8LWZhY3RvcihjcmltZV9jb21iJFVDUl9QQVJULGxldmVscyA9IGMoIlBhcnQgVGhyZWUiLCAiUGFydCBUd28iLCAiUGFydCBPbmUiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJTZXZlcmUiLCAiTWlsZCIsICJMb3ciKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRSApCgojIyBDb252ZXJ0aW5nIHRvIGZhY3RvcgoKY3JpbWVfY29tYiREQVlfT0ZfV0VFSyA8LSBmYWN0b3IoY3JpbWVfY29tYiREQVlfT0ZfV0VFSykKCiMjIENoZWNraW5nIHRoZSBzdHJ1Y3R1cmUgYWdhaW4KCnN0cihjcmltZV9jb21iKQoKCmBgYAoKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSAKCldlIGNhbiBzYXkgdGhhdCB0aGUgZGF0YXNldCBpcyBpbiB0aWR5IGNvbmRpdGlvbiBhcyBlYWNoIHZhcmlhYmxlIGluIHRoZSBkYXRhc2V0IGlzIHBsYWNlZCBpbiBpdHMgb3duIGxvY2F0aW9uIGFuZCBlYWNoIHJvdyBoYXMgc29tZSBvYnNlcnZhdGlvbi4gQWxzbyAoTG9uZ2l0dWRlLExhdGl0dWRlKSBnaXZlcyB1cyBsb2NhdGlvbiBvZiB0aGUgY3JpbWUgd2hpY2ggaXMgaW5mZXJlZCBmdXJ0aHVyIGFuZCBhbHNvIHdlIGNvdWxkIHNheSB0aGF0IGRhdGVzIGFyZSBwbGFjZWQgaW4gc2VwYXJhdGUgY29sdW1uIHdpdGggdGltZSBkZXRhaWxzICwgc28gd2UgcmVtb3ZlZCB0aGUgY29tYmluZWQgYXR0cmlidXRlIE9DQ1VSUkVEX09OX0RBVEUgZnJvbSB0aGUgZGF0YXNldC4KCmBgYHtyfQpgYGAKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCgpGaXJzdCAsIHdlIHNlZSB0aGF0IExBVCBhbmQgTE9ORyBnaXZlcyB1cyB0aGUgbG9jYXRpb24gb2YgdGhlIGNyaW1lLiBTbywgd2Ugd2lsbCBhZGQgYSBuZXcgY29sdW1uIExvY2F0aW9uIGluIG91ciBkYXRhc2V0IGZvciB0aGUgYW5hbHlzaXMuVG8gZmluZCB0aGUgY2VudHJvaWQgb2YgdGhlIExvbmcgYW5kIExhdCAsIHdlIHdpbGwgZG8gc28gbXkgZGVkdWNpbmcgdGhlIG1lYW4gb2YgYm90aCB0aGUgdmFyaWFibGVzLgoKCmBgYHtyfQoKIyMgQXMsIHRoZSB2YWx1ZXMgb2YgTGF0IGFuZCBsb25nIGFyZSBub3QgbWF0Y2hlZCAsIHNvIGZpcnN0IHdlIHdpbGwgZG8gcGF0dGVybiBtYXRjaGluZyBvZiB0aGUgY29sdW1ucyBhbmQgY2FsY3VsYXRlIHRoZSByaWdodCBtZWFuIGJ5IHVzaW5nIGNsdXN0ZXJlZCBjZW50cmUKCiMjIFRha2UgdGhlIG1lYW4gb2YgTGF0IGJ5IHVzaW5nIHRoZSBjbHVzdGVyZWQgY2VudHJlCgpjZW50cm9pZF9sYXQgPC0gY3JpbWVfY29tYiAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoTGF0LCByZWdleCgiNFswLTldezEsfVxcLlswLTldezQsfSIpKSkgJT4lIHN1bW1hcmlzZShtZWFuKExhdCkpCgojIyBUYWtlIHRoZSBtZWFuIG9mIExvbmcgYnkgdXNpbmcgdGhlIGNsdXN0ZXJlZCBjZW50cmUKCmNlbnRyb2lkX2xvbmcgPC0gY3JpbWVfY29tYiAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoTG9uZywgcmVnZXgoIi03WzAtOV17MSx9XFwuWzAtOV17NCx9IikpKSAlPiUgc3VtbWFyaXNlKG1lYW4oTG9uZykpCgojIyBOb3csIHRvIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzLCB3ZSB3aWxsIHVzZSB0aGUgSGF2ZXJzaW5lIGZvcm11bGEgZnJvbSB0aGUgc291cmNlIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI3OTI4L2NhbGN1bGF0ZS1kaXN0YW5jZS1iZXR3ZWVuLXR3by1sYXRpdHVkZS1sb25naXR1ZGUtcG9pbnRzLWhhdmVyc2luZS1mb3JtdWxhLzIzMDk1MzI5IzIzMDk1MzI5CgpoYXYubG9jYXRpb24gPC0gZnVuY3Rpb24obG9uZzEsIGxhdDEsIGxvbmcyLCBsYXQyKSB7CiAgUiA8LSA2MzcxCiAgZGlmZi5sb25nIDwtIChsb25nMiAtIGxvbmcxKQogIGRpZmYubGF0IDwtIChsYXQyIC0gbGF0MSkKICBhIDwtIHNpbihkaWZmLmxhdC8yKV4yICsgY29zKGxhdDEpICogY29zKGxhdDIpICogc2luKGRpZmYubG9uZy8yKV4yCiAgYiA8LSAyICogYXNpbihwbWluKDEsIHNxcnQoYSkpKSAKICBkID0gUiAqIGIKICByZXR1cm4oZCkKfQoKIyMgQWRkIHRoZSBMT0NBVElPTiBjb2x1bW4gCgpjcmltZV9jb21iIDwtIGNyaW1lX2NvbWIgJT4lIG11dGF0ZShMT0NBVElPTiA9IGhhdi5sb2NhdGlvbihjZW50cm9pZF9sb25nW1sxXV0sIGNlbnRyb2lkX2xhdFtbMV1dLCBMb25nLCBMYXQgKSkKCiMjIEhlYWQgYWZ0ZXIgbXV0YXRpbmcKCmhlYWQoY3JpbWVfY29tYiAlPiUgc2VsZWN0KExhdCxMb25nLExPQ0FUSU9OKSkKCmBgYAoKCiMjCVNjYW4gSSAKCldlLCB3aWxsIGNoZWNrIGZvciB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhpcyBzZWN0aW9uLgoKYGBge3J9CgojIyBGaXJzdCx3ZSB3aWxsIGNoZWNrIHRvdGFsIG1pc3NpbmcgdmFsdWVzLgoKY29sU3Vtcyhpcy5uYShjcmltZV9jb21iKSkKCiMjIFdlIGRlY2lkZWQgdG8gYXBwbHkgcGF0dGVybiBtYXRjaGluZyBmb3IgTGF0IGFuZCBMb25nIHZhbHVlcwojIyBSZXBsYWNlIHRoZSB2YWx1ZXMgd2l0aCBOQQoKY3JpbWVfY29tYiRMYXRbc3RyX2RldGVjdChjcmltZV9jb21iJExhdCwgcmVnZXgoIjRbMC05XXsxLH1cXC5bMC05XXs0LH0iKSkgPT0gRkFMU0VdIDwtIE5BCgpjcmltZV9jb21iJExvbmdbc3RyX2RldGVjdChjcmltZV9jb21iJExvbmcsIHJlZ2V4KCItN1swLTldezEsfVxcLlswLTldezQsfSIpKSA9PSBGQUxTRV0gPC0gTkEKCgojIyBBZ2FpbiBjaGVja2luZyBmb3IgdGhlIG1pc3NpbmcgdmFsdWVzCgpjb2xTdW1zKGlzLm5hKGNyaW1lX2NvbWIpKQoKIyMgTm93LCB3ZSB3aWxsIHJlbW92ZSB0aGUgcmVjb3JkcyB3aXRoIG1pc3NpbmcgdmFsdWVzLgoKY3JpbWVfY29tYiA8LSBuYS5vbWl0KGNyaW1lX2NvbWIpCgojIyBDaGVja2luZyB0aGUgbWlzc2luZyB2YWx1ZXMKCmNvbFN1bXMoaXMubmEoY3JpbWVfY29tYikpCgojIyBXZSB3aWxsIG5vdyBjcmVhdGUgYSBmdW5jdGlvbiB0byBjaGVjayBmb3IgdGhlIGluZmluaXRlIG51bWJlciBhbmQgTmFuIGluIHRoZSBkYXRhc2V0Cgppcy5zcGVjaWFsX3ZhbHVlIDwtIGZ1bmN0aW9uKHgpewogIGlmIChpcy5udW1lcmljKHgpKSAoaXMuaW5maW5pdGUoeCkgfCBpcy5uYW4oeCkpCn0KCiMjIEFwcGx5IHRoZSBmdW5jdGlvbiB0byB0aGUgZGF0YXNldAoKY29sU3VtcyhzYXBwbHkoY3JpbWVfY29tYlssIGMoIkxhdCIsICJMb25nIiwgIkxPQ0FUSU9OIildLCBpcy5zcGVjaWFsX3ZhbHVlKSkKCiMjIENoZWNraW5nIHRoZSBkaW1lbnNpb25zCmRpbShjcmltZV9jb21iKQoKYGBgCgoKIyMJU2NhbiBJSQoKRmlyc3Qgd2Ugd2lsbCBjcmVhdGUgYSBib3hwbG90IGZvciB0aGUgdW5pdmFyaWF0ZSB2YXJpYWJsZSB0byBjaGVjayBmb3IgdGhlIG91dGxpZXJzIGFuZCB0aGVuIGEgaGlzdG9ncmFtIHRvIGNoZWNrIGZvciB6LXNjb3JlcyBidXQgcmVxdWlyZSBub3JtYWxpdHkgZm9yIHRoYXQgYW5kIHdlIGNhbiBzZWUgdGhhdCBub3JtYWxpdHkgaXMgbm90IGF0dGFpbmVkIGFmdGVyIG1ha2luZyBoaXN0b2dyYW0sIHNvIHdlIGNhbm5vdCBjaGVjayB0aGUgei1zY29yZSBmb3IgdGhlIGRhdGFzZXQuCgpgYGB7cn0KCiMjIFdlIHdpbGwgY3JlYXRlIGEgYm94IHBsb3QKCmJveHBsb3QoY3JpbWVfY29tYiRMT0NBVElPTiwgbWFpbj0iQ1JJTUUgSU4gQk9TVE9OIiwgeWxhYj0iTE9DQVRJT04iLGNvbD0iZ3JleSIpCgojIyBDcmVhdGUgYSBoaXN0b2dyYW0gdG8gY2hlY2sgZm9yIG5vcm1hbGl0eSBjaGVjawoKaGlzdChjcmltZV9jb21iJExPQ0FUSU9OKQoKIyMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY2FwIHRoZSBvdXRsaWVycyB2YWx1ZXMKCmNhcCA8LSBmdW5jdGlvbih4KSB7CiAgcXRpbGVzIDwtIHF1YW50aWxlKHgsIGMoMC4wNSwgMC4yNSwgMC43NSwgMC43NSwgMC45NSkpCiAgeFsgeCA+IHF0aWxlc1szXSArIDEuNSAqIElRUih4KSBdIDwtIHF0aWxlc1s0XQogIHgKfQoKIyMgQXBwbHkgdGhlIGZ1bmN0aW9uIHRvIGNhcCBvdXRsaWVycwoKY3JpbWVfY29tYiRMT0NBVElPTiA8LSBjcmltZV9jb21iJExPQ0FUSU9OICU+JSBjYXAoKQoKIyMgTm93LCBsZXRzIGNoZWNrIGFnYWluIHRvIHNlZSB0aGUgb3V0bGllcnMKCmJveHBsb3QoY3JpbWVfY29tYiRMT0NBVElPTiwgbWFpbj0iQ1JJTUUgSU4gQk9TVE9OIiwgeWxhYj0iTE9DQVRJT04iLGNvbD0iZ3JleSIpCgpgYGAKCgojIwlUcmFuc2Zvcm0gCgpXZSB3aWxsIHVzZSBMT0NBVElPTiB2YXJpYWJsZSBhbmQgdHJ5IHRvIHNjYWxlIHRoZSB2YWx1ZXMgYWNjb3JkaW5nIHRvIHRoZSBza2V3bmVzcyBvZiB0aGUgaGlzdG9ncmFtLgpBcywgaGVyZSBJIGhhdmUgdXNlZCBCT1gtQ09YIGFzIGl0IHdpbGwgYmUgcGVyZmVjdCBmb3IgdG90YWwgdHJhbnNmb3JtYXRpb24gaW50byB0aGUgbm9ybWFsaXR5LgoKYGBge3J9CgojIyBOb3csIGNoZWNrIGZvciBoaXN0b2dyYW0gb2YgTE9DQVRJT04gdmFyaWFibGUgd2l0aCB0aGUgY3JpbWUgZnJlcXVlbmN5CgpoaXN0KGNyaW1lX2NvbWIkTE9DQVRJT04sCiAgICAgeGxhYiA9ICJMT0NBVElPTiIsCiAgICAgY29sID0gImdyZXkiLAogICAgIHlsYWIgPSAiQ1JJTUUgRlJFUVVFTkNZIikKCiMjIFVzaW5nIEJveC1jb3ggZm9yIHRyYW5zZm9ybWF0aW9uCgpib3hjb3hfY3JpbWU8LSBCb3hDb3goY3JpbWVfY29tYiRMT0NBVElPTixsYW1iZGEgPSAiYXV0byIpCgojIyBBZ2FpbiB3ZSB3aWxsIG1ha2UgYSBoaXN0b2dyYW0gdG8gY2hlY2sgZm9yIHRoZSB0cmFuc2Zvcm1hdGlvbgoKaGlzdChib3hjb3hfY3JpbWUseGxhYiA9ICJMT0NBVElPTiIsCiAgICAgY29sID0gImdyZWVuIiwKICAgICB5bGFiID0gIkNSSU1FIEZSRVFVRU5DWSIpCgoKYGBgCgo=