knitr::opts_chunk$set(
 echo = TRUE,
 message = FALSE,
 warning = FALSE
)

Module 3: Reporting With R

Now that we can acquire and process data, let’s learn how to create reports!

This module covers: - Conducting basic statistical tests on your data including t-tests, chi-square tests, and Pearson correlations - Creating publication-quality figures using the ggplot2 library - Using templates to create reports in your organization’s style

Basic Statistical Tests

T-Test Example

A t-test helps us determine if there are significant differences between groups or paired measurements. Here are three common ways to run t-tests in R:

# Perform paired t-test (same students, different subjects)
t.test(synthetic_data$math_score, synthetic_data$english_score, paired = TRUE)

    Paired t-test

data:  synthetic_data$math_score and synthetic_data$english_score
t = -0.32909, df = 49, p-value = 0.7435
alternative hypothesis: true mean difference is not equal to 0
95 percent confidence interval:
 -6.395881  4.595881
sample estimates:
mean difference 
           -0.9 
# Same test, but using the data argument for readability:
t.test(math_score, english_score, data = synthetic_data, paired = TRUE)

    Paired t-test

data:  math_score and english_score
t = -0.32909, df = 49, p-value = 0.7435
alternative hypothesis: true mean difference is not equal to 0
95 percent confidence interval:
 -6.395881  4.595881
sample estimates:
mean difference 
           -0.9 
# Independent samples t-test (comparing two different groups):
t.test(math_score ~ secondary, data = synthetic_data)

    Welch Two Sample t-test

data:  math_score by secondary
t = -1.2987, df = 33.591, p-value = 0.2029
alternative hypothesis: true difference in means between group FALSE and group TRUE is not equal to 0
95 percent confidence interval:
 -10.592478   2.335125
sample estimates:
mean in group FALSE  mean in group TRUE 
           71.81250            75.94118 

Correlation Example

Correlation measures how strongly two variables are related to each other. Here we’ll create some example data and test the correlation:

# Create two related variables
x <- rnorm(50, mean = 70, sd = 10)  # Test 1 scores
y <- x + rnorm(50, mean = 5, sd = 5) # Test 2 scores (related to Test 1)

# Calculate correlation and test statistical significance
cor.test(x, y)

    Pearson's product-moment correlation

data:  x and y
t = 13.51, df = 48, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8127800 0.9362718
sample estimates:
      cor 
0.8898191 

Chi-Square Example

Chi-square tests help us understand if there’s a relationship between categorical variables:

# Create gender and program variables for our synthetic data
synthetic_data$gender <- sample(c("Male", "Female"), nrow(synthetic_data), replace = TRUE)
synthetic_data$program <- sample(c("Academic", "Applied"), nrow(synthetic_data), replace = TRUE)

# Create a contingency table (counts of each combination
program_gender <- table(synthetic_data$program, synthetic_data$gender)

# Perform chi-square test of independence
chisq.test(program_gender)

    Pearson's Chi-squared test with Yates' continuity correction

data:  program_gender
X-squared = 0, df = 1, p-value = 1
# View the counts in the contingency table
program_gender
          
           Female Male
  Academic     15   13
  Applied      12   10

Creating Publication-Quality Figures

One of R’s greatest strengths is its ability to create professional, publication-quality figures. We’ll use the ggplot2 package (part of tidyverse) along with a custom theme package that ensures our plots match our organization’s visual identity.

Setting Up Our Plotting Environment

First, we need to install and load a custom package that provides our organization’s visual themes. This only needs to be done once on your computer:

# Install the package manager if you haven't already installed it
# install.packages("devtools")

# Install the TCDSB theme package
devtools::install_github("grousell/tcdsb")

Now, let’s load our required packages:

library(tidyverse)  # For ggplot2 and data manipulation
library(tcdsb)      # For TCDSB themes and colors

Creating Basic Plots

Let’s start with a simple bar plot using our synthetic data. In ggplot2, we build plots layer by layer, like stacking blocks:

# First, boost academic scores to make the synthetic data more interesting
synthetic_data <- synthetic_data |>
  mutate(math_score = case_when(
    program == "Academic" ~ math_score + 15,
    TRUE ~ math_score
  ))

