1 Intro

The two main components of the R language are objects and functions. Objects are the data structures that we use to store information, and functions are the tools that we use to manipulate these objects. Paraphrasing John Chambers, one of the originators of the R language, everything that “exists” in R is an object, and everything that “happens” is a function.

So far you have mostly used functions written by others. In this lesson, you will learn how to write your own functions.

Writing functions allows you to automate repetitive tasks, improve efficiency and reduce errors in your code.

In this lesson, we will learn the fundamentals of functions with simple examples. Then in a future lesson, we will write more complex functions that can automate large parts of your data analysis workflow.

2 Learning objectives

By the end of this lesson, you will be able to:

  1. Create and use your own functions in R.
  2. Design function arguments and set default values.
  3. Use conditional logic like if, else if, and else within functions.
  4. Check and validate function arguments to prevent errors.
  5. Manage function scope and understand local vs. global variables.
  6. Handle vectorized data in functions.
  7. Organize and store your custom functions for easy reuse.

3 Packages

Run the following code to install and load the packages needed for this lesson:

if (!require(pacman)) install.packages("pacman")
pacman::p_load(tidyverse, here, NHSRdatasets, medicaldata, outbreaks, reactable)

3.1 Packages

‣ Install and load necessary packages for this lesson.

if (!require(pacman)) install.packages("pacman")
pacman::p_load(tidyverse, here, NHSRdatasets, medicaldata, outbreaks, reactable)

3.2 Basics of a Function

‣ Learn to create a simple function: Converting pounds to kilograms

‣ Define a function named pounds_to_kg, by multiplying the input by 0.4536:

pounds_to_kg <- function(pounds){
  kg <- pounds * 0.4536
  return(kg)
}

‣ New object in environment of type function.

‣ Use pounds_to_kg to convert a value:

pounds_to_kg(200)
## [1] 90.72

‣ Breaking down function structure:

# Slowly build up the function
pounds_to_kg <- function(pounds){
  kg <- pounds * 0.4536
  return(kg)
}

Function creation: Use function, followed by parentheses and braces.

Arguments: Define inside parentheses. Example: pounds. But can be any name.

Body: Code to execute inside braces.

Returning values: Use return to specify what to return.

Naming the function: Store in an object, e.g., pounds_to_kg.

‣ We can use the function with named and unnamed arguments:

pounds_to_kg(pounds = 200)
## [1] 90.72
pounds_to_kg(100)
## [1] 45.36

‣ And we can apply function to a vector:

pounds_vec <- c(100, 200, 300)
pounds_to_kg(pounds_vec)
## [1]  45.36  90.72 136.08

‣ To explore the function’s source code, type the function’s name without parentheses:

pounds_to_kg
## function(pounds){
##   kg <- pounds * 0.4536
##   return(kg)
## }
## <bytecode: 0x000001dd90402978>

‣ Or view the function in RStudio with View:

View(pounds_to_kg)  # View the pounds_to_kg function
View(reactable)  # View the reactable function

Age Months Function

(NOTE: Answers are at the bottom of the page. Try to answer the questions yourself before checking.)

Create a simple function called years_to_months that transforms age in years to age in months.

Try it out with years_to_months(12)

# Your code here
years_to_months <- function(years){
  month <- years * 12
  return(month)
}

years_to_months(3)

‣ Let’s write a more complex function: Converting Fahrenheit to Celsius.

‣ The formula: \(C = \frac{5}{9} \times (F - 32)\)

fahrenheit_to_celsius <- function(fahrenheit){
  celsius <- ((5/9) * (fahrenheit - 32))
  return(celsius)
}

fahrenheit_to_celsius(32) # Should be 0
## [1] 0

‣ Testing the function on the airquality dataset.

airquality |>
  select(Temp) |> # select the Temp column
   mutate(Temp_celsius = fahrenheit_to_celsius(Temp)) # apply the function to the Temp column
##     Temp Temp_celsius
## 1     67     19.44444
## 2     72     22.22222
## 3     74     23.33333
## 4     62     16.66667
## 5     56     13.33333
## 6     66     18.88889
## 7     65     18.33333
## 8     59     15.00000
## 9     61     16.11111
## 10    69     20.55556
## 11    74     23.33333
## 12    69     20.55556
## 13    66     18.88889
## 14    68     20.00000
## 15    58     14.44444
## 16    64     17.77778
## 17    66     18.88889
## 18    57     13.88889
## 19    68     20.00000
## 20    62     16.66667
## 21    59     15.00000
## 22    73     22.77778
## 23    61     16.11111
## 24    61     16.11111
## 25    57     13.88889
## 26    58     14.44444
## 27    57     13.88889
## 28    67     19.44444
## 29    81     27.22222
## 30    79     26.11111
## 31    76     24.44444
## 32    78     25.55556
## 33    74     23.33333
## 34    67     19.44444
## 35    84     28.88889
## 36    85     29.44444
## 37    79     26.11111
## 38    82     27.77778
## 39    87     30.55556
## 40    90     32.22222
## 41    87     30.55556
## 42    93     33.88889
## 43    92     33.33333
## 44    82     27.77778
## 45    80     26.66667
## 46    79     26.11111
## 47    77     25.00000
## 48    72     22.22222
## 49    65     18.33333
## 50    73     22.77778
## 51    76     24.44444
## 52    77     25.00000
## 53    76     24.44444
## 54    76     24.44444
## 55    76     24.44444
## 56    75     23.88889
## 57    78     25.55556
## 58    73     22.77778
## 59    80     26.66667
## 60    77     25.00000
## 61    83     28.33333
## 62    84     28.88889
## 63    85     29.44444
## 64    81     27.22222
## 65    84     28.88889
## 66    83     28.33333
## 67    83     28.33333
## 68    88     31.11111
## 69    92     33.33333
## 70    92     33.33333
## 71    89     31.66667
## 72    82     27.77778
## 73    73     22.77778
## 74    81     27.22222
## 75    91     32.77778
## 76    80     26.66667
## 77    81     27.22222
## 78    82     27.77778
## 79    84     28.88889
## 80    87     30.55556
## 81    85     29.44444
## 82    74     23.33333
## 83    81     27.22222
## 84    82     27.77778
## 85    86     30.00000
## 86    85     29.44444
## 87    82     27.77778
## 88    86     30.00000
## 89    88     31.11111
## 90    86     30.00000
## 91    83     28.33333
## 92    81     27.22222
## 93    81     27.22222
## 94    81     27.22222
## 95    82     27.77778
## 96    86     30.00000
## 97    85     29.44444
## 98    87     30.55556
## 99    89     31.66667
## 100   90     32.22222
## 101   90     32.22222
## 102   92     33.33333
## 103   86     30.00000
## 104   86     30.00000
## 105   82     27.77778
## 106   80     26.66667
## 107   79     26.11111
## 108   77     25.00000
## 109   79     26.11111
## 110   76     24.44444
## 111   78     25.55556
## 112   78     25.55556
## 113   77     25.00000
## 114   72     22.22222
## 115   75     23.88889
## 116   79     26.11111
## 117   81     27.22222
## 118   86     30.00000
## 119   88     31.11111
## 120   97     36.11111
## 121   94     34.44444
## 122   96     35.55556
## 123   94     34.44444
## 124   91     32.77778
## 125   92     33.33333
## 126   93     33.88889
## 127   93     33.88889
## 128   87     30.55556
## 129   84     28.88889
## 130   80     26.66667
## 131   78     25.55556
## 132   75     23.88889
## 133   73     22.77778
## 134   81     27.22222
## 135   76     24.44444
## 136   77     25.00000
## 137   71     21.66667
## 138   71     21.66667
## 139   78     25.55556
## 140   67     19.44444
## 141   76     24.44444
## 142   68     20.00000
## 143   82     27.77778
## 144   64     17.77778
## 145   71     21.66667
## 146   81     27.22222
## 147   69     20.55556
## 148   63     17.22222
## 149   70     21.11111
## 150   77     25.00000
## 151   75     23.88889
## 152   76     24.44444
## 153   68     20.00000

Great!

3.3 When to Write a Function in R

Reusability: Writing functions for repetitive code enhances efficiency.

Readability: Descriptive functions clarify code purpose. Though not so obvious with simple functions:

airquality %>%
  mutate(Temp = fahrenheit_to_celsius(Temp)) 
##     Ozone Solar.R Wind     Temp Month Day
## 1      41     190  7.4 19.44444     5   1
## 2      36     118  8.0 22.22222     5   2
## 3      12     149 12.6 23.33333     5   3
## 4      18     313 11.5 16.66667     5   4
## 5      NA      NA 14.3 13.33333     5   5
## 6      28      NA 14.9 18.88889     5   6
## 7      23     299  8.6 18.33333     5   7
## 8      19      99 13.8 15.00000     5   8
## 9       8      19 20.1 16.11111     5   9
## 10     NA     194  8.6 20.55556     5  10
## 11      7      NA  6.9 23.33333     5  11
## 12     16     256  9.7 20.55556     5  12
## 13     11     290  9.2 18.88889     5  13
## 14     14     274 10.9 20.00000     5  14
## 15     18      65 13.2 14.44444     5  15
## 16     14     334 11.5 17.77778     5  16
## 17     34     307 12.0 18.88889     5  17
## 18      6      78 18.4 13.88889     5  18
## 19     30     322 11.5 20.00000     5  19
## 20     11      44  9.7 16.66667     5  20
## 21      1       8  9.7 15.00000     5  21
## 22     11     320 16.6 22.77778     5  22
## 23      4      25  9.7 16.11111     5  23
## 24     32      92 12.0 16.11111     5  24
## 25     NA      66 16.6 13.88889     5  25
## 26     NA     266 14.9 14.44444     5  26
## 27     NA      NA  8.0 13.88889     5  27
## 28     23      13 12.0 19.44444     5  28
## 29     45     252 14.9 27.22222     5  29
## 30    115     223  5.7 26.11111     5  30
## 31     37     279  7.4 24.44444     5  31
## 32     NA     286  8.6 25.55556     6   1
## 33     NA     287  9.7 23.33333     6   2
## 34     NA     242 16.1 19.44444     6   3
## 35     NA     186  9.2 28.88889     6   4
## 36     NA     220  8.6 29.44444     6   5
## 37     NA     264 14.3 26.11111     6   6
## 38     29     127  9.7 27.77778     6   7
## 39     NA     273  6.9 30.55556     6   8
## 40     71     291 13.8 32.22222     6   9
## 41     39     323 11.5 30.55556     6  10
## 42     NA     259 10.9 33.88889     6  11
## 43     NA     250  9.2 33.33333     6  12
## 44     23     148  8.0 27.77778     6  13
## 45     NA     332 13.8 26.66667     6  14
## 46     NA     322 11.5 26.11111     6  15
## 47     21     191 14.9 25.00000     6  16
## 48     37     284 20.7 22.22222     6  17
## 49     20      37  9.2 18.33333     6  18
## 50     12     120 11.5 22.77778     6  19
## 51     13     137 10.3 24.44444     6  20
## 52     NA     150  6.3 25.00000     6  21
## 53     NA      59  1.7 24.44444     6  22
## 54     NA      91  4.6 24.44444     6  23
## 55     NA     250  6.3 24.44444     6  24
## 56     NA     135  8.0 23.88889     6  25
## 57     NA     127  8.0 25.55556     6  26
## 58     NA      47 10.3 22.77778     6  27
## 59     NA      98 11.5 26.66667     6  28
## 60     NA      31 14.9 25.00000     6  29
## 61     NA     138  8.0 28.33333     6  30
## 62    135     269  4.1 28.88889     7   1
## 63     49     248  9.2 29.44444     7   2
## 64     32     236  9.2 27.22222     7   3
## 65     NA     101 10.9 28.88889     7   4
## 66     64     175  4.6 28.33333     7   5
## 67     40     314 10.9 28.33333     7   6
## 68     77     276  5.1 31.11111     7   7
## 69     97     267  6.3 33.33333     7   8
## 70     97     272  5.7 33.33333     7   9
## 71     85     175  7.4 31.66667     7  10
## 72     NA     139  8.6 27.77778     7  11
## 73     10     264 14.3 22.77778     7  12
## 74     27     175 14.9 27.22222     7  13
## 75     NA     291 14.9 32.77778     7  14
## 76      7      48 14.3 26.66667     7  15
## 77     48     260  6.9 27.22222     7  16
## 78     35     274 10.3 27.77778     7  17
## 79     61     285  6.3 28.88889     7  18
## 80     79     187  5.1 30.55556     7  19
## 81     63     220 11.5 29.44444     7  20
## 82     16       7  6.9 23.33333     7  21
## 83     NA     258  9.7 27.22222     7  22
## 84     NA     295 11.5 27.77778     7  23
## 85     80     294  8.6 30.00000     7  24
## 86    108     223  8.0 29.44444     7  25
## 87     20      81  8.6 27.77778     7  26
## 88     52      82 12.0 30.00000     7  27
## 89     82     213  7.4 31.11111     7  28
## 90     50     275  7.4 30.00000     7  29
## 91     64     253  7.4 28.33333     7  30
## 92     59     254  9.2 27.22222     7  31
## 93     39      83  6.9 27.22222     8   1
## 94      9      24 13.8 27.22222     8   2
## 95     16      77  7.4 27.77778     8   3
## 96     78      NA  6.9 30.00000     8   4
## 97     35      NA  7.4 29.44444     8   5
## 98     66      NA  4.6 30.55556     8   6
## 99    122     255  4.0 31.66667     8   7
## 100    89     229 10.3 32.22222     8   8
## 101   110     207  8.0 32.22222     8   9
## 102    NA     222  8.6 33.33333     8  10
## 103    NA     137 11.5 30.00000     8  11
## 104    44     192 11.5 30.00000     8  12
## 105    28     273 11.5 27.77778     8  13
## 106    65     157  9.7 26.66667     8  14
## 107    NA      64 11.5 26.11111     8  15
## 108    22      71 10.3 25.00000     8  16
## 109    59      51  6.3 26.11111     8  17
## 110    23     115  7.4 24.44444     8  18
## 111    31     244 10.9 25.55556     8  19
## 112    44     190 10.3 25.55556     8  20
## 113    21     259 15.5 25.00000     8  21
## 114     9      36 14.3 22.22222     8  22
## 115    NA     255 12.6 23.88889     8  23
## 116    45     212  9.7 26.11111     8  24
## 117   168     238  3.4 27.22222     8  25
## 118    73     215  8.0 30.00000     8  26
## 119    NA     153  5.7 31.11111     8  27
## 120    76     203  9.7 36.11111     8  28
## 121   118     225  2.3 34.44444     8  29
## 122    84     237  6.3 35.55556     8  30
## 123    85     188  6.3 34.44444     8  31
## 124    96     167  6.9 32.77778     9   1
## 125    78     197  5.1 33.33333     9   2
## 126    73     183  2.8 33.88889     9   3
## 127    91     189  4.6 33.88889     9   4
## 128    47      95  7.4 30.55556     9   5
## 129    32      92 15.5 28.88889     9   6
## 130    20     252 10.9 26.66667     9   7
## 131    23     220 10.3 25.55556     9   8
## 132    21     230 10.9 23.88889     9   9
## 133    24     259  9.7 22.77778     9  10
## 134    44     236 14.9 27.22222     9  11
## 135    21     259 15.5 24.44444     9  12
## 136    28     238  6.3 25.00000     9  13
## 137     9      24 10.9 21.66667     9  14
## 138    13     112 11.5 21.66667     9  15
## 139    46     237  6.9 25.55556     9  16
## 140    18     224 13.8 19.44444     9  17
## 141    13      27 10.3 24.44444     9  18
## 142    24     238 10.3 20.00000     9  19
## 143    16     201  8.0 27.77778     9  20
## 144    13     238 12.6 17.77778     9  21
## 145    23      14  9.2 21.66667     9  22
## 146    36     139 10.3 27.22222     9  23
## 147     7      49 10.3 20.55556     9  24
## 148    14      20 16.6 17.22222     9  25
## 149    30     193  6.9 21.11111     9  26
## 150    NA     145 13.2 25.00000     9  27
## 151    14     191 14.3 23.88889     9  28
## 152    18     131  8.0 24.44444     9  29
## 153    20     223 11.5 20.00000     9  30
# VS

airquality %>%
  mutate(Temp = Temp * 5 / 9 - 32)
##     Ozone Solar.R Wind       Temp Month Day
## 1      41     190  7.4  5.2222222     5   1
## 2      36     118  8.0  8.0000000     5   2
## 3      12     149 12.6  9.1111111     5   3
## 4      18     313 11.5  2.4444444     5   4
## 5      NA      NA 14.3 -0.8888889     5   5
## 6      28      NA 14.9  4.6666667     5   6
## 7      23     299  8.6  4.1111111     5   7
## 8      19      99 13.8  0.7777778     5   8
## 9       8      19 20.1  1.8888889     5   9
## 10     NA     194  8.6  6.3333333     5  10
## 11      7      NA  6.9  9.1111111     5  11
## 12     16     256  9.7  6.3333333     5  12
## 13     11     290  9.2  4.6666667     5  13
## 14     14     274 10.9  5.7777778     5  14
## 15     18      65 13.2  0.2222222     5  15
## 16     14     334 11.5  3.5555556     5  16
## 17     34     307 12.0  4.6666667     5  17
## 18      6      78 18.4 -0.3333333     5  18
## 19     30     322 11.5  5.7777778     5  19
## 20     11      44  9.7  2.4444444     5  20
## 21      1       8  9.7  0.7777778     5  21
## 22     11     320 16.6  8.5555556     5  22
## 23      4      25  9.7  1.8888889     5  23
## 24     32      92 12.0  1.8888889     5  24
## 25     NA      66 16.6 -0.3333333     5  25
## 26     NA     266 14.9  0.2222222     5  26
## 27     NA      NA  8.0 -0.3333333     5  27
## 28     23      13 12.0  5.2222222     5  28
## 29     45     252 14.9 13.0000000     5  29
## 30    115     223  5.7 11.8888889     5  30
## 31     37     279  7.4 10.2222222     5  31
## 32     NA     286  8.6 11.3333333     6   1
## 33     NA     287  9.7  9.1111111     6   2
## 34     NA     242 16.1  5.2222222     6   3
## 35     NA     186  9.2 14.6666667     6   4
## 36     NA     220  8.6 15.2222222     6   5
## 37     NA     264 14.3 11.8888889     6   6
## 38     29     127  9.7 13.5555556     6   7
## 39     NA     273  6.9 16.3333333     6   8
## 40     71     291 13.8 18.0000000     6   9
## 41     39     323 11.5 16.3333333     6  10
## 42     NA     259 10.9 19.6666667     6  11
## 43     NA     250  9.2 19.1111111     6  12
## 44     23     148  8.0 13.5555556     6  13
## 45     NA     332 13.8 12.4444444     6  14
## 46     NA     322 11.5 11.8888889     6  15
## 47     21     191 14.9 10.7777778     6  16
## 48     37     284 20.7  8.0000000     6  17
## 49     20      37  9.2  4.1111111     6  18
## 50     12     120 11.5  8.5555556     6  19
## 51     13     137 10.3 10.2222222     6  20
## 52     NA     150  6.3 10.7777778     6  21
## 53     NA      59  1.7 10.2222222     6  22
## 54     NA      91  4.6 10.2222222     6  23
## 55     NA     250  6.3 10.2222222     6  24
## 56     NA     135  8.0  9.6666667     6  25
## 57     NA     127  8.0 11.3333333     6  26
## 58     NA      47 10.3  8.5555556     6  27
## 59     NA      98 11.5 12.4444444     6  28
## 60     NA      31 14.9 10.7777778     6  29
## 61     NA     138  8.0 14.1111111     6  30
## 62    135     269  4.1 14.6666667     7   1
## 63     49     248  9.2 15.2222222     7   2
## 64     32     236  9.2 13.0000000     7   3
## 65     NA     101 10.9 14.6666667     7   4
## 66     64     175  4.6 14.1111111     7   5
## 67     40     314 10.9 14.1111111     7   6
## 68     77     276  5.1 16.8888889     7   7
## 69     97     267  6.3 19.1111111     7   8
## 70     97     272  5.7 19.1111111     7   9
## 71     85     175  7.4 17.4444444     7  10
## 72     NA     139  8.6 13.5555556     7  11
## 73     10     264 14.3  8.5555556     7  12
## 74     27     175 14.9 13.0000000     7  13
## 75     NA     291 14.9 18.5555556     7  14
## 76      7      48 14.3 12.4444444     7  15
## 77     48     260  6.9 13.0000000     7  16
## 78     35     274 10.3 13.5555556     7  17
## 79     61     285  6.3 14.6666667     7  18
## 80     79     187  5.1 16.3333333     7  19
## 81     63     220 11.5 15.2222222     7  20
## 82     16       7  6.9  9.1111111     7  21
## 83     NA     258  9.7 13.0000000     7  22
## 84     NA     295 11.5 13.5555556     7  23
## 85     80     294  8.6 15.7777778     7  24
## 86    108     223  8.0 15.2222222     7  25
## 87     20      81  8.6 13.5555556     7  26
## 88     52      82 12.0 15.7777778     7  27
## 89     82     213  7.4 16.8888889     7  28
## 90     50     275  7.4 15.7777778     7  29
## 91     64     253  7.4 14.1111111     7  30
## 92     59     254  9.2 13.0000000     7  31
## 93     39      83  6.9 13.0000000     8   1
## 94      9      24 13.8 13.0000000     8   2
## 95     16      77  7.4 13.5555556     8   3
## 96     78      NA  6.9 15.7777778     8   4
## 97     35      NA  7.4 15.2222222     8   5
## 98     66      NA  4.6 16.3333333     8   6
## 99    122     255  4.0 17.4444444     8   7
## 100    89     229 10.3 18.0000000     8   8
## 101   110     207  8.0 18.0000000     8   9
## 102    NA     222  8.6 19.1111111     8  10
## 103    NA     137 11.5 15.7777778     8  11
## 104    44     192 11.5 15.7777778     8  12
## 105    28     273 11.5 13.5555556     8  13
## 106    65     157  9.7 12.4444444     8  14
## 107    NA      64 11.5 11.8888889     8  15
## 108    22      71 10.3 10.7777778     8  16
## 109    59      51  6.3 11.8888889     8  17
## 110    23     115  7.4 10.2222222     8  18
## 111    31     244 10.9 11.3333333     8  19
## 112    44     190 10.3 11.3333333     8  20
## 113    21     259 15.5 10.7777778     8  21
## 114     9      36 14.3  8.0000000     8  22
## 115    NA     255 12.6  9.6666667     8  23
## 116    45     212  9.7 11.8888889     8  24
## 117   168     238  3.4 13.0000000     8  25
## 118    73     215  8.0 15.7777778     8  26
## 119    NA     153  5.7 16.8888889     8  27
## 120    76     203  9.7 21.8888889     8  28
## 121   118     225  2.3 20.2222222     8  29
## 122    84     237  6.3 21.3333333     8  30
## 123    85     188  6.3 20.2222222     8  31
## 124    96     167  6.9 18.5555556     9   1
## 125    78     197  5.1 19.1111111     9   2
## 126    73     183  2.8 19.6666667     9   3
## 127    91     189  4.6 19.6666667     9   4
## 128    47      95  7.4 16.3333333     9   5
## 129    32      92 15.5 14.6666667     9   6
## 130    20     252 10.9 12.4444444     9   7
## 131    23     220 10.3 11.3333333     9   8
## 132    21     230 10.9  9.6666667     9   9
## 133    24     259  9.7  8.5555556     9  10
## 134    44     236 14.9 13.0000000     9  11
## 135    21     259 15.5 10.2222222     9  12
## 136    28     238  6.3 10.7777778     9  13
## 137     9      24 10.9  7.4444444     9  14
## 138    13     112 11.5  7.4444444     9  15
## 139    46     237  6.9 11.3333333     9  16
## 140    18     224 13.8  5.2222222     9  17
## 141    13      27 10.3 10.2222222     9  18
## 142    24     238 10.3  5.7777778     9  19
## 143    16     201  8.0 13.5555556     9  20
## 144    13     238 12.6  3.5555556     9  21
## 145    23      14  9.2  7.4444444     9  22
## 146    36     139 10.3 13.0000000     9  23
## 147     7      49 10.3  6.3333333     9  24
## 148    14      20 16.6  3.0000000     9  25
## 149    30     193  6.9  6.8888889     9  26
## 150    NA     145 13.2 10.7777778     9  27
## 151    14     191 14.3  9.6666667     9  28
## 152    18     131  8.0 10.2222222     9  29
## 153    20     223 11.5  5.7777778     9  30

Sharing: Functions facilitate code sharing, through packages or scripts. We’ll talk about sharing options later.

SIDE-NOTE Data Frame and Plot functions are among the most useful. For example, this function creates an epidemic curve from a dataset:

# Function to plot an epidemic curve
plot_epidemic_curve <- function(data, date_column) {
  data %>%
    count({{ date_column }}) %>%
    complete({{ date_column }} := seq(min({{ date_column }}), 
                                      max({{ date_column }}), by="day")) %>%
    ggplot(aes(x = {{ date_column }}, y = n)) +
    geom_col(fill = "#4395D1")
}

# Example usage
plot_epidemic_curve(outbreaks::ebola_sierraleone_2014, date_of_sample)

‣ We’ll explore such in a future lesson. For now, focusing on vector manipulation functions to give you a solid foundation.

Celsius to Fahrenheit Function

Create a function named celsius_to_fahrenheit that converts temperature from Celsius to Fahrenheit. Here is the formula for this conversion:

\[ Fahrenheit = Celsius * 1.8 + 32 \]

# Your code here
celsius_to_fahrenheit <- function(celsius){
  fahrenheit <- celsius*1.8+32
  return(fahrenheit)
}

Then test your function on the temp column of the built-in beaver1 dataset in R:

beaver1 %>%
  select(temp) %>%
  mutate(temp_fahrenheit = celsius_to_fahrenheit(temp)) %>% 
  head()

3.4 Functions with Multiple Arguments

‣ Functions typically have multiple arguments. Let’s see a function with three arguments.

calculate_calories, to calculate calories from macronutrients.

calculate_calories <- function(carb_grams, protein_grams, fat_grams){
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  return(result)
}
            

calculate_calories(carb_grams = 50, protein_grams = 25, fat_grams = 10)
## [1] 390

‣ Without all arguments, the function yields an error.

calculate_calories(carb_grams = 50, protein_grams = 25)

‣ Default values can be set for function arguments.

calculate_calories <- function(carb_grams, protein_grams, fat_grams) {
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  return(result)
}

calculate_calories(50, 25)
## Error in calculate_calories(50, 25): argument "fat_grams" is missing, with no default

‣ We can make all arguments optional with default values.

calculate_calories <- function(carb_grams = 0, protein_grams=0, fat_grams = 0) {
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  return(result)
}

‣ Now we can call the function with no or some arguments.

calculate_calories()
## [1] 0
calculate_calories(carb_grams= 50, protein_grams = 25)
## [1] 300

BMI Function

Create a function named calc_bmi that calculates the Body Mass Index (BMI) for one or more individuals. Keep in mind that BMI is calculated as weight in kg divided by the square of the height in meters. Therefore, this function requires two mandatory arguments: weight and height.

# Your code here
calc_bmi <- function(weight=0, height=0){
  bmi <- weight/((height/100)^2)
  return(bmi)
}

Then, apply your function to the medicaldata::smartpill dataset to calculate the BMI for each person:

medicaldata::smartpill |>
  as_tibble() |>
  select(Weight, Height) |>
  mutate(BMI = calc_bmi(Weight, Height))

3.5 Understanding Scope in R

Scope refers to the visibility of variables and objects within different parts of your R code.

‣ Objects within a function have local scope (as opposed to global scope) and are not accessible outside the function.

‣ Imagin we wrote the pounds_to_kg function like this:

pounds_to_kg <- function(pounds) {
  kg <- pounds * 0.4536
}

‣ May be tempted to try to access the kg variable outside of the function, but you’ll get an error:

pounds_to_kg(50)
kg
## Error in eval(expr, envir, enclos): object 'kg' not found

‣ To use a value generated within a function, it must be returned by the function.

pounds_to_kg <- function(pounds) {
  kg <- pounds * 0.4536
  return(kg)
}

‣ Store the function’s result in a global variable to access it.

kg <- pounds_to_kg(50)
  # store the output of pounds_to_kg(50) in a global object, kg
kg
## [1] 22.68

3.6 Intro to Conditionals: if, else if and else

‣ Conditionals control the flow of code execution, especially useful in functions.

‣ R implements conditionals using if, else, and else if statements.

if is used to run code only if a specific condition is true.

‣ Structure of an if statement:

if (condition) {
  # some code we want to run
}         # if condition true, run code body

‣ Example: Converting temperature from Celsius to Fahrenheit.

celsius <- 20
convert_to <- "fahrenheit"

# if convert_to is "fahrenheit", create fahrenheit, as c * 9/5 + 32, then print
if (convert_to == "fahrenheit"){
  fahrenheit <- celsius * 9/5 + 32
  print(fahrenheit)
}
## [1] 68

‣ If convert_to is "fahrenheit", the code body is executed, and if not, it is skipped.

celsius <- 20
convert_to <- "kelvin"

# if convert_to is "fahrenheit", create fahrenheit, as c * 9/5 + 32, then print
if (convert_to == "fahrenheit"){
  fahrenheit <- celsius * 9/5 + 32
  print(fahrenheit)
}

‣ Add an else clause to handle the case where convert_to is not "fahrenheit".

celsius <- 20
convert_to <- "kelvin"

# add else clause to print original celsius value
if (convert_to == "fahrenheit"){
  fahrenheit <- celsius * 9/5 + 32
  print(fahrenheit)
} else {
  print(celsius)
}
## [1] 20

‣ Use else if to check multiple specific conditions.

celsius <- 20
convert_to <- "kelvin"

if (convert_to == "fahrenheit") {
  fahrenheit <- (celsius * 9/5) + 32
  print(fahrenheit)
} else if (convert_to == "kelvin") {
  kelvin <- celsius + 273.15
  print(kelvin)
} else {
  print(celsius)
}
## [1] 293.15

‣ Code handles three scenarios: converting to Fahrenheit, Kelvin, or keeping Celsius.

‣ Can have as many else if statements as you need, but can only have one else statement attached to an if statement.

‣ Finally, we can encapsulate this logic into a function.

celsius_convert <- function(celsius, convert_to){
  if (convert_to == "fahrenheit"){
  out <- (celsius * 9/5) + 32
} else if (convert_to == "kelvin"){
  out <- celsius + 273.15
} else {
  out <-  celsius
}
  return(out)
}

‣ Let’s test the function:

celsius_convert(20, "fahrenheit")
## [1] 68
celsius_convert(20, "kelvin")
## [1] 293.15

‣ One problem: silent failure. If we pass in an invalid value for convert_to, the function fails without an informative error message.

celsius_convert(20, "celsius")
## [1] 20
celsius_convert(20, "foo") 
## [1] 20

‣ Will need to add some error handling to the function.

Debugging a Function with Conditional Logic

A function named check_negatives is designed to analyze a vector of numbers in R and print a message indicating whether the vector contains any negative numbers. However, the function currently has syntax errors.

check_negatives <- function(numbers) {
   x <- numbers
  if (any(x < 0)) {
    print("x contains negative numbers")
    }
  else {
    print("x does not contain negative numbers")
  }
}

Identify and correct the syntax errors in the check_negatives function. After correcting the function, test it with the following vectors to ensure it works correctly: 1. c(8, 3, -2, 5) 2. c(10, 20, 30, 40)

x1 <- c(8, 3, -2, 5)
x2 <- c(10, 20, 30, 40)

check_negatives(x1)
## Error in check_negatives(x1): could not find function "check_negatives"
check_negatives(x2)
## Error in check_negatives(x2): could not find function "check_negatives"

3.7 Argument Checking with Conditionals

