In this notebook we will visualize the effect of the choice of elastic tree hyperparameters for the elastic and embedded tree. In this first part we will be working with the dataset by Guo et al., detailing early embryogenesis in the mouse. We will be needing MERLoT as well as some customized versions of the MERLoT functions.

We will read the dataset and the annotation, as well as the diffusion map coordinates.

# Read the example Guo dataset that is distributed with the package
DataFile= paste(find.package("merlot"), "/example/Guo2010.txt", sep="")
Dataset=ReadDataset(DataFile)
# Load the cell types
CellTypes=read.table(file=paste(find.package("merlot"), "/example/GuoFeatures.txt", sep=""), sep="\t", header = F, stringsAsFactors = F)
CellTypes=CellTypes[,2]
selected_colors=c("red", "orange", "yellow", "green", "cyan", "darkblue")
guo_colorcells=c()
guo_colorcells[which(CellTypes=="2C")]="red"
guo_colorcells[which(CellTypes=="4C")]="orange"
guo_colorcells[which(CellTypes=="8C")]="yellow"
guo_colorcells[which(CellTypes=="16C")]="green"
guo_colorcells[which(CellTypes=="32C")]="cyan"
guo_colorcells[which(CellTypes=="64C")]="darkblue"
CellCoordinates=read.table(file=paste(find.package("merlot"), "/example/GuoRotatedCoordinates.txt", sep=""), sep="\t", header = F, stringsAsFactors = F)
CellCoordinates=as.matrix(CellCoordinates[,1:2])

We calculate the scaffold tree without local averaging. The Guo dataset only has 428 cells; reducing that further will only make MERLoT’s job harder.

We will now calculate the elastic tree with different elasticity hyperparameters: the MERLoT defaults and the values that ElPiGraph.R proposes for the elastic_tree algorithm.

The exact number of nodes per branch differs, and the exact positions of endpoints and branchpoints do not overlap, but we see that there are no big differences between using the MERLoT hyperparameters (left) and the ElPiGraph.R ones (right).

For the embedding in gene space, we will use (1) the MERLoT elastic tree with MERLoT hyperparameters (benchmarked in original benchmark), (2) the ElPi elastic tree with ElPi hyperparameters (benchmarked in extended benchmark), and (3) the ElPi elastic tree with MERLoT hyperparameters (not benchmarked).

# Embedd the principal elastic tree on the gene expression space from which it was calculated.
EmbeddedTree= GenesSpaceEmbedding(ExpressionMatrix = Dataset$ExpressionMatrix, ElasticTree = ElasticTree)
nikoEmb <- nikoEmbed(ExpressionMatrix = Dataset$ExpressionMatrix, ElasticTree = nikoTree)
mixed = GenesSpaceEmbedding(ExpressionMatrix = Dataset$ExpressionMatrix, ElasticTree = nikoTree)

There is no immediate way to visualize the embedded tree, but we can plot the trees that result from the embedding in a schematic way, using the plot_flattened_tree function.

par(mfrow=c(1,3))
plot_flattened_tree(EmbeddedTree, cell_annot = CellTypes, node_size = "cells")
plot_flattened_tree(nikoEmb, cell_annot = CellTypes, node_size = "cells")
plot_flattened_tree(mixed, cell_annot = CellTypes, node_size = "cells")
par(mfrow=c(1,1))

Again, there is no big difference except for the absence of cell clumps in the endpoints of three of the four branches. We will now calculate pseudotimes for each cell in every embedded tree:

With the pseudotimes available, we can now plot the expression of genes through pseudotime, allowing another comparison between the three approaches.

par(mfrow=c(1, 3))
plot_pseudotime_expression_gene(GeneName = "Gata4" , EmbeddedTree = EmbeddedTree, Pseudotimes = Pseudotimes)
plot_pseudotime_expression_gene(GeneName = "Gata4" , EmbeddedTree = nikoEmb, Pseudotimes = nikotimes)
plot_pseudotime_expression_gene(GeneName = "Gata4" , EmbeddedTree = mixed, Pseudotimes = mixed_times)
par(mfrow=c(1,1))

Here we see a big difference for the first time. The default ElPi parameters allow the expression of the pseudocells to overfit (middle). On the other hand, not using the MERLoT hyperparameters for the elastic tree doesn’t seem to affect the end result that much (left and right). We conclude that while the ElPi hyperparameters might produce good elastic trees (especially in conjuction with local averaging), they do not lend themselves to the creation of embedded trees, since they will overfit the data and produce less useful gene trajectories over time.

