Now that we computed functional diversity, its SES, and put it on the map. We can proceed similarly with phylogenetic diversity. For this whole section we will use the ape package to manipulate phylogenetic trees and the picante package to compute phylogenetic diversity indices.

Getting the phylogenetic tree

We included a copy of the phylogenetic tree used in the article (it is given in the Supplementary Information). It is named phylo_tree.nwk in the data/ folder.

We can read it with the read.tree() function in the ape package:

phylo_tree = ape::read.tree("data/doi_10.5061_dryad.f77p7__v1/phylo_tree.nwk")

phylo_tree
str(phylo_tree)

Questions from you

  • Q20: How many taxa are in the phylogenetic tree?
  • Q21: How does this number compare to the number of taxa found in the dataset?

You can visualize the taxa in the phylogenetic tree in the tip.label slot of the phylogenetic tree:

phylo_tree$tip.label

Question for you

  • Q22: What do you notice with the species names? Especially compared to the ones available in species_traits.

To solve the naming issue we’ll have to match the names used in the phylogenetic tree to the species code used in the site-species matrix. For that we’ll match the epitheton to the first code available. You do not need to understand this code and can just copy-paste it to execute it because we’re going to use it further down.

# Create an indexed list of names
phylo_names = species_traits[, c("species.code", "species")]
phylo_names$code_id = seq(nrow(phylo_names))

# Get the first species code based on species epithet
code_id_to_use = aggregate(code_id ~ species, phylo_names,
                           FUN = function(x) head(x, 1))

# Get back the data.frame of species names with the actual species.code
code_species = merge(
  code_id_to_use, phylo_names[, c("code_id", "species.code")], by = "code_id"
)

# Tidying code for edge cases
code_species$species = gsub(" ", "", code_species$species)
code_species$species = paste0(
  tolower(substr(code_species$species, 1, 1)),
  substr(code_species$species, 2, nchar(code_species$species))
)

code_species = code_species[, c("species.code", "species")]

dim(code_species)

We can now check that we have all the names of the phylogenetic tree available as codes:

length(intersect(phylo_tree$tip.label, code_species$species))

Now that we have a clear correspondance between species code and phylogenetic name we can proceed to the computation of phylogenetic diversity indices. This won’t let use reproduce exactly the same analyses as in the paper but this is the best we can do, given the data at our disposal. If all the species were determined another possibility could have to re-create a phylogenetic tree from genetic sequences available from genetic databases. This approach however needs specific skills and is a story for another time!

Visualizing the phylogenetic tree

We can visualize the phylogenetic tree to better understand the relationship between species. With more than 600 taxa, the visualization can be quite challenging and some ajustements should be made to ease the vizualition.

The easiest way to show the phylogenetic tree is to use the plot.phylo() function available through the ape package.

ape::plot.phylo(phylo_tree)

By default the function shows the phylogram type of phylogenetic tree and plot all the labels for all species. Let’s make it easier to read:

ape::plot.phylo(phylo_tree, type = "fan", show.node.label = TRUE,
                show.tip.label = FALSE, cex = 0.6)

It is still difficult too read but we can already look at how botanical are related to one another.

Computing phylogenetic diversity indices

To compute phylogenetic diversity analyses we need to combine the phylogenetic tree with the site-species matrix. We need to subset the communities by selecting only species with a defined code from the previous section.

# Initial site-species matrix
head(sp_com[, 1:5])
dim(sp_com)

# Subset of site-species matrix compatible with phylogenetic tree
sub_phylo_com = sp_com[, as.character(code_species$species.code)]
dim(sub_phylo_com)

To measure phylogenetic diversity we will compute the Mean Pairwise Distance (MPD, Webb (2000)) using the picante package. The MPD is an index that represents the average distance between all pairs of species occurring in the community. It can also be weighted by the abundance or the biomass of considered species so that more weight is given to species that show the greatest abundance.

The first data needed to compute the MPD is the phylogenetic distance between pair of species. We’ll use the cophenetic distance which represent the same relationships as a phylogenetic tree but through a distance matrix. We can use the function cophenetic.phylo() in the ape package to obtain cophenetic distances.

# Compute cophenetic distances from the phylogenetic tree
cophen_dist = ape::cophenetic.phylo(phylo_tree)

str(cophen_dist)

# We need to change the names to species codes
corres_codes = data.frame(
  species = rownames(cophen_dist)
)
corres_codes = merge(corres_codes, code_species, by = "species")
rownames(cophen_dist) = corres_codes$species.code
colnames(cophen_dist) = corres_codes$species.code

Then to compute MPD we use the mpd() function in the picante package.

# Observed Mean Pairwise Distance
# Unweighted
mpd_val_uw = picante::mpd(sub_phylo_com, cophen_dist, abundance.weighted = FALSE)
# Weighted
mpd_val_w = picante::mpd(sub_phylo_com, cophen_dist, abundance.weighted = TRUE)

# Make a nice data.frame with observed MPD values
obs_mpd = data.frame(
  plot.code = rownames(sub_phylo_com),
  mpd_unweighted = mpd_val_uw,
  mpd_weighted = mpd_val_w
)

# Add forest loss proportion and richness for each site
obs_mpd = merge(obs_mpd, plot_data[, c("plot.code", "forestloss17", "ntaxa")])

