library(tidyr)
Warning messages:
1: The closing backticks on line 162 ("````") in morrisonkondaurova09-explorations-ky.Rmd do not match the opening backticks "```" on line 153. You are recommended to fix either the opening or closing delimiter of the code chunk to use exactly the same numbers of backticks and same level of indentation (or blockquote). 
2: The closing backticks on line 162 ("````") in morrisonkondaurova09-explorations-ky.Rmd do not match the opening backticks "```" on line 153. You are recommended to fix either the opening or closing delimiter of the code chunk to use exactly the same numbers of backticks and same level of indentation (or blockquote). 
3: The closing backticks on line 162 ("````") in morrisonkondaurova09-explorations-ky.Rmd do not match the opening backticks "```" on line 153. You are recommended to fix either the opening or closing delimiter of the code chunk to use exactly the same numbers of backticks and same level of indentation (or blockquote). 
library(ggplot2)
library(dplyr)

This code explores Morrison and Kondaurova (2009)’s methods paper using the data available from Morrison’s website.

The introduction of the paper says:

Experiment 1: First-language (L1) English listeners, L1-Spanish L2-English listeners, and L1-Russian L2-English listeners classified vowels from an English /i/-/ɪ/ continuum. The continuum consisted of a 99-point two-dimensional grid of linear-predictive-coding resynthesized /bVt/ tokens, in which the vowel duration ranged from 35 to 275 ms in 30 ms steps, and the vowel formant center frequencies ranged from F1=458Hz, F2=1876Hz, and F3=2523Hz to F1=326Hz, F2=2056Hz, and F3=2943Hz, respectively, in equal mel steps. The grid was labeled using reference numbers where duration and spectral values of 1 refer to the most /i/-like properties (long duration, and low F1 and high F2) and duration and spectral values of 9 refer to the most /ɪ/-like properties (short duration, and high F1 and low F2). The 81 stimuli were each presented ten times in random order and on each trial the listener classified the stimulus as either English /bit/ or /bɪt/ (there were a total of 16 L1-English listeners, 18 L1-Spanish listeners, and 19 L1-Russian listeners).

Read in data and label

dat.eng <- read.table('data/data_English.txt', sep = '\t')
dat.rus <- read.table('data/data_Russian.txt', sep = '\t')
dat.span <- read.table('data/data_Spanish.txt', sep = '\t')

Unfortunately I’m not sure where to find the information about what the columns mean but we can reverse-engineer:


summary(dat.eng)
       V1              V2          V3          V4               V5        
 Min.   : 1.00   Min.   :0   Min.   :0   Min.   : 0.000   Min.   : 0.000  
 1st Qu.: 4.75   1st Qu.:2   1st Qu.:2   1st Qu.: 0.000   1st Qu.: 0.000  
 Median : 8.50   Median :4   Median :4   Median : 5.000   Median : 5.000  
 Mean   : 8.50   Mean   :4   Mean   :4   Mean   : 4.916   Mean   : 5.084  
 3rd Qu.:12.25   3rd Qu.:6   3rd Qu.:6   3rd Qu.:10.000   3rd Qu.:10.000  
 Max.   :16.00   Max.   :8   Max.   :8   Max.   :10.000   Max.   :10.000  
apply(dat.eng, 2, unique)
$V1
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16

$V2
[1] 0 1 2 3 4 5 6 7 8

$V3
[1] 0 1 2 3 4 5 6 7 8

$V4
 [1]  2  4  1  3  8  5  0  7  9  6 10

$V5
 [1]  8  6  9  7  2  5 10  3  1  4  0

So the unique values in each column in the English data are: - Col 1: 1-16, in unit increments (must be listener id) - Col 2: 0-8, in unit increments - Col 3: 0-8, in unit increments - Col 4: 0-10, in unit increments - Col 5: 0-10, in unit increments

And in Logistic_Regression_input_arguments.txt it says:

stimcols {‘listener’ ‘spec’ ‘dur’} respcols {‘/I/’ ‘/i/’}

So maybe column 2 is the spectral steps, column 3 is the duration steps, and column 4 and 5 give how many times listener responded /I/ and /i/, respectively. Unfortunately, we don’t have trial-by-trial data here (but we could reconstruct that, except missing block/repetition number).

So let’s label the data frame accordingly and write a function to do this for each of the data sets.


load.label.dat <- function(filename, directory = 'data/') {
  filepath = paste(directory, filename, sep = '')
  dat = read.table(filepath, sep = '\t')
  names(dat) = c("listener", "step.spec", "step.dur", "num.ih", "num.iy")
  dat$listener = as.factor(dat$listener)
  return(dat)
}

and process each data set this way:


dat.eng <- load.label.dat("data_English.txt")
dat.rus <- load.label.dat("data_Russian.txt")
dat.span <- load.label.dat("data_Spanish.txt")

str(dat.eng)
'data.frame':   1296 obs. of  5 variables:
 $ listener : Factor w/ 16 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ step.spec: int  0 0 0 0 0 0 0 0 0 1 ...
 $ step.dur : int  0 1 2 3 4 5 6 7 8 0 ...
 $ num.ih   : int  2 4 1 1 2 4 3 8 5 0 ...
 $ num.iy   : int  8 6 9 9 8 6 7 2 5 10 ...

Exploratory plots

We can make plots by listener for proportion of /ih/ responses depending on one kind of step, aggregated over the other kind of step, like in Figure 1.

Here we aggregate over spectral steps to get by-listener responses for each duration step.


dat.span %>%
  group_by(listener,step.dur) %>%
  summarize(num.ih = sum(num.ih),
            num.iy = sum(num.iy)) %>%
  mutate(prop.ih = num.ih/(num.ih + num.iy)) -> dat.span.by.dur
`summarise()` has grouped output by 'listener'. You can override using the `.groups` argument.

ggplot(data = dat.span.by.dur, aes(x = step.dur, y = prop.ih, group = listener, color = listener)) + geom_line() + ylim(0,1.0) + theme(aspect.ratio=1)

We can also plot a subplot for each individual listener:


ggplot(data = dat.span.by.dur, aes(x = step.dur, y = prop.ih)) + geom_line() + ylim(0,1.0) + theme(aspect.ratio=1) + facet_wrap(~listener)

Endpoint calculations

In Ref. 3, in Experiment 1, the duration endpoint-difference scores were calculated as the proportion of /ɪ/ responses for all stimuli with duration value of 9 minus the proportion of /ɪ/ responses for all stimuli with duration value of 1. Likewise the spectral endpoint-difference scores were calculated as the difference in the proportion of /ɪ/ responses at the two spectral extremes of the stimulus set.

So we could compute endpoint difference scores with a function like this. Note that we must hold the step continuum over a single variable—either spectral quality or duration, but not both—basically we just drop the information about the other dimension. Or we could set one of the variables to some constant, e.g., let’s compute endpoint difference scores for spectral quality, with duration held constant at step 5.


dat.span.by.dur %>%
  dplyr::select(listener,step.dur,prop.ih) %>%
  filter(step.dur %in% c(0, 8)) %>%
  pivot_wider(names_from = step.dur, names_prefix = "prop.ih.", values_from = prop.ih) %>%
  mutate(endpoint_diff = prop.ih.8 - prop.ih.0)
NA
NA

You can write a function to generalize the computations, as I started to do below.


# compute.endpoint.diff <- function(data = dat.span, step = "step.spec") {
#   require(dplyr)
#   
#   dat.by.step <- dat %>%
#     group_by(listener,.data[[step]]) %>%
#     summarize(num.ih = sum(num.ih),
#             num.iy = sum(num.iy)) %>%
#     mutate(prop.ih = num.ih/(num.ih + num.iy)) 
#   
#   dat.by.step %>%
    

Linear discriminant analysis

The authors point out that data from an identification task is not normally distributed.

Here’s what two normal distributions over steps might look like:


norm_lower = rnorm(n = 1000, mean = 2, sd = 2)
norm_higher = rnorm(n = 1000, mean = 5, sd = 2)

plot(density(norm_lower))
lines(density(norm_higher))

NA
NA