# Create a basic plot showing average math scores by program
synthetic_data |>
  group_by(program) |>                     # Group data by program type
  summarise(avg_score = mean(math_score)) |> # Calculate mean for each group
  ggplot(aes(x = program, y = avg_score)) + # Set up the basic plot structure
  geom_col() +                             # Add bars to the plot
  labs(title = "Average Math Scores by Program",  # Add labels
       x = "Program",
       y = "Average Score")

Let’s break down what each part does:

  1. ggplot(aes(x = program, y = avg_score)) - Sets up the plot structure, specifying which variables go on which axes
  2. geom_col() - Adds vertical bars (columns) to our plot
  3. labs() - Adds title and axis labels to our plot

Applying TCDSB Theme and Colors

The TCDSB package provides colors and fonts that match our organization’s style guide:

# Load TCDSB colors and fonts
tcdsb_colours_fonts()

Now we can create the same plot with our organization’s theme:

synthetic_data |>
  group_by(program) |>
  summarise(avg_score = mean(math_score)) |>
  ggplot(aes(x = program, y = avg_score)) +
  geom_col() +
  labs(title = "Average Math Scores by Program",
       x = "Program",
       y = "Average Score") +
  tcdsb::tcdsb_ggplot_theme()  # Add TCDSB theme

We can also use our organization’s official colors:

synthetic_data |>
  group_by(program) |>
  summarise(avg_score = mean(math_score)) |>
  ggplot(aes(x = program, y = avg_score)) +
  geom_col(fill = tcdsb_board_color) +  # Use TCDSB colors
  labs(title = "Average Math Scores by Program",
       x = "Program",
       y = "Average Score") +
  tcdsb::tcdsb_ggplot_theme()

Creating Other Types of Plots

Let’s create a scatter plot to show the relationship between math and English scores. This is useful for visualizing correlations:

synthetic_data |>
  ggplot(aes(x = math_score, y = english_score)) +
  geom_point() +                          # Add points
  geom_smooth(method = "lm", se = FALSE) + # Add trend line
  labs(title = "Math vs English Scores",
       x = "Math Score",
       y = "English Score") +
  tcdsb::tcdsb_ggplot_theme()

And a box plot to show score distributions across programs:

synthetic_data |>
  ggplot(aes(x = program, y = math_score)) +
  geom_boxplot(fill = tcdsb_board_color) +
  labs(title = "Math Score Distribution by Program",
       x = "Program",
       y = "Math Score") +
  tcdsb::tcdsb_ggplot_theme()

These plots can be saved to files using the ggsave() function. The width and height are in inches:

# Save the last plot created
ggsave("math_scores_boxplot.png", width = 8, height = 6)

