Introduction
While R support object-oriented programming structures, it is not an object-oriented languages like Java or C#. However, when writing large R programs that process structured information objects, it is convenient to represent objects from the domain as objects in R. Being able to persist R objects is a natural extension. By persistence we mean that R objects can be externalized and reconstructed the next time the program runs.
In this tutorial we will look at how to build an object persistence mechanism using XML as the external object format. Lesson 6.124 Externalizing R Objects to SQL demonstrates how to store objects in a relational SQL database. Of course, objects could also be externalized by writing them in the RData format as binary objects.
We will start with a review of the Reference Class System for object-oriented programming and introduce a simple set of R classes representing a subset of some domain ontology.
Reference Class System
The Reference class system in R is similar to the object-oriented programming structures common languages like C++, Java, Python, etc.
Defining a Reference Class
Defining a reference class is done with the setRefClass()
function.
Member variables (attributes) of a reference class must be included as part of the class definition. Member variables of reference class are referred to as fields in R.
The code below defines a class Instructor with three fields: iid (number), name (string/text), and rank (string/text).
Instructor <- setRefClass("Instructor",
fields = list(iid="numeric",
name="character",
rank="character")
)
Instantiating a Reference Class
Instantiation is done with the function setRefClass()
, which returns a generator function that is then used to create objects of that class. In the example below, i is a reference to an instance of the class Instructor.
i <- Instructor(iid = 1, name = 'Jeff Alden', rank = 'FT-Associate')
Printing an object can be done with print()
. By default, print()
lists the name of the class and the values of the fields. Note that print()
can also be overridden.
## Reference class object of class "Instructor"
## Field "iid":
## [1] 1
## Field "name":
## [1] "Jeff Alden"
## Field "rank":
## [1] "FT-Associate"
Accessing Fields
Fields are accessed (and modified) with the $ operator. In the example below, i is an instance of the class Instructor (or, to day it another way, i is an object of type Instructor). To access the value of the name field of the instance i, use i$name
. It can be used as either an rvalue or an lvalue. If used as an lvalue, then the field can be updated. R does not support private or protected fields as is common in Java or C++; a fields are public.
i <- Instructor(iid = 1, name = 'Jeff Alden', rank = 'FT-Associate')
# read a field's value
n <- i$name
# update a field's value
i$name <- 'Jeffrey Alden'
Objects are References
When instantiating a reference object, R generates an internal object and returns a reference to the object (hence the name). So, assigning an object to another variable actually assigns the reference and does not make a copy. In the code below, i1 and i2 are references to the same Instructor object. This is similar to Java but unlike C++ when a copy constructor is defined.
i1 <- Instructor(iid = 1, name = 'Jeff Alden', rank = 'FT-Associate')
i2 <- i1
i2$name <- 'Xin Wang'
print(i1)
## Reference class object of class "Instructor"
## Field "iid":
## [1] 1
## Field "name":
## [1] "Xin Wang"
## Field "rank":
## [1] "FT-Associate"
In the code above, we create a new instance of the class Instructor and get a reference back which we store in the variable i1. We then assign i1 to i2 – but we are actually assigning the reference (or a pointer) to the object. Think of i1 being the location in memory where the object is stored – or think of i1 as the ID of the object. Any modification of the memory through the reference i2 modifies the same object that is pointed to by i1. So, be careful when “copying” objects.
Copying Objects
To make an actual copy, use the inherited method copy()
. This creates a new object that is equivalent of the original object.
i1 <- Instructor(iid = 1, name = 'Jeff Alden', rank = 'FT-Associate')
i2 <- i1$copy()
# modifying i2 does not modify i1
i2$name <- 'Susan Wollaston'
print(i1)
## Reference class object of class "Instructor"
## Field "iid":
## [1] 1
## Field "name":
## [1] "Jeff Alden"
## Field "rank":
## [1] "FT-Associate"
Defining Methods
Methods are defined for a reference class and do not belong to generic functions as in S3 and S4 classes. All reference classes have a set of predefined methods inherited from the superclass envRefClass. This is similar to all Java classes being subclasses of the Object class.
New methods can be added inline in the separate list methods.
Notice the operator <<- used to access fields within a method. Using the simple assignment operator <- would have created a local variable called salary, which would lead to different behavior. Fortunately, R will issue a warning in such a case.
Also note the , after the } to separate the method function definitions.
Instructor <- setRefClass("Instructor",
fields = list(iid="numeric",
name="character",
rank="character",
salary="numeric"
),
methods = list(
getMonthlySalary = function() {
return (salary / 12)
},
applyRaise = function(merit) {
salary <<- salary * (1 + merit)
}
))
Accessing Methods
Methods are accessed the same way as fields – with the $ operator.
i <- Instructor(iid = 2,
name = 'Dua Dipa',
rank = 'T-Assistant',
salary = 128000)
m.bef <- i$getMonthlySalary()
i$applyRaise(0.045)
m.aft <- i$getMonthlySalary()
cat("Salary raised from $", m.bef, "to $", m.aft, "per month")
## Salary raised from $ 10666.67 to $ 11146.67 per month
Inheritance
Inheritance is a key mechanism in object-oriented programming. It allows a programmer to define a new class (subclass or derived class) from an existing classes (superclass or base class). Derived classes can add new fields and methods. All fields and methods of the base class are automatically fields and methods of the derived class. This increases reusability of code and allows programmers to represent domain objects more accurately.
Inheritance is supported in all three class systems but is more like other object-oriented languages in the Reference class system. We will restrict ourselves to this class system.
In the example below, we have a base class Person with three fields and a method. We then define a derived class Instructor which extends Person with two additional fields and two methods by adding the base class Person name to the contains argument.
Person <- setRefClass("Person",
fields = list(pid="numeric",
name="character",
yob = "numeric"),
methods = list(
getAge = function() {
currYear <- as.numeric(format(Sys.time(), "%Y"))
return (currYear - yob)
}
))
Instructor <- setRefClass("Instructor",
contains = "Person",
fields = list(rank="character",
salary="numeric"
),
methods = list(
getMonthlySalary = function() {
return (salary / 12)
},
applyRaise = function(merit) {
salary <<- salary * (1 + merit)
}
))
We can then instantiate the derived class Instructor and find that it has all of the fields and methods of Person in addition to its additional fields and methods.
anInstructor <- Instructor(pid = 100,
name = 'Raj Metha',
rank = 'FT-Full',
yob = 1968,
salary = 182972)
anInstructor$getMonthlySalary()
## [1] 15247.67
## [1] 56
Object Aggregation
In an aggregation relationship between objects, there is a whole/part or container/part hierarchy. In ontology terms, there is a partonomy. In an aggregation, one object “contains” other objects, although the containment does not have to be “physical”, i.e., the part objects do not have to be part of the same memory structure. The whole/part relationship can be by reference where the container object (whole or aggregate) contains references to the contained (part) objects.
Let’s implement the part hierarchy expressed by the UML Class Diagram below:
Member <- setRefClass("Member", fields = list(
mID = "numeric",
name = "character",
yearJoined = "numeric"))
Club <- setRefClass("Club",
fields = list(
name = "character",
yearFounded = "numeric",
maxMemID = "numeric",
members = "list"),
methods = list(
getNumMembers = function() {
return (length(members))
},
addMember = function(m) {
if (is.null(members))
members <<- list(1024)
# add a member ID for the new member
m$mID <- maxMemID + 1
maxMemID <<- maxMemID + 1
# add the member to internal list
members[[length(members)+1]] <<- m
return (1)
}
))
A few noteworthy points about the above code. The field members is a “private” member variable that keeps track of all of the members added to the club. It is an empty list when created, so right before the first member is added it must be allocated.
Now that we have the classes defined, let’s create some sample instances for testing. We won’t set a member ID for new members as those are assigned to them when they get added to the club.
# create a Club
aClub <- Club(name = 'DATA Club',
yearFounded = 2015,
maxMemID = 0)
# create a few members and add them to the club
s <- aClub$addMember(
Member(name = 'Jeff Garol', yearJoined = 2022))
s <- aClub$addMember(
Member(name = 'Ursula Van Leiden', yearJoined = 2022))
s <- aClub$addMember(
Member(name = 'Garrett Liew', yearJoined = 2022))
# number of club members should be correct
aClub$getNumMembers()
## [1] 3
Building XML Documents
Now that we have an understanding of the mechanisms to build R objects, we need to turn our attention to the mechanisms for constructing XML documents from withing R
The functions used to build an in-memory XML document are from the XML package That same library is also used to parse XML elements in an XML document.
Using XML Package
Let’s start with a simple example that externalizes a data frame as XML. It will show us how to use the functions to generate an in-memory XML DOM which can then save to a file.
library(XML)
# Data in 3 columns in a data frame
df <- data.frame(refID = c(100, 200),
upc = c('20190818',
'20190823'),
desc = c('eReader 8',
'USB-C Cable')
)
Now that we have a data frame, let’s externalize the data frame to some XML structure.
# build XML structure
XMLdoc = newXMLDoc()
# root node is <catalog>
rootNode = newXMLNode("catalog", doc = XMLdoc)
# add elements to the XML underneath the root node
mvNode = newXMLNode("catVersion", "1.0.0", parent = rootNode)
# write each of the rows in the data frame
for (i in 1:nrow(df)){
# add a node with an attribute
prodNode = newXMLNode("product",
attrs = c(refID = df$refID[i]),
parent = rootNode)
# add details for each product as child nodes
newXMLNode("upc", df$upc[i], parent = prodNode)
newXMLNode("desc", df$desc[i], parent = prodNode)
}
# add an empty "flag" node
vwNode = newXMLNode("locked", parent = rootNode)
# save XML to a file
saveXML(XMLdoc, file = "prod-catalog.xml")
## [1] "prod-catalog.xml"
Using String Concatenation
A more flexible, often faster, but more error-prone and laborious process is to construct the XML document from concatenated character strings. After all, an XML document is a plain text document.
The code below constructs the same XML document as in the prior section. It continually adds to a character variable using the function paste0()
– the string concatenation function in R.
Note that the \n characters insert newline feeds into the result document for a cleaner look when viewed; they are not strictly necessary from an XML syntactic point of view. Also note the way that quotes are added within quotes. We use single quotes within the XML and double quotes to enclose strings in R – we could have done it the other way around as well; both R and XML accept single and double quotes for string enclosure. The strings are include leading spaces to, again, make the XML more “viewable”.
# start with the preamble and root tag
xml <- "<?xml version='1.0'?>\n\n<catalog>"
# add child element
xml <- paste0(xml, " <catVersion>", "1.0.0", "</catVersion>\n")
# write each of the rows in the data frame
for (i in 1:nrow(df)){
# add a node with an attribute
xml <- paste0(xml, " ",
"<product refID='", df$refID[i], "'>\n")
# add details for each product as child nodes
xml <- paste0(xml, " ",
"<upc>", df$upc[i], "</upc>\n")
xml <- paste0(xml, " ",
"<desc>", df$desc[i], "</desc>\n")
# terminate <product> element
xml <- paste0(xml, " ", "</product>\n")
}
# add an empty "flag" node
xml <- paste0(xml, " <locked />\n")
# terminate the root tag <catalog>
xml <- paste0(xml, "</catalog>")
# save the XML to a file
f <- file("prod-catalog-v2.xml")
writeLines(xml, f)
close(f)
Externalizing an Object
Now that we understand how to generate a DOM, we can externalize an object’s field values. A common approach is to add a method that takes care of the externalization of a class.
Let’s try this by adding a new method to the above class Member called ext2XML() which takes the pre-created XML DOM to which the object should be added as an input argument.
Member$methods(ext2XML = function(parentNode) {
n = newXMLNode("member",
.self$mID,
parent = parentNode)
newXMLNode("name", .self$name,
parent = n)
newXMLNode("year-joined", .self$yearJoined,
parent = n)
return(TRUE)
})
We’ll start by creating a new XML document and then we will call the newly created method ext2XML() on one of the already created instances of the class Member and then display the DOM.
library(XML)
# create new (empty) XML document
xml = newXMLDoc()
# add <members> as the root node
rootNode = newXMLNode("members", doc = xml)
# externalize a member object to the XML
aMember <- Member(name = 'Ozzy Osbourne', yearJoined = 1971)
isSuccess <- aMember$ext2XML(rootNode)
# inspect the XML
print(xml)
## <?xml version="1.0"?>
## <members>
## <member>
## <name>Ozzy Osbourne</name>
## <year-joined>1971</year-joined>
## </member>
## </members>
##
Now that we know how to externalize a single object, we can externalize a container which would then externalize its member elements. Note the ext2xml()
functions in each class. The code below demonstrates this:
Member <- setRefClass("Member",
fields = list(
mID = "numeric",
name = "character",
yearJoined = "numeric"),
methods = list(
ext2xml = function () {
memNode <- newXMLNode("member", parent = rootNode)
newXMLNode("name", .self$name, parent = memNode)
newXMLNode("yearJoined", .self$yearJoined, parent = memNode)
return (memNode)
}
))
Club <- setRefClass("Club",
fields = list(
name = "character",
yearFounded = "numeric",
maxMemID = "numeric",
members = "list"),
methods = list(
getNumMembers = function() {
return (length(members))
},
addMember = function(m) {
if (is.null(members))
members <<- list(1024)
# add a member ID for the new member
m$mID <- maxMemID + 1
maxMemID <<- maxMemID + 1
# add the member to internal list
members[[length(members)+1]] <<- m
return (1)
},
ext2xml = function () {
extXML <- newXMLDoc()
rootNode = newXMLNode("club",
attrs = c(name = .self$name),
doc = extXML)
newXMLNode("yearFounded",
.self$yearFounded, parent = rootNode)
for (m in 1:length(members)) {
aMember <- .self$members[[m]]
addChildren(rootNode, aMember$ext2xml()) }
return (extXML)
}
))
# create a Club
aClub <- Club(name = 'DATA Club',
yearFounded = 2015,
maxMemID = 0)
# create a few members and add them to the club
s <- aClub$addMember(
Member(name = 'Jeff Garol', yearJoined = 2022))
s <- aClub$addMember(
Member(name = 'Ursula Van Leiden', yearJoined = 2022))
s <- aClub$addMember(
Member(name = 'Garrett Liew', yearJoined = 2022))
# number of club members should be correct
aClub$getNumMembers()
## [1] 3
# externalize the Club and its Members to XML
xmlClubDoc <- aClub$ext2xml()
# save XML to a file
saveXML(xmlClubDoc, file = "club.xml")
## [1] "club.xml"
The “club.xml” file looks like this:
<?xml version="1.0"?>
<club name="DATA Club">
<yearFounded>2015</yearFounded>
<member>
<name>Jeff Garol</name>
<yearJoined>2022</yearJoined>
</member>
<member>
<name>Ursula Van Leiden</name>
<yearJoined>2022</yearJoined>
</member>
<member>
<name>Garrett Liew</name>
<yearJoined>2022</yearJoined>
</member>
</club>
LS0tCnRpdGxlOiAiRXh0ZXJuYWxpemluZyBSIE9iamVjdHMgdG8gWE1MIgpwYXJhbXM6CiAgY2F0ZWdvcnk6IDYKICBudW1iZXI6IDEyMwogIHRpbWU6IDQ1CiAgbGV2ZWw6IGludGVybWVkaWF0ZQogIHRhZ3M6ICJyLHhtbCxvYmplY3RzLGV4dGVybmFsaXphdGlvbiIKICBkZXNjcmlwdGlvbjogIlRoaXMgbGVzc29ucyBleHBsYWlucyBob3cgb2JqZWN0cyBidWlsdCB1c2luZyAKICAgICAgICAgICAgICAgIHRoZSByZWZlcmVuY2UgY2xhc3Mgc3lzdGVtIGNhbiBiZSBzdG9yZWQgCiAgICAgICAgICAgICAgICBwZXJzaXN0ZW50bHkgaW4gWE1MLiBTaG93cyBob3cgb2JqZWN0cyBhcmUgCiAgICAgICAgICAgICAgICBpbnN0YW50aWF0ZWQgZnJvbSBYTUwgYW5kIGV4dGVybmFsaXplZCB0byBYTUwKICAgICAgICAgICAgICAgIGZvciBwZXJzaXN0ZW5jZS4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIFByZXJlcXVpc2l0ZXMKClRoaXMgbGVzc29uIGFzc3VtZXMgdGhhdCB0aGUgcmVhZGVyIHVuZGVyc3RhbmRzIHRoZSBiYXNpY3Mgb2YgWE1MIGRvY3VtZW50IHN0cnVjdHVyZXMgYXMgZXhwbGFpbmVkIGluIHRoZXNlIGxlc3NvbnM6CgotICAgWzgwLjEwMSBCYXNpY3Mgb2YgWE1MIGFuZCBEVERdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvODAueG1sL2wtODAtMTAxLWJhc2ljLXhtbC9sLTgwLTEwMS5odG1sKQotICAgWzgwLjEwNiBQYXR0ZXJucyBmb3IgTWFwcGluZyBPbnRvbG9naWVzIHRvIFhNTF0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy84MC54bWwvbC04MC0xMDYtb250b2xvZ3ktdG8teG1sLXBhdHRlcm5zL2wtODAtMTA2Lmh0bWwpCi0gICBbODAuMTEyIFByaW1lciBvbiBYUGF0aCBGdW5jdGlvbnNdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvODAueG1sL2wtODAtMTEyLXhwYXRoLWZ1bmN0aW9ucy9sLTgwLTExMi5odG1sKQotICAgWzYuMzA1IFByb2Nlc3MgWE1MIERPTSB2aWEgWFBhdGggYW5kIE5vZGUgVHJhdmVyc2FsXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTMwNS1wcm9jLXhtbC1kb20teHBhdGgtaW4tci9sLTYtMzA1Lmh0bWwpCi0gICBbNi4xMjIgQ2xhc3NlcywgT2JqZWN0cywgYW5kIE1ldGhvZHMgaW4gUl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0xMjItb2JqZWN0cy1pbi1yL2wtNi0xMjIuaHRtbCkKCiMjIEludHJvZHVjdGlvbgoKV2hpbGUgUiBzdXBwb3J0IG9iamVjdC1vcmllbnRlZCBwcm9ncmFtbWluZyBzdHJ1Y3R1cmVzLCBpdCBpcyBub3QgYW4gb2JqZWN0LW9yaWVudGVkIGxhbmd1YWdlcyBsaWtlIEphdmEgb3IgQyMuIEhvd2V2ZXIsIHdoZW4gd3JpdGluZyBsYXJnZSBSIHByb2dyYW1zIHRoYXQgcHJvY2VzcyBzdHJ1Y3R1cmVkIGluZm9ybWF0aW9uIG9iamVjdHMsIGl0IGlzIGNvbnZlbmllbnQgdG8gcmVwcmVzZW50IG9iamVjdHMgZnJvbSB0aGUgZG9tYWluIGFzIG9iamVjdHMgaW4gUi4gQmVpbmcgYWJsZSB0byBwZXJzaXN0IFIgb2JqZWN0cyBpcyBhIG5hdHVyYWwgZXh0ZW5zaW9uLiBCeSBwZXJzaXN0ZW5jZSB3ZSBtZWFuIHRoYXQgUiBvYmplY3RzIGNhbiBiZSBleHRlcm5hbGl6ZWQgYW5kIHJlY29uc3RydWN0ZWQgdGhlIG5leHQgdGltZSB0aGUgcHJvZ3JhbSBydW5zLgoKSW4gdGhpcyB0dXRvcmlhbCB3ZSB3aWxsIGxvb2sgYXQgaG93IHRvIGJ1aWxkIGFuIG9iamVjdCBwZXJzaXN0ZW5jZSBtZWNoYW5pc20gdXNpbmcgWE1MIGFzIHRoZSBleHRlcm5hbCBvYmplY3QgZm9ybWF0LiBMZXNzb24gNi4xMjQgRXh0ZXJuYWxpemluZyBSIE9iamVjdHMgdG8gU1FMIGRlbW9uc3RyYXRlcyBob3cgdG8gc3RvcmUgb2JqZWN0cyBpbiBhIHJlbGF0aW9uYWwgU1FMIGRhdGFiYXNlLiBPZiBjb3Vyc2UsIG9iamVjdHMgY291bGQgYWxzbyBiZSBleHRlcm5hbGl6ZWQgYnkgd3JpdGluZyB0aGVtIGluIHRoZSAqUkRhdGEqIGZvcm1hdCBhcyBiaW5hcnkgb2JqZWN0cy4KCldlIHdpbGwgc3RhcnQgd2l0aCBhIHJldmlldyBvZiB0aGUgUmVmZXJlbmNlIENsYXNzIFN5c3RlbSBmb3Igb2JqZWN0LW9yaWVudGVkIHByb2dyYW1taW5nIGFuZCBpbnRyb2R1Y2UgYSBzaW1wbGUgc2V0IG9mIFIgY2xhc3NlcyByZXByZXNlbnRpbmcgYSBzdWJzZXQgb2Ygc29tZSBkb21haW4gb250b2xvZ3kuCgojIyBSZWZlcmVuY2UgQ2xhc3MgU3lzdGVtCgpUaGUgKlJlZmVyZW5jZSogY2xhc3Mgc3lzdGVtIGluIFIgaXMgc2ltaWxhciB0byB0aGUgb2JqZWN0LW9yaWVudGVkIHByb2dyYW1taW5nIHN0cnVjdHVyZXMgY29tbW9uIGxhbmd1YWdlcyBsaWtlIEMrKywgSmF2YSwgUHl0aG9uLCAqZXRjKi4KCiMjIyBEZWZpbmluZyBhIFJlZmVyZW5jZSBDbGFzcwoKRGVmaW5pbmcgYSByZWZlcmVuY2UgY2xhc3MgaXMgZG9uZSB3aXRoIHRoZSA8Y29kZT5zZXRSZWZDbGFzcygpPC9jb2RlPiBmdW5jdGlvbi4KCk1lbWJlciB2YXJpYWJsZXMgKGF0dHJpYnV0ZXMpIG9mIGEgcmVmZXJlbmNlIGNsYXNzIG11c3QgYmUgaW5jbHVkZWQgYXMgcGFydCBvZiB0aGUgY2xhc3MgZGVmaW5pdGlvbi4gTWVtYmVyIHZhcmlhYmxlcyBvZiByZWZlcmVuY2UgY2xhc3MgYXJlIHJlZmVycmVkIHRvIGFzIGZpZWxkcyBpbiBSLgoKVGhlIGNvZGUgYmVsb3cgZGVmaW5lcyBhIGNsYXNzICpJbnN0cnVjdG9yKiB3aXRoIHRocmVlIGZpZWxkczogKmlpZCogKG51bWJlciksICpuYW1lKiAoc3RyaW5nL3RleHQpLCBhbmQgKnJhbmsqIChzdHJpbmcvdGV4dCkuCgpgYGB7ciBkZWZSZWZlcmVuY2VDbGFzc30KSW5zdHJ1Y3RvciA8LSBzZXRSZWZDbGFzcygiSW5zdHJ1Y3RvciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGZpZWxkcyA9IGxpc3QoaWlkPSJudW1lcmljIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJjaGFyYWN0ZXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbms9ImNoYXJhY3RlciIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCiMjIyBJbnN0YW50aWF0aW5nIGEgUmVmZXJlbmNlIENsYXNzCgpJbnN0YW50aWF0aW9uIGlzIGRvbmUgd2l0aCB0aGUgZnVuY3Rpb24gPGNvZGU+c2V0UmVmQ2xhc3MoKTwvY29kZT4sIHdoaWNoIHJldHVybnMgYSBnZW5lcmF0b3IgZnVuY3Rpb24gdGhhdCBpcyB0aGVuIHVzZWQgdG8gY3JlYXRlIG9iamVjdHMgb2YgdGhhdCBjbGFzcy4gSW4gdGhlIGV4YW1wbGUgYmVsb3csICppKiBpcyBhIHJlZmVyZW5jZSB0byBhbiBpbnN0YW5jZSBvZiB0aGUgY2xhc3MgKkluc3RydWN0b3IuKgoKYGBge3IgY3JlYXRlSW5zdFJlZkNsYXNzfQppIDwtIEluc3RydWN0b3IoaWlkID0gMSwgbmFtZSA9ICdKZWZmIEFsZGVuJywgcmFuayA9ICdGVC1Bc3NvY2lhdGUnKQpgYGAKClByaW50aW5nIGFuIG9iamVjdCBjYW4gYmUgZG9uZSB3aXRoIDxjb2RlPnByaW50KCk8L2NvZGU+LiBCeSBkZWZhdWx0LCA8Y29kZT5wcmludCgpPC9jb2RlPiBsaXN0cyB0aGUgbmFtZSBvZiB0aGUgY2xhc3MgYW5kIHRoZSB2YWx1ZXMgb2YgdGhlIGZpZWxkcy4gTm90ZSB0aGF0IDxjb2RlPnByaW50KCk8L2NvZGU+IGNhbiBhbHNvIGJlIG92ZXJyaWRkZW4uCgpgYGB7ciBwcmludFJlZk9ian0KcHJpbnQoaSkKYGBgCgojIyMgQWNjZXNzaW5nIEZpZWxkcwoKRmllbGRzIGFyZSBhY2Nlc3NlZCAoYW5kIG1vZGlmaWVkKSB3aXRoIHRoZSAqXCQqIG9wZXJhdG9yLiBJbiB0aGUgZXhhbXBsZSBiZWxvdywgKmkqIGlzIGFuIGluc3RhbmNlIG9mIHRoZSBjbGFzcyAqSW5zdHJ1Y3RvciogKG9yLCB0byBkYXkgaXQgYW5vdGhlciB3YXksICppKiBpcyBhbiBvYmplY3Qgb2YgdHlwZSAqSW5zdHJ1Y3RvciopLiBUbyBhY2Nlc3MgdGhlIHZhbHVlIG9mIHRoZSBuYW1lIGZpZWxkIG9mIHRoZSBpbnN0YW5jZSAqaSosIHVzZSA8Y29kZT5pXCRuYW1lPC9jb2RlPi4gSXQgY2FuIGJlIHVzZWQgYXMgZWl0aGVyIGFuICpydmFsdWUqIG9yIGFuICpsdmFsdWUuKiBJZiB1c2VkIGFzIGFuICpsdmFsdWUqLCB0aGVuIHRoZSBmaWVsZCBjYW4gYmUgdXBkYXRlZC4gUiBkb2VzIG5vdCBzdXBwb3J0IHByaXZhdGUgb3IgcHJvdGVjdGVkIGZpZWxkcyBhcyBpcyBjb21tb24gaW4gSmF2YSBvciBDKys7IGEgZmllbGRzIGFyZSBwdWJsaWMuCgpgYGB7ciBhY2NSZWZPYmpGaWVsZHN9CmkgPC0gSW5zdHJ1Y3RvcihpaWQgPSAxLCBuYW1lID0gJ0plZmYgQWxkZW4nLCByYW5rID0gJ0ZULUFzc29jaWF0ZScpCgojIHJlYWQgYSBmaWVsZCdzIHZhbHVlCm4gPC0gaSRuYW1lCgojIHVwZGF0ZSBhIGZpZWxkJ3MgdmFsdWUKaSRuYW1lIDwtICdKZWZmcmV5IEFsZGVuJwpgYGAKCiMjIyBPYmplY3RzIGFyZSBSZWZlcmVuY2VzCgpXaGVuIGluc3RhbnRpYXRpbmcgYSByZWZlcmVuY2Ugb2JqZWN0LCBSIGdlbmVyYXRlcyBhbiBpbnRlcm5hbCBvYmplY3QgYW5kIHJldHVybnMgYSByZWZlcmVuY2UgdG8gdGhlIG9iamVjdCAoaGVuY2UgdGhlIG5hbWUpLiBTbywgYXNzaWduaW5nIGFuIG9iamVjdCB0byBhbm90aGVyIHZhcmlhYmxlIGFjdHVhbGx5IGFzc2lnbnMgdGhlIHJlZmVyZW5jZSBhbmQgZG9lcyBub3QgbWFrZSBhIGNvcHkuIEluIHRoZSBjb2RlIGJlbG93LCAqaTEqIGFuZCAqaTIqIGFyZSByZWZlcmVuY2VzIHRvIHRoZSBzYW1lICpJbnN0cnVjdG9yKiBvYmplY3QuIFRoaXMgaXMgc2ltaWxhciB0byBKYXZhIGJ1dCB1bmxpa2UgQysrIHdoZW4gYSBjb3B5IGNvbnN0cnVjdG9yIGlzIGRlZmluZWQuCgpgYGB7ciBvYmpzQXNSZWZlcmVuY2VzfQppMSA8LSBJbnN0cnVjdG9yKGlpZCA9IDEsIG5hbWUgPSAnSmVmZiBBbGRlbicsIHJhbmsgPSAnRlQtQXNzb2NpYXRlJykKaTIgPC0gaTEKCmkyJG5hbWUgPC0gJ1hpbiBXYW5nJwoKcHJpbnQoaTEpCmBgYAoKSW4gdGhlIGNvZGUgYWJvdmUsIHdlIGNyZWF0ZSBhIG5ldyBpbnN0YW5jZSBvZiB0aGUgY2xhc3MgKkluc3RydWN0b3IqIGFuZCBnZXQgYSByZWZlcmVuY2UgYmFjayB3aGljaCB3ZSBzdG9yZSBpbiB0aGUgdmFyaWFibGUgKmkxKi4gV2UgdGhlbiBhc3NpZ24gKmkxKiB0byAqaTIqIC0tIGJ1dCB3ZSBhcmUgYWN0dWFsbHkgYXNzaWduaW5nIHRoZSByZWZlcmVuY2UgKG9yIGEgcG9pbnRlcikgdG8gdGhlIG9iamVjdC4gVGhpbmsgb2YgKmkxKiBiZWluZyB0aGUgbG9jYXRpb24gaW4gbWVtb3J5IHdoZXJlIHRoZSBvYmplY3QgaXMgc3RvcmVkIC0tIG9yIHRoaW5rIG9mICppMSogYXMgdGhlIElEIG9mIHRoZSBvYmplY3QuIEFueSBtb2RpZmljYXRpb24gb2YgdGhlIG1lbW9yeSB0aHJvdWdoIHRoZSByZWZlcmVuY2UgKmkyKiBtb2RpZmllcyB0aGUgc2FtZSBvYmplY3QgdGhhdCBpcyBwb2ludGVkIHRvIGJ5ICppMSouIFNvLCBiZSBjYXJlZnVsIHdoZW4gImNvcHlpbmciIG9iamVjdHMuCgojIyMjIENvcHlpbmcgT2JqZWN0cwoKVG8gbWFrZSBhbiBhY3R1YWwgY29weSwgdXNlIHRoZSBpbmhlcml0ZWQgbWV0aG9kIDxjb2RlPmNvcHkoKTwvY29kZT4uIFRoaXMgY3JlYXRlcyBhIG5ldyBvYmplY3QgdGhhdCBpcyBlcXVpdmFsZW50IG9mIHRoZSBvcmlnaW5hbCBvYmplY3QuCgpgYGB7ciBjb3B5UmVmT2JqfQppMSA8LSBJbnN0cnVjdG9yKGlpZCA9IDEsIG5hbWUgPSAnSmVmZiBBbGRlbicsIHJhbmsgPSAnRlQtQXNzb2NpYXRlJykKCmkyIDwtIGkxJGNvcHkoKQoKIyBtb2RpZnlpbmcgaTIgZG9lcyBub3QgbW9kaWZ5IGkxCmkyJG5hbWUgPC0gJ1N1c2FuIFdvbGxhc3RvbicKCnByaW50KGkxKQpgYGAKCiMjIyBEZWZpbmluZyBNZXRob2RzCgpNZXRob2RzIGFyZSBkZWZpbmVkIGZvciBhIHJlZmVyZW5jZSBjbGFzcyBhbmQgZG8gbm90IGJlbG9uZyB0byBnZW5lcmljIGZ1bmN0aW9ucyBhcyBpbiAqUzMqIGFuZCAqUzQqIGNsYXNzZXMuIEFsbCByZWZlcmVuY2UgY2xhc3NlcyBoYXZlIGEgc2V0IG9mIHByZWRlZmluZWQgbWV0aG9kcyBpbmhlcml0ZWQgZnJvbSB0aGUgc3VwZXJjbGFzcyAqZW52UmVmQ2xhc3MqLiBUaGlzIGlzIHNpbWlsYXIgdG8gYWxsIEphdmEgY2xhc3NlcyBiZWluZyBzdWJjbGFzc2VzIG9mIHRoZSAqT2JqZWN0KiBjbGFzcy4KCk5ldyBtZXRob2RzIGNhbiBiZSBhZGRlZCBpbmxpbmUgaW4gdGhlIHNlcGFyYXRlIGxpc3QgKm1ldGhvZHMqLgoKTm90aWNlIHRoZSBvcGVyYXRvciAqXDxcPC0qIHVzZWQgdG8gYWNjZXNzIGZpZWxkcyB3aXRoaW4gYSBtZXRob2QuIFVzaW5nIHRoZSBzaW1wbGUgYXNzaWdubWVudCBvcGVyYXRvciAqXDwtKiB3b3VsZCBoYXZlIGNyZWF0ZWQgYSBsb2NhbCB2YXJpYWJsZSBjYWxsZWQgKnNhbGFyeSosIHdoaWNoIHdvdWxkIGxlYWQgdG8gZGlmZmVyZW50IGJlaGF2aW9yLiBGb3J0dW5hdGVseSwgUiB3aWxsIGlzc3VlIGEgd2FybmluZyBpbiBzdWNoIGEgY2FzZS4KCkFsc28gbm90ZSB0aGUgKiwqIGFmdGVyIHRoZSAqfSogdG8gc2VwYXJhdGUgdGhlIG1ldGhvZCBmdW5jdGlvbiBkZWZpbml0aW9ucy4KCmBgYHtyIGRpc3BSZWZPYmp9Ckluc3RydWN0b3IgPC0gc2V0UmVmQ2xhc3MoIkluc3RydWN0b3IiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZHMgPSBsaXN0KGlpZD0ibnVtZXJpYyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0iY2hhcmFjdGVyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5rPSJjaGFyYWN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsYXJ5PSJudW1lcmljIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdldE1vbnRobHlTYWxhcnkgPSBmdW5jdGlvbigpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChzYWxhcnkgLyAxMikKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFwcGx5UmFpc2UgPSBmdW5jdGlvbihtZXJpdCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnkgPDwtIHNhbGFyeSAqICgxICsgbWVyaXQpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKYGBgCgojIyMgQWNjZXNzaW5nIE1ldGhvZHMKCk1ldGhvZHMgYXJlIGFjY2Vzc2VkIHRoZSBzYW1lIHdheSBhcyBmaWVsZHMgLS0gd2l0aCB0aGUgKlwkKiBvcGVyYXRvci4KCmBgYHtyIGFjY2Vzc1JlZk1ldGhvZH0KaSA8LSBJbnN0cnVjdG9yKGlpZCA9IDIsIAogICAgICAgICAgICAgICAgbmFtZSA9ICdEdWEgRGlwYScsIAogICAgICAgICAgICAgICAgcmFuayA9ICdULUFzc2lzdGFudCcsIAogICAgICAgICAgICAgICAgc2FsYXJ5ID0gMTI4MDAwKQoKbS5iZWYgPC0gaSRnZXRNb250aGx5U2FsYXJ5KCkKaSRhcHBseVJhaXNlKDAuMDQ1KQoKbS5hZnQgPC0gaSRnZXRNb250aGx5U2FsYXJ5KCkKCmNhdCgiU2FsYXJ5IHJhaXNlZCBmcm9tICQiLCBtLmJlZiwgInRvICQiLCBtLmFmdCwgInBlciBtb250aCIpCmBgYAoKIyMjIEluaGVyaXRhbmNlCgpJbmhlcml0YW5jZSBpcyBhIGtleSBtZWNoYW5pc20gaW4gb2JqZWN0LW9yaWVudGVkIHByb2dyYW1taW5nLiBJdCBhbGxvd3MgYSBwcm9ncmFtbWVyIHRvIGRlZmluZSBhIG5ldyBjbGFzcyAoKnN1YmNsYXNzKiBvciAqZGVyaXZlZCBjbGFzcyopIGZyb20gYW4gZXhpc3RpbmcgY2xhc3NlcyAoKnN1cGVyY2xhc3MqIG9yICpiYXNlIGNsYXNzKikuIERlcml2ZWQgY2xhc3NlcyBjYW4gYWRkIG5ldyBmaWVsZHMgYW5kIG1ldGhvZHMuIEFsbCBmaWVsZHMgYW5kIG1ldGhvZHMgb2YgdGhlIGJhc2UgY2xhc3MgYXJlIGF1dG9tYXRpY2FsbHkgZmllbGRzIGFuZCBtZXRob2RzIG9mIHRoZSBkZXJpdmVkIGNsYXNzLiBUaGlzIGluY3JlYXNlcyByZXVzYWJpbGl0eSBvZiBjb2RlIGFuZCBhbGxvd3MgcHJvZ3JhbW1lcnMgdG8gcmVwcmVzZW50IGRvbWFpbiBvYmplY3RzIG1vcmUgYWNjdXJhdGVseS4KCkluaGVyaXRhbmNlIGlzIHN1cHBvcnRlZCBpbiBhbGwgdGhyZWUgY2xhc3Mgc3lzdGVtcyBidXQgaXMgbW9yZSBsaWtlIG90aGVyIG9iamVjdC1vcmllbnRlZCBsYW5ndWFnZXMgaW4gdGhlICpSZWZlcmVuY2UqIGNsYXNzIHN5c3RlbS4gV2Ugd2lsbCByZXN0cmljdCBvdXJzZWx2ZXMgdG8gdGhpcyBjbGFzcyBzeXN0ZW0uCgpJbiB0aGUgZXhhbXBsZSBiZWxvdywgd2UgaGF2ZSBhIGJhc2UgY2xhc3MgKlBlcnNvbiogd2l0aCB0aHJlZSBmaWVsZHMgYW5kIGEgbWV0aG9kLiBXZSB0aGVuIGRlZmluZSBhIGRlcml2ZWQgY2xhc3MgKkluc3RydWN0b3IqIHdoaWNoIGV4dGVuZHMgKlBlcnNvbiogd2l0aCB0d28gYWRkaXRpb25hbCBmaWVsZHMgYW5kIHR3byBtZXRob2RzIGJ5IGFkZGluZyB0aGUgYmFzZSBjbGFzcyAqUGVyc29uKiBuYW1lIHRvIHRoZSAqY29udGFpbnMqIGFyZ3VtZW50LgoKYGBge3IgZGVmRGVyaXZlZENsYXNzfQpQZXJzb24gPC0gc2V0UmVmQ2xhc3MoIlBlcnNvbiIsIAogICAgICAgICAgICAgICAgICAgICAgZmllbGRzID0gbGlzdChwaWQ9Im51bWVyaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0iY2hhcmFjdGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeW9iID0gIm51bWVyaWMiKSwKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICBnZXRBZ2UgPSBmdW5jdGlvbigpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyWWVhciA8LSBhcy5udW1lcmljKGZvcm1hdChTeXMudGltZSgpLCAiJVkiKSkKICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gKGN1cnJZZWFyIC0geW9iKQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICApKQoKSW5zdHJ1Y3RvciA8LSBzZXRSZWZDbGFzcygiSW5zdHJ1Y3RvciIsIAogICAgICAgICAgICAgICAgICAgICAgY29udGFpbnMgPSAiUGVyc29uIiwKICAgICAgICAgICAgICAgICAgICAgIGZpZWxkcyA9IGxpc3QocmFuaz0iY2hhcmFjdGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsYXJ5PSJudW1lcmljIgogICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICBnZXRNb250aGx5U2FsYXJ5ID0gZnVuY3Rpb24oKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChzYWxhcnkgLyAxMikKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgIGFwcGx5UmFpc2UgPSBmdW5jdGlvbihtZXJpdCkgewogICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGFyeSA8PC0gc2FsYXJ5ICogKDEgKyBtZXJpdCkKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgKSkKYGBgCgpXZSBjYW4gdGhlbiBpbnN0YW50aWF0ZSB0aGUgZGVyaXZlZCBjbGFzcyAqSW5zdHJ1Y3RvciogYW5kIGZpbmQgdGhhdCBpdCBoYXMgYWxsIG9mIHRoZSBmaWVsZHMgYW5kIG1ldGhvZHMgb2YgKlBlcnNvbiogaW4gYWRkaXRpb24gdG8gaXRzIGFkZGl0aW9uYWwgZmllbGRzIGFuZCBtZXRob2RzLgoKYGBge3IgaW5zdERlckNsYXNzfQphbkluc3RydWN0b3IgPC0gSW5zdHJ1Y3RvcihwaWQgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ1JhaiBNZXRoYScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICByYW5rID0gJ0ZULUZ1bGwnLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5b2IgPSAxOTY4LAogICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnkgPSAxODI5NzIpCgphbkluc3RydWN0b3IkZ2V0TW9udGhseVNhbGFyeSgpCmFuSW5zdHJ1Y3RvciRnZXRBZ2UoKQpgYGAKCiMjIyBPYmplY3QgQWdncmVnYXRpb24KCkluIGFuIGFnZ3JlZ2F0aW9uIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG9iamVjdHMsIHRoZXJlIGlzIGEgd2hvbGUvcGFydCBvciBjb250YWluZXIvcGFydCBoaWVyYXJjaHkuIEluIG9udG9sb2d5IHRlcm1zLCB0aGVyZSBpcyBhICpwYXJ0b25vbXkqLiBJbiBhbiBhZ2dyZWdhdGlvbiwgb25lIG9iamVjdCAiY29udGFpbnMiIG90aGVyIG9iamVjdHMsIGFsdGhvdWdoIHRoZSBjb250YWlubWVudCBkb2VzIG5vdCBoYXZlIHRvIGJlICJwaHlzaWNhbCIsIGkuZS4sIHRoZSBwYXJ0IG9iamVjdHMgZG8gbm90IGhhdmUgdG8gYmUgcGFydCBvZiB0aGUgc2FtZSBtZW1vcnkgc3RydWN0dXJlLiBUaGUgd2hvbGUvcGFydCByZWxhdGlvbnNoaXAgY2FuIGJlIGJ5IHJlZmVyZW5jZSB3aGVyZSB0aGUgY29udGFpbmVyIG9iamVjdCAod2hvbGUgb3IgYWdncmVnYXRlKSBjb250YWlucyByZWZlcmVuY2VzIHRvIHRoZSBjb250YWluZWQgKHBhcnQpIG9iamVjdHMuCgpMZXQncyBpbXBsZW1lbnQgdGhlIHBhcnQgaGllcmFyY2h5IGV4cHJlc3NlZCBieSB0aGUgVU1MIENsYXNzIERpYWdyYW0gYmVsb3c6CgohW10oVU1MQ2xhc3NBZ2dyZWdhdGlvbi5qcGcpe3dpZHRoPSI2MCUifQoKYGBge3IgZGVmQ2x1YkNsYXNzZXN9Ck1lbWJlciA8LSBzZXRSZWZDbGFzcygiTWVtYmVyIiwgZmllbGRzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgbUlEID0gIm51bWVyaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjaGFyYWN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICB5ZWFySm9pbmVkID0gIm51bWVyaWMiKSkKCkNsdWIgPC0gc2V0UmVmQ2xhc3MoIkNsdWIiLCAKICAgICAgICAgICAgICAgICAgICAgZmllbGRzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjaGFyYWN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICB5ZWFyRm91bmRlZCA9ICJudW1lcmljIiwKICAgICAgICAgICAgICAgICAgICAgICAgbWF4TWVtSUQgPSAibnVtZXJpYyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG1lbWJlcnMgPSAibGlzdCIpLAogICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgZ2V0TnVtTWVtYmVycyA9IGZ1bmN0aW9uKCkgewogICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAobGVuZ3RoKG1lbWJlcnMpKQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgYWRkTWVtYmVyID0gZnVuY3Rpb24obSkgewogICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpcy5udWxsKG1lbWJlcnMpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZW1iZXJzIDw8LSBsaXN0KDEwMjQpCiAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGQgYSBtZW1iZXIgSUQgZm9yIHRoZSBuZXcgbWVtYmVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgbSRtSUQgPC0gbWF4TWVtSUQgKyAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4TWVtSUQgPDwtIG1heE1lbUlEICsgMQogICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHRoZSBtZW1iZXIgdG8gaW50ZXJuYWwgbGlzdAogICAgICAgICAgICAgICAgICAgICAgICAgIG1lbWJlcnNbW2xlbmd0aChtZW1iZXJzKSsxXV0gPDwtIG0KICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gKDEpCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICkpCmBgYAoKQSBmZXcgbm90ZXdvcnRoeSBwb2ludHMgYWJvdXQgdGhlIGFib3ZlIGNvZGUuIFRoZSBmaWVsZCAqbWVtYmVycyogaXMgYSAicHJpdmF0ZSIgbWVtYmVyIHZhcmlhYmxlIHRoYXQga2VlcHMgdHJhY2sgb2YgYWxsIG9mIHRoZSBtZW1iZXJzIGFkZGVkIHRvIHRoZSBjbHViLiBJdCBpcyBhbiBlbXB0eSBsaXN0IHdoZW4gY3JlYXRlZCwgc28gcmlnaHQgYmVmb3JlIHRoZSBmaXJzdCBtZW1iZXIgaXMgYWRkZWQgaXQgbXVzdCBiZSBhbGxvY2F0ZWQuCgpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBjbGFzc2VzIGRlZmluZWQsIGxldCdzIGNyZWF0ZSBzb21lIHNhbXBsZSBpbnN0YW5jZXMgZm9yIHRlc3RpbmcuIFdlIHdvbid0IHNldCBhIG1lbWJlciBJRCBmb3IgbmV3IG1lbWJlcnMgYXMgdGhvc2UgYXJlIGFzc2lnbmVkIHRvIHRoZW0gd2hlbiB0aGV5IGdldCBhZGRlZCB0byB0aGUgY2x1Yi4KCmBgYHtyIGNyZWF0ZUNsdWJPYmplY3RzfQojIGNyZWF0ZSBhIENsdWIKYUNsdWIgPC0gQ2x1YihuYW1lID0gJ0RBVEEgQ2x1YicsIAogICAgICAgICAgICAgIHllYXJGb3VuZGVkID0gMjAxNSwKICAgICAgICAgICAgICBtYXhNZW1JRCA9IDApCgojIGNyZWF0ZSBhIGZldyBtZW1iZXJzIGFuZCBhZGQgdGhlbSB0byB0aGUgY2x1YgpzIDwtIGFDbHViJGFkZE1lbWJlcigKICBNZW1iZXIobmFtZSA9ICdKZWZmIEdhcm9sJywgeWVhckpvaW5lZCA9IDIwMjIpKQoKcyA8LSBhQ2x1YiRhZGRNZW1iZXIoCiAgTWVtYmVyKG5hbWUgPSAnVXJzdWxhIFZhbiBMZWlkZW4nLCB5ZWFySm9pbmVkID0gMjAyMikpCgpzIDwtIGFDbHViJGFkZE1lbWJlcigKICBNZW1iZXIobmFtZSA9ICdHYXJyZXR0IExpZXcnLCB5ZWFySm9pbmVkID0gMjAyMikpCgojIG51bWJlciBvZiBjbHViIG1lbWJlcnMgc2hvdWxkIGJlIGNvcnJlY3QKYUNsdWIkZ2V0TnVtTWVtYmVycygpCmBgYAoKIyMgQnVpbGRpbmcgWE1MIERvY3VtZW50cwoKTm93IHRoYXQgd2UgaGF2ZSBhbiB1bmRlcnN0YW5kaW5nIG9mIHRoZSBtZWNoYW5pc21zIHRvIGJ1aWxkIFIgb2JqZWN0cywgd2UgbmVlZCB0byB0dXJuIG91ciBhdHRlbnRpb24gdG8gdGhlIG1lY2hhbmlzbXMgZm9yIGNvbnN0cnVjdGluZyBYTUwgZG9jdW1lbnRzIGZyb20gd2l0aGluZyBSCgpUaGUgZnVuY3Rpb25zIHVzZWQgdG8gYnVpbGQgYW4gaW4tbWVtb3J5IFhNTCBkb2N1bWVudCBhcmUgZnJvbSB0aGUgKipYTUwqKiBwYWNrYWdlIFRoYXQgc2FtZSBsaWJyYXJ5IGlzIGFsc28gdXNlZCB0byBwYXJzZSBYTUwgZWxlbWVudHMgaW4gYW4gWE1MIGRvY3VtZW50LgoKIyMjIFVzaW5nIFhNTCBQYWNrYWdlCgpMZXQncyBzdGFydCB3aXRoIGEgc2ltcGxlIGV4YW1wbGUgdGhhdCBleHRlcm5hbGl6ZXMgYSBkYXRhIGZyYW1lIGFzIFhNTC4gSXQgd2lsbCBzaG93IHVzIGhvdyB0byB1c2UgdGhlIGZ1bmN0aW9ucyB0byBnZW5lcmF0ZSBhbiBpbi1tZW1vcnkgWE1MIERPTSB3aGljaCBjYW4gdGhlbiBzYXZlIHRvIGEgZmlsZS4KCmBgYHtyIHNhbXBsZURGfQpsaWJyYXJ5KFhNTCkKCiMgRGF0YSBpbiAzIGNvbHVtbnMgaW4gYSBkYXRhIGZyYW1lCmRmIDwtIGRhdGEuZnJhbWUocmVmSUQgPSBjKDEwMCwgMjAwKSwKICAgICAgICAgICAgICAgICB1cGMgPSBjKCcyMDE5MDgxOCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgJzIwMTkwODIzJyksCiAgICAgICAgICAgICAgICAgZGVzYyA9IGMoJ2VSZWFkZXIgOCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICdVU0ItQyBDYWJsZScpCiAgICAgICAgICAgICAgICApCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhIGRhdGEgZnJhbWUsIGxldCdzIGV4dGVybmFsaXplIHRoZSBkYXRhIGZyYW1lIHRvIHNvbWUgWE1MIHN0cnVjdHVyZS4KCmBgYHtyIGJ1aWxkWE1Md2l0aFhNTFBhY2thZ2V9CiMgYnVpbGQgWE1MIHN0cnVjdHVyZQpYTUxkb2MgPSBuZXdYTUxEb2MoKQoKIyByb290IG5vZGUgaXMgPGNhdGFsb2c+CnJvb3ROb2RlID0gbmV3WE1MTm9kZSgiY2F0YWxvZyIsIGRvYyA9IFhNTGRvYykKCiMgYWRkIGVsZW1lbnRzIHRvIHRoZSBYTUwgdW5kZXJuZWF0aCB0aGUgcm9vdCBub2RlCm12Tm9kZSA9IG5ld1hNTE5vZGUoImNhdFZlcnNpb24iLCAiMS4wLjAiLCBwYXJlbnQgPSByb290Tm9kZSkKCiMgd3JpdGUgZWFjaCBvZiB0aGUgcm93cyBpbiB0aGUgZGF0YSBmcmFtZQpmb3IgKGkgaW4gMTpucm93KGRmKSl7CiAgIyBhZGQgYSBub2RlIHdpdGggYW4gYXR0cmlidXRlCiAgcHJvZE5vZGUgPSBuZXdYTUxOb2RlKCJwcm9kdWN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGF0dHJzID0gYyhyZWZJRCA9IGRmJHJlZklEW2ldKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudCA9IHJvb3ROb2RlKQoKICAjIGFkZCBkZXRhaWxzIGZvciBlYWNoIHByb2R1Y3QgYXMgY2hpbGQgbm9kZXMKICBuZXdYTUxOb2RlKCJ1cGMiLCBkZiR1cGNbaV0sIHBhcmVudCA9IHByb2ROb2RlKQogIG5ld1hNTE5vZGUoImRlc2MiLCBkZiRkZXNjW2ldLCBwYXJlbnQgPSBwcm9kTm9kZSkKfQoKIyBhZGQgYW4gZW1wdHkgImZsYWciIG5vZGUKdndOb2RlID0gbmV3WE1MTm9kZSgibG9ja2VkIiwgcGFyZW50ID0gcm9vdE5vZGUpCgojIHNhdmUgWE1MIHRvIGEgZmlsZQpzYXZlWE1MKFhNTGRvYywgZmlsZSA9ICJwcm9kLWNhdGFsb2cueG1sIikKYGBgCgojIyMgVXNpbmcgU3RyaW5nIENvbmNhdGVuYXRpb24KCkEgbW9yZSBmbGV4aWJsZSwgb2Z0ZW4gZmFzdGVyLCBidXQgbW9yZSBlcnJvci1wcm9uZSBhbmQgbGFib3Jpb3VzIHByb2Nlc3MgaXMgdG8gY29uc3RydWN0IHRoZSBYTUwgZG9jdW1lbnQgZnJvbSBjb25jYXRlbmF0ZWQgY2hhcmFjdGVyIHN0cmluZ3MuIEFmdGVyIGFsbCwgYW4gWE1MIGRvY3VtZW50IGlzIGEgcGxhaW4gdGV4dCBkb2N1bWVudC4KClRoZSBjb2RlIGJlbG93IGNvbnN0cnVjdHMgdGhlIHNhbWUgWE1MIGRvY3VtZW50IGFzIGluIHRoZSBwcmlvciBzZWN0aW9uLiBJdCBjb250aW51YWxseSBhZGRzIHRvIGEgY2hhcmFjdGVyIHZhcmlhYmxlIHVzaW5nIHRoZSBmdW5jdGlvbiBgcGFzdGUwKClgIC0tIHRoZSBzdHJpbmcgY29uY2F0ZW5hdGlvbiBmdW5jdGlvbiBpbiBSLgoKTm90ZSB0aGF0IHRoZSAqXFxuKiBjaGFyYWN0ZXJzIGluc2VydCBuZXdsaW5lIGZlZWRzIGludG8gdGhlIHJlc3VsdCBkb2N1bWVudCBmb3IgYSBjbGVhbmVyIGxvb2sgd2hlbiB2aWV3ZWQ7IHRoZXkgYXJlIG5vdCBzdHJpY3RseSBuZWNlc3NhcnkgZnJvbSBhbiBYTUwgc3ludGFjdGljIHBvaW50IG9mIHZpZXcuIEFsc28gbm90ZSB0aGUgd2F5IHRoYXQgcXVvdGVzIGFyZSBhZGRlZCB3aXRoaW4gcXVvdGVzLiBXZSB1c2Ugc2luZ2xlIHF1b3RlcyB3aXRoaW4gdGhlIFhNTCBhbmQgZG91YmxlIHF1b3RlcyB0byBlbmNsb3NlIHN0cmluZ3MgaW4gUiAtLSB3ZSBjb3VsZCBoYXZlIGRvbmUgaXQgdGhlIG90aGVyIHdheSBhcm91bmQgYXMgd2VsbDsgYm90aCBSIGFuZCBYTUwgYWNjZXB0IHNpbmdsZSBhbmQgZG91YmxlIHF1b3RlcyBmb3Igc3RyaW5nIGVuY2xvc3VyZS4gVGhlIHN0cmluZ3MgYXJlIGluY2x1ZGUgbGVhZGluZyBzcGFjZXMgdG8sIGFnYWluLCBtYWtlIHRoZSBYTUwgbW9yZSAidmlld2FibGUiLgoKYGBge3IgYnVpbGRYTUxWaWFTdHJpbmdzfQojIHN0YXJ0IHdpdGggdGhlIHByZWFtYmxlIGFuZCByb290IHRhZwp4bWwgPC0gIjw/eG1sIHZlcnNpb249JzEuMCc/PlxuXG48Y2F0YWxvZz4iCgojIGFkZCBjaGlsZCBlbGVtZW50CnhtbCA8LSBwYXN0ZTAoeG1sLCAiICA8Y2F0VmVyc2lvbj4iLCAiMS4wLjAiLCAiPC9jYXRWZXJzaW9uPlxuIikKCiMgd3JpdGUgZWFjaCBvZiB0aGUgcm93cyBpbiB0aGUgZGF0YSBmcmFtZQpmb3IgKGkgaW4gMTpucm93KGRmKSl7CiAgIyBhZGQgYSBub2RlIHdpdGggYW4gYXR0cmlidXRlCiAgeG1sIDwtIHBhc3RlMCh4bWwsICIgICIsCiAgICAgICAgICAgICAgICAiPHByb2R1Y3QgcmVmSUQ9JyIsIGRmJHJlZklEW2ldLCAiJz5cbiIpCgogICMgYWRkIGRldGFpbHMgZm9yIGVhY2ggcHJvZHVjdCBhcyBjaGlsZCBub2RlcwogIHhtbCA8LSBwYXN0ZTAoeG1sLCAiICAgICIsCiAgICAgICAgICAgICAgICAiPHVwYz4iLCBkZiR1cGNbaV0sICI8L3VwYz5cbiIpCiAgeG1sIDwtIHBhc3RlMCh4bWwsICIgICAgIiwKICAgICAgICAgICAgICAgICI8ZGVzYz4iLCBkZiRkZXNjW2ldLCAiPC9kZXNjPlxuIikKICAKICAjIHRlcm1pbmF0ZSA8cHJvZHVjdD4gZWxlbWVudAogIHhtbCA8LSBwYXN0ZTAoeG1sLCAiICAiLCAiPC9wcm9kdWN0PlxuIikKfQoKIyBhZGQgYW4gZW1wdHkgImZsYWciIG5vZGUKeG1sIDwtIHBhc3RlMCh4bWwsICIgIDxsb2NrZWQgLz5cbiIpCgojIHRlcm1pbmF0ZSB0aGUgcm9vdCB0YWcgPGNhdGFsb2c+CnhtbCA8LSBwYXN0ZTAoeG1sLCAiPC9jYXRhbG9nPiIpCgojIHNhdmUgdGhlIFhNTCB0byBhIGZpbGUKZiA8LSBmaWxlKCJwcm9kLWNhdGFsb2ctdjIueG1sIikKd3JpdGVMaW5lcyh4bWwsIGYpCmNsb3NlKGYpCmBgYAoKIyMgRXh0ZXJuYWxpemluZyBhbiBPYmplY3QKCk5vdyB0aGF0IHdlIHVuZGVyc3RhbmQgaG93IHRvIGdlbmVyYXRlIGEgRE9NLCB3ZSBjYW4gZXh0ZXJuYWxpemUgYW4gb2JqZWN0J3MgZmllbGQgdmFsdWVzLiBBIGNvbW1vbiBhcHByb2FjaCBpcyB0byBhZGQgYSBtZXRob2QgdGhhdCB0YWtlcyBjYXJlIG9mIHRoZSBleHRlcm5hbGl6YXRpb24gb2YgYSBjbGFzcy4KCkxldCdzIHRyeSB0aGlzIGJ5IGFkZGluZyBhIG5ldyBtZXRob2QgdG8gdGhlIGFib3ZlIGNsYXNzICpNZW1iZXIqIGNhbGxlZCAqZXh0MlhNTCgpKiB3aGljaCB0YWtlcyB0aGUgcHJlLWNyZWF0ZWQgWE1MIERPTSB0byB3aGljaCB0aGUgb2JqZWN0IHNob3VsZCBiZSBhZGRlZCBhcyBhbiBpbnB1dCBhcmd1bWVudC4KCmBgYHtyfQpNZW1iZXIkbWV0aG9kcyhleHQyWE1MID0gZnVuY3Rpb24ocGFyZW50Tm9kZSkgewogICAgbiA9IG5ld1hNTE5vZGUoIm1lbWJlciIsIAogICAgICAgICAgICAgICAgICAgIC5zZWxmJG1JRCwgCiAgICAgICAgICAgICAgICAgICAgcGFyZW50ID0gcGFyZW50Tm9kZSkKICAgIG5ld1hNTE5vZGUoIm5hbWUiLCAuc2VsZiRuYW1lLAogICAgICAgICAgICAgICAgICAgIHBhcmVudCA9IG4pCiAgICBuZXdYTUxOb2RlKCJ5ZWFyLWpvaW5lZCIsIC5zZWxmJHllYXJKb2luZWQsCiAgICAgICAgICAgICAgICAgICAgcGFyZW50ID0gbikKICAgIHJldHVybihUUlVFKQogIH0pCmBgYAoKV2UnbGwgc3RhcnQgYnkgY3JlYXRpbmcgYSBuZXcgWE1MIGRvY3VtZW50IGFuZCB0aGVuIHdlIHdpbGwgY2FsbCB0aGUgbmV3bHkgY3JlYXRlZCBtZXRob2QgKmV4dDJYTUwoKSogb24gb25lIG9mIHRoZSBhbHJlYWR5IGNyZWF0ZWQgaW5zdGFuY2VzIG9mIHRoZSBjbGFzcyAqTWVtYmVyKiBhbmQgdGhlbiBkaXNwbGF5IHRoZSBET00uCgpgYGB7cn0KbGlicmFyeShYTUwpCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBYTUwgZG9jdW1lbnQKeG1sID0gbmV3WE1MRG9jKCkKCiMgYWRkIDxtZW1iZXJzPiBhcyB0aGUgcm9vdCBub2RlCnJvb3ROb2RlID0gbmV3WE1MTm9kZSgibWVtYmVycyIsIGRvYyA9IHhtbCkKCiMgZXh0ZXJuYWxpemUgYSBtZW1iZXIgb2JqZWN0IHRvIHRoZSBYTUwKYU1lbWJlciA8LSBNZW1iZXIobmFtZSA9ICdPenp5IE9zYm91cm5lJywgeWVhckpvaW5lZCA9IDE5NzEpCmlzU3VjY2VzcyA8LSBhTWVtYmVyJGV4dDJYTUwocm9vdE5vZGUpCgojIGluc3BlY3QgdGhlIFhNTApwcmludCh4bWwpCmBgYAoKTm93IHRoYXQgd2Uga25vdyBob3cgdG8gZXh0ZXJuYWxpemUgYSBzaW5nbGUgb2JqZWN0LCB3ZSBjYW4gZXh0ZXJuYWxpemUgYSBjb250YWluZXIgd2hpY2ggd291bGQgdGhlbiBleHRlcm5hbGl6ZSBpdHMgbWVtYmVyIGVsZW1lbnRzLiBOb3RlIHRoZSBgZXh0MnhtbCgpYCBmdW5jdGlvbnMgaW4gZWFjaCBjbGFzcy4gVGhlIGNvZGUgYmVsb3cgZGVtb25zdHJhdGVzIHRoaXM6CgpgYGB7ciBleHQyWE1MQ29udGFpbmVyfQpNZW1iZXIgPC0gc2V0UmVmQ2xhc3MoIk1lbWJlciIsIAogICAgICAgICAgICAgICAgICAgICAgZmllbGRzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgbUlEID0gIm51bWVyaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjaGFyYWN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICB5ZWFySm9pbmVkID0gIm51bWVyaWMiKSwKICAKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICBleHQyeG1sID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICAgIG1lbU5vZGUgPC0gbmV3WE1MTm9kZSgibWVtYmVyIiwgcGFyZW50ID0gcm9vdE5vZGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3WE1MTm9kZSgibmFtZSIsIC5zZWxmJG5hbWUsIHBhcmVudCA9IG1lbU5vZGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3WE1MTm9kZSgieWVhckpvaW5lZCIsIC5zZWxmJHllYXJKb2luZWQsIHBhcmVudCA9IG1lbU5vZGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChtZW1Ob2RlKQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICApKQoKCkNsdWIgPC0gc2V0UmVmQ2xhc3MoIkNsdWIiLCAKICAgICAgICAgICAgICAgICAgICBmaWVsZHMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjaGFyYWN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgeWVhckZvdW5kZWQgPSAibnVtZXJpYyIsCiAgICAgICAgICAgICAgICAgICAgICBtYXhNZW1JRCA9ICJudW1lcmljIiwKICAgICAgICAgICAgICAgICAgICAgIG1lbWJlcnMgPSAibGlzdCIpLAogICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgZ2V0TnVtTWVtYmVycyA9IGZ1bmN0aW9uKCkgewogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gKGxlbmd0aChtZW1iZXJzKSkKICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgIGFkZE1lbWJlciA9IGZ1bmN0aW9uKG0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzLm51bGwobWVtYmVycykpCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWVtYmVycyA8PC0gbGlzdCgxMDI0KQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGQgYSBtZW1iZXIgSUQgZm9yIHRoZSBuZXcgbWVtYmVyCiAgICAgICAgICAgICAgICAgICAgICAgIG0kbUlEIDwtIG1heE1lbUlEICsgMQogICAgICAgICAgICAgICAgICAgICAgICBtYXhNZW1JRCA8PC0gbWF4TWVtSUQgKyAxCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAjIGFkZCB0aGUgbWVtYmVyIHRvIGludGVybmFsIGxpc3QKICAgICAgICAgICAgICAgICAgICAgICAgbWVtYmVyc1tbbGVuZ3RoKG1lbWJlcnMpKzFdXSA8PC0gbQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuICgxKQogICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgZXh0MnhtbCA9IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgZXh0WE1MIDwtIG5ld1hNTERvYygpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICByb290Tm9kZSA9IG5ld1hNTE5vZGUoImNsdWIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF0dHJzID0gYyhuYW1lID0gLnNlbGYkbmFtZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb2MgPSBleHRYTUwpCiAgICAgICAgICAgICAgICAgICAgICAgIG5ld1hNTE5vZGUoInllYXJGb3VuZGVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnNlbGYkeWVhckZvdW5kZWQsIHBhcmVudCA9IHJvb3ROb2RlKQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgZm9yIChtIGluIDE6bGVuZ3RoKG1lbWJlcnMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgYU1lbWJlciA8LSAuc2VsZiRtZW1iZXJzW1ttXV0KICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICBhZGRDaGlsZHJlbihyb290Tm9kZSwgYU1lbWJlciRleHQyeG1sKCkpICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChleHRYTUwpCiAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgKSkKCiMgY3JlYXRlIGEgQ2x1YgphQ2x1YiA8LSBDbHViKG5hbWUgPSAnREFUQSBDbHViJywgCiAgICAgICAgICAgICAgeWVhckZvdW5kZWQgPSAyMDE1LAogICAgICAgICAgICAgIG1heE1lbUlEID0gMCkKCiMgY3JlYXRlIGEgZmV3IG1lbWJlcnMgYW5kIGFkZCB0aGVtIHRvIHRoZSBjbHViCnMgPC0gYUNsdWIkYWRkTWVtYmVyKAogIE1lbWJlcihuYW1lID0gJ0plZmYgR2Fyb2wnLCB5ZWFySm9pbmVkID0gMjAyMikpCgpzIDwtIGFDbHViJGFkZE1lbWJlcigKICBNZW1iZXIobmFtZSA9ICdVcnN1bGEgVmFuIExlaWRlbicsIHllYXJKb2luZWQgPSAyMDIyKSkKCnMgPC0gYUNsdWIkYWRkTWVtYmVyKAogIE1lbWJlcihuYW1lID0gJ0dhcnJldHQgTGlldycsIHllYXJKb2luZWQgPSAyMDIyKSkKCiMgbnVtYmVyIG9mIGNsdWIgbWVtYmVycyBzaG91bGQgYmUgY29ycmVjdAphQ2x1YiRnZXROdW1NZW1iZXJzKCkKCiMgZXh0ZXJuYWxpemUgdGhlIENsdWIgYW5kIGl0cyBNZW1iZXJzIHRvIFhNTAp4bWxDbHViRG9jIDwtIGFDbHViJGV4dDJ4bWwoKQoKIyBzYXZlIFhNTCB0byBhIGZpbGUKc2F2ZVhNTCh4bWxDbHViRG9jLCBmaWxlID0gImNsdWIueG1sIikKYGBgCgpUaGUgImNsdWIueG1sIiBmaWxlIGxvb2tzIGxpa2UgdGhpczoKCmBgYHt4bWx9Cjw/eG1sIHZlcnNpb249IjEuMCI/Pgo8Y2x1YiBuYW1lPSJEQVRBIENsdWIiPgogIDx5ZWFyRm91bmRlZD4yMDE1PC95ZWFyRm91bmRlZD4KICA8bWVtYmVyPgogICAgPG5hbWU+SmVmZiBHYXJvbDwvbmFtZT4KICAgIDx5ZWFySm9pbmVkPjIwMjI8L3llYXJKb2luZWQ+CiAgPC9tZW1iZXI+CiAgPG1lbWJlcj4KICAgIDxuYW1lPlVyc3VsYSBWYW4gTGVpZGVuPC9uYW1lPgogICAgPHllYXJKb2luZWQ+MjAyMjwveWVhckpvaW5lZD4KICA8L21lbWJlcj4KICA8bWVtYmVyPgogICAgPG5hbWU+R2FycmV0dCBMaWV3PC9uYW1lPgogICAgPHllYXJKb2luZWQ+MjAyMjwveWVhckpvaW5lZD4KICA8L21lbWJlcj4KPC9jbHViPgpgYGAKCiMjIENvbmNsdXNpb24KCk9iamVjdC1vcmllbnRhdGlvbiBpcyBhIGNvbW1vbiB3YXkgdG8gY3JlYXRlIGFic3RyYWN0aW9uIGFuZCBzdHJ1Y3R1cmUgY29tcGxleCBpbmZvcm1hdGlvbi4gVGhpcyBsZXNzb24gc2hvd2VkIGhvdyBSZWZlcmVuY2UgQ2xhc3Mgb2JqZWN0cyBjYW4gYmUgcGVyc2lzdGVkIGFzIGV4dGVybmFsIFhNTCBkb2N1bWVudHMuCgojIyBUdXRvcmlhbAoKVGhlIHZpZGVvIHR1dG9yaWFsIGRlbW9uc3RyYXRlcyB0aGUgY29uc3RydWN0cyBpbnRyb2R1Y2VkIGluIHRoaXMgbGVzc29uIGFuZCB3YWxrcyB0aHJvdWdoIHRoZSBjb2RlIHdpdGggZXhhbXBsZXMgYW5kIGV4cGxhbmF0aW9ucy4KCmBgYHs9aHRtbH0KPGlmcmFtZSBzcmM9Imh0dHBzOi8vcGxheWVyLnZpbWVvLmNvbS92aWRlby84Nzk2MDM4MDU/dGl0bGU9MCZhbXA7YnlsaW5lPTAmYW1wO3BvcnRyYWl0PTAmYW1wO3NwZWVkPTAmYW1wO2JhZGdlPTAmYW1wO2F1dG9wYXVzZT0wJmFtcDthaXJwbGF5PTAmYW1wO2F1ZGlvX3RyYWNrcz0wJmFtcDtjaGFwdGVycz0wJmFtcDtjaHJvbWVjYXN0PTAmYW1wO2Nsb3NlZF9jYXB0aW9ucz0wJmFtcDt0cmFuc2NyaXB0PTAmYW1wO3BsYXllcl9pZD0wJmFtcDthcHBfaWQ9NTg0NzkiIHdpZHRoPSI2NDAiIGhlaWdodD0iMzYwIiBmcmFtZWJvcmRlcj0iMiIgYWxsb3c9ImF1dG9wbGF5OyBmdWxsc2NyZWVuOyBwaWN0dXJlLWluLXBpY3R1cmUiIHRpdGxlPSJFeHRlcm5hbGl6aW5nIE9iamVjdHMgdG8gWE1MIGluIFIiIGRhdGEtZXh0ZXJuYWw9IjEiPjwvaWZyYW1lPgpgYGAKKipDb2RlIHVzZWQgaW4gdmlkZW8qKjogW2wtNi0xMjMteG1sLW11c2luZ3MuUl0oeG1sLW11c2luZ3MvbC02LTEyMy14bWwtbXVzaW5ncy5SKQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKLSAgIFtTNCBDbGFzc2VzIGluIFIuIERhdGFNZW50b3JdKGh0dHBzOi8vd3d3LmRhdGFtZW50b3IuaW8vci1wcm9ncmFtbWluZy9zNC1jbGFzcy8pCi0gICBbUmVmZXJlbmNlIENsYXNzIFN5c3RlbSBpbiBSLiBEYXRhTWVudG9yXShodHRwczovL3d3dy5kYXRhbWVudG9yLmlvL3ItcHJvZ3JhbW1pbmcvcmVmZXJlbmNlLWNsYXNzLykKCiMjIEVycmF0YQoKTm9uZSBjb2xsZWN0ZWQgeWV0LiBMZXQgdXMga25vdy4KCmBgYHs9aHRtbH0KPHNjcmlwdCBzcmM9Imh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS9zdGF0aWMvZmVlZGJhY2syLmpzIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogIG5ldyBKb3Rmb3JtRmVlZGJhY2soewogICAgZm9ybUlkOiAiMjEyMTg3MDcyNzg0MTU3IiwKICAgIGJ1dHRvblRleHQ6ICJGZWVkYmFjayIsCiAgICBiYXNlOiAiaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLyIsCiAgICBiYWNrZ3JvdW5kOiAiI0Y1OTIwMiIsCiAgICBmb250Q29sb3I6ICIjRkZGRkZGIiwKICAgIGJ1dHRvblNpZGU6ICJsZWZ0IiwKICAgIGJ1dHRvbkFsaWduOiAiY2VudGVyIiwKICAgIHR5cGU6IGZhbHNlLAogICAgd2lkdGg6IDcwMCwKICAgIGhlaWdodDogNTAwLAogICAgaXNDYXJkRm9ybTogZmFsc2UKICB9KTsKPC9zY3JpcHQ+CmBgYApgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9fZGVwbG95S25pdC5SJykpLCBpbmNsdWRlID0gRkFMU0V9CmBgYAo=