If you have read Haruki Murakami’s book: What I talk about when I talk about Running, you’ll probably be expecting some wise saying just about now πŸ˜„. Remaining true to the book, I guess we’ll have to get creative, right? So, there’s this wise saying that goes like this, Very few things indeed are really impaRsible. With the emphasis on the R of course, I guess you are getting the gist of our *wise* saying πŸ’­.

Anyhow, let’s get back to the serious stuff. What could we actually talk about when we talk about R or Arduino, or more interestingly R and Arduino? Frankly speaking, the name Arduino has a nice ring to it (selfishly because it starts with an R sound), and both have a thing for color blue 🀐.

But that’s beside the point. Putting it very simply, Arduino is an open-source electronics platform based on easy-to-use hardware (Arduino Board) and software (Arduino IDE). One can tell the board what to do if one has the correct form of data and a set of instructions for processing the data and performing subsequent operations. The Arduino’s microcontroller is responsible for holding all your compiled code and executing the commands you specify. The Arduino Software on the other hand is the board’s IDE where one writes the set of instructions governing the board. The getting started guide would be a good place to start learning about the Arduino ecosystem.

Switching over to R, we couldn’t have found better words to summarize what R is than with these words found in the book Advanced R by Hadley Wickham: Despite its sometimes frustrating quirks, R is, at its heart, an elegant and beautiful language, well tailored for data science. πŸ’™

With all this said, a fine convergence can be struck between the two: data. Consider this very simple example. We want the Arduino board to turn an LED (Light Emitting Diode) ON once it receives a 1 and OFF once it receives a 0. If one can get a way of sending some data (1 or 0) to the board’s microcontroller, then, the set objective will be achieved sooner or later. This will serve as the basis of our post: Exploring the interoperability between R and Arduino by establishing a flow of data between the two and having instructions on the board’s microcontroller that execute commands, based on the data received. In this context, R will be handling data and all the hustle associated with it and sending it to Arduino. Arduino on the other hand will be actuating peripherals based on the data it has received.

How will we achieve this? πŸ₯ πŸ₯ … using Arduino’s capability to be programmed directly via a serial port (more on this later).

Before we strap in

Below is a quick overview of what we’ll use to demonstrate the interoperability of R and Arduino:

  1. First, we’ll send a series of data defining the brightness (in the range 0% - 100%) of 3 LEDs from the Rstudio IDE to the Arduino’s serial port.

  2. An Arduino script waits until serial data is available, extracts the brightness values for the 3 LEDs, maps them to analog values (0 - 255), instructs the board’s microcontroller to write these values to the LEDs and then sends the mapped values (0 - 255) to the Rstudio-Arduino serial interface.

  3. Rstudio will read the values sent from Arduino to the serial. We’ll then use these values (in the range 0 - 255) to create a data set for rotating a servo motor and pass these values to the serial interface.

  4. Once the Arduino detects there is serial data again, it reads each value (0 - 255) on the serial interface, maps it to an angle rotation value (in the range 0Β° - 180Β°), rotates the servo and sends the angle back to the serial interface.

  5. Finally, we’ll read the motor angles and wrap it off with some ggplot πŸ“ˆ in Rstudio.

When all is said and done, this is what we want to achieve using R and Arduino, working in tandem:



To follow along, you’ll need an Arduino IDE and Board (we used the Arduino UNO board at the time of writing this), Red, Green and Blue LEDs, a Servo Motor (SG90) and Jumper wires, all wrapped up in enthusiasm tinged with some spunk! The hardware components will be connected as shown below:


For the RStudio part, we’ll be requiring libraries in the Tidyverse, the Magrittr package, Plotly package and the Serial package. The tidyverse is a collection of R packages designed for data science tasks such as data wrangling and visualization. The serial package enables reading and writing binary and ASCII data to RS232/RS422/RS485 or any other virtual serial interface of the computer. The plotly package creates interactive web graphics from β€˜ggplot2’ graphs. You can have them installed as follows:

install.packages(c("tidyverse", "serial", "plotly", "magrittr"))


Through the serial interface

Time to fire up Rstudio. Let’s begin by loading the libraries we’ve just installed.

suppressPackageStartupMessages({
  library(tidyverse)
  library(serial)
  library(plotly)
  library(magrittr)
})

To obtain a list of the installed serial interfaces in your computer, simply use the serial::listPorts function.

listPorts ()
## [1] "COM11" "COM12" "COM2"  "COM6"  "COM7"  "COM9"

Great! Seems we have about six serial interfaces at our disposal. More than enough!


Setting up a serial interface

Next, we’ll create a serial port object called arduino, which represents a serial client for communication with the USB serial port where our board is connected. Among the 6, where is our Arduino UNO connected πŸ€”?

This vital information can be obtained by firing up the Arduino IDE navigating to Tools ➒ Serial Port and then selecting the appropriate port as shown in the snippet below:

**Snippet of Arduino's IDE showing the the Arduino board's serial port.**

Snippet of Arduino’s IDE showing the the Arduino board’s serial port.


In our case, the USB serial port was COM9. With this info, we should be well on our way to creating a serial interface connection with the board. This is achieved using the serial::serialConnection function. The interface parameters are such that the baud rate (specifies the number of bits being transferred per second) is set to 9600, which is the same value in the Arduino script. Also, we have specified that the transmission ends with a new line and that the transmission is complete if the end of line symbol is the carriage return cr. Now, let’s R this up!

arduino <-  serialConnection(
                           port = "COM9",
                           mode = "9600,n,8,1" ,
                           buffering = "none",
                           newline = TRUE,
                           eof = "",
                           translation = "cr",
                           handshake = "none",
                           buffersize = 4096
                           
                           )


Now that the serial interface is in place, the next step is initialising the interface and keeping it open for later usage such as writing and reading data from it. Once serial::isOpen initialises the interface, the Arduino board blinks. This is because the board resets once a serial port is opened to allow the bootloader to receive a new sketch.

serial::isOpen tests whether the connection is open or not.

open(arduino)

# testing whether the connection is open or not
isOpen(arduino)
## [1] TRUE


Writing data from RStudio to the serial interface

At this point, we are all set to write some data to the serial interface. The values we’ll be sending to the serial interface are in the range of 0 - 100, expressing the desired percentage of LED brightness. Also, we’ll append letter characters R G B to help Arduino distinguish what value is written to what LED. We’ll see this in just a moment.

In the meantime, let’s just whip up some R script that creates a data set with 3 columns r g b and which appends a letter to their brightness values.

n <-  60
arduino_input <- tibble(
  r = (sample(1:100, size = n, replace = T) %>%
                     paste('R', sep = '')),
  g = (sample(1:100, size = n, replace = T) %>%
                     paste('G', sep = '')),
  b = (sample(1:100, size = n, replace = T) %>%
                     paste('B', sep = ''))
)

# get a glimpse of the arduino_input

glimpse(arduino_input)
## Rows: 60
## Columns: 3
## $ r <chr> "73R", "33R", "12R", "16R", "83R", "6R", "74R", "42R", "33R", "99...
## $ g <chr> "68G", "95G", "93G", "78G", "4G", "88G", "37G", "89G", "17G", "34...
## $ b <chr> "95B", "93B", "36B", "56B", "61B", "71B", "73B", "80B", "36B", "1...


With that brief glance tibble::glimpse() has accorded us, we are able to observe that the LED values that will be written to the serial interface are of type character. We can blame/thank paste() for this, but in retrospect, this is the desired data type for serial communications.

So this is it, in the case of serial communication, the ASCII character set is used to represent all the letters, numbers, symbols, and special commands that you might want to send.

The chunk below uses serial::write.serialConnection() to write the LED values to the serial port row by row.

Now, let’s SHIP IT!

# good practice to close then open the connection again
close(arduino)
open(arduino)

# gives enough time for the board to reset once a serial interface
# is initiated
Sys.sleep(2)


for (r in seq_len(n)){
  Sys.sleep(0.1)
  write.serialConnection(arduino, paste(arduino_input[r,], collapse = ''))
}


You are probably wondering, How will character values light up the LEDs?. Our Arduino script will handle this as shown in the snippet below:

**Snippet of Arduino's IDE showing how Arduino will make decisions based on incoming data.**

Snippet of Arduino’s IDE showing how Arduino will make decisions based on incoming data.


The main Arduino program loop waits until serial data is available (if(Serial.available())), stores the data on the interface as a character vector (mychar) and then runs it through a series of switch statements. In particular, a switch statement compares the value of a variable to the values specified in case statements. When a case statement is found whose value matches that of the variable, the code in that case statement is run.

Let’s take an example. For instance, say the character vector sent from RStudio is 94R44G22B. The first match case is case '0'...'9': which is converted to an integer by subtracting the zero-valued character.

\(t\), which represents LED brightness in the range 0 - 100% is first initialised to \(0\). The first value to be read will be \(9\). Consequently, the value of \(t\) becomes:

\(t\,=\,0\,\times\,10\,+\,('9'\,-\,'0')\)

\(\therefore\,t\,=\,9\)

The second value is a \(4\) and which matches the first case. The new value of \(t\) becomes:

\(t\,=\,9\,\times\,10\,+\,('4'\,-\,'0')\)

\(\therefore\,t\,=\,94\)

The next value that is read is an \('R'\) which matches case 'R'. In this case, the value of \(t\,=\,94\) is remapped to an analog value in the range \(0-255\) that can be used with analogWrite() functions.

For folks wondering why there is need for remapping values that go to the analogWrite() function, it’s coming right at you. So, if we just wanted to blink and LED ON and OFF, we would simply send a digital HIGH (5v) or a digital LOW (0v). But what if we want to output a voltage other than 0v or 5v, such as varying the brightness of the LED? Well, we can’t- unless we are using a digital-to-analog converter (DAC) integrated circuit.

However, one can get pretty close to generating analog output values by using a trick called pulse-width modulation (PWM). Select pins on each Arduino can use the analogWrite() command to generate PWM signals that can emulate a pure analog signal when used with certain peripherals. These pins are marked with a ~ on the board. On the Arduino Uno, pins 3, 5, 6, 9, 10, and 11 are PWM pins.

The PWM output is an 8-bit value. In other words, you can write values from \(0\) to \(2^8 - 1\), or \(0\) to \(255\). In the case of our LED circuit, mapping the output to 255 will result in full brightness, and 0 will result in the LED turning off, with the brightness varying between these two values.

Okay, now back to case 'R'! Once an analog value is written to the LED, one interesting instruction follows Serial.println(rval). As you might have guessed, this is Arduino’s way of saying: Write that value to the serial port!. After this is done, the value of t is set back to 0 and the next input characters are run through the subsequent cases.


Roger that!

Now, let’s read the mapped values sent to the serial port connection by Arduino. read.serialConnection() is put to the test 🀞.

# reading mapped data sent from Arduino
data_frm_arduino <- tibble(
  capture.output(cat(read.serialConnection(arduino,n=0)))
  ) 

# select the first 9 rows
data_frm_arduino %>% slice_head(n = 9)


Wow! Yeah! We’ve got our re-mapped data back home πŸ’οΈ! Something interesting to note is that read.serialConnection() reads the whole buffer at once. Also, the data is in a long format since reading takes place per line. This can probably be corrected by playing around with end-of-line characters specified at the translation option in serial::serialConnection but we’ll leave it at that, for now.

