1 Overview

batch condition media treatment induced exp_12
SK064 1 LB-IPTG LB DMSO yes control
SK065 2 LB-IPTG LB DMSO yes control
SK066 3 LB-IPTG LB DMSO yes control
SK085 1 SCFM-IPTG SCFM DMSO yes treatment
SK086 2 SCFM-IPTG SCFM DMSO yes treatment
SK087 3 SCFM-IPTG SCFM DMSO yes treatment

2 Project variables

##       batch condition media treatment induced    exp_12
## SK064     1   LB-IPTG    LB      DMSO     yes   control
## SK065     2   LB-IPTG    LB      DMSO     yes   control
## SK066     3   LB-IPTG    LB      DMSO     yes   control
## SK085     1 SCFM-IPTG  SCFM      DMSO     yes treatment
## SK086     2 SCFM-IPTG  SCFM      DMSO     yes treatment
## SK087     3 SCFM-IPTG  SCFM      DMSO     yes treatment
## [1] "The control is: LB-IPTG"
## [1] "The treatment is: SCFM-IPTG"
# Load raw count data
rawcounts <- read.csv("../../raw-data/htseq_SK_unfiltered_reverse_strand_count.csv", 
                      row.names = 1, header = TRUE)

# Append a prefix to the batch and run columns to turn to character class
metadata[["batch"]] <- paste0("b", metadata[["batch"]])
metadata[["run"]] <- paste0("r", metadata[["run"]])

# Subset the rawcounts dataframe to only include samples that are present in experiment
samples <- rownames(metadata)
rawcounts <- select(rawcounts, all_of(samples))

# Ensure that the order of samples in metadata matches the order of columns in rawcounts
all(rownames(metadata) == colnames(rawcounts))  # Should return TRUE if order is correct
## [1] TRUE
# Define the design formula for DESeq2, including main condition and batch effect
design_formula <- ~ exp_12 + batch

# Create DESeq2 dataset object from the filtered count matrix and metadata
dds <- DESeqDataSetFromMatrix(
  countData = rawcounts,
  colData = metadata,
  design = design_formula
)
## Warning in DESeqDataSet(se, design = design, ignoreRank): some variables in
## design formula are characters, converting to factors

3 Library size per sample

# Sum up the counts for each sample
rawcounts_sums <- colSums(rawcounts)

# Turn it into a data frame
data <- data.frame(
  Category = names(rawcounts_sums),
  Count = as.numeric(rawcounts_sums)
)

# Make sure sample order is consistent
data <- data %>%
  mutate(Category = factor(Category, levels = sort(unique(Category))))

# Plot bar chart of library sizes
p <- ggplot(data, aes(x = Category, y = Count, fill = Category)) +
  geom_bar(stat = "identity") +
  # coord_flip() # flip if too many samples to read labels easily
  labs(y = "Library size (M)", x = NULL) +  # remove x label, rename y axis
  # scale_fill_brewer(palette = "Dark2") +  # optional color palette
  scale_y_continuous(
    labels = scales::label_number(scale = 1e-6),  # show counts in millions
    limits = c(0, (max(data$Count) + 1e6))        # add a bit of space on top
  ) +
  theme(
    panel.grid = element_blank(),
    panel.background = element_blank(),
    axis.text = element_text(size = text.size, angle = 45, hjust = 1),  # slant labels
    axis.title = element_text(size = axis.text.size),
    legend.text = element_text(size = text.size),
    legend.title = element_text(size = axis.text.size),
    panel.border = element_rect(fill = NA),
    legend.position = "none",  # no legend needed since each bar is a different sample
    axis.ticks = element_line(linewidth = 0.2),
    legend.key = element_blank()
  )

# Show the plot
p

# Save the plot as a PDF with today’s date and project name
cairo_pdf(paste0(Sys.Date(), "_library_size_", exp, "_", treatment, ".vs.", control, ".pdf"))
p
dev.off() 
## quartz_off_screen 
##                 2

4 Expression distribution

# Stack rawcounts into single vector 
dat <- stack(rawcounts)

# Format expression distribution plot with the expression level and density 
expression_dist <- ggplot(dat, aes(x = log2(values+1),  fill=ind, label = ind )) + 
  geom_density(alpha = 0.5, position = "identity") + 
  ylab("Density") + 
  geom_textdensity(hjust = "ymax", vjust = 0,
           text_only = TRUE, text_smoothing = 20) + 
    theme(panel.grid = element_blank(),
        panel.background = element_blank(),
        axis.text = element_text(size = text.size),
        axis.title = element_text(size = axis.text.size),
        legend.text = element_text(size = text.size),
        legend.title = element_text(size = axis.text.size),
        panel.border = element_rect(fill = NA),
        legend.position = "none",
        axis.ticks = element_line(linewidth = 0.2),
        legend.key = element_blank()) +
  coord_cartesian(xlim = c(0, 15))

expression_dist

#save figure
cairo_pdf(paste0(Sys.Date(), "_expression_dist_", exp, "_", treatment, ".vs.", control, ".pdf"))
expression_dist
dev.off() 
## quartz_off_screen 
##                 2

5 Non-zero genes observed

# Prepare data by getting library sizes and non-zero counts
x <- colSums(rawcounts)
y <- colSums(rawcounts != 0) 
df <- data.frame(x,y)
df$ind <- rownames(df)

# Calculate axis limits
x_min <- min(df$x)
x_max <- max(df$x)
y_min <- min(df$y)
y_max <- max(df$y)

# Non-zero plot
nonzero <- ggplot(data = df, aes(x = x, y = y, color = ind, label = ind)) +
  geom_point(size = 3) +
  geom_text(hjust = -0.1, vjust = -0.1, color = "black", size = 5) +
  scale_y_continuous(limits=c(y_min, y_max)) + 
  scale_x_continuous(
    limits = c(x_min-1e3, x_max+9e5),
    labels = scales::comma_format(scale = 1e-6,  accuracy = 1)  # Format labels without decimals
  ) +
  xlab("Millions of reads") + ylab("Number of non-zero genes observed") +
  labs(color = "Sample") + 
    theme(panel.grid = element_blank(),
        panel.background = element_blank(),
        axis.text = element_text(size = text.size),
        axis.title = element_text(size = axis.text.size),
        legend.text = element_text(size = text.size),
        legend.title = element_text(size = axis.text.size),
        panel.border = element_rect(fill = NA),
        legend.position = "none",
        axis.ticks = element_line(linewidth = 0.2),
        legend.key = element_blank()) +
  coord_cartesian()

# Show nonzero plot
nonzero

# Save plot

cairo_pdf(paste0(Sys.Date(), "_non-zero_", exp, "_", treatment, ".vs.", control, ".pdf"))
nonzero
dev.off() 
## quartz_off_screen 
##                 2

6 RPKM and CPM transformations

# Load GFF annotation 
gff_annot <- load_gff_annotations("../../raw-data/paeruginosa_pa14.gff", 
                                  id_col = "gene_id", type = "gene")
## Returning a df with 16 columns and 5979 rows.
rownames(gff_annot) <- gff_annot$gene_id

head(gff_annot)
##               seqnames start  end width strand    source type score phase
## gene1650835 chromosome   483 2027  1545      + PseudoCAP gene    NA     0
## gene1650837 chromosome  2056 3159  1104      + PseudoCAP gene    NA     0
## gene1650839 chromosome  3169 4278  1110      + PseudoCAP gene    NA     0
## gene1650841 chromosome  4275 6695  2421      + PseudoCAP gene    NA     0
## gene1650843 chromosome  7018 7791   774      - PseudoCAP gene    NA     0
## gene1650845 chromosome  7803 8339   537      - PseudoCAP gene    NA     0
##                 gene_id Name         Dbxref      Alias       name Parent locus
## gene1650835 gene1650835      GeneID:4384099 PA14_00010       dnaA         <NA>
## gene1650837 gene1650837      GeneID:4384100 PA14_00020       dnaN         <NA>
## gene1650839 gene1650839      GeneID:4384101 PA14_00030       recF         <NA>
## gene1650841 gene1650841      GeneID:4384102 PA14_00050       gyrB         <NA>
## gene1650843 gene1650843      GeneID:4385186 PA14_00060 PA14_00060         <NA>
## gene1650845 gene1650845      GeneID:4385187 PA14_00070 PA14_00070         <NA>
head(rawcounts)
##             SK064 SK065 SK066 SK085 SK086 SK087
## gene1650835  2234  2528  1719  1579  1694  1158
## gene1650837  1266  1754  1368  1732  2280  1356
## gene1650839   722   770   657   377   454   293
## gene1650841  3671  4307  3347  1914  2500  1745
## gene1650843   436   435   313   292   389   236
## gene1650845   257   299   177   177   180   124
dim(gff_annot)
## [1] 5979   16
dim(rawcounts)
## [1] 5979    6
summary(rownames(gff_annot) == rownames(rawcounts))
##    Mode   FALSE    TRUE 
## logical     932    5047
gff_annot <- gff_annot[rownames(rawcounts),]
summary(rownames(gff_annot) == rownames(rawcounts))
##    Mode    TRUE 
## logical    5979
# Create an expression object with counts, metadata, and gene info
expt <- create_expt(count_dataframe = rawcounts,
                    metadata = metadata, 
                    gene_info = gff_annot)
## Reading the sample metadata.
## Did not find the column: sampleid.
## The rownames do not appear numeric, using them.
## The sample definitions comprises: 6 rows(samples) and 8 columns(metadata fields).
## Matched 5979 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## Saving the expressionset to 'expt.rda'.
## The final expressionset has 5979 features and 6 samples.
# Normalize counts log2 CPM
cpm_normalized_exp <- normalize_expt(expt, 
                                     transform = "log2", 
                                     norm = "raw", 
                                     convert = "cpm", 
                                     row_min = "10")
## transform_counts: Found 273 values equal to 0, adding 1 to the matrix.
####### Calculate RPKM ########

# Grab gene lengths from GFF (should be in base pairs)
gene_lengths <- gff_annot$width  
names(gene_lengths) <- rownames(gff_annot)  # set gene names

# Make sure gene_lengths are in the same order as rawcounts rows
gene_lengths <- gene_lengths[rownames(rawcounts)]

# Make DGEList object for edgeR
dge <- DGEList(counts = rawcounts)

# Calculate RPKM (reads per kilobase per million)
rpkm <- rpkm(dge, gene.length = gene_lengths)

# Wrap RPKM in an experiment object too
expt_rpkm <- create_expt(count_dataframe = rpkm,
                         metadata = metadata, 
                         gene_info = gff_annot)
## Reading the sample metadata.
## Did not find the column: sampleid.
## The rownames do not appear numeric, using them.
## The sample definitions comprises: 6 rows(samples) and 8 columns(metadata fields).
## Matched 5979 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## Saving the expressionset to 'expt.rda'.
## The final expressionset has 5979 features and 6 samples.
# log2 transform the RPKM (again, no normalization beyond log)
rpkm_normalized_exp <- normalize_expt(expt_rpkm, 
                                      transform = "log2", 
                                      norm = "raw")
## transform_counts: Found 273 values equal to 0, adding 1 to the matrix.
# Get the actual expression matrices from the experiment objects
rpkm <- exprs(rpkm_normalized_exp)
cpm <- exprs(cpm_normalized_exp)

# Merge CPM and RPKM matrices with gene annotation info
cpm_annotated <- merge(gff_annot, cpm, by = 0)
rpkm_annotated <- merge(gff_annot, rpkm, by = 0)

6.1 Heatmap of alginate genes

This generates a small heatmap of the alginate genes across samples to visualize expression.

# Loop through CPM and RPKM to plot both heatmaps
for (type in c("cpm", "rpkm")) {
  
  # Define gene list
  alg_operon_genes <- c(
    "algA", "algF", "algJ", "algI", "algL", "algX", "algG", "algE",
    "algK", "alg44", "alg8", "algD", "mucC", "mucB", "algU", "algW",
    "algP", "algQ", "algR", "algZ", "algC", "algB"
  )

  # Get the right data per type of transformation
  data_annot <- get(paste0(type, "_annotated"))
  
  # Fix rownames using gene name (make unique)
  rownames(data_annot) <- make.names(data_annot$name, unique = TRUE)
  
  # Drop undesired metadata columns
  cols_to_exclude <- c("Row.names","seqnames","start","end","width",
                       "strand","source","type","score","phase",
                       "gene_id","Name","Dbxref","Alias","name",
                       "Parent","locus")
  keep <- !names(data_annot) %in% cols_to_exclude
  expr_data <- data_annot[, keep]
  
  # Extract matrix for alg genes
  alg_genes <- expr_data[alg_operon_genes, , drop = FALSE]
  mat <- alg_genes - rowMeans(alg_genes)
  
  # Pull sample annotations
  anno <- as.data.frame(metadata[, c("condition", "induced")])
  colnames(anno) <- c("Condition", "Induced")
  
  # Color settings for annotation
 # ann_colors <- list(
 #  Condition = c("LB-DMSO" = "#D95F02", "LB-IPTG" = "#66A61E"),
 #  Induced = c("no" = "lightgrey", "yes" = "gray27")
 # )
  
  # Scale data by row (Z-score per gene)
  scaled_mat <- t(scale(t(mat)))

  # Build heatmap
  ht <- Heatmap(
    scaled_mat,
    name = "Z-score",
    top_annotation = HeatmapAnnotation(df = anno#, 
                                       #col = ann_colors
                                       ),
    cluster_rows = TRUE,
    cluster_columns = TRUE,
    row_names_side = "left",
    column_names_side = "bottom",
    column_names_rot = 45,
    row_names_gp = gpar(fontsize = 10),
    column_names_gp = gpar(fontsize = 10),
    show_row_dend = TRUE,
    show_column_dend = TRUE
  )
  
  # Draw and save
  draw(ht)
  
  cairo_pdf(paste0(Sys.Date(), "_alg_heatmap_", type, "_", exp, "_", treatment, ".vs.", control, ".pdf"))
  draw(ht)
  dev.off()
}

7 DESeq

# Create DESeqDataSet
dds <- DESeqDataSetFromMatrix(
  countData = rawcounts,
  colData = metadata,
  design = design_formula
)
## Warning in DESeqDataSet(se, design = design, ignoreRank): some variables in
## design formula are characters, converting to factors
# Check number of genes before filtering
print("Number of genes before filtering:")
## [1] "Number of genes before filtering:"
nrow(dds)
## [1] 5979
# Filter: keep genes with counts >=10 in at least 2 samples
print("Filtering genes with counts >=10 in at least 2 samples")
## [1] "Filtering genes with counts >=10 in at least 2 samples"
keep <- rowSums(counts(dds) >= 10) >= 2 
dds_filtered <- dds[keep, ]

# Check number of genes after filtering
print("Number of genes after filtering:")
## [1] "Number of genes after filtering:"
nrow(dds_filtered)
## [1] 5787
# Run DESeq on the filtered dataset
dds_final <- DESeq(dds_filtered)
## estimating size factors
## estimating dispersions
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## fitting model and testing
# Show available results
resultsNames(dds_final)
## [1] "Intercept"                   "exp_12_treatment_vs_control"
## [3] "batch_b2_vs_b1"              "batch_b3_vs_b1"
# Plot dispersion estimates
plotDispEsts(dds_final)

8 Save DE tables

res <- results(dds_final, contrast = c("exp_12", "treatment", "control"))
resdf <- as.data.frame(res)
resdf$gene_id <- rownames(resdf)

cpm <- as.data.frame(cpm)
rpkm <- as.data.frame(rpkm)

# append type of value to the dataframe   
colnames(rpkm) <- paste0(colnames(rpkm), "_rpkm")
colnames(cpm) <- paste0(colnames(cpm), "_cpm")
rawcounts2 <- rawcounts
colnames(rawcounts2) <- paste0(colnames(rawcounts2), "_rawcounts")

