library(tidyverse)
library(readxl)
library(ggthemes)
theme_set(theme_clean())

# Import data and clean names

district_data <- read_excel("../raw/NHGIS_District_data.xlsx")
source("../scripts/names_list.R")
names(district_data) <- names_list

Goals

  • Convert from Margin of Error columns to a margin of error (34% - 43% –> 9%)
  • Calculate standard error based on SE equation for proportions
  • Figure out the z* value used for the margins of error
  • Explore the relationship between number of children and margin of error

Motivation and Explanation

The data in the Excel file gives percentages of different households in school districts which fall under certain categories. Associated with these percentages, there is a margin of error. As an example, this is what some of the columns in our dataset look like:

district_data %>%
    head(2) %>%
    select(2:4, 6, 7)
## # A tibble: 2 × 5
##   state   dist                        children pct_SP SP_MOE
##   <chr>   <chr>                          <dbl>  <dbl> <chr> 
## 1 Alabama Fort Rucker School District      985 0.0490 0%-10%
## 2 Alabama Maxwell AFB School District      292 0.102  3%-17%

Our goal is to replace these margin of error columns with something which will be more useful, such as the one-sided range of the margin of error. We will first try this out with one column, then apply the analysis to every margin of error column. As an example, we can see that the Maxwell AFB School District has a margin of error going from \(3\%\) to \(17\%\). The range of this would be \(17%-3%=14%\), half of which would be \(7\%\). This gives us a tool to discover how they may have calculated the margin of error.

Mathematically, we know that a margin of error for a sample proportion is given by:

\[ \hat p \pm z^* \sqrt{\frac{\hat p (1-\hat p)}{n}} \]

This term which is added onto the estimated proportion provides the margin of error, meaning that what they have labeled as a “Margin of Error” is actually closer in meaning to a confidence interval with \(z^*\) standard errors away from the estimated proportion. For the sake of terminology, we will define Margin of Error as the one-sided version of the confidence interval, such that \[ {\rm ME} = z^* \sqrt{\frac{\hat p (1-\hat p)}{n}}. \]

Analysis

We will go in order of the goals, starting with converting the columns to a more usable format.

Margin of Error Extraction

We’ll start with a single column, then apply that technique to every margin of error column in the original data frame. We’ll use the Single Parent margin of error as the example.

moe_example <- district_data %>% 
    select(1, 4, 6, 7)
moe_example %>% head(5)
## # A tibble: 5 × 4
##   school_ID children pct_SP SP_MOE 
##   <chr>        <dbl>  <dbl> <chr>  
## 1 00001          985 0.0490 0%-10% 
## 2 00003          292 0.102  3%-17% 
## 3 00005         4591 0.353  26%-44%
## 4 00006         8299 0.295  24%-35%
## 5 00007        15397 0.208  17%-25%

One approach to this problem is to remove the percentage signs, split the column at the hyphen, then use the resulting two columns to calculate a difference and divide it by two. Another approach is to use regular expressions to capture the numbers, and subtract and divide them directly. We’ll utilize this second method using str_extract_all from the stringr package.

str_example <- "0%-10%"
str_extract_all(str_example, "\\d+") %>% 
    unlist() %>%
    as.numeric()
## [1]  0 10

We can see that for this example string, we can extract the numbers, unlist, and convert to numeric in order to get a vector of two numbers. We can pair these in combination with the difference function and division in order to get the margin of error. We will create a function which maps this function to a list of margins of error, and apply it to one column of the data.

to_moe <- function(moe) {
    moe_single <- function(x) {
        str_extract_all(x, "\\d+") %>%
            unlist() %>%        # List to Vector
            as.numeric() %>%    # Convert to numbers
            diff() %>%          # Take the difference
            `/`(2) %>%          # Single-sided MoE
            `/`(100) %>%        # Convert to proportion
            return()            # Return Margin of Error
    }
    lapply(moe, FUN = moe_single) %>%
        unlist() %>%
        return()
}

Now that we’ve created our function, let’s try it out on a list of just two strings to make sure that it works properly.

to_moe(c("25%-50%", "1%-2%"))
## [1] 0.125 0.005

Because it seems to work as intended, let’s see what it looks like if we mutate a whole column of the data:

moe_example %>% 
    mutate("moe_new" = to_moe(SP_MOE))
## # A tibble: 13,314 × 5
##    school_ID children pct_SP SP_MOE  moe_new
##    <chr>        <dbl>  <dbl> <chr>     <dbl>
##  1 00001          985 0.0490 0%-10%    0.05 
##  2 00003          292 0.102  3%-17%    0.07 
##  3 00005         4591 0.353  26%-44%   0.09 
##  4 00006         8299 0.295  24%-35%   0.055
##  5 00007        15397 0.208  17%-25%   0.04 
##  6 00008         9416 0.178  12%-23%   0.055
##  7 00011         2324 0.274  14%-41%   0.135
##  8 00012         1644 0.401  20%-60%   0.2  
##  9 00013         4476 0.229  15%-31%   0.08 
## 10 00030         3064 0.504  39%-62%   0.115
## # … with 13,304 more rows

Now that we have a function that works, let’s work on applying it to all of the Margin of Error columns.

district_data %>%
    head(5) %>%
    mutate(
        across(
            ends_with("MOE"), 
            to_moe
        )
    ) %>%
    select(ends_with("MOE"))
