Load R packages

rm(list=ls())
library(ftmsRanalysis)

Sample Data

fdata <- read.delim('input/FTICR_metadata.txt')
fdata <- fdata[fdata$Horizon %in% "Mineral",]
str(fdata)
'data.frame':   16 obs. of  7 variables:
 $ Sample_ID  : chr  "H2" "H4" "H6" "H8" ...
 $ Sample.name: chr  "H2M" "H4M" "H11M" "H17M" ...
 $ Horizon    : chr  "Mineral" "Mineral" "Mineral" "Mineral" ...
 $ Treatment  : chr  "Heated" "Heated" "Heated" "Heated" ...
 $ T_H        : chr  "Mineral_Heated" "Mineral_Heated" "Mineral_Heated" "Mineral_Heated" ...
 $ Sample.ID  : chr  "BW.H.2.M" "BW.H.4.M" "BW.H.11.M" "BW.H.17.M" ...
 $ wMT        : chr  "Y" "Y" "Y" "Y" ...

FTICR MS Data

mydata = read.delim('input/FTICR_data.txt')

Expression Data

edata <- subset(mydata,select=c("Mass",fdata$Sample_ID))
str(edata)
'data.frame':   16814 obs. of  17 variables:
 $ Mass: num  98.7 98.8 98.8 98.9 99 ...
 $ H2  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H4  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H6  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H8  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H10 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H12 : num  0 1312225 0 0 0 ...
 $ H14 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H16 : num  0 0 1128302 0 0 ...
 $ C2  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C4  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C6  : num  0 0 0 1381438 0 ...
 $ C8  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C10 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C12 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C14 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C16 : num  0 0 0 0 0 0 0 0 0 0 ...

Molecular Indentification Data

emeta <- subset(mydata,select=c("Mass","C","H","O","N","C13",
                                "S","P","Error_ppm","NeutralMass"))
str(emeta)
'data.frame':   16814 obs. of  10 variables:
 $ Mass       : num  98.7 98.8 98.8 98.9 99 ...
 $ C          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ H          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ O          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ N          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ C13        : int  0 0 0 0 0 0 0 0 0 0 ...
 $ S          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ P          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Error_ppm  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ NeutralMass: num  99.7 99.8 99.8 99.9 100 ...

PeakData Object

peakObj <- as.peakData(e_data = edata,f_data = fdata,e_meta = emeta,
                       edata_cname = "Mass",fdata_cname = "Sample_ID",
                       mass_cname = "Mass",c_cname="C", h_cname="H",
                       o_cname="O", n_cname="N", s_cname="S",
                       p_cname="P", isotopic_cname = "C13",
                       isotopic_notation = "1")
peakObj
peakData object
# Peaks: 15443
# Samples: 16
Meta data columns: [Mass, C, H, O, N, C13, S, P, Error_ppm, NeutralMass, MolForm]

“Currently, any peaks that are isotopic are removed from the dataset, as available methods (e.g. Van Krevelen plot) are not applicable to these peaks.”

Elements

names(peakObj)
[1] "e_data" "f_data" "e_meta"

Calculated Molecular Formula

tail(peakObj$e_meta)

Summary

summary(peakObj)
Samples: 16
Molecules: 15443
Percent Missing: 88.566%

Plotting (Default; presence/absence)

plot(peakObj)

Preprocessing

Transforming abundance values to log2

peakObj <- edata_transform(peakObj, data_scale="log2")

plotting

plot(peakObj)

Calculating meta-data

peakObj <- compound_calcs(peakObj)
peakObj
peakData object
# Peaks: 15443
# Samples: 16
Meta data columns: [Mass, C, H, O, N, C13, S, P, Error_ppm, NeutralMass, MolForm, AI, AI_Mod, DBE, DBE_O, DBE_AI, GFE, kmass.CH2, kdefect.CH2, NOSC, OtoC_ratio, HtoC_ratio, NtoC_ratio, PtoC_ratio, NtoP_ratio]

Compounds by Elements

peakObj <- assign_elemental_composition(peakObj)
table(peakObj$e_meta[,getElCompColName(peakObj)])

   CHO   CHON  CHONP  CHONS CHONSP   CHOP   CHOS  CHOSP 
  3033   1382    212    284     79    148    483     99 

Compounds by Class

