Prompt:

The following data set is the number of earthquakes per year magnitude 7.0 or greater. 1900-1998:

https://datamarket.com/data/set/22p8/number-of-earthquakes-per-year-magnitude-70-or-greater-1900-1998#!ds=22p8&display=line

Use outlier detections techniques including box plot, or other techniques such as R open source from twitter (AnomalyDection package), tsoutliers package, h2o package to analyze outliers. Discuss your findings and suggested actionable decision.

The first piece of this exercise begins by downloading the data set that is provided. I have gone ahead and done that, which is read in below, and where we begin our redition of this exercise.

setwd('C:\\Users\\JP\\Downloads')
Error in names(frame)[names(frame) == "x"] <- name : 
  names() applied to a non-vector
The working directory was changed to C:/Users/JP/Downloads inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the the working directory for notebook chunks.
data<-read.csv(file='number-of-earthquakes-per-year-m.csv',header=T)

Before we dive in, let’s take a look at the data qaulities and see what we imported.

str(data)
'data.frame':   100 obs. of  2 variables:
 $ Year                                                              : Factor w/ 100 levels "1900","1901",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ Number.of.earthquakes.per.year.magnitude.7.0.or.greater..1900.1998: int  13 14 8 10 16 26 32 27 18 32 ...
summary(data)
      Year    Number.of.earthquakes.per.year.magnitude.7.0.or.greater..1900.1998
 1900   : 1   Min.   : 6.00                                                     
 1901   : 1   1st Qu.:15.00                                                     
 1902   : 1   Median :20.00                                                     
 1903   : 1   Mean   :20.02                                                     
 1904   : 1   3rd Qu.:24.00                                                     
 1905   : 1   Max.   :41.00                                                     
 (Other):94   NA's   :1                                                         
plot(data)

The data appears to be very straight forward data over time, one record for each year, like a time series. In this case we are only looking for anomalies, and not trying to predict the future just yet. The most basic way to detect anomalies is to use a simple boxplot technique. This will help us to identify outliers. We do need to be careful here though. Our data is sequential and outliers are not always an anomaly.

names(data)[2]<-c('Earthquakes')
boxplot(data$Earthquakes)

There appear to be two obvious outliers in our dataset, but the boxplot does not consider the time series aspect, which should not be ignored.

The next approach we can take is using a package designed to identify anomalies for time series data. This is the package mentioned in the requirements above. Below we load the package and transform our data to meet the needs of the package. The Year field need to be converted to a date. We will also use ggplot2 to show plots of our data.

library(devtools)
library(AnomalyDetection)
library(ggplot2)
Year<-NULL
Year$year<-as.data.frame(as.POSIXlt(data$Year,format = '%Y'))
quakes<-as.data.frame(cbind(Year$year,data$Earthquakes))
names(quakes)<-c('Year','NumberOfQuakes')
ggplot(quakes, aes(Year, NumberOfQuakes)) + geom_line() + scale_x_datetime() + xlab("") + ylab("Number of Earthquakes")

It is good to see the data plotted as such, next we will use the package that we downloaded to identify the anomalies. To use the package it is helpful to go over all of the arguments that it takes. max.anoms is the argument that determines how many maximum anomalies there could be. alpha determines a level of significance for qaulifying anomalies. We can try a few of these out in different scenarios. Something that this package requires is a period of more than 1. We will just look at this in increments of 5 years for the purposes of getting results using this method.

m1<-AnomalyDetectionTs(quakes,max_anoms=.02,direction='both', plot=T,na.rm = T)
m1$plot

We found one this way, which is less than our boxplot. We can change a few things though. We can lower the alpha and see if any other anomalies show up.

m2<-AnomalyDetectionTs(quakes,max_anoms=.1,alpha =.25,direction='both', plot=T,na.rm = T)
m2$plot

At .25 we got a second, but its likely not significant at that level. This package appears to work alright, but not seeing the dates at the bottom is bothersome. The arguments are also very fragile. If we specified everything that matched our data set, we would get errors. Even the guide to this package on R-Bloggers had to change what they were doing to present their data in a way that satisfied their needs.