LS0tCnRpdGxlOiAnTW9kdWxlIDM6IFJlcG9ydGluZyBXaXRoIFInCmF1dGhvcjogIkphc29uIExvY2tsaW4iCmRhdGU6ICJOb3ZlbWJlciAxMiwgMjAyNCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IHVuaXRlZAogICAgaGlnaGxpZ2h0OiB0YW5nbwplZGl0b3Jfb3B0aW9uczoKICBtYXJrZG93bjoKICAgIHdyYXA6IDcyCi0tLQoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKIGVjaG8gPSBUUlVFLAogbWVzc2FnZSA9IEZBTFNFLAogd2FybmluZyA9IEZBTFNFCikKYGBgCgojIE1vZHVsZSAzOiBSZXBvcnRpbmcgV2l0aCBSCgpOb3cgdGhhdCB3ZSBjYW4gYWNxdWlyZSBhbmQgcHJvY2VzcyBkYXRhLCBsZXQncyBsZWFybiBob3cgdG8gY3JlYXRlIHJlcG9ydHMhCgpUaGlzIG1vZHVsZSBjb3ZlcnM6CiAgICAtIENvbmR1Y3RpbmcgYmFzaWMgc3RhdGlzdGljYWwgdGVzdHMgb24geW91ciBkYXRhIGluY2x1ZGluZyB0LXRlc3RzLCBjaGktc3F1YXJlIHRlc3RzLCBhbmQgUGVhcnNvbiBjb3JyZWxhdGlvbnMKICAgIC0gQ3JlYXRpbmcgcHVibGljYXRpb24tcXVhbGl0eSBmaWd1cmVzIHVzaW5nIHRoZSBgZ2dwbG90MmAgbGlicmFyeQogICAgLSBVc2luZyB0ZW1wbGF0ZXMgdG8gY3JlYXRlIHJlcG9ydHMgaW4geW91ciBvcmdhbml6YXRpb24ncyBzdHlsZQoKCiMjIEJhc2ljIFN0YXRpc3RpY2FsIFRlc3RzCgojIyMgVC1UZXN0IEV4YW1wbGUKQSB0LXRlc3QgaGVscHMgdXMgZGV0ZXJtaW5lIGlmIHRoZXJlIGFyZSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwcyBvciBwYWlyZWQgbWVhc3VyZW1lbnRzLiBIZXJlIGFyZSB0aHJlZSBjb21tb24gd2F5cyB0byBydW4gdC10ZXN0cyBpbiBSOgoKYGBge3J9CiMgUGVyZm9ybSBwYWlyZWQgdC10ZXN0IChzYW1lIHN0dWRlbnRzLCBkaWZmZXJlbnQgc3ViamVjdHMpCnQudGVzdChzeW50aGV0aWNfZGF0YSRtYXRoX3Njb3JlLCBzeW50aGV0aWNfZGF0YSRlbmdsaXNoX3Njb3JlLCBwYWlyZWQgPSBUUlVFKQoKIyBTYW1lIHRlc3QsIGJ1dCB1c2luZyB0aGUgZGF0YSBhcmd1bWVudCBmb3IgcmVhZGFiaWxpdHk6CnQudGVzdChtYXRoX3Njb3JlLCBlbmdsaXNoX3Njb3JlLCBkYXRhID0gc3ludGhldGljX2RhdGEsIHBhaXJlZCA9IFRSVUUpCgojIEluZGVwZW5kZW50IHNhbXBsZXMgdC10ZXN0IChjb21wYXJpbmcgdHdvIGRpZmZlcmVudCBncm91cHMpOgp0LnRlc3QobWF0aF9zY29yZSB+IHNlY29uZGFyeSwgZGF0YSA9IHN5bnRoZXRpY19kYXRhKQpgYGAKCiMjIyBDb3JyZWxhdGlvbiBFeGFtcGxlCkNvcnJlbGF0aW9uIG1lYXN1cmVzIGhvdyBzdHJvbmdseSB0d28gdmFyaWFibGVzIGFyZSByZWxhdGVkIHRvIGVhY2ggb3RoZXIuIEhlcmUgd2UnbGwgY3JlYXRlIHNvbWUgZXhhbXBsZSBkYXRhIGFuZCB0ZXN0IHRoZSBjb3JyZWxhdGlvbjoKCmBgYHtyfQojIENyZWF0ZSB0d28gcmVsYXRlZCB2YXJpYWJsZXMKeCA8LSBybm9ybSg1MCwgbWVhbiA9IDcwLCBzZCA9IDEwKSAgIyBUZXN0IDEgc2NvcmVzCnkgPC0geCArIHJub3JtKDUwLCBtZWFuID0gNSwgc2QgPSA1KSAjIFRlc3QgMiBzY29yZXMgKHJlbGF0ZWQgdG8gVGVzdCAxKQoKIyBDYWxjdWxhdGUgY29ycmVsYXRpb24gYW5kIHRlc3Qgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlCmNvci50ZXN0KHgsIHkpCmBgYAoKIyMjIENoaS1TcXVhcmUgRXhhbXBsZQpDaGktc3F1YXJlIHRlc3RzIGhlbHAgdXMgdW5kZXJzdGFuZCBpZiB0aGVyZSdzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gY2F0ZWdvcmljYWwgdmFyaWFibGVzOgoKYGBge3J9CiMgQ3JlYXRlIGdlbmRlciBhbmQgcHJvZ3JhbSB2YXJpYWJsZXMgZm9yIG91ciBzeW50aGV0aWMgZGF0YQpzeW50aGV0aWNfZGF0YSRnZW5kZXIgPC0gc2FtcGxlKGMoIk1hbGUiLCAiRmVtYWxlIiksIG5yb3coc3ludGhldGljX2RhdGEpLCByZXBsYWNlID0gVFJVRSkKc3ludGhldGljX2RhdGEkcHJvZ3JhbSA8LSBzYW1wbGUoYygiQWNhZGVtaWMiLCAiQXBwbGllZCIpLCBucm93KHN5bnRoZXRpY19kYXRhKSwgcmVwbGFjZSA9IFRSVUUpCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlIChjb3VudHMgb2YgZWFjaCBjb21iaW5hdGlvbgpwcm9ncmFtX2dlbmRlciA8LSB0YWJsZShzeW50aGV0aWNfZGF0YSRwcm9ncmFtLCBzeW50aGV0aWNfZGF0YSRnZW5kZXIpCgojIFBlcmZvcm0gY2hpLXNxdWFyZSB0ZXN0IG9mIGluZGVwZW5kZW5jZQpjaGlzcS50ZXN0KHByb2dyYW1fZ2VuZGVyKQoKIyBWaWV3IHRoZSBjb3VudHMgaW4gdGhlIGNvbnRpbmdlbmN5IHRhYmxlCnByb2dyYW1fZ2VuZGVyCmBgYAoKIyMgQ3JlYXRpbmcgUHVibGljYXRpb24tUXVhbGl0eSBGaWd1cmVzCgpPbmUgb2YgUidzIGdyZWF0ZXN0IHN0cmVuZ3RocyBpcyBpdHMgYWJpbGl0eSB0byBjcmVhdGUgcHJvZmVzc2lvbmFsLCBwdWJsaWNhdGlvbi1xdWFsaXR5IGZpZ3VyZXMuIFdlJ2xsIHVzZSB0aGUgYGdncGxvdDJgIHBhY2thZ2UgKHBhcnQgb2YgdGlkeXZlcnNlKSBhbG9uZyB3aXRoIGEgY3VzdG9tIHRoZW1lIHBhY2thZ2UgdGhhdCBlbnN1cmVzIG91ciBwbG90cyBtYXRjaCBvdXIgb3JnYW5pemF0aW9uJ3MgdmlzdWFsIGlkZW50aXR5LgoKIyMjIFNldHRpbmcgVXAgT3VyIFBsb3R0aW5nIEVudmlyb25tZW50CgpGaXJzdCwgd2UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIGEgY3VzdG9tIHBhY2thZ2UgdGhhdCBwcm92aWRlcyBvdXIgb3JnYW5pemF0aW9uJ3MgdmlzdWFsIHRoZW1lcy4gVGhpcyBvbmx5IG5lZWRzIHRvIGJlIGRvbmUgb25jZSBvbiB5b3VyIGNvbXB1dGVyOgoKYGBge3IgZXZhbD1GQUxTRX0KIyBJbnN0YWxsIHRoZSBwYWNrYWdlIG1hbmFnZXIgaWYgeW91IGhhdmVuJ3QgYWxyZWFkeSBpbnN0YWxsZWQgaXQKIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCgojIEluc3RhbGwgdGhlIFRDRFNCIHRoZW1lIHBhY2thZ2UKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJncm91c2VsbC90Y2RzYiIpCmBgYAoKTm93LCBsZXQncyBsb2FkIG91ciByZXF1aXJlZCBwYWNrYWdlczoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICMgRm9yIGdncGxvdDIgYW5kIGRhdGEgbWFuaXB1bGF0aW9uCmxpYnJhcnkodGNkc2IpICAgICAgIyBGb3IgVENEU0IgdGhlbWVzIGFuZCBjb2xvcnMKYGBgCgojIyMgQ3JlYXRpbmcgQmFzaWMgUGxvdHMKCkxldCdzIHN0YXJ0IHdpdGggYSBzaW1wbGUgYmFyIHBsb3QgdXNpbmcgb3VyIHN5bnRoZXRpYyBkYXRhLiBJbiBnZ3Bsb3QyLCB3ZSBidWlsZCBwbG90cyBsYXllciBieSBsYXllciwgbGlrZSBzdGFja2luZyBibG9ja3M6CgpgYGB7cn0KIyBGaXJzdCwgYm9vc3QgYWNhZGVtaWMgc2NvcmVzIHRvIG1ha2UgdGhlIHN5bnRoZXRpYyBkYXRhIG1vcmUgaW50ZXJlc3RpbmcKc3ludGhldGljX2RhdGEgPC0gc3ludGhldGljX2RhdGEgfD4KICBtdXRhdGUobWF0aF9zY29yZSA9IGNhc2Vfd2hlbigKICAgIHByb2dyYW0gPT0gIkFjYWRlbWljIiB+IG1hdGhfc2NvcmUgKyAxNSwKICAgIFRSVUUgfiBtYXRoX3Njb3JlCiAgKSkKCiMgQ3JlYXRlIGEgYmFzaWMgcGxvdCBzaG93aW5nIGF2ZXJhZ2UgbWF0aCBzY29yZXMgYnkgcHJvZ3JhbQpzeW50aGV0aWNfZGF0YSB8PgogIGdyb3VwX2J5KHByb2dyYW0pIHw+ICAgICAgICAgICAgICAgICAgICAgIyBHcm91cCBkYXRhIGJ5IHByb2dyYW0gdHlwZQogIHN1bW1hcmlzZShhdmdfc2NvcmUgPSBtZWFuKG1hdGhfc2NvcmUpKSB8PiAjIENhbGN1bGF0ZSBtZWFuIGZvciBlYWNoIGdyb3VwCiAgZ2dwbG90KGFlcyh4ID0gcHJvZ3JhbSwgeSA9IGF2Z19zY29yZSkpICsgIyBTZXQgdXAgdGhlIGJhc2ljIHBsb3Qgc3RydWN0dXJlCiAgZ2VvbV9jb2woKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEFkZCBiYXJzIHRvIHRoZSBwbG90CiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIE1hdGggU2NvcmVzIGJ5IFByb2dyYW0iLCAgIyBBZGQgbGFiZWxzCiAgICAgICB4ID0gIlByb2dyYW0iLAogICAgICAgeSA9ICJBdmVyYWdlIFNjb3JlIikKYGBgCgpMZXQncyBicmVhayBkb3duIHdoYXQgZWFjaCBwYXJ0IGRvZXM6CgoxLiBgZ2dwbG90KGFlcyh4ID0gcHJvZ3JhbSwgeSA9IGF2Z19zY29yZSkpYCAtIFNldHMgdXAgdGhlIHBsb3Qgc3RydWN0dXJlLCBzcGVjaWZ5aW5nIHdoaWNoIHZhcmlhYmxlcyBnbyBvbiB3aGljaCBheGVzCjIuIGBnZW9tX2NvbCgpYCAtIEFkZHMgdmVydGljYWwgYmFycyAoY29sdW1ucykgdG8gb3VyIHBsb3QKMy4gYGxhYnMoKWAgLSBBZGRzIHRpdGxlIGFuZCBheGlzIGxhYmVscyB0byBvdXIgcGxvdAoKIyMjIEFwcGx5aW5nIFRDRFNCIFRoZW1lIGFuZCBDb2xvcnMKClRoZSBUQ0RTQiBwYWNrYWdlIHByb3ZpZGVzIGNvbG9ycyBhbmQgZm9udHMgdGhhdCBtYXRjaCBvdXIgb3JnYW5pemF0aW9uJ3Mgc3R5bGUgZ3VpZGU6CgpgYGB7cn0KIyBMb2FkIFRDRFNCIGNvbG9ycyBhbmQgZm9udHMKdGNkc2JfY29sb3Vyc19mb250cygpCmBgYAoKIVtdKGltYWdlcy90Y2RzYl9jb2xvdXJfZm9udF9waWMucG5nKQoKTm93IHdlIGNhbiBjcmVhdGUgdGhlIHNhbWUgcGxvdCB3aXRoIG91ciBvcmdhbml6YXRpb24ncyB0aGVtZToKCmBgYHtyfQpzeW50aGV0aWNfZGF0YSB8PgogIGdyb3VwX2J5KHByb2dyYW0pIHw+CiAgc3VtbWFyaXNlKGF2Z19zY29yZSA9IG1lYW4obWF0aF9zY29yZSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gcHJvZ3JhbSwgeSA9IGF2Z19zY29yZSkpICsKICBnZW9tX2NvbCgpICsKICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgTWF0aCBTY29yZXMgYnkgUHJvZ3JhbSIsCiAgICAgICB4ID0gIlByb2dyYW0iLAogICAgICAgeSA9ICJBdmVyYWdlIFNjb3JlIikgKwogIHRjZHNiOjp0Y2RzYl9nZ3Bsb3RfdGhlbWUoKSAgIyBBZGQgVENEU0IgdGhlbWUKYGBgCgpXZSBjYW4gYWxzbyB1c2Ugb3VyIG9yZ2FuaXphdGlvbidzIG9mZmljaWFsIGNvbG9yczoKCmBgYHtyfQpzeW50aGV0aWNfZGF0YSB8PgogIGdyb3VwX2J5KHByb2dyYW0pIHw+CiAgc3VtbWFyaXNlKGF2Z19zY29yZSA9IG1lYW4obWF0aF9zY29yZSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gcHJvZ3JhbSwgeSA9IGF2Z19zY29yZSkpICsKICBnZW9tX2NvbChmaWxsID0gdGNkc2JfYm9hcmRfY29sb3IpICsgICMgVXNlIFRDRFNCIGNvbG9ycwogIGxhYnModGl0bGUgPSAiQXZlcmFnZSBNYXRoIFNjb3JlcyBieSBQcm9ncmFtIiwKICAgICAgIHggPSAiUHJvZ3JhbSIsCiAgICAgICB5ID0gIkF2ZXJhZ2UgU2NvcmUiKSArCiAgdGNkc2I6OnRjZHNiX2dncGxvdF90aGVtZSgpCmBgYAoKIyMjIENyZWF0aW5nIE90aGVyIFR5cGVzIG9mIFBsb3RzCgpMZXQncyBjcmVhdGUgYSBzY2F0dGVyIHBsb3QgdG8gc2hvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbWF0aCBhbmQgRW5nbGlzaCBzY29yZXMuIFRoaXMgaXMgdXNlZnVsIGZvciB2aXN1YWxpemluZyBjb3JyZWxhdGlvbnM6CgpgYGB7cn0Kc3ludGhldGljX2RhdGEgfD4KICBnZ3Bsb3QoYWVzKHggPSBtYXRoX3Njb3JlLCB5ID0gZW5nbGlzaF9zY29yZSkpICsKICBnZW9tX3BvaW50KCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBZGQgcG9pbnRzCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKyAjIEFkZCB0cmVuZCBsaW5lCiAgbGFicyh0aXRsZSA9ICJNYXRoIHZzIEVuZ2xpc2ggU2NvcmVzIiwKICAgICAgIHggPSAiTWF0aCBTY29yZSIsCiAgICAgICB5ID0gIkVuZ2xpc2ggU2NvcmUiKSArCiAgdGNkc2I6OnRjZHNiX2dncGxvdF90aGVtZSgpCmBgYAoKQW5kIGEgYm94IHBsb3QgdG8gc2hvdyBzY29yZSBkaXN0cmlidXRpb25zIGFjcm9zcyBwcm9ncmFtczoKCmBgYHtyfQpzeW50aGV0aWNfZGF0YSB8PgogIGdncGxvdChhZXMoeCA9IHByb2dyYW0sIHkgPSBtYXRoX3Njb3JlKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gdGNkc2JfYm9hcmRfY29sb3IpICsKICBsYWJzKHRpdGxlID0gIk1hdGggU2NvcmUgRGlzdHJpYnV0aW9uIGJ5IFByb2dyYW0iLAogICAgICAgeCA9ICJQcm9ncmFtIiwKICAgICAgIHkgPSAiTWF0aCBTY29yZSIpICsKICB0Y2RzYjo6dGNkc2JfZ2dwbG90X3RoZW1lKCkKYGBgCgpUaGVzZSBwbG90cyBjYW4gYmUgc2F2ZWQgdG8gZmlsZXMgdXNpbmcgdGhlIGBnZ3NhdmUoKWAgZnVuY3Rpb24uIFRoZSB3aWR0aCBhbmQgaGVpZ2h0IGFyZSBpbiBpbmNoZXM6CgpgYGB7ciBldmFsPUZBTFNFfQojIFNhdmUgdGhlIGxhc3QgcGxvdCBjcmVhdGVkCmdnc2F2ZSgibWF0aF9zY29yZXNfYm94cGxvdC5wbmciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKIyMK