Part 1: Data wrangling
Here we will recursively treat some x-ray diffraction (XRD) data
obtained during a high-pressure experiment at the ESRF (the synchrotron
in Grenoble). In this experiment, we measured the XRD patterns as a
function of the pressure for two samples of the same rock, nicknamed
PY02. PY02 is a pyrobitumen, i.e. some coal-like
amorphous and porous carbon structure. The goal was to measure the
bulk modulus \(\kappa\) of
this rock, defined as: \[\frac{1}{\kappa}=-\frac{1}{V}\frac{\partial
V}{\partial P}.\] The bulk modulus characterizes the volume
reduction of a sample as a function of the applied pressure, it’s unit
being a pressure unit (usually in the GPa range).
The bulk modulus is usually obtained during a high pressure
experiment thanks to the Murnaghan equation of state:
\[
\frac{V}{V_0}=\left(1+P\frac{\kappa'}{\kappa}\right)^{-1/\kappa'}
\] where \(V\) is the volume of
the unit cell, \(V_0\) the volume at
ambient pressure, and \(\kappa'\)
is the pressure derivative of \(\kappa\). In first approximation, we will
consider that \(\kappa'\) is
constant and that \(\kappa'\simeq4\).
- Define the
Murnaghan(P, K, Kp)
function that, given the pressure \(P\), the bulk modulus \(K\) and its derivative \(Kp\) (defaulting to \(Kp=4\)), returns the relative volume \(\frac{V}{V_0}\).
Murnaghan <- function(P, K, Kp=4){
(1+P*Kp/K)^(-1/Kp)
}
- Find in the
Data
folder all the
files containing the experimental diffraction patterns. They are under
the form PY02_Pxx.csv
or PY02bis_Pxx.csv
. Store this list of files in
a vector called flist
. Attention,
there are some .csv files that are note wanted in this list.
flist <- list.files(path="Data", pattern = "_P")
- Read and store the two files containing the pressures of each run,
PY02_pressures.dat
and PY02bis_pressures.dat
. Combine these two
tables into a single tidy one called Pressures
, containing three columns run
, P.kbar
and sample
(sample
being either PY02
or
PY02bis
).
PY02_P <- read_table("Data/PY02_pressures.dat")
PY02bis_P <- read_table("Data/PY02bis_pressures.dat")
PY02bis_P$sample <- "PY02bis"
PY02_P$sample <- "PY02"
Pressures <- bind_rows(PY02_P, PY02bis_P)
- Define the
norm01(x)
function
that given a vector, returns this vector normalized to [0,1]
norm01 <- function(x) {
(x-min(x))/(max(x)-min(x))
}
- Now let’s read all the data files in
flist
and store
the result in a tidy tibble
called data
with
three columns, file
, q
and int
(the data files containing the diffracted intensity as a function of the
scattering vector \(q\)). Either use
the tidyverse
-friendly version using purrr::map()
,
or look into the read_csv()
function
and notice that it can take a vector of file names as argument, and then
check the id
parameter. Add the int_n
column
containing the normalized intensity for each file
(i.e. each file’s data should be normalized to [0,1], do not
normalize the whole int
column directly, you need to use a
grouping operation).
data <- read_csv(file.path("Data", flist), id = "file", show_col_types = FALSE) %>%
mutate(file = basename(file)) %>%
group_by(file) %>%
mutate(int_n = norm01(int))
write_csv(data, "./Data/data.csv")
In case you struggled to get there, I provide the wanted
data
tibble as a csv file in the Data
folder.
Simply run:
data <- read_csv("Data/data.csv")
- Plot all the normalized diffractograms on top of each other (shifted
vertically by 1), with lines of a different color for each file. Define
nice axis labels, such as Scattering Vector q [1/Å] and
Intensity [arb. units].
data %>%
ggplot(aes(x=q, y=int_n+as.numeric(factor(file))-1, color=file))+
geom_line()+
labs(x = "Scattering Vector q [1/Å]",
y = "Intensity [arb. units]")
- We are interested in the position of the first peak around \(q=1.8\) Å\(^{-1}\). Starting from
data
,
create the pospeak
tibble that stores the position
Q
of the maximum of each diffractogram. You will do so by
recursively (use the pipe %>%
):
- Make sure to filter data for scattering vectors below 2.5 Å\(^{-1}\).
- For each file, summarize the data by creating the columns
Q
, storing the position of the maximum in
intensity
dQ
, storing the error on the value of Q
.
We will estimate it as 0.5% of the Q
values.
- Using the function
separate(column_name, c("column","name"))
,
add the columns run
and sample
that transforms
the column file
to the run number and sample name while
getting rid of the extension. Make sure the column run
contains a number (so, remove the string “P”).
- Finally, order the tibble by ascending run for each sample
pospeak <- data %>%
filter(q<2.5) %>%
group_by(file) %>%
summarise(Q = q[which.max(int)],
dQ = Q*0.005) %>%
separate(file, c("sample","run",NA)) %>%
mutate(run=as.numeric(gsub("P","",run))) %>%
arrange(sample, run)
- Now join this table together with the
Pressures
one in
order to get the sample names and pressures in this table. Finally, add
the new column P
containing the pressure in GPa (1 GPa = 10
kbar).
pospeak <- pospeak %>%
inner_join(Pressures) %>%
mutate(P=P.kbar/10)
write_csv(pospeak, "./Data/pospeak.csv")
In case you struggled to get there, I provide the wanted
pospeak
tibble as a csv file in the Data
folder. Simply run:
pospeak <- read_csv("Data/pospeak.csv")
Part 2: Fitting
- We now need to estimate the volume variation and the error on its
measurement. Normally, we would need to get the crystal lattice volume,
but as mentioned earlier, the PY02 sample is an amorphous rock, and thus
it has no crystalline order. In first approximation, we can consider
that the volume reduction is linked to the inter-atomic distance
reduction through: \[
\frac{V}{V_0}=\left(\frac{r}{r_0}\right)^3,
\] where \(r\) is the most
representative inter-atomic distance (i.e. the one whose
Fourier transform will have the highest intensity), the scattering
vector \(q\) being linked to the
distance \(r\) by \(q=2\pi/r\). In our case, \(r \simeq 2\pi/1.8\simeq 3.5\) Å is the
distance between two graphitic-like planes. Using this, add to the
pospeak
tibble the columns VV0
and
dVV0
containing, respectively, the volume reduction and an
estimation of its measurement error.
- Make sure that the
V0
part refers to the value at
ambient pressure (i.e. \(P=0\)) of the corresponding
sample.
- Make sure you propagate the errors in a proper way.
pospeakVV0 <- pospeak %>%
group_by(sample) %>%
mutate(VV0=(Q[P==0]/Q)^3,
dVV0=3*VV0*sqrt((dQ[P==0]/Q[P==0])^2 + (dQ/Q)^2))
write_csv(pospeakVV0, "./Data/pospeakVV0.csv")
In case you struggled to get there, I provide the wanted
pospeakVV0
tibble as a csv file in the Data
folder. Simply run:
pospeakVV0 <- read_csv("Data/pospeakVV0.csv")
- Using
nls()
, fit the
experimental data of the volume relative variation with the Murnaghan
equation of state. Make sure to use the proper weighing (\(1/\sigma^2_{V/V_0}\)). We fix the
derivative value at \(Kp=4\). The bulk
modulus \(K\) should be of the order of
a few GPa, so work with pressures in GPa.
fit <- nls(data = pospeakVV0,
VV0 ~ Murnaghan(P, K),
start = list(K=1),
weights = 1/dVV0^2)
- Store the resulting values of \(K\)
and it’s fitting standard error \(dK\)
in two variables. Round the values to the appropriate floating number.
You may use the
broom::tidy()
function.
K <- round(tidy(fit) %>% filter(term=="K") %>% .$estimate, 1)
dK <- round(tidy(fit) %>% filter(term=="K") %>% .$std.error, 1)
summary(fit)
##
## Formula: VV0 ~ Murnaghan(P, K)
##
## Parameters:
## Estimate Std. Error t value Pr(>|t|)
## K 11.2344 0.8975 12.52 2.42e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.52 on 15 degrees of freedom
##
## Number of iterations to convergence: 7
## Achieved convergence tolerance: 8.436e-07
- Finally, make the plot of the experimental points (a color per
sample) and the fit. Make it look like so:
pospeakVV0 %>%
ggplot(aes(x=P, y=VV0))+
geom_point(alpha=0.5, aes(color=sample))+
geom_errorbar(alpha=0.5, aes(color=sample, ymin=VV0-dVV0, ymax=VV0+dVV0))+
geom_line(aes(y=predict(fit)), color="royalblue", lty=2)+
labs(x="Pressure [GPa]",
y=expression("V/V"[0]))+
scale_color_manual(values = c("black","red"), name="Sample")+
ggtitle(paste("Murnaghan fit: K = ",K," ± ",dK," GPa"))+
theme(legend.position = c(.9,.85))
LS0tCnRpdGxlIDogIlIgRXhlcmNpc2VzIC0gQnVsayBNb2R1bHVzIgpkYXRlICA6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgdG9jICAgICAgICAgICAgOiB5ZXMKICAgICAgICB0b2NfZmxvYXQgICAgICA6IHllcwogICAgICAgIHRvY19kZXB0aCAgICAgIDogNAogICAgICAgIGhpZ2hsaWdodCAgICAgIDogdGFuZ28KICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgY29kZV9kb3dubG9hZCAgOiBUUlVFCnBhcmFtczogCiAgICBzb2x1dGlvbjoKICAgICAgICB2YWx1ZTogVFJVRQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJsb2NrcXVvdGUgewogIGJhY2tncm91bmQ6ICNFOUY5RkY7CiAgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjMDI2MDg2OwogIG1hcmdpbjogMS41ZW0gMTBweDsKICBwYWRkaW5nOiAwLjVlbSAxMHB4OwogIGZvbnQtc2l6ZTogMWVtOwp9Cjwvc3R5bGU+CgpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciJ9CmxpYnJhcnkoZG93bmxvYWR0aGlzKQpkb3dubG9hZF9saW5rKAogIGxpbmsgPSAiLi9BcmNoaXZlLnppcCIsCiAgb3V0cHV0X25hbWUgPSAiRGF0YSBGaWxlcyIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIERhdGEgRmlsZXMiLAogIGJ1dHRvbl90eXBlID0gImRlZmF1bHQiLAogIGhhc19pY29uID0gVFJVRSwKICBpY29uID0gImZhIGZhLXNhdmUiLAogIHNlbGZfY29udGFpbmVkID0gRkFMU0UKKQpgYGAKPGJyPgoKLS0tLQoKLSAqKk5vdGU6KiogQWxsIHBsb3RzIHNob3VsZCBiZSBwZXJmb3JtZWQgdXNpbmcgYGdncGxvdDJgLgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIGluY2x1ZGU9RkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2FjaGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGJyb29tKQp0aGVtZV9zZXQodGhlbWVfYncoKSkKYGBgCgotLS0KCiMgUGFydCAxOiBEYXRhIHdyYW5nbGluZyAKCkhlcmUgd2Ugd2lsbCByZWN1cnNpdmVseSB0cmVhdCBzb21lIHgtcmF5IGRpZmZyYWN0aW9uIChYUkQpIGRhdGEgb2J0YWluZWQgZHVyaW5nIGEgaGlnaC1wcmVzc3VyZSBleHBlcmltZW50IGF0IHRoZSBFU1JGICh0aGUgc3luY2hyb3Ryb24gaW4gR3Jlbm9ibGUpLiBJbiB0aGlzIGV4cGVyaW1lbnQsIHdlIG1lYXN1cmVkIHRoZSBYUkQgcGF0dGVybnMgYXMgYSBmdW5jdGlvbiBvZiB0aGUgcHJlc3N1cmUgZm9yIHR3byBzYW1wbGVzIG9mIHRoZSBzYW1lIHJvY2ssIG5pY2tuYW1lZCAqUFkwMiouIFBZMDIgaXMgYSBweXJvYml0dW1lbiwgKmkuZS4qIHNvbWUgY29hbC1saWtlIGFtb3JwaG91cyBhbmQgcG9yb3VzIGNhcmJvbiBzdHJ1Y3R1cmUuIFRoZSBnb2FsIHdhcyB0byBtZWFzdXJlIHRoZSAqYnVsayBtb2R1bHVzKiAkXGthcHBhJCBvZiB0aGlzIHJvY2ssIGRlZmluZWQgYXM6CiQkXGZyYWN7MX17XGthcHBhfT0tXGZyYWN7MX17Vn1cZnJhY3tccGFydGlhbCBWfXtccGFydGlhbCBQfS4kJApUaGUgYnVsayBtb2R1bHVzIGNoYXJhY3Rlcml6ZXMgdGhlIHZvbHVtZSByZWR1Y3Rpb24gb2YgYSBzYW1wbGUgYXMgYSBmdW5jdGlvbiBvZiB0aGUgYXBwbGllZCBwcmVzc3VyZSwgaXQncyB1bml0IGJlaW5nIGEgcHJlc3N1cmUgdW5pdCAodXN1YWxseSBpbiB0aGUgR1BhIHJhbmdlKS4KClRoZSBidWxrIG1vZHVsdXMgaXMgdXN1YWxseSBvYnRhaW5lZCBkdXJpbmcgYSBoaWdoIHByZXNzdXJlIGV4cGVyaW1lbnQgdGhhbmtzIHRvIHRoZSBNdXJuYWdoYW4gZXF1YXRpb24gb2Ygc3RhdGU6CgokJApcZnJhY3tWfXtWXzB9PVxsZWZ0KDErUFxmcmFje1xrYXBwYSd9e1xrYXBwYX1ccmlnaHQpXnstMS9ca2FwcGEnfQokJAp3aGVyZSAkViQgaXMgdGhlIHZvbHVtZSBvZiB0aGUgdW5pdCBjZWxsLCAkVl8wJCB0aGUgdm9sdW1lIGF0IGFtYmllbnQgcHJlc3N1cmUsIGFuZCAkXGthcHBhJyQgaXMgdGhlIHByZXNzdXJlIGRlcml2YXRpdmUgb2YgJFxrYXBwYSQuIEluIGZpcnN0IGFwcHJveGltYXRpb24sIHdlIHdpbGwgY29uc2lkZXIgdGhhdCAkXGthcHBhJyQgaXMgY29uc3RhbnQgYW5kIHRoYXQgJFxrYXBwYSdcc2ltZXE0JC4KCjEuIERlZmluZSB0aGUgYE11cm5hZ2hhbihQLCBLLCBLcClgey5SfSBmdW5jdGlvbiB0aGF0LCBnaXZlbiB0aGUgcHJlc3N1cmUgJFAkLCB0aGUgYnVsayBtb2R1bHVzICRLJCBhbmQgaXRzIGRlcml2YXRpdmUgJEtwJCAoZGVmYXVsdGluZyB0byAkS3A9NCQpLCByZXR1cm5zIHRoZSByZWxhdGl2ZSB2b2x1bWUgJFxmcmFje1Z9e1ZfMH0kLgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Ck11cm5hZ2hhbiA8LSBmdW5jdGlvbihQLCBLLCBLcD00KXsKICAgICgxK1AqS3AvSyleKC0xL0twKQp9CmBgYAoKMi4gRmluZCBpbiB0aGUgYERhdGFgey5SfSBmb2xkZXIgYWxsIHRoZSBmaWxlcyBjb250YWluaW5nIHRoZSBleHBlcmltZW50YWwgZGlmZnJhY3Rpb24gcGF0dGVybnMuIFRoZXkgYXJlIHVuZGVyIHRoZSBmb3JtIGBQWTAyX1B4eC5jc3Zgey5SfSBvciBgUFkwMmJpc19QeHguY3N2YHsuUn0uIFN0b3JlIHRoaXMgbGlzdCBvZiBmaWxlcyBpbiBhIHZlY3RvciBjYWxsZWQgYGZsaXN0YHsuUn0uIEF0dGVudGlvbiwgdGhlcmUgYXJlIHNvbWUgLmNzdiBmaWxlcyB0aGF0IGFyZSBub3RlIHdhbnRlZCBpbiB0aGlzIGxpc3QuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZmxpc3QgPC0gbGlzdC5maWxlcyhwYXRoPSJEYXRhIiwgcGF0dGVybiA9ICJfUCIpCmBgYAoKMy4gUmVhZCBhbmQgc3RvcmUgdGhlIHR3byBmaWxlcyBjb250YWluaW5nIHRoZSBwcmVzc3VyZXMgb2YgZWFjaCBydW4sIGBQWTAyX3ByZXNzdXJlcy5kYXRgey5SfSBhbmQgYFBZMDJiaXNfcHJlc3N1cmVzLmRhdGB7LlJ9LiBDb21iaW5lIHRoZXNlIHR3byB0YWJsZXMgaW50byBhIHNpbmdsZSB0aWR5IG9uZSBjYWxsZWQgYFByZXNzdXJlc2B7LlJ9LCBjb250YWluaW5nIHRocmVlIGNvbHVtbnMgYHJ1bmB7LlJ9LCBgUC5rYmFyYHsuUn0gYW5kIGBzYW1wbGVgey5SfSAoYHNhbXBsZWB7LlJ9IGJlaW5nIGVpdGhlciBgUFkwMmAgb3IgYFBZMDJiaXNgKS4KCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpQWTAyX1AgICAgICAgICAgIDwtIHJlYWRfdGFibGUoIkRhdGEvUFkwMl9wcmVzc3VyZXMuZGF0IikKUFkwMmJpc19QICAgICAgICA8LSByZWFkX3RhYmxlKCJEYXRhL1BZMDJiaXNfcHJlc3N1cmVzLmRhdCIpClBZMDJiaXNfUCRzYW1wbGUgPC0gIlBZMDJiaXMiClBZMDJfUCRzYW1wbGUgICAgPC0gIlBZMDIiClByZXNzdXJlcyAgICAgICAgPC0gYmluZF9yb3dzKFBZMDJfUCwgUFkwMmJpc19QKQpgYGAKCjQuIERlZmluZSB0aGUgYG5vcm0wMSh4KWB7LlJ9IGZ1bmN0aW9uIHRoYXQgZ2l2ZW4gYSB2ZWN0b3IsIHJldHVybnMgdGhpcyB2ZWN0b3Igbm9ybWFsaXplZCB0byBbMCwxXQoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm5vcm0wMSA8LSBmdW5jdGlvbih4KSB7CiAgICAoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKQp9CmBgYAoKNS4gTm93IGxldCdzIHJlYWQgYWxsIHRoZSBkYXRhIGZpbGVzIGluIGBmbGlzdGAgYW5kIHN0b3JlIHRoZSByZXN1bHQgaW4gYSB0aWR5IGB0aWJibGVgIGNhbGxlZCBgZGF0YWAgd2l0aCB0aHJlZSBjb2x1bW5zLCBgZmlsZWAsIGBxYCBhbmQgYGludGAgKHRoZSBkYXRhIGZpbGVzIGNvbnRhaW5pbmcgdGhlIGRpZmZyYWN0ZWQgaW50ZW5zaXR5IGFzIGEgZnVuY3Rpb24gb2YgdGhlIHNjYXR0ZXJpbmcgdmVjdG9yICRxJCkuIEVpdGhlciB1c2UgdGhlIGB0aWR5dmVyc2VgLWZyaWVuZGx5IHZlcnNpb24gdXNpbmcgYHB1cnJyOjptYXAoKWB7LlJ9LCBvciBsb29rIGludG8gdGhlIGByZWFkX2NzdigpYHsuUn0gZnVuY3Rpb24gYW5kIG5vdGljZSB0aGF0IGl0IGNhbiB0YWtlIGEgdmVjdG9yIG9mIGZpbGUgbmFtZXMgYXMgYXJndW1lbnQsIGFuZCB0aGVuIGNoZWNrIHRoZSBgaWRgIHBhcmFtZXRlci4gQWRkIHRoZSBgaW50X25gIGNvbHVtbiBjb250YWluaW5nIHRoZSBub3JtYWxpemVkIGludGVuc2l0eSAqKmZvciBlYWNoIGZpbGUqKiAoKmkuZS4qIGVhY2ggZmlsZSdzIGRhdGEgc2hvdWxkIGJlIG5vcm1hbGl6ZWQgdG8gWzAsMV0sIGRvIG5vdCBub3JtYWxpemUgdGhlIHdob2xlIGBpbnRgIGNvbHVtbiBkaXJlY3RseSwgeW91IG5lZWQgdG8gdXNlIGEgZ3JvdXBpbmcgb3BlcmF0aW9uKS4KCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpkYXRhIDwtIHJlYWRfY3N2KGZpbGUucGF0aCgiRGF0YSIsIGZsaXN0KSwgaWQgPSAiZmlsZSIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JSAKICAgIG11dGF0ZShmaWxlID0gYmFzZW5hbWUoZmlsZSkpICU+JSAKICAgIGdyb3VwX2J5KGZpbGUpICU+JSAKICAgIG11dGF0ZShpbnRfbiA9IG5vcm0wMShpbnQpKQp3cml0ZV9jc3YoZGF0YSwgIi4vRGF0YS9kYXRhLmNzdiIpCmBgYAoKPiBJbiBjYXNlIHlvdSBzdHJ1Z2dsZWQgdG8gZ2V0IHRoZXJlLCBJIHByb3ZpZGUgdGhlIHdhbnRlZCBgZGF0YWAgdGliYmxlIGFzIGEgY3N2IGZpbGUgaW4gdGhlIGBEYXRhYCBmb2xkZXIuIFNpbXBseSBydW46Cj4gYGBgcgo+IGRhdGEgPC0gcmVhZF9jc3YoIkRhdGEvZGF0YS5jc3YiKQo+IGBgYAoKNi4gUGxvdCBhbGwgdGhlIG5vcm1hbGl6ZWQgZGlmZnJhY3RvZ3JhbXMgb24gdG9wIG9mIGVhY2ggb3RoZXIgKHNoaWZ0ZWQgdmVydGljYWxseSBieSAxKSwgd2l0aCBsaW5lcyBvZiBhIGRpZmZlcmVudCBjb2xvciBmb3IgZWFjaCBmaWxlLiBEZWZpbmUgbmljZSBheGlzIGxhYmVscywgc3VjaCBhcyAqU2NhdHRlcmluZyBWZWN0b3IgcSBbMS/DhV0qIGFuZCAqSW50ZW5zaXR5IFthcmIuIHVuaXRzXSouCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZGF0YSAlPiUKICAgIGdncGxvdChhZXMoeD1xLCB5PWludF9uK2FzLm51bWVyaWMoZmFjdG9yKGZpbGUpKS0xLCBjb2xvcj1maWxlKSkrCiAgICAgICAgZ2VvbV9saW5lKCkrCiAgICAgICAgbGFicyh4ID0gIlNjYXR0ZXJpbmcgVmVjdG9yIHEgWzEvw4VdIiwgCiAgICAgICAgICAgICB5ID0gIkludGVuc2l0eSBbYXJiLiB1bml0c10iKQpgYGAKCjcuIFdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBwb3NpdGlvbiBvZiB0aGUgZmlyc3QgcGVhayBhcm91bmQgJHE9MS44JCDDhSReey0xfSQuIFN0YXJ0aW5nIGZyb20gYGRhdGFgLCBjcmVhdGUgdGhlIGBwb3NwZWFrYCB0aWJibGUgdGhhdCBzdG9yZXMgdGhlIHBvc2l0aW9uIGBRYCBvZiB0aGUgbWF4aW11bSBvZiBlYWNoIGRpZmZyYWN0b2dyYW0uIFlvdSB3aWxsIGRvIHNvIGJ5IHJlY3Vyc2l2ZWx5ICh1c2UgdGhlIHBpcGUgYCU+JWApOgogICAgLSBNYWtlIHN1cmUgdG8gZmlsdGVyIGRhdGEgZm9yIHNjYXR0ZXJpbmcgdmVjdG9ycyBiZWxvdyAyLjUgw4UkXnstMX0kLiAKICAgIC0gRm9yIGVhY2ggZmlsZSwgc3VtbWFyaXplIHRoZSBkYXRhIGJ5IGNyZWF0aW5nIHRoZSBjb2x1bW5zCiAgICAgICAgLSBgUWAsIHN0b3JpbmcgdGhlIHBvc2l0aW9uIG9mIHRoZSBtYXhpbXVtIGluIGludGVuc2l0eQogICAgICAgIC0gYGRRYCwgc3RvcmluZyB0aGUgZXJyb3Igb24gdGhlIHZhbHVlIG9mIGBRYC4gV2Ugd2lsbCBlc3RpbWF0ZSBpdCBhcyAwLjUlIG9mIHRoZSBgUWAgdmFsdWVzLgogICAgLSBVc2luZyB0aGUgZnVuY3Rpb24gYHNlcGFyYXRlKGNvbHVtbl9uYW1lLCBjKCJjb2x1bW4iLCJuYW1lIikpYHsuUn0sIGFkZCB0aGUgY29sdW1ucyBgcnVuYCBhbmQgYHNhbXBsZWAgdGhhdCB0cmFuc2Zvcm1zIHRoZSBjb2x1bW4gYGZpbGVgIHRvIHRoZSBydW4gbnVtYmVyIGFuZCBzYW1wbGUgbmFtZSB3aGlsZSBnZXR0aW5nIHJpZCBvZiB0aGUgZXh0ZW5zaW9uLiBNYWtlIHN1cmUgdGhlIGNvbHVtbiBgcnVuYCBjb250YWlucyBhIG51bWJlciAoc28sIHJlbW92ZSB0aGUgc3RyaW5nICJQIikuCiAgICAtIEZpbmFsbHksIG9yZGVyIHRoZSB0aWJibGUgYnkgYXNjZW5kaW5nIHJ1biBmb3IgZWFjaCBzYW1wbGUKCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwb3NwZWFrIDwtIGRhdGEgJT4lIAogICAgZmlsdGVyKHE8Mi41KSAlPiUgCiAgICBncm91cF9ieShmaWxlKSAlPiUgCiAgICBzdW1tYXJpc2UoUSAgPSBxW3doaWNoLm1heChpbnQpXSwKICAgICAgICAgICAgICBkUSA9IFEqMC4wMDUpICU+JSAKICAgIHNlcGFyYXRlKGZpbGUsIGMoInNhbXBsZSIsInJ1biIsTkEpKSAlPiUgCiAgICBtdXRhdGUocnVuPWFzLm51bWVyaWMoZ3N1YigiUCIsIiIscnVuKSkpICU+JSAKICAgIGFycmFuZ2Uoc2FtcGxlLCBydW4pCmBgYAoKOC4gTm93IGpvaW4gdGhpcyB0YWJsZSB0b2dldGhlciB3aXRoIHRoZSBgUHJlc3N1cmVzYCBvbmUgaW4gb3JkZXIgdG8gZ2V0IHRoZSBzYW1wbGUgbmFtZXMgYW5kIHByZXNzdXJlcyBpbiB0aGlzIHRhYmxlLiBGaW5hbGx5LCBhZGQgdGhlIG5ldyBjb2x1bW4gYFBgIGNvbnRhaW5pbmcgdGhlIHByZXNzdXJlIGluIEdQYSAoMSBHUGEgPSAxMCBrYmFyKS4KCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwb3NwZWFrIDwtIHBvc3BlYWsgJT4lIAogICAgaW5uZXJfam9pbihQcmVzc3VyZXMpICU+JSAKICAgIG11dGF0ZShQPVAua2Jhci8xMCkKd3JpdGVfY3N2KHBvc3BlYWssICIuL0RhdGEvcG9zcGVhay5jc3YiKQpgYGAKCj4gSW4gY2FzZSB5b3Ugc3RydWdnbGVkIHRvIGdldCB0aGVyZSwgSSBwcm92aWRlIHRoZSB3YW50ZWQgYHBvc3BlYWtgIHRpYmJsZSBhcyBhIGNzdiBmaWxlIGluIHRoZSBgRGF0YWAgZm9sZGVyLiBTaW1wbHkgcnVuOgo+IGBgYHIKPiBwb3NwZWFrIDwtIHJlYWRfY3N2KCJEYXRhL3Bvc3BlYWsuY3N2IikKPiBgYGAKCgojIFBhcnQgMjogRml0dGluZwoKCjkuIFdlIG5vdyBuZWVkIHRvIGVzdGltYXRlIHRoZSB2b2x1bWUgdmFyaWF0aW9uIGFuZCB0aGUgZXJyb3Igb24gaXRzIG1lYXN1cmVtZW50LiBOb3JtYWxseSwgd2Ugd291bGQgbmVlZCB0byBnZXQgdGhlIGNyeXN0YWwgbGF0dGljZSB2b2x1bWUsIGJ1dCBhcyBtZW50aW9uZWQgZWFybGllciwgdGhlIFBZMDIgc2FtcGxlIGlzIGFuIGFtb3JwaG91cyByb2NrLCBhbmQgdGh1cyBpdCBoYXMgbm8gY3J5c3RhbGxpbmUgb3JkZXIuIEluIGZpcnN0IGFwcHJveGltYXRpb24sIHdlIGNhbiBjb25zaWRlciB0aGF0IHRoZSB2b2x1bWUgcmVkdWN0aW9uIGlzIGxpbmtlZCB0byB0aGUgaW50ZXItYXRvbWljIGRpc3RhbmNlIHJlZHVjdGlvbiB0aHJvdWdoOgokJApcZnJhY3tWfXtWXzB9PVxsZWZ0KFxmcmFje3J9e3JfMH1ccmlnaHQpXjMsCiQkCndoZXJlICRyJCBpcyB0aGUgbW9zdCByZXByZXNlbnRhdGl2ZSBpbnRlci1hdG9taWMgZGlzdGFuY2UgKCppLmUuKiB0aGUgb25lIHdob3NlIEZvdXJpZXIgdHJhbnNmb3JtIHdpbGwgaGF2ZSB0aGUgaGlnaGVzdCBpbnRlbnNpdHkpLCB0aGUgc2NhdHRlcmluZyB2ZWN0b3IgJHEkIGJlaW5nIGxpbmtlZCB0byB0aGUgZGlzdGFuY2UgJHIkIGJ5ICRxPTJccGkvciQuIEluIG91ciBjYXNlLCAkciBcc2ltZXEgMlxwaS8xLjhcc2ltZXEgMy41JCDDhSBpcyB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gZ3JhcGhpdGljLWxpa2UgcGxhbmVzLgpVc2luZyB0aGlzLCBhZGQgdG8gdGhlIGBwb3NwZWFrYCB0aWJibGUgdGhlIGNvbHVtbnMgYFZWMGAgYW5kIGBkVlYwYCBjb250YWluaW5nLCByZXNwZWN0aXZlbHksIHRoZSB2b2x1bWUgcmVkdWN0aW9uIGFuZCBhbiBlc3RpbWF0aW9uIG9mIGl0cyBtZWFzdXJlbWVudCBlcnJvci4gCiAgICAtIE1ha2Ugc3VyZSB0aGF0IHRoZSBgVjBgIHBhcnQgcmVmZXJzIHRvIHRoZSB2YWx1ZSBhdCBhbWJpZW50IHByZXNzdXJlICgqaS5lLiogJFA9MCQpICpvZiB0aGUgY29ycmVzcG9uZGluZyBzYW1wbGUqLiAKICAgIC0gTWFrZSBzdXJlIHlvdSBwcm9wYWdhdGUgdGhlIGVycm9ycyBpbiBhIHByb3BlciB3YXkuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcG9zcGVha1ZWMCA8LSBwb3NwZWFrICU+JSAKICAgIGdyb3VwX2J5KHNhbXBsZSkgJT4lIAogICAgbXV0YXRlKFZWMD0oUVtQPT0wXS9RKV4zLAogICAgICAgICAgZFZWMD0zKlZWMCpzcXJ0KChkUVtQPT0wXS9RW1A9PTBdKV4yICsgKGRRL1EpXjIpKQp3cml0ZV9jc3YocG9zcGVha1ZWMCwgIi4vRGF0YS9wb3NwZWFrVlYwLmNzdiIpCmBgYAoKPiBJbiBjYXNlIHlvdSBzdHJ1Z2dsZWQgdG8gZ2V0IHRoZXJlLCBJIHByb3ZpZGUgdGhlIHdhbnRlZCBgcG9zcGVha1ZWMGAgdGliYmxlIGFzIGEgY3N2IGZpbGUgaW4gdGhlIGBEYXRhYCBmb2xkZXIuIFNpbXBseSBydW46Cj4gYGBgcgo+IHBvc3BlYWtWVjAgPC0gcmVhZF9jc3YoIkRhdGEvcG9zcGVha1ZWMC5jc3YiKQo+IGBgYAoKCjEwLiBVc2luZyBgbmxzKClgey5SfSwgZml0IHRoZSBleHBlcmltZW50YWwgZGF0YSBvZiB0aGUgdm9sdW1lIHJlbGF0aXZlIHZhcmlhdGlvbiB3aXRoIHRoZSBNdXJuYWdoYW4gZXF1YXRpb24gb2Ygc3RhdGUuIE1ha2Ugc3VyZSB0byB1c2UgdGhlIHByb3BlciB3ZWlnaGluZyAoJDEvXHNpZ21hXjJfe1YvVl8wfSQpLiBXZSBmaXggdGhlIGRlcml2YXRpdmUgdmFsdWUgYXQgJEtwPTQkLiBUaGUgYnVsayBtb2R1bHVzICRLJCBzaG91bGQgYmUgb2YgdGhlIG9yZGVyIG9mIGEgZmV3IEdQYSwgc28gd29yayB3aXRoIHByZXNzdXJlcyBpbiBHUGEuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZml0IDwtIG5scyhkYXRhID0gcG9zcGVha1ZWMCwKICAgICAgICAgICBWVjAgfiBNdXJuYWdoYW4oUCwgSyksCiAgICAgICAgICAgc3RhcnQgPSBsaXN0KEs9MSksCiAgICAgICAgICAgd2VpZ2h0cyA9IDEvZFZWMF4yKQpgYGAKCjExLiBTdG9yZSB0aGUgcmVzdWx0aW5nIHZhbHVlcyBvZiAkSyQgYW5kIGl0J3MgZml0dGluZyBzdGFuZGFyZCBlcnJvciAkZEskIGluIHR3byB2YXJpYWJsZXMuIFJvdW5kIHRoZSB2YWx1ZXMgdG8gdGhlIGFwcHJvcHJpYXRlIGZsb2F0aW5nIG51bWJlci4gWW91IG1heSB1c2UgdGhlIGBicm9vbTo6dGlkeSgpYHsuUn0gZnVuY3Rpb24uCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KSyAgPC0gcm91bmQodGlkeShmaXQpICU+JSBmaWx0ZXIodGVybT09IksiKSAlPiUgLiRlc3RpbWF0ZSwgMSkKZEsgPC0gcm91bmQodGlkeShmaXQpICU+JSBmaWx0ZXIodGVybT09IksiKSAlPiUgLiRzdGQuZXJyb3IsIDEpCnN1bW1hcnkoZml0KQpgYGAKCjEyLiBGaW5hbGx5LCBtYWtlIHRoZSBwbG90IG9mIHRoZSBleHBlcmltZW50YWwgcG9pbnRzIChhIGNvbG9yIHBlciBzYW1wbGUpIGFuZCB0aGUgZml0LiBNYWtlIGl0IGxvb2sgbGlrZSBzbzoKCmBgYHtyIGVjaG89cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwb3NwZWFrVlYwICU+JQogICAgZ2dwbG90KGFlcyh4PVAsIHk9VlYwKSkrCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYT0wLjUsIGFlcyhjb2xvcj1zYW1wbGUpKSsKICAgICAgICBnZW9tX2Vycm9yYmFyKGFscGhhPTAuNSwgYWVzKGNvbG9yPXNhbXBsZSwgeW1pbj1WVjAtZFZWMCwgeW1heD1WVjArZFZWMCkpKwogICAgICAgIGdlb21fbGluZShhZXMoeT1wcmVkaWN0KGZpdCkpLCBjb2xvcj0icm95YWxibHVlIiwgbHR5PTIpKwogICAgICAgIGxhYnMoeD0iUHJlc3N1cmUgW0dQYV0iLAogICAgICAgICAgICAgeT1leHByZXNzaW9uKCJWL1YiWzBdKSkrCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwicmVkIiksIG5hbWU9IlNhbXBsZSIpKwogICAgICAgIGdndGl0bGUocGFzdGUoIk11cm5hZ2hhbiBmaXQ6IEsgPSAiLEssIiDCsSAiLGRLLCIgR1BhIikpKwogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjksLjg1KSkKYGBgCgoKCg==