So far we know that the year with ~40 earthquakes is most likely an anomaly. Using the boxplot, and when we lower the significance, the year with just under 40 earthquakes also appears to be an anomaly. These two points in time are close together and therefore may not be anomalies, but outliers.

To dig further, we can try a few other methods. We will look at the package “tsoutliers” next, the second package mentioned in the prompt above. This one requires time series objects:

library(tsoutliers)
library(forecast)
quakesTS<-ts(quakes$NumberOfQuakes,start = c(1900))
quakesTSL<-log(quakesTS)
plot(quakesTS)

plot(quakesTSL)

We created a time series object to work on here and ran the auto arima function. We also took the log values of our original data to help with distances.

?tso()
there is no package called <U+393C><U+3E31>anomalyDetection<U+393C><U+3E32>
ts1<-tso(quakesTS,types = c("AO","LS","TC","IO",'SLS'),tsmethod = 'auto.arima')
ts1
Series: quakesTS 
Regression with ARIMA(1,0,0) errors 

Coefficients:
         ar1  intercept     IO58
      0.6138    19.9440  15.8789
s.e.  0.0789     1.4161   3.4839

sigma^2 estimated as 31.14:  log likelihood=-309.9
AIC=627.8   AICc=628.22   BIC=638.22

Outliers:
  type ind time coefhat tstat
1   IO  58 1957   15.88 4.558

Interesting, here we have a third point just before 1960, the spike that can be seen in the plots above. Here it is idenfitied as an innovative outlier, which means that it does skew our results quite as much.

ts2<-tso(quakesTS,types = c("AO","LS","TC","IO",'SLS'),remove.method = 'en-masse',tsmethod = 'auto.arima')
ts2
Series: quakesTS 
Regression with ARIMA(1,0,0) errors 

Coefficients:
         ar1  intercept     IO58
      0.6138    19.9440  15.8789
s.e.  0.0789     1.4161   3.4839

sigma^2 estimated as 31.14:  log likelihood=-309.9
AIC=627.8   AICc=628.22   BIC=638.22

Outliers:
  type ind time coefhat tstat
1   IO  58 1957   15.88 4.558
ts3<-tso(quakesTS,types = c("AO","LS","TC","IO",'SLS'),remove.method = 'bottom-up',tsmethod = 'auto.arima')
ts3
Series: quakesTS 
Regression with ARIMA(1,0,0) errors 

Coefficients:
         ar1  intercept     IO58
      0.6138    19.9440  15.8789
s.e.  0.0789     1.4161   3.4839

sigma^2 estimated as 31.14:  log likelihood=-309.9
AIC=627.8   AICc=628.22   BIC=638.22

Outliers:
  type ind time coefhat tstat
1   IO  58 1957   15.88 4.558
ts4<-tso(quakesTSL,types = c("AO","LS","TC","IO",'SLS'),remove.method = 'bottom-up',args.tsmethod = list(1,0,1))
ts4
Series: 1 
ARIMA(1,0,1) with non-zero mean 

Coefficients:
         ar1      ma1    mean
      0.8381  -0.4290  2.9087
s.e.  0.0774   0.1197  0.1057

sigma^2 estimated as 0.09805:  log likelihood=-24.77
AIC=57.55   AICc=57.97   BIC=67.97

No outliers were detected.

We try a few different options here, but this method still only shows the one outlier.

The final package that is recommended in the prompt is the ‘h20’ package. h20 is a deep learning algorithm and needs to have data presented in factor. We will add the

We were able to isolate points with the greatest error. Interestingly, we do not have the 1960 year in here, but rather, our points that the boxplot and the open source AnomalyDetectionTS dectected earlier, 1950 and 1943. To conclude this exercise, it is most likely that 1943 was a year that is likely an anomaly in terms of the number of earthquakes. 1950 is also likley an anomaly. 1957 could also be considered an anomaly, but has little impact on the data, as it spikes rather than misleads a trend.

