Last week there were four widely-covered Senate votes about healthcare. The results of the votes were often broken down by party (R)/(D) but I wanted a geographic visual summary of the results. I use this notebook to visualize the recent results in the Senate with the machinery of facet_geo in the geofacet package.

First, let’s generate/clean/format our data.

I load required packages.

library(geofacet);library(dplyr);library(ggplot2);library(ggrepel); library(extrafont); library(ggthemes);library(reshape);library(grid);
library(scales);library(RColorBrewer);library(gridExtra);
library(magrittr);library(magick)

Now, I then constructed a csv file that contained structured information on the vote to start debate and the three following proposal votes. The NYTimes consistently provided information on how all Senators voted, so I used the following two articles to construct my data set: article on the vote to start debate & article on the three following votes.

I then read in the csv file I created from the NYTimes data on the senate votes.

healthcare <- read.csv('hcare_votes.csv')

For the purposes of ggplot2, we have to reshape everything to be long format. Let’s do that with a column for the categories of senators. Then we will subset for each of the 4 votes of interest (starting debate, repeal and replace, partial repeal, and ‘skinny’ repeal) to create plots for each.

healthcare<-reshape(healthcare, 
            varying = c("Dem", "R_vote_for_debate", "R_vote_against_debate", "R_vote_for_repeal_replace","R_vote_against_repeal_replace", "R_vote_for_partial_repeal", "R_vote_against_partial_repeal", "R_vote_for_skinny_repeal", "R_vote_against_skinny_repeal"), 
            v.names = "votes",
            timevar = "senator_group", 
            times = c("Dem", "R_vote_for_debate", "R_vote_against_debate", "R_vote_for_repeal_replace","R_vote_against_repeal_replace", "R_vote_for_partial_repeal", "R_vote_against_partial_repeal", "R_vote_for_skinny_repeal", "R_vote_against_skinny_repeal"), 
            direction = "long")

Now the data is of the desired long format. I now call my theme that I use for coming graphics in ggplot2.

my_theme <- function() {
  # Define colors for the chart
  palette <- brewer.pal("Greys", n=9)
  color.background = palette[2]
  color.grid.major = palette[4]
  color.panel = palette[3]
  color.axis.text = palette[9]
  color.axis.title = palette[9]
  color.title = palette[9]
  # Create basic construction of chart
  theme_bw(base_size=9, base_family="Palatino") + 
  # Set the entire chart region to a light gray color
  theme(panel.background=element_rect(fill=color.panel, color=color.background)) +
  theme(plot.background=element_rect(fill=color.background, color=color.background)) +
  theme(panel.border=element_rect(color=color.background)) +
  # Format grid
  theme(panel.grid.major=element_line(color=color.grid.major,size=.25)) +
  theme(panel.grid.minor=element_blank()) +
  theme(axis.ticks=element_blank()) +
  # Format legend
  theme(legend.position="right") +
  theme(legend.background = element_rect(fill=color.background)) +
  theme(legend.text = element_text(size=12,color=color.axis.title)) + 
  theme(legend.title = element_blank()) + 
  
  #Format facet labels
  theme(strip.text.x = element_text(size = 8, face="bold"))+
  # Format title and axes labels these and tick marks
  theme(plot.title=element_text(color=color.title, size=28)) +
  theme(axis.text.x=element_blank()) +
  theme(axis.text.y=element_blank()) +
  theme(axis.title.x=element_blank()) +
  theme(axis.title.y=element_blank()) +
  #Format title and facet_wrap title
  theme(strip.text = element_text(size=8), plot.title = element_text(size = 24, colour = "black", vjust = 1, hjust=0.5))+
    
  # Plot margins
  theme(plot.margin = unit(c(.5, .2, .2, 2), "cm"))
}

Let’s make plots vote by vote…

1. Vote to Start Debate

This passed 51-50 on Tuesday, July 25, 2017. (Pence cast the tie-brekaing vote)

We subset to the columns relevant to these results.

healthcare_debate_vote<-healthcare[which(healthcare$senator_group=="R_vote_for_debate" | healthcare$senator_group=="R_vote_against_debate"| healthcare$senator_group == "Dem"),]

Plot and save as png:

deb<-ggplot(healthcare_debate_vote, aes("", votes, fill = senator_group)) +
  geom_col(alpha = 1, width = 1) +
  my_theme()+
  coord_flip()+
  scale_fill_manual(values = c("dodgerblue2", "darkorchid2", "firebrick2"), breaks=c("Dem", "R_vote_against_debate", "R_vote_for_debate"), labels=c("(D) NO     ", "(R) NO     ", "(R) YES     ")) +
  facet_geo(~ state, grid = "us_state_grid2", label="code") +
  scale_y_continuous(expand = c(0, 0)) +
  ggtitle("Senate Votes Visualized:\n Vote to Begin Debate")+
  labs(caption = "Final vote count: 51-50 (passed with Pence casting the tie-breaking vote)\nBernie Sanders (I-VT) and Angus King (I-ME) caucus with the Democrats\n\nData source: NYTimes | Visualization via Alex Albright (thelittledataset.com) | DC emoji choice via Jesse White") +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text.x = element_text(size = 7))+
  ggsave("deb.png", width = 12, height = 8, dpi = 800)