We include some more examples below. Always, from left to right: MERLoT+MERLoT, ElPi+ElPi, ElPi+MERLoT.

# Plot gene expression profile as a function of pseudotime
par(mfrow=c(5, 3))
gene <- "Runx1"
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = EmbeddedTree, Pseudotimes = Pseudotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = nikoEmb, Pseudotimes = nikotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = mixed, Pseudotimes = mixed_times)
gene <- "Nanog"
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = EmbeddedTree, Pseudotimes = Pseudotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = nikoEmb, Pseudotimes = nikotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = mixed, Pseudotimes = mixed_times)
gene <- "Fgf4"
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = EmbeddedTree, Pseudotimes = Pseudotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = nikoEmb, Pseudotimes = nikotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = mixed, Pseudotimes = mixed_times)
gene <- "Fgfr2"
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = EmbeddedTree, Pseudotimes = Pseudotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = nikoEmb, Pseudotimes = nikotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = mixed, Pseudotimes = mixed_times)
gene <- "Klf2"
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = EmbeddedTree, Pseudotimes = Pseudotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = nikoEmb, Pseudotimes = nikotimes)
plot_pseudotime_expression_gene(GeneName = gene , EmbeddedTree = mixed, Pseudotimes = mixed_times)
par(mfrow=c(1,1))

LS0tCnRpdGxlOiAiVGhlIGh5cGVycGFyYW1ldGVyIGNob2ljZSAtIHB0LiAxIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpJbiB0aGlzIG5vdGVib29rIHdlIHdpbGwgdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgdGhlIGNob2ljZSBvZiBlbGFzdGljIHRyZWUgaHlwZXJwYXJhbWV0ZXJzIGZvciB0aGUgZWxhc3RpYyBhbmQgZW1iZWRkZWQgdHJlZS4gSW4gdGhpcyBmaXJzdCBwYXJ0IHdlIHdpbGwgYmUgd29ya2luZyB3aXRoIHRoZSBkYXRhc2V0IGJ5IEd1byBfZXQgYWwuXywgZGV0YWlsaW5nIGVhcmx5IGVtYnJ5b2dlbmVzaXMgaW4gdGhlIG1vdXNlLiBXZSB3aWxsIGJlIG5lZWRpbmcgTUVSTG9UIGFzIHdlbGwgYXMgc29tZSBjdXN0b21pemVkIHZlcnNpb25zIG9mIHRoZSBNRVJMb1QgZnVuY3Rpb25zLgoKYGBge3J9CmxpYnJhcnkobWVybG90KQpzb3VyY2UoIn4vRG9jdW1lbnRzL3JlcG9zL21lcmxvdC1zY3JpcHRzL3NjcmlwdHMvbmlrb190cmVlX2Z1bmNzLlIiKQpgYGAKCldlIHdpbGwgcmVhZCB0aGUgZGF0YXNldCBhbmQgdGhlIGFubm90YXRpb24sIGFzIHdlbGwgYXMgdGhlIGRpZmZ1c2lvbiBtYXAgY29vcmRpbmF0ZXMuCgpgYGB7cn0KIyBSZWFkIHRoZSBleGFtcGxlIEd1byBkYXRhc2V0IHRoYXQgaXMgZGlzdHJpYnV0ZWQgd2l0aCB0aGUgcGFja2FnZQpEYXRhRmlsZT0gcGFzdGUoZmluZC5wYWNrYWdlKCJtZXJsb3QiKSwgIi9leGFtcGxlL0d1bzIwMTAudHh0Iiwgc2VwPSIiKQpEYXRhc2V0PVJlYWREYXRhc2V0KERhdGFGaWxlKQoKIyBMb2FkIHRoZSBjZWxsIHR5cGVzCkNlbGxUeXBlcz1yZWFkLnRhYmxlKGZpbGU9cGFzdGUoZmluZC5wYWNrYWdlKCJtZXJsb3QiKSwgIi9leGFtcGxlL0d1b0ZlYXR1cmVzLnR4dCIsIHNlcD0iIiksIHNlcD0iXHQiLCBoZWFkZXIgPSBGLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKQ2VsbFR5cGVzPUNlbGxUeXBlc1ssMl0Kc2VsZWN0ZWRfY29sb3JzPWMoInJlZCIsICJvcmFuZ2UiLCAieWVsbG93IiwgImdyZWVuIiwgImN5YW4iLCAiZGFya2JsdWUiKQoKZ3VvX2NvbG9yY2VsbHM9YygpCmd1b19jb2xvcmNlbGxzW3doaWNoKENlbGxUeXBlcz09IjJDIildPSJyZWQiCmd1b19jb2xvcmNlbGxzW3doaWNoKENlbGxUeXBlcz09IjRDIildPSJvcmFuZ2UiCmd1b19jb2xvcmNlbGxzW3doaWNoKENlbGxUeXBlcz09IjhDIildPSJ5ZWxsb3ciCmd1b19jb2xvcmNlbGxzW3doaWNoKENlbGxUeXBlcz09IjE2QyIpXT0iZ3JlZW4iCmd1b19jb2xvcmNlbGxzW3doaWNoKENlbGxUeXBlcz09IjMyQyIpXT0iY3lhbiIKZ3VvX2NvbG9yY2VsbHNbd2hpY2goQ2VsbFR5cGVzPT0iNjRDIildPSJkYXJrYmx1ZSIKCkNlbGxDb29yZGluYXRlcz1yZWFkLnRhYmxlKGZpbGU9cGFzdGUoZmluZC5wYWNrYWdlKCJtZXJsb3QiKSwgIi9leGFtcGxlL0d1b1JvdGF0ZWRDb29yZGluYXRlcy50eHQiLCBzZXA9IiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9Ilx0IiwgaGVhZGVyID0gRiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCkNlbGxDb29yZGluYXRlcz1hcy5tYXRyaXgoQ2VsbENvb3JkaW5hdGVzWywxOjJdKQpgYGAKCldlIGNhbGN1bGF0ZSB0aGUgc2NhZmZvbGQgdHJlZSB3aXRob3V0IGxvY2FsIGF2ZXJhZ2luZy4gVGhlIEd1byBkYXRhc2V0IG9ubHkgaGFzIDQyOCBjZWxsczsgcmVkdWNpbmcgdGhhdCBmdXJ0aGVyIHdpbGwgb25seSBtYWtlIE1FUkxvVCdzIGpvYiBoYXJkZXIuCgpgYGB7cn0KIyBXZSBjYWxjdWxhdGUgdGhlIHNjYWZmb2xkIHRyZWUgdXNpbmcgdGhlIGZpcnN0IDMgZGlmZnVzaW9uIGNvbXBvbmVudHMgZnJvbSB0aGUgZGlmZnVzaW9uIG1hcApTY2FmZm9sZFRyZWU9Q2FsY3VsYXRlU2NhZmZvbGRUcmVlKENlbGxDb29yZGluYXRlcyA9IENlbGxDb29yZGluYXRlcywgcHl0aG9uX2xvY2F0aW9uID0gIn4vbWluaWNvbmRhMy9lbnZzL3B5MzYvYmluL3B5dGhvbiIpCnBsb3Rfc2NhZmZvbGRfdHJlZShTY2FmZm9sZFRyZWUgPSBTY2FmZm9sZFRyZWUsIGNvbG9yY2VsbHMgPSBndW9fY29sb3JjZWxscykKbGVnZW5kKHg9ImJvdHRvbXJpZ2h0IiwgbGVnZW5kPWMoIjJDIiwgIjRDIiwgIjhDIiwgIjE2QyIsICIzMkMiLCAiNjRDIiksIGNvbD1zZWxlY3RlZF9jb2xvcnMsIHBjaD0xNikKYGBgCgpXZSB3aWxsIG5vdyBjYWxjdWxhdGUgdGhlIGVsYXN0aWMgdHJlZSB3aXRoIGRpZmZlcmVudCBlbGFzdGljaXR5IGh5cGVycGFyYW1ldGVyczogdGhlIE1FUkxvVCBkZWZhdWx0cyBhbmQgdGhlIHZhbHVlcyB0aGF0IEVsUGlHcmFwaC5SIHByb3Bvc2VzIGZvciB0aGUgYGVsYXN0aWNfdHJlZWAgYWxnb3JpdGhtLgoKYGBge3J9Ck51bWJlck9mTm9kZXM9MTAwCgpFbGFzdGljVHJlZSA8LSBDYWxjdWxhdGVFbGFzdGljVHJlZShTY2FmZm9sZFRyZWUgPSBTY2FmZm9sZFRyZWUsIE5feWsgPSBOdW1iZXJPZk5vZGVzKQpuaWtvVHJlZSA8LSBuaWtvRWxhc3RpYyhTY2FmZm9sZFRyZWUgPSBTY2FmZm9sZFRyZWUsIE5feWsgPSBOdW1iZXJPZk5vZGVzKQpwYXIobWZyb3c9YygxLDIpKQpwbG90X2VsYXN0aWNfdHJlZShFbGFzdGljVHJlZSkKcGxvdF9lbGFzdGljX3RyZWUobmlrb1RyZWUpCnBhcihtZnJvdz1jKDEsMSkpCmBgYAoKVGhlIGV4YWN0IG51bWJlciBvZiBub2RlcyBwZXIgYnJhbmNoIGRpZmZlcnMsIGFuZCB0aGUgZXhhY3QgcG9zaXRpb25zIG9mIGVuZHBvaW50cyBhbmQgYnJhbmNocG9pbnRzIGRvIG5vdCBvdmVybGFwLCBidXQgd2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5vIGJpZyBkaWZmZXJlbmNlcyBiZXR3ZWVuIHVzaW5nIHRoZSBNRVJMb1QgaHlwZXJwYXJhbWV0ZXJzIChsZWZ0KSBhbmQgdGhlIEVsUGlHcmFwaC5SIG9uZXMgKHJpZ2h0KS4KCkZvciB0aGUgZW1iZWRkaW5nIGluIGdlbmUgc3BhY2UsIHdlIHdpbGwgdXNlICgxKSB0aGUgTUVSTG9UIGVsYXN0aWMgdHJlZSB3aXRoIE1FUkxvVCBoeXBlcnBhcmFtZXRlcnMgKGJlbmNobWFya2VkIGluIG9yaWdpbmFsIGJlbmNobWFyayksICgyKSB0aGUgRWxQaSBlbGFzdGljIHRyZWUgd2l0aCBFbFBpIGh5cGVycGFyYW1ldGVycyAoYmVuY2htYXJrZWQgaW4gZXh0ZW5kZWQgYmVuY2htYXJrKSwgYW5kICgzKSB0aGUgRWxQaSBlbGFzdGljIHRyZWUgd2l0aCBNRVJMb1QgaHlwZXJwYXJhbWV0ZXJzIChub3QgYmVuY2htYXJrZWQpLgoKYGBge3J9CiMgRW1iZWRkIHRoZSBwcmluY2lwYWwgZWxhc3RpYyB0cmVlIG9uIHRoZSBnZW5lIGV4cHJlc3Npb24gc3BhY2UgZnJvbSB3aGljaCBpdCB3YXMgY2FsY3VsYXRlZC4KRW1iZWRkZWRUcmVlPSBHZW5lc1NwYWNlRW1iZWRkaW5nKEV4cHJlc3Npb25NYXRyaXggPSBEYXRhc2V0JEV4cHJlc3Npb25NYXRyaXgsIEVsYXN0aWNUcmVlID0gRWxhc3RpY1RyZWUpCm5pa29FbWIgPC0gbmlrb0VtYmVkKEV4cHJlc3Npb25NYXRyaXggPSBEYXRhc2V0JEV4cHJlc3Npb25NYXRyaXgsIEVsYXN0aWNUcmVlID0gbmlrb1RyZWUpCm1peGVkID0gR2VuZXNTcGFjZUVtYmVkZGluZyhFeHByZXNzaW9uTWF0cml4ID0gRGF0YXNldCRFeHByZXNzaW9uTWF0cml4LCBFbGFzdGljVHJlZSA9IG5pa29UcmVlKQpgYGAKClRoZXJlIGlzIG5vIGltbWVkaWF0ZSB3YXkgdG8gdmlzdWFsaXplIHRoZSBlbWJlZGRlZCB0cmVlLCBidXQgd2UgY2FuIHBsb3QgdGhlIHRyZWVzIHRoYXQgcmVzdWx0IGZyb20gdGhlIGVtYmVkZGluZyBpbiBhIHNjaGVtYXRpYyB3YXksIHVzaW5nIHRoZSBgcGxvdF9mbGF0dGVuZWRfdHJlZWAgZnVuY3Rpb24uCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDEsMykpCnBsb3RfZmxhdHRlbmVkX3RyZWUoRW1iZWRkZWRUcmVlLCBjZWxsX2Fubm90ID0gQ2VsbFR5cGVzLCBub2RlX3NpemUgPSAiY2VsbHMiKQpwbG90X2ZsYXR0ZW5lZF90cmVlKG5pa29FbWIsIGNlbGxfYW5ub3QgPSBDZWxsVHlwZXMsIG5vZGVfc2l6ZSA9ICJjZWxscyIpCnBsb3RfZmxhdHRlbmVkX3RyZWUobWl4ZWQsIGNlbGxfYW5ub3QgPSBDZWxsVHlwZXMsIG5vZGVfc2l6ZSA9ICJjZWxscyIpCnBhcihtZnJvdz1jKDEsMSkpCmBgYAoKQWdhaW4sIHRoZXJlIGlzIG5vIGJpZyBkaWZmZXJlbmNlIGV4Y2VwdCBmb3IgdGhlIGFic2VuY2Ugb2YgY2VsbCBjbHVtcHMgaW4gdGhlIGVuZHBvaW50cyBvZiB0aHJlZSBvZiB0aGUgZm91ciBicmFuY2hlcy4gV2Ugd2lsbCBub3cgY2FsY3VsYXRlIHBzZXVkb3RpbWVzIGZvciBlYWNoIGNlbGwgaW4gZXZlcnkgZW1iZWRkZWQgdHJlZToKCmBgYHtyfQpQc2V1ZG90aW1lcz1DYWxjdWxhdGVQc2V1ZG90aW1lcyhFbWJlZGRlZFRyZWUsIFQwPTEpCm5pa290aW1lcyA9IENhbGN1bGF0ZVBzZXVkb3RpbWVzKG5pa29FbWIsIFQwPTEpCm1peGVkX3RpbWVzID0gQ2FsY3VsYXRlUHNldWRvdGltZXMobWl4ZWQsIFQwPTEpCmBgYAoKV2l0aCB0aGUgcHNldWRvdGltZXMgYXZhaWxhYmxlLCB3ZSBjYW4gbm93IHBsb3QgdGhlIGV4cHJlc3Npb24gb2YgZ2VuZXMgdGhyb3VnaCBwc2V1ZG90aW1lLCBhbGxvd2luZyBhbm90aGVyIGNvbXBhcmlzb24gYmV0d2VlbiB0aGUgdGhyZWUgYXBwcm9hY2hlcy4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9M30KcGFyKG1mcm93PWMoMSwgMykpCmdlbmUgPSAiR2F0YTQiCnBsb3RfcHNldWRvdGltZV9leHByZXNzaW9uX2dlbmUoR2VuZU5hbWUgPSBnZW5lICwgRW1iZWRkZWRUcmVlID0gRW1iZWRkZWRUcmVlLCBQc2V1ZG90aW1lcyA9IFBzZXVkb3RpbWVzKQp0ZXh0KDQsIDEwLCBsYWJlbHMgPSAiQSIsIGNleCA9IDMsIGFkaj1jKDAuNSwgMC41KSkKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBuaWtvRW1iLCBQc2V1ZG90aW1lcyA9IG5pa290aW1lcykKdGV4dCg0LCA4LjUsIGxhYmVscyA9ICJBIiwgY2V4ID0gMywgYWRqPWMoMC41LCAwLjUpKQpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IG1peGVkLCBQc2V1ZG90aW1lcyA9IG1peGVkX3RpbWVzKQpwYXIobWZyb3c9YygxLDEpKQpgYGAKCkhlcmUgd2Ugc2VlIGEgYmlnIGRpZmZlcmVuY2UgZm9yIHRoZSBmaXJzdCB0aW1lLiBUaGUgZGVmYXVsdCBFbFBpIHBhcmFtZXRlcnMgYWxsb3cgdGhlIGV4cHJlc3Npb24gb2YgdGhlIHBzZXVkb2NlbGxzIHRvIG92ZXJmaXQgKG1pZGRsZSkuIE9uIHRoZSBvdGhlciBoYW5kLCBub3QgdXNpbmcgdGhlIE1FUkxvVCBoeXBlcnBhcmFtZXRlcnMgZm9yIHRoZSBlbGFzdGljIHRyZWUgZG9lc24ndCBzZWVtIHRvIGFmZmVjdCB0aGUgZW5kIHJlc3VsdCB0aGF0IG11Y2ggKGxlZnQgYW5kIHJpZ2h0KS4gV2UgY29uY2x1ZGUgdGhhdCB3aGlsZSB0aGUgRWxQaSBoeXBlcnBhcmFtZXRlcnMgbWlnaHQgcHJvZHVjZSBnb29kIGVsYXN0aWMgdHJlZXMgKGVzcGVjaWFsbHkgaW4gY29uanVjdGlvbiB3aXRoIGxvY2FsIGF2ZXJhZ2luZyksIHRoZXkgZG8gbm90IGxlbmQgdGhlbXNlbHZlcyB0byB0aGUgY3JlYXRpb24gb2YgZW1iZWRkZWQgdHJlZXMsIHNpbmNlIHRoZXkgd2lsbCBvdmVyZml0IHRoZSBkYXRhIGFuZCBwcm9kdWNlIGxlc3MgdXNlZnVsIGdlbmUgdHJhamVjdG9yaWVzIG92ZXIgdGltZS4KCldlIGluY2x1ZGUgc29tZSBtb3JlIGV4YW1wbGVzIGJlbG93LiBBbHdheXMsIGZyb20gbGVmdCB0byByaWdodDogTUVSTG9UK01FUkxvVCwgRWxQaStFbFBpLCBFbFBpK01FUkxvVC4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTV9CiMgUGxvdCBnZW5lIGV4cHJlc3Npb24gcHJvZmlsZSBhcyBhIGZ1bmN0aW9uIG9mIHBzZXVkb3RpbWUKcGFyKG1mcm93PWMoNSwgMykpCmdlbmUgPC0gIlJ1bngxIgpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IEVtYmVkZGVkVHJlZSwgUHNldWRvdGltZXMgPSBQc2V1ZG90aW1lcykKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBuaWtvRW1iLCBQc2V1ZG90aW1lcyA9IG5pa290aW1lcykKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBtaXhlZCwgUHNldWRvdGltZXMgPSBtaXhlZF90aW1lcykKZ2VuZSA8LSAiTmFub2ciCnBsb3RfcHNldWRvdGltZV9leHByZXNzaW9uX2dlbmUoR2VuZU5hbWUgPSBnZW5lICwgRW1iZWRkZWRUcmVlID0gRW1iZWRkZWRUcmVlLCBQc2V1ZG90aW1lcyA9IFBzZXVkb3RpbWVzKQpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IG5pa29FbWIsIFBzZXVkb3RpbWVzID0gbmlrb3RpbWVzKQpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IG1peGVkLCBQc2V1ZG90aW1lcyA9IG1peGVkX3RpbWVzKQpnZW5lIDwtICJGZ2Y0IgpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IEVtYmVkZGVkVHJlZSwgUHNldWRvdGltZXMgPSBQc2V1ZG90aW1lcykKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBuaWtvRW1iLCBQc2V1ZG90aW1lcyA9IG5pa290aW1lcykKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBtaXhlZCwgUHNldWRvdGltZXMgPSBtaXhlZF90aW1lcykKZ2VuZSA8LSAiRmdmcjIiCnBsb3RfcHNldWRvdGltZV9leHByZXNzaW9uX2dlbmUoR2VuZU5hbWUgPSBnZW5lICwgRW1iZWRkZWRUcmVlID0gRW1iZWRkZWRUcmVlLCBQc2V1ZG90aW1lcyA9IFBzZXVkb3RpbWVzKQpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IG5pa29FbWIsIFBzZXVkb3RpbWVzID0gbmlrb3RpbWVzKQpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IG1peGVkLCBQc2V1ZG90aW1lcyA9IG1peGVkX3RpbWVzKQpnZW5lIDwtICJLbGYyIgpwbG90X3BzZXVkb3RpbWVfZXhwcmVzc2lvbl9nZW5lKEdlbmVOYW1lID0gZ2VuZSAsIEVtYmVkZGVkVHJlZSA9IEVtYmVkZGVkVHJlZSwgUHNldWRvdGltZXMgPSBQc2V1ZG90aW1lcykKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBuaWtvRW1iLCBQc2V1ZG90aW1lcyA9IG5pa290aW1lcykKcGxvdF9wc2V1ZG90aW1lX2V4cHJlc3Npb25fZ2VuZShHZW5lTmFtZSA9IGdlbmUgLCBFbWJlZGRlZFRyZWUgPSBtaXhlZCwgUHNldWRvdGltZXMgPSBtaXhlZF90aW1lcykKcGFyKG1mcm93PWMoMSwxKSkKYGBgCg==