---
title: "MutSeqR Summary Report"
author: "Matthew J. Meier & Annette E. Dodge"
output:
  html_document:
    toc: yes
    toc_float: yes
    number_sections: yes
    code_folding: hide
    theme: spacelab
    code_download: yes
  pdf_document:
    toc: yes
---

```{r load_libraries, warning=FALSE, include=FALSE}
library(MutSeqR)
library(here)
library(knitr)
library(DT)
library(bs4Dash)
```

```{r check-for-suggests, include=FALSE}
check_pkgs <- function(pkgs) {
     installed_status <- vapply(pkgs, requireNamespace, logical(1), quietly = TRUE)
     missing_packages <- pkgs[!installed_status]
     if (length(missing_packages) > 0) {
    stop("Rendering halted: The following required packages are not installed:\n  ",
      paste(missing_packages, collapse = ", "), call. = FALSE)
  } else {
    return(invisible(TRUE))
  }
}
plotting <- c(
  "colorspace", # plot_mean_mf
  "dendsort", # plot_spectra
  "ggh4x", # plot_spectra
  "ggrepel", # plot_mf plot_mean_mf
  "gtools", # plot_mf plot_mean_mf plot_spectra
  "packcircles", #plot_bubbles
  "patchwork", # plot_spectra
  "RColorBrewer" # plot_mf plot_mean_mf plot_spectra plot_bubbles plot_lollipop
)
stats <- c("doBy", "car", "lme4", "withr") # model_mf & proast
general <- c("openxlsx", "BSgenome")
pkg_ls <- c(plotting, general)
if (!is.null(params$exp_variable)) {
pkg_ls <- c(pkg_ls, stats)
}

check_pkgs(pkg_ls)
```

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
has_description <- !is.null(params$project_description)
description_text <- paste("Purpose of the report:", params$project_description)
```

# `r params$project_title` Summary Report {-}

Date report generated: `r format(Sys.time(), '%d %B, %Y')`

Report prepared by: `r params$user_name`

Report prepared for: `r params$researcher_name`

Sequencing technology: `r params$ecs_technology`

`r if(has_description) {description_text}`

Output saved to : `r params$outputdir`

---

# Import the Mutation Data

```{r import-data}
no_depth_warning <- "Could not find an appropriate depth column. Some package functionality may be limited.\n"
correct_depth_warning <-  "The total_depth may be double-counted in some instances due to overlapping positions. Set the correct_depth parameter in calculate_mf() to correct the total_depth for these instances."
warnings_list <- character(0)
mutation_data <- NULL

if (params$file_type == "table") {
  mutation_data <- withCallingHandlers(
    import_mut_data(mut_file = params$mutation_file,
                    mut_sep = "\t",
                    is_0_based_mut = params$is_0_based_mut,
                    sample_data = params$sample_data,
                    sd_sep = "\t",
                    regions = params$regions,
                    rg_sep = "\t",
                    is_0_based_rg = params$is_0_based_rg,
                    BS_genome = params$BS_genome),
    warning = function(w) {
      warnings_list <<- c(warnings_list, w$message)
    }
  )
  no_depth <- any(grepl(no_depth_warning,
                        warnings_list,
                        fixed = TRUE))
  correct_depth <- any(grepl(correct_depth_warning,
                             warnings_list,
                             fixed = TRUE))
} else if (params$mut_file_type == "vcf") {
  mutation_data <- withCallingHandlers(
    import_vcf_data(vcf_file = params$mutation_file,
                    sample_data = params$sample_data,
                    sd_sep = "\t",
                    regions = params$regions,
                    rg_sep = "\t",
                    is_0_based_rg = params$is_0_based_rg,
                    BS_genome = params$BS_genome),
    warning = function(w) {
      warnings_list <<- c(warnings_list, w$message)
    }
  )
  no_depth <- any(grepl(no_depth_warning, warnings_list, fixed = TRUE))
  correct_depth <- any(grepl(correct_depth_warning,
                             warnings_list,
                             fixed = TRUE))
}
if (no_depth & is.null(params$precalc_depth_data_global)) {
  no_mf <- TRUE
} else {
  no_mf <- FALSE
}
sample_no <- length(unique(mutation_data$sample))
entries <- nrow(mutation_data)

```

```{r check-calc-depth, eval=!no_depth, echo=FALSE, results='asis'}
no_variants_present <- "no_variant" %in% unique(mutation_data$variation_type)
if (!no_variants_present) {
  cat("WARNING! The imported mutation data contains a total_depth column",
    "but does not contain no_variant sites. Group depth and mutation",
    "frequencies may not be accurate if the mutation data does not contain",
    "total_depth values for ~ every sequenced site ~. Consider",
    "removing the total_depth column and precalculating the per-sample depth",
    "(see calculate_mf(precalc_depth_data)) if it is not possible to import all sequenced",
    "sites into R."
  )
}
```

Imported `r entries` rows from `r file.path(params$projectdir, params$mutation_file)`.

Mutation data has `r sample_no` samples.

# Filter Mutation Data

The mutation data is filtered using the parameters defined below. Rows are
either flagged or removed from the mutation data. Variants that are flagged
as TRUE within the _filter_mut_ column are excluded from mutation counts in all
downstream analyses. However, total*depth of flagged variants is retained for
frequency calculations^1^. All records flagged or removed during the filtering
process are returned in \_filtered_variants.xlsx.*

**Flagging variants with a VAF > than `r params$vaf_cutoff` as germline.**
VAF (variant allele fraction) is calculated as alt_depth/total_depth.

**Flagging SNVs that overlap with germline MNVs: `r params$snv_in_germ_mnv`.**
Germline MNVs are defined as having a VAF > `r params$vaf_cutoff`.
These SNVs are often artifacts from variant calling.

**Removing rows with an abnormal VAF: `r params$rm_abnormal_vaf`.**
Removes rows with 0.05 > VAF < 0.45 or 0.55 > VAF < 0.95.

**Using a custom filter: `r !is.null(params$custom_filter_col)`.**

```{r echo=FALSE, eval=!is.null(params$custom_filter_col), results='asis'}
cat("Applying Custom Filter to column: '", params$custom_filter_col, "'. ",
    "Records are ", ifelse(params$custom_filter_rm, " removed", "flagged"),
    " when the value matches '", params$custom_filter_val, "'", sep = "")
```

**Removing records based on a regions file: `r !is.null(params$filtering_regions)`.**

```{r echo=FALSE, eval= !is.null(params$filtering_regions), results='asis'}
cat("Filtering records using regions file: ", params$filtering_regions, ".",
    " Records ", ifelse(params$regions_filter == "keep_within",
    "outside of ", "within "), " the specified filtering regions are",
    " removed from the dataset.", sep = "")
cat ("Records must start and end within the filtering regions to be included: ",
     !params$allow_half_overlap, sep = "")
```

**Flagged variants (except those identified as germline) have their alt_depth subtracted from their total_depth: `r params$rm_filtered_mut_from_depth`.**
If TRUE, flagged variants are treated as N-calls within the data.

^1^ _Only applicable for mutation data that contains an appropriate total_depth column._

```{r filter-mutation-data, message=TRUE, results='asis'}
if (no_depth) {
  params$rm_filtered_mut_from_depth <- FALSE
}

