FuncVAR: Annotation and functional enrichment of variant sets

In this vignette we will learn to take a set of variants, arbitrarily defined as any set of GenomicRanges, overlap with multiomics datasets and then calculate enrichment against an appropriately chosen background set.

Setup

First we need to import the packages we’ll be using. FunciVar can be accessed directly from our github repositories via biocLite. GenomicRanges and VariantAnnotation packages are available through bioconductor:

Install the required packages, if they are not already availible.

source("https://bioconductor.org/biocLite.R")
biocLite("Simon-Coetzee/StatePaintR", suppressUpdates = TRUE, dependencies = TRUE)
biocLite("Simon-Coetzee/funciVar", suppressUpdates = TRUE, dependencies = TRUE)
biocLite("Simon-Coetzee/motifbreakR", suppressUpdates = TRUE, dependencies = TRUE)
biocLite("SNPlocs.Hsapiens.dbSNP144.GRCh37")

Then load the packages we’ll be using in this workshop.

library(StatePaintR)
library(funciVar)
library(GenomicRanges)
library(VariantAnnotation)
library(UpSetR)

Getting annotations

Annotations can come from any source, the key requirement be that they be coerced into GRanges format for use by the funciVar package. For our purposes, We can search on StateHub for all cell types that are annotated in hg19 with segmentations combining at least, H3K4me1, H3K4me3, H3K27ac, and CTCF.

StateHub Search for hg19, H3K4me1, H3K4me3, H3K27ac, and CTCF

StateHub Search for hg19, H3K4me1, H3K4me3, H3K27ac, and CTCF

Each segmentation file contains a set of chromatin state calls made by our StatePaintR package.

How does StatePaintR work?

Lets look at StatePaintR: slides and rmarkdown

Returning to Analysis

FunciVar imports these files efficiently by pointing a function called “GetSegmentations” directly. GetSegmentations() expects files to be in BED format with column 4 (Name) corresponding to the state call. Then it’s just a matter of renaming the state abbreviations to easily recognized, human readable form.

## Base url of StateHub files in our search
statehub.encode.aws <- "http://s3-us-west-2.amazonaws.com/statehub-trackhub/tracks/5813b67f46e0fb06b493ceb0/hg19/ENCODE/"
## A list of StateHub urls
segmentation.files <- c(
  paste0(statehub.encode.aws,
         "bipolar_spindle_neuron.8mark.segmentation.bed"),
  paste0(statehub.encode.aws, 
         "dohh2.8mark.segmentation.bed"),
  paste0(statehub.encode.aws, 
         "gm12878.11mark.segmentation.bed"),
  paste0(statehub.encode.aws,
         "hepatocyte.9mark.segmentation.bed"),
  paste0(statehub.encode.aws,
         "induced_pluripotent_stem_cell.7mark.segmentation.bed"),
  paste0(statehub.encode.aws, 
         "mcf-7.12mark.segmentation.bed"),
  paste0(statehub.encode.aws,
         "neutrophil.8mark.segmentation.bed"))
## Import States
encode.segmentations <- GetSegmentations(files = segmentation.files)
esegs <- unlist(encode.segmentations)
esegs
GRanges object with 4400992 ranges and 2 metadata columns:
                         seqnames               ranges strand |                 sample       state
                            <Rle>            <IRanges>  <Rle> |            <character> <character>
  bipolar spindle neuron     chr1     [ 54343,  54728]      * | bipolar spindle neuron         SCR
  bipolar spindle neuron     chr1     [ 83693,  84250]      * | bipolar spindle neuron         SCR
  bipolar spindle neuron     chr1     [ 85703,  86097]      * | bipolar spindle neuron         SCR
  bipolar spindle neuron     chr1     [ 89735,  90145]      * | bipolar spindle neuron         SCR
  bipolar spindle neuron     chr1     [713064, 713592]      * | bipolar spindle neuron         PWR
                     ...      ...                  ...    ... .                    ...         ...
              neutrophil     chrY [59023293, 59023989]      * |             neutrophil         HET
              neutrophil     chrY [59026316, 59026627]      * |             neutrophil         HET
              neutrophil     chrY [59029021, 59029372]      * |             neutrophil         HET
              neutrophil     chrM [      37,      405]      * |             neutrophil          AR
              neutrophil     chrM [   15931,    16269]      * |             neutrophil          AR
  -------
  seqinfo: 93 sequences (1 circular) from hg19 genome
## Group States in Human readable collections.
mcols(esegs[esegs$state %in% c("EAR", "AR"), ])$state <- "Active Enhancer"
mcols(esegs[esegs$state %in% c("EPR", "EWR"), ])$state <- "Weak Enhancer"
mcols(esegs[esegs$state %in% c("PAR"), ])$state <- "Active Promoter"
mcols(esegs[esegs$state %in% c("PPR", "PPWR", "PWR"), ])$state <- "Weak Promoter"
mcols(esegs[esegs$state %in% c("CTCF"), ])$state <- "CTCF"
mcols(esegs[esegs$state %in% c("HET", "SCR", "TRS"), ])$state <- "Other"

Importing Variants

There are two potential use cases that we envision for funciVar. In the first case, our enrichment calculations are based on a foreground and background set that are predefined and nothing needs to be done outside of importing to R and coercing into GRanges. Such foreground sets could include any kind of variant; common population polymorphisms or differentially methylated CpGs for example.

In the second case, especially common for primary GWAS studies, a single top “hit” has been identified and a set of variants known to be in linkage disequilibrium (LD) should be calculated. We will cover this latter case. The LD calculations can be used to further subdivide regional variants into foreground and background based on some LD threshold (typically at R² = 0.8). In other instances, more creative methods of obtaining a background set might be warranted.

We will use 1000 genomes data for our LD calculations, but in principle we could apply this to any population stored in VCF. First we need to identify the file containing the 1000 and assign all the sample metadata into a variable called “my.samples”.

my.samples <- read.delim("http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/integrated_call_samples_v3.20130502.ALL.panel", stringsAsFactors = FALSE)
my.samples <- my.samples[, c("sample", "pop", "super_pop", "gender")]
my.samples

Next we will identify the file with chromosome 10 variants and define the index region as a filter for the foreground variant set. For simplicity’s sake, we are testing a single region, but we could easily analyze multiple ranges. For this example, we are using a GWAS variant linked to breast cancer on chromosome 10.

chr10.remote.vcf <- "ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/ALL.chr10.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz"
index10q26 <- "rs2981579"
pos10q26 <- GRanges("10:123337335-123337335") + 500000
pos10q26
GRanges object with 1 range and 0 metadata columns:
      seqnames                 ranges strand
         <Rle>              <IRanges>  <Rle>
  [1]       10 [122837335, 123837335]      *
  -------
  seqinfo: 1 sequence from an unspecified genome; no seqlengths

Using the 1MB window that we defined for the index variant, we capture the relevant variants from the VCF file for our LD calculations.

vcf10q26 <- GetVariantsInWindow(file = chr10.remote.vcf, 
                                position = pos10q26, 
                                type = "vcf")

EBI throttles downloads from here pretty hard. So We have a local version of the vcf included also, in case you get the above error.

chr10.local.vcf <- system.file("extdata",
                               "1000genomes.chr10.122836335-123838335.vcf.gz",
                               package="bioc2017")
vcf10q26 <- GetVariantsInWindow(file = chr10.local.vcf, 
                                position = pos10q26, 
                                type = "vcf")

To ensure that we make our LD calculation from the correct population structure, we set the population using metadata from “my.samples”.

vcf10q26 <- SetPopulation(vcf = vcf10q26, sample_sheet = my.samples)
vcf10q26
class: CollapsedVCF 
dim: 31499 2504 
rowRanges(vcf):
  GRanges with 5 metadata columns: paramRangeID, REF, ALT, QUAL, FILTER
info(vcf):
  DataFrame with 27 columns: CIEND, CIPOS, CS, END, IMPRECISE, MC, MEINFO, MEND, MLEN, MSTART, SVLEN, SVTYPE, TSD, AC, AF, NS, AN, ...
info(header(vcf)):
                 Number Type    Description                                                                                           
   CIEND         2      Integer Confidence interval around END for imprecise variants                                                 
   CIPOS         2      Integer Confidence interval around POS for imprecise variants                                                 
   CS            1      String  Source call set.                                                                                      
   END           1      Integer End coordinate of this variant                                                                        
   IMPRECISE     0      Flag    Imprecise structural variation                                                                        
   MC            .      String  Merged calls.                                                                                         
   MEINFO        4      String  Mobile element info of the form NAME,START,END<POLARITY; If there is only 5' OR 3' support for this...
   MEND          1      Integer Mitochondrial end coordinate of inserted sequence                                                     
   MLEN          1      Integer Estimated length of mitochondrial insert                                                              
   MSTART        1      Integer Mitochondrial start coordinate of inserted sequence                                                   
   SVLEN         .      Integer SV length. It is only calculated for structural variation MEIs. For other types of SVs; one may cal...
   SVTYPE        1      String  Type of structural variant                                                                            
   TSD           1      String  Precise Target Site Duplication for bases, if unknown, value will be NULL                             
   AC            A      Integer Total number of alternate alleles in called genotypes                                                 
   AF            A      Float   Estimated allele frequency in the range (0,1)                                                         
   NS            1      Integer Number of samples with data                                                                           
   AN            1      Integer Total number of alleles in called genotypes                                                           
   EAS_AF        A      Float   Allele frequency in the EAS populations calculated from AC and AN, in the range (0,1)                 
   EUR_AF        A      Float   Allele frequency in the EUR populations calculated from AC and AN, in the range (0,1)                 
   AFR_AF        A      Float   Allele frequency in the AFR populations calculated from AC and AN, in the range (0,1)                 
   AMR_AF        A      Float   Allele frequency in the AMR populations calculated from AC and AN, in the range (0,1)                 
   SAS_AF        A      Float   Allele frequency in the SAS populations calculated from AC and AN, in the range (0,1)                 
   DP            1      Integer Total read depth; only low coverage data were counted towards the DP, exome data were not used        
   AA            1      String  Ancestral Allele. Format: AA|REF|ALT|IndelType. AA: Ancestral allele, REF:Reference Allele, ALT:Alt...
   VT            .      String  indicates what type of variant the line represents                                                    
   EX_TARGET     0      Flag    indicates whether a variant is within the exon pull down target boundaries                            
   MULTI_ALLELIC 0      Flag    indicates whether a site is multi-allelic                                                             