peakObj <- assign_class(peakObj, boundary_set = "bs1")
table(peakObj$e_meta[, getBS1ColName(peakObj)])

                       Amino Sugar                       Carbohydrate           Carbohydrate;Amino Sugar 
                               219                                536                                 35 
                  Cond Hydrocarbon                             Lignin                 Lignin;Amino Sugar 
                               465                               1407                                 62 
                     Lignin;Tannin                              Lipid                      Lipid;Protein 
                                 6                                420                                 17 
                             Other                            Protein                Protein;Amino Sugar 
                              1146                                785                                 21 
                    Protein;Lignin                             Tannin            Tannin;Cond Hydrocarbon 
                                 6                                416                                 22 
                 Unsat Hydrocarbon Unsat Hydrocarbon;Cond Hydrocarbon 
                               150                                  7 

*Something is going on boundary_set argument when bs2 or bs3 selected.

Filtering

Before filtering

summary(peakObj)
Samples: 16
Molecules: 15443
Percent Missing: 88.566%

After filtering

filter_obj <- mass_filter(peakObj)
plot(filter_obj, min_mass=200, max_mass=900)
peakObj <- applyFilt(filter_obj, peakObj, min_mass = 200,
                  max_mass = 900)
summary(peakObj)
Samples: 16
Molecules: 8574
Percent Missing: 83.674%
peakObj
peakData object
# Peaks: 8574
# Samples: 16
Meta data columns: [Mass, C, H, O, N, C13, S, P, Error_ppm, NeutralMass, MolForm, AI, AI_Mod, DBE, DBE_O, DBE_AI, GFE, kmass.CH2, kdefect.CH2, NOSC, OtoC_ratio, HtoC_ratio, NtoC_ratio, PtoC_ratio, NtoP_ratio, ElComposition, bs1_class]
A mass filter was applied to the data, removing Masses that had a mass less than 200 or a mass greater than 900. A total of 6869 Masses were filtered out of the dataset by this filter.

Other filters

# minimum number to be observed across all samples in order to retain the biomolecule
peakObj <- applyFilt(molecule_filter(peakObj), peakObj, min_num=2)
# remove biomolec without molecular formula; remove=NoFormula)
peakObj <- applyFilt(formula_filter(peakObj), peakObj)
summary(peakObj)
Samples: 16
Molecules: 2168
Percent Missing: 47.008%

Comparison of Experimental Groups

peakObj <- group_designation(peakObj, main_effects=c("Treatment"))
getGroupDF(peakObj)

Group-level Summary per Peak

group_summary <- summarizeGroups(peakObj, summary_functions =
                                   c("n_present", "prop_present"))
head(group_summary$e_data)

NOSC Distrubution

p <- densityPlot(peakObj, samples=FALSE, groups=c("Control","Heated"), variable="NOSC",title="Comparison of NOSC Between Experimental Plots: Mineral Horizon")
p

Compounds Distribution

byGroup <- divideByGroupComparisons(peakObj,
                                comparisons = "all")[[1]]$value

Available Summary Options

getGroupComparisonSummaryFunctionNames()
[1] "uniqueness_gtest"  "uniqueness_nsamps" "uniqueness_prop"  

Determine Unique and Common Masses/Peaks: g-test

“Use a g-test to compare two groups and determine which peaks are uniquely expressed in each group based on a p-value threshold.”

mineral_unique <- summarizeGroupComparisons(byGroup,
            summary_functions="uniqueness_gtest",
            summary_function_params=list(
                  uniqueness_gtest=list(pres_fn="nsamps",
                          pres_thresh=1, pvalue_thresh=0.1)))

head(mineral_unique$e_data)
mineral_unique
peakData object
# Peaks: 2168
# Samples: 1
Meta data columns: [Mass, C, H, O, N, C13, S, P, Error_ppm, NeutralMass, MolForm, AI, AI_Mod, DBE, DBE_O, DBE_AI, GFE, kmass.CH2, kdefect.CH2, NOSC, OtoC_ratio, HtoC_ratio, NtoC_ratio, PtoC_ratio, NtoP_ratio, ElComposition, bs1_class]
Group info: main effects=[Treatment]
A mass filter was applied to the data, removing Masses that had a mass less than 200 or a mass greater than 900. A total of 6869 Masses were filtered out of the dataset by this filter.
A molecule filter was applied to the data, removing Masses that were present in fewer than 2 samples. A total of 6096 Masses were filtered out of the dataset by this filter.
A formula filter was applied to the data, removing Masses that had NoFormula assigned. A total of 310 Masses were filtered out of the dataset by this filter.