filtered_mutation_data <- filter_mut(
  mutation_data,
  vaf_cutoff = params$vaf_cutoff,
  snv_in_germ_mnv = params$snv_in_germ_mnv,
  rm_abnormal_vaf = params$rm_abnormal_vaf,
  custom_filter_col = params$custom_filter_col,
  custom_filter_val = params$custom_filter_val,
  custom_filter_rm = params$custom_filter_rm,
  regions = params$filtering_regions,
  rg_sep = params$filtering_rg_sep,
  regions_filter = params$regions_filter,
  allow_half_overlap = params$allow_half_overlap,
  is_0_based_rg = params$filtering_is_0_based_rg,
  rm_filtered_mut_from_depth = params$rm_filtered_mut_from_depth,
  return_filtered_rows = TRUE
)
filtered_variants <- filtered_mutation_data$filtered_rows
write_excel(data = filtered_variants,
            output_path = params$outputdir,
            workbook_name = "filtered_variants")
filtered_mutation_data <- filtered_mutation_data$mutation_data
filter_no <- nrow(filtered_variants)
entries <- nrow(filtered_mutation_data)
```

`r filter_no` total rows were flagged/removed.

`r entries` rows remaining in mutation data.

# Calculate Per-Sample Mutation Frequency {.tabset}

Mutation frequency (MF) is calculated by dividing the sum of mutations by the
sum of the total_depth for a given sample (units: mutations/bp).

**Mutation Frequency minimum** (mf_min). Each mutation is counted once, regardless
of the number of reads that contain the non-reference allele.

**Mutation Frequency maximum** (mf_max). Multiple identical mutations at the same
position within a sample are counted as independent mutation events.

**Correcting the total_depth for overlapping positions: `r correct_depth`.**
When TRUE, the total_depth at overlapping positions within a single sample's
mutation data will have depth corrected. Overlapping positions are defined as
those sharing the same sample, contig, and start values. For these positions,
the total_depth will be retained for only one row. All other rows will have
their total_depth set to 0 to prevent double-counting. This parameter is set
to TRUE automatically when overlapping positions are detected within
the mutation data.

```{r no-depth-msg-global, eval=no_depth, results='asis'}
if (is.null(params$precalc_depth_data_global)) {
  cat("No appropriate total_depth column within the mutation data.
      Cannot calculate the sample group depth.
      Cannot calculate mutation frequencies.")
} else {
  cat("Importing precalculated sample group depth from ", params$projectdir,
      "/", params$precalc_depth_data_global, sep = "")
}
```

## Summary Table

```{r calculate-global-mf-sample}
mf_data <- calculate_mf(
  mutation_data = filtered_mutation_data,
  cols_to_group = "sample",
  subtype_resolution = "none",
  variant_types = c("-ambiguous", "-uncategorized"),
  calculate_depth = !no_depth,
  precalc_depth_data = params$precalc_depth_data_global,
  d_sep = params$d_sep,
  correct_depth = correct_depth,
  retain_metadata_cols = params$exp_variable
)
plot_max <- all(mf_data$sum_min != mf_data$sum_max)
if (!no_mf) {
  DT::datatable(mf_data,
    caption = "Figure 1. Mutation Frequency per Sample",
    options = list(
      columnDefs = list(
        list(targets = c("mf_min", "mf_max"), # check if ths works no mf.
            render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
      ),
      rowCallback = JS(
        "function(row, data, dataIndex) {
          $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
        }"
      )
    )
  )
} else {
  DT::datatable(mf_data,
    caption = "Figure 1. Mutation Sums per Sample"
  )
}
# Results List for Export
Results <- list("Global MF per Sample" = mf_data)
```

## Plot Mutation Frequency per Sample

```{r set-plot-size3.2, include=FALSE}
fig_width <- 8
fig_height <- 6
labels <- "count"
n_samples <- length(mf_data$sample)
if (!plot_max && n_samples > 24 || plot_max && n_samples > 8) {
  fig_width <- 12
  fig_height <- 6
}
if (!plot_max && n_samples > 48 || plot_max && n_samples > 24) {
  fig_width <- 24
  fig_height <- 12
}
if (plot_max && n_samples > 54) {
  fig_width <- 30
  fig_height <- 10
  if (n_samples > 72) {
    labels <- "none"
  }
}
```

```{r plot3.2-caption, include=FALSE, eval = !no_mf}
# Define your caption components
caption <- paste0(
  "Mutation Frequency (MF) Minimum",
  if (plot_max) " and Maximum",
  " (mutations/bp) per Sample.",
  if (plot_max) " Light coloured bars represent MFmin and dark coloured bars represent MFmax.",
  if (!is.null(params$exp_variable)) paste0(" Bars are coloured and grouped by ", params$exp_variable, "."),
  " Data labels are the number of mutations per sample."
)

```

```{r plot-mf, eval = !no_mf, fig.width=fig_width, fig.height=fig_height, fig.cap=caption}
plot_data <- mf_data
if (!is.null(params$exp_variable)) {
  plot_data[[params$exp_variable]] <- factor(plot_data[[params$exp_variable]],
                                             levels = params$exp_variable_order)
}
plot3.2 <- plot_mf(
  mf_data = plot_data,
  group_col = "sample",
  plot_type = "bar",
  mf_type = ifelse(plot_max, "both", "min"),
  group_order = ifelse(is.null(params$exp_variable),
                       "smart",
                       "arranged"),
  group_order_input = params$exp_variable,
  fill_col = params$exp_variable,
  labels = labels,
  x_lab = "Sample",
  y_lab = "Mutation Frequency (mutations/bp)"
)
plot3.2
```

## Plot Mean Mutation Frequency

```{r set-plot-size3.3, eval=!is.null(params$exp_variable) & !no_mf, include=FALSE}
fig_width <- 8
fig_height <- 6
n_expv <- length(unique(plot_data[params$exp_variable]))
if (!plot_max && n_expv > 15 || plot_max && n_expv > 10) {
  fig_width <- 12
}
if (!plot_max && n_expv > 24 || plot_max && n_expv > 18) {
  fig_width <- 24
  fig_height <- 12
}
```

```{r plot3.3-caption, eval=!is.null(params$exp_variable) & !no_mf, include=FALSE}
# Define your caption components
caption <- paste0(
  "Mean Mutation Frequency (MF) Minimum",
  if (plot_max) " and Maximum",
  " (mutations/bp) per ", params$exp_variable, ". Lines are the mean ± S.E.M.",
  " Points are individual samples, coloured by ", params$exp_variable,
  if (plot_max) " with light shades being MFmin and dark shades being MFmax",
  "."
)
```

```{r plot-mean-mf, eval=!is.null(params$exp_variable) & !no_mf, fig.width=fig_width, fig.height=fig_height, fig.cap=caption}
plot3.3 <- plot_mean_mf(
  mf_data = plot_data,
  group_col = params$exp_variable,
  fill_col = params$exp_variable,
  mf_type = ifelse(plot_max, "both", "min"),
  plot_type = "line",
  plot_error_bars = TRUE,
  plot_indiv_vals = TRUE,
  group_order = ifelse(is.null(params$exp_variable),
                       "smart",
                       "arranged"),
  group_order_input = params$exp_variable,
  add_labels = "none",
  plot_legend = FALSE
) +
  ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 90,
                                            vjust = 0.5,
                                            hjust = 1))
plot3.3
```

# Modelling Mutation Frequency

## Generalized Linear Modelling {.tabset}

### Model Fit

```{r glm, eval=!is.null(params$exp_variable) & !no_mf, message=TRUE, fig.show='hide'}
model <- model_mf(mf_data = mf_data,
                  fixed_effects = params$exp_variable,
                  random_effects = NULL,
                  reference_level = params$reference_level,
                  muts = "sum_min",
                  total_count = "group_depth",
                  contrasts = params$contrasts,
                  cont_sep = params$cont_sep)
