Habitat Configuration

The spatial distribution of habitat features can have many direct impacts on the way organisms occupy and move across a landscape. In this topic, we will provide a brief overview of how we can quantify habitat types and insert their metrics into our data workflows.

What is a Landscape?

Ecologists Perspective

A landscape is a mosaic of interacting ecosystems which are heterogeneous in at least one factor of interest.

Organism Perspective

The spatial distribution of suitable habitat1.

We can think of landscapes being described in the following categories of increasing complexity

  • Landscape Type - Categories of landscape features (binary)
  • Landscape Composition - Relative abundance of landscape features (continuous)
  • Landscape Configuration - Spatial arrangement of of landscape features (raster)

So let’s grab some raw data and look at it

Raw Data

The Rice Center

For this exercise we are going to be looking at the area around the Rice Rivers Center using the national

The Multi-Resolution Land Characteristics Consortium mrlc.gov maintains a spatial database of land cover types for the US. If you were to download these in their entirety, they are huge and you’d spend quite a bit of time doing the “download \(\to\) open in R \(\to\) define boundary box \(\to\) crop and save” kind of stuff. Fortunately, there is an interactive viewer and download tool at https://www.mrlc.gov/viewer/ that works just fine for us.

mrlc interactive vierwer

Here we are going to work with land-cover types and for this, I’m taking a 25km\(^2\) box that includes the Rice Rivers Center. The data we get are in the form of a GeoTIFF, which we’ve used before in the rasters lecture. The interface to get the data is one that has you specify the are and then submit a request with your email to get a download. Depending upon the size of your request and the current traffic, it may take a while to get the rasters. When you do they’ll send you a link to a zip file you can grab.

For our purposes, I uploaded the land cover data from 2016 to Github and it can be found at the following url.

library( raster )

land_2016_url <- "https://github.com/dyerlab/ENVS-Lectures/raw/master/data/NLCD_2016_Land_Cover_L48_20190424_qn2B1f8ganicJNKnJN0e.tiff"
land_2016 <- raster( land_2016_url )
land_2016

Notice the following from the output:
- The raster has 6806 pixels (almost square…)
- The CRS has units of meters
- The resolution of each pixel is 30x30.

and it looks like:

plot( land_2016 )

The actual values of the raster include the following categories.

values( land_2016 ) %>%
  table() 

This is not uncommon. Whomever decided to make this raster has defined some kind of numbering scheme for each pixel. In the Zip file that you get form NLCD, there is a CSV file that has the legend. I’ve also uploaded that one, so lets go grab this and do a left_join() on this baby!

legend_url <- "https://raw.githubusercontent.com/dyerlab/ENVS-Lectures/master/data/NLCD_landcover_legend_2018_12_17_qn2B1f8ganicJNKnJN0e.csv"
legend <- read_csv( legend_url )

If you look at the data you’ll see that out of the 17 entries, 0 of them are NA. Again, I’m sure there is some good reason why this is the case, it is just not obvious to me at this time what that may be other than historic reasons2.

legend %>%
  filter( !is.na(Legend))

Describing Landscapes - Feature Types

From above, we defined three kinds of landscape analyses, the first of which was a listing of the types of features on landscape.

values( land_2016 ) %>%
  table() %>% 
  as_tibble() -> landscape_types
landscape_types

So the table() function does not really do much in the way of naming stuff, so let’s fix these.

names( landscape_types ) <- c("Value", "Frequency")
landscape_types

Now we can reach back to our relational data and use the Value column as the foreign key into the data from the legend where Value is the Primary Key.

legend %>%
  left_join( landscape_types, by="Value")

Ah yesss. If we look at the Value in the landscape types, it is (for some reason) a character representation! Let’s fix that and then make another run at it.

landscape_types$Value <- as.numeric(landscape_types$Value)
legend %>%
  left_join( landscape_types, by="Value") %>%
  filter( !is.na(Frequency) ) -> landscape_types
unique(landscape_types$Legend)

Describing Landscapes - Feature Composition

The next level of description is to examine the relative frequency of each of these habitat types on the landscape. Let’s grab the data and summarize it by the relative frequencies of the present types.

landscape_types %>%
  mutate( Cover = Frequency / sum(!is.na(values(land_2016))) ) %>%
  select( Legend, Value, Frequency, Cover) %>%
  arrange( -Cover ) %>%
  knitr::kable() %>%
  kableExtra::kable_material_dark(full_width = FALSE )

Describing Landscapes - Spatial Configuration

There are a lot more metrics that we can estimate from landscape features than just identity and frequency and what we will be covering here is just a bit off the top. We can loosely categorize the kinds of metrics into the following groups:

We are mostly going to focus on Area & Edge as well as Diversity metrics here for simplicity. If you take a course in Landscape Ecology, you can get into the other types. I’m going to use the landscapemetrics library (if you do not have it, then install it using install.packages()).

