Collaborators: Dan Rosenfeld, Jered Ataky, and Rick Sughrue

The collaborators (noted above) contributed to and collaborated on solving Project 1.

Background

In this project, we’re given a text file with chess tournament results where the information has some structure. Our job is to create an R Markdown file that generates a .CSV file (that could for example be imported into a SQL database) with the following information for all of the players:

Player Name | State | Total Points | Pre-Rating | Avg Opp Pre-Rating

For the first player, the information would be: Gary Hua, ON, 6.0, 1794, 1605

1605 was calculated by using the pre-tournament opponents’ ratings of 1436, 1563, 1600, 1610, 1649, 1663, 1716, and dividing by the total number of games played.

Step 1

Import the data from the text file.

chess_input <- read.table(url("https://raw.githubusercontent.com/Magnus-PS/CUNY-SPS-DATA-607/Project-1/tournamentinfo.txt"), sep = ",")

#head(chess_input)

From the output above, we can see that the header has been included and that the data is not organized. Thus, prior to outputting our data in the desired form, we’ll have to pull variables, etc. from this set in an organized manner. We’ll have to wrangle data and sort through our ‘raw’ data set for what is essential / desired.

Step 2

Remove header, “- - -” (dashed lines), and " " excess white spaces.

#Remove header
chess_input <- chess_input[-c(1:4),]

#Exclude every 3rd row (of dashed lines)
i <- seq(0, 192, 3)
chess_input <- chess_input[-i]

#Compress white space for readability
chess_input <- str_replace_all(chess_input, "\\s+", " ")

#head(chess_input)

The result of our processed data is alternating rows of values where the values themselves are separated by “|”s.

Odd rows: ID | Player Name | Total Pts | W/L/D Opp# …

Even rows: State | USCF ID | Rtg Pre -> Post | …

Step 3

Sort by value row value (even v odd) to simplify later extraction.

o_rows <- seq(1, 128, 2)
e_rows <- seq(2, 128, 2)
  
o_chess <- unlist(data.frame(x=chess_input[o_rows]))
e_chess <- unlist(data.frame(x=chess_input[e_rows]))

#head(o_chess)
#head(e_chess)

We now have 2 vectors (o_chess and e_chess) produced via unlist() to contain all their atomic components. From each of these vectors, we can begin to use regular expressions to extract essential components from each vector.

Step 4

Extract essential components from each vector: Player Name | State | Total Points | Pre-Rating

and later Avg Opp Pre-Rating.

#Extract player IDs: 
id<-unlist(str_extract_all(o_chess,"\\d{1,2} \\| "))
id<-unlist(str_extract_all(id, "\\d{1,2}"))
#head(id)

#Extract player names:
name <-unlist(str_extract_all(o_chess,"\\w+ ?\\w+ \\w+"))
#head(name)

#Extract player point total:
pt_total <-unlist(str_extract_all(o_chess,"\\d.\\d"))
#head(pt_total)

#Extract player state:
state <-unlist(str_extract_all(e_chess, " \\w{2} \\| "))
state<-unlist(str_extract_all(state, "\\w{2}"))
#head(state)

#Extract player pre-rating:
prerating <-unlist(str_extract_all(e_chess, "(R:\\s*)(\\d+)"))
prerating <-as.numeric(unlist(str_extract_all(prerating, "(\\d+)")))
#prerating

At this point, we’ve extracted the player’s ID, name, point total, state, and prerating. Prerating has been stored as a double for later purposes.

We would like to combine what we’ve extracted (the variables of interest) into one table but before we can do so we have one more variable to compute … the average opponent ranking.

Step 5

Compute the Avg Opp Pre-Rating.

We’re going to build a matrix of opponent ID#s, replace these numbers with their corresponding prerating and then take the average across each row to compute the Avg Opp Pre-Rating.

#Extract the entire row from the pt_total onward, set missing values @ B,H,U and X to be " 0" and then extract digits.