Questions for you

  • Q23: What is the relationship between the weighted and the unweighted version of the MPD?
  • Q24: What are the relationships between MPD and taxa richness? And with forest loss? Plot these relationships to visualize them and use the cor.test() function to validate your observations.

Null modeling

Because of the expected relationship between MPD and species richness, we have to perform null models in a similar fashion to what we’ve done for functional diversity indices. Because, as with functional diversity, we want to keep null sites with same total biomass and same total biomass per species as observed sites, we can perform a “swap” null model. We will use a null model that shuffle the names of the species at the tip of the phylogenetic tree.

Fortunately, compared to functional diversity, the null models are all integrated in the ses.mpd() function in the picante package. The null model we’ll use is the "taxa.labels" one. Caution: null models can be computationally challenging; for the sake of the example we’ll do only 99 iterations but as for functional diversity a version of the null models with 999 iterations is saved in the data folder.

# Set random seed for repeatability of analysis
set.seed(20210705)

# Compute null permutation of MPD
ses_mpd = picante::ses.mpd(
  sub_phylo_com, cophen_dist, null.model = "taxa.labels",
  abundance.weighted = TRUE, runs = 99
)
head(ses_mpd)

The function ses.mpd() computes many values. You can get the detail by looking at the help of the functions with ?picante::ses.mpd in the Value section.

We’ll now load the version with 999 iterations.

ses_mpd_999 = readRDS("data/null_mpd_999.Rds")

Questions for you

  • Q25: Explain what does the column mpd.obs.z means? How does this compare with the SES values we computed for functional diversity indices?
  • Q26: How does the standardized value relates with taxa richness?
  • Q27: What are the relationships between MPD values considering null models and forest loss? Visualize the relationships with the plot() function, validate your observations with the cor.test() function.

Mapping phylogenetic diversity

In order to see if there is a geographical pattern in phylogenetic diversity we can plot maps of MPD.

ses_mpd_999$plot.code = rownames(ses_mpd_999)

ggplot() +
  geom_sf(
    data = merge(subset(plot_sf, block != "og"), ses_mpd_999, by = "plot.code"),
    aes(color = mpd.obs.z)
  ) +
  scale_color_distiller(type = "div", palette = "RdYlBu",
                        name =  "SES of MPD") +
  coord_sf(crs = sf::st_crs(3376), xlim = c(875000, 890000),
           ylim = c(518500, 531000)) +
  labs(title = "Map of all plots but block 'og'") +
  ggspatial::annotation_scale() +
  ggspatial::annotation_north_arrow(location = "br") +
  theme_gray()

By eye at least, the pattern doesn’t seem obvious on the map. And the observed SES values seems to vary widely within each forest block.

Comparing facets

One burning question in the scientific literature and that is quite debated still is the relationship between taxonomic, functional, and phylogenetic diversity (Pavoine et al. 2013).

We can leverage on the computation we have to test the relationships between all facets of diversity. /!\ NOTE: Because we had trouble with the phylogenetic tree, we’re not strictly comparing the same subset of data, we’re going to compare them anyway for the sake of the example. The proper way would be to subset the similar sets of species and recompute functional diversity.

# Combine taxonomic, functional, and phylogenetic diversity
all_diversity = merge(
  plot_data[, c("plot.code", "ntaxa")],
  merge(
    ses_fd, ses_mpd_999[, -1], by = "plot.code"
  )
)

# Comparison of observed values
pairs(all_diversity[, c("ntaxa", "FRic", "Q", "FEve", "mpd.obs")],
      upper.panel = panel.cor)

# Comparison of SESs
pairs(all_diversity[, c("ntaxa", "ses_FRic", "ses_Q", "ses_FEve", "mpd.obs.z")],
      upper.panel = panel.cor)

Question for you

  • Q28: How are related are observed values of functional diversity and phylogenetic diversity? What about the SESs?

References

