The data used here is from jci research
article. Study is about Silencing (KO) the gene SMOC2 protects from
kidney fibrosis. so the data is of this format: WT_norm, WT_fibrosis,
SMOC2_overexpression_normal, SMOC2_overexpression_fibrosis
#setwd("D:/shilpa/RNAseq/datacamp")
library(DESeq2)
library(RColorBrewer)
library(pheatmap)
library(tidyverse)
raw_count_matrix <- read.csv('fibrosis_smoc2_rawcounts_unordered.csv')
ggplot(raw_count_matrix) + geom_histogram(aes(x=smoc2_normal1), stat="bin", bins= 200) + xlab("Raw expression counts") + ylab("Number of genes") + xlim(0, 25000) + ylim(0, 5000)

The gene SMOC2, bound to fate, its expression studies we now
undertake, with this spirit now we shall see how our data looks like
head(raw_count_matrix)
str(raw_count_matrix)
'data.frame': 47729 obs. of 8 variables:
$ X : chr "ENSMUSG00000102693" "ENSMUSG00000064842" "ENSMUSG00000051951" "ENSMUSG00000102851" ...
$ smoc2_fibrosis1: int 0 0 72 0 0 0 0 0 0 1 ...
$ smoc2_fibrosis4: int 0 0 30 0 0 0 0 0 0 1 ...
$ smoc2_normal1 : int 0 0 0 0 1 0 0 0 0 1 ...
$ smoc2_normal3 : int 0 0 3 0 0 0 0 0 0 0 ...
$ smoc2_fibrosis3: int 0 0 36 0 0 0 0 0 0 1 ...
$ smoc2_normal4 : int 0 0 1 0 0 0 0 0 0 0 ...
$ smoc2_fibrosis2: int 0 0 51 0 0 0 0 0 0 1 ...
With a steady hand and keen eye, we shall forge the metadata to
construct our DESeq2 object!
genotype <- c("smoc2_oe", "smoc2_oe", "smoc2_oe", "smoc2_oe", "smoc2_oe", "smoc2_oe", "smoc2_oe")
condition <- c("fibrosis", "fibrosis", "fibrosis", "fibrosis", "normal", "normal", "normal")
metadata <- data.frame(genotype, condition)
# Assign the row names of the data frame
rownames(metadata) <- c("smoc2_fibrosis1", "smoc2_fibrosis2", "smoc2_fibrosis3", "smoc2_fibrosis4", "smoc2_normal1", "smoc2_normal3", "smoc2_normal4")
metadata
NA
While there are many packages for DE analysis, we are going to use
DESeq2. It has one of more popular ones and its vignetter is pretty
helpful. DESeq2 models the gene expression (count data) by negetive
binomial distribution.
The raw data from ncbi GEO has already been preprocessed (QC steps
& counts).
What is a count matrix: count matrix represents number of reads
matching the exons of each gene. Barcodes (for each cell) and UMI (for
each gene) information is used to generate this matrix. A little bit
about count matrix:
The negative binomial model commonly used to model RNA-seq count
data
it is discrete and it starts with zero.
Highest frequency near zero i.e. many genes have low number of
counts
The expression range is between zero to inf (no max
limit)
For discrete numbers i.e. counts one could also use poisson but
there is too much variation in the data than poisson can handle
NOTE: DESeq2 model internally corrects for the library size so
transformed or normalized values such as counts scaled by library size
should not be used as input.
Bringing in data for DESeq2: we need to bring the samples from
metadata and countdata in the same order
#metadata <- read.csv("metadata.csv")
#all(colnames(raw_count_matrix) == rownames(metadata))
# Use the match() function to reorder the columns of the raw counts
reorder_idx <- match(rownames(metadata), colnames(raw_count_matrix))
# Reorder the columns of the count data
reordered_raw_count_matrix <- raw_count_matrix[ , reorder_idx]
# Create a DESeq2 object (DESeqDataSet dds)
dds <- DESeqDataSetFromMatrix(countData = reordered_raw_count_matrix,
colData = metadata,
design = ~ condition)
Warning: some variables in design formula are characters, converting to factors
# design formula is used to estimate the dispersions and to estimate the log2 fold changes of the model
dds
class: DESeqDataSet
dim: 47729 7
metadata(1): version
assays(1): counts
rownames: NULL
rowData names(0):
colnames(7): smoc2_fibrosis1 smoc2_fibrosis2 ... smoc2_normal3 smoc2_normal4
colData names(2): genotype condition
There are 4 ways of constructing DESeqDataSet depending on which
pipeline is used upstream. We have count matrix here so we use
‘countData’ while constructing dds. Now we can explore the data with
DESeq2 functions and perform the differential expression analysis.
NORMALIZING COUNTS WITH DESeq2 -> normalized for library size
while accounting for library composition (library size is the total
number of gene counts per sample)
dds <- estimateSizeFactors(dds)
normalized_counts <- counts(dds, normalized=TRUE)
We will be using the normalized counts to explore similarities in
gene expression between each of our samples. To do this we use
clustering after using dimensional reduction. Before clustering, we need
to log transform the normalized counts to improve the visualization of
the clustering and hence our understanding. we can use DESeq2’s vst()
function. vst stands for Variance stabilizing transformation. The
transformed data should be approximated variance stabilized and also
includes correction for size factors or normalization factors. The
transformed data is on the log2 scale for large counts. The blind=TRUE
argument specifies that the transformation should be blind to the sample
information given in the design formula; this argument should be
specified when performing quality assessment.
vsd_trans <- vst(dds, blind = TRUE)
vsd_trans
class: DESeqTransform
dim: 47729 7
metadata(1): version
assays(1): ''
rownames: NULL
rowData names(4): baseMean baseVar allZero dispFit
colnames(7): smoc2_fibrosis1 smoc2_fibrosis2 ... smoc2_normal3 smoc2_normal4
colData names(3): genotype condition sizeFactor
From vsd, we need to extract the VST-trasformed normalized counts as
a matrix
vsd_mat <- assay(vsd_trans)
Then, we need to compute the pairwise correlation values between each
pair of samples
vsd_cor <- cor(vsd_mat)
Now we do clustering. We choose Hierarchical clustering here.
pheatmap(vsd_cor, annotation = select(metadata, condition))