geno(vcf):
  SimpleList of length 1: GT
geno(header(vcf)):
      Number Type   Description
   GT 1      String Genotype   

Now we calculate LD structure relative to our index variant in the european population background. This operation only operates on SNVs and will not return indels or other variants.

You will also notice that the rowRanges of the vcf object has been updated with additional data about each variants.

vcf10q26snps <- CalcLD(vcf = vcf10q26, index = index10q26, population = "EUR")
CalcLD only operates on SNVs some of your variants were dropped at this step, set return = 'all', to override this behaviour
Attaching package: 'Matrix'

The following object is masked from 'package:VariantAnnotation':

    expand

The following object is masked from 'package:S4Vectors':

    expand
vcf10q26snps
class: CollapsedVCF 
dim: 30105 503 
rowRanges(vcf):
  GRanges with 10 metadata columns: ref, alt, refAlleleFreq, altAlleleFreq, HWEpvalue, indexSNP, population, distanceToIndex, D.prime, R.squared
info(vcf):
  DataFrame with 27 columns: CIEND, CIPOS, CS, END, IMPRECISE, MC, MEINFO, MEND, MLEN, MSTART, SVLEN, SVTYPE, TSD, AC, AF, NS, AN, ...
info(header(vcf)):
                 Number Type    Description                                                                                           
   CIEND         2      Integer Confidence interval around END for imprecise variants                                                 
   CIPOS         2      Integer Confidence interval around POS for imprecise variants                                                 
   CS            1      String  Source call set.                                                                                      
   END           1      Integer End coordinate of this variant                                                                        
   IMPRECISE     0      Flag    Imprecise structural variation                                                                        
   MC            .      String  Merged calls.                                                                                         
   MEINFO        4      String  Mobile element info of the form NAME,START,END<POLARITY; If there is only 5' OR 3' support for this...
   MEND          1      Integer Mitochondrial end coordinate of inserted sequence                                                     
   MLEN          1      Integer Estimated length of mitochondrial insert                                                              
   MSTART        1      Integer Mitochondrial start coordinate of inserted sequence                                                   
   SVLEN         .      Integer SV length. It is only calculated for structural variation MEIs. For other types of SVs; one may cal...
   SVTYPE        1      String  Type of structural variant                                                                            
   TSD           1      String  Precise Target Site Duplication for bases, if unknown, value will be NULL                             
   AC            A      Integer Total number of alternate alleles in called genotypes                                                 
   AF            A      Float   Estimated allele frequency in the range (0,1)                                                         
   NS            1      Integer Number of samples with data                                                                           
   AN            1      Integer Total number of alleles in called genotypes                                                           
   EAS_AF        A      Float   Allele frequency in the EAS populations calculated from AC and AN, in the range (0,1)                 
   EUR_AF        A      Float   Allele frequency in the EUR populations calculated from AC and AN, in the range (0,1)                 
   AFR_AF        A      Float   Allele frequency in the AFR populations calculated from AC and AN, in the range (0,1)                 
   AMR_AF        A      Float   Allele frequency in the AMR populations calculated from AC and AN, in the range (0,1)                 
   SAS_AF        A      Float   Allele frequency in the SAS populations calculated from AC and AN, in the range (0,1)                 
   DP            1      Integer Total read depth; only low coverage data were counted towards the DP, exome data were not used        
   AA            1      String  Ancestral Allele. Format: AA|REF|ALT|IndelType. AA: Ancestral allele, REF:Reference Allele, ALT:Alt...
   VT            .      String  indicates what type of variant the line represents                                                    
   EX_TARGET     0      Flag    indicates whether a variant is within the exon pull down target boundaries                            
   MULTI_ALLELIC 0      Flag    indicates whether a site is multi-allelic                                                             
geno(vcf):
  SimpleList of length 1: GT
geno(header(vcf)):
      Number Type   Description
   GT 1      String Genotype   
rowRanges(vcf10q26snps)
GRanges object with 30105 ranges and 10 metadata columns:
              seqnames                 ranges strand |            ref             alt refAlleleFreq altAlleleFreq HWEpvalue
                 <Rle>              <IRanges>  <Rle> | <DNAStringSet> <CharacterList>     <numeric>     <numeric> <numeric>
   rs78692379    chr10 [122837367, 122837367]      * |              C               T      0.999006  0.0009940358 0.9821958
  rs535445408    chr10 [122837368, 122837368]      * |              G               A      1.000000  0.0000000000      <NA>
  rs556048793    chr10 [122837392, 122837392]      * |              T               C      1.000000  0.0000000000      <NA>
  rs139516184    chr10 [122837424, 122837424]      * |              C               T      1.000000  0.0000000000      <NA>
  rs553171117    chr10 [122837426, 122837426]      * |              C               G      0.999006  0.0009940358 0.9821958
          ...      ...                    ...    ... .            ...             ...           ...           ...       ...
  rs530559069    chr10 [123837244, 123837244]      * |              G               T     1.0000000   0.000000000      <NA>
  rs550282246    chr10 [123837263, 123837263]      * |              C               T     1.0000000   0.000000000      <NA>
  rs192908348    chr10 [123837273, 123837273]      * |              C               T     1.0000000   0.000000000      <NA>
  rs539947923    chr10 [123837299, 123837299]      * |              G               A     0.9980119   0.001988072 0.9643651
  rs184426385    chr10 [123837319, 123837319]      * |              G               C     1.0000000   0.000000000      <NA>
                 indexSNP  population distanceToIndex   D.prime    R.squared
              <character> <character>       <integer> <numeric>    <numeric>
   rs78692379   rs2981579         EUR          499968         1 0.0008183719
  rs535445408   rs2981579         EUR          499967      <NA>         <NA>
  rs556048793   rs2981579         EUR          499943      <NA>         <NA>
  rs139516184   rs2981579         EUR          499911      <NA>         <NA>
  rs553171117   rs2981579         EUR          499909         1 0.0008183719
          ...         ...         ...             ...       ...          ...
  rs530559069   rs2981579         EUR          499909      <NA>         <NA>
  rs550282246   rs2981579         EUR          499928      <NA>         <NA>
  rs192908348   rs2981579         EUR          499938      <NA>         <NA>
  rs539947923   rs2981579         EUR          499964         1  0.001638374
  rs184426385   rs2981579         EUR          499984      <NA>         <NA>
  -------
  seqinfo: 1 sequence from hg19 genome

Next we use the “SplitVcfLd” as a convenience function to define our foreground and background sets based on LD. This is a sensible choice since other variants in the same region are likely to have similar properties relative to genomic features like gene Density, gc bias, epigenetic marks etc. As an alternative, we might consider using a resource like SNPsnap to match these properties genome wide.

vcf10q26snps <- SplitVcfLd(vcf = vcf10q26snps, ld = c(metric = "R.squared",
                                                      cutoff = 0.8,
                                                      maf = 0.01))

Functional enrichment by chromatin state

In the next part of our workflow, we want to determine whether the index SNP and its LD proxies, which we defined to be in the “foreground”, are enriched in specific cellular features. As alluded to earlier, the FunciVar package could be used to carry out any analagous operation to find enrichment of one set of features in another. Here we plan to use chromatin state calls from 119 human tissues in Roadmap, which we’ve imported at the beginning of our session. We specify that we are using this type of annotation with the “feature.type” argument (alternative “biofeatures” argument can be used for single element features, e.g. a bed file with H3K27ac peaks). Using the “CalculateEnrichment” function we generate an object containing summary enrichment statistics.

Enrichment calculations

FunciVar by default calculates a likelihood based on the beta-binomial distribution, returning a 95% credible interval (optionally set by the “CI” argument) for the range of differences between the two populations of variants (i.e. foreground and background). Specifically it calculates a distribution of true enrichment (as probability of overlap) for both sets of variants in the genomic features based on the observed number of overlaps:

\[\theta_{fg} ~ Beta(S_{fg} + \alpha, N_{fg} + \beta)\] \[\theta_{bg} ~ Beta(S_{bg} + \alpha, N_{bg} + \beta)\]

for S successes in N trials. FunciVar uses an uninformative Jeffreys prior c(a=0.5, b=0.5) to compare the two distributions directly by subtracting permuted samples to obtain the distribution of differences. The prior can be overidden in special cases (see documentation).

Jeffreys prior and flat prior densities

Jeffreys prior and flat prior densities

To perform these operations with funciVar, we call the “CalculateEnrichment” function.

enrich10q26 <- CalculateEnrichment(variants = vcf10q26snps,
                                   features = esegs,
                                   feature.type = "segmentations", 
                                   CI = 0.8, 
                                   return.overlaps = TRUE)

   |                                                  | 0 % ~calculating  
   |+++++++++                                         | 17% ~03s          
   |+++++++++++++++++                                 | 33% ~02s          
   |+++++++++++++++++++++++++                         | 50% ~01s          
   |++++++++++++++++++++++++++++++++++                | 67% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 02s

At first perhaps we are interested which cell types have the most overlaps with with snps, say among active enhancers.

snps.10q26.enhancers <- enrich10q26$overlaps$`Active Enhancer`
fg.snps.df <- as.data.frame(mcols(snps.10q26.enhancers$foregound.overlaps))
bg.snps.df <- as.data.frame(mcols(snps.10q26.enhancers$background.overlaps))
upset(fg.snps.df, nsets = 7)
upset(bg.snps.df, nsets = 7)

Foreground Background

While it looks like the snps are largely falling within hepatocyte active enhancers, if we examine the enrichment statistics we can see otherwise. Now we’ll extract the enrichment stats and plot the enrichment profile of the variants.

enrich10q26.enrich <- enrich10q26$enrichment
PlotEnrichment(enrich10q26.enrich, 
               value = "difference", 
               block1 = "state", 
               color.by = "sample", 
               ncol = 6)

Annotations of interest

While there is a tendency for SNPs in LD with each other to be enriched in some cellular interest, it is probable that only a subset of them are of functional interest. In our case, we would like to examine the hypothesis that one of our putative functional SNPs disrupts a transcription factor binding site.

Since our index SNP was found in association with breast cancer, and we see enrichment of it’s LD proxies in MCF7, we’ll choose linked variants that overlap Active Enhancer in the MCF7 cell line from ENCODE. Segmentation features live in the overlaps slot of the enrichment object from the previous steps.

snps.10q26.enhancers <- snps.10q26.enhancers$foregound.overlaps[snps.10q26.enhancers$foregound.overlaps$MCF.7 > 0, ]
snps.10q26.enhancers
GRanges object with 20 ranges and 7 metadata columns:
             seqnames                 ranges strand | bipolar.spindle.neuron   GM12878 hepatocyte induced.pluripotent.stem.cell
                <Rle>              <IRanges>  <Rle> |              <integer> <integer>  <integer>                     <integer>
  rs72830627    chr10 [123006362, 123006362]      * |                      0         0          0                             0
   rs1896416    chr10 [123084490, 123084490]      * |                      0         0          1                             0
   rs1078806    chr10 [123338975, 123338975]      * |                      1         0          1                             0
  rs11599804    chr10 [123340664, 123340664]      * |                      1         0          1                             0
   rs1219642    chr10 [123348389, 123348389]      * |                      0         0          0                             0
         ...      ...                    ...    ... .                    ...       ...        ...                           ...
  rs34354213    chr10 [123794212, 123794212]      * |                      0         0          0                             0
  rs11200359    chr10 [123794217, 123794217]      * |                      0         0          0                             0
   rs2459091    chr10 [123819539, 123819539]      * |                      0         0          0                             0
   rs2459090    chr10 [123819715, 123819715]      * |                      0         0          1                             0
   rs2461233    chr10 [123819808, 123819808]      * |                      0         0          1                             0
                 MCF.7 neutrophil     DOHH2
             <integer>  <integer> <integer>
  rs72830627         1          0         0
   rs1896416         1          0         0
   rs1078806         1          0         0
  rs11599804         1          0         0
   rs1219642         1          0         0
         ...       ...        ...       ...
  rs34354213         1          0         0
  rs11200359         1          0         0
   rs2459091         1          0         0
   rs2459090         1          0         0
   rs2461233         1          0         0
  -------
  seqinfo: 1 sequence from hg19 genome

MotifBreakR analysis of TF binding disruption

Now that we have a SNP of interest from breast tissue enhancer, let’s work with the motifBreakR package to determine whether a putative transcription factor binding site (TFBS) is disrupted. MotifBreakR allows us to do this with nothing more than the rsID or a location and allele information. It enables us to use any genome housed in bioconductor BSgenome packages and any set of motifs curated in the MotifDB package (plus some custom databases in MotifDB format that are included with the MotifBreakR installation)1.

We also have some slides explaining how motifbreakR works in addition to the paper and vignette in the footnote indicated above.

Setup

We need to import some version of the human genome and a compatible set of variants from Bioconductor.

library(motifbreakR)
library(BSgenome.Hsapiens.UCSC.hg19)
library(SNPlocs.Hsapiens.dbSNP144.GRCh37)

We need to grab the SNP variant objects out of SNPlocs package that correspond to our enhancer proxies.

snps.mb.10q26.enhancers <- snps.from.rsid(rsid = names(snps.10q26.enhancers),
                                          dbSNP = SNPlocs.Hsapiens.dbSNP144.GRCh37,
                                          search.genome = BSgenome.Hsapiens.UCSC.hg19)
the condition has length > 1 and only the first element will be used
snps.mb.10q26.enhancers
GRanges object with 21 ranges and 4 metadata columns:
              seqnames                 ranges strand |      SNP_id alleles_as_ambig            REF            ALT
                 <Rle>              <IRanges>  <Rle> | <character>   <DNAStringSet> <DNAStringSet> <DNAStringSet>
   rs10736308    chr10 [123601849, 123601849]      * |  rs10736308                S              G              C
    rs1078806    chr10 [123338975, 123338975]      * |   rs1078806                R              A              G
   rs11200358    chr10 [123794209, 123794209]      * |  rs11200358                K              G              T
   rs11200359    chr10 [123794217, 123794217]      * |  rs11200359                Y              T              C
  rs113012332    chr10 [123747830, 123747830]      * | rs113012332                Y              C              T
          ...      ...                    ...    ... .         ...              ...            ...            ...
    rs2981584    chr10 [123350216, 123350216]      * |   rs2981584                M              A              C
   rs34354213    chr10 [123794212, 123794212]      * |  rs34354213                Y              C              T
    rs3862115    chr10 [123793061, 123793061]      * |   rs3862115                Y              T              C
   rs72830627    chr10 [123006362, 123006362]      * |  rs72830627                R              A              G
   rs76133150    chr10 [123747500, 123747500]      * |  rs76133150                R              A              G
  -------
  seqinfo: 93 sequences (1 circular) from hg19 genome

Analysis

Now a simple call to the motifbreakR function completes the analysis. Here, we specify that we want to filter our results by p-value (rather than scaled motif scores), we’ll set that p-value threshold at 10^-4, using information content (set argument method = "ic") and a uniform prior on the GC content.

results.10q26.enhancers <- motifbreakR(snpList = snps.mb.10q26.enhancers,
                                       filterp = TRUE,
                                       pwmList = hocomoco,
                                       threshold = 1e-4,
                                       method = "ic",
                                       bkg = c(A=0.25, C=0.25, G=0.25, T=0.25),
                                       BPPARAM = BiocParallel::bpparam())
'*' ranges were treated as '+''*' ranges were treated as '+''*' ranges were treated as '+''*' ranges were treated as '+''*' ranges were treated as '+''*' ranges were treated as '+'
results.10q26.enhancers
GRanges object with 39 ranges and 18 metadata columns:
             seqnames                 ranges strand |            REF            ALT    snpPos  motifPos  geneSymbol  dataSource
                <Rle>              <IRanges>  <Rle> | <DNAStringSet> <DNAStringSet> <integer> <integer> <character> <character>
   rs1219642    chr10 [123348377, 123348389]      - |              T              C 123348389         1       CEBPG    HOCOMOCO
   rs2459091    chr10 [123819533, 123819546]      + |              C              T 123819539         7        ELF3    HOCOMOCO
   rs2459091    chr10 [123819537, 123819548]      + |              C              T 123819539         3        ELK3    HOCOMOCO
   rs2981583    chr10 [123350516, 123350528]      - |              A              G 123350523         6       EPAS1    HOCOMOCO
   rs2981584    chr10 [123350207, 123350218]      - |              A              C 123350216         3       FUBP1    HOCOMOCO
         ...      ...                    ...    ... .            ...            ...       ...       ...         ...         ...
   rs2981584    chr10 [123350202, 123350218]      + |              A              C 123350216        15        TLX1    HOCOMOCO
  rs11200358    chr10 [123794197, 123794218]      + |              G              T 123794209        13      ZBTB7B    HOCOMOCO
  rs11200358    chr10 [123794199, 123794217]      + |              G              T 123794209        11         ZFX    HOCOMOCO
  rs34354213    chr10 [123794199, 123794217]      + |              C              T 123794212        14         ZFX    HOCOMOCO
  rs11200358    chr10 [123794198, 123794212]      - |              G              T 123794209         4      ZNF423    HOCOMOCO
             providerName  providerId                                seqMatch    pctRef    pctAlt  scoreRef  scoreAlt Refpvalue
              <character> <character>                             <character> <numeric> <numeric> <numeric> <numeric> <logical>
   rs1219642     CEBPG_si CEBPG_HUMAN   acaattgcaccaT                         0.9326388 0.9131911  8.110324  7.941205      <NA>
   rs2459091      ELF3_f1  ELF3_HUMAN         gtgataCaggaact                  0.9240643 0.8269081  9.003045  8.056464      <NA>
   rs2459091      ELK3_f1  ELK3_HUMAN             taCaggaactgc                0.9415539 0.8235314  6.955565  6.083694      <NA>
   rs2981583     EPAS1_si EPAS1_HUMAN        cacacgcAcactc                    0.9410674 0.8898237  7.272689  6.876672      <NA>
   rs2981584     FUBP1_f1 FUBP1_HUMAN      acaaagccaAga                       0.7012659 0.9244287  5.395118  7.111998      <NA>
         ...          ...         ...                                     ...       ...       ...       ...       ...       ...
   rs2981584      TLX1_f2  TLX1_HUMAN attgcacaaagccaAga                       0.9011307 0.8206667  4.555611  4.148830      <NA>
  rs11200358     ZBT7B_si ZBT7B_HUMAN   agcactttgggaGgccgaggtg                0.8119717 0.7656583  9.442073  8.903514      <NA>
  rs11200358       ZFX_f1   ZFX_HUMAN     cactttgggaGgccgaggt                 0.9352668 0.7682406 10.638093  8.738271      <NA>
  rs34354213       ZFX_f1   ZFX_HUMAN  cactttgggaggcCgaggt                    0.9352668 0.8312528 10.638093  9.454997      <NA>
  rs11200358     ZN423_f1 ZN423_HUMAN    gcactttgggaGgcc                      0.6739751 0.7503685 14.692071 16.357381      <NA>
             Altpvalue  alleleRef  alleleAlt      effect
             <logical>  <numeric>  <numeric> <character>
   rs1219642      <NA> 0.60000000  0.1485714        weak
   rs2459091      <NA> 0.86507937  0.0000000      strong
   rs2459091      <NA> 0.90566038  0.0000000      strong
   rs2981583      <NA> 0.73914182  0.1676945        weak
   rs2981584      <NA> 0.01904762  0.9809524      strong
         ...       ...        ...        ...         ...
   rs2981584      <NA>  0.6653994 0.01631485        weak
  rs11200358      <NA>  0.7443182 0.00000000      strong
  rs11200358      <NA>  0.9929020 0.00000000      strong
  rs34354213      <NA>  0.8850690 0.02484316      strong
  rs11200358      <NA>  0.0000000 1.00000000      strong
  -------
  seqinfo: 93 sequences (1 circular) from hg19 genome

MotifbreakR does a quick estimate of the p-value threshold initially. To obtain accurate p-values, we run “calculatePvalue” in a seperate step (beware this can be slow!).

pvalue.results.10q26.enhancers <- calculatePvalue(results = results.10q26.enhancers)

Visualization

MotifbreakR includes a plotting function to visualize the SNP in its genomic context, aligned with the motif logos it is purported to alter.

plotMB(results.10q26.enhancers, "rs34354213", effect = "strong")

Here we can see that the variant disrupts a Pax-5 motif which is interesting given the results in this paper Breast Cancer Malignant Processes are Regulated by Pax-5 Through the Disruption of FAK Signaling Pathways


  1. see the manuscript and vignette for details.

