This post is about “R interface for Apache Spark” using R package. For newbie like me, settings, installation, prerequisite, etc.. interfacing, connectings between components are always hard, most of cases, it takes lots of time.
Wanna share kindly with others the important notes when settings are ongoing.
Okay, Let’s begin.
List of Downloads
Please read below carefully before downloading resources.
Note: Spark runs on Java 8, Python 2.7+/3.4+ and R 3.1+. For the Scala API, Spark 2.4.4 uses Scala 2.12. You will need to use a compatible Scala version (2.12.x) (Dec 2019).
Personally, I was in trouble with settings because of Java versions. The version of Java was Java 11. and it was difficult to use. If you don’t have Java 8, then please download Java 8. (Spark Documentation)
The other way to download is brew install blah~. But, In this post, the way will not be included.
Okay, lists of downloads are followed.
You can install the sparklyr package from CRAN
install.packages("sparklyr")
Setting version is important, you may check which version is available with spark_available_versions().
library(sparklyr)
spark_available_versions()
The, You are able to install a local version of Spark for development purposes:
> spark_install(version = "2.4.0")
Installing Spark 2.4.0 for Hadoop 2.7 or later.
Downloading from:
- 'https://archive.apache.org/dist/spark/spark-2.4.0/spark-2.4.0-bin-hadoop2.7.tgz'
Installing to:
- '~/spark/spark-2.4.0-bin-hadoop2.7'
trying URL 'https://archive.apache.org/dist/spark/spark-2.4.0/spark-2.4.0-bin-hadoop2.7.tgz'
Content type 'application/x-gzip' length 227893062 bytes (217.3 MB)
When running spark_install(), the spark installation folders are downloaded at directory ~/spark/spark-2.4.0-bin-hadoop2.7
Then, you get all resources in order to connect between spark and R.
Step 2. Preparations
2.1. Home folder
- The main home folder is
/Users/your_account_name
- If you don’t know your home folder, then please type
cd $HOME and run it.
- My case is
/Users/evan/
2.2. The installation folder
- Java, Python are sets automatically when installing it. You don’t need to touch them.
- However, Sbt, Scala, and Spark will be installed at
/Users/evan/server
- How to make server folder on terminal? It’s easy.
~ evan$ mkdir server
~ evan$ cd server
Note for beginners, the command cd changes your working directory (from wherever it is) to HOME directory.
2.3 Move all downloaded files to $HOME/server folder
Once you copy all files, please double check the necessary files below. scalar, sbt, spark

3. Set up Shell Environment editing bash_profile file
Here are the directory paths of the programs that we have installed so far:
- JDK: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk
- Python: /Library/Frameworks/Python.framework/Versions/3.7
- Sbt: /Users/evan/server/sbt
- Scala: /Users/evan/server/scala-2.13.1
- Spark: /Users/evan/server/spark-2.4.0-bin-hadoop2.7
To check one more if every folder is at the directory where it should be, always use the command cd. For instance try to put command $ cd /Users/evan/server/sbt. If directory is changed, then it’s correct. if not, then some files are not saved correctly.
3.1. Set up .bash_profile file
For beginners, this file starts with a “dot”. Therefore, make sure that you type the file name correctly, which is .bash_profile (with a “dot” in front).
Open the .bash_profile file, which is located at your HOME directory (i.e., ~/.bash_profile), using any text editor (e.g., TextEdit, nano, vi, or emacs). For example, my favorite editor is emacs. So, it could be following.

3.2. Edit .bash_profile file
Copy these lines to the file.
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/
export SPARK_HOME=/Users/evan/server/spark-2.4.0-bin-hadoop2.7
export SBT_HOME=/Users/evan/server/sbt
export SCALA_HOME=/Users/evan/server/scala-2.13.1
export PATH=$JAVA_HOME/bin:$SBT_HOME/bin:$SBT_HOME/lib:$SCALA_HOME/bin:$SCALA_HOME/lib:$PATH
export PATH=$JAVA_HOME/bin:$SPARK_HOME:$SPARK_HOME/bin:$SPARK_HOME/sbin:$PATH
export PYSPARK_PYTHON=python3
When copyting to .bash_profile, DO NOT DELETE OTHER LINES. After copying all to .bash_profile, then save and close file.
3.3. Apply update .bash_profile file
Since the .bash_profile has been changed, we have to reload it. Options are
- back to terminal, and type
source ~/.bash_profile
Or
- Quit and reopen the Terminal program. Make sure you completely quit the Terminal using menu → Quit Terminal (⌘Q), otherwise the environment variables declared above will not be loaded.
4. Connecting to Spark
You can connect to both local instances of Spark as well as remote Spark clusters. Here we’ll connect to a local instance of Spark via the spark_connect function:
> library(sparklyr)
> sc <- spark_connect(master = "local", spark_home = "../../server/spark-2.4.0-bin-hadoop2.7/")
* Using Spark: 2.4.0
Welcome to Spark. The details are sparklyr: R interface for Apache Spark
5. Examples
All sample codes are written at the offical documentations.
5.1. Using dplyr
We are able to use all of the available dplyr functions within the spark cluster.
We’ll start by copying some datasets from R into the Spark cluster (note that you may need to install the nycflights13 and Lahman packages in order to execute this code):
> library(dplyr)
> library(nycflights13)
> library(Lahman)
> iris_tbl <- copy_to(sc, iris)
> flights_tbl <- copy_to(sc, nycflights13::flights, "flights")
> batting_tbl <- copy_to(sc, Lahman::Batting, "batting")
> src_tbls(sc)
[1] "batting" "flights" "iris"
When copyting to spark, you may see these dataset at spark UI. Open web browser, type http://localhost:4040/storage/. Then you may see pic below.