This heatmap shows us that the biological replicates cluster together
(as expected, phew…). This is a good sign because, very likely our
differentially expressed genes must be driving this separation. Also we
do not see any outliers.
We will crosscheck this with PCA (QC step 2)
plotPCA(vsd_trans, intgroup="condition")

Now that we have explored the quality of the samples, checked for
outliers, we can go ahead and find which genes have significant
differences in expression between the normal and fibrosis sample
groups.
At this stage we do not need to re-create the object since we have
not removed samples or found additional sources of variation during QC
steps.
dds_run <- DESeq(dds)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
dds_run
class: DESeqDataSet
dim: 47729 7
metadata(1): version
assays(4): counts mu H cooks
rownames: NULL
rowData names(22): baseMean baseVar ... deviance maxCooks
colnames(7): smoc2_fibrosis1 smoc2_fibrosis2 ... smoc2_normal3 smoc2_normal4
colData names(3): genotype condition sizeFactor
mean_counts <- apply(raw_count_matrix[,1:3], 1, mean)
Dispersion estimates are used to model the raw counts; if the
dispersions don’t follow the assumptions made by DESeq2, then the
variation in the data could be poorly estimated and outout of which
genes are differentially expressed could be wrong. The assumptions made
by DESeq2 are:
- The counts data fit to the negative binomial distribution
- The dispersions should generally decrease with increase in the
mean
plotDispEsts(dds_run)

From the dispersion plot we can see that fit of our data to the model
is pretty good. Gene-est is estimated dispersion of the genes.
Plotting the dispersion estimates is a useful diagnostic. The
dispersion plot which we see here is a typical one with the final
estimates shrunk from the gene-wise estimates towards the fitted
estimates. Some gene-wise estimates are flagged (open circles) as
outliers and not shrunk towards the fitted value.
res0.01 <- results(dds_run,alpha = 0.01)
summary(res0.01)
out of 29556 with nonzero total read count
adjusted p-value < 0.01
LFC > 0 (up) : 4534, 15%
LFC < 0 (down) : 4798, 16%
outliers [1] : 15, 0.051%
low counts [2] : 8317, 28%
(mean count < 2)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# contrasts
resultsNames(dds_run)
[1] "Intercept" "condition_normal_vs_fibrosis"
# MA plot
plotMA(res0.01)

