This exercise is based on data from a PhD student in cognitive psychology. In his work, this student collects response times (RT) to two simultaneous tasks, and then he has to analyse the response times.

  • At \(t_0\), the stimulus S1 is triggered
  • At \(t_1=t_0 + SOA\), the stimulus S2 is triggered (SOA is the time between the 2 stimuli)
  • At \(t_2=t_0 + RT1\), the subject responds to stimulus S1
  • At \(t_3=t_1 + RT2\), the subject responds to stimulus S2

The inter-response interval (IRI) is defined as the time difference between \(t_3\) and \(t_2\), thus \(IRI=SOA+RT2-RT1\). Negatives IRI therefore mean that the response to S2 is emitted before the response to S1.

For theoretical reasons, one can consider that if \(SOA=1500\) ms, both stimulis are answered independently. One can thus use the RT1 and RT2 values for SOA=1500 ms to estimate the IRI in the case of independent responses to the stimuli.


1 Data wrangling

  • Download the datafiles archive and unzip it, then create a R project in this folder in Rstudio.
  • Load the tidyverse and patchwork packages:
library(tidyverse)
library(patchwork)
  • Load the .csv file in the Data folder and save it into raw_data. Look into the help of read_csv() to help you get rid of the error.
raw_data <- read_csv2("Data/data exemple.csv", show_col_types = FALSE)
  • Using successive pipe operations, we will now create a dataclean table from raw_data, where we:
    • Filter raw_data so that Procedure[Trial] is only equal to "EssaisDT"
    • Mutate the table by adding a column IRI containing SOAdur + S2Visuel.RT - S1Audio.RT
    • Filter the table so that some extreme values are excluded:
      • S1Audio.RT is smaller or equal to 2500 and larger or equal to 100
      • S2Visuel.RT is smaller or equal to 2500 and larger or equal to 100
      • S1Audio.ACC, S1response.ACC and S2Visuel.ACC are equal to 1
dataclean <- raw_data %>% 
    filter(`Procedure[Trial]` == "EssaisDT") %>%
    mutate(IRI = SOAdur + S2Visuel.RT - S1Audio.RT) %>% 
    filter(S1Audio.RT <= 2500 & S1Audio.RT >= 100 &
        S2Visuel.RT <= 2500 & S2Visuel.RT >= 100 &
        S1Audio.ACC == 1 & S1response.ACC == 1 & S2Visuel.ACC == 1)
  • Let’s compute the simulated values in the case the responses to the stimuli are independent, i.e. using SOA=1500ms, and save it into a tibble called IRI_sim. Using successive pipe operations and starting from dataclean:
    • Filter rows so that SOAdur is equal to 1500
    • Delete the SOAdur and IRI columns
    • Mutate the table to add 3 columns IRI_sim_xx, where xx=15, 65 or 250 and IRI_sim_xx = xx + S2Visuel.RT - S1Audio.RT.
    • Using pivot_longer() and the options names_prefix = "IRI_sim_", names_to = "SOAdur", values_to = "IRI_sim", pivot the columns containing "IRI_sim_" into a long table (you need to add the option to select the corresponding columns).
IRI_sim <- dataclean %>%
    filter(SOAdur == 1500) %>%
    select(-c(SOAdur, IRI)) %>%
    mutate(
        IRI_sim_15 = 15 + S2Visuel.RT - S1Audio.RT,
        IRI_sim_65 = 65 + S2Visuel.RT - S1Audio.RT,
        IRI_sim_250 = 250 + S2Visuel.RT - S1Audio.RT
    ) %>%
    pivot_longer(
        cols = contains("IRI_sim_"),
        names_prefix = "IRI_sim_",
        names_to = "SOAdur",
        values_to = "IRI_sim"
    )
  • We want now to get the averaged IRI per subject and per SOA, and its standard deviation. Using group_by() and summarise(), store the mean and standard deviation of IRI in a table called stats_obs, starting from dataclean. It should look like this:
(stats_obs <- dataclean %>%
    group_by(Subject, SOAdur) %>%
    summarise(
        mean = mean(IRI),
        sd = sd(IRI)
    ))
## # A tibble: 24 × 4
## # Groups:   Subject [6]
##    Subject SOAdur    mean    sd
##      <dbl>  <dbl>   <dbl> <dbl>
##  1       1     15  -64.8   291.
##  2       1     65   20.8   297.
##  3       1    250  218.    217.
##  4       1   1500 1019.    228.
##  5       2     15 -159.    193.
##  6       2     65 -156.    236.
##  7       2    250    4.42  191.
##  8       2   1500 1184.    198.
##  9       3     15 -289.    207.
## 10       3     65 -251.    262.
## # … with 14 more rows
  • We want to do the same for the 3 simulated IRI.
(stats_sim <- IRI_sim %>%
    group_by(Subject, SOAdur) %>%
    summarise(
        mean = mean(IRI_sim),
        sd = sd(IRI_sim)
    ))
## # A tibble: 18 × 4
## # Groups:   Subject [6]
##    Subject SOAdur   mean    sd
##      <dbl> <chr>   <dbl> <dbl>
##  1       1 15     -466.   228.
##  2       1 250    -231.   228.
##  3       1 65     -416.   228.
##  4       2 15     -301.   198.
##  5       2 250     -65.9  198.
##  6       2 65     -251.   198.
##  7       3 15      -99.0  157.
##  8       3 250     136.   157.
##  9       3 65      -49.0  157.
## 10       4 15     -168.   214.
## 11       4 250      66.9  214.
## 12       4 65     -118.   214.
## 13       5 15     -367.   249.
## 14       5 250    -132.   249.
## 15       5 65     -317.   249.
## 16       6 15     -313.   161.
## 17       6 250     -77.9  161.
## 18       6 65     -263.   161.

2 Plotting

  • We want now to produce a graph showing the histograms of the observed IRI column using ggplot2.
    • Create a ggplot using the dataclean dataset
    • Set the aesthetics to x = IRI
    • Create the histograms using geom_histogram(), with a fill color depending on SOAdur
    • Arrange the plots on a grid depending on the Subject column using facet_wrap()
    • Add a vertical lign marking the average value for each subject using geom_vline(). The data for these lines are stored in the stats_obs dataset.
    • Play with the theme and other ggplot commands to make the plot look like the one below
dataclean %>% 
    ggplot(aes(x = IRI, fill = factor(SOAdur))) +
    geom_histogram(aes(y = stat(count / sum(count))), bins=20) +
    scale_y_continuous(labels = scales::percent_format())+
    facet_wrap(~ paste("Subject", Subject)) +
    geom_vline(data = stats_obs, aes(xintercept = mean, color=factor(SOAdur)), lty = 2, show.legend = FALSE) +
    scale_x_continuous(limits = c(-2000,2000))+
    labs(title = "Observations",
         x = "IRI [ms]",
         y = "Occurence", 
         fill="SOA [ms]") +
    theme_bw() +
    theme(strip.background = element_rect(fill = "transparent", colour = NA),
          strip.text = element_text(face = "bold"),
          axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))

  • Let’s do the same for the simulated dataset. It should look like this:
IRI_sim %>% 
    ggplot(aes(x = IRI_sim, fill = factor(SOAdur, levels=c(15, 65, 250)))) +
    geom_histogram(aes(y = stat(count / sum(count))), bins = 20) +
    scale_y_continuous(labels = scales::percent_format()) +
    facet_wrap(~ paste("Subject", Subject)) +
    geom_vline(data = stats_sim, aes(xintercept = mean, color=factor(SOAdur)), lty = 2, show.legend = FALSE) +
    scale_x_continuous(limits = c(-2000,2000))+
    labs(
        title = "Simulations",
        x = "IRI [ms]",
        y = "Occurence",
        fill = "SOA [ms]"
    ) +
    theme_bw() +
    theme(
        strip.background = element_rect(fill = "transparent", colour = NA),
        strip.text = element_text(face = "bold"),
        axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)
    )