Compare to the sigmoidal identification curves:


sigmoid = function(x) {
   1 / (1 + exp(-x))
}

x1 <- seq(-5, 5, 0.1)
plot(x1, sigmoid(x1),pch = '.')
points(x1+2, sigmoid(x1), pch = '.')

Why do the authors say that LDA assumes a normal distribution? This comes from thinking about LDA as a special case of Bayes classification….

LS0tCnRpdGxlOiAiTW9ycmlzb24gYW5kIEtvbmRhdXJvdmEgKDIwMDkpIGludmVzdGlnYXRpb25zIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBpbXBvcnQtbGlic30KbGlicmFyeSh0aWR5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKClRoaXMgY29kZSBleHBsb3JlcyBbTW9ycmlzb24gYW5kIEtvbmRhdXJvdmEgKDIwMDkpJ3MgbWV0aG9kcyBwYXBlcl0oaHR0cHM6Ly9hc2Euc2NpdGF0aW9uLm9yZy9kb2kvMTAuMTEyMS8xLjMyMTY5MTcpIHVzaW5nIFt0aGUgZGF0YSBhdmFpbGFibGUgZnJvbSBNb3JyaXNvbidzIHdlYnNpdGVdKGh0dHBzOi8vZ2VvZmYtbW9ycmlzb24ubmV0LyNMb2dSZWcyMDA5KS4KClRoZSBpbnRyb2R1Y3Rpb24gb2YgdGhlIHBhcGVyIHNheXM6Cgo+IEV4cGVyaW1lbnQgMTogRmlyc3QtbGFuZ3VhZ2UgKEwxKSBFbmdsaXNoIGxpc3RlbmVycywgTDEtU3BhbmlzaCBMMi1FbmdsaXNoIGxpc3RlbmVycywgYW5kIEwxLVJ1c3NpYW4gTDItRW5nbGlzaCBsaXN0ZW5lcnMgY2xhc3NpZmllZCB2b3dlbHMgZnJvbSBhbiBFbmdsaXNoIC9pLy0vyaovIGNvbnRpbnV1bS4gVGhlIGNvbnRpbnV1bSBjb25zaXN0ZWQgb2YgYSA5OS1wb2ludCB0d28tZGltZW5zaW9uYWwgZ3JpZCBvZiBsaW5lYXItcHJlZGljdGl2ZS1jb2RpbmcgcmVzeW50aGVzaXplZCAvYlZ0LyB0b2tlbnMsIGluIHdoaWNoIHRoZSB2b3dlbCBkdXJhdGlvbiByYW5nZWQgZnJvbSAzNSB0byAyNzUgbXMgaW4gMzAgbXMgc3RlcHMsIGFuZCB0aGUgdm93ZWwgZm9ybWFudCBjZW50ZXIgZnJlcXVlbmNpZXMgcmFuZ2VkIGZyb20gRjE9NDU4SHosIEYyPTE4NzZIeiwgYW5kIEYzPTI1MjNIeiB0byBGMT0zMjZIeiwgRjI9MjA1Nkh6LCBhbmQgRjM9Mjk0M0h6LCByZXNwZWN0aXZlbHksIGluIGVxdWFsIG1lbCBzdGVwcy4gVGhlIGdyaWQgd2FzIGxhYmVsZWQgdXNpbmcgcmVmZXJlbmNlIG51bWJlcnMgd2hlcmUgZHVyYXRpb24gYW5kIHNwZWN0cmFsIHZhbHVlcyBvZiAxIHJlZmVyIHRvIHRoZSBtb3N0IC9pLy1saWtlIHByb3BlcnRpZXMgKGxvbmcgZHVyYXRpb24sIGFuZCBsb3cgRjEgYW5kIGhpZ2ggRjIpIGFuZCBkdXJhdGlvbiBhbmQgc3BlY3RyYWwgdmFsdWVzIG9mIDkgcmVmZXIgdG8gdGhlIG1vc3QgL8mqLy1saWtlIHByb3BlcnRpZXMgKHNob3J0IGR1cmF0aW9uLCBhbmQgaGlnaCBGMSBhbmQgbG93IEYyKS4gVGhlIDgxIHN0aW11bGkgd2VyZSBlYWNoIHByZXNlbnRlZCB0ZW4gdGltZXMgaW4gcmFuZG9tIG9yZGVyIGFuZCBvbiBlYWNoIHRyaWFsIHRoZSBsaXN0ZW5lciBjbGFzc2lmaWVkIHRoZSBzdGltdWx1cyBhcyBlaXRoZXIgRW5nbGlzaCAvYml0LyBvciAvYsmqdC8gKHRoZXJlIHdlcmUgYSB0b3RhbCBvZiAxNiBMMS1FbmdsaXNoIGxpc3RlbmVycywgMTggTDEtU3BhbmlzaCBsaXN0ZW5lcnMsIGFuZCAxOSBMMS1SdXNzaWFuIGxpc3RlbmVycykuCgojIFJlYWQgaW4gZGF0YSBhbmQgbGFiZWwKCmBgYHtyIHJlYWQtaW59CmRhdC5lbmcgPC0gcmVhZC50YWJsZSgnZGF0YS9kYXRhX0VuZ2xpc2gudHh0Jywgc2VwID0gJ1x0JykKZGF0LnJ1cyA8LSByZWFkLnRhYmxlKCdkYXRhL2RhdGFfUnVzc2lhbi50eHQnLCBzZXAgPSAnXHQnKQpkYXQuc3BhbiA8LSByZWFkLnRhYmxlKCdkYXRhL2RhdGFfU3BhbmlzaC50eHQnLCBzZXAgPSAnXHQnKQoKYGBgCgpVbmZvcnR1bmF0ZWx5IEknbSBub3Qgc3VyZSB3aGVyZSB0byBmaW5kIHRoZSBpbmZvcm1hdGlvbiBhYm91dCB3aGF0IHRoZSBjb2x1bW5zIG1lYW4gYnV0IHdlIGNhbiByZXZlcnNlLWVuZ2luZWVyOgoKYGBge3IgbGFiZWwtY29sc30KCnN1bW1hcnkoZGF0LmVuZykKYXBwbHkoZGF0LmVuZywgMiwgdW5pcXVlKQoKYGBgCgpTbyB0aGUgdW5pcXVlIHZhbHVlcyBpbiBlYWNoIGNvbHVtbiBpbiB0aGUgRW5nbGlzaCBkYXRhIGFyZToKLSBDb2wgMTogMS0xNiwgaW4gdW5pdCBpbmNyZW1lbnRzIChtdXN0IGJlIGxpc3RlbmVyIGlkKQotIENvbCAyOiAwLTgsIGluIHVuaXQgaW5jcmVtZW50cwotIENvbCAzOiAwLTgsIGluIHVuaXQgaW5jcmVtZW50cwotIENvbCA0OiAwLTEwLCBpbiB1bml0IGluY3JlbWVudHMKLSBDb2wgNTogMC0xMCwgaW4gdW5pdCBpbmNyZW1lbnRzCgpBbmQgaW4gYExvZ2lzdGljX1JlZ3Jlc3Npb25faW5wdXRfYXJndW1lbnRzLnR4dGAgaXQgc2F5czoKCj4gc3RpbWNvbHMgICAgICAgIHsnbGlzdGVuZXInICdzcGVjJyAnZHVyJ30KcmVzcGNvbHMgICAgICAgIHsnL0kvJyAnL2kvJ30KClNvIG1heWJlIGNvbHVtbiAyIGlzIHRoZSBzcGVjdHJhbCBzdGVwcywgY29sdW1uIDMgaXMgdGhlIGR1cmF0aW9uIHN0ZXBzLCBhbmQgY29sdW1uIDQgYW5kIDUgZ2l2ZSBob3cgbWFueSB0aW1lcyBsaXN0ZW5lciByZXNwb25kZWQgL0kvIGFuZCAvaS8sIHJlc3BlY3RpdmVseS4gIFVuZm9ydHVuYXRlbHksIHdlIGRvbid0IGhhdmUgdHJpYWwtYnktdHJpYWwgZGF0YSBoZXJlIChidXQgd2UgY291bGQgcmVjb25zdHJ1Y3QgdGhhdCwgZXhjZXB0IG1pc3NpbmcgYmxvY2svcmVwZXRpdGlvbiBudW1iZXIpLgoKU28gbGV0J3MgbGFiZWwgdGhlIGRhdGEgZnJhbWUgYWNjb3JkaW5nbHkgYW5kIHdyaXRlIGEgZnVuY3Rpb24gdG8gZG8gdGhpcyBmb3IgZWFjaCBvZiB0aGUgZGF0YSBzZXRzLgoKYGBge3IgZnhuLWxvYWQtbGFiZWwtZGF0YX0KCmxvYWQubGFiZWwuZGF0IDwtIGZ1bmN0aW9uKGZpbGVuYW1lLCBkaXJlY3RvcnkgPSAnZGF0YS8nKSB7CiAgZmlsZXBhdGggPSBwYXN0ZShkaXJlY3RvcnksIGZpbGVuYW1lLCBzZXAgPSAnJykKICBkYXQgPSByZWFkLnRhYmxlKGZpbGVwYXRoLCBzZXAgPSAnXHQnKQogIG5hbWVzKGRhdCkgPSBjKCJsaXN0ZW5lciIsICJzdGVwLnNwZWMiLCAic3RlcC5kdXIiLCAibnVtLmloIiwgIm51bS5peSIpCiAgZGF0JGxpc3RlbmVyID0gYXMuZmFjdG9yKGRhdCRsaXN0ZW5lcikKICByZXR1cm4oZGF0KQp9CmBgYAoKYW5kIHByb2Nlc3MgZWFjaCBkYXRhIHNldCB0aGlzIHdheToKCmBgYHtyfQoKZGF0LmVuZyA8LSBsb2FkLmxhYmVsLmRhdCgiZGF0YV9FbmdsaXNoLnR4dCIpCmRhdC5ydXMgPC0gbG9hZC5sYWJlbC5kYXQoImRhdGFfUnVzc2lhbi50eHQiKQpkYXQuc3BhbiA8LSBsb2FkLmxhYmVsLmRhdCgiZGF0YV9TcGFuaXNoLnR4dCIpCgpzdHIoZGF0LmVuZykKYGBgCgojIEV4cGxvcmF0b3J5IHBsb3RzCgpXZSBjYW4gbWFrZSBwbG90cyBieSBsaXN0ZW5lciBmb3IgcHJvcG9ydGlvbiBvZiAvaWgvIHJlc3BvbnNlcyBkZXBlbmRpbmcgb24gb25lIGtpbmQgb2Ygc3RlcCwgYWdncmVnYXRlZCBvdmVyIHRoZSBvdGhlciBraW5kIG9mIHN0ZXAsIGxpa2UgaW4gRmlndXJlIDEuCgpIZXJlIHdlIGFnZ3JlZ2F0ZSBvdmVyIHNwZWN0cmFsIHN0ZXBzIHRvIGdldCBieS1saXN0ZW5lciByZXNwb25zZXMgZm9yIGVhY2ggZHVyYXRpb24gc3RlcC4KCmBgYHtyIGRhdC1pZC1jdXJ2ZXMtYnktc3BlY30KCmRhdC5zcGFuICU+JQogIGdyb3VwX2J5KGxpc3RlbmVyLHN0ZXAuZHVyKSAlPiUKICBzdW1tYXJpemUobnVtLmloID0gc3VtKG51bS5paCksCiAgICAgICAgICAgIG51bS5peSA9IHN1bShudW0uaXkpKSAlPiUKICBtdXRhdGUocHJvcC5paCA9IG51bS5paC8obnVtLmloICsgbnVtLml5KSkgLT4gZGF0LnNwYW4uYnkuZHVyCgoKYGBgCgoKCgoKYGBge3IgcGxvdC1pZC1jdXJ2ZXMtZHVyfQoKZ2dwbG90KGRhdGEgPSBkYXQuc3Bhbi5ieS5kdXIsIGFlcyh4ID0gc3RlcC5kdXIsIHkgPSBwcm9wLmloLCBncm91cCA9IGxpc3RlbmVyLCBjb2xvciA9IGxpc3RlbmVyKSkgKyBnZW9tX2xpbmUoKSArIHlsaW0oMCwxLjApICsgdGhlbWUoYXNwZWN0LnJhdGlvPTEpCgpgYGAKCldlIGNhbiBhbHNvIHBsb3QgYSBzdWJwbG90IGZvciBlYWNoIGluZGl2aWR1YWwgbGlzdGVuZXI6CgpgYGB7ciBwbG90LWlkLWN1cnZlcy1kdXItc3Vian0KCmdncGxvdChkYXRhID0gZGF0LnNwYW4uYnkuZHVyLCBhZXMoeCA9IHN0ZXAuZHVyLCB5ID0gcHJvcC5paCkpICsgZ2VvbV9saW5lKCkgKyB5bGltKDAsMS4wKSArIHRoZW1lKGFzcGVjdC5yYXRpbz0xKSArIGZhY2V0X3dyYXAofmxpc3RlbmVyKQoKYGBgCgoKIyBFbmRwb2ludCBjYWxjdWxhdGlvbnMKCj4gSW4gUmVmLiAzLCBpbiBFeHBlcmltZW50IDEsIHRoZSBkdXJhdGlvbiBlbmRwb2ludC1kaWZmZXJlbmNlIHNjb3JlcyB3ZXJlIGNhbGN1bGF0ZWQgYXMgdGhlIHByb3BvcnRpb24gb2YgL8mqLyByZXNwb25zZXMgZm9yIGFsbCBzdGltdWxpIHdpdGggZHVyYXRpb24gdmFsdWUgb2YgOSBtaW51cyB0aGUgcHJvcG9ydGlvbiBvZiAvyaovIHJlc3BvbnNlcyBmb3IgYWxsIHN0aW11bGkgd2l0aCBkdXJhdGlvbiB2YWx1ZSBvZiAxLiBMaWtld2lzZSB0aGUgc3BlY3RyYWwgZW5kcG9pbnQtZGlmZmVyZW5jZSBzY29yZXMgd2VyZSBjYWxjdWxhdGVkIGFzIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBwcm9wb3J0aW9uIG9mIC/Jqi8gcmVzcG9uc2VzIGF0IHRoZSB0d28gc3BlY3RyYWwgZXh0cmVtZXMgb2YgdGhlIHN0aW11bHVzIHNldC4KClNvIHdlIGNvdWxkIGNvbXB1dGUgZW5kcG9pbnQgZGlmZmVyZW5jZSBzY29yZXMgd2l0aCBhIGZ1bmN0aW9uIGxpa2UgdGhpcy4gTm90ZSB0aGF0IHdlIG11c3QgaG9sZCB0aGUgc3RlcCBjb250aW51dW0gb3ZlciBhIHNpbmdsZSB2YXJpYWJsZS0tLWVpdGhlciBzcGVjdHJhbCBxdWFsaXR5IG9yIGR1cmF0aW9uLCBidXQgbm90IGJvdGgtLS1iYXNpY2FsbHkgd2UganVzdCBkcm9wIHRoZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgb3RoZXIgZGltZW5zaW9uLiBPciB3ZSBjb3VsZCBzZXQgb25lIG9mIHRoZSB2YXJpYWJsZXMgdG8gc29tZSBjb25zdGFudCwgZS5nLiwgbGV0J3MgY29tcHV0ZSBlbmRwb2ludCBkaWZmZXJlbmNlIHNjb3JlcyBmb3Igc3BlY3RyYWwgcXVhbGl0eSwgd2l0aCBkdXJhdGlvbiBoZWxkIGNvbnN0YW50IGF0IHN0ZXAgNS4KCmBgYHtyIGV4YW1wbGUtY29tcHV0ZS1lbmRwb2ludC1kaWZmfQoKZGF0LnNwYW4uYnkuZHVyICU+JQogIGRwbHlyOjpzZWxlY3QobGlzdGVuZXIsc3RlcC5kdXIscHJvcC5paCkgJT4lCiAgZmlsdGVyKHN0ZXAuZHVyICVpbiUgYygwLCA4KSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0ZXAuZHVyLCBuYW1lc19wcmVmaXggPSAicHJvcC5paC4iLCB2YWx1ZXNfZnJvbSA9IHByb3AuaWgpICU+JQogIG11dGF0ZShlbmRwb2ludF9kaWZmID0gcHJvcC5paC44IC0gcHJvcC5paC4wKQoKCmBgYAoKWW91IGNhbiB3cml0ZSBhIGZ1bmN0aW9uIHRvIGdlbmVyYWxpemUgdGhlIGNvbXB1dGF0aW9ucywgYXMgSSBzdGFydGVkIHRvIGRvIGJlbG93LgoKYGBge3IgZnhuLWNvbXB1dGUtZW5kcG9pbnQtZGlmZn0KCiMgY29tcHV0ZS5lbmRwb2ludC5kaWZmIDwtIGZ1bmN0aW9uKGRhdGEgPSBkYXQuc3Bhbiwgc3RlcCA9ICJzdGVwLnNwZWMiKSB7CiMgICByZXF1aXJlKGRwbHlyKQojICAgCiMgICBkYXQuYnkuc3RlcCA8LSBkYXQgJT4lCiMgICAgIGdyb3VwX2J5KGxpc3RlbmVyLC5kYXRhW1tzdGVwXV0pICU+JQojICAgICBzdW1tYXJpemUobnVtLmloID0gc3VtKG51bS5paCksCiMgICAgICAgICAgICAgbnVtLml5ID0gc3VtKG51bS5peSkpICU+JQojICAgICBtdXRhdGUocHJvcC5paCA9IG51bS5paC8obnVtLmloICsgbnVtLml5KSkgCiMgICAKIyAgIGRhdC5ieS5zdGVwICU+JQogICAgCgoKYGBgCgoKIyBMaW5lYXIgZGlzY3JpbWluYW50IGFuYWx5c2lzCgpUaGUgYXV0aG9ycyBwb2ludCBvdXQgdGhhdCBkYXRhIGZyb20gYW4gaWRlbnRpZmljYXRpb24gdGFzayBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgpIZXJlJ3Mgd2hhdCB0d28gbm9ybWFsIGRpc3RyaWJ1dGlvbnMgb3ZlciBzdGVwcyBtaWdodCBsb29rIGxpa2U6CgpgYGB7cn0KCm5vcm1fbG93ZXIgPSBybm9ybShuID0gMTAwMCwgbWVhbiA9IDIsIHNkID0gMikKbm9ybV9oaWdoZXIgPSBybm9ybShuID0gMTAwMCwgbWVhbiA9IDUsIHNkID0gMikKCnBsb3QoZGVuc2l0eShub3JtX2xvd2VyKSkKbGluZXMoZGVuc2l0eShub3JtX2hpZ2hlcikpCgoKYGBgYAoKQ29tcGFyZSB0byB0aGUgc2lnbW9pZGFsIGlkZW50aWZpY2F0aW9uIGN1cnZlczoKCmBgYHtyIHBsb3Qtc2lnbW9pZHN9CgpzaWdtb2lkID0gZnVuY3Rpb24oeCkgewogICAxIC8gKDEgKyBleHAoLXgpKQp9Cgp4MSA8LSBzZXEoLTUsIDUsIDAuMSkKcGxvdCh4MSwgc2lnbW9pZCh4MSkscGNoID0gJy4nKQpwb2ludHMoeDErMiwgc2lnbW9pZCh4MSksIHBjaCA9ICcuJykKYGBgCgpXaHkgZG8gdGhlIGF1dGhvcnMgc2F5IHRoYXQgTERBIGFzc3VtZXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uPwpUaGlzIGNvbWVzIGZyb20gdGhpbmtpbmcgYWJvdXQgTERBIGFzIGEgc3BlY2lhbCBjYXNlIG9mIEJheWVzIGNsYXNzaWZpY2F0aW9uLi4uLgoKCgoKCgoKCgoKCgoKCgo=