Plotting - Van Krevelen

vanKrevelenPlot(mineral_unique, colorCName = "uniqueness_gtest")

Compounds breakdown

table(mineral_unique$e_data$uniqueness_gtest)

 Unique to Heated Unique to Control  Observed in Both 
              373                77              1718 

Determine Unique and Common Masses/Peaks: g-test

“Use number of samples for which a mass/peak is present to compare two groups and determine which peaks are uniquely expressed in each group.”

mineral_unique <- summarizeGroupComparisons(byGroup,
            summary_functions="uniqueness_nsamps",
            summary_function_params=list(
                  uniqueness_nsamps=list(pres_thresh=1, absn_thresh=0)))

head(mineral_unique$e_data)
mineral_unique
peakData object
# Peaks: 2168
# Samples: 1
Meta data columns: [Mass, C, H, O, N, C13, S, P, Error_ppm, NeutralMass, MolForm, AI, AI_Mod, DBE, DBE_O, DBE_AI, GFE, kmass.CH2, kdefect.CH2, NOSC, OtoC_ratio, HtoC_ratio, NtoC_ratio, PtoC_ratio, NtoP_ratio, ElComposition, bs1_class]
Group info: main effects=[Treatment]
A mass filter was applied to the data, removing Masses that had a mass less than 200 or a mass greater than 900. A total of 6869 Masses were filtered out of the dataset by this filter.
A molecule filter was applied to the data, removing Masses that were present in fewer than 2 samples. A total of 6096 Masses were filtered out of the dataset by this filter.
A formula filter was applied to the data, removing Masses that had NoFormula assigned. A total of 310 Masses were filtered out of the dataset by this filter.

Plotting - Van Krevelen

vanKrevelenPlot(mineral_unique, colorCName = "uniqueness_nsamps")

Compounds breakdown

table(mineral_unique$e_data$uniqueness_nsamps)

 Unique to Heated Unique to Control  Observed in Both 
              272                25              1871 