cat("Dispersion Parameter taken to be :", model$summary$dispersion)
if (!is.null(params$contrasts) && any(grepl("\\*", model$pairwise_comparisons$Significance))) {
  plot_signif <- TRUE
} else {
  plot_signif <- FALSE
}
# Save the model results
write_excel(data = model,
            output_path = params$outputdir,
            workbook_name = "Generalized Linear Model Results",
            model_results = TRUE)
```

### Residuals Histogram

```{r glm-hist-caption, eval=!is.null(params$exp_variable) & !no_mf, include=FALSE}
caption <- paste(
  "GLM residuals of MFmin modelled as an effect of ",
  params$exp_variable,
  ". x is pearson's residuals, y is frequency.",
  " Plotted to validate model assumptions. n = ",
  length(unique(mf_data$sample))
)
```

```{r glm-residuals-hist, eval=!is.null(params$exp_variable) & !no_mf, fig.cap=caption}
model_data <- model$model_data
hist_data <- hist(model_data$residuals, plot = FALSE)
ylim_max <- max(hist_data$counts) + 1
plot4.12 <- hist(model_data$residuals,
  main = "Pearson Residuals",
  col = "yellow",
  ylim = c(0, ylim_max))
```

### Residuals QQ Plot

```{r glm-qq-caption, eval=!is.null(params$exp_variable) & !no_mf, include=FALSE}
caption <- paste0(
  "GLM residuals of MFmin modelled as an effect of ",
  params$exp_variable, " expressed as a quantile-quantile plot.",
  " Y is the pearson's residuals of the model in ascending order",
  " x is the quantiles of standard normal distribution for n of ",
  length(unique(mf_data$sample)),
  ". Plotted to validate model assumptions."
)
```

```{r glm-residuals-qq, eval=!is.null(params$exp_variable) & !no_mf, fig.cap=caption}
qqplot4.13 <- stats::qqnorm(model_data$residuals, main = "QQ Plot of Residuals")
stats::qqline(model_data$residuals, col = "red")
```

### Model Estimates

```{r glm-point-estimates, eval=!is.null(params$exp_variable) & !no_mf}
DT::datatable(
  model$point_estimates,
  caption = "Estimated Mean Mutation Frequency",
  options = list(
    columnDefs = list(
      list(targets = 1:4,
           render = JS("function(data, type, row, meta) {
            return data.toExponential(2);
          }"))
    ),
    rowCallback = JS("function(row, data, dataIndex) {
        $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }")
  )
)
```

### Pairwise Comparisons

```{r glm-pairwise, eval=!is.null(params$contrasts) & !no_mf}
DT::datatable(
  model$pairwise_comparisons,
  caption = "Pairwise Comparisons",
  options = list(
    columnDefs = list(
      list(targets = c("p.value", "adj_p.value"),
        render = JS("function(data, type, row, meta) { return data.toExponential(2); }")
      )
    ),
    rowCallback = JS(
      "function(row, data, dataIndex) {
      $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }"
    )
  )
) %>%
  DT::formatRound(columns = c(1:3, 6:7), digits = 2)
```

### Plot

```{r plot4.16-caption, eval=!is.null(params$exp_variable) & !no_mf, include=FALSE}
caption <- paste0(
  "Mean Mutation Frequency Minimum (mutations/bp) per ",
  params$exp_variable,
  " estimated using a generalized linear model. Error bars are the S.E.M.",
  if (plot_signif) " Symbols indicate significance differences (p < 0.05)."
)
```

```{r plot-glm, eval=!is.null(params$exp_variable) & !no_mf, fig.cap=caption}
plot4.16 <- plot_model_mf(
  model = model,
  plot_type = "bar",
  plot_error_bars = TRUE,
  plot_signif = plot_signif,
  x_order = params$exp_variable_order
)
plot4.16
```

## Benchmark Dose Analysis

Benchmark dose (BMD) was calculated using PROAST.

BMD is the dose at which a `r params$bmr * 100`% in mutation frequency occurs above controls.

Returns the BMD, and the upper (BMDU) and lower (BMDL) 90% confidence intervals, calculated using model averaging.

```{r bmd-analysis, eval=params$bmd & !no_mf, results='hide', fig.show = 'hide'}
if (plot_max) {
  response_col <- c("mf_min", "mf_max")
} else {
  response_col <- "mf_min"
}
  bmd <- bmd_proast(mf_data = mf_data,
                    dose_col = params$exp_variable,
                    response_col = response_col,
                    bmr = params$bmr,
                    model_averaging = TRUE,
                    num_bootstraps = params$num_boots,
                    plot_results = TRUE,
                    output_path = NULL)
```

### BMD Results

```{r bmd-results, eval=params$bmd & !no_mf}
DT::datatable(bmd$summary) %>%
  DT::formatRound(columns = 3:8, digits = 2)