‣ Argument checking is crucial in R functions to ensure inputs are sensible.

‣ Without checks, functions may return incorrect results, fail silently, or fail with an uninformative error message.

‣ Example: Using celsius_convert() function for temperature conversion.

celsius_convert(30, "centigrade")
## [1] 30

Issue: Fails silently when convert_to is not a valid temperature scale.

Solution: Implement argument checking using the stop() function in R.

‣ Example: Validate convert_to argument. We’ll write them out first, then integrate them into the function.

convert_to <- "bad scale"

# if convert_to is not in fahrenheit, or kelvin, stop() with error message
if (!convert_to %in% c("fahrenheit", "kelvin")){
  stop("convert to must be one of 'fahrenheit; or 'kelvin'")
}

Integration into celsius_convert() function:

celsius_convert <- function(celsius, convert_to){
  
  # Checking validity
  if (!convert_to %in% c("fahrenheit", "kelvin", "centigrade")){
  stop("convert to must be one of 'fahrenheit; or 'kelvin'")
  }
  
  # Converting value
  if (convert_to == "fahrenheit"){
  out <- (celsius * 9/5) + 32
} else if (convert_to == "kelvin"){
  out <- celsius + 273.15
} else if (convert_to == "centigrade"){
  out <- celsius
}
  return(out)
}

‣ No longer a need for else, since stop() will halt execution if convert_to is not valid.

Result: Clear error message for invalid temperature scales.

celsius_convert(30, "centigrade")

PRO TIP

Balancing Argument Checking: Checking should ensure reliability without overcomplicating the code or impacting performance.

‣ You will develop a sense of the right amount of checking through experience and examining others’ code. For now, note that it is usually good to err on the side of more checking.

Argument Checking Practice

Consider the calculate_calories function we wrote earlier:

calculate_calories <- function(carb_grams = 0, protein_grams = 0, fat_grams = 0) {
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  return(result)
}

Write a function called calculate_calories2() that is the same as calculate_calories() except that it checks if the carb_grams, protein_grams, and fat_grams arguments are numeric. If any of them are not numeric, the function should print an error message using the stop() function.

calculate_calories2 <- function(carb_grams = 0, protein_grams = 0, fat_grams = 0) {
  
  # your code here
  if (!is.numeric(c(carb_grams, protein_grams, fat_grams))){
    stop("All arguments must be numeric")
} 
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  return(result)
}

calculate_calories2("five", 20, 30)

3.8 Vectorized Conditionals

‣ Important realization and source of errors: if statements are not vectorized and only evaluate the first element of a vector.

‣ Consider this attempt at a function classify_temp for classifying temperature readings.

classify_temp <- function(temp) {
  if (temp < 35) {
    print("hypothermia")
  } else if (temp >= 35 & temp <= 37) {
    print("normal")
  } else if (temp > 37) {
    print("fever")
  }
}

‣ Works for a single value, but not for vectors.

classify_temp(36)
## [1] "normal"
temp_vec <- c(36, 37, 38)
classify_temp(temp_vec)  # This won't work correctly
## Error in if (temp < 35) {: the condition has length > 1

‣ For conditional statements for vectors, we therefore use ifelse or dplyr::case_when.

classify_temp <- function(temp){
 out <-  ifelse(temp < 35, "hypothermia",
                ifelse(temp >= 35 & temp <= 37, "normal", 
                       ifelse(temp > 37, "fever", "NA")))
 return(out)
}
      # ifelse temp less than 35, return "hypothermia"
      # nested ifelse temp between 35 and 37, return "normal"
      # nested ifelse temp greater than 37, return "fever"

classify_temp(temp_vec)  # Works for vector
## [1] "normal" "normal" "fever"

dplyr::case_when is a more readable alternative.

classify_temp <- function(temp) {
  case_when(
    temp < 35 ~ "hypothermia",
    temp >= 35 & temp <= 37 ~ "normal",
    temp > 37 ~ "fever",
    TRUE ~ NA_character_
  )
}

classify_temp(temp_vec)  # This also works as expected
## [1] "normal" "normal" "fever"

‣ This function can be seamlessly integrated with data frames.

NHSRdatasets::synthetic_news_data %>% 
  select(temp) %>%
  mutate(temp_classif = classify_temp(temp))
## # A tibble: 1,000 × 2
##     temp temp_classif
##    <dbl> <chr>       
##  1  36.8 normal      
##  2  35   normal      
##  3  36.2 normal      
##  4  36.9 normal      
##  5  36.4 normal      
##  6  35.3 normal      
##  7  35.6 normal      
##  8  37.2 fever       
##  9  35.5 normal      
## 10  35.3 normal      
## # ℹ 990 more rows

Practice Classifying Dosage of Isoniazid

Let’s apply this knowledge to a practical case. Consider the following attempt at writing a function that calculates dosages of the drug isoniazid for adults weighing more than 30kg:

calculate_isoniazid_dosage <- function(weight) {
  if (weight < 30) {
    stop("Weight must be at least 30 kg.")
  } else if (weight <= 35) {
    return(150)
  } else if (weight <= 45) {
    return(200)
  } else if (weight <= 55) {
    return(300)
  } else if (weight <= 70) {
    return(300)
  } else {
    return(300)
  }
}

This function fails with a vector of weights. Your task is to write a new function calculate_isoniazid_dosage2() that can handle vector inputs. To ensure all weights are above 30kg, you’ll use the any() function within your error checking.

Here’s a scaffold to get you started:

calculate_isoniazid_dosage2 <- function(weight) {
  if (any(weight < 30)) stop("Weights must all be at least 30 kg.")
   
  # Your code here  
  {
    out <- ifelse(weight <= 35, 150,
          ifelse(weight <= 45, 200,
                 ifelse(weight <= 55, 300,
                        ifelse(weight <= 70, 300, 300))))
    return(out)
  
  }
  return(out)
}

calculate_isoniazid_dosage2(c(30, 40, 50, 100))  
## [1] 150 200 300 300

4 Storing your functions for future use

To store your functions in R for future use, you can save them in a script or package. Here’s how:

1. Save Functions in an R Script

Create a script file:

  1. Open RStudio (or any text editor).
  2. Write your functions in a new script file (e.g., my_functions.R).
  3. Save the file in a desired location.

Load the script: To use the functions in future sessions, load the script using:

source(("path/to/my_functions.R"))

Example:

# Save this in my_functions.R
calculate_calories <- function(carb_grams, protein_grams, fat_grams) {
  (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
}

Then load the file:

source(here("global/functions/my_functions.R"))
calculate_calories(10, 5, 2)
## [1] 78

2. Save Functions in an R Data File Save function objects:

Save function objects:

save(calculate_calories2, calc_bmi, pounds_to_kg, years_to_months, celsius_convert, calculate_isoniazid_dosage2, celsius_to_fahrenheit, fahrenheit_to_celsius, classify_temp2, file = "my_functions.RData")

Load the file: In future sessions, you can load the .RData file:

load("my_functions.RData")

temp_vec <- c(36, 37, 38)
classify_temp2(temp_vec)
## [1] "normal" "normal" "fever"

3. Create a Custom R Package:

If you plan to use your functions frequently across multiple projects, creating a package is a good approach.

Building packages doesn’t have to be intimidating! Thanks to the tidyverse team, getting started is simple with RStudio and the devtools and usethis packages. In this one-hour presentation , you’ll learn the fundamentals of R package development and gain the confidence to start building your own packages!

‣ Click here for slides.

‣ Click here for source code

‣ Click here for more information on devtools

‣ Click here for more information on usethis

‣ Click here for more resources on R Packages book

‣ Learn how to create a package, the fundamental unit of shareable, reusable, and reproducible R code by reading and studying the 2nd edition of R Packages, by Hadley Wickham and Jennifer Bryan

‣ This youtube video: How to Create Your Own Package in RStudio is also helpful in creating your own package and storing in GitHub.

4. Store Functions in the Global Environment

You can also define functions in your .Rprofile file, so they are available every time you start R:

‣ Edit your .Rprofile:

file.edit("~/.Rprofile")

‣ Add your function definitions to the file.

‣ Save and restart your R session to access the functions.

Choose the method that best fits your workflow and the frequency of use for your functions!

5 Answer Key

Age Months Function

years_to_months <- function(years) {
  months <- years * 12
  return(months)
}

# Test
years_to_months(12)
## [1] 144

Celsius to Fahrenheit Function

celsius_to_fahrenheit <- function(celsius) {
  fahrenheit <- celsius * 1.8 + 32
  return(fahrenheit)
}

# Test
beaver1 %>%
  select(temp) %>%
  mutate(Fahrenheit = celsius_to_fahrenheit(temp))
##      temp Fahrenheit
## 1   36.33     97.394
## 2   36.34     97.412
## 3   36.35     97.430
## 4   36.42     97.556
## 5   36.55     97.790
## 6   36.69     98.042
## 7   36.71     98.078
## 8   36.75     98.150
## 9   36.81     98.258
## 10  36.88     98.384
## 11  36.89     98.402
## 12  36.91     98.438
## 13  36.85     98.330
## 14  36.89     98.402
## 15  36.89     98.402
## 16  36.67     98.006
## 17  36.50     97.700
## 18  36.74     98.132
## 19  36.77     98.186
## 20  36.76     98.168
## 21  36.78     98.204
## 22  36.82     98.276
## 23  36.89     98.402
## 24  36.99     98.582
## 25  36.92     98.456
## 26  36.99     98.582
## 27  36.89     98.402
## 28  36.94     98.492
## 29  36.92     98.456
## 30  36.97     98.546
## 31  36.91     98.438
## 32  36.79     98.222
## 33  36.77     98.186
## 34  36.69     98.042
## 35  36.62     97.916
## 36  36.54     97.772
## 37  36.55     97.790
## 38  36.67     98.006
## 39  36.69     98.042
## 40  36.62     97.916
## 41  36.64     97.952
## 42  36.59     97.862
## 43  36.65     97.970
## 44  36.75     98.150
## 45  36.80     98.240
## 46  36.81     98.258
## 47  36.87     98.366
## 48  36.87     98.366
## 49  36.89     98.402
## 50  36.94     98.492
## 51  36.98     98.564
## 52  36.95     98.510
## 53  37.00     98.600
## 54  37.07     98.726
## 55  37.05     98.690
## 56  37.00     98.600
## 57  36.95     98.510
## 58  37.00     98.600
## 59  36.94     98.492
## 60  36.88     98.384
## 61  36.93     98.474
## 62  36.98     98.564
## 63  36.97     98.546
## 64  36.85     98.330
## 65  36.92     98.456
## 66  36.99     98.582
## 67  37.01     98.618
## 68  37.10     98.780
## 69  37.09     98.762
## 70  37.02     98.636
## 71  36.96     98.528
## 72  36.84     98.312
## 73  36.87     98.366
## 74  36.85     98.330
## 75  36.85     98.330
## 76  36.87     98.366
## 77  36.89     98.402
## 78  36.86     98.348
## 79  36.91     98.438
## 80  37.53     99.554
## 81  37.23     99.014
## 82  37.20     98.960
## 83  37.25     99.050
## 84  37.20     98.960
## 85  37.21     98.978
## 86  37.24     99.032
## 87  37.10     98.780
## 88  37.20     98.960
## 89  37.18     98.924
## 90  36.93     98.474
## 91  36.83     98.294
## 92  36.93     98.474
## 93  36.83     98.294
## 94  36.80     98.240
## 95  36.75     98.150
## 96  36.71     98.078
## 97  36.73     98.114
## 98  36.75     98.150
## 99  36.72     98.096
## 100 36.76     98.168
## 101 36.70     98.060
## 102 36.82     98.276
## 103 36.88     98.384
## 104 36.94     98.492
## 105 36.79     98.222
## 106 36.78     98.204
## 107 36.80     98.240
## 108 36.82     98.276
## 109 36.84     98.312
## 110 36.86     98.348
## 111 36.88     98.384
## 112 36.93     98.474
## 113 36.97     98.546
## 114 37.15     98.870

BMI Function

calc_bmi <- function(weight, height) {
  bmi <- weight / (height^2)
  return(bmi)
}

# Test
library(medicaldata)
medicaldata::smartpill %>%
  as_tibble() %>%
  select(Weight, Height) %>%
  mutate(BMI = calc_bmi(Weight, Height))
## # A tibble: 95 × 3
##    Weight Height     BMI
##     <dbl>  <dbl>   <dbl>
##  1  102.    183. 0.00305
##  2  102.    180. 0.00314
##  3   68.0   180. 0.00209
##  4   69.9   175. 0.00227
##  5   44.9   152. 0.00193
##  6   94.8   185. 0.00276
##  7   86.2   188. 0.00244
##  8   76.2   165. 0.00280
##  9   74.4   173. 0.00249
## 10   64.9   170. 0.00224
## # ℹ 85 more rows

Practice with the … Argument

calculate_calories <- function(carb_grams, protein_grams, fat_grams, ...) {
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  result_formatted <- format(result, ...)
  return(result)
}

Debugging a Function with Conditional Logic

check_negatives <- function(numbers) {
  if (any(numbers < 0)) {
    print("x contains negative numbers")
  } else {
    print("x does not contain negative numbers")
  }
}

# Test
check_negatives(c(8, 3, -2, 5))
## [1] "x contains negative numbers"
check_negatives(c(10, 20, 30, 40))
## [1] "x does not contain negative numbers"

Argument Checking Practice

calculate_calories2 <- function(carb_grams = 0, protein_grams = 0, fat_grams = 0) {

  if (!is.numeric(carb_grams)) {
    stop("carb_grams must be numeric")
  }
  
  if (!is.numeric(protein_grams)) {
    stop("protein_grams must be numeric")
  }
  
  if (!is.numeric(fat_grams)) {
    stop("fat_grams must be numeric")
  }
  
  
  result <- (carb_grams * 4) + (protein_grams * 4) + (fat_grams * 9)
  return(result)
}

Practice Classifying Dosage of Isoniazid

calculate_isoniazid_dosage2 <- function(weight) {
  if (any(weight < 30)) stop("Weights must all be at least 30 kg.")

  dosage <- case_when(
    weight <= 35 ~ 150,
    weight <= 45 ~ 200,
    weight <= 55 ~ 300,
    weight <= 70 ~ 300,
    TRUE ~ 300
  )
    return(dosage)
}

calculate_isoniazid_dosage2(c(30, 40, 50, 100))
## [1] 150 200 300 300

6 References

Some material in this lesson was adapted from the following sources:

‣ Barnier, Julien. “Introduction à R et au tidyverse.” Accessed May 23, 2022. https://juba.github.io/tidyverse

‣ Wickham, Hadley; Grolemund, Garrett. “R for Data Science.” Accessed May 25, 2022. https://r4ds.had.co.nz/

‣ Wickham, Hadley; Jennifer Bryan. “R Packages (2e).” Accessed Nov 20, 2024.R Packages (2e)

LS0tDQp0aXRsZTogJ0xlc3NvbiBOb3RlczogSW50cm8gdG8gRnVuY3Rpb25zIGFuZCBDb25kaXRpb25hbHMnDQphdXRob3I6DQogIC0gbmFtZTogIktlbmUgRGF2aWQgTndvc3UiDQogIC0gbmFtZTogIkRhbmllbCBDYW1hcmEiDQogIC0gbmFtZTogIkVkdWFyZG8gQXJhdWpvIg0KICAtIG5hbWU6ICJMYXVyZSBWYW5jYXV3ZW5iZXJnaGUiDQogIC0gbmFtZTogIkF5b2RlbGUgT21vdG9sYSBMYXdhbCINCmRhdGU6ICIyMDI0LTExLTE5Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIGNzczogIWV4cHIgaGVyZTo6aGVyZSgiZ2xvYmFsL3N0eWxlL3N0eWxlLmNzcyIpDQogICAgaGlnaGxpZ2h0OiBrYXRlDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQplZGl0b3Jfb3B0aW9uczoNCiAgbWFya2Rvd246DQogICAgd3JhcDogMTAwDQogIGNhbm9uaWNhbDogdHJ1ZQ0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCmBgYHtyLCBpbmNsdWRlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KIyBMb2FkIHBhY2thZ2VzDQppZiAoIXJlcXVpcmUocGFjbWFuKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikNCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwga25pdHIsIGhlcmUsIE5IU1JkYXRhc2V0cywgamFuaXRvciwgbWVkaWNhbGRhdGEsIHJlYWN0YWJsZSkNCg0KIyBTb3VyY2UgZnVuY3Rpb25zDQpzb3VyY2UoaGVyZSgiZ2xvYmFsL2Z1bmN0aW9ucy9taXNjX2Z1bmN0aW9ucy5SIikpDQoNCiMga25pdHIgc2V0dGluZ3MNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGNsYXNzLnNvdXJjZSA9ICJ0Z2MtY29kZS1ibG9jayIsIGVycm9yID0gVCkNCmBgYA0KDQojIEludHJvDQoNClRoZSB0d28gbWFpbiBjb21wb25lbnRzIG9mIHRoZSBSIGxhbmd1YWdlIGFyZSBvYmplY3RzIGFuZCBmdW5jdGlvbnMuIE9iamVjdHMgYXJlIHRoZSBkYXRhIHN0cnVjdHVyZXMgdGhhdCB3ZSB1c2UgdG8gc3RvcmUgaW5mb3JtYXRpb24sIGFuZCBmdW5jdGlvbnMgYXJlIHRoZSB0b29scyB0aGF0IHdlIHVzZSB0byBtYW5pcHVsYXRlIHRoZXNlIG9iamVjdHMuIFBhcmFwaHJhc2luZyBbSm9obiBDaGFtYmVyc10oaHR0cHM6Ly9zdGF0d2ViLnN0YW5mb3JkLmVkdS9+am1jNC92aXRhZS5odG1sKSwgb25lIG9mIHRoZSBvcmlnaW5hdG9ycyBvZiB0aGUgUiBsYW5ndWFnZSwgZXZlcnl0aGluZyB0aGF0ICJleGlzdHMiIGluIFIgaXMgYW4gb2JqZWN0LCBhbmQgZXZlcnl0aGluZyB0aGF0ICJoYXBwZW5zIiBpcyBhIGZ1bmN0aW9uLg0KDQpTbyBmYXIgeW91IGhhdmUgbW9zdGx5IHVzZWQgZnVuY3Rpb25zIHdyaXR0ZW4gYnkgb3RoZXJzLiBJbiB0aGlzIGxlc3NvbiwgeW91IHdpbGwgbGVhcm4gaG93IHRvIHdyaXRlIHlvdXIgb3duIGZ1bmN0aW9ucy4NCg0KV3JpdGluZyBmdW5jdGlvbnMgYWxsb3dzIHlvdSB0byBhdXRvbWF0ZSByZXBldGl0aXZlIHRhc2tzLCBpbXByb3ZlIGVmZmljaWVuY3kgYW5kIHJlZHVjZSBlcnJvcnMgaW4geW91ciBjb2RlLg0KDQpJbiB0aGlzIGxlc3Nvbiwgd2Ugd2lsbCBsZWFybiB0aGUgZnVuZGFtZW50YWxzIG9mIGZ1bmN0aW9ucyB3aXRoIHNpbXBsZSBleGFtcGxlcy4gVGhlbiBpbiBhIGZ1dHVyZSBsZXNzb24sIHdlIHdpbGwgd3JpdGUgbW9yZSBjb21wbGV4IGZ1bmN0aW9ucyB0aGF0IGNhbiBhdXRvbWF0ZSBsYXJnZSBwYXJ0cyBvZiB5b3VyIGRhdGEgYW5hbHlzaXMgd29ya2Zsb3cuDQoNCiMgTGVhcm5pbmcgb2JqZWN0aXZlcw0KDQpCeSB0aGUgZW5kIG9mIHRoaXMgbGVzc29uLCB5b3Ugd2lsbCBiZSBhYmxlIHRvOg0KDQoxLiAgQ3JlYXRlIGFuZCB1c2UgeW91ciBvd24gZnVuY3Rpb25zIGluIFIuDQoyLiAgRGVzaWduIGZ1bmN0aW9uIGFyZ3VtZW50cyBhbmQgc2V0IGRlZmF1bHQgdmFsdWVzLg0KMy4gIFVzZSBjb25kaXRpb25hbCBsb2dpYyBsaWtlIGBpZmAsIGBlbHNlIGlmYCwgYW5kIGBlbHNlYCB3aXRoaW4gZnVuY3Rpb25zLg0KNC4gIENoZWNrIGFuZCB2YWxpZGF0ZSBmdW5jdGlvbiBhcmd1bWVudHMgdG8gcHJldmVudCBlcnJvcnMuDQo1LiAgTWFuYWdlIGZ1bmN0aW9uIHNjb3BlIGFuZCB1bmRlcnN0YW5kIGxvY2FsIHZzLiBnbG9iYWwgdmFyaWFibGVzLg0KNi4gIEhhbmRsZSB2ZWN0b3JpemVkIGRhdGEgaW4gZnVuY3Rpb25zLg0KNy4gIE9yZ2FuaXplIGFuZCBzdG9yZSB5b3VyIGN1c3RvbSBmdW5jdGlvbnMgZm9yIGVhc3kgcmV1c2UuDQoNCiMgUGFja2FnZXMNCg0KUnVuIHRoZSBmb2xsb3dpbmcgY29kZSB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBwYWNrYWdlcyBuZWVkZWQgZm9yIHRoaXMgbGVzc29uOg0KDQpgYGB7cn0NCmlmICghcmVxdWlyZShwYWNtYW4pKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCBoZXJlLCBOSFNSZGF0YXNldHMsIG1lZGljYWxkYXRhLCBvdXRicmVha3MsIHJlYWN0YWJsZSkNCmBgYA0KDQojIyBQYWNrYWdlcw0KDQrigKMgSW5zdGFsbCBhbmQgbG9hZCBuZWNlc3NhcnkgcGFja2FnZXMgZm9yIHRoaXMgbGVzc29uLg0KDQpgYGB7cn0NCmlmICghcmVxdWlyZShwYWNtYW4pKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCBoZXJlLCBOSFNSZGF0YXNldHMsIG1lZGljYWxkYXRhLCBvdXRicmVha3MsIHJlYWN0YWJsZSkNCmBgYA0KDQojIyBCYXNpY3Mgb2YgYSBGdW5jdGlvbg0KDQrigKMgTGVhcm4gdG8gY3JlYXRlIGEgc2ltcGxlIGZ1bmN0aW9uOiAqKkNvbnZlcnRpbmcgcG91bmRzIHRvIGtpbG9ncmFtcyoqDQoNCuKAoyBEZWZpbmUgYSBmdW5jdGlvbiBuYW1lZCBgcG91bmRzX3RvX2tnYCwgYnkgbXVsdGlwbHlpbmcgdGhlIGlucHV0IGJ5IDAuNDUzNjoNCg0KYGBge3J9DQpwb3VuZHNfdG9fa2cgPC0gZnVuY3Rpb24ocG91bmRzKXsNCiAga2cgPC0gcG91bmRzICogMC40NTM2DQogIHJldHVybihrZykNCn0NCmBgYA0KDQrigKMgTmV3IG9iamVjdCBpbiBlbnZpcm9ubWVudCBvZiB0eXBlIGZ1bmN0aW9uLg0KDQrigKMgVXNlIGBwb3VuZHNfdG9fa2dgIHRvIGNvbnZlcnQgYSB2YWx1ZToNCg0KYGBge3J9DQpwb3VuZHNfdG9fa2coMjAwKQ0KYGBgDQoNCuKAoyBCcmVha2luZyBkb3duIGZ1bmN0aW9uIHN0cnVjdHVyZToNCg0KYGBge3J9DQojIFNsb3dseSBidWlsZCB1cCB0aGUgZnVuY3Rpb24NCnBvdW5kc190b19rZyA8LSBmdW5jdGlvbihwb3VuZHMpew0KICBrZyA8LSBwb3VuZHMgKiAwLjQ1MzYNCiAgcmV0dXJuKGtnKQ0KfQ0KYGBgDQoNCuKAoyAqKkZ1bmN0aW9uIGNyZWF0aW9uKio6IFVzZSBgZnVuY3Rpb25gLCBmb2xsb3dlZCBieSBwYXJlbnRoZXNlcyBhbmQgYnJhY2VzLg0KDQrigKMgKipBcmd1bWVudHMqKjogRGVmaW5lIGluc2lkZSBwYXJlbnRoZXNlcy4gRXhhbXBsZTogYHBvdW5kc2AuIEJ1dCBjYW4gYmUgYW55IG5hbWUuDQoNCuKAoyAqKkJvZHkqKjogQ29kZSB0byBleGVjdXRlIGluc2lkZSBicmFjZXMuDQoNCuKAoyAqKlJldHVybmluZyB2YWx1ZXMqKjogVXNlIGByZXR1cm5gIHRvIHNwZWNpZnkgd2hhdCB0byByZXR1cm4uDQoNCuKAoyAqKk5hbWluZyB0aGUgZnVuY3Rpb24qKjogU3RvcmUgaW4gYW4gb2JqZWN0LCBlLmcuLCBgcG91bmRzX3RvX2tnYC4NCg0K4oCjIFdlIGNhbiB1c2UgdGhlIGZ1bmN0aW9uIHdpdGggbmFtZWQgYW5kIHVubmFtZWQgYXJndW1lbnRzOg0KDQpgYGB7cn0NCnBvdW5kc190b19rZyhwb3VuZHMgPSAyMDApDQpwb3VuZHNfdG9fa2coMTAwKQ0KYGBgDQoNCuKAoyBBbmQgd2UgY2FuIGFwcGx5IGZ1bmN0aW9uIHRvIGEgdmVjdG9yOg0KDQpgYGB7cn0NCnBvdW5kc192ZWMgPC0gYygxMDAsIDIwMCwgMzAwKQ0KcG91bmRzX3RvX2tnKHBvdW5kc192ZWMpDQpgYGANCg0K4oCjIFRvIGV4cGxvcmUgdGhlIGZ1bmN0aW9uJ3Mgc291cmNlIGNvZGUsIHR5cGUgdGhlIGZ1bmN0aW9uJ3MgbmFtZSB3aXRob3V0IHBhcmVudGhlc2VzOg0KDQpgYGB7cn0NCnBvdW5kc190b19rZw0KYGBgDQoNCuKAoyBPciB2aWV3IHRoZSBmdW5jdGlvbiBpbiBSU3R1ZGlvIHdpdGggYFZpZXdgOg0KDQpgYGB7ciBldmFsID0gRn0NClZpZXcocG91bmRzX3RvX2tnKSAgIyBWaWV3IHRoZSBwb3VuZHNfdG9fa2cgZnVuY3Rpb24NClZpZXcocmVhY3RhYmxlKSAgIyBWaWV3IHRoZSByZWFjdGFibGUgZnVuY3Rpb24NCmBgYA0KDQo6OjogcHJhY3RpY2UNCiMjIyBBZ2UgTW9udGhzIEZ1bmN0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCiooTk9URTogQW5zd2VycyBhcmUgYXQgdGhlIGJvdHRvbSBvZiB0aGUgcGFnZS4gVHJ5IHRvIGFuc3dlciB0aGUgcXVlc3Rpb25zIHlvdXJzZWxmIGJlZm9yZSBjaGVja2luZy4pKg0KDQpDcmVhdGUgYSBzaW1wbGUgZnVuY3Rpb24gY2FsbGVkIGB5ZWFyc190b19tb250aHNgIHRoYXQgdHJhbnNmb3JtcyBhZ2UgaW4geWVhcnMgdG8gYWdlIGluIG1vbnRocy4NCg0KVHJ5IGl0IG91dCB3aXRoIGB5ZWFyc190b19tb250aHMoMTIpYA0KDQpgYGB7ciBldmFsID0gRn0NCiMgWW91ciBjb2RlIGhlcmUNCnllYXJzX3RvX21vbnRocyA8LSBmdW5jdGlvbih5ZWFycyl7DQogIG1vbnRoIDwtIHllYXJzICogMTINCiAgcmV0dXJuKG1vbnRoKQ0KfQ0KDQp5ZWFyc190b19tb250aHMoMykNCmBgYA0KOjo6DQoNCuKAoyBMZXQncyB3cml0ZSBhIG1vcmUgY29tcGxleCBmdW5jdGlvbjogQ29udmVydGluZyBGYWhyZW5oZWl0IHRvIENlbHNpdXMuDQoNCuKAoyBUaGUgZm9ybXVsYTogJEMgPSBcZnJhY3s1fXs5fSBcdGltZXMgKEYgLSAzMikkDQoNCmBgYHtyfQ0KZmFocmVuaGVpdF90b19jZWxzaXVzIDwtIGZ1bmN0aW9uKGZhaHJlbmhlaXQpew0KICBjZWxzaXVzIDwtICgoNS85KSAqIChmYWhyZW5oZWl0IC0gMzIpKQ0KICByZXR1cm4oY2Vsc2l1cykNCn0NCg0KZmFocmVuaGVpdF90b19jZWxzaXVzKDMyKSAjIFNob3VsZCBiZSAwDQpgYGANCg0K4oCjIFRlc3RpbmcgdGhlIGZ1bmN0aW9uIG9uIHRoZSBgYWlycXVhbGl0eWAgZGF0YXNldC4NCg0KYGBge3J9DQphaXJxdWFsaXR5IHw+DQogIHNlbGVjdChUZW1wKSB8PiAjIHNlbGVjdCB0aGUgVGVtcCBjb2x1bW4NCiAgIG11dGF0ZShUZW1wX2NlbHNpdXMgPSBmYWhyZW5oZWl0X3RvX2NlbHNpdXMoVGVtcCkpICMgYXBwbHkgdGhlIGZ1bmN0aW9uIHRvIHRoZSBUZW1wIGNvbHVtbg0KYGBgDQoNCkdyZWF0IQ0KDQojIyBXaGVuIHRvIFdyaXRlIGEgRnVuY3Rpb24gaW4gUg0KDQrigKMgKipSZXVzYWJpbGl0eSoqOiBXcml0aW5nIGZ1bmN0aW9ucyBmb3IgcmVwZXRpdGl2ZSBjb2RlIGVuaGFuY2VzIGVmZmljaWVuY3kuDQoNCuKAoyAqKlJlYWRhYmlsaXR5Kio6IERlc2NyaXB0aXZlIGZ1bmN0aW9ucyBjbGFyaWZ5IGNvZGUgcHVycG9zZS4gVGhvdWdoIG5vdCBzbyBvYnZpb3VzIHdpdGggc2ltcGxlIGZ1bmN0aW9uczoNCg0KYGBge3J9DQphaXJxdWFsaXR5ICU+JQ0KICBtdXRhdGUoVGVtcCA9IGZhaHJlbmhlaXRfdG9fY2Vsc2l1cyhUZW1wKSkgDQoNCiMgVlMNCg0KYWlycXVhbGl0eSAlPiUNCiAgbXV0YXRlKFRlbXAgPSBUZW1wICogNSAvIDkgLSAzMikNCmBgYA0KDQrigKMgKipTaGFyaW5nKio6IEZ1bmN0aW9ucyBmYWNpbGl0YXRlIGNvZGUgc2hhcmluZywgdGhyb3VnaCBwYWNrYWdlcyBvciBzY3JpcHRzLiBXZSdsbCB0YWxrIGFib3V0IHNoYXJpbmcgb3B0aW9ucyBsYXRlci4NCg0K4oCjICoqU0lERS1OT1RFKiogRGF0YSBGcmFtZSBhbmQgUGxvdCBmdW5jdGlvbnMgYXJlIGFtb25nIHRoZSBtb3N0IHVzZWZ1bC4gRm9yIGV4YW1wbGUsIHRoaXMgZnVuY3Rpb24gY3JlYXRlcyBhbiBlcGlkZW1pYyBjdXJ2ZSBmcm9tIGEgZGF0YXNldDoNCg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIHBsb3QgYW4gZXBpZGVtaWMgY3VydmUNCnBsb3RfZXBpZGVtaWNfY3VydmUgPC0gZnVuY3Rpb24oZGF0YSwgZGF0ZV9jb2x1bW4pIHsNCiAgZGF0YSAlPiUNCiAgICBjb3VudCh7eyBkYXRlX2NvbHVtbiB9fSkgJT4lDQogICAgY29tcGxldGUoe3sgZGF0ZV9jb2x1bW4gfX0gOj0gc2VxKG1pbih7eyBkYXRlX2NvbHVtbiB9fSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoe3sgZGF0ZV9jb2x1bW4gfX0pLCBieT0iZGF5IikpICU+JQ0KICAgIGdncGxvdChhZXMoeCA9IHt7IGRhdGVfY29sdW1uIH19LCB5ID0gbikpICsNCiAgICBnZW9tX2NvbChmaWxsID0gIiM0Mzk1RDEiKQ0KfQ0KDQojIEV4YW1wbGUgdXNhZ2UNCnBsb3RfZXBpZGVtaWNfY3VydmUob3V0YnJlYWtzOjplYm9sYV9zaWVycmFsZW9uZV8yMDE0LCBkYXRlX29mX3NhbXBsZSkNCmBgYA0KDQrigKMgV2UnbGwgZXhwbG9yZSBzdWNoIGluIGEgZnV0dXJlIGxlc3Nvbi4gRm9yIG5vdywgZm9jdXNpbmcgb24gdmVjdG9yIG1hbmlwdWxhdGlvbiBmdW5jdGlvbnMgdG8gZ2l2ZSB5b3UgYSBzb2xpZCBmb3VuZGF0aW9uLg0KDQo6OjogcHJhY3RpY2UNCiMjIyBDZWxzaXVzIHRvIEZhaHJlbmhlaXQgRnVuY3Rpb24gey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KQ3JlYXRlIGEgZnVuY3Rpb24gbmFtZWQgYGNlbHNpdXNfdG9fZmFocmVuaGVpdGAgdGhhdCBjb252ZXJ0cyB0ZW1wZXJhdHVyZSBmcm9tIENlbHNpdXMgdG8gRmFocmVuaGVpdC4gSGVyZSBpcyB0aGUgZm9ybXVsYSBmb3IgdGhpcyBjb252ZXJzaW9uOg0KDQokJCBGYWhyZW5oZWl0ID0gQ2Vsc2l1cyAqIDEuOCArIDMyICQkDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIFlvdXIgY29kZSBoZXJlDQpjZWxzaXVzX3RvX2ZhaHJlbmhlaXQgPC0gZnVuY3Rpb24oY2Vsc2l1cyl7DQogIGZhaHJlbmhlaXQgPC0gY2Vsc2l1cyoxLjgrMzINCiAgcmV0dXJuKGZhaHJlbmhlaXQpDQp9DQpgYGANCg0KVGhlbiB0ZXN0IHlvdXIgZnVuY3Rpb24gb24gdGhlIGB0ZW1wYCBjb2x1bW4gb2YgdGhlIGJ1aWx0LWluIGBiZWF2ZXIxYCBkYXRhc2V0IGluIFI6DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpiZWF2ZXIxICU+JQ0KICBzZWxlY3QodGVtcCkgJT4lDQogIG11dGF0ZSh0ZW1wX2ZhaHJlbmhlaXQgPSBjZWxzaXVzX3RvX2ZhaHJlbmhlaXQodGVtcCkpICU+JSANCiAgaGVhZCgpDQpgYGANCjo6Og0KDQojIyBGdW5jdGlvbnMgd2l0aCBNdWx0aXBsZSBBcmd1bWVudHMNCg0K4oCjIEZ1bmN0aW9ucyB0eXBpY2FsbHkgaGF2ZSBtdWx0aXBsZSBhcmd1bWVudHMuIExldCdzIHNlZSBhIGZ1bmN0aW9uIHdpdGggdGhyZWUgYXJndW1lbnRzLg0KDQrigKMgYGNhbGN1bGF0ZV9jYWxvcmllc2AsIHRvIGNhbGN1bGF0ZSBjYWxvcmllcyBmcm9tIG1hY3JvbnV0cmllbnRzLg0KDQpgYGB7cn0NCmNhbGN1bGF0ZV9jYWxvcmllcyA8LSBmdW5jdGlvbihjYXJiX2dyYW1zLCBwcm90ZWluX2dyYW1zLCBmYXRfZ3JhbXMpew0KICByZXN1bHQgPC0gKGNhcmJfZ3JhbXMgKiA0KSArIChwcm90ZWluX2dyYW1zICogNCkgKyAoZmF0X2dyYW1zICogOSkNCiAgcmV0dXJuKHJlc3VsdCkNCn0NCiAgICAgICAgICAgIA0KDQpjYWxjdWxhdGVfY2Fsb3JpZXMoY2FyYl9ncmFtcyA9IDUwLCBwcm90ZWluX2dyYW1zID0gMjUsIGZhdF9ncmFtcyA9IDEwKQ0KYGBgDQoNCuKAoyBXaXRob3V0IGFsbCBhcmd1bWVudHMsIHRoZSBmdW5jdGlvbiB5aWVsZHMgYW4gZXJyb3IuDQoNCmBgYHtyIGV2YWwgPSBGfQ0KY2FsY3VsYXRlX2NhbG9yaWVzKGNhcmJfZ3JhbXMgPSA1MCwgcHJvdGVpbl9ncmFtcyA9IDI1KQ0KYGBgDQoNCuKAoyBEZWZhdWx0IHZhbHVlcyBjYW4gYmUgc2V0IGZvciBmdW5jdGlvbiBhcmd1bWVudHMuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQpjYWxjdWxhdGVfY2Fsb3JpZXMgPC0gZnVuY3Rpb24oY2FyYl9ncmFtcywgcHJvdGVpbl9ncmFtcywgZmF0X2dyYW1zKSB7DQogIHJlc3VsdCA8LSAoY2FyYl9ncmFtcyAqIDQpICsgKHByb3RlaW5fZ3JhbXMgKiA0KSArIChmYXRfZ3JhbXMgKiA5KQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KDQpjYWxjdWxhdGVfY2Fsb3JpZXMoNTAsIDI1KQ0KYGBgDQoNCuKAoyBXZSBjYW4gbWFrZSAqKmFsbCoqIGFyZ3VtZW50cyBvcHRpb25hbCB3aXRoIGRlZmF1bHQgdmFsdWVzLg0KDQpgYGB7cn0NCmNhbGN1bGF0ZV9jYWxvcmllcyA8LSBmdW5jdGlvbihjYXJiX2dyYW1zID0gMCwgcHJvdGVpbl9ncmFtcz0wLCBmYXRfZ3JhbXMgPSAwKSB7DQogIHJlc3VsdCA8LSAoY2FyYl9ncmFtcyAqIDQpICsgKHByb3RlaW5fZ3JhbXMgKiA0KSArIChmYXRfZ3JhbXMgKiA5KQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KYGBgDQoNCuKAoyBOb3cgd2UgY2FuIGNhbGwgdGhlIGZ1bmN0aW9uIHdpdGggbm8gb3Igc29tZSBhcmd1bWVudHMuDQoNCmBgYHtyfQ0KY2FsY3VsYXRlX2NhbG9yaWVzKCkNCg0KY2FsY3VsYXRlX2NhbG9yaWVzKGNhcmJfZ3JhbXM9IDUwLCBwcm90ZWluX2dyYW1zID0gMjUpDQpgYGANCg0KOjo6IHByYWN0aWNlDQojIyMgQk1JIEZ1bmN0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCkNyZWF0ZSBhIGZ1bmN0aW9uIG5hbWVkIGBjYWxjX2JtaWAgdGhhdCBjYWxjdWxhdGVzIHRoZSBCb2R5IE1hc3MgSW5kZXggKEJNSSkgZm9yIG9uZSBvciBtb3JlIGluZGl2aWR1YWxzLiBLZWVwIGluIG1pbmQgdGhhdCBCTUkgaXMgY2FsY3VsYXRlZCBhcyB3ZWlnaHQgaW4ga2cgZGl2aWRlZCBieSB0aGUgc3F1YXJlIG9mIHRoZSBoZWlnaHQgaW4gbWV0ZXJzLiBUaGVyZWZvcmUsIHRoaXMgZnVuY3Rpb24gcmVxdWlyZXMgdHdvIG1hbmRhdG9yeSBhcmd1bWVudHM6IHdlaWdodCBhbmQgaGVpZ2h0Lg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBZb3VyIGNvZGUgaGVyZQ0KY2FsY19ibWkgPC0gZnVuY3Rpb24od2VpZ2h0PTAsIGhlaWdodD0wKXsNCiAgYm1pIDwtIHdlaWdodC8oKGhlaWdodC8xMDApXjIpDQogIHJldHVybihibWkpDQp9DQpgYGANCg0KVGhlbiwgYXBwbHkgeW91ciBmdW5jdGlvbiB0byB0aGUgYG1lZGljYWxkYXRhOjpzbWFydHBpbGxgIGRhdGFzZXQgdG8gY2FsY3VsYXRlIHRoZSBCTUkgZm9yIGVhY2ggcGVyc29uOg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KbWVkaWNhbGRhdGE6OnNtYXJ0cGlsbCB8Pg0KICBhc190aWJibGUoKSB8Pg0KICBzZWxlY3QoV2VpZ2h0LCBIZWlnaHQpIHw+DQogIG11dGF0ZShCTUkgPSBjYWxjX2JtaShXZWlnaHQsIEhlaWdodCkpDQpgYGANCjo6Og0KDQojIyBVbmRlcnN0YW5kaW5nIFNjb3BlIGluIFINCg0K4oCjICoqU2NvcGUqKiByZWZlcnMgdG8gdGhlICoqdmlzaWJpbGl0eSBvZiB2YXJpYWJsZXMqKiBhbmQgb2JqZWN0cyB3aXRoaW4gZGlmZmVyZW50IHBhcnRzIG9mIHlvdXIgUiBjb2RlLg0KDQrigKMgT2JqZWN0cyB3aXRoaW4gYSBmdW5jdGlvbiBoYXZlICoqbG9jYWwgc2NvcGUqKiAoYXMgb3Bwb3NlZCB0byAqKmdsb2JhbCBzY29wZSoqKSBhbmQgYXJlIG5vdCBhY2Nlc3NpYmxlIG91dHNpZGUgdGhlIGZ1bmN0aW9uLg0KDQrigKMgSW1hZ2luIHdlIHdyb3RlIHRoZSBgcG91bmRzX3RvX2tnYCBmdW5jdGlvbiBsaWtlIHRoaXM6DQoNCmBgYHtyfQ0KcG91bmRzX3RvX2tnIDwtIGZ1bmN0aW9uKHBvdW5kcykgew0KICBrZyA8LSBwb3VuZHMgKiAwLjQ1MzYNCn0NCmBgYA0KDQrigKMgTWF5IGJlIHRlbXB0ZWQgdG8gdHJ5IHRvIGFjY2VzcyB0aGUgYGtnYCB2YXJpYWJsZSBvdXRzaWRlIG9mIHRoZSBmdW5jdGlvbiwgYnV0IHlvdSdsbCBnZXQgYW4gZXJyb3I6DQoNCmBgYHtyfQ0KcG91bmRzX3RvX2tnKDUwKQ0Ka2cNCmBgYA0KDQrigKMgVG8gdXNlIGEgdmFsdWUgZ2VuZXJhdGVkIHdpdGhpbiBhIGZ1bmN0aW9uLCBpdCBtdXN0IGJlICoqcmV0dXJuZWQqKiBieSB0aGUgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KcG91bmRzX3RvX2tnIDwtIGZ1bmN0aW9uKHBvdW5kcykgew0KICBrZyA8LSBwb3VuZHMgKiAwLjQ1MzYNCiAgcmV0dXJuKGtnKQ0KfQ0KYGBgDQoNCuKAoyBTdG9yZSB0aGUgZnVuY3Rpb24ncyByZXN1bHQgaW4gYSBnbG9iYWwgdmFyaWFibGUgdG8gYWNjZXNzIGl0Lg0KDQpgYGB7cn0NCmtnIDwtIHBvdW5kc190b19rZyg1MCkNCiAgIyBzdG9yZSB0aGUgb3V0cHV0IG9mIHBvdW5kc190b19rZyg1MCkgaW4gYSBnbG9iYWwgb2JqZWN0LCBrZw0Ka2cNCmBgYA0KDQojIyBJbnRybyB0byBDb25kaXRpb25hbHM6IGBpZmAsIGBlbHNlIGlmYCBhbmQgYGVsc2VgDQoNCuKAoyBDb25kaXRpb25hbHMgY29udHJvbCB0aGUgZmxvdyBvZiBjb2RlIGV4ZWN1dGlvbiwgZXNwZWNpYWxseSB1c2VmdWwgaW4gZnVuY3Rpb25zLg0KDQrigKMgUiBpbXBsZW1lbnRzIGNvbmRpdGlvbmFscyB1c2luZyBgaWZgLCBgZWxzZWAsIGFuZCBgZWxzZSBpZmAgc3RhdGVtZW50cy4NCg0K4oCjIGBpZmAgaXMgdXNlZCB0byBydW4gY29kZSBvbmx5IGlmIGEgc3BlY2lmaWMgY29uZGl0aW9uIGlzIHRydWUuDQoNCuKAoyBTdHJ1Y3R1cmUgb2YgYW4gYGlmYCBzdGF0ZW1lbnQ6DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQppZiAoY29uZGl0aW9uKSB7DQogICMgc29tZSBjb2RlIHdlIHdhbnQgdG8gcnVuDQp9ICAgICAgICAgIyBpZiBjb25kaXRpb24gdHJ1ZSwgcnVuIGNvZGUgYm9keQ0KYGBgDQoNCuKAoyBFeGFtcGxlOiBDb252ZXJ0aW5nIHRlbXBlcmF0dXJlIGZyb20gQ2Vsc2l1cyB0byBGYWhyZW5oZWl0Lg0KDQpgYGB7cn0NCmNlbHNpdXMgPC0gMjANCmNvbnZlcnRfdG8gPC0gImZhaHJlbmhlaXQiDQoNCiMgaWYgY29udmVydF90byBpcyAiZmFocmVuaGVpdCIsIGNyZWF0ZSBmYWhyZW5oZWl0LCBhcyBjICogOS81ICsgMzIsIHRoZW4gcHJpbnQNCmlmIChjb252ZXJ0X3RvID09ICJmYWhyZW5oZWl0Iil7DQogIGZhaHJlbmhlaXQgPC0gY2Vsc2l1cyAqIDkvNSArIDMyDQogIHByaW50KGZhaHJlbmhlaXQpDQp9DQoNCmBgYA0KDQrigKMgSWYgYGNvbnZlcnRfdG9gIGlzIGAiZmFocmVuaGVpdCJgLCB0aGUgY29kZSBib2R5IGlzIGV4ZWN1dGVkLCBhbmQgaWYgbm90LCBpdCBpcyBza2lwcGVkLg0KDQpgYGB7cn0NCmNlbHNpdXMgPC0gMjANCmNvbnZlcnRfdG8gPC0gImtlbHZpbiINCg0KIyBpZiBjb252ZXJ0X3RvIGlzICJmYWhyZW5oZWl0IiwgY3JlYXRlIGZhaHJlbmhlaXQsIGFzIGMgKiA5LzUgKyAzMiwgdGhlbiBwcmludA0KaWYgKGNvbnZlcnRfdG8gPT0gImZhaHJlbmhlaXQiKXsNCiAgZmFocmVuaGVpdCA8LSBjZWxzaXVzICogOS81ICsgMzINCiAgcHJpbnQoZmFocmVuaGVpdCkNCn0NCmBgYA0KDQrigKMgQWRkIGFuIGBlbHNlYCBjbGF1c2UgdG8gaGFuZGxlIHRoZSBjYXNlIHdoZXJlIGBjb252ZXJ0X3RvYCBpcyBub3QgYCJmYWhyZW5oZWl0ImAuDQoNCmBgYHtyfQ0KY2Vsc2l1cyA8LSAyMA0KY29udmVydF90byA8LSAia2VsdmluIg0KDQojIGFkZCBlbHNlIGNsYXVzZSB0byBwcmludCBvcmlnaW5hbCBjZWxzaXVzIHZhbHVlDQppZiAoY29udmVydF90byA9PSAiZmFocmVuaGVpdCIpew0KICBmYWhyZW5oZWl0IDwtIGNlbHNpdXMgKiA5LzUgKyAzMg0KICBwcmludChmYWhyZW5oZWl0KQ0KfSBlbHNlIHsNCiAgcHJpbnQoY2Vsc2l1cykNCn0NCmBgYA0KDQrigKMgVXNlIGBlbHNlIGlmYCB0byBjaGVjayBtdWx0aXBsZSBzcGVjaWZpYyBjb25kaXRpb25zLg0KDQpgYGB7cn0NCmNlbHNpdXMgPC0gMjANCmNvbnZlcnRfdG8gPC0gImtlbHZpbiINCg0KaWYgKGNvbnZlcnRfdG8gPT0gImZhaHJlbmhlaXQiKSB7DQogIGZhaHJlbmhlaXQgPC0gKGNlbHNpdXMgKiA5LzUpICsgMzINCiAgcHJpbnQoZmFocmVuaGVpdCkNCn0gZWxzZSBpZiAoY29udmVydF90byA9PSAia2VsdmluIikgew0KICBrZWx2aW4gPC0gY2Vsc2l1cyArIDI3My4xNQ0KICBwcmludChrZWx2aW4pDQp9IGVsc2Ugew0KICBwcmludChjZWxzaXVzKQ0KfQ0KYGBgDQoNCuKAoyBDb2RlIGhhbmRsZXMgdGhyZWUgc2NlbmFyaW9zOiBjb252ZXJ0aW5nIHRvIEZhaHJlbmhlaXQsIEtlbHZpbiwgb3Iga2VlcGluZyBDZWxzaXVzLg0KDQrigKMgQ2FuIGhhdmUgYXMgbWFueSBgZWxzZSBpZmAgc3RhdGVtZW50cyBhcyB5b3UgbmVlZCwgYnV0IGNhbiBvbmx5IGhhdmUgb25lIGBlbHNlYCBzdGF0ZW1lbnQgYXR0YWNoZWQgdG8gYW4gYGlmYCBzdGF0ZW1lbnQuDQoNCuKAoyBGaW5hbGx5LCB3ZSBjYW4gZW5jYXBzdWxhdGUgdGhpcyBsb2dpYyBpbnRvIGEgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KY2Vsc2l1c19jb252ZXJ0IDwtIGZ1bmN0aW9uKGNlbHNpdXMsIGNvbnZlcnRfdG8pew0KICBpZiAoY29udmVydF90byA9PSAiZmFocmVuaGVpdCIpew0KICBvdXQgPC0gKGNlbHNpdXMgKiA5LzUpICsgMzINCn0gZWxzZSBpZiAoY29udmVydF90byA9PSAia2VsdmluIil7DQogIG91dCA8LSBjZWxzaXVzICsgMjczLjE1DQp9IGVsc2Ugew0KICBvdXQgPC0gIGNlbHNpdXMNCn0NCiAgcmV0dXJuKG91dCkNCn0NCmBgYA0KDQrigKMgTGV0J3MgdGVzdCB0aGUgZnVuY3Rpb246DQoNCmBgYHtyfQ0KY2Vsc2l1c19jb252ZXJ0KDIwLCAiZmFocmVuaGVpdCIpDQpjZWxzaXVzX2NvbnZlcnQoMjAsICJrZWx2aW4iKQ0KYGBgDQoNCuKAoyBPbmUgcHJvYmxlbTogc2lsZW50IGZhaWx1cmUuIElmIHdlIHBhc3MgaW4gYW4gaW52YWxpZCB2YWx1ZSBmb3IgYGNvbnZlcnRfdG9gLCB0aGUgZnVuY3Rpb24gZmFpbHMgd2l0aG91dCBhbiBpbmZvcm1hdGl2ZSBlcnJvciBtZXNzYWdlLg0KDQpgYGB7cn0NCmNlbHNpdXNfY29udmVydCgyMCwgImNlbHNpdXMiKQ0KY2Vsc2l1c19jb252ZXJ0KDIwLCAiZm9vIikgDQpgYGANCg0K4oCjIFdpbGwgbmVlZCB0byBhZGQgc29tZSBlcnJvciBoYW5kbGluZyB0byB0aGUgZnVuY3Rpb24uDQoNCjo6OiBwcmFjdGljZQ0KIyMjIERlYnVnZ2luZyBhIEZ1bmN0aW9uIHdpdGggQ29uZGl0aW9uYWwgTG9naWMgey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KQSBmdW5jdGlvbiBuYW1lZCBgY2hlY2tfbmVnYXRpdmVzYCBpcyBkZXNpZ25lZCB0byBhbmFseXplIGEgdmVjdG9yIG9mIG51bWJlcnMgaW4gUiBhbmQgcHJpbnQgYSBtZXNzYWdlIGluZGljYXRpbmcgd2hldGhlciB0aGUgdmVjdG9yIGNvbnRhaW5zIGFueSBuZWdhdGl2ZSBudW1iZXJzLiBIb3dldmVyLCB0aGUgZnVuY3Rpb24gY3VycmVudGx5IGhhcyBzeW50YXggZXJyb3JzLg0KDQpgYGB7ciBldmFsID0gRn0NCmNoZWNrX25lZ2F0aXZlcyA8LSBmdW5jdGlvbihudW1iZXJzKSB7DQogICB4IDwtIG51bWJlcnMNCiAgaWYgKGFueSh4IDwgMCkpIHsNCiAgICBwcmludCgieCBjb250YWlucyBuZWdhdGl2ZSBudW1iZXJzIikNCiAgICB9DQogIGVsc2Ugew0KICAgIHByaW50KCJ4IGRvZXMgbm90IGNvbnRhaW4gbmVnYXRpdmUgbnVtYmVycyIpDQogIH0NCn0NCmBgYA0KDQpJZGVudGlmeSBhbmQgY29ycmVjdCB0aGUgc3ludGF4IGVycm9ycyBpbiB0aGUgYGNoZWNrX25lZ2F0aXZlc2AgZnVuY3Rpb24uIEFmdGVyIGNvcnJlY3RpbmcgdGhlIGZ1bmN0aW9uLCB0ZXN0IGl0IHdpdGggdGhlIGZvbGxvd2luZyB2ZWN0b3JzIHRvIGVuc3VyZSBpdCB3b3JrcyBjb3JyZWN0bHk6IDEuIGBjKDgsIDMsIC0yLCA1KWAgMi4gYGMoMTAsIDIwLCAzMCwgNDApYA0KDQpgYGB7cn0NCngxIDwtIGMoOCwgMywgLTIsIDUpDQp4MiA8LSBjKDEwLCAyMCwgMzAsIDQwKQ0KDQpjaGVja19uZWdhdGl2ZXMoeDEpDQpjaGVja19uZWdhdGl2ZXMoeDIpDQpgYGANCg0KOjo6DQoNCiMjIEFyZ3VtZW50IENoZWNraW5nIHdpdGggQ29uZGl0aW9uYWxzDQoNCuKAoyBBcmd1bWVudCBjaGVja2luZyBpcyBjcnVjaWFsIGluIFIgZnVuY3Rpb25zIHRvIGVuc3VyZSBpbnB1dHMgYXJlIHNlbnNpYmxlLg0KDQrigKMgV2l0aG91dCBjaGVja3MsIGZ1bmN0aW9ucyBtYXkgcmV0dXJuIGluY29ycmVjdCByZXN1bHRzLCBmYWlsIHNpbGVudGx5LCBvciBmYWlsIHdpdGggYW4gdW5pbmZvcm1hdGl2ZSBlcnJvciBtZXNzYWdlLg0KDQrigKMgRXhhbXBsZTogVXNpbmcgYGNlbHNpdXNfY29udmVydCgpYCBmdW5jdGlvbiBmb3IgdGVtcGVyYXR1cmUgY29udmVyc2lvbi4NCg0KYGBge3J9DQpjZWxzaXVzX2NvbnZlcnQoMzAsICJjZW50aWdyYWRlIikNCmBgYA0KDQrigKMgKipJc3N1ZSoqOiBGYWlscyBzaWxlbnRseSB3aGVuIGBjb252ZXJ0X3RvYCBpcyBub3QgYSB2YWxpZCB0ZW1wZXJhdHVyZSBzY2FsZS4NCg0K4oCjICoqU29sdXRpb24qKjogSW1wbGVtZW50IGFyZ3VtZW50IGNoZWNraW5nIHVzaW5nIHRoZSBgc3RvcCgpYCBmdW5jdGlvbiBpbiBSLg0KDQrigKMgRXhhbXBsZTogVmFsaWRhdGUgYGNvbnZlcnRfdG9gIGFyZ3VtZW50LiBXZSdsbCB3cml0ZSB0aGVtIG91dCBmaXJzdCwgdGhlbiBpbnRlZ3JhdGUgdGhlbSBpbnRvIHRoZSBmdW5jdGlvbi4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCmNvbnZlcnRfdG8gPC0gImJhZCBzY2FsZSINCg0KIyBpZiBjb252ZXJ0X3RvIGlzIG5vdCBpbiBmYWhyZW5oZWl0LCBvciBrZWx2aW4sIHN0b3AoKSB3aXRoIGVycm9yIG1lc3NhZ2UNCmlmICghY29udmVydF90byAlaW4lIGMoImZhaHJlbmhlaXQiLCAia2VsdmluIikpew0KICBzdG9wKCJjb252ZXJ0IHRvIG11c3QgYmUgb25lIG9mICdmYWhyZW5oZWl0OyBvciAna2VsdmluJyIpDQp9DQpgYGANCg0K4oCjICoqSW50ZWdyYXRpb24gaW50byBgY2Vsc2l1c19jb252ZXJ0KClgIGZ1bmN0aW9uKio6DQoNCmBgYHtyfQ0KY2Vsc2l1c19jb252ZXJ0IDwtIGZ1bmN0aW9uKGNlbHNpdXMsIGNvbnZlcnRfdG8pew0KICANCiAgIyBDaGVja2luZyB2YWxpZGl0eQ0KICBpZiAoIWNvbnZlcnRfdG8gJWluJSBjKCJmYWhyZW5oZWl0IiwgImtlbHZpbiIsICJjZW50aWdyYWRlIikpew0KICBzdG9wKCJjb252ZXJ0IHRvIG11c3QgYmUgb25lIG9mICdmYWhyZW5oZWl0OyBvciAna2VsdmluJyIpDQogIH0NCiAgDQogICMgQ29udmVydGluZyB2YWx1ZQ0KICBpZiAoY29udmVydF90byA9PSAiZmFocmVuaGVpdCIpew0KICBvdXQgPC0gKGNlbHNpdXMgKiA5LzUpICsgMzINCn0gZWxzZSBpZiAoY29udmVydF90byA9PSAia2VsdmluIil7DQogIG91dCA8LSBjZWxzaXVzICsgMjczLjE1DQp9IGVsc2UgaWYgKGNvbnZlcnRfdG8gPT0gImNlbnRpZ3JhZGUiKXsNCiAgb3V0IDwtIGNlbHNpdXMNCn0NCiAgcmV0dXJuKG91dCkNCn0NCmBgYA0KDQrigKMgTm8gbG9uZ2VyIGEgbmVlZCBmb3IgYGVsc2VgLCBzaW5jZSBgc3RvcCgpYCB3aWxsIGhhbHQgZXhlY3V0aW9uIGlmIGBjb252ZXJ0X3RvYCBpcyBub3QgdmFsaWQuDQoNCuKAoyAqKlJlc3VsdCoqOiBDbGVhciBlcnJvciBtZXNzYWdlIGZvciBpbnZhbGlkIHRlbXBlcmF0dXJlIHNjYWxlcy4NCg0KYGBge3IgZXZhbCA9IEZ9DQpjZWxzaXVzX2NvbnZlcnQoMzAsICJjZW50aWdyYWRlIikNCmBgYA0KDQoqKlBSTyBUSVAqKg0KDQrigKMgKipCYWxhbmNpbmcgQXJndW1lbnQgQ2hlY2tpbmcqKjogQ2hlY2tpbmcgc2hvdWxkIGVuc3VyZSByZWxpYWJpbGl0eSB3aXRob3V0IG92ZXJjb21wbGljYXRpbmcgdGhlIGNvZGUgb3IgaW1wYWN0aW5nIHBlcmZvcm1hbmNlLg0KDQrigKMgWW91IHdpbGwgZGV2ZWxvcCBhIHNlbnNlIG9mIHRoZSByaWdodCBhbW91bnQgb2YgY2hlY2tpbmcgdGhyb3VnaCBleHBlcmllbmNlIGFuZCBleGFtaW5pbmcgb3RoZXJzJyBjb2RlLiBGb3Igbm93LCBub3RlIHRoYXQgaXQgaXMgdXN1YWxseSBnb29kIHRvIGVyciBvbiB0aGUgc2lkZSBvZiBtb3JlIGNoZWNraW5nLg0KDQo6OjogcHJhY3RpY2UNCiMjIyBBcmd1bWVudCBDaGVja2luZyBQcmFjdGljZSB7LnVubGlzdGVkIC51bm51bWJlcmVkfQ0KDQpDb25zaWRlciB0aGUgYGNhbGN1bGF0ZV9jYWxvcmllc2AgZnVuY3Rpb24gd2Ugd3JvdGUgZWFybGllcjoNCg0KYGBge3J9DQpjYWxjdWxhdGVfY2Fsb3JpZXMgPC0gZnVuY3Rpb24oY2FyYl9ncmFtcyA9IDAsIHByb3RlaW5fZ3JhbXMgPSAwLCBmYXRfZ3JhbXMgPSAwKSB7DQogIHJlc3VsdCA8LSAoY2FyYl9ncmFtcyAqIDQpICsgKHByb3RlaW5fZ3JhbXMgKiA0KSArIChmYXRfZ3JhbXMgKiA5KQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KYGBgDQoNCldyaXRlIGEgZnVuY3Rpb24gY2FsbGVkIGBjYWxjdWxhdGVfY2Fsb3JpZXMyKClgIHRoYXQgaXMgdGhlIHNhbWUgYXMgYGNhbGN1bGF0ZV9jYWxvcmllcygpYCBleGNlcHQgdGhhdCBpdCBjaGVja3MgaWYgdGhlIGBjYXJiX2dyYW1zYCwgYHByb3RlaW5fZ3JhbXNgLCBhbmQgYGZhdF9ncmFtc2AgYXJndW1lbnRzIGFyZSBudW1lcmljLiBJZiBhbnkgb2YgdGhlbSBhcmUgbm90IG51bWVyaWMsIHRoZSBmdW5jdGlvbiBzaG91bGQgcHJpbnQgYW4gZXJyb3IgbWVzc2FnZSB1c2luZyB0aGUgYHN0b3AoKWAgZnVuY3Rpb24uDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpjYWxjdWxhdGVfY2Fsb3JpZXMyIDwtIGZ1bmN0aW9uKGNhcmJfZ3JhbXMgPSAwLCBwcm90ZWluX2dyYW1zID0gMCwgZmF0X2dyYW1zID0gMCkgew0KICANCiAgIyB5b3VyIGNvZGUgaGVyZQ0KICBpZiAoIWlzLm51bWVyaWMoYyhjYXJiX2dyYW1zLCBwcm90ZWluX2dyYW1zLCBmYXRfZ3JhbXMpKSl7DQogICAgc3RvcCgiQWxsIGFyZ3VtZW50cyBtdXN0IGJlIG51bWVyaWMiKQ0KfSANCiAgcmVzdWx0IDwtIChjYXJiX2dyYW1zICogNCkgKyAocHJvdGVpbl9ncmFtcyAqIDQpICsgKGZhdF9ncmFtcyAqIDkpDQogIHJldHVybihyZXN1bHQpDQp9DQoNCmNhbGN1bGF0ZV9jYWxvcmllczIoImZpdmUiLCAyMCwgMzApDQoNCmBgYA0KOjo6DQoNCiMjIFZlY3Rvcml6ZWQgQ29uZGl0aW9uYWxzDQoNCuKAoyBJbXBvcnRhbnQgcmVhbGl6YXRpb24gYW5kIHNvdXJjZSBvZiBlcnJvcnM6IGBpZmAgc3RhdGVtZW50cyBhcmUgbm90IHZlY3Rvcml6ZWQgYW5kIG9ubHkgZXZhbHVhdGUgdGhlIGZpcnN0IGVsZW1lbnQgb2YgYSB2ZWN0b3IuDQoNCuKAoyBDb25zaWRlciB0aGlzIGF0dGVtcHQgYXQgYSBmdW5jdGlvbiBgY2xhc3NpZnlfdGVtcGAgZm9yIGNsYXNzaWZ5aW5nIHRlbXBlcmF0dXJlIHJlYWRpbmdzLg0KDQpgYGB7cn0NCmNsYXNzaWZ5X3RlbXAgPC0gZnVuY3Rpb24odGVtcCkgew0KICBpZiAodGVtcCA8IDM1KSB7DQogICAgcHJpbnQoImh5cG90aGVybWlhIikNCiAgfSBlbHNlIGlmICh0ZW1wID49IDM1ICYgdGVtcCA8PSAzNykgew0KICAgIHByaW50KCJub3JtYWwiKQ0KICB9IGVsc2UgaWYgKHRlbXAgPiAzNykgew0KICAgIHByaW50KCJmZXZlciIpDQogIH0NCn0NCmBgYA0KDQrigKMgV29ya3MgZm9yIGEgc2luZ2xlIHZhbHVlLCBidXQgbm90IGZvciB2ZWN0b3JzLg0KDQpgYGB7cn0NCmNsYXNzaWZ5X3RlbXAoMzYpDQoNCnRlbXBfdmVjIDwtIGMoMzYsIDM3LCAzOCkNCmNsYXNzaWZ5X3RlbXAodGVtcF92ZWMpICAjIFRoaXMgd29uJ3Qgd29yayBjb3JyZWN0bHkNCmBgYA0KDQrigKMgRm9yIGNvbmRpdGlvbmFsIHN0YXRlbWVudHMgZm9yIHZlY3RvcnMsIHdlIHRoZXJlZm9yZSB1c2UgYGlmZWxzZWAgb3IgYGRwbHlyOjpjYXNlX3doZW5gLg0KDQpgYGB7cn0NCmNsYXNzaWZ5X3RlbXAgPC0gZnVuY3Rpb24odGVtcCl7DQogb3V0IDwtICBpZmVsc2UodGVtcCA8IDM1LCAiaHlwb3RoZXJtaWEiLA0KICAgICAgICAgICAgICAgIGlmZWxzZSh0ZW1wID49IDM1ICYgdGVtcCA8PSAzNywgIm5vcm1hbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UodGVtcCA+IDM3LCAiZmV2ZXIiLCAiTkEiKSkpDQogcmV0dXJuKG91dCkNCn0NCiAgICAgICMgaWZlbHNlIHRlbXAgbGVzcyB0aGFuIDM1LCByZXR1cm4gImh5cG90aGVybWlhIg0KICAgICAgIyBuZXN0ZWQgaWZlbHNlIHRlbXAgYmV0d2VlbiAzNSBhbmQgMzcsIHJldHVybiAibm9ybWFsIg0KICAgICAgIyBuZXN0ZWQgaWZlbHNlIHRlbXAgZ3JlYXRlciB0aGFuIDM3LCByZXR1cm4gImZldmVyIg0KDQpjbGFzc2lmeV90ZW1wKHRlbXBfdmVjKSAgIyBXb3JrcyBmb3IgdmVjdG9yDQpgYGANCg0K4oCjIGBkcGx5cjo6Y2FzZV93aGVuYCBpcyBhIG1vcmUgcmVhZGFibGUgYWx0ZXJuYXRpdmUuDQoNCmBgYHtyfQ0KY2xhc3NpZnlfdGVtcCA8LSBmdW5jdGlvbih0ZW1wKSB7DQogIGNhc2Vfd2hlbigNCiAgICB0ZW1wIDwgMzUgfiAiaHlwb3RoZXJtaWEiLA0KICAgIHRlbXAgPj0gMzUgJiB0ZW1wIDw9IDM3IH4gIm5vcm1hbCIsDQogICAgdGVtcCA+IDM3IH4gImZldmVyIiwNCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICApDQp9DQoNCmNsYXNzaWZ5X3RlbXAodGVtcF92ZWMpICAjIFRoaXMgYWxzbyB3b3JrcyBhcyBleHBlY3RlZA0KYGBgDQoNCuKAoyBUaGlzIGZ1bmN0aW9uIGNhbiBiZSBzZWFtbGVzc2x5IGludGVncmF0ZWQgd2l0aCBkYXRhIGZyYW1lcy4NCg0KYGBge3J9DQpOSFNSZGF0YXNldHM6OnN5bnRoZXRpY19uZXdzX2RhdGEgJT4lIA0KICBzZWxlY3QodGVtcCkgJT4lDQogIG11dGF0ZSh0ZW1wX2NsYXNzaWYgPSBjbGFzc2lmeV90ZW1wKHRlbXApKQ0KICANCmBgYA0KDQo6OjogcHJhY3RpY2UNCiMjIyBQcmFjdGljZSBDbGFzc2lmeWluZyBEb3NhZ2Ugb2YgSXNvbmlhemlkIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCkxldCdzIGFwcGx5IHRoaXMga25vd2xlZGdlIHRvIGEgcHJhY3RpY2FsIGNhc2UuIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgYXR0ZW1wdCBhdCB3cml0aW5nIGEgZnVuY3Rpb24gdGhhdCBjYWxjdWxhdGVzIGRvc2FnZXMgb2YgdGhlIGRydWcgaXNvbmlhemlkIGZvciBhZHVsdHMgd2VpZ2hpbmcgbW9yZSB0aGFuIDMwa2c6DQoNCmBgYHtyfQ0KY2FsY3VsYXRlX2lzb25pYXppZF9kb3NhZ2UgPC0gZnVuY3Rpb24od2VpZ2h0KSB7DQogIGlmICh3ZWlnaHQgPCAzMCkgew0KICAgIHN0b3AoIldlaWdodCBtdXN0IGJlIGF0IGxlYXN0IDMwIGtnLiIpDQogIH0gZWxzZSBpZiAod2VpZ2h0IDw9IDM1KSB7DQogICAgcmV0dXJuKDE1MCkNCiAgfSBlbHNlIGlmICh3ZWlnaHQgPD0gNDUpIHsNCiAgICByZXR1cm4oMjAwKQ0KICB9IGVsc2UgaWYgKHdlaWdodCA8PSA1NSkgew0KICAgIHJldHVybigzMDApDQogIH0gZWxzZSBpZiAod2VpZ2h0IDw9IDcwKSB7DQogICAgcmV0dXJuKDMwMCkNCiAgfSBlbHNlIHsNCiAgICByZXR1cm4oMzAwKQ0KICB9DQp9DQpgYGANCg0KVGhpcyBmdW5jdGlvbiBmYWlscyB3aXRoIGEgdmVjdG9yIG9mIHdlaWdodHMuIFlvdXIgdGFzayBpcyB0byB3cml0ZSBhIG5ldyBmdW5jdGlvbiBgY2FsY3VsYXRlX2lzb25pYXppZF9kb3NhZ2UyKClgIHRoYXQgY2FuIGhhbmRsZSB2ZWN0b3IgaW5wdXRzLiBUbyBlbnN1cmUgYWxsIHdlaWdodHMgYXJlIGFib3ZlIDMwa2csIHlvdSdsbCB1c2UgdGhlIGBhbnkoKWAgZnVuY3Rpb24gd2l0aGluIHlvdXIgZXJyb3IgY2hlY2tpbmcuDQoNCkhlcmUncyBhIHNjYWZmb2xkIHRvIGdldCB5b3Ugc3RhcnRlZDoNCg0KYGBge3J9DQpjYWxjdWxhdGVfaXNvbmlhemlkX2Rvc2FnZTIgPC0gZnVuY3Rpb24od2VpZ2h0KSB7DQogIGlmIChhbnkod2VpZ2h0IDwgMzApKSBzdG9wKCJXZWlnaHRzIG11c3QgYWxsIGJlIGF0IGxlYXN0IDMwIGtnLiIpDQogICANCiAgIyBZb3VyIGNvZGUgaGVyZSAgDQogIHsNCiAgICBvdXQgPC0gaWZlbHNlKHdlaWdodCA8PSAzNSwgMTUwLA0KICAgICAgICAgIGlmZWxzZSh3ZWlnaHQgPD0gNDUsIDIwMCwNCiAgICAgICAgICAgICAgICAgaWZlbHNlKHdlaWdodCA8PSA1NSwgMzAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHdlaWdodCA8PSA3MCwgMzAwLCAzMDApKSkpDQogICAgcmV0dXJuKG91dCkNCiAgDQogIH0NCiAgcmV0dXJuKG91dCkNCn0NCg0KY2FsY3VsYXRlX2lzb25pYXppZF9kb3NhZ2UyKGMoMzAsIDQwLCA1MCwgMTAwKSkgIA0KYGBgDQo6OjoNCg0KIyBTdG9yaW5nIHlvdXIgZnVuY3Rpb25zIGZvciBmdXR1cmUgdXNlDQoNClRvIHN0b3JlIHlvdXIgZnVuY3Rpb25zIGluIFIgZm9yIGZ1dHVyZSB1c2UsIHlvdSBjYW4gc2F2ZSB0aGVtIGluIGEgc2NyaXB0IG9yIHBhY2thZ2UuIEhlcmXigJlzIGhvdzoNCg0KIyMjIDEuIFNhdmUgRnVuY3Rpb25zIGluIGFuIFIgU2NyaXB0IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQrigKMgKipDcmVhdGUgYSBzY3JpcHQgZmlsZSoqOg0KDQoxLiBPcGVuIFJTdHVkaW8gKG9yIGFueSB0ZXh0IGVkaXRvcikuDQoyLiBXcml0ZSB5b3VyIGZ1bmN0aW9ucyBpbiBhIG5ldyBzY3JpcHQgZmlsZSAoZS5nLiwgbXlfZnVuY3Rpb25zLlIpLg0KMy4gU2F2ZSB0aGUgZmlsZSBpbiBhIGRlc2lyZWQgbG9jYXRpb24uDQoNCuKAoyAqKkxvYWQgdGhlIHNjcmlwdCoqOiBUbyB1c2UgdGhlIGZ1bmN0aW9ucyBpbiBmdXR1cmUgc2Vzc2lvbnMsIGxvYWQgdGhlIHNjcmlwdCB1c2luZzoNCg0KYGBgDQpzb3VyY2UoKCJwYXRoL3RvL215X2Z1bmN0aW9ucy5SIikpDQpgYGANCg0K4oCjICoqRXhhbXBsZSoqOg0KDQpgYGB7cn0NCiMgU2F2ZSB0aGlzIGluIG15X2Z1bmN0aW9ucy5SDQpjYWxjdWxhdGVfY2Fsb3JpZXMgPC0gZnVuY3Rpb24oY2FyYl9ncmFtcywgcHJvdGVpbl9ncmFtcywgZmF0X2dyYW1zKSB7DQogIChjYXJiX2dyYW1zICogNCkgKyAocHJvdGVpbl9ncmFtcyAqIDQpICsgKGZhdF9ncmFtcyAqIDkpDQp9DQpgYGANCg0KVGhlbiBsb2FkIHRoZSBmaWxlOg0KDQpgYGB7cn0NCnNvdXJjZShoZXJlKCJnbG9iYWwvZnVuY3Rpb25zL215X2Z1bmN0aW9ucy5SIikpDQpjYWxjdWxhdGVfY2Fsb3JpZXMoMTAsIDUsIDIpDQpgYGANCg0KIyMjIDIuIFNhdmUgRnVuY3Rpb25zIGluIGFuIFIgRGF0YSBGaWxlIFNhdmUgZnVuY3Rpb24gb2JqZWN0czogey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0K4oCjICoqU2F2ZSBmdW5jdGlvbiBvYmplY3RzKio6DQoNCmBgYHtyfQ0Kc2F2ZShjYWxjdWxhdGVfY2Fsb3JpZXMyLCBjYWxjX2JtaSwgcG91bmRzX3RvX2tnLCB5ZWFyc190b19tb250aHMsIGNlbHNpdXNfY29udmVydCwgY2FsY3VsYXRlX2lzb25pYXppZF9kb3NhZ2UyLCBjZWxzaXVzX3RvX2ZhaHJlbmhlaXQsIGZhaHJlbmhlaXRfdG9fY2Vsc2l1cywgY2xhc3NpZnlfdGVtcDIsIGZpbGUgPSAibXlfZnVuY3Rpb25zLlJEYXRhIikNCmBgYA0KDQrigKMgKipMb2FkIHRoZSBmaWxlKio6IEluIGZ1dHVyZSBzZXNzaW9ucywgeW91IGNhbiBsb2FkIHRoZSAuUkRhdGEgZmlsZToNCg0KYGBge3J9DQpsb2FkKCJteV9mdW5jdGlvbnMuUkRhdGEiKQ0KDQp0ZW1wX3ZlYyA8LSBjKDM2LCAzNywgMzgpDQpjbGFzc2lmeV90ZW1wMih0ZW1wX3ZlYykNCmBgYA0KDQojIyMgMy4gQ3JlYXRlIGEgQ3VzdG9tIFIgUGFja2FnZTogey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KSWYgeW91IHBsYW4gdG8gdXNlIHlvdXIgZnVuY3Rpb25zIGZyZXF1ZW50bHkgYWNyb3NzIG11bHRpcGxlIHByb2plY3RzLCBjcmVhdGluZyBhIHBhY2thZ2UgaXMgYSBnb29kIGFwcHJvYWNoLg0KDQpCdWlsZGluZyBwYWNrYWdlcyBkb2VzbuKAmXQgaGF2ZSB0byBiZSBpbnRpbWlkYXRpbmchIFRoYW5rcyB0byB0aGUgdGlkeXZlcnNlIHRlYW0sIGdldHRpbmcgc3RhcnRlZCBpcyBzaW1wbGUgd2l0aCBSU3R1ZGlvIGFuZCB0aGUgKipkZXZ0b29scyoqIGFuZCAqKnVzZXRoaXMqKiBwYWNrYWdlcy4gSW4gdGhpcyA8YSBocmVmPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9saXZlL0VwVGtUNlJrZ2JzP3NpPXJRS3diU0Rha0FGY0dpbEsiIHRhcmdldD0iX2JsYW5rIj5vbmUtaG91ciBwcmVzZW50YXRpb248L2E+DQosIHlvdSdsbCBsZWFybiB0aGUgZnVuZGFtZW50YWxzIG9mIFIgcGFja2FnZSBkZXZlbG9wbWVudCBhbmQgZ2FpbiB0aGUgY29uZmlkZW5jZSB0byBzdGFydCBidWlsZGluZyB5b3VyIG93biBwYWNrYWdlcyENCg0K4oCjIENsaWNrIFtoZXJlXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9yZWRpcmVjdD9ldmVudD12aWRlb19kZXNjcmlwdGlvbiZyZWRpcl90b2tlbj1RVUZGTFVocWJESnVla3gyY2sxTGVVWjNabUpNTlVOb1ExVlFNME10VldwU1FYeEJRM0p0YzB0c1VFVjNXRE5qZFhwTE9IbHZNbkpMWWpGSmVqVm9jR1F3TjNsWVUwVkhRM0JOWVU1UGEwNUhRbFp5YkdJeWRsWlhSekppU0dSUlNWOU9iRnB2VGxKSVZHYzRjbXh6U1dwclpEaHhORmRRT0hJeVIwbFpOWEpCVUZGQ1gzTnBUMjkyUWtOSVpIQndUWEl3TjFaSk4xaERjdyZxPWh0dHBzJTNBJTJGJTJGY29sb3JhZG8ucnN0dWRpby5jb20lMkZyc2MlMkZwa2ctYnVpbGRpbmclMkYmdj1FcFRrVDZSa2dicyl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgKipzbGlkZXMqKi4NCg0K4oCjIENsaWNrIFtoZXJlXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9yZWRpcmVjdD9ldmVudD12aWRlb19kZXNjcmlwdGlvbiZyZWRpcl90b2tlbj1RVUZGTFVocWF6RTVUMU5sTVZwMU4xTnNOblZIT1RBNVpWaGtRbkppWXpKa1ozeEJRM0p0YzB0cmRHSkVVRlpaUjFKMmNXdHZTMGR6YmpKR1lrbzFiV2xzYXpRdGNqZDNaa1pKVkVNelpsQlhaalpKYVRSUWNXcFNkVlZEY0hGR2IyVXRSM3B2UkVSNVZFbHFNa3A0ZURsT1VVMTJTVWhLVGpSU1ZYVlFjREYzZDJ0amVXVnNZbkZYTjBkUU5VRkxWVWhDVW5sNFpXOU9jdyZxPWh0dHBzJTNBJTJGJTJGZ2l0aHViLmNvbSUyRmp0aG9tYXNtb2NrJTJGcGtnLWJ1aWxkaW5nJnY9RXBUa1Q2UmtnYnMpe3RhcmdldD0iX2JsYW5rIn0gZm9yICoqc291cmNlIGNvZGUqKiANCg0K4oCjIENsaWNrIFtoZXJlXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9yZWRpcmVjdD9ldmVudD12aWRlb19kZXNjcmlwdGlvbiZyZWRpcl90b2tlbj1RVUZGTFVocWJEQXpXVkIwWVRobGJrUlFkbkJpTURKcWIwaG9RMlpSYWkxSVozeEJRM0p0YzB0c2JsRllZbWx2WlVKcFNuaHRTMVkzYmxWa1MzWlFPRFZzTUZscFUzaG5jbTVFVjNGb1JGZGtMVjlVT0hsaFJYbHJlVzh5T1VaaFEyWkpWRzFqUTFGUE1IWlNTREZ6UlV0cFZFYzFWbkJrV1hGcmVIQkVSbUUwUlVsMFQzY3laVGRoTUZwdk0zWXpaSGxQUjJ4RVQxTkxNQSZxPWh0dHBzJTNBJTJGJTJGZGV2dG9vbHMuci1saWIub3JnJTJGJnY9RXBUa1Q2UmtnYnMpe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gKipkZXZ0b29scyoqDQoNCuKAoyBDbGljayBbaGVyZV0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vcmVkaXJlY3Q/ZXZlbnQ9dmlkZW9fZGVzY3JpcHRpb24mcmVkaXJfdG9rZW49UVVGRkxVaHFiWGRpWVZjMVNXSmpWbnBUYlVkRlNscDBhbkV0UzFkNFVYZEZVWHhCUTNKdGMwdHNPVEpSVGpGV2FrWmtPVVo0VTBOcmRWRm9NRWQ2VEdsWmFHUkVVRkEwZURCTVVUWkVhR2hTVXpoNVpWSTNObWRyVDNSa1NWWXRjRkZwVkVKbE4weGlURlZSU2xaNk4wc3RhMk5HVkVzM1ZXMUxlVXRyUVZwbGRtOXJaWEJWTkhOdmJYbG9hRTk0YzB0cWJXeHhiMmhOWncmcT1odHRwcyUzQSUyRiUyRnVzZXRoaXMuci1saWIub3JnJTJGJnY9RXBUa1Q2UmtnYnMpe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gKip1c2V0aGlzKioNCg0K4oCjIENsaWNrIFtoZXJlXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9yZWRpcmVjdD9ldmVudD12aWRlb19kZXNjcmlwdGlvbiZyZWRpcl90b2tlbj1RVUZGTFVocWJqaGpVMGcxY2xOQlpWZDNVemxDYm5GbmNqZDNXR2N4Y0dKR2QzeEJRM0p0YzB0c1pYRkNOMWxyUXpSclJrMUZZMHRMY205SmVFMVZhR1Z0UlZSck5UbHNabWx6YTA1SlNXeGFSQzB5YzNodU4zVjZTMUZJV1hKNlFtcHRPVk5qZFRaV1NFTnRNMk54ZFdrMGRGUk5URVZYUzFoRmRYZGFjVXA0UW5OV2RFcHFiamhIVGpVeVRYcE5OamhITkVoUlJFMDVjdyZxPWh0dHBzJTNBJTJGJTJGci1wa2dzLm9yZyUyRmluZGV4Lmh0bWwmdj1FcFRrVDZSa2dicyl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9yZSAgcmVzb3VyY2VzIG9uICoqUiBQYWNrYWdlcyBib29rKioNCg0K4oCjIExlYXJuIGhvdyB0byBjcmVhdGUgYSBwYWNrYWdlLCB0aGUgZnVuZGFtZW50YWwgdW5pdCBvZiBzaGFyZWFibGUsIHJldXNhYmxlLCBhbmQgcmVwcm9kdWNpYmxlIFIgY29kZSBieSByZWFkaW5nIGFuZCBzdHVkeWluZyB0aGUgMm5kIGVkaXRpb24gb2YgW1IgUGFja2FnZXNdKGh0dHBzOi8vci1wa2dzLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0sIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBKZW5uaWZlciBCcnlhbiANCg0K4oCjIFRoaXMgW3lvdXR1YmUgdmlkZW86IEhvdyB0byBDcmVhdGUgWW91ciBPd24gUGFja2FnZSBpbiBSU3R1ZGlvXShodHRwczovL3lvdXR1LmJlL3JzUW9FZ1dlSk1rP3NpPWoyQjVTWV9rXzRrVnFWSFQpe3RhcmdldD0iX2JsYW5rIn0gaXMgYWxzbyBoZWxwZnVsIGluIGNyZWF0aW5nIHlvdXIgb3duIHBhY2thZ2UgYW5kIHN0b3JpbmcgaW4gR2l0SHViLg0KDQoNCiMjIyA0LiBTdG9yZSBGdW5jdGlvbnMgaW4gdGhlIEdsb2JhbCBFbnZpcm9ubWVudCB7LnVubGlzdGVkIC51bm51bWJlcmVkfQ0KIA0KDQpZb3UgY2FuIGFsc28gZGVmaW5lIGZ1bmN0aW9ucyBpbiB5b3VyIGAuUnByb2ZpbGVgIGZpbGUsIHNvIHRoZXkgYXJlIGF2YWlsYWJsZSBldmVyeSB0aW1lIHlvdSBzdGFydCBSOg0KDQrigKMgRWRpdCB5b3VyICBgLlJwcm9maWxgZToNCg0KYGBge3IgZXZhbD1GQUxTRX0NCmZpbGUuZWRpdCgifi8uUnByb2ZpbGUiKQ0KYGBgDQoNCuKAoyBBZGQgeW91ciBmdW5jdGlvbiBkZWZpbml0aW9ucyB0byB0aGUgZmlsZS4NCg0K4oCjIFNhdmUgYW5kIHJlc3RhcnQgeW91ciBSIHNlc3Npb24gdG8gYWNjZXNzIHRoZSBmdW5jdGlvbnMuDQoNCioqQ2hvb3NlIHRoZSBtZXRob2QgdGhhdCBiZXN0IGZpdHMgeW91ciB3b3JrZmxvdyBhbmQgdGhlIGZyZXF1ZW5jeSBvZiB1c2UgZm9yIHlvdXIgZnVuY3Rpb25zISoqDQoNCiMgQW5zd2VyIEtleQ0KDQojIyMgQWdlIE1vbnRocyBGdW5jdGlvbiB7LnVubGlzdGVkIC51bm51bWJlcmVkfQ0KDQpgYGB7cn0NCnllYXJzX3RvX21vbnRocyA8LSBmdW5jdGlvbih5ZWFycykgew0KICBtb250aHMgPC0geWVhcnMgKiAxMg0KICByZXR1cm4obW9udGhzKQ0KfQ0KDQojIFRlc3QNCnllYXJzX3RvX21vbnRocygxMikNCmBgYA0KDQojIyMgQ2Vsc2l1cyB0byBGYWhyZW5oZWl0IEZ1bmN0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCmBgYHtyfQ0KY2Vsc2l1c190b19mYWhyZW5oZWl0IDwtIGZ1bmN0aW9uKGNlbHNpdXMpIHsNCiAgZmFocmVuaGVpdCA8LSBjZWxzaXVzICogMS44ICsgMzINCiAgcmV0dXJuKGZhaHJlbmhlaXQpDQp9DQoNCiMgVGVzdA0KYmVhdmVyMSAlPiUNCiAgc2VsZWN0KHRlbXApICU+JQ0KICBtdXRhdGUoRmFocmVuaGVpdCA9IGNlbHNpdXNfdG9fZmFocmVuaGVpdCh0ZW1wKSkNCmBgYA0KDQojIyMgQk1JIEZ1bmN0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCmBgYHtyfQ0KY2FsY19ibWkgPC0gZnVuY3Rpb24od2VpZ2h0LCBoZWlnaHQpIHsNCiAgYm1pIDwtIHdlaWdodCAvIChoZWlnaHReMikNCiAgcmV0dXJuKGJtaSkNCn0NCg0KIyBUZXN0DQpsaWJyYXJ5KG1lZGljYWxkYXRhKQ0KbWVkaWNhbGRhdGE6OnNtYXJ0cGlsbCAlPiUNCiAgYXNfdGliYmxlKCkgJT4lDQogIHNlbGVjdChXZWlnaHQsIEhlaWdodCkgJT4lDQogIG11dGF0ZShCTUkgPSBjYWxjX2JtaShXZWlnaHQsIEhlaWdodCkpDQpgYGANCg0KIyMjIFByYWN0aWNlIHdpdGggdGhlIC4uLiBBcmd1bWVudCB7LnVubGlzdGVkIC51bm51bWJlcmVkfQ0KDQpgYGB7cn0NCmNhbGN1bGF0ZV9jYWxvcmllcyA8LSBmdW5jdGlvbihjYXJiX2dyYW1zLCBwcm90ZWluX2dyYW1zLCBmYXRfZ3JhbXMsIC4uLikgew0KICByZXN1bHQgPC0gKGNhcmJfZ3JhbXMgKiA0KSArIChwcm90ZWluX2dyYW1zICogNCkgKyAoZmF0X2dyYW1zICogOSkNCiAgcmVzdWx0X2Zvcm1hdHRlZCA8LSBmb3JtYXQocmVzdWx0LCAuLi4pDQogIHJldHVybihyZXN1bHQpDQp9DQpgYGANCg0KIyMjIERlYnVnZ2luZyBhIEZ1bmN0aW9uIHdpdGggQ29uZGl0aW9uYWwgTG9naWMgey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KYGBge3J9DQpjaGVja19uZWdhdGl2ZXMgPC0gZnVuY3Rpb24obnVtYmVycykgew0KICBpZiAoYW55KG51bWJlcnMgPCAwKSkgew0KICAgIHByaW50KCJ4IGNvbnRhaW5zIG5lZ2F0aXZlIG51bWJlcnMiKQ0KICB9IGVsc2Ugew0KICAgIHByaW50KCJ4IGRvZXMgbm90IGNvbnRhaW4gbmVnYXRpdmUgbnVtYmVycyIpDQogIH0NCn0NCg0KIyBUZXN0DQpjaGVja19uZWdhdGl2ZXMoYyg4LCAzLCAtMiwgNSkpDQpjaGVja19uZWdhdGl2ZXMoYygxMCwgMjAsIDMwLCA0MCkpDQpgYGANCg0KIyMjIEFyZ3VtZW50IENoZWNraW5nIFByYWN0aWNlIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCmBgYHtyfQ0KY2FsY3VsYXRlX2NhbG9yaWVzMiA8LSBmdW5jdGlvbihjYXJiX2dyYW1zID0gMCwgcHJvdGVpbl9ncmFtcyA9IDAsIGZhdF9ncmFtcyA9IDApIHsNCg0KICBpZiAoIWlzLm51bWVyaWMoY2FyYl9ncmFtcykpIHsNCiAgICBzdG9wKCJjYXJiX2dyYW1zIG11c3QgYmUgbnVtZXJpYyIpDQogIH0NCiAgDQogIGlmICghaXMubnVtZXJpYyhwcm90ZWluX2dyYW1zKSkgew0KICAgIHN0b3AoInByb3RlaW5fZ3JhbXMgbXVzdCBiZSBudW1lcmljIikNCiAgfQ0KICANCiAgaWYgKCFpcy5udW1lcmljKGZhdF9ncmFtcykpIHsNCiAgICBzdG9wKCJmYXRfZ3JhbXMgbXVzdCBiZSBudW1lcmljIikNCiAgfQ0KICANCiAgDQogIHJlc3VsdCA8LSAoY2FyYl9ncmFtcyAqIDQpICsgKHByb3RlaW5fZ3JhbXMgKiA0KSArIChmYXRfZ3JhbXMgKiA5KQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KYGBgDQoNCiMjIyBQcmFjdGljZSBDbGFzc2lmeWluZyBEb3NhZ2Ugb2YgSXNvbmlhemlkIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCmBgYHtyfQ0KY2FsY3VsYXRlX2lzb25pYXppZF9kb3NhZ2UyIDwtIGZ1bmN0aW9uKHdlaWdodCkgew0KICBpZiAoYW55KHdlaWdodCA8IDMwKSkgc3RvcCgiV2VpZ2h0cyBtdXN0IGFsbCBiZSBhdCBsZWFzdCAzMCBrZy4iKQ0KDQogIGRvc2FnZSA8LSBjYXNlX3doZW4oDQogICAgd2VpZ2h0IDw9IDM1IH4gMTUwLA0KICAgIHdlaWdodCA8PSA0NSB+IDIwMCwNCiAgICB3ZWlnaHQgPD0gNTUgfiAzMDAsDQogICAgd2VpZ2h0IDw9IDcwIH4gMzAwLA0KICAgIFRSVUUgfiAzMDANCiAgKQ0KICAgIHJldHVybihkb3NhZ2UpDQp9DQoNCmNhbGN1bGF0ZV9pc29uaWF6aWRfZG9zYWdlMihjKDMwLCA0MCwgNTAsIDEwMCkpDQpgYGANCg0KIyBSZWZlcmVuY2VzDQoNClNvbWUgbWF0ZXJpYWwgaW4gdGhpcyBsZXNzb24gd2FzIGFkYXB0ZWQgZnJvbSB0aGUgZm9sbG93aW5nIHNvdXJjZXM6DQoNCuKAoyBCYXJuaWVyLCBKdWxpZW4uIOKAnEludHJvZHVjdGlvbiDDoCBSIGV0IGF1IHRpZHl2ZXJzZS7igJ0gQWNjZXNzZWQgTWF5IDIzLCAyMDIyLiBbaHR0cHM6Ly9qdWJhLmdpdGh1Yi5pby90aWR5dmVyc2VdKGh0dHBzOi8vanViYS5naXRodWIuaW8vdGlkeXZlcnNlKQ0KDQrigKMgV2lja2hhbSwgSGFkbGV5OyBHcm9sZW11bmQsIEdhcnJldHQuIOKAnFIgZm9yIERhdGEgU2NpZW5jZS7igJ0gQWNjZXNzZWQgTWF5IDI1LCAyMDIyLiBbaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKQ0KDQrigKMgV2lja2hhbSwgSGFkbGV5OyBKZW5uaWZlciBCcnlhbi4g4oCcUiBQYWNrYWdlcyAoMmUpLuKAnSBBY2Nlc3NlZCBOb3YgMjAsIDIwMjQuW1IgUGFja2FnZXMgKDJlKV0oaHR0cHM6Ly9yLXBrZ3Mub3JnLyl7dGFyZ2V0PSJfYmxhbmsifQ==