1 Introduction

1.1 Objective

  1. Demonstrate import and examination of sf* spatial data objects.
  2. Demonstrate import and examination of sp* spatial data objects.
  3. Demonstrate converting between sp* and sf* formats.

1.2 Background

Spatial data is more complex than simple rectangular attribute data (e.g. data tables where a row is an observation and a column is a variable). Spatial data may (or may not) have attributes, but typically include location information stored in either a vector or raster data model, and then held in memory or stored on disc in some format.

Over the past 10+ years, R has increasingly been used to analyze and visualize spatial data. Early on, investigators tackling the complexities of spatial data analysis developed a number of ad hoc one-off approaches to these data. This helped in the short term but created other problems as users needed to chain together steps and had to convert one data format to another. A result of this tumult was a thoughtful and systematic approach to tackling the unique challenges of spatial data in R. Roger Bivand, Edzer Pebesma and others developed the sp package which defined spatial data classes, and provided functional tools to interact with them.

The sp package defined specifica data classes to hold points, lines, and polygons, as well as raster/grid data; each of these data classes can be geometry only (these have names like SpatialPoints or SpatialPolygons) or could contain geometry plus related data attributes (these have names like SPatialPointsDataFrame or SpatialPolygonsDataFrame). Each spatial object can contain all the information spatial data might include: the spatial extent (min/max x, y values), the coordinate system or spatial projectin, the geometry information, the attribute information, etc.

Because of the flexibility and power of the sp* class of objects, they have become a standard up until the last year or so. They continue to be the only format allowed by many of the packages we will use this semester. However analysts sometimes find the complexity of the sp* objects to be a hindrance to efficient processing of geographic data. Specifically the information is stored in numerous ‘slots’ (not a formal list, but conceptually a little like list elements). As the number of ways to visualize data increases, there was desire to make spatial data behave more like tabular data. This led to the same team (e.g. Bivand, Pebesma, others) to develop the Simple Features set of spatial data classes for R. Loaded with the sf package, this data format is almost surely going to become the standard for the coming years. Recognizing that many users and functions prefer the familiar sp* objects, the sf package includes a number of utility functions for easily converting back and forth. In this class we will use sf* objects as the preferred data format, but because some of the tools we’ll learn require sp* we will go back and forth.

sf* data classes are designed to hold all the essential spatial information (projection, extent, geometry), but do so with an easy to evaluate data frame format that integrates the attribute information and the geometry information together. The result is more intuitive sorting, selecting, aggregating, and visualizing.

2 Spatial data classes from sf and sp

2.1 Why prefer sf over sp spatial class definitions:

As Robin Lovelace writes in his online eBook, Gecomputation in R, sf offers an approach to spatial data that is compatible with QGIS and PostGIS, important non-ESRI open source GIS platforms, and sf functionality compared to sp provides:

  1. Fast reading and writing of data
  2. Enhanced plotting performance
  3. sf objects can be treated as data frames in most operations
  4. sf functions can be combined using %>% operator and works well with the tidyverse collection of R packages (we’ll talk about this more later in the semester)
  5. sf function names are relatively consistent and intuitive (all begin with st_)

2.2 sf package

I will refer to the package names as sp and sf. When I use the asterisk (e.g. sp* and sf*) I am referring to the class of objects defined by these packages. For instance in package sf a polygon vector dataset has class sfc_MULTIPOLYGON whereas a point vector dataset has class sfc_MULTIPOINT.

First, we will look at a spatial polygon file that is currently stored in sf* data class (data available for download on Canvas). The name of the target spatial data is ga_mvc.gpkg. There is more about the meaning of this extension .gpkg below. The ga_mvc dataset is a county polygon vector file with information about motor vehicle fatalities by county in Georgia for 2005, 2014, and 2017. We’ll discuss the relevance of these years more in lab.

rr rr

library(sf)
fp <- '../../DATA/GA_MVC/ga_mvc.gpkg'
mvc <- st_read(fp)
Reading layer `ga_mvc' from data source `H:\SpatialEpidemiology\DATA\GA_MVC\ga_mvc.gpkg' using driver `GPKG'
Simple feature collection with 159 features and 17 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -85.60516 ymin: 30.35785 xmax: -80.83973 ymax: 35.00066
epsg (SRID):    4269
proj4string:    +proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs

rr rr

#Not run but included for illustration 
#mvc <- st_read('../../DATA/GA_MVC/ga_mvc.gpkg')

A few things to note here. First, I needed to load the sf package with the library(sf) call. The second thing I did was define where the file exists on my computer. This will obviously be different for you depending on where you saved the data. The reason I put the location in a separate object called fp (for filepath), was to emphasize that this location is specific to your use. In the final call I use the function st_read(fp) to import the file from the location I just named fp; the imported file is held in my computer memory and referenced by the object name mvc.

Another observation we can make at this point is that the spatially-oriented function call st_read() begins with the st_ prefix. Most of the spatial functions in package sf begin with the prefix, making it easier to search for functions specific to spatial data manipulation.

2.2.1 Exploring the object mvc?

To explore what this object contains and how it is structured, begin with a few simple checks.

rr rr

class(mvc)
[1] \sf\         \data.frame\

Why are there so many values returned for the class of this object? Because it conforms to many data classes including the very general class data.frame, as well as the closely related table formats tbl_df and tbl, and finally a class sf. Below I use three different functions (names(mvc), summary(mvc), and head(mvc) ) to examine these data. Look at the results of each and explore what information is returned by each. There is overlap, but also some unique information provided particularly by the latter two functions.

rr rr

names(mvc)
 [1] \GEOID\                \NAME\                 \variable\            
 [4] \estimate\             \County\               \MVCDEATHS_05\        
 [7] \MVCDEATHS_14\         \MVCDEATH_17\          \TPOP_05\             
[10] \TPOP_14\              \TPOP_17\              \NCHS_RURAL_CODE_2013\
[13] \nchs_code\            \rural\                \MVCRATE_05\          
[16] \MVCRATE_14\           \MVCRATE_17\           \geom\                

rr rr

summary(mvc)
     GEOID                           NAME           variable      estimate    
 13001  :  1   Appling County, Georgia :  1   B00001_001:159   Min.   :  260  
 13003  :  1   Atkinson County, Georgia:  1                    1st Qu.: 1093  
 13005  :  1   Bacon County, Georgia   :  1                    Median : 1831  
 13007  :  1   Baker County, Georgia   :  1                    Mean   : 4219  
 13009  :  1   Baldwin County, Georgia :  1                    3rd Qu.: 3762  
 13011  :  1   Banks County, Georgia   :  1                    Max.   :58814  
 (Other):153   (Other)                 :153                                   
             County     MVCDEATHS_05      MVCDEATHS_14     MVCDEATH_17    
 Appling County :  1   Min.   :  0.000   Min.   : 0.000   Min.   : 0.000  
 Atkinson County:  1   1st Qu.:  3.000   1st Qu.: 2.000   1st Qu.: 3.000  
 Bacon County   :  1   Median :  5.000   Median : 4.000   Median : 6.000  
 Baker County   :  1   Mean   :  9.862   Mean   : 7.667   Mean   : 9.774  
 Baldwin County :  1   3rd Qu.: 11.000   3rd Qu.: 9.000   3rd Qu.:11.000  
 Banks County   :  1   Max.   :105.000   Max.   :91.000   Max.   :98.000  
 (Other)        :153                                                      
    TPOP_05          TPOP_14          TPOP_17        NCHS_RURAL_CODE_2013
 Min.   :  1858   Min.   :  1693   Min.   :   1628   Min.   :1.000       
 1st Qu.: 11492   1st Qu.: 11543   1st Qu.:  11412   1st Qu.:3.000       
 Median : 22055   Median : 22755   Median :  22736   Median :5.000       
 Mean   : 56138   Mean   : 63505   Mean   :  65594   Mean   :4.428       
 3rd Qu.: 46712   3rd Qu.: 53725   3rd Qu.:  55067   3rd Qu.:6.000       
 Max.   :818737   Max.   :996319   Max.   :1041423   Max.   :6.000       
                                                                         
               nchs_code        rural       MVCRATE_05      MVCRATE_14    
 Large central metro: 1   non-Rural:102   Min.   : 0.00   Min.   : 0.000  
 Large fringe metro :28   Rural    : 57   1st Qu.:13.82   1st Qu.: 9.665  
 Medium metro       :15                   Median :22.46   Median :14.096  
 Micropolitan       :28                   Mean   :24.41   Mean   :17.912  
 Non-core           :57                   3rd Qu.:33.94   3rd Qu.:23.317  
 Small metro        :30                   Max.   :66.34   Max.   :74.944  
                                                                          
   MVCRATE_17                geom    
 Min.   :  0.00   MULTIPOLYGON :159  
 1st Qu.: 12.17   epsg:4269    :  0  
 Median : 20.43   +proj=long...:  0  
 Mean   : 22.57                      
 3rd Qu.: 29.53                      
 Max.   :115.16                      
                                     

rr rr

head(mvc)
Simple feature collection with 6 features and 17 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -84.64195 ymin: 31.0784 xmax: -82.04858 ymax: 34.49172
epsg (SRID):    4269
proj4string:    +proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs
  GEOID                     NAME   variable estimate          County
1 13001  Appling County, Georgia B00001_001     1504  Appling County
2 13003 Atkinson County, Georgia B00001_001      875 Atkinson County
3 13005    Bacon County, Georgia B00001_001      945    Bacon County
4 13007    Baker County, Georgia B00001_001      390    Baker County
5 13009  Baldwin County, Georgia B00001_001     2943  Baldwin County
6 13011    Banks County, Georgia B00001_001     1767    Banks County
  MVCDEATHS_05 MVCDEATHS_14 MVCDEATH_17 TPOP_05 TPOP_14 TPOP_17
1            4            4          10   17769   18540   18521
2            5            1           3    8096    8223    8342
3            7            5           0   10552   11281   11319
4            1            1           1    3967    3255    3200
5            6            8          13   46304   45909   44906
6            4            8           6   16683   18295   18634
  NCHS_RURAL_CODE_2013    nchs_code     rural MVCRATE_05 MVCRATE_14 MVCRATE_17