There are some requirements for your raster to use this library:

  1. The distance units of your projection are meter, as the package converts units internally and returns results in either meters, square meters or hectares. For more information see the help file of each function.

  2. Your raster encodes landscape classes as integers (1, 2, 3, 4, …, n).

  3. Landscape metrics describe categorical landscapes, that means that your landscape needs to be classified (we throw a warning if you have more than 30 classes to make sure you work with a classified landscape).

There is a check_landscape() function that you can use and find out. Here is a raster that is not good.

library( landscapemetrics )
check_landscape( landscapemetrics::podlasie_ccilc )

Fortunately, this library was made with the NLCD data in mind, so ours works just fine.

check_landscape( land_2016 )

We can examine landscape metrics at several different nested scales, the landscape at a whole, the patch (spatially contiguous habitat types), and the class level (all values for “Barren Land” for example). For the landscape level, the following metrics are available.

list_lsm(level="landscape")

At the patch level, the following metrics are available.

list_lsm(level="patch")

And for each class of feature, the following are available.

list_lsm(level="class")

Take a look through these metric types for each level. For each of these metrics, there is a function whose names has the following configuration

lsm_{l|p|c}_metric()

where {l|p|c} is one of l, p, or c and the metric is the name as shown above. So, for example, if we are looking at the area of each patch in the landscape we would use the function

lsm_p_area( land_2016 ) 

As you see, it divides the landscape into individual patches, numbers them, and provides metric feedback. Let’s summarize for mixed (type=43) and evergreen (type = 42) forests.

lsm_p_area( land_2016 ) %>%
  filter( class %in% c(42, 43) ) %>%
  mutate( Forest = ifelse( class == 42, "Evergreen", "Mixed Hardwoods") ) %>%
  ggplot( aes(value) ) + 
  geom_density() + 
  facet_grid( Forest ~ .) +
  xlab("Area (ha)") + 
  ylab("Frequency") + 
  ggtitle("Area Distribution of Mixed Forest Types")

Other metrics you may be interested in include (use View( list_lsm()) to see them all):

Finding Patches

To connect back to the spatial arrangement of features, we can use the following functions to point out where specific patches may be on the original landscape. For example, we can plot the spatial location of the most common types (say the first 3) of habitat as:

lsm_p_area( land_2016 ) %>%
  group_by( class ) %>%
  summarize( `Area` = sum( value) ) %>%
  arrange(-Area)

Which turn out to

plot( get_patches( land_2016, class=43)[[1]] ) 
plot( get_patches( land_2016, class=90)[[1]] ) 
plot( get_patches( land_2016, class=43)[[1]] ) 

The difference in the colors of these patches reflect the patch ID number that lsm_* give them.


  1. A particular section of land may represent many differnt ‘landscapes’ when taken from the perspective of different organisms.↩︎

  2. In the very old days of computing, like when I started, we had so little memory on the computers that we had to pack information as tightly as possible, which is why things like numbrer naming schemes and look-up tables were necessary↩︎