LS0tDQp0aXRsZTogIkFuYWx5c2lzIG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gaW4gREVTZXEgaW4gUiBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClRoZSBkYXRhIHVzZWQgaGVyZSBpcyBmcm9tIFtqY2kgcmVzZWFyY2ggYXJ0aWNsZV0oaHR0cHM6Ly9pbnNpZ2h0LmpjaS5vcmcvYXJ0aWNsZXMvdmlldy85MDI5OSkuDQpTdHVkeSBpcyBhYm91dCBTaWxlbmNpbmcgKEtPKSB0aGUgZ2VuZSBTTU9DMiBwcm90ZWN0cyBmcm9tIGtpZG5leSBmaWJyb3Npcy4NCnNvIHRoZSBkYXRhIGlzIG9mIHRoaXMgZm9ybWF0OiBXVF9ub3JtLCBXVF9maWJyb3NpcywgU01PQzJfb3ZlcmV4cHJlc3Npb25fbm9ybWFsLCBTTU9DMl9vdmVyZXhwcmVzc2lvbl9maWJyb3Npcw0KDQpgYGB7cn0NCiNzZXR3ZCgiRDovc2hpbHBhL1JOQXNlcS9kYXRhY2FtcCIpDQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShwaGVhdG1hcCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCg0KYGBge3J9DQpyYXdfY291bnRfbWF0cml4IDwtIHJlYWQuY3N2KCdmaWJyb3Npc19zbW9jMl9yYXdjb3VudHNfdW5vcmRlcmVkLmNzdicpDQpnZ3Bsb3QocmF3X2NvdW50X21hdHJpeCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeD1zbW9jMl9ub3JtYWwxKSwgc3RhdD0iYmluIiwgYmlucz0gMjAwKSArIHhsYWIoIlJhdyBleHByZXNzaW9uIGNvdW50cyIpICsgeWxhYigiTnVtYmVyIG9mIGdlbmVzIikgKyB4bGltKDAsIDI1MDAwKSArIHlsaW0oMCwgNTAwMCkNCmBgYA0KVGhlIGdlbmUgU01PQzIsIGJvdW5kIHRvIGZhdGUsIGl0cyBleHByZXNzaW9uIHN0dWRpZXMgd2Ugbm93IHVuZGVydGFrZSwgd2l0aCB0aGlzIHNwaXJpdCBub3cgd2Ugc2hhbGwgc2VlIGhvdyBvdXIgZGF0YSBsb29rcyBsaWtlDQoNCmBgYHtyfQ0KaGVhZChyYXdfY291bnRfbWF0cml4KQ0KYGBgDQpgYGB7cn0NCnN0cihyYXdfY291bnRfbWF0cml4KQ0KDQpgYGANCldpdGggYSBzdGVhZHkgaGFuZCBhbmQga2VlbiBleWUsIHdlIHNoYWxsIGZvcmdlIHRoZSBtZXRhZGF0YSB0byBjb25zdHJ1Y3Qgb3VyIERFU2VxMiBvYmplY3QhDQoNCmBgYHtyfQ0KZ2Vub3R5cGUgPC0gYygic21vYzJfb2UiLCAic21vYzJfb2UiLCAic21vYzJfb2UiLCAic21vYzJfb2UiLCAic21vYzJfb2UiLCAic21vYzJfb2UiLCAic21vYzJfb2UiKQ0KY29uZGl0aW9uIDwtIGMoImZpYnJvc2lzIiwgImZpYnJvc2lzIiwgImZpYnJvc2lzIiwgImZpYnJvc2lzIiwgIm5vcm1hbCIsICJub3JtYWwiLCAibm9ybWFsIikNCm1ldGFkYXRhIDwtIGRhdGEuZnJhbWUoZ2Vub3R5cGUsIGNvbmRpdGlvbikNCg0KIyBBc3NpZ24gdGhlIHJvdyBuYW1lcyBvZiB0aGUgZGF0YSBmcmFtZQ0Kcm93bmFtZXMobWV0YWRhdGEpIDwtIGMoInNtb2MyX2ZpYnJvc2lzMSIsICJzbW9jMl9maWJyb3NpczIiLCAic21vYzJfZmlicm9zaXMzIiwgInNtb2MyX2ZpYnJvc2lzNCIsICJzbW9jMl9ub3JtYWwxIiwgInNtb2MyX25vcm1hbDMiLCAic21vYzJfbm9ybWFsNCIpDQoNCm1ldGFkYXRhDQoNCmBgYA0KDQpXaGlsZSB0aGVyZSBhcmUgbWFueSBwYWNrYWdlcyBmb3IgREUgYW5hbHlzaXMsIHdlIGFyZSBnb2luZyB0byB1c2UgREVTZXEyLiBJdCBoYXMgb25lIG9mIG1vcmUgcG9wdWxhciBvbmVzIGFuZCBpdHMgdmlnbmV0dGVyIGlzIHByZXR0eSBoZWxwZnVsLiANCkRFU2VxMiBtb2RlbHMgdGhlIGdlbmUgZXhwcmVzc2lvbiAoY291bnQgZGF0YSkgYnkgbmVnZXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uLg0KDQpUaGUgcmF3IGRhdGEgZnJvbSBuY2JpIEdFTyBoYXMgYWxyZWFkeSBiZWVuIHByZXByb2Nlc3NlZCAoUUMgc3RlcHMgJiBjb3VudHMpLg0KDQpXaGF0IGlzIGEgY291bnQgbWF0cml4OiANCmNvdW50IG1hdHJpeCByZXByZXNlbnRzIG51bWJlciBvZiByZWFkcyBtYXRjaGluZyB0aGUgZXhvbnMgb2YgZWFjaCBnZW5lLiBCYXJjb2RlcyAoZm9yIGVhY2ggY2VsbCkgYW5kIFVNSSAoZm9yIGVhY2ggZ2VuZSkgaW5mb3JtYXRpb24gaXMgdXNlZCB0byBnZW5lcmF0ZSB0aGlzIG1hdHJpeC4NCkEgbGl0dGxlIGJpdCBhYm91dCBjb3VudCBtYXRyaXg6DQoNCi0gVGhlIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsIGNvbW1vbmx5IHVzZWQgdG8gbW9kZWwgUk5BLXNlcSBjb3VudCBkYXRhDQoNCi0gaXQgaXMgZGlzY3JldGUgYW5kIGl0IHN0YXJ0cyB3aXRoIHplcm8uDQoNCi0gSGlnaGVzdCBmcmVxdWVuY3kgbmVhciB6ZXJvIGkuZS4gbWFueSBnZW5lcyBoYXZlIGxvdyBudW1iZXIgb2YgY291bnRzDQoNCi0gVGhlIGV4cHJlc3Npb24gcmFuZ2UgaXMgYmV0d2VlbiB6ZXJvIHRvIGluZiAobm8gbWF4IGxpbWl0KQ0KDQotIEZvciBkaXNjcmV0ZSBudW1iZXJzIGkuZS4gY291bnRzIG9uZSBjb3VsZCBhbHNvIHVzZSBwb2lzc29uIGJ1dCB0aGVyZSBpcyB0b28gbXVjaCB2YXJpYXRpb24gaW4gdGhlIGRhdGEgdGhhbiBwb2lzc29uIGNhbiBoYW5kbGUNCg0KTk9URTogREVTZXEyIG1vZGVsIGludGVybmFsbHkgY29ycmVjdHMgZm9yIHRoZSBsaWJyYXJ5IHNpemUgc28gdHJhbnNmb3JtZWQgb3Igbm9ybWFsaXplZCB2YWx1ZXMgc3VjaCBhcyBjb3VudHMgc2NhbGVkIGJ5IGxpYnJhcnkgc2l6ZSBzaG91bGQgbm90IGJlIHVzZWQgYXMgaW5wdXQuDQoNCkJyaW5naW5nIGluIGRhdGEgZm9yIERFU2VxMjogd2UgbmVlZCB0byBicmluZyB0aGUgc2FtcGxlcyBmcm9tIG1ldGFkYXRhIGFuZCBjb3VudGRhdGEgaW4gdGhlIHNhbWUgb3JkZXINCg0KYGBge3J9DQojbWV0YWRhdGEgPC0gcmVhZC5jc3YoIm1ldGFkYXRhLmNzdiIpDQoNCiNhbGwoY29sbmFtZXMocmF3X2NvdW50X21hdHJpeCkgPT0gcm93bmFtZXMobWV0YWRhdGEpKQ0KDQojIFVzZSB0aGUgbWF0Y2goKSBmdW5jdGlvbiB0byByZW9yZGVyIHRoZSBjb2x1bW5zIG9mIHRoZSByYXcgY291bnRzDQpyZW9yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhtZXRhZGF0YSksIGNvbG5hbWVzKHJhd19jb3VudF9tYXRyaXgpKQ0KDQojIFJlb3JkZXIgdGhlIGNvbHVtbnMgb2YgdGhlIGNvdW50IGRhdGENCnJlb3JkZXJlZF9yYXdfY291bnRfbWF0cml4IDwtIHJhd19jb3VudF9tYXRyaXhbICwgcmVvcmRlcl9pZHhdDQojIENyZWF0ZSBhIERFU2VxMiBvYmplY3QgKERFU2VxRGF0YVNldCBkZHMpDQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSByZW9yZGVyZWRfcmF3X2NvdW50X21hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBtZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gY29uZGl0aW9uKQ0KIyBkZXNpZ24gZm9ybXVsYSBpcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBkaXNwZXJzaW9ucyBhbmQgdG8gZXN0aW1hdGUgdGhlIGxvZzIgZm9sZCBjaGFuZ2VzIG9mIHRoZSBtb2RlbA0KDQpkZHMNCg0KYGBgDQpUaGVyZSBhcmUgNCB3YXlzIG9mIGNvbnN0cnVjdGluZyBERVNlcURhdGFTZXQgZGVwZW5kaW5nIG9uIHdoaWNoIHBpcGVsaW5lIGlzIHVzZWQgdXBzdHJlYW0uIFdlIGhhdmUgY291bnQgbWF0cml4IGhlcmUgc28gd2UgdXNlICdjb3VudERhdGEnIHdoaWxlIGNvbnN0cnVjdGluZyBkZHMuDQpOb3cgd2UgY2FuIGV4cGxvcmUgdGhlIGRhdGEgd2l0aCBERVNlcTIgZnVuY3Rpb25zIGFuZCBwZXJmb3JtIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4NCg0KTk9STUFMSVpJTkcgQ09VTlRTIFdJVEggREVTZXEyDQotPiBub3JtYWxpemVkIGZvciBsaWJyYXJ5IHNpemUgd2hpbGUgYWNjb3VudGluZyBmb3IgbGlicmFyeSBjb21wb3NpdGlvbiAobGlicmFyeSBzaXplIGlzIHRoZSB0b3RhbCBudW1iZXIgb2YgZ2VuZSBjb3VudHMgcGVyIHNhbXBsZSkNCg0KYGBge3J9DQpkZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHMpDQpub3JtYWxpemVkX2NvdW50cyA8LSBjb3VudHMoZGRzLCBub3JtYWxpemVkPVRSVUUpDQoNCmBgYA0KIFdlIHdpbGwgYmUgdXNpbmcgdGhlIG5vcm1hbGl6ZWQgY291bnRzIHRvIGV4cGxvcmUgc2ltaWxhcml0aWVzIGluIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIGVhY2ggb2Ygb3VyIHNhbXBsZXMuIFRvIGRvIHRoaXMgd2UgdXNlIGNsdXN0ZXJpbmcgYWZ0ZXIgdXNpbmcgZGltZW5zaW9uYWwgcmVkdWN0aW9uLiANCiBCZWZvcmUgY2x1c3RlcmluZywgd2UgbmVlZCB0byBsb2cgdHJhbnNmb3JtIHRoZSBub3JtYWxpemVkIGNvdW50cyB0byBpbXByb3ZlIHRoZSB2aXN1YWxpemF0aW9uIG9mIHRoZSBjbHVzdGVyaW5nIGFuZCBoZW5jZSBvdXIgdW5kZXJzdGFuZGluZy4NCndlIGNhbiB1c2UgREVTZXEyJ3MgdnN0KCkgZnVuY3Rpb24uIHZzdCBzdGFuZHMgZm9yIFZhcmlhbmNlIHN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uLiBUaGUgdHJhbnNmb3JtZWQgZGF0YSBzaG91bGQgYmUgYXBwcm94aW1hdGVkIHZhcmlhbmNlIHN0YWJpbGl6ZWQgYW5kIGFsc28gaW5jbHVkZXMgY29ycmVjdGlvbiBmb3Igc2l6ZSBmYWN0b3JzIG9yIG5vcm1hbGl6YXRpb24gZmFjdG9ycy4gVGhlIHRyYW5zZm9ybWVkIGRhdGEgaXMgb24gdGhlIGxvZzIgc2NhbGUgZm9yIGxhcmdlIGNvdW50cy4NClRoZSBibGluZD1UUlVFIGFyZ3VtZW50IHNwZWNpZmllcyB0aGF0IHRoZSB0cmFuc2Zvcm1hdGlvbiBzaG91bGQgYmUgYmxpbmQgdG8gdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiBnaXZlbiBpbiB0aGUgZGVzaWduIGZvcm11bGE7IHRoaXMgYXJndW1lbnQgc2hvdWxkIGJlIHNwZWNpZmllZCB3aGVuIHBlcmZvcm1pbmcgcXVhbGl0eSBhc3Nlc3NtZW50Lg0KDQoNCmBgYHtyfQ0KdnNkX3RyYW5zIDwtIHZzdChkZHMsIGJsaW5kID0gVFJVRSkNCnZzZF90cmFucw0KYGBgDQpGcm9tIHZzZCwgd2UgbmVlZCB0byBleHRyYWN0IHRoZSBWU1QtdHJhc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cyBhcyBhIG1hdHJpeA0KYGBge3J9DQp2c2RfbWF0IDwtIGFzc2F5KHZzZF90cmFucykNCg0KYGBgDQpUaGVuLCB3ZSBuZWVkIHRvIGNvbXB1dGUgdGhlIHBhaXJ3aXNlIGNvcnJlbGF0aW9uIHZhbHVlcyBiZXR3ZWVuIGVhY2ggcGFpciBvZiBzYW1wbGVzDQpgYGB7cn0NCnZzZF9jb3IgPC0gY29yKHZzZF9tYXQpDQoNCmBgYA0KTm93IHdlIGRvIGNsdXN0ZXJpbmcuIFdlIGNob29zZSBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBoZXJlLg0KDQpgYGB7cn0NCnBoZWF0bWFwKHZzZF9jb3IsIGFubm90YXRpb24gPSBzZWxlY3QobWV0YWRhdGEsIGNvbmRpdGlvbikpIA0KDQpgYGANCg0KDQpUaGlzIGhlYXRtYXAgc2hvd3MgdXMgdGhhdCB0aGUgYmlvbG9naWNhbCByZXBsaWNhdGVzIGNsdXN0ZXIgdG9nZXRoZXIgKGFzIGV4cGVjdGVkLCBwaGV3Li4uKS4NClRoaXMgaXMgYSBnb29kIHNpZ24gYmVjYXVzZSwgdmVyeSBsaWtlbHkgb3VyIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBtdXN0IGJlIGRyaXZpbmcgdGhpcyBzZXBhcmF0aW9uLg0KQWxzbyB3ZSBkbyBub3Qgc2VlIGFueSBvdXRsaWVycy4NCg0KV2Ugd2lsbCBjcm9zc2NoZWNrIHRoaXMgd2l0aCBQQ0EgKFFDIHN0ZXAgMikNCg0KYGBge3J9DQpwbG90UENBKHZzZF90cmFucywgaW50Z3JvdXA9ImNvbmRpdGlvbiIpDQoNCmBgYA0KDQogTm93IHRoYXQgd2UgaGF2ZSBleHBsb3JlZCB0aGUgcXVhbGl0eSBvZiB0aGUgc2FtcGxlcywgY2hlY2tlZCBmb3Igb3V0bGllcnMsIHdlIGNhbiBnbyBhaGVhZCBhbmQgZmluZCB3aGljaCBnZW5lcyBoYXZlIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIGV4cHJlc3Npb24gYmV0d2VlbiB0aGUgbm9ybWFsIGFuZCBmaWJyb3NpcyBzYW1wbGUgZ3JvdXBzLg0KIA0KQXQgdGhpcyBzdGFnZSB3ZSBkbyBub3QgbmVlZCB0byByZS1jcmVhdGUgdGhlIG9iamVjdCBzaW5jZSB3ZSBoYXZlIG5vdCByZW1vdmVkIHNhbXBsZXMgb3IgZm91bmQgYWRkaXRpb25hbCBzb3VyY2VzIG9mIHZhcmlhdGlvbiBkdXJpbmcgUUMgc3RlcHMuDQoNCmBgYHtyfQ0KZGRzX3J1biA8LSBERVNlcShkZHMpDQpkZHNfcnVuDQoNCmBgYA0KDQpgYGB7cn0NCm1lYW5fY291bnRzIDwtIGFwcGx5KHJhd19jb3VudF9tYXRyaXhbLDE6M10sIDEsIG1lYW4pDQpgYGANCg0KDQpEaXNwZXJzaW9uIGVzdGltYXRlcyBhcmUgdXNlZCB0byBtb2RlbCB0aGUgcmF3IGNvdW50czsgaWYgdGhlIGRpc3BlcnNpb25zIGRvbid0IGZvbGxvdyB0aGUgYXNzdW1wdGlvbnMgbWFkZSBieSBERVNlcTIsIHRoZW4gdGhlIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBjb3VsZCBiZSBwb29ybHkgZXN0aW1hdGVkIGFuZCBvdXRvdXQgb2Ygd2hpY2ggZ2VuZXMgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBjb3VsZCBiZSB3cm9uZy4NClRoZSBhc3N1bXB0aW9ucyBtYWRlIGJ5IERFU2VxMiBhcmU6DQoNCi0gVGhlIGNvdW50cyBkYXRhIGZpdCB0byB0aGUgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uDQotIFRoZSBkaXNwZXJzaW9ucyBzaG91bGQgZ2VuZXJhbGx5IGRlY3JlYXNlIHdpdGggaW5jcmVhc2UgaW4gdGhlIG1lYW4NCg0KYGBge3J9DQpwbG90RGlzcEVzdHMoZGRzX3J1bikNCmBgYA0KDQpGcm9tIHRoZSBkaXNwZXJzaW9uIHBsb3Qgd2UgY2FuIHNlZSB0aGF0IGZpdCBvZiBvdXIgZGF0YSB0byB0aGUgbW9kZWwgaXMgcHJldHR5IGdvb2QuDQpHZW5lLWVzdCBpcyBlc3RpbWF0ZWQgZGlzcGVyc2lvbiBvZiB0aGUgZ2VuZXMuDQoNClBsb3R0aW5nIHRoZSBkaXNwZXJzaW9uIGVzdGltYXRlcyBpcyBhIHVzZWZ1bCBkaWFnbm9zdGljLiBUaGUgZGlzcGVyc2lvbiBwbG90IHdoaWNoIHdlIHNlZSBoZXJlIGlzIGEgdHlwaWNhbCBvbmUgd2l0aCB0aGUgZmluYWwgZXN0aW1hdGVzIHNocnVuayBmcm9tIHRoZSBnZW5lLXdpc2UgZXN0aW1hdGVzIHRvd2FyZHMgdGhlIGZpdHRlZCBlc3RpbWF0ZXMuIFNvbWUgZ2VuZS13aXNlIGVzdGltYXRlcyBhcmUgZmxhZ2dlZCAob3BlbiBjaXJjbGVzKSBhcyBvdXRsaWVycyBhbmQgbm90IHNocnVuayB0b3dhcmRzIHRoZSBmaXR0ZWQgdmFsdWUuIA0KDQpgYGB7cn0NCnJlczAuMDEgPC0gcmVzdWx0cyhkZHNfcnVuLGFscGhhID0gMC4wMSkNCnN1bW1hcnkocmVzMC4wMSkNCg0KIyBjb250cmFzdHMNCnJlc3VsdHNOYW1lcyhkZHNfcnVuKQ0KDQpgYGANCmBgYHtyfQ0KIyBNQSBwbG90DQpwbG90TUEocmVzMC4wMSkNCmBgYA0KDQoNCg0K