LS0tCnRpdGxlOiAiVmFyaWFudCBBbm5vdGF0aW9uIFdvcmtzaG9wIHdpdGggRnVuY2lWQVIsIFN0YXRlSHViIGFuZCBNb3RpZkJyZWFrUiIKYXV0aG9yOiAiU2ltb24gRy4gQ29ldHplZSAmIERlbm5pcyBKLiBIYXplbGV0dCIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0aGVtZTogam91cm5hbAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQoKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXtGdW5jaVZhciwgTW90aWZCcmVha1IgYW5kIFN0YXRlUGFpbnRSfQogICVcVmlnbmV0dGVFbmdpbmV7a25pdHI6OnJtYXJrZG93bn0KICAlXFZpZ25ldHRlRW5jb2Rpbmd7VVRGLTh9Ci0tLQoKIyBGdW5jVkFSOiBBbm5vdGF0aW9uIGFuZCBmdW5jdGlvbmFsIGVucmljaG1lbnQgb2YgdmFyaWFudCBzZXRzCgpJbiB0aGlzIHZpZ25ldHRlIHdlIHdpbGwgbGVhcm4gdG8gdGFrZSBhIHNldCBvZiB2YXJpYW50cywgYXJiaXRyYXJpbHkgZGVmaW5lZCBhcyBhbnkgc2V0IG9mIEdlbm9taWNSYW5nZXMsIG92ZXJsYXAgd2l0aCBtdWx0aW9taWNzIGRhdGFzZXRzIGFuZCB0aGVuIGNhbGN1bGF0ZSBlbnJpY2htZW50IGFnYWluc3QgYW4gYXBwcm9wcmlhdGVseSBjaG9zZW4gYmFja2dyb3VuZCBzZXQuCgojIyBTZXR1cAoKRmlyc3Qgd2UgbmVlZCB0byBpbXBvcnQgdGhlIHBhY2thZ2VzIHdlJ2xsIGJlIHVzaW5nLiBGdW5jaVZhciBjYW4gYmUgYWNjZXNzZWQgZGlyZWN0bHkgZnJvbSBvdXIgZ2l0aHViIHJlcG9zaXRvcmllcyB2aWEgYmlvY0xpdGUuIEdlbm9taWNSYW5nZXMgYW5kIFZhcmlhbnRBbm5vdGF0aW9uIHBhY2thZ2VzIGFyZSBhdmFpbGFibGUgdGhyb3VnaCBiaW9jb25kdWN0b3I6CgpJbnN0YWxsIHRoZSByZXF1aXJlZCBwYWNrYWdlcywgaWYgdGhleSBhcmUgbm90IGFscmVhZHkgYXZhaWxpYmxlLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9CnNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQpiaW9jTGl0ZSgiU2ltb24tQ29ldHplZS9TdGF0ZVBhaW50UiIsIHN1cHByZXNzVXBkYXRlcyA9IFRSVUUsIGRlcGVuZGVuY2llcyA9IFRSVUUpCmJpb2NMaXRlKCJTaW1vbi1Db2V0emVlL2Z1bmNpVmFyIiwgc3VwcHJlc3NVcGRhdGVzID0gVFJVRSwgZGVwZW5kZW5jaWVzID0gVFJVRSkKYmlvY0xpdGUoIlNpbW9uLUNvZXR6ZWUvbW90aWZicmVha1IiLCBzdXBwcmVzc1VwZGF0ZXMgPSBUUlVFLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQpiaW9jTGl0ZSgiU05QbG9jcy5Ic2FwaWVucy5kYlNOUDE0NC5HUkNoMzciKQpgYGAKVGhlbiBsb2FkIHRoZSBwYWNrYWdlcyB3ZSdsbCBiZSB1c2luZyBpbiB0aGlzIHdvcmtzaG9wLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KFN0YXRlUGFpbnRSKQpsaWJyYXJ5KGZ1bmNpVmFyKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCmxpYnJhcnkoVmFyaWFudEFubm90YXRpb24pCmxpYnJhcnkoVXBTZXRSKQpgYGAKCiMjIEdldHRpbmcgYW5ub3RhdGlvbnMKCkFubm90YXRpb25zIGNhbiBjb21lIGZyb20gYW55IHNvdXJjZSwgdGhlIGtleSByZXF1aXJlbWVudCBiZSB0aGF0IHRoZXkgYmUgY29lcmNlZCBpbnRvIEdSYW5nZXMgZm9ybWF0IGZvciB1c2UgYnkgdGhlIGZ1bmNpVmFyIHBhY2thZ2UuIEZvciBvdXIgcHVycG9zZXMsIFdlIGNhbiBzZWFyY2ggb24gU3RhdGVIdWIgZm9yIGFsbCBjZWxsIHR5cGVzIHRoYXQgYXJlIGFubm90YXRlZCBpbiAqKmhnMTkqKiB3aXRoIHNlZ21lbnRhdGlvbnMgY29tYmluaW5nIGF0IGxlYXN0LCAqKkgzSzRtZTEsIEgzSzRtZTMsIEgzSzI3YWMsIGFuZCBDVENGKiouCgohWypTdGF0ZUh1YiBTZWFyY2ggZm9yIGhnMTksIEgzSzRtZTEsIEgzSzRtZTMsIEgzSzI3YWMsIGFuZCBDVENGKl0oc3RhdGVodWJzZWFyY2gucG5nKQoKRWFjaCBzZWdtZW50YXRpb24gZmlsZSBjb250YWlucyBhIHNldCBvZiBjaHJvbWF0aW4gc3RhdGUgY2FsbHMgbWFkZSBieSBvdXIgU3RhdGVQYWludFIgcGFja2FnZS4KCiMjIyBIb3cgZG9lcyBTdGF0ZVBhaW50UiB3b3JrPwpMZXRzIGxvb2sgYXQgKlN0YXRlUGFpbnRSKjogW3NsaWRlc10oaHR0cDovL2dvby5nbC93dlBWYmgpIGFuZCBbcm1hcmtkb3duXShodHRwOi8vd3d3LnN0YXRlaHViLm9yZy9zdGF0ZWh1Yl9tZWRpYS9zdGF0ZXBhaW50ci5uYi5odG1sKQoKIyMjIFJldHVybmluZyB0byBBbmFseXNpcwpGdW5jaVZhciBpbXBvcnRzIHRoZXNlIGZpbGVzIGVmZmljaWVudGx5IGJ5IHBvaW50aW5nIGEgZnVuY3Rpb24gY2FsbGVkICJHZXRTZWdtZW50YXRpb25zIiBkaXJlY3RseS4gYEdldFNlZ21lbnRhdGlvbnMoKWAgZXhwZWN0cyBmaWxlcyB0byBiZSBpbiBbQkVEXShodHRwczovL2dlbm9tZS51Y3NjLmVkdS9GQVEvRkFRZm9ybWF0I2Zvcm1hdDEpIGZvcm1hdCB3aXRoIGNvbHVtbiA0IChOYW1lKSBjb3JyZXNwb25kaW5nIHRvIHRoZSBzdGF0ZSBjYWxsLiBUaGVuIGl0J3MganVzdCBhIG1hdHRlciBvZiByZW5hbWluZyB0aGUgc3RhdGUgYWJicmV2aWF0aW9ucyB0byBlYXNpbHkgcmVjb2duaXplZCwgaHVtYW4gcmVhZGFibGUgZm9ybS4KCmBgYHtyfQojIyBCYXNlIHVybCBvZiBTdGF0ZUh1YiBmaWxlcyBpbiBvdXIgc2VhcmNoCgpzdGF0ZWh1Yi5lbmNvZGUuYXdzIDwtICJodHRwOi8vczMtdXMtd2VzdC0yLmFtYXpvbmF3cy5jb20vc3RhdGVodWItdHJhY2todWIvdHJhY2tzLzU4MTNiNjdmNDZlMGZiMDZiNDkzY2ViMC9oZzE5L0VOQ09ERS8iCgojIyBBIGxpc3Qgb2YgU3RhdGVIdWIgdXJscwoKc2VnbWVudGF0aW9uLmZpbGVzIDwtIGMoCiAgcGFzdGUwKHN0YXRlaHViLmVuY29kZS5hd3MsCiAgICAgICAgICJiaXBvbGFyX3NwaW5kbGVfbmV1cm9uLjhtYXJrLnNlZ21lbnRhdGlvbi5iZWQiKSwKICBwYXN0ZTAoc3RhdGVodWIuZW5jb2RlLmF3cywgCiAgICAgICAgICJkb2hoMi44bWFyay5zZWdtZW50YXRpb24uYmVkIiksCiAgcGFzdGUwKHN0YXRlaHViLmVuY29kZS5hd3MsIAogICAgICAgICAiZ20xMjg3OC4xMW1hcmsuc2VnbWVudGF0aW9uLmJlZCIpLAogIHBhc3RlMChzdGF0ZWh1Yi5lbmNvZGUuYXdzLAogICAgICAgICAiaGVwYXRvY3l0ZS45bWFyay5zZWdtZW50YXRpb24uYmVkIiksCiAgcGFzdGUwKHN0YXRlaHViLmVuY29kZS5hd3MsCiAgICAgICAgICJpbmR1Y2VkX3BsdXJpcG90ZW50X3N0ZW1fY2VsbC43bWFyay5zZWdtZW50YXRpb24uYmVkIiksCiAgcGFzdGUwKHN0YXRlaHViLmVuY29kZS5hd3MsIAogICAgICAgICAibWNmLTcuMTJtYXJrLnNlZ21lbnRhdGlvbi5iZWQiKSwKICBwYXN0ZTAoc3RhdGVodWIuZW5jb2RlLmF3cywKICAgICAgICAgIm5ldXRyb3BoaWwuOG1hcmsuc2VnbWVudGF0aW9uLmJlZCIpKQoKIyMgSW1wb3J0IFN0YXRlcwoKZW5jb2RlLnNlZ21lbnRhdGlvbnMgPC0gR2V0U2VnbWVudGF0aW9ucyhmaWxlcyA9IHNlZ21lbnRhdGlvbi5maWxlcykKZXNlZ3MgPC0gdW5saXN0KGVuY29kZS5zZWdtZW50YXRpb25zKQplc2VncwoKIyMgR3JvdXAgU3RhdGVzIGluIEh1bWFuIHJlYWRhYmxlIGNvbGxlY3Rpb25zLgoKbWNvbHMoZXNlZ3NbZXNlZ3Mkc3RhdGUgJWluJSBjKCJFQVIiLCAiQVIiKSwgXSkkc3RhdGUgPC0gIkFjdGl2ZSBFbmhhbmNlciIKbWNvbHMoZXNlZ3NbZXNlZ3Mkc3RhdGUgJWluJSBjKCJFUFIiLCAiRVdSIiksIF0pJHN0YXRlIDwtICJXZWFrIEVuaGFuY2VyIgptY29scyhlc2Vnc1tlc2VncyRzdGF0ZSAlaW4lIGMoIlBBUiIpLCBdKSRzdGF0ZSA8LSAiQWN0aXZlIFByb21vdGVyIgptY29scyhlc2Vnc1tlc2VncyRzdGF0ZSAlaW4lIGMoIlBQUiIsICJQUFdSIiwgIlBXUiIpLCBdKSRzdGF0ZSA8LSAiV2VhayBQcm9tb3RlciIKbWNvbHMoZXNlZ3NbZXNlZ3Mkc3RhdGUgJWluJSBjKCJDVENGIiksIF0pJHN0YXRlIDwtICJDVENGIgptY29scyhlc2Vnc1tlc2VncyRzdGF0ZSAlaW4lIGMoIkhFVCIsICJTQ1IiLCAiVFJTIiksIF0pJHN0YXRlIDwtICJPdGhlciIKYGBgCgojIyBJbXBvcnRpbmcgVmFyaWFudHMKClRoZXJlIGFyZSB0d28gcG90ZW50aWFsIHVzZSBjYXNlcyB0aGF0IHdlIGVudmlzaW9uIGZvciBmdW5jaVZhci4gSW4gdGhlIGZpcnN0IGNhc2UsIG91ciBlbnJpY2htZW50IGNhbGN1bGF0aW9ucyBhcmUgYmFzZWQgb24gYSBmb3JlZ3JvdW5kIGFuZCBiYWNrZ3JvdW5kIHNldCB0aGF0IGFyZSBwcmVkZWZpbmVkIGFuZCBub3RoaW5nIG5lZWRzIHRvIGJlIGRvbmUgb3V0c2lkZSBvZiBpbXBvcnRpbmcgdG8gUiBhbmQgY29lcmNpbmcgaW50byBHUmFuZ2VzLiBTdWNoIGZvcmVncm91bmQgc2V0cyBjb3VsZCBpbmNsdWRlIGFueSBraW5kIG9mIHZhcmlhbnQ7IGNvbW1vbiBwb3B1bGF0aW9uIHBvbHltb3JwaGlzbXMgb3IgZGlmZmVyZW50aWFsbHkgbWV0aHlsYXRlZCBDcEdzIGZvciBleGFtcGxlLiAKCkluIHRoZSBzZWNvbmQgY2FzZSwgZXNwZWNpYWxseSBjb21tb24gZm9yIHByaW1hcnkgR1dBUyBzdHVkaWVzLCBhIHNpbmdsZSB0b3AgImhpdCIgaGFzIGJlZW4gaWRlbnRpZmllZCBhbmQgYSBzZXQgb2YgdmFyaWFudHMga25vd24gdG8gYmUgaW4gbGlua2FnZSBkaXNlcXVpbGlicml1bSAoTEQpIHNob3VsZCBiZSBjYWxjdWxhdGVkLiBXZSB3aWxsIGNvdmVyIHRoaXMgbGF0dGVyIGNhc2UuIFRoZSBMRCBjYWxjdWxhdGlvbnMgY2FuIGJlIHVzZWQgdG8gZnVydGhlciBzdWJkaXZpZGUgcmVnaW9uYWwgdmFyaWFudHMgaW50byBmb3JlZ3JvdW5kIGFuZCBiYWNrZ3JvdW5kIGJhc2VkIG9uIHNvbWUgTEQgdGhyZXNob2xkICh0eXBpY2FsbHkgYXQgUiZzdXAyOyA9IDAuOCkuIEluIG90aGVyIGluc3RhbmNlcywgbW9yZSBjcmVhdGl2ZSBtZXRob2RzIG9mIG9idGFpbmluZyBhIGJhY2tncm91bmQgc2V0IG1pZ2h0IGJlIHdhcnJhbnRlZC4KCldlIHdpbGwgdXNlIDEwMDAgZ2Vub21lcyBkYXRhIGZvciBvdXIgTEQgY2FsY3VsYXRpb25zLCBidXQgaW4gcHJpbmNpcGxlIHdlIGNvdWxkIGFwcGx5IHRoaXMgdG8gYW55IHBvcHVsYXRpb24gc3RvcmVkIGluIFZDRi4gRmlyc3Qgd2UgbmVlZCB0byBpZGVudGlmeSB0aGUgZmlsZSBjb250YWluaW5nIHRoZSAxMDAwIGFuZCBhc3NpZ24gYWxsIHRoZSBzYW1wbGUgbWV0YWRhdGEgaW50byBhIHZhcmlhYmxlIGNhbGxlZCAibXkuc2FtcGxlcyIuCgpgYGB7cn0KbXkuc2FtcGxlcyA8LSByZWFkLmRlbGltKCJodHRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay92b2wxL2Z0cC9yZWxlYXNlLzIwMTMwNTAyL2ludGVncmF0ZWRfY2FsbF9zYW1wbGVzX3YzLjIwMTMwNTAyLkFMTC5wYW5lbCIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKbXkuc2FtcGxlcyA8LSBteS5zYW1wbGVzWywgYygic2FtcGxlIiwgInBvcCIsICJzdXBlcl9wb3AiLCAiZ2VuZGVyIildCm15LnNhbXBsZXMKYGBgCgpOZXh0IHdlIHdpbGwgaWRlbnRpZnkgdGhlIGZpbGUgd2l0aCBjaHJvbW9zb21lIDEwIHZhcmlhbnRzIGFuZCBkZWZpbmUgdGhlIGluZGV4IHJlZ2lvbiBhcyBhIGZpbHRlciBmb3IgdGhlIGZvcmVncm91bmQgdmFyaWFudCBzZXQuIEZvciBzaW1wbGljaXR5J3Mgc2FrZSwgd2UgYXJlIHRlc3RpbmcgYSBzaW5nbGUgcmVnaW9uLCBidXQgd2UgY291bGQgZWFzaWx5IGFuYWx5emUgbXVsdGlwbGUgcmFuZ2VzLiBGb3IgdGhpcyBleGFtcGxlLCB3ZSBhcmUgdXNpbmcgYSBHV0FTIHZhcmlhbnQgbGlua2VkIHRvIGJyZWFzdCBjYW5jZXIgb24gY2hyb21vc29tZSAxMC4KCmBgYHtyfQpjaHIxMC5yZW1vdGUudmNmIDwtICJmdHA6Ly9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL3ZvbDEvZnRwL3JlbGVhc2UvMjAxMzA1MDIvQUxMLmNocjEwLnBoYXNlM19zaGFwZWl0Ml9tdm5jYWxsX2ludGVncmF0ZWRfdjVhLjIwMTMwNTAyLmdlbm90eXBlcy52Y2YuZ3oiCmluZGV4MTBxMjYgPC0gInJzMjk4MTU3OSIKcG9zMTBxMjYgPC0gR1JhbmdlcygiMTA6MTIzMzM3MzM1LTEyMzMzNzMzNSIpICsgNTAwMDAwCnBvczEwcTI2CmBgYAoKVXNpbmcgdGhlIDFNQiB3aW5kb3cgdGhhdCB3ZSBkZWZpbmVkIGZvciB0aGUgaW5kZXggdmFyaWFudCwgd2UgY2FwdHVyZSB0aGUgcmVsZXZhbnQgdmFyaWFudHMgZnJvbSB0aGUgVkNGIGZpbGUgZm9yIG91ciBMRCBjYWxjdWxhdGlvbnMuCgpgYGB7ciwgZXZhbD1GQUxTRX0KdmNmMTBxMjYgPC0gR2V0VmFyaWFudHNJbldpbmRvdyhmaWxlID0gY2hyMTAucmVtb3RlLnZjZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3MxMHEyNiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJ2Y2YiKQpgYGAKCkVCSSB0aHJvdHRsZXMgZG93bmxvYWRzIGZyb20gaGVyZSBwcmV0dHkgaGFyZC4gU28gV2UgaGF2ZSBhIGxvY2FsIHZlcnNpb24gb2YgdGhlIHZjZiBpbmNsdWRlZCBhbHNvLCBpbiBjYXNlIHlvdSBnZXQgdGhlIGFib3ZlIGVycm9yLgoKYGBge3J9CmNocjEwLmxvY2FsLnZjZiA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTAwMGdlbm9tZXMuY2hyMTAuMTIyODM2MzM1LTEyMzgzODMzNS52Y2YuZ3oiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2FnZT0iYmlvYzIwMTciKQoKdmNmMTBxMjYgPC0gR2V0VmFyaWFudHNJbldpbmRvdyhmaWxlID0gY2hyMTAubG9jYWwudmNmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvczEwcTI2LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInZjZiIpCmBgYAoKVG8gZW5zdXJlIHRoYXQgd2UgbWFrZSBvdXIgTEQgY2FsY3VsYXRpb24gZnJvbSB0aGUgY29ycmVjdCBwb3B1bGF0aW9uIHN0cnVjdHVyZSwgd2Ugc2V0IHRoZSBwb3B1bGF0aW9uIHVzaW5nIG1ldGFkYXRhIGZyb20gIm15LnNhbXBsZXMiLgoKYGBge3J9CnZjZjEwcTI2IDwtIFNldFBvcHVsYXRpb24odmNmID0gdmNmMTBxMjYsIHNhbXBsZV9zaGVldCA9IG15LnNhbXBsZXMpCnZjZjEwcTI2CmBgYAoKTm93IHdlIGNhbGN1bGF0ZSBMRCBzdHJ1Y3R1cmUgcmVsYXRpdmUgdG8gb3VyIGluZGV4IHZhcmlhbnQgaW4gdGhlIGV1cm9wZWFuIHBvcHVsYXRpb24gYmFja2dyb3VuZC4gVGhpcyBvcGVyYXRpb24gb25seSBvcGVyYXRlcyBvbiBTTlZzIGFuZCB3aWxsIG5vdCByZXR1cm4gaW5kZWxzIG9yIG90aGVyIHZhcmlhbnRzLgoKWW91IHdpbGwgYWxzbyBub3RpY2UgdGhhdCB0aGUgcm93UmFuZ2VzIG9mIHRoZSB2Y2Ygb2JqZWN0IGhhcyBiZWVuIHVwZGF0ZWQgd2l0aCBhZGRpdGlvbmFsIGRhdGEgYWJvdXQgZWFjaCB2YXJpYW50cy4KCmBgYHtyfQp2Y2YxMHEyNnNucHMgPC0gQ2FsY0xEKHZjZiA9IHZjZjEwcTI2LCBpbmRleCA9IGluZGV4MTBxMjYsIHBvcHVsYXRpb24gPSAiRVVSIikKdmNmMTBxMjZzbnBzCnJvd1Jhbmdlcyh2Y2YxMHEyNnNucHMpCmBgYAoKTmV4dCB3ZSB1c2UgdGhlICJTcGxpdFZjZkxkIiBhcyBhIGNvbnZlbmllbmNlIGZ1bmN0aW9uIHRvIGRlZmluZSBvdXIgZm9yZWdyb3VuZCBhbmQgYmFja2dyb3VuZCBzZXRzIGJhc2VkIG9uIExELiBUaGlzIGlzIGEgc2Vuc2libGUgY2hvaWNlIHNpbmNlIG90aGVyIHZhcmlhbnRzIGluIHRoZSBzYW1lIHJlZ2lvbiBhcmUgbGlrZWx5IHRvIGhhdmUgc2ltaWxhciBwcm9wZXJ0aWVzIHJlbGF0aXZlIHRvIGdlbm9taWMgZmVhdHVyZXMgbGlrZSBnZW5lIERlbnNpdHksIGdjIGJpYXMsIGVwaWdlbmV0aWMgbWFya3MgZXRjLiBBcyBhbiBhbHRlcm5hdGl2ZSwgd2UgbWlnaHQgY29uc2lkZXIgdXNpbmcgYSByZXNvdXJjZSBsaWtlIFNOUHNuYXAgdG8gbWF0Y2ggdGhlc2UgcHJvcGVydGllcyBnZW5vbWUgd2lkZS4KCmBgYHtyfQp2Y2YxMHEyNnNucHMgPC0gU3BsaXRWY2ZMZCh2Y2YgPSB2Y2YxMHEyNnNucHMsIGxkID0gYyhtZXRyaWMgPSAiUi5zcXVhcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmID0gMC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWYgPSAwLjAxKSkKYGBgCgojIyBGdW5jdGlvbmFsIGVucmljaG1lbnQgYnkgY2hyb21hdGluIHN0YXRlCgpJbiB0aGUgbmV4dCBwYXJ0IG9mIG91ciB3b3JrZmxvdywgd2Ugd2FudCB0byBkZXRlcm1pbmUgd2hldGhlciB0aGUgaW5kZXggU05QIGFuZCBpdHMgTEQgcHJveGllcywgd2hpY2ggd2UgZGVmaW5lZCB0byBiZSBpbiB0aGUgImZvcmVncm91bmQiLCBhcmUgZW5yaWNoZWQgaW4gc3BlY2lmaWMgY2VsbHVsYXIgZmVhdHVyZXMuIEFzIGFsbHVkZWQgdG8gZWFybGllciwgdGhlIEZ1bmNpVmFyIHBhY2thZ2UgY291bGQgYmUgdXNlZCB0byBjYXJyeSBvdXQgYW55IGFuYWxhZ291cyBvcGVyYXRpb24gdG8gZmluZCBlbnJpY2htZW50IG9mIG9uZSBzZXQgb2YgZmVhdHVyZXMgaW4gYW5vdGhlci4gSGVyZSB3ZSBwbGFuIHRvIHVzZSBjaHJvbWF0aW4gc3RhdGUgY2FsbHMgZnJvbSAxMTkgaHVtYW4gdGlzc3VlcyBpbiBSb2FkbWFwLCB3aGljaCB3ZSd2ZSBpbXBvcnRlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIG91ciBzZXNzaW9uLiBXZSBzcGVjaWZ5IHRoYXQgd2UgYXJlIHVzaW5nIHRoaXMgdHlwZSBvZiBhbm5vdGF0aW9uIHdpdGggdGhlICJmZWF0dXJlLnR5cGUiIGFyZ3VtZW50IChhbHRlcm5hdGl2ZSAiYmlvZmVhdHVyZXMiIGFyZ3VtZW50IGNhbiBiZSB1c2VkIGZvciBzaW5nbGUgZWxlbWVudCBmZWF0dXJlcywgX2UuZy5fIGEgYmVkIGZpbGUgd2l0aCBIM0syN2FjIHBlYWtzKS4gVXNpbmcgdGhlICJDYWxjdWxhdGVFbnJpY2htZW50IiBmdW5jdGlvbiB3ZSBnZW5lcmF0ZSBhbiBvYmplY3QgY29udGFpbmluZyBzdW1tYXJ5IGVucmljaG1lbnQgc3RhdGlzdGljcy4KCiMjIyBFbnJpY2htZW50IGNhbGN1bGF0aW9ucwoKRnVuY2lWYXIgYnkgZGVmYXVsdCBjYWxjdWxhdGVzIGEgbGlrZWxpaG9vZCBiYXNlZCBvbiB0aGUgYmV0YS1iaW5vbWlhbCBkaXN0cmlidXRpb24sIHJldHVybmluZyBhIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCAob3B0aW9uYWxseSBzZXQgYnkgdGhlICJDSSIgYXJndW1lbnQpIGZvciB0aGUgcmFuZ2Ugb2YgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdHdvIHBvcHVsYXRpb25zIG9mIHZhcmlhbnRzIChfaS5lLl8gZm9yZWdyb3VuZCBhbmQgYmFja2dyb3VuZCkuIFNwZWNpZmljYWxseSBpdCBjYWxjdWxhdGVzIGEgZGlzdHJpYnV0aW9uIG9mIHRydWUgZW5yaWNobWVudCAoYXMgcHJvYmFiaWxpdHkgb2Ygb3ZlcmxhcCkgZm9yIGJvdGggc2V0cyBvZiB2YXJpYW50cyBpbiB0aGUgZ2Vub21pYyBmZWF0dXJlcyBiYXNlZCBvbiB0aGUgb2JzZXJ2ZWQgbnVtYmVyIG9mIG92ZXJsYXBzOgoKJCRcdGhldGFfe2ZnfSB+IEJldGEoU197Zmd9ICsgXGFscGhhLCBOX3tmZ30gKyBcYmV0YSkkJAokJFx0aGV0YV97Ymd9IH4gQmV0YShTX3tiZ30gKyBcYWxwaGEsIE5fe2JnfSArIFxiZXRhKSQkCgpmb3IgUyBzdWNjZXNzZXMgaW4gTiB0cmlhbHMuIEZ1bmNpVmFyIHVzZXMgYW4gdW5pbmZvcm1hdGl2ZSBbSmVmZnJleXMgcHJpb3JdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0plZmZyZXlzX3ByaW9yKSBjKGE9MC41LCBiPTAuNSkgdG8gY29tcGFyZSB0aGUgdHdvIGRpc3RyaWJ1dGlvbnMgZGlyZWN0bHkgYnkgc3VidHJhY3RpbmcgcGVybXV0ZWQgc2FtcGxlcyB0byBvYnRhaW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBkaWZmZXJlbmNlcy4gVGhlIHByaW9yIGNhbiBiZSBvdmVyaWRkZW4gaW4gc3BlY2lhbCBjYXNlcyAoc2VlIGRvY3VtZW50YXRpb24pLgoKIVsqSmVmZnJleXMgcHJpb3IgYW5kIGZsYXQgcHJpb3IgZGVuc2l0aWVzKl0oamVmZnJleXNwcmlvci5wbmcpCgpUbyBwZXJmb3JtIHRoZXNlIG9wZXJhdGlvbnMgd2l0aCBmdW5jaVZhciwgd2UgY2FsbCB0aGUgIkNhbGN1bGF0ZUVucmljaG1lbnQiIGZ1bmN0aW9uLgoKYGBge3J9CmVucmljaDEwcTI2IDwtIENhbGN1bGF0ZUVucmljaG1lbnQodmFyaWFudHMgPSB2Y2YxMHEyNnNucHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBlc2VncywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlLnR5cGUgPSAic2VnbWVudGF0aW9ucyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENJID0gMC44LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub3ZlcmxhcHMgPSBUUlVFKQpgYGAKCkF0IGZpcnN0IHBlcmhhcHMgd2UgYXJlIGludGVyZXN0ZWQgd2hpY2ggY2VsbCB0eXBlcyBoYXZlIHRoZSBtb3N0IG92ZXJsYXBzIHdpdGggd2l0aCBzbnBzLCBzYXkgYW1vbmcgYWN0aXZlIGVuaGFuY2Vycy4KCmBgYHtyfQpzbnBzLjEwcTI2LmVuaGFuY2VycyA8LSBlbnJpY2gxMHEyNiRvdmVybGFwcyRgQWN0aXZlIEVuaGFuY2VyYApmZy5zbnBzLmRmIDwtIGFzLmRhdGEuZnJhbWUobWNvbHMoc25wcy4xMHEyNi5lbmhhbmNlcnMkZm9yZWdvdW5kLm92ZXJsYXBzKSkKYmcuc25wcy5kZiA8LSBhcy5kYXRhLmZyYW1lKG1jb2xzKHNucHMuMTBxMjYuZW5oYW5jZXJzJGJhY2tncm91bmQub3ZlcmxhcHMpKQpgYGAKYGBge3IsIGV2YWw9RkFMU0V9CnVwc2V0KGZnLnNucHMuZGYsIG5zZXRzID0gNykKdXBzZXQoYmcuc25wcy5kZiwgbnNldHMgPSA3KQpgYGAKKipGb3JlZ3JvdW5kKioKIVtdKGZvcmVncm91bmQudXBzZXRyLnBuZykKKipCYWNrZ3JvdW5kKioKIVtdKGJhY2tncm91bmQudXBzZXRyLnBuZykKCldoaWxlIGl0IGxvb2tzIGxpa2UgdGhlIHNucHMgYXJlIGxhcmdlbHkgZmFsbGluZyB3aXRoaW4gaGVwYXRvY3l0ZSBhY3RpdmUgZW5oYW5jZXJzLCBpZiB3ZSBleGFtaW5lIHRoZSBlbnJpY2htZW50IHN0YXRpc3RpY3Mgd2UgY2FuIHNlZSBvdGhlcndpc2UuIE5vdyB3ZSdsbCBleHRyYWN0IHRoZSBlbnJpY2htZW50IHN0YXRzIGFuZCBwbG90IHRoZSBlbnJpY2htZW50IHByb2ZpbGUgb2YgdGhlIHZhcmlhbnRzLgoKYGBge3IsIGZpZy53aWR0aD02fQplbnJpY2gxMHEyNi5lbnJpY2ggPC0gZW5yaWNoMTBxMjYkZW5yaWNobWVudApQbG90RW5yaWNobWVudChlbnJpY2gxMHEyNi5lbnJpY2gsIAogICAgICAgICAgICAgICB2YWx1ZSA9ICJkaWZmZXJlbmNlIiwgCiAgICAgICAgICAgICAgIGJsb2NrMSA9ICJzdGF0ZSIsIAogICAgICAgICAgICAgICBjb2xvci5ieSA9ICJzYW1wbGUiLCAKICAgICAgICAgICAgICAgbmNvbCA9IDYpCmBgYAoKIyMjIEFubm90YXRpb25zIG9mIGludGVyZXN0CgpXaGlsZSB0aGVyZSBpcyBhIHRlbmRlbmN5IGZvciBTTlBzIGluIExEIHdpdGggZWFjaCBvdGhlciB0byBiZSBlbnJpY2hlZCBpbiBzb21lIGNlbGx1bGFyIGludGVyZXN0LCBpdCBpcyBwcm9iYWJsZSB0aGF0IG9ubHkgYSBzdWJzZXQgb2YgdGhlbSBhcmUgb2YgZnVuY3Rpb25hbCBpbnRlcmVzdC4gSW4gb3VyIGNhc2UsIHdlIHdvdWxkIGxpa2UgdG8gZXhhbWluZSB0aGUgaHlwb3RoZXNpcyB0aGF0IG9uZSBvZiBvdXIgcHV0YXRpdmUgZnVuY3Rpb25hbCBTTlBzIGRpc3J1cHRzIGEgdHJhbnNjcmlwdGlvbiBmYWN0b3IgYmluZGluZyBzaXRlLiAKClNpbmNlIG91ciBpbmRleCBTTlAgd2FzIGZvdW5kIGluIGFzc29jaWF0aW9uIHdpdGggYnJlYXN0IGNhbmNlciwgYW5kIHdlIHNlZSBlbnJpY2htZW50IG9mIGl0J3MgTEQgcHJveGllcyBpbiBNQ0Y3LCB3ZSdsbCBjaG9vc2UgbGlua2VkIHZhcmlhbnRzIHRoYXQgb3ZlcmxhcCBBY3RpdmUgRW5oYW5jZXIgaW4gdGhlIE1DRjcgY2VsbCBsaW5lIGZyb20gRU5DT0RFLiBTZWdtZW50YXRpb24gZmVhdHVyZXMgbGl2ZSBpbiB0aGUgb3ZlcmxhcHMgc2xvdCBvZiB0aGUgZW5yaWNobWVudCBvYmplY3QgZnJvbSB0aGUgcHJldmlvdXMgc3RlcHMuCgpgYGB7cn0Kc25wcy4xMHEyNi5lbmhhbmNlcnMgPC0gc25wcy4xMHEyNi5lbmhhbmNlcnMkZm9yZWdvdW5kLm92ZXJsYXBzW3NucHMuMTBxMjYuZW5oYW5jZXJzJGZvcmVnb3VuZC5vdmVybGFwcyRNQ0YuNyA+IDAsIF0Kc25wcy4xMHEyNi5lbmhhbmNlcnMKYGBgCgojIE1vdGlmQnJlYWtSIGFuYWx5c2lzIG9mIFRGIGJpbmRpbmcgZGlzcnVwdGlvbgoKTm93IHRoYXQgd2UgaGF2ZSBhIFNOUCBvZiBpbnRlcmVzdCBmcm9tIGJyZWFzdCB0aXNzdWUgZW5oYW5jZXIsIGxldCdzIHdvcmsgd2l0aCB0aGUgbW90aWZCcmVha1IgcGFja2FnZSB0byBkZXRlcm1pbmUgd2hldGhlciBhIHB1dGF0aXZlIHRyYW5zY3JpcHRpb24gZmFjdG9yIGJpbmRpbmcgc2l0ZSAoVEZCUykgaXMgZGlzcnVwdGVkLiBNb3RpZkJyZWFrUiBhbGxvd3MgdXMgdG8gZG8gdGhpcyB3aXRoIG5vdGhpbmcgbW9yZSB0aGFuIHRoZSByc0lEIG9yIGEgbG9jYXRpb24gYW5kIGFsbGVsZSBpbmZvcm1hdGlvbi4gSXQgZW5hYmxlcyB1cyB0byB1c2UgYW55IGdlbm9tZSBob3VzZWQgaW4gYmlvY29uZHVjdG9yIEJTZ2Vub21lIHBhY2thZ2VzIGFuZCBhbnkgc2V0IG9mIG1vdGlmcyBjdXJhdGVkIGluIHRoZSBNb3RpZkRCIHBhY2thZ2UgKHBsdXMgc29tZSBjdXN0b20gZGF0YWJhc2VzIGluIE1vdGlmREIgZm9ybWF0IHRoYXQgYXJlIGluY2x1ZGVkIHdpdGggdGhlIE1vdGlmQnJlYWtSIGluc3RhbGxhdGlvbilbXjFdLgoKV2UgYWxzbyBoYXZlIHNvbWUgc2xpZGVzIGV4cGxhaW5pbmcgaG93IFttb3RpZmJyZWFrUiB3b3Jrc10oaHR0cDovL2dvby5nbC9QSlFiNGIpIGluIGFkZGl0aW9uIHRvIHRoZSBwYXBlciBhbmQgdmlnbmV0dGUgaW4gdGhlIGZvb3Rub3RlIGluZGljYXRlZCBhYm92ZS4KClteMV06IHNlZSB0aGUgW21hbnVzY3JpcHRdKGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9iaW9pbmZvcm1hdGljcy9hcnRpY2xlLzMxLzIzLzM4NDcvMjA5NDQwL21vdGlmYnJlYWtSLWFuLVItQmlvY29uZHVjdG9yLXBhY2thZ2UtZm9yKSBhbmQgW3ZpZ25ldHRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9tb3RpZmJyZWFrUi9pbnN0L2RvYy9tb3RpZmJyZWFrUi12aWduZXR0ZS5odG1sKSBmb3IgZGV0YWlscy4KCiMjIFNldHVwCgpXZSBuZWVkIHRvIGltcG9ydCBzb21lIHZlcnNpb24gb2YgdGhlIGh1bWFuIGdlbm9tZSBhbmQgYSBjb21wYXRpYmxlIHNldCBvZiB2YXJpYW50cyBmcm9tIEJpb2NvbmR1Y3Rvci4KCmBgYHtyfQpsaWJyYXJ5KG1vdGlmYnJlYWtSKQpsaWJyYXJ5KEJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGcxOSkKbGlicmFyeShTTlBsb2NzLkhzYXBpZW5zLmRiU05QMTQ0LkdSQ2gzNykKYGBgCgpXZSBuZWVkIHRvIGdyYWIgdGhlIFNOUCB2YXJpYW50IG9iamVjdHMgb3V0IG9mIFNOUGxvY3MgcGFja2FnZSB0aGF0IGNvcnJlc3BvbmQgdG8gb3VyIGVuaGFuY2VyIHByb3hpZXMuCgpgYGB7ciwgd2FybmluZ3MgPSBGQUxTRX0Kc25wcy5tYi4xMHEyNi5lbmhhbmNlcnMgPC0gc25wcy5mcm9tLnJzaWQocnNpZCA9IG5hbWVzKHNucHMuMTBxMjYuZW5oYW5jZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGJTTlAgPSBTTlBsb2NzLkhzYXBpZW5zLmRiU05QMTQ0LkdSQ2gzNywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VhcmNoLmdlbm9tZSA9IEJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGcxOSkKc25wcy5tYi4xMHEyNi5lbmhhbmNlcnMKYGBgCgojIyBBbmFseXNpcwoKTm93IGEgc2ltcGxlIGNhbGwgdG8gdGhlIG1vdGlmYnJlYWtSIGZ1bmN0aW9uIGNvbXBsZXRlcyB0aGUgYW5hbHlzaXMuIEhlcmUsIHdlIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIGZpbHRlciBvdXIgcmVzdWx0cyBieSBwLXZhbHVlIChyYXRoZXIgdGhhbiBzY2FsZWQgbW90aWYgc2NvcmVzKSwgd2UnbGwgc2V0IHRoYXQgcC12YWx1ZSB0aHJlc2hvbGQgYXQgMTBeLTQsIHVzaW5nIGluZm9ybWF0aW9uIGNvbnRlbnQgKHNldCBhcmd1bWVudCBgbWV0aG9kID0gImljImApIGFuZCBhIHVuaWZvcm0gcHJpb3Igb24gdGhlIEdDIGNvbnRlbnQuCgpgYGB7cn0KcmVzdWx0cy4xMHEyNi5lbmhhbmNlcnMgPC0gbW90aWZicmVha1Ioc25wTGlzdCA9IHNucHMubWIuMTBxMjYuZW5oYW5jZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJwID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHdtTGlzdCA9IGhvY29tb2NvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSAxZS00LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiaWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBia2cgPSBjKEE9MC4yNSwgQz0wLjI1LCBHPTAuMjUsIFQ9MC4yNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJQUEFSQU0gPSBCaW9jUGFyYWxsZWw6OmJwcGFyYW0oKSkKcmVzdWx0cy4xMHEyNi5lbmhhbmNlcnMKYGBgCgpNb3RpZmJyZWFrUiBkb2VzIGEgcXVpY2sgZXN0aW1hdGUgb2YgdGhlIHAtdmFsdWUgdGhyZXNob2xkIGluaXRpYWxseS4gVG8gb2J0YWluIGFjY3VyYXRlIHAtdmFsdWVzLCB3ZSBydW4gImNhbGN1bGF0ZVB2YWx1ZSIgaW4gYSBzZXBlcmF0ZSBzdGVwIChiZXdhcmUgdGhpcyBjYW4gYmUgc2xvdyEpLgoKYGBge3IsIGV2YWw9RkFMU0V9CnB2YWx1ZS5yZXN1bHRzLjEwcTI2LmVuaGFuY2VycyA8LSBjYWxjdWxhdGVQdmFsdWUocmVzdWx0cyA9IHJlc3VsdHMuMTBxMjYuZW5oYW5jZXJzKQpgYGAKCiMjIFZpc3VhbGl6YXRpb24KCk1vdGlmYnJlYWtSIGluY2x1ZGVzIGEgcGxvdHRpbmcgZnVuY3Rpb24gdG8gdmlzdWFsaXplIHRoZSBTTlAgaW4gaXRzIGdlbm9taWMgY29udGV4dCwgYWxpZ25lZCB3aXRoIHRoZSBtb3RpZiBsb2dvcyBpdCBpcyBwdXJwb3J0ZWQgdG8gYWx0ZXIuCgpgYGB7ciwgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9NX0KcGxvdE1CKHJlc3VsdHMuMTBxMjYuZW5oYW5jZXJzLCAicnMzNDM1NDIxMyIsIGVmZmVjdCA9ICJzdHJvbmciKQpgYGAKCkhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYW50IGRpc3J1cHRzIGEgUGF4LTUgbW90aWYgd2hpY2ggaXMgaW50ZXJlc3RpbmcgZ2l2ZW4gdGhlIHJlc3VsdHMgaW4gdGhpcyBwYXBlciBbKkJyZWFzdCBDYW5jZXIgTWFsaWduYW50IFByb2Nlc3NlcyBhcmUgUmVndWxhdGVkIGJ5IFBheC01IFRocm91Z2ggdGhlIERpc3J1cHRpb24gb2YgRkFLIFNpZ25hbGluZyBQYXRod2F5cypdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUyMTk4OTIvKQ==