Load R packages

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

Sample Data

fdata <- read.delim('input/FTICR_metadata.txt')
fdata <- fdata[fdata$Horizon %in% "Organic",]
str(fdata)
'data.frame':   16 obs. of  7 variables:
 $ Sample_ID  : chr  "H1" "H3" "H5" "H7" ...
 $ Sample.name: chr  "H2O" "H4O" "H11O" "H17O" ...
 $ Horizon    : chr  "Organic" "Organic" "Organic" "Organic" ...
 $ Treatment  : chr  "Heated" "Heated" "Heated" "Heated" ...
 $ T_H        : chr  "Organic_Heated" "Organic_Heated" "Organic_Heated" "Organic_Heated" ...
 $ Sample.ID  : chr  "BW.H.2.O" "BW.H.4.O" "BW.H.11.O" "BW.H.17.O" ...
 $ 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 ...
 $ H1  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H3  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H5  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H7  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H9  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H11 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H13 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ H15 : num  1099266 0 0 0 0 ...
 $ C1  : num  0 0 0 0 0 ...
 $ C3  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C5  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ C7  : num  0 0 0 0 0 ...
 $ C9  : num  0 0 0 0 1046645 ...
 $ C11 : num  0 0 0 0 0 0 0 0 0 0 ...
 $ CS13: num  0 0 0 0 0 ...
 $ C15 : num  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: 80.524%

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: 80.524%

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: 69.770%
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: 3423
Percent Missing: 32.859%

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

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

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.”

organic_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(organic_unique$e_data)
organic_unique
peakData object
# Peaks: 3423
# 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 4720 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 431 Masses were filtered out of the dataset by this filter.

Plotting - Van Krevelen

vanKrevelenPlot(organic_unique, colorCName = "uniqueness_gtest")

Compounds breakdown

table(organic_unique$e_data$uniqueness_nsamps)
< table of extent 0 >

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.”

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

head(organic_unique$e_data)
organic_unique
peakData object
# Peaks: 3423
# 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 4720 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 431 Masses were filtered out of the dataset by this filter.

Plotting - Van Krevelen

vanKrevelenPlot(organic_unique, colorCName = "uniqueness_nsamps")

Compounds breakdown

table(organic_unique$e_data$uniqueness_nsamps)

 Unique to Heated Unique to Control  Observed in Both 
               36               296              3091 
