digest_guide <-tribble(~item, ~initial_conc, ~initial_vol_ul, 
                   "psti_100000u/ml_ul", 100000, (params$enzyme_units/50*params$digest_rxn_size)/(100),
                   "mluci_10000u/mL_ul", 10000, (params$enzyme_units/50*params$digest_rxn_size)/(10),
                   "10x_buffer_ul", 10, params$digest_rxn_size/10,
                   "ph2o_ul", 0, 0) %>% 
  mutate(final_vol_ul = params$digest_rxn_size,
         initial_vol_ul = ifelse(item == "ph2o_ul",final_vol_ul - sum(initial_vol_ul, na.rm = T), initial_vol_ul))

digest_dna <- digest_guide %>% 
  select(item, initial_vol_ul) %>% 
  rename(quantity = initial_vol_ul) %>% 
  mutate(item = ifelse(item == "ph2o_ul", "ph2o_ml", item), 
         quantity = ifelse(item == "ph2o_ml", quantity/1000, quantity)) %>% 
  bind_rows(tribble(~item, ~quantity,
                     ifelse(params$num_samples <20, "tubes_200ul", "plates"), ifelse(params$num_samples < 20, params$num_samples, ceiling(params$num_samples/96)),
            "tips_10", 3,
            "tips_100", 1,
            "tips_100", params$num_samples))
rm(digest_guide)

Copy and paste this file into the lab-notebooks folder, changing the name to match the sample range you are currently working on.

Explanation of parameters:

 (params) 

first: the first extraction in the range of samples for this plate last: the last extraciton in the range of samples for this plate digest_rxn_size: the size in uL of each digest reaction.

This protocol assumes that you have read and understand the manufacturer’s instructions attached below. Please read the full manufacturer’s instructions before using this abbreviated protocol.

## Prep for digest The most difficult part of the digest is figuring out where to put the samples. In most cases, it is very straightforward. You pull the sample from the extraction plate and place it in the same well on the digest plate. For example, if an extract is in A4 of the extraction plate, run the digestion reaction in well A4 of the digest plate.

If this is the case, the source plate map would look like this:

 need_digest <- lab %>% tbl("extraction") %>% collect() %>%  
   # hasn't been digested 
   filter(extraction_id >= params$first & extraction_id <= params$last) %>%  
   # select columns 
   select(extraction_id, well, plate, quant) %>%  
   # need enough material to work with 
   filter(quant > 5) %>%  
   # sort by extraction id 
   arrange(extraction_id) %>%  
   # create row and column ids from well 
   mutate(row = substr(well, 1,1),  
          col = as.numeric(substr(well, 2,3))) %>%  
   select(row, col, extraction_id) 

 # define wells 
 plate <- data.frame(row = rep(LETTERS[1:8], 12), col = unlist(lapply(1:12, rep, 8))) %>%  
   mutate(extraction_id = ifelse(row == "D" & col == 2, "XXXX", NA),  
     extraction_id = ifelse(row == "E" & col == 8, "XXXX", extraction_id)) 

 # if the plate is not full 
 x <- nrow(need_digest) + 2 
 if (x < 96) { 
   plate <- plate %>%  
     slice(1:x) 
 } 

 # remove the non-blank wells from the plate table 
 plate <- anti_join(plate, need_digest, by = c("row", "col")) 

 # rejoin the samples into the plate with the blanks, resulting in a plate of defined wells for samples and blanks. 
 plate <- rbind(plate, need_digest) %>%  
   arrange(col, row) 

 platemap <- as.matrix(reshape2::acast(plate, plate$row ~ plate$col), value.var = plate$extraction) 
 knitr::kable(platemap, booktabs = T) %>%  
   # use scale_down to get map to fit within the bounds of the pdf 
   kable_styling(latex_options = "scale_down") 

However, sometimes we skip some samples that did not perform well, or we want to digest a bunch of extracts from random plates. If it is not a simple plate to plate transfer, the source plate map would look like this:

 digested <- lab %>% tbl("digest") %>% collect() 
 # TODO: should filter this for successful digests 

   need_digest <- lab %>% tbl("extraction") %>% collect() %>%  
   # hasn't been digested 
   filter(!extraction_id %in% digested$extraction_id) %>%  
   # select columns 
   select(extraction_id, well, plate, quant) %>%  
   # need enough material to work with 
   filter(quant > 5) %>%  
   # needs to be in a plate 
   filter(!is.na(plate)) %>%  
   # sort by extraction id 
   arrange(extraction_id) 

   # define wells 
 plate <- data.frame(row = rep(LETTERS[1:8], 12), col = unlist(lapply(1:12, rep, 8))) %>%  
   mutate(extraction_id = ifelse(row == "D" & col == 2, "XXXX", NA),  
     extraction_id = ifelse(row == "E" & col == 8, "XXXX", extraction_id)) 

 # if the plate is not full 
 x <- nrow(need_digest) + 2 
 if (x < 96) { 
   if(x < 48){ 
     plate <- plate %>%  
       slice(1:x-1) 
   }else{ 
     plate <- plate %>%  
     slice(1:x) 
   } 
 } 

 # move all of the non-blank wells into their own table 
 samples <- plate %>%  
   filter(is.na(extraction_id)) %>%  
   select(-extraction_id) 

 # remove the non-blank wells from the plate table 
 plate <- anti_join(plate, samples, by = c("row", "col")) 

 # join the work sample_ids to the sample table well definitions 
 samples <- cbind(samples, need_digest) %>%  
   select(row, col, extraction_id) 

 # rejoin the samples into the plate with the blanks, resulting in a plate of defined wells for samples and blanks. 
 plate <- rbind(plate, samples) %>%  
   arrange(col, row) 

 source_map <- as.matrix(reshape2::acast(plate, plate$row ~ plate$col), value.var = plate$extraction) 
 knitr::kable(source_map, booktabs = T) %>%  
   # use scale_down to get map to fit within the bounds of the pdf 
   kable_styling(latex_options = "scale_down") 

A digest id must be created for these samples.
Only do this step once!
It creates new digest_ids for the database.
The destination plate map would look like this:

 ### ONLY DO THIS ONCE ### 
 dig_max <- lab %>% tbl("digest") %>%  
   summarise(last = max(digest_id, na.rm = T)) %>%  
   collect() %>%  
   mutate(last = as.numeric(substr(last, 2,5))) 

 x <- dig_max$last+1 
 y <- dig_max$last+nrow(plate) 
 id_range <- x:y 

 plate <- plate %>%  
   mutate(digest_id = paste0("D", id_range), 
          well = paste(row, col, sep = ""),  
          enzymes = "PstI-MluCI", 
          vol_in = 30, 
          final_vol = 45) 

 plate_name <- plate %>%  
   summarise(first = min(digest_id),  
     last = max(digest_id, na.rm = T)) 

 dest_map <- plate %>%  
   mutate(plate = paste(plate_name$first, plate_name$last, sep = "-")) %>%  
   select(row, col, digest_id) 

 dest_map <- as.matrix(reshape2::acast(dest_map, dest_map$row ~ dest_map$col), value.var = dest_map$digest_id) 

 knitr::kable(dest_map, booktabs = T) %>%  
   kable_styling() 

Print out plate maps and highlight source maps for ease of loading. Make sure to note any samples that are heavily concentrated and need to be diluted.

Prepare digest plate by loading 30uL of sample in to the plate.

For the October 2017 set of plates, I highlighted the 15uL samples on the maps in orange and the “hole-fillers” in pink, then any samples I could take with a multichannel with a blue line down the column.