Now, I want to add a zipper emoji to DC’s plot space, as DC has no senators but appears in the graph.

# Now call back the plot
background <- image_read("deb.png")
# And bring in a zipper emoji
zipper_raw <- image_read("zipper.png")
zipper <- zipper_raw %>%
  image_scale("400") 
new <- image_composite(background, zipper, offset = "+6650+2850")
image_write(new, "deb_final.png", flatten = F)

Here’s the png that I just made: here are the votes

2. Repeal and replace amendment

The Better Care Reconciliation Act failed on Tuesday, July 25, 2017.

We subset to the columns relevant to these results.

healthcare_rr_vote<-healthcare[which(healthcare$senator_group=="R_vote_for_repeal_replace" | healthcare$senator_group=="R_vote_against_repeal_replace"| healthcare$senator_group == "Dem"),]

Plot and save:

rr<-ggplot(healthcare_rr_vote, aes("", votes, fill = senator_group)) +
  geom_col(alpha = 1, width = 1) +
  my_theme()+
  coord_flip()+
  scale_fill_manual(values = c("dodgerblue2", "darkorchid2", "firebrick2"), breaks=c("Dem", "R_vote_against_repeal_replace", "R_vote_for_repeal_replace"), labels=c("(D) NO     ", "(R) NO     ", "(R) YES     ")) +
  facet_geo(~ state, grid = "us_state_grid2", label="code") +
  scale_y_continuous(expand = c(0, 0)) +
  ggtitle("Senate Votes Visualized:\n Repeal and Replace Amendment")+
  labs(caption = "Final vote count: 43-57 (failed)\nBernie Sanders (I-VT) and Angus King (I-ME) caucus with the Democrats\n\nData source: NYTimes | Visualization via Alex Albright (thelittledataset.com) | DC emoji choice via Jesse White") +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text.x = element_text(size = 7))+
   ggsave("rr.png", width = 12, height = 8, dpi = 800)

Save with DC emoji:

background <- image_read("rr.png")
new <- image_composite(background, zipper, offset = "+6650+2850")
image_write(new, "rr_final.png", flatten = F)

Here’s the png that I just made: here are the votes

3. Partial repeal amendment

The Obamacare Repeal and Reconciliation Act failed on Wednesday, July 26, 2017.

We subset to the columns relevant to these results.

healthcare_pr_vote<-healthcare[which(healthcare$senator_group=="R_vote_for_partial_repeal" | healthcare$senator_group=="R_vote_against_partial_repeal"| healthcare$senator_group == "Dem"),]

Plot and save:

pr<-ggplot(healthcare_pr_vote, aes("", votes, fill = senator_group)) +
  geom_col(alpha = 1, width = 1) +
  my_theme()+
  coord_flip()+
  scale_fill_manual(values = c("dodgerblue2", "darkorchid2", "firebrick2"), breaks=c("Dem", "R_vote_against_partial_repeal", "R_vote_for_partial_repeal"), labels=c("(D) NO     ", "(R) NO     ", "(R) YES     ")) +
  facet_geo(~ state, grid = "us_state_grid2", label="code") +
  scale_y_continuous(expand = c(0, 0)) +
  ggtitle("Senate Votes Visualized:\n Partial Repeal Amendment")+
  labs(caption = "Final vote count: 45-55 (failed)\nBernie Sanders (I-VT) and Angus King (I-ME) caucus with the Democrats\n\nData source: NYTimes | Visualization via Alex Albright (thelittledataset.com) | DC emoji choice via Jesse White") +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text.x = element_text(size = 7))+
   ggsave("pr.png", width = 12, height = 8, dpi = 800)

Save with DC emoji:

background <- image_read("pr.png")
new <- image_composite(background, zipper, offset = "+6650+2850")
image_write(new, "pr_final.png", flatten = F)

Here’s the png that I just made: here are the votes

4. ‘Skinny’ repeal amendment

The Health Care Freedom Act failed on Friday, July 28, 2017.

We subset to the columns relevant to these results.

healthcare_sk_vote<-healthcare[which(healthcare$senator_group=="R_vote_for_skinny_repeal" | healthcare$senator_group=="R_vote_against_skinny_repeal"| healthcare$senator_group == "Dem"),]

Plot and save:

sk<-ggplot(healthcare_sk_vote, aes("", votes, fill = senator_group)) +
  geom_col(alpha = 1, width = 1) +
  my_theme()+
  coord_flip()+
  scale_fill_manual(values = c("dodgerblue2", "darkorchid2", "firebrick2"), breaks=c("Dem", "R_vote_against_skinny_repeal", "R_vote_for_skinny_repeal"), labels=c("(D) NO     ", "(R) NO     ", "(R) YES     ")) +
  facet_geo(~ state, grid = "us_state_grid2", label = "code") +
  scale_y_continuous(expand = c(0, 0)) +
  ggtitle("Senate Votes Visualized:\n 'Skinny' Repeal Amendment")+
  labs(caption = "Final vote count: 49-51 (failed)\nBernie Sanders (I-VT) and Angus King (I-ME) caucus with the Democrats\n\nData source: NYTimes | Visualization via Alex Albright (thelittledataset.com) | DC emoji choice via Jesse White") +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text.x = element_text(size = 7))+
   ggsave("sk.png", width = 12, height = 8, dpi = 800)

Save with DC emoji:

background <- image_read("sk.png")
new <- image_composite(background, zipper, offset = "+6650+2850")
image_write(new, "sk_final.png", flatten = F)

Here’s the png that I just made: here are the votes

We have now created png plots for each of the four relevant votes!