Let’s use filter() in dplyr package
# filter by departure delay and print the first few records
> flights_tbl %>% filter(dep_delay == 2)
# Source: spark<?> [?? x 19]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 1 1 542 540 2 923 850
3 2013 1 1 702 700 2 1058 1014
4 2013 1 1 715 713 2 911 850
5 2013 1 1 752 750 2 1025 1029
6 2013 1 1 917 915 2 1206 1211
7 2013 1 1 932 930 2 1219 1225
8 2013 1 1 1028 1026 2 1350 1339
9 2013 1 1 1042 1040 2 1325 1326
10 2013 1 1 1231 1229 2 1523 1529
# … with more rows, and 11 more variables: arr_delay <dbl>, carrier <chr>,
# flight <int>, tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>,
# distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
Here, you may read # Source: spark<?> [?? x 19]. This points that dplyr function uses datasets from spark-cluster stored.
Using SQL
It’s possible to execute SQL queries directly against tables if you are more familar with SQL. The spark_connection object implements a DBI interface for Spark, so you can use dbGetQuery to execute SQL and return the result as an R data frame:
> library(DBI)
> iris_preview <- dbGetQuery(sc, "SELECT * FROM iris LIMIT 10")
> iris_preview
Sepal_Length Sepal_Width Petal_Length Petal_Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
Machine Learning
You can orchestrate machine learning algorithms in a Spark cluster via the machine learning functions within sparklyr. These functions connect to a set of high-level APIs built on top of DataFrames that help you create and tune machine learning workflows.
Here’s an example where we use ml_linear_regression to fit a linear regression model. We’ll use the built-in mtcars dataset, and see if we can predict a car’s fuel consumption (mpg) based on its weight (wt), and the number of cylinders the engine contains (cyl). We’ll assume in each case that the relationship between mpg and each of our features is linear.
# copy mtcars into spark
> mtcars_tbl <- copy_to(sc, mtcars)
# transform our data set, and then partition into 'training', 'test'
> partitions <- mtcars_tbl %>%
+ filter(hp >= 100) %>%
+ mutate(cyl8 = cyl == 8) %>%
+ sdf_random_split(training = 0.5, test = 0.5, seed = 1099)
# fit a linear model to the training dataset
> fit <- partitions$training %>%
+ ml_linear_regression(response = "mpg", features = c("wt", "cyl"))
> fit
Formula: mpg ~ wt + cyl
Coefficients:
(Intercept) wt cyl
33.499452 -2.818463 -0.923187
> summary(fit)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.752 -1.134 -0.499 1.296 2.282
Coefficients:
(Intercept) wt cyl
33.499452 -2.818463 -0.923187
R-Squared: 0.8274
Root Mean Squared Error: 1.422
Spark machine learning supports a wide array of algorithms and feature transformations and as illustrated above it’s easy to chain these functions together with dplyr pipelines.
6. Conclusion
Dealing with this tutorial for a couple of days. The hard thing to me was to satisfy system requirements. Environment settings are not always comfortable with me who has studied Liberal Arts - Philiosophy, Religious Studies, Development Studies. All area is trying to collect data and store somewhere else, and get data from Database or Clusters, and finally analyse them. Although settings up data pipeline is a bit far away from analyzing data, still it’s valuable for them to deal with part of data engineering area.
Hope to enjoy and happy to code.
LS0tCnRpdGxlOiAiSG93IHRvIHVzZSBTcGFyayBpbiBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBFdmFuIEp1bmcKZGF0ZTogIjIwMTktMTItMDYiCi0tLQoKVGhpcyBwb3N0IGlzIGFib3V0ICJSIGludGVyZmFjZSBmb3IgQXBhY2hlIFNwYXJrIiB1c2luZyBSIHBhY2thZ2UuIEZvciBuZXdiaWUgbGlrZSBtZSwgc2V0dGluZ3MsIGluc3RhbGxhdGlvbiwgcHJlcmVxdWlzaXRlLCBldGMuLiBpbnRlcmZhY2luZywgY29ubmVjdGluZ3MgYmV0d2VlbiBjb21wb25lbnRzIGFyZSBhbHdheXMgaGFyZCwgbW9zdCBvZiBjYXNlcywgaXQgdGFrZXMgbG90cyBvZiB0aW1lLiAKCldhbm5hIHNoYXJlIGtpbmRseSB3aXRoIG90aGVycyB0aGUgaW1wb3J0YW50IG5vdGVzIHdoZW4gc2V0dGluZ3MgYXJlIG9uZ29pbmcuIAoKT2theSwgTGV0J3MgYmVnaW4uIAoKIyBMaXN0IG9mIERvd25sb2FkcwpQbGVhc2UgcmVhZCBiZWxvdyBjYXJlZnVsbHkgYmVmb3JlIGRvd25sb2FkaW5nIHJlc291cmNlcy4gCgo+IE5vdGU6IFNwYXJrIHJ1bnMgb24gSmF2YSA4LCBQeXRob24gMi43Ky8zLjQrIGFuZCBSIDMuMSsuIEZvciB0aGUgU2NhbGEgQVBJLCBTcGFyayAyLjQuNCB1c2VzIFNjYWxhIDIuMTIuIFlvdSB3aWxsIG5lZWQgdG8gdXNlIGEgY29tcGF0aWJsZSBTY2FsYSB2ZXJzaW9uICgyLjEyLngpIChEZWMgMjAxOSkuCgpQZXJzb25hbGx5LCBJIHdhcyBpbiB0cm91YmxlIHdpdGggc2V0dGluZ3MgYmVjYXVzZSBvZiBKYXZhIHZlcnNpb25zLiBUaGUgdmVyc2lvbiBvZiBKYXZhIHdhcyBKYXZhIDExLiBhbmQgaXQgd2FzIGRpZmZpY3VsdCB0byB1c2UuIElmIHlvdSBkb24ndCBoYXZlIEphdmEgOCwgdGhlbiBwbGVhc2UgZG93bmxvYWQgSmF2YSA4LiAoW1NwYXJrIERvY3VtZW50YXRpb25dKGh0dHBzOi8vc3BhcmsuYXBhY2hlLm9yZy9kb2NzL2xhdGVzdC8pKQoKVGhlIG90aGVyIHdheSB0byBkb3dubG9hZCBpcyBgYnJldyBpbnN0YWxsIGJsYWh+YC4gQnV0LCBJbiB0aGlzIHBvc3QsIHRoZSB3YXkgd2lsbCBub3QgYmUgaW5jbHVkZWQuIAoKT2theSwgbGlzdHMgb2YgZG93bmxvYWRzIGFyZSBmb2xsb3dlZC4gCgotIEpBVkE6IFtqZGstOHUyMzFdKGh0dHBzOi8vd3d3Lm9yYWNsZS5jb20vdGVjaG5ldHdvcmsvamF2YS9qYXZhc2UvZG93bmxvYWRzL2pkazgtZG93bmxvYWRzLTIxMzMxNTEuaHRtbCkKLSBTY2FsYTogW3NjYWxhLTIuMTMuMV0oaHR0cHM6Ly9kb3dubG9hZHMubGlnaHRiZW5kLmNvbS9zY2FsYS8yLjEzLjEvc2NhbGEtMi4xMy4xLnRnegopCi0gU0JUOiBbc2J0LTEuMy40XShodHRwczovL3BpY2NvbG8ubGluay9zYnQtMS4zLjQuemlwKQotIFNwYXJrIGNhbiBiZSBkb3dubG9hZGVkIHZpYSBSIFBhY2thZ2UgKFtzcGFya2x5cl0oaHR0cHM6Ly9zcGFyay5yc3R1ZGlvLmNvbS8pKQoKWW91IGNhbiBpbnN0YWxsIHRoZSBzcGFya2x5ciBwYWNrYWdlIGZyb20gQ1JBTgpgYGAKaW5zdGFsbC5wYWNrYWdlcygic3BhcmtseXIiKQpgYGAKClNldHRpbmcgdmVyc2lvbiBpcyBpbXBvcnRhbnQsIHlvdSBtYXkgY2hlY2sgd2hpY2ggdmVyc2lvbiBpcyBhdmFpbGFibGUgd2l0aCBgc3BhcmtfYXZhaWxhYmxlX3ZlcnNpb25zKClgLiAKYGBgCmxpYnJhcnkoc3BhcmtseXIpCnNwYXJrX2F2YWlsYWJsZV92ZXJzaW9ucygpCmBgYAoKVGhlLCBZb3UgYXJlIGFibGUgdG8gaW5zdGFsbCBhIGxvY2FsIHZlcnNpb24gb2YgU3BhcmsgZm9yIGRldmVsb3BtZW50IHB1cnBvc2VzOgpgYGAKPiBzcGFya19pbnN0YWxsKHZlcnNpb24gPSAiMi40LjAiKQpJbnN0YWxsaW5nIFNwYXJrIDIuNC4wIGZvciBIYWRvb3AgMi43IG9yIGxhdGVyLgpEb3dubG9hZGluZyBmcm9tOgotICdodHRwczovL2FyY2hpdmUuYXBhY2hlLm9yZy9kaXN0L3NwYXJrL3NwYXJrLTIuNC4wL3NwYXJrLTIuNC4wLWJpbi1oYWRvb3AyLjcudGd6JwpJbnN0YWxsaW5nIHRvOgotICd+L3NwYXJrL3NwYXJrLTIuNC4wLWJpbi1oYWRvb3AyLjcnCnRyeWluZyBVUkwgJ2h0dHBzOi8vYXJjaGl2ZS5hcGFjaGUub3JnL2Rpc3Qvc3Bhcmsvc3BhcmstMi40LjAvc3BhcmstMi40LjAtYmluLWhhZG9vcDIuNy50Z3onCkNvbnRlbnQgdHlwZSAnYXBwbGljYXRpb24veC1nemlwJyBsZW5ndGggMjI3ODkzMDYyIGJ5dGVzICgyMTcuMyBNQikKYGBgCldoZW4gcnVubmluZyBgc3BhcmtfaW5zdGFsbCgpYCwgdGhlIHNwYXJrIGluc3RhbGxhdGlvbiBmb2xkZXJzIGFyZSBkb3dubG9hZGVkIGF0IGRpcmVjdG9yeSBgfi9zcGFyay9zcGFyay0yLjQuMC1iaW4taGFkb29wMi43YAoKVGhlbiwgeW91IGdldCBhbGwgcmVzb3VyY2VzIGluIG9yZGVyIHRvIGNvbm5lY3QgYmV0d2VlbiBzcGFyayBhbmQgUi4KCiMgU3RlcCAyLiBQcmVwYXJhdGlvbnMKIyMgMi4xLiBIb21lIGZvbGRlcgotIFRoZSBtYWluIGhvbWUgZm9sZGVyIGlzIGAvVXNlcnMveW91cl9hY2NvdW50X25hbWVgCi0gSWYgeW91IGRvbid0IGtub3cgeW91ciBob21lIGZvbGRlciwgdGhlbiBwbGVhc2UgdHlwZSBgY2QgJEhPTUVgIGFuZCBydW4gaXQuIAotIE15IGNhc2UgaXMgYC9Vc2Vycy9ldmFuL2AKCiMjIDIuMi4gVGhlIGluc3RhbGxhdGlvbiBmb2xkZXIKLSBKYXZhLCBQeXRob24gYXJlIHNldHMgYXV0b21hdGljYWxseSB3aGVuIGluc3RhbGxpbmcgaXQuIFlvdSBkb24ndCBuZWVkIHRvIHRvdWNoIHRoZW0uIAotIEhvd2V2ZXIsIFNidCwgU2NhbGEsIGFuZCBTcGFyayB3aWxsIGJlIGluc3RhbGxlZCBhdCBgL1VzZXJzL2V2YW4vc2VydmVyYAotIEhvdyB0byBtYWtlIHNlcnZlciBmb2xkZXIgb24gdGVybWluYWw/IEl0J3MgZWFzeS4gCmBgYAp+IGV2YW4kIG1rZGlyIHNlcnZlcgp+IGV2YW4kIGNkIHNlcnZlcgpgYGAKTm90ZSBmb3IgYmVnaW5uZXJzLCB0aGUgY29tbWFuZCBgY2RgIGNoYW5nZXMgeW91ciB3b3JraW5nIGRpcmVjdG9yeSAoZnJvbSB3aGVyZXZlciBpdCBpcykgdG8gSE9NRSBkaXJlY3RvcnkuCgojIyAyLjMgTW92ZSBhbGwgZG93bmxvYWRlZCBmaWxlcyB0byAkSE9NRS9zZXJ2ZXIgZm9sZGVyCk9uY2UgeW91IGNvcHkgYWxsIGZpbGVzLCBwbGVhc2UgZG91YmxlIGNoZWNrIHRoZSBuZWNlc3NhcnkgZmlsZXMgYmVsb3cuIGBzY2FsYXJgLCBgc2J0YCwgYHNwYXJrYAoKIVtdKHBpY3MvcGljMDFfZmlsZXMucG5nKQoKIyAzLiBTZXQgdXAgU2hlbGwgRW52aXJvbm1lbnQgZWRpdGluZyBiYXNoX3Byb2ZpbGUgZmlsZQpIZXJlIGFyZSB0aGUgZGlyZWN0b3J5IHBhdGhzIG9mIHRoZSBwcm9ncmFtcyB0aGF0IHdlIGhhdmUgaW5zdGFsbGVkIHNvIGZhcjoKCi0gSkRLOiAvTGlicmFyeS9KYXZhL0phdmFWaXJ0dWFsTWFjaGluZXMvamRrMS44LjBfMTkxLmpkawotIFB5dGhvbjogL0xpYnJhcnkvRnJhbWV3b3Jrcy9QeXRob24uZnJhbWV3b3JrL1ZlcnNpb25zLzMuNwotIFNidDogL1VzZXJzL2V2YW4vc2VydmVyL3NidAotIFNjYWxhOiAvVXNlcnMvZXZhbi9zZXJ2ZXIvc2NhbGEtMi4xMy4xCi0gU3Bhcms6IC9Vc2Vycy9ldmFuL3NlcnZlci9zcGFyay0yLjQuMC1iaW4taGFkb29wMi43CgpUbyBjaGVjayBvbmUgbW9yZSBpZiBldmVyeSBmb2xkZXIgaXMgYXQgdGhlIGRpcmVjdG9yeSB3aGVyZSBpdCBzaG91bGQgYmUsIGFsd2F5cyB1c2UgdGhlIGNvbW1hbmQgYGNkYC4gRm9yIGluc3RhbmNlIHRyeSB0byBwdXQgY29tbWFuZCBgJCBjZCAvVXNlcnMvZXZhbi9zZXJ2ZXIvc2J0YC4gSWYgZGlyZWN0b3J5IGlzIGNoYW5nZWQsIHRoZW4gaXQncyBjb3JyZWN0LiBpZiBub3QsIHRoZW4gc29tZSBmaWxlcyBhcmUgbm90IHNhdmVkIGNvcnJlY3RseS4gCgojIyAzLjEuIFNldCB1cCAuYmFzaF9wcm9maWxlIGZpbGUKLSBGb3IgYmVnaW5uZXJzLCB0aGlzIGZpbGUgc3RhcnRzIHdpdGggYSDigJxkb3TigJ0uIFRoZXJlZm9yZSwgbWFrZSBzdXJlIHRoYXQgeW91IHR5cGUgdGhlIGZpbGUgbmFtZSBjb3JyZWN0bHksIHdoaWNoIGlzIGAuYmFzaF9wcm9maWxlYCAod2l0aCBhIOKAnGRvdOKAnSBpbiBmcm9udCkuCgotIE9wZW4gdGhlIC5iYXNoX3Byb2ZpbGUgZmlsZSwgd2hpY2ggaXMgbG9jYXRlZCBhdCB5b3VyIEhPTUUgZGlyZWN0b3J5IChpLmUuLCB+Ly5iYXNoX3Byb2ZpbGUpLCB1c2luZyBhbnkgdGV4dCBlZGl0b3IgKGUuZy4sIFRleHRFZGl0LCBuYW5vLCB2aSwgb3IgZW1hY3MpLiBGb3IgZXhhbXBsZSwgbXkgZmF2b3JpdGUgZWRpdG9yIGlzIGVtYWNzLiBTbywgaXQgY291bGQgYmUgZm9sbG93aW5nLiAKCiFbXShwaWNzL3BpYzAyX3Rlcm1pbmFsLnBuZykKCgojIyAzLjIuIEVkaXQgLmJhc2hfcHJvZmlsZSBmaWxlCkNvcHkgdGhlc2UgbGluZXMgdG8gdGhlIGZpbGUuCmBgYApleHBvcnQgSkFWQV9IT01FPS9MaWJyYXJ5L0phdmEvSmF2YVZpcnR1YWxNYWNoaW5lcy9qZGsxLjguMF8xOTEuamRrL0NvbnRlbnRzL0hvbWUvCmV4cG9ydCBTUEFSS19IT01FPS9Vc2Vycy9ldmFuL3NlcnZlci9zcGFyay0yLjQuMC1iaW4taGFkb29wMi43CmV4cG9ydCBTQlRfSE9NRT0vVXNlcnMvZXZhbi9zZXJ2ZXIvc2J0CmV4cG9ydCBTQ0FMQV9IT01FPS9Vc2Vycy9ldmFuL3NlcnZlci9zY2FsYS0yLjEzLjEKZXhwb3J0IFBBVEg9JEpBVkFfSE9NRS9iaW46JFNCVF9IT01FL2JpbjokU0JUX0hPTUUvbGliOiRTQ0FMQV9IT01FL2JpbjokU0NBTEFfSE9NRS9saWI6JFBBVEgKZXhwb3J0IFBBVEg9JEpBVkFfSE9NRS9iaW46JFNQQVJLX0hPTUU6JFNQQVJLX0hPTUUvYmluOiRTUEFSS19IT01FL3NiaW46JFBBVEgKZXhwb3J0IFBZU1BBUktfUFlUSE9OPXB5dGhvbjMKYGBgCgpXaGVuIGNvcHl0aW5nIHRvIGAuYmFzaF9wcm9maWxlYCwgRE8gTk9UIERFTEVURSBPVEhFUiBMSU5FUy4gQWZ0ZXIgY29weWluZyBhbGwgdG8gYC5iYXNoX3Byb2ZpbGVgLCB0aGVuIHNhdmUgYW5kIGNsb3NlIGZpbGUuIAoKIyMgMy4zLiBBcHBseSB1cGRhdGUgLmJhc2hfcHJvZmlsZSBmaWxlClNpbmNlIHRoZSAuYmFzaF9wcm9maWxlIGhhcyBiZWVuIGNoYW5nZWQsIHdlIGhhdmUgdG8gcmVsb2FkIGl0LiBPcHRpb25zIGFyZQoKLSBiYWNrIHRvIHRlcm1pbmFsLCBhbmQgdHlwZSAKYGBgCnNvdXJjZSB+Ly5iYXNoX3Byb2ZpbGUKYGBgCgpPciAKCi0gUXVpdCBhbmQgcmVvcGVuIHRoZSBUZXJtaW5hbCBwcm9ncmFtLiBNYWtlIHN1cmUgeW91IGNvbXBsZXRlbHkgcXVpdCB0aGUgVGVybWluYWwgdXNpbmcgbWVudSDihpIgUXVpdCBUZXJtaW5hbCAo4oyYUSksIG90aGVyd2lzZSB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGRlY2xhcmVkIGFib3ZlIHdpbGwgbm90IGJlIGxvYWRlZC4gCgoKIyA0LiBDb25uZWN0aW5nIHRvIFNwYXJrCllvdSBjYW4gY29ubmVjdCB0byBib3RoIGxvY2FsIGluc3RhbmNlcyBvZiBTcGFyayBhcyB3ZWxsIGFzIHJlbW90ZSBTcGFyayBjbHVzdGVycy4gSGVyZSB3ZeKAmWxsIGNvbm5lY3QgdG8gYSBsb2NhbCBpbnN0YW5jZSBvZiBTcGFyayB2aWEgdGhlIHNwYXJrX2Nvbm5lY3QgZnVuY3Rpb246CgpgYGAKPiBsaWJyYXJ5KHNwYXJrbHlyKQo+IHNjIDwtIHNwYXJrX2Nvbm5lY3QobWFzdGVyID0gImxvY2FsIiwgc3BhcmtfaG9tZSA9ICIuLi8uLi9zZXJ2ZXIvc3BhcmstMi40LjAtYmluLWhhZG9vcDIuNy8iKQoqIFVzaW5nIFNwYXJrOiAyLjQuMApgYGAKCldlbGNvbWUgdG8gU3BhcmsuIFRoZSBkZXRhaWxzIGFyZSBbc3BhcmtseXI6IFIgaW50ZXJmYWNlIGZvciBBcGFjaGUgU3BhcmtdKGh0dHBzOi8vc3BhcmsucnN0dWRpby5jb20vKQoKIyA1LiBFeGFtcGxlcwpBbGwgc2FtcGxlIGNvZGVzIGFyZSB3cml0dGVuIGF0IHRoZSBbb2ZmaWNhbCBkb2N1bWVudGF0aW9uc10oaHR0cHM6Ly9zcGFyay5yc3R1ZGlvLmNvbS8pLiAKCiMjIDUuMS4gVXNpbmcgZHBseXIKV2UgYXJlIGFibGUgdG8gdXNlIGFsbCBvZiB0aGUgYXZhaWxhYmxlIGRwbHlyIGZ1bmN0aW9ucyB3aXRoaW4gdGhlIHNwYXJrIGNsdXN0ZXIuIAoKV2XigJlsbCBzdGFydCBieSBjb3B5aW5nIHNvbWUgZGF0YXNldHMgZnJvbSBSIGludG8gdGhlIFNwYXJrIGNsdXN0ZXIgKG5vdGUgdGhhdCB5b3UgbWF5IG5lZWQgdG8gaW5zdGFsbCB0aGUgbnljZmxpZ2h0czEzIGFuZCBMYWhtYW4gcGFja2FnZXMgaW4gb3JkZXIgdG8gZXhlY3V0ZSB0aGlzIGNvZGUpOgoKYGBgCj4gbGlicmFyeShkcGx5cikKPiBsaWJyYXJ5KG55Y2ZsaWdodHMxMykKPiBsaWJyYXJ5KExhaG1hbikKCj4gaXJpc190YmwgPC0gY29weV90byhzYywgaXJpcykKPiBmbGlnaHRzX3RibCA8LSBjb3B5X3RvKHNjLCBueWNmbGlnaHRzMTM6OmZsaWdodHMsICJmbGlnaHRzIikKPiBiYXR0aW5nX3RibCA8LSBjb3B5X3RvKHNjLCBMYWhtYW46OkJhdHRpbmcsICJiYXR0aW5nIikKPiBzcmNfdGJscyhzYykKWzFdICJiYXR0aW5nIiAiZmxpZ2h0cyIgImlyaXMiCmBgYAoKV2hlbiBjb3B5dGluZyB0byBzcGFyaywgeW91IG1heSBzZWUgdGhlc2UgZGF0YXNldCBhdCBzcGFyayBVSS4gT3BlbiB3ZWIgYnJvd3NlciwgdHlwZSBgaHR0cDovL2xvY2FsaG9zdDo0MDQwL3N0b3JhZ2UvYC4gVGhlbiB5b3UgbWF5IHNlZSBwaWMgYmVsb3cuIAoKIVtdKHBpY3MvcGljMDNfc3BhcmtVSS5wbmcpCgpMZXQncyB1c2UgZmlsdGVyKCkgaW4gZHBseXIgcGFja2FnZQpgYGAKIyBmaWx0ZXIgYnkgZGVwYXJ0dXJlIGRlbGF5IGFuZCBwcmludCB0aGUgZmlyc3QgZmV3IHJlY29yZHMKPiBmbGlnaHRzX3RibCAlPiUgZmlsdGVyKGRlcF9kZWxheSA9PSAyKQojIFNvdXJjZTogc3Bhcms8Pz4gWz8/IHggMTldCiAgICB5ZWFyIG1vbnRoICAgZGF5IGRlcF90aW1lIHNjaGVkX2RlcF90aW1lIGRlcF9kZWxheSBhcnJfdGltZSBzY2hlZF9hcnJfdGltZQogICA8aW50PiA8aW50PiA8aW50PiAgICA8aW50PiAgICAgICAgICA8aW50PiAgICAgPGRibD4gICAgPGludD4gICAgICAgICAgPGludD4KIDEgIDIwMTMgICAgIDEgICAgIDEgICAgICA1MTcgICAgICAgICAgICA1MTUgICAgICAgICAyICAgICAgODMwICAgICAgICAgICAgODE5CiAyICAyMDEzICAgICAxICAgICAxICAgICAgNTQyICAgICAgICAgICAgNTQwICAgICAgICAgMiAgICAgIDkyMyAgICAgICAgICAgIDg1MAogMyAgMjAxMyAgICAgMSAgICAgMSAgICAgIDcwMiAgICAgICAgICAgIDcwMCAgICAgICAgIDIgICAgIDEwNTggICAgICAgICAgIDEwMTQKIDQgIDIwMTMgICAgIDEgICAgIDEgICAgICA3MTUgICAgICAgICAgICA3MTMgICAgICAgICAyICAgICAgOTExICAgICAgICAgICAgODUwCiA1ICAyMDEzICAgICAxICAgICAxICAgICAgNzUyICAgICAgICAgICAgNzUwICAgICAgICAgMiAgICAgMTAyNSAgICAgICAgICAgMTAyOQogNiAgMjAxMyAgICAgMSAgICAgMSAgICAgIDkxNyAgICAgICAgICAgIDkxNSAgICAgICAgIDIgICAgIDEyMDYgICAgICAgICAgIDEyMTEKIDcgIDIwMTMgICAgIDEgICAgIDEgICAgICA5MzIgICAgICAgICAgICA5MzAgICAgICAgICAyICAgICAxMjE5ICAgICAgICAgICAxMjI1CiA4ICAyMDEzICAgICAxICAgICAxICAgICAxMDI4ICAgICAgICAgICAxMDI2ICAgICAgICAgMiAgICAgMTM1MCAgICAgICAgICAgMTMzOQogOSAgMjAxMyAgICAgMSAgICAgMSAgICAgMTA0MiAgICAgICAgICAgMTA0MCAgICAgICAgIDIgICAgIDEzMjUgICAgICAgICAgIDEzMjYKMTAgIDIwMTMgICAgIDEgICAgIDEgICAgIDEyMzEgICAgICAgICAgIDEyMjkgICAgICAgICAyICAgICAxNTIzICAgICAgICAgICAxNTI5CiMg4oCmIHdpdGggbW9yZSByb3dzLCBhbmQgMTEgbW9yZSB2YXJpYWJsZXM6IGFycl9kZWxheSA8ZGJsPiwgY2FycmllciA8Y2hyPiwKIyAgIGZsaWdodCA8aW50PiwgdGFpbG51bSA8Y2hyPiwgb3JpZ2luIDxjaHI+LCBkZXN0IDxjaHI+LCBhaXJfdGltZSA8ZGJsPiwKIyAgIGRpc3RhbmNlIDxkYmw+LCBob3VyIDxkYmw+LCBtaW51dGUgPGRibD4sIHRpbWVfaG91ciA8ZHR0bT4KYGBgCgpIZXJlLCB5b3UgbWF5IHJlYWQgYCMgU291cmNlOiBzcGFyazw/PiBbPz8geCAxOV1gLiBUaGlzIHBvaW50cyB0aGF0IGRwbHlyIGZ1bmN0aW9uIHVzZXMgZGF0YXNldHMgZnJvbSBzcGFyay1jbHVzdGVyIHN0b3JlZC4gCgojIyBVc2luZyBTUUwKSXQncyBwb3NzaWJsZSB0byBleGVjdXRlIFNRTCBxdWVyaWVzIGRpcmVjdGx5IGFnYWluc3QgdGFibGVzIGlmIHlvdSBhcmUgbW9yZSBmYW1pbGFyIHdpdGggU1FMLiBUaGUgc3BhcmtfY29ubmVjdGlvbiBvYmplY3QgaW1wbGVtZW50cyBhIERCSSBpbnRlcmZhY2UgZm9yIFNwYXJrLCBzbyB5b3UgY2FuIHVzZSBgZGJHZXRRdWVyeWAgdG8gZXhlY3V0ZSBTUUwgYW5kIHJldHVybiB0aGUgcmVzdWx0IGFzIGFuIFIgZGF0YSBmcmFtZToKCmBgYAo+IGxpYnJhcnkoREJJKQo+IGlyaXNfcHJldmlldyA8LSBkYkdldFF1ZXJ5KHNjLCAiU0VMRUNUICogRlJPTSBpcmlzIExJTUlUIDEwIikKPiBpcmlzX3ByZXZpZXcKICAgU2VwYWxfTGVuZ3RoIFNlcGFsX1dpZHRoIFBldGFsX0xlbmd0aCBQZXRhbF9XaWR0aCBTcGVjaWVzCjEgICAgICAgICAgIDUuMSAgICAgICAgIDMuNSAgICAgICAgICAxLjQgICAgICAgICAwLjIgIHNldG9zYQoyICAgICAgICAgICA0LjkgICAgICAgICAzLjAgICAgICAgICAgMS40ICAgICAgICAgMC4yICBzZXRvc2EKMyAgICAgICAgICAgNC43ICAgICAgICAgMy4yICAgICAgICAgIDEuMyAgICAgICAgIDAuMiAgc2V0b3NhCjQgICAgICAgICAgIDQuNiAgICAgICAgIDMuMSAgICAgICAgICAxLjUgICAgICAgICAwLjIgIHNldG9zYQo1ICAgICAgICAgICA1LjAgICAgICAgICAzLjYgICAgICAgICAgMS40ICAgICAgICAgMC4yICBzZXRvc2EKNiAgICAgICAgICAgNS40ICAgICAgICAgMy45ICAgICAgICAgIDEuNyAgICAgICAgIDAuNCAgc2V0b3NhCjcgICAgICAgICAgIDQuNiAgICAgICAgIDMuNCAgICAgICAgICAxLjQgICAgICAgICAwLjMgIHNldG9zYQo4ICAgICAgICAgICA1LjAgICAgICAgICAzLjQgICAgICAgICAgMS41ICAgICAgICAgMC4yICBzZXRvc2EKOSAgICAgICAgICAgNC40ICAgICAgICAgMi45ICAgICAgICAgIDEuNCAgICAgICAgIDAuMiAgc2V0b3NhCjEwICAgICAgICAgIDQuOSAgICAgICAgIDMuMSAgICAgICAgICAxLjUgICAgICAgICAwLjEgIHNldG9zYQpgYGAKCiMjIE1hY2hpbmUgTGVhcm5pbmcgCllvdSBjYW4gb3JjaGVzdHJhdGUgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIGluIGEgU3BhcmsgY2x1c3RlciB2aWEgdGhlIG1hY2hpbmUgbGVhcm5pbmcgZnVuY3Rpb25zIHdpdGhpbiBzcGFya2x5ci4gVGhlc2UgZnVuY3Rpb25zIGNvbm5lY3QgdG8gYSBzZXQgb2YgaGlnaC1sZXZlbCBBUElzIGJ1aWx0IG9uIHRvcCBvZiBEYXRhRnJhbWVzIHRoYXQgaGVscCB5b3UgY3JlYXRlIGFuZCB0dW5lIG1hY2hpbmUgbGVhcm5pbmcgd29ya2Zsb3dzLgoKSGVyZeKAmXMgYW4gZXhhbXBsZSB3aGVyZSB3ZSB1c2UgbWxfbGluZWFyX3JlZ3Jlc3Npb24gdG8gZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuIFdl4oCZbGwgdXNlIHRoZSBidWlsdC1pbiBtdGNhcnMgZGF0YXNldCwgYW5kIHNlZSBpZiB3ZSBjYW4gcHJlZGljdCBhIGNhcuKAmXMgZnVlbCBjb25zdW1wdGlvbiAobXBnKSBiYXNlZCBvbiBpdHMgd2VpZ2h0ICh3dCksIGFuZCB0aGUgbnVtYmVyIG9mIGN5bGluZGVycyB0aGUgZW5naW5lIGNvbnRhaW5zIChjeWwpLiBXZeKAmWxsIGFzc3VtZSBpbiBlYWNoIGNhc2UgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbXBnIGFuZCBlYWNoIG9mIG91ciBmZWF0dXJlcyBpcyBsaW5lYXIuCgpgYGAKIyBjb3B5IG10Y2FycyBpbnRvIHNwYXJrCj4gbXRjYXJzX3RibCA8LSBjb3B5X3RvKHNjLCBtdGNhcnMpCgojIHRyYW5zZm9ybSBvdXIgZGF0YSBzZXQsIGFuZCB0aGVuIHBhcnRpdGlvbiBpbnRvICd0cmFpbmluZycsICd0ZXN0Jwo+IHBhcnRpdGlvbnMgPC0gbXRjYXJzX3RibCAlPiUKKyAgZmlsdGVyKGhwID49IDEwMCkgJT4lCisgIG11dGF0ZShjeWw4ID0gY3lsID09IDgpICU+JQorICBzZGZfcmFuZG9tX3NwbGl0KHRyYWluaW5nID0gMC41LCB0ZXN0ID0gMC41LCBzZWVkID0gMTA5OSkKCiMgZml0IGEgbGluZWFyIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBkYXRhc2V0Cj4gZml0IDwtIHBhcnRpdGlvbnMkdHJhaW5pbmcgJT4lCisgIG1sX2xpbmVhcl9yZWdyZXNzaW9uKHJlc3BvbnNlID0gIm1wZyIsIGZlYXR1cmVzID0gYygid3QiLCAiY3lsIikpCj4gZml0CkZvcm11bGE6IG1wZyB+IHd0ICsgY3lsCgpDb2VmZmljaWVudHM6CihJbnRlcmNlcHQpICAgICAgICAgIHd0ICAgICAgICAgY3lsIAogIDMzLjQ5OTQ1MiAgIC0yLjgxODQ2MyAgIC0wLjkyMzE4NyAKPiBzdW1tYXJ5KGZpdCkKRGV2aWFuY2UgUmVzaWR1YWxzOgogICBNaW4gICAgIDFRIE1lZGlhbiAgICAgM1EgICAgTWF4IAotMS43NTIgLTEuMTM0IC0wLjQ5OSAgMS4yOTYgIDIuMjgyIAoKQ29lZmZpY2llbnRzOgooSW50ZXJjZXB0KSAgICAgICAgICB3dCAgICAgICAgIGN5bCAKICAzMy40OTk0NTIgICAtMi44MTg0NjMgICAtMC45MjMxODcgCgpSLVNxdWFyZWQ6IDAuODI3NApSb290IE1lYW4gU3F1YXJlZCBFcnJvcjogMS40MjIKYGBgCgpTcGFyayBtYWNoaW5lIGxlYXJuaW5nIHN1cHBvcnRzIGEgd2lkZSBhcnJheSBvZiBhbGdvcml0aG1zIGFuZCBmZWF0dXJlIHRyYW5zZm9ybWF0aW9ucyBhbmQgYXMgaWxsdXN0cmF0ZWQgYWJvdmUgaXTigJlzIGVhc3kgdG8gY2hhaW4gdGhlc2UgZnVuY3Rpb25zIHRvZ2V0aGVyIHdpdGggZHBseXIgcGlwZWxpbmVzLgoKIyA2LiBDb25jbHVzaW9uCkRlYWxpbmcgd2l0aCB0aGlzIHR1dG9yaWFsIGZvciBhIGNvdXBsZSBvZiBkYXlzLiBUaGUgaGFyZCB0aGluZyB0byBtZSB3YXMgdG8gc2F0aXNmeSBzeXN0ZW0gcmVxdWlyZW1lbnRzLiBFbnZpcm9ubWVudCBzZXR0aW5ncyBhcmUgbm90IGFsd2F5cyBjb21mb3J0YWJsZSB3aXRoIG1lIHdobyBoYXMgc3R1ZGllZCBMaWJlcmFsIEFydHMgLSBQaGlsaW9zb3BoeSwgUmVsaWdpb3VzIFN0dWRpZXMsIERldmVsb3BtZW50IFN0dWRpZXMuIEFsbCBhcmVhIGlzIHRyeWluZyB0byBjb2xsZWN0IGRhdGEgYW5kIHN0b3JlIHNvbWV3aGVyZSBlbHNlLCBhbmQgZ2V0IGRhdGEgZnJvbSBEYXRhYmFzZSBvciBDbHVzdGVycywgYW5kIGZpbmFsbHkgYW5hbHlzZSB0aGVtLiBBbHRob3VnaCBzZXR0aW5ncyB1cCBkYXRhIHBpcGVsaW5lIGlzIGEgYml0IGZhciBhd2F5IGZyb20gYW5hbHl6aW5nIGRhdGEsIHN0aWxsIGl0J3MgdmFsdWFibGUgZm9yIHRoZW0gdG8gZGVhbCB3aXRoIHBhcnQgb2YgZGF0YSBlbmdpbmVlcmluZyBhcmVhLiAKCkhvcGUgdG8gZW5qb3kgYW5kIGhhcHB5IHRvIGNvZGUuIAoKCgoK