Results[["BMD Results"]] <- bmd$summary
```

### BMD Plots {.tabset}

```{r bmd-exphill-tab, eval=params$bmd & !no_mf, results='asis'}
cat("#### Exponential & Hill Models\n\n")
```

```{r bmd-exphill-cap, eval=params$bmd & !no_mf, include=FALSE}
caption1 <- paste(
  "The fitted curve of the selected Exponenetial (left) and Hill (right)",
  "models. Data is MFmin by", params$exp_variable, "following",
  "log-transformation. Individual data points are plotted using small",
  "triangles. The geometric mean at each", params$exp_variable, "is plotted",
  "as a large triangle. The estimated BMD is indicated by a dotted line."
)
caption2 <- paste(
  "The fitted curve of the selected Exponenetial (left) and Hill (right)",
  "models. Data is MFmax by", params$exp_variable, "following",
  "log-transformation. Individual data points are plotted using small",
  "triangles. The geometric mean at each", params$exp_variable, "is plotted",
  "as a large triangle. The estimated BMD is indicated by a dotted line."
)
```

```{r bmd-exphill-plot1, eval=params$bmd & !no_mf, fig.cap=caption1}
replayPlot(bmd$mf_min_Expon_HILL)
```

```{r bmd-exphill-plot2, eval=params$bmd & plot_max & !no_mf, fig.cap=caption2}
replayPlot(bmd$mf_max_Expon_HILL)
```

```{r bmd-invln-tab, eval=params$bmd & !no_mf, results='asis'}
cat("####  Inverse Exponential & Log-Normal Models\n\n")
```

```{r bmd-invln-cap, eval=params$bmd & !no_mf, include=FALSE}
caption1 <- paste(
  "The fitted curve of the selected Inverse-Exponenetial (left) and",
  "Log-Normal (right) models. Data is MFmin by", params$exp_variable,
  "following log-transformation. Individual data points are plotted using",
  "small triangles. The geometric mean at each", params$exp_variable,
  "is plotted as a large triangle. The estimated BMD is indicated by a dotted",
  "line."
)
caption2 <- paste(
  "The fitted curve of the selected Inverse-Exponenetial (left) and",
  "Log-Normal (right) models. Data is MFmax by", params$exp_variable,
  "following log-transformation. Individual data points are plotted using",
  "small triangles. The geometric mean at each", params$exp_variable,
  "is plotted as a large triangle. The estimated BMD is indicated by a dotted",
  "line."
)
```

```{r bmd-invln-plot1, eval=params$bmd & !no_mf, fig.cap=caption1}
replayPlot(bmd$mf_min_InvExp_LN)
```

```{r bmd-invln-plot2, eval=params$bmd & plot_max & !no_mf, fig.cap=caption2}
replayPlot(bmd$mf_max_InvExp_LN)
```

```{r bmd-boot-tab, eval=params$bmd & !no_mf, results='asis'}
cat("####  Bootstrap Curves\n\n")
```

```{r bmd-boot-cap, eval=params$bmd & !no_mf, include=FALSE}
caption1 <- paste(
  "The bootstrap curves based on model averaging. Data is MFmin by",
  params$exp_variable, "with log-transformation. The geometric mean at each",
  params$exp_variabe, "is plotted as a triangle. Number of bootstraps =",
  params$num_bootstraps
)
caption2 <- paste(
  "The bootstrap curves based on model averaging. Data is MFmin (left) and MFmax (right) by",
  params$exp_variable, "with log-transformation. The geometric mean at each",
  params$exp_variabe, "is plotted as a triangle. Number of bootstraps =",
  params$num_bootstraps
)
```

```{r bmd-boot-plot1, eval=params$bmd & !plot_max & !no_mf, fig.cap=caption1}
replayPlot(bmd$mf_min_bootstrap_curves)
```

```{r bmd-boot-plot2, eval=params$bmd & plot_max & !no_mf, fig.cap=caption2}
replayPlot(bmd$mf_max_bootstrap_curves)
```

#### Cleveland Plots

```{r bmd-cvd-cap, eval=params$bmd & !no_mf, include=FALSE}
caption1 <- paste(
  "MFmin. The BMD estimate for each model plotted as a red point alongside the 90%",
  "confidence intervals. The size of the BMD point represents the model weight",
  "assigned during model averaging based in AIC."
)
caption2 <- paste(
  "MFmax. The BMD estimate for each model plotted as a red point alongside the 90%",
  "confidence intervals. The size of the BMD point represents the model weight",
  "assigned during model averaging based on AIC."
)
```

```{r bmd-cvd-plot1, eval=params$bmd & !no_mf, fig.cap=caption1}
bmd$mf_min_cleveland
```

```{r bmd-cvd-plot2, eval=params$bmd&plot_max & !no_mf, fig.cap=caption2}
bmd$mf_max_cleveland
```

#### Confidence Interval Plot

```{r bmd-ci-cap, eval=params$bmd & !no_mf, include=FALSE}
caption <- paste(
  "Benchmark dose with 90% confidence intervals representing the dose at which a ",
  params$bmr * 100, " % increase in mutation frequency occurs from reference level.",
  " Calculated using PROAST software.",
  "Black points represent the BMD, red points the BMDL, and blue points, the BMDU"
)
```

```{r bmd-ci-plot, eval=params$bmd & !no_mf, fig.cap=caption, fig.width=8, fig.height=6}
bmd <- bmd$summary %>% dplyr::filter(Model == "Model averaging")
plot4.2 <- plot_ci(bmd)
plot4.2
```

# Mutation Spectra

## 6-Base Spectrum {.tabset}

### Mutation Spectra per Sample

```{r calculate-base6-mf}
mf6 <- calculate_mf(
  mutation_data = filtered_mutation_data,
  cols_to_group = "sample",
  subtype_resolution = "base_6",
  variant_types = c("-ambiguous", "-uncategorized"),
  calculate_depth = !no_depth,
  precalc_depth_data = params$precalc_depth_data_base6,
  d_sep = params$d_sep,
  correct_depth = correct_depth
)
# Add expv separately to prevent NANs in data
if (!is.null(params$exp_variable)) {
  sample_exp_map <- filtered_mutation_data %>%
    dplyr::distinct(sample, .data[[params$exp_variable]])
  mf6 <- dplyr::left_join(mf6, sample_exp_map, by = "sample")
}
if ("mf_min" %in% colnames(mf6)) {
  no_mf <- FALSE
} else {
  no_mf <- TRUE
}
```

```{r mf6-no-mf-msg, results = 'asis', eval = no_mf}
cat("No depth column in mutation data, nor was a precalculated depth file provided.",
    "Unable to calculate mutation frequency.",
    "Unable to normalized subtype proportions to depth")
```

```{r mf6-tab}
if (!no_mf) {
DT::datatable(
  mf6,
  caption = "6-base Mutation Frequnecy per Sample",
  options = list(
    columnDefs = list(
      list(targets = c("mf_min", "mf_max"),
           render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
    ),
    rowCallback = JS(
      "function(row, data, dataIndex) {
        $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }"
    )
  )
) %>%
  DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
} else {
  DT::datatable(
    mf6,
    caption = "6-base Mutation Sums and Proportions per Sample"
  )  %>%
  DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
}
Results[["6-base MF per Sample"]] <- mf6
```

### Hierarchical Clustering of Samples

```{r plot-spectra-clust-caption, include=FALSE}
caption <- paste0(
  "Mutation spectrum (minimum) per sample. Subtypes include single-nucleotide,",
  " variants at 6-base resolution, complex variants, deletions, insertions,",
  " multi-nucleotide variants (mnv) and structural variants (sv). Subtypes",
  " are represented by colour. Data is the proportion",
  if (!no_mf) " normalized to sequencing depth",
  ". Samples are clustered based on the Euclidean distance between their",
  " subtype proportions."
)
```

```{r plot-spectra-clustered, fig.cap=caption, fig.width=12, fig.height=8}
plot5.12 <- plot_spectra(
  mf_data = mf6,
  group_col = "sample",
  subtype_resolution = "base_6",
  response = "proportion",
  group_order = "clustered",
  rotate_xlabs = TRUE
)
plot5.12
```

### Mutation Spectra per `r params$exp_variable`

```{r base6-spectra-exp-variable, eval=!is.null(params$exp_variable)}

# Precalc_depth: Aggregate across the experimental variable