## # A tibble: 5 × 6
##   SP_MOE HHVJ_MOE CC_MOE nci_MOE CD_MOE CLI_MOE
##    <dbl>    <dbl>  <dbl>   <dbl>  <dbl>   <dbl>
## 1  0.05     0.13   0.04    0.025  0.03    0.015
## 2  0.07     0.265  0.05    0.03   0.035   0.04 
## 3  0.09     0.045  0.02    0.095  0.015   0.07 
## 4  0.055    0.025  0.005   0.035  0.015   0.03 
## 5  0.04     0.02   0.005   0.02   0.015   0.035

We can see that all of the Margin of Error columns have been converted into the more useful margin of error format.

Comparing to Standard Error values

In reality, ACS does not use the aforementioned form of standard error for the reason that there are so many districts for which the estimated proportions are 0% or 100%, which would yield a margin of error equal to zero. Their technique is more complicated, and takes into account special cases for when there are counts or percents of zero or 100. They utilize Variance Replicate Estimates, which are used almost exclusively for Census data. According to census.gov in their methodology, they have:

\[ {\rm Variance} = \frac{4}{80}\sum_{i=1}^{80}({\rm VarRep}_i - {\rm Estimate})^2 \] and \[ \begin{aligned} \text{Margin of Error (90% Confidence)} &= 1.645 \times \text{Standard Error} \\ &= 1.645 \times \sqrt{\rm Variance} \end{aligned} \]

Because this technique uses information which is not available to the public, let’s see how good of an estimate the more typical form of standard error holds up in this scenario.

With the knowledge that standard error for proportions assumes the form: \({\rm SE} = \sqrt{\hat p(1-\hat p)/n}\), we can calculate this form of standard error for an example column, and figure out which value of \(z^*\) corresponds to the data. Because this form of standard error doesn’t take special consideration for 0% and 100% cases, our value of \(z^*\) is likely to be very different from that which is explained in the methodology section.

While it is certainly not a good estimate of the sample size, we can use the number of children in the school district as our \(n\), which will very likely yield very small values for the standard error. An alternative is that, since we know the value of \(z^*\) that the ACS uses for calculating margins of error, we could attempt to make some estimate of the sample size used in the community surveys. Because this is not useful, however, it is beyond the scope of this analysis and we will trudge onwards.

We’ll use the same moe_example dataframe we created earlier, looking at just the percentage of households with single parents. If we divide the margin of error we get from the data by the standard error we get from the equation, we’ll get an estimate of the individual \(z^*_i\) values for each data point. Mathematically, if we assume that the margin of error estimates from ACS assume the same form that is typically seen for proportions, then for a single row \(i\), we should have:

\[ \frac{\rm MoE_i}{\rm SE_i} = \frac{z^*_i \sqrt{\frac{\hat p_i (1-\hat p_i)}{n_i}}}{\sqrt{\frac{\hat p_i (1-\hat p_i)}{n_i}}} = z^*_i = 1.645. \]

If the divided distribution happens to be centered on 1.645, then our estimate is pretty good. If not, we can explore the distribution of the margins of error and decide what to do from there.

moe_example %>%
    filter(children > 0) %>%
    mutate(SP_MOE = to_moe(SP_MOE), 
           SP_SE = sqrt(pct_SP*(1-pct_SP)/children), 
           z_star_i = SP_MOE/SP_SE) %>%
    filter(SP_SE > 0) %>%
    pull(z_star_i) %>%
    hist(main = "z-star Distribution", 
         xlab = "z-star_i")

We can see that this data is very clearly not centered on 1.645, and as such we will rely on the margins of error calculated by ACS, as their techniques are more refined and specific to census data than the typical margin of error formulation seen for proportions.

Replacing Margin of Error Columns

We’ve decided that it’s best to use the estimates of the margin of error calculated by the ACS. Because of this, in order to get the data with margins of error into a more usable format, we’ll just convert the margin of error columns into proper margins of error, one-sided, which describe a 90% confidence band. We’ll also convert these values to proportions rather than percents, since currently the estimate columns are in different units than the margin of error columns.

district_fixed_moe <- district_data %>%
    mutate(
        across(
            ends_with("MOE"), 
            to_moe
        ) 
    )

This replaces the margin of error columns with the proper one-sided margins of error. We assume, in this case, that the margins of error are symmetric on both sides of the point estimate proportion.

Margin of Error and Number of Children

We would expect, based on the typical definition of margin of error for proportions, that in general, as the number of children increases, the margin of error should decrease. This will also fluctuate with the value of the proportion itself, but let’s see if this trend is, in general, true.

district_fixed_moe %>%
    select(children, ends_with("MOE")) %>%
    pivot_longer(cols = c("SP_MOE":"CLI_MOE"), 
                 names_to = "measure", 
                 values_to = "MOE") %>%
    ggplot(mapping = aes(x = children, 
                         y = MOE,
                         color = measure)) + 
    geom_point(alpha = 0.1) + 
    geom_smooth(se = F) + 
    scale_x_log10()
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

The plot would appear to reveal that as the number of children increases, yes, the margin of error does tend to decrease. The exceptions seem to be those school districts for which there is a margin of error equal to 0.5, which means that the margin of error in the original dataset must have ranged from 0%-100%. This is typically seen for school districts which have very few students (< 1000), though there does seem to be one school in one year with between one and ten thousand students for which the margin of error is equal to 50%. Let’s see which school this is:

district_fixed_moe %>%
    filter(children > 1000) %>%
    filter(
        if_any(ends_with("MOE"), ~ .x > 0.4)
    ) %>%
    as.list()