opp_ids<-unlist(str_extract_all(o_chess,"\\|[0-9].*"))
opp_ids<-str_replace_all(opp_ids, "[BHUX]", " 0")
opp_ids<-str_extract_all(opp_ids,"\\s\\d{1,2}")

#Convert into numeric matrix and from 64 x 7 to 7 x 64.

opp_ids_m <- matrix(unlist(opp_ids),  byrow= TRUE, nrow = length(opp_ids))
opp_ids_m <- t(apply(opp_ids_m, 1, as.numeric))

#Use embedded for loops to iterate over rows and columns. If the entry is 0, replace it with NA. Otherwise, replace the id# with its corresponding preratings.

for (i in 1:nrow(opp_ids_m)) {
  for (j in 1:ncol(opp_ids_m)) {
    if (opp_ids_m[i,j] == 0){
      opp_ids_m[i,j] = NA
    } else {
      opp_ids_m[i,j] <- prerating[opp_ids_m[i,j]]
    }
  }
}

#Note: if an error message pops up for the for loop portion, please clear objects from workspace and run again.


#Take the average value per row while omitting missing values.
avg_opp_rating <- c(rowMeans(opp_ids_m, na.rm = TRUE))
avg_opp_rating <- round(avg_opp_rating)

avg_opp_rating
##  [1] 1605 1469 1564 1574 1501 1519 1372 1468 1523 1554 1468 1506 1498 1515 1484
## [16] 1386 1499 1480 1426 1411 1470 1300 1214 1357 1363 1507 1222 1522 1314 1144
## [31] 1260 1379 1277 1375 1150 1388 1385 1539 1430 1391 1248 1150 1107 1327 1152
## [46] 1358 1392 1356 1286 1296 1356 1495 1345 1206 1406 1414 1363 1391 1319 1330
## [61] 1327 1186 1350 1263

At this point, we have extracted all the essential components and all we have to do is combine and export them to .csv.

Step 6

Combine desired components into dataframe and export to .csv.

