This is a Frantz Moudoute Notebook. The following document will provide an overlook of a few ML techniques used in R between 2014 and 2022.

Introduction to vectors

Among the key concept in R are the way R manages data. Data are stored as vectors, each vector contains elements. Watch out, because all element must be of the same type in R. there can be integer, double, character, logical (Boolean) among others.

Let create some vector with a few elements below, first a vector of strings.

my_favorite_cities <- c("Hong Kong", "Tokyo", "Paris", "Douala")
print(my_favorite_cities)

Then a vector of doubles

their_temperatures <- c(25.3, 21.0,15.0,35.0)
print(their_temperatures)

And last a vector of booleans

mega_cities <- c(FALSE, TRUE, FALSE, FALSE)  # You can see that TRUE/FALSE are all in capital letters 
print(mega_cities)

In R, as one would expect, values stored in a vector will preserve their order, so we can call each value using its position, let call the second city and the second temperature. Lets play a bit with the data and print as well the second to the fourth cities.

print(my_favorite_cities[2])

print(their_temperatures[2])

print(my_favorite_cities[2:4])

We can manipulate vectors as it pleases us, say for example we want to remove the first element of the cities (You notice that we are not using the ‘<-’ sign to update the value of the vector.

print(my_favorite_cities[-1])

Introduction to Factors

Factors are a data structure in R that is ideal for representing nominal data (Nominal data is data that can be labelled or classified into mutually exclusive categories within a variable). One could use a vector of strings to stor nominal data, but we will see in a few steps some of the advantages of R’s factors data structure. Factors are a special type of vector.

Basic factors

cities_weather <- factor(c("Tempered", "Cold", "Cold","Warm"))
print(cities_weather)

We can notice in the print command here above that there is a mention of levels. Levels provide us with the unique categories comprises in this factor vector.

Advanced factors - setting levels

We can modify factors to ready them for additional values. One way to do it is to set the levels manually.

cities_weather <- factor(c("Tempered", "Cold", "Cold","Warm"), levels = c("Tempered", "Cold","Warm", "Extremely Warm"))
print(cities_weather)

Advanced factors - setting levels and ordering them

On top of manually setting up levels we can also order them, see below

cities_weather <- factor(c("Tempered", "Cold", "Cold","Warm"), levels = c("Cold","Tempered", "Warm", "Extremely Warm"), ordered = TRUE)
print(cities_weather)

Comparing factor value

One of the key advantage of the factors is that we can compare the value or the elements, something we could not do if we had used a vector of string instead. IN the following example, we will check which of the cities_weather value is not that cold

cities_weather > "Tempered"
print(cities_weather)

As seen above, the R factors enables for a comparison of nominal or string like elements.

Introduction to lists

List in R are pretty similar to vectors, except that they accept all sort of data type. cities_memories <-

cities_memories <- list("Sighseeing", TRUE, 2323, FALSE)
print(cities_memories)

The other interesting point about lists is that we can assign a name to each of the element. let’s try:

cities_list <- list(activity = "Sighseeing", parking_free = TRUE, postcode = 2323, free_wifi = FALSE)
print(cities_list)

With the name of the items in the list assigned, we can now call the items as follows:

cities_list$activity

Meanwhile the list carries some similar functions to the Vector. we can call elements by their position. See the example below we call the second element of the list:

cities_list[2]

Introduction to Dataframe

Dataframe are at the very center of the R programming framework. They can be compared to a spreadsheet from a visual angle, or to a database for the most advanced of you. A dataframe is nothing else than a combination of vectors, of the same length. Lets create a dataframe by combining some of the fectors that we created earlier.

destination_df <- data.frame(my_favorite_cities , their_temperatures ,mega_cities , cities_weather  )
print(destination_df)

Now that we have introduced the dataframe, we might want to verify that each of the columns contains the type of value that we expected. The str function is quite handy for that.

print(str(destination_df))

Our data have been integrated into the new vector as we expected.

Extracting from dataframe

Extracting one column from a dataframe

Extracting a column from a dataframe in R is very easy. call the dataframe df and the dollar sign $ followed by the name of the column or attribute to be more precise. Lets give it a try

print(destination_df$my_favorite_cities)

Extracting two columns from a dataframe

To extract multiple columns, we will simply pass a vector to the dataframe. That vector will contain the name of the columns

destination_df[c("my_favorite_cities", "cities_weather")]

Extracting the 1st row in the second column

destination_df[1,2]

Extracting the 1st and the 3rd row of the second and third columns

destination_df[c(1,3),c(2,3)]

Extracting all the rows of the second column

destination_df[,2]

Extracting the first and second rows of two specific columns

destination_df[c(1,2), c("mega_cities","cities_weather")]

We can keep playing with these combination and even add negative selectors such as destination_city[-2,-2], try it and see what comes out.

Introduction to matrices

In R, matrices are an object used to store data in a tabular shape. Usually they are of a single data type, as one would mostly used them for calculus. Remember as c() created vectors, list() creates list and data.frame() creates dataframe? for matrices we can simply use matrix() to create a matrix. Careful with the trick here, we need to specify the number of columns or rows. Lets do a few examples.

mat_a <- matrix(c(4,5,8,7,5,4,11,21,3), nrow=3)
print(mat_a)

As one can see, the matrix contains a vector, of which the lengh should be greater to than the square value of the number of rows or columns instructed. Let review the same matrix but with a ncol this time, and see what happens.

mat_b <- matrix(c(4,5,8,7,5,4,11,21,3), ncol=2)
print(mat_b)

We got a warning that indicates that the data length is inconsistent.

LS0tDQp0aXRsZTogIlIsIGZyb20gWmVybyB0byBIZXJvOiBJbnRyb2R1Y3Rpb24iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGEgW0ZyYW50eiBNb3Vkb3V0ZV0oaW5zdGFsbC5wYWNrYWdlcygnUldla2EnKSkgTm90ZWJvb2suIFRoZSBmb2xsb3dpbmcgZG9jdW1lbnQgd2lsbCBwcm92aWRlIGFuIG92ZXJsb29rIG9mIGEgZmV3IE1MIHRlY2huaXF1ZXMgdXNlZCBpbiBSIGJldHdlZW4gMjAxNCBhbmQgMjAyMi4NCg0KIyBJbnRyb2R1Y3Rpb24gdG8gdmVjdG9ycw0KDQpBbW9uZyB0aGUga2V5IGNvbmNlcHQgaW4gUiBhcmUgdGhlIHdheSBSIG1hbmFnZXMgZGF0YS4gRGF0YSBhcmUgc3RvcmVkIGFzIHZlY3RvcnMsIGVhY2ggdmVjdG9yIGNvbnRhaW5zIGVsZW1lbnRzLiBXYXRjaCBvdXQsIGJlY2F1c2UgYWxsIGVsZW1lbnQgbXVzdCBiZSBvZiB0aGUgc2FtZSB0eXBlIGluIFIuIHRoZXJlIGNhbiBiZSBpbnRlZ2VyLCBkb3VibGUsIGNoYXJhY3RlciwgbG9naWNhbCAoQm9vbGVhbikgYW1vbmcgb3RoZXJzLg0KDQpMZXQgY3JlYXRlIHNvbWUgdmVjdG9yIHdpdGggYSBmZXcgZWxlbWVudHMgYmVsb3csIGZpcnN0IGEgdmVjdG9yIG9mIHN0cmluZ3MuDQoNCmBgYHtyfQ0KbXlfZmF2b3JpdGVfY2l0aWVzIDwtIGMoIkhvbmcgS29uZyIsICJUb2t5byIsICJQYXJpcyIsICJEb3VhbGEiKQ0KcHJpbnQobXlfZmF2b3JpdGVfY2l0aWVzKQ0KYGBgDQoNClRoZW4gYSB2ZWN0b3Igb2YgZG91Ymxlcw0KDQpgYGB7cn0NCnRoZWlyX3RlbXBlcmF0dXJlcyA8LSBjKDI1LjMsIDIxLjAsMTUuMCwzNS4wKQ0KcHJpbnQodGhlaXJfdGVtcGVyYXR1cmVzKQ0KYGBgDQoNCkFuZCBsYXN0IGEgdmVjdG9yIG9mIGJvb2xlYW5zDQoNCmBgYHtyfQ0KbWVnYV9jaXRpZXMgPC0gYyhGQUxTRSwgVFJVRSwgRkFMU0UsIEZBTFNFKSAgIyBZb3UgY2FuIHNlZSB0aGF0IFRSVUUvRkFMU0UgYXJlIGFsbCBpbiBjYXBpdGFsIGxldHRlcnMgDQpwcmludChtZWdhX2NpdGllcykNCmBgYA0KDQpJbiBSLCBhcyBvbmUgd291bGQgZXhwZWN0LCB2YWx1ZXMgc3RvcmVkIGluIGEgdmVjdG9yIHdpbGwgcHJlc2VydmUgdGhlaXIgb3JkZXIsIHNvIHdlIGNhbiBjYWxsIGVhY2ggdmFsdWUgdXNpbmcgaXRzIHBvc2l0aW9uLCBsZXQgY2FsbCB0aGUgc2Vjb25kIGNpdHkgYW5kIHRoZSBzZWNvbmQgdGVtcGVyYXR1cmUuIExldHMgcGxheSBhIGJpdCB3aXRoIHRoZSBkYXRhIGFuZCBwcmludCBhcyB3ZWxsIHRoZSBzZWNvbmQgdG8gdGhlIGZvdXJ0aCBjaXRpZXMuDQoNCmBgYHtyfQ0KcHJpbnQobXlfZmF2b3JpdGVfY2l0aWVzWzJdKQ0KDQpwcmludCh0aGVpcl90ZW1wZXJhdHVyZXNbMl0pDQoNCnByaW50KG15X2Zhdm9yaXRlX2NpdGllc1syOjRdKQ0KYGBgDQoNCldlIGNhbiBtYW5pcHVsYXRlIHZlY3RvcnMgYXMgaXQgcGxlYXNlcyB1cywgc2F5IGZvciBleGFtcGxlIHdlIHdhbnQgdG8gcmVtb3ZlIHRoZSBmaXJzdCBlbGVtZW50IG9mICB0aGUgY2l0aWVzIChZb3Ugbm90aWNlIHRoYXQgIHdlIGFyZSBub3QgdXNpbmcgdGhlICc8LScgc2lnbiB0byB1cGRhdGUgdGhlIHZhbHVlIG9mIHRoZSB2ZWN0b3IuDQoNCmBgYHtyfQ0KcHJpbnQobXlfZmF2b3JpdGVfY2l0aWVzWy0xXSkNCmBgYA0KDQojIEludHJvZHVjdGlvbiB0byBGYWN0b3JzDQpGYWN0b3JzIGFyZSBhIGRhdGEgc3RydWN0dXJlIGluIFIgdGhhdCBpcyBpZGVhbCBmb3IgcmVwcmVzZW50aW5nIG5vbWluYWwgZGF0YSAoTm9taW5hbCBkYXRhIGlzIGRhdGEgdGhhdCBjYW4gYmUgbGFiZWxsZWQgb3IgY2xhc3NpZmllZCBpbnRvIG11dHVhbGx5IGV4Y2x1c2l2ZSBjYXRlZ29yaWVzIHdpdGhpbiBhIHZhcmlhYmxlKS4gT25lIGNvdWxkIHVzZSBhIHZlY3RvciBvZiBzdHJpbmdzIHRvIHN0b3Igbm9taW5hbCBkYXRhLCBidXQgd2Ugd2lsbCBzZWUgaW4gYSBmZXcgc3RlcHMgc29tZSBvZiB0aGUgYWR2YW50YWdlcyBvZiBSJ3MgZmFjdG9ycyBkYXRhIHN0cnVjdHVyZS4gRmFjdG9ycyBhcmUgYSBzcGVjaWFsIHR5cGUgb2YgdmVjdG9yLg0KDQojIyBCYXNpYyBmYWN0b3JzDQpgYGB7cn0NCmNpdGllc193ZWF0aGVyIDwtIGZhY3RvcihjKCJUZW1wZXJlZCIsICJDb2xkIiwgIkNvbGQiLCJXYXJtIikpDQpwcmludChjaXRpZXNfd2VhdGhlcikNCmBgYA0KV2UgY2FuIG5vdGljZSBpbiB0aGUgcHJpbnQgY29tbWFuZCBoZXJlIGFib3ZlIHRoYXQgdGhlcmUgaXMgYSBtZW50aW9uIG9mIGxldmVscy4gTGV2ZWxzIHByb3ZpZGUgdXMgd2l0aCB0aGUgdW5pcXVlIGNhdGVnb3JpZXMgY29tcHJpc2VzIGluIHRoaXMgZmFjdG9yIHZlY3Rvci4NCg0KIyMgQWR2YW5jZWQgZmFjdG9ycyAtIHNldHRpbmcgbGV2ZWxzDQpXZSBjYW4gbW9kaWZ5IGZhY3RvcnMgdG8gcmVhZHkgdGhlbSBmb3IgYWRkaXRpb25hbCB2YWx1ZXMuIE9uZSB3YXkgdG8gZG8gaXQgaXMgdG8gc2V0IHRoZSBsZXZlbHMgbWFudWFsbHkuDQpgYGB7cn0NCmNpdGllc193ZWF0aGVyIDwtIGZhY3RvcihjKCJUZW1wZXJlZCIsICJDb2xkIiwgIkNvbGQiLCJXYXJtIiksIGxldmVscyA9IGMoIlRlbXBlcmVkIiwgIkNvbGQiLCJXYXJtIiwgIkV4dHJlbWVseSBXYXJtIikpDQpwcmludChjaXRpZXNfd2VhdGhlcikNCmBgYA0KDQojIyBBZHZhbmNlZCBmYWN0b3JzIC0gc2V0dGluZyBsZXZlbHMgYW5kIG9yZGVyaW5nIHRoZW0NCk9uIHRvcCBvZiBtYW51YWxseSBzZXR0aW5nIHVwIGxldmVscyB3ZSBjYW4gYWxzbyBvcmRlciB0aGVtLCBzZWUgYmVsb3cNCmBgYHtyfQ0KY2l0aWVzX3dlYXRoZXIgPC0gZmFjdG9yKGMoIlRlbXBlcmVkIiwgIkNvbGQiLCAiQ29sZCIsIldhcm0iKSwgbGV2ZWxzID0gYygiQ29sZCIsIlRlbXBlcmVkIiwgIldhcm0iLCAiRXh0cmVtZWx5IFdhcm0iKSwgb3JkZXJlZCA9IFRSVUUpDQpwcmludChjaXRpZXNfd2VhdGhlcikNCmBgYA0KIyMjIENvbXBhcmluZyBmYWN0b3IgdmFsdWUNCk9uZSBvZiB0aGUga2V5IGFkdmFudGFnZSBvZiB0aGUgZmFjdG9ycyBpcyB0aGF0IHdlIGNhbiBjb21wYXJlIHRoZSB2YWx1ZSBvciB0aGUgZWxlbWVudHMsIHNvbWV0aGluZyB3ZSBjb3VsZCBub3QgZG8gaWYgd2UgaGFkIHVzZWQgYSB2ZWN0b3Igb2Ygc3RyaW5nIGluc3RlYWQuIA0KSU4gdGhlIGZvbGxvd2luZyBleGFtcGxlLCB3ZSB3aWxsIGNoZWNrIHdoaWNoIG9mIHRoZSBjaXRpZXNfd2VhdGhlciB2YWx1ZSBpcyBub3QgdGhhdCBjb2xkDQpgYGB7cn0NCmNpdGllc193ZWF0aGVyID4gIlRlbXBlcmVkIg0KcHJpbnQoY2l0aWVzX3dlYXRoZXIpDQpgYGANCkFzIHNlZW4gYWJvdmUsIHRoZSBSIGZhY3RvcnMgZW5hYmxlcyBmb3IgYSBjb21wYXJpc29uIG9mIG5vbWluYWwgb3Igc3RyaW5nIGxpa2UgZWxlbWVudHMuDQoNCiMgSW50cm9kdWN0aW9uIHRvIGxpc3RzDQpMaXN0IGluIFIgYXJlIHByZXR0eSBzaW1pbGFyIHRvIHZlY3RvcnMsIGV4Y2VwdCB0aGF0IHRoZXkgYWNjZXB0IGFsbCBzb3J0IG9mIGRhdGEgdHlwZS4gDQpjaXRpZXNfbWVtb3JpZXMgPC0gDQpgYGB7cn0NCmNpdGllc19tZW1vcmllcyA8LSBsaXN0KCJTaWdoc2VlaW5nIiwgVFJVRSwgMjMyMywgRkFMU0UpDQpwcmludChjaXRpZXNfbWVtb3JpZXMpDQpgYGANCiBUaGUgb3RoZXIgaW50ZXJlc3RpbmcgcG9pbnQgYWJvdXQgbGlzdHMgaXMgdGhhdCB3ZSBjYW4gYXNzaWduIGEgbmFtZSB0byBlYWNoIG9mIHRoZSBlbGVtZW50LiBsZXQncyB0cnk6DQoNCmBgYHtyfQ0KY2l0aWVzX2xpc3QgPC0gbGlzdChhY3Rpdml0eSA9ICJTaWdoc2VlaW5nIiwgcGFya2luZ19mcmVlID0gVFJVRSwgcG9zdGNvZGUgPSAyMzIzLCBmcmVlX3dpZmkgPSBGQUxTRSkNCnByaW50KGNpdGllc19saXN0KQ0KYGBgDQpXaXRoIHRoZSBuYW1lIG9mIHRoZSBpdGVtcyBpbiB0aGUgbGlzdCBhc3NpZ25lZCwgd2UgY2FuIG5vdyBjYWxsIHRoZSBpdGVtcyBhcyBmb2xsb3dzOg0KYGBge3J9DQpjaXRpZXNfbGlzdCRhY3Rpdml0eQ0KYGBgDQoNCk1lYW53aGlsZSB0aGUgbGlzdCBjYXJyaWVzIHNvbWUgc2ltaWxhciBmdW5jdGlvbnMgdG8gdGhlIFZlY3Rvci4gd2UgY2FuIGNhbGwgZWxlbWVudHMgYnkgdGhlaXIgcG9zaXRpb24uIFNlZSB0aGUgZXhhbXBsZSBiZWxvdyB3ZSBjYWxsIHRoZSBzZWNvbmQgZWxlbWVudCBvZiB0aGUgbGlzdDoNCmBgYHtyfQ0KY2l0aWVzX2xpc3RbMl0NCg0KYGBgDQoNCiMgSW50cm9kdWN0aW9uIHRvIERhdGFmcmFtZQ0KRGF0YWZyYW1lIGFyZSBhdCB0aGUgdmVyeSBjZW50ZXIgb2YgdGhlIFIgcHJvZ3JhbW1pbmcgZnJhbWV3b3JrLiBUaGV5IGNhbiBiZSBjb21wYXJlZCB0byBhIHNwcmVhZHNoZWV0IGZyb20gYSB2aXN1YWwgYW5nbGUsIG9yICB0byBhIGRhdGFiYXNlIGZvciB0aGUgbW9zdCBhZHZhbmNlZCBvZiB5b3UuIEEgZGF0YWZyYW1lIGlzIG5vdGhpbmcgZWxzZSB0aGFuIGEgY29tYmluYXRpb24gb2YgdmVjdG9ycywgb2YgdGhlIHNhbWUgbGVuZ3RoLiBMZXRzIGNyZWF0ZSBhIGRhdGFmcmFtZSBieSBjb21iaW5pbmcgc29tZSBvZiB0aGUgZmVjdG9ycyB0aGF0IHdlIGNyZWF0ZWQgZWFybGllci4NCg0KYGBge3J9DQpkZXN0aW5hdGlvbl9kZiA8LSBkYXRhLmZyYW1lKG15X2Zhdm9yaXRlX2NpdGllcyAsIHRoZWlyX3RlbXBlcmF0dXJlcyAsbWVnYV9jaXRpZXMgLCBjaXRpZXNfd2VhdGhlciAgKQ0KcHJpbnQoZGVzdGluYXRpb25fZGYpDQoNCmBgYA0KTm93IHRoYXQgd2UgaGF2ZSBpbnRyb2R1Y2VkIHRoZSBkYXRhZnJhbWUsIHdlIG1pZ2h0IHdhbnQgdG8gdmVyaWZ5IHRoYXQgZWFjaCBvZiB0aGUgY29sdW1ucyBjb250YWlucyB0aGUgdHlwZSBvZiB2YWx1ZSB0aGF0IHdlIGV4cGVjdGVkLiBUaGUgKnN0ciogZnVuY3Rpb24gaXMgcXVpdGUgaGFuZHkgZm9yIHRoYXQuDQpgYGB7cn0NCnByaW50KHN0cihkZXN0aW5hdGlvbl9kZikpDQpgYGANCk91ciBkYXRhIGhhdmUgYmVlbiBpbnRlZ3JhdGVkIGludG8gdGhlIG5ldyB2ZWN0b3IgYXMgd2UgZXhwZWN0ZWQuIA0KDQojIyBFeHRyYWN0aW5nIGZyb20gZGF0YWZyYW1lDQojIyMgRXh0cmFjdGluZyBvbmUgY29sdW1uIGZyb20gYSBkYXRhZnJhbWUNCkV4dHJhY3RpbmcgYSBjb2x1bW4gZnJvbSBhIGRhdGFmcmFtZSBpbiBSIGlzIHZlcnkgZWFzeS4gY2FsbCB0aGUgZGF0YWZyYW1lICpkZiogYW5kIHRoZSBkb2xsYXIgc2lnbiAqJCogZm9sbG93ZWQgYnkgdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiBvciBhdHRyaWJ1dGUgdG8gYmUgbW9yZSBwcmVjaXNlLiBMZXRzIGdpdmUgaXQgYSB0cnkgDQpgYGB7cn0NCnByaW50KGRlc3RpbmF0aW9uX2RmJG15X2Zhdm9yaXRlX2NpdGllcykNCg0KYGBgDQoNCiMjIyBFeHRyYWN0aW5nIHR3byBjb2x1bW5zIGZyb20gYSBkYXRhZnJhbWUNClRvIGV4dHJhY3QgbXVsdGlwbGUgY29sdW1ucywgd2Ugd2lsbCBzaW1wbHkgcGFzcyBhIHZlY3RvciB0byB0aGUgZGF0YWZyYW1lLiBUaGF0IHZlY3RvciB3aWxsIGNvbnRhaW4gdGhlIG5hbWUgb2YgdGhlIGNvbHVtbnMNCmBgYHtyfQ0KZGVzdGluYXRpb25fZGZbYygibXlfZmF2b3JpdGVfY2l0aWVzIiwgImNpdGllc193ZWF0aGVyIildDQpgYGANCg0KIyMjIEV4dHJhY3RpbmcgdGhlIDFzdCByb3cgaW4gdGhlIHNlY29uZCBjb2x1bW4NCg0KYGBge3J9DQpkZXN0aW5hdGlvbl9kZlsxLDJdDQoNCmBgYA0KDQojIyMgRXh0cmFjdGluZyB0aGUgMXN0IGFuZCB0aGUgM3JkIHJvdyBvZiB0aGUgc2Vjb25kIGFuZCB0aGlyZCBjb2x1bW5zDQpgYGB7cn0NCmRlc3RpbmF0aW9uX2RmW2MoMSwzKSxjKDIsMyldDQoNCmBgYA0KDQojIyMgRXh0cmFjdGluZyBhbGwgdGhlIHJvd3Mgb2YgdGhlIHNlY29uZCBjb2x1bW4NCmBgYHtyfQ0KZGVzdGluYXRpb25fZGZbLDJdDQoNCmBgYA0KIyMjIEV4dHJhY3RpbmcgdGhlIGZpcnN0IGFuZCBzZWNvbmQgcm93cyBvZiB0d28gc3BlY2lmaWMgY29sdW1ucyANCmBgYHtyfQ0KZGVzdGluYXRpb25fZGZbYygxLDIpLCBjKCJtZWdhX2NpdGllcyIsImNpdGllc193ZWF0aGVyIildDQoNCmBgYA0KV2UgY2FuIGtlZXAgcGxheWluZyB3aXRoIHRoZXNlIGNvbWJpbmF0aW9uIGFuZCBldmVuIGFkZCBuZWdhdGl2ZSBzZWxlY3RvcnMgc3VjaCBhcyAqZGVzdGluYXRpb25fY2l0eVstMiwtMl0qLCB0cnkgaXQgYW5kIHNlZSB3aGF0IGNvbWVzIG91dC4NCg0KIyBJbnRyb2R1Y3Rpb24gIHRvIG1hdHJpY2VzDQpJbiBSLCBtYXRyaWNlcyBhcmUgYW4gb2JqZWN0IHVzZWQgdG8gc3RvcmUgZGF0YSBpbiBhIHRhYnVsYXIgc2hhcGUuIFVzdWFsbHkgdGhleSBhcmUgb2YgYSBzaW5nbGUgZGF0YSB0eXBlLCBhcyBvbmUgd291bGQgbW9zdGx5IHVzZWQgdGhlbSBmb3IgY2FsY3VsdXMuIFJlbWVtYmVyIGFzIGMoKSBjcmVhdGVkIHZlY3RvcnMsIGxpc3QoKSBjcmVhdGVzIGxpc3QgYW5kIGRhdGEuZnJhbWUoKSBjcmVhdGVzIGRhdGFmcmFtZT8gZm9yIG1hdHJpY2VzIHdlIGNhbiBzaW1wbHkgdXNlIG1hdHJpeCgpIHRvIGNyZWF0ZSBhIG1hdHJpeC4gQ2FyZWZ1bCB3aXRoIHRoZSB0cmljayBoZXJlLCB3ZSBuZWVkIHRvIHNwZWNpZnkgdGhlIG51bWJlciBvZiBjb2x1bW5zIG9yIHJvd3MuIExldHMgZG8gYSBmZXcgZXhhbXBsZXMuDQoNCmBgYHtyfQ0KbWF0X2EgPC0gbWF0cml4KGMoNCw1LDgsNyw1LDQsMTEsMjEsMyksIG5yb3c9MykNCnByaW50KG1hdF9hKQ0KYGBgDQpBcyBvbmUgY2FuIHNlZSwgdGhlIG1hdHJpeCBjb250YWlucyBhIHZlY3Rvciwgb2Ygd2hpY2ggdGhlIGxlbmdoIHNob3VsZCBiZSBncmVhdGVyIHRvIHRoYW4gdGhlIHNxdWFyZSB2YWx1ZSBvZiB0aGUgbnVtYmVyIG9mIHJvd3Mgb3IgY29sdW1ucyBpbnN0cnVjdGVkLiBMZXQgcmV2aWV3IHRoZSBzYW1lIG1hdHJpeCBidXQgd2l0aCBhIG5jb2wgdGhpcyB0aW1lLCBhbmQgc2VlIHdoYXQgaGFwcGVucy4NCg0KDQpgYGB7cn0NCm1hdF9iIDwtIG1hdHJpeChjKDQsNSw4LDcsNSw0LDExLDIxLDMpLCBuY29sPTIpDQpwcmludChtYXRfYikNCmBgYA0KV2UgZ290IGEgd2FybmluZyB0aGF0IGluZGljYXRlcyB0aGF0IHRoZSBkYXRhIGxlbmd0aCBpcyBpbmNvbnNpc3RlbnQuDQoNCg0K