## $school_ID
## [1] "00900"
## 
## $state
## [1] "New Mexico"
## 
## $dist
## [1] "Española Municipal Schools"
## 
## $children
## [1] 5860
## 
## $pct_pov
## [1] 0.2536434
## 
## $pct_SP
## [1] 0
## 
## $SP_MOE
## [1] 0.5
## 
## $pct_HHVJ
## [1] 0
## 
## $HHVJ_MOE
## [1] 0.5
## 
## $pct_CC
## [1] 0.02449738
## 
## $CC_MOE
## [1] 0.005
## 
## $pct_NCI
## [1] 0.3952864
## 
## $nci_MOE
## [1] 0.065
## 
## $pct_CD
## [1] 0.0274744
## 
## $CD_MOE
## [1] 0.015
## 
## $pct_CLI
## [1] 0.01467577
## 
## $CLI_MOE
## [1] 0.005

Interestingly, it appears that there is one school in New Mexico for which the point estimates of single parent households and of households with vulnerable jobs are both zero. This seems like faulty data given the poverty rate and the visible outlying nature of the data point, but we will continue forth cautiously. This analysis will inform how we choose a cutoff value for future regression analyses.

LS0tCnRpdGxlOiAiRGVhbGluZyB3aXRoIE1hcmdpbnMgb2YgRXJyb3IiCmF1dGhvcjogIkpvbiBHZWlnZXIiCmRhdGU6ICJBcHJpbCAxMCwgMjAyMiIKb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQKLS0tCgpgYGB7ciBsb2FkLXBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZ2d0aGVtZXMpCnRoZW1lX3NldCh0aGVtZV9jbGVhbigpKQoKIyBJbXBvcnQgZGF0YSBhbmQgY2xlYW4gbmFtZXMKCmRpc3RyaWN0X2RhdGEgPC0gcmVhZF9leGNlbCgiLi4vcmF3L05IR0lTX0Rpc3RyaWN0X2RhdGEueGxzeCIpCnNvdXJjZSgiLi4vc2NyaXB0cy9uYW1lc19saXN0LlIiKQpuYW1lcyhkaXN0cmljdF9kYXRhKSA8LSBuYW1lc19saXN0CmBgYAoKIyBHb2FscwoKLSBbeF0gQ29udmVydCBmcm9tIE1hcmdpbiBvZiBFcnJvciBjb2x1bW5zIHRvIGEgbWFyZ2luIG9mIGVycm9yICgzNCUgLSA0MyUgIC0tPiA5JSkKLSBbeF0gQ2FsY3VsYXRlIHN0YW5kYXJkIGVycm9yIGJhc2VkIG9uIFNFIGVxdWF0aW9uIGZvciBwcm9wb3J0aW9ucwotIFt4XSBGaWd1cmUgb3V0IHRoZSB6XCogdmFsdWUgdXNlZCBmb3IgdGhlIG1hcmdpbnMgb2YgZXJyb3IKLSBbeF0gRXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbnVtYmVyIG9mIGNoaWxkcmVuIGFuZCBtYXJnaW4gb2YgZXJyb3IKCiMgTW90aXZhdGlvbiBhbmQgRXhwbGFuYXRpb24KClRoZSBkYXRhIGluIHRoZSBFeGNlbCBmaWxlIGdpdmVzIHBlcmNlbnRhZ2VzIG9mIGRpZmZlcmVudCBob3VzZWhvbGRzIGluIHNjaG9vbCBkaXN0cmljdHMgd2hpY2ggZmFsbCB1bmRlciBjZXJ0YWluIGNhdGVnb3JpZXMuIEFzc29jaWF0ZWQgd2l0aCB0aGVzZSBwZXJjZW50YWdlcywgdGhlcmUgaXMgYSBtYXJnaW4gb2YgZXJyb3IuIEFzIGFuIGV4YW1wbGUsIHRoaXMgaXMgd2hhdCBzb21lIG9mIHRoZSBjb2x1bW5zIGluIG91ciBkYXRhc2V0IGxvb2sgbGlrZToKCmBgYHtyfQpkaXN0cmljdF9kYXRhICU+JQogICAgaGVhZCgyKSAlPiUKICAgIHNlbGVjdCgyOjQsIDYsIDcpCmBgYAoKT3VyIGdvYWwgaXMgdG8gcmVwbGFjZSB0aGVzZSBtYXJnaW4gb2YgZXJyb3IgY29sdW1ucyB3aXRoIHNvbWV0aGluZyB3aGljaCB3aWxsIGJlIG1vcmUgdXNlZnVsLCBzdWNoIGFzIHRoZSBvbmUtc2lkZWQgcmFuZ2Ugb2YgdGhlIG1hcmdpbiBvZiBlcnJvci4gV2Ugd2lsbCBmaXJzdCB0cnkgdGhpcyBvdXQgd2l0aCBvbmUgY29sdW1uLCB0aGVuIGFwcGx5IHRoZSBhbmFseXNpcyB0byBldmVyeSBtYXJnaW4gb2YgZXJyb3IgY29sdW1uLiBBcyBhbiBleGFtcGxlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIE1heHdlbGwgQUZCIFNjaG9vbCBEaXN0cmljdCBoYXMgYSBtYXJnaW4gb2YgZXJyb3IgZ29pbmcgZnJvbSAkM1wlJCB0byAkMTdcJSQuIFRoZSByYW5nZSBvZiB0aGlzIHdvdWxkIGJlICQxNyUtMyU9MTQlJCwgaGFsZiBvZiB3aGljaCB3b3VsZCBiZSAkN1wlJC4gVGhpcyBnaXZlcyB1cyBhIHRvb2wgdG8gZGlzY292ZXIgaG93IHRoZXkgbWF5IGhhdmUgY2FsY3VsYXRlZCB0aGUgbWFyZ2luIG9mIGVycm9yLgoKTWF0aGVtYXRpY2FsbHksIHdlIGtub3cgdGhhdCBhIG1hcmdpbiBvZiBlcnJvciBmb3IgYSBzYW1wbGUgcHJvcG9ydGlvbiBpcyBnaXZlbiBieToKCiQkClxoYXQgcCBccG0gel4qIFxzcXJ0e1xmcmFje1xoYXQgcCAoMS1caGF0IHApfXtufX0KJCQKClRoaXMgdGVybSB3aGljaCBpcyBhZGRlZCBvbnRvIHRoZSBlc3RpbWF0ZWQgcHJvcG9ydGlvbiBwcm92aWRlcyB0aGUgbWFyZ2luIG9mIGVycm9yLCBtZWFuaW5nIHRoYXQgd2hhdCB0aGV5IGhhdmUgbGFiZWxlZCBhcyBhICJNYXJnaW4gb2YgRXJyb3IiIGlzIGFjdHVhbGx5IGNsb3NlciBpbiBtZWFuaW5nIHRvIGEgY29uZmlkZW5jZSBpbnRlcnZhbCB3aXRoICR6XiokIHN0YW5kYXJkIGVycm9ycyBhd2F5IGZyb20gdGhlIGVzdGltYXRlZCBwcm9wb3J0aW9uLiBGb3IgdGhlIHNha2Ugb2YgdGVybWlub2xvZ3ksIHdlIHdpbGwgZGVmaW5lIE1hcmdpbiBvZiBFcnJvciBhcyB0aGUgb25lLXNpZGVkIHZlcnNpb24gb2YgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwsIHN1Y2ggdGhhdCAKJCQKe1xybSBNRX0gPSB6XiogXHNxcnR7XGZyYWN7XGhhdCBwICgxLVxoYXQgcCl9e259fS4KJCQKCiMgQW5hbHlzaXMKCldlIHdpbGwgZ28gaW4gb3JkZXIgb2YgdGhlIGdvYWxzLCBzdGFydGluZyB3aXRoIGNvbnZlcnRpbmcgdGhlIGNvbHVtbnMgdG8gYSBtb3JlIHVzYWJsZSBmb3JtYXQuIAoKIyMgTWFyZ2luIG9mIEVycm9yIEV4dHJhY3Rpb24KCldlJ2xsIHN0YXJ0IHdpdGggYSBzaW5nbGUgY29sdW1uLCB0aGVuIGFwcGx5IHRoYXQgdGVjaG5pcXVlIHRvIGV2ZXJ5IG1hcmdpbiBvZiBlcnJvciBjb2x1bW4gaW4gdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUuIFdlJ2xsIHVzZSB0aGUgU2luZ2xlIFBhcmVudCBtYXJnaW4gb2YgZXJyb3IgYXMgdGhlIGV4YW1wbGUuCgpgYGB7cn0KbW9lX2V4YW1wbGUgPC0gZGlzdHJpY3RfZGF0YSAlPiUgCiAgICBzZWxlY3QoMSwgNCwgNiwgNykKbW9lX2V4YW1wbGUgJT4lIGhlYWQoNSkKYGBgCgpPbmUgYXBwcm9hY2ggdG8gdGhpcyBwcm9ibGVtIGlzIHRvIHJlbW92ZSB0aGUgcGVyY2VudGFnZSBzaWducywgc3BsaXQgdGhlIGNvbHVtbiBhdCB0aGUgaHlwaGVuLCB0aGVuIHVzZSB0aGUgcmVzdWx0aW5nIHR3byBjb2x1bW5zIHRvIGNhbGN1bGF0ZSBhIGRpZmZlcmVuY2UgYW5kIGRpdmlkZSBpdCBieSB0d28uIEFub3RoZXIgYXBwcm9hY2ggaXMgdG8gdXNlIHJlZ3VsYXIgZXhwcmVzc2lvbnMgdG8gY2FwdHVyZSB0aGUgbnVtYmVycywgYW5kIHN1YnRyYWN0IGFuZCBkaXZpZGUgdGhlbSBkaXJlY3RseS4gV2UnbGwgdXRpbGl6ZSB0aGlzIHNlY29uZCBtZXRob2QgdXNpbmcgYHN0cl9leHRyYWN0X2FsbGAgZnJvbSB0aGUgYHN0cmluZ3JgIHBhY2thZ2UuIAoKYGBge3J9CnN0cl9leGFtcGxlIDwtICIwJS0xMCUiCnN0cl9leHRyYWN0X2FsbChzdHJfZXhhbXBsZSwgIlxcZCsiKSAlPiUgCiAgICB1bmxpc3QoKSAlPiUKICAgIGFzLm51bWVyaWMoKQpgYGAKCldlIGNhbiBzZWUgdGhhdCBmb3IgdGhpcyBleGFtcGxlIHN0cmluZywgd2UgY2FuIGV4dHJhY3QgdGhlIG51bWJlcnMsIGB1bmxpc3RgLCBhbmQgY29udmVydCB0byBudW1lcmljIGluIG9yZGVyIHRvIGdldCBhIHZlY3RvciBvZiB0d28gbnVtYmVycy4gV2UgY2FuIHBhaXIgdGhlc2UgaW4gY29tYmluYXRpb24gd2l0aCB0aGUgZGlmZmVyZW5jZSBmdW5jdGlvbiBhbmQgZGl2aXNpb24gaW4gb3JkZXIgdG8gZ2V0IHRoZSBtYXJnaW4gb2YgZXJyb3IuIFdlIHdpbGwgY3JlYXRlIGEgZnVuY3Rpb24gd2hpY2ggbWFwcyB0aGlzIGZ1bmN0aW9uIHRvIGEgbGlzdCBvZiBtYXJnaW5zIG9mIGVycm9yLCBhbmQgYXBwbHkgaXQgdG8gb25lIGNvbHVtbiBvZiB0aGUgZGF0YS4gCgpgYGB7cn0KdG9fbW9lIDwtIGZ1bmN0aW9uKG1vZSkgewogICAgbW9lX3NpbmdsZSA8LSBmdW5jdGlvbih4KSB7CiAgICAgICAgc3RyX2V4dHJhY3RfYWxsKHgsICJcXGQrIikgJT4lCiAgICAgICAgICAgIHVubGlzdCgpICU+JSAgICAgICAgIyBMaXN0IHRvIFZlY3RvcgogICAgICAgICAgICBhcy5udW1lcmljKCkgJT4lICAgICMgQ29udmVydCB0byBudW1iZXJzCiAgICAgICAgICAgIGRpZmYoKSAlPiUgICAgICAgICAgIyBUYWtlIHRoZSBkaWZmZXJlbmNlCiAgICAgICAgICAgIGAvYCgyKSAlPiUgICAgICAgICAgIyBTaW5nbGUtc2lkZWQgTW9FCiAgICAgICAgICAgIGAvYCgxMDApICU+JSAgICAgICAgIyBDb252ZXJ0IHRvIHByb3BvcnRpb24KICAgICAgICAgICAgcmV0dXJuKCkgICAgICAgICAgICAjIFJldHVybiBNYXJnaW4gb2YgRXJyb3IKICAgIH0KICAgIGxhcHBseShtb2UsIEZVTiA9IG1vZV9zaW5nbGUpICU+JQogICAgICAgIHVubGlzdCgpICU+JQogICAgICAgIHJldHVybigpCn0KYGBgCgpOb3cgdGhhdCB3ZSd2ZSBjcmVhdGVkIG91ciBmdW5jdGlvbiwgbGV0J3MgdHJ5IGl0IG91dCBvbiBhIGxpc3Qgb2YganVzdCB0d28gc3RyaW5ncyB0byBtYWtlIHN1cmUgdGhhdCBpdCB3b3JrcyBwcm9wZXJseS4gCgpgYGB7cn0KdG9fbW9lKGMoIjI1JS01MCUiLCAiMSUtMiUiKSkKYGBgCgpCZWNhdXNlIGl0IHNlZW1zIHRvIHdvcmsgYXMgaW50ZW5kZWQsIGxldCdzIHNlZSB3aGF0IGl0IGxvb2tzIGxpa2UgaWYgd2UgbXV0YXRlIGEgd2hvbGUgY29sdW1uIG9mIHRoZSBkYXRhOiAKCmBgYHtyfQptb2VfZXhhbXBsZSAlPiUgCiAgICBtdXRhdGUoIm1vZV9uZXciID0gdG9fbW9lKFNQX01PRSkpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhIGZ1bmN0aW9uIHRoYXQgd29ya3MsIGxldCdzIHdvcmsgb24gYXBwbHlpbmcgaXQgdG8gYWxsIG9mIHRoZSBNYXJnaW4gb2YgRXJyb3IgY29sdW1ucy4KCmBgYHtyfQpkaXN0cmljdF9kYXRhICU+JQogICAgaGVhZCg1KSAlPiUKICAgIG11dGF0ZSgKICAgICAgICBhY3Jvc3MoCiAgICAgICAgICAgIGVuZHNfd2l0aCgiTU9FIiksIAogICAgICAgICAgICB0b19tb2UKICAgICAgICApCiAgICApICU+JQogICAgc2VsZWN0KGVuZHNfd2l0aCgiTU9FIikpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IGFsbCBvZiB0aGUgTWFyZ2luIG9mIEVycm9yIGNvbHVtbnMgaGF2ZSBiZWVuIGNvbnZlcnRlZCBpbnRvIHRoZSBtb3JlIHVzZWZ1bCBtYXJnaW4gb2YgZXJyb3IgZm9ybWF0LiAKCiMjIENvbXBhcmluZyB0byBTdGFuZGFyZCBFcnJvciB2YWx1ZXMKCkluIHJlYWxpdHksIEFDUyAqZG9lcyBub3QqIHVzZSB0aGUgYWZvcmVtZW50aW9uZWQgZm9ybSBvZiBzdGFuZGFyZCBlcnJvciBmb3IgdGhlIHJlYXNvbiB0aGF0IHRoZXJlIGFyZSBzbyBtYW55IGRpc3RyaWN0cyBmb3Igd2hpY2ggdGhlIGVzdGltYXRlZCBwcm9wb3J0aW9ucyBhcmUgMFwlIG9yIDEwMFwlLCB3aGljaCB3b3VsZCB5aWVsZCBhIG1hcmdpbiBvZiBlcnJvciBlcXVhbCB0byB6ZXJvLiBUaGVpciB0ZWNobmlxdWUgaXMgbW9yZSBjb21wbGljYXRlZCwgYW5kIHRha2VzIGludG8gYWNjb3VudCBzcGVjaWFsIGNhc2VzIGZvciB3aGVuIHRoZXJlIGFyZSBjb3VudHMgb3IgcGVyY2VudHMgb2YgemVybyBvciAxMDAuIFRoZXkgdXRpbGl6ZSBWYXJpYW5jZSBSZXBsaWNhdGUgRXN0aW1hdGVzLCB3aGljaCBhcmUgdXNlZCBhbG1vc3QgZXhjbHVzaXZlbHkgZm9yIENlbnN1cyBkYXRhLiBBY2NvcmRpbmcgdG8gY2Vuc3VzLmdvdiBpbiB0aGVpciBtZXRob2RvbG9neSwgdGhleSBoYXZlOiAKCiQkCntccm0gVmFyaWFuY2V9ID0gXGZyYWN7NH17ODB9XHN1bV97aT0xfV57ODB9KHtccm0gVmFyUmVwfV9pIC0ge1xybSBFc3RpbWF0ZX0pXjIKJCQKYW5kCiQkClxiZWdpbnthbGlnbmVkfQogICAgXHRleHR7TWFyZ2luIG9mIEVycm9yICg5MCUgQ29uZmlkZW5jZSl9ICY9IDEuNjQ1IFx0aW1lcyBcdGV4dHtTdGFuZGFyZCBFcnJvcn0gXFwKICAgICY9IDEuNjQ1IFx0aW1lcyBcc3FydHtccm0gVmFyaWFuY2V9ClxlbmR7YWxpZ25lZH0KJCQKCkJlY2F1c2UgdGhpcyB0ZWNobmlxdWUgdXNlcyBpbmZvcm1hdGlvbiB3aGljaCBpcyBub3QgYXZhaWxhYmxlIHRvIHRoZSBwdWJsaWMsIGxldCdzIHNlZSBob3cgZ29vZCBvZiBhbiBlc3RpbWF0ZSB0aGUgbW9yZSB0eXBpY2FsIGZvcm0gb2Ygc3RhbmRhcmQgZXJyb3IgaG9sZHMgdXAgaW4gdGhpcyBzY2VuYXJpby4KCldpdGggdGhlIGtub3dsZWRnZSB0aGF0IHN0YW5kYXJkIGVycm9yIGZvciBwcm9wb3J0aW9ucyBhc3N1bWVzIHRoZSBmb3JtOiAke1xybSBTRX0gPSBcc3FydHtcaGF0IHAoMS1caGF0IHApL259JCwgd2UgY2FuIGNhbGN1bGF0ZSB0aGlzIGZvcm0gb2Ygc3RhbmRhcmQgZXJyb3IgZm9yIGFuIGV4YW1wbGUgY29sdW1uLCBhbmQgZmlndXJlIG91dCB3aGljaCB2YWx1ZSBvZiAkel4qJCBjb3JyZXNwb25kcyB0byB0aGUgZGF0YS4gQmVjYXVzZSB0aGlzIGZvcm0gb2Ygc3RhbmRhcmQgZXJyb3IgZG9lc24ndCB0YWtlIHNwZWNpYWwgY29uc2lkZXJhdGlvbiBmb3IgMFwlIGFuZCAxMDBcJSBjYXNlcywgb3VyIHZhbHVlIG9mICR6XiokIGlzIGxpa2VseSB0byBiZSB2ZXJ5IGRpZmZlcmVudCBmcm9tIHRoYXQgd2hpY2ggaXMgZXhwbGFpbmVkIGluIHRoZSBtZXRob2RvbG9neSBzZWN0aW9uLiAKCldoaWxlIGl0IGlzIGNlcnRhaW5seSBub3QgYSBnb29kIGVzdGltYXRlIG9mIHRoZSBzYW1wbGUgc2l6ZSwgd2UgY2FuIHVzZSB0aGUgbnVtYmVyIG9mIGNoaWxkcmVuIGluIHRoZSBzY2hvb2wgZGlzdHJpY3QgYXMgb3VyICRuJCwgd2hpY2ggd2lsbCB2ZXJ5IGxpa2VseSB5aWVsZCB2ZXJ5IHNtYWxsIHZhbHVlcyBmb3IgdGhlIHN0YW5kYXJkIGVycm9yLiBBbiBhbHRlcm5hdGl2ZSBpcyB0aGF0LCBzaW5jZSB3ZSBrbm93IHRoZSB2YWx1ZSBvZiAkel4qJCB0aGF0IHRoZSBBQ1MgdXNlcyBmb3IgY2FsY3VsYXRpbmcgbWFyZ2lucyBvZiBlcnJvciwgd2UgY291bGQgYXR0ZW1wdCB0byBtYWtlIHNvbWUgZXN0aW1hdGUgb2YgdGhlIHNhbXBsZSBzaXplIHVzZWQgaW4gdGhlIGNvbW11bml0eSBzdXJ2ZXlzLiBCZWNhdXNlIHRoaXMgaXMgbm90IHVzZWZ1bCwgaG93ZXZlciwgaXQgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGFuYWx5c2lzIGFuZCB3ZSB3aWxsIHRydWRnZSBvbndhcmRzLgoKV2UnbGwgdXNlIHRoZSBzYW1lIGBtb2VfZXhhbXBsZWAgZGF0YWZyYW1lIHdlIGNyZWF0ZWQgZWFybGllciwgbG9va2luZyBhdCBqdXN0IHRoZSBwZXJjZW50YWdlIG9mIGhvdXNlaG9sZHMgd2l0aCBzaW5nbGUgcGFyZW50cy4gSWYgd2UgZGl2aWRlIHRoZSBtYXJnaW4gb2YgZXJyb3Igd2UgZ2V0IGZyb20gdGhlIGRhdGEgYnkgdGhlIHN0YW5kYXJkIGVycm9yIHdlIGdldCBmcm9tIHRoZSBlcXVhdGlvbiwgd2UnbGwgZ2V0IGFuIGVzdGltYXRlIG9mIHRoZSBpbmRpdmlkdWFsICR6XipfaSQgdmFsdWVzIGZvciBlYWNoIGRhdGEgcG9pbnQuIE1hdGhlbWF0aWNhbGx5LCBpZiB3ZSBhc3N1bWUgdGhhdCB0aGUgbWFyZ2luIG9mIGVycm9yIGVzdGltYXRlcyBmcm9tIEFDUyBhc3N1bWUgdGhlIHNhbWUgZm9ybSB0aGF0IGlzIHR5cGljYWxseSBzZWVuIGZvciBwcm9wb3J0aW9ucywgdGhlbiBmb3IgYSBzaW5nbGUgcm93ICRpJCwgd2Ugc2hvdWxkIGhhdmU6IAoKJCQKXGZyYWN7XHJtIE1vRV9pfXtccm0gU0VfaX0gPSBcZnJhY3t6XipfaSBcc3FydHtcZnJhY3tcaGF0IHBfaSAoMS1caGF0IHBfaSl9e25faX19fXtcc3FydHtcZnJhY3tcaGF0IHBfaSAoMS1caGF0IHBfaSl9e25faX19fSA9IHpeKl9pID0gMS42NDUuCiQkCgpJZiB0aGUgZGl2aWRlZCBkaXN0cmlidXRpb24gaGFwcGVucyB0byBiZSBjZW50ZXJlZCBvbiAxLjY0NSwgdGhlbiBvdXIgZXN0aW1hdGUgaXMgcHJldHR5IGdvb2QuIElmIG5vdCwgd2UgY2FuIGV4cGxvcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWFyZ2lucyBvZiBlcnJvciBhbmQgZGVjaWRlIHdoYXQgdG8gZG8gZnJvbSB0aGVyZS4KCmBgYHtyfQptb2VfZXhhbXBsZSAlPiUKICAgIGZpbHRlcihjaGlsZHJlbiA+IDApICU+JQogICAgbXV0YXRlKFNQX01PRSA9IHRvX21vZShTUF9NT0UpLCAKICAgICAgICAgICBTUF9TRSA9IHNxcnQocGN0X1NQKigxLXBjdF9TUCkvY2hpbGRyZW4pLCAKICAgICAgICAgICB6X3N0YXJfaSA9IFNQX01PRS9TUF9TRSkgJT4lCiAgICBmaWx0ZXIoU1BfU0UgPiAwKSAlPiUKICAgIHB1bGwoel9zdGFyX2kpICU+JQogICAgaGlzdChtYWluID0gInotc3RhciBEaXN0cmlidXRpb24iLCAKICAgICAgICAgeGxhYiA9ICJ6LXN0YXJfaSIpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoaXMgZGF0YSBpcyB2ZXJ5IGNsZWFybHkgKipub3QqKiBjZW50ZXJlZCBvbiAxLjY0NSwgYW5kIGFzIHN1Y2ggd2Ugd2lsbCByZWx5IG9uIHRoZSBtYXJnaW5zIG9mIGVycm9yIGNhbGN1bGF0ZWQgYnkgQUNTLCBhcyB0aGVpciB0ZWNobmlxdWVzIGFyZSBtb3JlIHJlZmluZWQgYW5kIHNwZWNpZmljIHRvIGNlbnN1cyBkYXRhIHRoYW4gdGhlIHR5cGljYWwgbWFyZ2luIG9mIGVycm9yIGZvcm11bGF0aW9uIHNlZW4gZm9yIHByb3BvcnRpb25zLiAKCiMjIFJlcGxhY2luZyBNYXJnaW4gb2YgRXJyb3IgQ29sdW1ucwoKV2UndmUgZGVjaWRlZCB0aGF0IGl0J3MgYmVzdCB0byB1c2UgdGhlIGVzdGltYXRlcyBvZiB0aGUgbWFyZ2luIG9mIGVycm9yIGNhbGN1bGF0ZWQgYnkgdGhlIEFDUy4gQmVjYXVzZSBvZiB0aGlzLCBpbiBvcmRlciB0byBnZXQgdGhlIGRhdGEgd2l0aCBtYXJnaW5zIG9mIGVycm9yIGludG8gYSBtb3JlIHVzYWJsZSBmb3JtYXQsIHdlJ2xsIGp1c3QgY29udmVydCB0aGUgbWFyZ2luIG9mIGVycm9yIGNvbHVtbnMgaW50byBwcm9wZXIgbWFyZ2lucyBvZiBlcnJvciwgb25lLXNpZGVkLCB3aGljaCBkZXNjcmliZSBhIDkwXCUgY29uZmlkZW5jZSBiYW5kLiBXZSdsbCBhbHNvIGNvbnZlcnQgdGhlc2UgdmFsdWVzIHRvIHByb3BvcnRpb25zIHJhdGhlciB0aGFuIHBlcmNlbnRzLCBzaW5jZSBjdXJyZW50bHkgdGhlIGVzdGltYXRlIGNvbHVtbnMgYXJlIGluIGRpZmZlcmVudCB1bml0cyB0aGFuIHRoZSBtYXJnaW4gb2YgZXJyb3IgY29sdW1ucy4gCgpgYGB7cn0KZGlzdHJpY3RfZml4ZWRfbW9lIDwtIGRpc3RyaWN0X2RhdGEgJT4lCiAgICBtdXRhdGUoCiAgICAgICAgYWNyb3NzKAogICAgICAgICAgICBlbmRzX3dpdGgoIk1PRSIpLCAKICAgICAgICAgICAgdG9fbW9lCiAgICAgICAgKSAKICAgICkKYGBgCgpUaGlzIHJlcGxhY2VzIHRoZSBtYXJnaW4gb2YgZXJyb3IgY29sdW1ucyB3aXRoIHRoZSBwcm9wZXIgb25lLXNpZGVkIG1hcmdpbnMgb2YgZXJyb3IuIFdlIGFzc3VtZSwgaW4gdGhpcyBjYXNlLCB0aGF0IHRoZSBtYXJnaW5zIG9mIGVycm9yIGFyZSBzeW1tZXRyaWMgb24gYm90aCBzaWRlcyBvZiB0aGUgcG9pbnQgZXN0aW1hdGUgcHJvcG9ydGlvbi4gCgojIyBNYXJnaW4gb2YgRXJyb3IgYW5kIE51bWJlciBvZiBDaGlsZHJlbgoKV2Ugd291bGQgZXhwZWN0LCBiYXNlZCBvbiB0aGUgdHlwaWNhbCBkZWZpbml0aW9uIG9mIG1hcmdpbiBvZiBlcnJvciBmb3IgcHJvcG9ydGlvbnMsIHRoYXQgaW4gZ2VuZXJhbCwgYXMgdGhlIG51bWJlciBvZiBjaGlsZHJlbiBpbmNyZWFzZXMsIHRoZSBtYXJnaW4gb2YgZXJyb3Igc2hvdWxkIGRlY3JlYXNlLiBUaGlzIHdpbGwgYWxzbyBmbHVjdHVhdGUgd2l0aCB0aGUgdmFsdWUgb2YgdGhlIHByb3BvcnRpb24gaXRzZWxmLCBidXQgbGV0J3Mgc2VlIGlmIHRoaXMgdHJlbmQgaXMsIGluIGdlbmVyYWwsIHRydWUuIApgYGB7ciwgd2FybmluZz1GfQpkaXN0cmljdF9maXhlZF9tb2UgJT4lCiAgICBzZWxlY3QoY2hpbGRyZW4sIGVuZHNfd2l0aCgiTU9FIikpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJTUF9NT0UiOiJDTElfTU9FIiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1lYXN1cmUiLCAKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiTU9FIikgJT4lCiAgICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY2hpbGRyZW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IE1PRSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWVhc3VyZSkpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArIAogICAgZ2VvbV9zbW9vdGgoc2UgPSBGKSArIAogICAgc2NhbGVfeF9sb2cxMCgpCiAgICAKYGBgCgpUaGUgcGxvdCB3b3VsZCBhcHBlYXIgdG8gcmV2ZWFsIHRoYXQgYXMgdGhlIG51bWJlciBvZiBjaGlsZHJlbiBpbmNyZWFzZXMsIHllcywgdGhlIG1hcmdpbiBvZiBlcnJvciBkb2VzIHRlbmQgdG8gZGVjcmVhc2UuIFRoZSBleGNlcHRpb25zIHNlZW0gdG8gYmUgdGhvc2Ugc2Nob29sIGRpc3RyaWN0cyBmb3Igd2hpY2ggdGhlcmUgaXMgYSBtYXJnaW4gb2YgZXJyb3IgZXF1YWwgdG8gMC41LCB3aGljaCBtZWFucyB0aGF0IHRoZSBtYXJnaW4gb2YgZXJyb3IgaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQgbXVzdCBoYXZlIHJhbmdlZCBmcm9tIDBcJS0xMDBcJS4gVGhpcyBpcyB0eXBpY2FsbHkgc2VlbiBmb3Igc2Nob29sIGRpc3RyaWN0cyB3aGljaCBoYXZlIHZlcnkgZmV3IHN0dWRlbnRzICg8IDEwMDApLCB0aG91Z2ggdGhlcmUgZG9lcyBzZWVtIHRvIGJlIG9uZSBzY2hvb2wgaW4gb25lIHllYXIgd2l0aCBiZXR3ZWVuIG9uZSBhbmQgdGVuIHRob3VzYW5kIHN0dWRlbnRzIGZvciB3aGljaCB0aGUgbWFyZ2luIG9mIGVycm9yIGlzIGVxdWFsIHRvIDUwXCUuIExldCdzIHNlZSB3aGljaCBzY2hvb2wgdGhpcyBpczogCmBgYHtyfQpkaXN0cmljdF9maXhlZF9tb2UgJT4lCiAgICBmaWx0ZXIoY2hpbGRyZW4gPiAxMDAwKSAlPiUKICAgIGZpbHRlcigKICAgICAgICBpZl9hbnkoZW5kc193aXRoKCJNT0UiKSwgfiAueCA+IDAuNCkKICAgICkgJT4lCiAgICBhcy5saXN0KCkKYGBgCgpJbnRlcmVzdGluZ2x5LCBpdCBhcHBlYXJzIHRoYXQgdGhlcmUgaXMgb25lIHNjaG9vbCBpbiBOZXcgTWV4aWNvIGZvciB3aGljaCB0aGUgcG9pbnQgZXN0aW1hdGVzIG9mIHNpbmdsZSBwYXJlbnQgaG91c2Vob2xkcyAqYW5kKiBvZiBob3VzZWhvbGRzIHdpdGggdnVsbmVyYWJsZSBqb2JzIGFyZSBib3RoIHplcm8uIFRoaXMgc2VlbXMgbGlrZSBmYXVsdHkgZGF0YSBnaXZlbiB0aGUgcG92ZXJ0eSByYXRlIGFuZCB0aGUgdmlzaWJsZSBvdXRseWluZyBuYXR1cmUgb2YgdGhlIGRhdGEgcG9pbnQsIGJ1dCB3ZSB3aWxsIGNvbnRpbnVlIGZvcnRoIGNhdXRpb3VzbHkuIFRoaXMgYW5hbHlzaXMgd2lsbCBpbmZvcm0gaG93IHdlIGNob29zZSBhIGN1dG9mZiB2YWx1ZSBmb3IgZnV0dXJlIHJlZ3Jlc3Npb24gYW5hbHlzZXMuIA==