LS0tDQp0aXRsZTogIkZUSUNSLU1TOk9yZ2FuaWMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogIldpbGxpYW0gUm9kcmlndWV6Ig0KLS0tDQoNCiMgTG9hZCBSIHBhY2thZ2VzDQoNCmBgYHtyfQ0Kcm0obGlzdD1scygpKQ0KbGlicmFyeShmdG1zUmFuYWx5c2lzKQ0KYGBgDQoNCg0KIyBTYW1wbGUgRGF0YQ0KYGBge3J9DQpmZGF0YSA8LSByZWFkLmRlbGltKCdpbnB1dC9GVElDUl9tZXRhZGF0YS50eHQnKQ0KZmRhdGEgPC0gZmRhdGFbZmRhdGEkSG9yaXpvbiAlaW4lICJPcmdhbmljIixdDQpzdHIoZmRhdGEpDQpgYGANCg0KIyBGVElDUiBNUyBEYXRhDQpgYGB7cn0NCm15ZGF0YSA9IHJlYWQuZGVsaW0oJ2lucHV0L0ZUSUNSX2RhdGEudHh0JykNCmBgYA0KDQojIEV4cHJlc3Npb24gRGF0YQ0KDQpgYGB7cn0NCmVkYXRhIDwtIHN1YnNldChteWRhdGEsc2VsZWN0PWMoIk1hc3MiLGZkYXRhJFNhbXBsZV9JRCkpDQpzdHIoZWRhdGEpDQpgYGANCg0KIyBNb2xlY3VsYXIgSW5kZW50aWZpY2F0aW9uIERhdGENCg0KYGBge3J9DQplbWV0YSA8LSBzdWJzZXQobXlkYXRhLHNlbGVjdD1jKCJNYXNzIiwiQyIsIkgiLCJPIiwiTiIsIkMxMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTIiwiUCIsIkVycm9yX3BwbSIsIk5ldXRyYWxNYXNzIikpDQpzdHIoZW1ldGEpDQpgYGANCg0KDQojIFBlYWtEYXRhIE9iamVjdA0KDQpgYGB7cn0NCnBlYWtPYmogPC0gYXMucGVha0RhdGEoZV9kYXRhID0gZWRhdGEsZl9kYXRhID0gZmRhdGEsZV9tZXRhID0gZW1ldGEsDQogICAgICAgICAgICAgICAgICAgICAgIGVkYXRhX2NuYW1lID0gIk1hc3MiLGZkYXRhX2NuYW1lID0gIlNhbXBsZV9JRCIsDQogICAgICAgICAgICAgICAgICAgICAgIG1hc3NfY25hbWUgPSAiTWFzcyIsY19jbmFtZT0iQyIsIGhfY25hbWU9IkgiLA0KICAgICAgICAgICAgICAgICAgICAgICBvX2NuYW1lPSJPIiwgbl9jbmFtZT0iTiIsIHNfY25hbWU9IlMiLA0KICAgICAgICAgICAgICAgICAgICAgICBwX2NuYW1lPSJQIiwgaXNvdG9waWNfY25hbWUgPSAiQzEzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgaXNvdG9waWNfbm90YXRpb24gPSAiMSIpDQpwZWFrT2JqDQpgYGANCg0KIkN1cnJlbnRseSwgYW55IHBlYWtzIHRoYXQgYXJlIGlzb3RvcGljIGFyZSByZW1vdmVkIGZyb20gdGhlIGRhdGFzZXQsIGFzIGF2YWlsYWJsZSBtZXRob2RzIChlLmcuIFZhbiBLcmV2ZWxlbiBwbG90KSBhcmUgbm90IGFwcGxpY2FibGUgdG8gdGhlc2UgcGVha3MuIg0KDQoNCiMjIEVsZW1lbnRzDQoNCmBgYHtyfQ0KbmFtZXMocGVha09iaikNCmBgYA0KDQojIyBDYWxjdWxhdGVkIE1vbGVjdWxhciBGb3JtdWxhDQoNCmBgYHtyfQ0KdGFpbChwZWFrT2JqJGVfbWV0YSkNCmBgYA0KDQojIyBTdW1tYXJ5DQoNCmBgYHtyfQ0Kc3VtbWFyeShwZWFrT2JqKQ0KYGBgDQoNCiMjIFBsb3R0aW5nIChEZWZhdWx0OyBwcmVzZW5jZS9hYnNlbmNlKQ0KDQpgYGB7cn0NCnBsb3QocGVha09iaikNCmBgYA0KDQojIFByZXByb2Nlc3NpbmcNCg0KVHJhbnNmb3JtaW5nIGFidW5kYW5jZSB2YWx1ZXMgdG8gbG9nMg0KDQpgYGB7cn0NCnBlYWtPYmogPC0gZWRhdGFfdHJhbnNmb3JtKHBlYWtPYmosIGRhdGFfc2NhbGU9ImxvZzIiKQ0KYGBgDQoNCnBsb3R0aW5nDQoNCmBgYHtyfQ0KcGxvdChwZWFrT2JqKQ0KYGBgDQoNCiMgQ2FsY3VsYXRpbmcgbWV0YS1kYXRhDQoNCmBgYHtyfQ0KcGVha09iaiA8LSBjb21wb3VuZF9jYWxjcyhwZWFrT2JqKQ0KcGVha09iag0KYGBgDQoNCiMjIENvbXBvdW5kcyBieSBFbGVtZW50cw0KDQpgYGB7cn0NCnBlYWtPYmogPC0gYXNzaWduX2VsZW1lbnRhbF9jb21wb3NpdGlvbihwZWFrT2JqKQ0KdGFibGUocGVha09iaiRlX21ldGFbLGdldEVsQ29tcENvbE5hbWUocGVha09iaildKQ0KYGBgDQoNCiMjIENvbXBvdW5kcyBieSBDbGFzcw0KDQpgYGB7cn0NCnBlYWtPYmogPC0gYXNzaWduX2NsYXNzKHBlYWtPYmosIGJvdW5kYXJ5X3NldCA9ICJiczEiKQ0KdGFibGUocGVha09iaiRlX21ldGFbLCBnZXRCUzFDb2xOYW1lKHBlYWtPYmopXSkNCmBgYA0KKlNvbWV0aGluZyBpcyBnb2luZyBvbiBib3VuZGFyeV9zZXQgYXJndW1lbnQgd2hlbiBiczIgb3IgYnMzIHNlbGVjdGVkLg0KDQojIyBGaWx0ZXJpbmcNCg0KQmVmb3JlIGZpbHRlcmluZw0KDQpgYGB7cn0NCnN1bW1hcnkocGVha09iaikNCmBgYA0KDQpBZnRlciBmaWx0ZXJpbmcNCg0KDQpgYGB7cn0NCmZpbHRlcl9vYmogPC0gbWFzc19maWx0ZXIocGVha09iaikNCnBsb3QoZmlsdGVyX29iaiwgbWluX21hc3M9MjAwLCBtYXhfbWFzcz05MDApDQpgYGANCg0KDQpgYGB7cn0NCnBlYWtPYmogPC0gYXBwbHlGaWx0KGZpbHRlcl9vYmosIHBlYWtPYmosIG1pbl9tYXNzID0gMjAwLA0KICAgICAgICAgICAgICAgICAgbWF4X21hc3MgPSA5MDApDQpzdW1tYXJ5KHBlYWtPYmopDQpgYGANCg0KYGBge3J9DQpwZWFrT2JqDQpgYGANCg0KT3RoZXIgZmlsdGVycw0KDQpgYGB7cn0NCiMgbWluaW11bSBudW1iZXIgdG8gYmUgb2JzZXJ2ZWQgYWNyb3NzIGFsbCBzYW1wbGVzIGluIG9yZGVyIHRvIHJldGFpbiB0aGUgYmlvbW9sZWN1bGUNCnBlYWtPYmogPC0gYXBwbHlGaWx0KG1vbGVjdWxlX2ZpbHRlcihwZWFrT2JqKSwgcGVha09iaiwgbWluX251bT0yKQ0KIyByZW1vdmUgYmlvbW9sZWMgd2l0aG91dCBtb2xlY3VsYXIgZm9ybXVsYTsgcmVtb3ZlPU5vRm9ybXVsYSkNCnBlYWtPYmogPC0gYXBwbHlGaWx0KGZvcm11bGFfZmlsdGVyKHBlYWtPYmopLCBwZWFrT2JqKQ0Kc3VtbWFyeShwZWFrT2JqKQ0KYGBgDQoNCiMjIENvbXBhcmlzb24gb2YgRXhwZXJpbWVudGFsIEdyb3Vwcw0KDQpgYGB7cn0NCnBlYWtPYmogPC0gZ3JvdXBfZGVzaWduYXRpb24ocGVha09iaiwgbWFpbl9lZmZlY3RzPWMoIlRyZWF0bWVudCIpKQ0KZ2V0R3JvdXBERihwZWFrT2JqKQ0KYGBgDQojIyBHcm91cC1sZXZlbCBTdW1tYXJ5IHBlciBQZWFrDQoNCmBgYHtyfQ0KZ3JvdXBfc3VtbWFyeSA8LSBzdW1tYXJpemVHcm91cHMocGVha09iaiwgc3VtbWFyeV9mdW5jdGlvbnMgPQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJuX3ByZXNlbnQiLCAicHJvcF9wcmVzZW50IikpDQpoZWFkKGdyb3VwX3N1bW1hcnkkZV9kYXRhKQ0KYGBgDQoNCiMjIE5PU0MgRGlzdHJ1YnV0aW9uDQoNCmBgYHtyLGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KZGVuc2l0eVBsb3QocGVha09iaiwgc2FtcGxlcz1GQUxTRSwgZ3JvdXBzPWMoIkNvbnRyb2wiLCJIZWF0ZWQiKSwgdmFyaWFibGU9Ik5PU0MiLHRpdGxlPSJDb21wYXJpc29uIG9mIE5PU0MgQmV0d2VlbiBFeHBlcmltZW50YWwgUGxvdHM6IE9yZ2FuaWMgSG9yaXpvbiIpDQpgYGANCiMjIENvbXBvdW5kcyBEaXN0cmlidXRpb24NCg0KYGBge3J9DQpieUdyb3VwIDwtIGRpdmlkZUJ5R3JvdXBDb21wYXJpc29ucyhwZWFrT2JqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29ucyA9ICJhbGwiKVtbMV1dJHZhbHVlDQpgYGANCg0KIyMjIEF2YWlsYWJsZSBTdW1tYXJ5IE9wdGlvbnMNCg0KYGBge3J9DQpnZXRHcm91cENvbXBhcmlzb25TdW1tYXJ5RnVuY3Rpb25OYW1lcygpDQpgYGANCg0KIyMgRGV0ZXJtaW5lIFVuaXF1ZSBhbmQgQ29tbW9uIE1hc3Nlcy9QZWFrczogZy10ZXN0DQoNCj4gIlVzZSBhIGctdGVzdCB0byBjb21wYXJlIHR3byBncm91cHMgYW5kIGRldGVybWluZSB3aGljaCBwZWFrcyBhcmUgdW5pcXVlbHkgZXhwcmVzc2VkIGluIGVhY2ggZ3JvdXAgYmFzZWQgb24gYSBwLXZhbHVlIHRocmVzaG9sZC4iDQoNCmBgYHtyfQ0Kb3JnYW5pY191bmlxdWUgPC0gc3VtbWFyaXplR3JvdXBDb21wYXJpc29ucyhieUdyb3VwLA0KICAgICAgICAgICAgc3VtbWFyeV9mdW5jdGlvbnM9InVuaXF1ZW5lc3NfZ3Rlc3QiLA0KICAgICAgICAgICAgc3VtbWFyeV9mdW5jdGlvbl9wYXJhbXM9bGlzdCgNCiAgICAgICAgICAgICAgICAgIHVuaXF1ZW5lc3NfZ3Rlc3Q9bGlzdChwcmVzX2ZuPSJuc2FtcHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVzX3RocmVzaD0xLCBwdmFsdWVfdGhyZXNoPTAuMSkpKQ0KDQpoZWFkKG9yZ2FuaWNfdW5pcXVlJGVfZGF0YSkNCmBgYA0KDQpgYGB7cn0NCm9yZ2FuaWNfdW5pcXVlDQpgYGANCg0KDQoNCiMjIyBQbG90dGluZyAtIFZhbiBLcmV2ZWxlbg0KDQpgYGB7cixmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCnZhbktyZXZlbGVuUGxvdChvcmdhbmljX3VuaXF1ZSwgY29sb3JDTmFtZSA9ICJ1bmlxdWVuZXNzX2d0ZXN0IikNCmBgYA0KDQoNCkNvbXBvdW5kcyBicmVha2Rvd24NCg0KYGBge3J9DQp0YWJsZShvcmdhbmljX3VuaXF1ZSRlX2RhdGEkdW5pcXVlbmVzc19uc2FtcHMpDQpgYGANCg0KDQoNCiMjIERldGVybWluZSBVbmlxdWUgYW5kIENvbW1vbiBNYXNzZXMvUGVha3M6IGctdGVzdA0KDQo+ICJVc2UgbnVtYmVyIG9mIHNhbXBsZXMgZm9yIHdoaWNoIGEgbWFzcy9wZWFrIGlzIHByZXNlbnQgdG8gY29tcGFyZSB0d28gZ3JvdXBzIGFuZCBkZXRlcm1pbmUgd2hpY2ggcGVha3MgYXJlIHVuaXF1ZWx5IGV4cHJlc3NlZCBpbiBlYWNoIGdyb3VwLiINCg0KDQpgYGB7cn0NCm9yZ2FuaWNfdW5pcXVlIDwtIHN1bW1hcml6ZUdyb3VwQ29tcGFyaXNvbnMoYnlHcm91cCwNCiAgICAgICAgICAgIHN1bW1hcnlfZnVuY3Rpb25zPSJ1bmlxdWVuZXNzX25zYW1wcyIsDQogICAgICAgICAgICBzdW1tYXJ5X2Z1bmN0aW9uX3BhcmFtcz1saXN0KA0KICAgICAgICAgICAgICAgICAgdW5pcXVlbmVzc19uc2FtcHM9bGlzdChwcmVzX3RocmVzaD0xLCBhYnNuX3RocmVzaD0wKSkpDQoNCmhlYWQob3JnYW5pY191bmlxdWUkZV9kYXRhKQ0KYGBgDQoNCmBgYHtyfQ0Kb3JnYW5pY191bmlxdWUNCmBgYA0KDQoNCiMjIyBQbG90dGluZyAtIFZhbiBLcmV2ZWxlbg0KDQpgYGB7cixmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCnZhbktyZXZlbGVuUGxvdChvcmdhbmljX3VuaXF1ZSwgY29sb3JDTmFtZSA9ICJ1bmlxdWVuZXNzX25zYW1wcyIpDQpgYGANCg0KDQpDb21wb3VuZHMgYnJlYWtkb3duDQoNCmBgYHtyfQ0KdGFibGUob3JnYW5pY191bmlxdWUkZV9kYXRhJHVuaXF1ZW5lc3NfbnNhbXBzKQ0KYGBgDQoNCg0KDQoNCg==