LS0tCnRpdGxlOiAiU2VuYXRlIFZvdGVzIFZpc3VhbGl6ZWQiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IEFsZXggQWxicmlnaHQKZGF0ZTogOC0xLTE3Ci0tLQpMYXN0IHdlZWsgdGhlcmUgd2VyZSBmb3VyIHdpZGVseS1jb3ZlcmVkIFNlbmF0ZSB2b3RlcyBhYm91dCBoZWFsdGhjYXJlLiBUaGUgcmVzdWx0cyBvZiB0aGUgdm90ZXMgd2VyZSBvZnRlbiBicm9rZW4gZG93biBieSBwYXJ0eSAoUikvKEQpIGJ1dCBJIHdhbnRlZCBhIGdlb2dyYXBoaWMgdmlzdWFsIHN1bW1hcnkgb2YgdGhlIHJlc3VsdHMuIEkgdXNlIHRoaXMgbm90ZWJvb2sgdG8gdmlzdWFsaXplIHRoZSByZWNlbnQgcmVzdWx0cyBpbiB0aGUgU2VuYXRlIHdpdGggdGhlIG1hY2hpbmVyeSBvZiBgZmFjZXRfZ2VvYCBpbiB0aGUgYGdlb2ZhY2V0YCBwYWNrYWdlLgoKIyMjIEZpcnN0LCBsZXQncyBnZW5lcmF0ZS9jbGVhbi9mb3JtYXQgb3VyIGRhdGEuCkkgbG9hZCByZXF1aXJlZCBwYWNrYWdlcy4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2VvZmFjZXQpO2xpYnJhcnkoZHBseXIpO2xpYnJhcnkoZ2dwbG90Mik7bGlicmFyeShnZ3JlcGVsKTsgbGlicmFyeShleHRyYWZvbnQpOyBsaWJyYXJ5KGdndGhlbWVzKTtsaWJyYXJ5KHJlc2hhcGUpO2xpYnJhcnkoZ3JpZCk7CmxpYnJhcnkoc2NhbGVzKTtsaWJyYXJ5KFJDb2xvckJyZXdlcik7bGlicmFyeShncmlkRXh0cmEpOwpsaWJyYXJ5KG1hZ3JpdHRyKTtsaWJyYXJ5KG1hZ2ljaykKYGBgCk5vdywgSSB0aGVuIGNvbnN0cnVjdGVkIGEgYGNzdmAgZmlsZSB0aGF0IGNvbnRhaW5lZCBzdHJ1Y3R1cmVkIGluZm9ybWF0aW9uIG9uIHRoZSB2b3RlIHRvIHN0YXJ0IGRlYmF0ZSBhbmQgdGhlIHRocmVlIGZvbGxvd2luZyBwcm9wb3NhbCB2b3Rlcy4gVGhlIE5ZVGltZXMgY29uc2lzdGVudGx5IHByb3ZpZGVkIGluZm9ybWF0aW9uIG9uIGhvdyBhbGwgU2VuYXRvcnMgdm90ZWQsIHNvIEkgdXNlZCB0aGUgZm9sbG93aW5nIHR3byBhcnRpY2xlcyB0byBjb25zdHJ1Y3QgbXkgZGF0YSBzZXQ6IFthcnRpY2xlXShodHRwczovL3d3dy5ueXRpbWVzLmNvbS9pbnRlcmFjdGl2ZS8yMDE3LzA3LzI1L3VzL3BvbGl0aWNzL3NlbmF0ZS12b3RlLXJlcHVibGljYW4taGVhbHRoLWNhcmUtYmlsbC5odG1sKSBvbiB0aGUgdm90ZSB0byBzdGFydCBkZWJhdGUgJiBbYXJ0aWNsZV0oaHR0cHM6Ly93d3cubnl0aW1lcy5jb20vaW50ZXJhY3RpdmUvMjAxNy8wNy8yNS91cy9wb2xpdGljcy9zZW5hdGUtdm90ZXMtcmVwZWFsLW9iYW1hY2FyZS5odG1sKSBvbiB0aGUgdGhyZWUgZm9sbG93aW5nIHZvdGVzLiAKCgpJIHRoZW4gcmVhZCBpbiB0aGUgYGNzdmAgZmlsZSBJIGNyZWF0ZWQgZnJvbSB0aGUgTllUaW1lcyBkYXRhIG9uIHRoZSBzZW5hdGUgdm90ZXMuCmBgYHtyfQpoZWFsdGhjYXJlIDwtIHJlYWQuY3N2KCdoY2FyZV92b3Rlcy5jc3YnKQpgYGAKRm9yIHRoZSBwdXJwb3NlcyBvZiBgZ2dwbG90MmAsIHdlIGhhdmUgdG8gcmVzaGFwZSBldmVyeXRoaW5nIHRvIGJlIGxvbmcgZm9ybWF0LiBMZXQncyBkbyB0aGF0IHdpdGggYSBjb2x1bW4gZm9yIHRoZSBjYXRlZ29yaWVzIG9mIHNlbmF0b3JzLiBUaGVuIHdlIHdpbGwgc3Vic2V0IGZvciBlYWNoIG9mIHRoZSA0IHZvdGVzIG9mIGludGVyZXN0IChzdGFydGluZyBkZWJhdGUsIHJlcGVhbCBhbmQgcmVwbGFjZSwgcGFydGlhbCByZXBlYWwsIGFuZCAnc2tpbm55JyByZXBlYWwpIHRvIGNyZWF0ZSBwbG90cyBmb3IgZWFjaC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpoZWFsdGhjYXJlPC1yZXNoYXBlKGhlYWx0aGNhcmUsIAogICAgICAgICAgICB2YXJ5aW5nID0gYygiRGVtIiwgIlJfdm90ZV9mb3JfZGViYXRlIiwgIlJfdm90ZV9hZ2FpbnN0X2RlYmF0ZSIsICJSX3ZvdGVfZm9yX3JlcGVhbF9yZXBsYWNlIiwiUl92b3RlX2FnYWluc3RfcmVwZWFsX3JlcGxhY2UiLCAiUl92b3RlX2Zvcl9wYXJ0aWFsX3JlcGVhbCIsICJSX3ZvdGVfYWdhaW5zdF9wYXJ0aWFsX3JlcGVhbCIsICJSX3ZvdGVfZm9yX3NraW5ueV9yZXBlYWwiLCAiUl92b3RlX2FnYWluc3Rfc2tpbm55X3JlcGVhbCIpLCAKICAgICAgICAgICAgdi5uYW1lcyA9ICJ2b3RlcyIsCiAgICAgICAgICAgIHRpbWV2YXIgPSAic2VuYXRvcl9ncm91cCIsIAogICAgICAgICAgICB0aW1lcyA9IGMoIkRlbSIsICJSX3ZvdGVfZm9yX2RlYmF0ZSIsICJSX3ZvdGVfYWdhaW5zdF9kZWJhdGUiLCAiUl92b3RlX2Zvcl9yZXBlYWxfcmVwbGFjZSIsIlJfdm90ZV9hZ2FpbnN0X3JlcGVhbF9yZXBsYWNlIiwgIlJfdm90ZV9mb3JfcGFydGlhbF9yZXBlYWwiLCAiUl92b3RlX2FnYWluc3RfcGFydGlhbF9yZXBlYWwiLCAiUl92b3RlX2Zvcl9za2lubnlfcmVwZWFsIiwgIlJfdm90ZV9hZ2FpbnN0X3NraW5ueV9yZXBlYWwiKSwgCiAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJsb25nIikKYGBgCk5vdyB0aGUgZGF0YSBpcyBvZiB0aGUgZGVzaXJlZCBsb25nIGZvcm1hdC4KSSBub3cgY2FsbCBteSB0aGVtZSB0aGF0IEkgdXNlIGZvciBjb21pbmcgZ3JhcGhpY3MgaW4gYGdncGxvdDJgLgpgYGB7cn0KbXlfdGhlbWUgPC0gZnVuY3Rpb24oKSB7CgogICMgRGVmaW5lIGNvbG9ycyBmb3IgdGhlIGNoYXJ0CiAgcGFsZXR0ZSA8LSBicmV3ZXIucGFsKCJHcmV5cyIsIG49OSkKICBjb2xvci5iYWNrZ3JvdW5kID0gcGFsZXR0ZVsyXQogIGNvbG9yLmdyaWQubWFqb3IgPSBwYWxldHRlWzRdCiAgY29sb3IucGFuZWwgPSBwYWxldHRlWzNdCiAgY29sb3IuYXhpcy50ZXh0ID0gcGFsZXR0ZVs5XQogIGNvbG9yLmF4aXMudGl0bGUgPSBwYWxldHRlWzldCiAgY29sb3IudGl0bGUgPSBwYWxldHRlWzldCgogICMgQ3JlYXRlIGJhc2ljIGNvbnN0cnVjdGlvbiBvZiBjaGFydAogIHRoZW1lX2J3KGJhc2Vfc2l6ZT05LCBiYXNlX2ZhbWlseT0iUGFsYXRpbm8iKSArIAoKICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBhIGxpZ2h0IGdyYXkgY29sb3IKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsPWNvbG9yLnBhbmVsLCBjb2xvcj1jb2xvci5iYWNrZ3JvdW5kKSkgKwogIHRoZW1lKHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD1jb2xvci5iYWNrZ3JvdW5kLCBjb2xvcj1jb2xvci5iYWNrZ3JvdW5kKSkgKwogIHRoZW1lKHBhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3I9Y29sb3IuYmFja2dyb3VuZCkpICsKCiAgIyBGb3JtYXQgZ3JpZAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG9yPWNvbG9yLmdyaWQubWFqb3Isc2l6ZT0uMjUpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSkgKwoKICAjIEZvcm1hdCBsZWdlbmQKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKwogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9Y29sb3IuYmFja2dyb3VuZCkpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGNvbG9yPWNvbG9yLmF4aXMudGl0bGUpKSArIAogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICAKICAjRm9ybWF0IGZhY2V0IGxhYmVscwogIHRoZW1lKHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgZmFjZT0iYm9sZCIpKSsKCiAgIyBGb3JtYXQgdGl0bGUgYW5kIGF4ZXMgbGFiZWxzIHRoZXNlIGFuZCB0aWNrIG1hcmtzCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoY29sb3I9Y29sb3IudGl0bGUsIHNpemU9MjgpKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkpICsKCiAgI0Zvcm1hdCB0aXRsZSBhbmQgZmFjZXRfd3JhcCB0aXRsZQogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjQsIGNvbG91ciA9ICJibGFjayIsIHZqdXN0ID0gMSwgaGp1c3Q9MC41KSkrCiAgICAKICAjIFBsb3QgbWFyZ2lucwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKC41LCAuMiwgLjIsIDIpLCAiY20iKSkKfQpgYGAKCkxldCdzIG1ha2UgcGxvdHMgdm90ZSBieSB2b3RlLi4uCgojIDEuIFZvdGUgdG8gU3RhcnQgRGViYXRlCgojIyMjIFRoaXMgcGFzc2VkIDUxLTUwIG9uIFR1ZXNkYXksIEp1bHkgMjUsIDIwMTcuIChQZW5jZSBjYXN0IHRoZSB0aWUtYnJla2Fpbmcgdm90ZSkKCldlIHN1YnNldCB0byB0aGUgY29sdW1ucyByZWxldmFudCB0byB0aGVzZSByZXN1bHRzLgpgYGB7cn0KaGVhbHRoY2FyZV9kZWJhdGVfdm90ZTwtaGVhbHRoY2FyZVt3aGljaChoZWFsdGhjYXJlJHNlbmF0b3JfZ3JvdXA9PSJSX3ZvdGVfZm9yX2RlYmF0ZSIgfCBoZWFsdGhjYXJlJHNlbmF0b3JfZ3JvdXA9PSJSX3ZvdGVfYWdhaW5zdF9kZWJhdGUifCBoZWFsdGhjYXJlJHNlbmF0b3JfZ3JvdXAgPT0gIkRlbSIpLF0KYGBgClBsb3QgYW5kIHNhdmUgYXMgYHBuZ2A6CmBgYHtyfQpkZWI8LWdncGxvdChoZWFsdGhjYXJlX2RlYmF0ZV92b3RlLCBhZXMoIiIsIHZvdGVzLCBmaWxsID0gc2VuYXRvcl9ncm91cCkpICsKICBnZW9tX2NvbChhbHBoYSA9IDEsIHdpZHRoID0gMSkgKwogIG15X3RoZW1lKCkrCiAgY29vcmRfZmxpcCgpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWUyIiwgImRhcmtvcmNoaWQyIiwgImZpcmVicmljazIiKSwgYnJlYWtzPWMoIkRlbSIsICJSX3ZvdGVfYWdhaW5zdF9kZWJhdGUiLCAiUl92b3RlX2Zvcl9kZWJhdGUiKSwgbGFiZWxzPWMoIihEKSBOTyAgICAgIiwgIihSKSBOTyAgICAgIiwgIihSKSBZRVMgICAgICIpKSArCiAgZmFjZXRfZ2VvKH4gc3RhdGUsIGdyaWQgPSAidXNfc3RhdGVfZ3JpZDIiLCBsYWJlbD0iY29kZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKwogIGdndGl0bGUoIlNlbmF0ZSBWb3RlcyBWaXN1YWxpemVkOlxuIFZvdGUgdG8gQmVnaW4gRGViYXRlIikrCiAgbGFicyhjYXB0aW9uID0gIkZpbmFsIHZvdGUgY291bnQ6IDUxLTUwIChwYXNzZWQgd2l0aCBQZW5jZSBjYXN0aW5nIHRoZSB0aWUtYnJlYWtpbmcgdm90ZSlcbkJlcm5pZSBTYW5kZXJzIChJLVZUKSBhbmQgQW5ndXMgS2luZyAoSS1NRSkgY2F1Y3VzIHdpdGggdGhlIERlbW9jcmF0c1xuXG5EYXRhIHNvdXJjZTogTllUaW1lcyB8IFZpc3VhbGl6YXRpb24gdmlhIEFsZXggQWxicmlnaHQgKHRoZWxpdHRsZWRhdGFzZXQuY29tKSB8IERDIGVtb2ppIGNob2ljZSB2aWEgSmVzc2UgV2hpdGUiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpKwogIGdnc2F2ZSgiZGViLnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDgwMCkKYGBgCk5vdywgSSB3YW50IHRvIGFkZCBhIHppcHBlciBlbW9qaSB0byBEQydzIHBsb3Qgc3BhY2UsIGFzIERDIGhhcyBubyBzZW5hdG9ycyBidXQgYXBwZWFycyBpbiB0aGUgZ3JhcGguIApgYGB7cn0KIyBOb3cgY2FsbCBiYWNrIHRoZSBwbG90CmJhY2tncm91bmQgPC0gaW1hZ2VfcmVhZCgiZGViLnBuZyIpCgojIEFuZCBicmluZyBpbiBhIHppcHBlciBlbW9qaQp6aXBwZXJfcmF3IDwtIGltYWdlX3JlYWQoInppcHBlci5wbmciKQoKemlwcGVyIDwtIHppcHBlcl9yYXcgJT4lCiAgaW1hZ2Vfc2NhbGUoIjQwMCIpIAoKbmV3IDwtIGltYWdlX2NvbXBvc2l0ZShiYWNrZ3JvdW5kLCB6aXBwZXIsIG9mZnNldCA9ICIrNjY1MCsyODUwIikKaW1hZ2Vfd3JpdGUobmV3LCAiZGViX2ZpbmFsLnBuZyIsIGZsYXR0ZW4gPSBGKQpgYGAKCkhlcmUncyB0aGUgcG5nIHRoYXQgSSBqdXN0IG1hZGU6CiFbaGVyZSBhcmUgdGhlIHZvdGVzXShkZWJfZmluYWwucG5nKQoKIyAyLiBSZXBlYWwgYW5kIHJlcGxhY2UgYW1lbmRtZW50CiMjIyMgVGhlIGBCZXR0ZXIgQ2FyZSBSZWNvbmNpbGlhdGlvbiBBY3RgIGZhaWxlZCBvbiBUdWVzZGF5LCBKdWx5IDI1LCAyMDE3LgoKV2Ugc3Vic2V0IHRvIHRoZSBjb2x1bW5zIHJlbGV2YW50IHRvIHRoZXNlIHJlc3VsdHMuCmBgYHtyfQpoZWFsdGhjYXJlX3JyX3ZvdGU8LWhlYWx0aGNhcmVbd2hpY2goaGVhbHRoY2FyZSRzZW5hdG9yX2dyb3VwPT0iUl92b3RlX2Zvcl9yZXBlYWxfcmVwbGFjZSIgfCBoZWFsdGhjYXJlJHNlbmF0b3JfZ3JvdXA9PSJSX3ZvdGVfYWdhaW5zdF9yZXBlYWxfcmVwbGFjZSJ8IGhlYWx0aGNhcmUkc2VuYXRvcl9ncm91cCA9PSAiRGVtIiksXQpgYGAKUGxvdCBhbmQgc2F2ZToKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJyPC1nZ3Bsb3QoaGVhbHRoY2FyZV9ycl92b3RlLCBhZXMoIiIsIHZvdGVzLCBmaWxsID0gc2VuYXRvcl9ncm91cCkpICsKICBnZW9tX2NvbChhbHBoYSA9IDEsIHdpZHRoID0gMSkgKwogIG15X3RoZW1lKCkrCiAgY29vcmRfZmxpcCgpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWUyIiwgImRhcmtvcmNoaWQyIiwgImZpcmVicmljazIiKSwgYnJlYWtzPWMoIkRlbSIsICJSX3ZvdGVfYWdhaW5zdF9yZXBlYWxfcmVwbGFjZSIsICJSX3ZvdGVfZm9yX3JlcGVhbF9yZXBsYWNlIiksIGxhYmVscz1jKCIoRCkgTk8gICAgICIsICIoUikgTk8gICAgICIsICIoUikgWUVTICAgICAiKSkgKwogIGZhY2V0X2dlbyh+IHN0YXRlLCBncmlkID0gInVzX3N0YXRlX2dyaWQyIiwgbGFiZWw9ImNvZGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICBnZ3RpdGxlKCJTZW5hdGUgVm90ZXMgVmlzdWFsaXplZDpcbiBSZXBlYWwgYW5kIFJlcGxhY2UgQW1lbmRtZW50IikrCiAgbGFicyhjYXB0aW9uID0gIkZpbmFsIHZvdGUgY291bnQ6IDQzLTU3IChmYWlsZWQpXG5CZXJuaWUgU2FuZGVycyAoSS1WVCkgYW5kIEFuZ3VzIEtpbmcgKEktTUUpIGNhdWN1cyB3aXRoIHRoZSBEZW1vY3JhdHNcblxuRGF0YSBzb3VyY2U6IE5ZVGltZXMgfCBWaXN1YWxpemF0aW9uIHZpYSBBbGV4IEFsYnJpZ2h0ICh0aGVsaXR0bGVkYXRhc2V0LmNvbSkgfCBEQyBlbW9qaSBjaG9pY2UgdmlhIEplc3NlIFdoaXRlIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSsKICAgZ2dzYXZlKCJyci5wbmciLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCBkcGkgPSA4MDApCmBgYApTYXZlIHdpdGggREMgZW1vamk6CmBgYHtyfQpiYWNrZ3JvdW5kIDwtIGltYWdlX3JlYWQoInJyLnBuZyIpCm5ldyA8LSBpbWFnZV9jb21wb3NpdGUoYmFja2dyb3VuZCwgemlwcGVyLCBvZmZzZXQgPSAiKzY2NTArMjg1MCIpCmltYWdlX3dyaXRlKG5ldywgInJyX2ZpbmFsLnBuZyIsIGZsYXR0ZW4gPSBGKQpgYGAKCkhlcmUncyB0aGUgcG5nIHRoYXQgSSBqdXN0IG1hZGU6CiFbaGVyZSBhcmUgdGhlIHZvdGVzXShycl9maW5hbC5wbmcpCgojIDMuIFBhcnRpYWwgcmVwZWFsIGFtZW5kbWVudAojIyMjIFRoZSBgT2JhbWFjYXJlIFJlcGVhbCBhbmQgUmVjb25jaWxpYXRpb24gQWN0YCBmYWlsZWQgb24gV2VkbmVzZGF5LCBKdWx5IDI2LCAyMDE3LgoKV2Ugc3Vic2V0IHRvIHRoZSBjb2x1bW5zIHJlbGV2YW50IHRvIHRoZXNlIHJlc3VsdHMuCmBgYHtyfQpoZWFsdGhjYXJlX3ByX3ZvdGU8LWhlYWx0aGNhcmVbd2hpY2goaGVhbHRoY2FyZSRzZW5hdG9yX2dyb3VwPT0iUl92b3RlX2Zvcl9wYXJ0aWFsX3JlcGVhbCIgfCBoZWFsdGhjYXJlJHNlbmF0b3JfZ3JvdXA9PSJSX3ZvdGVfYWdhaW5zdF9wYXJ0aWFsX3JlcGVhbCJ8IGhlYWx0aGNhcmUkc2VuYXRvcl9ncm91cCA9PSAiRGVtIiksXQpgYGAKUGxvdCBhbmQgc2F2ZToKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByPC1nZ3Bsb3QoaGVhbHRoY2FyZV9wcl92b3RlLCBhZXMoIiIsIHZvdGVzLCBmaWxsID0gc2VuYXRvcl9ncm91cCkpICsKICBnZW9tX2NvbChhbHBoYSA9IDEsIHdpZHRoID0gMSkgKwogIG15X3RoZW1lKCkrCiAgY29vcmRfZmxpcCgpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWUyIiwgImRhcmtvcmNoaWQyIiwgImZpcmVicmljazIiKSwgYnJlYWtzPWMoIkRlbSIsICJSX3ZvdGVfYWdhaW5zdF9wYXJ0aWFsX3JlcGVhbCIsICJSX3ZvdGVfZm9yX3BhcnRpYWxfcmVwZWFsIiksIGxhYmVscz1jKCIoRCkgTk8gICAgICIsICIoUikgTk8gICAgICIsICIoUikgWUVTICAgICAiKSkgKwogIGZhY2V0X2dlbyh+IHN0YXRlLCBncmlkID0gInVzX3N0YXRlX2dyaWQyIiwgbGFiZWw9ImNvZGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICBnZ3RpdGxlKCJTZW5hdGUgVm90ZXMgVmlzdWFsaXplZDpcbiBQYXJ0aWFsIFJlcGVhbCBBbWVuZG1lbnQiKSsKICBsYWJzKGNhcHRpb24gPSAiRmluYWwgdm90ZSBjb3VudDogNDUtNTUgKGZhaWxlZClcbkJlcm5pZSBTYW5kZXJzIChJLVZUKSBhbmQgQW5ndXMgS2luZyAoSS1NRSkgY2F1Y3VzIHdpdGggdGhlIERlbW9jcmF0c1xuXG5EYXRhIHNvdXJjZTogTllUaW1lcyB8IFZpc3VhbGl6YXRpb24gdmlhIEFsZXggQWxicmlnaHQgKHRoZWxpdHRsZWRhdGFzZXQuY29tKSB8IERDIGVtb2ppIGNob2ljZSB2aWEgSmVzc2UgV2hpdGUiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpKwogICBnZ3NhdmUoInByLnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDgwMCkKYGBgClNhdmUgd2l0aCBEQyBlbW9qaToKYGBge3J9CmJhY2tncm91bmQgPC0gaW1hZ2VfcmVhZCgicHIucG5nIikKbmV3IDwtIGltYWdlX2NvbXBvc2l0ZShiYWNrZ3JvdW5kLCB6aXBwZXIsIG9mZnNldCA9ICIrNjY1MCsyODUwIikKaW1hZ2Vfd3JpdGUobmV3LCAicHJfZmluYWwucG5nIiwgZmxhdHRlbiA9IEYpCmBgYApIZXJlJ3MgdGhlIHBuZyB0aGF0IEkganVzdCBtYWRlOgohW2hlcmUgYXJlIHRoZSB2b3Rlc10ocHJfZmluYWwucG5nKQoKIyA0LiAnU2tpbm55JyByZXBlYWwgYW1lbmRtZW50CiMjIyMgVGhlIGBIZWFsdGggQ2FyZSBGcmVlZG9tIEFjdGAgZmFpbGVkIG9uIEZyaWRheSwgSnVseSAyOCwgMjAxNy4KCldlIHN1YnNldCB0byB0aGUgY29sdW1ucyByZWxldmFudCB0byB0aGVzZSByZXN1bHRzLgpgYGB7cn0KaGVhbHRoY2FyZV9za192b3RlPC1oZWFsdGhjYXJlW3doaWNoKGhlYWx0aGNhcmUkc2VuYXRvcl9ncm91cD09IlJfdm90ZV9mb3Jfc2tpbm55X3JlcGVhbCIgfCBoZWFsdGhjYXJlJHNlbmF0b3JfZ3JvdXA9PSJSX3ZvdGVfYWdhaW5zdF9za2lubnlfcmVwZWFsInwgaGVhbHRoY2FyZSRzZW5hdG9yX2dyb3VwID09ICJEZW0iKSxdCmBgYApQbG90IGFuZCBzYXZlOgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc2s8LWdncGxvdChoZWFsdGhjYXJlX3NrX3ZvdGUsIGFlcygiIiwgdm90ZXMsIGZpbGwgPSBzZW5hdG9yX2dyb3VwKSkgKwogIGdlb21fY29sKGFscGhhID0gMSwgd2lkdGggPSAxKSArCiAgbXlfdGhlbWUoKSsKICBjb29yZF9mbGlwKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZG9kZ2VyYmx1ZTIiLCAiZGFya29yY2hpZDIiLCAiZmlyZWJyaWNrMiIpLCBicmVha3M9YygiRGVtIiwgIlJfdm90ZV9hZ2FpbnN0X3NraW5ueV9yZXBlYWwiLCAiUl92b3RlX2Zvcl9za2lubnlfcmVwZWFsIiksIGxhYmVscz1jKCIoRCkgTk8gICAgICIsICIoUikgTk8gICAgICIsICIoUikgWUVTICAgICAiKSkgKwogIGZhY2V0X2dlbyh+IHN0YXRlLCBncmlkID0gInVzX3N0YXRlX2dyaWQyIiwgbGFiZWwgPSAiY29kZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKwogIGdndGl0bGUoIlNlbmF0ZSBWb3RlcyBWaXN1YWxpemVkOlxuICdTa2lubnknIFJlcGVhbCBBbWVuZG1lbnQiKSsKICBsYWJzKGNhcHRpb24gPSAiRmluYWwgdm90ZSBjb3VudDogNDktNTEgKGZhaWxlZClcbkJlcm5pZSBTYW5kZXJzIChJLVZUKSBhbmQgQW5ndXMgS2luZyAoSS1NRSkgY2F1Y3VzIHdpdGggdGhlIERlbW9jcmF0c1xuXG5EYXRhIHNvdXJjZTogTllUaW1lcyB8IFZpc3VhbGl6YXRpb24gdmlhIEFsZXggQWxicmlnaHQgKHRoZWxpdHRsZWRhdGFzZXQuY29tKSB8IERDIGVtb2ppIGNob2ljZSB2aWEgSmVzc2UgV2hpdGUiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpKwogICBnZ3NhdmUoInNrLnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDgwMCkKYGBgCgpTYXZlIHdpdGggREMgZW1vamk6CmBgYHtyfQpiYWNrZ3JvdW5kIDwtIGltYWdlX3JlYWQoInNrLnBuZyIpCm5ldyA8LSBpbWFnZV9jb21wb3NpdGUoYmFja2dyb3VuZCwgemlwcGVyLCBvZmZzZXQgPSAiKzY2NTArMjg1MCIpCmltYWdlX3dyaXRlKG5ldywgInNrX2ZpbmFsLnBuZyIsIGZsYXR0ZW4gPSBGKQpgYGAKCkhlcmUncyB0aGUgcG5nIHRoYXQgSSBqdXN0IG1hZGU6CiFbaGVyZSBhcmUgdGhlIHZvdGVzXShza19maW5hbC5wbmcpCgojIyBXZSBoYXZlIG5vdyBjcmVhdGVkIGBwbmdgIHBsb3RzIGZvciBlYWNoIG9mIHRoZSBmb3VyIHJlbGV2YW50IHZvdGVzIQ==