LS0tCnRpdGxlOiAiU3BhdGlhbCBIYWJpdGF0IEFuYWx5c2lzIGluIFIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCB0aWR5dmVyc2UgKQpsaWJyYXJ5KCByYXN0ZXIgKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoIHRoZW1lX2J3KCApKQpgYGAKCgoKIyMgSGFiaXRhdCBDb25maWd1cmF0aW9uCgpUaGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgaGFiaXRhdCBmZWF0dXJlcyBjYW4gaGF2ZSBtYW55IGRpcmVjdCBpbXBhY3RzIG9uIHRoZSB3YXkgb3JnYW5pc21zIG9jY3VweSBhbmQgbW92ZSBhY3Jvc3MgYSBsYW5kc2NhcGUuICBJbiB0aGlzIHRvcGljLCB3ZSB3aWxsIHByb3ZpZGUgYSBicmllZiBvdmVydmlldyBvZiBob3cgd2UgY2FuIHF1YW50aWZ5IGhhYml0YXQgdHlwZXMgYW5kIGluc2VydCB0aGVpciBtZXRyaWNzIGludG8gb3VyIGRhdGEgd29ya2Zsb3dzLgoKCgoKCiMjIyBXaGF0IGlzIGEgTGFuZHNjYXBlPwoKRWNvbG9naXN0cyBQZXJzcGVjdGl2ZQoKPiBBIGxhbmRzY2FwZSBpcyBhIG1vc2FpYyBvZiBpbnRlcmFjdGluZyBlY29zeXN0ZW1zIHdoaWNoIGFyZSBoZXRlcm9nZW5lb3VzIGluIGF0IGxlYXN0IG9uZSBmYWN0b3Igb2YgaW50ZXJlc3QuCgpPcmdhbmlzbSBQZXJzcGVjdGl2ZQoKPiBUaGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2Ygc3VpdGFibGUgaGFiaXRhdF5bQSBwYXJ0aWN1bGFyIHNlY3Rpb24gb2YgbGFuZCBtYXkgcmVwcmVzZW50IG1hbnkgZGlmZmVybnQgJ2xhbmRzY2FwZXMnIHdoZW4gdGFrZW4gZnJvbSB0aGUgcGVyc3BlY3RpdmUgb2YgZGlmZmVyZW50IG9yZ2FuaXNtcy5dLgoKCgpXZSBjYW4gdGhpbmsgb2YgbGFuZHNjYXBlcyBiZWluZyBkZXNjcmliZWQgaW4gdGhlIGZvbGxvd2luZyBjYXRlZ29yaWVzIG9mIGluY3JlYXNpbmcgY29tcGxleGl0eQoKLSBMYW5kc2NhcGUgVHlwZSAtIENhdGVnb3JpZXMgb2YgbGFuZHNjYXBlIGZlYXR1cmVzIChiaW5hcnkpCi0gTGFuZHNjYXBlIENvbXBvc2l0aW9uIC0gUmVsYXRpdmUgYWJ1bmRhbmNlIG9mIGxhbmRzY2FwZSBmZWF0dXJlcyAoY29udGludW91cykKLSBMYW5kc2NhcGUgQ29uZmlndXJhdGlvbiAtIFNwYXRpYWwgYXJyYW5nZW1lbnQgb2Ygb2YgbGFuZHNjYXBlIGZlYXR1cmVzIChyYXN0ZXIpCgpTbyBsZXQncyBncmFiIHNvbWUgcmF3IGRhdGEgYW5kIGxvb2sgYXQgaXQgCgojIyMgUmF3IERhdGEKCgo8Y2VudGVyPgohW1RoZSBSaWNlIENlbnRlcl0oaHR0cHM6Ly9saXZlLnN0YXRpY2ZsaWNrci5jb20vNjU1MzUvNTA5OTg0OTgxNjlfMGU1MDJiYzdiYV9jX2QuanBnKQo8L2NlbnRlcj4KCkZvciB0aGlzIGV4ZXJjaXNlIHdlIGFyZSBnb2luZyB0byBiZSBsb29raW5nIGF0IHRoZSBhcmVhIGFyb3VuZCB0aGUgUmljZSBSaXZlcnMgQ2VudGVyIHVzaW5nIHRoZSBuYXRpb25hbCAKCgoKVGhlIE11bHRpLVJlc29sdXRpb24gTGFuZCBDaGFyYWN0ZXJpc3RpY3MgQ29uc29ydGl1bSBbbXJsYy5nb3ZdKGh0dHBzOi8vd3d3Lm1ybGMuZ292KSBtYWludGFpbnMgYSBzcGF0aWFsIGRhdGFiYXNlIG9mIGxhbmQgY292ZXIgdHlwZXMgZm9yIHRoZSBVUy4gIElmIHlvdSB3ZXJlIHRvIGRvd25sb2FkIHRoZXNlIGluIHRoZWlyIGVudGlyZXR5LCB0aGV5IGFyZSAqKmh1Z2UqKiBhbmQgeW91J2Qgc3BlbmQgcXVpdGUgYSBiaXQgb2YgdGltZSBkb2luZyB0aGUgImRvd25sb2FkICRcdG8kIG9wZW4gaW4gUiAkXHRvJCBkZWZpbmUgYm91bmRhcnkgYm94ICRcdG8kIGNyb3AgYW5kIHNhdmUiIGtpbmQgb2Ygc3R1ZmYuICBGb3J0dW5hdGVseSwgdGhlcmUgaXMgYW4gaW50ZXJhY3RpdmUgdmlld2VyIGFuZCBkb3dubG9hZCB0b29sIGF0IGh0dHBzOi8vd3d3Lm1ybGMuZ292L3ZpZXdlci8gdGhhdCB3b3JrcyBqdXN0IGZpbmUgZm9yIHVzLiAgCgohW21ybGMgaW50ZXJhY3RpdmUgdmllcndlcl0oaHR0cHM6Ly9saXZlLnN0YXRpY2ZsaWNrci5jb20vNjU1MzUvNTEwMTQyNDE2MjVfZjA4OTRlYzQzMl9jX2QuanBnKQoKSGVyZSB3ZSBhcmUgZ29pbmcgdG8gd29yayB3aXRoIGxhbmQtY292ZXIgdHlwZXMgYW5kIGZvciB0aGlzLCBJJ20gdGFraW5nIGEgMjVrbSReMiQgYm94IHRoYXQgaW5jbHVkZXMgdGhlIFtSaWNlIFJpdmVycyBDZW50ZXJdKGh0dHBzOi8vcmljZXJpdmVycy52Y3UuZWR1KS4gIFRoZSBkYXRhIHdlIGdldCBhcmUgaW4gdGhlIGZvcm0gb2YgYSBHZW9USUZGLCB3aGljaCB3ZSd2ZSB1c2VkIGJlZm9yZSBpbiB0aGUgW3Jhc3RlcnNdKGh0dHBzOi8vZHllcmxhYi5naXRodWIuaW8vRU5WUy1MZWN0dXJlcy9zcGF0aWFsL3Jhc3RlcnMvc2xpZGVzLmh0bWwjMSkgbGVjdHVyZS4gIFRoZSBpbnRlcmZhY2UgdG8gZ2V0IHRoZSBkYXRhIGlzIG9uZSB0aGF0IGhhcyB5b3Ugc3BlY2lmeSB0aGUgYXJlIGFuZCB0aGVuIHN1Ym1pdCBhIHJlcXVlc3Qgd2l0aCB5b3VyIGVtYWlsIHRvIGdldCBhIGRvd25sb2FkLiAgRGVwZW5kaW5nIHVwb24gdGhlIHNpemUgb2YgeW91ciByZXF1ZXN0IGFuZCB0aGUgY3VycmVudCB0cmFmZmljLCBpdCBtYXkgdGFrZSBhIHdoaWxlIHRvIGdldCB0aGUgcmFzdGVycy4gIFdoZW4geW91IGRvIHRoZXknbGwgc2VuZCB5b3UgYSBsaW5rIHRvIGEgemlwIGZpbGUgeW91IGNhbiBncmFiLgoKRm9yIG91ciBwdXJwb3NlcywgSSB1cGxvYWRlZCB0aGUgbGFuZCBjb3ZlciBkYXRhIGZyb20gMjAxNiB0byBHaXRodWIgYW5kIGl0IGNhbiBiZSBmb3VuZCBhdCB0aGUgZm9sbG93aW5nIHVybC4KCgpgYGB7cn0KbGlicmFyeSggcmFzdGVyICkKCmxhbmRfMjAxNl91cmwgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS9keWVybGFiL0VOVlMtTGVjdHVyZXMvcmF3L21hc3Rlci9kYXRhL05MQ0RfMjAxNl9MYW5kX0NvdmVyX0w0OF8yMDE5MDQyNF9xbjJCMWY4Z2FuaWNKTktuSk4wZS50aWZmIgpsYW5kXzIwMTYgPC0gcmFzdGVyKCBsYW5kXzIwMTZfdXJsICkKbGFuZF8yMDE2CmBgYAoKTm90aWNlIHRoZSBmb2xsb3dpbmcgZnJvbSB0aGUgb3V0cHV0OiAgCi0gVGhlIHJhc3RlciBoYXMgNjgwNiBwaXhlbHMgKGFsbW9zdCBzcXVhcmUuLi4pICAKLSBUaGUgQ1JTIGhhcyB1bml0cyBvZiBtZXRlcnMgIAotIFRoZSByZXNvbHV0aW9uIG9mIGVhY2ggcGl4ZWwgaXMgMzB4MzAuCgphbmQgaXQgbG9va3MgbGlrZToKCmBgYHtyfQpwbG90KCBsYW5kXzIwMTYgKQpgYGAKCgpUaGUgYWN0dWFsIHZhbHVlcyBvZiB0aGUgcmFzdGVyIGluY2x1ZGUgdGhlIGZvbGxvd2luZyBjYXRlZ29yaWVzLgoKYGBge3J9CnZhbHVlcyggbGFuZF8yMDE2ICkgJT4lCiAgdGFibGUoKSAKYGBgCgpUaGlzIGlzIG5vdCB1bmNvbW1vbi4gV2hvbWV2ZXIgZGVjaWRlZCB0byBtYWtlIHRoaXMgcmFzdGVyIGhhcyBkZWZpbmVkIHNvbWUga2luZCBvZiBudW1iZXJpbmcgc2NoZW1lIGZvciBlYWNoIHBpeGVsLiAgSW4gdGhlIFppcCBmaWxlIHRoYXQgeW91IGdldCBmb3JtIE5MQ0QsIHRoZXJlIGlzIGEgQ1NWIGZpbGUgdGhhdCBoYXMgdGhlIGxlZ2VuZC4gIEkndmUgYWxzbyB1cGxvYWRlZCB0aGF0IG9uZSwgc28gbGV0cyBnbyBncmFiIHRoaXMgYW5kIGRvIGEgYGxlZnRfam9pbigpYCBvbiB0aGlzIGJhYnkhCgpgYGB7cn0KbGVnZW5kX3VybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2R5ZXJsYWIvRU5WUy1MZWN0dXJlcy9tYXN0ZXIvZGF0YS9OTENEX2xhbmRjb3Zlcl9sZWdlbmRfMjAxOF8xMl8xN19xbjJCMWY4Z2FuaWNKTktuSk4wZS5jc3YiCmxlZ2VuZCA8LSByZWFkX2NzdiggbGVnZW5kX3VybCApCmBgYAoKSWYgeW91IGxvb2sgYXQgdGhlIGRhdGEgeW91J2xsIHNlZSB0aGF0IG91dCBvZiB0aGUgYHIgbnJvdyhsZWdlbmQpYCBlbnRyaWVzLCBgciBzdW0oaXMubmEobGVnZW5kJExlZ2VuZCkpYCBvZiB0aGVtIGFyZSBgTkFgLiAgQWdhaW4sIEknbSBzdXJlIHRoZXJlIGlzIHNvbWUgZ29vZCByZWFzb24gd2h5IHRoaXMgaXMgdGhlIGNhc2UsIGl0IGlzIGp1c3Qgbm90IG9idmlvdXMgdG8gbWUgYXQgdGhpcyB0aW1lIHdoYXQgdGhhdCBtYXkgYmUgb3RoZXIgdGhhbiBoaXN0b3JpYyByZWFzb25zXltJbiB0aGUgdmVyeSBvbGQgZGF5cyBvZiBjb21wdXRpbmcsIGxpa2Ugd2hlbiBJIHN0YXJ0ZWQsIHdlIGhhZCBzbyBsaXR0bGUgbWVtb3J5IG9uIHRoZSBjb21wdXRlcnMgdGhhdCB3ZSBoYWQgdG8gcGFjayBpbmZvcm1hdGlvbiBhcyB0aWdodGx5IGFzIHBvc3NpYmxlLCB3aGljaCBpcyB3aHkgdGhpbmdzIGxpa2UgbnVtYnJlciBuYW1pbmcgc2NoZW1lcyBhbmQgbG9vay11cCB0YWJsZXMgd2VyZSBuZWNlc3NhcnldLgoKYGBge3J9CmxlZ2VuZCAlPiUKICBmaWx0ZXIoICFpcy5uYShMZWdlbmQpKQoKYGBgCgojIyBEZXNjcmliaW5nIExhbmRzY2FwZXMgLSBGZWF0dXJlIFR5cGVzCgpGcm9tIGFib3ZlLCB3ZSBkZWZpbmVkIHRocmVlIGtpbmRzIG9mIGxhbmRzY2FwZSBhbmFseXNlcywgdGhlIGZpcnN0IG9mIHdoaWNoIHdhcyBhIGxpc3Rpbmcgb2YgdGhlIHR5cGVzIG9mIGZlYXR1cmVzIG9uIGxhbmRzY2FwZS4gIAoKCmBgYHtyfQp2YWx1ZXMoIGxhbmRfMjAxNiApICU+JQogIHRhYmxlKCkgJT4lIAogIGFzX3RpYmJsZSgpIC0+IGxhbmRzY2FwZV90eXBlcwpsYW5kc2NhcGVfdHlwZXMKYGBgCgpTbyB0aGUgYHRhYmxlKClgIGZ1bmN0aW9uIGRvZXMgbm90IHJlYWxseSBkbyBtdWNoIGluIHRoZSB3YXkgb2YgbmFtaW5nIHN0dWZmLCBzbyBsZXQncyBmaXggdGhlc2UuCgpgYGB7cn0KbmFtZXMoIGxhbmRzY2FwZV90eXBlcyApIDwtIGMoIlZhbHVlIiwgIkZyZXF1ZW5jeSIpCmxhbmRzY2FwZV90eXBlcwpgYGAKCk5vdyB3ZSBjYW4gcmVhY2ggYmFjayB0byBvdXIgcmVsYXRpb25hbCBkYXRhIGFuZCB1c2UgdGhlIGBWYWx1ZWAgY29sdW1uIGFzIHRoZSBmb3JlaWduIGtleSBpbnRvIHRoZSBkYXRhIGZyb20gdGhlIGxlZ2VuZCB3aGVyZSBgVmFsdWVgIGlzIHRoZSBgUHJpbWFyeSBLZXlgLgoKYGBge3J9CmxlZ2VuZCAlPiUKICBsZWZ0X2pvaW4oIGxhbmRzY2FwZV90eXBlcywgYnk9IlZhbHVlIikKYGBgCgpBaCB5ZXNzcy4gIElmIHdlIGxvb2sgYXQgdGhlIGBWYWx1ZWAgaW4gdGhlIGxhbmRzY2FwZSB0eXBlcywgaXQgaXMgKGZvciBzb21lIHJlYXNvbikgYSBgY2hhcmFjdGVyYCByZXByZXNlbnRhdGlvbiEgIExldCdzIGZpeCB0aGF0IGFuZCB0aGVuIG1ha2UgYW5vdGhlciBydW4gYXQgaXQuCgoKYGBge3J9CmxhbmRzY2FwZV90eXBlcyRWYWx1ZSA8LSBhcy5udW1lcmljKGxhbmRzY2FwZV90eXBlcyRWYWx1ZSkKbGVnZW5kICU+JQogIGxlZnRfam9pbiggbGFuZHNjYXBlX3R5cGVzLCBieT0iVmFsdWUiKSAlPiUKICBmaWx0ZXIoICFpcy5uYShGcmVxdWVuY3kpICkgLT4gbGFuZHNjYXBlX3R5cGVzCnVuaXF1ZShsYW5kc2NhcGVfdHlwZXMkTGVnZW5kKQpgYGAKCgoKIyMgRGVzY3JpYmluZyBMYW5kc2NhcGVzIC0gRmVhdHVyZSBDb21wb3NpdGlvbgoKVGhlIG5leHQgbGV2ZWwgb2YgZGVzY3JpcHRpb24gaXMgdG8gZXhhbWluZSB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IG9mIGVhY2ggb2YgdGhlc2UgaGFiaXRhdCB0eXBlcyBvbiB0aGUgbGFuZHNjYXBlLiAgTGV0J3MgZ3JhYiB0aGUgZGF0YSBhbmQgc3VtbWFyaXplIGl0IGJ5IHRoZSByZWxhdGl2ZSBmcmVxdWVuY2llcyBvZiB0aGUgcHJlc2VudCB0eXBlcy4KCgpgYGB7cn0KbGFuZHNjYXBlX3R5cGVzICU+JQogIG11dGF0ZSggQ292ZXIgPSBGcmVxdWVuY3kgLyBzdW0oIWlzLm5hKHZhbHVlcyhsYW5kXzIwMTYpKSkgKSAlPiUKICBzZWxlY3QoIExlZ2VuZCwgVmFsdWUsIEZyZXF1ZW5jeSwgQ292ZXIpICU+JQogIGFycmFuZ2UoIC1Db3ZlciApICU+JQogIGtuaXRyOjprYWJsZSgpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX21hdGVyaWFsX2RhcmsoZnVsbF93aWR0aCA9IEZBTFNFICkKYGBgCgojIyBEZXNjcmliaW5nIExhbmRzY2FwZXMgLSBTcGF0aWFsIENvbmZpZ3VyYXRpb24KClRoZXJlIGFyZSAqYSBsb3QqIG1vcmUgbWV0cmljcyB0aGF0IHdlIGNhbiBlc3RpbWF0ZSBmcm9tIGxhbmRzY2FwZSBmZWF0dXJlcyB0aGFuIGp1c3QgaWRlbnRpdHkgYW5kIGZyZXF1ZW5jeSBhbmQgd2hhdCB3ZSB3aWxsIGJlIGNvdmVyaW5nIGhlcmUgaXMganVzdCBhIGJpdCBvZmYgdGhlIHRvcC4gIFdlIGNhbiBsb29zZWx5IGNhdGVnb3JpemUgdGhlIGtpbmRzIG9mIG1ldHJpY3MgaW50byB0aGUgZm9sbG93aW5nIGdyb3VwczogIAoKLSBBZ2dyZWdhdGlvbiBtZXRyaWNzICAKLSBBcmVhICYgRWRnZSBtZXRyaWNzICAKLSBDb250cmFzdCBtZXRyaWNzICAKLSBDb3JlIGFyZWEgbWV0cmljcyAgCi0gRGl2ZXJzaXR5IG1ldHJpY3MgIAotIElzb2xhdGlvbiBtZXRyaWNzICAKLSBTaGFwZSBtZXRyaWNzICAKLSBTdWJkaXZpc2lvbiBtZXRyaWNzCgpXZSBhcmUgbW9zdGx5IGdvaW5nIHRvIGZvY3VzIG9uIEFyZWEgJiBFZGdlIGFzIHdlbGwgYXMgRGl2ZXJzaXR5IG1ldHJpY3MgaGVyZSBmb3Igc2ltcGxpY2l0eS4gIElmIHlvdSB0YWtlIGEgY291cnNlIGluIExhbmRzY2FwZSBFY29sb2d5LCB5b3UgY2FuIGdldCBpbnRvIHRoZSBvdGhlciB0eXBlcy4gSSdtIGdvaW5nIHRvIHVzZSB0aGUgYGxhbmRzY2FwZW1ldHJpY3NgIGxpYnJhcnkgKGlmIHlvdSBkbyBub3QgaGF2ZSBpdCwgdGhlbiBpbnN0YWxsIGl0IHVzaW5nIGBpbnN0YWxsLnBhY2thZ2VzKClgKS4KClRoZXJlIGFyZSBzb21lIHJlcXVpcmVtZW50cyBmb3IgeW91ciByYXN0ZXIgdG8gdXNlIHRoaXMgbGlicmFyeTogIAoKMS4gVGhlIGRpc3RhbmNlIHVuaXRzIG9mIHlvdXIgcHJvamVjdGlvbiBhcmUgbWV0ZXIsIGFzIHRoZSBwYWNrYWdlIGNvbnZlcnRzIHVuaXRzIGludGVybmFsbHkgYW5kIHJldHVybnMgcmVzdWx0cyBpbiBlaXRoZXIgbWV0ZXJzLCBzcXVhcmUgbWV0ZXJzIG9yIGhlY3RhcmVzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgdGhlIGhlbHAgZmlsZSBvZiBlYWNoIGZ1bmN0aW9uLiAgCgoyLiBZb3VyIHJhc3RlciBlbmNvZGVzIGxhbmRzY2FwZSBjbGFzc2VzIGFzIGludGVnZXJzICgxLCAyLCAzLCA0LCDigKYsIG4pLiAgCgozLiBMYW5kc2NhcGUgbWV0cmljcyBkZXNjcmliZSBjYXRlZ29yaWNhbCBsYW5kc2NhcGVzLCB0aGF0IG1lYW5zIHRoYXQgeW91ciBsYW5kc2NhcGUgbmVlZHMgdG8gYmUgY2xhc3NpZmllZCAod2UgdGhyb3cgYSB3YXJuaW5nIGlmIHlvdSBoYXZlIG1vcmUgdGhhbiAzMCBjbGFzc2VzIHRvIG1ha2Ugc3VyZSB5b3Ugd29yayB3aXRoIGEgY2xhc3NpZmllZCBsYW5kc2NhcGUpLiAgCgpUaGVyZSBpcyBhIGBjaGVja19sYW5kc2NhcGUoKWAgZnVuY3Rpb24gdGhhdCB5b3UgY2FuIHVzZSBhbmQgZmluZCBvdXQuICBIZXJlIGlzIGEgcmFzdGVyIHRoYXQgaXMgbm90IGdvb2QuCgpgYGB7cn0KbGlicmFyeSggbGFuZHNjYXBlbWV0cmljcyApCmNoZWNrX2xhbmRzY2FwZSggbGFuZHNjYXBlbWV0cmljczo6cG9kbGFzaWVfY2NpbGMgKQpgYGAKCkZvcnR1bmF0ZWx5LCB0aGlzIGxpYnJhcnkgd2FzIG1hZGUgd2l0aCB0aGUgTkxDRCBkYXRhIGluIG1pbmQsIHNvIG91cnMgd29ya3MganVzdCBmaW5lLgoKYGBge3J9CmNoZWNrX2xhbmRzY2FwZSggbGFuZF8yMDE2ICkKYGBgCgpXZSBjYW4gZXhhbWluZSBsYW5kc2NhcGUgbWV0cmljcyBhdCBzZXZlcmFsIGRpZmZlcmVudCBuZXN0ZWQgc2NhbGVzLCB0aGUgbGFuZHNjYXBlIGF0IGEgd2hvbGUsIHRoZSBwYXRjaCAoc3BhdGlhbGx5IGNvbnRpZ3VvdXMgaGFiaXRhdCB0eXBlcyksIGFuZCB0aGUgY2xhc3MgbGV2ZWwgKGFsbCB2YWx1ZXMgZm9yICJCYXJyZW4gTGFuZCIgZm9yIGV4YW1wbGUpLiAgRm9yIHRoZSBsYW5kc2NhcGUgbGV2ZWwsIHRoZSBmb2xsb3dpbmcgbWV0cmljcyBhcmUgYXZhaWxhYmxlLgoKYGBge3J9Cmxpc3RfbHNtKGxldmVsPSJsYW5kc2NhcGUiKQpgYGAKCkF0IHRoZSBwYXRjaCBsZXZlbCwgdGhlIGZvbGxvd2luZyBtZXRyaWNzIGFyZSBhdmFpbGFibGUuCgpgYGB7cn0KbGlzdF9sc20obGV2ZWw9InBhdGNoIikKYGBgCgoKQW5kIGZvciBlYWNoIGNsYXNzIG9mIGZlYXR1cmUsIHRoZSBmb2xsb3dpbmcgYXJlIGF2YWlsYWJsZS4KCmBgYHtyfQpsaXN0X2xzbShsZXZlbD0iY2xhc3MiKQpgYGAKClRha2UgYSBsb29rIHRocm91Z2ggdGhlc2UgbWV0cmljIHR5cGVzIGZvciBlYWNoIGxldmVsLiAgRm9yIGVhY2ggb2YgdGhlc2UgbWV0cmljcywgdGhlcmUgaXMgYSBmdW5jdGlvbiB3aG9zZSBuYW1lcyBoYXMgdGhlIGZvbGxvd2luZyBjb25maWd1cmF0aW9uCgpgbHNtX3tsfHB8Y31fbWV0cmljKClgCgp3aGVyZSBge2x8cHxjfWAgaXMgb25lIG9mIGBsYCwgYHBgLCBvciBgY2AgYW5kIHRoZSBtZXRyaWMgaXMgdGhlIG5hbWUgYXMgc2hvd24gYWJvdmUuICBTbywgZm9yIGV4YW1wbGUsIGlmIHdlIGFyZSBsb29raW5nIGF0IHRoZSBhcmVhIG9mIGVhY2ggcGF0Y2ggaW4gdGhlIGxhbmRzY2FwZSB3ZSB3b3VsZCB1c2UgdGhlIGZ1bmN0aW9uCgpgYGB7cn0KbHNtX3BfYXJlYSggbGFuZF8yMDE2ICkgCmBgYAoKQXMgeW91IHNlZSwgaXQgZGl2aWRlcyB0aGUgbGFuZHNjYXBlIGludG8gaW5kaXZpZHVhbCBwYXRjaGVzLCBudW1iZXJzIHRoZW0sIGFuZCBwcm92aWRlcyBtZXRyaWMgZmVlZGJhY2suIExldCdzIHN1bW1hcml6ZSBmb3IgbWl4ZWQgKHR5cGU9NDMpIGFuZCBldmVyZ3JlZW4gKHR5cGUgPSA0MikgZm9yZXN0cy4KCmBgYHtyfQpsc21fcF9hcmVhKCBsYW5kXzIwMTYgKSAlPiUKICBmaWx0ZXIoIGNsYXNzICVpbiUgYyg0MiwgNDMpICkgJT4lCiAgbXV0YXRlKCBGb3Jlc3QgPSBpZmVsc2UoIGNsYXNzID09IDQyLCAiRXZlcmdyZWVuIiwgIk1peGVkIEhhcmR3b29kcyIpICkgJT4lCiAgZ2dwbG90KCBhZXModmFsdWUpICkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIGZhY2V0X2dyaWQoIEZvcmVzdCB+IC4pICsKICB4bGFiKCJBcmVhIChoYSkiKSArIAogIHlsYWIoIkZyZXF1ZW5jeSIpICsgCiAgZ2d0aXRsZSgiQXJlYSBEaXN0cmlidXRpb24gb2YgTWl4ZWQgRm9yZXN0IFR5cGVzIikKYGBgCgoKT3RoZXIgbWV0cmljcyB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gaW5jbHVkZSAodXNlIGBWaWV3KCBsaXN0X2xzbSgpKWAgdG8gc2VlIHRoZW0gYWxsKTogCgotICpneXJhdGUqIC0gVGhlIG1lYW4gZGlzdGFuY2UgYmV0d2VlbiBjZWxscyB3aXRoaW4gaXQgd2hpY2ggYXBwcm94aW1hdGVzIHNvbWV0aGluZyBsaWtlIHRoZSBhdmVyYWdlIGRpc3RhbmNlIGFuIG9yZ2FuaXNtIGNhbiBtb3ZlIHdpdGhpbiBhIHBhdGNoIGJlZm9yZSBlbmNvdW50ZXJpbmcgYSBib3VuZGFyeS4gIAoKLSAqY2EqIC0gVGhlIHBoeXNpY2FsIGFyZWEgYXNzb2NpYXRlZCB3aXRoIGVhY2ggbGFuZHNjYXBlIHR5cGUuCgoKLSAqY2x1bXAqIC0gQSBtZWFzdXJlIG9mIGhvdyBzcGF0aWFsbHkgY29udGluZ2VudCB0aGUgZmVhdHVyZXMgYXJlIHJhbmdpbmcgZnJvbSByYW5kb20gKG5vIGNsdW1waW5nKSB0byBhbGwgaW4gb25lIGxhcmdlIHBhdGNoIChmdWxseSBjbHVtcGVkKS4KCi0gKnByKiAtIFBhdGNoIHJpY2huZXNzLgoKLSAqc2hkaSogLSBTaGFubm9uJ3MgRGl2ZXJzaXR5IGluZGV4CgoKIyMgRmluZGluZyBQYXRjaGVzCgpUbyBjb25uZWN0IGJhY2sgdG8gdGhlIHNwYXRpYWwgYXJyYW5nZW1lbnQgb2YgZmVhdHVyZXMsIHdlIGNhbiB1c2UgdGhlIGZvbGxvd2luZyBmdW5jdGlvbnMgdG8gcG9pbnQgb3V0IHdoZXJlIHNwZWNpZmljIHBhdGNoZXMgbWF5IGJlIG9uIHRoZSBvcmlnaW5hbCBsYW5kc2NhcGUuICBGb3IgZXhhbXBsZSwgd2UgY2FuIHBsb3QgdGhlIHNwYXRpYWwgbG9jYXRpb24gb2YgdGhlIG1vc3QgY29tbW9uIHR5cGVzIChzYXkgdGhlIGZpcnN0IDMpIG9mIGhhYml0YXQgYXM6CgoKCmBgYHtyfQpsc21fcF9hcmVhKCBsYW5kXzIwMTYgKSAlPiUKICBncm91cF9ieSggY2xhc3MgKSAlPiUKICBzdW1tYXJpemUoIGBBcmVhYCA9IHN1bSggdmFsdWUpICkgJT4lCiAgYXJyYW5nZSgtQXJlYSkKYGBgCgpXaGljaCB0dXJuIG91dCB0byAKCmBgYHtyfQpwbG90KCBnZXRfcGF0Y2hlcyggbGFuZF8yMDE2LCBjbGFzcz00MylbWzFdXSApIApgYGAKCgpgYGB7cn0KcGxvdCggZ2V0X3BhdGNoZXMoIGxhbmRfMjAxNiwgY2xhc3M9OTApW1sxXV0gKSAKYGBgCgpgYGB7cn0KcGxvdCggZ2V0X3BhdGNoZXMoIGxhbmRfMjAxNiwgY2xhc3M9NDMpW1sxXV0gKSAKYGBgCgpUaGUgZGlmZmVyZW5jZSBpbiB0aGUgY29sb3JzIG9mIHRoZXNlIHBhdGNoZXMgcmVmbGVjdCB0aGUgcGF0Y2ggSUQgbnVtYmVyIHRoYXQgYGxzbV8qYCBnaXZlIHRoZW0u