# ensure column gene_id is there
rpkm$gene_id <- rownames(rpkm)
cpm$gene_id <- rownames(cpm)
rawcounts2$gene_id <- rownames(rawcounts2)

gff_annot <- gff_annot[rownames(resdf),]

dim(resdf)
## [1] 5787    7
# merge with siggenes
resdf <- full_join(resdf, gff_annot, by = "gene_id", copy = FALSE)
head(resdf)
##    baseMean log2FoldChange     lfcSE      stat       pvalue         padj
## 1 1780.6985     -0.4694423 0.1477369 -3.177557 1.485214e-03 3.361335e-03
## 2 1603.8835      0.3530781 0.1607698  2.196171 2.807968e-02 4.966281e-02
## 3  535.8251     -0.8730243 0.1754552 -4.975767 6.498978e-07 2.238666e-06
## 4 2859.9504     -0.8078822 0.1414506 -5.711408 1.120450e-08 4.540645e-08
## 5  342.5872     -0.3013717 0.1903226 -1.583479 1.133124e-01 1.690484e-01
## 6  197.1930     -0.5169827 0.2218758 -2.330054 1.980329e-02 3.632357e-02
##       gene_id   seqnames start  end width strand    source type score phase
## 1 gene1650835 chromosome   483 2027  1545      + PseudoCAP gene    NA     0
## 2 gene1650837 chromosome  2056 3159  1104      + PseudoCAP gene    NA     0
## 3 gene1650839 chromosome  3169 4278  1110      + PseudoCAP gene    NA     0
## 4 gene1650841 chromosome  4275 6695  2421      + PseudoCAP gene    NA     0
## 5 gene1650843 chromosome  7018 7791   774      - PseudoCAP gene    NA     0
## 6 gene1650845 chromosome  7803 8339   537      - PseudoCAP gene    NA     0
##   Name         Dbxref      Alias       name Parent locus
## 1      GeneID:4384099 PA14_00010       dnaA         <NA>
## 2      GeneID:4384100 PA14_00020       dnaN         <NA>
## 3      GeneID:4384101 PA14_00030       recF         <NA>
## 4      GeneID:4384102 PA14_00050       gyrB         <NA>
## 5      GeneID:4385186 PA14_00060 PA14_00060         <NA>
## 6      GeneID:4385187 PA14_00070 PA14_00070         <NA>
pa_go <- read.csv("../../raw-data/208963_microbes.online.csv", header = TRUE)
head(pa_go)
##   locusId   accession        GI scaffoldId start stop strand      Alias
## 1 2194572 YP_788156.1 116053721       4582   483 2027      + PA14_00010
## 2 2194573 YP_788157.1 116053722       4582  2056 3159      + PA14_00020
## 3 2194574 YP_788158.1 116053723       4582  3169 4278      + PA14_00030
## 4 2194575 YP_788159.1 116053724       4582  4275 6695      + PA14_00050
## 5 2194576 YP_788160.1 116053725       4582  7791 7018      - PA14_00060
## 6 2194577 YP_788161.1 116053726       4582  8339 7803      - PA14_00070
##         name                                                  desc     COG
## 1       dnaA chromosomal replication initiator protein DnaA (NCBI)  COG593
## 2       dnaN                 DNA polymerase III, beta chain (NCBI)  COG592
## 3       recF        DNA replication and repair protein RecF (NCBI) COG1195
## 4       gyrB                           DNA gyrase subunit B (NCBI)  COG187
## 5 PA14_00060                       putative acyltransferase (NCBI)  COG204
## 6 PA14_00070                putative histidinol-phosphatase (NCBI)  COG241
##   COGFun
## 1      L
## 2      L
## 3      L
## 4      L
## 5      I
## 6      E
##                                                                    COGDesc
## 1                            ATPase involved in DNA replication initiation
## 2                      DNA polymerase sliding clamp subunit (PCNA homolog)
## 3                         Recombinational DNA repair ATPase (RecF pathway)
## 4 Type IIA topoisomerase (DNA gyrase/topo II, topoisomerase IV), B subunit
## 5                           1-acyl-sn-glycerol-3-phosphate acyltransferase
## 6                          Histidinol phosphatase and related phosphatases
##                                                                                  TIGRFam
## 1                        TIGR00362 chromosomal replication initiator protein DnaA [dnaA]
## 2                                      TIGR00663 DNA polymerase III, beta subunit [dnaN]
## 3                               TIGR00611 DNA replication and repair protein RecF [recF]
## 4                                                 TIGR01059 DNA gyrase, B subunit [gyrB]
## 5                                                                                       
## 6 TIGR01656 histidinol-phosphate phosphatase domain,TIGR01662 HAD hydrolase, family IIIA
##                                                   TIGRRoles
## 1 DNA metabolism:DNA replication, recombination, and repair
## 2 DNA metabolism:DNA replication, recombination, and repair
## 3 DNA metabolism:DNA replication, recombination, and repair
## 4 DNA metabolism:DNA replication, recombination, and repair
## 5                                                          
## 6           Unknown function:Enzymes of unknown specificity
##                                                                                                                                                                                           GO
## 1                                                                                                                                     GO:0006270,GO:0006275,GO:0003688,GO:0017111,GO:0005524
## 2 GO:0006260,GO:0003677,GO:0003893,GO:0008408,GO:0016449,GO:0019984,GO:0003889,GO:0003894,GO:0015999,GO:0016450,GO:0003890,GO:0003895,GO:0016000,GO:0016451,GO:0003891,GO:0016448,GO:0016452
## 3                                                                                                                                     GO:0006281,GO:0005694,GO:0005524,GO:0017111,GO:0003697
## 4                                                                                                                                     GO:0006304,GO:0006265,GO:0005694,GO:0003918,GO:0005524
## 5                                                                                                                                                                      GO:0008152,GO:0003841
## 6                                                                                                                                                                      GO:0000105,GO:0004401
##         EC                                        ECDesc
## 1                                                       
## 2  2.7.7.7                  DNA-directed DNA polymerase.
## 3                                                       
## 4 5.99.1.3          DNA topoisomerase (ATP-hydrolyzing).
## 5 2.3.1.51 1-acylglycerol-3-phosphate O-acyltransferase.
## 6  3.1.3.-
col_to_remove <- intersect(colnames(pa_go),colnames(resdf))
col_to_remove <- setdiff(col_to_remove, "Alias") # returns all except the second vector

pa_go <- pa_go %>% select(-all_of(col_to_remove))

resdf <- full_join(resdf, rpkm, by = "gene_id", copy = FALSE)
resdf <- full_join(resdf, cpm, by = "gene_id", copy = FALSE)
resdf <- full_join(resdf, rawcounts2, by = "gene_id", copy = FALSE)
#resdf <- full_join(resdf, pa_go, by = "Alias", copy = FALSE)
resdf <- merge(resdf, pa_go, by = "Alias", copy = FALSE)

head(resdf)
##        Alias  baseMean log2FoldChange     lfcSE      stat       pvalue
## 1 PA14_00010 1780.6985     -0.4694423 0.1477369 -3.177557 1.485214e-03
## 2 PA14_00020 1603.8835      0.3530781 0.1607698  2.196171 2.807968e-02
## 3 PA14_00030  535.8251     -0.8730243 0.1754552 -4.975767 6.498978e-07
## 4 PA14_00050 2859.9504     -0.8078822 0.1414506 -5.711408 1.120450e-08
## 5 PA14_00060  342.5872     -0.3013717 0.1903226 -1.583479 1.133124e-01
## 6 PA14_00070  197.1930     -0.5169827 0.2218758 -2.330054 1.980329e-02
##           padj     gene_id   seqnames start  end width strand    source type
## 1 3.361335e-03 gene1650835 chromosome   483 2027  1545      + PseudoCAP gene
## 2 4.966281e-02 gene1650837 chromosome  2056 3159  1104      + PseudoCAP gene
## 3 2.238666e-06 gene1650839 chromosome  3169 4278  1110      + PseudoCAP gene
## 4 4.540645e-08 gene1650841 chromosome  4275 6695  2421      + PseudoCAP gene
## 5 1.690484e-01 gene1650843 chromosome  7018 7791   774      - PseudoCAP gene
## 6 3.632357e-02 gene1650845 chromosome  7803 8339   537      - PseudoCAP gene
##   score phase Name         Dbxref       name Parent locus SK064_rpkm SK065_rpkm
## 1    NA     0      GeneID:4384099       dnaA         <NA>   8.176179   8.165287
## 2    NA     0      GeneID:4384100       dnaN         <NA>   7.842995   8.122956
## 3    NA     0      GeneID:4384101       recF         <NA>   7.029755   6.934080
## 4    NA     0      GeneID:4384102       gyrB         <NA>   8.244496   8.285574
## 5    NA     0      GeneID:4385186 PA14_00060         <NA>   6.823946   6.633153
## 6    NA     0      GeneID:4385187 PA14_00070         <NA>   6.591050   6.619830
##   SK066_rpkm SK085_rpkm SK086_rpkm SK087_rpkm SK064_cpm SK065_cpm SK066_cpm
## 1   7.893669   7.989449   7.833958   7.830711  8.802026  8.791120  8.519135
## 2   8.048415   8.605764   8.744455   8.540827  7.985143  8.265208  8.190642
## 3   6.988440   6.411482   6.421924   6.336705  7.179220  7.083470  7.137873
## 4   8.205787   7.620694   7.747847   7.774559  9.517304  9.558461  9.478520
## 5   6.444121   6.561349   6.716019   6.542347  6.458065  6.267796  6.079357
## 6   6.152874   6.368749   6.138494   6.146263  5.706889  5.735417  5.273245
##   SK085_cpm SK086_cpm SK087_cpm SK064_rawcounts SK065_rawcounts SK066_rawcounts
## 1  8.615052  8.459333  8.456080            2234            2528            1719
## 2  8.748155  8.886879  8.683202            1266            1754            1368
## 3  6.560362  6.570815  6.485495             722             770             657
## 4  8.891988  9.019505  9.046289            3671            4307            3347
## 5  6.196208  6.350426  6.177265             436             435             313
## 6  5.486717  5.259038  5.266713             257             299             177
##   SK085_rawcounts SK086_rawcounts SK087_rawcounts locusId   accession        GI
## 1            1579            1694            1158 2194572 YP_788156.1 116053721
## 2            1732            2280            1356 2194573 YP_788157.1 116053722
## 3             377             454             293 2194574 YP_788158.1 116053723
## 4            1914            2500            1745 2194575 YP_788159.1 116053724
## 5             292             389             236 2194576 YP_788160.1 116053725
## 6             177             180             124 2194577 YP_788161.1 116053726
##   scaffoldId stop                                                  desc     COG
## 1       4582 2027 chromosomal replication initiator protein DnaA (NCBI)  COG593
## 2       4582 3159                 DNA polymerase III, beta chain (NCBI)  COG592
## 3       4582 4278        DNA replication and repair protein RecF (NCBI) COG1195
## 4       4582 6695                           DNA gyrase subunit B (NCBI)  COG187
## 5       4582 7018                       putative acyltransferase (NCBI)  COG204
## 6       4582 7803                putative histidinol-phosphatase (NCBI)  COG241
##   COGFun
## 1      L
## 2      L
## 3      L
## 4      L
## 5      I
## 6      E
##                                                                    COGDesc
## 1                            ATPase involved in DNA replication initiation
## 2                      DNA polymerase sliding clamp subunit (PCNA homolog)
## 3                         Recombinational DNA repair ATPase (RecF pathway)
## 4 Type IIA topoisomerase (DNA gyrase/topo II, topoisomerase IV), B subunit
## 5                           1-acyl-sn-glycerol-3-phosphate acyltransferase
## 6                          Histidinol phosphatase and related phosphatases
##                                                                                  TIGRFam
## 1                        TIGR00362 chromosomal replication initiator protein DnaA [dnaA]
## 2                                      TIGR00663 DNA polymerase III, beta subunit [dnaN]
## 3                               TIGR00611 DNA replication and repair protein RecF [recF]
## 4                                                 TIGR01059 DNA gyrase, B subunit [gyrB]
## 5                                                                                       
## 6 TIGR01656 histidinol-phosphate phosphatase domain,TIGR01662 HAD hydrolase, family IIIA
##                                                   TIGRRoles
## 1 DNA metabolism:DNA replication, recombination, and repair
## 2 DNA metabolism:DNA replication, recombination, and repair
## 3 DNA metabolism:DNA replication, recombination, and repair
## 4 DNA metabolism:DNA replication, recombination, and repair
## 5                                                          
## 6           Unknown function:Enzymes of unknown specificity
##                                                                                                                                                                                           GO
## 1                                                                                                                                     GO:0006270,GO:0006275,GO:0003688,GO:0017111,GO:0005524
## 2 GO:0006260,GO:0003677,GO:0003893,GO:0008408,GO:0016449,GO:0019984,GO:0003889,GO:0003894,GO:0015999,GO:0016450,GO:0003890,GO:0003895,GO:0016000,GO:0016451,GO:0003891,GO:0016448,GO:0016452
## 3                                                                                                                                     GO:0006281,GO:0005694,GO:0005524,GO:0017111,GO:0003697
## 4                                                                                                                                     GO:0006304,GO:0006265,GO:0005694,GO:0003918,GO:0005524
## 5                                                                                                                                                                      GO:0008152,GO:0003841
## 6                                                                                                                                                                      GO:0000105,GO:0004401
##         EC                                        ECDesc
## 1                                                       
## 2  2.7.7.7                  DNA-directed DNA polymerase.
## 3                                                       
## 4 5.99.1.3          DNA topoisomerase (ATP-hydrolyzing).
## 5 2.3.1.51 1-acylglycerol-3-phosphate O-acyltransferase.
## 6  3.1.3.-
write.csv(resdf, paste0(Sys.Date() , "_results_",exp, "_", treatment,".vs.", control,".csv"), row.names = FALSE)

9 Save DEG list

# filter for significantly differentially expressed genes (LFC >= 3, padj < pval_cutoff)
dim(resdf)
## [1] 5780   54
head(resdf)
##        Alias  baseMean log2FoldChange     lfcSE      stat       pvalue
## 1 PA14_00010 1780.6985     -0.4694423 0.1477369 -3.177557 1.485214e-03
## 2 PA14_00020 1603.8835      0.3530781 0.1607698  2.196171 2.807968e-02
## 3 PA14_00030  535.8251     -0.8730243 0.1754552 -4.975767 6.498978e-07
## 4 PA14_00050 2859.9504     -0.8078822 0.1414506 -5.711408 1.120450e-08
## 5 PA14_00060  342.5872     -0.3013717 0.1903226 -1.583479 1.133124e-01
## 6 PA14_00070  197.1930     -0.5169827 0.2218758 -2.330054 1.980329e-02
##           padj     gene_id   seqnames start  end width strand    source type
## 1 3.361335e-03 gene1650835 chromosome   483 2027  1545      + PseudoCAP gene
## 2 4.966281e-02 gene1650837 chromosome  2056 3159  1104      + PseudoCAP gene
## 3 2.238666e-06 gene1650839 chromosome  3169 4278  1110      + PseudoCAP gene
## 4 4.540645e-08 gene1650841 chromosome  4275 6695  2421      + PseudoCAP gene
## 5 1.690484e-01 gene1650843 chromosome  7018 7791   774      - PseudoCAP gene
## 6 3.632357e-02 gene1650845 chromosome  7803 8339   537      - PseudoCAP gene
##   score phase Name         Dbxref       name Parent locus SK064_rpkm SK065_rpkm
## 1    NA     0      GeneID:4384099       dnaA         <NA>   8.176179   8.165287
## 2    NA     0      GeneID:4384100       dnaN         <NA>   7.842995   8.122956
## 3    NA     0      GeneID:4384101       recF         <NA>   7.029755   6.934080
## 4    NA     0      GeneID:4384102       gyrB         <NA>   8.244496   8.285574
## 5    NA     0      GeneID:4385186 PA14_00060         <NA>   6.823946   6.633153
## 6    NA     0      GeneID:4385187 PA14_00070         <NA>   6.591050   6.619830
##   SK066_rpkm SK085_rpkm SK086_rpkm SK087_rpkm SK064_cpm SK065_cpm SK066_cpm
## 1   7.893669   7.989449   7.833958   7.830711  8.802026  8.791120  8.519135
## 2   8.048415   8.605764   8.744455   8.540827  7.985143  8.265208  8.190642
## 3   6.988440   6.411482   6.421924   6.336705  7.179220  7.083470  7.137873
## 4   8.205787   7.620694   7.747847   7.774559  9.517304  9.558461  9.478520
## 5   6.444121   6.561349   6.716019   6.542347  6.458065  6.267796  6.079357
## 6   6.152874   6.368749   6.138494   6.146263  5.706889  5.735417  5.273245
##   SK085_cpm SK086_cpm SK087_cpm SK064_rawcounts SK065_rawcounts SK066_rawcounts
## 1  8.615052  8.459333  8.456080            2234            2528            1719
## 2  8.748155  8.886879  8.683202            1266            1754            1368
## 3  6.560362  6.570815  6.485495             722             770             657
## 4  8.891988  9.019505  9.046289            3671            4307            3347
## 5  6.196208  6.350426  6.177265             436             435             313
## 6  5.486717  5.259038  5.266713             257             299             177
##   SK085_rawcounts SK086_rawcounts SK087_rawcounts locusId   accession        GI
## 1            1579            1694            1158 2194572 YP_788156.1 116053721
## 2            1732            2280            1356 2194573 YP_788157.1 116053722
## 3             377             454             293 2194574 YP_788158.1 116053723
## 4            1914            2500            1745 2194575 YP_788159.1 116053724
## 5             292             389             236 2194576 YP_788160.1 116053725
## 6             177             180             124 2194577 YP_788161.1 116053726
##   scaffoldId stop                                                  desc     COG
## 1       4582 2027 chromosomal replication initiator protein DnaA (NCBI)  COG593
## 2       4582 3159                 DNA polymerase III, beta chain (NCBI)  COG592
## 3       4582 4278        DNA replication and repair protein RecF (NCBI) COG1195
## 4       4582 6695                           DNA gyrase subunit B (NCBI)  COG187
## 5       4582 7018                       putative acyltransferase (NCBI)  COG204
## 6       4582 7803                putative histidinol-phosphatase (NCBI)  COG241
##   COGFun
## 1      L
## 2      L
## 3      L
## 4      L
## 5      I
## 6      E
##                                                                    COGDesc
## 1                            ATPase involved in DNA replication initiation
## 2                      DNA polymerase sliding clamp subunit (PCNA homolog)
## 3                         Recombinational DNA repair ATPase (RecF pathway)
## 4 Type IIA topoisomerase (DNA gyrase/topo II, topoisomerase IV), B subunit
## 5                           1-acyl-sn-glycerol-3-phosphate acyltransferase
## 6                          Histidinol phosphatase and related phosphatases
##                                                                                  TIGRFam
## 1                        TIGR00362 chromosomal replication initiator protein DnaA [dnaA]
## 2                                      TIGR00663 DNA polymerase III, beta subunit [dnaN]
## 3                               TIGR00611 DNA replication and repair protein RecF [recF]
## 4                                                 TIGR01059 DNA gyrase, B subunit [gyrB]
## 5                                                                                       
## 6 TIGR01656 histidinol-phosphate phosphatase domain,TIGR01662 HAD hydrolase, family IIIA
##                                                   TIGRRoles
## 1 DNA metabolism:DNA replication, recombination, and repair
## 2 DNA metabolism:DNA replication, recombination, and repair
## 3 DNA metabolism:DNA replication, recombination, and repair
## 4 DNA metabolism:DNA replication, recombination, and repair
## 5                                                          
## 6           Unknown function:Enzymes of unknown specificity
##                                                                                                                                                                                           GO
## 1                                                                                                                                     GO:0006270,GO:0006275,GO:0003688,GO:0017111,GO:0005524
## 2 GO:0006260,GO:0003677,GO:0003893,GO:0008408,GO:0016449,GO:0019984,GO:0003889,GO:0003894,GO:0015999,GO:0016450,GO:0003890,GO:0003895,GO:0016000,GO:0016451,GO:0003891,GO:0016448,GO:0016452
## 3                                                                                                                                     GO:0006281,GO:0005694,GO:0005524,GO:0017111,GO:0003697
## 4                                                                                                                                     GO:0006304,GO:0006265,GO:0005694,GO:0003918,GO:0005524
## 5                                                                                                                                                                      GO:0008152,GO:0003841
## 6                                                                                                                                                                      GO:0000105,GO:0004401
##         EC                                        ECDesc
## 1                                                       
## 2  2.7.7.7                  DNA-directed DNA polymerase.
## 3                                                       
## 4 5.99.1.3          DNA topoisomerase (ATP-hydrolyzing).
## 5 2.3.1.51 1-acylglycerol-3-phosphate O-acyltransferase.
## 6  3.1.3.-
summary(is.na(resdf$log2FoldChange))
##    Mode   FALSE 
## logical    5780
summary(is.na(resdf$padj))
##    Mode   FALSE 
## logical    5780
sig_genes <- resdf[abs(resdf$log2FoldChange) >= logfc_cutoff & resdf$padj < pval_cutoff, ]  %>% drop_na(log2FoldChange)
sig_genes$direction <- ifelse(sig_genes$log2FoldChange > 0, "up", "down")

summary(is.na(sig_genes$log2FoldChange))
##    Mode   FALSE 
## logical     765
summary(is.na(sig_genes$padj))
##    Mode   FALSE 
## logical     765
dim(sig_genes)
## [1] 765  55
head(sig_genes)
##        Alias  baseMean log2FoldChange     lfcSE       stat       pvalue
## 1 PA14_00080 467.67726       2.587064 0.1847156  14.005660 1.439361e-44
## 2 PA14_00560 216.38063      -2.030352 0.2329481  -8.715898 2.884636e-18
## 3 PA14_00570 229.32258      -3.012398 0.2389427 -12.607195 1.927300e-36
## 4 PA14_00580  73.25642      -2.162844 0.3376209  -6.406130 1.492598e-10
## 5 PA14_00590  62.74549      -2.210213 0.3833066  -5.766175 8.109065e-09
## 6 PA14_00630 425.80690       2.405223 0.1950717  12.329947 6.248789e-35
##           padj     gene_id   seqnames start   end width strand    source type
## 1 3.331833e-43 gene1650847 chromosome  8671 10377  1707      + PseudoCAP gene
## 2 2.249782e-17 gene1650925 chromosome 57148 58476  1329      + PseudoCAP gene
## 3 3.442371e-35 gene1650927 chromosome 59018 59704   687      + PseudoCAP gene
## 4 7.016786e-10 gene1650929 chromosome 59735 60088   354      + PseudoCAP gene
## 5 3.323453e-08 gene1650931 chromosome 60241 60750   510      + PseudoCAP gene
## 6 1.051213e-33 gene1650937 chromosome 63701 63841   141      + PseudoCAP gene
##   score phase Name         Dbxref       name Parent locus SK064_rpkm SK065_rpkm
## 1    NA     0      GeneID:4385188 PA14_00080         <NA>   4.107099   4.085597
## 2    NA     0      GeneID:4381738       exoT         <NA>   5.658212   5.734095
## 3    NA     0      GeneID:4381739 PA14_00570         <NA>   7.184945   6.920300
## 4    NA     0      GeneID:4381740 PA14_00580         <NA>   6.123140   6.069983
## 5    NA     0      GeneID:4381741 PA14_00590         <NA>   5.339107   5.644514
## 6    NA     0      GeneID:4381744 PA14_00630         <NA>   7.484965   7.639493
##   SK066_rpkm SK085_rpkm SK086_rpkm SK087_rpkm SK064_cpm SK065_cpm SK066_cpm
## 1   3.996607   6.706746   6.898945   6.939056  4.843465  4.821429  4.730142
## 2   5.805594   3.538913   4.181795   4.378246  6.061464  6.137710  6.209535
## 3   6.614281   4.405289   4.426778   3.828367  6.647838  6.384099  6.079357
## 4   6.119808   3.906260   4.302233   4.588845  4.662246  4.610469  4.658999
## 5   5.121183   3.960665   3.055283   3.545111  4.401519  4.700530  4.189039
## 6   7.667280  10.355004  10.184017  10.276869  4.706979  4.856679  4.883650
##   SK085_cpm SK086_cpm SK087_cpm SK064_rawcounts SK065_rawcounts SK066_rawcounts
## 1  7.472477  7.665392  7.705641             139             156             120
## 2  3.918194  4.572322  4.771310             330             397             343
## 3  3.894362  3.915402  3.332293             498             472             313
## 4  2.573782  2.931684  3.196118             122             134             114
## 5  3.075622  2.241650  2.687791             101             143              81
## 6  7.535467  7.365320  7.457703             126             160             134
##   SK085_rawcounts SK086_rawcounts SK087_rawcounts locusId   accession        GI
## 1             713             975             687 2194578 YP_788162.1 116053727
## 2              57             110              87 2194617 YP_788201.1 116053766
## 3              56              68              30 2194618 YP_788202.1 116053767
## 4              20              32              27 2194619 YP_788203.1 116053768
## 5              30              18              18 2194620 YP_788204.1 116053769
## 6             745             791             578 2194623 YP_788207.1 116053772
##   scaffoldId  stop                        desc     COG COGFun
## 1       4582 10377 hypothetical protein (NCBI)               
## 2       4582 58476          exoenzyme T (NCBI) COG5585      T
## 3       4582 59704 putative lipoprotein (NCBI) COG1462      M
## 4       4582 60088 putative lipoprotein (NCBI) COG4259      S
## 5       4582 60750 putative lipoprotein (NCBI) COG4380      S
## 6       4582 63841 hypothetical protein (NCBI)               
##                                                           COGDesc TIGRFam
## 1                                                                        
## 2                         NAD+--asparagine ADP-ribosyltransferase        
## 3 Uncharacterized protein involved in formation of curli polymers        
## 4                   Uncharacterized protein conserved in bacteria        
## 5                   Uncharacterized protein conserved in bacteria        
## 6                                                                        
##   TIGRRoles                                                                GO
## 1                                 GO:0006118,GO:0020037,GO:0005506,GO:0009055
## 2           GO:0009405,GO:0006471,GO:0016020,GO:0005576,GO:0003956,GO:0005096
## 3                                                       GO:0015031,GO:0042597
## 4                                                                            
## 5                                                                  GO:0005975
## 6                                                                            
##   EC ECDesc direction
## 1                  up
## 2                down
## 3                down
## 4                down
## 5                down
## 6                  up
write.csv(sig_genes, paste0(Sys.Date(),"_significantgenes_", exp, "_", treatment,".vs.", control,".csv"), row.names = FALSE)

10 PCA plot

vst <- DESeqDataSetFromMatrix(countData = rawcounts, colData = metadata, design = ~ exp_12 + batch)
## Warning in DESeqDataSet(se, design = design, ignoreRank): some variables in
## design formula are characters, converting to factors
# Variance stabilizing transformations
vsd <- vst(vst, blind = TRUE)

# PCA plot
pcaData <- plotPCA(vsd, intgroup = c("exp_12", "batch", "condition"), returnData = TRUE)
## using ntop=500 top features by variance
percentVar <- round(100 * attr(pcaData, "percentVar"))

# Calculate the maximum absolute value among PC1 and PC2
max_abs_val <- max(c(abs(min(pcaData$PC1)), abs(min(pcaData$PC2)), abs(max(pcaData$PC1)), abs(max(pcaData$PC2))))

axis_size <- 10
theme_set(theme_minimal(base_family = "Arial"))

pca <- ggplot(pcaData, aes(PC1, PC2, color = condition, shape = condition)) +
  geom_point(size = 3) +
  xlab(paste0("PC1: ", percentVar[1], "% variance")) +
  ylab(paste0("PC2: ", percentVar[2], "% variance")) +
  scale_colour_brewer(palette = "Dark2") +
  scale_shape_manual(values = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) +
  coord_fixed(ratio = 1, xlim = c(-max_abs_val, max_abs_val), ylim = c(-max_abs_val, max_abs_val)) +
  labs(color = "condition", shape = "condition") +
  guides(shape = guide_legend(title = "Condition"),  # Adjust size of shape legend
    color = guide_legend(title = "Condition")) +
  theme(
    panel.grid = element_blank(),
    panel.background = element_blank(),
    axis.text = element_text(size = axis_size),
    axis.ticks = element_line(linewidth = 0.2),
     axis.line = element_line(linewidth = 0, color = "black"),  # Set the axis line linewidth
   panel.border = element_rect(fill = NA, linewidth = 0.5),  # Set the panel border linewidth
  legend.key = element_rect(fill = "white", color = NA)  # Set legend key background to white and remove border
  )

print(pca)

cairo_pdf(paste0(Sys.Date(),"_pca_",exp, "_", treatment,".vs.", control,".pdf", sep = ""))
pca
dev.off()
## quartz_off_screen 
##                 2

11 Volcano plot

sequence <- c("treatment")

for (i in sequence) {
    set.seed(0)
    res <- results(dds_final, contrast = c("exp_12", i, "control"))

  gp2 <- wes_palettes$Zissou1
  key <- read.csv("../../raw-data/pa14_gene_key.csv",  header = TRUE)

  rownames(key) <- key$gene_id

  res <- merge(key, as.data.frame(res), by=0)
  res <- as.data.frame(res)
  res$Alias <- rownames(res)

 # Define gene list
  alg_genes <- c(
    "algA", "algF", "algJ", "algI", "algL", "algX", "algG", "algE",
    "algK", "alg44", "alg8", "algD", "mucC", "mucB", "algU", "algW",
    "algP", "algQ", "algR", "algZ", "algC", "algB"
  )

logfc_cutoff
keyvals <- ifelse(
 res$log2FoldChange <= -logfc_cutoff & res$padj <= pval_cutoff, gp2[1],
  ifelse(res$log2FoldChange >= logfc_cutoff & res$padj <= pval_cutoff, gp2[5],
         'lightgrey'))
keyvals[is.na(keyvals)] <- 'lightgrey'
names(keyvals)[keyvals == gp2[5]] <- 'Up-regulated'
names(keyvals)[keyvals == 'lightgrey'] <- 'NS'
names(keyvals)[keyvals == gp2[1]] <- 'Down-regulated'

volcano <- EnhancedVolcano(res,
                     lab = res[["name"]],
                     selectLab = alg_genes,
                     x = 'log2FoldChange',
                     y = 'padj',
                     xlab = bquote(~Log[2]~ 'fold change'),
                     pCutoff = pval_cutoff,
                     FCcutoff = logfc_cutoff,
                     cutoffLineType = "dashed",
                     pointSize = 1,
                     labSize = 3.0,
                     axisLabSize = 14,
                     labCol = 'black',
                     labFace = 'bold',
                     #col = c('grey', gp2[2], gp2[3], gp2[1]),
                     colCustom = keyvals,
                     colAlpha = 5/5,
                     legendPosition = 'bottom',
                     legendLabSize = 12,
                     legendIconSize = 4.0,
                     drawConnectors = TRUE,
                     arrowheads = FALSE,
                     widthConnectors = 0.2,
                     gridlines.major = FALSE,
                     borderWidth = 0.3,
                     gridlines.minor = FALSE,
                     title = paste0(treatment," vs ", control, sep = ""),
                     subtitle = "",
                     #caption = paste0("total = ", nrow(toptable), " variables"),
                     caption = paste0("Volcano plot with log2 FC of ", logfc_cutoff," and P value of ",pval_cutoff, "."),
                     max.overlaps = 30,
                     #ylim = c(0,10),
                     colConnectors = 'black') + 
      theme(axis.ticks=element_line(linewidth =0.2)) # +
 # scale_x_continuous(breaks = c(-5, -2.5, 0, 2.5, 5), limits = c(-6,6))

print(volcano)

cairo_pdf(paste0(Sys.Date(),"_volcano_", exp, "_", treatment,".vs.", control,".pdf", sep = ""))
print(volcano)
dev.off()
}
## Warning: One or more p-values is 0. Converting to 10^-1 * current lowest
## non-zero p-value...

12 MA plot

sequence <- c("treatment")

for (i in sequence) {
    set.seed(0)

res <- results(dds_final, contrast = c("exp_12", i, "control"))

gp2 <- wes_palettes$Zissou1

res$gene_id <- rownames(res)
res <- as.data.frame(res)
head(res)
res <- full_join(key, as.data.frame(res), by="gene_id")
rownames(res) <- res$Alias

any(is.na(res))  # Check if there are any NAs

res <- res %>% filter(!is.na(baseMean))


ma <- ggmaplot(res, 
               fdr = pval_cutoff, 
               fc = 2^(logfc_cutoff), 
               size = 1,
               palette = c((gp2[5]), (gp2[1])),
               select.top.method = "fc",
             #  ylim = c(-15,15),
               legend = "bottom", 
               title = paste0(treatment," vs ", control, sep = ""),
              subtitle = paste0("Volcano plot with log2 FC of ", logfc_cutoff," and P value of ",pval_cutoff, "."),
               font.label = c( 12), 
             label.rectangle = FALSE,
              genenames = res$name,
              font.legend = c(12),
               font.main = c("bold", 14),
               alpha = 1.5,
               ggtheme = ggplot2::theme(panel.grid =element_blank(),
                                        axis.text = element_text(size = 12),
                                        axis.title = element_text(size = 14),
                                        panel.background = element_blank(),
                                        panel.border = element_rect(fill = NA))) 
print(ma)

cairo_pdf(paste0(Sys.Date(),"_ma_", exp, "_", treatment,".vs.", control,".pdf"))
print(ma)
dev.off()
}

13 Circos plot

library(circlize)
## ========================================
## circlize version 0.4.16
## CRAN page: https://cran.r-project.org/package=circlize
## Github page: https://github.com/jokergoo/circlize
## Documentation: https://jokergoo.github.io/circlize_book/book/
## 
## If you use it in published research, please cite:
## Gu, Z. circlize implements and enhances circular visualization
##   in R. Bioinformatics 2014.
## 
## This message can be suppressed by:
##   suppressPackageStartupMessages(library(circlize))
## ========================================
library(scales)
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
gff_annot <- gff_annot[rownames(rawcounts),]
head(rawcounts)
##             SK064 SK065 SK066 SK085 SK086 SK087
## gene1650835  2234  2528  1719  1579  1694  1158
## gene1650837  1266  1754  1368  1732  2280  1356
## gene1650839   722   770   657   377   454   293
## gene1650841  3671  4307  3347  1914  2500  1745
## gene1650843   436   435   313   292   389   236
## gene1650845   257   299   177   177   180   124
head(gff_annot)
##               seqnames start  end width strand    source type score phase
## gene1650835 chromosome   483 2027  1545      + PseudoCAP gene    NA     0
## gene1650837 chromosome  2056 3159  1104      + PseudoCAP gene    NA     0
## gene1650839 chromosome  3169 4278  1110      + PseudoCAP gene    NA     0
## gene1650841 chromosome  4275 6695  2421      + PseudoCAP gene    NA     0
## gene1650843 chromosome  7018 7791   774      - PseudoCAP gene    NA     0
## gene1650845 chromosome  7803 8339   537      - PseudoCAP gene    NA     0
##                 gene_id Name         Dbxref      Alias       name Parent locus
## gene1650835 gene1650835      GeneID:4384099 PA14_00010       dnaA         <NA>
## gene1650837 gene1650837      GeneID:4384100 PA14_00020       dnaN         <NA>
## gene1650839 gene1650839      GeneID:4384101 PA14_00030       recF         <NA>
## gene1650841 gene1650841      GeneID:4384102 PA14_00050       gyrB         <NA>
## gene1650843 gene1650843      GeneID:4385186 PA14_00060 PA14_00060         <NA>
## gene1650845 gene1650845      GeneID:4385187 PA14_00070 PA14_00070         <NA>
head(res)
##                gene_id      Alias       name  baseMean log2FoldChange     lfcSE
## PA14_00010 gene1650835 PA14_00010       dnaA 1780.6985     -0.4694423 0.1477369
## PA14_00020 gene1650837 PA14_00020       dnaN 1603.8835      0.3530781 0.1607698
## PA14_00030 gene1650839 PA14_00030       recF  535.8251     -0.8730243 0.1754552
## PA14_00050 gene1650841 PA14_00050       gyrB 2859.9504     -0.8078822 0.1414506
## PA14_00060 gene1650843 PA14_00060 PA14_00060  342.5872     -0.3013717 0.1903226
## PA14_00070 gene1650845 PA14_00070 PA14_00070  197.1930     -0.5169827 0.2218758
##                 stat       pvalue         padj
## PA14_00010 -3.177557 1.485214e-03 3.361335e-03
## PA14_00020  2.196171 2.807968e-02 4.966281e-02
## PA14_00030 -4.975767 6.498978e-07 2.238666e-06
## PA14_00050 -5.711408 1.120450e-08 4.540645e-08
## PA14_00060 -1.583479 1.133124e-01 1.690484e-01
## PA14_00070 -2.330054 1.980329e-02 3.632357e-02
rownames(res) <- res$gene_id
gff_annot <- gff_annot[rownames(res),]
summary(rownames(gff_annot) == rownames(res))
##    Mode    TRUE 
## logical    5787
# Step 2: Add to annotation

gff_annot$log2FoldChange <- res$log2FoldChange

head(gff_annot)
##               seqnames start  end width strand    source type score phase
## gene1650835 chromosome   483 2027  1545      + PseudoCAP gene    NA     0
## gene1650837 chromosome  2056 3159  1104      + PseudoCAP gene    NA     0
## gene1650839 chromosome  3169 4278  1110      + PseudoCAP gene    NA     0
## gene1650841 chromosome  4275 6695  2421      + PseudoCAP gene    NA     0
## gene1650843 chromosome  7018 7791   774      - PseudoCAP gene    NA     0
## gene1650845 chromosome  7803 8339   537      - PseudoCAP gene    NA     0
##                 gene_id Name         Dbxref      Alias       name Parent locus
## gene1650835 gene1650835      GeneID:4384099 PA14_00010       dnaA         <NA>
## gene1650837 gene1650837      GeneID:4384100 PA14_00020       dnaN         <NA>
## gene1650839 gene1650839      GeneID:4384101 PA14_00030       recF         <NA>
## gene1650841 gene1650841      GeneID:4384102 PA14_00050       gyrB         <NA>
## gene1650843 gene1650843      GeneID:4385186 PA14_00060 PA14_00060         <NA>
## gene1650845 gene1650845      GeneID:4385187 PA14_00070 PA14_00070         <NA>
##             log2FoldChange
## gene1650835     -0.4694423
## gene1650837      0.3530781
## gene1650839     -0.8730243
## gene1650841     -0.8078822
## gene1650843     -0.3013717
## gene1650845     -0.5169827
# Step 3: Create summary for each chromosome (usually just 1 in bacteria)
chr_ranges <- gff_annot |>
  dplyr::group_by(seqnames) |>
  dplyr::summarise(start = min(start), end = max(end)) |>
  dplyr::ungroup()

cairo_pdf(paste0(Sys.Date(),"_circos_", exp, "_", treatment,".vs.", control,".pdf", sep = ""))

circos.clear()
circos.par(cell.padding = c(0, 0, 0, 0))

# Normalize log2FC to range [-1, 1]
max_fc <- max(abs(gff_annot$log2FoldChange), na.rm = TRUE)
gff_annot$norm_fc <- gff_annot$log2FoldChange / max_fc

# Assign color: red (up), blue (down), gray (not significant)
gff_annot$col <- "gray"
gff_annot$col[gff_annot$log2FoldChange > 2 & res$padj < 0.01] <- rgb(1, 0, 0, alpha = 0.7)  # red
gff_annot$col[gff_annot$log2FoldChange < -2 & res$padj < 0.01] <- rgb(0, 0, 1, alpha = 0.7)  # blue

# Initialize circos plot
circos.initialize(factors = chr_ranges$seqnames, xlim = chr_ranges[, c("start", "end")])

# Track plotting
circos.trackPlotRegion(
  ylim = c(-1, 1),  # allow bars to go below and above axis
  track.height = 0.2,
  panel.fun = function(region, value, ...) {
    sector <- get.cell.meta.data("sector.index")
    gene_data <- gff_annot[gff_annot$seqnames == sector, ]
    circos.rect(
      xleft = gene_data$start,
      ybottom = 0,
      xright = gene_data$end,
      ytop = gene_data$norm_fc,
      col = gene_data$col,
      border = NA
    )
  },
  bg.border = NA,
  bg.col = "white"
)


# Define gene list
  alg_genes <- c(
    "algA", "algF", "algJ", "algI", "algL", "algX", "algG", "algE",
    "algK", "alg44", "alg8", "algD", "mucC", "mucB", "algU", "algW",
    "algP", "algQ", "algR", "algZ", "algC", "algB", "dnaA"
  )

alg_genes_found <- gff_annot[gff_annot$name %in% alg_genes | gff_annot$Alias %in% alg_genes, ]
gff_annot$highlight <- ifelse(gff_annot$name %in% alg_genes | gff_annot$Alias %in% alg_genes, TRUE, FALSE)

circos.trackPlotRegion(
  ylim = c(0, 1),
  track.height = 0.05,
  panel.fun = function(...) {
    sector <- get.cell.meta.data("sector.index")
    gene_data <- gff_annot[gff_annot$seqnames == sector & gff_annot$highlight == TRUE, ]
    if(nrow(gene_data) == 0) return()
    
    # Draw rectangles or points at gene start position
    circos.rect(
      xleft = gene_data$start,
      ybottom = 0,
      xright = gene_data$end,
      ytop = 1,
      col = "gold",   # highlight color
      border = "black"
    )
    
    # Optional: add gene labels
    circos.text(
      x = (gene_data$start + gene_data$end) / 2,
      y = 1.05,
      labels = gene_data$name,
      facing = "clockwise",
      niceFacing = TRUE,
      cex = 0.5,
      adj = c(0, 0.5)
    )
  },
  bg.border = NA
)
## Note: 23 points are out of plotting region in sector 'chromosome',
## track '2'.
## Note: 3 points are out of plotting region in sector 'chromosome', track
## '2'.
## Note: 20 points are out of plotting region in sector 'chromosome',
## track '2'.
# Draw an empty track (no data) for the tick marks and labels
circos.trackPlotRegion(
  ylim = c(0, 1),
  track.height = 0.05,
  bg.border = NA,
  panel.fun = function(x, y) {
    sector <- get.cell.meta.data("sector.index")
    xlim <- get.cell.meta.data("xlim")
    ylim <- get.cell.meta.data("ylim")
    circos.center <- get.cell.meta.data("cell.start.degree") # for possible angle reference
    
    # Create tick positions every 1Mb within sector range
    ticks <- seq(from = ceiling(xlim[1] / 1e6) * 1e6, to = xlim[2], by = 1e6)
    
    # Draw vertical ticks (lines) on circle at each tick position
    circos.segments(
      x0 = ticks, y0 = 0,
      x1 = ticks, y1 = 0.2,
      sector.index = sector,
      col = "black",
      lwd = 1
    )
    
    # Add labels a little outside the ticks, showing "X Mb"
    label_pos <- ticks + 5e4  # slightly offset to the right, adjust if needed
    
    # Convert label position to Mb and add "Mb"
    labels <- paste0(ticks / 1e6, " Mb")
    
    circos.text(
      x = ticks,
      y = 0.25,
      labels = labels,
      sector.index = sector,
      facing = "clockwise",
      niceFacing = TRUE,
      adj = c(0, 0.5),
      cex = 0.6
    )
  }
)

# Example: define significant DEGs (adjust your threshold as needed)
gff_annot$padj <- res$padj

sig_degs <- gff_annot[!is.na(gff_annot$padj) & gff_annot$padj < 0.01, ]
num_up <- sum(sig_degs$log2FoldChange > 2)
num_down <- sum(sig_degs$log2FoldChange < -2)


library(grid)

# After all circos plotting
grid.text(
  label = paste0("Up: ", num_up, "\nDown: ", num_down),
  x = 0.5, y = 0.5,         # Center of plotting area
  gp = gpar(fontsize = 14, col = "black")
)