if (!is.null(params$precalc_depth_data_base6)) {
  depth_base6_expv <- read.delim(
    file.path(params$precalc_depth_data_base6),
    sep = params$d_sep, header = TRUE
  )
  # add on the experimental variable
  depth_base6_expv <- dplyr::left_join(depth_base6_expv, sample_exp_map, by = "sample")

  depth_base6_expv <- depth_base6_expv %>%
    dplyr::group_by(normalized_ref,
                    dplyr::across(dplyr::all_of(params$exp_variable))) %>%
    dplyr::summarize(subtype_depth = sum(subtype_depth),
                     group_depth = sum(group_depth))
} else {
  depth_base6_expv <- NULL
}
# Calculate subtype sums and proportions for the experimental variable
mf6_expv <- calculate_mf(
  mutation_data = filtered_mutation_data,
  cols_to_group = params$exp_variable,
  subtype_resolution = "base_6",
  variant_types = c("-ambiguous", "-uncategorized"),
  calculate_depth = !no_depth,
  precalc_depth_data = depth_base6_expv,
  d_sep = params$d_sep,
  correct_depth = correct_depth
)
# Calculate the mean subtype MF for the experimental variable
if (!no_mf) {
  mf6_expv_mean <- mf6 %>%
    dplyr::group_by(normalized_subtype,
                    dplyr::across(dplyr::all_of(params$exp_variable))) %>%
    dplyr::summarize(mean_mf_min = mean(mf_min),
                    se_mf_min = sd(mf_min) / dplyr::n(),
                    mean_mf_max = mean(mf_max),
                    se_mf_max = sd(mf_max) / dplyr::n())

  mf6_expv <- dplyr::left_join(mf6_expv, mf6_expv_mean,
                              by = c(params$exp_variable,
                                      "normalized_subtype")) %>%
    dplyr::select(-"mf_min", -"mf_max")

  DT::datatable(
    mf6_expv,
    caption = paste("6-base Mutation Frequency per", params$exp_variable),
    options = list(
      columnDefs = list(
        list(targets = c("mean_mf_min", "se_mf_min", "mean_mf_max", "se_mf_max"),
            render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
      ),
      rowCallback = JS(
        "function(row, data, dataIndex) {
          $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
        }"
      )
    )
  ) %>%
    DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
} else {
  DT::datatable(
    mf6_expv,
    caption = paste("6-base Mutation Sum and Proportions per", params$exp_variable)
  ) %>%
  DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
}
Results[["6-base MF per Exp-variable"]] <- mf6_expv
```

### 6-base Spectral Comparison

```{r spectra-comparison, eval=!is.null(params$exp_variable)&!is.null(params$contrasts)}
base6_comp <- spectra_comparison(
  mf_data = mf6_expv,
  exp_variable = params$exp_variable,
  contrasts = params$contrasts,
  cont_sep = params$cont_sep
)
DT::datatable(
  base6_comp,
  caption = "6-base spectral comparison",
  options = list(
    columnDefs = list(
      list(targets = c("p.value", "adj_p.value"),
           render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
    ),
    rowCallback = JS(
      "function(row, data, dataIndex) {
        $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }"
    )
  )
) %>%
  DT::formatRound(columns = "G2", digits = 2)

Results[["6-base Spectral Comparison"]] <- base6_comp
```

### Plot 6-base Mutation Spectra per `r params$exp_variable`

```{r plot-spectra-exp-caption, eval=!is.null(params$exp_variable), include=FALSE, fig.height = 8}
caption <- paste0(
  "Mutation spectrum (minimum) per ", params$exp_variable,
  ". Subtypes include single-nucleotide variants at 6-base resolution,",
  " complex variants, deletions, insertions, multi-nucleotide variants (mnv)",
  " and structural variants (sv). Subtypes are represented by colour.",
  " Data is the proportion",
  if (!no_mf) " normalized to sequencing depth",
  "."
)
```

```{r plot-spectra-expv, eval=!is.null(params$exp_variable), fig.cap=caption}
mf6_expv[[params$exp_variable]] <- factor(mf6_expv[[params$exp_variable]],
                                          levels = params$exp_variable_order)

plot5.15 <- plot_spectra(mf_data = mf6_expv,
  group_col = params$exp_variable,
  subtype_resolution = "base_6",
  response = "proportion",
  group_order = "arranged",
  group_order_input = params$exp_variable,
  y_lab = "Subtype Proportion"
)
plot5.15
```

## 96-Base Trinucleotide Spectrum {.tabset}

### Trinucleotide Spectra per Sample

```{r trinuc-per-sample, message=FALSE}
mf96 <- calculate_mf(
  mutation_data = filtered_mutation_data,
  cols_to_group = "sample",
  subtype_resolution = "base_96",
  variant_types = "snv",
  calculate_depth = !no_depth,
  precalc_depth_data = params$precalc_depth_data_base96,
  d_sep = params$d_sep,
  correct_depth = correct_depth
)
# Add expv separately to prevent NANs in data
if (!is.null(params$exp_variable))  {
  mf96 <- dplyr::left_join(mf96, sample_exp_map, by = "sample")
}

if ("mf_min"%in% colnames(mf96)) {
  no_mf <- FALSE
} else {
  no_mf <- TRUE
}
```

```{r mf96-no-mf-msg, results = 'asis', eval = no_mf}
cat("No depth column in mutation data, nor was a precalculated depth file provided.",
    "Unable to calculate mutation frequency.",
    "Unable to normalized subtype proportions to depth")
```

```{r mf96-tab}
if (!no_mf) {
DT::datatable(
  mf96,
  caption = "96-base Mutation Frequnecy per Sample",
  options = list(
    columnDefs = list(
      list(targets = c("mf_min", "mf_max"),
           render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
    ),
    rowCallback = JS(
      "function(row, data, dataIndex) {
        $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }"
    )
  )
) %>%
  DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
} else {
  DT::datatable(
  mf96,
  caption = "96-base Mutation Sum and Proportion per Sample"
) %>%
  DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
}
Results[["96-base MF per Sample"]] <- mf96
```

### Trinucleotide Spectra per `r params$exp_variable`

```{r trinuc-spectra-expc, eval=!is.null(params$exp_variable)}
# Precalc_depth: Aggregate across the experimental variable
if (!is.null(params$precalc_depth_data_base96)) {
  precalc_depth_data_base96 <- read.delim(
      file.path(params$precalc_depth_data_base96),
      sep = params$d_sep, header = TRUE
  )

  precalc_depth_data_base96 <- dplyr::left_join(precalc_depth_data_base96, sample_exp_map, by = "sample") %>%
    dplyr::group_by(normalized_context,
                    dplyr::across(dplyr::all_of(params$exp_variable))) %>%
    dplyr::summarize(subtype_depth = sum(subtype_depth),
                     group_depth = sum(group_depth))
} else {
  precalc_depth_data_base96 <- NULL
}
# Calculate subtype sums and proportions for the experimental variable
mf96_expv <- calculate_mf(
  mutation_data = filtered_mutation_data,
  cols_to_group = params$exp_variable,
  subtype_resolution = "base_96",
  variant_types = "snv",
  calculate_depth = !no_depth,
  precalc_depth_data = precalc_depth_data_base96,
  d_sep = params$d_sep,
  correct_depth = correct_depth
)
if (!no_mf) {
  # Calculate the mean subtype MF for the experimental variable
  mf96_expv_mean <- mf96 %>%
    dplyr::group_by(normalized_context_with_mutation,
                    dplyr::across(dplyr::all_of(params$exp_variable))) %>%
    dplyr::summarize(mean_mf_min = mean(mf_min),
                    se_mf_min = sd(mf_min) / dplyr::n(),
                    mean_mf_max = mean(mf_max),
                    se_mf_max = sd(mf_max) / dplyr::n())

  mf96_expv <- dplyr::left_join(mf96_expv, mf96_expv_mean,
                                by = c(params$exp_variable,
                                      "normalized_context_with_mutation")) %>%
    dplyr::select(-"mf_min", -"mf_max")

  DT::datatable(
    mf96_expv,
    caption = paste("96-base Mutation Frequency per", params$exp_variable),
    options = list(
      columnDefs = list(
        list(targets = c("mean_mf_min", "se_mf_min", "mean_mf_max", "se_mf_max"),
            render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
      ),
      rowCallback = JS(
        "function(row, data, dataIndex) {
          $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
        }"
      )
    )
  ) %>%
    DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
} else {
  DT::datatable(
    mf96_expv,
    caption = paste("96-base Mutation Sums and Proportions per", params$exp_variable)
  ) %>%
    DT::formatRound(columns = c("proportion_min", "proportion_max"), digits = 2)
}
Results[["96-base MF per Exp Variable"]] <- mf96_expv
```

## Trinucleotide Plots {.tabset}

```{r trinuc-plots-caption, include=FALSE}
caption <- paste0(
  "96-base trinucleotide spectra (minimum) per ",
  if (is.null(params$exp_variable)) "Sample" else params$exp_variable,
  ". Bars are the proportion of SNV subtypes within their trinucleotide context",
  if (!no_mf) " normalized to the sequencing depth. ",
  "Bars are coloured based on SNV subtype. Data labels indicate the total",
  " number of mutations for each SNV subtype"
)
```

```{r trinuc-plots, fig.show='hide'}
if (is.null(params$exp_variable)) {
  data <- mf96
  group_col <- "sample"
} else {
  data <- mf96_expv
  group_col <- params$exp_variable
}
plots <- plot_trinucleotide(
  mf_96 = data,
  response = "proportion",
  mf_type = "min",
  group_col = group_col,
  output_path = NULL
)
```

```{r display-plots, echo=FALSE, results = 'asis', fig.width=12, fig.height=6}
for (i in seq_along(plots)) {
  cat("### Plot ", names(plots)[i], "\n")
  print(plots[i])
  cat("\n\n")
  cat(caption, "\n\n")
}
```

## Signature Analysis

**Currently not implemented**

**Ran Signature Assignment: `r params$run_sigfitting`**
See the Sigprofiler Output folder in `r output_path`

```{r signature-analysis, eval=FALSE}
# Think of a way to get these results in the report....