To load, I first used a single channel pipet to fill in the 15uL samples (extra concentrated samples), pulling the tips from the same location they were on the plate. Then I filled in the water for the 15uL wells and change the pipet to 30uL to fill the blanks. Then I added any 30uL “hole fillers” - again using the tips from the same location in the tip box.

At this point, any empty wells on the plate correspond to tips in the tip box. I used the multichannel pipet to pick up the tips and held them up to the plate to make sure no tips were going into already filled wells, and no empty wells were being left empty. Then I filled the rest of the plate.

## Make the master mix recipe in a falcon tube or smaller tube if possible:

 num_samples <- nrow(plate) 

 # make some room for error 15% 
 samples_with_error <- floor(num_samples * 1.15) 

 # use a total of 10 enzyme Units per 50uL reaction, 5 of each enzyme. 

 digest_rxn <- read_csv("item, concentration, per_reaction 
                        sample, NA, NA 
                        enzyme1, NA, NA 
                        enzyme2, NA, NA 
                        buffer, NA, NA 
                        pH2O, NA, NA") %>%  
   mutate(per_reaction = ifelse(item == "sample", params$digest_rxn_size/1.6666, per_reaction),  
          concentration = ifelse(item =="enzyme1", 100000, concentration), 
          per_reaction = ifelse(item == "enzyme1", (params$digest_rxn_size)/10/(concentration/1000), per_reaction), 
          concentration = ifelse(item =="enzyme2", 10000, concentration), 
           per_reaction = ifelse(item == "enzyme2", (params$digest_rxn_size)/10/(concentration/1000), per_reaction), 
          concentration = ifelse(item =="buffer", 10, concentration), 
           per_reaction = ifelse(item == "buffer", params$digest_rxn_size/concentration, per_reaction)) 

 pH2O <- digest_rxn %>%  
   summarise(this = params$digest_rxn_size-sum(per_reaction, na.rm=T)) 

 digest_rxn <- digest_rxn %>%  
   mutate(per_reaction = ifelse(item == "pH2O", pH2O$this, per_reaction), 
          master_mix = per_reaction * samples_with_error,  
          master_mix = ifelse(item == "sample", NA, master_mix)) 

 # double check the math - should return TRUE 
 (params$digest_rxn_size*samples_with_error)-(params$digest_rxn_size*.6*samples_with_error) == round(sum(digest_rxn$master_mix, na.rm=T)) 

 print(paste0("Total master mix volume is ", round(sum(digest_rxn$master_mix, na.rm=T)), "mL")) 
 kable_styling(kable(digest_rxn)) 
 * split the master mix into 8 wells, use a multichannel pipet to pipet 12µL of master mix into each sample well.  
 print(paste0("Split into 8 wells by pipetting ", (round(sum(digest_rxn$master_mix, na.rm=T)))/8, "ul into each well")) 

# Supplies needed plus the volumes on the master mix recipe.

 digest <- tibble( 
   plates = num_samples/96, 
   tubes = num_samples/96,  
   plate_seals = num_samples/96,  
   tips_100 = num_samples * 2 + (ceiling(digest_rxn$master_mix[3]/100)),# referring to enzyme2 
   tips_10 = ceiling(digest_rxn$master_mix[2]/10), # referring to enzyme1 
   tips_300 = ceiling(digest_rxn$master_mix[4]/100), # referring to buffer 
   tips_1000 = ceiling(digest_rxn$master_mix[5]/1000) # referring to pH2O 
   ) 
     * Do not add the master mix to all of the wells and then add the sample.  Because you are working with active enzymes, they should be the last things added to the master mix and the last step of plate preparation. 

Clean the digests using 1.5x the volume of the digest (75µL) Ampure per sample. Put the plates on yellow lifts on the magnet so that the pellet is lower before you finish the alcohol steps. Because these are in plates and it is difficult to get all of the elute out of the well, elute in 45µL of buffer and pull 40µL from the well to place in a new cleaned digest plate. Update the database. - Ampure clean up protocol

Quantifiy - quant-it pico green - import the quantification results into the Sample_Data spreadsheet, Digests Page quant column. For any sample with a negative quantification value, change the Ligated? column value to N/A (the rest should contain a formula that results in the value “FALSE”). Also, add an “_F” to the extraction ID (not the digest ID) in the extraction ID column (example: PADE14_093E1516 becomes PADE14_093E1516_F). This digest failed and so the sample should not reflect that a successful digest exits.

 * More decision making is necessary at this point.  If an extract has less than 5ng/ul DNA, it will most likely fail to digest well.  This is a tough call, some digests with much less than 5ng/ul have ligated successfully and others have not.  Proceed with caution. If there are plenty of samples to choose from, choose the desired number with the most “sample_ng_in”. 
     * As of 8/19/2014  
         * any extraction less than 5ng/µL we are putting on the back burner to continue processing at a later date 
         * anything that is more than 5ng/µL but makes less than 1µg of DNA when you multiply the quant result by 30µL (the digest addition amount) - calculate the amount of extract to make 1µg DNA and clean that amount, elute in 30µL and add to digest plate  
         * As of 11/2014 we are not worried about “too low” DNA and are not cleaning samples that are below 1000ng.  We have successfully digested down to 100ng.   
         * anything that makes more than 5µg of DNA when you multiply the quant result by 30µL - add 15µL of extract to plate and 15µL pH2O - do the math to make sure this dilution will still be less than 5µg. 
 * For the time being, enter the ExtractID in the Digest ID column for ease of sorting (it is easiest to work on extract plates in sample order) - do this by entering the formula =right(B1882,5) in column A 
 * Sort by Extract Id (no digest number yet, will sort by sample number), only the range of this plate!!! 
 * Fill in date, enzyme, Digest# and replace the Extract# in the Digest_ID column with the formula to concatenate a digest ID. 
 * print a map of your extraction plate and new digest plate 

Optimizing Restriction Endonuclease Reactions | New England Biolabs For a 50µL rxn, use:

1µg DNA - fill to volume w/ pH2O 5µL 10x buffer (50µL/10x=5)

Want a total of 10U in rxn For single digest: if enzyme is 10,000U/mL - 1µL = 10U if enzyme is 20,000U/mL - 0.5µL = 10U

For double digest if enzyme is 10,000U/mL - 0.5µL = 5U if enzyme is 20,000U/mL - 0.25µL = 5U

PstI - cutsite is https://www.neb.com/products/r0140-psti

MluCI - cutsite is https://www.neb.com/products/r0538-mluci

EcoRI cutsite is https://www.neb.com/products/r0101-ecori

LS0tCnRpdGxlOiAiRGlnZXN0IE1ldGhvZCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGRmX3ByaW50OiBwYWdlZApwYXJhbXM6CiAgZmlyc3Q6IGZpcnN0CiAgbGFzdDogbGFzdAogIGRpZ2VzdF9yeG5fc2l6ZTogNTAKICBudW1fZG5hX3NhbXBsZXM6IDk2CiAgZW56eW1lX3VuaXRzOiA1Ci0tLQpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7cn0KZGlnZXN0X2d1aWRlIDwtdHJpYmJsZSh+aXRlbSwgfmluaXRpYWxfY29uYywgfmluaXRpYWxfdm9sX3VsLCAKICAgICAgICAgICAgICAgICAgICJwc3RpXzEwMDAwMHUvbWxfdWwiLCAxMDAwMDAsIChwYXJhbXMkZW56eW1lX3VuaXRzLzUwKnBhcmFtcyRkaWdlc3RfcnhuX3NpemUpLygxMDApLAogICAgICAgICAgICAgICAgICAgIm1sdWNpXzEwMDAwdS9tTF91bCIsIDEwMDAwLCAocGFyYW1zJGVuenltZV91bml0cy81MCpwYXJhbXMkZGlnZXN0X3J4bl9zaXplKS8oMTApLAogICAgICAgICAgICAgICAgICAgIjEweF9idWZmZXJfdWwiLCAxMCwgcGFyYW1zJGRpZ2VzdF9yeG5fc2l6ZS8xMCwKICAgICAgICAgICAgICAgICAgICJwaDJvX3VsIiwgMCwgMCkgJT4lIAogIG11dGF0ZShmaW5hbF92b2xfdWwgPSBwYXJhbXMkZGlnZXN0X3J4bl9zaXplLAogICAgICAgICBpbml0aWFsX3ZvbF91bCA9IGlmZWxzZShpdGVtID09ICJwaDJvX3VsIixmaW5hbF92b2xfdWwgLSBzdW0oaW5pdGlhbF92b2xfdWwsIG5hLnJtID0gVCksIGluaXRpYWxfdm9sX3VsKSkKCmRpZ2VzdF9kbmEgPC0gZGlnZXN0X2d1aWRlICU+JSAKICBzZWxlY3QoaXRlbSwgaW5pdGlhbF92b2xfdWwpICU+JSAKICByZW5hbWUocXVhbnRpdHkgPSBpbml0aWFsX3ZvbF91bCkgJT4lIAogIG11dGF0ZShpdGVtID0gaWZlbHNlKGl0ZW0gPT0gInBoMm9fdWwiLCAicGgyb19tbCIsIGl0ZW0pLCAKICAgICAgICAgcXVhbnRpdHkgPSBpZmVsc2UoaXRlbSA9PSAicGgyb19tbCIsIHF1YW50aXR5LzEwMDAsIHF1YW50aXR5KSkgJT4lIAogIGJpbmRfcm93cyh0cmliYmxlKH5pdGVtLCB+cXVhbnRpdHksCiAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwYXJhbXMkbnVtX3NhbXBsZXMgPDIwLCAidHViZXNfMjAwdWwiLCAicGxhdGVzIiksIGlmZWxzZShwYXJhbXMkbnVtX3NhbXBsZXMgPCAyMCwgcGFyYW1zJG51bV9zYW1wbGVzLCBjZWlsaW5nKHBhcmFtcyRudW1fc2FtcGxlcy85NikpLAogICAgICAgICAgICAidGlwc18xMCIsIDMsCiAgICAgICAgICAgICJ0aXBzXzEwMCIsIDEsCiAgICAgICAgICAgICJ0aXBzXzEwMCIsIHBhcmFtcyRudW1fc2FtcGxlcykpCnJtKGRpZ2VzdF9ndWlkZSkKYGBgCgogKkNvcHkgYW5kIHBhc3RlIHRoaXMgZmlsZSBpbnRvIHRoZSBsYWItbm90ZWJvb2tzIGZvbGRlciwgY2hhbmdpbmcgdGhlIG5hbWUgdG8gbWF0Y2ggdGhlIHNhbXBsZSByYW5nZSB5b3UgYXJlIGN1cnJlbnRseSB3b3JraW5nIG9uLiogICAKCiBFeHBsYW5hdGlvbiBvZiBwYXJhbWV0ZXJzOiAKYGBge3J9IAogKHBhcmFtcykgCmBgYCAKIGZpcnN0OiB0aGUgZmlyc3QgZXh0cmFjdGlvbiBpbiB0aGUgcmFuZ2Ugb2Ygc2FtcGxlcyBmb3IgdGhpcyBwbGF0ZSAKIGxhc3Q6IHRoZSBsYXN0IGV4dHJhY2l0b24gaW4gdGhlIHJhbmdlIG9mIHNhbXBsZXMgZm9yIHRoaXMgcGxhdGUgCiBkaWdlc3RfcnhuX3NpemU6IHRoZSBzaXplIGluIHVMIG9mIGVhY2ggZGlnZXN0IHJlYWN0aW9uLiAKCgoKICoqVGhpcyBwcm90b2NvbCBhc3N1bWVzIHRoYXQgeW91IGhhdmUgcmVhZCBhbmQgdW5kZXJzdGFuZCB0aGUgbWFudWZhY3R1cmVy4oCZcyBpbnN0cnVjdGlvbnMgYXR0YWNoZWQgYmVsb3cuICBQbGVhc2UgcmVhZCB0aGUgZnVsbCBtYW51ZmFjdHVyZXLigJlzIGluc3RydWN0aW9ucyBiZWZvcmUgdXNpbmcgdGhpcyBhYmJyZXZpYXRlZCBwcm90b2NvbC4qKiAKCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9IAoga25pdHI6Om9wdHNfY2h1bmskc2V0KCAKIAllY2hvID0gRkFMU0UsIAogCW1lc3NhZ2UgPSBGQUxTRSwgCiAJd2FybmluZyA9IEZBTFNFIAogKSAKICMgbG9hZCBsaWJyYXJpZXMgCiBsaWJyYXJ5KHRpZHl2ZXJzZSkgCiBsaWJyYXJ5KGthYmxlRXh0cmEpIAogbGlicmFyeShrbml0cikgCgogIyBsb2FkIGZ1bmN0aW9ucyAKIHNvdXJjZSgifi9kYi1jb25uZWN0aW9ucy5SIikgCgogIyBsb2FkIGRhdGEgCiBsYWIgPC0gcmVhZF9kYigiTGFib3JhdG9yeSIpIApgYGAgCgoKICMjIFByZXAgZm9yIGRpZ2VzdCAKIFRoZSBtb3N0IGRpZmZpY3VsdCBwYXJ0IG9mIHRoZSBkaWdlc3QgaXMgZmlndXJpbmcgb3V0IHdoZXJlIHRvIHB1dCB0aGUgc2FtcGxlcy4gIEluIG1vc3QgY2FzZXMsIGl0IGlzIHZlcnkgc3RyYWlnaHRmb3J3YXJkLiAgWW91IHB1bGwgdGhlIHNhbXBsZSBmcm9tIHRoZSBleHRyYWN0aW9uIHBsYXRlIGFuZCBwbGFjZSBpdCBpbiB0aGUgc2FtZSB3ZWxsIG9uIHRoZSBkaWdlc3QgcGxhdGUuICBGb3IgZXhhbXBsZSwgaWYgYW4gZXh0cmFjdCBpcyBpbiBBNCBvZiB0aGUgZXh0cmFjdGlvbiBwbGF0ZSwgcnVuIHRoZSBkaWdlc3Rpb24gcmVhY3Rpb24gaW4gd2VsbCBBNCBvZiB0aGUgZGlnZXN0IHBsYXRlLiAgIAoKIElmIHRoaXMgaXMgdGhlIGNhc2UsIHRoZSBzb3VyY2UgcGxhdGUgbWFwIHdvdWxkIGxvb2sgbGlrZSB0aGlzOiAKYGBge3J9IAogbmVlZF9kaWdlc3QgPC0gbGFiICU+JSB0YmwoImV4dHJhY3Rpb24iKSAlPiUgY29sbGVjdCgpICU+JSAgCiAgICMgaGFzbid0IGJlZW4gZGlnZXN0ZWQgCiAgIGZpbHRlcihleHRyYWN0aW9uX2lkID49IHBhcmFtcyRmaXJzdCAmIGV4dHJhY3Rpb25faWQgPD0gcGFyYW1zJGxhc3QpICU+JSAgCiAgICMgc2VsZWN0IGNvbHVtbnMgCiAgIHNlbGVjdChleHRyYWN0aW9uX2lkLCB3ZWxsLCBwbGF0ZSwgcXVhbnQpICU+JSAgCiAgICMgbmVlZCBlbm91Z2ggbWF0ZXJpYWwgdG8gd29yayB3aXRoIAogICBmaWx0ZXIocXVhbnQgPiA1KSAlPiUgIAogICAjIHNvcnQgYnkgZXh0cmFjdGlvbiBpZCAKICAgYXJyYW5nZShleHRyYWN0aW9uX2lkKSAlPiUgIAogICAjIGNyZWF0ZSByb3cgYW5kIGNvbHVtbiBpZHMgZnJvbSB3ZWxsIAogICBtdXRhdGUocm93ID0gc3Vic3RyKHdlbGwsIDEsMSksICAKICAgICAgICAgIGNvbCA9IGFzLm51bWVyaWMoc3Vic3RyKHdlbGwsIDIsMykpKSAlPiUgIAogICBzZWxlY3Qocm93LCBjb2wsIGV4dHJhY3Rpb25faWQpIAoKICMgZGVmaW5lIHdlbGxzIAogcGxhdGUgPC0gZGF0YS5mcmFtZShyb3cgPSByZXAoTEVUVEVSU1sxOjhdLCAxMiksIGNvbCA9IHVubGlzdChsYXBwbHkoMToxMiwgcmVwLCA4KSkpICU+JSAgCiAgIG11dGF0ZShleHRyYWN0aW9uX2lkID0gaWZlbHNlKHJvdyA9PSAiRCIgJiBjb2wgPT0gMiwgIlhYWFgiLCBOQSksICAKICAgICBleHRyYWN0aW9uX2lkID0gaWZlbHNlKHJvdyA9PSAiRSIgJiBjb2wgPT0gOCwgIlhYWFgiLCBleHRyYWN0aW9uX2lkKSkgCgogIyBpZiB0aGUgcGxhdGUgaXMgbm90IGZ1bGwgCiB4IDwtIG5yb3cobmVlZF9kaWdlc3QpICsgMiAKIGlmICh4IDwgOTYpIHsgCiAgIHBsYXRlIDwtIHBsYXRlICU+JSAgCiAgICAgc2xpY2UoMTp4KSAKIH0gCgogIyByZW1vdmUgdGhlIG5vbi1ibGFuayB3ZWxscyBmcm9tIHRoZSBwbGF0ZSB0YWJsZSAKIHBsYXRlIDwtIGFudGlfam9pbihwbGF0ZSwgbmVlZF9kaWdlc3QsIGJ5ID0gYygicm93IiwgImNvbCIpKSAKCiAjIHJlam9pbiB0aGUgc2FtcGxlcyBpbnRvIHRoZSBwbGF0ZSB3aXRoIHRoZSBibGFua3MsIHJlc3VsdGluZyBpbiBhIHBsYXRlIG9mIGRlZmluZWQgd2VsbHMgZm9yIHNhbXBsZXMgYW5kIGJsYW5rcy4gCiBwbGF0ZSA8LSByYmluZChwbGF0ZSwgbmVlZF9kaWdlc3QpICU+JSAgCiAgIGFycmFuZ2UoY29sLCByb3cpIAoKIHBsYXRlbWFwIDwtIGFzLm1hdHJpeChyZXNoYXBlMjo6YWNhc3QocGxhdGUsIHBsYXRlJHJvdyB+IHBsYXRlJGNvbCksIHZhbHVlLnZhciA9IHBsYXRlJGV4dHJhY3Rpb24pIAoga25pdHI6OmthYmxlKHBsYXRlbWFwLCBib29rdGFicyA9IFQpICU+JSAgCiAgICMgdXNlIHNjYWxlX2Rvd24gdG8gZ2V0IG1hcCB0byBmaXQgd2l0aGluIHRoZSBib3VuZHMgb2YgdGhlIHBkZiAKICAga2FibGVfc3R5bGluZyhsYXRleF9vcHRpb25zID0gInNjYWxlX2Rvd24iKSAKCgpgYGAKCgoKIEhvd2V2ZXIsIHNvbWV0aW1lcyB3ZSBza2lwIHNvbWUgc2FtcGxlcyB0aGF0IGRpZCBub3QgcGVyZm9ybSB3ZWxsLCBvciB3ZSB3YW50IHRvIGRpZ2VzdCBhIGJ1bmNoIG9mIGV4dHJhY3RzIGZyb20gcmFuZG9tIHBsYXRlcy4gIElmIGl0IGlzIG5vdCBhIHNpbXBsZSBwbGF0ZSB0byBwbGF0ZSB0cmFuc2ZlciwgdGhlIHNvdXJjZSBwbGF0ZSBtYXAgd291bGQgbG9vayBsaWtlIHRoaXM6IApgYGB7cn0gCiBkaWdlc3RlZCA8LSBsYWIgJT4lIHRibCgiZGlnZXN0IikgJT4lIGNvbGxlY3QoKSAKICMgVE9ETzogc2hvdWxkIGZpbHRlciB0aGlzIGZvciBzdWNjZXNzZnVsIGRpZ2VzdHMgCgogICBuZWVkX2RpZ2VzdCA8LSBsYWIgJT4lIHRibCgiZXh0cmFjdGlvbiIpICU+JSBjb2xsZWN0KCkgJT4lICAKICAgIyBoYXNuJ3QgYmVlbiBkaWdlc3RlZCAKICAgZmlsdGVyKCFleHRyYWN0aW9uX2lkICVpbiUgZGlnZXN0ZWQkZXh0cmFjdGlvbl9pZCkgJT4lICAKICAgIyBzZWxlY3QgY29sdW1ucyAKICAgc2VsZWN0KGV4dHJhY3Rpb25faWQsIHdlbGwsIHBsYXRlLCBxdWFudCkgJT4lICAKICAgIyBuZWVkIGVub3VnaCBtYXRlcmlhbCB0byB3b3JrIHdpdGggCiAgIGZpbHRlcihxdWFudCA+IDUpICU+JSAgCiAgICMgbmVlZHMgdG8gYmUgaW4gYSBwbGF0ZSAKICAgZmlsdGVyKCFpcy5uYShwbGF0ZSkpICU+JSAgCiAgICMgc29ydCBieSBleHRyYWN0aW9uIGlkIAogICBhcnJhbmdlKGV4dHJhY3Rpb25faWQpIAoKICAgIyBkZWZpbmUgd2VsbHMgCiBwbGF0ZSA8LSBkYXRhLmZyYW1lKHJvdyA9IHJlcChMRVRURVJTWzE6OF0sIDEyKSwgY29sID0gdW5saXN0KGxhcHBseSgxOjEyLCByZXAsIDgpKSkgJT4lICAKICAgbXV0YXRlKGV4dHJhY3Rpb25faWQgPSBpZmVsc2Uocm93ID09ICJEIiAmIGNvbCA9PSAyLCAiWFhYWCIsIE5BKSwgIAogICAgIGV4dHJhY3Rpb25faWQgPSBpZmVsc2Uocm93ID09ICJFIiAmIGNvbCA9PSA4LCAiWFhYWCIsIGV4dHJhY3Rpb25faWQpKSAKCiAjIGlmIHRoZSBwbGF0ZSBpcyBub3QgZnVsbCAKIHggPC0gbnJvdyhuZWVkX2RpZ2VzdCkgKyAyIAogaWYgKHggPCA5NikgeyAKICAgaWYoeCA8IDQ4KXsgCiAgICAgcGxhdGUgPC0gcGxhdGUgJT4lICAKICAgICAgIHNsaWNlKDE6eC0xKSAKICAgfWVsc2V7IAogICAgIHBsYXRlIDwtIHBsYXRlICU+JSAgCiAgICAgc2xpY2UoMTp4KSAKICAgfSAKIH0gCgogIyBtb3ZlIGFsbCBvZiB0aGUgbm9uLWJsYW5rIHdlbGxzIGludG8gdGhlaXIgb3duIHRhYmxlIAogc2FtcGxlcyA8LSBwbGF0ZSAlPiUgIAogICBmaWx0ZXIoaXMubmEoZXh0cmFjdGlvbl9pZCkpICU+JSAgCiAgIHNlbGVjdCgtZXh0cmFjdGlvbl9pZCkgCgogIyByZW1vdmUgdGhlIG5vbi1ibGFuayB3ZWxscyBmcm9tIHRoZSBwbGF0ZSB0YWJsZSAKIHBsYXRlIDwtIGFudGlfam9pbihwbGF0ZSwgc2FtcGxlcywgYnkgPSBjKCJyb3ciLCAiY29sIikpIAoKICMgam9pbiB0aGUgd29yayBzYW1wbGVfaWRzIHRvIHRoZSBzYW1wbGUgdGFibGUgd2VsbCBkZWZpbml0aW9ucyAKIHNhbXBsZXMgPC0gY2JpbmQoc2FtcGxlcywgbmVlZF9kaWdlc3QpICU+JSAgCiAgIHNlbGVjdChyb3csIGNvbCwgZXh0cmFjdGlvbl9pZCkgCgogIyByZWpvaW4gdGhlIHNhbXBsZXMgaW50byB0aGUgcGxhdGUgd2l0aCB0aGUgYmxhbmtzLCByZXN1bHRpbmcgaW4gYSBwbGF0ZSBvZiBkZWZpbmVkIHdlbGxzIGZvciBzYW1wbGVzIGFuZCBibGFua3MuIAogcGxhdGUgPC0gcmJpbmQocGxhdGUsIHNhbXBsZXMpICU+JSAgCiAgIGFycmFuZ2UoY29sLCByb3cpIAoKIHNvdXJjZV9tYXAgPC0gYXMubWF0cml4KHJlc2hhcGUyOjphY2FzdChwbGF0ZSwgcGxhdGUkcm93IH4gcGxhdGUkY29sKSwgdmFsdWUudmFyID0gcGxhdGUkZXh0cmFjdGlvbikgCiBrbml0cjo6a2FibGUoc291cmNlX21hcCwgYm9va3RhYnMgPSBUKSAlPiUgIAogICAjIHVzZSBzY2FsZV9kb3duIHRvIGdldCBtYXAgdG8gZml0IHdpdGhpbiB0aGUgYm91bmRzIG9mIHRoZSBwZGYgCiAgIGthYmxlX3N0eWxpbmcobGF0ZXhfb3B0aW9ucyA9ICJzY2FsZV9kb3duIikgCmBgYCAKCiBBIGRpZ2VzdCBpZCBtdXN0IGJlIGNyZWF0ZWQgZm9yIHRoZXNlIHNhbXBsZXMuICAgCiAqKk9ubHkgZG8gdGhpcyBzdGVwIG9uY2UhKiogICAKIEl0IGNyZWF0ZXMgbmV3IGRpZ2VzdF9pZHMgZm9yIHRoZSBkYXRhYmFzZS4gICAKIFRoZSBkZXN0aW5hdGlvbiBwbGF0ZSBtYXAgd291bGQgbG9vayBsaWtlIHRoaXM6ICAgCgpgYGB7cn0gCiAjIyMgT05MWSBETyBUSElTIE9OQ0UgIyMjIAogZGlnX21heCA8LSBsYWIgJT4lIHRibCgiZGlnZXN0IikgJT4lICAKICAgc3VtbWFyaXNlKGxhc3QgPSBtYXgoZGlnZXN0X2lkLCBuYS5ybSA9IFQpKSAlPiUgIAogICBjb2xsZWN0KCkgJT4lICAKICAgbXV0YXRlKGxhc3QgPSBhcy5udW1lcmljKHN1YnN0cihsYXN0LCAyLDUpKSkgCgogeCA8LSBkaWdfbWF4JGxhc3QrMSAKIHkgPC0gZGlnX21heCRsYXN0K25yb3cocGxhdGUpIAogaWRfcmFuZ2UgPC0geDp5IAoKIHBsYXRlIDwtIHBsYXRlICU+JSAgCiAgIG11dGF0ZShkaWdlc3RfaWQgPSBwYXN0ZTAoIkQiLCBpZF9yYW5nZSksIAogICAgICAgICAgd2VsbCA9IHBhc3RlKHJvdywgY29sLCBzZXAgPSAiIiksICAKICAgICAgICAgIGVuenltZXMgPSAiUHN0SS1NbHVDSSIsIAogICAgICAgICAgdm9sX2luID0gMzAsIAogICAgICAgICAgZmluYWxfdm9sID0gNDUpIAoKIHBsYXRlX25hbWUgPC0gcGxhdGUgJT4lICAKICAgc3VtbWFyaXNlKGZpcnN0ID0gbWluKGRpZ2VzdF9pZCksICAKICAgICBsYXN0ID0gbWF4KGRpZ2VzdF9pZCwgbmEucm0gPSBUKSkgCgogZGVzdF9tYXAgPC0gcGxhdGUgJT4lICAKICAgbXV0YXRlKHBsYXRlID0gcGFzdGUocGxhdGVfbmFtZSRmaXJzdCwgcGxhdGVfbmFtZSRsYXN0LCBzZXAgPSAiLSIpKSAlPiUgIAogICBzZWxlY3Qocm93LCBjb2wsIGRpZ2VzdF9pZCkgCgogZGVzdF9tYXAgPC0gYXMubWF0cml4KHJlc2hhcGUyOjphY2FzdChkZXN0X21hcCwgZGVzdF9tYXAkcm93IH4gZGVzdF9tYXAkY29sKSwgdmFsdWUudmFyID0gZGVzdF9tYXAkZGlnZXN0X2lkKSAKCiBrbml0cjo6a2FibGUoZGVzdF9tYXAsIGJvb2t0YWJzID0gVCkgJT4lICAKICAga2FibGVfc3R5bGluZygpIAoKYGBgIAoKCgoKIFByaW50IG91dCBwbGF0ZSBtYXBzIGFuZCBoaWdobGlnaHQgc291cmNlIG1hcHMgZm9yIGVhc2Ugb2YgbG9hZGluZy4gIE1ha2Ugc3VyZSB0byBub3RlIGFueSBzYW1wbGVzIHRoYXQgYXJlIGhlYXZpbHkgY29uY2VudHJhdGVkIGFuZCBuZWVkIHRvIGJlIGRpbHV0ZWQuIAoKIFByZXBhcmUgZGlnZXN0IHBsYXRlIGJ5IGxvYWRpbmcgMzB1TCBvZiBzYW1wbGUgaW4gdG8gdGhlIHBsYXRlLiAKCiBGb3IgdGhlIE9jdG9iZXIgMjAxNyBzZXQgb2YgcGxhdGVzLCBJIGhpZ2hsaWdodGVkIHRoZSAxNXVMIHNhbXBsZXMgb24gdGhlIG1hcHMgaW4gb3JhbmdlIGFuZCB0aGUg4oCcaG9sZS1maWxsZXJz4oCdIGluIHBpbmssIHRoZW4gYW55IHNhbXBsZXMgSSBjb3VsZCB0YWtlIHdpdGggYSBtdWx0aWNoYW5uZWwgd2l0aCBhIGJsdWUgbGluZSBkb3duIHRoZSBjb2x1bW4uIAoKIFRvIGxvYWQsIEkgZmlyc3QgdXNlZCBhIHNpbmdsZSBjaGFubmVsIHBpcGV0IHRvIGZpbGwgaW4gdGhlIDE1dUwgc2FtcGxlcyAoZXh0cmEgY29uY2VudHJhdGVkIHNhbXBsZXMpLCBwdWxsaW5nIHRoZSB0aXBzIGZyb20gdGhlIHNhbWUgbG9jYXRpb24gdGhleSB3ZXJlIG9uIHRoZSBwbGF0ZS4gIFRoZW4gSSBmaWxsZWQgaW4gdGhlIHdhdGVyIGZvciB0aGUgMTV1TCB3ZWxscyBhbmQgY2hhbmdlIHRoZSBwaXBldCB0byAzMHVMIHRvIGZpbGwgdGhlIGJsYW5rcy4gCiBUaGVuIEkgYWRkZWQgYW55IDMwdUwg4oCcaG9sZSBmaWxsZXJz4oCdIC0gYWdhaW4gdXNpbmcgdGhlIHRpcHMgZnJvbSB0aGUgc2FtZSBsb2NhdGlvbiBpbiB0aGUgdGlwIGJveC4gCgogQXQgdGhpcyBwb2ludCwgYW55IGVtcHR5IHdlbGxzIG9uIHRoZSBwbGF0ZSBjb3JyZXNwb25kIHRvIHRpcHMgaW4gdGhlIHRpcCBib3guICBJIHVzZWQgdGhlIG11bHRpY2hhbm5lbCBwaXBldCB0byBwaWNrIHVwIHRoZSB0aXBzIGFuZCBoZWxkIHRoZW0gdXAgdG8gdGhlIHBsYXRlIHRvIG1ha2Ugc3VyZSBubyB0aXBzIHdlcmUgZ29pbmcgaW50byBhbHJlYWR5IGZpbGxlZCB3ZWxscywgYW5kIG5vIGVtcHR5IHdlbGxzIHdlcmUgYmVpbmcgbGVmdCBlbXB0eS4gIFRoZW4gSSBmaWxsZWQgdGhlIHJlc3Qgb2YgdGhlIHBsYXRlLiAKCgogIyMgTWFrZSB0aGUgbWFzdGVyIG1peCByZWNpcGUgaW4gYSBmYWxjb24gdHViZSBvciBzbWFsbGVyIHR1YmUgaWYgcG9zc2libGU6IApgYGB7cn0gCiBudW1fc2FtcGxlcyA8LSBucm93KHBsYXRlKSAKCiAjIG1ha2Ugc29tZSByb29tIGZvciBlcnJvciAxNSUgCiBzYW1wbGVzX3dpdGhfZXJyb3IgPC0gZmxvb3IobnVtX3NhbXBsZXMgKiAxLjE1KSAKCiAjIHVzZSBhIHRvdGFsIG9mIDEwIGVuenltZSBVbml0cyBwZXIgNTB1TCByZWFjdGlvbiwgNSBvZiBlYWNoIGVuenltZS4gCgogZGlnZXN0X3J4biA8LSByZWFkX2NzdigiaXRlbSwgY29uY2VudHJhdGlvbiwgcGVyX3JlYWN0aW9uIAogICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGUsIE5BLCBOQSAKICAgICAgICAgICAgICAgICAgICAgICAgZW56eW1lMSwgTkEsIE5BIAogICAgICAgICAgICAgICAgICAgICAgICBlbnp5bWUyLCBOQSwgTkEgCiAgICAgICAgICAgICAgICAgICAgICAgIGJ1ZmZlciwgTkEsIE5BIAogICAgICAgICAgICAgICAgICAgICAgICBwSDJPLCBOQSwgTkEiKSAlPiUgIAogICBtdXRhdGUocGVyX3JlYWN0aW9uID0gaWZlbHNlKGl0ZW0gPT0gInNhbXBsZSIsIHBhcmFtcyRkaWdlc3RfcnhuX3NpemUvMS42NjY2LCBwZXJfcmVhY3Rpb24pLCAgCiAgICAgICAgICBjb25jZW50cmF0aW9uID0gaWZlbHNlKGl0ZW0gPT0iZW56eW1lMSIsIDEwMDAwMCwgY29uY2VudHJhdGlvbiksIAogICAgICAgICAgcGVyX3JlYWN0aW9uID0gaWZlbHNlKGl0ZW0gPT0gImVuenltZTEiLCAocGFyYW1zJGRpZ2VzdF9yeG5fc2l6ZSkvMTAvKGNvbmNlbnRyYXRpb24vMTAwMCksIHBlcl9yZWFjdGlvbiksIAogICAgICAgICAgY29uY2VudHJhdGlvbiA9IGlmZWxzZShpdGVtID09ImVuenltZTIiLCAxMDAwMCwgY29uY2VudHJhdGlvbiksIAogICAgICAgICAgIHBlcl9yZWFjdGlvbiA9IGlmZWxzZShpdGVtID09ICJlbnp5bWUyIiwgKHBhcmFtcyRkaWdlc3RfcnhuX3NpemUpLzEwLyhjb25jZW50cmF0aW9uLzEwMDApLCBwZXJfcmVhY3Rpb24pLCAKICAgICAgICAgIGNvbmNlbnRyYXRpb24gPSBpZmVsc2UoaXRlbSA9PSJidWZmZXIiLCAxMCwgY29uY2VudHJhdGlvbiksIAogICAgICAgICAgIHBlcl9yZWFjdGlvbiA9IGlmZWxzZShpdGVtID09ICJidWZmZXIiLCBwYXJhbXMkZGlnZXN0X3J4bl9zaXplL2NvbmNlbnRyYXRpb24sIHBlcl9yZWFjdGlvbikpIAoKIHBIMk8gPC0gZGlnZXN0X3J4biAlPiUgIAogICBzdW1tYXJpc2UodGhpcyA9IHBhcmFtcyRkaWdlc3RfcnhuX3NpemUtc3VtKHBlcl9yZWFjdGlvbiwgbmEucm09VCkpIAoKIGRpZ2VzdF9yeG4gPC0gZGlnZXN0X3J4biAlPiUgIAogICBtdXRhdGUocGVyX3JlYWN0aW9uID0gaWZlbHNlKGl0ZW0gPT0gInBIMk8iLCBwSDJPJHRoaXMsIHBlcl9yZWFjdGlvbiksIAogICAgICAgICAgbWFzdGVyX21peCA9IHBlcl9yZWFjdGlvbiAqIHNhbXBsZXNfd2l0aF9lcnJvciwgIAogICAgICAgICAgbWFzdGVyX21peCA9IGlmZWxzZShpdGVtID09ICJzYW1wbGUiLCBOQSwgbWFzdGVyX21peCkpIAoKICMgZG91YmxlIGNoZWNrIHRoZSBtYXRoIC0gc2hvdWxkIHJldHVybiBUUlVFIAogKHBhcmFtcyRkaWdlc3RfcnhuX3NpemUqc2FtcGxlc193aXRoX2Vycm9yKS0ocGFyYW1zJGRpZ2VzdF9yeG5fc2l6ZSouNipzYW1wbGVzX3dpdGhfZXJyb3IpID09IHJvdW5kKHN1bShkaWdlc3RfcnhuJG1hc3Rlcl9taXgsIG5hLnJtPVQpKSAKCiBwcmludChwYXN0ZTAoIlRvdGFsIG1hc3RlciBtaXggdm9sdW1lIGlzICIsIHJvdW5kKHN1bShkaWdlc3RfcnhuJG1hc3Rlcl9taXgsIG5hLnJtPVQpKSwgIm1MIikpIApgYGAgCmBgYHtyfSAKIGthYmxlX3N0eWxpbmcoa2FibGUoZGlnZXN0X3J4bikpIApgYGAgCgogICAgICogc3BsaXQgdGhlIG1hc3RlciBtaXggaW50byA4IHdlbGxzLCB1c2UgYSBtdWx0aWNoYW5uZWwgcGlwZXQgdG8gcGlwZXQgMTLCtUwgb2YgbWFzdGVyIG1peCBpbnRvIGVhY2ggc2FtcGxlIHdlbGwuICAKYGBge3J9IAogcHJpbnQocGFzdGUwKCJTcGxpdCBpbnRvIDggd2VsbHMgYnkgcGlwZXR0aW5nICIsIChyb3VuZChzdW0oZGlnZXN0X3J4biRtYXN0ZXJfbWl4LCBuYS5ybT1UKSkpLzgsICJ1bCBpbnRvIGVhY2ggd2VsbCIpKSAKYGBgIAoKICMgU3VwcGxpZXMgbmVlZGVkIHBsdXMgdGhlIHZvbHVtZXMgb24gdGhlIG1hc3RlciBtaXggcmVjaXBlLiAKYGBge3J9IAogZGlnZXN0IDwtIHRpYmJsZSggCiAgIHBsYXRlcyA9IG51bV9zYW1wbGVzLzk2LCAKICAgdHViZXMgPSBudW1fc2FtcGxlcy85NiwgIAogICBwbGF0ZV9zZWFscyA9IG51bV9zYW1wbGVzLzk2LCAgCiAgIHRpcHNfMTAwID0gbnVtX3NhbXBsZXMgKiAyICsgKGNlaWxpbmcoZGlnZXN0X3J4biRtYXN0ZXJfbWl4WzNdLzEwMCkpLCMgcmVmZXJyaW5nIHRvIGVuenltZTIgCiAgIHRpcHNfMTAgPSBjZWlsaW5nKGRpZ2VzdF9yeG4kbWFzdGVyX21peFsyXS8xMCksICMgcmVmZXJyaW5nIHRvIGVuenltZTEgCiAgIHRpcHNfMzAwID0gY2VpbGluZyhkaWdlc3RfcnhuJG1hc3Rlcl9taXhbNF0vMTAwKSwgIyByZWZlcnJpbmcgdG8gYnVmZmVyIAogICB0aXBzXzEwMDAgPSBjZWlsaW5nKGRpZ2VzdF9yeG4kbWFzdGVyX21peFs1XS8xMDAwKSAjIHJlZmVycmluZyB0byBwSDJPIAogICApIApgYGAgCgoKICAgICAgICAgKiBEbyBub3QgYWRkIHRoZSBtYXN0ZXIgbWl4IHRvIGFsbCBvZiB0aGUgd2VsbHMgYW5kIHRoZW4gYWRkIHRoZSBzYW1wbGUuICBCZWNhdXNlIHlvdSBhcmUgd29ya2luZyB3aXRoIGFjdGl2ZSBlbnp5bWVzLCB0aGV5IHNob3VsZCBiZSB0aGUgbGFzdCB0aGluZ3MgYWRkZWQgdG8gdGhlIG1hc3RlciBtaXggYW5kIHRoZSBsYXN0IHN0ZXAgb2YgcGxhdGUgcHJlcGFyYXRpb24uIAogKiBJbmN1YmF0ZSBQc3RJIGFuZCBNbHVDSSBhdCAzN8uaIC0gTm8gYmVuZWZpdCBmcm9tIGluY3ViYXRpbmcgTWx1Q0kgbG9uZ2VyIHRoYW4gb25lIGhvdXIsIFBzdEkgaXMgYWN0aXZlIDItNCBob3VycywgZG9u4oCZdCBuZWVkIHRvIGhlYXQga2lsbCBkaWdlc3RzIAoKIENsZWFuIHRoZSBkaWdlc3RzIHVzaW5nIDEuNXggdGhlIHZvbHVtZSBvZiB0aGUgZGlnZXN0ICg3NcK1TCkgQW1wdXJlIHBlciBzYW1wbGUuIFB1dCB0aGUgcGxhdGVzIG9uIHllbGxvdyBsaWZ0cyBvbiB0aGUgbWFnbmV0IHNvIHRoYXQgdGhlIHBlbGxldCBpcyBsb3dlciBiZWZvcmUgeW91IGZpbmlzaCB0aGUgYWxjb2hvbCBzdGVwcy4gICBCZWNhdXNlIHRoZXNlIGFyZSBpbiBwbGF0ZXMgYW5kIGl0IGlzIGRpZmZpY3VsdCB0byBnZXQgYWxsIG9mIHRoZSBlbHV0ZSBvdXQgb2YgdGhlIHdlbGwsIGVsdXRlIGluIDQ1wrVMIG9mIGJ1ZmZlciBhbmQgcHVsbCA0MMK1TCBmcm9tIHRoZSB3ZWxsIHRvIHBsYWNlIGluIGEgbmV3IGNsZWFuZWQgZGlnZXN0IHBsYXRlLiAgVXBkYXRlIHRoZSBkYXRhYmFzZS4gLSBBbXB1cmUgY2xlYW4gdXAgcHJvdG9jb2wgCgogUXVhbnRpZml5IC0gcXVhbnQtaXQgcGljbyBncmVlbiAtIGltcG9ydCB0aGUgcXVhbnRpZmljYXRpb24gcmVzdWx0cyBpbnRvIHRoZSBTYW1wbGVfRGF0YSBzcHJlYWRzaGVldCwgRGlnZXN0cyBQYWdlIHF1YW50IGNvbHVtbi4gIEZvciBhbnkgc2FtcGxlIHdpdGggYSBuZWdhdGl2ZSBxdWFudGlmaWNhdGlvbiB2YWx1ZSwgY2hhbmdlIHRoZSBMaWdhdGVkPyBjb2x1bW4gdmFsdWUgdG8gTi9BICh0aGUgcmVzdCBzaG91bGQgY29udGFpbiBhIGZvcm11bGEgdGhhdCByZXN1bHRzIGluIHRoZSB2YWx1ZSDigJxGQUxTReKAnSkuICBBbHNvLCBhZGQgYW4g4oCcX0bigJ0gdG8gdGhlIGV4dHJhY3Rpb24gSUQgKG5vdCB0aGUgZGlnZXN0IElEKSBpbiB0aGUgZXh0cmFjdGlvbiBJRCBjb2x1bW4gKGV4YW1wbGU6IFBBREUxNF8wOTNFMTUxNiBiZWNvbWVzIFBBREUxNF8wOTNFMTUxNl9GKS4gIFRoaXMgZGlnZXN0IGZhaWxlZCBhbmQgc28gdGhlIHNhbXBsZSBzaG91bGQgbm90IHJlZmxlY3QgdGhhdCBhIHN1Y2Nlc3NmdWwgZGlnZXN0IGV4aXRzLiAKCgoKCgoKCgoKCgogICAgICogTW9yZSBkZWNpc2lvbiBtYWtpbmcgaXMgbmVjZXNzYXJ5IGF0IHRoaXMgcG9pbnQuICBJZiBhbiBleHRyYWN0IGhhcyBsZXNzIHRoYW4gNW5nL3VsIEROQSwgaXQgd2lsbCBtb3N0IGxpa2VseSBmYWlsIHRvIGRpZ2VzdCB3ZWxsLiAgVGhpcyBpcyBhIHRvdWdoIGNhbGwsIHNvbWUgZGlnZXN0cyB3aXRoIG11Y2ggbGVzcyB0aGFuIDVuZy91bCBoYXZlIGxpZ2F0ZWQgc3VjY2Vzc2Z1bGx5IGFuZCBvdGhlcnMgaGF2ZSBub3QuICBQcm9jZWVkIHdpdGggY2F1dGlvbi4gSWYgdGhlcmUgYXJlIHBsZW50eSBvZiBzYW1wbGVzIHRvIGNob29zZSBmcm9tLCBjaG9vc2UgdGhlIGRlc2lyZWQgbnVtYmVyIHdpdGggdGhlIG1vc3Qg4oCcc2FtcGxlX25nX2lu4oCdLiAKICAgICAgICAgKiBBcyBvZiA4LzE5LzIwMTQgIAogICAgICAgICAgICAgKiBhbnkgZXh0cmFjdGlvbiBsZXNzIHRoYW4gNW5nL8K1TCB3ZSBhcmUgcHV0dGluZyBvbiB0aGUgYmFjayBidXJuZXIgdG8gY29udGludWUgcHJvY2Vzc2luZyBhdCBhIGxhdGVyIGRhdGUgCiAgICAgICAgICAgICAqIGFueXRoaW5nIHRoYXQgaXMgbW9yZSB0aGFuIDVuZy/CtUwgYnV0IG1ha2VzIGxlc3MgdGhhbiAxwrVnIG9mIEROQSB3aGVuIHlvdSBtdWx0aXBseSB0aGUgcXVhbnQgcmVzdWx0IGJ5IDMwwrVMICh0aGUgZGlnZXN0IGFkZGl0aW9uIGFtb3VudCkgLSBjYWxjdWxhdGUgdGhlIGFtb3VudCBvZiBleHRyYWN0IHRvIG1ha2UgMcK1ZyBETkEgYW5kIGNsZWFuIHRoYXQgYW1vdW50LCBlbHV0ZSBpbiAzMMK1TCBhbmQgYWRkIHRvIGRpZ2VzdCBwbGF0ZSAgCiAgICAgICAgICAgICAqIEFzIG9mIDExLzIwMTQgd2UgYXJlIG5vdCB3b3JyaWVkIGFib3V0IOKAnHRvbyBsb3figJ0gRE5BIGFuZCBhcmUgbm90IGNsZWFuaW5nIHNhbXBsZXMgdGhhdCBhcmUgYmVsb3cgMTAwMG5nLiAgV2UgaGF2ZSBzdWNjZXNzZnVsbHkgZGlnZXN0ZWQgZG93biB0byAxMDBuZy4gICAKICAgICAgICAgICAgICogYW55dGhpbmcgdGhhdCBtYWtlcyBtb3JlIHRoYW4gNcK1ZyBvZiBETkEgd2hlbiB5b3UgbXVsdGlwbHkgdGhlIHF1YW50IHJlc3VsdCBieSAzMMK1TCAtIGFkZCAxNcK1TCBvZiBleHRyYWN0IHRvIHBsYXRlIGFuZCAxNcK1TCBwSDJPIC0gZG8gdGhlIG1hdGggdG8gbWFrZSBzdXJlIHRoaXMgZGlsdXRpb24gd2lsbCBzdGlsbCBiZSBsZXNzIHRoYW4gNcK1Zy4gCiAgICAgKiBGb3IgdGhlIHRpbWUgYmVpbmcsIGVudGVyIHRoZSBFeHRyYWN0SUQgaW4gdGhlIERpZ2VzdCBJRCBjb2x1bW4gZm9yIGVhc2Ugb2Ygc29ydGluZyAoaXQgaXMgZWFzaWVzdCB0byB3b3JrIG9uIGV4dHJhY3QgcGxhdGVzIGluIHNhbXBsZSBvcmRlcikgLSBkbyB0aGlzIGJ5IGVudGVyaW5nIHRoZSBmb3JtdWxhID1yaWdodChCMTg4Miw1KSBpbiBjb2x1bW4gQSAKICAgICAqIFNvcnQgYnkgRXh0cmFjdCBJZCAobm8gZGlnZXN0IG51bWJlciB5ZXQsIHdpbGwgc29ydCBieSBzYW1wbGUgbnVtYmVyKSwgb25seSB0aGUgcmFuZ2Ugb2YgdGhpcyBwbGF0ZSEhISAKICAgICAqIEZpbGwgaW4gZGF0ZSwgZW56eW1lLCBEaWdlc3QjIGFuZCByZXBsYWNlIHRoZSBFeHRyYWN0IyBpbiB0aGUgRGlnZXN0X0lEIGNvbHVtbiB3aXRoIHRoZSBmb3JtdWxhIHRvIGNvbmNhdGVuYXRlIGEgZGlnZXN0IElELiAKICAgICAqIHByaW50IGEgbWFwIG9mIHlvdXIgZXh0cmFjdGlvbiBwbGF0ZSBhbmQgbmV3IGRpZ2VzdCBwbGF0ZSAKICogaGlnaGxpZ2h0IHNhbXBsZXMgaW4gcHJpbnRlZCBleHRyYWN0aW9uIHBsYXRlIG1hcHMgCiAqIHBpcGV0ZWQgYWxpcXVvdCB0byBkaWdlc3QgcGxhdGUgZm9yIGEgNTDCtUwgZGlnZXN0aW9uIHJlYWN0aW9uIAogKiBQcmludCBhIHBsYXRlIG1hcCBhbmQgZGV0ZXJtaW5lIHdoaWNoIHNhbXBsZXMgY2FuIGJlIGRpZ2VzdGVkIGFzIGlzLCBhbmQgd2hpY2ggbmVlZCB0byBiZSBkaWx1dGVkIC0gaGlnaGxpZ2h0IGFjY29yZGluZ2x5IAoKCgogT3B0aW1pemluZyBSZXN0cmljdGlvbiBFbmRvbnVjbGVhc2UgUmVhY3Rpb25zIHwgTmV3IEVuZ2xhbmQgQmlvbGFicyAKIEZvciBhIDUwwrVMIHJ4biwgdXNlOiAKCiAxwrVnIEROQSAtIGZpbGwgdG8gdm9sdW1lIHcvIHBIMk8gCiA1wrVMIDEweCBidWZmZXIgKDUwwrVMLzEweD01KSAKCiBXYW50IGEgdG90YWwgb2YgMTBVIGluIHJ4biAKIEZvciBzaW5nbGUgZGlnZXN0OiAKIGlmIGVuenltZSBpcyAxMCwwMDBVL21MIC0gMcK1TCA9IDEwVSAKIGlmIGVuenltZSBpcyAyMCwwMDBVL21MIC0gMC41wrVMID0gMTBVIAoKIEZvciBkb3VibGUgZGlnZXN0IAogaWYgZW56eW1lIGlzIDEwLDAwMFUvbUwgLSAwLjXCtUwgPSA1VSAKIGlmIGVuenltZSBpcyAyMCwwMDBVL21MIC0gMC4yNcK1TCA9IDVVICAKCiBQc3RJIC0gY3V0c2l0ZSBpcyAgaHR0cHM6Ly93d3cubmViLmNvbS9wcm9kdWN0cy9yMDE0MC1wc3RpIAoKIE1sdUNJIC0gY3V0c2l0ZSBpcyAgaHR0cHM6Ly93d3cubmViLmNvbS9wcm9kdWN0cy9yMDUzOC1tbHVjaSAKCiBFY29SSSBjdXRzaXRlIGlzICBodHRwczovL3d3dy5uZWIuY29tL3Byb2R1Y3RzL3IwMTAxLWVjb3JpIAo=