This R Notebook steps through some important aspects of pupillometry data visualization, in the context of condition-averaged responses.

Developed by Lauren Fink with much external inspiration!

Explore relevant links:

https://www.research.autodesk.com/publications/same-stats-different-graphs/ http://www.thefunctionalart.com/2016/08/download-datasaurus-never-trust-summary.html http://robertgrantstats.co.uk/drawmydata.html https://cran.r-project.org/web/packages/datasauRus/vignettes/Datasaurus.html

Install required packages, if not already installed

The code below checks if each package we need in the list.of.packages variable exists in the user’s installed packages. If the user is missing any packages, we install them.

# define list of necessary packages
list.of.packages <- c("dplyr", "here", "ggplot2") 

# define list of packages that will need to be installed
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])]

# insall required packages, if any
if(length(new.packages)) install.packages(new.packages) 

Load data

We use the here package so that all file paths are relative. As long as the data .csv file lives in the same directory as the current script you are reading, it will be found. Users should be sure not to change the directory structure of the repository.

By default, figures will be plotted in line. If the user wants to save the figures to a folder on their local machine, set op = 1. Then, all figures will save into the file path defined as fig_path.

# Use the here package to locate this script on the users' machine
here::i_am("KonstanzPupilWorkshop.Rmd")
here() starts at /Users/laurenfink/Desktop
library(here) # establish all filepaths relative to this script

# Read in our data
df <- read.csv(here("KonstanzWorkshop_pupilData_byCondition.csv")) 
# df$Y = df$Y + 50
# df2 <- read.csv(here("dataVisualisation_dataset.csv"))
# df2 <- df2[!grepl("condition_3", df2$condition), ]
# df2$condition[df2$condition == "condition_1"] <- "condition_3"
# df2$y[df2$condition == "condition_4"] <- df2$y[df2$condition == "condition_4"] - 30
# colnames(df2) <- c("condition", "time", "pupil")
# df$condition <- "condition_1"
# colnames(df)[colnames(df) == "X"] <- "time"
# colnames(df)[colnames(df) == "Y"] <- "pupil"
# new_df <- rbind(df, df2)
# df <- new_df
write.csv(df, file = "KonstanzWorkshop_pupilData_byCondition.csv", row.names = FALSE)


# Define file I/O for figures
# Create an output folder for figures, if it does not already exist in this directory
fig_path <- here("figures/") # path to save generated figures to
ifelse(!dir.exists(fig_path), dir.create(fig_path), FALSE) # return FALSE if the directory already exists or can't be created. TRUE if it has been successfully created.
[1] FALSE
# Define whether to output the plot inline (op=0) or save to file (op=1). 
# Inline by default
op = 0 

Plot mean and std of pupil size by condition

Define plotting constants

NOTE: I am randomly choosing 4 color-blind-friendly colors and 4 different marker shapes. That means that those two features will change every time that the plot is generated. To make the color and marker choices constant, I provide an example in the commented code below.

# define axis limits
# Best to set them dynamically in case we might want to use this same code for a different dataset, which might have different limits. Let's find our min and max and round them to the nearest whole number.  
axisMin =floor(min(df$time, df$pupil)) # min across both our columns of interest
axisMax = ceiling(max(df$time, df$pupil)) # min across both our columns of interest

# choose a unique, color-blind-friendly color for each condition
nconds = length(unique(df$condition)) # determin number of conditions
colorBlindFriendlyPallette = c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7") # define color-blind-friendly palette
ourColors = sample(colorBlindFriendlyPallette, nconds, replace=FALSE) # randomly choose the number of colors we need, without replacement

# The reason for choosing colors dynamically (using nconds) is so that if the number of conditions in our dataset changes, our code will not break. 
# NOTE. You could set colors manually by doing something like this:
# cond1 =  "#009E73"
# cond2 =  "#0072B2"
# cond3 =  "#D55E00"
# cond4 =  "#CC79A7"
# ourColors = c(cond1, cond2, cond3, cond4)

# We can do the same thing for marker shapes
# Marker shapes are defined by number. See here for the full list of options: https://r-graphics.org/recipe-scatter-shapes
ourMarkers = sample(1:20, nconds, replace=FALSE)

# Pre-set option
# ourMarkers = c(17, 1, 5, 8)

Create bar plot

Errors bars represent std dev

if(requireNamespace("ggplot2")){ # make sure our required package is loaded
  suppressPackageStartupMessages(library(ggplot2))}
Loading required namespace: ggplot2
if (op) {png(paste(fig_path, "pupilBarPlot.png", sep=""), units="in", width=11, height=11, res=300)}
  
pupilbarplot <- ggplot(condstats, aes(x = condition, fill = condition, y = mean_pupil)) +
  geom_col(position = "dodge") + 
  scale_fill_manual(values=ourColors) +
  geom_errorbar(aes(ymin = mean_pupil - std_dev_pupil, ymax = mean_pupil + std_dev_pupil), position = position_dodge(0.9), width = .2) + labs(fill = "Condition")  

pupilbarplot + xlab("Condition") + ylab("Avg. Pupil Size (au)")

Take-aways so far?

Plenty of pupil papers would only show the results up until this point. But. There is often useful information in the pupil trajectory. Let’s plot the pupil trace over time.


Plot pupil trace over time, by condition

Create scatterplot

# Initialize output file if we want to save the generated figure to file
if (op) {png(paste(fig_path, "pupilOverTime.png", sep=""), units="in", width=11, height=11, res=300)}

# create plots
if(requireNamespace("ggplot2")){ # make sure required package is loaded
  library(ggplot2)}

ggplot(df, aes(x = time, y = pupil, colour=condition, shape=condition))+ 
    geom_point(size=4) + # create scatter plot
    scale_colour_manual(values=ourColors) + 
    scale_shape_manual(values=ourMarkers) + 
    theme_classic() + 
    theme(aspect.ratio=1, axis.text = element_text(size = 12), axis.title = element_text(size = 12), strip.text = element_text(size = 12), legend.position = "none") +
    xlim(axisMin, axisMax) + 
    ylim(axisMin, axisMax) +
    facet_wrap(~condition, ncol = 2) # create one scatter plot per condition

Main take-away

Hopefully, stepping through the exercises above has made you realize the importance of data visualization – you might not even be looking at real pupil data!!

LS0tCnRpdGxlOiAiS29uc3RhbnogUHVwaWxsb21ldHJ5IFdvcmtzaG9wIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBMYXVyZW4gRmluawpjb250YWN0OiBmaW5rbDFAbWNtYXN0ZXIuY2EKLS0tClRoaXMgUiBOb3RlYm9vayBzdGVwcyB0aHJvdWdoIHNvbWUgaW1wb3J0YW50IGFzcGVjdHMgb2YgcHVwaWxsb21ldHJ5IGRhdGEgdmlzdWFsaXphdGlvbiwgaW4gdGhlIGNvbnRleHQgb2YgY29uZGl0aW9uLWF2ZXJhZ2VkIHJlc3BvbnNlcy4gCgpEZXZlbG9wZWQgYnkgTGF1cmVuIEZpbmsgd2l0aCBtdWNoIGV4dGVybmFsIGluc3BpcmF0aW9uISAKCkV4cGxvcmUgcmVsZXZhbnQgbGlua3M6IAoKPiBodHRwczovL3d3dy5yZXNlYXJjaC5hdXRvZGVzay5jb20vcHVibGljYXRpb25zL3NhbWUtc3RhdHMtZGlmZmVyZW50LWdyYXBocy8KPiBodHRwOi8vd3d3LnRoZWZ1bmN0aW9uYWxhcnQuY29tLzIwMTYvMDgvZG93bmxvYWQtZGF0YXNhdXJ1cy1uZXZlci10cnVzdC1zdW1tYXJ5Lmh0bWwgCj4gaHR0cDovL3JvYmVydGdyYW50c3RhdHMuY28udWsvZHJhd215ZGF0YS5odG1sIAo+IGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9kYXRhc2F1UnVzL3ZpZ25ldHRlcy9EYXRhc2F1cnVzLmh0bWwgCgoKIyBJbnN0YWxsIHJlcXVpcmVkIHBhY2thZ2VzLCBpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQKVGhlIGNvZGUgYmVsb3cgY2hlY2tzIGlmIGVhY2ggcGFja2FnZSB3ZSBuZWVkIGluIHRoZSBsaXN0Lm9mLnBhY2thZ2VzIHZhcmlhYmxlIGV4aXN0cyBpbiB0aGUgdXNlcidzIGluc3RhbGxlZCBwYWNrYWdlcy4gSWYgdGhlIHVzZXIgaXMgbWlzc2luZyBhbnkgcGFja2FnZXMsIHdlIGluc3RhbGwgdGhlbS4gIApgYGB7cn0KIyBkZWZpbmUgbGlzdCBvZiBuZWNlc3NhcnkgcGFja2FnZXMKbGlzdC5vZi5wYWNrYWdlcyA8LSBjKCJkcGx5ciIsICJoZXJlIiwgImdncGxvdDIiKSAKCiMgZGVmaW5lIGxpc3Qgb2YgcGFja2FnZXMgdGhhdCB3aWxsIG5lZWQgdG8gYmUgaW5zdGFsbGVkCm5ldy5wYWNrYWdlcyA8LSBsaXN0Lm9mLnBhY2thZ2VzWyEobGlzdC5vZi5wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKV0KCiMgaW5zYWxsIHJlcXVpcmVkIHBhY2thZ2VzLCBpZiBhbnkKaWYobGVuZ3RoKG5ldy5wYWNrYWdlcykpIGluc3RhbGwucGFja2FnZXMobmV3LnBhY2thZ2VzKSAKYGBgCgojIExvYWQgZGF0YQpXZSB1c2UgdGhlICpoZXJlKiBwYWNrYWdlIHNvIHRoYXQgYWxsIGZpbGUgcGF0aHMgYXJlIHJlbGF0aXZlLiBBcyBsb25nIGFzIHRoZSBkYXRhIC5jc3YgZmlsZSBsaXZlcyBpbiB0aGUgc2FtZSBkaXJlY3RvcnkgYXMgdGhlIGN1cnJlbnQgc2NyaXB0IHlvdSBhcmUgcmVhZGluZywgaXQgd2lsbCBiZSBmb3VuZC4gVXNlcnMgc2hvdWxkIGJlIHN1cmUgbm90IHRvIGNoYW5nZSB0aGUgZGlyZWN0b3J5IHN0cnVjdHVyZSBvZiB0aGUgcmVwb3NpdG9yeS4gCgpCeSBkZWZhdWx0LCBmaWd1cmVzIHdpbGwgYmUgcGxvdHRlZCBpbiBsaW5lLiBJZiB0aGUgdXNlciB3YW50cyB0byBzYXZlIHRoZSBmaWd1cmVzIHRvIGEgZm9sZGVyIG9uIHRoZWlyIGxvY2FsIG1hY2hpbmUsIHNldCBgb3AgPSAxYC4gVGhlbiwgYWxsIGZpZ3VyZXMgd2lsbCBzYXZlIGludG8gdGhlIGZpbGUgcGF0aCBkZWZpbmVkIGFzIGBmaWdfcGF0aGAuCmBgYHtyfQojIFVzZSB0aGUgaGVyZSBwYWNrYWdlIHRvIGxvY2F0ZSB0aGlzIHNjcmlwdCBvbiB0aGUgdXNlcnMnIG1hY2hpbmUKaGVyZTo6aV9hbSgiS29uc3RhbnpQdXBpbFdvcmtzaG9wLlJtZCIpCmxpYnJhcnkoaGVyZSkgIyBlc3RhYmxpc2ggYWxsIGZpbGVwYXRocyByZWxhdGl2ZSB0byB0aGlzIHNjcmlwdAoKIyBSZWFkIGluIG91ciBkYXRhCmRmIDwtIHJlYWQuY3N2KGhlcmUoIktvbnN0YW56V29ya3Nob3BfcHVwaWxEYXRhX2J5Q29uZGl0aW9uLmNzdiIpKSAKIyBkZiRZID0gZGYkWSArIDUwCiMgZGYyIDwtIHJlYWQuY3N2KGhlcmUoImRhdGFWaXN1YWxpc2F0aW9uX2RhdGFzZXQuY3N2IikpCiMgZGYyIDwtIGRmMlshZ3JlcGwoImNvbmRpdGlvbl8zIiwgZGYyJGNvbmRpdGlvbiksIF0KIyBkZjIkY29uZGl0aW9uW2RmMiRjb25kaXRpb24gPT0gImNvbmRpdGlvbl8xIl0gPC0gImNvbmRpdGlvbl8zIgojIGRmMiR5W2RmMiRjb25kaXRpb24gPT0gImNvbmRpdGlvbl80Il0gPC0gZGYyJHlbZGYyJGNvbmRpdGlvbiA9PSAiY29uZGl0aW9uXzQiXSAtIDMwCiMgY29sbmFtZXMoZGYyKSA8LSBjKCJjb25kaXRpb24iLCAidGltZSIsICJwdXBpbCIpCiMgZGYkY29uZGl0aW9uIDwtICJjb25kaXRpb25fMSIKIyBjb2xuYW1lcyhkZilbY29sbmFtZXMoZGYpID09ICJYIl0gPC0gInRpbWUiCiMgY29sbmFtZXMoZGYpW2NvbG5hbWVzKGRmKSA9PSAiWSJdIDwtICJwdXBpbCIKIyBuZXdfZGYgPC0gcmJpbmQoZGYsIGRmMikKIyBkZiA8LSBuZXdfZGYKd3JpdGUuY3N2KGRmLCBmaWxlID0gIktvbnN0YW56V29ya3Nob3BfcHVwaWxEYXRhX2J5Q29uZGl0aW9uLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKCiMgRGVmaW5lIGZpbGUgSS9PIGZvciBmaWd1cmVzCiMgQ3JlYXRlIGFuIG91dHB1dCBmb2xkZXIgZm9yIGZpZ3VyZXMsIGlmIGl0IGRvZXMgbm90IGFscmVhZHkgZXhpc3QgaW4gdGhpcyBkaXJlY3RvcnkKZmlnX3BhdGggPC0gaGVyZSgiZmlndXJlcy8iKSAjIHBhdGggdG8gc2F2ZSBnZW5lcmF0ZWQgZmlndXJlcyB0bwppZmVsc2UoIWRpci5leGlzdHMoZmlnX3BhdGgpLCBkaXIuY3JlYXRlKGZpZ19wYXRoKSwgRkFMU0UpICMgcmV0dXJuIEZBTFNFIGlmIHRoZSBkaXJlY3RvcnkgYWxyZWFkeSBleGlzdHMgb3IgY2FuJ3QgYmUgY3JlYXRlZC4gVFJVRSBpZiBpdCBoYXMgYmVlbiBzdWNjZXNzZnVsbHkgY3JlYXRlZC4KCiMgRGVmaW5lIHdoZXRoZXIgdG8gb3V0cHV0IHRoZSBwbG90IGlubGluZSAob3A9MCkgb3Igc2F2ZSB0byBmaWxlIChvcD0xKS4gCiMgSW5saW5lIGJ5IGRlZmF1bHQKb3AgPSAwIApgYGAKCgojIFByaW50IHN1bW1hcnkgc3RhdHMsIGJ5IGNvbmRpdGlvbiwgdG8gYSB0YWJsZQpCZWxvdyB3ZSBvdXRwdXQgbWluLCBtYXgsIG1lYW4sIHN0ZC4gRmVlbCBmcmVlIHRvIGFkZCBvdGhlcnMhCmBgYHtyfQppZihyZXF1aXJlTmFtZXNwYWNlKCJkcGx5ciIpKXsgIyBtYWtlIHN1cmUgb3VyIHJlcXVpcmVkIHBhY2thZ2UgaXMgbG9hZGVkCiAgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKX0KICAKICAjIHNwZWNpZnkgc3RhdHMgdG8gcHJpbnQKY29uZHN0YXRzIDwtICBkZiAlPiUgCiAgICBncm91cF9ieShjb25kaXRpb24pICU+JSAgIyBwcmludCBmb3IgZWFjaCBjb25kaXRpb24gc2VwYXJhdGVseQogICAgc3VtbWFyaXplKAogICAgICBtaW5fcHVwaWwgICAgID0gbWluKHB1cGlsKSwgIyBkZWZpbmUgc3RhdHMgd2UgY2FyZSBhYm91dAogICAgICBtYXhfcHVwaWwgICAgPSBtYXgocHVwaWwpLAogICAgICBtZWFuX3B1cGlsICAgID0gbWVhbihwdXBpbCksCiAgICAgIHN0ZF9kZXZfcHVwaWwgPSBzZChwdXBpbCksCiAgICApCgpjb25kc3RhdHMgIyBwcmludCBpbmxpbmUKYGBgCgoKIyBQbG90IG1lYW4gYW5kIHN0ZCBvZiBwdXBpbCBzaXplIGJ5IGNvbmRpdGlvbgoKIyMjIyBEZWZpbmUgcGxvdHRpbmcgY29uc3RhbnRzCk5PVEU6IEkgYW0gcmFuZG9tbHkgY2hvb3NpbmcgNCBjb2xvci1ibGluZC1mcmllbmRseSBjb2xvcnMgYW5kIDQgZGlmZmVyZW50IG1hcmtlciBzaGFwZXMuIFRoYXQgbWVhbnMgdGhhdCB0aG9zZSB0d28gZmVhdHVyZXMgd2lsbCBjaGFuZ2UgZXZlcnkgdGltZSB0aGF0IHRoZSBwbG90IGlzIGdlbmVyYXRlZC4gVG8gbWFrZSB0aGUgY29sb3IgYW5kIG1hcmtlciBjaG9pY2VzIGNvbnN0YW50LCBJIHByb3ZpZGUgYW4gZXhhbXBsZSBpbiB0aGUgY29tbWVudGVkIGNvZGUgYmVsb3cuIApgYGB7cn0KIyBkZWZpbmUgYXhpcyBsaW1pdHMKIyBCZXN0IHRvIHNldCB0aGVtIGR5bmFtaWNhbGx5IGluIGNhc2Ugd2UgbWlnaHQgd2FudCB0byB1c2UgdGhpcyBzYW1lIGNvZGUgZm9yIGEgZGlmZmVyZW50IGRhdGFzZXQsIHdoaWNoIG1pZ2h0IGhhdmUgZGlmZmVyZW50IGxpbWl0cy4gTGV0J3MgZmluZCBvdXIgbWluIGFuZCBtYXggYW5kIHJvdW5kIHRoZW0gdG8gdGhlIG5lYXJlc3Qgd2hvbGUgbnVtYmVyLiAgCmF4aXNNaW4gPWZsb29yKG1pbihkZiR0aW1lLCBkZiRwdXBpbCkpICMgbWluIGFjcm9zcyBib3RoIG91ciBjb2x1bW5zIG9mIGludGVyZXN0CmF4aXNNYXggPSBjZWlsaW5nKG1heChkZiR0aW1lLCBkZiRwdXBpbCkpICMgbWluIGFjcm9zcyBib3RoIG91ciBjb2x1bW5zIG9mIGludGVyZXN0CgojIGNob29zZSBhIHVuaXF1ZSwgY29sb3ItYmxpbmQtZnJpZW5kbHkgY29sb3IgZm9yIGVhY2ggY29uZGl0aW9uCm5jb25kcyA9IGxlbmd0aCh1bmlxdWUoZGYkY29uZGl0aW9uKSkgIyBkZXRlcm1pbiBudW1iZXIgb2YgY29uZGl0aW9ucwpjb2xvckJsaW5kRnJpZW5kbHlQYWxsZXR0ZSA9IGMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiwgIiMwMDlFNzMiLCAiI0YwRTQ0MiIsICIjMDA3MkIyIiwgIiNENTVFMDAiLCAiI0NDNzlBNyIpICMgZGVmaW5lIGNvbG9yLWJsaW5kLWZyaWVuZGx5IHBhbGV0dGUKb3VyQ29sb3JzID0gc2FtcGxlKGNvbG9yQmxpbmRGcmllbmRseVBhbGxldHRlLCBuY29uZHMsIHJlcGxhY2U9RkFMU0UpICMgcmFuZG9tbHkgY2hvb3NlIHRoZSBudW1iZXIgb2YgY29sb3JzIHdlIG5lZWQsIHdpdGhvdXQgcmVwbGFjZW1lbnQKCiMgVGhlIHJlYXNvbiBmb3IgY2hvb3NpbmcgY29sb3JzIGR5bmFtaWNhbGx5ICh1c2luZyBuY29uZHMpIGlzIHNvIHRoYXQgaWYgdGhlIG51bWJlciBvZiBjb25kaXRpb25zIGluIG91ciBkYXRhc2V0IGNoYW5nZXMsIG91ciBjb2RlIHdpbGwgbm90IGJyZWFrLiAKIyBOT1RFLiBZb3UgY291bGQgc2V0IGNvbG9ycyBtYW51YWxseSBieSBkb2luZyBzb21ldGhpbmcgbGlrZSB0aGlzOgojIGNvbmQxID0gICIjMDA5RTczIgojIGNvbmQyID0gICIjMDA3MkIyIgojIGNvbmQzID0gICIjRDU1RTAwIgojIGNvbmQ0ID0gICIjQ0M3OUE3IgojIG91ckNvbG9ycyA9IGMoY29uZDEsIGNvbmQyLCBjb25kMywgY29uZDQpCgojIFdlIGNhbiBkbyB0aGUgc2FtZSB0aGluZyBmb3IgbWFya2VyIHNoYXBlcwojIE1hcmtlciBzaGFwZXMgYXJlIGRlZmluZWQgYnkgbnVtYmVyLiBTZWUgaGVyZSBmb3IgdGhlIGZ1bGwgbGlzdCBvZiBvcHRpb25zOiBodHRwczovL3ItZ3JhcGhpY3Mub3JnL3JlY2lwZS1zY2F0dGVyLXNoYXBlcwpvdXJNYXJrZXJzID0gc2FtcGxlKDE6MjAsIG5jb25kcywgcmVwbGFjZT1GQUxTRSkKCiMgUHJlLXNldCBvcHRpb24KIyBvdXJNYXJrZXJzID0gYygxNywgMSwgNSwgOCkKYGBgCgojIyMgQ3JlYXRlIGJhciBwbG90IApFcnJvcnMgYmFycyByZXByZXNlbnQgc3RkIGRldgpgYGB7cn0KaWYocmVxdWlyZU5hbWVzcGFjZSgiZ2dwbG90MiIpKXsgIyBtYWtlIHN1cmUgb3VyIHJlcXVpcmVkIHBhY2thZ2UgaXMgbG9hZGVkCiAgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZ2dwbG90MikpfQoKaWYgKG9wKSB7cG5nKHBhc3RlKGZpZ19wYXRoLCAicHVwaWxCYXJQbG90LnBuZyIsIHNlcD0iIiksIHVuaXRzPSJpbiIsIHdpZHRoPTExLCBoZWlnaHQ9MTEsIHJlcz0zMDApfQogIApwdXBpbGJhcnBsb3QgPC0gZ2dwbG90KGNvbmRzdGF0cywgYWVzKHggPSBjb25kaXRpb24sIGZpbGwgPSBjb25kaXRpb24sIHkgPSBtZWFuX3B1cGlsKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9b3VyQ29sb3JzKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW5fcHVwaWwgLSBzdGRfZGV2X3B1cGlsLCB5bWF4ID0gbWVhbl9wdXBpbCArIHN0ZF9kZXZfcHVwaWwpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0gLjIpICsgbGFicyhmaWxsID0gIkNvbmRpdGlvbiIpICAKCnB1cGlsYmFycGxvdCArIHhsYWIoIkNvbmRpdGlvbiIpICsgeWxhYigiQXZnLiBQdXBpbCBTaXplIChhdSkiKQpgYGAKIyBUYWtlLWF3YXlzIHNvIGZhcj8KUGxlbnR5IG9mIHB1cGlsIHBhcGVycyB3b3VsZCBvbmx5IHNob3cgdGhlIHJlc3VsdHMgdXAgdW50aWwgdGhpcyBwb2ludC4gQnV0LiBUaGVyZSBpcyBvZnRlbiB1c2VmdWwgaW5mb3JtYXRpb24gaW4gdGhlIHB1cGlsIHRyYWplY3RvcnkuIExldCdzIHBsb3QgdGhlIHB1cGlsIHRyYWNlIG92ZXIgdGltZS4gCgpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwoKIyBQbG90IHB1cGlsIHRyYWNlIG92ZXIgdGltZSwgYnkgY29uZGl0aW9uCgojIyMgQ3JlYXRlIHNjYXR0ZXJwbG90CmBgYHtyfQojIEluaXRpYWxpemUgb3V0cHV0IGZpbGUgaWYgd2Ugd2FudCB0byBzYXZlIHRoZSBnZW5lcmF0ZWQgZmlndXJlIHRvIGZpbGUKaWYgKG9wKSB7cG5nKHBhc3RlKGZpZ19wYXRoLCAicHVwaWxPdmVyVGltZS5wbmciLCBzZXA9IiIpLCB1bml0cz0iaW4iLCB3aWR0aD0xMSwgaGVpZ2h0PTExLCByZXM9MzAwKX0KCiMgY3JlYXRlIHBsb3RzCmlmKHJlcXVpcmVOYW1lc3BhY2UoImdncGxvdDIiKSl7ICMgbWFrZSBzdXJlIHJlcXVpcmVkIHBhY2thZ2UgaXMgbG9hZGVkCiAgbGlicmFyeShnZ3Bsb3QyKX0KCmdncGxvdChkZiwgYWVzKHggPSB0aW1lLCB5ID0gcHVwaWwsIGNvbG91cj1jb25kaXRpb24sIHNoYXBlPWNvbmRpdGlvbikpKyAKICAgIGdlb21fcG9pbnQoc2l6ZT00KSArICMgY3JlYXRlIHNjYXR0ZXIgcGxvdAogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9b3VyQ29sb3JzKSArIAogICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1vdXJNYXJrZXJzKSArIAogICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICB0aGVtZShhc3BlY3QucmF0aW89MSwgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIHhsaW0oYXhpc01pbiwgYXhpc01heCkgKyAKICAgIHlsaW0oYXhpc01pbiwgYXhpc01heCkgKwogICAgZmFjZXRfd3JhcCh+Y29uZGl0aW9uLCBuY29sID0gMikgIyBjcmVhdGUgb25lIHNjYXR0ZXIgcGxvdCBwZXIgY29uZGl0aW9uCmBgYAoKCiMjIE1haW4gdGFrZS1hd2F5CkhvcGVmdWxseSwgc3RlcHBpbmcgdGhyb3VnaCB0aGUgZXhlcmNpc2VzIGFib3ZlIGhhcyBtYWRlIHlvdSByZWFsaXplIHRoZSBpbXBvcnRhbmNlIG9mIGRhdGEgdmlzdWFsaXphdGlvbiAtLSB5b3UgbWlnaHQgbm90IGV2ZW4gYmUgbG9va2luZyBhdCByZWFsIHB1cGlsIGRhdGEhIQo=