This refresher course is based on:
Franken, W.M. & Bouts, R.A. (2002). Wiskunde voor statistiek: Een voorbereiding. Bussum, Netherlands: Uitgeverij Coutinho
The refresher course consists of five chapters:
- Set Theory
- Mathematical Operations
- Equations
- Functions & Graphs
- Statistical Operations
This module illustrates all topics in the textbook, and how they can be implemented in R.
On the fly, you will learn many of the basic functions of R.
1. Sets
1.1 Sets in Mathematics
- A set is a well-defined collection of objects.
- Each object in a set is called an element of the set.
- Two sets are equal if they have exactly the same elements in them.
- A set that contains no elements is called a null set or an empty set.
- If every element in Set A is also in Set B, then Set A is a subset of Set B.
Example: if A is the set of first five charachters of the alphabet, then: A = {a,b,c,d,e}
In R we can create this set as follows.
We can decide on the name ourselves. Here, we use “SetA”.
The two characters “<” and “-” look like an arrow. We combine the characters on the right of the arrow, using the c() function We then assign this combination to SetA, on the lefthand side.
SetA <- c("a","b","c","d","e")
"a" %in% SetA
[1] TRUE
"f" %in% SetA
[1] FALSE
Likewise, we can create a set of numbers, from 1 to 100.
SetN <- c(1:100)
length(SetN)
[1] 100
88 %in% SetN # Is 88 an element of our set? [TRUE, it is!]
[1] TRUE
105 %in%SetN # Is 105 an element of our set? [FALSE, it is not!]
[1] FALSE
1.2 Relations between sets
Empty Set
An empty set is a set without elements.
SetEmpty <- NULL
length(SetEmpty)
[1] 0
"2" %in% SetEmpty
[1] FALSE
Identical sets
Apart from the sequence of elements, the two sets below are identical.
All elements of A occur in B, and all elements of B are part of A.
SetA <- c("1","3","4","6")
SetB <- c("1","4","6","3")
SetB %in% SetA
[1] TRUE TRUE TRUE TRUE
SetA %in% SetB
[1] TRUE TRUE TRUE TRUE
We can test the differences and similarities using the commands below.
The overlap between two sets, is called the intersection. The intersection is graphically displayed below.
All elements that are part of two sets combined (either A, B or both) form the union of the two sets. In a graph:
The intersect of two sets is written as \(A \cap B\)
The union of two sets is written as \(A \cup B\)
setdiff(SetA, SetB)
character(0)
setequal(SetA, SetB)
[1] TRUE
intersect(SetA, SetB)
[1] "1" "3" "4" "6"
Let’s look at some other examples.
The two sets below are not identical, and contain duplicates.
From the result we see that duplicates are removed!
SetA <- c("a","a","b","c","d")
SetB <- c("c","c","d","e")
union(SetA, SetB)
[1] "a" "b" "c" "d" "e"
(SetA)
[1] "a" "a" "b" "c" "d"
unique(SetA) # Removes duplicates
[1] "a" "b" "c" "d"
duplicated(SetA)
[1] FALSE TRUE FALSE FALSE FALSE
SetA[!duplicated(SetA)] # Removes duplicates
[1] "a" "b" "c" "d"
setdiff(SetA, SetB)
[1] "a" "b"
setdiff(SetB, SetA)
[1] "e"
To challenge your skills, we can define the union of sets A and B as follows:
- Elements unique to A, plus:
- Elements unique to B, plus:
- Elements in the interesection of A and B.
We can sort the combination of these three parts alphabetically, and check if indeed the result is equal to what we have defined as the union:
sort(c(setdiff(SetA, SetB), setdiff(SetB, SetA), intersect(SetB, SetA)))
[1] "a" "b" "c" "d" "e"
all(sort(c(setdiff(SetA, SetB), setdiff(SetB, SetA), intersect(SetB, SetA))) == union(SetA, SetB))
[1] TRUE
The all() function can be used to see if all elements of one set, are contained by the other.
All elements of set C (a duplicated element “a” ) are part of D.
One element of D, is not contained in A.
SetC <- c("a","a"); SetD <- c("a","b")
SetC %in% SetD
[1] TRUE TRUE
all(SetC %in% SetD)
[1] TRUE
SetD %in% SetC
[1] TRUE FALSE
all(SetD %in% SetC)
[1] FALSE
Sets of Numbers
(SetN <- c(-10:+10)) # Putting the expression between brackets, prints the object in the console!
[1] -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10
length(SetN)
[1] 21
SetN[3]
[1] -8
SetN[3] < SetN[4]
[1] TRUE
Exercises
Exercise 1
Given are the following sets:
A = {2,4,6,8,10,12,14,16} B = {1,2,3,4,5,6,7} C = {3,6,9,12,15,18}
- A \(\cap\) B = (Intersection of A and B)
- A \(\cap\) C =
- B \(\cap\) C =
- A \(\cap\) B \(\cap\) C =
- A \(\cup\) B = (Union of A and B)
- A \(\cap\) B \(\cap\) C =
Solution
Let’s first create the sets A, B and C
A <- seq(2, 16, 2)
B <- c(1:7)
C <- seq(3, 18, 3)
A; B; C # Prints the three sets
[1] 2 4 6 8 10 12 14 16
[1] 1 2 3 4 5 6 7
[1] 3 6 9 12 15 18
- A \(\cap\) B
(intersect(A, B)) # Putting the command between brackets, prints the result
[1] 2 4 6
- A \(\cap\) C
(intersect(A, C))
[1] 6 12
- B \(\cap\) C
(intersect(B, C))
[1] 3 6
- A \(\cap\) B \(\cap\) C
intersect(A, B, C) will give an error message, as intersect() works on two sets!
we can do it in 2 steps; first, we determine the intersection of A and B; followed by the intersection of that result with C!
You can imagine that this is doable for three sets. But if we were to find the intersection between many sets, then the experession would get very lengthy! A better alternative is to use the Reduce() function!
Often, you can find solutions to this kind of challenging by googling. The Reduce() function, for example, we found on link
intersect(intersect(A,B),C)
[1] 6
Reduce(intersect, list(A,B,C))
[1] 6
- A \(\cup\) B
sort((union(A, B)))
[1] 1 2 3 4 5 6 7 8 10 12 14 16
- A \(\cap\) B \(\cap\) C
sort(Reduce(union, list(A,B, C)))
[1] 1 2 3 4 5 6 7 8 9 10 12 14 15 16 18
Are you in for a challenge?
Which elements are in intersections of sets, but not in all sets?
(AB <- intersect(A,B)) # Pairwise intersection A and B; stored as set AB
[1] 2 4 6
(AC <- intersect(A,C)) # Id, for A and C
[1] 6 12
(BC <- intersect(B,C)) # Id, for B and C
[1] 3 6
(sort(pairs <- Reduce(union, list(AB,AC,BC)))) # All pairwise intersection combined, stored as "pairs"
[1] 2 3 4 6 12
(trios <- Reduce(intersect, list(A,B,C))) # Elements present in the intersection of all three sets
[1] 6
sort(setdiff(pairs, trios)) # Elements in pairs, but not in trios
[1] 2 3 4 12
You see that elements 2, 3, 4 and 12 are in pairwise intersections, but not in the three-way intersection!
Graphically, just to convince you:
Exercise 2
Are the following statements true or false?
- {2,4} \(\subset\) (A \(\cap\) B)
- 6 \(\in\) (A \(\cap\) B \(\cap\) C)
- {6} \(\subset\) (A \(\cap\) B)
- (A \(\cap\) B) \(\subset\) (A \(\cup\) B)
Solutions
- {2,4} \(\subset\) (A \(\cap\) B)
SetTest <- c(2,4)
all(SetTest %in% A)
[1] TRUE
- 6 \(\in\) (A \(\cap\) B \(\cap\) C)
- {6} \(\subset\) (A \(\cap\) B)
SetTest <- c(6)
trios
[1] 6
all(SetTest %in% trios) # Remember we stored the intersect as "trios"!
[1] TRUE
# Alternatively, define 6 as a single element rather than a set of one element
6 %in% trios
[1] TRUE
- (A \(\cap\) B) \(\subset\) (A \(\cup\) B)
This is true in general. Elements in the intersection of A and B, are by definition part of both A and B. The intersection is therefore a subset of all elements in A or B!
Just to train the formulation of this exercise in R:
all(intersect(A, B) %in% union(A, B))
[1] TRUE
Exercise 3
A = {1,2,3,4,5,6} B = {5,6} C = {1,2,5,6} D = {2,3,4} E = {2,3,4,5}
Complete the statement with one of the symbols:
- \(\in\) (element of)
- \(\notin\) (not an element of)
- \(\subset\) (subset of)
- \(\supset\) (superset; if A is subset of B, then B is superset of A)
- \(\cap\) (intersection)
- \(\cup\)
- B….C
- B….C = B
- B….C = C
- B….D = 0 (empty set)
- C….D = A
- D….E = D
- 4….C \(\cap\) B
- D….E….A
Solutions
Use reason to answer each of the questions! As an additional challenge, formulate the sets in R, and use any of the commands introduced in this chapter to check your answer!
- \(\subset\) (B is obviously a subset of C, as all elements of B are also in C)
- \(\cap\) (the intersect of B and C, is equal to B, as B is a subset of B)
- \(\cup\) (the combined elements of B and C, are equal to C, as C is a superset of B)
- \(\cap\) (as B and D have no elements in common, the interesect is the empty set)
- \(\cup\) (all elements of C and D combined, match A)
- \(\cap\) or \(\subset\)
- \(\notin\) (4 is not part of the intersection of B and C; it is not even part of the union of B and C)
- \(\subset\) (D is a subset of E, which in turn is a subset of A; you can conclude that therefore D is a subset of A)
Working with data files (in data science) requires logicaal thinking. Set theory is a good exercise in logical thinking!
Exercise 4
Consider the following sets.
- A = {x | x is an even number and x<20; x is a positive natural number}
- B = {x | x is a multiple of 3 and x<20; x is a positive natural number}
- C = {x | x is an odd numbber and x<20; x is a positive natural number}
This looks cryptic. Set A, for example, reads like the set of numbers x conditioned by the following rules: x is a positive natural number (1, 2, 3 to infinity), smaller than 20 and divisible by 2. We can enumerate these numbers easily: 2, 4, 6 .. up to 18.
Set B then is 3, 6, 9 .. up to 18).
Set C is 1, 3, 5 .. up to 19.
Determine:
- A \(\cap\) B
- A \(\cap\) B \(\cap\) C
- \(\cup\) B
Again, create the sets in R and use the proper functions to get the solutions!
Answers:
- {6, 12, 18}
- 0 (empty set)
- {2,3,4,6,8,9,10,11,12,14,15,16,18}
LS0tDQp0aXRsZTogIk1hdGhlbWF0aWNzIGZvciBTdGF0aXN0aWNzIC0gQSBSZWZyZXNoZXIiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNClRoaXMgcmVmcmVzaGVyIGNvdXJzZSBpcyBiYXNlZCBvbjogIA0KDQo+IEZyYW5rZW4sIFcuTS4gJiBCb3V0cywgUi5BLiAoMjAwMikuICpXaXNrdW5kZSB2b29yIHN0YXRpc3RpZWs6IEVlbiB2b29yYmVyZWlkaW5nKi4gQnVzc3VtLCBOZXRoZXJsYW5kczogVWl0Z2V2ZXJpaiBDb3V0aW5obyAgDQoNClRoZSByZWZyZXNoZXIgY291cnNlIGNvbnNpc3RzIG9mIGZpdmUgY2hhcHRlcnM6ICANCg0KMS4gU2V0IFRoZW9yeSAgDQoyLiBNYXRoZW1hdGljYWwgT3BlcmF0aW9ucyAgDQozLiBFcXVhdGlvbnMgIA0KNC4gRnVuY3Rpb25zICYgR3JhcGhzDQo1LiBTdGF0aXN0aWNhbCBPcGVyYXRpb25zICANCg0KVGhpcyBtb2R1bGUgaWxsdXN0cmF0ZXMgYWxsIHRvcGljcyBpbiB0aGUgdGV4dGJvb2ssIGFuZCBob3cgdGhleSBjYW4gYmUgaW1wbGVtZW50ZWQgaW4gUi4gIA0KT24gdGhlIGZseSwgeW91IHdpbGwgbGVhcm4gbWFueSBvZiB0aGUgYmFzaWMgZnVuY3Rpb25zIG9mIFIuICAgDQoNCiMgMS4gU2V0cw0KDQojIyAxLjEgU2V0cyBpbiBNYXRoZW1hdGljcw0KDQoqIEEgc2V0IGlzIGEgd2VsbC1kZWZpbmVkIGNvbGxlY3Rpb24gb2Ygb2JqZWN0cy4gDQoqIEVhY2ggb2JqZWN0IGluIGEgc2V0IGlzIGNhbGxlZCBhbiBlbGVtZW50IG9mIHRoZSBzZXQuIA0KKiBUd28gc2V0cyBhcmUgZXF1YWwgaWYgdGhleSBoYXZlIGV4YWN0bHkgdGhlIHNhbWUgZWxlbWVudHMgaW4gdGhlbS4gDQoqIEEgc2V0IHRoYXQgY29udGFpbnMgbm8gZWxlbWVudHMgaXMgY2FsbGVkIGEgbnVsbCBzZXQgb3IgYW4gZW1wdHkgc2V0LiANCiogSWYgZXZlcnkgZWxlbWVudCBpbiBTZXQgQSBpcyBhbHNvIGluIFNldCBCLCB0aGVuIFNldCBBIGlzIGEgc3Vic2V0IG9mIFNldCBCLg0KDQo+IEV4YW1wbGU6IGlmIEEgaXMgdGhlIHNldCBvZiBmaXJzdCBmaXZlIGNoYXJhY2h0ZXJzIG9mIHRoZSBhbHBoYWJldCwgdGhlbjogQSA9IHthLGIsYyxkLGV9DQoNCkluIFIgd2UgY2FuIGNyZWF0ZSB0aGlzIHNldCBhcyBmb2xsb3dzLiAgIA0KDQpXZSBjYW4gZGVjaWRlIG9uIHRoZSBuYW1lIG91cnNlbHZlcy4gSGVyZSwgd2UgdXNlICJTZXRBIi4gIA0KDQpUaGUgdHdvIGNoYXJhY3RlcnMgIjwiIGFuZCAiLSIgbG9vayBsaWtlIGFuIGFycm93LiANCldlIGNvbWJpbmUgdGhlIGNoYXJhY3RlcnMgb24gdGhlIHJpZ2h0IG9mIHRoZSBhcnJvdywgdXNpbmcgdGhlIGMoKSBmdW5jdGlvbg0KV2UgdGhlbiBhc3NpZ24gdGhpcyBjb21iaW5hdGlvbiB0byBTZXRBLCBvbiB0aGUgbGVmdGhhbmQgc2lkZS4NCg0KYGBge3J9DQpTZXRBIDwtIGMoImEiLCJiIiwiYyIsImQiLCJlIikNCiJhIiAlaW4lIFNldEENCiJmIiAlaW4lIFNldEENCmBgYA0KDQo+IExpa2V3aXNlLCB3ZSBjYW4gY3JlYXRlIGEgc2V0IG9mIG51bWJlcnMsIGZyb20gMSB0byAxMDAuDQoNCmBgYHtyfQ0KU2V0TiA8LSBjKDE6MTAwKSAgDQpsZW5ndGgoU2V0TikNCjg4ICVpbiUgU2V0TiAgIyBJcyA4OCAgYW4gZWxlbWVudCBvZiBvdXIgc2V0PyBbVFJVRSwgaXQgaXMhXQ0KMTA1ICVpbiVTZXROICAjIElzIDEwNSBhbiBlbGVtZW50IG9mIG91ciBzZXQ/IFtGQUxTRSwgaXQgaXMgbm90IV0gICAgICAgIA0KYGBgDQoNCiMjIDEuMiBSZWxhdGlvbnMgYmV0d2VlbiBzZXRzDQoNCiMjIyBFbXB0eSBTZXQNCg0KQW4gZW1wdHkgc2V0IGlzIGEgc2V0IHdpdGhvdXQgZWxlbWVudHMuDQoNCmBgYHtyfQ0KU2V0RW1wdHkgPC0gTlVMTA0KbGVuZ3RoKFNldEVtcHR5KSAgICMgdGhlIGxlbmd0aCBvZiBhbiBlbXB0eSBzZXQgaXMgemVybw0KIjIiICVpbiUgU2V0RW1wdHkgICMgaXMgMiBwYXJ0IG9mIG91ciBlbXB0eSBzZXQ/DQpgYGANCg0KIyMjIElkZW50aWNhbCBzZXRzDQoNCkFwYXJ0IGZyb20gdGhlIHNlcXVlbmNlIG9mIGVsZW1lbnRzLCB0aGUgdHdvIHNldHMgYmVsb3cgYXJlIGlkZW50aWNhbC4gICANCg0KQWxsIGVsZW1lbnRzIG9mIEEgb2NjdXIgaW4gQiwgYW5kIGFsbCBlbGVtZW50cyBvZiBCIGFyZSBwYXJ0IG9mIEEuDQoNCmBgYHtyfQ0KU2V0QSA8LSBjKCIxIiwiMyIsIjQiLCI2IikNClNldEIgPC0gYygiMSIsIjQiLCI2IiwiMyIpDQpTZXRCICVpbiUgU2V0QQ0KU2V0QSAlaW4lIFNldEINCmBgYA0KDQpXZSBjYW4gdGVzdCB0aGUgZGlmZmVyZW5jZXMgYW5kIHNpbWlsYXJpdGllcyB1c2luZyB0aGUgY29tbWFuZHMgYmVsb3cuIA0KDQpUaGUgb3ZlcmxhcCBiZXR3ZWVuIHR3byBzZXRzLCBpcyBjYWxsZWQgdGhlIGludGVyc2VjdGlvbi4gVGhlIGludGVyc2VjdGlvbiBpcyBncmFwaGljYWxseSBkaXNwbGF5ZWQgYmVsb3cuICANCg0KIVtdKEM6XDA0LiBIQU5cTUlOT1IzRE1cMDEuIE1BVEgmU1RBVFxpbnRlcnNlY3QuanBnKQ0KDQpBbGwgZWxlbWVudHMgdGhhdCBhcmUgcGFydCBvZiB0d28gc2V0cyBjb21iaW5lZCAoZWl0aGVyIEEsIEIgb3IgYm90aCkgZm9ybSB0aGUgdW5pb24gb2YgdGhlIHR3byBzZXRzLiBJbiBhIGdyYXBoOiANCg0KIVtdKEM6XDA0LiBIQU5cTUlOT1IzRE1cMDEuIE1BVEgmU1RBVFx1bmlvbi5qcGcpDQoNClRoZSBpbnRlcnNlY3Qgb2YgdHdvIHNldHMgaXMgd3JpdHRlbiBhcyAkQSBcY2FwIEIkICANCg0KVGhlIHVuaW9uIG9mIHR3byBzZXRzIGlzIHdyaXR0ZW4gYXMgJEEgXGN1cCBCJCAgDQoNCmBgYHtyfQ0Kc2V0ZGlmZihTZXRBLCBTZXRCKQ0Kc2V0ZXF1YWwoU2V0QSwgU2V0QikNCmludGVyc2VjdChTZXRBLCBTZXRCKQ0KYGBgDQoNCkxldCdzIGxvb2sgYXQgc29tZSBvdGhlciBleGFtcGxlcy4gIA0KDQpUaGUgdHdvIHNldHMgYmVsb3cgYXJlIG5vdCBpZGVudGljYWwsIGFuZCBjb250YWluIGR1cGxpY2F0ZXMuIA0KDQpGcm9tIHRoZSByZXN1bHQgd2Ugc2VlIHRoYXQgZHVwbGljYXRlcyBhcmUgcmVtb3ZlZCENCg0KYGBge3J9DQpTZXRBIDwtIGMoImEiLCJhIiwiYiIsImMiLCJkIikNClNldEIgPC0gYygiYyIsImMiLCJkIiwiZSIpDQp1bmlvbihTZXRBLCBTZXRCKQ0KYGBgDQoNCmBgYHtyfQ0KKFNldEEpDQp1bmlxdWUoU2V0QSkgICAgICAgICAgICAgIyBSZW1vdmVzIGR1cGxpY2F0ZXMNCmR1cGxpY2F0ZWQoU2V0QSkNClNldEFbIWR1cGxpY2F0ZWQoU2V0QSldICAjIFJlbW92ZXMgZHVwbGljYXRlcywgaW4gYSBzbGlnaHRseSBtb3JlIGNvbXBsaWNhdGVkIHdheQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0ZGlmZihTZXRBLCBTZXRCKSAgIyBhIGVuIGIgb2NjdXIgaW4gQSwgYnV0IG5vdCBpbiBCDQpzZXRkaWZmKFNldEIsIFNldEEpICAjIGUgb2NjdXJzIGluIEIsIGJ1dCBub3QgaW4gQQ0KYGBgDQoNClRvIGNoYWxsZW5nZSB5b3VyIHNraWxscywgd2UgY2FuIGRlZmluZSB0aGUgdW5pb24gb2Ygc2V0cyBBIGFuZCBCIGFzIGZvbGxvd3M6ICANCg0KKiBFbGVtZW50cyB1bmlxdWUgdG8gQSwgcGx1czogIA0KKiBFbGVtZW50cyB1bmlxdWUgdG8gQiwgcGx1czogIA0KKiBFbGVtZW50cyBpbiB0aGUgaW50ZXJlc2VjdGlvbiBvZiBBIGFuZCBCLiAgDQoNCldlIGNhbiBzb3J0IHRoZSBjb21iaW5hdGlvbiBvZiB0aGVzZSB0aHJlZSBwYXJ0cyBhbHBoYWJldGljYWxseSwgYW5kIGNoZWNrIGlmIGluZGVlZCB0aGUgcmVzdWx0IGlzIGVxdWFsIHRvIHdoYXQgd2UgaGF2ZSBkZWZpbmVkIGFzIHRoZSB1bmlvbjoNCg0KYGBge3J9DQpzb3J0KGMoc2V0ZGlmZihTZXRBLCBTZXRCKSwgc2V0ZGlmZihTZXRCLCBTZXRBKSwgaW50ZXJzZWN0KFNldEIsIFNldEEpKSkNCmFsbChzb3J0KGMoc2V0ZGlmZihTZXRBLCBTZXRCKSwgc2V0ZGlmZihTZXRCLCBTZXRBKSwgaW50ZXJzZWN0KFNldEIsIFNldEEpKSkgPT0gdW5pb24oU2V0QSwgU2V0QikpDQpgYGANCg0KVGhlIGFsbCgpIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIHNlZSBpZiBhbGwgZWxlbWVudHMgb2Ygb25lIHNldCwgYXJlIGNvbnRhaW5lZCBieSB0aGUgb3RoZXIuDQoNCkFsbCBlbGVtZW50cyBvZiBzZXQgQyAoYSBkdXBsaWNhdGVkIGVsZW1lbnQgImEiICkgYXJlIHBhcnQgb2YgRC4NCg0KT25lIGVsZW1lbnQgb2YgRCwgaXMgbm90IGNvbnRhaW5lZCBpbiBBLg0KDQpgYGB7cn0NClNldEMgPC0gYygiYSIsImEiKTsgU2V0RCA8LSBjKCJhIiwiYiIpDQpTZXRDICVpbiUgU2V0RA0KYWxsKFNldEMgJWluJSBTZXREKQ0KU2V0RCAlaW4lIFNldEMNCmFsbChTZXREICVpbiUgU2V0QykNCmBgYA0KDQojIFNldHMgb2YgTnVtYmVycw0KDQpgYGB7cn0NCihTZXROIDwtIGMoLTEwOisxMCkpICMgUHV0dGluZyB0aGUgZXhwcmVzc2lvbiBiZXR3ZWVuIGJyYWNrZXRzLCBwcmludHMgdGhlIG9iamVjdCBpbiB0aGUgY29uc29sZSENCmxlbmd0aChTZXROKQ0KU2V0TlszXQ0KU2V0TlszXSA8IFNldE5bNF0NCmBgYA0KDQojIEV4ZXJjaXNlcw0KDQojIyBFeGVyY2lzZSAxDQoNCkdpdmVuIGFyZSB0aGUgZm9sbG93aW5nIHNldHM6IA0KDQpBID0gezIsNCw2LDgsMTAsMTIsMTQsMTZ9DQpCID0gezEsMiwzLDQsNSw2LDd9DQpDID0gezMsNiw5LDEyLDE1LDE4fSANCg0KYS4gQSAkXGNhcCQgQiA9IChJbnRlcnNlY3Rpb24gb2YgQSBhbmQgQikNCmIuIEEgJFxjYXAkIEMgPSANCmMuIEIgJFxjYXAkIEMgPSANCmQuIEEgJFxjYXAkIEIgJFxjYXAkIEMgPSANCmUuIEEgJFxjdXAkIEIgPSAoVW5pb24gb2YgQSBhbmQgQikNCmYuIEEgJFxjYXAkIEIgJFxjYXAkIEMgPQ0KDQojIFNvbHV0aW9uDQoNCkxldCdzIGZpcnN0IGNyZWF0ZSB0aGUgc2V0cyBBLCBCIGFuZCBDDQoNCmBgYHtyfQ0KQSA8LSBzZXEoMiwgMTYsIDIpDQpCIDwtIGMoMTo3KQ0KQyA8LSBzZXEoMywgMTgsIDMpDQpBOyBCOyBDICAjIFByaW50cyB0aGUgdGhyZWUgc2V0cw0KYGBgDQoNCmEuIEEgJFxjYXAkIEINCmBgYHtyfQ0KKGludGVyc2VjdChBLCBCKSkgIyBQdXR0aW5nIHRoZSBjb21tYW5kIGJldHdlZW4gYnJhY2tldHMsIHByaW50cyB0aGUgcmVzdWx0DQpgYGANCmIuIEEgJFxjYXAkIEMNCmBgYHtyfQ0KKGludGVyc2VjdChBLCBDKSkgDQpgYGANCg0KYy4gQiAkXGNhcCQgQw0KYGBge3J9DQooaW50ZXJzZWN0KEIsIEMpKSANCmBgYA0KDQpkLiBBICRcY2FwJCBCICRcY2FwJCBDDQoNCmludGVyc2VjdChBLCBCLCBDKSB3aWxsIGdpdmUgYW4gZXJyb3IgbWVzc2FnZSwgYXMgaW50ZXJzZWN0KCkgd29ya3Mgb24gdHdvIHNldHMhICANCg0Kd2UgY2FuIGRvIGl0IGluIDIgc3RlcHM7IGZpcnN0LCB3ZSBkZXRlcm1pbmUgdGhlIGludGVyc2VjdGlvbiBvZiBBIGFuZCBCOyANCmZvbGxvd2VkIGJ5IHRoZSBpbnRlcnNlY3Rpb24gb2YgdGhhdCByZXN1bHQgd2l0aCBDIQ0KDQpZb3UgY2FuIGltYWdpbmUgdGhhdCB0aGlzIGlzIGRvYWJsZSBmb3IgdGhyZWUgc2V0cy4NCkJ1dCBpZiB3ZSB3ZXJlIHRvIGZpbmQgdGhlIGludGVyc2VjdGlvbiBiZXR3ZWVuIG1hbnkgc2V0cywgdGhlbiB0aGUgZXhwZXJlc3Npb24gd291bGQgZ2V0IHZlcnkgbGVuZ3RoeSENCkEgYmV0dGVyIGFsdGVybmF0aXZlIGlzIHRvIHVzZSB0aGUgUmVkdWNlKCkgZnVuY3Rpb24hDQoNCk9mdGVuLCB5b3UgY2FuIGZpbmQgc29sdXRpb25zIHRvIHRoaXMga2luZCBvZiBjaGFsbGVuZ2luZyBieSBnb29nbGluZy4NClRoZSBSZWR1Y2UoKSBmdW5jdGlvbiwgZm9yIGV4YW1wbGUsIHdlIGZvdW5kIG9uIFtsaW5rXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9zZXQtb3BlcmF0aW9ucy1vbi1tb3JlLXRoYW4tdHdvLXNldHMtaW4tci8pDQoNCmBgYHtyfQ0KaW50ZXJzZWN0KGludGVyc2VjdChBLEIpLEMpDQpSZWR1Y2UoaW50ZXJzZWN0LCBsaXN0KEEsQixDKSkNCmBgYA0KDQplLiBBICRcY3VwJCBCDQpgYGB7cn0NCnNvcnQoKHVuaW9uKEEsIEIpKSkNCmBgYA0KDQpmLiBBICRcY2FwJCBCICRcY2FwJCBDDQpgYGB7cn0NCnNvcnQoUmVkdWNlKHVuaW9uLCBsaXN0KEEsQiwgQykpKQ0KYGBgDQoNCiMgQXJlIHlvdSBpbiBmb3IgYSBjaGFsbGVuZ2U/DQoNCiMjIFdoaWNoIGVsZW1lbnRzIGFyZSBpbiBpbnRlcnNlY3Rpb25zIG9mIHNldHMsIGJ1dCBub3QgaW4gYWxsIHNldHM/DQoNCmBgYHtyfQ0KKEFCIDwtIGludGVyc2VjdChBLEIpKSAjIFBhaXJ3aXNlIGludGVyc2VjdGlvbiBBIGFuZCBCOyBzdG9yZWQgYXMgc2V0IEFCDQooQUMgPC0gaW50ZXJzZWN0KEEsQykpICMgSWQsIGZvciBBIGFuZCBDDQooQkMgPC0gaW50ZXJzZWN0KEIsQykpICMgSWQsIGZvciBCIGFuZCBDDQooc29ydChwYWlycyA8LSBSZWR1Y2UodW5pb24sIGxpc3QoQUIsQUMsQkMpKSkpICMgQWxsIHBhaXJ3aXNlIGludGVyc2VjdGlvbiBjb21iaW5lZCwgc3RvcmVkIGFzICJwYWlycyINCih0cmlvcyA8LSBSZWR1Y2UoaW50ZXJzZWN0LCBsaXN0KEEsQixDKSkpICMgRWxlbWVudHMgcHJlc2VudCBpbiB0aGUgaW50ZXJzZWN0aW9uIG9mIGFsbCB0aHJlZSBzZXRzDQpzb3J0KHNldGRpZmYocGFpcnMsIHRyaW9zKSkgIyBFbGVtZW50cyBpbiBwYWlycywgYnV0IG5vdCBpbiB0cmlvcw0KYGBgDQoNCllvdSBzZWUgdGhhdCBlbGVtZW50cyAyLCAzLCA0IGFuZCAxMiBhcmUgaW4gcGFpcndpc2UgaW50ZXJzZWN0aW9ucywgYnV0IG5vdCBpbiB0aGUgdGhyZWUtd2F5IGludGVyc2VjdGlvbiENCg0KR3JhcGhpY2FsbHksIGp1c3QgdG8gY29udmluY2UgeW91Og0KDQohW10oQzpcMDQuIEhBTlxNSU5PUjNETVwwMS4gTUFUSCZTVEFUXGNoYWxsZW5nZS5qcGcpDQoNCiMgRXhlcmNpc2UgMg0KDQpBcmUgdGhlIGZvbGxvd2luZyBzdGF0ZW1lbnRzIHRydWUgb3IgZmFsc2U/ICANCg0KYS4gezIsNH0gJFxzdWJzZXQkIChBICRcY2FwJCBCKSAgDQpiLiA2ICRcaW4kIChBICRcY2FwJCBCICRcY2FwJCBDKSAgDQpjLiB7Nn0gJFxzdWJzZXQkIChBICRcY2FwJCBCKSAgDQpkLiAoQSAkXGNhcCQgQikgJFxzdWJzZXQkIChBICRcY3VwJCBCKSAgDQoNCiMjIFNvbHV0aW9ucyAgDQoNCmEuIHsyLDR9ICRcc3Vic2V0JCAoQSAkXGNhcCQgQikgDQoNCmBgYHtyfQ0KU2V0VGVzdCA8LSBjKDIsNCkNCmFsbChTZXRUZXN0ICVpbiUgQSkNCmBgYA0KDQpiLiA2ICRcaW4kIChBICRcY2FwJCBCICRcY2FwJCBDKSAgDQpjLiB7Nn0gJFxzdWJzZXQkIChBICRcY2FwJCBCKSAgDQoNCmBgYHtyfQ0KIyBJbiB0aGUgZm9ybXVsYXRpb24gb2YgYy4NCg0KU2V0VGVzdCA8LSBjKDYpDQp0cmlvcw0KYWxsKFNldFRlc3QgJWluJSB0cmlvcykgIyBSZW1lbWJlciB3ZSBzdG9yZWQgdGhlIGludGVyc2VjdCBhcyAidHJpb3MiIQ0KDQojIEFsdGVybmF0aXZlbHkgKGFzIGluIGIuKSwgZGVmaW5lIDYgYXMgYSBzaW5nbGUgZWxlbWVudCByYXRoZXIgdGhhbiBhIHNldCBvZiBvbmUgZWxlbWVudA0KDQo2ICVpbiUgdHJpb3MNCg0KIyBOb3RlIHRoYXQgYi4gYW5kIGMuIGFyZSBiYXNpY2FsbHkgdGhlIHNhbWUhDQpgYGANCg0KZC4gKEEgJFxjYXAkIEIpICRcc3Vic2V0JCAoQSAkXGN1cCQgQikgDQoNClRoaXMgaXMgdHJ1ZSBpbiBnZW5lcmFsLiBFbGVtZW50cyBpbiB0aGUgaW50ZXJzZWN0aW9uIG9mIEEgYW5kIEIsIGFyZSBieSBkZWZpbml0aW9uIHBhcnQgb2YgYm90aCBBIGFuZCBCLiBUaGUgaW50ZXJzZWN0aW9uIGlzIHRoZXJlZm9yZSBhIHN1YnNldCBvZiBhbGwgZWxlbWVudHMgaW4gQSBvciBCIQ0KDQpKdXN0IHRvIHRyYWluIHRoZSBmb3JtdWxhdGlvbiBvZiB0aGlzIGV4ZXJjaXNlIGluIFI6DQoNCmBgYHtyfQ0KYWxsKGludGVyc2VjdChBLCBCKSAlaW4lIHVuaW9uKEEsIEIpKQ0KYGBgDQoNCiMgRXhlcmNpc2UgMw0KDQpBID0gezEsMiwzLDQsNSw2fQ0KQiA9IHs1LDZ9DQpDID0gezEsMiw1LDZ9DQpEID0gezIsMyw0fQ0KRSA9IHsyLDMsNCw1fQ0KDQpDb21wbGV0ZSB0aGUgc3RhdGVtZW50IHdpdGggb25lIG9mIHRoZSBzeW1ib2xzOiAgDQoNCiogJFxpbiQgKGVsZW1lbnQgb2YpICANCiogJFxub3RpbiQgKG5vdCBhbiBlbGVtZW50IG9mKSAgDQoqICRcc3Vic2V0JCAoc3Vic2V0IG9mKSAgDQoqICRcc3Vwc2V0JCAoc3VwZXJzZXQ7IGlmIEEgaXMgc3Vic2V0IG9mIEIsIHRoZW4gQiBpcyBzdXBlcnNldCBvZiBBKSAgDQoqICRcY2FwJCAoaW50ZXJzZWN0aW9uKSAgICANCiogJFxjdXAkICANCg0KYS4gQi4uLi5DICANCmIuIEIuLi4uQyA9IEIgIA0KYy4gQi4uLi5DID0gQyAgDQpkLiBCLi4uLkQgPSAwIChlbXB0eSBzZXQpICANCmUuIEMuLi4uRCA9IEEgIA0KZi4gRC4uLi5FID0gRCAgDQpnLiA0Li4uLkMgJFxjYXAkIEIgIA0KaC4gRC4uLi5FLi4uLkEgIA0KDQojIyBTb2x1dGlvbnMNCg0KVXNlIHJlYXNvbiB0byBhbnN3ZXIgZWFjaCBvZiB0aGUgcXVlc3Rpb25zIQ0KQXMgYW4gYWRkaXRpb25hbCBjaGFsbGVuZ2UsIGZvcm11bGF0ZSB0aGUgc2V0cyBpbiBSLCBhbmQgdXNlIGFueSBvZiB0aGUgY29tbWFuZHMgaW50cm9kdWNlZCBpbiB0aGlzIGNoYXB0ZXIgdG8gY2hlY2sgeW91ciBhbnN3ZXIhDQoNCmEuICRcc3Vic2V0JCAoQiBpcyBvYnZpb3VzbHkgYSBzdWJzZXQgb2YgQywgYXMgYWxsIGVsZW1lbnRzIG9mIEIgYXJlIGFsc28gaW4gQykgICANCmIuICRcY2FwJCAodGhlIGludGVyc2VjdCBvZiBCIGFuZCBDLCBpcyBlcXVhbCB0byBCLCBhcyBCIGlzIGEgc3Vic2V0IG9mIEIpIA0KYy4gJFxjdXAkICh0aGUgY29tYmluZWQgZWxlbWVudHMgb2YgQiBhbmQgQywgYXJlIGVxdWFsIHRvIEMsIGFzIEMgaXMgYSBzdXBlcnNldCBvZiBCKSANCmQuICRcY2FwJCAoYXMgQiBhbmQgRCBoYXZlIG5vIGVsZW1lbnRzIGluIGNvbW1vbiwgdGhlIGludGVyZXNlY3QgaXMgdGhlIGVtcHR5IHNldCkgIA0KZS4gJFxjdXAkIChhbGwgZWxlbWVudHMgb2YgQyBhbmQgRCBjb21iaW5lZCwgbWF0Y2ggQSkgIA0KZi4gJFxjYXAkIG9yICRcc3Vic2V0JCAgDQpnLiAkXG5vdGluJCAoNCBpcyBub3QgcGFydCBvZiB0aGUgaW50ZXJzZWN0aW9uIG9mIEIgYW5kIEM7IGl0IGlzIG5vdCBldmVuIHBhcnQgb2YgdGhlIHVuaW9uIG9mIEIgYW5kIEMpICANCmguICRcc3Vic2V0JCAoRCBpcyBhIHN1YnNldCBvZiBFLCB3aGljaCBpbiB0dXJuIGlzIGEgc3Vic2V0IG9mIEE7IHlvdSBjYW4gY29uY2x1ZGUgdGhhdCB0aGVyZWZvcmUgRCBpcyBhIHN1YnNldCBvZiBBKSAgDQoNCldvcmtpbmcgd2l0aCBkYXRhIGZpbGVzIChpbiBkYXRhIHNjaWVuY2UpIHJlcXVpcmVzIGxvZ2ljYWFsIHRoaW5raW5nLiBTZXQgdGhlb3J5IGlzIGEgZ29vZCBleGVyY2lzZSBpbiBsb2dpY2FsIHRoaW5raW5nIQ0KDQojIEV4ZXJjaXNlIDQgDQoNCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgc2V0cy4NCg0KKiBBID0ge3ggfCB4IGlzIGFuIGV2ZW4gbnVtYmVyIGFuZCB4PDIwOyB4IGlzIGEgcG9zaXRpdmUgbmF0dXJhbCBudW1iZXJ9ICANCiogQiA9IHt4IHwgeCBpcyBhIG11bHRpcGxlIG9mIDMgYW5kIHg8MjA7IHggaXMgYSBwb3NpdGl2ZSBuYXR1cmFsIG51bWJlcn0gIA0KKiBDID0ge3ggfCB4IGlzIGFuIG9kZCBudW1iYmVyIGFuZCB4PDIwOyB4IGlzIGEgcG9zaXRpdmUgbmF0dXJhbCBudW1iZXJ9ICANCg0KVGhpcyBsb29rcyBjcnlwdGljLiBTZXQgQSwgZm9yIGV4YW1wbGUsIHJlYWRzIGxpa2UgdGhlIHNldCBvZiBudW1iZXJzIHggY29uZGl0aW9uZWQgYnkgdGhlIGZvbGxvd2luZyBydWxlczogeCBpcyBhIHBvc2l0aXZlIG5hdHVyYWwgbnVtYmVyICgxLCAyLCAzIHRvIGluZmluaXR5KSwgc21hbGxlciB0aGFuIDIwIGFuZCBkaXZpc2libGUgYnkgMi4gV2UgY2FuIGVudW1lcmF0ZSB0aGVzZSBudW1iZXJzIGVhc2lseTogMiwgNCwgNiAuLiB1cCB0byAxOC4gDQoNClNldCBCIHRoZW4gaXMgMywgNiwgOSAuLiB1cCB0byAxOCkuDQoNClNldCBDIGlzIDEsIDMsIDUgLi4gdXAgdG8gMTkuIA0KDQpEZXRlcm1pbmU6IA0KDQphLiBBICRcY2FwJCBCICANCmIuIEEgJFxjYXAkIEIgJFxjYXAkIEMgIA0KYy4gJFxjdXAkIEIgIA0KDQpBZ2FpbiwgY3JlYXRlIHRoZSBzZXRzIGluIFIgYW5kIHVzZSB0aGUgcHJvcGVyIGZ1bmN0aW9ucyB0byBnZXQgdGhlIHNvbHV0aW9ucyEgDQoNCkFuc3dlcnM6IA0KDQphLiB7NiwgMTIsIDE4fSAgDQpiLiAwIChlbXB0eSBzZXQpICANCmMuIHsyLDMsNCw2LDgsOSwxMCwxMSwxMiwxNCwxNSwxNiwxOH0gIA0KDQoNCg0KDQoNCg0KDQoNCg==