LS0tDQp0aXRsZTogIkZUSUNSLU1TOk1pbmVyYWwiDQphdXRob3I6ICJXaWxsaWFtIFJvZHJpZ3VleiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojIExvYWQgUiBwYWNrYWdlcw0KDQpgYGB7cn0NCnJtKGxpc3Q9bHMoKSkNCmxpYnJhcnkoZnRtc1JhbmFseXNpcykNCmBgYA0KDQoNCiMgU2FtcGxlIERhdGENCmBgYHtyfQ0KZmRhdGEgPC0gcmVhZC5kZWxpbSgnaW5wdXQvRlRJQ1JfbWV0YWRhdGEudHh0JykNCmZkYXRhIDwtIGZkYXRhW2ZkYXRhJEhvcml6b24gJWluJSAiTWluZXJhbCIsXQ0Kc3RyKGZkYXRhKQ0KYGBgDQoNCiMgRlRJQ1IgTVMgRGF0YQ0KYGBge3J9DQpteWRhdGEgPSByZWFkLmRlbGltKCdpbnB1dC9GVElDUl9kYXRhLnR4dCcpDQpgYGANCg0KIyBFeHByZXNzaW9uIERhdGENCg0KYGBge3J9DQplZGF0YSA8LSBzdWJzZXQobXlkYXRhLHNlbGVjdD1jKCJNYXNzIixmZGF0YSRTYW1wbGVfSUQpKQ0Kc3RyKGVkYXRhKQ0KYGBgDQoNCiMgTW9sZWN1bGFyIEluZGVudGlmaWNhdGlvbiBEYXRhDQoNCmBgYHtyfQ0KZW1ldGEgPC0gc3Vic2V0KG15ZGF0YSxzZWxlY3Q9YygiTWFzcyIsIkMiLCJIIiwiTyIsIk4iLCJDMTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUyIsIlAiLCJFcnJvcl9wcG0iLCJOZXV0cmFsTWFzcyIpKQ0Kc3RyKGVtZXRhKQ0KYGBgDQoNCg0KIyBQZWFrRGF0YSBPYmplY3QNCg0KYGBge3J9DQpwZWFrT2JqIDwtIGFzLnBlYWtEYXRhKGVfZGF0YSA9IGVkYXRhLGZfZGF0YSA9IGZkYXRhLGVfbWV0YSA9IGVtZXRhLA0KICAgICAgICAgICAgICAgICAgICAgICBlZGF0YV9jbmFtZSA9ICJNYXNzIixmZGF0YV9jbmFtZSA9ICJTYW1wbGVfSUQiLA0KICAgICAgICAgICAgICAgICAgICAgICBtYXNzX2NuYW1lID0gIk1hc3MiLGNfY25hbWU9IkMiLCBoX2NuYW1lPSJIIiwNCiAgICAgICAgICAgICAgICAgICAgICAgb19jbmFtZT0iTyIsIG5fY25hbWU9Ik4iLCBzX2NuYW1lPSJTIiwNCiAgICAgICAgICAgICAgICAgICAgICAgcF9jbmFtZT0iUCIsIGlzb3RvcGljX2NuYW1lID0gIkMxMyIsDQogICAgICAgICAgICAgICAgICAgICAgIGlzb3RvcGljX25vdGF0aW9uID0gIjEiKQ0KcGVha09iag0KYGBgDQoNCiJDdXJyZW50bHksIGFueSBwZWFrcyB0aGF0IGFyZSBpc290b3BpYyBhcmUgcmVtb3ZlZCBmcm9tIHRoZSBkYXRhc2V0LCBhcyBhdmFpbGFibGUgbWV0aG9kcyAoZS5nLiBWYW4gS3JldmVsZW4gcGxvdCkgYXJlIG5vdCBhcHBsaWNhYmxlIHRvIHRoZXNlIHBlYWtzLiINCg0KDQojIyBFbGVtZW50cw0KDQpgYGB7cn0NCm5hbWVzKHBlYWtPYmopDQpgYGANCg0KIyMgQ2FsY3VsYXRlZCBNb2xlY3VsYXIgRm9ybXVsYQ0KDQpgYGB7cn0NCnRhaWwocGVha09iaiRlX21ldGEpDQpgYGANCg0KIyMgU3VtbWFyeQ0KDQpgYGB7cn0NCnN1bW1hcnkocGVha09iaikNCmBgYA0KDQojIyBQbG90dGluZyAoRGVmYXVsdDsgcHJlc2VuY2UvYWJzZW5jZSkNCg0KYGBge3J9DQpwbG90KHBlYWtPYmopDQpgYGANCg0KIyBQcmVwcm9jZXNzaW5nDQoNClRyYW5zZm9ybWluZyBhYnVuZGFuY2UgdmFsdWVzIHRvIGxvZzINCg0KYGBge3J9DQpwZWFrT2JqIDwtIGVkYXRhX3RyYW5zZm9ybShwZWFrT2JqLCBkYXRhX3NjYWxlPSJsb2cyIikNCmBgYA0KDQpwbG90dGluZw0KDQpgYGB7cn0NCnBsb3QocGVha09iaikNCmBgYA0KDQojIENhbGN1bGF0aW5nIG1ldGEtZGF0YQ0KDQpgYGB7cn0NCnBlYWtPYmogPC0gY29tcG91bmRfY2FsY3MocGVha09iaikNCnBlYWtPYmoNCmBgYA0KDQojIyBDb21wb3VuZHMgYnkgRWxlbWVudHMNCg0KYGBge3J9DQpwZWFrT2JqIDwtIGFzc2lnbl9lbGVtZW50YWxfY29tcG9zaXRpb24ocGVha09iaikNCnRhYmxlKHBlYWtPYmokZV9tZXRhWyxnZXRFbENvbXBDb2xOYW1lKHBlYWtPYmopXSkNCmBgYA0KDQojIyBDb21wb3VuZHMgYnkgQ2xhc3MNCg0KYGBge3J9DQpwZWFrT2JqIDwtIGFzc2lnbl9jbGFzcyhwZWFrT2JqLCBib3VuZGFyeV9zZXQgPSAiYnMxIikNCnRhYmxlKHBlYWtPYmokZV9tZXRhWywgZ2V0QlMxQ29sTmFtZShwZWFrT2JqKV0pDQpgYGANCipTb21ldGhpbmcgaXMgZ29pbmcgb24gYm91bmRhcnlfc2V0IGFyZ3VtZW50IHdoZW4gYnMyIG9yIGJzMyBzZWxlY3RlZC4NCg0KIyMgRmlsdGVyaW5nDQoNCkJlZm9yZSBmaWx0ZXJpbmcNCg0KYGBge3J9DQpzdW1tYXJ5KHBlYWtPYmopDQpgYGANCg0KQWZ0ZXIgZmlsdGVyaW5nDQoNCg0KYGBge3J9DQpmaWx0ZXJfb2JqIDwtIG1hc3NfZmlsdGVyKHBlYWtPYmopDQpwbG90KGZpbHRlcl9vYmosIG1pbl9tYXNzPTIwMCwgbWF4X21hc3M9OTAwKQ0KYGBgDQoNCg0KYGBge3J9DQpwZWFrT2JqIDwtIGFwcGx5RmlsdChmaWx0ZXJfb2JqLCBwZWFrT2JqLCBtaW5fbWFzcyA9IDIwMCwNCiAgICAgICAgICAgICAgICAgIG1heF9tYXNzID0gOTAwKQ0Kc3VtbWFyeShwZWFrT2JqKQ0KYGBgDQoNCmBgYHtyfQ0KcGVha09iag0KYGBgDQoNCk90aGVyIGZpbHRlcnMNCg0KYGBge3J9DQojIG1pbmltdW0gbnVtYmVyIHRvIGJlIG9ic2VydmVkIGFjcm9zcyBhbGwgc2FtcGxlcyBpbiBvcmRlciB0byByZXRhaW4gdGhlIGJpb21vbGVjdWxlDQpwZWFrT2JqIDwtIGFwcGx5RmlsdChtb2xlY3VsZV9maWx0ZXIocGVha09iaiksIHBlYWtPYmosIG1pbl9udW09MikNCiMgcmVtb3ZlIGJpb21vbGVjIHdpdGhvdXQgbW9sZWN1bGFyIGZvcm11bGE7IHJlbW92ZT1Ob0Zvcm11bGEpDQpwZWFrT2JqIDwtIGFwcGx5RmlsdChmb3JtdWxhX2ZpbHRlcihwZWFrT2JqKSwgcGVha09iaikNCnN1bW1hcnkocGVha09iaikNCmBgYA0KDQojIyBDb21wYXJpc29uIG9mIEV4cGVyaW1lbnRhbCBHcm91cHMNCg0KYGBge3J9DQpwZWFrT2JqIDwtIGdyb3VwX2Rlc2lnbmF0aW9uKHBlYWtPYmosIG1haW5fZWZmZWN0cz1jKCJUcmVhdG1lbnQiKSkNCmdldEdyb3VwREYocGVha09iaikNCmBgYA0KIyMgR3JvdXAtbGV2ZWwgU3VtbWFyeSBwZXIgUGVhaw0KDQpgYGB7cn0NCmdyb3VwX3N1bW1hcnkgPC0gc3VtbWFyaXplR3JvdXBzKHBlYWtPYmosIHN1bW1hcnlfZnVuY3Rpb25zID0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygibl9wcmVzZW50IiwgInByb3BfcHJlc2VudCIpKQ0KaGVhZChncm91cF9zdW1tYXJ5JGVfZGF0YSkNCmBgYA0KDQojIyBOT1NDIERpc3RydWJ1dGlvbg0KDQpgYGB7cixmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCnAgPC0gZGVuc2l0eVBsb3QocGVha09iaiwgc2FtcGxlcz1GQUxTRSwgZ3JvdXBzPWMoIkNvbnRyb2wiLCJIZWF0ZWQiKSwgdmFyaWFibGU9Ik5PU0MiLHRpdGxlPSJDb21wYXJpc29uIG9mIE5PU0MgQmV0d2VlbiBFeHBlcmltZW50YWwgUGxvdHM6IE1pbmVyYWwgSG9yaXpvbiIpDQpwDQpgYGANCiMjIENvbXBvdW5kcyBEaXN0cmlidXRpb24NCg0KYGBge3J9DQpieUdyb3VwIDwtIGRpdmlkZUJ5R3JvdXBDb21wYXJpc29ucyhwZWFrT2JqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29ucyA9ICJhbGwiKVtbMV1dJHZhbHVlDQpgYGANCg0KIyMjIEF2YWlsYWJsZSBTdW1tYXJ5IE9wdGlvbnMNCg0KYGBge3J9DQpnZXRHcm91cENvbXBhcmlzb25TdW1tYXJ5RnVuY3Rpb25OYW1lcygpDQpgYGANCg0KIyMgRGV0ZXJtaW5lIFVuaXF1ZSBhbmQgQ29tbW9uIE1hc3Nlcy9QZWFrczogZy10ZXN0DQoNCj4gIlVzZSBhIGctdGVzdCB0byBjb21wYXJlIHR3byBncm91cHMgYW5kIGRldGVybWluZSB3aGljaCBwZWFrcyBhcmUgdW5pcXVlbHkgZXhwcmVzc2VkIGluIGVhY2ggZ3JvdXAgYmFzZWQgb24gYSBwLXZhbHVlIHRocmVzaG9sZC4iDQoNCmBgYHtyfQ0KbWluZXJhbF91bmlxdWUgPC0gc3VtbWFyaXplR3JvdXBDb21wYXJpc29ucyhieUdyb3VwLA0KICAgICAgICAgICAgc3VtbWFyeV9mdW5jdGlvbnM9InVuaXF1ZW5lc3NfZ3Rlc3QiLA0KICAgICAgICAgICAgc3VtbWFyeV9mdW5jdGlvbl9wYXJhbXM9bGlzdCgNCiAgICAgICAgICAgICAgICAgIHVuaXF1ZW5lc3NfZ3Rlc3Q9bGlzdChwcmVzX2ZuPSJuc2FtcHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVzX3RocmVzaD0xLCBwdmFsdWVfdGhyZXNoPTAuMSkpKQ0KDQpoZWFkKG1pbmVyYWxfdW5pcXVlJGVfZGF0YSkNCmBgYA0KDQpgYGB7cn0NCm1pbmVyYWxfdW5pcXVlDQpgYGANCg0KDQoNCiMjIyBQbG90dGluZyAtIFZhbiBLcmV2ZWxlbg0KDQpgYGB7cixmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCnZhbktyZXZlbGVuUGxvdChtaW5lcmFsX3VuaXF1ZSwgY29sb3JDTmFtZSA9ICJ1bmlxdWVuZXNzX2d0ZXN0IikNCmBgYA0KQ29tcG91bmRzIGJyZWFrZG93bg0KDQpgYGB7cn0NCnRhYmxlKG1pbmVyYWxfdW5pcXVlJGVfZGF0YSR1bmlxdWVuZXNzX2d0ZXN0KQ0KYGBgDQoNCg0KIyMgRGV0ZXJtaW5lIFVuaXF1ZSBhbmQgQ29tbW9uIE1hc3Nlcy9QZWFrczogZy10ZXN0DQoNCj4gIlVzZSBudW1iZXIgb2Ygc2FtcGxlcyBmb3Igd2hpY2ggYSBtYXNzL3BlYWsgaXMgcHJlc2VudCB0byBjb21wYXJlIHR3byBncm91cHMgYW5kIGRldGVybWluZSB3aGljaCBwZWFrcyBhcmUgdW5pcXVlbHkgZXhwcmVzc2VkIGluIGVhY2ggZ3JvdXAuIg0KDQoNCmBgYHtyfQ0KbWluZXJhbF91bmlxdWUgPC0gc3VtbWFyaXplR3JvdXBDb21wYXJpc29ucyhieUdyb3VwLA0KICAgICAgICAgICAgc3VtbWFyeV9mdW5jdGlvbnM9InVuaXF1ZW5lc3NfbnNhbXBzIiwNCiAgICAgICAgICAgIHN1bW1hcnlfZnVuY3Rpb25fcGFyYW1zPWxpc3QoDQogICAgICAgICAgICAgICAgICB1bmlxdWVuZXNzX25zYW1wcz1saXN0KHByZXNfdGhyZXNoPTEsIGFic25fdGhyZXNoPTApKSkNCg0KaGVhZChtaW5lcmFsX3VuaXF1ZSRlX2RhdGEpDQpgYGANCg0KYGBge3J9DQptaW5lcmFsX3VuaXF1ZQ0KYGBgDQoNCg0KIyMjIFBsb3R0aW5nIC0gVmFuIEtyZXZlbGVuDQoNCmBgYHtyLGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KdmFuS3JldmVsZW5QbG90KG1pbmVyYWxfdW5pcXVlLCBjb2xvckNOYW1lID0gInVuaXF1ZW5lc3NfbnNhbXBzIikNCmBgYA0KDQoNCkNvbXBvdW5kcyBicmVha2Rvd24NCg0KYGBge3J9DQp0YWJsZShtaW5lcmFsX3VuaXF1ZSRlX2RhdGEkdW5pcXVlbmVzc19uc2FtcHMpDQpgYGANCg0KDQoNCg0KDQo=