Data Wrangling in R: Get all your data and get it tidy
Dan Isbell
Data Wrangling
Broad term, essentially referring to the process of getting your “raw” data into a format you can work with.Chances are, it might take longer to wrangle your data than it does to actually run your analyses. This motivates us to find ways to wrangle efficiently. We also want to make sure we wrangle our data without errors, as we know for analysis:
GarbageIn -> GarbageOut
We’ll cover a bit of getting all your data together and getting it tidy (Wickham, 2014).
Part I: Get all your data
For Numerous Files, Avoid Copy and Paste
Often, we run into one type of data spread over multiple files. This is common with…- Eye-tracking [1 file per participant]
- SuperLab-based experiments (e.g., self-paced reading, GJTs) [1 file per participant]
- Longitudinal test score data [1 file per test administration]
- Longitudinal survey data [1 file per year/semester/etc.]
If all of your columns in these files are the same, we have a relatively easy, automated solution in R.
Solution, in 3 Steps
From Wickham (2014):- Read in a list of filenames
- Add a column to each file with its name (optional; we’ll skip this today)
- Combine all files in a single dataset
Prep
First, make sure you have the files I sent in a folder on your desktop. In RStudio, click the “Files” tab in the bottom-right window. Click the “…” button and navigate to your desktop folder. Then, click the blue gear icon and select “Set as Working Directory.”Now we’re ready to start. Click the icon of white square with a plus sign to and choose “R Script.”
Step 1
In the script editor (top-left window), enter the following code.#first, we load some libraries that we need to use
library(dplyr)
library(tidyr)
#now let's get to the data
#first, we'll make a list of files in our directory
temp <- list.files(pattern = "*.csv")
print(temp)
[1] "wave_1.csv" "wave_2.csv" "wave_3.csv" "wave_4.csv"
Step 3 (remember, we’re skipping step 2, which was optional)
Now that we have a list of files we want to read in, we can start bringing in the data.#we'll use the lapply (list-apply) function to read in everything on our list
myfiles <- lapply(temp, read.csv, stringsAsFactors = F)
#now we have a list that holds 4 dataframes. We'll put this all into one table.
all <- bind_rows(myfiles)
#and now we have a single table with 4364 observations from 4 different files
head(all)
There are several other ways of doing this (e.g., using a for-loop), but this one is fairly simple. The really nice part of this is that it scales beautifully- whether you have 4 or 400 files, you can get them all in the same file for your analysis without having to open up Excel and cut-and-paste.
Let’s clean things up
While we’re super happy to have successfully gotten all of this data in, we realize that we don’t actually need all of it. Let’s select only the bits we’re interested in:- Student.Identification
- Wave
- Language
- Speaking.Score
d <- select(all, 1:3, Speaking.Score)
head(d)
In the select() function, the first argument is the data you’re selecting from, and after that you can list which columns you are grabbing. You can refer to column numbers explicitly call column names (make sure you match case!).
As you can see, we now have 4 variables- just the stuff I’m interested in for my ongoing project that’s looking at oral proficiency development. But we need to do a bit more wrangling, because I’m only interested in identifiable people with at least 2 valid test scores.
Clean out invalid data
If we look at the dataset, we see that some rows are missing IDs, and some are missing scores. Some scores say things like “UR” or “NR”, which indicated that the person took the wrong level of the test and a valid score could not be assigned.We’ll clear these observations out with dplyr function filter()
#we're going to make a quick list of our valid ACTFL scores
ACTFL <- c("NL","NM","NH","IL","IM","IH","AL","AM","AH","S")
#now the filtering
d <- filter(d, !is.na(Student.Identification), !is.na(Speaking.Score),
Speaking.Score %in% ACTFL)
tail(d)
You can see here that we’re down to 3804 observations. We did this by telling filter() to only keep rows with Student.Identification and Speaking.Score that were not (!) NA (missing). We also told it to only keep rows with valid ACTFL scores.
What if you have some duplicate rows?
This can happen with database downloads. In my case, students may have been registered for multiple language classes at the same time, so they had multiple entries in our database. R can easily find and delete duplicate rows with the distinct() functiond <- distinct(d)
tail(d)
…and now we’re down to 3519 observations.
Get only individual with repeated measures
First, let’s arrange the data in a sensible way with the arrange() function:d <- arrange(d, language, Student.Identification, Wave)
head(d)
What we just did was basically a sorting action like in Excel. In the arrange() function, first we say which data we’re arranging, then we tell the sorting criteria. By default, it uses ascending order, but you can do something like desc(Wave) if you want.
We see that we do indeed have people with 2 or more scores. Hurray! We’re kicking everyone else out by using the group_by() and filter() functions in dplyr. We’ll also use a special operator (%>%) that makes our code a little more concise:
d.rep <- d %>% group_by(Student.Identification) %>% filter(n() > 1)
tail(d.rep)
And here we’re down to 1982 observations. We can see that there are at least 2 observations for each student. This is the data I wanted for my analysis, and now I have a record of how I pulled it. If I got more data later, I could run the same code again.
Part II: Make it tidy
In this part of the workshop, we’ll focus on having a logical structure for datasets, and look at ways to make that happen in R, primarily using the tidyr package.Datasets are composed of values (or data, sing. datum), and each value is associated with a variable and an observation.
A variable is all the data that measure the same attribute. This could be person IDs (i.e., nominal variables that distinguish one person from another) or things like test scores (e.g., ACTFL OPIc scores that measure speaking ability).
An observation is a collection of all variables belonging to the same object (e.g., person) at the same time/in the same snapshot.
Wickham (2014) argues that data are tidy if:
- Each variable has its own column.
- Each observation has its own row.
Warm-Up: A Simple Dataset Design
Scenario (based on Loewen et al., in prep): We are interested in the statistical literacy of SLA researchers. We had 89 researchers take a test on statistics, and we have a total score for each person. We also collected some background data, including research orientation (7 point scale, 1 = qual, 7 = quant), number of stats courses taken, and years in the field. We are interested in the relationships between statistical literacy and statistical background.How would you design this dataset? In groups of 3-4, sketch out a table with values for 1 subject.
Warm-Up: Solution
ID | Literacy Score | Orientation | StatsCourses | YearsinField |
---|---|---|---|---|
1 | 21 | 6 | 2 | 7 |
Now for a challenge…
Things get a bit more complicated when you are measuring people at different times and with multiple measures of the same attribute- but this is very common in our field. Let’s think about the scenario below, and how we could design a tidy dataset.Scenario (based on Isbell, Park, & Lee, under review): We were interested in the impact of pronunciation instruction on the comprehensibility of L2 Korean learners. We had 36 students divided into 2 groups: treatment and control. Students differed in their L1 (English, Chinese) and Proficiency (beginner, intermediate). We had them complete 2 tasks (picture description, read-aloud) at 2 times (pretest, posttest). Each task was scored for comprehensibility by several raters, we’ll look at the average score.
How would you design this dataset? In groups of 3-4, sketch out a table with values for 1 subject.
Solution
Student | L1 | Prof | Group | Time | Task | Score |
---|---|---|---|---|---|---|
01 | E | Int | T | 1 | Pic | 4.2 |
01 | E | Int | T | 1 | Read | 5.1 |
01 | E | Int | T | 2 | Pic | 4.7 |
01 | E | Int | T | 2 | Read | 6.3 |
.. | . | … | . | . | …. | . |
Going from wide to long
In R, we can use the gather() function to go from conventional wide datasets to long, tidy datasets.First, let’s read in our data. You may want to start a new script, but you don’t have to.
library(tidyr)
library(dplyr)
#read in data
df <- read.delim("comprehensibility.txt")
head(df)
So we see here that the comprehensibility scores, which all measure the same attribute, are in 4 different columns. At the same time, we have 4 different measurement instances on the same row; each comprehensibility score is associated with a unique speech sample (task) at a unique time (pre, post).
gather() our comprehensibilty in a single variable column
We need to get all the comprehensibility scores in the same column:df.long <- gather(df, "Task_Time", "Score", 5:8)
#just to see things easier...
df.long <- arrange(df.long, Subj_ID)
head(df.long)
I decided to make a new dataframe (df.long) so that I could go back to my original wide dataset if needed. In the gather() function, I told it to take the data from “df” (our original dataframe), what the “key” column should be called, what the “value” column should be called, and which columns to pull together (in this case columns 5 to 8).
The pre-gather column names are what goes in the “key” column. Since my column names had information on Task and Time, I called the new key “Task_Time.” The values these are connected to are comprehensibilty “Scores.”
As you can see, we’re not quite done yet- I need to split up that Task_Time column so I can run my analyses.
There’s a function for that, too- separate()
Luckily, tidyr has a function for splitting up confounded columns- separate(). Let’s try it out.df.long <- separate(df.long, Task_Time, c("Task", "Time"), sep = "_")
#and a sensible ordering...
df.long <- arrange(df.long, Subj_ID, Time, Task)
head(df.long)
And there we have it! From wide to long, ready for all kinds of snazzy graphics and sophisticated repeated-measures analyses.
There’s more that could/should be done with the dataset, like converting variables to factors, creating some recoded variables (from string to number codes), but at the very least our data is in the right shape.
Q&A
Any questions about the functions and examples introduced today?Any current data problems you’d like to share/brainstorm with the group?
References and Resources
- Cheatsheets: In RStudio, click the Help drop-down menu at the top of the window. Go to Cheatsheets > Data manipulation with dplyr, tidyr
- Help Files: If you need some additional reference material for a particular function, type ?
in the Console (bottom-left panel). For example, typing ?separate will bring up a help file in the bottom-right panel with some example code. - Wickham, H. (2014). Tidy data. Journal of Statistical Software, 59,(10), 1-23. https://www.jstatsoft.org/article/view/v059i10
- Google. You can use normal question syntax (e.g. “How do you…”) and then add a phrase like “in R” or “with dplyr r”
LS0tDQp0aXRsZTogJ0RhdGEgV3JhbmdsaW5nIGluIFI6IEdldCBhbGwgeW91ciBkYXRhIGFuZCBnZXQgaXQgdGlkeScNCmF1dGhvcjogIkRhbiBJc2JlbGwiDQpvdXRwdXQ6DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojRGF0YSBXcmFuZ2xpbmcNCkJyb2FkIHRlcm0sIGVzc2VudGlhbGx5IHJlZmVycmluZyB0byB0aGUgcHJvY2VzcyBvZiBnZXR0aW5nIHlvdXIgInJhdyIgZGF0YSBpbnRvIGEgZm9ybWF0IHlvdSBjYW4gd29yayB3aXRoLg0KDQpDaGFuY2VzIGFyZSwgaXQgbWlnaHQgdGFrZSBsb25nZXIgdG8gd3JhbmdsZSB5b3VyIGRhdGEgdGhhbiBpdCBkb2VzIHRvIGFjdHVhbGx5IHJ1biB5b3VyIGFuYWx5c2VzLiBUaGlzIG1vdGl2YXRlcw0KdXMgdG8gZmluZCB3YXlzIHRvIHdyYW5nbGUgZWZmaWNpZW50bHkuIFdlIGFsc28gd2FudCB0byBtYWtlIHN1cmUgd2Ugd3JhbmdsZSBvdXIgZGF0YSB3aXRob3V0IGVycm9ycywgYXMgd2Uga25vdw0KZm9yIGFuYWx5c2lzOg0KDQoqR2FyYmFnZUluKiAtPiAqR2FyYmFnZU91dCoNCg0KV2UnbGwgY292ZXIgYSBiaXQgb2YgZ2V0dGluZyBhbGwgeW91ciBkYXRhIHRvZ2V0aGVyIGFuZCBnZXR0aW5nIGl0ICp0aWR5KiAoV2lja2hhbSwgMjAxNCkuDQoNCiNQYXJ0IEk6IEdldCBhbGwgeW91ciBkYXRhDQoNCiNGb3IgTnVtZXJvdXMgRmlsZXMsIEF2b2lkIENvcHkgYW5kIFBhc3RlDQoNCk9mdGVuLCB3ZSBydW4gaW50byBvbmUgdHlwZSBvZiBkYXRhIHNwcmVhZCBvdmVyIG11bHRpcGxlIGZpbGVzLiBUaGlzIGlzIGNvbW1vbiB3aXRoLi4uDQoNCi0gRXllLXRyYWNraW5nIFsxIGZpbGUgcGVyIHBhcnRpY2lwYW50XQ0KDQotIFN1cGVyTGFiLWJhc2VkIGV4cGVyaW1lbnRzIChlLmcuLCBzZWxmLXBhY2VkIHJlYWRpbmcsIEdKVHMpIFsxIGZpbGUgcGVyIHBhcnRpY2lwYW50XQ0KDQotIExvbmdpdHVkaW5hbCB0ZXN0IHNjb3JlIGRhdGEgWzEgZmlsZSBwZXIgdGVzdCBhZG1pbmlzdHJhdGlvbl0NCg0KLSBMb25naXR1ZGluYWwgc3VydmV5IGRhdGEgWzEgZmlsZSBwZXIgeWVhci9zZW1lc3Rlci9ldGMuXQ0KDQpIYW5kbGluZyB0aGlzIG1hbnVhbGx5IGlzIG5vdCBkaWZmaWN1bHQsIGJ1dCBpdCBpcyB0ZWRpb3VzLSBpbWFnaW5lIGNvcHlpbmcgYW5kIHBhc3RpbmcgNjAgc3ByZWFkc2hlZXRzIGludG8gb25lIHNwcmVhZHNoZWV0ISBUaGUgcmVhbCBkYW5nZXIsIGhvd2V2ZXIsIGlzIHNlbGVjdGl2ZWx5IGNvcHlpbmcgYml0cyBmcm9tIDYwIHNwcmVhZHNoZWV0cy4gRm9yIGV4YW1wbGUsIHlvdSBtaWdodCBvbmx5IG5lZWQgYSBmZXcgY29sdW1ucyBmcm9tIGVhY2ggU3VwZXJMYWIgb3V0cHV0IGZpbGUuIEJ1dCB0aGlzIGluY3JlYXNlcyB0aGUgbnVtYmVyIG9mIG1hbnVhbCBhY3Rpb25zIGdyZWF0bHksIGFuZCB0aHVzIGluY3JlYXNlcyBjaGFuY2Ugb2YgZXJyb3IgKHN1Y2ggYXMgaGl0dGluZyBjdHJsLXYgd2hlbiB5b3UgbWVhbnQgdG8gY3RybC1jLi4uKS4NCg0KSWYgYWxsIG9mIHlvdXIgY29sdW1ucyBpbiB0aGVzZSBmaWxlcyBhcmUgdGhlIHNhbWUsIHdlIGhhdmUgYSByZWxhdGl2ZWx5IGVhc3ksIGF1dG9tYXRlZCBzb2x1dGlvbiBpbiBSLg0KDQojU29sdXRpb24sIGluIDMgU3RlcHMNCkZyb20gV2lja2hhbSAoMjAxNCk6DQoNCiAgMS4gUmVhZCBpbiBhIGxpc3Qgb2YgZmlsZW5hbWVzDQogIDIuIEFkZCBhIGNvbHVtbiB0byBlYWNoIGZpbGUgd2l0aCBpdHMgbmFtZSAob3B0aW9uYWw7IHdlJ2xsIHNraXAgdGhpcyB0b2RheSkNCiAgMy4gQ29tYmluZSBhbGwgZmlsZXMgaW4gYSBzaW5nbGUgZGF0YXNldA0KDQogIExldCdzIHRyeSEgV2UnbGwgd2FsayB0aHJvdWdoIHNvbWUgd3JhbmdsaW5nIGZyb20gbXkgY3VycmVudCBwcm9qZWN0IGxvb2tpbmcgYXQgbG9uZ2l0dWRpbmFsIG9yYWwgcHJvZmljaWVuY3kgZGV2ZWxvcG1lbnQgbWVhc3VyZWQgYnkgdGhlIEFDVEZMIE9QSWMuDQoNCiNQcmVwDQogIEZpcnN0LCBtYWtlIHN1cmUgeW91IGhhdmUgdGhlIGZpbGVzIEkgc2VudCBpbiBhIGZvbGRlciBvbiB5b3VyIGRlc2t0b3AuDQogIEluIFJTdHVkaW8sIGNsaWNrIHRoZSAiRmlsZXMiIHRhYiBpbiB0aGUgYm90dG9tLXJpZ2h0IHdpbmRvdy4gQ2xpY2sgdGhlICIuLi4iIGJ1dHRvbiBhbmQgbmF2aWdhdGUgdG8geW91cg0KICBkZXNrdG9wIGZvbGRlci4gVGhlbiwgY2xpY2sgdGhlIGJsdWUgZ2VhciBpY29uIGFuZCBzZWxlY3QgIlNldCBhcyBXb3JraW5nIERpcmVjdG9yeS4iDQogIA0KICBOb3cgd2UncmUgcmVhZHkgdG8gc3RhcnQuIENsaWNrIHRoZSBpY29uIG9mIHdoaXRlIHNxdWFyZSB3aXRoIGEgcGx1cyBzaWduIHRvIGFuZCBjaG9vc2UgIlIgU2NyaXB0LiINCiAgDQojU3RlcCAxDQogIA0KICBJbiB0aGUgc2NyaXB0IGVkaXRvciAodG9wLWxlZnQgd2luZG93KSwgZW50ZXIgdGhlIGZvbGxvd2luZyBjb2RlLg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KI2ZpcnN0LCB3ZSBsb2FkIHNvbWUgbGlicmFyaWVzIHRoYXQgd2UgbmVlZCB0byB1c2UNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KYGBgDQpgYGB7cn0NCiNub3cgbGV0J3MgZ2V0IHRvIHRoZSBkYXRhDQojZmlyc3QsIHdlJ2xsIG1ha2UgYSBsaXN0IG9mIGZpbGVzIGluIG91ciBkaXJlY3RvcnkNCnRlbXAgPC0gbGlzdC5maWxlcyhwYXR0ZXJuID0gIiouY3N2IikNCg0KYGBgDQpgYGB7cn0NCnByaW50KHRlbXApDQpgYGANCg0KDQojU3RlcCAzIChyZW1lbWJlciwgd2UncmUgc2tpcHBpbmcgc3RlcCAyLCB3aGljaCB3YXMgb3B0aW9uYWwpDQpOb3cgdGhhdCB3ZSBoYXZlIGEgbGlzdCBvZiBmaWxlcyB3ZSB3YW50IHRvIHJlYWQgaW4sIHdlIGNhbiBzdGFydCBicmluZ2luZyBpbiB0aGUgZGF0YS4NCg0KYGBge3J9DQoNCiN3ZSdsbCB1c2UgdGhlIGxhcHBseSAobGlzdC1hcHBseSkgZnVuY3Rpb24gdG8gcmVhZCBpbiBldmVyeXRoaW5nIG9uIG91ciBsaXN0DQpteWZpbGVzIDwtIGxhcHBseSh0ZW1wLCByZWFkLmNzdiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQoNCiNub3cgd2UgaGF2ZSBhIGxpc3QgdGhhdCBob2xkcyA0IGRhdGFmcmFtZXMuIFdlJ2xsIHB1dCB0aGlzIGFsbCBpbnRvIG9uZSB0YWJsZS4NCmFsbCA8LSBiaW5kX3Jvd3MobXlmaWxlcykNCg0KYGBgDQoNCmBgYHtyfQ0KI2FuZCBub3cgd2UgaGF2ZSBhIHNpbmdsZSB0YWJsZSB3aXRoIDQzNjQgb2JzZXJ2YXRpb25zIGZyb20gNCBkaWZmZXJlbnQgZmlsZXMNCmhlYWQoYWxsKQ0KYGBgDQoNCg0KVGhlcmUgYXJlIHNldmVyYWwgb3RoZXIgd2F5cyBvZiBkb2luZyB0aGlzIChlLmcuLCB1c2luZyBhIGZvci1sb29wKSwgYnV0IHRoaXMgb25lIGlzIGZhaXJseSBzaW1wbGUuIFRoZSByZWFsbHkgbmljZQ0KcGFydCBvZiB0aGlzIGlzIHRoYXQgaXQgc2NhbGVzIGJlYXV0aWZ1bGx5LSB3aGV0aGVyIHlvdSBoYXZlIDQgb3IgNDAwIGZpbGVzLCB5b3UgY2FuIGdldCB0aGVtIGFsbCBpbiB0aGUgc2FtZSBmaWxlDQpmb3IgeW91ciBhbmFseXNpcyB3aXRob3V0IGhhdmluZyB0byBvcGVuIHVwIEV4Y2VsIGFuZCBjdXQtYW5kLXBhc3RlLg0KDQojTGV0J3MgY2xlYW4gdGhpbmdzIHVwDQpXaGlsZSB3ZSdyZSBzdXBlciBoYXBweSB0byBoYXZlIHN1Y2Nlc3NmdWxseSBnb3R0ZW4gYWxsIG9mIHRoaXMgZGF0YSBpbiwgd2UgcmVhbGl6ZSB0aGF0IHdlIGRvbid0IGFjdHVhbGx5IG5lZWQgDQphbGwgb2YgaXQuIExldCdzIHNlbGVjdCBvbmx5IHRoZSBiaXRzIHdlJ3JlIGludGVyZXN0ZWQgaW46DQoNCi0gU3R1ZGVudC5JZGVudGlmaWNhdGlvbg0KLSBXYXZlDQotIExhbmd1YWdlDQotIFNwZWFraW5nLlNjb3JlDQoNCldlJ2xsIHVzZSB0aGUgc2VsZWN0KCkgZnVuY3Rpb24gZnJvbSB0aGUgZHBseXIgcGFja2FnZSB0byBncmFiIHRoZXNlIGNvbHVtbnMuDQoNCmBgYHtyfQ0KDQpkIDwtIHNlbGVjdChhbGwsIDE6MywgU3BlYWtpbmcuU2NvcmUpDQpgYGANCg0KDQpgYGB7cn0NCg0KaGVhZChkKQ0KDQpgYGANCkluIHRoZSBzZWxlY3QoKSBmdW5jdGlvbiwgdGhlIGZpcnN0IGFyZ3VtZW50IGlzIHRoZSBkYXRhIHlvdSdyZSBzZWxlY3RpbmcgZnJvbSwgYW5kIGFmdGVyIHRoYXQgeW91IGNhbiBsaXN0IHdoaWNoIGNvbHVtbnMNCnlvdSBhcmUgZ3JhYmJpbmcuIFlvdSBjYW4gcmVmZXIgdG8gY29sdW1uIG51bWJlcnMgZXhwbGljaXRseSBjYWxsIGNvbHVtbiBuYW1lcyAobWFrZSBzdXJlIHlvdSBtYXRjaCBjYXNlISkuDQoNCkFzIHlvdSBjYW4gc2VlLCB3ZSBub3cgaGF2ZSA0IHZhcmlhYmxlcy0ganVzdCB0aGUgc3R1ZmYgSSdtIGludGVyZXN0ZWQgaW4gZm9yIG15IG9uZ29pbmcgcHJvamVjdCB0aGF0J3MgbG9va2luZyBhdCBvcmFsIA0KcHJvZmljaWVuY3kgZGV2ZWxvcG1lbnQuIEJ1dCB3ZSBuZWVkIHRvIGRvIGEgYml0IG1vcmUgd3JhbmdsaW5nLCBiZWNhdXNlIEknbSBvbmx5IGludGVyZXN0ZWQgaW4gKippZGVudGlmaWFibGUqKiBwZW9wbGUgd2l0aCBhdCBsZWFzdCAyICoqdmFsaWQqKiB0ZXN0IHNjb3Jlcy4NCg0KI0NsZWFuIG91dCBpbnZhbGlkIGRhdGENCklmIHdlIGxvb2sgYXQgdGhlIGRhdGFzZXQsIHdlIHNlZSB0aGF0IHNvbWUgcm93cyBhcmUgbWlzc2luZyBJRHMsIGFuZCBzb21lIGFyZSBtaXNzaW5nIHNjb3Jlcy4gU29tZSBzY29yZXMgc2F5IHRoaW5ncyBsaWtlICJVUiIgb3IgIk5SIiwgd2hpY2ggaW5kaWNhdGVkIHRoYXQgdGhlIHBlcnNvbiB0b29rIHRoZSB3cm9uZyBsZXZlbCBvZiB0aGUgdGVzdCBhbmQgYSB2YWxpZCBzY29yZSBjb3VsZCBub3QgYmUgYXNzaWduZWQuDQoNCldlJ2xsIGNsZWFyIHRoZXNlIG9ic2VydmF0aW9ucyBvdXQgd2l0aCBkcGx5ciBmdW5jdGlvbiBmaWx0ZXIoKQ0KDQpgYGB7cn0NCg0KI3dlJ3JlIGdvaW5nIHRvIG1ha2UgYSBxdWljayBsaXN0IG9mIG91ciB2YWxpZCBBQ1RGTCBzY29yZXMNCkFDVEZMIDwtIGMoIk5MIiwiTk0iLCJOSCIsIklMIiwiSU0iLCJJSCIsIkFMIiwiQU0iLCJBSCIsIlMiKQ0KDQojbm93IHRoZSBmaWx0ZXJpbmcNCmQgPC0gZmlsdGVyKGQsICFpcy5uYShTdHVkZW50LklkZW50aWZpY2F0aW9uKSwgIWlzLm5hKFNwZWFraW5nLlNjb3JlKSwgDQogICAgICAgICAgICBTcGVha2luZy5TY29yZSAlaW4lIEFDVEZMKQ0KYGBgDQoNCg0KYGBge3J9DQp0YWlsKGQpDQpgYGANCllvdSBjYW4gc2VlIGhlcmUgdGhhdCB3ZSdyZSBkb3duIHRvIDM4MDQgb2JzZXJ2YXRpb25zLiBXZSBkaWQgdGhpcyBieSB0ZWxsaW5nIGZpbHRlcigpIHRvIG9ubHkga2VlcCByb3dzIHdpdGggU3R1ZGVudC5JZGVudGlmaWNhdGlvbiBhbmQgU3BlYWtpbmcuU2NvcmUgdGhhdCB3ZXJlICoqbm90KiogKCEpIE5BIChtaXNzaW5nKS4gV2UgYWxzbyB0b2xkIGl0IHRvIG9ubHkga2VlcCByb3dzIHdpdGggdmFsaWQgQUNURkwgc2NvcmVzLg0KDQojV2hhdCBpZiB5b3UgaGF2ZSBzb21lIGR1cGxpY2F0ZSByb3dzPw0KVGhpcyBjYW4gaGFwcGVuIHdpdGggZGF0YWJhc2UgZG93bmxvYWRzLiBJbiBteSBjYXNlLCBzdHVkZW50cyBtYXkgaGF2ZSBiZWVuIHJlZ2lzdGVyZWQgZm9yIG11bHRpcGxlIGxhbmd1YWdlIGNsYXNzZXMgYXQgdGhlIHNhbWUgdGltZSwgc28gdGhleSBoYWQgbXVsdGlwbGUgZW50cmllcyBpbiBvdXIgZGF0YWJhc2UuIFIgY2FuIGVhc2lseSBmaW5kIGFuZCBkZWxldGUgZHVwbGljYXRlIHJvd3Mgd2l0aCB0aGUgZGlzdGluY3QoKSBmdW5jdGlvbg0KDQpgYGB7cn0NCmQgPC0gZGlzdGluY3QoZCkNCmBgYA0KDQoNCmBgYHtyfQ0KdGFpbChkKQ0KYGBgDQouLi5hbmQgbm93IHdlJ3JlIGRvd24gdG8gMzUxOSBvYnNlcnZhdGlvbnMuDQoNCiNHZXQgb25seSBpbmRpdmlkdWFsIHdpdGggcmVwZWF0ZWQgbWVhc3VyZXMNCkZpcnN0LCBsZXQncyBhcnJhbmdlIHRoZSBkYXRhIGluIGEgc2Vuc2libGUgd2F5IHdpdGggdGhlIGFycmFuZ2UoKSBmdW5jdGlvbjoNCg0KYGBge3J9DQpkIDwtIGFycmFuZ2UoZCwgbGFuZ3VhZ2UsIFN0dWRlbnQuSWRlbnRpZmljYXRpb24sIFdhdmUpDQpgYGANCg0KDQpgYGB7cn0NCmhlYWQoZCkNCmBgYA0KV2hhdCB3ZSBqdXN0IGRpZCB3YXMgYmFzaWNhbGx5IGEgc29ydGluZyBhY3Rpb24gbGlrZSBpbiBFeGNlbC4gSW4gdGhlIGFycmFuZ2UoKSBmdW5jdGlvbiwgZmlyc3Qgd2Ugc2F5IHdoaWNoIGRhdGEgd2UncmUgYXJyYW5naW5nLCB0aGVuIHdlIHRlbGwgdGhlIHNvcnRpbmcgY3JpdGVyaWEuIEJ5IGRlZmF1bHQsIGl0IHVzZXMgYXNjZW5kaW5nIG9yZGVyLCBidXQgeW91IGNhbiBkbyBzb21ldGhpbmcgbGlrZSBkZXNjKFdhdmUpIGlmIHlvdSB3YW50Lg0KDQpXZSBzZWUgdGhhdCB3ZSBkbyBpbmRlZWQgaGF2ZSBwZW9wbGUgd2l0aCAyIG9yIG1vcmUgc2NvcmVzLiBIdXJyYXkhIFdlJ3JlIGtpY2tpbmcgZXZlcnlvbmUgZWxzZSBvdXQgYnkgdXNpbmcgdGhlIGdyb3VwX2J5KCkgYW5kIGZpbHRlcigpIGZ1bmN0aW9ucyBpbiBkcGx5ci4gV2UnbGwgYWxzbyB1c2UgYSBzcGVjaWFsIG9wZXJhdG9yICglPiUpIHRoYXQgbWFrZXMgb3VyIGNvZGUgYSBsaXR0bGUgbW9yZSBjb25jaXNlOg0KDQpgYGB7cn0NCmQucmVwIDwtIGQgJT4lIGdyb3VwX2J5KFN0dWRlbnQuSWRlbnRpZmljYXRpb24pICU+JSBmaWx0ZXIobigpID4gMSkNCmBgYA0KDQoNCmBgYHtyfQ0KdGFpbChkLnJlcCkNCmBgYA0KQW5kIGhlcmUgd2UncmUgZG93biB0byAxOTgyIG9ic2VydmF0aW9ucy4gV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBhdCBsZWFzdCAyIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBzdHVkZW50LiBUaGlzIGlzIHRoZSBkYXRhIEkgd2FudGVkIGZvciBteSBhbmFseXNpcywgYW5kIG5vdyBJIGhhdmUgYSByZWNvcmQgb2YgaG93IEkgcHVsbGVkIGl0LiBJZiBJIGdvdCBtb3JlIGRhdGEgbGF0ZXIsIEkgY291bGQgcnVuIHRoZSBzYW1lIGNvZGUgYWdhaW4uDQoNCiNQYXJ0IElJOiBNYWtlIGl0ICp0aWR5Kg0KDQpJbiB0aGlzIHBhcnQgb2YgdGhlIHdvcmtzaG9wLCB3ZSdsbCBmb2N1cyBvbiBoYXZpbmcgYSBsb2dpY2FsIHN0cnVjdHVyZSBmb3IgZGF0YXNldHMsIGFuZCBsb29rIGF0IHdheXMgdG8gbWFrZSB0aGF0IGhhcHBlbiBpbiBSLCBwcmltYXJpbHkgdXNpbmcgdGhlICp0aWR5ciogcGFja2FnZS4NCg0KRGF0YXNldHMgYXJlIGNvbXBvc2VkIG9mICp2YWx1ZXMqIChvciAqZGF0YSosIHNpbmcuICpkYXR1bSopLCBhbmQgZWFjaCAqdmFsdWUqIGlzIGFzc29jaWF0ZWQgd2l0aCBhICp2YXJpYWJsZSogYW5kIGFuICpvYnNlcnZhdGlvbiouDQoNCkEgKnZhcmlhYmxlKiBpcyBhbGwgdGhlIGRhdGEgdGhhdCBtZWFzdXJlIHRoZSBzYW1lIGF0dHJpYnV0ZS4gVGhpcyBjb3VsZCBiZSBwZXJzb24gSURzIChpLmUuLCBub21pbmFsIHZhcmlhYmxlcyB0aGF0IGRpc3Rpbmd1aXNoIG9uZSBwZXJzb24gZnJvbSBhbm90aGVyKSBvciB0aGluZ3MgbGlrZSB0ZXN0IHNjb3JlcyAoZS5nLiwgQUNURkwgT1BJYyBzY29yZXMgdGhhdCBtZWFzdXJlIHNwZWFraW5nIGFiaWxpdHkpLg0KDQpBbiAqb2JzZXJ2YXRpb24qIGlzIGEgY29sbGVjdGlvbiBvZiBhbGwgdmFyaWFibGVzIGJlbG9uZ2luZyB0byB0aGUgc2FtZSBvYmplY3QgKGUuZy4sIHBlcnNvbikgKmF0IHRoZSBzYW1lIHRpbWUvaW4gdGhlIHNhbWUgc25hcHNob3QqLg0KDQpXaWNraGFtICgyMDE0KSBhcmd1ZXMgdGhhdCBkYXRhIGFyZSAqdGlkeSogaWY6DQoNCjEuIEVhY2ggdmFyaWFibGUgaGFzIGl0cyBvd24gY29sdW1uLg0KMi4gRWFjaCBvYnNlcnZhdGlvbiBoYXMgaXRzIG93biByb3cuDQoNClRoaXMgc2VlbXMgc2ltcGxlLCBidXQgY2FuIGJlIHN1cnByaXNpbmdseSB0cmlja3kgaW4gbW9yZSBjb21wbGljYXRlZCByZXNlYXJjaCAob3IgbWVhc3VyZW1lbnQpIGRlc2lnbnMuDQoNCiNXYXJtLVVwOiBBIFNpbXBsZSBEYXRhc2V0IERlc2lnbg0KDQpTY2VuYXJpbyAoYmFzZWQgb24gTG9ld2VuIGV0IGFsLiwgaW4gcHJlcCk6IFdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBzdGF0aXN0aWNhbCBsaXRlcmFjeSBvZiBTTEEgcmVzZWFyY2hlcnMuIFdlIGhhZCA4OSByZXNlYXJjaGVycyB0YWtlIGEgdGVzdCBvbiBzdGF0aXN0aWNzLCBhbmQgd2UgaGF2ZSBhIHRvdGFsIHNjb3JlIGZvciBlYWNoIHBlcnNvbi4gV2UgYWxzbyBjb2xsZWN0ZWQgc29tZSBiYWNrZ3JvdW5kIGRhdGEsIGluY2x1ZGluZyByZXNlYXJjaCBvcmllbnRhdGlvbiAoNyBwb2ludCBzY2FsZSwgMSA9IHF1YWwsIDcgPSBxdWFudCksIG51bWJlciBvZiBzdGF0cyBjb3Vyc2VzIHRha2VuLCBhbmQgeWVhcnMgaW4gdGhlIGZpZWxkLiBXZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHN0YXRpc3RpY2FsIGxpdGVyYWN5IGFuZCBzdGF0aXN0aWNhbCBiYWNrZ3JvdW5kLg0KDQpIb3cgd291bGQgeW91IGRlc2lnbiB0aGlzIGRhdGFzZXQ/IEluIGdyb3VwcyBvZiAzLTQsIHNrZXRjaCBvdXQgYSB0YWJsZSB3aXRoIHZhbHVlcyBmb3IgMSBzdWJqZWN0Lg0KDQojV2FybS1VcDogU29sdXRpb24NCg0KSUQgfCBMaXRlcmFjeSBTY29yZSB8IE9yaWVudGF0aW9uIHwgU3RhdHNDb3Vyc2VzIHwgWWVhcnNpbkZpZWxkDQotLS18LS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0NCjEgIHwyMSAgICAgICAgICAgICAgfDYgICAgICAgICAgICB8MiAgICAgICAgICAgICB8Nw0KDQojTm93IGZvciBhIGNoYWxsZW5nZS4uLg0KVGhpbmdzIGdldCBhIGJpdCBtb3JlIGNvbXBsaWNhdGVkIHdoZW4geW91IGFyZSBtZWFzdXJpbmcgcGVvcGxlIGF0IGRpZmZlcmVudCB0aW1lcyBhbmQgd2l0aCBtdWx0aXBsZSBtZWFzdXJlcyBvZiB0aGUgc2FtZSBhdHRyaWJ1dGUtIGJ1dCB0aGlzIGlzICp2ZXJ5KiBjb21tb24gaW4gb3VyIGZpZWxkLiBMZXQncyB0aGluayBhYm91dCB0aGUgc2NlbmFyaW8gYmVsb3csIGFuZCBob3cgd2UgY291bGQgZGVzaWduIGEgKnRpZHkqIGRhdGFzZXQuDQoNClNjZW5hcmlvIChiYXNlZCBvbiBJc2JlbGwsIFBhcmssICYgTGVlLCB1bmRlciByZXZpZXcpOiBXZSB3ZXJlIGludGVyZXN0ZWQgaW4gdGhlIGltcGFjdCBvZiBwcm9udW5jaWF0aW9uIGluc3RydWN0aW9uIG9uIHRoZSBjb21wcmVoZW5zaWJpbGl0eSBvZiBMMiBLb3JlYW4gbGVhcm5lcnMuIFdlIGhhZCAzNiAqKnN0dWRlbnRzKiogZGl2aWRlZCBpbnRvIDIgKipncm91cCoqczogdHJlYXRtZW50IGFuZCBjb250cm9sLiBTdHVkZW50cyBkaWZmZXJlZCBpbiB0aGVpciAqKkwxKiogKEVuZ2xpc2gsIENoaW5lc2UpIGFuZCAqKlByb2ZpY2llbmN5KiogKGJlZ2lubmVyLCBpbnRlcm1lZGlhdGUpLiBXZSBoYWQgdGhlbSBjb21wbGV0ZSAyICoqdGFzayoqcyAocGljdHVyZSBkZXNjcmlwdGlvbiwgcmVhZC1hbG91ZCkgYXQgMiAqKnRpbWUqKnMgKHByZXRlc3QsIHBvc3R0ZXN0KS4gRWFjaCB0YXNrIHdhcyBzY29yZWQgZm9yIGNvbXByZWhlbnNpYmlsaXR5IGJ5IHNldmVyYWwgcmF0ZXJzLCB3ZSdsbCBsb29rIGF0IHRoZSBhdmVyYWdlICoqc2NvcmUqKi4NCg0KSG93IHdvdWxkIHlvdSBkZXNpZ24gdGhpcyBkYXRhc2V0PyBJbiBncm91cHMgb2YgMy00LCBza2V0Y2ggb3V0IGEgdGFibGUgd2l0aCB2YWx1ZXMgZm9yIDEgc3ViamVjdC4NCg0KI1NvbHV0aW9uDQoNClN0dWRlbnR8TDF8UHJvZnxHcm91cHxUaW1lfFRhc2t8U2NvcmUNCi0tLS0tLS18LS18LS0tLXwtLS0tLXwtLS0tfC0tLS18LS0tLS0NCjAxfEV8SW50fFR8MXxQaWN8NC4yDQowMXxFfEludHxUfDF8UmVhZHw1LjENCjAxfEV8SW50fFR8MnxQaWN8NC43DQowMXxFfEludHxUfDJ8UmVhZHw2LjMNCi4ufC58Li4ufC58LnwuLi4ufC4NCg0KQWx0aG91Z2ggd2UgbWlnaHQgaW50dWl0aXZlbHkgdGhpbmsgMSBzdWJqZWN0ID0gMSByb3csICp0aWR5KiBkYXRhc2V0IGRlc2lnbiBjYW4gdHVybiAxIHN1YmplY3QgaW4gdG8gZG96ZW5zIG9mIHJvd3MuIEFzaWRlIGZyb20gcHJvdmlkaW5nIGEgY29tbW9uIHJlZmVyZW5jZSBwb2ludCBmb3IgZGF0YXNldCBkZXNpZ24sIGl0IGp1c3Qgc28gaGFwcGVucyB0aGF0IGxvdHMgb2Ygc3RhdHMgcHJvZ3JhbXMgcGxheSB2ZXJ5IG5pY2Ugd2l0aCB0aWR5IGRhdGEuDQoNCiNHb2luZyBmcm9tIHdpZGUgdG8gbG9uZw0KDQpJbiBSLCB3ZSBjYW4gdXNlIHRoZSBnYXRoZXIoKSBmdW5jdGlvbiB0byBnbyBmcm9tIGNvbnZlbnRpb25hbCB3aWRlIGRhdGFzZXRzIHRvIGxvbmcsIHRpZHkgZGF0YXNldHMuDQoNCkZpcnN0LCBsZXQncyByZWFkIGluIG91ciBkYXRhLiBZb3UgbWF5IHdhbnQgdG8gc3RhcnQgYSBuZXcgc2NyaXB0LCBidXQgeW91IGRvbid0IGhhdmUgdG8uDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZHBseXIpDQoNCiNyZWFkIGluIGRhdGENCmRmIDwtIHJlYWQuZGVsaW0oImNvbXByZWhlbnNpYmlsaXR5LnR4dCIpDQpgYGANCg0KYGBge3J9DQpoZWFkKGRmKQ0KYGBgDQpTbyB3ZSBzZWUgaGVyZSB0aGF0IHRoZSBjb21wcmVoZW5zaWJpbGl0eSBzY29yZXMsIHdoaWNoIGFsbCBtZWFzdXJlIHRoZSBzYW1lIGF0dHJpYnV0ZSwgYXJlIGluIDQgZGlmZmVyZW50IGNvbHVtbnMuIEF0IHRoZSBzYW1lIHRpbWUsIHdlIGhhdmUgNCBkaWZmZXJlbnQgbWVhc3VyZW1lbnQgaW5zdGFuY2VzIG9uIHRoZSBzYW1lIHJvdzsgZWFjaCBjb21wcmVoZW5zaWJpbGl0eSBzY29yZSBpcyBhc3NvY2lhdGVkIHdpdGggYSB1bmlxdWUgc3BlZWNoIHNhbXBsZSAodGFzaykgYXQgYSB1bmlxdWUgdGltZSAocHJlLCBwb3N0KS4gDQoNCiNnYXRoZXIoKSBvdXIgY29tcHJlaGVuc2liaWx0eSBpbiBhIHNpbmdsZSB2YXJpYWJsZSBjb2x1bW4NCldlIG5lZWQgdG8gZ2V0IGFsbCB0aGUgY29tcHJlaGVuc2liaWxpdHkgc2NvcmVzIGluIHRoZSBzYW1lIGNvbHVtbjoNCg0KYGBge3J9DQpkZi5sb25nIDwtIGdhdGhlcihkZiwgIlRhc2tfVGltZSIsICJTY29yZSIsIDU6OCkNCg0KI2p1c3QgdG8gc2VlIHRoaW5ncyBlYXNpZXIuLi4NCmRmLmxvbmcgPC0gYXJyYW5nZShkZi5sb25nLCBTdWJqX0lEKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChkZi5sb25nKQ0KYGBgDQoNCkkgZGVjaWRlZCB0byBtYWtlIGEgbmV3IGRhdGFmcmFtZSAoZGYubG9uZykgc28gdGhhdCBJIGNvdWxkIGdvIGJhY2sgdG8gbXkgb3JpZ2luYWwgd2lkZSBkYXRhc2V0IGlmIG5lZWRlZC4gSW4gdGhlIGdhdGhlcigpIGZ1bmN0aW9uLCBJIHRvbGQgaXQgdG8gdGFrZSB0aGUgZGF0YSBmcm9tICJkZiIgKG91ciBvcmlnaW5hbCBkYXRhZnJhbWUpLCB3aGF0IHRoZSAia2V5IiBjb2x1bW4gc2hvdWxkIGJlIGNhbGxlZCwgd2hhdCB0aGUgInZhbHVlIiBjb2x1bW4gc2hvdWxkIGJlIGNhbGxlZCwgYW5kIHdoaWNoIGNvbHVtbnMgdG8gcHVsbCB0b2dldGhlciAoaW4gdGhpcyBjYXNlIGNvbHVtbnMgNSB0byA4KS4gDQoNClRoZSBwcmUtZ2F0aGVyIGNvbHVtbiBuYW1lcyBhcmUgd2hhdCBnb2VzIGluIHRoZSAia2V5IiBjb2x1bW4uIFNpbmNlIG15IGNvbHVtbiBuYW1lcyBoYWQgaW5mb3JtYXRpb24gb24gVGFzayBhbmQgVGltZSwgSSBjYWxsZWQgdGhlIG5ldyBrZXkgIlRhc2tfVGltZS4iIFRoZSB2YWx1ZXMgdGhlc2UgYXJlIGNvbm5lY3RlZCB0byBhcmUgY29tcHJlaGVuc2liaWx0eSAiU2NvcmVzLiINCg0KQXMgeW91IGNhbiBzZWUsIHdlJ3JlIG5vdCBxdWl0ZSBkb25lIHlldC0gSSBuZWVkIHRvIHNwbGl0IHVwIHRoYXQgVGFza19UaW1lIGNvbHVtbiBzbyBJIGNhbiBydW4gbXkgYW5hbHlzZXMuDQoNCiNUaGVyZSdzIGEgZnVuY3Rpb24gZm9yIHRoYXQsIHRvby0gc2VwYXJhdGUoKQ0KTHVja2lseSwgKnRpZHlyKiBoYXMgYSBmdW5jdGlvbiBmb3Igc3BsaXR0aW5nIHVwIGNvbmZvdW5kZWQgY29sdW1ucy0gc2VwYXJhdGUoKS4gTGV0J3MgdHJ5IGl0IG91dC4NCg0KYGBge3J9DQpkZi5sb25nIDwtIHNlcGFyYXRlKGRmLmxvbmcsIFRhc2tfVGltZSwgYygiVGFzayIsICJUaW1lIiksIHNlcCA9ICJfIikNCg0KI2FuZCBhIHNlbnNpYmxlIG9yZGVyaW5nLi4uDQpkZi5sb25nIDwtIGFycmFuZ2UoZGYubG9uZywgU3Vial9JRCwgVGltZSwgVGFzaykNCmBgYA0KDQpgYGB7cn0NCmhlYWQoZGYubG9uZykNCmBgYA0KDQpBbmQgdGhlcmUgd2UgaGF2ZSBpdCEgRnJvbSB3aWRlIHRvIGxvbmcsIHJlYWR5IGZvciBhbGwga2luZHMgb2Ygc25henp5IGdyYXBoaWNzIGFuZCBzb3BoaXN0aWNhdGVkIHJlcGVhdGVkLW1lYXN1cmVzIGFuYWx5c2VzLg0KDQpUaGVyZSdzIG1vcmUgdGhhdCBjb3VsZC9zaG91bGQgYmUgZG9uZSB3aXRoIHRoZSBkYXRhc2V0LCBsaWtlIGNvbnZlcnRpbmcgdmFyaWFibGVzIHRvIGZhY3RvcnMsIGNyZWF0aW5nIHNvbWUgcmVjb2RlZCB2YXJpYWJsZXMgKGZyb20gc3RyaW5nIHRvIG51bWJlciBjb2RlcyksIGJ1dCBhdCB0aGUgdmVyeSBsZWFzdCBvdXIgZGF0YSBpcyBpbiB0aGUgcmlnaHQgc2hhcGUuDQoNCiNRJkENCg0KQW55IHF1ZXN0aW9ucyBhYm91dCB0aGUgZnVuY3Rpb25zIGFuZCBleGFtcGxlcyBpbnRyb2R1Y2VkIHRvZGF5Pw0KDQpBbnkgY3VycmVudCBkYXRhIHByb2JsZW1zIHlvdSdkIGxpa2UgdG8gc2hhcmUvYnJhaW5zdG9ybSB3aXRoIHRoZSBncm91cD8NCg0KI1JlZmVyZW5jZXMgYW5kIFJlc291cmNlcw0KDQotIENoZWF0c2hlZXRzOiBJbiBSU3R1ZGlvLCBjbGljayB0aGUgSGVscCBkcm9wLWRvd24gbWVudSBhdCB0aGUgdG9wIG9mIHRoZSB3aW5kb3cuIEdvIHRvIENoZWF0c2hlZXRzID4gRGF0YSBtYW5pcHVsYXRpb24gd2l0aCBkcGx5ciwgdGlkeXINCg0KLSBIZWxwIEZpbGVzOiBJZiB5b3UgbmVlZCBzb21lIGFkZGl0aW9uYWwgcmVmZXJlbmNlIG1hdGVyaWFsIGZvciBhIHBhcnRpY3VsYXIgZnVuY3Rpb24sIHR5cGUgPzxmdW5jdGlvbj4gaW4gdGhlIENvbnNvbGUgKGJvdHRvbS1sZWZ0IHBhbmVsKS4gRm9yIGV4YW1wbGUsIHR5cGluZyA/c2VwYXJhdGUgd2lsbCBicmluZyB1cCBhIGhlbHAgZmlsZSBpbiB0aGUgYm90dG9tLXJpZ2h0IHBhbmVsIHdpdGggc29tZSBleGFtcGxlIGNvZGUuDQoNCi0gV2lja2hhbSwgSC4gKDIwMTQpLiBUaWR5IGRhdGEuICpKb3VybmFsIG9mIFN0YXRpc3RpY2FsIFNvZnR3YXJlLCA1OSwqKDEwKSwgMS0yMy4gIFtodHRwczovL3d3dy5qc3RhdHNvZnQub3JnL2FydGljbGUvdmlldy92MDU5aTEwXShodHRwczovL3d3dy5qc3RhdHNvZnQub3JnL2FydGljbGUvdmlldy92MDU5aTEwKQ0KDQotIEdvb2dsZS4gWW91IGNhbiB1c2Ugbm9ybWFsIHF1ZXN0aW9uIHN5bnRheCAoZS5nLiAiSG93IGRvIHlvdS4uLiIpIGFuZCB0aGVuIGFkZCBhIHBocmFzZSBsaWtlICJpbiBSIiBvciAid2l0aCBkcGx5ciByIg0K
Nice and good article. It is very useful for me to learn and understand easily. Thanks for sharing your valuable information and time. Please keep updating MicroStrategy Online Training Hyderabad
ReplyDelete