LS0tCnRpdGxlIDogIlIgRXhlcmNpc2VzIC0gQ29nbml0aXZlIFBzeWNob2xvZ3kiCmRhdGUgIDogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogICAgaHRtbF9kb2N1bWVudDoKICAgICAgICB0b2MgICAgICAgICAgICA6IHRydWUKICAgICAgICB0b2NfZmxvYXQgICAgICA6IHRydWUKICAgICAgICB0b2NfZGVwdGggICAgICA6IDQKICAgICAgICBoaWdobGlnaHQgICAgICA6IHRhbmdvCiAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICAgICAgY29kZV9kb3dubG9hZCAgOiBUUlVFCnBhcmFtczogCiAgICBzb2x1dGlvbjoKICAgICAgICB2YWx1ZTogVFJVRQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJsb2NrcXVvdGUgewogIGJhY2tncm91bmQ6ICNFOUY5RkY7CiAgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjMDI2MDg2OwogIG1hcmdpbjogMS41ZW0gMTBweDsKICBwYWRkaW5nOiAwLjVlbSAxMHB4OwogIGZvbnQtc2l6ZTogMWVtOwp9Cjwvc3R5bGU+CgpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciJ9CmxpYnJhcnkoZG93bmxvYWR0aGlzKQpkb3dubG9hZF9saW5rKAogIGxpbmsgPSAiLi9BcmNoaXZlLnppcCIsCiAgb3V0cHV0X25hbWUgPSAiRGF0YSBGaWxlcyIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIERhdGEgRmlsZXMiLAogIGJ1dHRvbl90eXBlID0gImRlZmF1bHQiLAogIGhhc19pY29uID0gVFJVRSwKICBpY29uID0gImZhIGZhLXNhdmUiLAogIHNlbGZfY29udGFpbmVkID0gRkFMU0UKKQpgYGAKPGJyPgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShrbml0cikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UsIG91dC53aWR0aD0nMTAwJScsIHdhcm5pbmdzPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQpvcHRpb25zKHdpZHRoID0gODApCmBgYAoKLS0tLQoKVGhpcyBleGVyY2lzZSBpcyBiYXNlZCBvbiBkYXRhIGZyb20gYSBQaEQgc3R1ZGVudCBpbiBjb2duaXRpdmUgcHN5Y2hvbG9neS4gSW4gaGlzIHdvcmssIHRoaXMgc3R1ZGVudCBjb2xsZWN0cyByZXNwb25zZSB0aW1lcyAoUlQpIHRvIHR3byBzaW11bHRhbmVvdXMgdGFza3MsIGFuZCB0aGVuIGhlIGhhcyB0byBhbmFseXNlIHRoZSByZXNwb25zZSB0aW1lcy4KCi0gQXQgJHRfMCQsIHRoZSBzdGltdWx1cyBTMSBpcyB0cmlnZ2VyZWQKLSBBdCAkdF8xPXRfMCArIFNPQSQsIHRoZSBzdGltdWx1cyBTMiBpcyB0cmlnZ2VyZWQgKFNPQSBpcyB0aGUgdGltZSBiZXR3ZWVuIHRoZSAyIHN0aW11bGkpCi0gQXQgJHRfMj10XzAgKyBSVDEkLCB0aGUgc3ViamVjdCByZXNwb25kcyB0byBzdGltdWx1cyBTMQotIEF0ICR0XzM9dF8xICsgUlQyJCwgdGhlIHN1YmplY3QgcmVzcG9uZHMgdG8gc3RpbXVsdXMgUzIKClRoZSBpbnRlci1yZXNwb25zZSBpbnRlcnZhbCAoSVJJKSBpcyBkZWZpbmVkIGFzIHRoZSB0aW1lIGRpZmZlcmVuY2UgYmV0d2VlbiAkdF8zJCBhbmQgJHRfMiQsIHRodXMgJElSST1TT0ErUlQyLVJUMSQuIE5lZ2F0aXZlcyBJUkkgdGhlcmVmb3JlIG1lYW4gdGhhdCB0aGUgcmVzcG9uc2UgdG8gUzIgaXMgZW1pdHRlZCBiZWZvcmUgdGhlIHJlc3BvbnNlIHRvIFMxLgoKRm9yIHRoZW9yZXRpY2FsIHJlYXNvbnMsIG9uZSBjYW4gY29uc2lkZXIgdGhhdCBpZiAkU09BPTE1MDAkIG1zLCBib3RoIHN0aW11bGlzIGFyZSBhbnN3ZXJlZCBpbmRlcGVuZGVudGx5LiBPbmUgY2FuIHRodXMgdXNlIHRoZSBSVDEgYW5kIFJUMiB2YWx1ZXMgZm9yIFNPQT0xNTAwIG1zIHRvIGVzdGltYXRlIHRoZSBJUkkgaW4gdGhlIGNhc2Ugb2YgaW5kZXBlbmRlbnQgcmVzcG9uc2VzIHRvIHRoZSBzdGltdWxpLgoKLS0tCgojIERhdGEgd3JhbmdsaW5nCgotIERvd25sb2FkIHRoZSBkYXRhZmlsZXMgPGEgaHJlZj0iQXJjaGl2ZS56aXAiIGRvd25sb2FkIHRhcmdldD0iX2JsYW5rIj5hcmNoaXZlPC9hPiBhbmQgdW56aXAgaXQsIHRoZW4gY3JlYXRlIGEgUiBwcm9qZWN0IGluIHRoaXMgZm9sZGVyIGluIFJzdHVkaW8uCi0gTG9hZCB0aGUgYHRpZHl2ZXJzZWAgYW5kIGBwYXRjaHdvcmtgIHBhY2thZ2VzOgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykKYGBgCgotIExvYWQgdGhlIC5jc3YgZmlsZSBpbiB0aGUgYERhdGFgIGZvbGRlciBhbmQgc2F2ZSBpdCBpbnRvIGByYXdfZGF0YWAuIExvb2sgaW50byB0aGUgaGVscCBvZiBgcmVhZF9jc3YoKWB7LlJ9IHRvIGhlbHAgeW91IGdldCByaWQgb2YgdGhlIGVycm9yLgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CnJhd19kYXRhIDwtIHJlYWRfY3N2MigiRGF0YS9kYXRhIGV4ZW1wbGUuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKYGBgCgotIFVzaW5nIHN1Y2Nlc3NpdmUgKipwaXBlIG9wZXJhdGlvbnMqKiwgd2Ugd2lsbCBub3cgY3JlYXRlIGEgYGRhdGFjbGVhbmAgdGFibGUgZnJvbSBgcmF3X2RhdGFgLCB3aGVyZSB3ZToKICAgIC0gKipGaWx0ZXIqKiBgcmF3X2RhdGFgIHNvIHRoYXQgYFByb2NlZHVyZVtUcmlhbF1gIGlzIG9ubHkgZXF1YWwgdG8gYCJFc3NhaXNEVCJgCiAgICAtICoqTXV0YXRlKiogdGhlIHRhYmxlIGJ5IGFkZGluZyBhIGNvbHVtbiBgSVJJYCBjb250YWluaW5nIGBTT0FkdXIgKyBTMlZpc3VlbC5SVCAtIFMxQXVkaW8uUlRgCiAgICAtICoqRmlsdGVyKiogdGhlIHRhYmxlIHNvIHRoYXQgc29tZSBleHRyZW1lIHZhbHVlcyBhcmUgZXhjbHVkZWQ6CiAgICAgICAgLSBgUzFBdWRpby5SVGAgaXMgc21hbGxlciBvciBlcXVhbCB0byAyNTAwIGFuZCBsYXJnZXIgb3IgZXF1YWwgdG8gMTAwCiAgICAgICAgLSBgUzJWaXN1ZWwuUlRgIGlzIHNtYWxsZXIgb3IgZXF1YWwgdG8gMjUwMCBhbmQgbGFyZ2VyIG9yIGVxdWFsIHRvIDEwMAogICAgICAgIC0gYFMxQXVkaW8uQUNDYCwgYFMxcmVzcG9uc2UuQUNDYCBhbmQgYFMyVmlzdWVsLkFDQ2AgYXJlIGVxdWFsIHRvIDEKCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPUZBTFNFfQpkYXRhY2xlYW4gPC0gcmF3X2RhdGEgJT4lIAogICAgZmlsdGVyKGBQcm9jZWR1cmVbVHJpYWxdYCA9PSAiRXNzYWlzRFQiKSAlPiUKICAgIG11dGF0ZShJUkkgPSBTT0FkdXIgKyBTMlZpc3VlbC5SVCAtIFMxQXVkaW8uUlQpICU+JSAKICAgIGZpbHRlcihTMUF1ZGlvLlJUIDw9IDI1MDAgJiBTMUF1ZGlvLlJUID49IDEwMCAmCiAgICAgICAgUzJWaXN1ZWwuUlQgPD0gMjUwMCAmIFMyVmlzdWVsLlJUID49IDEwMCAmCiAgICAgICAgUzFBdWRpby5BQ0MgPT0gMSAmIFMxcmVzcG9uc2UuQUNDID09IDEgJiBTMlZpc3VlbC5BQ0MgPT0gMSkKYGBgCgotIExldCdzIGNvbXB1dGUgdGhlIHNpbXVsYXRlZCB2YWx1ZXMgaW4gdGhlIGNhc2UgdGhlIHJlc3BvbnNlcyB0byB0aGUgc3RpbXVsaSBhcmUgaW5kZXBlbmRlbnQsIF9pLmUuXyB1c2luZyBTT0E9MTUwMG1zLCBhbmQgc2F2ZSBpdCBpbnRvIGEgdGliYmxlIGNhbGxlZCBgSVJJX3NpbWAuIFVzaW5nIHN1Y2Nlc3NpdmUgcGlwZSBvcGVyYXRpb25zIGFuZCBzdGFydGluZyBmcm9tIGBkYXRhY2xlYW5gOgogICAgLSAqKkZpbHRlcioqIHJvd3Mgc28gdGhhdCBgU09BZHVyYCBpcyBlcXVhbCB0byAxNTAwCiAgICAtIERlbGV0ZSB0aGUgYFNPQWR1cmAgYW5kIGBJUklgIGNvbHVtbnMKICAgIC0gKipNdXRhdGUqKiB0aGUgdGFibGUgdG8gYWRkIDMgY29sdW1ucyBgSVJJX3NpbV94eGAsIHdoZXJlIGB4eGA9MTUsIDY1IG9yIDI1MCBhbmQgYElSSV9zaW1feHggPSB4eCArIFMyVmlzdWVsLlJUIC0gUzFBdWRpby5SVGAuCiAgICAtIFVzaW5nIGBwaXZvdF9sb25nZXIoKWB7LlJ9IGFuZCB0aGUgb3B0aW9ucyBgbmFtZXNfcHJlZml4ID0gIklSSV9zaW1fIiwgbmFtZXNfdG8gPSAiU09BZHVyIiwgdmFsdWVzX3RvID0gIklSSV9zaW0iYCwgcGl2b3QgdGhlIGNvbHVtbnMgY29udGFpbmluZyBgIklSSV9zaW1fImAgaW50byBhIGxvbmcgdGFibGUgKHlvdSBuZWVkIHRvIGFkZCB0aGUgb3B0aW9uIHRvIHNlbGVjdCB0aGUgY29ycmVzcG9uZGluZyBjb2x1bW5zKS4KCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPUZBTFNFfQpJUklfc2ltIDwtIGRhdGFjbGVhbiAlPiUKICAgIGZpbHRlcihTT0FkdXIgPT0gMTUwMCkgJT4lCiAgICBzZWxlY3QoLWMoU09BZHVyLCBJUkkpKSAlPiUKICAgIG11dGF0ZSgKICAgICAgICBJUklfc2ltXzE1ID0gMTUgKyBTMlZpc3VlbC5SVCAtIFMxQXVkaW8uUlQsCiAgICAgICAgSVJJX3NpbV82NSA9IDY1ICsgUzJWaXN1ZWwuUlQgLSBTMUF1ZGlvLlJULAogICAgICAgIElSSV9zaW1fMjUwID0gMjUwICsgUzJWaXN1ZWwuUlQgLSBTMUF1ZGlvLlJUCiAgICApICU+JQogICAgcGl2b3RfbG9uZ2VyKAogICAgICAgIGNvbHMgPSBjb250YWlucygiSVJJX3NpbV8iKSwKICAgICAgICBuYW1lc19wcmVmaXggPSAiSVJJX3NpbV8iLAogICAgICAgIG5hbWVzX3RvID0gIlNPQWR1ciIsCiAgICAgICAgdmFsdWVzX3RvID0gIklSSV9zaW0iCiAgICApCmBgYAoKLSBXZSB3YW50IG5vdyB0byBnZXQgdGhlIGF2ZXJhZ2VkIGBJUklgIHBlciBzdWJqZWN0IGFuZCBwZXIgU09BLCBhbmQgaXRzIHN0YW5kYXJkIGRldmlhdGlvbi4gVXNpbmcgYGdyb3VwX2J5KClgey5SfSBhbmQgYHN1bW1hcmlzZSgpYHsuUn0sIHN0b3JlIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYElSSWAgaW4gYSB0YWJsZSBjYWxsZWQgYHN0YXRzX29ic2AsIHN0YXJ0aW5nIGZyb20gYGRhdGFjbGVhbmAuIEl0IHNob3VsZCBsb29rIGxpa2UgdGhpczoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KKHN0YXRzX29icyA8LSBkYXRhY2xlYW4gJT4lCiAgICBncm91cF9ieShTdWJqZWN0LCBTT0FkdXIpICU+JQogICAgc3VtbWFyaXNlKAogICAgICAgIG1lYW4gPSBtZWFuKElSSSksCiAgICAgICAgc2QgPSBzZChJUkkpCiAgICApKQpgYGAKCi0gV2Ugd2FudCB0byBkbyB0aGUgc2FtZSBmb3IgdGhlIDMgc2ltdWxhdGVkIElSSS4gCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KKHN0YXRzX3NpbSA8LSBJUklfc2ltICU+JQogICAgZ3JvdXBfYnkoU3ViamVjdCwgU09BZHVyKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgICBtZWFuID0gbWVhbihJUklfc2ltKSwKICAgICAgICBzZCA9IHNkKElSSV9zaW0pCiAgICApKQpgYGAKCiMgUGxvdHRpbmcKCi0gV2Ugd2FudCBub3cgdG8gcHJvZHVjZSBhIGdyYXBoIHNob3dpbmcgdGhlIGhpc3RvZ3JhbXMgb2YgdGhlIG9ic2VydmVkIGBJUklgIGNvbHVtbiB1c2luZyBgZ2dwbG90MmAuIAogICAgLSBDcmVhdGUgYSBgZ2dwbG90YCB1c2luZyB0aGUgYGRhdGFjbGVhbmAgZGF0YXNldAogICAgLSBTZXQgdGhlIGFlc3RoZXRpY3MgdG8gYHggPSBJUklgCiAgICAtIENyZWF0ZSB0aGUgaGlzdG9ncmFtcyB1c2luZyBgZ2VvbV9oaXN0b2dyYW0oKWB7LlJ9LCB3aXRoIGEgZmlsbCBjb2xvciBkZXBlbmRpbmcgb24gYFNPQWR1cmAKICAgIC0gQXJyYW5nZSB0aGUgcGxvdHMgb24gYSBncmlkIGRlcGVuZGluZyBvbiB0aGUgYFN1YmplY3RgIGNvbHVtbiB1c2luZyBgZmFjZXRfd3JhcCgpYHsuUn0KICAgIC0gQWRkIGEgdmVydGljYWwgbGlnbiBtYXJraW5nIHRoZSBhdmVyYWdlIHZhbHVlIGZvciBlYWNoIHN1YmplY3QgdXNpbmcgYGdlb21fdmxpbmUoKWB7LlJ9LiBUaGUgZGF0YSBmb3IgdGhlc2UgbGluZXMgYXJlIHN0b3JlZCBpbiB0aGUgYHN0YXRzX29ic2AgZGF0YXNldC4KICAgIC0gUGxheSB3aXRoIHRoZSB0aGVtZSBhbmQgb3RoZXIgZ2dwbG90IGNvbW1hbmRzIHRvIG1ha2UgdGhlIHBsb3QgbG9vayBsaWtlIHRoZSBvbmUgYmVsb3cKICAgICAgICAtIFRyeSBwbG90dGluZyBhIG5vcm1hbGl6ZWQgaGlzdG9ncmFtIGJ5IFtsb29raW5nIHVwIG9uIHRoZSBJbnRlcm5ldCBob3cgdG8gZG8gdGhpc10oaHR0cHM6Ly93d3cuZ29vZ2xlLmZyL3NlYXJjaD9zb3VyY2U9aHAmZWk9ZzBNTFhlR3dLTkxQZ3dlVjA0LUlCQSZxPWdncGxvdCtub3JtYWxpemVkK2hpc3RvZ3JhbSZvcT1nZ3Bsb3Qrbm9ybWFsaXplZCtoaXN0b2dyYW0pCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmRhdGFjbGVhbiAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBJUkksIGZpbGwgPSBmYWN0b3IoU09BZHVyKSkpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gc3RhdChjb3VudCAvIHN1bShjb3VudCkpKSwgYmlucz0yMCkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkrCiAgICBmYWNldF93cmFwKH4gcGFzdGUoIlN1YmplY3QiLCBTdWJqZWN0KSkgKwogICAgZ2VvbV92bGluZShkYXRhID0gc3RhdHNfb2JzLCBhZXMoeGludGVyY2VwdCA9IG1lYW4sIGNvbG9yPWZhY3RvcihTT0FkdXIpKSwgbHR5ID0gMiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTIwMDAsMjAwMCkpKwogICAgbGFicyh0aXRsZSA9ICJPYnNlcnZhdGlvbnMiLAogICAgICAgICB4ID0gIklSSSBbbXNdIiwKICAgICAgICAgeSA9ICJPY2N1cmVuY2UiLCAKICAgICAgICAgZmlsbD0iU09BIFttc10iKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG91ciA9IE5BKSwKICAgICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSkpCmBgYAoKLSBMZXQncyBkbyB0aGUgc2FtZSBmb3IgdGhlIHNpbXVsYXRlZCBkYXRhc2V0LiBJdCBzaG91bGQgbG9vayBsaWtlIHRoaXM6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CklSSV9zaW0gJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gSVJJX3NpbSwgZmlsbCA9IGZhY3RvcihTT0FkdXIsIGxldmVscz1jKDE1LCA2NSwgMjUwKSkpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IHN0YXQoY291bnQgLyBzdW0oY291bnQpKSksIGJpbnMgPSAyMCkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogICAgZmFjZXRfd3JhcCh+IHBhc3RlKCJTdWJqZWN0IiwgU3ViamVjdCkpICsKICAgIGdlb21fdmxpbmUoZGF0YSA9IHN0YXRzX3NpbSwgYWVzKHhpbnRlcmNlcHQgPSBtZWFuLCBjb2xvcj1mYWN0b3IoU09BZHVyKSksIGx0eSA9IDIsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0yMDAwLDIwMDApKSsKICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiU2ltdWxhdGlvbnMiLAogICAgICAgIHggPSAiSVJJIFttc10iLAogICAgICAgIHkgPSAiT2NjdXJlbmNlIiwKICAgICAgICBmaWxsID0gIlNPQSBbbXNdIgogICAgKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG91ciA9IE5BKSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKQogICAgKQpgYGA=