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))
Manipulate and link data
# adjust revenues based on edbuild methodology - leave arkansas and texas alone
fin_adjusted <- finance %>%
mutate(adjrev_state_total = rev_state_total - rev_state_outlay_capital_debt, # remove state capital rev
adjrev_local_total = rev_local_total - rev_local_property_sale, # remove local property sales
pct_local = rev_local_total/rev_total,
pct_state = rev_state_total/rev_total,
pct_fed = rev_fed_total/rev_total,
adjrev_local_total = adjrev_local_total - (payments_charter_schools*pct_local), # remove payments to charters
adjrev_state_total = adjrev_state_total - (payments_charter_schools*pct_state), # remove payments to charters
adjrev_fed_total = rev_fed_total - (payments_charter_schools*pct_state), # remove payments to charters
adjrev_total = adjrev_fed_total+adjrev_state_total+adjrev_local_total, # calculate total rev adjusted
adjrev_sl = adjrev_local_total+adjrev_state_total) # calc total state and local rev adjusted
# reshape race table
enroll_race <- enrollment %>%
filter(sex == 'Total',
grade == 'Total') %>%
group_by(leaid, fips, race) %>%
summarize(enrollment = sum(enrollment)) %>%
tidyr:: spread(key = race, value = enrollment)
# link enrollment to finance and cola index
enroll_race_finance <- enroll_race %>%
left_join(fin_adjusted[c('leaid','rev_total','rev_fed_total','rev_state_total','rev_local_total',
'adjrev_total', 'adjrev_state_total','adjrev_local_total','adjrev_fed_total','adjrev_sl')]) %>%
inner_join(district_cola, by = c('leaid' = 'NCESID')) %>%
filter(Total > 0, rev_total >= 0) %>%
mutate(adjrev_total_cola = adjrev_total*cola_sr, # adj total rev cola
adjrev_sl_cola = adjrev_sl*cola_sr, # adj state and local rev cola
adjrev_fed_total_cola = adjrev_fed_total*cola_sr, # adj federal rev cola
adjrev_state_total_cola = adjrev_state_total*cola_sr, # adj state rev cola
adjrev_local_total_cola = adjrev_local_total*cola_sr, # adj local rev cola
adjrev_per_pupil_cola = adjrev_total_cola / Total, # adj total rev per pupil
adjrev_sl_per_pupil_cola = adjrev_sl_cola/ Total, # adj state and local rev per pupil
pct_black = Black / Total * 100,
pct_hisp = Hispanic / Total * 100,
pct_white = White / Total * 100,
pct_nonwhite = 100 - pct_white,
nonwhite = Total - White,
w_nw_schools = case_when(pct_nonwhite >= 75 ~ 'nonwhite',
pct_nonwhite <= 25 ~ 'white',
TRUE ~ 'NotConcentrated'),
w_b_schools = case_when(pct_black >= 75 ~ 'black',
pct_white >= 75 ~ 'white',
TRUE ~ 'NotConcentrated')) %>%
tidyr::drop_na() %>% ungroup()
# bucket race pcts black and nonwhite
enroll_race_finance$pct_black_bin <- cut(enroll_race_finance$pct_black, breaks = 10, labels = c(10,20,30,40,50,60,70,80,90,100))
enroll_race_finance$pct_nonwhite_bin <- cut(enroll_race_finance$pct_nonwhite, breaks = 10, labels = c(10,20,30,40,50,60,70,80,90,100))
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=