#Combine desired components into dataframe
combined <- data.frame(name, state, pt_total, prerating, avg_opp_rating)
colnames(combined) <- c("Player Name", "Player's State", "Point Total", "Pre Rating", "Avg Opponent Rating") #waiting to calculate and add avg_opp_rating
combined
##                 Player Name Player's State Point Total Pre Rating
## 1                  GARY HUA             ON         6.0       1794
## 2           DAKSHESH DARURI             MI         6.0       1553
## 3              ADITYA BAJAJ             MI         6.0       1384
## 4       PATRICK H SCHILLING             MI         5.5       1716
## 5                HANSHI ZUO             MI         5.5       1655
## 6               HANSEN SONG             OH         5.0       1686
## 7         GARY DEE SWATHELL             MI         5.0       1649
## 8          EZEKIEL HOUGHTON             MI         5.0       1641
## 9               STEFANO LEE             ON         5.0       1411
## 10                ANVIT RAO             MI         5.0       1365
## 11       CAMERON WILLIAM MC             MI         4.5       1712
## 12           KENNETH J TACK             MI         4.5       1663
## 13        TORRANCE HENRY JR             MI         4.5       1666
## 14             BRADLEY SHAW             MI         4.5       1610
## 15   ZACHARY JAMES HOUGHTON             MI         4.5       1220
## 16             MIKE NIKITIN             MI         4.0       1604
## 17       RONALD GRZEGORCZYK             MI         4.0       1629
## 18            DAVID SUNDEEN             MI         4.0       1600
## 19             DIPANKAR ROY             MI         4.0       1564
## 20              JASON ZHENG             MI         4.0       1595
## 21            DINH DANG BUI             ON         4.0       1563
## 22         EUGENE L MCCLURE             MI         4.0       1555
## 23                 ALAN BUI             ON         4.0       1363
## 24        MICHAEL R ALDRICH             MI         4.0       1229
## 25         LOREN SCHWIEBERT             MI         3.5       1745
## 26                  MAX ZHU             ON         3.5       1579
## 27           GAURAV GIDWANI             MI         3.5       1552
## 28     SOFIA ADINA STANESCU             MI         3.5       1507
## 29         CHIEDOZIE OKORIE             MI         3.5       1602
## 30       GEORGE AVERY JONES             ON         3.5       1522
## 31             RISHI SHETTY             MI         3.5       1494
## 32    JOSHUA PHILIP MATHEWS             ON         3.5       1441
## 33                  JADE GE             MI         3.5       1449
## 34   MICHAEL JEFFERY THOMAS             MI         3.5       1399
## 35         JOSHUA DAVID LEE             MI         3.5       1438
## 36            SIDDHARTH JHA             MI         3.5       1355
## 37     AMIYATOSH PWNANANDAM             MI         3.5        980
## 38                BRIAN LIU             MI         3.0       1423
## 39            JOEL R HENDON             MI         3.0       1436
## 40             FOREST ZHANG             MI         3.0       1348
## 41      KYLE WILLIAM MURPHY             MI         3.0       1403
## 42                 JARED GE             MI         3.0       1332
## 43        ROBERT GLEN VASEY             MI         3.0       1283
## 44       JUSTIN D SCHILLING             MI         3.0       1199
## 45                DEREK YAN             MI         3.0       1242
## 46 JACOB ALEXANDER LAVALLEY             MI         3.0        377
## 47              ERIC WRIGHT             MI         2.5       1362
## 48             DANIEL KHAIN             MI         2.5       1382
## 49         MICHAEL J MARTIN             MI         2.5       1291
## 50               SHIVAM JHA             MI         2.5       1056
## 51           TEJAS AYYAGARI             MI         2.5       1011
## 52                ETHAN GUO             MI         2.5        935
## 53            JOSE C YBARRA             MI         2.0       1393
## 54              LARRY HODGE             MI         2.0       1270
## 55                ALEX KONG             MI         2.0       1186
## 56             MARISA RICCI             MI         2.0       1153
## 57               MICHAEL LU             MI         2.0       1092
## 58             VIRAJ MOHILE             MI         2.0        917
## 59                SEAN M MC             MI         2.0        853
## 60               JULIA SHEN             MI         1.5        967
## 61            JEZZEL FARKAS             ON         1.5        955
## 62            ASHWIN BALAJI             MI         1.0       1530
## 63     THOMAS JOSEPH HOSMER             MI         1.0       1175
## 64                   BEN LI             MI         1.0       1163
##    Avg Opponent Rating
## 1                 1605
## 2                 1469
## 3                 1564
## 4                 1574
## 5                 1501
## 6                 1519
## 7                 1372
## 8                 1468
## 9                 1523
## 10                1554
## 11                1468
## 12                1506
## 13                1498
## 14                1515
## 15                1484
## 16                1386
## 17                1499
## 18                1480
## 19                1426
## 20                1411
## 21                1470
## 22                1300
## 23                1214
## 24                1357
## 25                1363
## 26                1507
## 27                1222
## 28                1522
## 29                1314
## 30                1144
## 31                1260
## 32                1379
## 33                1277
## 34                1375
## 35                1150
## 36                1388
## 37                1385
## 38                1539
## 39                1430
## 40                1391
## 41                1248
## 42                1150
## 43                1107
## 44                1327
## 45                1152
## 46                1358
## 47                1392
## 48                1356
## 49                1286
## 50                1296
## 51                1356
## 52                1495
## 53                1345
## 54                1206
## 55                1406
## 56                1414
## 57                1363
## 58                1391
## 59                1319
## 60                1330
## 61                1327
## 62                1186
## 63                1350
## 64                1263
#Export to .csv: relative code not included per grading rubric.
#To execute: uncomment out, enter file path, replace '\' with '/' and run.