1                    6     Non-core     Rural   22.51111   21.57497   53.99276
2                    6     Non-core     Rural   61.75889   12.16101   35.96260
3                    6     Non-core     Rural   66.33813   44.32231    0.00000
4                    4  Small metro non-Rural   25.20797   30.72197   31.25000
5                    5 Micropolitan non-Rural   12.95784   17.42578   28.94936
6                    6     Non-core     Rural   23.97650   43.72779   32.19921
                            geom
1 MULTIPOLYGON (((-82.55069 3...
2 MULTIPOLYGON (((-83.141 31....
3 MULTIPOLYGON (((-82.62819 3...
4 MULTIPOLYGON (((-84.64166 3...
5 MULTIPOLYGON (((-83.42674 3...
6 MULTIPOLYGON (((-83.66862 3...

Look through the names of the variables and the summary information. Can you figure out what these variable columns contain/mean? Also notice the general information provided at the top of the output returned from the head() function call. This information is specific to class sf and tells us a great deal about the spatial attributes of data class (MULTIPOLYGON), coordinate system/projection, spatial extent and bounding box. Another way to examine the dataset is to look at the data structure using the str() function.

rr rr

str(mvc)
Classes ‘sf’ and 'data.frame': 159 obs. of  18 variables:
 $ GEOID               : Factor w/ 159 levels \13001\,\13003\,..: 1 2 3 4 5 6 7 8 9 10 ...
 $ NAME                : Factor w/ 159 levels \Appling County

2.2.2 Where is the geometry information?

One of the advantages of the sf* data classes is that the object is primarily a data table. But then where is the geometry information? Look at the column called ‘geom’. Notice in the str() call how this last field/column is of class sfc_MULTIPOLYGON.

rr rr

class(mvc$geom)
[1] \sfc_MULTIPOLYGON\ \sfc\             

rr rr

head(mvc$geom)
Geometry set for 6 features 
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -84.64195 ymin: 31.0784 xmax: -82.04858 ymax: 34.49172
epsg (SRID):    4269
proj4string:    +proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs
First 5 geometries:
MULTIPOLYGON (((-82.55069 31.74911, -82.54744 3...
MULTIPOLYGON (((-83.141 31.40673, -83.13898 31....
MULTIPOLYGON (((-82.62819 31.56593, -82.62734 3...
MULTIPOLYGON (((-84.64166 31.3125, -84.63994 31...
MULTIPOLYGON (((-83.42674 33.18273, -83.42496 3...

sfc means simple feature column list. This means that in one column of our data.frame(), each value is actually a list. The list contains the x,y locations of the spatial geometry. So if each is a list, what is in the list? Remember to extract an element of a list we use the double bracket, [[]]. Below I ask R to tell me the class of the mvc$geom that is first in the list, meaning the one that goes with Appling County, the top row of data.

rr rr

class(mvc$geom[[1]])
[1] \XY\           \MULTIPOLYGON\ \sfg\         

What does this mean? It means that for each row of data (e.g. each county) there is a list of XY points that make up a polygon. The sfg stands for simple feature geometry, which is the information required to draw a single unit of observation (single county).

2.2.3 3 classes that compose an sf* data object

Now we know three specific classes of data important to the simple feature format:

  1. sf, the table (data.frame) with feature attributes and feature geometries, which contains…
  2. sfc, the list-column with the geometries for each feature (record), which is composed of…
  3. sfg, the feature geometry of an individual simple feature (e.g. individual county).

Is that confusing? Don’t worry too much about it now. The point is mostly to realize that the geometry part of the spatial data is intricately integrated into the table of attributes. This will provide great benefit for some parts of spatial analysis in epidemiology.

2.2.4 Making a boring map

How do we really know what kind of spatial information the geom column contains? One quick way to look is to render a simple plot. Note that if you use the base function plot() with an sf* object, it will plot a map for every column or attribute! In other words there are 17 data columns in the object mvc, and thus 17 maps would be rendered by default. But if we only want to take a quick look we could either plot the geometry alone, or we could specify a single variable to plot. To plot the geometry (e.g. shapes) only I use the function st_geometry().

rr rr

plot(st_geometry(mvc))

rr rr

plot(mvc['MVCRATE_14'])

2.3 sp package

It is easy to convert between the current standard format, sp* Spatial classes and the emerging (and for our purposes, primary) format, sf* Simple Feature classes. In general we can coerce objects to sp* classes using the as(x, 'ClassType') format like this:

rr rr

library(sp)
mvc.spatial <- as(mvc, 'Spatial')
class(mvc.spatial)
[1] \SpatialPolygonsDataFrame\
attr(,\package\)
[1] \sp\

rr rr

summary(mvc.spatial)
Object of class SpatialPolygonsDataFrame
Coordinates:
        min       max
x -85.60516 -80.83973
y  30.35785  35.00066
Is projected: FALSE 
proj4string :
[+proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs]
Data attributes:
     GEOID                           NAME           variable      estimate    
 13001  :  1   Appling County, Georgia :  1   B00001_001:159   Min.   :  260  
 13003  :  1   Atkinson County, Georgia:  1                    1st Qu.: 1093  
 13005  :  1   Bacon County, Georgia   :  1                    Median : 1831  
 13007  :  1   Baker County, Georgia   :  1                    Mean   : 4219  
 13009  :  1   Baldwin County, Georgia :  1                    3rd Qu.: 3762  
 13011  :  1   Banks County, Georgia   :  1                    Max.   :58814  
 (Other):153   (Other)                 :153                                   
             County     MVCDEATHS_05      MVCDEATHS_14     MVCDEATH_17    
 Appling County :  1   Min.   :  0.000   Min.   : 0.000   Min.   : 0.000  
 Atkinson County:  1   1st Qu.:  3.000   1st Qu.: 2.000   1st Qu.: 3.000  
 Bacon County   :  1   Median :  5.000   Median : 4.000   Median : 6.000  
 Baker County   :  1   Mean   :  9.862   Mean   : 7.667   Mean   : 9.774  
 Baldwin County :  1   3rd Qu.: 11.000   3rd Qu.: 9.000   3rd Qu.:11.000  
 Banks County   :  1   Max.   :105.000   Max.   :91.000   Max.   :98.000  
 (Other)        :153                                                      
    TPOP_05          TPOP_14          TPOP_17        NCHS_RURAL_CODE_2013
 Min.   :  1858   Min.   :  1693   Min.   :   1628   Min.   :1.000       
 1st Qu.: 11492   1st Qu.: 11543   1st Qu.:  11412   1st Qu.:3.000       
 Median : 22055   Median : 22755   Median :  22736   Median :5.000       
 Mean   : 56138   Mean   : 63505   Mean   :  65594   Mean   :4.428       
 3rd Qu.: 46712   3rd Qu.: 53725   3rd Qu.:  55067   3rd Qu.:6.000       
 Max.   :818737   Max.   :996319   Max.   :1041423   Max.   :6.000       
                                                                         
               nchs_code        rural       MVCRATE_05      MVCRATE_14    
 Large central metro: 1   non-Rural:102   Min.   : 0.00   Min.   : 0.000  
 Large fringe metro :28   Rural    : 57   1st Qu.:13.82   1st Qu.: 9.665  
 Medium metro       :15                   Median :22.46   Median :14.096  
 Micropolitan       :28                   Mean   :24.41   Mean   :17.912  
 Non-core           :57                   3rd Qu.:33.94   3rd Qu.:23.317  
 Small metro        :30                   Max.   :66.34   Max.   :74.944  
                                                                          
   MVCRATE_17    
 Min.   :  0.00  
 1st Qu.: 12.17  
 Median : 20.43  
 Mean   : 22.57  
 3rd Qu.: 29.53  
 Max.   :115.16  
                 

Recall the class-type for the original object, mvc was "sf" (as well as "tbl_df" and "data.frame"). Now the mvc.spatial object has class "SpatialPolygonsDataFrame". Notice the summary() call gives the expected summary of attribute data, but just like the sf objects, includes some information about geometry and projection at the top.

To make a simple plot of the geometry of the SpatialPolygonsDataFrame, you can once again use the base plot() functionality:

rr rr

plot(mvc.spatial)

The sp package actually has a function called spplot() which provides a great deal more control for cartography than base plot(). You can review the documentation about use of spplot() by asking for help: ?spplot. While it is a useful function, we will be using a different approach to map-production for most of our work in this course.

2.4 Converting between sf* and sp*

In the previous section we used the as() syntax to convert from sf to sp class. The syntax for going the other way is a little different.

# Go from sf to sp:
mvc.spatial <- as(mvc, Class = "Spatial")

# Go from sp to sf:
mvc_sf <- st_as_sf(mvc.spatial, "sf")

3 A bit more on import/export of spatial data

3.1 st_read() for importing data

One of the supposed benefits of using the sf package over sp was faster and more efficient reading/writing (importing/exporting) of data. So what does that mean exactly? Well for one thing, the st_read() function we used to originally import the mvc dataset is not limited to only Simple Feature data, or to only the .gpkg file format. It can import dozens of different data formats including .shp, .kml, and even from the ESRI spatial database format, the geodatabase (.gdb). You can read more about reading and writing spatial data using package sf in one of the online vignettes (click here).

While the st_read() arguments allow you to specify many specifics about the data you wish to import, often the function ‘guesses’ the right one based on the file extension name. For instance:

rr rr

mvc.shp <- st_read('../../DATA/GA_MVC/ga_mvc.shp')
Reading layer `ga_mvc' from data source `H:\SpatialEpidemiology\DATA\GA_MVC\ga_mvc.shp' using driver `ESRI Shapefile'
Simple feature collection with 159 features and 17 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -85.60516 ymin: 30.35785 xmax: -80.83973 ymax: 35.00066
epsg (SRID):    NA
proj4string:    +proj=longlat +ellps=GRS80 +no_defs

rr rr

class(mvc.shp)
[1] \sf\         \data.frame\

rr rr

names(mvc.shp)
 [1] \GEOID\      \NAME\       \varbl\      \estmt\      \Conty\     
 [6] \MVCDEATHS_\ \MVCDEATH_1\ \MVCDEATH_\  \TPOP_0\     \TPOP_14\   
[11] \TPOP_17\    \NCHS_\      \nchs__1\    \rural\      \MVCRATE_0\ 
[16] \MVCRATE_14\ \MVCRATE_17\ \geometry\  

What was different here from our initial st_read()? In this case we actually read in a totally different dataset, call ga_mvc.shp. It was saved in the ESRI shapefile format, but when read in we see the geometry information (multipolygon), projection infromation, and attribute data have all been imported as an sf object. One note about the field names: ESRI has rules (annoyingly restricting rules in my opinion) about the length of variable names…so notice how our variable names are truncated.

3.2 st_write() for exporting data

What about saving/writing data? It’s just about as easy. The corresponding command is, st_write() and again the function can ‘guess’ what format based on the file name, although you can also specify the arguments to be certain:

# Save as a geopackage .gpkg (this is efficient storage format)
st_write(mvc, 'ga_mvc2.gpkg')

# Save as an ESRI shapefile
st_write(mvc, 'ga_mvc2.shp')

# Save as shapefile, but specify arguments directly
st_write(mvc, dsn = "ga_mvc2.shp", layer = "ga_mvc.shp", driver = "ESRI Shapefile"))

3.3 What is .gpkg?

.gpkg is short for the geopackage format, which is an open-source format for storing vector or raster data. Geopackages are stored as a single file on your computer, and in a format that can be read by many other GIS software packages including ArcGIS! You can try navigating within Arc Catalog to whereever you save your .gpkg data and see that it appears as a spatial database in Arc. You can add the dataset into ArcMap directly from this format.

While I recognize that much of the vector spatial data in the world is in the ESRI .shp format, we will primarily save data in the .gpkg format because it is flexible, efficient, and stored as a single file on disk so avoids the potential confusion that occurs when moving data that are contained in 4-8 separate files on disk.

4 Coordinate systems & Projections in R

There are two related concepts important to properly managing, analyzing, and visualizing spatial data: the Coordinate System and the Projection. I assume you have learned about each of these in your intro to GIS course, so I only very briefly review them, with attention to how we manage them in R.

The coordinate system refers to an agreed upon reference system for representing location information across datasets in a way that allows them to be properly aligned with one another and have a known relationship with the world. The coordinate system is defined by its geographical shape (spherical or planar), reference points (e.g. the prime meridian), its units of measurement (e.g. degrees latitude/longitude or meters). A common coordinate system is the GCS84 (World Geodetic System 1984).

Map projections are varying ways to represent the spherical world on a flat piece of paper. Because this is inherently impossible to do with complete accuracy, projections vary with respect to what aspect of representation is most important. For instance some projections maintain map distance to be consistent across the map; other maintain area; others maintain angles, etc. You can see online resources to read more about common projections.

4.1 Projections & coordinate systems in R

Any spatial data we will see in this course was created using a coordinate system, although not all data are ‘projected’. There is built-in functionality to transform a given dataset into another coordinate system or projection, but before we can do that we must know where we are starting!

rr rr

# to see the current coordinate information use st_crs()
st_crs(mvc)
Coordinate Reference System:
  EPSG: 4269 
  proj4string: \+proj=longlat +ellps=GRS80 +towgs84=0

This little bit of code and result opens up a whole set of words and symbols we will see often. Briefly here they are:

  • CRS (as in st_crs) stands for coordinate reference system. Many projection/coordinate related tasks in R require defining an objects CRS
  • EPSG stands for European Petroleum Survey Group. The EPSG system is one of several used to reference different coordinate/projection systems. EPSG 4269 is the NAD83 datum lat/lon coordinate system; this is the most commonly used coordinate system by US federal agencies.
  • proj4string Another tool to define projections is a standardized text string maintained by an international body. This is a parallel or alternate means for specifying projection/coordinate as the EPSG.

Sometimes you know you want a specific projection but don’t know the number. There are many resources online but one that is useful is http://spatialreference.org/.

So in the case of our current Georgia Counties dataset we can see that it is ‘unprojected’. For public health mapping of areal units (like counties) I prefer equal-area projections because it assures each geographic unit is fairly represented to the viewer. To transform the current data to something new we must first define the ‘new’ projected coordinate system. I used one of the online directories (http://www.epsg-registry.org/) to look up the Albers Equal Area USA projection. It is EPSG::5069. To change from old to new we use st_transform().

rr rr

mvc.albers <- st_transform(mvc, 5069)
st_crs(mvc.albers)
Coordinate Reference System:
  EPSG: 5069 
  proj4string: \+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=23 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD27 +units=m +no_defs\

Now we will plot the ‘old’ and the ‘new’ to see if they differ.

rr rr

plot(st_geometry(mvc), main = 'Unprojected lat/lon')

rr rr

plot(st_geometry(mvc.albers), main = 'Albers projection')

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBzcGF0aWFsIGRhdGEgaW4gUiINCmF1dGhvcjogIk0gS3JhbWVyIg0KZGF0ZTogIkZhbGwgMjAxOCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIHBkZl9kb2N1bWVudDoNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQpzdWJ0aXRsZTogU3BhdGlhbCBFcGlkZW1pb2xvZ3kgKEVQSSA1OTBSKQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KIyBJbnRyb2R1Y3Rpb24NCg0KIyMgT2JqZWN0aXZlDQoNCjEuIERlbW9uc3RyYXRlIGltcG9ydCBhbmQgZXhhbWluYXRpb24gb2YgYHNmKmAgc3BhdGlhbCBkYXRhIG9iamVjdHMuDQoyLiBEZW1vbnN0cmF0ZSBpbXBvcnQgYW5kIGV4YW1pbmF0aW9uIG9mIGBzcCpgIHNwYXRpYWwgZGF0YSBvYmplY3RzLg0KMy4gRGVtb25zdHJhdGUgY29udmVydGluZyBiZXR3ZWVuIGBzcCpgIGFuZCBgc2YqYCBmb3JtYXRzLg0KDQojIyBCYWNrZ3JvdW5kDQoNClNwYXRpYWwgZGF0YSBpcyBtb3JlIGNvbXBsZXggdGhhbiBzaW1wbGUgcmVjdGFuZ3VsYXIgYXR0cmlidXRlIGRhdGEgKGUuZy4gZGF0YSB0YWJsZXMgd2hlcmUgYSByb3cgaXMgYW4gb2JzZXJ2YXRpb24gYW5kIGEgY29sdW1uIGlzIGEgdmFyaWFibGUpLiAgU3BhdGlhbCBkYXRhIG1heSAob3IgbWF5IG5vdCkgaGF2ZSBhdHRyaWJ1dGVzLCBidXQgdHlwaWNhbGx5IGluY2x1ZGUgbG9jYXRpb24gaW5mb3JtYXRpb24gc3RvcmVkIGluIGVpdGhlciBhIHZlY3RvciBvciByYXN0ZXIgZGF0YSBtb2RlbCwgYW5kIHRoZW4gaGVsZCBpbiBtZW1vcnkgb3Igc3RvcmVkIG9uIGRpc2MgaW4gc29tZSBmb3JtYXQuDQoNCk92ZXIgdGhlIHBhc3QgMTArIHllYXJzLCBSIGhhcyBpbmNyZWFzaW5nbHkgYmVlbiB1c2VkIHRvIGFuYWx5emUgYW5kIHZpc3VhbGl6ZSBzcGF0aWFsIGRhdGEuIEVhcmx5IG9uLCBpbnZlc3RpZ2F0b3JzIHRhY2tsaW5nIHRoZSBjb21wbGV4aXRpZXMgb2Ygc3BhdGlhbCBkYXRhIGFuYWx5c2lzIGRldmVsb3BlZCBhIG51bWJlciBvZiBhZCBob2Mgb25lLW9mZiBhcHByb2FjaGVzIHRvIHRoZXNlIGRhdGEuIFRoaXMgaGVscGVkIGluIHRoZSBzaG9ydCB0ZXJtIGJ1dCBjcmVhdGVkIG90aGVyIHByb2JsZW1zIGFzIHVzZXJzIG5lZWRlZCB0byBjaGFpbiB0b2dldGhlciBzdGVwcyBhbmQgaGFkIHRvIGNvbnZlcnQgb25lIGRhdGEgZm9ybWF0IHRvIGFub3RoZXIuIEEgcmVzdWx0IG9mIHRoaXMgdHVtdWx0IHdhcyBhIHRob3VnaHRmdWwgYW5kIHN5c3RlbWF0aWMgYXBwcm9hY2ggdG8gdGFja2xpbmcgdGhlIHVuaXF1ZSBjaGFsbGVuZ2VzIG9mIHNwYXRpYWwgZGF0YSBpbiBSLiBSb2dlciBCaXZhbmQsIEVkemVyIFBlYmVzbWEgYW5kIG90aGVycyBkZXZlbG9wZWQgdGhlIGBzcGAgcGFja2FnZSB3aGljaCBkZWZpbmVkIHNwYXRpYWwgZGF0YSBjbGFzc2VzLCBhbmQgcHJvdmlkZWQgZnVuY3Rpb25hbCB0b29scyB0byBpbnRlcmFjdCB3aXRoIHRoZW0uDQoNCg0KVGhlIGBzcGAgcGFja2FnZSBkZWZpbmVkIHNwZWNpZmljYSBkYXRhIGNsYXNzZXMgdG8gaG9sZCBwb2ludHMsIGxpbmVzLCBhbmQgcG9seWdvbnMsIGFzIHdlbGwgYXMgcmFzdGVyL2dyaWQgZGF0YTsgZWFjaCBvZiB0aGVzZSBkYXRhIGNsYXNzZXMgY2FuIGJlIGdlb21ldHJ5IG9ubHkgKHRoZXNlIGhhdmUgbmFtZXMgbGlrZSBgU3BhdGlhbFBvaW50c2Agb3IgYFNwYXRpYWxQb2x5Z29uc2ApIG9yIGNvdWxkIGNvbnRhaW4gZ2VvbWV0cnkgcGx1cyByZWxhdGVkIGRhdGEgYXR0cmlidXRlcyAodGhlc2UgaGF2ZSBuYW1lcyBsaWtlIGBTUGF0aWFsUG9pbnRzRGF0YUZyYW1lYCBvciBgU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lYCkuIEVhY2ggc3BhdGlhbCBvYmplY3QgY2FuIGNvbnRhaW4gYWxsIHRoZSBpbmZvcm1hdGlvbiBzcGF0aWFsIGRhdGEgbWlnaHQgaW5jbHVkZTogdGhlIHNwYXRpYWwgZXh0ZW50IChtaW4vbWF4IHgsIHkgdmFsdWVzKSwgdGhlIGNvb3JkaW5hdGUgc3lzdGVtIG9yIHNwYXRpYWwgcHJvamVjdGluLCB0aGUgZ2VvbWV0cnkgaW5mb3JtYXRpb24sIHRoZSBhdHRyaWJ1dGUgaW5mb3JtYXRpb24sIGV0Yy4gDQoNCkJlY2F1c2Ugb2YgdGhlIGZsZXhpYmlsaXR5IGFuZCBwb3dlciBvZiB0aGUgYHNwKmAgY2xhc3Mgb2Ygb2JqZWN0cywgdGhleSBoYXZlIGJlY29tZSBhIHN0YW5kYXJkIHVwIHVudGlsIHRoZSBsYXN0IHllYXIgb3Igc28uICBUaGV5IGNvbnRpbnVlIHRvIGJlIHRoZSBvbmx5IGZvcm1hdCBhbGxvd2VkIGJ5IG1hbnkgb2YgdGhlIHBhY2thZ2VzIHdlIHdpbGwgdXNlIHRoaXMgc2VtZXN0ZXIuIEhvd2V2ZXIgYW5hbHlzdHMgc29tZXRpbWVzIGZpbmQgdGhlIGNvbXBsZXhpdHkgb2YgdGhlIGBzcCpgIG9iamVjdHMgdG8gYmUgYSBoaW5kcmFuY2UgdG8gZWZmaWNpZW50IHByb2Nlc3Npbmcgb2YgZ2VvZ3JhcGhpYyBkYXRhLiBTcGVjaWZpY2FsbHkgdGhlIGluZm9ybWF0aW9uIGlzIHN0b3JlZCBpbiBudW1lcm91cyAnc2xvdHMnIChub3QgYSBmb3JtYWwgbGlzdCwgYnV0IGNvbmNlcHR1YWxseSBhIGxpdHRsZSBsaWtlIGxpc3QgZWxlbWVudHMpLiBBcyB0aGUgbnVtYmVyIG9mIHdheXMgdG8gdmlzdWFsaXplIGRhdGEgaW5jcmVhc2VzLCB0aGVyZSB3YXMgZGVzaXJlIHRvIG1ha2Ugc3BhdGlhbCBkYXRhIGJlaGF2ZSBtb3JlIGxpa2UgdGFidWxhciBkYXRhLiAgVGhpcyBsZWQgdG8gdGhlIHNhbWUgdGVhbSAoZS5nLiBCaXZhbmQsIFBlYmVzbWEsIG90aGVycykgdG8gZGV2ZWxvcCB0aGUgU2ltcGxlIEZlYXR1cmVzIHNldCBvZiBzcGF0aWFsIGRhdGEgY2xhc3NlcyBmb3IgUi4gTG9hZGVkIHdpdGggdGhlIGBzZmAgcGFja2FnZSwgdGhpcyBkYXRhIGZvcm1hdCBpcyBhbG1vc3Qgc3VyZWx5IGdvaW5nIHRvIGJlY29tZSB0aGUgc3RhbmRhcmQgZm9yIHRoZSBjb21pbmcgeWVhcnMuICBSZWNvZ25pemluZyB0aGF0IG1hbnkgdXNlcnMgYW5kIGZ1bmN0aW9ucyBwcmVmZXIgdGhlIGZhbWlsaWFyIGBzcCpgIG9iamVjdHMsIHRoZSBgc2ZgIHBhY2thZ2UgaW5jbHVkZXMgYSBudW1iZXIgb2YgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGVhc2lseSBjb252ZXJ0aW5nIGJhY2sgYW5kIGZvcnRoLiBfX0luIHRoaXMgY2xhc3Mgd2Ugd2lsbCB1c2UgYHNmKmAgb2JqZWN0cyBhcyB0aGUgcHJlZmVycmVkIGRhdGEgZm9ybWF0LCBidXQgYmVjYXVzZSBzb21lIG9mIHRoZSB0b29scyB3ZSdsbCBsZWFybiByZXF1aXJlIGBzcCpgIHdlIHdpbGwgZ28gYmFjayBhbmQgZm9ydGguX18NCg0KYHNmKmAgZGF0YSBjbGFzc2VzIGFyZSBkZXNpZ25lZCB0byBob2xkIGFsbCB0aGUgZXNzZW50aWFsIHNwYXRpYWwgaW5mb3JtYXRpb24gKHByb2plY3Rpb24sIGV4dGVudCwgZ2VvbWV0cnkpLCBidXQgZG8gc28gd2l0aCBhbiBlYXN5IHRvIGV2YWx1YXRlIGRhdGEgZnJhbWUgZm9ybWF0IHRoYXQgaW50ZWdyYXRlcyB0aGUgYXR0cmlidXRlIGluZm9ybWF0aW9uIGFuZCB0aGUgZ2VvbWV0cnkgaW5mb3JtYXRpb24gdG9nZXRoZXIuIFRoZSByZXN1bHQgaXMgbW9yZSBpbnR1aXRpdmUgc29ydGluZywgc2VsZWN0aW5nLCBhZ2dyZWdhdGluZywgYW5kIHZpc3VhbGl6aW5nLiANCg0KICAgIA0KIyBTcGF0aWFsIGRhdGEgY2xhc3NlcyBmcm9tIGBzZmAgYW5kIGBzcGANCg0KDQojIyBXaHkgcHJlZmVyIGBzZmAgb3ZlciBgc3BgIHNwYXRpYWwgY2xhc3MgZGVmaW5pdGlvbnM6DQoNCkFzIFJvYmluIExvdmVsYWNlIHdyaXRlcyBpbiBoaXMgb25saW5lIGVCb29rLCBbR2Vjb21wdXRhdGlvbiBpbiBSXShodHRwczovL2dlb2NvbXByLnJvYmlubG92ZWxhY2UubmV0LyksIGBzZmAgb2ZmZXJzIGFuIGFwcHJvYWNoIHRvIHNwYXRpYWwgZGF0YSB0aGF0IGlzIGNvbXBhdGlibGUgd2l0aCBRR0lTIGFuZCBQb3N0R0lTLCBpbXBvcnRhbnQgbm9uLUVTUkkgb3BlbiBzb3VyY2UgR0lTIHBsYXRmb3JtcywgYW5kIGBzZmAgZnVuY3Rpb25hbGl0eSBjb21wYXJlZCB0byBgc3BgIHByb3ZpZGVzOg0KDQoxLiBGYXN0IHJlYWRpbmcgYW5kIHdyaXRpbmcgb2YgZGF0YQ0KMi4gRW5oYW5jZWQgcGxvdHRpbmcgcGVyZm9ybWFuY2UNCjMuIGBzZmAgb2JqZWN0cyBjYW4gYmUgdHJlYXRlZCBhcyBkYXRhIGZyYW1lcyBpbiBtb3N0IG9wZXJhdGlvbnMNCjQuIGBzZmAgZnVuY3Rpb25zIGNhbiBiZSBjb21iaW5lZCB1c2luZyBgJT4lYCBvcGVyYXRvciBhbmQgd29ya3Mgd2VsbCB3aXRoIHRoZSBgdGlkeXZlcnNlYCBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgKHdlJ2xsIHRhbGsgYWJvdXQgdGhpcyBtb3JlIGxhdGVyIGluIHRoZSBzZW1lc3RlcikNCjUuIGBzZmAgZnVuY3Rpb24gbmFtZXMgYXJlIHJlbGF0aXZlbHkgY29uc2lzdGVudCBhbmQgaW50dWl0aXZlIChhbGwgYmVnaW4gd2l0aCBgc3RfYCkNCg0KDQoNCiMjIGBzZmAgcGFja2FnZQ0KDQpJIHdpbGwgcmVmZXIgdG8gdGhlIHBhY2thZ2UgbmFtZXMgYXMgYHNwYCBhbmQgYHNmYC4gIFdoZW4gSSB1c2UgdGhlIGFzdGVyaXNrIChlLmcuIGBzcCpgIGFuZCBgc2YqYCkgSSBhbSByZWZlcnJpbmcgdG8gdGhlIGNsYXNzIG9mIG9iamVjdHMgZGVmaW5lZCBieSB0aGVzZSBwYWNrYWdlcy4gRm9yIGluc3RhbmNlIGluIHBhY2thZ2UgYHNmYCBhIHBvbHlnb24gdmVjdG9yIGRhdGFzZXQgaGFzIGNsYXNzIGBzZmNfTVVMVElQT0xZR09OYCB3aGVyZWFzIGEgcG9pbnQgdmVjdG9yIGRhdGFzZXQgaGFzIGNsYXNzIGBzZmNfTVVMVElQT0lOVGAuIA0KDQoNCkZpcnN0LCB3ZSB3aWxsIGxvb2sgYXQgYSBzcGF0aWFsIHBvbHlnb24gZmlsZSB0aGF0IGlzIGN1cnJlbnRseSBzdG9yZWQgaW4gYHNmKmAgZGF0YSBjbGFzcyAoZGF0YSBhdmFpbGFibGUgZm9yIGRvd25sb2FkIG9uIENhbnZhcykuICBUaGUgbmFtZSBvZiB0aGUgdGFyZ2V0IHNwYXRpYWwgZGF0YSBpcyBgZ2FfbXZjLmdwa2dgLiBUaGVyZSBpcyBtb3JlIGFib3V0IHRoZSBtZWFuaW5nIG9mIHRoaXMgZXh0ZW5zaW9uIGAuZ3BrZ2AgYmVsb3cuIFRoZSBgZ2FfbXZjYCBkYXRhc2V0IGlzIGEgY291bnR5IHBvbHlnb24gdmVjdG9yIGZpbGUgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCBtb3RvciB2ZWhpY2xlIGZhdGFsaXRpZXMgYnkgY291bnR5IGluIEdlb3JnaWEgZm9yIDIwMDUsIDIwMTQsIGFuZCAyMDE3LiBXZSdsbCBkaXNjdXNzIHRoZSByZWxldmFuY2Ugb2YgdGhlc2UgeWVhcnMgbW9yZSBpbiBsYWIuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbGlicmFyeShzZikNCmZwIDwtICcuLi8uLi9EQVRBL0dBX01WQy9nYV9tdmMuZ3BrZycNCm12YyA8LSBzdF9yZWFkKGZwKQ0KI05vdCBydW4gYnV0IGluY2x1ZGVkIGZvciBpbGx1c3RyYXRpb24gDQojbXZjIDwtIHN0X3JlYWQoJy4uLy4uL0RBVEEvR0FfTVZDL2dhX212Yy5ncGtnJykNCmBgYA0KDQpBIGZldyB0aGluZ3MgdG8gbm90ZSBoZXJlLiBGaXJzdCwgSSBuZWVkZWQgdG8gbG9hZCB0aGUgYHNmYCBwYWNrYWdlIHdpdGggdGhlIGBsaWJyYXJ5KHNmKWAgY2FsbC4gVGhlIHNlY29uZCB0aGluZyBJIGRpZCB3YXMgZGVmaW5lICp3aGVyZSogdGhlIGZpbGUgZXhpc3RzIG9uIG15IGNvbXB1dGVyLiAgVGhpcyB3aWxsIG9idmlvdXNseSBiZSBkaWZmZXJlbnQgZm9yIHlvdSBkZXBlbmRpbmcgb24gd2hlcmUgIHlvdSBzYXZlZCB0aGUgZGF0YS4gIFRoZSByZWFzb24gSSBwdXQgdGhlIGxvY2F0aW9uIGluIGEgc2VwYXJhdGUgb2JqZWN0IGNhbGxlZCBgZnBgIChmb3IgZmlsZXBhdGgpLCB3YXMgdG8gZW1waGFzaXplIHRoYXQgdGhpcyBsb2NhdGlvbiBpcyBzcGVjaWZpYyB0byB5b3VyIHVzZS4gSW4gdGhlIGZpbmFsIGNhbGwgSSB1c2UgdGhlIGZ1bmN0aW9uIGBzdF9yZWFkKGZwKWAgdG8gaW1wb3J0IHRoZSBmaWxlIGZyb20gdGhlIGxvY2F0aW9uIEkganVzdCBuYW1lZCBgZnBgOyB0aGUgaW1wb3J0ZWQgZmlsZSBpcyBoZWxkIGluIG15IGNvbXB1dGVyIG1lbW9yeSBhbmQgcmVmZXJlbmNlZCBieSB0aGUgb2JqZWN0IG5hbWUgYG12Y2AuICANCg0KQW5vdGhlciBvYnNlcnZhdGlvbiB3ZSBjYW4gbWFrZSBhdCB0aGlzIHBvaW50IGlzIHRoYXQgdGhlIHNwYXRpYWxseS1vcmllbnRlZCBmdW5jdGlvbiBjYWxsIGBzdF9yZWFkKClgIGJlZ2lucyB3aXRoIHRoZSBgc3RfYCBwcmVmaXguIE1vc3Qgb2YgdGhlIHNwYXRpYWwgZnVuY3Rpb25zIGluIHBhY2thZ2UgYHNmYCBiZWdpbiB3aXRoIHRoZSBwcmVmaXgsIG1ha2luZyBpdCBlYXNpZXIgdG8gc2VhcmNoIGZvciBmdW5jdGlvbnMgc3BlY2lmaWMgdG8gc3BhdGlhbCBkYXRhIG1hbmlwdWxhdGlvbi4gDQoNCg0KDQojIyMgRXhwbG9yaW5nIHRoZSBvYmplY3QgYG12Y2A/DQoNCg0KVG8gZXhwbG9yZSB3aGF0IHRoaXMgb2JqZWN0IGNvbnRhaW5zIGFuZCBob3cgaXQgaXMgc3RydWN0dXJlZCwgYmVnaW4gd2l0aCBhIGZldyBzaW1wbGUgY2hlY2tzLg0KDQpgYGB7cn0NCmNsYXNzKG12YykNCmBgYA0KDQpXaHkgYXJlIHRoZXJlIHNvIG1hbnkgdmFsdWVzIHJldHVybmVkIGZvciB0aGUgY2xhc3Mgb2YgdGhpcyBvYmplY3Q/IEJlY2F1c2UgaXQgY29uZm9ybXMgdG8gbWFueSBkYXRhIGNsYXNzZXMgaW5jbHVkaW5nIHRoZSB2ZXJ5IGdlbmVyYWwgY2xhc3MgYGRhdGEuZnJhbWVgLCBhcyB3ZWxsIGFzIHRoZSBjbG9zZWx5IHJlbGF0ZWQgdGFibGUgZm9ybWF0cyBgdGJsX2RmYCBhbmQgYHRibGAsIGFuZCBmaW5hbGx5IGEgY2xhc3MgYHNmYC4gQmVsb3cgSSB1c2UgdGhyZWUgZGlmZmVyZW50IGZ1bmN0aW9ucyAoYG5hbWVzKG12YylgLCBgc3VtbWFyeShtdmMpYCwgYW5kIGBoZWFkKG12YylgICkgdG8gZXhhbWluZSB0aGVzZSBkYXRhLiBMb29rIGF0IHRoZSByZXN1bHRzIG9mIGVhY2ggYW5kIGV4cGxvcmUgd2hhdCBpbmZvcm1hdGlvbiBpcyByZXR1cm5lZCBieSBlYWNoLiBUaGVyZSBpcyBvdmVybGFwLCBidXQgYWxzbyBzb21lIHVuaXF1ZSBpbmZvcm1hdGlvbiBwcm92aWRlZCBwYXJ0aWN1bGFybHkgYnkgdGhlIGxhdHRlciB0d28gZnVuY3Rpb25zLiANCg0KYGBge3J9DQpuYW1lcyhtdmMpDQpzdW1tYXJ5KG12YykNCmhlYWQobXZjKQ0KYGBgDQoNCg0KTG9vayB0aHJvdWdoIHRoZSBuYW1lcyBvZiB0aGUgdmFyaWFibGVzIGFuZCB0aGUgc3VtbWFyeSBpbmZvcm1hdGlvbi4gQ2FuIHlvdSBmaWd1cmUgb3V0IHdoYXQgdGhlc2UgdmFyaWFibGUgY29sdW1ucyBjb250YWluL21lYW4/ICBBbHNvIG5vdGljZSB0aGUgZ2VuZXJhbCBpbmZvcm1hdGlvbiBwcm92aWRlZCBhdCB0aGUgdG9wIG9mIHRoZSBvdXRwdXQgcmV0dXJuZWQgZnJvbSB0aGUgYGhlYWQoKWAgZnVuY3Rpb24gY2FsbC4gIFRoaXMgaW5mb3JtYXRpb24gaXMgc3BlY2lmaWMgdG8gY2xhc3MgYHNmYCBhbmQgdGVsbHMgdXMgYSBncmVhdCBkZWFsIGFib3V0IHRoZSBzcGF0aWFsIGF0dHJpYnV0ZXMgb2YgZGF0YSBjbGFzcyAoTVVMVElQT0xZR09OKSwgY29vcmRpbmF0ZSBzeXN0ZW0vcHJvamVjdGlvbiwgc3BhdGlhbCBleHRlbnQgYW5kIGJvdW5kaW5nIGJveC4gQW5vdGhlciB3YXkgdG8gZXhhbWluZSB0aGUgZGF0YXNldCBpcyB0byBsb29rIGF0IHRoZSBkYXRhIHN0cnVjdHVyZSB1c2luZyB0aGUgYHN0cigpYCBmdW5jdGlvbi4gIA0KDQpgYGB7cn0NCnN0cihtdmMpDQpgYGANCg0KDQoNCiMjIyBXaGVyZSBpcyB0aGUgZ2VvbWV0cnkgaW5mb3JtYXRpb24/DQoNCk9uZSBvZiB0aGUgYWR2YW50YWdlcyBvZiB0aGUgYHNmKmAgZGF0YSBjbGFzc2VzIGlzIHRoYXQgdGhlIG9iamVjdCBpcyBwcmltYXJpbHkgYSBkYXRhIHRhYmxlLiBCdXQgdGhlbiB3aGVyZSBpcyB0aGUgZ2VvbWV0cnkgaW5mb3JtYXRpb24/IExvb2sgYXQgdGhlIGNvbHVtbiBjYWxsZWQgJ2dlb20nLiAgTm90aWNlIGluIHRoZSBgc3RyKClgIGNhbGwgaG93IHRoaXMgbGFzdCBmaWVsZC9jb2x1bW4gaXMgb2YgY2xhc3MgYHNmY19NVUxUSVBPTFlHT05gLg0KDQoNCmBgYHtyfQ0KY2xhc3MobXZjJGdlb20pDQpoZWFkKG12YyRnZW9tKQ0KYGBgDQoNCmBzZmNgIG1lYW5zICpzaW1wbGUgZmVhdHVyZSBjb2x1bW4gbGlzdCouIFRoaXMgbWVhbnMgdGhhdCBpbiBvbmUgY29sdW1uIG9mIG91ciBgZGF0YS5mcmFtZSgpYCwgZWFjaCB2YWx1ZSBpcyBhY3R1YWxseSBhIGxpc3QuIFRoZSBsaXN0IGNvbnRhaW5zIHRoZSB4LHkgbG9jYXRpb25zIG9mIHRoZSBzcGF0aWFsIGdlb21ldHJ5LiAgU28gaWYgZWFjaCBpcyBhIGxpc3QsIHdoYXQgaXMgaW4gdGhlIGxpc3Q/IFJlbWVtYmVyIHRvIGV4dHJhY3QgYW4gZWxlbWVudCBvZiBhIGxpc3Qgd2UgdXNlIHRoZSBkb3VibGUgYnJhY2tldCwgYFtbXV1gLiBCZWxvdyBJIGFzayBSIHRvIHRlbGwgbWUgdGhlIGNsYXNzIG9mIHRoZSBgbXZjJGdlb21gIHRoYXQgaXMgZmlyc3QgaW4gdGhlIGxpc3QsIG1lYW5pbmcgdGhlIG9uZSB0aGF0IGdvZXMgd2l0aCBBcHBsaW5nIENvdW50eSwgdGhlIHRvcCByb3cgb2YgZGF0YS4NCg0KYGBge3J9DQpjbGFzcyhtdmMkZ2VvbVtbMV1dKQ0KYGBgDQoNCldoYXQgZG9lcyB0aGlzIG1lYW4/IEl0IG1lYW5zIHRoYXQgZm9yIGVhY2ggcm93IG9mIGRhdGEgKGUuZy4gZWFjaCBjb3VudHkpIHRoZXJlIGlzIGEgbGlzdCBvZiBYWSBwb2ludHMgdGhhdCBtYWtlIHVwIGEgcG9seWdvbi4gVGhlIGBzZmdgIHN0YW5kcyBmb3IgKnNpbXBsZSBmZWF0dXJlIGdlb21ldHJ5Kiwgd2hpY2ggaXMgdGhlIGluZm9ybWF0aW9uIHJlcXVpcmVkIHRvIGRyYXcgYSBzaW5nbGUgdW5pdCBvZiBvYnNlcnZhdGlvbiAoc2luZ2xlIGNvdW50eSkuIA0KDQoNCiMjIyAzIGNsYXNzZXMgdGhhdCBjb21wb3NlIGFuIGBzZipgIGRhdGEgb2JqZWN0DQoNCk5vdyB3ZSBrbm93IHRocmVlIHNwZWNpZmljIGNsYXNzZXMgb2YgZGF0YSBpbXBvcnRhbnQgdG8gdGhlIHNpbXBsZSBmZWF0dXJlIGZvcm1hdDoNCg0KMS4gYHNmYCwgdGhlIHRhYmxlIChgZGF0YS5mcmFtZWApIHdpdGggZmVhdHVyZSBhdHRyaWJ1dGVzIGFuZCBmZWF0dXJlIGdlb21ldHJpZXMsIHdoaWNoIGNvbnRhaW5zLi4uDQoyLiBgc2ZjYCwgdGhlIGxpc3QtY29sdW1uIHdpdGggdGhlIGdlb21ldHJpZXMgZm9yIGVhY2ggZmVhdHVyZSAocmVjb3JkKSwgd2hpY2ggaXMgY29tcG9zZWQgb2YuLi4NCjMuIGBzZmdgLCB0aGUgZmVhdHVyZSBnZW9tZXRyeSBvZiBhbiBpbmRpdmlkdWFsIHNpbXBsZSBmZWF0dXJlIChlLmcuIGluZGl2aWR1YWwgY291bnR5KS4NCg0KDQpJcyB0aGF0IGNvbmZ1c2luZz8gRG9uJ3Qgd29ycnkgdG9vIG11Y2ggYWJvdXQgaXQgbm93LiBUaGUgcG9pbnQgaXMgbW9zdGx5IHRvIHJlYWxpemUgdGhhdCB0aGUgZ2VvbWV0cnkgcGFydCBvZiB0aGUgc3BhdGlhbCBkYXRhIGlzIGludHJpY2F0ZWx5IGludGVncmF0ZWQgaW50byB0aGUgdGFibGUgb2YgYXR0cmlidXRlcy4gVGhpcyB3aWxsIHByb3ZpZGUgZ3JlYXQgYmVuZWZpdCBmb3Igc29tZSBwYXJ0cyBvZiBzcGF0aWFsIGFuYWx5c2lzIGluIGVwaWRlbWlvbG9neS4gDQoNCg0KIyMjIE1ha2luZyBhIGJvcmluZyBtYXANCg0KSG93IGRvIHdlIHJlYWxseSBrbm93IHdoYXQga2luZCBvZiBzcGF0aWFsIGluZm9ybWF0aW9uIHRoZSBnZW9tIGNvbHVtbiBjb250YWlucz8gT25lIHF1aWNrIHdheSB0byBsb29rIGlzIHRvIHJlbmRlciBhIHNpbXBsZSBwbG90LiBOb3RlIHRoYXQgaWYgeW91IHVzZSB0aGUgYmFzZSBmdW5jdGlvbiBgcGxvdCgpYCB3aXRoIGFuIGBzZipgIG9iamVjdCwgaXQgd2lsbCBwbG90IGEgbWFwIGZvciAqZXZlcnkgY29sdW1uIG9yIGF0dHJpYnV0ZSEqIEluIG90aGVyIHdvcmRzIHRoZXJlIGFyZSAxNyBkYXRhIGNvbHVtbnMgaW4gdGhlIG9iamVjdCBgbXZjYCwgYW5kIHRodXMgMTcgbWFwcyB3b3VsZCBiZSByZW5kZXJlZCBieSBkZWZhdWx0LiBCdXQgaWYgd2Ugb25seSB3YW50IHRvIHRha2UgYSBxdWljayBsb29rIHdlIGNvdWxkIGVpdGhlciBwbG90IHRoZSBnZW9tZXRyeSBhbG9uZSwgb3Igd2UgY291bGQgc3BlY2lmeSBhIHNpbmdsZSB2YXJpYWJsZSB0byBwbG90LiBUbyBwbG90IHRoZSBnZW9tZXRyeSAoZS5nLiBzaGFwZXMpIG9ubHkgSSB1c2UgdGhlIGZ1bmN0aW9uIGBzdF9nZW9tZXRyeSgpYC4NCg0KDQoNCmBgYHtyIGZpZy5zaG93ID0gJ2hvbGQnLCBvdXQud2lkdGggPSAnNTAlJ30NCnBsb3Qoc3RfZ2VvbWV0cnkobXZjKSkNCnBsb3QobXZjWydNVkNSQVRFXzE0J10pDQpgYGANCg0KDQoNCiMjIGBzcGAgcGFja2FnZQ0KDQpJdCBpcyBlYXN5IHRvIGNvbnZlcnQgYmV0d2VlbiB0aGUgY3VycmVudCBzdGFuZGFyZCBmb3JtYXQsIGBzcCpgIFNwYXRpYWwgY2xhc3NlcyBhbmQgdGhlIGVtZXJnaW5nIChhbmQgZm9yIG91ciBwdXJwb3NlcywgcHJpbWFyeSkgZm9ybWF0LCBgc2YqYCBTaW1wbGUgRmVhdHVyZSBjbGFzc2VzLiAgSW4gZ2VuZXJhbCB3ZSBjYW4gY29lcmNlIG9iamVjdHMgdG8gYHNwKmAgY2xhc3NlcyB1c2luZyB0aGUgYGFzKHgsICdDbGFzc1R5cGUnKWAgZm9ybWF0IGxpa2UgdGhpczoNCg0KYGBge3J9DQpsaWJyYXJ5KHNwKQ0KbXZjLnNwYXRpYWwgPC0gYXMobXZjLCAnU3BhdGlhbCcpDQoNCmNsYXNzKG12Yy5zcGF0aWFsKQ0Kc3VtbWFyeShtdmMuc3BhdGlhbCkNCmBgYA0KDQoNClJlY2FsbCB0aGUgY2xhc3MtdHlwZSBmb3IgdGhlIG9yaWdpbmFsIG9iamVjdCwgYG12Y2Agd2FzIGAic2YiYCAoYXMgd2VsbCBhcyBgInRibF9kZiJgIGFuZCBgImRhdGEuZnJhbWUiYCkuICBOb3cgdGhlIGBtdmMuc3BhdGlhbGAgb2JqZWN0IGhhcyBjbGFzcyBgIlNwYXRpYWxQb2x5Z29uc0RhdGFGcmFtZSJgLiAgTm90aWNlIHRoZSBgc3VtbWFyeSgpYCBjYWxsIGdpdmVzIHRoZSBleHBlY3RlZCBzdW1tYXJ5IG9mIGF0dHJpYnV0ZSBkYXRhLCBidXQganVzdCBsaWtlIHRoZSBgc2ZgIG9iamVjdHMsIGluY2x1ZGVzIHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgZ2VvbWV0cnkgYW5kIHByb2plY3Rpb24gYXQgdGhlIHRvcC4gDQoNClRvIG1ha2UgYSBzaW1wbGUgcGxvdCBvZiB0aGUgZ2VvbWV0cnkgb2YgdGhlIGBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWVgLCB5b3UgY2FuIG9uY2UgYWdhaW4gdXNlIHRoZSBiYXNlIGBwbG90KClgIGZ1bmN0aW9uYWxpdHk6DQoNCmBgYHtyfQ0KcGxvdChtdmMuc3BhdGlhbCkNCmBgYA0KDQpUaGUgYHNwYCBwYWNrYWdlIGFjdHVhbGx5IGhhcyBhIGZ1bmN0aW9uIGNhbGxlZCBgc3BwbG90KClgIHdoaWNoIHByb3ZpZGVzIGEgZ3JlYXQgZGVhbCBtb3JlIGNvbnRyb2wgZm9yIGNhcnRvZ3JhcGh5IHRoYW4gYmFzZSBgcGxvdCgpYC4gWW91IGNhbiByZXZpZXcgdGhlIGRvY3VtZW50YXRpb24gYWJvdXQgdXNlIG9mIGBzcHBsb3QoKWAgYnkgYXNraW5nIGZvciBoZWxwOiBgP3NwcGxvdGAuICBXaGlsZSBpdCBpcyBhIHVzZWZ1bCBmdW5jdGlvbiwgd2Ugd2lsbCBiZSB1c2luZyBhIGRpZmZlcmVudCBhcHByb2FjaCB0byBtYXAtcHJvZHVjdGlvbiBmb3IgbW9zdCBvZiBvdXIgd29yayBpbiB0aGlzIGNvdXJzZS4gDQoNCg0KIyMgQ29udmVydGluZyBiZXR3ZWVuIGBzZipgIGFuZCBgc3AqYA0KDQpJbiB0aGUgcHJldmlvdXMgc2VjdGlvbiB3ZSB1c2VkIHRoZSBgYXMoKWAgc3ludGF4IHRvIGNvbnZlcnQgZnJvbSBgc2ZgIHRvIGBzcGAgY2xhc3MuIFRoZSBzeW50YXggZm9yIGdvaW5nIHRoZSBvdGhlciB3YXkgaXMgYSBsaXR0bGUgZGlmZmVyZW50Lg0KDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIEdvIGZyb20gc2YgdG8gc3A6DQptdmMuc3BhdGlhbCA8LSBhcyhtdmMsIENsYXNzID0gIlNwYXRpYWwiKQ0KDQojIEdvIGZyb20gc3AgdG8gc2Y6DQptdmNfc2YgPC0gc3RfYXNfc2YobXZjLnNwYXRpYWwsICJzZiIpDQpgYGANCg0KDQoNCiMgQSBiaXQgbW9yZSBvbiBpbXBvcnQvZXhwb3J0IG9mIHNwYXRpYWwgZGF0YQ0KDQojIyBgc3RfcmVhZCgpYCBmb3IgaW1wb3J0aW5nIGRhdGENCg0KT25lIG9mIHRoZSBzdXBwb3NlZCBiZW5lZml0cyBvZiB1c2luZyB0aGUgYHNmYCBwYWNrYWdlIG92ZXIgYHNwYCB3YXMgZmFzdGVyIGFuZCBtb3JlIGVmZmljaWVudCByZWFkaW5nL3dyaXRpbmcgKGltcG9ydGluZy9leHBvcnRpbmcpIG9mIGRhdGEuIFNvIHdoYXQgZG9lcyB0aGF0IG1lYW4gZXhhY3RseT8gV2VsbCBmb3Igb25lIHRoaW5nLCB0aGUgYHN0X3JlYWQoKWAgZnVuY3Rpb24gd2UgdXNlZCB0byBvcmlnaW5hbGx5IGltcG9ydCB0aGUgYG12Y2AgZGF0YXNldCBpcyBub3QgbGltaXRlZCB0byBvbmx5IFNpbXBsZSBGZWF0dXJlIGRhdGEsIG9yIHRvIG9ubHkgdGhlIGAuZ3BrZ2AgZmlsZSBmb3JtYXQuICBJdCBjYW4gaW1wb3J0IGRvemVucyBvZiBkaWZmZXJlbnQgZGF0YSBmb3JtYXRzIGluY2x1ZGluZyAuc2hwLCAua21sLCBhbmQgZXZlbiBmcm9tIHRoZSBFU1JJIHNwYXRpYWwgZGF0YWJhc2UgZm9ybWF0LCB0aGUgZ2VvZGF0YWJhc2UgKC5nZGIpLiAgWW91IGNhbiByZWFkIG1vcmUgYWJvdXQgcmVhZGluZyBhbmQgd3JpdGluZyBzcGF0aWFsIGRhdGEgdXNpbmcgcGFja2FnZSBgc2ZgIGluIG9uZSBvZiB0aGUgW29ubGluZSB2aWduZXR0ZXMgKGNsaWNrIGhlcmUpXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc2YvdmlnbmV0dGVzL3NmMi5odG1sKS4NCg0KV2hpbGUgdGhlIGBzdF9yZWFkKClgIGFyZ3VtZW50cyBhbGxvdyB5b3UgdG8gc3BlY2lmeSBtYW55IHNwZWNpZmljcyBhYm91dCB0aGUgZGF0YSB5b3Ugd2lzaCB0byBpbXBvcnQsIG9mdGVuIHRoZSBmdW5jdGlvbiAnZ3Vlc3NlcycgdGhlIHJpZ2h0IG9uZSBiYXNlZCBvbiB0aGUgZmlsZSBleHRlbnNpb24gbmFtZS4gRm9yIGluc3RhbmNlOg0KDQpgYGB7cn0NCm12Yy5zaHAgPC0gc3RfcmVhZCgnLi4vLi4vREFUQS9HQV9NVkMvZ2FfbXZjLnNocCcpDQoNCmNsYXNzKG12Yy5zaHApDQpuYW1lcyhtdmMuc2hwKQ0KYGBgDQoNCg0KV2hhdCB3YXMgZGlmZmVyZW50IGhlcmUgZnJvbSBvdXIgaW5pdGlhbCBgc3RfcmVhZCgpYD8gSW4gdGhpcyBjYXNlIHdlIGFjdHVhbGx5IHJlYWQgaW4gYSB0b3RhbGx5IGRpZmZlcmVudCBkYXRhc2V0LCBjYWxsIGdhX212Yy5zaHAuIEl0IHdhcyBzYXZlZCBpbiB0aGUgRVNSSSBzaGFwZWZpbGUgZm9ybWF0LCBidXQgd2hlbiByZWFkIGluIHdlIHNlZSB0aGUgZ2VvbWV0cnkgaW5mb3JtYXRpb24gKG11bHRpcG9seWdvbiksIHByb2plY3Rpb24gaW5mcm9tYXRpb24sIGFuZCBhdHRyaWJ1dGUgZGF0YSBoYXZlIGFsbCBiZWVuIGltcG9ydGVkIGFzIGFuIGBzZmAgb2JqZWN0LiAgT25lIG5vdGUgYWJvdXQgdGhlIGZpZWxkIG5hbWVzOiBFU1JJIGhhcyBydWxlcyAoYW5ub3lpbmdseSByZXN0cmljdGluZyBydWxlcyBpbiBteSBvcGluaW9uKSBhYm91dCB0aGUgbGVuZ3RoIG9mIHZhcmlhYmxlIG5hbWVzLi4uc28gbm90aWNlIGhvdyBvdXIgdmFyaWFibGUgbmFtZXMgYXJlIHRydW5jYXRlZC4gDQoNCg0KIyMgYHN0X3dyaXRlKClgIGZvciBleHBvcnRpbmcgZGF0YSANCg0KDQpXaGF0IGFib3V0IHNhdmluZy93cml0aW5nIGRhdGE/IEl0J3MganVzdCBhYm91dCBhcyBlYXN5LiBUaGUgY29ycmVzcG9uZGluZyBjb21tYW5kIGlzLCBgc3Rfd3JpdGUoKWAgYW5kIGFnYWluIHRoZSBmdW5jdGlvbiBjYW4gJ2d1ZXNzJyB3aGF0IGZvcm1hdCBiYXNlZCBvbiB0aGUgZmlsZSBuYW1lLCBhbHRob3VnaCB5b3UgY2FuIGFsc28gc3BlY2lmeSB0aGUgYXJndW1lbnRzIHRvIGJlIGNlcnRhaW46DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQoNCiMgU2F2ZSBhcyBhIGdlb3BhY2thZ2UgLmdwa2cgKHRoaXMgaXMgZWZmaWNpZW50IHN0b3JhZ2UgZm9ybWF0KQ0Kc3Rfd3JpdGUobXZjLCAnZ2FfbXZjMi5ncGtnJykNCg0KIyBTYXZlIGFzIGFuIEVTUkkgc2hhcGVmaWxlDQpzdF93cml0ZShtdmMsICdnYV9tdmMyLnNocCcpDQoNCiMgU2F2ZSBhcyBzaGFwZWZpbGUsIGJ1dCBzcGVjaWZ5IGFyZ3VtZW50cyBkaXJlY3RseQ0Kc3Rfd3JpdGUobXZjLCBkc24gPSAiZ2FfbXZjMi5zaHAiLCBsYXllciA9ICJnYV9tdmMuc2hwIiwgZHJpdmVyID0gIkVTUkkgU2hhcGVmaWxlIikpDQoNCmBgYA0KDQoNCiMjIFdoYXQgaXMgYC5ncGtnYD8NCg0KYC5ncGtnYCBpcyBzaG9ydCBmb3IgdGhlIGdlb3BhY2thZ2UgZm9ybWF0LCB3aGljaCBpcyBhbiBvcGVuLXNvdXJjZSBmb3JtYXQgZm9yIHN0b3JpbmcgdmVjdG9yIG9yIHJhc3RlciBkYXRhLiBHZW9wYWNrYWdlcyBhcmUgc3RvcmVkIGFzIGEgc2luZ2xlIGZpbGUgb24geW91ciBjb21wdXRlciwgYW5kIGluIGEgZm9ybWF0IHRoYXQgY2FuIGJlIHJlYWQgYnkgbWFueSBvdGhlciBHSVMgc29mdHdhcmUgcGFja2FnZXMgaW5jbHVkaW5nIEFyY0dJUyEgIFlvdSBjYW4gdHJ5IG5hdmlnYXRpbmcgd2l0aGluIEFyYyBDYXRhbG9nIHRvIHdoZXJlZXZlciB5b3Ugc2F2ZSB5b3VyIGAuZ3BrZ2AgZGF0YSBhbmQgc2VlIHRoYXQgaXQgYXBwZWFycyBhcyBhIHNwYXRpYWwgZGF0YWJhc2UgaW4gQXJjLiBZb3UgY2FuIGFkZCB0aGUgZGF0YXNldCBpbnRvIEFyY01hcCBkaXJlY3RseSBmcm9tIHRoaXMgZm9ybWF0LiANCg0KV2hpbGUgSSByZWNvZ25pemUgdGhhdCBtdWNoIG9mIHRoZSB2ZWN0b3Igc3BhdGlhbCBkYXRhIGluIHRoZSB3b3JsZCBpcyBpbiB0aGUgRVNSSSAuc2hwIGZvcm1hdCwgd2Ugd2lsbCBwcmltYXJpbHkgc2F2ZSBkYXRhIGluIHRoZSBgLmdwa2dgIGZvcm1hdCBiZWNhdXNlIGl0IGlzIGZsZXhpYmxlLCBlZmZpY2llbnQsIGFuZCBzdG9yZWQgYXMgYSBzaW5nbGUgZmlsZSBvbiBkaXNrIHNvIGF2b2lkcyB0aGUgcG90ZW50aWFsIGNvbmZ1c2lvbiB0aGF0IG9jY3VycyB3aGVuIG1vdmluZyBkYXRhIHRoYXQgYXJlIGNvbnRhaW5lZCBpbiA0LTggc2VwYXJhdGUgZmlsZXMgb24gZGlzay4NCg0KDQojIENvb3JkaW5hdGUgc3lzdGVtcyAmIFByb2plY3Rpb25zIGluIFINCg0KVGhlcmUgYXJlIHR3byByZWxhdGVkIGNvbmNlcHRzIGltcG9ydGFudCB0byBwcm9wZXJseSBtYW5hZ2luZywgYW5hbHl6aW5nLCBhbmQgdmlzdWFsaXppbmcgc3BhdGlhbCBkYXRhOiB0aGUgQ29vcmRpbmF0ZSBTeXN0ZW0gYW5kIHRoZSBQcm9qZWN0aW9uLiBJIGFzc3VtZSB5b3UgaGF2ZSBsZWFybmVkIGFib3V0IGVhY2ggb2YgdGhlc2UgaW4geW91ciBpbnRybyB0byBHSVMgY291cnNlLCBzbyBJIG9ubHkgdmVyeSBicmllZmx5IHJldmlldyB0aGVtLCB3aXRoIGF0dGVudGlvbiB0byBob3cgd2UgbWFuYWdlIHRoZW0gaW4gUi4NCg0KKlRoZSBjb29yZGluYXRlIHN5c3RlbSogcmVmZXJzIHRvIGFuIGFncmVlZCB1cG9uIHJlZmVyZW5jZSBzeXN0ZW0gZm9yIHJlcHJlc2VudGluZyBsb2NhdGlvbiBpbmZvcm1hdGlvbiBhY3Jvc3MgZGF0YXNldHMgaW4gYSB3YXkgdGhhdCBhbGxvd3MgdGhlbSB0byBiZSBwcm9wZXJseSBhbGlnbmVkIHdpdGggb25lIGFub3RoZXIgYW5kIGhhdmUgYSBrbm93biByZWxhdGlvbnNoaXAgd2l0aCB0aGUgd29ybGQuIFRoZSBjb29yZGluYXRlIHN5c3RlbSBpcyBkZWZpbmVkIGJ5IGl0cyBnZW9ncmFwaGljYWwgc2hhcGUgKHNwaGVyaWNhbCBvciBwbGFuYXIpLCByZWZlcmVuY2UgcG9pbnRzIChlLmcuIHRoZSBwcmltZSBtZXJpZGlhbiksIGl0cyB1bml0cyBvZiBtZWFzdXJlbWVudCAoZS5nLiBkZWdyZWVzIGxhdGl0dWRlL2xvbmdpdHVkZSBvciBtZXRlcnMpLiAgQSBjb21tb24gY29vcmRpbmF0ZSBzeXN0ZW0gaXMgdGhlIEdDUzg0IChXb3JsZCBHZW9kZXRpYyBTeXN0ZW0gMTk4NCkuICANCg0KKk1hcCBwcm9qZWN0aW9ucyogYXJlIHZhcnlpbmcgd2F5cyB0byByZXByZXNlbnQgdGhlIHNwaGVyaWNhbCB3b3JsZCBvbiBhIGZsYXQgcGllY2Ugb2YgcGFwZXIuIEJlY2F1c2UgdGhpcyBpcyBpbmhlcmVudGx5IGltcG9zc2libGUgdG8gZG8gd2l0aCBjb21wbGV0ZSBhY2N1cmFjeSwgcHJvamVjdGlvbnMgdmFyeSB3aXRoIHJlc3BlY3QgdG8gd2hhdCBhc3BlY3Qgb2YgcmVwcmVzZW50YXRpb24gaXMgbW9zdCBpbXBvcnRhbnQuIEZvciBpbnN0YW5jZSBzb21lIHByb2plY3Rpb25zIG1haW50YWluIG1hcCBkaXN0YW5jZSB0byBiZSBjb25zaXN0ZW50IGFjcm9zcyB0aGUgbWFwOyBvdGhlciBtYWludGFpbiBhcmVhOyBvdGhlcnMgbWFpbnRhaW4gYW5nbGVzLCBldGMuIFlvdSBjYW4gc2VlIG9ubGluZSByZXNvdXJjZXMgdG8gcmVhZCBtb3JlIGFib3V0IGNvbW1vbiBwcm9qZWN0aW9ucy4NCg0KDQojIyBQcm9qZWN0aW9ucyAmIGNvb3JkaW5hdGUgc3lzdGVtcyBpbiBSICANCg0KQW55IHNwYXRpYWwgZGF0YSB3ZSB3aWxsIHNlZSBpbiB0aGlzIGNvdXJzZSB3YXMgY3JlYXRlZCB1c2luZyBhIGNvb3JkaW5hdGUgc3lzdGVtLCBhbHRob3VnaCBub3QgYWxsIGRhdGEgYXJlICdwcm9qZWN0ZWQnLiAgVGhlcmUgaXMgYnVpbHQtaW4gZnVuY3Rpb25hbGl0eSB0byB0cmFuc2Zvcm0gYSBnaXZlbiBkYXRhc2V0IGludG8gYW5vdGhlciBjb29yZGluYXRlIHN5c3RlbSBvciBwcm9qZWN0aW9uLCBidXQgYmVmb3JlIHdlIGNhbiBkbyB0aGF0IHdlIG11c3Qga25vdyB3aGVyZSB3ZSBhcmUgc3RhcnRpbmchDQoNCg0KYGBge3J9DQojIHRvIHNlZSB0aGUgY3VycmVudCBjb29yZGluYXRlIGluZm9ybWF0aW9uIHVzZSBzdF9jcnMoKQ0KDQpzdF9jcnMobXZjKQ0KYGBgDQoNCg0KVGhpcyBsaXR0bGUgYml0IG9mIGNvZGUgYW5kIHJlc3VsdCBvcGVucyB1cCBhIHdob2xlIHNldCBvZiB3b3JkcyBhbmQgc3ltYm9scyB3ZSB3aWxsIHNlZSBvZnRlbi4gQnJpZWZseSBoZXJlIHRoZXkgYXJlOg0KDQoqICAqKkNSUyoqIChhcyBpbiBzdF9jcnMpIHN0YW5kcyBmb3IgY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtLiBNYW55IHByb2plY3Rpb24vY29vcmRpbmF0ZSByZWxhdGVkIHRhc2tzIGluIFIgcmVxdWlyZSBkZWZpbmluZyBhbiBvYmplY3RzIENSUw0KKiAgKipFUFNHKiogc3RhbmRzIGZvciBFdXJvcGVhbiBQZXRyb2xldW0gU3VydmV5IEdyb3VwLiBUaGUgRVBTRyBzeXN0ZW0gaXMgb25lIG9mIHNldmVyYWwgdXNlZCB0byByZWZlcmVuY2UgZGlmZmVyZW50IGNvb3JkaW5hdGUvcHJvamVjdGlvbiBzeXN0ZW1zLiAgRVBTRyA0MjY5IGlzIHRoZSBOQUQ4MyBkYXR1bSBsYXQvbG9uIGNvb3JkaW5hdGUgc3lzdGVtOyB0aGlzIGlzIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgY29vcmRpbmF0ZSBzeXN0ZW0gYnkgVVMgZmVkZXJhbCBhZ2VuY2llcy4NCiogICoqcHJvajRzdHJpbmcqKiBBbm90aGVyIHRvb2wgdG8gZGVmaW5lIHByb2plY3Rpb25zIGlzIGEgc3RhbmRhcmRpemVkIHRleHQgc3RyaW5nIG1haW50YWluZWQgYnkgYW4gaW50ZXJuYXRpb25hbCBib2R5LiAgVGhpcyBpcyBhIHBhcmFsbGVsIG9yIGFsdGVybmF0ZSBtZWFucyBmb3Igc3BlY2lmeWluZyBwcm9qZWN0aW9uL2Nvb3JkaW5hdGUgYXMgdGhlIEVQU0cuDQoNCg0KU29tZXRpbWVzIHlvdSBrbm93IHlvdSB3YW50IGEgc3BlY2lmaWMgcHJvamVjdGlvbiBidXQgZG9uJ3Qga25vdyB0aGUgbnVtYmVyLiBUaGVyZSBhcmUgbWFueSByZXNvdXJjZXMgb25saW5lIGJ1dCBvbmUgdGhhdCBpcyB1c2VmdWwgaXMgaHR0cDovL3NwYXRpYWxyZWZlcmVuY2Uub3JnLy4gIA0KDQpTbyBpbiB0aGUgY2FzZSBvZiBvdXIgY3VycmVudCBHZW9yZ2lhIENvdW50aWVzIGRhdGFzZXQgd2UgY2FuIHNlZSB0aGF0IGl0IGlzICd1bnByb2plY3RlZCcuIEZvciBwdWJsaWMgaGVhbHRoIG1hcHBpbmcgb2YgYXJlYWwgdW5pdHMgKGxpa2UgY291bnRpZXMpIEkgcHJlZmVyIGVxdWFsLWFyZWEgcHJvamVjdGlvbnMgYmVjYXVzZSBpdCBhc3N1cmVzIGVhY2ggZ2VvZ3JhcGhpYyB1bml0IGlzIGZhaXJseSByZXByZXNlbnRlZCB0byB0aGUgdmlld2VyLiBUbyB0cmFuc2Zvcm0gdGhlIGN1cnJlbnQgZGF0YSB0byBzb21ldGhpbmcgbmV3IHdlIG11c3QgZmlyc3QgZGVmaW5lIHRoZSAnbmV3JyBwcm9qZWN0ZWQgY29vcmRpbmF0ZSBzeXN0ZW0uIEkgdXNlZCBvbmUgb2YgdGhlIG9ubGluZSBkaXJlY3RvcmllcyAoaHR0cDovL3d3dy5lcHNnLXJlZ2lzdHJ5Lm9yZy8pIHRvIGxvb2sgdXAgdGhlIEFsYmVycyBFcXVhbCBBcmVhIFVTQSBwcm9qZWN0aW9uLiBJdCBpcyBgRVBTRzo6NTA2OWAuIFRvIGNoYW5nZSBmcm9tIG9sZCB0byBuZXcgd2UgdXNlIGBzdF90cmFuc2Zvcm0oKWAuDQoNCmBgYHtyfQ0KbXZjLmFsYmVycyA8LSBzdF90cmFuc2Zvcm0obXZjLCAxMDIwMDMpDQoNCnN0X2NycyhtdmMuYWxiZXJzKQ0KYGBgDQoNCk5vdyB3ZSB3aWxsIHBsb3QgdGhlICdvbGQnIGFuZCB0aGUgJ25ldycgdG8gc2VlIGlmIHRoZXkgZGlmZmVyLg0KDQpgYGB7cn0NCnBsb3Qoc3RfZ2VvbWV0cnkobXZjKSwgbWFpbiA9ICdVbnByb2plY3RlZCBsYXQvbG9uJykNCnBsb3Qoc3RfZ2VvbWV0cnkobXZjLmFsYmVycyksIG1haW4gPSAnQWxiZXJzIHByb2plY3Rpb24nKQ0KYGBgDQoNCg0KDQoNCg0K