Would we be staying true to R and the principles of Tidy data if we left the data_frm_arduino data set as it is? Nope! Well then, let’s get our wrangling on!

data_frm_arduino %<>% tibble(
  # assigning values to their approriate LED
  led_names = rep_along(seq_len(nrow(data_frm_arduino)), c('mapped_r','mapped_g','mapped_b'))) %>%
  # renaming the first column
  rename("led_val" = 1) %>%
  group_by(led_names) %>%
  # adding identifiers as required by pivot_wider
  mutate(row = row_number()) %>%
  # creating new columns using 'led_val' values
  pivot_wider(names_from = led_names, values_from = led_val) %>%
  # dropping the 'row' column
  select(-row) %>%
  # converting all columns to data type integer
  mutate_all(as.integer)

data_frm_arduino %>% slice_head(n = 10)


A data set showing the initial LED values sent from RStudio to Arduino’s serial port and the mapped values sent back would communicate things better. Let’s get right at it.

combined_data <- as_tibble(
  # merge the two data sets
  cbind(arduino_input, data_frm_arduino)) %>%
  # drop non numeric characters eg R, G, B
  mutate(across(where(is.character), ~parse_number(.x)), across(where(is.double), as.integer)) %>% 
  # reorder columns.. dplyr::relocate can do the trick too
  select(c(1, 4, 2, 5, 3, 6))

combined_data %>% slice_head(n = 10)


One last trip: To a servo and back

At this point, we’ve already sent data from RStudio to Arduino, remapped it, lighted up some LEDs, and sent the remapped data back to the RStudio IDE. That’s been an incredible voyage by all means. Let’s wrap it with one final adventure: driving a servo motor.

So, this is it. From the RStudio IDE’s end, we’ll create a new dataset from the received remapped values (0-255), append a terminating character, and write these values to the Arduino’s serial port.

# creating a new dataset that selects values in the order:
# maxmimum of received LED values then minimum of the LED values
# then maximum then minimum and on and on we go ...


row_min <- tibble(min_input = data_frm_arduino %>% apply(1,min)) %>%
                    # select even rows
                    filter(row_number() %% 2 == 0)

servo_input <- tibble(servo_in = data_frm_arduino %>% apply(1,max)) 
                    

# replacing the even rows with a minimum value 
servo_input[c(1:n)[c(F,T)],] <- row_min

# appending a terminating character
servo_input %<>% mutate(servo_in = servo_in %>% paste('S', sep = ''))

And off we write the values to the serial interface.

close(arduino)
open(arduino)
Sys.sleep(2)
for (r in seq_len(n)){
  Sys.sleep(1)
  write.serialConnection(arduino, paste(servo_input[r,], collapse = ''))
 
}


The main Arduino program loop waits until serial data is available, extracts the integer value, remaps the value from the range \(0 - 255\) to a servo angle \(0 - 179\), and the writes this value to the servo. Our stalwart board then prints the mapped angle value to the serial interface. An Arduino snippet where this magic happens is as shown:


Now, let’s get what Arduino echoed back at us and do some data wrangling while at it.

angl_frm_ard <- tibble(
  # reading mapped angles sent from Arduino
  capture.output(cat(read.serialConnection(arduino,n=0)))) %>%
  # renaming first column
  rename("mapped_servo_angles" = 1) %>% 
  mutate_all(as.integer)

# select the first 10 rows 
angl_frm_ard %>% slice_head(n = 10)
############### what we sent vs what we received ##############

combined_angles <- as_tibble(
  # merge the two data sets
  cbind(servo_input, angl_frm_ard)) %>%
  # drop non numeric character S
  mutate(across(where(is.character), ~parse_number(.x)),
         across(where(is.double), as.integer))

combined_angles %>%
  slice_head(n = 10)

Much better! Column servo_in shows the data we sent to Arduino, while mapped_servo_angles represents what Arduino wrote to the servo and echoed back at us. Such friendship 🀭!


Now, as they say, all’s well that ends with an informative visualization (to be taken with a grain of salt πŸ˜€).

Let’s see the sweep made by the servo at each instance of writing angle data.

theme_set(theme_light())

plt <- angl_frm_ard %>%
  ggplot(mapping = aes(x = 1:nrow(angl_frm_ard),
                       y = mapped_servo_angles)) +
  geom_line() +
  # smooth line fitted to the data
  geom_smooth(se = F) +
  labs(x = "Count",
       y = "Servo angle", 
       title = "Variation of servo angle at each count instance")+
  theme(plot.title = element_text(hjust = 0.5))

ggplotly(plt)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

That must have been quite an erratic trajectory! But yeah, we just wanted to illustrate that a servo could be rotated. We’ll get to real-life and more practical use-cases real soon.


Wrapup

It’s time we wrapped things up.

In this post, we really tried to show the bi-directional flow of data between RStudio IDE and the Arduino. At each instance, the data we sent to the Arduino got transormed, actuated a peripheral and then echoed back. The data we got back then went through some tidying and wrangling to put it in the right format that would be executed by the Arduino in subsequent operations.

We hope this got you up to speed with both Arduino and R, and ignited a genuine interest to explore the amazing things one can do with these two beauties!

We really look forward to exploring, learning and Ring more on this topic … soon.

Thanks for reading!

Be sure to check out great blogs, tutorials and other formats of R resources coming out every day at RWeekly.org!

Till then,

Happy Learning πŸ‘©πŸ½β€πŸ’» πŸ‘¨β€πŸ’» πŸ‘¨πŸΎβ€πŸ’» πŸ‘©β€πŸ’» ,

Eric (R_ic) (Gold Microsoft Learn Student Ambassador), Ian (Co-organizer DekutR Data Science Community) and Sam (Co-organizer DekutR Data Science Community).


Reference Material

Appendix: Arduino script used

Here is the Arduino script that was uploaded to the board. It is responsible for instructing the board what to do based on the data it receives from RStudio IDE.

# include <Servo.h>

// for storing readings for RGB leds and servo
int rval = 0;
int gval = 0;
int bval = 0;
int sval = 0;

int RED = 6; // Red LED on pin 6
int GREEN = 5;
int BLUE = 3;
int SERVO = 9; // Servo on Pin 9



Servo myServo;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); // serial port at 9600 baud
  
  // setting pins as output
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);

  // Attaching the Servo object
  myServo.attach(SERVO);
  

}

