This notebook documents racial/ethnic disparities in school funding. It uses the Urban Institutes R package to extract school finance and enrollment data and makes adjustments for charter schools (along with a couple other small adjustments) and applies a cost of living adjustment based on the county the district is located in.

A lot of spending disparity research focuses only on state and local funding sources because of the federal programs "supplement not supplant" provisions, but this note is just interested in total disparities, so I include local, state, and federal funding sources.

Collect Data

# get district cost of living index from edbuild
district_cola <- masterpull('2016',data_type = 'fin') %>%
  mutate(LR_cola = LRPP_cola*ENROLL,
         SR_cola = SRPP_cola*ENROLL,
         SLR_cola = SLRPP_cola*ENROLL,
         cola_sr = SR_cola/SR,
         cola_lr = LR_cola/LR) %>%
  select(NCESID, cola_sr)

# school district revenue and expenditures
finance <- get_education_data(level = 'school-districts',
                              source = 'ccd',
                              topic = 'finance',
                              filters = list(year = 2016))

# district enrollment by race
enrollment <- get_education_data(level = 'school-districts',
                                 source = 'ccd',
                                 topic = 'enrollment',
                                 subtopic = list('race'),
                                 filters = list(year = 2016),
                                 add_labels = TRUE)

# district characteristics
directory <- get_education_data(level = 'school-districts',
                                source = 'ccd',
                                topic = 'directory',
                                filters = list(year = 2016))

Graph of Revenue per Pupil by District Racial Composition

# federal, state and local adj rev cola, black bin scatterplot
p1 <- enroll_race_finance %>% filter(pct_black <= 100) %>%
  group_by(pct_black_bin) %>% 
  summarize(adjrev_per_pupil_cola = sum(adjrev_total_cola) / sum(Total)) %>%
  ggplot(mapping = aes(x = pct_black_bin, y = adjrev_per_pupil_cola)) + 
  geom_point() + labs(title = 'Revenue per Pupil', subtitle =  'Binned Black Percent') + 
  theme(title = element_text(size = 10))
# federal, state and local adj rev cola, nonwhite bin scatterplot
p2 <- enroll_race_finance %>% filter(pct_nonwhite <= 100) %>%
  group_by(pct_nonwhite_bin) %>% 
  summarize(adjrev_per_pupil_cola = sum(adjrev_total_cola) / sum(Total)) %>%
  ggplot(mapping = aes(x = pct_nonwhite_bin, y = adjrev_per_pupil_cola)) + 
  geom_point() + labs(title = 'Revenue per Pupil', subtitle =  'Binned Nonwhite Percent') + 
  theme(title = element_text(size = 10))
gridExtra::grid.arrange(p1, p2, nrow = 1)

Revenue per Pupil is higher in districts with a higher share of black students, but is lower in districts with a higher share of nonwhite students.

Average Revenue per Pupil in Segregated Schools

# rev per student for majority black schools vs majority white schools (>75%)
enroll_race_finance %>%
  group_by(w_b_schools) %>%
  summarize(rev_per_pupil_cola = sum(adjrev_total_cola) / sum(Total),
            numstudents = sum(Total)) %>%
  filter(w_b_schools != 'NotConcentrated')
# rev per student for majority nonwhite schools vs majority white schools (>75%)
enroll_race_finance %>%
  group_by(w_nw_schools) %>%
  summarize(rev_per_pupil_cola = sum(adjrev_total_cola) / sum(Total),
            numstudents = sum(Total)) %>% filter(w_nw_schools != 'NotConcentrated')

Revenue per pupil in majority black districts is fairly close to revenue per pupil in majority white districts. Though there are very few black students educated in districts where 75% of kids are black.

Revenue per pupil is substantially lower in districts that are majority (>75%) non-white (black, hispanic, asian) than in districts that are majority (>75%) white.

National Average Revenue per Pupil By Race

enroll_race_finance %>%
  mutate(adjrev_black_students = adjrev_per_pupil_cola*Black,
         adjrev_white_students = adjrev_per_pupil_cola*White,
         national = 'national') %>%
  group_by(national) %>%
  summarize(avg_rev_black_students  = sum(adjrev_black_students)/sum(Black),
            avg_rev_white_students  = sum(adjrev_white_students)/sum(White)) %>%
  mutate(percent_diff = (avg_rev_black_students-avg_rev_white_students)/avg_rev_black_students)
enroll_race_finance %>%
  mutate(adjrev_nonwhite_students = adjrev_per_pupil_cola*nonwhite,
         adjrev_white_students = adjrev_per_pupil_cola*White,
         national = 'national') %>%
  group_by(national) %>%
  summarize(avg_rev_nonwhite_students  = sum(adjrev_nonwhite_students)/sum(nonwhite),
            avg_rev_white_students  = sum(adjrev_white_students)/sum(White)) %>%
  mutate(percent_diff = (avg_rev_nonwhite_students - avg_rev_white_students) / avg_rev_white_students)

The average black student in the US attends a district where revenue is 2.5% lower than districts attended by the average white student. The average nonwhite student in the US attends a district where revenue is about 6.1% lower than for the average white student.

State Average Revenue per Pupil Difference By Race

p3 <- enroll_race_finance %>%
  mutate(adjrev_black_students = adjrev_per_pupil_cola*Black,
         adjrev_white_students = adjrev_per_pupil_cola*White,
         national = 'national') %>%
  group_by(fips) %>%
  summarize(natl_rev_black_students  = sum(adjrev_black_students)/sum(Black),
            natl_rev_white_students  = sum(adjrev_white_students)/sum(White)) %>%
  mutate(pct_black_white_difference = (natl_rev_black_students - natl_rev_white_students)/natl_rev_white_students*100) %>%
  
  ggplot(mapping = aes(x = fips, y = pct_black_white_difference)) + geom_col() +
  theme(axis.text.x = element_text(angle = 90,hjust = 1, vjust = .5))
# rev difference for average nonwhite and white student in each state
p4 <- enroll_race_finance %>%
  mutate(adjrev_nonwhite_students = adjrev_per_pupil_cola*nonwhite,
         adjrev_white_students = adjrev_per_pupil_cola*White,
         national = 'national') %>%
  group_by(fips) %>%
  summarize(natl_rev_nonwhite_students  = sum(adjrev_nonwhite_students)/sum(nonwhite),
            natl_rev_white_students  = sum(adjrev_white_students)/sum(White),
            white = sum(White),
            nonwhite = sum(nonwhite)) %>%
  mutate(pct_nonwhite_white_difference = (natl_rev_nonwhite_students - natl_rev_white_students)/natl_rev_white_students*100) %>%
  
  ggplot(mapping = aes(x = fips, y = pct_nonwhite_white_difference)) + geom_col() +
  theme(axis.text.x = element_text(angle = 90,hjust = 1, vjust = .5))
gridExtra::grid.arrange(p3,p4,nrow=1)

Revenue by Source for Segregated Schools

# revenue by source in white and black (>75%) schools
enroll_race_finance %>%
  group_by(w_b_schools) %>%
  summarize(FederalRev = sum(adjrev_fed_total_cola)/sum(Total),
            StateRev = sum(adjrev_state_total_cola)/sum(Total),
            LocalRev = sum(adjrev_local_total_cola)/sum(Total)) %>%
  filter(w_b_schools != 'NotConcentrated')
# revenue by source in white and nonwhite (>75%) schools
enroll_race_finance %>%
  group_by(w_nw_schools) %>%
  summarize(FederalRev = sum(adjrev_fed_total_cola)/sum(Total),
            StateRev = sum(adjrev_state_total_cola)/sum(Total),
            LocalRev = sum(adjrev_local_total_cola)/sum(Total)) %>%
  filter(w_nw_schools != 'NotConcentrated')

No surprises here. State revenue is fairly equal, local revenue favors white students, and federal revenue favors nonwhite students.

LS0tCnRpdGxlOiAiU2Nob29sIERpc3RyaWN0IFNwZW5kaW5nIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGZpZ193aWR0aDogOAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KVGhpcyBub3RlYm9vayBkb2N1bWVudHMgcmFjaWFsL2V0aG5pYyBkaXNwYXJpdGllcyBpbiBzY2hvb2wgZnVuZGluZy4gSXQgdXNlcyB0aGUgVXJiYW4gSW5zdGl0dXRlcyBSIHBhY2thZ2UgdG8gZXh0cmFjdCBzY2hvb2wgZmluYW5jZSBhbmQgZW5yb2xsbWVudCBkYXRhIGFuZCBtYWtlcyBhZGp1c3RtZW50cyBmb3IgY2hhcnRlciBzY2hvb2xzIChhbG9uZyB3aXRoIGEgY291cGxlIG90aGVyIHNtYWxsIGFkanVzdG1lbnRzKSBhbmQgYXBwbGllcyBhIGNvc3Qgb2YgbGl2aW5nIGFkanVzdG1lbnQgYmFzZWQgb24gdGhlIGNvdW50eSB0aGUgZGlzdHJpY3QgaXMgbG9jYXRlZCBpbi4KCkEgbG90IG9mIHNwZW5kaW5nIGRpc3Bhcml0eSByZXNlYXJjaCBmb2N1c2VzIG9ubHkgb24gc3RhdGUgYW5kIGxvY2FsIGZ1bmRpbmcgc291cmNlcyBiZWNhdXNlIG9mIHRoZSBmZWRlcmFsIHByb2dyYW1zICJzdXBwbGVtZW50IG5vdCBzdXBwbGFudCIgcHJvdmlzaW9ucywgYnV0IHRoaXMgbm90ZSBpcyBqdXN0IGludGVyZXN0ZWQgaW4gdG90YWwgZGlzcGFyaXRpZXMsIHNvIEkgaW5jbHVkZSBsb2NhbCwgc3RhdGUsIGFuZCBmZWRlcmFsIGZ1bmRpbmcgc291cmNlcy4KCiMjIENvbGxlY3QgRGF0YQoKYGBge3J9CiMgZ2V0IGRpc3RyaWN0IGNvc3Qgb2YgbGl2aW5nIGluZGV4IGZyb20gZWRidWlsZApkaXN0cmljdF9jb2xhIDwtIG1hc3RlcnB1bGwoJzIwMTYnLGRhdGFfdHlwZSA9ICdmaW4nKSAlPiUKICBtdXRhdGUoTFJfY29sYSA9IExSUFBfY29sYSpFTlJPTEwsCiAgICAgICAgIFNSX2NvbGEgPSBTUlBQX2NvbGEqRU5ST0xMLAogICAgICAgICBTTFJfY29sYSA9IFNMUlBQX2NvbGEqRU5ST0xMLAogICAgICAgICBjb2xhX3NyID0gU1JfY29sYS9TUiwKICAgICAgICAgY29sYV9sciA9IExSX2NvbGEvTFIpICU+JQogIHNlbGVjdChOQ0VTSUQsIGNvbGFfc3IpCgojIHNjaG9vbCBkaXN0cmljdCByZXZlbnVlIGFuZCBleHBlbmRpdHVyZXMKZmluYW5jZSA8LSBnZXRfZWR1Y2F0aW9uX2RhdGEobGV2ZWwgPSAnc2Nob29sLWRpc3RyaWN0cycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZSA9ICdjY2QnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BpYyA9ICdmaW5hbmNlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVycyA9IGxpc3QoeWVhciA9IDIwMTYpKQoKIyBkaXN0cmljdCBlbnJvbGxtZW50IGJ5IHJhY2UKZW5yb2xsbWVudCA8LSBnZXRfZWR1Y2F0aW9uX2RhdGEobGV2ZWwgPSAnc2Nob29sLWRpc3RyaWN0cycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZSA9ICdjY2QnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BpYyA9ICdlbnJvbGxtZW50JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidG9waWMgPSBsaXN0KCdyYWNlJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcnMgPSBsaXN0KHllYXIgPSAyMDE2KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkX2xhYmVscyA9IFRSVUUpCgojIGRpc3RyaWN0IGNoYXJhY3RlcmlzdGljcwpkaXJlY3RvcnkgPC0gZ2V0X2VkdWNhdGlvbl9kYXRhKGxldmVsID0gJ3NjaG9vbC1kaXN0cmljdHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZSA9ICdjY2QnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcGljID0gJ2RpcmVjdG9yeScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVycyA9IGxpc3QoeWVhciA9IDIwMTYpKQoKCmBgYAoKCiMjIE1hbmlwdWxhdGUgYW5kIGxpbmsgZGF0YQoKYGBge3J9CiMgYWRqdXN0IHJldmVudWVzIGJhc2VkIG9uIGVkYnVpbGQgbWV0aG9kb2xvZ3kgLSBsZWF2ZSBhcmthbnNhcyBhbmQgdGV4YXMgYWxvbmUKZmluX2FkanVzdGVkIDwtIGZpbmFuY2UgJT4lCiAgbXV0YXRlKGFkanJldl9zdGF0ZV90b3RhbCA9IHJldl9zdGF0ZV90b3RhbCAtIHJldl9zdGF0ZV9vdXRsYXlfY2FwaXRhbF9kZWJ0LCAjIHJlbW92ZSBzdGF0ZSBjYXBpdGFsIHJldgogICAgICAgICBhZGpyZXZfbG9jYWxfdG90YWwgPSByZXZfbG9jYWxfdG90YWwgLSByZXZfbG9jYWxfcHJvcGVydHlfc2FsZSwgIyByZW1vdmUgbG9jYWwgcHJvcGVydHkgc2FsZXMKICAgICAgICAgcGN0X2xvY2FsID0gcmV2X2xvY2FsX3RvdGFsL3Jldl90b3RhbCwKICAgICAgICAgcGN0X3N0YXRlID0gcmV2X3N0YXRlX3RvdGFsL3Jldl90b3RhbCwKICAgICAgICAgcGN0X2ZlZCA9IHJldl9mZWRfdG90YWwvcmV2X3RvdGFsLAogICAgICAgICBhZGpyZXZfbG9jYWxfdG90YWwgPSBhZGpyZXZfbG9jYWxfdG90YWwgLSAocGF5bWVudHNfY2hhcnRlcl9zY2hvb2xzKnBjdF9sb2NhbCksICMgcmVtb3ZlIHBheW1lbnRzIHRvIGNoYXJ0ZXJzCiAgICAgICAgIGFkanJldl9zdGF0ZV90b3RhbCA9IGFkanJldl9zdGF0ZV90b3RhbCAtIChwYXltZW50c19jaGFydGVyX3NjaG9vbHMqcGN0X3N0YXRlKSwgIyByZW1vdmUgcGF5bWVudHMgdG8gY2hhcnRlcnMKICAgICAgICAgYWRqcmV2X2ZlZF90b3RhbCA9IHJldl9mZWRfdG90YWwgLSAocGF5bWVudHNfY2hhcnRlcl9zY2hvb2xzKnBjdF9zdGF0ZSksICMgcmVtb3ZlIHBheW1lbnRzIHRvIGNoYXJ0ZXJzCiAgICAgICAgIGFkanJldl90b3RhbCA9IGFkanJldl9mZWRfdG90YWwrYWRqcmV2X3N0YXRlX3RvdGFsK2FkanJldl9sb2NhbF90b3RhbCwgIyBjYWxjdWxhdGUgdG90YWwgcmV2IGFkanVzdGVkCiAgICAgICAgIGFkanJldl9zbCA9IGFkanJldl9sb2NhbF90b3RhbCthZGpyZXZfc3RhdGVfdG90YWwpICMgY2FsYyB0b3RhbCBzdGF0ZSBhbmQgbG9jYWwgcmV2IGFkanVzdGVkCgojIHJlc2hhcGUgcmFjZSB0YWJsZQplbnJvbGxfcmFjZSA8LSBlbnJvbGxtZW50ICU+JQogIGZpbHRlcihzZXggPT0gJ1RvdGFsJywKICAgICAgICAgZ3JhZGUgPT0gJ1RvdGFsJykgJT4lCiAgZ3JvdXBfYnkobGVhaWQsIGZpcHMsIHJhY2UpICU+JQogIHN1bW1hcml6ZShlbnJvbGxtZW50ID0gc3VtKGVucm9sbG1lbnQpKSAlPiUKICB0aWR5cjo6IHNwcmVhZChrZXkgPSByYWNlLCB2YWx1ZSA9IGVucm9sbG1lbnQpCgojIGxpbmsgZW5yb2xsbWVudCB0byBmaW5hbmNlIGFuZCBjb2xhIGluZGV4CmVucm9sbF9yYWNlX2ZpbmFuY2UgPC0gZW5yb2xsX3JhY2UgJT4lCiAgbGVmdF9qb2luKGZpbl9hZGp1c3RlZFtjKCdsZWFpZCcsJ3Jldl90b3RhbCcsJ3Jldl9mZWRfdG90YWwnLCdyZXZfc3RhdGVfdG90YWwnLCdyZXZfbG9jYWxfdG90YWwnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRqcmV2X3RvdGFsJywgJ2FkanJldl9zdGF0ZV90b3RhbCcsJ2FkanJldl9sb2NhbF90b3RhbCcsJ2FkanJldl9mZWRfdG90YWwnLCdhZGpyZXZfc2wnKV0pICU+JQogIGlubmVyX2pvaW4oZGlzdHJpY3RfY29sYSwgYnkgPSBjKCdsZWFpZCcgPSAnTkNFU0lEJykpICU+JQogIGZpbHRlcihUb3RhbCA+IDAsIHJldl90b3RhbCA+PSAwKSAlPiUKICBtdXRhdGUoYWRqcmV2X3RvdGFsX2NvbGEgPSBhZGpyZXZfdG90YWwqY29sYV9zciwgIyBhZGogdG90YWwgcmV2IGNvbGEKICAgICAgICAgYWRqcmV2X3NsX2NvbGEgPSBhZGpyZXZfc2wqY29sYV9zciwgIyBhZGogc3RhdGUgYW5kIGxvY2FsIHJldiBjb2xhCiAgICAgICAgIGFkanJldl9mZWRfdG90YWxfY29sYSA9IGFkanJldl9mZWRfdG90YWwqY29sYV9zciwgIyBhZGogZmVkZXJhbCByZXYgY29sYQogICAgICAgICBhZGpyZXZfc3RhdGVfdG90YWxfY29sYSA9IGFkanJldl9zdGF0ZV90b3RhbCpjb2xhX3NyLCAjIGFkaiBzdGF0ZSByZXYgY29sYQogICAgICAgICBhZGpyZXZfbG9jYWxfdG90YWxfY29sYSA9IGFkanJldl9sb2NhbF90b3RhbCpjb2xhX3NyLCAjIGFkaiBsb2NhbCByZXYgY29sYQogICAgICAgICBhZGpyZXZfcGVyX3B1cGlsX2NvbGEgPSBhZGpyZXZfdG90YWxfY29sYSAvIFRvdGFsLCAjIGFkaiB0b3RhbCByZXYgcGVyIHB1cGlsCiAgICAgICAgIGFkanJldl9zbF9wZXJfcHVwaWxfY29sYSA9IGFkanJldl9zbF9jb2xhLyBUb3RhbCwgIyBhZGogc3RhdGUgYW5kIGxvY2FsIHJldiBwZXIgcHVwaWwKICAgICAgICAgcGN0X2JsYWNrID0gQmxhY2sgLyBUb3RhbCAqIDEwMCwKICAgICAgICAgcGN0X2hpc3AgPSBIaXNwYW5pYyAvIFRvdGFsICogMTAwLAogICAgICAgICBwY3Rfd2hpdGUgPSBXaGl0ZSAvIFRvdGFsICogMTAwLAogICAgICAgICBwY3Rfbm9ud2hpdGUgPSAxMDAgLSBwY3Rfd2hpdGUsCiAgICAgICAgIG5vbndoaXRlID0gVG90YWwgLSBXaGl0ZSwKICAgICAgICAgd19ud19zY2hvb2xzID0gY2FzZV93aGVuKHBjdF9ub253aGl0ZSA+PSA3NSB+ICdub253aGl0ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwY3Rfbm9ud2hpdGUgPD0gMjUgfiAnd2hpdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdOb3RDb25jZW50cmF0ZWQnKSwKICAgICAgICAgd19iX3NjaG9vbHMgPSBjYXNlX3doZW4ocGN0X2JsYWNrID49IDc1IH4gJ2JsYWNrJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGN0X3doaXRlID49IDc1IH4gJ3doaXRlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdOb3RDb25jZW50cmF0ZWQnKSkgJT4lCiAgdGlkeXI6OmRyb3BfbmEoKSAlPiUgdW5ncm91cCgpCgojIGJ1Y2tldCByYWNlIHBjdHMgYmxhY2sgYW5kIG5vbndoaXRlCmVucm9sbF9yYWNlX2ZpbmFuY2UkcGN0X2JsYWNrX2JpbiA8LSBjdXQoZW5yb2xsX3JhY2VfZmluYW5jZSRwY3RfYmxhY2ssIGJyZWFrcyA9IDEwLCBsYWJlbHMgPSBjKDEwLDIwLDMwLDQwLDUwLDYwLDcwLDgwLDkwLDEwMCkpCmVucm9sbF9yYWNlX2ZpbmFuY2UkcGN0X25vbndoaXRlX2JpbiA8LSBjdXQoZW5yb2xsX3JhY2VfZmluYW5jZSRwY3Rfbm9ud2hpdGUsIGJyZWFrcyA9IDEwLCBsYWJlbHMgPSBjKDEwLDIwLDMwLDQwLDUwLDYwLDcwLDgwLDkwLDEwMCkpCgpgYGAKCgojIyBHcmFwaCBvZiBSZXZlbnVlIHBlciBQdXBpbCBieSBEaXN0cmljdCBSYWNpYWwgQ29tcG9zaXRpb24KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KCiMgZmVkZXJhbCwgc3RhdGUgYW5kIGxvY2FsIGFkaiByZXYgY29sYSwgYmxhY2sgYmluIHNjYXR0ZXJwbG90CnAxIDwtIGVucm9sbF9yYWNlX2ZpbmFuY2UgJT4lIGZpbHRlcihwY3RfYmxhY2sgPD0gMTAwKSAlPiUKICBncm91cF9ieShwY3RfYmxhY2tfYmluKSAlPiUgCiAgc3VtbWFyaXplKGFkanJldl9wZXJfcHVwaWxfY29sYSA9IHN1bShhZGpyZXZfdG90YWxfY29sYSkgLyBzdW0oVG90YWwpKSAlPiUKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gcGN0X2JsYWNrX2JpbiwgeSA9IGFkanJldl9wZXJfcHVwaWxfY29sYSkpICsgCiAgZ2VvbV9wb2ludCgpICsgbGFicyh0aXRsZSA9ICdSZXZlbnVlIHBlciBQdXBpbCcsIHN1YnRpdGxlID0gICdCaW5uZWQgQmxhY2sgUGVyY2VudCcpICsgCiAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCiMgZmVkZXJhbCwgc3RhdGUgYW5kIGxvY2FsIGFkaiByZXYgY29sYSwgbm9ud2hpdGUgYmluIHNjYXR0ZXJwbG90CnAyIDwtIGVucm9sbF9yYWNlX2ZpbmFuY2UgJT4lIGZpbHRlcihwY3Rfbm9ud2hpdGUgPD0gMTAwKSAlPiUKICBncm91cF9ieShwY3Rfbm9ud2hpdGVfYmluKSAlPiUgCiAgc3VtbWFyaXplKGFkanJldl9wZXJfcHVwaWxfY29sYSA9IHN1bShhZGpyZXZfdG90YWxfY29sYSkgLyBzdW0oVG90YWwpKSAlPiUKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gcGN0X25vbndoaXRlX2JpbiwgeSA9IGFkanJldl9wZXJfcHVwaWxfY29sYSkpICsgCiAgZ2VvbV9wb2ludCgpICsgbGFicyh0aXRsZSA9ICdSZXZlbnVlIHBlciBQdXBpbCcsIHN1YnRpdGxlID0gICdCaW5uZWQgTm9ud2hpdGUgUGVyY2VudCcpICsgCiAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAxKQpgYGAKClJldmVudWUgcGVyIFB1cGlsIGlzIGhpZ2hlciBpbiBkaXN0cmljdHMgd2l0aCBhIGhpZ2hlciBzaGFyZSBvZiBibGFjayBzdHVkZW50cywgYnV0IGlzIGxvd2VyIGluIGRpc3RyaWN0cyB3aXRoIGEgaGlnaGVyIHNoYXJlIG9mIG5vbndoaXRlIHN0dWRlbnRzLgoKIyMgQXZlcmFnZSBSZXZlbnVlIHBlciBQdXBpbCBpbiBTZWdyZWdhdGVkIFNjaG9vbHMKCmBgYHtyfQoKIyByZXYgcGVyIHN0dWRlbnQgZm9yIG1ham9yaXR5IGJsYWNrIHNjaG9vbHMgdnMgbWFqb3JpdHkgd2hpdGUgc2Nob29scyAoPjc1JSkKZW5yb2xsX3JhY2VfZmluYW5jZSAlPiUKICBncm91cF9ieSh3X2Jfc2Nob29scykgJT4lCiAgc3VtbWFyaXplKHJldl9wZXJfcHVwaWxfY29sYSA9IHN1bShhZGpyZXZfdG90YWxfY29sYSkgLyBzdW0oVG90YWwpLAogICAgICAgICAgICBudW1zdHVkZW50cyA9IHN1bShUb3RhbCkpICU+JQogIGZpbHRlcih3X2Jfc2Nob29scyAhPSAnTm90Q29uY2VudHJhdGVkJykKCiMgcmV2IHBlciBzdHVkZW50IGZvciBtYWpvcml0eSBub253aGl0ZSBzY2hvb2xzIHZzIG1ham9yaXR5IHdoaXRlIHNjaG9vbHMgKD43NSUpCmVucm9sbF9yYWNlX2ZpbmFuY2UgJT4lCiAgZ3JvdXBfYnkod19ud19zY2hvb2xzKSAlPiUKICBzdW1tYXJpemUocmV2X3Blcl9wdXBpbF9jb2xhID0gc3VtKGFkanJldl90b3RhbF9jb2xhKSAvIHN1bShUb3RhbCksCiAgICAgICAgICAgIG51bXN0dWRlbnRzID0gc3VtKFRvdGFsKSkgJT4lIGZpbHRlcih3X253X3NjaG9vbHMgIT0gJ05vdENvbmNlbnRyYXRlZCcpCgpgYGAKClJldmVudWUgcGVyIHB1cGlsIGluIG1ham9yaXR5IGJsYWNrIGRpc3RyaWN0cyBpcyBmYWlybHkgY2xvc2UgdG8gcmV2ZW51ZSBwZXIgcHVwaWwgaW4gbWFqb3JpdHkgd2hpdGUgZGlzdHJpY3RzLiBUaG91Z2ggdGhlcmUgYXJlIHZlcnkgZmV3IGJsYWNrIHN0dWRlbnRzIGVkdWNhdGVkIGluIGRpc3RyaWN0cyB3aGVyZSA3NSUgb2Yga2lkcyBhcmUgYmxhY2suCgpSZXZlbnVlIHBlciBwdXBpbCBpcyBzdWJzdGFudGlhbGx5IGxvd2VyIGluIGRpc3RyaWN0cyB0aGF0IGFyZSBtYWpvcml0eSAoPjc1JSkgbm9uLXdoaXRlIChibGFjaywgaGlzcGFuaWMsIGFzaWFuKSB0aGFuIGluIGRpc3RyaWN0cyB0aGF0IGFyZSBtYWpvcml0eSAoPjc1JSkgd2hpdGUuIAoKIyMgTmF0aW9uYWwgQXZlcmFnZSBSZXZlbnVlIHBlciBQdXBpbCBCeSBSYWNlCgpgYGB7cn0KZW5yb2xsX3JhY2VfZmluYW5jZSAlPiUKICBtdXRhdGUoYWRqcmV2X2JsYWNrX3N0dWRlbnRzID0gYWRqcmV2X3Blcl9wdXBpbF9jb2xhKkJsYWNrLAogICAgICAgICBhZGpyZXZfd2hpdGVfc3R1ZGVudHMgPSBhZGpyZXZfcGVyX3B1cGlsX2NvbGEqV2hpdGUsCiAgICAgICAgIG5hdGlvbmFsID0gJ25hdGlvbmFsJykgJT4lCiAgZ3JvdXBfYnkobmF0aW9uYWwpICU+JQogIHN1bW1hcml6ZShhdmdfcmV2X2JsYWNrX3N0dWRlbnRzICA9IHN1bShhZGpyZXZfYmxhY2tfc3R1ZGVudHMpL3N1bShCbGFjayksCiAgICAgICAgICAgIGF2Z19yZXZfd2hpdGVfc3R1ZGVudHMgID0gc3VtKGFkanJldl93aGl0ZV9zdHVkZW50cykvc3VtKFdoaXRlKSkgJT4lCiAgbXV0YXRlKHBlcmNlbnRfZGlmZiA9IChhdmdfcmV2X2JsYWNrX3N0dWRlbnRzLWF2Z19yZXZfd2hpdGVfc3R1ZGVudHMpIC8gYXZnX3Jldl9ibGFja19zdHVkZW50cykKCmVucm9sbF9yYWNlX2ZpbmFuY2UgJT4lCiAgbXV0YXRlKGFkanJldl9ub253aGl0ZV9zdHVkZW50cyA9IGFkanJldl9wZXJfcHVwaWxfY29sYSpub253aGl0ZSwKICAgICAgICAgYWRqcmV2X3doaXRlX3N0dWRlbnRzID0gYWRqcmV2X3Blcl9wdXBpbF9jb2xhKldoaXRlLAogICAgICAgICBuYXRpb25hbCA9ICduYXRpb25hbCcpICU+JQogIGdyb3VwX2J5KG5hdGlvbmFsKSAlPiUKICBzdW1tYXJpemUoYXZnX3Jldl9ub253aGl0ZV9zdHVkZW50cyAgPSBzdW0oYWRqcmV2X25vbndoaXRlX3N0dWRlbnRzKS9zdW0obm9ud2hpdGUpLAogICAgICAgICAgICBhdmdfcmV2X3doaXRlX3N0dWRlbnRzICA9IHN1bShhZGpyZXZfd2hpdGVfc3R1ZGVudHMpL3N1bShXaGl0ZSkpICU+JQogIG11dGF0ZShwZXJjZW50X2RpZmYgPSAoYXZnX3Jldl9ub253aGl0ZV9zdHVkZW50cyAtIGF2Z19yZXZfd2hpdGVfc3R1ZGVudHMpIC8gYXZnX3Jldl93aGl0ZV9zdHVkZW50cykKCmBgYAoKVGhlIGF2ZXJhZ2UgYmxhY2sgc3R1ZGVudCBpbiB0aGUgVVMgYXR0ZW5kcyBhIGRpc3RyaWN0IHdoZXJlIHJldmVudWUgaXMgMi41JSBsb3dlciB0aGFuIGRpc3RyaWN0cyBhdHRlbmRlZCBieSB0aGUgYXZlcmFnZSB3aGl0ZSBzdHVkZW50LiBUaGUgYXZlcmFnZSBub253aGl0ZSBzdHVkZW50IGluIHRoZSBVUyBhdHRlbmRzIGEgZGlzdHJpY3Qgd2hlcmUgcmV2ZW51ZSBpcyBhYm91dCA2LjElIGxvd2VyIHRoYW4gZm9yIHRoZSBhdmVyYWdlIHdoaXRlIHN0dWRlbnQuCgojIyBTdGF0ZSBBdmVyYWdlIFJldmVudWUgcGVyIFB1cGlsIERpZmZlcmVuY2UgQnkgUmFjZQoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQoKcDMgPC0gZW5yb2xsX3JhY2VfZmluYW5jZSAlPiUKICBtdXRhdGUoYWRqcmV2X2JsYWNrX3N0dWRlbnRzID0gYWRqcmV2X3Blcl9wdXBpbF9jb2xhKkJsYWNrLAogICAgICAgICBhZGpyZXZfd2hpdGVfc3R1ZGVudHMgPSBhZGpyZXZfcGVyX3B1cGlsX2NvbGEqV2hpdGUsCiAgICAgICAgIG5hdGlvbmFsID0gJ25hdGlvbmFsJykgJT4lCiAgZ3JvdXBfYnkoZmlwcykgJT4lCiAgc3VtbWFyaXplKG5hdGxfcmV2X2JsYWNrX3N0dWRlbnRzICA9IHN1bShhZGpyZXZfYmxhY2tfc3R1ZGVudHMpL3N1bShCbGFjayksCiAgICAgICAgICAgIG5hdGxfcmV2X3doaXRlX3N0dWRlbnRzICA9IHN1bShhZGpyZXZfd2hpdGVfc3R1ZGVudHMpL3N1bShXaGl0ZSkpICU+JQogIG11dGF0ZShwY3RfYmxhY2tfd2hpdGVfZGlmZmVyZW5jZSA9IChuYXRsX3Jldl9ibGFja19zdHVkZW50cyAtIG5hdGxfcmV2X3doaXRlX3N0dWRlbnRzKS9uYXRsX3Jldl93aGl0ZV9zdHVkZW50cyoxMDApICU+JQogIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBmaXBzLCB5ID0gcGN0X2JsYWNrX3doaXRlX2RpZmZlcmVuY2UpKSArIGdlb21fY29sKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsaGp1c3QgPSAxLCB2anVzdCA9IC41KSkKCiMgcmV2IGRpZmZlcmVuY2UgZm9yIGF2ZXJhZ2Ugbm9ud2hpdGUgYW5kIHdoaXRlIHN0dWRlbnQgaW4gZWFjaCBzdGF0ZQpwNCA8LSBlbnJvbGxfcmFjZV9maW5hbmNlICU+JQogIG11dGF0ZShhZGpyZXZfbm9ud2hpdGVfc3R1ZGVudHMgPSBhZGpyZXZfcGVyX3B1cGlsX2NvbGEqbm9ud2hpdGUsCiAgICAgICAgIGFkanJldl93aGl0ZV9zdHVkZW50cyA9IGFkanJldl9wZXJfcHVwaWxfY29sYSpXaGl0ZSwKICAgICAgICAgbmF0aW9uYWwgPSAnbmF0aW9uYWwnKSAlPiUKICBncm91cF9ieShmaXBzKSAlPiUKICBzdW1tYXJpemUobmF0bF9yZXZfbm9ud2hpdGVfc3R1ZGVudHMgID0gc3VtKGFkanJldl9ub253aGl0ZV9zdHVkZW50cykvc3VtKG5vbndoaXRlKSwKICAgICAgICAgICAgbmF0bF9yZXZfd2hpdGVfc3R1ZGVudHMgID0gc3VtKGFkanJldl93aGl0ZV9zdHVkZW50cykvc3VtKFdoaXRlKSwKICAgICAgICAgICAgd2hpdGUgPSBzdW0oV2hpdGUpLAogICAgICAgICAgICBub253aGl0ZSA9IHN1bShub253aGl0ZSkpICU+JQogIG11dGF0ZShwY3Rfbm9ud2hpdGVfd2hpdGVfZGlmZmVyZW5jZSA9IChuYXRsX3Jldl9ub253aGl0ZV9zdHVkZW50cyAtIG5hdGxfcmV2X3doaXRlX3N0dWRlbnRzKS9uYXRsX3Jldl93aGl0ZV9zdHVkZW50cyoxMDApICU+JQogIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBmaXBzLCB5ID0gcGN0X25vbndoaXRlX3doaXRlX2RpZmZlcmVuY2UpKSArIGdlb21fY29sKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsaGp1c3QgPSAxLCB2anVzdCA9IC41KSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAzLHA0LG5yb3c9MSkKYGBgCgoKCgojIyBSZXZlbnVlIGJ5IFNvdXJjZSBmb3IgU2VncmVnYXRlZCBTY2hvb2xzCgpgYGB7cn0KCiMgcmV2ZW51ZSBieSBzb3VyY2UgaW4gd2hpdGUgYW5kIGJsYWNrICg+NzUlKSBzY2hvb2xzCmVucm9sbF9yYWNlX2ZpbmFuY2UgJT4lCiAgZ3JvdXBfYnkod19iX3NjaG9vbHMpICU+JQogIHN1bW1hcml6ZShGZWRlcmFsUmV2ID0gc3VtKGFkanJldl9mZWRfdG90YWxfY29sYSkvc3VtKFRvdGFsKSwKICAgICAgICAgICAgU3RhdGVSZXYgPSBzdW0oYWRqcmV2X3N0YXRlX3RvdGFsX2NvbGEpL3N1bShUb3RhbCksCiAgICAgICAgICAgIExvY2FsUmV2ID0gc3VtKGFkanJldl9sb2NhbF90b3RhbF9jb2xhKS9zdW0oVG90YWwpKSAlPiUKICBmaWx0ZXIod19iX3NjaG9vbHMgIT0gJ05vdENvbmNlbnRyYXRlZCcpCgojIHJldmVudWUgYnkgc291cmNlIGluIHdoaXRlIGFuZCBub253aGl0ZSAoPjc1JSkgc2Nob29scwplbnJvbGxfcmFjZV9maW5hbmNlICU+JQogIGdyb3VwX2J5KHdfbndfc2Nob29scykgJT4lCiAgc3VtbWFyaXplKEZlZGVyYWxSZXYgPSBzdW0oYWRqcmV2X2ZlZF90b3RhbF9jb2xhKS9zdW0oVG90YWwpLAogICAgICAgICAgICBTdGF0ZVJldiA9IHN1bShhZGpyZXZfc3RhdGVfdG90YWxfY29sYSkvc3VtKFRvdGFsKSwKICAgICAgICAgICAgTG9jYWxSZXYgPSBzdW0oYWRqcmV2X2xvY2FsX3RvdGFsX2NvbGEpL3N1bShUb3RhbCkpICU+JQogIGZpbHRlcih3X253X3NjaG9vbHMgIT0gJ05vdENvbmNlbnRyYXRlZCcpCgoKYGBgCgpObyBzdXJwcmlzZXMgaGVyZS4gU3RhdGUgcmV2ZW51ZSBpcyBmYWlybHkgZXF1YWwsIGxvY2FsIHJldmVudWUgZmF2b3JzIHdoaXRlIHN0dWRlbnRzLCBhbmQgZmVkZXJhbCByZXZlbnVlIGZhdm9ycyBub253aGl0ZSBzdHVkZW50cy4KCgo=