signature_fitting(
  mutation_data = filtered_mutation_data,
  project_name = params$project_title,
  project_genome = params$genome,
  env_name = "MutSeqR",
  group = ifelse(is.null(params$exp_variable),
                 "sample", params$exp_variable),
  output_path = params$outputdir,
  python_version = params$python_version
)
```

# Regions Analysis {.tabset}

## Mutation Frequency per Genomic Target

```{r regions-calc-mf, eval = params$do_regions_analysis}
mf_rg <- calculate_mf(
  mutation_data = filtered_mutation_data,
  cols_to_group = c("sample", params$region_col),
  subtype_resolution = "none",
  variant_types = c("-ambiguous", "-uncategorized"),
  calculate_depth = !no_depth,
  precalc_depth_data = params$precalc_depth_data_rg,
  d_sep = params$d_sep,
  retain_metadata_cols = params$exp_variable,
  correct_depth = correct_depth
)
if ("mf_min" %in% colnames(mf_rg)) {
  no_mf <- FALSE
  DT::datatable(
    mf_rg,
    caption = "MF per sample for each genomic target",
    options = list(
      columnDefs = list(
        list(targets = c("mf_min", "mf_max"),
            render = JS("function(data, type, row, meta) { return data.toExponential(2); }"))
      ),
      rowCallback = JS(
        "function(row, data, dataIndex) {
          $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
        }"
      )
    )
  )
} else {
  no_mf <- TRUE
  DT::datatable(
    mf_rg,
    caption = "Mutation counts per sample for each genomic target"
  )
}
Results[["MF per Target Region"]] <- mf_rg
```

## Generalized Linear Mixed Model

```{r regions-glmm, eval=params$do_regions_analysis & !no_mf, message=TRUE, fig.show='hide'}
rg <- as.data.frame(load_regions_file(params$regions))
rg_levels <- unique(rg[params$region_col])

# Build a contrasts file: each comparison in contrasts is done for each target:
if (!is.null(params$contrasts)) {
  contrasts <- read.delim(params$contrasts,
                          sep = params$cont_sep,
                          header = FALSE)
  contrasts_rg <- data.frame(col1 = character(),
                             col2 = character(),
                             stringsAsFactors = FALSE)

    num_rg <- length(rg_levels)
    num_contrasts_rows <- nrow(contrasts)
    expanded_V1 <- rep(contrasts[,1], each = num_rg)
    expanded_V2 <- rep(contrasts[,2], each = num_rg)
    expanded_rg <- rep(rg_levels, times = num_contrasts_rows)
    contrasts_rg <- data.frame(
        col1 = paste(expanded_V1, expanded_rg, sep = ":"),
        col2 = paste(expanded_V2, expanded_rg, sep = ":")
    )
} else {
  contrasts_rg <- NULL
}
# Define parameters for model_mf
if (is.null(params$exp_variable)) {
  fixed_effects <- params$region_col
  reference_level <- rg_levels[1, 1]
} else {
  fixed_effects <- c(params$exp_variable, params$region_col)
  reference_level <- c(params$reference_level, rg_levels[1, 1])
}
model_rg <- model_mf(
  mf_data = mf_rg,
  fixed_effects = fixed_effects,
  random_effects = "sample",
  reference_level = reference_level,
  contrasts = contrasts_rg,
  control = lme4::glmerControl(optimizer = "bobyqa",
    optCtrl = list(maxfun = 2e5)
  )
)
# Save the results.
write_excel(
  data = model_rg,
  output_path = params$outputdir,
  workbook_name = "Regions Analysis GLMM",
  model_results = TRUE
)
```

## Residuals Histogram

```{r glmm-hist-caption, eval=params$do_regions_analysis & !no_mf, include=FALSE}
caption <- paste0(
  "GLMM residuals of MFmin modelled as an effect of ",
  params$region_col,
  if (!is.null(params$exp_variable)) " and", params$exp_variable,
  ". X is pearson's residuals, y is frequency.",
  " Plotted to validate model assumptions. n = ",
  nrow(mf_rg)
)
```

```{r glmm-residuals-hist, eval=params$do_regions_analysis & !no_mf, fig.cap=caption}
model_rg_data <- model_rg$model_data
hist_data <- hist(model_rg_data$residuals, plot = FALSE)
ylim_max <- max(hist_data$counts) + 1
plot6.3 <- hist(model_rg_data$residuals,
  main = "Pearson Residuals",
  col = "yellow",
  ylim = c(0, ylim_max)
)
```

## Residuals QQ Plot

```{r glmm-qq-caption, eval=params$do_regions_analysis & !no_mf, include=FALSE}
caption <- paste0(
  "GLMM residuals of MFmin modelled as an effect of ",
  params$region_col,
  if (!is.null(params$exp_variable)) " and ", params$exp_variable,
  ". Expressed as a quantile-quantile plot.",
  " Y is the pearson's residuals of the model in ascending order",
  " x is the quantiles of standard normal distribution for n of ",
  nrow(mf_rg),
  ". Plotted to validate model assumptions."
)
```

```{r glmm-residuals-qq, eval=params$do_regions_analysis & !no_mf, fig.cap=caption}
qqplot6.4 <- stats::qqnorm(
  model_rg_data$residuals,
  main = "QQ Plot of Residuals"
)
stats::qqline(model_rg_data$residuals, col = "red")
```

## Model Estimates

```{r glmm-point-estimates, eval=params$do_regions_analysis & !no_mf}
DT::datatable(
  model_rg$point_estimates,
  caption = "Estimated Mean Mutation Frequency",
  options = list(
    columnDefs = list(
      list(targets = 1:4,
           render = JS("function(data, type, row, meta) {
            return data.toExponential(2);
          }"))
    ),
    rowCallback = JS("function(row, data, dataIndex) {
        $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }")
  )
)
```

## Pairwise Comparisons

```{r glmm-pairwise, eval=!is.null(params$contrasts) && params$do_regions_analysis & !no_mf}
DT::datatable(
  model_rg$pairwise_comparisons,
  caption = "Pairwise Comparisons",
  options = list(
    columnDefs = list(
      list(targets = c("p.value", "adj_p.value"),
        render = JS("function(data, type, row, meta) { return data.toExponential(2); }")
      )
    ),
    rowCallback = JS(
      "function(row, data, dataIndex) {
      $('td:eq(2)', row).css('text-align', 'right'); // Align the content of column 2 to the right
      }"
    )
  )
) %>%
  DT::formatRound(columns = c(1:3, 6:7), digits = 2)