dev.off()
## quartz_off_screen 
##                 2
LS0tCnRpdGxlOiAiMjAyNV9leHBfMTJfYWxnaW5hdGVfREVHIgphdXRob3I6ICJOb3VyIEVsIEh1c3NlaW5pIgpkYXRlOiAiMjAyNSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZSAgIyBFbnN1cmVzIFRPQyBpcyBleHBhbmRlZCBieSBkZWZhdWx0CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBoaWdobGlnaHQ6IGthdGUKICAgIHRoZW1lOgogICAgICBib290c3dhdGNoOiB6ZXBoeXIKICAgIGNzczogc3R5bGUuY3NzICAjIExpbmsgdG8geW91ciBjdXN0b20gQ1NTIGZpbGUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgoKYGBge3IgbG9hZF9saWJyYXJpZXMsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoIkRFU2VxMiIpCmxpYnJhcnkoImdncGxvdDIiKQpsaWJyYXJ5KCJrYWJsZUV4dHJhIikKbGlicmFyeSgiZHBseXIiKQpsaWJyYXJ5KCJnZ2ZvcmNlIikKbGlicmFyeSgiUkNvbG9yQnJld2VyIikKbGlicmFyeSgicGhlYXRtYXAiKQpsaWJyYXJ5KCJ3ZXNhbmRlcnNvbiIpCmxpYnJhcnkoIkVuaGFuY2VkVm9sY2FubyIpCmxpYnJhcnkoImdncHViciIpCmxpYnJhcnkoImhwZ2x0b29scyIpCmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgidGliYmxlIikgCmxpYnJhcnkoImVkZ2VSIikKbGlicmFyeSgiZ2VuZWZpbHRlciIpCmxpYnJhcnkoInBoZWF0bWFwIikKbGlicmFyeSgiZHBseXIiKQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KCJERVNlcTIiKQpsaWJyYXJ5KCJDb21wbGV4SGVhdG1hcCIpCmxpYnJhcnkoImdvc2VxIikKbGlicmFyeSgicGx5ciIpCmxpYnJhcnkoImdlb210ZXh0cGF0aCIpCmBgYAoKIyBPdmVydmlldwoKYGBge3Igc2FtcGxlX3RhYmxlLCBlY2hvPUZBTFNFfQojIExvYWQgbWV0YWRhdGEgQ1NWIGZpbGUgaW50byBhIGRhdGFmcmFtZSwgdXNpbmcgdGhlIGZpcnN0IGNvbHVtbiBhcyByb3cgbmFtZXMKbWV0YWRhdGEgPC0gcmVhZC5jc3YoIi4uLy4uL3Jhdy1kYXRhL21ldGFkYXRhX2FsZ2luYXRlX3Z0bC5jc3YiLCByb3cubmFtZXMgPSAxLCBoZWFkZXIgPSBUUlVFKQoKIyBGaWx0ZXIgdGhlIGRhdGEgdG8gaW5jbHVkZSBvbmx5IHJvd3Mgd2hlcmUgZXhwXzEyIGlzIGVpdGhlciAiY29udHJvbCIgb3IgInRyZWF0bWVudCIKIyBUaGVuIHJlbW92ZSBvdGhlciBleHBlcmltZW50cyAoZXhwXzIgdG8gZXhwXzYpCm1ldGFkYXRhIDwtIGZpbHRlcihtZXRhZGF0YSwgZXhwXzEyICVpbiUgYygiY29udHJvbCIsICJ0cmVhdG1lbnQiKSkgJT4lCiAgICBzZWxlY3QoYmF0Y2gsIGNvbmRpdGlvbiwgbWVkaWEsIHRyZWF0bWVudCwgaW5kdWNlZCwgZXhwXzEyKSAKCiMgRGlzcGxheSB0aGUgY2xlYW5lZCBtZXRhZGF0YSB0YWJsZSB1c2luZyBrbml0cjo6a2FibGUgYW5kIGthYmxlRXh0cmEgc3R5bGluZwptZXRhZGF0YSAlPiUKICBrYmwoKSAlPiUgICMgQ3JlYXRlIGEgYmFzaWMgdGFibGUKICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBUUlVFKSAgIyBBcHBseSBhIGNsYXNzaWMgdGFibGUgc3R5bGUgd2l0aCBmdWxsIHdpZHRoCmBgYCAKCiMgUHJvamVjdCB2YXJpYWJsZXMKYGBge3IgcHJvamVjdF9wYXJhbWV0ZXJzLCBlY2hvPUZBTFNFfQojIERlZmluZSB0aGUgbGFiZWxzIGZvciBjb250cm9sIGFuZCB0cmVhdG1lbnQgZ3JvdXBzIHVzZWQgaW4gdGhlIGV4cGVyaW1lbnQKaGVhZChtZXRhZGF0YSkKIyBFeHRyYWN0IGNvbnRyb2wgYW5kIHRyZWF0bWVudCBjb25kaXRpb25zIGJhc2VkIG9uIGBleHBfMTJgIGxhYmVscwpjb250cm9sIDwtIG1ldGFkYXRhW21ldGFkYXRhJGV4cF8xMiA9PSAiY29udHJvbCIsIF0KdHJlYXRtZW50IDwtIG1ldGFkYXRhW21ldGFkYXRhJGV4cF8xMiA9PSAidHJlYXRtZW50IiwgXQoKY29udHJvbCA8LSB1bmlxdWUoY29udHJvbCRjb25kaXRpb24pCnRyZWF0bWVudCA8LSB1bmlxdWUodHJlYXRtZW50JGNvbmRpdGlvbikKCnByaW50KHBhc3RlMCgiVGhlIGNvbnRyb2wgaXM6ICIsIGNvbnRyb2wpKQpwcmludChwYXN0ZTAoIlRoZSB0cmVhdG1lbnQgaXM6ICIsIHRyZWF0bWVudCkpCgojIFNldCBkZWZhdWx0IHRleHQgc2l6ZXMgZm9yIHBsb3RzCnRleHQuc2l6ZSA8LSAxMiAgICAKYXhpcy50ZXh0LnNpemUgPC0gMTQKCmxvZ2ZjX2N1dG9mZiA8LSAyCnB2YWxfY3V0b2ZmIDwtIDAuMDEKCiMgU2V0IHZhcmlhYmxlcyBmb3IgdGhlIHByb2plY3QgdXNlZCBpbiBmaWxlbmFtZXMgYW5kIHBsb3QgdGl0bGVzCnByb2plY3QgPC0gIm9ybl9hbGdpbmF0ZV9ybmFzZXEiCmV4cCA8LSAiZXhwXzEyIiAgICAgICAgICAgICAgICAgICAgCmBgYAoKYGBge3IgcmF3ZGF0YX0KIyBMb2FkIHJhdyBjb3VudCBkYXRhCnJhd2NvdW50cyA8LSByZWFkLmNzdigiLi4vLi4vcmF3LWRhdGEvaHRzZXFfU0tfdW5maWx0ZXJlZF9yZXZlcnNlX3N0cmFuZF9jb3VudC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIGhlYWRlciA9IFRSVUUpCgojIEFwcGVuZCBhIHByZWZpeCB0byB0aGUgYmF0Y2ggYW5kIHJ1biBjb2x1bW5zIHRvIHR1cm4gdG8gY2hhcmFjdGVyIGNsYXNzCm1ldGFkYXRhW1siYmF0Y2giXV0gPC0gcGFzdGUwKCJiIiwgbWV0YWRhdGFbWyJiYXRjaCJdXSkKbWV0YWRhdGFbWyJydW4iXV0gPC0gcGFzdGUwKCJyIiwgbWV0YWRhdGFbWyJydW4iXV0pCgojIFN1YnNldCB0aGUgcmF3Y291bnRzIGRhdGFmcmFtZSB0byBvbmx5IGluY2x1ZGUgc2FtcGxlcyB0aGF0IGFyZSBwcmVzZW50IGluIGV4cGVyaW1lbnQKc2FtcGxlcyA8LSByb3duYW1lcyhtZXRhZGF0YSkKcmF3Y291bnRzIDwtIHNlbGVjdChyYXdjb3VudHMsIGFsbF9vZihzYW1wbGVzKSkKCiMgRW5zdXJlIHRoYXQgdGhlIG9yZGVyIG9mIHNhbXBsZXMgaW4gbWV0YWRhdGEgbWF0Y2hlcyB0aGUgb3JkZXIgb2YgY29sdW1ucyBpbiByYXdjb3VudHMKYWxsKHJvd25hbWVzKG1ldGFkYXRhKSA9PSBjb2xuYW1lcyhyYXdjb3VudHMpKSAgIyBTaG91bGQgcmV0dXJuIFRSVUUgaWYgb3JkZXIgaXMgY29ycmVjdAoKIyBEZWZpbmUgdGhlIGRlc2lnbiBmb3JtdWxhIGZvciBERVNlcTIsIGluY2x1ZGluZyBtYWluIGNvbmRpdGlvbiBhbmQgYmF0Y2ggZWZmZWN0CmRlc2lnbl9mb3JtdWxhIDwtIH4gZXhwXzEyICsgYmF0Y2gKCiMgQ3JlYXRlIERFU2VxMiBkYXRhc2V0IG9iamVjdCBmcm9tIHRoZSBmaWx0ZXJlZCBjb3VudCBtYXRyaXggYW5kIG1ldGFkYXRhCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KAogIGNvdW50RGF0YSA9IHJhd2NvdW50cywKICBjb2xEYXRhID0gbWV0YWRhdGEsCiAgZGVzaWduID0gZGVzaWduX2Zvcm11bGEKKQpgYGAKCgojIExpYnJhcnkgc2l6ZSBwZXIgc2FtcGxlCmBgYHtyIGxpYnJhcnlfc2l6ZX0KIyBTdW0gdXAgdGhlIGNvdW50cyBmb3IgZWFjaCBzYW1wbGUKcmF3Y291bnRzX3N1bXMgPC0gY29sU3VtcyhyYXdjb3VudHMpCgojIFR1cm4gaXQgaW50byBhIGRhdGEgZnJhbWUKZGF0YSA8LSBkYXRhLmZyYW1lKAogIENhdGVnb3J5ID0gbmFtZXMocmF3Y291bnRzX3N1bXMpLAogIENvdW50ID0gYXMubnVtZXJpYyhyYXdjb3VudHNfc3VtcykKKQoKIyBNYWtlIHN1cmUgc2FtcGxlIG9yZGVyIGlzIGNvbnNpc3RlbnQKZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZShDYXRlZ29yeSA9IGZhY3RvcihDYXRlZ29yeSwgbGV2ZWxzID0gc29ydCh1bmlxdWUoQ2F0ZWdvcnkpKSkpCgojIFBsb3QgYmFyIGNoYXJ0IG9mIGxpYnJhcnkgc2l6ZXMKcCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBDYXRlZ29yeSwgeSA9IENvdW50LCBmaWxsID0gQ2F0ZWdvcnkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICAjIGNvb3JkX2ZsaXAoKSAjIGZsaXAgaWYgdG9vIG1hbnkgc2FtcGxlcyB0byByZWFkIGxhYmVscyBlYXNpbHkKICBsYWJzKHkgPSAiTGlicmFyeSBzaXplIChNKSIsIHggPSBOVUxMKSArICAjIHJlbW92ZSB4IGxhYmVsLCByZW5hbWUgeSBheGlzCiAgIyBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKyAgIyBvcHRpb25hbCBjb2xvciBwYWxldHRlCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbGFiZWxzID0gc2NhbGVzOjpsYWJlbF9udW1iZXIoc2NhbGUgPSAxZS02KSwgICMgc2hvdyBjb3VudHMgaW4gbWlsbGlvbnMKICAgIGxpbWl0cyA9IGMoMCwgKG1heChkYXRhJENvdW50KSArIDFlNikpICAgICAgICAjIGFkZCBhIGJpdCBvZiBzcGFjZSBvbiB0b3AKICApICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSB0ZXh0LnNpemUsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksICAjIHNsYW50IGxhYmVscwogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gYXhpcy50ZXh0LnNpemUpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHRleHQuc2l6ZSksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGF4aXMudGV4dC5zaXplKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCAgIyBubyBsZWdlbmQgbmVlZGVkIHNpbmNlIGVhY2ggYmFyIGlzIGEgZGlmZmVyZW50IHNhbXBsZQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShsaW5ld2lkdGggPSAwLjIpLAogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKQogICkKCiMgU2hvdyB0aGUgcGxvdApwCgojIFNhdmUgdGhlIHBsb3QgYXMgYSBQREYgd2l0aCB0b2RheeKAmXMgZGF0ZSBhbmQgcHJvamVjdCBuYW1lCmNhaXJvX3BkZihwYXN0ZTAoU3lzLkRhdGUoKSwgIl9saWJyYXJ5X3NpemVfIiwgZXhwLCAiXyIsIHRyZWF0bWVudCwgIi52cy4iLCBjb250cm9sLCAiLnBkZiIpKQpwCmRldi5vZmYoKSAKCmBgYAoKIyBFeHByZXNzaW9uIGRpc3RyaWJ1dGlvbgoKYGBge3IgZXhwcmVzc2lvbl9kaXN0cmlidXRpb259CgojIFN0YWNrIHJhd2NvdW50cyBpbnRvIHNpbmdsZSB2ZWN0b3IgCmRhdCA8LSBzdGFjayhyYXdjb3VudHMpCgojIEZvcm1hdCBleHByZXNzaW9uIGRpc3RyaWJ1dGlvbiBwbG90IHdpdGggdGhlIGV4cHJlc3Npb24gbGV2ZWwgYW5kIGRlbnNpdHkgCmV4cHJlc3Npb25fZGlzdCA8LSBnZ3Bsb3QoZGF0LCBhZXMoeCA9IGxvZzIodmFsdWVzKzEpLCAgZmlsbD1pbmQsIGxhYmVsID0gaW5kICkpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41LCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgZ2VvbV90ZXh0ZGVuc2l0eShoanVzdCA9ICJ5bWF4Iiwgdmp1c3QgPSAwLAogICAgICAgICAgIHRleHRfb25seSA9IFRSVUUsIHRleHRfc21vb3RoaW5nID0gMjApICsgCiAgICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSB0ZXh0LnNpemUpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGF4aXMudGV4dC5zaXplKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gdGV4dC5zaXplKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGF4aXMudGV4dC5zaXplKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShsaW5ld2lkdGggPSAwLjIpLAogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMTUpKQoKZXhwcmVzc2lvbl9kaXN0Cgojc2F2ZSBmaWd1cmUKY2Fpcm9fcGRmKHBhc3RlMChTeXMuRGF0ZSgpLCAiX2V4cHJlc3Npb25fZGlzdF8iLCBleHAsICJfIiwgdHJlYXRtZW50LCAiLnZzLiIsIGNvbnRyb2wsICIucGRmIikpCmV4cHJlc3Npb25fZGlzdApkZXYub2ZmKCkgCgpgYGAKCiMgTm9uLXplcm8gZ2VuZXMgb2JzZXJ2ZWQKCmBgYHtyIG5vbi16ZXJvX2dlbmVzfQojIFByZXBhcmUgZGF0YSBieSBnZXR0aW5nIGxpYnJhcnkgc2l6ZXMgYW5kIG5vbi16ZXJvIGNvdW50cwp4IDwtIGNvbFN1bXMocmF3Y291bnRzKQp5IDwtIGNvbFN1bXMocmF3Y291bnRzICE9IDApIApkZiA8LSBkYXRhLmZyYW1lKHgseSkKZGYkaW5kIDwtIHJvd25hbWVzKGRmKQoKIyBDYWxjdWxhdGUgYXhpcyBsaW1pdHMKeF9taW4gPC0gbWluKGRmJHgpCnhfbWF4IDwtIG1heChkZiR4KQp5X21pbiA8LSBtaW4oZGYkeSkKeV9tYXggPC0gbWF4KGRmJHkpCgojIE5vbi16ZXJvIHBsb3QKbm9uemVybyA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IHgsIHkgPSB5LCBjb2xvciA9IGluZCwgbGFiZWwgPSBpbmQpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIGdlb21fdGV4dChoanVzdCA9IC0wLjEsIHZqdXN0ID0gLTAuMSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9Yyh5X21pbiwgeV9tYXgpKSArIAogIHNjYWxlX3hfY29udGludW91cygKICAgIGxpbWl0cyA9IGMoeF9taW4tMWUzLCB4X21heCs5ZTUpLAogICAgbGFiZWxzID0gc2NhbGVzOjpjb21tYV9mb3JtYXQoc2NhbGUgPSAxZS02LCAgYWNjdXJhY3kgPSAxKSAgIyBGb3JtYXQgbGFiZWxzIHdpdGhvdXQgZGVjaW1hbHMKICApICsKICB4bGFiKCJNaWxsaW9ucyBvZiByZWFkcyIpICsgeWxhYigiTnVtYmVyIG9mIG5vbi16ZXJvIGdlbmVzIG9ic2VydmVkIikgKwogIGxhYnMoY29sb3IgPSAiU2FtcGxlIikgKyAKICAgIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHRleHQuc2l6ZSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gYXhpcy50ZXh0LnNpemUpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSB0ZXh0LnNpemUpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gYXhpcy50ZXh0LnNpemUpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9IDAuMiksCiAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGNvb3JkX2NhcnRlc2lhbigpCgojIFNob3cgbm9uemVybyBwbG90Cm5vbnplcm8KCiMgU2F2ZSBwbG90CgpjYWlyb19wZGYocGFzdGUwKFN5cy5EYXRlKCksICJfbm9uLXplcm9fIiwgZXhwLCAiXyIsIHRyZWF0bWVudCwgIi52cy4iLCBjb250cm9sLCAiLnBkZiIpKQpub256ZXJvCmRldi5vZmYoKSAKCmBgYAoKIyBSUEtNIGFuZCBDUE0gdHJhbnNmb3JtYXRpb25zCmBgYHtyIGRhdGFfdHJhbnNmb3JtYXRpb25zfQojIExvYWQgR0ZGIGFubm90YXRpb24gCmdmZl9hbm5vdCA8LSBsb2FkX2dmZl9hbm5vdGF0aW9ucygiLi4vLi4vcmF3LWRhdGEvcGFlcnVnaW5vc2FfcGExNC5nZmYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkX2NvbCA9ICJnZW5lX2lkIiwgdHlwZSA9ICJnZW5lIikKcm93bmFtZXMoZ2ZmX2Fubm90KSA8LSBnZmZfYW5ub3QkZ2VuZV9pZAoKaGVhZChnZmZfYW5ub3QpCmhlYWQocmF3Y291bnRzKQpkaW0oZ2ZmX2Fubm90KQpkaW0ocmF3Y291bnRzKQoKc3VtbWFyeShyb3duYW1lcyhnZmZfYW5ub3QpID09IHJvd25hbWVzKHJhd2NvdW50cykpCmdmZl9hbm5vdCA8LSBnZmZfYW5ub3Rbcm93bmFtZXMocmF3Y291bnRzKSxdCnN1bW1hcnkocm93bmFtZXMoZ2ZmX2Fubm90KSA9PSByb3duYW1lcyhyYXdjb3VudHMpKQoKCiMgQ3JlYXRlIGFuIGV4cHJlc3Npb24gb2JqZWN0IHdpdGggY291bnRzLCBtZXRhZGF0YSwgYW5kIGdlbmUgaW5mbwpleHB0IDwtIGNyZWF0ZV9leHB0KGNvdW50X2RhdGFmcmFtZSA9IHJhd2NvdW50cywKICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YSA9IG1ldGFkYXRhLCAKICAgICAgICAgICAgICAgICAgICBnZW5lX2luZm8gPSBnZmZfYW5ub3QpCgojIE5vcm1hbGl6ZSBjb3VudHMgbG9nMiBDUE0KY3BtX25vcm1hbGl6ZWRfZXhwIDwtIG5vcm1hbGl6ZV9leHB0KGV4cHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtID0gImxvZzIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicmF3IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X21pbiA9ICIxMCIpCgojIyMjIyMjIENhbGN1bGF0ZSBSUEtNICMjIyMjIyMjCgojIEdyYWIgZ2VuZSBsZW5ndGhzIGZyb20gR0ZGIChzaG91bGQgYmUgaW4gYmFzZSBwYWlycykKZ2VuZV9sZW5ndGhzIDwtIGdmZl9hbm5vdCR3aWR0aCAgCm5hbWVzKGdlbmVfbGVuZ3RocykgPC0gcm93bmFtZXMoZ2ZmX2Fubm90KSAgIyBzZXQgZ2VuZSBuYW1lcwoKIyBNYWtlIHN1cmUgZ2VuZV9sZW5ndGhzIGFyZSBpbiB0aGUgc2FtZSBvcmRlciBhcyByYXdjb3VudHMgcm93cwpnZW5lX2xlbmd0aHMgPC0gZ2VuZV9sZW5ndGhzW3Jvd25hbWVzKHJhd2NvdW50cyldCgojIE1ha2UgREdFTGlzdCBvYmplY3QgZm9yIGVkZ2VSCmRnZSA8LSBER0VMaXN0KGNvdW50cyA9IHJhd2NvdW50cykKCiMgQ2FsY3VsYXRlIFJQS00gKHJlYWRzIHBlciBraWxvYmFzZSBwZXIgbWlsbGlvbikKcnBrbSA8LSBycGttKGRnZSwgZ2VuZS5sZW5ndGggPSBnZW5lX2xlbmd0aHMpCgojIFdyYXAgUlBLTSBpbiBhbiBleHBlcmltZW50IG9iamVjdCB0b28KZXhwdF9ycGttIDwtIGNyZWF0ZV9leHB0KGNvdW50X2RhdGFmcmFtZSA9IHJwa20sCiAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YSA9IG1ldGFkYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfaW5mbyA9IGdmZl9hbm5vdCkKCiMgbG9nMiB0cmFuc2Zvcm0gdGhlIFJQS00gKGFnYWluLCBubyBub3JtYWxpemF0aW9uIGJleW9uZCBsb2cpCnJwa21fbm9ybWFsaXplZF9leHAgPC0gbm9ybWFsaXplX2V4cHQoZXhwdF9ycGttLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm0gPSAibG9nMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicmF3IikKCiMgR2V0IHRoZSBhY3R1YWwgZXhwcmVzc2lvbiBtYXRyaWNlcyBmcm9tIHRoZSBleHBlcmltZW50IG9iamVjdHMKcnBrbSA8LSBleHBycyhycGttX25vcm1hbGl6ZWRfZXhwKQpjcG0gPC0gZXhwcnMoY3BtX25vcm1hbGl6ZWRfZXhwKQoKIyBNZXJnZSBDUE0gYW5kIFJQS00gbWF0cmljZXMgd2l0aCBnZW5lIGFubm90YXRpb24gaW5mbwpjcG1fYW5ub3RhdGVkIDwtIG1lcmdlKGdmZl9hbm5vdCwgY3BtLCBieSA9IDApCnJwa21fYW5ub3RhdGVkIDwtIG1lcmdlKGdmZl9hbm5vdCwgcnBrbSwgYnkgPSAwKQpgYGAKCiMjIEhlYXRtYXAgb2YgYWxnaW5hdGUgZ2VuZXMKVGhpcyBnZW5lcmF0ZXMgYSBzbWFsbCBoZWF0bWFwIG9mIHRoZSBhbGdpbmF0ZSBnZW5lcyBhY3Jvc3Mgc2FtcGxlcyB0byB2aXN1YWxpemUgZXhwcmVzc2lvbi4KCmBgYHtyIGhlYXRtYXBzfQojIExvb3AgdGhyb3VnaCBDUE0gYW5kIFJQS00gdG8gcGxvdCBib3RoIGhlYXRtYXBzCmZvciAodHlwZSBpbiBjKCJjcG0iLCAicnBrbSIpKSB7CiAgCiAgIyBEZWZpbmUgZ2VuZSBsaXN0CiAgYWxnX29wZXJvbl9nZW5lcyA8LSBjKAogICAgImFsZ0EiLCAiYWxnRiIsICJhbGdKIiwgImFsZ0kiLCAiYWxnTCIsICJhbGdYIiwgImFsZ0ciLCAiYWxnRSIsCiAgICAiYWxnSyIsICJhbGc0NCIsICJhbGc4IiwgImFsZ0QiLCAibXVjQyIsICJtdWNCIiwgImFsZ1UiLCAiYWxnVyIsCiAgICAiYWxnUCIsICJhbGdRIiwgImFsZ1IiLCAiYWxnWiIsICJhbGdDIiwgImFsZ0IiCiAgKQoKICAjIEdldCB0aGUgcmlnaHQgZGF0YSBwZXIgdHlwZSBvZiB0cmFuc2Zvcm1hdGlvbgogIGRhdGFfYW5ub3QgPC0gZ2V0KHBhc3RlMCh0eXBlLCAiX2Fubm90YXRlZCIpKQogIAogICMgRml4IHJvd25hbWVzIHVzaW5nIGdlbmUgbmFtZSAobWFrZSB1bmlxdWUpCiAgcm93bmFtZXMoZGF0YV9hbm5vdCkgPC0gbWFrZS5uYW1lcyhkYXRhX2Fubm90JG5hbWUsIHVuaXF1ZSA9IFRSVUUpCiAgCiAgIyBEcm9wIHVuZGVzaXJlZCBtZXRhZGF0YSBjb2x1bW5zCiAgY29sc190b19leGNsdWRlIDwtIGMoIlJvdy5uYW1lcyIsInNlcW5hbWVzIiwic3RhcnQiLCJlbmQiLCJ3aWR0aCIsCiAgICAgICAgICAgICAgICAgICAgICAgInN0cmFuZCIsInNvdXJjZSIsInR5cGUiLCJzY29yZSIsInBoYXNlIiwKICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZV9pZCIsIk5hbWUiLCJEYnhyZWYiLCJBbGlhcyIsIm5hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICJQYXJlbnQiLCJsb2N1cyIpCiAga2VlcCA8LSAhbmFtZXMoZGF0YV9hbm5vdCkgJWluJSBjb2xzX3RvX2V4Y2x1ZGUKICBleHByX2RhdGEgPC0gZGF0YV9hbm5vdFssIGtlZXBdCiAgCiAgIyBFeHRyYWN0IG1hdHJpeCBmb3IgYWxnIGdlbmVzCiAgYWxnX2dlbmVzIDwtIGV4cHJfZGF0YVthbGdfb3Blcm9uX2dlbmVzLCAsIGRyb3AgPSBGQUxTRV0KICBtYXQgPC0gYWxnX2dlbmVzIC0gcm93TWVhbnMoYWxnX2dlbmVzKQogIAogICMgUHVsbCBzYW1wbGUgYW5ub3RhdGlvbnMKICBhbm5vIDwtIGFzLmRhdGEuZnJhbWUobWV0YWRhdGFbLCBjKCJjb25kaXRpb24iLCAiaW5kdWNlZCIpXSkKICBjb2xuYW1lcyhhbm5vKSA8LSBjKCJDb25kaXRpb24iLCAiSW5kdWNlZCIpCiAgCiAgIyBDb2xvciBzZXR0aW5ncyBmb3IgYW5ub3RhdGlvbgogIyBhbm5fY29sb3JzIDwtIGxpc3QoCiAjICBDb25kaXRpb24gPSBjKCJMQi1ETVNPIiA9ICIjRDk1RjAyIiwgIkxCLUlQVEciID0gIiM2NkE2MUUiKSwKICMgIEluZHVjZWQgPSBjKCJubyIgPSAibGlnaHRncmV5IiwgInllcyIgPSAiZ3JheTI3IikKICMgKQogIAogICMgU2NhbGUgZGF0YSBieSByb3cgKFotc2NvcmUgcGVyIGdlbmUpCiAgc2NhbGVkX21hdCA8LSB0KHNjYWxlKHQobWF0KSkpCgogICMgQnVpbGQgaGVhdG1hcAogIGh0IDwtIEhlYXRtYXAoCiAgICBzY2FsZWRfbWF0LAogICAgbmFtZSA9ICJaLXNjb3JlIiwKICAgIHRvcF9hbm5vdGF0aW9uID0gSGVhdG1hcEFubm90YXRpb24oZGYgPSBhbm5vIywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNjb2wgPSBhbm5fY29sb3JzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICAgIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogICAgY29sdW1uX25hbWVzX3NpZGUgPSAiYm90dG9tIiwKICAgIGNvbHVtbl9uYW1lc19yb3QgPSA0NSwKICAgIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgICBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRQogICkKICAKICAjIERyYXcgYW5kIHNhdmUKICBkcmF3KGh0KQogIAogIGNhaXJvX3BkZihwYXN0ZTAoU3lzLkRhdGUoKSwgIl9hbGdfaGVhdG1hcF8iLCB0eXBlLCAiXyIsIGV4cCwgIl8iLCB0cmVhdG1lbnQsICIudnMuIiwgY29udHJvbCwgIi5wZGYiKSkKICBkcmF3KGh0KQogIGRldi5vZmYoKQp9CgpgYGAKCiMgREVTZXEKYGBge3IgREVTZXF9CiMgQ3JlYXRlIERFU2VxRGF0YVNldApkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeCgKICBjb3VudERhdGEgPSByYXdjb3VudHMsCiAgY29sRGF0YSA9IG1ldGFkYXRhLAogIGRlc2lnbiA9IGRlc2lnbl9mb3JtdWxhCikKCiMgQ2hlY2sgbnVtYmVyIG9mIGdlbmVzIGJlZm9yZSBmaWx0ZXJpbmcKcHJpbnQoIk51bWJlciBvZiBnZW5lcyBiZWZvcmUgZmlsdGVyaW5nOiIpCm5yb3coZGRzKQoKIyBGaWx0ZXI6IGtlZXAgZ2VuZXMgd2l0aCBjb3VudHMgPj0xMCBpbiBhdCBsZWFzdCAyIHNhbXBsZXMKcHJpbnQoIkZpbHRlcmluZyBnZW5lcyB3aXRoIGNvdW50cyA+PTEwIGluIGF0IGxlYXN0IDIgc2FtcGxlcyIpCmtlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzKSA+PSAxMCkgPj0gMiAKZGRzX2ZpbHRlcmVkIDwtIGRkc1trZWVwLCBdCgojIENoZWNrIG51bWJlciBvZiBnZW5lcyBhZnRlciBmaWx0ZXJpbmcKcHJpbnQoIk51bWJlciBvZiBnZW5lcyBhZnRlciBmaWx0ZXJpbmc6IikKbnJvdyhkZHNfZmlsdGVyZWQpCgojIFJ1biBERVNlcSBvbiB0aGUgZmlsdGVyZWQgZGF0YXNldApkZHNfZmluYWwgPC0gREVTZXEoZGRzX2ZpbHRlcmVkKQoKIyBTaG93IGF2YWlsYWJsZSByZXN1bHRzCnJlc3VsdHNOYW1lcyhkZHNfZmluYWwpCgojIFBsb3QgZGlzcGVyc2lvbiBlc3RpbWF0ZXMKcGxvdERpc3BFc3RzKGRkc19maW5hbCkKYGBgCgojIFNhdmUgREUgdGFibGVzIApgYGB7ciBzYXZlX3RhYmxlc30KcmVzIDwtIHJlc3VsdHMoZGRzX2ZpbmFsLCBjb250cmFzdCA9IGMoImV4cF8xMiIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKQpyZXNkZiA8LSBhcy5kYXRhLmZyYW1lKHJlcykKcmVzZGYkZ2VuZV9pZCA8LSByb3duYW1lcyhyZXNkZikKCmNwbSA8LSBhcy5kYXRhLmZyYW1lKGNwbSkKcnBrbSA8LSBhcy5kYXRhLmZyYW1lKHJwa20pCgojIGFwcGVuZCB0eXBlIG9mIHZhbHVlIHRvIHRoZSBkYXRhZnJhbWUgICAKY29sbmFtZXMocnBrbSkgPC0gcGFzdGUwKGNvbG5hbWVzKHJwa20pLCAiX3Jwa20iKQpjb2xuYW1lcyhjcG0pIDwtIHBhc3RlMChjb2xuYW1lcyhjcG0pLCAiX2NwbSIpCnJhd2NvdW50czIgPC0gcmF3Y291bnRzCmNvbG5hbWVzKHJhd2NvdW50czIpIDwtIHBhc3RlMChjb2xuYW1lcyhyYXdjb3VudHMyKSwgIl9yYXdjb3VudHMiKQoKIyBlbnN1cmUgY29sdW1uIGdlbmVfaWQgaXMgdGhlcmUKcnBrbSRnZW5lX2lkIDwtIHJvd25hbWVzKHJwa20pCmNwbSRnZW5lX2lkIDwtIHJvd25hbWVzKGNwbSkKcmF3Y291bnRzMiRnZW5lX2lkIDwtIHJvd25hbWVzKHJhd2NvdW50czIpCgpnZmZfYW5ub3QgPC0gZ2ZmX2Fubm90W3Jvd25hbWVzKHJlc2RmKSxdCgpkaW0ocmVzZGYpCiMgbWVyZ2Ugd2l0aCBzaWdnZW5lcwpyZXNkZiA8LSBmdWxsX2pvaW4ocmVzZGYsIGdmZl9hbm5vdCwgYnkgPSAiZ2VuZV9pZCIsIGNvcHkgPSBGQUxTRSkKaGVhZChyZXNkZikKCnBhX2dvIDwtIHJlYWQuY3N2KCIuLi8uLi9yYXctZGF0YS8yMDg5NjNfbWljcm9iZXMub25saW5lLmNzdiIsIGhlYWRlciA9IFRSVUUpCmhlYWQocGFfZ28pCgpjb2xfdG9fcmVtb3ZlIDwtIGludGVyc2VjdChjb2xuYW1lcyhwYV9nbyksY29sbmFtZXMocmVzZGYpKQpjb2xfdG9fcmVtb3ZlIDwtIHNldGRpZmYoY29sX3RvX3JlbW92ZSwgIkFsaWFzIikgIyByZXR1cm5zIGFsbCBleGNlcHQgdGhlIHNlY29uZCB2ZWN0b3IKCnBhX2dvIDwtIHBhX2dvICU+JSBzZWxlY3QoLWFsbF9vZihjb2xfdG9fcmVtb3ZlKSkKCnJlc2RmIDwtIGZ1bGxfam9pbihyZXNkZiwgcnBrbSwgYnkgPSAiZ2VuZV9pZCIsIGNvcHkgPSBGQUxTRSkKcmVzZGYgPC0gZnVsbF9qb2luKHJlc2RmLCBjcG0sIGJ5ID0gImdlbmVfaWQiLCBjb3B5ID0gRkFMU0UpCnJlc2RmIDwtIGZ1bGxfam9pbihyZXNkZiwgcmF3Y291bnRzMiwgYnkgPSAiZ2VuZV9pZCIsIGNvcHkgPSBGQUxTRSkKI3Jlc2RmIDwtIGZ1bGxfam9pbihyZXNkZiwgcGFfZ28sIGJ5ID0gIkFsaWFzIiwgY29weSA9IEZBTFNFKQpyZXNkZiA8LSBtZXJnZShyZXNkZiwgcGFfZ28sIGJ5ID0gIkFsaWFzIiwgY29weSA9IEZBTFNFKQoKaGVhZChyZXNkZikKd3JpdGUuY3N2KHJlc2RmLCBwYXN0ZTAoU3lzLkRhdGUoKSAsICJfcmVzdWx0c18iLGV4cCwgIl8iLCB0cmVhdG1lbnQsIi52cy4iLCBjb250cm9sLCIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCiMgU2F2ZSBERUcgbGlzdApgYGB7ciBzYXZlX2RlZ30KIyBmaWx0ZXIgZm9yIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIChMRkMgPj0gMywgcGFkaiA8IHB2YWxfY3V0b2ZmKQpkaW0ocmVzZGYpCmhlYWQocmVzZGYpCgpzdW1tYXJ5KGlzLm5hKHJlc2RmJGxvZzJGb2xkQ2hhbmdlKSkKc3VtbWFyeShpcy5uYShyZXNkZiRwYWRqKSkKCnNpZ19nZW5lcyA8LSByZXNkZlthYnMocmVzZGYkbG9nMkZvbGRDaGFuZ2UpID49IGxvZ2ZjX2N1dG9mZiAmIHJlc2RmJHBhZGogPCBwdmFsX2N1dG9mZiwgXSAgJT4lIGRyb3BfbmEobG9nMkZvbGRDaGFuZ2UpCnNpZ19nZW5lcyRkaXJlY3Rpb24gPC0gaWZlbHNlKHNpZ19nZW5lcyRsb2cyRm9sZENoYW5nZSA+IDAsICJ1cCIsICJkb3duIikKCnN1bW1hcnkoaXMubmEoc2lnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlKSkKc3VtbWFyeShpcy5uYShzaWdfZ2VuZXMkcGFkaikpCgpkaW0oc2lnX2dlbmVzKQpoZWFkKHNpZ19nZW5lcykKd3JpdGUuY3N2KHNpZ19nZW5lcywgcGFzdGUwKFN5cy5EYXRlKCksIl9zaWduaWZpY2FudGdlbmVzXyIsIGV4cCwgIl8iLCB0cmVhdG1lbnQsIi52cy4iLCBjb250cm9sLCIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCiMgUENBIHBsb3QgCmBgYHtyIFBDQX0KdnN0IDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gcmF3Y291bnRzLCBjb2xEYXRhID0gbWV0YWRhdGEsIGRlc2lnbiA9IH4gZXhwXzEyICsgYmF0Y2gpCgojIFZhcmlhbmNlIHN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9ucwp2c2QgPC0gdnN0KHZzdCwgYmxpbmQgPSBUUlVFKQoKIyBQQ0EgcGxvdApwY2FEYXRhIDwtIHBsb3RQQ0EodnNkLCBpbnRncm91cCA9IGMoImV4cF8xMiIsICJiYXRjaCIsICJjb25kaXRpb24iKSwgcmV0dXJuRGF0YSA9IFRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKIyBDYWxjdWxhdGUgdGhlIG1heGltdW0gYWJzb2x1dGUgdmFsdWUgYW1vbmcgUEMxIGFuZCBQQzIKbWF4X2Fic192YWwgPC0gbWF4KGMoYWJzKG1pbihwY2FEYXRhJFBDMSkpLCBhYnMobWluKHBjYURhdGEkUEMyKSksIGFicyhtYXgocGNhRGF0YSRQQzEpKSwgYWJzKG1heChwY2FEYXRhJFBDMikpKSkKCmF4aXNfc2l6ZSA8LSAxMAp0aGVtZV9zZXQodGhlbWVfbWluaW1hbChiYXNlX2ZhbWlseSA9ICJBcmlhbCIpKQoKcGNhIDwtIGdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIGNvbG9yID0gY29uZGl0aW9uLCBzaGFwZSA9IGNvbmRpdGlvbikpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgeGxhYihwYXN0ZTAoIlBDMTogIiwgcGVyY2VudFZhclsxXSwgIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIsIHBlcmNlbnRWYXJbMl0sICIlIHZhcmlhbmNlIikpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTAsIDExLCAxMiwgMTMsIDE0LCAxNSwgMTYpKSArCiAgY29vcmRfZml4ZWQocmF0aW8gPSAxLCB4bGltID0gYygtbWF4X2Fic192YWwsIG1heF9hYnNfdmFsKSwgeWxpbSA9IGMoLW1heF9hYnNfdmFsLCBtYXhfYWJzX3ZhbCkpICsKICBsYWJzKGNvbG9yID0gImNvbmRpdGlvbiIsIHNoYXBlID0gImNvbmRpdGlvbiIpICsKICBndWlkZXMoc2hhcGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiQ29uZGl0aW9uIiksICAjIEFkanVzdCBzaXplIG9mIHNoYXBlIGxlZ2VuZAogICAgY29sb3IgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiQ29uZGl0aW9uIikpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBheGlzX3NpemUpLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShsaW5ld2lkdGggPSAwLjIpLAogICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShsaW5ld2lkdGggPSAwLCBjb2xvciA9ICJibGFjayIpLCAgIyBTZXQgdGhlIGF4aXMgbGluZSBsaW5ld2lkdGgKICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgbGluZXdpZHRoID0gMC41KSwgICMgU2V0IHRoZSBwYW5lbCBib3JkZXIgbGluZXdpZHRoCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSBOQSkgICMgU2V0IGxlZ2VuZCBrZXkgYmFja2dyb3VuZCB0byB3aGl0ZSBhbmQgcmVtb3ZlIGJvcmRlcgogICkKCnByaW50KHBjYSkKCmNhaXJvX3BkZihwYXN0ZTAoU3lzLkRhdGUoKSwiX3BjYV8iLGV4cCwgIl8iLCB0cmVhdG1lbnQsIi52cy4iLCBjb250cm9sLCIucGRmIiwgc2VwID0gIiIpKQpwY2EKZGV2Lm9mZigpCgpgYGAKCiMgVm9sY2FubyBwbG90CmBgYHtyIHZvbGNhbm99CnNlcXVlbmNlIDwtIGMoInRyZWF0bWVudCIpCgpmb3IgKGkgaW4gc2VxdWVuY2UpIHsKICAgIHNldC5zZWVkKDApCiAgICByZXMgPC0gcmVzdWx0cyhkZHNfZmluYWwsIGNvbnRyYXN0ID0gYygiZXhwXzEyIiwgaSwgImNvbnRyb2wiKSkKCiAgZ3AyIDwtIHdlc19wYWxldHRlcyRaaXNzb3UxCiAga2V5IDwtIHJlYWQuY3N2KCIuLi8uLi9yYXctZGF0YS9wYTE0X2dlbmVfa2V5LmNzdiIsICBoZWFkZXIgPSBUUlVFKQoKICByb3duYW1lcyhrZXkpIDwtIGtleSRnZW5lX2lkCgogIHJlcyA8LSBtZXJnZShrZXksIGFzLmRhdGEuZnJhbWUocmVzKSwgYnk9MCkKICByZXMgPC0gYXMuZGF0YS5mcmFtZShyZXMpCiAgcmVzJEFsaWFzIDwtIHJvd25hbWVzKHJlcykKCiAjIERlZmluZSBnZW5lIGxpc3QKICBhbGdfZ2VuZXMgPC0gYygKICAgICJhbGdBIiwgImFsZ0YiLCAiYWxnSiIsICJhbGdJIiwgImFsZ0wiLCAiYWxnWCIsICJhbGdHIiwgImFsZ0UiLAogICAgImFsZ0siLCAiYWxnNDQiLCAiYWxnOCIsICJhbGdEIiwgIm11Y0MiLCAibXVjQiIsICJhbGdVIiwgImFsZ1ciLAogICAgImFsZ1AiLCAiYWxnUSIsICJhbGdSIiwgImFsZ1oiLCAiYWxnQyIsICJhbGdCIgogICkKCmxvZ2ZjX2N1dG9mZgprZXl2YWxzIDwtIGlmZWxzZSgKIHJlcyRsb2cyRm9sZENoYW5nZSA8PSAtbG9nZmNfY3V0b2ZmICYgcmVzJHBhZGogPD0gcHZhbF9jdXRvZmYsIGdwMlsxXSwKICBpZmVsc2UocmVzJGxvZzJGb2xkQ2hhbmdlID49IGxvZ2ZjX2N1dG9mZiAmIHJlcyRwYWRqIDw9IHB2YWxfY3V0b2ZmLCBncDJbNV0sCiAgICAgICAgICdsaWdodGdyZXknKSkKa2V5dmFsc1tpcy5uYShrZXl2YWxzKV0gPC0gJ2xpZ2h0Z3JleScKbmFtZXMoa2V5dmFscylba2V5dmFscyA9PSBncDJbNV1dIDwtICdVcC1yZWd1bGF0ZWQnCm5hbWVzKGtleXZhbHMpW2tleXZhbHMgPT0gJ2xpZ2h0Z3JleSddIDwtICdOUycKbmFtZXMoa2V5dmFscylba2V5dmFscyA9PSBncDJbMV1dIDwtICdEb3duLXJlZ3VsYXRlZCcKCnZvbGNhbm8gPC0gRW5oYW5jZWRWb2xjYW5vKHJlcywKICAgICAgICAgICAgICAgICAgICAgbGFiID0gcmVzW1sibmFtZSJdXSwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0TGFiID0gYWxnX2dlbmVzLAogICAgICAgICAgICAgICAgICAgICB4ID0gJ2xvZzJGb2xkQ2hhbmdlJywKICAgICAgICAgICAgICAgICAgICAgeSA9ICdwYWRqJywKICAgICAgICAgICAgICAgICAgICAgeGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwKICAgICAgICAgICAgICAgICAgICAgcEN1dG9mZiA9IHB2YWxfY3V0b2ZmLAogICAgICAgICAgICAgICAgICAgICBGQ2N1dG9mZiA9IGxvZ2ZjX2N1dG9mZiwKICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmTGluZVR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgICAgICAgcG9pbnRTaXplID0gMSwKICAgICAgICAgICAgICAgICAgICAgbGFiU2l6ZSA9IDMuMCwKICAgICAgICAgICAgICAgICAgICAgYXhpc0xhYlNpemUgPSAxNCwKICAgICAgICAgICAgICAgICAgICAgbGFiQ29sID0gJ2JsYWNrJywKICAgICAgICAgICAgICAgICAgICAgbGFiRmFjZSA9ICdib2xkJywKICAgICAgICAgICAgICAgICAgICAgI2NvbCA9IGMoJ2dyZXknLCBncDJbMl0sIGdwMlszXSwgZ3AyWzFdKSwKICAgICAgICAgICAgICAgICAgICAgY29sQ3VzdG9tID0ga2V5dmFscywKICAgICAgICAgICAgICAgICAgICAgY29sQWxwaGEgPSA1LzUsCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZFBvc2l0aW9uID0gJ2JvdHRvbScsCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZExhYlNpemUgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgbGVnZW5kSWNvblNpemUgPSA0LjAsCiAgICAgICAgICAgICAgICAgICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgYXJyb3doZWFkcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjIsCiAgICAgICAgICAgICAgICAgICAgIGdyaWRsaW5lcy5tYWpvciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICBib3JkZXJXaWR0aCA9IDAuMywKICAgICAgICAgICAgICAgICAgICAgZ3JpZGxpbmVzLm1pbm9yID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gcGFzdGUwKHRyZWF0bWVudCwiIHZzICIsIGNvbnRyb2wsIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgI2NhcHRpb24gPSBwYXN0ZTAoInRvdGFsID0gIiwgbnJvdyh0b3B0YWJsZSksICIgdmFyaWFibGVzIiksCiAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZTAoIlZvbGNhbm8gcGxvdCB3aXRoIGxvZzIgRkMgb2YgIiwgbG9nZmNfY3V0b2ZmLCIgYW5kIFAgdmFsdWUgb2YgIixwdmFsX2N1dG9mZiwgIi4iKSwKICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gMzAsCiAgICAgICAgICAgICAgICAgICAgICN5bGltID0gYygwLDEwKSwKICAgICAgICAgICAgICAgICAgICAgY29sQ29ubmVjdG9ycyA9ICdibGFjaycpICsgCiAgICAgIHRoZW1lKGF4aXMudGlja3M9ZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9MC4yKSkgIyArCiAjIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKC01LCAtMi41LCAwLCAyLjUsIDUpLCBsaW1pdHMgPSBjKC02LDYpKQoKcHJpbnQodm9sY2FubykKCmNhaXJvX3BkZihwYXN0ZTAoU3lzLkRhdGUoKSwiX3ZvbGNhbm9fIiwgZXhwLCAiXyIsIHRyZWF0bWVudCwiLnZzLiIsIGNvbnRyb2wsIi5wZGYiLCBzZXAgPSAiIikpCnByaW50KHZvbGNhbm8pCmRldi5vZmYoKQp9CgpgYGAKCiMgTUEgcGxvdApgYGB7ciBNQV9wbG90fQpzZXF1ZW5jZSA8LSBjKCJ0cmVhdG1lbnQiKQoKZm9yIChpIGluIHNlcXVlbmNlKSB7CiAgICBzZXQuc2VlZCgwKQoKcmVzIDwtIHJlc3VsdHMoZGRzX2ZpbmFsLCBjb250cmFzdCA9IGMoImV4cF8xMiIsIGksICJjb250cm9sIikpCgpncDIgPC0gd2VzX3BhbGV0dGVzJFppc3NvdTEKCnJlcyRnZW5lX2lkIDwtIHJvd25hbWVzKHJlcykKcmVzIDwtIGFzLmRhdGEuZnJhbWUocmVzKQpoZWFkKHJlcykKcmVzIDwtIGZ1bGxfam9pbihrZXksIGFzLmRhdGEuZnJhbWUocmVzKSwgYnk9ImdlbmVfaWQiKQpyb3duYW1lcyhyZXMpIDwtIHJlcyRBbGlhcwoKYW55KGlzLm5hKHJlcykpICAjIENoZWNrIGlmIHRoZXJlIGFyZSBhbnkgTkFzCgpyZXMgPC0gcmVzICU+JSBmaWx0ZXIoIWlzLm5hKGJhc2VNZWFuKSkKCgptYSA8LSBnZ21hcGxvdChyZXMsIAogICAgICAgICAgICAgICBmZHIgPSBwdmFsX2N1dG9mZiwgCiAgICAgICAgICAgICAgIGZjID0gMl4obG9nZmNfY3V0b2ZmKSwgCiAgICAgICAgICAgICAgIHNpemUgPSAxLAogICAgICAgICAgICAgICBwYWxldHRlID0gYygoZ3AyWzVdKSwgKGdwMlsxXSkpLAogICAgICAgICAgICAgICBzZWxlY3QudG9wLm1ldGhvZCA9ICJmYyIsCiAgICAgICAgICAgICAjICB5bGltID0gYygtMTUsMTUpLAogICAgICAgICAgICAgICBsZWdlbmQgPSAiYm90dG9tIiwgCiAgICAgICAgICAgICAgIHRpdGxlID0gcGFzdGUwKHRyZWF0bWVudCwiIHZzICIsIGNvbnRyb2wsIHNlcCA9ICIiKSwKICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiVm9sY2FubyBwbG90IHdpdGggbG9nMiBGQyBvZiAiLCBsb2dmY19jdXRvZmYsIiBhbmQgUCB2YWx1ZSBvZiAiLHB2YWxfY3V0b2ZmLCAiLiIpLAogICAgICAgICAgICAgICBmb250LmxhYmVsID0gYyggMTIpLCAKICAgICAgICAgICAgIGxhYmVsLnJlY3RhbmdsZSA9IEZBTFNFLAogICAgICAgICAgICAgIGdlbmVuYW1lcyA9IHJlcyRuYW1lLAogICAgICAgICAgICAgIGZvbnQubGVnZW5kID0gYygxMiksCiAgICAgICAgICAgICAgIGZvbnQubWFpbiA9IGMoImJvbGQiLCAxNCksCiAgICAgICAgICAgICAgIGFscGhhID0gMS41LAogICAgICAgICAgICAgICBnZ3RoZW1lID0gZ2dwbG90Mjo6dGhlbWUocGFuZWwuZ3JpZCA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSkpKSAKcHJpbnQobWEpCgpjYWlyb19wZGYocGFzdGUwKFN5cy5EYXRlKCksIl9tYV8iLCBleHAsICJfIiwgdHJlYXRtZW50LCIudnMuIiwgY29udHJvbCwiLnBkZiIpKQpwcmludChtYSkKZGV2Lm9mZigpCn0KCmBgYAoKIyBDaXJjb3MgcGxvdApgYGB7ciBjaXJjbGl6ZX0KbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShzY2FsZXMpCgoKCmdmZl9hbm5vdCA8LSBnZmZfYW5ub3Rbcm93bmFtZXMocmF3Y291bnRzKSxdCmhlYWQocmF3Y291bnRzKQpoZWFkKGdmZl9hbm5vdCkKaGVhZChyZXMpCnJvd25hbWVzKHJlcykgPC0gcmVzJGdlbmVfaWQKZ2ZmX2Fubm90IDwtIGdmZl9hbm5vdFtyb3duYW1lcyhyZXMpLF0Kc3VtbWFyeShyb3duYW1lcyhnZmZfYW5ub3QpID09IHJvd25hbWVzKHJlcykpCgojIFN0ZXAgMjogQWRkIHRvIGFubm90YXRpb24KCmdmZl9hbm5vdCRsb2cyRm9sZENoYW5nZSA8LSByZXMkbG9nMkZvbGRDaGFuZ2UKCmhlYWQoZ2ZmX2Fubm90KQoKIyBTdGVwIDM6IENyZWF0ZSBzdW1tYXJ5IGZvciBlYWNoIGNocm9tb3NvbWUgKHVzdWFsbHkganVzdCAxIGluIGJhY3RlcmlhKQpjaHJfcmFuZ2VzIDwtIGdmZl9hbm5vdCB8PgogIGRwbHlyOjpncm91cF9ieShzZXFuYW1lcykgfD4KICBkcGx5cjo6c3VtbWFyaXNlKHN0YXJ0ID0gbWluKHN0YXJ0KSwgZW5kID0gbWF4KGVuZCkpIHw+CiAgZHBseXI6OnVuZ3JvdXAoKQoKY2Fpcm9fcGRmKHBhc3RlMChTeXMuRGF0ZSgpLCJfY2lyY29zXyIsIGV4cCwgIl8iLCB0cmVhdG1lbnQsIi52cy4iLCBjb250cm9sLCIucGRmIiwgc2VwID0gIiIpKQoKY2lyY29zLmNsZWFyKCkKY2lyY29zLnBhcihjZWxsLnBhZGRpbmcgPSBjKDAsIDAsIDAsIDApKQoKIyBOb3JtYWxpemUgbG9nMkZDIHRvIHJhbmdlIFstMSwgMV0KbWF4X2ZjIDwtIG1heChhYnMoZ2ZmX2Fubm90JGxvZzJGb2xkQ2hhbmdlKSwgbmEucm0gPSBUUlVFKQpnZmZfYW5ub3Qkbm9ybV9mYyA8LSBnZmZfYW5ub3QkbG9nMkZvbGRDaGFuZ2UgLyBtYXhfZmMKCiMgQXNzaWduIGNvbG9yOiByZWQgKHVwKSwgYmx1ZSAoZG93biksIGdyYXkgKG5vdCBzaWduaWZpY2FudCkKZ2ZmX2Fubm90JGNvbCA8LSAiZ3JheSIKZ2ZmX2Fubm90JGNvbFtnZmZfYW5ub3QkbG9nMkZvbGRDaGFuZ2UgPiAyICYgcmVzJHBhZGogPCAwLjAxXSA8LSByZ2IoMSwgMCwgMCwgYWxwaGEgPSAwLjcpICAjIHJlZApnZmZfYW5ub3QkY29sW2dmZl9hbm5vdCRsb2cyRm9sZENoYW5nZSA8IC0yICYgcmVzJHBhZGogPCAwLjAxXSA8LSByZ2IoMCwgMCwgMSwgYWxwaGEgPSAwLjcpICAjIGJsdWUKCiMgSW5pdGlhbGl6ZSBjaXJjb3MgcGxvdApjaXJjb3MuaW5pdGlhbGl6ZShmYWN0b3JzID0gY2hyX3JhbmdlcyRzZXFuYW1lcywgeGxpbSA9IGNocl9yYW5nZXNbLCBjKCJzdGFydCIsICJlbmQiKV0pCgojIFRyYWNrIHBsb3R0aW5nCmNpcmNvcy50cmFja1Bsb3RSZWdpb24oCiAgeWxpbSA9IGMoLTEsIDEpLCAgIyBhbGxvdyBiYXJzIHRvIGdvIGJlbG93IGFuZCBhYm92ZSBheGlzCiAgdHJhY2suaGVpZ2h0ID0gMC4yLAogIHBhbmVsLmZ1biA9IGZ1bmN0aW9uKHJlZ2lvbiwgdmFsdWUsIC4uLikgewogICAgc2VjdG9yIDwtIGdldC5jZWxsLm1ldGEuZGF0YSgic2VjdG9yLmluZGV4IikKICAgIGdlbmVfZGF0YSA8LSBnZmZfYW5ub3RbZ2ZmX2Fubm90JHNlcW5hbWVzID09IHNlY3RvciwgXQogICAgY2lyY29zLnJlY3QoCiAgICAgIHhsZWZ0ID0gZ2VuZV9kYXRhJHN0YXJ0LAogICAgICB5Ym90dG9tID0gMCwKICAgICAgeHJpZ2h0ID0gZ2VuZV9kYXRhJGVuZCwKICAgICAgeXRvcCA9IGdlbmVfZGF0YSRub3JtX2ZjLAogICAgICBjb2wgPSBnZW5lX2RhdGEkY29sLAogICAgICBib3JkZXIgPSBOQQogICAgKQogIH0sCiAgYmcuYm9yZGVyID0gTkEsCiAgYmcuY29sID0gIndoaXRlIgopCgoKIyBEZWZpbmUgZ2VuZSBsaXN0CiAgYWxnX2dlbmVzIDwtIGMoCiAgICAiYWxnQSIsICJhbGdGIiwgImFsZ0oiLCAiYWxnSSIsICJhbGdMIiwgImFsZ1giLCAiYWxnRyIsICJhbGdFIiwKICAgICJhbGdLIiwgImFsZzQ0IiwgImFsZzgiLCAiYWxnRCIsICJtdWNDIiwgIm11Y0IiLCAiYWxnVSIsICJhbGdXIiwKICAgICJhbGdQIiwgImFsZ1EiLCAiYWxnUiIsICJhbGdaIiwgImFsZ0MiLCAiYWxnQiIsICJkbmFBIgogICkKCmFsZ19nZW5lc19mb3VuZCA8LSBnZmZfYW5ub3RbZ2ZmX2Fubm90JG5hbWUgJWluJSBhbGdfZ2VuZXMgfCBnZmZfYW5ub3QkQWxpYXMgJWluJSBhbGdfZ2VuZXMsIF0KZ2ZmX2Fubm90JGhpZ2hsaWdodCA8LSBpZmVsc2UoZ2ZmX2Fubm90JG5hbWUgJWluJSBhbGdfZ2VuZXMgfCBnZmZfYW5ub3QkQWxpYXMgJWluJSBhbGdfZ2VuZXMsIFRSVUUsIEZBTFNFKQoKY2lyY29zLnRyYWNrUGxvdFJlZ2lvbigKICB5bGltID0gYygwLCAxKSwKICB0cmFjay5oZWlnaHQgPSAwLjA1LAogIHBhbmVsLmZ1biA9IGZ1bmN0aW9uKC4uLikgewogICAgc2VjdG9yIDwtIGdldC5jZWxsLm1ldGEuZGF0YSgic2VjdG9yLmluZGV4IikKICAgIGdlbmVfZGF0YSA8LSBnZmZfYW5ub3RbZ2ZmX2Fubm90JHNlcW5hbWVzID09IHNlY3RvciAmIGdmZl9hbm5vdCRoaWdobGlnaHQgPT0gVFJVRSwgXQogICAgaWYobnJvdyhnZW5lX2RhdGEpID09IDApIHJldHVybigpCiAgICAKICAgICMgRHJhdyByZWN0YW5nbGVzIG9yIHBvaW50cyBhdCBnZW5lIHN0YXJ0IHBvc2l0aW9uCiAgICBjaXJjb3MucmVjdCgKICAgICAgeGxlZnQgPSBnZW5lX2RhdGEkc3RhcnQsCiAgICAgIHlib3R0b20gPSAwLAogICAgICB4cmlnaHQgPSBnZW5lX2RhdGEkZW5kLAogICAgICB5dG9wID0gMSwKICAgICAgY29sID0gImdvbGQiLCAgICMgaGlnaGxpZ2h0IGNvbG9yCiAgICAgIGJvcmRlciA9ICJibGFjayIKICAgICkKICAgIAogICAgIyBPcHRpb25hbDogYWRkIGdlbmUgbGFiZWxzCiAgICBjaXJjb3MudGV4dCgKICAgICAgeCA9IChnZW5lX2RhdGEkc3RhcnQgKyBnZW5lX2RhdGEkZW5kKSAvIDIsCiAgICAgIHkgPSAxLjA1LAogICAgICBsYWJlbHMgPSBnZW5lX2RhdGEkbmFtZSwKICAgICAgZmFjaW5nID0gImNsb2Nrd2lzZSIsCiAgICAgIG5pY2VGYWNpbmcgPSBUUlVFLAogICAgICBjZXggPSAwLjUsCiAgICAgIGFkaiA9IGMoMCwgMC41KQogICAgKQogIH0sCiAgYmcuYm9yZGVyID0gTkEKKQoKIyBEcmF3IGFuIGVtcHR5IHRyYWNrIChubyBkYXRhKSBmb3IgdGhlIHRpY2sgbWFya3MgYW5kIGxhYmVscwpjaXJjb3MudHJhY2tQbG90UmVnaW9uKAogIHlsaW0gPSBjKDAsIDEpLAogIHRyYWNrLmhlaWdodCA9IDAuMDUsCiAgYmcuYm9yZGVyID0gTkEsCiAgcGFuZWwuZnVuID0gZnVuY3Rpb24oeCwgeSkgewogICAgc2VjdG9yIDwtIGdldC5jZWxsLm1ldGEuZGF0YSgic2VjdG9yLmluZGV4IikKICAgIHhsaW0gPC0gZ2V0LmNlbGwubWV0YS5kYXRhKCJ4bGltIikKICAgIHlsaW0gPC0gZ2V0LmNlbGwubWV0YS5kYXRhKCJ5bGltIikKICAgIGNpcmNvcy5jZW50ZXIgPC0gZ2V0LmNlbGwubWV0YS5kYXRhKCJjZWxsLnN0YXJ0LmRlZ3JlZSIpICMgZm9yIHBvc3NpYmxlIGFuZ2xlIHJlZmVyZW5jZQogICAgCiAgICAjIENyZWF0ZSB0aWNrIHBvc2l0aW9ucyBldmVyeSAxTWIgd2l0aGluIHNlY3RvciByYW5nZQogICAgdGlja3MgPC0gc2VxKGZyb20gPSBjZWlsaW5nKHhsaW1bMV0gLyAxZTYpICogMWU2LCB0byA9IHhsaW1bMl0sIGJ5ID0gMWU2KQogICAgCiAgICAjIERyYXcgdmVydGljYWwgdGlja3MgKGxpbmVzKSBvbiBjaXJjbGUgYXQgZWFjaCB0aWNrIHBvc2l0aW9uCiAgICBjaXJjb3Muc2VnbWVudHMoCiAgICAgIHgwID0gdGlja3MsIHkwID0gMCwKICAgICAgeDEgPSB0aWNrcywgeTEgPSAwLjIsCiAgICAgIHNlY3Rvci5pbmRleCA9IHNlY3RvciwKICAgICAgY29sID0gImJsYWNrIiwKICAgICAgbHdkID0gMQogICAgKQogICAgCiAgICAjIEFkZCBsYWJlbHMgYSBsaXR0bGUgb3V0c2lkZSB0aGUgdGlja3MsIHNob3dpbmcgIlggTWIiCiAgICBsYWJlbF9wb3MgPC0gdGlja3MgKyA1ZTQgICMgc2xpZ2h0bHkgb2Zmc2V0IHRvIHRoZSByaWdodCwgYWRqdXN0IGlmIG5lZWRlZAogICAgCiAgICAjIENvbnZlcnQgbGFiZWwgcG9zaXRpb24gdG8gTWIgYW5kIGFkZCAiTWIiCiAgICBsYWJlbHMgPC0gcGFzdGUwKHRpY2tzIC8gMWU2LCAiIE1iIikKICAgIAogICAgY2lyY29zLnRleHQoCiAgICAgIHggPSB0aWNrcywKICAgICAgeSA9IDAuMjUsCiAgICAgIGxhYmVscyA9IGxhYmVscywKICAgICAgc2VjdG9yLmluZGV4ID0gc2VjdG9yLAogICAgICBmYWNpbmcgPSAiY2xvY2t3aXNlIiwKICAgICAgbmljZUZhY2luZyA9IFRSVUUsCiAgICAgIGFkaiA9IGMoMCwgMC41KSwKICAgICAgY2V4ID0gMC42CiAgICApCiAgfQopCgojIEV4YW1wbGU6IGRlZmluZSBzaWduaWZpY2FudCBERUdzIChhZGp1c3QgeW91ciB0aHJlc2hvbGQgYXMgbmVlZGVkKQpnZmZfYW5ub3QkcGFkaiA8LSByZXMkcGFkagoKc2lnX2RlZ3MgPC0gZ2ZmX2Fubm90WyFpcy5uYShnZmZfYW5ub3QkcGFkaikgJiBnZmZfYW5ub3QkcGFkaiA8IDAuMDEsIF0KbnVtX3VwIDwtIHN1bShzaWdfZGVncyRsb2cyRm9sZENoYW5nZSA+IDIpCm51bV9kb3duIDwtIHN1bShzaWdfZGVncyRsb2cyRm9sZENoYW5nZSA8IC0yKQoKCmxpYnJhcnkoZ3JpZCkKCiMgQWZ0ZXIgYWxsIGNpcmNvcyBwbG90dGluZwpncmlkLnRleHQoCiAgbGFiZWwgPSBwYXN0ZTAoIlVwOiAiLCBudW1fdXAsICJcbkRvd246ICIsIG51bV9kb3duKSwKICB4ID0gMC41LCB5ID0gMC41LCAgICAgICAgICMgQ2VudGVyIG9mIHBsb3R0aW5nIGFyZWEKICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgY29sID0gImJsYWNrIikKKQoKZGV2Lm9mZigpCgoKYGBgCg==