LS0tDQp0aXRsZTogIldlZWsgNyBQcm9qZWN0IEFub21hbHkgRGV0ZWN0aW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQphdXRob3I6IEpvaG4gTmV2aWxsZQ0KLS0tDQoNCiMjIyMjIFByb21wdDoNCg0KVGhlIGZvbGxvd2luZyBkYXRhIHNldCBpcyB0aGUgbnVtYmVyIG9mIGVhcnRocXVha2VzIHBlciB5ZWFyIG1hZ25pdHVkZSA3LjAgb3IgZ3JlYXRlci4gMTkwMC0xOTk4Og0KDQpodHRwczovL2RhdGFtYXJrZXQuY29tL2RhdGEvc2V0LzIycDgvbnVtYmVyLW9mLWVhcnRocXVha2VzLXBlci15ZWFyLW1hZ25pdHVkZS03MC1vci1ncmVhdGVyLTE5MDAtMTk5OCMhZHM9MjJwOCZkaXNwbGF5PWxpbmUNCg0KVXNlIG91dGxpZXIgZGV0ZWN0aW9ucyB0ZWNobmlxdWVzIGluY2x1ZGluZyBib3ggcGxvdCwgb3Igb3RoZXIgdGVjaG5pcXVlcyBzdWNoIGFzIFIgb3BlbiBzb3VyY2UgZnJvbSB0d2l0dGVyIChBbm9tYWx5RGVjdGlvbiBwYWNrYWdlKSwgdHNvdXRsaWVycyBwYWNrYWdlLCBoMm8gcGFja2FnZSB0byBhbmFseXplIG91dGxpZXJzLiBEaXNjdXNzIHlvdXIgZmluZGluZ3MgYW5kIHN1Z2dlc3RlZCBhY3Rpb25hYmxlIGRlY2lzaW9uLg0KDQpUaGUgZmlyc3QgcGllY2Ugb2YgdGhpcyBleGVyY2lzZSBiZWdpbnMgYnkgZG93bmxvYWRpbmcgdGhlIGRhdGEgc2V0IHRoYXQgaXMgcHJvdmlkZWQuICBJIGhhdmUgZ29uZSBhaGVhZCBhbmQgZG9uZSB0aGF0LCB3aGljaCBpcyByZWFkIGluIGJlbG93LCBhbmQgd2hlcmUgd2UgYmVnaW4gb3VyIHJlZGl0aW9uIG9mIHRoaXMgZXhlcmNpc2UuDQoNCmBgYHtyfQ0Kc2V0d2QoJ0M6XFxVc2Vyc1xcSlBcXERvd25sb2FkcycpDQpkYXRhPC1yZWFkLmNzdihmaWxlPSdudW1iZXItb2YtZWFydGhxdWFrZXMtcGVyLXllYXItbS5jc3YnLGhlYWRlcj1UKQ0KYGBgDQoNCkJlZm9yZSB3ZSBkaXZlIGluLCBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YSBxYXVsaXRpZXMgYW5kIHNlZSB3aGF0IHdlIGltcG9ydGVkLg0KDQpgYGB7cn0NCnN0cihkYXRhKQ0Kc3VtbWFyeShkYXRhKQ0KcGxvdChkYXRhKQ0KYGBgDQoNClRoZSBkYXRhIGFwcGVhcnMgdG8gYmUgdmVyeSBzdHJhaWdodCBmb3J3YXJkIGRhdGEgb3ZlciB0aW1lLCBvbmUgcmVjb3JkIGZvciBlYWNoIHllYXIsIGxpa2UgYSB0aW1lIHNlcmllcy4gIEluIHRoaXMgY2FzZSB3ZSBhcmUgb25seSBsb29raW5nIGZvciBhbm9tYWxpZXMsIGFuZCBub3QgdHJ5aW5nIHRvIHByZWRpY3QgdGhlIGZ1dHVyZSBqdXN0IHlldC4gIFRoZSBtb3N0IGJhc2ljIHdheSB0byBkZXRlY3QgYW5vbWFsaWVzIGlzIHRvIHVzZSBhIHNpbXBsZSBib3hwbG90IHRlY2huaXF1ZS4gIFRoaXMgd2lsbCBoZWxwIHVzIHRvIGlkZW50aWZ5IG91dGxpZXJzLiAgV2UgZG8gbmVlZCB0byBiZSBjYXJlZnVsIGhlcmUgdGhvdWdoLiAgT3VyIGRhdGEgaXMgc2VxdWVudGlhbCBhbmQgb3V0bGllcnMgYXJlIG5vdCBhbHdheXMgYW4gYW5vbWFseS4NCg0KYGBge3J9DQpuYW1lcyhkYXRhKVsyXTwtYygnRWFydGhxdWFrZXMnKQ0KYm94cGxvdChkYXRhJEVhcnRocXVha2VzKQ0KYGBgDQoNCg0KVGhlcmUgYXBwZWFyIHRvIGJlIHR3byBvYnZpb3VzIG91dGxpZXJzIGluIG91ciBkYXRhc2V0LCBidXQgdGhlIGJveHBsb3QgZG9lcyBub3QgY29uc2lkZXIgdGhlIHRpbWUgc2VyaWVzIGFzcGVjdCwgd2hpY2ggc2hvdWxkIG5vdCBiZSBpZ25vcmVkLg0KDQpUaGUgbmV4dCBhcHByb2FjaCB3ZSBjYW4gdGFrZSBpcyB1c2luZyBhIHBhY2thZ2UgZGVzaWduZWQgdG8gaWRlbnRpZnkgYW5vbWFsaWVzIGZvciB0aW1lIHNlcmllcyBkYXRhLiAgVGhpcyBpcyB0aGUgcGFja2FnZSBtZW50aW9uZWQgaW4gdGhlIHJlcXVpcmVtZW50cyBhYm92ZS4gIEJlbG93IHdlIGxvYWQgdGhlIHBhY2thZ2UgYW5kIHRyYW5zZm9ybSBvdXIgZGF0YSB0byBtZWV0IHRoZSBuZWVkcyBvZiB0aGUgcGFja2FnZS4gIFRoZSBZZWFyIGZpZWxkIG5lZWQgdG8gYmUgY29udmVydGVkIHRvIGEgZGF0ZS4gIFdlIHdpbGwgYWxzbyB1c2UgZ2dwbG90MiB0byBzaG93IHBsb3RzIG9mIG91ciBkYXRhLg0KDQpgYGB7cn0NCmxpYnJhcnkoZGV2dG9vbHMpDQpsaWJyYXJ5KEFub21hbHlEZXRlY3Rpb24pDQpsaWJyYXJ5KGdncGxvdDIpDQpZZWFyPC1OVUxMDQpZZWFyJHllYXI8LWFzLmRhdGEuZnJhbWUoYXMuUE9TSVhsdChkYXRhJFllYXIsZm9ybWF0ID0gJyVZJykpDQpxdWFrZXM8LWFzLmRhdGEuZnJhbWUoY2JpbmQoWWVhciR5ZWFyLGRhdGEkRWFydGhxdWFrZXMpKQ0KbmFtZXMocXVha2VzKTwtYygnWWVhcicsJ051bWJlck9mUXVha2VzJykNCmdncGxvdChxdWFrZXMsIGFlcyhZZWFyLCBOdW1iZXJPZlF1YWtlcykpICsgZ2VvbV9saW5lKCkgKyBzY2FsZV94X2RhdGV0aW1lKCkgKyB4bGFiKCIiKSArIHlsYWIoIk51bWJlciBvZiBFYXJ0aHF1YWtlcyIpDQoNCmBgYA0KSXQgaXMgZ29vZCB0byBzZWUgdGhlIGRhdGEgcGxvdHRlZCBhcyBzdWNoLCBuZXh0IHdlIHdpbGwgdXNlIHRoZSBwYWNrYWdlIHRoYXQgd2UgZG93bmxvYWRlZCB0byBpZGVudGlmeSB0aGUgYW5vbWFsaWVzLiAgVG8gdXNlIHRoZSBwYWNrYWdlIGl0IGlzIGhlbHBmdWwgdG8gZ28gb3ZlciBhbGwgb2YgdGhlIGFyZ3VtZW50cyB0aGF0IGl0IHRha2VzLiAgbWF4LmFub21zIGlzIHRoZSBhcmd1bWVudCB0aGF0IGRldGVybWluZXMgaG93IG1hbnkgbWF4aW11bSBhbm9tYWxpZXMgdGhlcmUgY291bGQgYmUuICBhbHBoYSBkZXRlcm1pbmVzIGEgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlIGZvciBxYXVsaWZ5aW5nIGFub21hbGllcy4gIFdlIGNhbiB0cnkgYSBmZXcgb2YgdGhlc2Ugb3V0IGluIGRpZmZlcmVudCBzY2VuYXJpb3MuICBTb21ldGhpbmcgdGhhdCB0aGlzIHBhY2thZ2UgcmVxdWlyZXMgaXMgYSBwZXJpb2Qgb2YgbW9yZSB0aGFuIDEuICBXZSB3aWxsIGp1c3QgbG9vayBhdCB0aGlzIGluIGluY3JlbWVudHMgb2YgNSB5ZWFycyBmb3IgdGhlIHB1cnBvc2VzIG9mIGdldHRpbmcgcmVzdWx0cyB1c2luZyB0aGlzIG1ldGhvZC4NCmBgYHtyfQ0KbTE8LUFub21hbHlEZXRlY3Rpb25UcyhxdWFrZXMsbWF4X2Fub21zPS4wMixkaXJlY3Rpb249J2JvdGgnLCBwbG90PVQsbmEucm0gPSBUKQ0KbTEkcGxvdA0KDQpgYGANCldlIGZvdW5kIG9uZSB0aGlzIHdheSwgd2hpY2ggaXMgbGVzcyB0aGFuIG91ciBib3hwbG90LiAgV2UgY2FuIGNoYW5nZSBhIGZldyB0aGluZ3MgdGhvdWdoLiAgV2UgY2FuIGxvd2VyIHRoZSBhbHBoYSBhbmQgc2VlIGlmIGFueSBvdGhlciBhbm9tYWxpZXMgc2hvdyB1cC4NCg0KYGBge3J9DQptMjwtQW5vbWFseURldGVjdGlvblRzKHF1YWtlcyxtYXhfYW5vbXM9LjEsYWxwaGEgPS4yNSxkaXJlY3Rpb249J2JvdGgnLCBwbG90PVQsbmEucm0gPSBUKQ0KbTIkcGxvdA0KDQpgYGANCkF0IC4yNSB3ZSBnb3QgYSBzZWNvbmQsIGJ1dCBpdHMgbGlrZWx5IG5vdCBzaWduaWZpY2FudCBhdCB0aGF0IGxldmVsLiAgVGhpcyBwYWNrYWdlIGFwcGVhcnMgdG8gd29yayBhbHJpZ2h0LCBidXQgbm90IHNlZWluZyB0aGUgZGF0ZXMgYXQgdGhlIGJvdHRvbSBpcyBib3RoZXJzb21lLiBUaGUgYXJndW1lbnRzIGFyZSBhbHNvIHZlcnkgZnJhZ2lsZS4gIElmIHdlIHNwZWNpZmllZCBldmVyeXRoaW5nIHRoYXQgbWF0Y2hlZCBvdXIgZGF0YSBzZXQsIHdlIHdvdWxkIGdldCBlcnJvcnMuICBFdmVuIHRoZSBndWlkZSB0byB0aGlzIHBhY2thZ2Ugb24gUi1CbG9nZ2VycyBoYWQgdG8gY2hhbmdlIHdoYXQgdGhleSB3ZXJlIGRvaW5nIHRvIHByZXNlbnQgdGhlaXIgZGF0YSBpbiBhIHdheSB0aGF0IHNhdGlzZmllZCB0aGVpciBuZWVkcy4NCg0KU28gZmFyIHdlIGtub3cgdGhhdCB0aGUgeWVhciB3aXRoIH40MCBlYXJ0aHF1YWtlcyBpcyBtb3N0IGxpa2VseSBhbiBhbm9tYWx5LiAgVXNpbmcgdGhlIGJveHBsb3QsIGFuZCB3aGVuIHdlIGxvd2VyIHRoZSBzaWduaWZpY2FuY2UsIHRoZSB5ZWFyIHdpdGgganVzdCB1bmRlciA0MCBlYXJ0aHF1YWtlcyBhbHNvIGFwcGVhcnMgdG8gYmUgYW4gYW5vbWFseS4gIFRoZXNlIHR3byBwb2ludHMgaW4gdGltZSBhcmUgY2xvc2UgdG9nZXRoZXIgYW5kIHRoZXJlZm9yZSBtYXkgbm90IGJlIGFub21hbGllcywgYnV0IG91dGxpZXJzLiAgDQoNClRvIGRpZyBmdXJ0aGVyLCB3ZSBjYW4gdHJ5IGEgZmV3IG90aGVyIG1ldGhvZHMuIFdlIHdpbGwgbG9vayBhdCB0aGUgcGFja2FnZSAidHNvdXRsaWVycyIgbmV4dCwgdGhlIHNlY29uZCBwYWNrYWdlIG1lbnRpb25lZCBpbiB0aGUgcHJvbXB0IGFib3ZlLg0KVGhpcyBvbmUgcmVxdWlyZXMgdGltZSBzZXJpZXMgb2JqZWN0czoNCg0KYGBge3J9DQpsaWJyYXJ5KHRzb3V0bGllcnMpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KcXVha2VzVFM8LXRzKHF1YWtlcyROdW1iZXJPZlF1YWtlcyxzdGFydCA9IGMoMTkwMCkpDQpxdWFrZXNUU0w8LWxvZyhxdWFrZXNUUykNCnBsb3QocXVha2VzVFMpDQpwbG90KHF1YWtlc1RTTCkNCmBgYA0KV2UgY3JlYXRlZCBhIHRpbWUgc2VyaWVzIG9iamVjdCB0byB3b3JrIG9uIGhlcmUgYW5kIHJhbiB0aGUgYXV0byBhcmltYSBmdW5jdGlvbi4gIFdlIGFsc28gdG9vayB0aGUgbG9nIHZhbHVlcyBvZiBvdXIgb3JpZ2luYWwgZGF0YSB0byBoZWxwIHdpdGggZGlzdGFuY2VzLiAgDQoNCmBgYHtyfQ0KP3RzbygpDQoNCnRzMTwtdHNvKHF1YWtlc1RTLHR5cGVzID0gYygiQU8iLCJMUyIsIlRDIiwiSU8iLCdTTFMnKSx0c21ldGhvZCA9ICdhdXRvLmFyaW1hJykNCnRzMQ0KYGBgDQoNCkludGVyZXN0aW5nLCBoZXJlIHdlIGhhdmUgYSB0aGlyZCBwb2ludCBqdXN0IGJlZm9yZSAxOTYwLCB0aGUgc3Bpa2UgdGhhdCBjYW4gYmUgc2VlbiBpbiB0aGUgcGxvdHMgYWJvdmUuICBIZXJlIGl0IGlzIGlkZW5maXRpZWQgYXMgYW4gaW5ub3ZhdGl2ZSBvdXRsaWVyLCB3aGljaCBtZWFucyB0aGF0IGl0IGRvZXMgc2tldyBvdXIgcmVzdWx0cyBxdWl0ZSBhcyBtdWNoLiANCg0KYGBge3J9DQp0czI8LXRzbyhxdWFrZXNUUyx0eXBlcyA9IGMoIkFPIiwiTFMiLCJUQyIsIklPIiwnU0xTJykscmVtb3ZlLm1ldGhvZCA9ICdlbi1tYXNzZScsdHNtZXRob2QgPSAnYXV0by5hcmltYScpDQp0czINCnRzMzwtdHNvKHF1YWtlc1RTLHR5cGVzID0gYygiQU8iLCJMUyIsIlRDIiwiSU8iLCdTTFMnKSxyZW1vdmUubWV0aG9kID0gJ2JvdHRvbS11cCcsdHNtZXRob2QgPSAnYXV0by5hcmltYScpDQp0czMNCnRzNDwtdHNvKHF1YWtlc1RTTCx0eXBlcyA9IGMoIkFPIiwiTFMiLCJUQyIsIklPIiwnU0xTJykscmVtb3ZlLm1ldGhvZCA9ICdib3R0b20tdXAnLGFyZ3MudHNtZXRob2QgPSBsaXN0KDEsMCwxKSkNCnRzNA0KYGBgDQoNCldlIHRyeSBhIGZldyBkaWZmZXJlbnQgb3B0aW9ucyBoZXJlLCBidXQgdGhpcyBtZXRob2Qgc3RpbGwgb25seSBzaG93cyB0aGUgb25lIG91dGxpZXIuDQoNClRoZSBmaW5hbCBwYWNrYWdlIHRoYXQgaXMgcmVjb21tZW5kZWQgaW4gdGhlIHByb21wdCBpcyB0aGUgJ2gyMCcgcGFja2FnZS4gIGgyMCBpcyBhIGRlZXAgbGVhcm5pbmcgYWxnb3JpdGhtIGFuZCBuZWVkcyB0byBoYXZlIGRhdGEgcHJlc2VudGVkIGluIGZhY3Rvci4gIFdlIHdpbGwgYWRkIHRoZSANCg0KYGBge3J9DQpsaWJyYXJ5KGgybykNCmgyby5pbml0KCkNCnNldC5zZWVkKDEwKQ0KZGF0YS5oZXg8LWFzLmgybyhkYXRhKQ0KDQpkYXRhMm8uZGwgPSBoMm8uZGVlcGxlYXJuaW5nKHggPSAxOjEwMCwgdHJhaW5pbmdfZnJhbWUgPSBkYXRhLmhleCwgYXV0b2VuY29kZXIgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoMzAsIDMwLCAzMCksIGVwb2NocyA9IDUwLHNlZWQ9MSkNCmVycm9ycyA8LSBoMm8uYW5vbWFseShkYXRhMm8uZGwsIGRhdGEuaGV4LCBwZXJfZmVhdHVyZSA9IEZBTFNFKQ0KcHJpbnQoZXJyb3JzKQ0KZXJyb3JzPC1hcy5kYXRhLmZyYW1lKGVycm9ycykNCnBsb3Qoc29ydChlcnJvcnMkUmVjb25zdHJ1Y3Rpb24uTVNFKSkNCm91dGxpZXJzPC13aGljaChlcnJvcnMgPiAwLjAxMikNCmRhdGFbb3V0bGllcnMsXQ0KYGBgDQoNCldlIHdlcmUgYWJsZSB0byBpc29sYXRlIHBvaW50cyB3aXRoIHRoZSBncmVhdGVzdCBlcnJvci4gIEludGVyZXN0aW5nbHksIHdlIGRvIG5vdCBoYXZlIHRoZSAxOTYwIHllYXIgaW4gaGVyZSwgYnV0IHJhdGhlciwgb3VyIHBvaW50cyB0aGF0IHRoZSBib3hwbG90IGFuZCB0aGUgb3BlbiBzb3VyY2UgQW5vbWFseURldGVjdGlvblRTIGRlY3RlY3RlZCBlYXJsaWVyLCAxOTUwIGFuZCAxOTQzLg0KVG8gY29uY2x1ZGUgdGhpcyBleGVyY2lzZSwgaXQgaXMgbW9zdCBsaWtlbHkgdGhhdCAxOTQzIHdhcyBhIHllYXIgdGhhdCBpcyBsaWtlbHkgYW4gYW5vbWFseSBpbiB0ZXJtcyBvZiB0aGUgbnVtYmVyIG9mIGVhcnRocXVha2VzLiAgMTk1MCBpcyBhbHNvIGxpa2xleSBhbiBhbm9tYWx5LiAgMTk1NyBjb3VsZCBhbHNvIGJlIGNvbnNpZGVyZWQgYW4gYW5vbWFseSwgYnV0IGhhcyBsaXR0bGUgaW1wYWN0IG9uIHRoZSBkYXRhLCBhcyBpdCBzcGlrZXMgcmF0aGVyIHRoYW4gbWlzbGVhZHMgYSB0cmVuZC4NCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=