plot_signif <- TRUE
```

## Model Plot

```{r set-plot-size6.7, include=FALSE, eval=params$do_regions_analysis & !no_mf}
n_rg <- nrow(rg_levels)
fig_width <- 8
fig_height <- 6
```

```{r plot6.7-caption, eval=params$do_regions_analysis & !no_mf, include=FALSE}
caption <- paste0(
  "Mean Mutation Frequency Minimum (mutations/bp) per ",
  params$region_col,
  if (!is.null(params$exp_variable)) " and ", params$exp_variable,
  " estimated using a generalized linear mixed model.",
  " Error bars are the S.E.M.",
  if (plot_signif) " Symbols indicate significance differences (p < 0.05) between ", params$exp_variable, " levels for individual genomic regions."
)
```

```{r plot-glmm, eval=params$do_regions_analysis & !no_mf, fig.width=fig_width, fig.height=fig_height, fig.cap=caption}
# Define parameters for plot
if (!is.null(params$exp_variable)) {
  # rg ordered by expv level with highest avg mf
  top_exp_level <- model_rg$point_estimates %>%
    group_by(.data[[params$exp_variable]]) %>%
    summarize(mean_Estimate = mean(Estimate, na.rm = TRUE),
              .groups = "drop") %>%
    filter(mean_Estimate == max(mean_Estimate, na.rm = TRUE)) %>%
    pull(.data[[params$exp_variable]])
  rg_order <- model_rg$point_estimates %>%
    dplyr::filter(.data[[params$exp_variable]] == top_exp_level) %>%
    dplyr::arrange(Estimate) %>%
    dplyr::pull(params$region_col)
  fill_order <- params$exp_variable_order
  ref_effect <- params$exp_variable
  x_effect <- params$region_col
} else {
  rg_order <- model_rg$point_estimates %>%
    dplyr::arrange(Estimate) %>%
    dplyr::pull(params$region_col)
  fill_order <- NULL
  ref_effect <- NULL
  x_effect <- NULL
}

plot6.7 <- plot_model_mf(model = model_rg,
  x_effect = x_effect,
  ref_effect = ref_effect,
  plot_type = "bar",
  plot_error_bars = TRUE,
  x_order = rg_order,
  fill_order = fill_order,
  plot_signif = plot_signif
) +
  ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 90,
                                            vjust = 0.5,
                                            hjust = 1))

plot6.7
```

# Mutation Hotspots

```{r, recurrent-mutations}
recurring_mutations_all <- filtered_mutation_data %>%
  dplyr::filter(!variation_type %in% "no_variant",
                filter_mut == FALSE) %>%
  dplyr::group_by(contig, start, end, subtype) %>%
  dplyr::tally() %>%
  dplyr::arrange(-n) %>%
  dplyr::filter(n > 1)

no_recur <- nrow(recurring_mutations_all)
no_recur_sum <- sum(recurring_mutations_all$n)
```

Within the dataset, `r no_recur` mutations were found to occur in more than one sample.
Recurrent mutations between samples are observed at the same genomic position, with the same (normalized) subtype.
These mutations accounted for `r no_recur_sum` of total mutations (Minimum Mutation Counting Method).

## Lollipop Plots {.tabset}

```{r lolliplot-caption, eval=params$do_regions_analysis}
caption <- paste(
  "Recurrent mutations across genomic positions within each contig. Y is",
  "recurrence (n) a specific mutation (coloured by normalized subtype) is",
  "observed at each genomic position (X, start position). Only mutations with",
  "recurrence > 2 are displayed for clarity."
)
```

```{r lolliplot, eval=params$do_regions_analysis, fig.cap=caption, fig.width=12, fig.height=6, results='asis'}
plot <- plot_lollipop(mutation_data = filtered_mutation_data,
                      min_recurrence = 2,
                      group_col = params$region_col,
                      custom_palette = NULL)
plot_list <- names(plot)
for (p in plot_list) {
  cat("### ", p, "\n\n")
  print(plot[p])
  cat("\n")
}
```

```{r, recur-per-sample, eval=FALSE}
# Samples with +1 alt allele at a given position
recurring_mutations_sample <- filtered_mutation_data %>%
  dplyr::filter(!variation_type %in% "no_variant",
                filter_mut == FALSE) %>%
  dplyr::group_by(contig, start, end, sample) %>%
  dplyr::tally() %>%
  dplyr::arrange(-n) %>%
  dplyr::filter(n > 1)

recur_mut_sample_summary <- recurring_mutations_sample %>%
  dplyr::mutate(alt_depth = n, variation_type = "snv", filter_mut = FALSE) %>%
  calculate_mf(calculate_depth = FALSE) %>%
  dplyr::rename(
    multi_allelic_site_count = sum_min,
    total_recurring_mutations = sum_max
  )
total <- recur_mut_sample_summary %>%
  dplyr::summarise(
    sample = "total",
    multi_allelic_site_count = sum(multi_allelic_site_count),
    total_recurring_mutations = sum(total_recurring_mutations)
  )