void loop() {
  if (Serial.available()){
   // creates variables visible to only 1 function. They persist
   // beyond the function call and preserve their value
   static int t = 0;
   
   char mychar = Serial.read();

   switch(mychar){
    //mychar: a variable whose value to compare with various cases.
    case '0'...'9':
      t = t * 10 + mychar - '0';
      break;     
    case 'R':
    {
      rval = map(t, 0, 100, 0, 255);
      analogWrite(RED, rval);
      Serial.println(rval);  
    }
    t = 0;
    break;
    case 'G':
    {
      gval = map(t, 0, 100, 0, 255);
      analogWrite(GREEN, gval);
      Serial.println(gval);
    }
    t = 0;
    break;

    case 'B':
    {
      bval = map(t, 0, 100, 0, 255);
      analogWrite(BLUE, bval);
      Serial.println(bval);
    }
    t = 0;
    break;

    case 'S':
    {
      // map analogue LED value to an angle between 0 and 180 degrees
      sval = map(t, 0, 255, 0, 179);
      Serial.println(sval);
      delay(5);
      myServo.write (sval);
      delay(150);
      
      
    }
    t = 0;
    break;
    
    
   }

    
  }

}
LS0tDQp0aXRsZTogIioqV2hhdCB3ZSBSIGFib3V0IHdoZW4gd2UgUiBhYm91dCBSIGFuZCBBcmR1aW5vKioiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY3NzOiBzdHlsZV81LmNzcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBoaWdobGlnaHQ6IGJyZWV6ZWRhcmsNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KLS0tDQoNCjxicj4NCklmIHlvdSBoYXZlIHJlYWQgSGFydWtpIE11cmFrYW1pJ3MgYm9vazogYFdoYXQgSSB0YWxrIGFib3V0IHdoZW4gSSB0YWxrIGFib3V0IFJ1bm5pbmdgLCB5b3UnbGwgcHJvYmFibHkgYmUgZXhwZWN0aW5nIHNvbWUgd2lzZSBzYXlpbmcganVzdCBhYm91dCBub3cg8J+YhC4gUmVtYWluaW5nIHRydWUgdG8gdGhlIGJvb2ssIEkgZ3Vlc3Mgd2UnbGwgaGF2ZSB0byBnZXQgY3JlYXRpdmUsIHJpZ2h0PyBTbywgdGhlcmUncyB0aGlzIHdpc2Ugc2F5aW5nIHRoYXQgZ29lcyBsaWtlIHRoaXMsIGBWZXJ5IGZldyB0aGluZ3MgaW5kZWVkIGFyZSByZWFsbHkgaW1wYVJzaWJsZWAuIFdpdGggdGhlIGVtcGhhc2lzIG9uIHRoZSBgUmAgb2YgY291cnNlLCBJIGd1ZXNzIHlvdSBhcmUgZ2V0dGluZyB0aGUgZ2lzdCBvZiBvdXIgYCp3aXNlKmAgc2F5aW5nIPCfkq0uDQoNCkFueWhvdywgbGV0J3MgZ2V0IGJhY2sgdG8gdGhlIHNlcmlvdXMgc3R1ZmYuIFdoYXQgY291bGQgd2UgYWN0dWFsbHkgdGFsayBhYm91dCB3aGVuIHdlIHRhbGsgYWJvdXQgUiBvciBBcmR1aW5vLCBvciBtb3JlIGludGVyZXN0aW5nbHkgUiBhbmQgQXJkdWlubz8gRnJhbmtseSBzcGVha2luZywgdGhlIG5hbWUgYEFyZHVpbm9gIGhhcyBhIG5pY2UgcmluZyB0byBpdCAoc2VsZmlzaGx5IGJlY2F1c2UgaXQgc3RhcnRzIHdpdGggYW4gUiBzb3VuZCksIGFuZCBib3RoIGhhdmUgYSB0aGluZyBmb3IgY29sb3IgYmx1ZSDwn6SQLiANCg0KQnV0IHRoYXQncyBiZXNpZGUgdGhlIHBvaW50LiBQdXR0aW5nIGl0IHZlcnkgc2ltcGx5LCBbYEFyZHVpbm9gXShodHRwczovL3d3dy5hcmR1aW5vLmNjL2VuL0d1aWRlL0ludHJvZHVjdGlvbikgaXMgYW4gb3Blbi1zb3VyY2UgZWxlY3Ryb25pY3MgcGxhdGZvcm0gYmFzZWQgb24gZWFzeS10by11c2UgaGFyZHdhcmUgKGBBcmR1aW5vIEJvYXJkYCkgYW5kIHNvZnR3YXJlIChgQXJkdWlubyBJREVgKS4gT25lIGNhbiB0ZWxsIHRoZSBib2FyZCB3aGF0IHRvIGRvIGlmIG9uZSBoYXMgdGhlIGNvcnJlY3QgZm9ybSBvZiBkYXRhIGFuZCBhIHNldCBvZiBpbnN0cnVjdGlvbnMgZm9yIHByb2Nlc3NpbmcgdGhlIGRhdGEgYW5kIHBlcmZvcm1pbmcgc3Vic2VxdWVudCBvcGVyYXRpb25zLiBUaGUgQXJkdWlub+KAmXMgbWljcm9jb250cm9sbGVyIGlzIHJlc3BvbnNpYmxlIGZvciBob2xkaW5nIGFsbCB5b3VyIGNvbXBpbGVkIGNvZGUgYW5kIGV4ZWN1dGluZyB0aGUgY29tbWFuZHMgeW91IHNwZWNpZnkuIFRoZSBBcmR1aW5vIFNvZnR3YXJlIG9uIHRoZSBvdGhlciBoYW5kIGlzIHRoZSBib2FyZCdzIElERSB3aGVyZSBvbmUgd3JpdGVzIHRoZSBzZXQgb2YgaW5zdHJ1Y3Rpb25zIGdvdmVybmluZyB0aGUgYm9hcmQuIFRoZSBnZXR0aW5nIFtzdGFydGVkIGd1aWRlXShodHRwczovL3d3dy5hcmR1aW5vLmNjL2VuL0d1aWRlKSB3b3VsZCBiZSBhIGdvb2QgcGxhY2UgdG8gc3RhcnQgbGVhcm5pbmcgYWJvdXQgdGhlIEFyZHVpbm8gZWNvc3lzdGVtLg0KDQpTd2l0Y2hpbmcgb3ZlciB0byBSLCB3ZSBjb3VsZG4ndCBoYXZlIGZvdW5kIGJldHRlciB3b3JkcyB0byBzdW1tYXJpemUgd2hhdCBgUmAgaXMgdGhhbiB3aXRoIHRoZXNlIHdvcmRzIGZvdW5kIGluIHRoZSBib29rIFtBZHZhbmNlZCBSXShodHRwczovL2Fkdi1yLmhhZGxleS5uei9pbnRyb2R1Y3Rpb24uaHRtbCkgYnkgYEhhZGxleSBXaWNraGFtYDoNCkRlc3BpdGUgaXRzIHNvbWV0aW1lcyBmcnVzdHJhdGluZyBxdWlya3MsIFIgaXMsIGF0IGl0cyBoZWFydCwgYW4gZWxlZ2FudCBhbmQgYmVhdXRpZnVsIGxhbmd1YWdlLCB3ZWxsIHRhaWxvcmVkIGZvciBkYXRhIHNjaWVuY2UuIPCfkpkNCg0KV2l0aCBhbGwgdGhpcyBzYWlkLCBhIGZpbmUgY29udmVyZ2VuY2UgY2FuIGJlIHN0cnVjayBiZXR3ZWVuIHRoZSB0d286IGBkYXRhYC4gQ29uc2lkZXIgdGhpcyB2ZXJ5IHNpbXBsZSBleGFtcGxlLiBXZSB3YW50IHRoZSBBcmR1aW5vIGJvYXJkIHRvIHR1cm4gYW4gTEVEIChMaWdodCBFbWl0dGluZyBEaW9kZSkgT04gb25jZSBpdCByZWNlaXZlcyBhIGAxYCBhbmQgT0ZGIG9uY2UgaXQgcmVjZWl2ZXMgYSBgMGAuIElmIG9uZSBjYW4gZ2V0IGEgd2F5IG9mIHNlbmRpbmcgc29tZSBkYXRhICgxIG9yIDApIHRvIHRoZSBib2FyZCdzIG1pY3JvY29udHJvbGxlciwgdGhlbiwgdGhlIHNldCBvYmplY3RpdmUgd2lsbCBiZSBhY2hpZXZlZCBzb29uZXIgb3IgbGF0ZXIuIFRoaXMgd2lsbCBzZXJ2ZSBhcyB0aGUgYmFzaXMgb2Ygb3VyIHBvc3Q6IEV4cGxvcmluZyB0aGUgaW50ZXJvcGVyYWJpbGl0eSBiZXR3ZWVuIFIgYW5kIEFyZHVpbm8gYnkgZXN0YWJsaXNoaW5nIGEgZmxvdyBvZiBkYXRhIGJldHdlZW4gdGhlIHR3byBhbmQgaGF2aW5nIGluc3RydWN0aW9ucyBvbiB0aGUgYm9hcmQncyBtaWNyb2NvbnRyb2xsZXIgdGhhdCBleGVjdXRlIGNvbW1hbmRzLCBiYXNlZCBvbiB0aGUgZGF0YSByZWNlaXZlZC4gSW4gdGhpcyBjb250ZXh0LCBSIHdpbGwgYmUgaGFuZGxpbmcgZGF0YSBhbmQgYWxsIHRoZSBodXN0bGUgYXNzb2NpYXRlZCB3aXRoIGl0IGFuZCBzZW5kaW5nIGl0IHRvIEFyZHVpbm8uIEFyZHVpbm8gb24gdGhlIG90aGVyIGhhbmQgd2lsbCBiZSBhY3R1YXRpbmcgcGVyaXBoZXJhbHMgYmFzZWQgb24gdGhlIGRhdGEgaXQgaGFzIHJlY2VpdmVkLg0KDQpIb3cgd2lsbCB3ZSBhY2hpZXZlIHRoaXM/IPCfpYEg8J+lgSAuLi4gdXNpbmcgQXJkdWlubydzIGNhcGFiaWxpdHkgdG8gYmUgcHJvZ3JhbW1lZCBkaXJlY3RseSB2aWEgYSBzZXJpYWwgcG9ydCAobW9yZSBvbiB0aGlzIGxhdGVyKS4NCg0KIyAqKkJlZm9yZSB3ZSBzdHJhcCBpbioqDQoNCkJlbG93IGlzIGEgcXVpY2sgb3ZlcnZpZXcgb2Ygd2hhdCB3ZSdsbCB1c2UgdG8gZGVtb25zdHJhdGUgdGhlIGludGVyb3BlcmFiaWxpdHkgb2YgUiBhbmQgQXJkdWlubzoNCg0KMS4gRmlyc3QsIHdlJ2xsIHNlbmQgYSBzZXJpZXMgb2YgZGF0YSBkZWZpbmluZyB0aGUgYnJpZ2h0bmVzcyAoaW4gdGhlIHJhbmdlIDAlIC0gMTAwJSkgb2YgMyBMRURzIGZyb20gdGhlIFJzdHVkaW8gSURFIHRvIHRoZSBBcmR1aW5vJ3Mgc2VyaWFsIHBvcnQuDQoNCjIuIEFuIEFyZHVpbm8gc2NyaXB0IHdhaXRzIHVudGlsIHNlcmlhbCBkYXRhIGlzIGF2YWlsYWJsZSwgZXh0cmFjdHMgdGhlIGJyaWdodG5lc3MgdmFsdWVzIGZvciB0aGUgMyBMRURzLCBtYXBzIHRoZW0gdG8gYW5hbG9nIHZhbHVlcyAoMCAtIDI1NSksIGluc3RydWN0cyB0aGUgYm9hcmQncyBtaWNyb2NvbnRyb2xsZXIgdG8gd3JpdGUgdGhlc2UgdmFsdWVzIHRvIHRoZSBMRURzIGFuZCB0aGVuIHNlbmRzIHRoZSBtYXBwZWQgdmFsdWVzICgwIC0gMjU1KSB0byB0aGUgUnN0dWRpby1BcmR1aW5vIHNlcmlhbCBpbnRlcmZhY2UuIA0KDQozLiBSc3R1ZGlvIHdpbGwgcmVhZCB0aGUgdmFsdWVzIHNlbnQgZnJvbSBBcmR1aW5vIHRvIHRoZSBzZXJpYWwuIFdlJ2xsIHRoZW4gdXNlIHRoZXNlIHZhbHVlcyAoaW4gdGhlIHJhbmdlIDAgLSAyNTUpIHRvIGNyZWF0ZSBhIGRhdGEgc2V0IGZvciByb3RhdGluZyBhIHNlcnZvIG1vdG9yIGFuZCBwYXNzIHRoZXNlIHZhbHVlcyB0byB0aGUgc2VyaWFsIGludGVyZmFjZS4NCg0KNC4gT25jZSB0aGUgQXJkdWlubyBkZXRlY3RzIHRoZXJlIGlzIHNlcmlhbCBkYXRhIGFnYWluLCBpdCByZWFkcyBlYWNoIHZhbHVlICgwIC0gMjU1KSBvbiB0aGUgc2VyaWFsIGludGVyZmFjZSwgbWFwcyBpdCB0byBhbiBhbmdsZSByb3RhdGlvbiB2YWx1ZSAoaW4gdGhlIHJhbmdlIDDCsCAtIDE4MMKwKSwgcm90YXRlcyB0aGUgc2Vydm8gYW5kIHNlbmRzIHRoZSBhbmdsZSBiYWNrIHRvIHRoZSBzZXJpYWwgaW50ZXJmYWNlLg0KDQo1LiBGaW5hbGx5LCB3ZSdsbCByZWFkIHRoZSBtb3RvciBhbmdsZXMgYW5kIHdyYXAgaXQgb2ZmIHdpdGggc29tZSBnZ3Bsb3Qg8J+TiCBpbiBSc3R1ZGlvLg0KDQpXaGVuIGFsbCBpcyBzYWlkIGFuZCBkb25lLCB0aGlzIGlzIHdoYXQgd2Ugd2FudCB0byBhY2hpZXZlIHVzaW5nIFIgYW5kIEFyZHVpbm8sIHdvcmtpbmcgaW4gdGFuZGVtOg0KDQo8YnI+DQoNCg0KPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9FVktXM1c3dkhWOCIgZnJhbWVib3JkZXI9IjAiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4NCg0KDQo8YnI+DQoNCg0KDQoNCg0KVG8gZm9sbG93IGFsb25nLCB5b3UnbGwgbmVlZCBhbiBBcmR1aW5vIElERSBhbmQgQm9hcmQgKHdlIHVzZWQgdGhlIGBBcmR1aW5vIFVOT2AgYm9hcmQgYXQgdGhlIHRpbWUgb2Ygd3JpdGluZyB0aGlzKSwgUmVkLCBHcmVlbiBhbmQgQmx1ZSBMRURzLCBhIFNlcnZvIE1vdG9yIChgU0c5MGApIGFuZCBKdW1wZXIgd2lyZXMsIGFsbCB3cmFwcGVkIHVwIGluIGVudGh1c2lhc20gdGluZ2VkIHdpdGggc29tZSBzcHVuayEgVGhlIGhhcmR3YXJlIGNvbXBvbmVudHMgd2lsbCBiZSBjb25uZWN0ZWQgYXMgc2hvd24gYmVsb3c6DQo8YnI+DQoNCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9Rn0NCmkgPSAxDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoew0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoRUJJbWFnZSkNCmxpYnJhcnkoZHBseXIpDQp9KQ0KDQoNCmltZ19maWxlcyA8LSBsaXN0LmZpbGVzKA0KICBwYXRoID0gIkM6L1VzZXJzL2tlcmFzL09uZURyaXZlIC0gTWljcm9zb2Z0IFN0dWRlbnQgUGFydG5lcnMvYVJkdWluby9yZXNvdXJjZXMvIiwgZnVsbC5uYW1lcyA9IFRSVUUgKQ0KcmVhZEltYWdlKGltZ19maWxlc1tpXSkgJT4lIGRpc3BsYXkobWV0aG9kID0gJ3Jhc3RlcicpDQpgYGANCg0KPGJyPg0KDQpGb3IgdGhlIFtSU3R1ZGlvXShodHRwczovL3JzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvI2Rvd25sb2FkKSBwYXJ0LCB3ZSdsbCBiZSByZXF1aXJpbmcgbGlicmFyaWVzIGluIHRoZSBUaWR5dmVyc2UsIHRoZSBNYWdyaXR0ciBwYWNrYWdlLCBQbG90bHkgcGFja2FnZSBhbmQgdGhlIFNlcmlhbCBwYWNrYWdlLiBUaGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZykgaXMgYSBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgZGVzaWduZWQgZm9yIGRhdGEgc2NpZW5jZSB0YXNrcyBzdWNoIGFzIGRhdGEgd3JhbmdsaW5nIGFuZCB2aXN1YWxpemF0aW9uLiBUaGUgW3NlcmlhbF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NlcmlhbC9pbmRleC5odG1sKSBwYWNrYWdlIGVuYWJsZXMgcmVhZGluZyBhbmQgd3JpdGluZyBiaW5hcnkgYW5kIEFTQ0lJIGRhdGEgdG8gUlMyMzIvUlM0MjIvUlM0ODUgb3IgYW55IG90aGVyIHZpcnR1YWwgc2VyaWFsIGludGVyZmFjZSBvZiB0aGUgY29tcHV0ZXIuIFRoZSBbcGxvdGx5XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcGxvdGx5L2luZGV4Lmh0bWwpIHBhY2thZ2UgY3JlYXRlcyBpbnRlcmFjdGl2ZSB3ZWIgZ3JhcGhpY3MgZnJvbSAnZ2dwbG90MicgZ3JhcGhzLiBZb3UgY2FuIGhhdmUgdGhlbSBpbnN0YWxsZWQgYXMgZm9sbG93czoNCg0KDQpgYGANCmluc3RhbGwucGFja2FnZXMoYygidGlkeXZlcnNlIiwgInNlcmlhbCIsICJwbG90bHkiLCAibWFncml0dHIiKSkNCg0KYGBgDQoNCjxicj4NCg0KIyAqKlRocm91Z2ggdGhlIHNlcmlhbCBpbnRlcmZhY2UqKiANCg0KVGltZSB0byBmaXJlIHVwIFJzdHVkaW8uIExldCdzIGJlZ2luIGJ5IGxvYWRpbmcgdGhlIGxpYnJhcmllcyB3ZSd2ZSBqdXN0IGluc3RhbGxlZC4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoew0KICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCiAgbGlicmFyeShzZXJpYWwpDQogIGxpYnJhcnkocGxvdGx5KQ0KICBsaWJyYXJ5KG1hZ3JpdHRyKQ0KfSkNCg0KYGBgDQoNClRvIG9idGFpbiBhIGxpc3Qgb2YgdGhlIGluc3RhbGxlZCBzZXJpYWwgaW50ZXJmYWNlcyBpbiB5b3VyIGNvbXB1dGVyLCBzaW1wbHkgdXNlIHRoZSBgc2VyaWFsOjpsaXN0UG9ydHNgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCg0KbGlzdFBvcnRzICgpDQoNCmBgYA0KR3JlYXQhIFNlZW1zIHdlIGhhdmUgYWJvdXQgc2l4IHNlcmlhbCBpbnRlcmZhY2VzIGF0IG91ciBkaXNwb3NhbC4gTW9yZSB0aGFuIGVub3VnaCENCg0KPGJyPg0KDQojIyMgKipTZXR0aW5nIHVwIGEgc2VyaWFsIGludGVyZmFjZSoqDQoNCk5leHQsIHdlJ2xsIGNyZWF0ZSBhIHNlcmlhbCBwb3J0IG9iamVjdCBjYWxsZWQgYGFyZHVpbm9gLCB3aGljaCByZXByZXNlbnRzIGEgc2VyaWFsIGNsaWVudCBmb3IgY29tbXVuaWNhdGlvbiB3aXRoIHRoZSBVU0Igc2VyaWFsIHBvcnQgd2hlcmUgb3VyIGJvYXJkIGlzIGNvbm5lY3RlZC4gQW1vbmcgdGhlIDYsIHdoZXJlIGlzIG91ciBgQXJkdWlubyBVTk9gIGNvbm5lY3RlZCDwn6SUPw0KDQpUaGlzIHZpdGFsIGluZm9ybWF0aW9uIGNhbiBiZSBvYnRhaW5lZCBieSBmaXJpbmcgdXAgdGhlIGBBcmR1aW5vIElERWAgbmF2aWdhdGluZyB0byBUb29scyDinqIgU2VyaWFsIFBvcnQgYW5kIHRoZW4gc2VsZWN0aW5nIHRoZSBhcHByb3ByaWF0ZSBwb3J0IGFzIHNob3duIGluIHRoZSBzbmlwcGV0IGJlbG93Og0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GLCBmaWcuY2FwPSAiKipTbmlwcGV0IG9mIEFyZHVpbm8ncyBJREUgc2hvd2luZyB0aGUgdGhlIEFyZHVpbm8gYm9hcmQncyBzZXJpYWwgcG9ydC4qKiJ9DQppID0gaSArIDENCg0KDQoNCmltZ19maWxlcyA8LSBsaXN0LmZpbGVzKA0KICBwYXRoID0gIkM6L1VzZXJzL2tlcmFzL09uZURyaXZlIC0gTWljcm9zb2Z0IFN0dWRlbnQgUGFydG5lcnMvYVJkdWluby9yZXNvdXJjZXMvIiwgZnVsbC5uYW1lcyA9IFRSVUUgKQ0KcmVhZEltYWdlKGltZ19maWxlc1tpXSkgJT4lIGRpc3BsYXkobWV0aG9kID0gJ3Jhc3RlcicpDQpgYGANCg0KDQo8YnI+DQpJbiBvdXIgY2FzZSwgdGhlIFVTQiBzZXJpYWwgcG9ydCB3YXMgYENPTTlgLiBXaXRoIHRoaXMgaW5mbywgd2Ugc2hvdWxkIGJlIHdlbGwgb24gb3VyIHdheSB0byBjcmVhdGluZyBhIHNlcmlhbCBpbnRlcmZhY2UgY29ubmVjdGlvbiB3aXRoIHRoZSBib2FyZC4gVGhpcyBpcyBhY2hpZXZlZCB1c2luZyB0aGUgYHNlcmlhbDo6c2VyaWFsQ29ubmVjdGlvbmAgZnVuY3Rpb24uIFRoZSBpbnRlcmZhY2UgcGFyYW1ldGVycyBhcmUgc3VjaCB0aGF0IHRoZSBiYXVkIHJhdGUgKHNwZWNpZmllcyB0aGUgbnVtYmVyIG9mIGJpdHMgYmVpbmcgdHJhbnNmZXJyZWQgcGVyIHNlY29uZCkgaXMgc2V0IHRvIGA5NjAwYCwgd2hpY2ggaXMgdGhlIHNhbWUgdmFsdWUgaW4gdGhlIEFyZHVpbm8gc2NyaXB0LiBBbHNvLCB3ZSBoYXZlIHNwZWNpZmllZCB0aGF0IHRoZSB0cmFuc21pc3Npb24gZW5kcyB3aXRoIGEgbmV3IGxpbmUgYW5kIHRoYXQgdGhlIHRyYW5zbWlzc2lvbiBpcyBjb21wbGV0ZSBpZiB0aGUgZW5kIG9mIGxpbmUgc3ltYm9sIGlzIHRoZSBgY2FycmlhZ2UgcmV0dXJuIGNyYC4gTm93LCBsZXQncyBSIHRoaXMgdXAhDQoNCmBgYHtyfQ0KYXJkdWlubyA8LSAgc2VyaWFsQ29ubmVjdGlvbigNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcnQgPSAiQ09NOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gIjk2MDAsbiw4LDEiICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ1ZmZlcmluZyA9ICJub25lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2xpbmUgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZW9mID0gIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2xhdGlvbiA9ICJjciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBoYW5kc2hha2UgPSAibm9uZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBidWZmZXJzaXplID0gNDA5Ng0KICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICApDQpgYGANCg0KPGJyPg0KTm93IHRoYXQgdGhlIHNlcmlhbCBpbnRlcmZhY2UgaXMgaW4gcGxhY2UsIHRoZSBuZXh0IHN0ZXAgaXMgaW5pdGlhbGlzaW5nIHRoZSBpbnRlcmZhY2UgYW5kIGtlZXBpbmcgaXQgb3BlbiBmb3IgbGF0ZXIgdXNhZ2Ugc3VjaCBhcyB3cml0aW5nIGFuZCByZWFkaW5nIGRhdGEgZnJvbSBpdC4gT25jZSBgc2VyaWFsOjppc09wZW5gIGluaXRpYWxpc2VzIHRoZSBpbnRlcmZhY2UsIHRoZSBBcmR1aW5vIGJvYXJkIGJsaW5rcy4gVGhpcyBpcyBiZWNhdXNlIHRoZSBib2FyZCByZXNldHMgb25jZSBhIHNlcmlhbCBwb3J0IGlzIG9wZW5lZCB0byBhbGxvdyB0aGUgYm9vdGxvYWRlciB0byByZWNlaXZlIGEgbmV3IHNrZXRjaC4NCg0KYHNlcmlhbDo6aXNPcGVuYCB0ZXN0cyB3aGV0aGVyIHRoZSBjb25uZWN0aW9uIGlzIG9wZW4gb3Igbm90Lg0KYGBge3J9DQpvcGVuKGFyZHVpbm8pDQoNCiMgdGVzdGluZyB3aGV0aGVyIHRoZSBjb25uZWN0aW9uIGlzIG9wZW4gb3Igbm90DQppc09wZW4oYXJkdWlubykNCmBgYA0KDQoNCjxicj4NCg0KIyMjICoqV3JpdGluZyBkYXRhIGZyb20gUlN0dWRpbyB0byB0aGUgc2VyaWFsIGludGVyZmFjZSoqDQoNCg0KQXQgdGhpcyBwb2ludCwgd2UgYXJlIGFsbCBzZXQgdG8gd3JpdGUgc29tZSBkYXRhIHRvIHRoZSBzZXJpYWwgaW50ZXJmYWNlLiBUaGUgdmFsdWVzIHdlJ2xsIGJlIHNlbmRpbmcgdG8gdGhlIHNlcmlhbCBpbnRlcmZhY2UgYXJlIGluIHRoZSByYW5nZSBvZiAwIC0gMTAwLCBleHByZXNzaW5nIHRoZSBkZXNpcmVkIHBlcmNlbnRhZ2Ugb2YgTEVEIGJyaWdodG5lc3MuIEFsc28sIHdlJ2xsIGFwcGVuZCBsZXR0ZXIgY2hhcmFjdGVycyBgUmAgYEdgIGBCYCB0byBoZWxwIEFyZHVpbm8gZGlzdGluZ3Vpc2ggd2hhdCB2YWx1ZSBpcyB3cml0dGVuIHRvIHdoYXQgTEVELiBXZSdsbCBzZWUgdGhpcyBpbiBqdXN0IGEgbW9tZW50Lg0KDQpJbiB0aGUgbWVhbnRpbWUsIGxldCdzIGp1c3Qgd2hpcCB1cCBzb21lIFIgc2NyaXB0IHRoYXQgY3JlYXRlcyBhIGRhdGEgc2V0IHdpdGggMyBjb2x1bW5zIGByYCBgZ2AgYGJgIGFuZCB3aGljaCBhcHBlbmRzIGEgbGV0dGVyIHRvIHRoZWlyIGJyaWdodG5lc3MgdmFsdWVzLg0KDQpgYGB7cn0NCm4gPC0gIDYwDQphcmR1aW5vX2lucHV0IDwtIHRpYmJsZSgNCiAgciA9IChzYW1wbGUoMToxMDAsIHNpemUgPSBuLCByZXBsYWNlID0gVCkgJT4lDQogICAgICAgICAgICAgICAgICAgICBwYXN0ZSgnUicsIHNlcCA9ICcnKSksDQogIGcgPSAoc2FtcGxlKDE6MTAwLCBzaXplID0gbiwgcmVwbGFjZSA9IFQpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgcGFzdGUoJ0cnLCBzZXAgPSAnJykpLA0KICBiID0gKHNhbXBsZSgxOjEwMCwgc2l6ZSA9IG4sIHJlcGxhY2UgPSBUKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCdCJywgc2VwID0gJycpKQ0KKQ0KDQojIGdldCBhIGdsaW1wc2Ugb2YgdGhlIGFyZHVpbm9faW5wdXQNCg0KZ2xpbXBzZShhcmR1aW5vX2lucHV0KQ0KYGBgDQoNCjxicj4NCg0KV2l0aCB0aGF0IGJyaWVmIGdsYW5jZSBgdGliYmxlOjpnbGltcHNlKClgIGhhcyBhY2NvcmRlZCB1cywgd2UgYXJlIGFibGUgdG8gb2JzZXJ2ZSB0aGF0IHRoZSBMRUQgdmFsdWVzIHRoYXQgd2lsbCBiZSB3cml0dGVuIHRvIHRoZSBzZXJpYWwgaW50ZXJmYWNlIGFyZSBvZiB0eXBlIGBjaGFyYWN0ZXJgLiBXZSBjYW4gYmxhbWUvdGhhbmsgYHBhc3RlKClgIGZvciB0aGlzLCBidXQgaW4gcmV0cm9zcGVjdCwgdGhpcyBpcyB0aGUgZGVzaXJlZCBkYXRhIHR5cGUgZm9yIHNlcmlhbCBjb21tdW5pY2F0aW9ucy4NCg0KU28gdGhpcyBpcyBpdCwgaW4gdGhlIGNhc2Ugb2Ygc2VyaWFsIGNvbW11bmljYXRpb24sIHRoZSBbQVNDSUldKGh0dHBzOi8vd3d3Lnczc2Nob29scy5jb20vY2hhcnNldHMvcmVmX2h0bWxfYXNjaWkuYXNwKSBjaGFyYWN0ZXIgc2V0IGlzIHVzZWQgdG8gcmVwcmVzZW50IGFsbCB0aGUgbGV0dGVycywgbnVtYmVycywgc3ltYm9scywgYW5kIHNwZWNpYWwgY29tbWFuZHMgdGhhdCB5b3UgbWlnaHQgd2FudCB0byBzZW5kLg0KDQoNClRoZSBjaHVuayBiZWxvdyB1c2VzIGBzZXJpYWw6OndyaXRlLnNlcmlhbENvbm5lY3Rpb24oKWAgdG8gd3JpdGUgdGhlIExFRCB2YWx1ZXMgdG8gdGhlIHNlcmlhbCBwb3J0IHJvdyBieSByb3cuDQoNCk5vdywgbGV0J3MgU0hJUCBJVCEgDQoNCmBgYHtyfQ0KIyBnb29kIHByYWN0aWNlIHRvIGNsb3NlIHRoZW4gb3BlbiB0aGUgY29ubmVjdGlvbiBhZ2Fpbg0KY2xvc2UoYXJkdWlubykNCm9wZW4oYXJkdWlubykNCg0KIyBnaXZlcyBlbm91Z2ggdGltZSBmb3IgdGhlIGJvYXJkIHRvIHJlc2V0IG9uY2UgYSBzZXJpYWwgaW50ZXJmYWNlDQojIGlzIGluaXRpYXRlZA0KU3lzLnNsZWVwKDIpDQoNCg0KZm9yIChyIGluIHNlcV9sZW4obikpew0KICBTeXMuc2xlZXAoMC4xKQ0KICB3cml0ZS5zZXJpYWxDb25uZWN0aW9uKGFyZHVpbm8sIHBhc3RlKGFyZHVpbm9faW5wdXRbcixdLCBjb2xsYXBzZSA9ICcnKSkNCn0NCg0KDQpgYGANCg0KDQo8YnI+DQoNCllvdSBhcmUgcHJvYmFibHkgd29uZGVyaW5nLCBgSG93IHdpbGwgY2hhcmFjdGVyIHZhbHVlcyBsaWdodCB1cCB0aGUgTEVEcz9gLiBPdXIgQXJkdWlubyBzY3JpcHQgd2lsbCBoYW5kbGUgdGhpcyBhcyBzaG93biBpbiB0aGUgc25pcHBldCBiZWxvdzoNCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RiwgZmlnLmNhcD0gIioqU25pcHBldCBvZiBBcmR1aW5vJ3MgSURFIHNob3dpbmcgaG93IEFyZHVpbm8gd2lsbCBtYWtlIGRlY2lzaW9ucyBiYXNlZCBvbiBpbmNvbWluZyBkYXRhLioqIn0NCmkgPSBpICsgMQ0KaW1nX2ZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICJDOi9Vc2Vycy9rZXJhcy9PbmVEcml2ZSAtIE1pY3Jvc29mdCBTdHVkZW50IFBhcnRuZXJzL2FSZHVpbm8vcmVzb3VyY2VzLyIsIGZ1bGwubmFtZXMgPSBUUlVFICkNCg0KcmVhZEltYWdlKGltZ19maWxlc1tpXSkgJT4lIGRpc3BsYXkobWV0aG9kID0gJ3Jhc3RlcicpDQpgYGANCg0KDQo8YnI+DQoNClRoZSBtYWluIEFyZHVpbm8gcHJvZ3JhbSBsb29wIHdhaXRzIHVudGlsIHNlcmlhbCBkYXRhIGlzIGF2YWlsYWJsZSAoYGlmKFNlcmlhbC5hdmFpbGFibGUoKSlgKSwgc3RvcmVzIHRoZSBkYXRhIG9uIHRoZSBpbnRlcmZhY2UgYXMgYSBjaGFyYWN0ZXIgdmVjdG9yIChgbXljaGFyYCkgYW5kIHRoZW4gcnVucyBpdCB0aHJvdWdoIGEgc2VyaWVzIG9mIFtzd2l0Y2hdKGh0dHBzOi8vd3d3LmFyZHVpbm8uY2MvcmVmZXJlbmNlL2VuL2xhbmd1YWdlL3N0cnVjdHVyZS9jb250cm9sLXN0cnVjdHVyZS9zd2l0Y2hjYXNlLykgc3RhdGVtZW50cy4gSW4gcGFydGljdWxhciwgYSBzd2l0Y2ggc3RhdGVtZW50IGNvbXBhcmVzIHRoZSB2YWx1ZSBvZiBhIHZhcmlhYmxlIHRvIHRoZSB2YWx1ZXMgc3BlY2lmaWVkIGluIGNhc2Ugc3RhdGVtZW50cy4gV2hlbiBhIGNhc2Ugc3RhdGVtZW50IGlzIGZvdW5kIHdob3NlIHZhbHVlIG1hdGNoZXMgdGhhdCBvZiB0aGUgdmFyaWFibGUsIHRoZSBjb2RlIGluIHRoYXQgY2FzZSBzdGF0ZW1lbnQgaXMgcnVuLg0KDQpMZXQncyB0YWtlIGFuIGV4YW1wbGUuIEZvciBpbnN0YW5jZSwgc2F5IHRoZSBjaGFyYWN0ZXIgdmVjdG9yIHNlbnQgZnJvbSBSU3R1ZGlvIGlzIGA5NFI0NEcyMkJgLiBUaGUgZmlyc3QgbWF0Y2ggY2FzZSBpcyBgY2FzZSAnMCcuLi4nOSc6YCB3aGljaCBpcyBjb252ZXJ0ZWQgdG8gYW4gaW50ZWdlciBieSBzdWJ0cmFjdGluZyB0aGUgemVyby12YWx1ZWQgY2hhcmFjdGVyLg0KDQokdCQsIHdoaWNoIHJlcHJlc2VudHMgTEVEIGJyaWdodG5lc3MgaW4gdGhlIHJhbmdlIGAwIC0gMTAwYCUgaXMgZmlyc3QgaW5pdGlhbGlzZWQgdG8gJDAkLiBUaGUgZmlyc3QgdmFsdWUgdG8gYmUgcmVhZCB3aWxsIGJlICQ5JC4gQ29uc2VxdWVudGx5LCB0aGUgdmFsdWUgb2YgJHQkIGJlY29tZXM6DQoNCiR0XCw9XCwwXCxcdGltZXNcLDEwXCwrXCwoJzknXCwtXCwnMCcpJA0KDQokXHRoZXJlZm9yZVwsdFwsPVwsOSQNCg0KVGhlIHNlY29uZCB2YWx1ZSBpcyBhICQ0JCBhbmQgd2hpY2ggbWF0Y2hlcyB0aGUgZmlyc3QgY2FzZS4gVGhlIG5ldyB2YWx1ZSBvZiAkdCQgYmVjb21lczoNCg0KJHRcLD1cLDlcLFx0aW1lc1wsMTBcLCtcLCgnNCdcLC1cLCcwJykkDQoNCiRcdGhlcmVmb3JlXCx0XCw9XCw5NCQNCg0KVGhlIG5leHQgdmFsdWUgdGhhdCBpcyByZWFkIGlzIGFuICQnUickIHdoaWNoIG1hdGNoZXMgYGNhc2UgJ1InYC4gSW4gdGhpcyBjYXNlLCB0aGUgdmFsdWUgb2YgJHRcLD1cLDk0JCBpcyByZW1hcHBlZCB0byBhbiBhbmFsb2cgdmFsdWUgaW4gdGhlIHJhbmdlICQwLTI1NSQgdGhhdCBjYW4gYmUgdXNlZCB3aXRoIGBhbmFsb2dXcml0ZSgpYCBmdW5jdGlvbnMuIA0KDQpGb3IgZm9sa3Mgd29uZGVyaW5nIHdoeSB0aGVyZSBpcyBuZWVkIGZvciByZW1hcHBpbmcgdmFsdWVzIHRoYXQgZ28gdG8gdGhlIGBhbmFsb2dXcml0ZSgpYCBmdW5jdGlvbiwgaXQncyBjb21pbmcgcmlnaHQgYXQgeW91Lg0KU28sIGlmIHdlIGp1c3Qgd2FudGVkIHRvIGJsaW5rIGFuZCBMRUQgT04gYW5kIE9GRiwgd2Ugd291bGQgc2ltcGx5IHNlbmQgYSBkaWdpdGFsIEhJR0ggKDV2KSBvciBhIGRpZ2l0YWwgTE9XICgwdikuIEJ1dCB3aGF0IGlmIHdlIHdhbnQgdG8NCm91dHB1dCBhIHZvbHRhZ2Ugb3RoZXIgdGhhbiAwdiBvciA1diwgc3VjaCBhcyB2YXJ5aW5nIHRoZSBicmlnaHRuZXNzIG9mIHRoZSBMRUQ/IFdlbGwsIHdlIGNhbuKAmXQtIHVubGVzcyB3ZSBhcmUgdXNpbmcgYSBkaWdpdGFsLXRvLWFuYWxvZyBjb252ZXJ0ZXIgKERBQykgaW50ZWdyYXRlZCBjaXJjdWl0Lg0KDQpIb3dldmVyLCBvbmUgY2FuIGdldCBwcmV0dHkgY2xvc2UgdG8gZ2VuZXJhdGluZyBhbmFsb2cgb3V0cHV0IHZhbHVlcyBieSB1c2luZyBhIHRyaWNrIGNhbGxlZCBbcHVsc2Utd2lkdGggbW9kdWxhdGlvbl0oaHR0cHM6Ly93d3cuYXJkdWluby5jYy9lbi9UdXRvcmlhbC9Gb3VuZGF0aW9ucy9QV00pIChQV00pLiBTZWxlY3QgcGlucyBvbiBlYWNoIEFyZHVpbm8gY2FuIHVzZSB0aGUgYGFuYWxvZ1dyaXRlKClgIGNvbW1hbmQgdG8gZ2VuZXJhdGUgUFdNIHNpZ25hbHMgdGhhdCBjYW4gZW11bGF0ZSBhIHB1cmUgYW5hbG9nIHNpZ25hbCB3aGVuIHVzZWQgd2l0aCBjZXJ0YWluIHBlcmlwaGVyYWxzLiBUaGVzZSBwaW5zIGFyZSBtYXJrZWQgd2l0aCBhIGB+YCBvbiB0aGUgYm9hcmQuIE9uIHRoZSBBcmR1aW5vIFVubywgcGlucyAzLCA1LCA2LCA5LCAxMCwgYW5kIDExIGFyZSBQV00gcGlucy4NCg0KVGhlIFBXTSBvdXRwdXQgaXMgYW4gOC1iaXQgdmFsdWUuIEluIG90aGVyIHdvcmRzLCB5b3UgY2FuIHdyaXRlIHZhbHVlcyBmcm9tICQwJCB0byAkMl44IC0gMSQsIG9yICQwJCB0byAkMjU1JC4gSW4gdGhlIGNhc2Ugb2Ygb3VyIExFRCBjaXJjdWl0LCBtYXBwaW5nIHRoZSBvdXRwdXQgdG8gMjU1IHdpbGwgcmVzdWx0IGluIGZ1bGwgYnJpZ2h0bmVzcywgYW5kIDAgd2lsbCByZXN1bHQgaW4gdGhlIExFRCB0dXJuaW5nIG9mZiwgd2l0aCB0aGUgYnJpZ2h0bmVzcyB2YXJ5aW5nDQpiZXR3ZWVuIHRoZXNlIHR3byB2YWx1ZXMuDQoNCk9rYXksIG5vdyBiYWNrIHRvIGBjYXNlICdSJ2AhIE9uY2UgYW4gYW5hbG9nIHZhbHVlIGlzIHdyaXR0ZW4gdG8gdGhlIExFRCwgb25lIGludGVyZXN0aW5nIGluc3RydWN0aW9uIGZvbGxvd3MgYFNlcmlhbC5wcmludGxuKHJ2YWwpYC4gQXMgeW91IG1pZ2h0IGhhdmUgZ3Vlc3NlZCwgdGhpcyBpcyBBcmR1aW5vJ3Mgd2F5IG9mIHNheWluZzogYFdyaXRlIHRoYXQgdmFsdWUgdG8gdGhlIHNlcmlhbCBwb3J0IWAuIEFmdGVyIHRoaXMgaXMgZG9uZSwgdGhlIHZhbHVlIG9mIHQgaXMgc2V0IGJhY2sgdG8gMCBhbmQgdGhlIG5leHQgaW5wdXQgY2hhcmFjdGVycyBhcmUgcnVuIHRocm91Z2ggdGhlIHN1YnNlcXVlbnQgY2FzZXMuDQoNCjxicj4NCg0KIyMjICoqUm9nZXIgdGhhdCEqKg0KDQpOb3csIGxldCdzIHJlYWQgdGhlIGBtYXBwZWRgIHZhbHVlcyBzZW50IHRvIHRoZSBzZXJpYWwgcG9ydCBjb25uZWN0aW9uIGJ5IEFyZHVpbm8uIGByZWFkLnNlcmlhbENvbm5lY3Rpb24oKWAgaXMgcHV0IHRvIHRoZSB0ZXN0IPCfpJ4uDQoNCmBgYHtyfQ0KIyByZWFkaW5nIG1hcHBlZCBkYXRhIHNlbnQgZnJvbSBBcmR1aW5vDQpkYXRhX2ZybV9hcmR1aW5vIDwtIHRpYmJsZSgNCiAgY2FwdHVyZS5vdXRwdXQoY2F0KHJlYWQuc2VyaWFsQ29ubmVjdGlvbihhcmR1aW5vLG49MCkpKQ0KICApIA0KDQojIHNlbGVjdCB0aGUgZmlyc3QgOSByb3dzDQpkYXRhX2ZybV9hcmR1aW5vICU+JSBzbGljZV9oZWFkKG4gPSA5KQ0KDQpgYGANCg0KPGJyPg0KDQpXb3chIFllYWghIFdlJ3ZlIGdvdCBvdXIgcmUtbWFwcGVkIGRhdGEgYmFjayBob21lIPCfkoHvuI8hIA0KU29tZXRoaW5nIGludGVyZXN0aW5nIHRvIG5vdGUgaXMgdGhhdCBgcmVhZC5zZXJpYWxDb25uZWN0aW9uKClgIHJlYWRzIHRoZSB3aG9sZSBidWZmZXIgYXQgb25jZS4gQWxzbywgdGhlIGRhdGEgaXMgaW4gYSBgbG9uZ2AgZm9ybWF0IHNpbmNlIHJlYWRpbmcgdGFrZXMgcGxhY2UgcGVyIGxpbmUuIFRoaXMgY2FuIHByb2JhYmx5IGJlIGNvcnJlY3RlZCBieSBwbGF5aW5nIGFyb3VuZCB3aXRoIGVuZC1vZi1saW5lIGNoYXJhY3RlcnMgc3BlY2lmaWVkIGF0IHRoZSBgdHJhbnNsYXRpb25gIG9wdGlvbiBpbiBgc2VyaWFsOjpzZXJpYWxDb25uZWN0aW9uYCBidXQgd2UnbGwgbGVhdmUgaXQgYXQgdGhhdCwgZm9yIG5vdy4NCg0KV291bGQgd2UgYmUgc3RheWluZyB0cnVlIHRvIFIgYW5kIHRoZSBwcmluY2lwbGVzIG9mIFRpZHkgZGF0YSBpZiB3ZSBsZWZ0IHRoZSBgZGF0YV9mcm1fYXJkdWlub2AgZGF0YSBzZXQgYXMgaXQgaXM/IE5vcGUhIFdlbGwgdGhlbiwgbGV0J3MgZ2V0IG91ciB3cmFuZ2xpbmcgb24hDQoNCmBgYHtyfQ0KZGF0YV9mcm1fYXJkdWlubyAlPD4lIHRpYmJsZSgNCiAgIyBhc3NpZ25pbmcgdmFsdWVzIHRvIHRoZWlyIGFwcHJvcmlhdGUgTEVEDQogIGxlZF9uYW1lcyA9IHJlcF9hbG9uZyhzZXFfbGVuKG5yb3coZGF0YV9mcm1fYXJkdWlubykpLCBjKCdtYXBwZWRfcicsJ21hcHBlZF9nJywnbWFwcGVkX2InKSkpICU+JQ0KICAjIHJlbmFtaW5nIHRoZSBmaXJzdCBjb2x1bW4NCiAgcmVuYW1lKCJsZWRfdmFsIiA9IDEpICU+JQ0KICBncm91cF9ieShsZWRfbmFtZXMpICU+JQ0KICAjIGFkZGluZyBpZGVudGlmaWVycyBhcyByZXF1aXJlZCBieSBwaXZvdF93aWRlcg0KICBtdXRhdGUocm93ID0gcm93X251bWJlcigpKSAlPiUNCiAgIyBjcmVhdGluZyBuZXcgY29sdW1ucyB1c2luZyAnbGVkX3ZhbCcgdmFsdWVzDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBsZWRfbmFtZXMsIHZhbHVlc19mcm9tID0gbGVkX3ZhbCkgJT4lDQogICMgZHJvcHBpbmcgdGhlICdyb3cnIGNvbHVtbg0KICBzZWxlY3QoLXJvdykgJT4lDQogICMgY29udmVydGluZyBhbGwgY29sdW1ucyB0byBkYXRhIHR5cGUgaW50ZWdlcg0KICBtdXRhdGVfYWxsKGFzLmludGVnZXIpDQoNCmRhdGFfZnJtX2FyZHVpbm8gJT4lIHNsaWNlX2hlYWQobiA9IDEwKQ0KYGBgDQoNCjxicj4NCg0KQSBkYXRhIHNldCBzaG93aW5nIHRoZSBpbml0aWFsIExFRCB2YWx1ZXMgc2VudCBmcm9tIFJTdHVkaW8gdG8gQXJkdWlubydzIHNlcmlhbCBwb3J0IGFuZCB0aGUgbWFwcGVkIHZhbHVlcyBzZW50IGJhY2sgd291bGQgY29tbXVuaWNhdGUgdGhpbmdzIGJldHRlci4gTGV0J3MgZ2V0IHJpZ2h0IGF0IGl0Lg0KDQpgYGB7cn0NCmNvbWJpbmVkX2RhdGEgPC0gYXNfdGliYmxlKA0KICAjIG1lcmdlIHRoZSB0d28gZGF0YSBzZXRzDQogIGNiaW5kKGFyZHVpbm9faW5wdXQsIGRhdGFfZnJtX2FyZHVpbm8pKSAlPiUNCiAgIyBkcm9wIG5vbiBudW1lcmljIGNoYXJhY3RlcnMgZWcgUiwgRywgQg0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIH5wYXJzZV9udW1iZXIoLngpKSwgYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIGFzLmludGVnZXIpKSAlPiUgDQogICMgcmVvcmRlciBjb2x1bW5zLi4gZHBseXI6OnJlbG9jYXRlIGNhbiBkbyB0aGUgdHJpY2sgdG9vDQogIHNlbGVjdChjKDEsIDQsIDIsIDUsIDMsIDYpKQ0KDQpjb21iaW5lZF9kYXRhICU+JSBzbGljZV9oZWFkKG4gPSAxMCkNCmBgYA0KDQo8YnI+DQoNCiMjIyAqKk9uZSBsYXN0IHRyaXA6KiogVG8gYSBzZXJ2byBhbmQgYmFjaw0KDQpBdCB0aGlzIHBvaW50LCB3ZSd2ZSBhbHJlYWR5IHNlbnQgZGF0YSBmcm9tIFJTdHVkaW8gdG8gQXJkdWlubywgcmVtYXBwZWQgaXQsIGxpZ2h0ZWQgdXAgc29tZSBMRURzLCBhbmQgc2VudCB0aGUgcmVtYXBwZWQgZGF0YSBiYWNrIHRvIHRoZSBSU3R1ZGlvIElERS4gVGhhdCdzIGJlZW4gYW4gaW5jcmVkaWJsZSB2b3lhZ2UgYnkgYWxsIG1lYW5zLiBMZXQncyB3cmFwIGl0IHdpdGggb25lIGZpbmFsIGFkdmVudHVyZTogYGRyaXZpbmcgYSBzZXJ2byBtb3RvcmAuDQoNClNvLCB0aGlzIGlzIGl0LiBGcm9tIHRoZSBSU3R1ZGlvIElERSdzIGVuZCwgd2UnbGwgY3JlYXRlIGEgbmV3IGRhdGFzZXQgZnJvbSB0aGUgcmVjZWl2ZWQgcmVtYXBwZWQgdmFsdWVzICgwLTI1NSksIGFwcGVuZCBhIHRlcm1pbmF0aW5nIGNoYXJhY3RlciwgYW5kIHdyaXRlIHRoZXNlIHZhbHVlcyB0byB0aGUgQXJkdWlubydzIHNlcmlhbCBwb3J0Lg0KDQpgYGB7cn0NCiMgY3JlYXRpbmcgYSBuZXcgZGF0YXNldCB0aGF0IHNlbGVjdHMgdmFsdWVzIGluIHRoZSBvcmRlcjoNCiMgbWF4bWltdW0gb2YgcmVjZWl2ZWQgTEVEIHZhbHVlcyB0aGVuIG1pbmltdW0gb2YgdGhlIExFRCB2YWx1ZXMNCiMgdGhlbiBtYXhpbXVtIHRoZW4gbWluaW11bSBhbmQgb24gYW5kIG9uIHdlIGdvIC4uLg0KDQoNCnJvd19taW4gPC0gdGliYmxlKG1pbl9pbnB1dCA9IGRhdGFfZnJtX2FyZHVpbm8gJT4lIGFwcGx5KDEsbWluKSkgJT4lDQogICAgICAgICAgICAgICAgICAgICMgc2VsZWN0IGV2ZW4gcm93cw0KICAgICAgICAgICAgICAgICAgICBmaWx0ZXIocm93X251bWJlcigpICUlIDIgPT0gMCkNCg0Kc2Vydm9faW5wdXQgPC0gdGliYmxlKHNlcnZvX2luID0gZGF0YV9mcm1fYXJkdWlubyAlPiUgYXBwbHkoMSxtYXgpKSANCiAgICAgICAgICAgICAgICAgICAgDQoNCiMgcmVwbGFjaW5nIHRoZSBldmVuIHJvd3Mgd2l0aCBhIG1pbmltdW0gdmFsdWUgDQpzZXJ2b19pbnB1dFtjKDE6bilbYyhGLFQpXSxdIDwtIHJvd19taW4NCg0KIyBhcHBlbmRpbmcgYSB0ZXJtaW5hdGluZyBjaGFyYWN0ZXINCnNlcnZvX2lucHV0ICU8PiUgbXV0YXRlKHNlcnZvX2luID0gc2Vydm9faW4gJT4lIHBhc3RlKCdTJywgc2VwID0gJycpKQ0KYGBgDQoNCkFuZCBvZmYgd2Ugd3JpdGUgdGhlIHZhbHVlcyB0byB0aGUgc2VyaWFsIGludGVyZmFjZS4NCg0KYGBge3J9DQpjbG9zZShhcmR1aW5vKQ0Kb3BlbihhcmR1aW5vKQ0KU3lzLnNsZWVwKDIpDQpmb3IgKHIgaW4gc2VxX2xlbihuKSl7DQogIFN5cy5zbGVlcCgxKQ0KICB3cml0ZS5zZXJpYWxDb25uZWN0aW9uKGFyZHVpbm8sIHBhc3RlKHNlcnZvX2lucHV0W3IsXSwgY29sbGFwc2UgPSAnJykpDQogDQp9DQpgYGANCg0KPGJyPg0KDQpUaGUgbWFpbiBBcmR1aW5vIHByb2dyYW0gbG9vcCB3YWl0cyB1bnRpbCBzZXJpYWwgZGF0YSBpcyBhdmFpbGFibGUsIGV4dHJhY3RzIHRoZSBpbnRlZ2VyIHZhbHVlLCByZW1hcHMgdGhlIHZhbHVlIGZyb20gdGhlIHJhbmdlICQwIC0gMjU1JCB0byBhIHNlcnZvIGFuZ2xlICQwIC0gMTc5JCwgYW5kIHRoZSB3cml0ZXMgdGhpcyB2YWx1ZSB0byB0aGUgc2Vydm8uIE91ciBzdGFsd2FydCBib2FyZCB0aGVuIHByaW50cyB0aGUgbWFwcGVkIGFuZ2xlIHZhbHVlIHRvIHRoZSBzZXJpYWwgaW50ZXJmYWNlLiBBbiBBcmR1aW5vIHNuaXBwZXQgd2hlcmUgdGhpcyBtYWdpYyBoYXBwZW5zIGlzIGFzIHNob3duOg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KaSA9IGkgKyAxDQppbWdfZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gIkM6L1VzZXJzL2tlcmFzL09uZURyaXZlIC0gTWljcm9zb2Z0IFN0dWRlbnQgUGFydG5lcnMvYVJkdWluby9yZXNvdXJjZXMvIiwgZnVsbC5uYW1lcyA9IFRSVUUgKQ0KDQpyZWFkSW1hZ2UoaW1nX2ZpbGVzW2ldKSAlPiUgZGlzcGxheShtZXRob2QgPSAncmFzdGVyJykNCmBgYA0KDQo8YnI+DQoNCk5vdywgbGV0J3MgZ2V0IHdoYXQgQXJkdWlubyBlY2hvZWQgYmFjayBhdCB1cyBhbmQgZG8gc29tZSBkYXRhIHdyYW5nbGluZyB3aGlsZSBhdCBpdC4NCg0KYGBge3J9DQphbmdsX2ZybV9hcmQgPC0gdGliYmxlKA0KICAjIHJlYWRpbmcgbWFwcGVkIGFuZ2xlcyBzZW50IGZyb20gQXJkdWlubw0KICBjYXB0dXJlLm91dHB1dChjYXQocmVhZC5zZXJpYWxDb25uZWN0aW9uKGFyZHVpbm8sbj0wKSkpKSAlPiUNCiAgIyByZW5hbWluZyBmaXJzdCBjb2x1bW4NCiAgcmVuYW1lKCJtYXBwZWRfc2Vydm9fYW5nbGVzIiA9IDEpICU+JSANCiAgbXV0YXRlX2FsbChhcy5pbnRlZ2VyKQ0KDQojIHNlbGVjdCB0aGUgZmlyc3QgMTAgcm93cyANCmFuZ2xfZnJtX2FyZCAlPiUgc2xpY2VfaGVhZChuID0gMTApDQoNCiMjIyMjIyMjIyMjIyMjIyB3aGF0IHdlIHNlbnQgdnMgd2hhdCB3ZSByZWNlaXZlZCAjIyMjIyMjIyMjIyMjIw0KDQpjb21iaW5lZF9hbmdsZXMgPC0gYXNfdGliYmxlKA0KICAjIG1lcmdlIHRoZSB0d28gZGF0YSBzZXRzDQogIGNiaW5kKHNlcnZvX2lucHV0LCBhbmdsX2ZybV9hcmQpKSAlPiUNCiAgIyBkcm9wIG5vbiBudW1lcmljIGNoYXJhY3RlciBTDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfnBhcnNlX251bWJlcigueCkpLA0KICAgICAgICAgYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIGFzLmludGVnZXIpKQ0KDQpjb21iaW5lZF9hbmdsZXMgJT4lDQogIHNsaWNlX2hlYWQobiA9IDEwKQ0KIA0KYGBgDQoNCk11Y2ggYmV0dGVyISBDb2x1bW4gYHNlcnZvX2luYCBzaG93cyB0aGUgZGF0YSB3ZSBzZW50IHRvIEFyZHVpbm8sIHdoaWxlIGBtYXBwZWRfc2Vydm9fYW5nbGVzYCByZXByZXNlbnRzIHdoYXQgQXJkdWlubyB3cm90ZSB0byB0aGUgc2Vydm8gYW5kIGVjaG9lZCBiYWNrIGF0IHVzLiBTdWNoIGZyaWVuZHNoaXAg8J+krSENCg0KDQo8YnI+DQoNCk5vdywgYXMgdGhleSBzYXksIGFsbCdzIHdlbGwgdGhhdCBlbmRzIHdpdGggYW4gaW5mb3JtYXRpdmUgdmlzdWFsaXphdGlvbiAodG8gYmUgdGFrZW4gd2l0aCBhIGdyYWluIG9mIHNhbHQg8J+YgCkuDQoNCkxldCdzIHNlZSB0aGUgc3dlZXAgbWFkZSBieSB0aGUgc2Vydm8gYXQgZWFjaCBpbnN0YW5jZSBvZiB3cml0aW5nIGFuZ2xlIGRhdGEuDQoNCg0KDQoNCmBgYHtyfQ0KDQp0aGVtZV9zZXQodGhlbWVfbGlnaHQoKSkNCg0KcGx0IDwtIGFuZ2xfZnJtX2FyZCAlPiUNCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IDE6bnJvdyhhbmdsX2ZybV9hcmQpLA0KICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWFwcGVkX3NlcnZvX2FuZ2xlcykpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjIHNtb290aCBsaW5lIGZpdHRlZCB0byB0aGUgZGF0YQ0KICBnZW9tX3Ntb290aChzZSA9IEYpICsNCiAgbGFicyh4ID0gIkNvdW50IiwNCiAgICAgICB5ID0gIlNlcnZvIGFuZ2xlIiwgDQogICAgICAgdGl0bGUgPSAiVmFyaWF0aW9uIG9mIHNlcnZvIGFuZ2xlIGF0IGVhY2ggY291bnQgaW5zdGFuY2UiKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmdncGxvdGx5KHBsdCkNCg0KYGBgDQoNClRoYXQgbXVzdCBoYXZlIGJlZW4gcXVpdGUgYW4gZXJyYXRpYyB0cmFqZWN0b3J5ISBCdXQgeWVhaCwgd2UganVzdCB3YW50ZWQgdG8gaWxsdXN0cmF0ZSB0aGF0IGEgc2Vydm8gY291bGQgYmUgcm90YXRlZC4gV2UnbGwgZ2V0IHRvIHJlYWwtbGlmZSBhbmQgbW9yZSBwcmFjdGljYWwgdXNlLWNhc2VzIHJlYWwgc29vbi4NCg0KDQoNCjxicj4NCg0KIyAqKldyYXB1cCoqDQoNCkl0J3MgdGltZSB3ZSB3cmFwcGVkIHRoaW5ncyB1cC4NCg0KSW4gdGhpcyBwb3N0LCB3ZSByZWFsbHkgdHJpZWQgdG8gc2hvdyB0aGUgYmktZGlyZWN0aW9uYWwgZmxvdyBvZiBkYXRhIGJldHdlZW4gUlN0dWRpbyBJREUgYW5kIHRoZSBBcmR1aW5vLiBBdCBlYWNoIGluc3RhbmNlLCB0aGUgZGF0YSB3ZSBzZW50IHRvIHRoZSBBcmR1aW5vIGdvdCB0cmFuc29ybWVkLCBhY3R1YXRlZCBhIHBlcmlwaGVyYWwgYW5kIHRoZW4gZWNob2VkIGJhY2suIFRoZSBkYXRhIHdlIGdvdCBiYWNrIHRoZW4gd2VudCB0aHJvdWdoIHNvbWUgdGlkeWluZyBhbmQgd3JhbmdsaW5nIHRvIHB1dCBpdCBpbiB0aGUgcmlnaHQgZm9ybWF0IHRoYXQgd291bGQgYmUgZXhlY3V0ZWQgYnkgdGhlIEFyZHVpbm8gaW4gc3Vic2VxdWVudCBvcGVyYXRpb25zLg0KDQpXZSBob3BlIHRoaXMgZ290IHlvdSB1cCB0byBzcGVlZCB3aXRoIGJvdGggQXJkdWlubyBhbmQgUiwgYW5kIGlnbml0ZWQgYSBnZW51aW5lIGludGVyZXN0IHRvIGV4cGxvcmUgdGhlIGFtYXppbmcgdGhpbmdzIG9uZSBjYW4gZG8gd2l0aCB0aGVzZSB0d28gYmVhdXRpZXMhDQoNCldlIHJlYWxseSBsb29rIGZvcndhcmQgdG8gZXhwbG9yaW5nLCBsZWFybmluZyBhbmQgYFJgaW5nIG1vcmUgb24gdGhpcyB0b3BpYyAuLi4gc29vbi4NCg0KVGhhbmtzIGZvciByZWFkaW5nIQ0KDQpCZSBzdXJlIHRvIGNoZWNrIG91dCBncmVhdCBibG9ncywgdHV0b3JpYWxzIGFuZCBvdGhlciBmb3JtYXRzIG9mIFIgcmVzb3VyY2VzIGNvbWluZyBvdXQgZXZlcnkgZGF5IGF0IFtSV2Vla2x5Lm9yZ10oaHR0cHM6Ly9yd2Vla2x5Lm9yZy8pIQ0KDQpUaWxsIHRoZW4sIA0KDQpIYXBweSBMZWFybmluZyDwn5Gp8J+PveKAjfCfkrsg8J+RqOKAjfCfkrsg8J+RqPCfj77igI3wn5K7IPCfkanigI3wn5K7ICwNCg0KW0VyaWMgKFJfaWMpXShodHRwczovL3R3aXR0ZXIuY29tL2VyaWNudGF5KSAoR29sZCBNaWNyb3NvZnQgTGVhcm4gU3R1ZGVudCBBbWJhc3NhZG9yKSwgW0lhbl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9JYW5OeWFnYTQ/cz0wOSkgKENvLW9yZ2FuaXplciBEZWt1dFIgRGF0YSBTY2llbmNlIENvbW11bml0eSkgYW5kIFtTYW1dKGh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9pbi9zYW1zYW11ZWwta2FyaXVraS8pIChDby1vcmdhbml6ZXIgRGVrdXRSIERhdGEgU2NpZW5jZSBDb21tdW5pdHkpLiANCg0KPGJyPg0KDQojICoqUmVmZXJlbmNlIE1hdGVyaWFsKioNCg0KKiBILiBXaWNraGFtIGFuZCBHLiBHcm9sZW11bmQsIFsqUiBmb3IgRGF0YSBTY2llbmNlOiBWaXN1YWxpemUsIE1vZGVsLCBUcmFuc2Zvcm0sIFRpZHksIGFuZCBJbXBvcnQgRGF0YSpdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKS4gMjAxNy4NCg0KKiBKLiBCbHVtLCAqRXhwbG9yaW5nIEFSRFVJTk8gdG9vbHMgYW5kIHRlY2huaXF1ZXMgZm9yIGVuZ2luZWVyaW5nIHdpemFyZHJ5LCAybmQgRWRpdGlvbiouIDIwMTkuDQoNCg0KIyAqKkFwcGVuZGl4OioqIEFyZHVpbm8gc2NyaXB0IHVzZWQNCg0KSGVyZSBpcyB0aGUgQXJkdWlubyBzY3JpcHQgdGhhdCB3YXMgdXBsb2FkZWQgdG8gdGhlIGJvYXJkLiBJdCBpcyByZXNwb25zaWJsZSBmb3IgaW5zdHJ1Y3RpbmcgdGhlIGJvYXJkIHdoYXQgdG8gZG8gYmFzZWQgb24gdGhlIGRhdGEgaXQgcmVjZWl2ZXMgZnJvbSBSU3R1ZGlvIElERS4gDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9VH0NCg0KIyBpbmNsdWRlIDxTZXJ2by5oPg0KDQovLyBmb3Igc3RvcmluZyByZWFkaW5ncyBmb3IgUkdCIGxlZHMgYW5kIHNlcnZvDQppbnQgcnZhbCA9IDA7DQppbnQgZ3ZhbCA9IDA7DQppbnQgYnZhbCA9IDA7DQppbnQgc3ZhbCA9IDA7DQoNCmludCBSRUQgPSA2OyAvLyBSZWQgTEVEIG9uIHBpbiA2DQppbnQgR1JFRU4gPSA1Ow0KaW50IEJMVUUgPSAzOw0KaW50IFNFUlZPID0gOTsgLy8gU2Vydm8gb24gUGluIDkNCg0KDQoNClNlcnZvIG15U2Vydm87DQp2b2lkIHNldHVwKCkgew0KICAvLyBwdXQgeW91ciBzZXR1cCBjb2RlIGhlcmUsIHRvIHJ1biBvbmNlOg0KICBTZXJpYWwuYmVnaW4oOTYwMCk7IC8vIHNlcmlhbCBwb3J0IGF0IDk2MDAgYmF1ZA0KICANCiAgLy8gc2V0dGluZyBwaW5zIGFzIG91dHB1dA0KICBwaW5Nb2RlKFJFRCwgT1VUUFVUKTsNCiAgcGluTW9kZShHUkVFTiwgT1VUUFVUKTsNCiAgcGluTW9kZShCTFVFLCBPVVRQVVQpOw0KDQogIC8vIEF0dGFjaGluZyB0aGUgU2Vydm8gb2JqZWN0DQogIG15U2Vydm8uYXR0YWNoKFNFUlZPKTsNCiAgDQoNCn0NCg0Kdm9pZCBsb29wKCkgew0KICBpZiAoU2VyaWFsLmF2YWlsYWJsZSgpKXsNCiAgIC8vIGNyZWF0ZXMgdmFyaWFibGVzIHZpc2libGUgdG8gb25seSAxIGZ1bmN0aW9uLiBUaGV5IHBlcnNpc3QNCiAgIC8vIGJleW9uZCB0aGUgZnVuY3Rpb24gY2FsbCBhbmQgcHJlc2VydmUgdGhlaXIgdmFsdWUNCiAgIHN0YXRpYyBpbnQgdCA9IDA7DQogICANCiAgIGNoYXIgbXljaGFyID0gU2VyaWFsLnJlYWQoKTsNCg0KICAgc3dpdGNoKG15Y2hhcil7DQogICAgLy9teWNoYXI6IGEgdmFyaWFibGUgd2hvc2UgdmFsdWUgdG8gY29tcGFyZSB3aXRoIHZhcmlvdXMgY2FzZXMuDQogICAgY2FzZSAnMCcuLi4nOSc6DQogICAgICB0ID0gdCAqIDEwICsgbXljaGFyIC0gJzAnOw0KICAgICAgYnJlYWs7ICAgICANCiAgICBjYXNlICdSJzoNCiAgICB7DQogICAgICBydmFsID0gbWFwKHQsIDAsIDEwMCwgMCwgMjU1KTsNCiAgICAgIGFuYWxvZ1dyaXRlKFJFRCwgcnZhbCk7DQogICAgICBTZXJpYWwucHJpbnRsbihydmFsKTsgIA0KICAgIH0NCiAgICB0ID0gMDsNCiAgICBicmVhazsNCiAgICBjYXNlICdHJzoNCiAgICB7DQogICAgICBndmFsID0gbWFwKHQsIDAsIDEwMCwgMCwgMjU1KTsNCiAgICAgIGFuYWxvZ1dyaXRlKEdSRUVOLCBndmFsKTsNCiAgICAgIFNlcmlhbC5wcmludGxuKGd2YWwpOw0KICAgIH0NCiAgICB0ID0gMDsNCiAgICBicmVhazsNCg0KICAgIGNhc2UgJ0InOg0KICAgIHsNCiAgICAgIGJ2YWwgPSBtYXAodCwgMCwgMTAwLCAwLCAyNTUpOw0KICAgICAgYW5hbG9nV3JpdGUoQkxVRSwgYnZhbCk7DQogICAgICBTZXJpYWwucHJpbnRsbihidmFsKTsNCiAgICB9DQogICAgdCA9IDA7DQogICAgYnJlYWs7DQoNCiAgICBjYXNlICdTJzoNCiAgICB7DQogICAgICAvLyBtYXAgYW5hbG9ndWUgTEVEIHZhbHVlIHRvIGFuIGFuZ2xlIGJldHdlZW4gMCBhbmQgMTgwIGRlZ3JlZXMNCiAgICAgIHN2YWwgPSBtYXAodCwgMCwgMjU1LCAwLCAxNzkpOw0KICAgICAgU2VyaWFsLnByaW50bG4oc3ZhbCk7DQogICAgICBkZWxheSg1KTsNCiAgICAgIG15U2Vydm8ud3JpdGUgKHN2YWwpOw0KICAgICAgZGVsYXkoMTUwKTsNCiAgICAgIA0KICAgICAgDQogICAgfQ0KICAgIHQgPSAwOw0KICAgIGJyZWFrOw0KICAgIA0KICAgIA0KICAgfQ0KDQogICAgDQogIH0NCg0KfQ0KDQpgYGANCg0KDQoNCg0KDQo=