#write.csv(combined,"[ENTER FILE PATH HERE]chess_combined.csv", row.names = FALSE)
LS0tDQp0aXRsZTogIkRBVEEgNjA3IFByb2plY3QgMSINCmF1dGhvcjogIk1hZ251cyBTa29uYmVyZyINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogDQogIG9wZW5pbnRybzo6bGFiX3JlcG9ydDogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50Og0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KYGBge3IgbGlicmFyaWVzLCBpbmNsdWRlPUZBTFNFfQ0KI2xvYWQgcmVsZXZhbnQgbGlicmFyaWVzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgI2luY2x1ZGVzIHN0cmluZ3IgcGFja2FnZQ0KbGlicmFyeShzdHJpbmdpKQ0KYGBgDQoNCjxzdHlsZT4NCmRpdi5ibHVlIHsgYmFja2dyb3VuZC1jb2xvcjojZTZmMGZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7fQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAiYmx1ZSI+DQoNCioqQ29sbGFib3JhdG9yczoqKiBEYW4gUm9zZW5mZWxkLCBKZXJlZCBBdGFreSwgYW5kIFJpY2sgU3VnaHJ1ZQ0KDQoqVGhlIGNvbGxhYm9yYXRvcnMgKG5vdGVkIGFib3ZlKSBjb250cmlidXRlZCB0byBhbmQgY29sbGFib3JhdGVkIG9uIHNvbHZpbmcgUHJvamVjdCAxLioNCg0KPC9kaXY+IFxoZmlsbFxicmVhaw0KDQoNCiMjIEJhY2tncm91bmQNCkluIHRoaXMgcHJvamVjdCwgd2XigJlyZSBnaXZlbiBhIHRleHQgZmlsZSB3aXRoIGNoZXNzIHRvdXJuYW1lbnQgcmVzdWx0cyB3aGVyZSB0aGUgaW5mb3JtYXRpb24gaGFzIHNvbWUgc3RydWN0dXJlLiBPdXIgam9iIGlzIHRvIGNyZWF0ZSBhbiBSIE1hcmtkb3duIGZpbGUgdGhhdCBnZW5lcmF0ZXMgYSAuQ1NWIGZpbGUgKHRoYXQgY291bGQgZm9yIGV4YW1wbGUgYmUgaW1wb3J0ZWQgaW50byBhIFNRTCBkYXRhYmFzZSkNCndpdGggdGhlIGZvbGxvd2luZyBpbmZvcm1hdGlvbiBmb3IgYWxsIG9mIHRoZSBwbGF5ZXJzOg0KDQoqUGxheWVyIE5hbWUgfCBTdGF0ZSB8IFRvdGFsIFBvaW50cyB8IFByZS1SYXRpbmcgfCBBdmcgT3BwIFByZS1SYXRpbmcqDQoNCkZvciB0aGUgZmlyc3QgcGxheWVyLCB0aGUgaW5mb3JtYXRpb24gd291bGQgYmU6DQoqR2FyeSBIdWEsIE9OLCA2LjAsIDE3OTQsIDE2MDUqDQoNCioxNjA1IHdhcyBjYWxjdWxhdGVkIGJ5IHVzaW5nIHRoZSBwcmUtdG91cm5hbWVudCBvcHBvbmVudHPigJkgcmF0aW5ncyBvZiAxNDM2LCAxNTYzLCAxNjAwLCAxNjEwLCAxNjQ5LCAxNjYzLCAxNzE2LCBhbmQgZGl2aWRpbmcgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBnYW1lcyBwbGF5ZWQuKg0KDQojIyBTdGVwIDENCg0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9DQo8L3N0eWxlPg0KPGRpdiBjbGFzcyA9ICJibHVlIj4NCg0KSW1wb3J0IHRoZSBkYXRhIGZyb20gdGhlIHRleHQgZmlsZS4NCg0KPC9kaXY+IFxoZmlsbFxicmVhaw0KDQoNCg0KYGBge3J9DQpjaGVzc19pbnB1dCA8LSByZWFkLnRhYmxlKHVybCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL01hZ251cy1QUy9DVU5ZLVNQUy1EQVRBLTYwNy9Qcm9qZWN0LTEvdG91cm5hbWVudGluZm8udHh0IiksIHNlcCA9ICIsIikNCg0KI2hlYWQoY2hlc3NfaW5wdXQpDQpgYGANCkZyb20gdGhlIG91dHB1dCBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBoZWFkZXIgaGFzIGJlZW4gaW5jbHVkZWQgYW5kIHRoYXQgdGhlIGRhdGEgaXMgbm90IG9yZ2FuaXplZC4gVGh1cywgKnByaW9yIHRvKiBvdXRwdXR0aW5nIG91ciBkYXRhIGluIHRoZSBkZXNpcmVkIGZvcm0sIHdlJ2xsIGhhdmUgdG8gcHVsbCB2YXJpYWJsZXMsIGV0Yy4gZnJvbSB0aGlzIHNldCBpbiBhbiBvcmdhbml6ZWQgbWFubmVyLiBXZSdsbCBoYXZlIHRvIHdyYW5nbGUgZGF0YSBhbmQgc29ydCB0aHJvdWdoIG91ciAncmF3JyBkYXRhIHNldCBmb3Igd2hhdCBpcyBlc3NlbnRpYWwgLyBkZXNpcmVkLg0KDQojIyBTdGVwIDINCg0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9DQo8L3N0eWxlPg0KPGRpdiBjbGFzcyA9ICJibHVlIj4NCg0KUmVtb3ZlIGhlYWRlciwgIi0gLSAtIiAoZGFzaGVkIGxpbmVzKSwgYW5kICIgICIgZXhjZXNzIHdoaXRlIHNwYWNlcy4NCg0KPC9kaXY+IFxoZmlsbFxicmVhaw0KDQpgYGB7cn0NCiNSZW1vdmUgaGVhZGVyDQpjaGVzc19pbnB1dCA8LSBjaGVzc19pbnB1dFstYygxOjQpLF0NCg0KI0V4Y2x1ZGUgZXZlcnkgM3JkIHJvdyAob2YgZGFzaGVkIGxpbmVzKQ0KaSA8LSBzZXEoMCwgMTkyLCAzKQ0KY2hlc3NfaW5wdXQgPC0gY2hlc3NfaW5wdXRbLWldDQoNCiNDb21wcmVzcyB3aGl0ZSBzcGFjZSBmb3IgcmVhZGFiaWxpdHkNCmNoZXNzX2lucHV0IDwtIHN0cl9yZXBsYWNlX2FsbChjaGVzc19pbnB1dCwgIlxccysiLCAiICIpDQoNCiNoZWFkKGNoZXNzX2lucHV0KQ0KDQpgYGANCg0KVGhlIHJlc3VsdCBvZiBvdXIgKnByb2Nlc3NlZCBkYXRhKiBpcyBhbHRlcm5hdGluZyByb3dzIG9mIHZhbHVlcyB3aGVyZSB0aGUgdmFsdWVzIHRoZW1zZWx2ZXMgYXJlIHNlcGFyYXRlZCBieSAifCJzLg0KDQoqKk9kZCByb3dzKio6IElEIHwgUGxheWVyIE5hbWUgfCBUb3RhbCBQdHMgfCBXL0wvRCBPcHAjIC4uLg0KDQoqKkV2ZW4gcm93cyoqOiBTdGF0ZSB8IFVTQ0YgSUQgfCBSdGcgUHJlIC0+IFBvc3QgfCAuLi4NCg0KIyMgU3RlcCAzDQoNCjxzdHlsZT4NCmRpdi5ibHVlIHsgYmFja2dyb3VuZC1jb2xvcjojZTZmMGZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7fQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAiYmx1ZSI+DQoNClNvcnQgYnkgdmFsdWUgcm93IHZhbHVlIChldmVuIHYgb2RkKSB0byBzaW1wbGlmeSBsYXRlciBleHRyYWN0aW9uLiANCg0KPC9kaXY+IFxoZmlsbFxicmVhaw0KDQpgYGB7cn0NCg0Kb19yb3dzIDwtIHNlcSgxLCAxMjgsIDIpDQplX3Jvd3MgPC0gc2VxKDIsIDEyOCwgMikNCiAgDQpvX2NoZXNzIDwtIHVubGlzdChkYXRhLmZyYW1lKHg9Y2hlc3NfaW5wdXRbb19yb3dzXSkpDQplX2NoZXNzIDwtIHVubGlzdChkYXRhLmZyYW1lKHg9Y2hlc3NfaW5wdXRbZV9yb3dzXSkpDQoNCiNoZWFkKG9fY2hlc3MpDQojaGVhZChlX2NoZXNzKQ0KDQpgYGANCldlIG5vdyBoYXZlIDIgdmVjdG9ycyAob19jaGVzcyBhbmQgZV9jaGVzcykgcHJvZHVjZWQgdmlhIHVubGlzdCgpIHRvIGNvbnRhaW4gYWxsIHRoZWlyIGF0b21pYyBjb21wb25lbnRzLiBGcm9tIGVhY2ggb2YgdGhlc2UgdmVjdG9ycywgd2UgY2FuIGJlZ2luIHRvIHVzZSByZWd1bGFyIGV4cHJlc3Npb25zIHRvIGV4dHJhY3QgKmVzc2VudGlhbCBjb21wb25lbnRzKiBmcm9tIGVhY2ggdmVjdG9yLg0KDQojIyBTdGVwIDQNCg0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9DQo8L3N0eWxlPg0KPGRpdiBjbGFzcyA9ICJibHVlIj4NCg0KRXh0cmFjdCAqZXNzZW50aWFsIGNvbXBvbmVudHMqIGZyb20gZWFjaCB2ZWN0b3I6ICpQbGF5ZXIgTmFtZSB8IFN0YXRlIHwgVG90YWwgUG9pbnRzIHwgUHJlLVJhdGluZyoNCg0KYW5kIGxhdGVyICpBdmcgT3BwIFByZS1SYXRpbmcqLg0KDQo8L2Rpdj4gXGhmaWxsXGJyZWFrDQoNCg0KYGBge3J9DQojRXh0cmFjdCBwbGF5ZXIgSURzOiANCmlkPC11bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKG9fY2hlc3MsIlxcZHsxLDJ9IFxcfCAiKSkNCmlkPC11bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGlkLCAiXFxkezEsMn0iKSkNCiNoZWFkKGlkKQ0KDQojRXh0cmFjdCBwbGF5ZXIgbmFtZXM6DQpuYW1lIDwtdW5saXN0KHN0cl9leHRyYWN0X2FsbChvX2NoZXNzLCJcXHcrID9cXHcrIFxcdysiKSkNCiNoZWFkKG5hbWUpDQoNCiNFeHRyYWN0IHBsYXllciBwb2ludCB0b3RhbDoNCnB0X3RvdGFsIDwtdW5saXN0KHN0cl9leHRyYWN0X2FsbChvX2NoZXNzLCJcXGQuXFxkIikpDQojaGVhZChwdF90b3RhbCkNCg0KI0V4dHJhY3QgcGxheWVyIHN0YXRlOg0Kc3RhdGUgPC11bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGVfY2hlc3MsICIgXFx3ezJ9IFxcfCAiKSkNCnN0YXRlPC11bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKHN0YXRlLCAiXFx3ezJ9IikpDQojaGVhZChzdGF0ZSkNCg0KI0V4dHJhY3QgcGxheWVyIHByZS1yYXRpbmc6DQpwcmVyYXRpbmcgPC11bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGVfY2hlc3MsICIoUjpcXHMqKShcXGQrKSIpKQ0KcHJlcmF0aW5nIDwtYXMubnVtZXJpYyh1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKHByZXJhdGluZywgIihcXGQrKSIpKSkNCiNwcmVyYXRpbmcNCmBgYA0KQXQgdGhpcyBwb2ludCwgd2UndmUgZXh0cmFjdGVkIHRoZSBwbGF5ZXIncyBJRCwgbmFtZSwgcG9pbnQgdG90YWwsIHN0YXRlLCBhbmQgcHJlcmF0aW5nLiBQcmVyYXRpbmcgaGFzIGJlZW4gc3RvcmVkIGFzIGEgZG91YmxlIGZvciBsYXRlciBwdXJwb3Nlcy4NCg0KV2Ugd291bGQgbGlrZSB0byBjb21iaW5lIHdoYXQgd2UndmUgZXh0cmFjdGVkICh0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0KSBpbnRvIG9uZSB0YWJsZSBidXQgYmVmb3JlIHdlIGNhbiBkbyBzbyB3ZSBoYXZlIG9uZSBtb3JlIHZhcmlhYmxlIHRvIGNvbXB1dGUgLi4uIHRoZSBhdmVyYWdlIG9wcG9uZW50IHJhbmtpbmcuDQoNCiMjIFN0ZXAgNQ0KDQo8c3R5bGU+DQpkaXYuYmx1ZSB7IGJhY2tncm91bmQtY29sb3I6I2U2ZjBmZjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30NCjwvc3R5bGU+DQo8ZGl2IGNsYXNzID0gImJsdWUiPg0KDQpDb21wdXRlIHRoZSAqQXZnIE9wcCBQcmUtUmF0aW5nKi4NCg0KPC9kaXY+IFxoZmlsbFxicmVhaw0KDQpXZSdyZSBnb2luZyB0byBidWlsZCBhIG1hdHJpeCBvZiBvcHBvbmVudCBJRCNzLCByZXBsYWNlIHRoZXNlIG51bWJlcnMgd2l0aCB0aGVpciBjb3JyZXNwb25kaW5nIHByZXJhdGluZyBhbmQgdGhlbiB0YWtlIHRoZSBhdmVyYWdlIGFjcm9zcyBlYWNoIHJvdyB0byBjb21wdXRlIHRoZSAqQXZnIE9wcCBQcmUtUmF0aW5nKi4NCg0KYGBge3J9DQojRXh0cmFjdCB0aGUgZW50aXJlIHJvdyBmcm9tIHRoZSBwdF90b3RhbCBvbndhcmQsIHNldCBtaXNzaW5nIHZhbHVlcyBAIEIsSCxVIGFuZCBYIHRvIGJlICIgMCIgYW5kIHRoZW4gZXh0cmFjdCBkaWdpdHMuDQoNCm9wcF9pZHM8LXVubGlzdChzdHJfZXh0cmFjdF9hbGwob19jaGVzcywiXFx8WzAtOV0uKiIpKQ0Kb3BwX2lkczwtc3RyX3JlcGxhY2VfYWxsKG9wcF9pZHMsICJbQkhVWF0iLCAiIDAiKQ0Kb3BwX2lkczwtc3RyX2V4dHJhY3RfYWxsKG9wcF9pZHMsIlxcc1xcZHsxLDJ9IikNCg0KI0NvbnZlcnQgaW50byBudW1lcmljIG1hdHJpeCBhbmQgZnJvbSA2NCB4IDcgdG8gNyB4IDY0Lg0KDQpvcHBfaWRzX20gPC0gbWF0cml4KHVubGlzdChvcHBfaWRzKSwgIGJ5cm93PSBUUlVFLCBucm93ID0gbGVuZ3RoKG9wcF9pZHMpKQ0Kb3BwX2lkc19tIDwtIHQoYXBwbHkob3BwX2lkc19tLCAxLCBhcy5udW1lcmljKSkNCg0KI1VzZSBlbWJlZGRlZCBmb3IgbG9vcHMgdG8gaXRlcmF0ZSBvdmVyIHJvd3MgYW5kIGNvbHVtbnMuIElmIHRoZSBlbnRyeSBpcyAwLCByZXBsYWNlIGl0IHdpdGggTkEuIE90aGVyd2lzZSwgcmVwbGFjZSB0aGUgaWQjIHdpdGggaXRzIGNvcnJlc3BvbmRpbmcgcHJlcmF0aW5ncy4NCg0KZm9yIChpIGluIDE6bnJvdyhvcHBfaWRzX20pKSB7DQogIGZvciAoaiBpbiAxOm5jb2wob3BwX2lkc19tKSkgew0KICAgIGlmIChvcHBfaWRzX21baSxqXSA9PSAwKXsNCiAgICAgIG9wcF9pZHNfbVtpLGpdID0gTkENCiAgICB9IGVsc2Ugew0KICAgICAgb3BwX2lkc19tW2ksal0gPC0gcHJlcmF0aW5nW29wcF9pZHNfbVtpLGpdXQ0KICAgIH0NCiAgfQ0KfQ0KDQojTm90ZTogaWYgYW4gZXJyb3IgbWVzc2FnZSBwb3BzIHVwIGZvciB0aGUgZm9yIGxvb3AgcG9ydGlvbiwgcGxlYXNlIGNsZWFyIG9iamVjdHMgZnJvbSB3b3Jrc3BhY2UgYW5kIHJ1biBhZ2Fpbi4NCg0KDQojVGFrZSB0aGUgYXZlcmFnZSB2YWx1ZSBwZXIgcm93IHdoaWxlIG9taXR0aW5nIG1pc3NpbmcgdmFsdWVzLg0KYXZnX29wcF9yYXRpbmcgPC0gYyhyb3dNZWFucyhvcHBfaWRzX20sIG5hLnJtID0gVFJVRSkpDQphdmdfb3BwX3JhdGluZyA8LSByb3VuZChhdmdfb3BwX3JhdGluZykNCg0KYXZnX29wcF9yYXRpbmcNCmBgYA0KDQpBdCB0aGlzIHBvaW50LCB3ZSBoYXZlIGV4dHJhY3RlZCBhbGwgdGhlIGVzc2VudGlhbCBjb21wb25lbnRzIGFuZCBhbGwgd2UgaGF2ZSB0byBkbyBpcyBjb21iaW5lIGFuZCBleHBvcnQgdGhlbSB0byAuY3N2Lg0KDQojIyBTdGVwIDYNCg0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9DQo8L3N0eWxlPg0KPGRpdiBjbGFzcyA9ICJibHVlIj4NCg0KQ29tYmluZSBkZXNpcmVkIGNvbXBvbmVudHMgaW50byBkYXRhZnJhbWUgYW5kIGV4cG9ydCB0byAuY3N2Lg0KDQo8L2Rpdj4gXGhmaWxsXGJyZWFrDQoNCg0KYGBge3J9DQojQ29tYmluZSBkZXNpcmVkIGNvbXBvbmVudHMgaW50byBkYXRhZnJhbWUNCmNvbWJpbmVkIDwtIGRhdGEuZnJhbWUobmFtZSwgc3RhdGUsIHB0X3RvdGFsLCBwcmVyYXRpbmcsIGF2Z19vcHBfcmF0aW5nKQ0KY29sbmFtZXMoY29tYmluZWQpIDwtIGMoIlBsYXllciBOYW1lIiwgIlBsYXllcidzIFN0YXRlIiwgIlBvaW50IFRvdGFsIiwgIlByZSBSYXRpbmciLCAiQXZnIE9wcG9uZW50IFJhdGluZyIpICN3YWl0aW5nIHRvIGNhbGN1bGF0ZSBhbmQgYWRkIGF2Z19vcHBfcmF0aW5nDQpjb21iaW5lZA0KDQojRXhwb3J0IHRvIC5jc3Y6IHJlbGF0aXZlIGNvZGUgbm90IGluY2x1ZGVkIHBlciBncmFkaW5nIHJ1YnJpYy4NCiNUbyBleGVjdXRlOiB1bmNvbW1lbnQgb3V0LCBlbnRlciBmaWxlIHBhdGgsIHJlcGxhY2UgJ1wnIHdpdGggJy8nIGFuZCBydW4uDQoNCiN3cml0ZS5jc3YoY29tYmluZWQsIltFTlRFUiBGSUxFIFBBVEggSEVSRV1jaGVzc19jb21iaW5lZC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KDQpgYGANCg==