recur_mut_sample_summary <- rbind(recur_mut_sample_summary, total)
DT::datatable(recur_mut_sample_summary,
  caption = "Number of recurrent mutations per sample.
  multi_allelic_site_count is the number of genomic positions (contig/start/end) where multiple distinct alternate alleles were observed within a given sample.
  total_recurring_mutations is the total number of recurrent mutations (Minimum Count Method) per sample.")

no_recur <- nrow(recurring_mutations_sample)
no_recur_sum <- sum(recurring_mutations_sample$n)

# For each sample, we identified genomic sites where multiple distinct alternate alleles were called at the same position.
# A total of r no_recur such positions was observed across the entire data set, accounting for r no_recur_sum of total mutations (Minimum Mutation Counting Method).


```

## Mutation Hotspots Within `r params$exp_variable`

```{r recur-per-expv, eval=!is.null(params$exp_variable)}
recurring_mutations_expv <- filtered_mutation_data %>%
  dplyr::filter(
    !variation_type %in% "no_variant",
    filter_mut == FALSE
  ) %>%
  dplyr::group_by(
    contig, start, end,
    .data[[params$exp_variable]],
    variation_type, # just to add it to the df
    subtype
  ) %>%
  dplyr::tally() %>%
  dplyr::arrange(-n) %>%
  dplyr::filter(n > 1)

recur_mut_expv_summary <- recurring_mutations_expv %>%
  dplyr::mutate(alt_depth = n, filter_mut = FALSE) %>%
  calculate_mf(
    cols_to_group = params$exp_variable,
    calculate_depth = FALSE
  ) %>%
  dplyr::rename(
    recurrent_mutation_events = sum_min,
    total_recurring_mutations = sum_max
  )
total <- recur_mut_expv_summary %>%
  dplyr::summarise(
    recurrent_mutation_events = sum(recurrent_mutation_events),
    total_recurring_mutations = sum(total_recurring_mutations)
  ) %>%
  mutate(!!params$exp_variable := "total")
recur_mut_expv_summary <- rbind(recur_mut_expv_summary, total)

caption <- paste(
  "Number of recurrent mutations per", params$exp_variable,
  "recurrent_mutation_events is the number of times a mutation of the same (normalized) subtype and genomic position was called within each ",
  params$exp_variable, ".",
  "total_recurring_mutations is the total number of mutations per",
  params$exp_variable, "that were recurrent."
)
DT::datatable(recur_mut_expv_summary, caption = caption)
no_recur <- nrow(recurring_mutations_expv)
no_recur_sum <- sum(recurring_mutations_expv$n)
```

## Bubble Plot: Multiplet Mutations

```{r bubble-cap, include=FALSE}
if (is.null(params$exp_variable)) {
  facet <- "sample"
} else {
  facet <- params$exp_variable
}
caption <- paste0(
  "Mutiple mutations per ", facet, ". Each circle represents a mutation,",
  "coloured by normalized mutation subtype The size of the circle is scaled",
  "by the mutation's alternative depth."
)
```

```{r plot-bubbles, fig.cap=caption, fig.width=12, fig.height=6}
plot7.2 <- plot_bubbles(
  mutation_data = filtered_mutation_data,
  size_by = "alt_depth",
  facet_col = facet
)
plot7.2
```

```{r cpg-mutations, eval=FALSE}
# CpG Mutations
# For each subtype, # of the subtype at CpG sites divided by the total number of that subtype.
# Annotate Mutation Data for CpG sites:
mut_data_cpgs <- annotate_cpg_sites(mutation_data = filtered_mutation_data) %>%
  dplyr::filter(CpG_site == TRUE)
# For counting, make rows distinct in case of duplicated positions.
mut_data_cpgs_distinct <- mut_data_cpgs %>%
  dplyr::distinct(sample, contig, start, .keep_all = TRUE)
# Number of CpG dinucleotides per sample:
n_cpgs_per_sample <- table(
  factor(mut_data_cpgs_distinct$sample,
         levels = unique(filtered_mutation_data$sample))
) / 2
# Dinucleotides = sites ?  / 2
# Explain how this is working (calculating the sites)

cpg_summary_global <- calculate_mf(
  mutation_data = mut_data_cpgs,
  cols_to_group = "sample",
  calculate_depth = FALSE,
  retain_metadata_cols = params$exp_variable
)
cpg_summary_global <- merge(
  cpg_summary_global,
  as.data.frame(n_cpgs_per_sample),
  by.x = "sample",
  by.y = "Var1",
  all.x = TRUE
) %>%
  dplyr::rename(n_CpG_sites = Freq, cpg_mut = sum_min) %>%
  dplyr::select(-sum_max) %>%
  dplyr::mutate(prct_mutated_CpG_sites = cpg_mut / n_CpG_sites * 100)
cpg_summary_global <- dplyr::left_join(mf_data, cpg_summary_global) %>%
  dplyr::select(-sum_max, -mf_min, -mf_max, -group_depth) %>%
  dplyr::rename(total_mut = sum_min) %>%
  dplyr::mutate(pct_muts_at_CpG = 100 * cpg_mut / total_mut)

# Above we are calculating the Percent of CpG sites that were mutated within a sample
# And the Percent of Mutations in the samples that are at CpG sites.
# For the former, we have summed the number of CpG site mutations irrespective
# of which dinucleotide it came from. So maybe we want a unique count of
# how many of the unique CpG sites were mutated?

# Create a GRanges object, containing all mutation data, but a column added
# to indicate whether the 'context' column contains a CpG site (i.e., is the
# mutation at a CpG site or not)
mutation_data_cpgs <- annotate_cpg_sites(filtered_mutation_data)
cpg_mutations <- as.data.frame(mutation_data_cpgs) %>%
dplyr::filter(
  CpG_site == TRUE,
  filter_mut == FALSE)
cpg_sites <- get_cpg_regions(my_regions)

# Testing general function for this application...
cpg_summary_subtype <- calculate_mf(mutation_data = cpg_mutations,
                                    subtype_resolution = "base_6",
                                    cols_to_group = "sample",
                                    calculate_depth = FALSE)

# Mutations at CpG sites...
cpg_summary_subtype <- as.data.frame(cpg_mutations) %>%
  dplyr::add_count(dose, alt_depth, name = "total_muts_per_group") %>%
  dplyr::ungroup() %>%
  dplyr::group_by(subtype, dose, total_muts_per_group) %>%
  dplyr::tally() %>%
  dplyr::mutate(numeric_pct_cpg = 100 * dplyr::n() / (nrow(as.data.frame(cpg_sites)) * n_samples)) %>%
  dplyr::mutate(proportional_pct_cpg = 100*(100*dplyr::n()/total_muts_per_group)/(nrow(as.data.frame(cpg_sites))*n_samples))

# # Mutations at CpG sites, broken down by trinucleotide mutation context
cpg_summary_context <- as.data.frame(cpg_mutations) %>%
  group_by(!!ensym(group_var)) %>%
  add_count(alt_depth, name = "total_muts_per_group") %>%
  ungroup() %>%
  group_by(context_with_mutation, !!ensym(params$group_var), total_muts_per_group) %>%
  tally() %>%
  mutate(numeric_pct_cpg = 100*n/(nrow(as.data.frame(cpg_sites))*n_samples)) %>%
  mutate(proportional_pct_cpg = 100*(100*n/total_muts_per_group)/(nrow(as.data.frame(cpg_sites))*n_samples))

# cpg_base_12_pct <- as.data.frame(mutation_data_cpgs) %>%
#   group_by(CpG_site, subtype, !!ensym(params$group_var)) %>%
#   tally() %>%
#   ungroup() %>%
#   group_by(subtype, !!ensym(params$group_var)) %>%
#   mutate(total_mutations = sum(n)) %>%
#   ungroup() %>%
#   mutate(percent = 100*n/total_mutations) %>%
#   filter(CpG_site == TRUE) %>%
#   dplyr::select(-1)
#
# cpg_base_6_pct <- as.data.frame(mutation_data_cpgs) %>%
#   group_by(CpG_site, normalized_subtype, !!ensym(params$group_var)) %>%
#   tally() %>%
#   ungroup() %>%
#   group_by(normalized_subtype, !!ensym(params$group_var)) %>%
#   mutate(total_mutations = sum(n)) %>%
#   ungroup() %>%
#   mutate(percent = 100*n/total_mutations) %>%
#   filter(CpG_site == TRUE) %>%
#   dplyr::select(-1)
#
# cpg_base_96_pct <- as.data.frame(mutation_data_cpgs) %>%
#   group_by(CpG_site, context_with_mutation, !!ensym(params$group_var)) %>%
#   tally() %>%
#   ungroup() %>%
#   group_by(context_with_mutation, !!ensym(params$group_var)) %>%
#   mutate(total_mutations = sum(n)) %>%
#   ungroup() %>%
#   mutate(percent = 100*n/total_mutations) %>%
#   filter(CpG_site == TRUE) %>%
#   dplyr::select(-1)
#
# cpg_tables <- list(CpGs_base_12_percentage = cpg_base_12_pct,
#                    CpGs_base_6_percentage = cpg_base_6_pct,
#                    #CpGs_trinucleotide_percentage = cpg_base_96_pct, # Data too sparse.
#                    CpGs_by_subtype = cpg_summary_subtype,
#                    CpGs_by_trinucleotide = cpg_summary_context)
#
# write_excel_from_list(list_of_tables = cpg_tables,
#                       output_path = output_dir,
#                       workbook_name = "cpg_tables")

```

```{r output-Results}
write_excel(
  data = Results,
  output_path = params$outputdir,
  workbook_name = "MutSeqR Summary Report Results"
)
```

<div class="tocify-extend-page" data-unique="tocify-extend-page" style="height: 0;"></div>