Pavoine, Sandrine, Amandine Gasc, Michael B. Bonsall, and Norman W. H. Mason. 2013. “Correlations Between Phylogenetic and Functional Diversity: Mathematical Artefacts or True Ecological and Evolutionary Processes?” Journal of Vegetation Science 24 (5): 781–93. https://doi.org/10.1111/jvs.12051.
Webb, null. 2000. “Exploring the Phylogenetic Structure of Ecological Communities: An Example for Rain Forest Trees.” The American Naturalist 156 (2): 145–55. https://doi.org/10.1086/303378.
LS0tCnRpdGxlOiAiUGh5bG9nZW5ldGljIERpdmVyc2l0eSIKb3V0cHV0OiBodG1sX2RvY3VtZW50CmJpYmxpb2dyYXBoeTogYmlibGlvZ3JhcGh5LmJpYgplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFKQpgYGAKCk5vdyB0aGF0IHdlIGNvbXB1dGVkIGZ1bmN0aW9uYWwgZGl2ZXJzaXR5LCBpdHMgU0VTLCBhbmQgcHV0IGl0IG9uIHRoZSBtYXAuIFdlIGNhbiBwcm9jZWVkIHNpbWlsYXJseSB3aXRoIHBoeWxvZ2VuZXRpYyBkaXZlcnNpdHkuIEZvciB0aGlzIHdob2xlIHNlY3Rpb24gd2Ugd2lsbCB1c2UgdGhlIGBhcGVgIHBhY2thZ2UgdG8gbWFuaXB1bGF0ZSBwaHlsb2dlbmV0aWMgdHJlZXMgYW5kIHRoZSBgcGljYW50ZWAgcGFja2FnZSB0byBjb21wdXRlIHBoeWxvZ2VuZXRpYyBkaXZlcnNpdHkgaW5kaWNlcy4KCiMjIEdldHRpbmcgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlCgpXZSBpbmNsdWRlZCBhIGNvcHkgb2YgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIHVzZWQgaW4gdGhlIGFydGljbGUgKGl0IGlzIGdpdmVuIGluIHRoZSBTdXBwbGVtZW50YXJ5IEluZm9ybWF0aW9uKS4gSXQgaXMgbmFtZWQgYHBoeWxvX3RyZWUubndrYCBpbiB0aGUgYGRhdGEvYCBmb2xkZXIuCgpgYGB7ciBkb3dubG9hZC10cmVlLCBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFfQpkb3dubG9hZHRoaXM6OmRvd25sb2FkX2xpbmsoCiAgbGluayA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vUmVreXQvYmlvZGl2ZXJzaXR5X2ZhY2V0c190dXRvcmlhbC8yNjZiYmVjNjEwZjU1NTI1ZDJlYzhkMzZiM2ZiZjk3OGNmZmE3YWE0L2RhdGEvZG9pXzEwLjUwNjFfZHJ5YWQuZjc3cDdfX3YxL3BoeWxvX3RyZWUubndrIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgUGh5bG9nZW5ldGljIFRyZWUiLAogIGJ1dHRvbl90eXBlID0gImRhbmdlciIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKV2UgY2FuIHJlYWQgaXQgd2l0aCB0aGUgYHJlYWQudHJlZSgpYCBmdW5jdGlvbiBpbiB0aGUgYGFwZWAgcGFja2FnZToKCmBgYHtyIGxvYWQtdHJlZX0KcGh5bG9fdHJlZSA9IGFwZTo6cmVhZC50cmVlKCJkYXRhL2RvaV8xMC41MDYxX2RyeWFkLmY3N3A3X192MS9waHlsb190cmVlLm53ayIpCgpwaHlsb190cmVlCnN0cihwaHlsb190cmVlKQpgYGAKCjo6OiB7LnF1ZXN0aW9uc30KIyMjIyBRdWVzdGlvbnMgZnJvbSB5b3UKCiogKipRMjAqKjogSG93IG1hbnkgdGF4YSBhcmUgaW4gdGhlIHBoeWxvZ2VuZXRpYyB0cmVlPwoqICoqUTIxKio6IEhvdyBkb2VzIHRoaXMgbnVtYmVyIGNvbXBhcmUgdG8gdGhlIG51bWJlciBvZiB0YXhhIGZvdW5kIGluIHRoZSBkYXRhc2V0Pwo6OjoKCllvdSBjYW4gdmlzdWFsaXplIHRoZSB0YXhhIGluIHRoZSBwaHlsb2dlbmV0aWMgdHJlZSBpbiB0aGUgYHRpcC5sYWJlbGAgc2xvdCBvZiB0aGUgcGh5bG9nZW5ldGljIHRyZWU6CgpgYGB7ciBwaHlsby1zcGVjaWVzfQpwaHlsb190cmVlJHRpcC5sYWJlbApgYGAKCjo6OiB7LnF1ZXN0aW9uc30KIyMjIyBRdWVzdGlvbiBmb3IgeW91CgoqICoqUTIyKio6IFdoYXQgZG8geW91IG5vdGljZSB3aXRoIHRoZSBzcGVjaWVzIG5hbWVzPyBFc3BlY2lhbGx5IGNvbXBhcmVkIHRvIHRoZSBvbmVzIGF2YWlsYWJsZSBpbiBgc3BlY2llc190cmFpdHNgLgo6OjoKClRvIHNvbHZlIHRoZSBuYW1pbmcgaXNzdWUgd2UnbGwgaGF2ZSB0byBtYXRjaCB0aGUgbmFtZXMgdXNlZCBpbiB0aGUgcGh5bG9nZW5ldGljIHRyZWUgdG8gdGhlIHNwZWNpZXMgY29kZSB1c2VkIGluIHRoZSBzaXRlLXNwZWNpZXMgbWF0cml4LiBGb3IgdGhhdCB3ZSdsbCBtYXRjaCB0aGUgZXBpdGhldG9uIHRvIHRoZSBmaXJzdCBjb2RlIGF2YWlsYWJsZS4gWW91IGRvIG5vdCBuZWVkIHRvIHVuZGVyc3RhbmQgdGhpcyBjb2RlIGFuZCBjYW4ganVzdCBjb3B5LXBhc3RlIGl0IHRvIGV4ZWN1dGUgaXQgYmVjYXVzZSB3ZSdyZSBnb2luZyB0byB1c2UgaXQgZnVydGhlciBkb3duLgoKYGBge3IgcGh5bG8tbmFtZS1jb3JyZXN9CiMgQ3JlYXRlIGFuIGluZGV4ZWQgbGlzdCBvZiBuYW1lcwpwaHlsb19uYW1lcyA9IHNwZWNpZXNfdHJhaXRzWywgYygic3BlY2llcy5jb2RlIiwgInNwZWNpZXMiKV0KcGh5bG9fbmFtZXMkY29kZV9pZCA9IHNlcShucm93KHBoeWxvX25hbWVzKSkKCiMgR2V0IHRoZSBmaXJzdCBzcGVjaWVzIGNvZGUgYmFzZWQgb24gc3BlY2llcyBlcGl0aGV0CmNvZGVfaWRfdG9fdXNlID0gYWdncmVnYXRlKGNvZGVfaWQgfiBzcGVjaWVzLCBwaHlsb19uYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgaGVhZCh4LCAxKSkKCiMgR2V0IGJhY2sgdGhlIGRhdGEuZnJhbWUgb2Ygc3BlY2llcyBuYW1lcyB3aXRoIHRoZSBhY3R1YWwgc3BlY2llcy5jb2RlCmNvZGVfc3BlY2llcyA9IG1lcmdlKAogIGNvZGVfaWRfdG9fdXNlLCBwaHlsb19uYW1lc1ssIGMoImNvZGVfaWQiLCAic3BlY2llcy5jb2RlIildLCBieSA9ICJjb2RlX2lkIgopCgojIFRpZHlpbmcgY29kZSBmb3IgZWRnZSBjYXNlcwpjb2RlX3NwZWNpZXMkc3BlY2llcyA9IGdzdWIoIiAiLCAiIiwgY29kZV9zcGVjaWVzJHNwZWNpZXMpCmNvZGVfc3BlY2llcyRzcGVjaWVzID0gcGFzdGUwKAogIHRvbG93ZXIoc3Vic3RyKGNvZGVfc3BlY2llcyRzcGVjaWVzLCAxLCAxKSksCiAgc3Vic3RyKGNvZGVfc3BlY2llcyRzcGVjaWVzLCAyLCBuY2hhcihjb2RlX3NwZWNpZXMkc3BlY2llcykpCikKCmNvZGVfc3BlY2llcyA9IGNvZGVfc3BlY2llc1ssIGMoInNwZWNpZXMuY29kZSIsICJzcGVjaWVzIildCgpkaW0oY29kZV9zcGVjaWVzKQpgYGAKCldlIGNhbiBub3cgY2hlY2sgdGhhdCB3ZSBoYXZlIGFsbCB0aGUgbmFtZXMgb2YgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIGF2YWlsYWJsZSBhcyBjb2RlczoKCmBgYHtyIHBoeWxvLWNvZGUtaW50ZXJzZWN0fQpsZW5ndGgoaW50ZXJzZWN0KHBoeWxvX3RyZWUkdGlwLmxhYmVsLCBjb2RlX3NwZWNpZXMkc3BlY2llcykpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhIGNsZWFyIGNvcnJlc3BvbmRhbmNlIGJldHdlZW4gc3BlY2llcyBjb2RlIGFuZCBwaHlsb2dlbmV0aWMgbmFtZSB3ZSBjYW4gcHJvY2VlZCB0byB0aGUgY29tcHV0YXRpb24gb2YgcGh5bG9nZW5ldGljIGRpdmVyc2l0eSBpbmRpY2VzLiBUaGlzIHdvbid0IGxldCB1c2UgcmVwcm9kdWNlIGV4YWN0bHkgdGhlIHNhbWUgYW5hbHlzZXMgYXMgaW4gdGhlIHBhcGVyIGJ1dCB0aGlzIGlzIHRoZSBiZXN0IHdlIGNhbiBkbywgZ2l2ZW4gdGhlIGRhdGEgYXQgb3VyIGRpc3Bvc2FsLiBJZiBhbGwgdGhlIHNwZWNpZXMgd2VyZSBkZXRlcm1pbmVkIGFub3RoZXIgcG9zc2liaWxpdHkgY291bGQgaGF2ZSB0byByZS1jcmVhdGUgYSBwaHlsb2dlbmV0aWMgdHJlZSBmcm9tIGdlbmV0aWMgc2VxdWVuY2VzIGF2YWlsYWJsZSBmcm9tIGdlbmV0aWMgZGF0YWJhc2VzLiBUaGlzIGFwcHJvYWNoIGhvd2V2ZXIgbmVlZHMgc3BlY2lmaWMgc2tpbGxzIGFuZCBpcyBhIHN0b3J5IGZvciBhbm90aGVyIHRpbWUhCgojIyBWaXN1YWxpemluZyB0aGUgcGh5bG9nZW5ldGljIHRyZWUKCldlIGNhbiB2aXN1YWxpemUgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzcGVjaWVzLiBXaXRoIG1vcmUgdGhhbiA2MDAgdGF4YSwgdGhlIHZpc3VhbGl6YXRpb24gY2FuIGJlIHF1aXRlIGNoYWxsZW5naW5nIGFuZCBzb21lIGFqdXN0ZW1lbnRzIHNob3VsZCBiZSBtYWRlIHRvIGVhc2UgdGhlIHZpenVhbGl0aW9uLgoKVGhlIGVhc2llc3Qgd2F5IHRvIHNob3cgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIGlzIHRvIHVzZSB0aGUgYHBsb3QucGh5bG8oKWAgZnVuY3Rpb24gYXZhaWxhYmxlIHRocm91Z2ggdGhlIGBhcGVgIHBhY2thZ2UuCgpgYGB7ciBwbG90LXRyZWV9CmFwZTo6cGxvdC5waHlsbyhwaHlsb190cmVlKQpgYGAKCkJ5IGRlZmF1bHQgdGhlIGZ1bmN0aW9uIHNob3dzIHRoZSBwaHlsb2dyYW0gdHlwZSBvZiBwaHlsb2dlbmV0aWMgdHJlZSBhbmQgcGxvdCBhbGwgdGhlIGxhYmVscyBmb3IgYWxsIHNwZWNpZXMuIExldCdzIG1ha2UgaXQgZWFzaWVyIHRvIHJlYWQ6CgpgYGB7ciBiZXR0ZXItcGxvdC10cmVlfQphcGU6OnBsb3QucGh5bG8ocGh5bG9fdHJlZSwgdHlwZSA9ICJmYW4iLCBzaG93Lm5vZGUubGFiZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgc2hvdy50aXAubGFiZWwgPSBGQUxTRSwgY2V4ID0gMC42KQpgYGAKCkl0IGlzIHN0aWxsIGRpZmZpY3VsdCB0b28gcmVhZCBidXQgd2UgY2FuIGFscmVhZHkgbG9vayBhdCBob3cgYm90YW5pY2FsIGFyZSByZWxhdGVkIHRvIG9uZSBhbm90aGVyLgoKCiMjIENvbXB1dGluZyBwaHlsb2dlbmV0aWMgZGl2ZXJzaXR5IGluZGljZXMKClRvIGNvbXB1dGUgcGh5bG9nZW5ldGljIGRpdmVyc2l0eSBhbmFseXNlcyB3ZSBuZWVkIHRvIGNvbWJpbmUgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIHdpdGggdGhlIHNpdGUtc3BlY2llcyBtYXRyaXguIFdlIG5lZWQgdG8gc3Vic2V0IHRoZSBjb21tdW5pdGllcyBieSBzZWxlY3Rpbmcgb25seSBzcGVjaWVzIHdpdGggYSBkZWZpbmVkIGNvZGUgZnJvbSB0aGUgcHJldmlvdXMgc2VjdGlvbi4KCmBgYHtyIHN1Yi1waHlsby1jb219CiMgSW5pdGlhbCBzaXRlLXNwZWNpZXMgbWF0cml4CmhlYWQoc3BfY29tWywgMTo1XSkKZGltKHNwX2NvbSkKCiMgU3Vic2V0IG9mIHNpdGUtc3BlY2llcyBtYXRyaXggY29tcGF0aWJsZSB3aXRoIHBoeWxvZ2VuZXRpYyB0cmVlCnN1Yl9waHlsb19jb20gPSBzcF9jb21bLCBhcy5jaGFyYWN0ZXIoY29kZV9zcGVjaWVzJHNwZWNpZXMuY29kZSldCmRpbShzdWJfcGh5bG9fY29tKQpgYGAKClRvIG1lYXN1cmUgcGh5bG9nZW5ldGljIGRpdmVyc2l0eSB3ZSB3aWxsIGNvbXB1dGUgdGhlIE1lYW4gUGFpcndpc2UgRGlzdGFuY2UgKE1QRCwgQFdlYmJfRXhwbG9yaW5nXzIwMDApIHVzaW5nIHRoZSBgcGljYW50ZWAgcGFja2FnZS4gVGhlIE1QRCBpcyBhbiBpbmRleCB0aGF0IHJlcHJlc2VudHMgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgYmV0d2VlbiBhbGwgcGFpcnMgb2Ygc3BlY2llcyBvY2N1cnJpbmcgaW4gdGhlIGNvbW11bml0eS4gSXQgY2FuIGFsc28gYmUgd2VpZ2h0ZWQgYnkgdGhlIGFidW5kYW5jZSBvciB0aGUgYmlvbWFzcyBvZiBjb25zaWRlcmVkIHNwZWNpZXMgc28gdGhhdCBtb3JlIHdlaWdodCBpcyBnaXZlbiB0byBzcGVjaWVzIHRoYXQgc2hvdyB0aGUgZ3JlYXRlc3QgYWJ1bmRhbmNlLgoKVGhlIGZpcnN0IGRhdGEgbmVlZGVkIHRvIGNvbXB1dGUgdGhlIE1QRCBpcyB0aGUgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGJldHdlZW4gcGFpciBvZiBzcGVjaWVzLiBXZSdsbCB1c2UgdGhlIGNvcGhlbmV0aWMgZGlzdGFuY2Ugd2hpY2ggcmVwcmVzZW50IHRoZSBzYW1lIHJlbGF0aW9uc2hpcHMgYXMgYSBwaHlsb2dlbmV0aWMgdHJlZSBidXQgdGhyb3VnaCBhIGRpc3RhbmNlIG1hdHJpeC4gV2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGNvcGhlbmV0aWMucGh5bG8oKWAgaW4gdGhlIGBhcGVgIHBhY2thZ2UgdG8gb2J0YWluIGNvcGhlbmV0aWMgZGlzdGFuY2VzLgoKYGBge3IgY29waGVuZXRpYy1kaXN0fQojIENvbXB1dGUgY29waGVuZXRpYyBkaXN0YW5jZXMgZnJvbSB0aGUgcGh5bG9nZW5ldGljIHRyZWUKY29waGVuX2Rpc3QgPSBhcGU6OmNvcGhlbmV0aWMucGh5bG8ocGh5bG9fdHJlZSkKCnN0cihjb3BoZW5fZGlzdCkKCiMgV2UgbmVlZCB0byBjaGFuZ2UgdGhlIG5hbWVzIHRvIHNwZWNpZXMgY29kZXMKY29ycmVzX2NvZGVzID0gZGF0YS5mcmFtZSgKICBzcGVjaWVzID0gcm93bmFtZXMoY29waGVuX2Rpc3QpCikKY29ycmVzX2NvZGVzID0gbWVyZ2UoY29ycmVzX2NvZGVzLCBjb2RlX3NwZWNpZXMsIGJ5ID0gInNwZWNpZXMiKQpyb3duYW1lcyhjb3BoZW5fZGlzdCkgPSBjb3JyZXNfY29kZXMkc3BlY2llcy5jb2RlCmNvbG5hbWVzKGNvcGhlbl9kaXN0KSA9IGNvcnJlc19jb2RlcyRzcGVjaWVzLmNvZGUKYGBgCgpUaGVuIHRvIGNvbXB1dGUgTVBEIHdlIHVzZSB0aGUgYG1wZCgpYCBmdW5jdGlvbiBpbiB0aGUgYHBpY2FudGVgIHBhY2thZ2UuCgpgYGB7ciBtcGR9CiMgT2JzZXJ2ZWQgTWVhbiBQYWlyd2lzZSBEaXN0YW5jZQojIFVud2VpZ2h0ZWQKbXBkX3ZhbF91dyA9IHBpY2FudGU6Om1wZChzdWJfcGh5bG9fY29tLCBjb3BoZW5fZGlzdCwgYWJ1bmRhbmNlLndlaWdodGVkID0gRkFMU0UpCiMgV2VpZ2h0ZWQKbXBkX3ZhbF93ID0gcGljYW50ZTo6bXBkKHN1Yl9waHlsb19jb20sIGNvcGhlbl9kaXN0LCBhYnVuZGFuY2Uud2VpZ2h0ZWQgPSBUUlVFKQoKIyBNYWtlIGEgbmljZSBkYXRhLmZyYW1lIHdpdGggb2JzZXJ2ZWQgTVBEIHZhbHVlcwpvYnNfbXBkID0gZGF0YS5mcmFtZSgKICBwbG90LmNvZGUgPSByb3duYW1lcyhzdWJfcGh5bG9fY29tKSwKICBtcGRfdW53ZWlnaHRlZCA9IG1wZF92YWxfdXcsCiAgbXBkX3dlaWdodGVkID0gbXBkX3ZhbF93CikKCiMgQWRkIGZvcmVzdCBsb3NzIHByb3BvcnRpb24gYW5kIHJpY2huZXNzIGZvciBlYWNoIHNpdGUKb2JzX21wZCA9IG1lcmdlKG9ic19tcGQsIHBsb3RfZGF0YVssIGMoInBsb3QuY29kZSIsICJmb3Jlc3Rsb3NzMTciLCAibnRheGEiKV0pCmBgYAoKOjo6IHsucXVlc3Rpb25zfQojIyMjIFF1ZXN0aW9ucyBmb3IgeW91CgoqICoqUTIzKio6IFdoYXQgaXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB3ZWlnaHRlZCBhbmQgdGhlIHVud2VpZ2h0ZWQgdmVyc2lvbiBvZiB0aGUgTVBEPwoqICoqUTI0Kio6IFdoYXQgYXJlIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gTVBEIGFuZCB0YXhhIHJpY2huZXNzPyBBbmQgd2l0aCBmb3Jlc3QgbG9zcz8gUGxvdCB0aGVzZSByZWxhdGlvbnNoaXBzIHRvIHZpc3VhbGl6ZSB0aGVtIGFuZCB1c2UgdGhlIGBjb3IudGVzdCgpYCBmdW5jdGlvbiB0byB2YWxpZGF0ZSB5b3VyIG9ic2VydmF0aW9ucy4KOjo6CgojIyBOdWxsIG1vZGVsaW5nIAoKQmVjYXVzZSBvZiB0aGUgZXhwZWN0ZWQgcmVsYXRpb25zaGlwIGJldHdlZW4gTVBEIGFuZCBzcGVjaWVzIHJpY2huZXNzLCB3ZSBoYXZlIHRvIHBlcmZvcm0gbnVsbCBtb2RlbHMgaW4gYSBzaW1pbGFyIGZhc2hpb24gdG8gd2hhdCB3ZSd2ZSBkb25lIGZvciBmdW5jdGlvbmFsIGRpdmVyc2l0eSBpbmRpY2VzLiBCZWNhdXNlLCBhcyB3aXRoIGZ1bmN0aW9uYWwgZGl2ZXJzaXR5LCB3ZSB3YW50IHRvIGtlZXAgbnVsbCBzaXRlcyB3aXRoIHNhbWUgdG90YWwgYmlvbWFzcyBhbmQgc2FtZSB0b3RhbCBiaW9tYXNzIHBlciBzcGVjaWVzIGFzIG9ic2VydmVkIHNpdGVzLCB3ZSBjYW4gcGVyZm9ybSBhICJzd2FwIiBudWxsIG1vZGVsLiBXZSB3aWxsIHVzZSBhIG51bGwgbW9kZWwgdGhhdCBzaHVmZmxlIHRoZSBuYW1lcyBvZiB0aGUgc3BlY2llcyBhdCB0aGUgdGlwIG9mIHRoZSBwaHlsb2dlbmV0aWMgdHJlZS4KCkZvcnR1bmF0ZWx5LCBjb21wYXJlZCB0byBmdW5jdGlvbmFsIGRpdmVyc2l0eSwgdGhlIG51bGwgbW9kZWxzIGFyZSBhbGwgaW50ZWdyYXRlZCBpbiB0aGUgYHNlcy5tcGQoKWAgZnVuY3Rpb24gaW4gdGhlIGBwaWNhbnRlYCBwYWNrYWdlLiBUaGUgbnVsbCBtb2RlbCB3ZSdsbCB1c2UgaXMgdGhlIGAidGF4YS5sYWJlbHMiYCBvbmUuICoqQ2F1dGlvbioqOiBudWxsIG1vZGVscyBjYW4gYmUgY29tcHV0YXRpb25hbGx5IGNoYWxsZW5naW5nOyBmb3IgdGhlIHNha2Ugb2YgdGhlIGV4YW1wbGUgd2UnbGwgZG8gb25seSA5OSBpdGVyYXRpb25zIGJ1dCBhcyBmb3IgZnVuY3Rpb25hbCBkaXZlcnNpdHkgYSB2ZXJzaW9uIG9mIHRoZSBudWxsIG1vZGVscyB3aXRoIDk5OSBpdGVyYXRpb25zIGlzIHNhdmVkIGluIHRoZSBkYXRhIGZvbGRlci4KCmBgYHtyIHNlcy1tcGR9CiMgU2V0IHJhbmRvbSBzZWVkIGZvciByZXBlYXRhYmlsaXR5IG9mIGFuYWx5c2lzCnNldC5zZWVkKDIwMjEwNzA1KQoKIyBDb21wdXRlIG51bGwgcGVybXV0YXRpb24gb2YgTVBECnNlc19tcGQgPSBwaWNhbnRlOjpzZXMubXBkKAogIHN1Yl9waHlsb19jb20sIGNvcGhlbl9kaXN0LCBudWxsLm1vZGVsID0gInRheGEubGFiZWxzIiwKICBhYnVuZGFuY2Uud2VpZ2h0ZWQgPSBUUlVFLCBydW5zID0gOTkKKQpoZWFkKHNlc19tcGQpCmBgYAoKVGhlIGZ1bmN0aW9uIGBzZXMubXBkKClgIGNvbXB1dGVzIG1hbnkgdmFsdWVzLiBZb3UgY2FuIGdldCB0aGUgZGV0YWlsIGJ5IGxvb2tpbmcgYXQgdGhlIGhlbHAgb2YgdGhlIGZ1bmN0aW9ucyB3aXRoIGA/cGljYW50ZTo6c2VzLm1wZGAgaW4gdGhlIGBWYWx1ZWAgc2VjdGlvbi4KCldlJ2xsIG5vdyBsb2FkIHRoZSB2ZXJzaW9uIHdpdGggOTk5IGl0ZXJhdGlvbnMuCgpgYGB7ciBzZXMtbXBkLTk5OX0Kc2VzX21wZF85OTkgPSByZWFkUkRTKCJkYXRhL251bGxfbXBkXzk5OS5SZHMiKQpgYGAKCjo6OiB7LnF1ZXN0aW9uc30KIyMjIyBRdWVzdGlvbnMgZm9yIHlvdQoKKiAqKlEyNSoqOiBFeHBsYWluIHdoYXQgZG9lcyB0aGUgY29sdW1uIGBtcGQub2JzLnpgIG1lYW5zPyBIb3cgZG9lcyB0aGlzIGNvbXBhcmUgd2l0aCB0aGUgU0VTIHZhbHVlcyB3ZSBjb21wdXRlZCBmb3IgZnVuY3Rpb25hbCBkaXZlcnNpdHkgaW5kaWNlcz8KKiAqKlEyNioqOiBIb3cgZG9lcyB0aGUgc3RhbmRhcmRpemVkIHZhbHVlIHJlbGF0ZXMgd2l0aCB0YXhhIHJpY2huZXNzPwoqICoqUTI3Kio6IFdoYXQgYXJlIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gTVBEIHZhbHVlcyBjb25zaWRlcmluZyBudWxsIG1vZGVscyBhbmQgZm9yZXN0IGxvc3M/IFZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwcyB3aXRoIHRoZSBgcGxvdCgpYCBmdW5jdGlvbiwgdmFsaWRhdGUgeW91ciBvYnNlcnZhdGlvbnMgd2l0aCB0aGUgYGNvci50ZXN0KClgIGZ1bmN0aW9uLgo6OjoKCiMjIE1hcHBpbmcgcGh5bG9nZW5ldGljIGRpdmVyc2l0eQoKSW4gb3JkZXIgdG8gc2VlIGlmIHRoZXJlIGlzIGEgZ2VvZ3JhcGhpY2FsIHBhdHRlcm4gaW4gcGh5bG9nZW5ldGljIGRpdmVyc2l0eSB3ZSBjYW4gcGxvdCBtYXBzIG9mIE1QRC4KCmBgYHtyIG1hcC1zZXMtbXBkfQpzZXNfbXBkXzk5OSRwbG90LmNvZGUgPSByb3duYW1lcyhzZXNfbXBkXzk5OSkKCmdncGxvdCgpICsKICBnZW9tX3NmKAogICAgZGF0YSA9IG1lcmdlKHN1YnNldChwbG90X3NmLCBibG9jayAhPSAib2ciKSwgc2VzX21wZF85OTksIGJ5ID0gInBsb3QuY29kZSIpLAogICAgYWVzKGNvbG9yID0gbXBkLm9icy56KQogICkgKwogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcih0eXBlID0gImRpdiIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICAiU0VTIG9mIE1QRCIpICsKICBjb29yZF9zZihjcnMgPSBzZjo6c3RfY3JzKDMzNzYpLCB4bGltID0gYyg4NzUwMDAsIDg5MDAwMCksCiAgICAgICAgICAgeWxpbSA9IGMoNTE4NTAwLCA1MzEwMDApKSArCiAgbGFicyh0aXRsZSA9ICJNYXAgb2YgYWxsIHBsb3RzIGJ1dCBibG9jayAnb2cnIikgKwogIGdnc3BhdGlhbDo6YW5ub3RhdGlvbl9zY2FsZSgpICsKICBnZ3NwYXRpYWw6OmFubm90YXRpb25fbm9ydGhfYXJyb3cobG9jYXRpb24gPSAiYnIiKSArCiAgdGhlbWVfZ3JheSgpCmBgYAoKQnkgZXllIGF0IGxlYXN0LCB0aGUgcGF0dGVybiBkb2Vzbid0IHNlZW0gb2J2aW91cyBvbiB0aGUgbWFwLiBBbmQgdGhlIG9ic2VydmVkIFNFUyB2YWx1ZXMgc2VlbXMgdG8gdmFyeSB3aWRlbHkgd2l0aGluIGVhY2ggZm9yZXN0IGJsb2NrLgoKIyMgQ29tcGFyaW5nIGZhY2V0cwoKT25lIGJ1cm5pbmcgcXVlc3Rpb24gaW4gdGhlIHNjaWVudGlmaWMgbGl0ZXJhdHVyZSBhbmQgdGhhdCBpcyBxdWl0ZSBkZWJhdGVkIHN0aWxsIGlzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0YXhvbm9taWMsIGZ1bmN0aW9uYWwsIGFuZCBwaHlsb2dlbmV0aWMgZGl2ZXJzaXR5IFtAUGF2b2luZV9Db3JyZWxhdGlvbnNfMjAxM10uCgpXZSBjYW4gbGV2ZXJhZ2Ugb24gdGhlIGNvbXB1dGF0aW9uIHdlIGhhdmUgdG8gdGVzdCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGFsbCBmYWNldHMgb2YgZGl2ZXJzaXR5LiAqKi8hXFwgTk9URSoqOiBCZWNhdXNlIHdlIGhhZCB0cm91YmxlIHdpdGggdGhlIHBoeWxvZ2VuZXRpYyB0cmVlLCB3ZSdyZSBub3Qgc3RyaWN0bHkgY29tcGFyaW5nIHRoZSBzYW1lIHN1YnNldCBvZiBkYXRhLCB3ZSdyZSBnb2luZyB0byBjb21wYXJlIHRoZW0gYW55d2F5IGZvciB0aGUgc2FrZSBvZiB0aGUgZXhhbXBsZS4gVGhlIHByb3BlciB3YXkgd291bGQgYmUgdG8gc3Vic2V0IHRoZSBzaW1pbGFyIHNldHMgb2Ygc3BlY2llcyBhbmQgcmVjb21wdXRlIGZ1bmN0aW9uYWwgZGl2ZXJzaXR5LgoKYGBge3IgYWxsLWRpdmVyc2l0eS1mYWNldHN9CiMgQ29tYmluZSB0YXhvbm9taWMsIGZ1bmN0aW9uYWwsIGFuZCBwaHlsb2dlbmV0aWMgZGl2ZXJzaXR5CmFsbF9kaXZlcnNpdHkgPSBtZXJnZSgKICBwbG90X2RhdGFbLCBjKCJwbG90LmNvZGUiLCAibnRheGEiKV0sCiAgbWVyZ2UoCiAgICBzZXNfZmQsIHNlc19tcGRfOTk5WywgLTFdLCBieSA9ICJwbG90LmNvZGUiCiAgKQopCgojIENvbXBhcmlzb24gb2Ygb2JzZXJ2ZWQgdmFsdWVzCnBhaXJzKGFsbF9kaXZlcnNpdHlbLCBjKCJudGF4YSIsICJGUmljIiwgIlEiLCAiRkV2ZSIsICJtcGQub2JzIildLAogICAgICB1cHBlci5wYW5lbCA9IHBhbmVsLmNvcikKCiMgQ29tcGFyaXNvbiBvZiBTRVNzCnBhaXJzKGFsbF9kaXZlcnNpdHlbLCBjKCJudGF4YSIsICJzZXNfRlJpYyIsICJzZXNfUSIsICJzZXNfRkV2ZSIsICJtcGQub2JzLnoiKV0sCiAgICAgIHVwcGVyLnBhbmVsID0gcGFuZWwuY29yKQpgYGAKCjo6OiB7LnF1ZXN0aW9uc30KIyMjIyBRdWVzdGlvbiBmb3IgeW91CgoqICoqUTI4Kio6IEhvdyBhcmUgcmVsYXRlZCBhcmUgb2JzZXJ2ZWQgdmFsdWVzIG9mIGZ1bmN0aW9uYWwgZGl2ZXJzaXR5IGFuZCBwaHlsb2dlbmV0aWMgZGl2ZXJzaXR5PyBXaGF0IGFib3V0IHRoZSBTRVNzPwo6OjoKCiMjIFJlZmVyZW5jZXM=


License: Matthias Grenié & Marten Winter CC-BY 4.0