Note: analyses are based on code from Steve Rathje
Load Libraries and API Key
In order to call ChatGPT, you’ll need to generate an API key. To do
that, log into OpenAI
Platform and click on “API Keys.” Select “Create a new secret key”
and copy the code generated. That will be the key you enter here.
library(httr)
library(stringr)
library(dplyr)
library(svMisc)
library(purrr)
library(tidyverse)
my_API <- "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"
Example function
Here, we’re checking to see if we’ve successfully connected to GPT by
asking GPT why NYC pizza tastes better. Note that this might take a
minute.
chatGPT_response <- POST(
# use chatGPT website (you can copy paste)
url = "https://api.openai.com/v1/chat/completions",
# Authorize
add_headers(Authorization = paste("Bearer", my_API)),
# Output type: use JSON
content_type_json(),
# encode the value to json format
encode = "json",
# Controlling what to show as the output, it's going to be a list of following things
body = list(
model = "gpt-4-0125-preview", # GPT model
messages = list(list(role = "user", content = "Why does NYC pizza taste better?"))
)
)
# Selecting the portion we want to display
answer_one <- content(chatGPT_response)$choices[[1]]$message$content
# cleaning the selected output
answer_one <- stringr::str_trim(answer_one)
# Printing the message as a character string
cat(answer_one)
Using GPT to code a dataset
To run that same process through our data, we’re going to convert
those two functions into one function.
# Asking Questions to ChatGPT, Saving and Cleaning Answer
hey_chatGPT <- function(answer_my_question) {
chat_GPT_answer <- POST(
url = "https://api.openai.com/v1/chat/completions",
add_headers(Authorization = paste("Bearer", my_API)),
content_type_json(),
encode = "json",
body = list(
model = "gpt-4-0125-preview",
messages = list(
list(
role = "user",
content = answer_my_question
)
)
)
)
str_trim(content(chat_GPT_answer)$choices[[1]]$message$content)
}
Test our new function
response <- hey_chatGPT("What are the difference between R and Python?")
cat(response)
Real analyses
Because I want to use GPT for coding, I’m setting temperature to 0.
The higher the temp is, the more creative you’re allowing GPT to be.
This can be helpful for generating images, but it hasn’t worked well for
my purposes.
Feel free to play with this. Some people prefer 0.1-0.2.
chatGPT_0temp <- function(answer_my_question) {
chat_GPT_answer <- POST(
url = "https://api.openai.com/v1/chat/completions",
add_headers(Authorization = paste("Bearer", my_API)),
content_type_json(),
encode = "json",
body = list(
model = "gpt-4-0125-preview",
temperature = 0, # specifying 0 temp to limit gpt "creativity"
messages = list(
list(
role = "user",
content = answer_my_question
)
)
)
)
str_trim(content(chat_GPT_answer)$choices[[1]]$message$content)
}
Load the dataframe
For GPT to enter new values into your dataframe, you’ll need to first
create a blank variable to direct it where to go. I’m making three new
variables so I can compare GPT’s coding against itself. You can also
make multiple columns if you want GPT to give you multiple responses
within one dataframe.
df<- read.csv("/Users/kareenadelrosario/Desktop/morality study/miis_text_analysis/miis_clean_df_withallnegotiationoutcomes.csv")
data <- df %>%
transmute(Dyad.ID, text=negotiationLog) # selecting the two necessary variables and renaming the text column
# Create a "gpt" column
data$gpt <- NA
data$gpt2 <- NA
data$gpt3 <- NA
Coding with custom function
First I’m going to do a test run with a subset of my data.
# let's limit this to 30 dyads
data_30 <- data %>%
dplyr::slice_min(order_by = Dyad.ID, n=60) %>%
distinct(Dyad.ID, .keep_all = TRUE)
# coding for persuasiveness - gpt 1
for (i in 1:nrow(data_30)) {
print(i)
question <- "Rate persuasiveness of only the lines that start with 'A:' in the negotiation on a 1 to 7 scale. Each line starts with <br>. Answer ONLY with a number. If there are no lines that start with 'A:' write 0. To help rate from 1-7, use these guidelines:
1 out of 7 would be something like, oh I think we should go for this offer (no reasoning/argument)
4 out of 7 would be the participant restating what's on their individual role sheet. Ex: I like the global firm because since they're international, they can look at applicants from all over the world.
7 out of 7 would be the participant having their own reasoning, argument, etc. A 7 should make you think, oh that's a good argument.
Dont be afraid to use all the scale numbers -- please dont shy away from 1 and 7. Here is the text:"
text <- data_30[i, 2] # Assuming the text is in the second column of the df
concat <- paste(question, text)
result <- chatGPT_0temp(concat)
while(length(result) == 0){
result <- chatGPT_0temp(concat)
print(result)
}
print(result)
data_30$gpt[i] <- result # make sure this matches your df$variable
}
# gpt 2
for (i in 1:nrow(data_30)) {
print(i)
question <- "Rate persuasiveness of only the lines that start with 'A:' in the negotiation on a 1 to 7 scale. Each line starts with <br>. Answer ONLY with a number. If there are no lines that start with 'A:' write 0. To help rate from 1-7, use these guidelines:
1 out of 7 would be something like, oh I think we should go for this offer (no reasoning/argument)
4 out of 7 would be the participant restating what's on their individual role sheet. Ex: I like the global firm because since they're international, they can look at applicants from all over the world.
7 out of 7 would be the participant having their own reasoning, argument, etc. A 7 should make you think, oh that's a good argument.
Dont be afraid to use all the scale numbers -- please dont shy away from 1 and 7. Here is the text:"
text <- data_30[i, 2] # Assuming the text is in the second column of the df
concat <- paste(question, text)
result <- chatGPT_0temp(concat)
while(length(result) == 0){
result <- chatGPT_0temp(concat)
print(result)
}
print(result)
data_30$gpt2[i] <- result # make sure this matches your df$variable
}
# gpt 3
for (i in 1:nrow(data_30)) {
print(i)
question <- "Rate persuasiveness of only the lines that start with 'A:' in the negotiation on a 1 to 7 scale. Each line starts with <br>. Answer ONLY with a number. If there are no lines that start with 'A:' write 0. To help rate from 1-7, use these guidelines:
1 out of 7 would be something like, oh I think we should go for this offer (no reasoning/argument)
4 out of 7 would be the participant restating what's on their individual role sheet. Ex: I like the global firm because since they're international, they can look at applicants from all over the world.
7 out of 7 would be the participant having their own reasoning, argument, etc. A 7 should make you think, oh that's a good argument.
Dont be afraid to use all the scale numbers -- please dont shy away from 1 and 7. Here is the text:"
text <- data_30[i, 2] # Assuming the text is in the second column of the df
concat <- paste(question, text)
result <- chatGPT_0temp(concat)
while(length(result) == 0){
result <- chatGPT_0temp(concat)
print(result)
}
print(result)
data_30$gpt3[i] <- result # make sure this matches your df$variable
}
#write.csv(data_30, "gptdemo_data_30.csv", row.names = FALSE)
Let’s test the interreliability within ChatGPT
# remove this line in your analyses
data_30 <- read.csv("gptdemo_data_30.csv")
library(psych)
data_30 <- data_30 %>%
dplyr::mutate(across(starts_with("gpt"), ~as.numeric(as.character(.))))
icc_gpt <- ICC(data_30[,-1]) # Assuming the first column is `Dyad.ID`
print(icc_gpt)
Call: ICC(x = data_30[, -1])
Intraclass correlation coefficients
type ICC F df1 df2 p lower bound upper bound
Single_raters_absolute ICC1 0.68 7.4 29 60 4.0e-11 0.50 0.82
Single_random_raters ICC2 0.68 7.5 29 58 4.8e-11 0.51 0.82
Single_fixed_raters ICC3 0.68 7.5 29 58 4.8e-11 0.51 0.82
Average_raters_absolute ICC1k 0.87 7.4 29 60 4.0e-11 0.75 0.93
Average_random_raters ICC2k 0.87 7.5 29 58 4.8e-11 0.75 0.93
Average_fixed_raters ICC3k 0.87 7.5 29 58 4.8e-11 0.76 0.93
Number of subjects = 30 Number of Judges = 3
See the help file for a discussion of the other 4 McGraw and Wong estimates,
Referencing ICC3, we see that GPT’s ICC = 0.685, p < .001.
Now, let’s compare it to humans.
Human vs Machine
Note that the human coder ratings are averaged across two coders.
We’ll separate them for a different analysis later.
humancoders <- read.csv("humancoder_comparison_mar2024.csv") # human df
# we're still using a subset
comparison <- inner_join(humancoders, data_30, by = "Dyad.ID") %>% # combine df
select(-c(X)) %>%
dplyr::mutate(across(starts_with("gpt"), ~as.numeric(as.character(.))))
icc_results <- ICC(comparison[,-1]) # Assuming the first column is `Dyad.ID`
print(icc_results)
Call: ICC(x = comparison[, -1])
Intraclass correlation coefficients
type ICC F df1 df2 p lower bound upper bound
Single_raters_absolute ICC1 0.57 6.4 29 90 5.5e-12 0.40 0.74
Single_random_raters ICC2 0.58 6.9 29 87 1.3e-12 0.40 0.74
Single_fixed_raters ICC3 0.59 6.9 29 87 1.3e-12 0.42 0.75
Average_raters_absolute ICC1k 0.84 6.4 29 90 5.5e-12 0.73 0.92
Average_random_raters ICC2k 0.84 6.9 29 87 1.3e-12 0.73 0.92
Average_fixed_raters ICC3k 0.85 6.9 29 87 1.3e-12 0.74 0.92
Number of subjects = 30 Number of Judges = 4
See the help file for a discussion of the other 4 McGraw and Wong estimates,
Now let’s use the full datasets.
data_full <- data %>%
distinct(Dyad.ID, .keep_all = TRUE)
for (i in 1:nrow(data_full)) {
print(i)
question <- "Rate persuasiveness of only the lines that start with 'A:' in the negotiation on a 1 to 7 scale. Each line starts with <br>. Answer ONLY with a number. If there are no lines that start with 'A:' write 0. To help rate from 1-7, use these guidelines:
1 out of 7 would be something like, oh I think we should go for this offer (no reasoning/argument)
4 out of 7 would be the participant restating what's on their individual role sheet. Ex: I like the global firm because since they're international, they can look at applicants from all over the world.
7 out of 7 would be the participant having their own reasoning, argument, etc. A 7 should make you think, oh that's a good argument.
Dont be afraid to use all the scale numbers -- please dont shy away from 1 and 7. Here is the text:"
text <- data_full[i, 2] # Assuming the text is in the second column of the df
concat <- paste(question, text)
result <- chatGPT_0temp(concat)
while(length(result) == 0){
result <- chatGPT_0temp(concat)
print(result)
}
print(result)
data_full$gpt[i] <- result # make sure this matches your df$variable
}
# because I'm "knitting" this RMD, I don't want to totally rerun these analyses. I'm going to read in my output instead. Just know that I did actually run this.
# write.csv(data_full, "gptdemo_data_full.csv", row.names = FALSE)
# remove this line when you actually run the code.
data_full <- read.csv("gptdemo_data_full.csv")
# combine with humancoder data
comp_full <- inner_join(humancoders, data_full, by = "Dyad.ID") %>% # combine df
select(-c(X, text, gpt2, gpt3)) %>%
dplyr::mutate(across(starts_with("gpt"), ~as.numeric(as.character(.))))
icc_full <- ICC(comp_full[,-1])
print(icc_full)
Call: ICC(x = comp_full[, -1])
Intraclass correlation coefficients
type ICC F df1 df2 p lower bound upper bound
Single_raters_absolute ICC1 0.57 3.7 160 161 9.4e-16 0.46 0.67
Single_random_raters ICC2 0.59 4.5 160 160 7.0e-20 0.40 0.71
Single_fixed_raters ICC3 0.63 4.5 160 160 7.0e-20 0.53 0.72
Average_raters_absolute ICC1k 0.73 3.7 160 161 9.4e-16 0.63 0.80
Average_random_raters ICC2k 0.74 4.5 160 160 7.0e-20 0.57 0.83
Average_fixed_raters ICC3k 0.78 4.5 160 160 7.0e-20 0.69 0.84
Number of subjects = 161 Number of Judges = 2
See the help file for a discussion of the other 4 McGraw and Wong estimates,
When looking at the ICCs between GPT and human coders across all of
the data, we get ICC = 0.634, p < .001.
LS0tCnRpdGxlOiAiQmVoYXZpb3JhbCBDb2RpbmcgdXNpbmcgQ2hhdEdQVCIKYXV0aG9yOiAiS2FyZWVuYSBkZWwgUm9zYXJpbyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IGFycm93CiAgICB0b2M6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogZmFsc2UKLS0tCgoqTm90ZTogYW5hbHlzZXMgYXJlIGJhc2VkIG9uIGNvZGUgZnJvbSBbU3RldmUgUmF0aGplXShodHRwczovL29zZi5pby9wcmVwcmludHMvcHN5YXJ4aXYvc2VrZjUpKgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGVycm9yID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgaGlnaGxpZ2h0ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBwcm9tcHQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIFIub3B0aW9ucyA9IGxpc3Qod2lkdGggPSA5MCkpCgoKbGlicmFyeShjb25mbGljdGVkKQpjb25mbGljdF9wcmVmZXIoIm11dGF0ZSIsICJkcGx5ciIpCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIikKY29uZmxpY3RfcHJlZmVyKCJzdW1tYXJpemUiLCAiZHBseXIiKQpgYGAKCiMjIExvYWQgTGlicmFyaWVzIGFuZCBBUEkgS2V5CgpJbiBvcmRlciB0byBjYWxsIENoYXRHUFQsIHlvdSdsbCBuZWVkIHRvIGdlbmVyYXRlIGFuIEFQSSBrZXkuIFRvIGRvIHRoYXQsIGxvZyBpbnRvIFtPcGVuQUkgUGxhdGZvcm1dKGh0dHBzOi8vcGxhdGZvcm0ub3BlbmFpLmNvbS9zaWdudXApIGFuZCBjbGljayBvbiAiQVBJIEtleXMuIiBTZWxlY3QgIkNyZWF0ZSBhIG5ldyBzZWNyZXQga2V5IiBhbmQgY29weSB0aGUgY29kZSBnZW5lcmF0ZWQuIFRoYXQgd2lsbCBiZSB0aGUga2V5IHlvdSBlbnRlciBoZXJlLgoKYGBge3J9CmxpYnJhcnkoaHR0cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHN2TWlzYykKbGlicmFyeShwdXJycikKbGlicmFyeSh0aWR5dmVyc2UpCgpteV9BUEkgPC0gInNrLVEwVExPVTBFSHRPMEF1c3l6UTA2VDNCbGJrRkpkc0dKV25VbnR2RE9pSVNEdnY4aCIKYGBgCgojIyBFeGFtcGxlIGZ1bmN0aW9uCgpIZXJlLCB3ZSdyZSBjaGVja2luZyB0byBzZWUgaWYgd2UndmUgc3VjY2Vzc2Z1bGx5IGNvbm5lY3RlZCB0byBHUFQgYnkgYXNraW5nIEdQVCB3aHkgTllDIHBpenphIHRhc3RlcyBiZXR0ZXIuIE5vdGUgdGhhdCB0aGlzIG1pZ2h0IHRha2UgYSBtaW51dGUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY2hhdEdQVF9yZXNwb25zZSA8LSBQT1NUKAogICMgdXNlIGNoYXRHUFQgd2Vic2l0ZSAoeW91IGNhbiBjb3B5IHBhc3RlKQogIHVybCA9ICJodHRwczovL2FwaS5vcGVuYWkuY29tL3YxL2NoYXQvY29tcGxldGlvbnMiLAogICMgQXV0aG9yaXplCiAgYWRkX2hlYWRlcnMoQXV0aG9yaXphdGlvbiA9IHBhc3RlKCJCZWFyZXIiLCBteV9BUEkpKSwKICAjIE91dHB1dCB0eXBlOiB1c2UgSlNPTgogIGNvbnRlbnRfdHlwZV9qc29uKCksCiAgIyBlbmNvZGUgdGhlIHZhbHVlIHRvIGpzb24gZm9ybWF0CiAgZW5jb2RlID0gImpzb24iLAogICMgQ29udHJvbGxpbmcgd2hhdCB0byBzaG93IGFzIHRoZSBvdXRwdXQsIGl0J3MgZ29pbmcgdG8gYmUgYSBsaXN0IG9mIGZvbGxvd2luZyB0aGluZ3MKICBib2R5ID0gbGlzdCgKICAgIG1vZGVsID0gImdwdC00LTAxMjUtcHJldmlldyIsICMgR1BUIG1vZGVsCiAgICBtZXNzYWdlcyA9IGxpc3QobGlzdChyb2xlID0gInVzZXIiLCBjb250ZW50ID0gIldoeSBkb2VzIE5ZQyBwaXp6YSB0YXN0ZSBiZXR0ZXI/IikpCiAgKQopCgojIFNlbGVjdGluZyB0aGUgcG9ydGlvbiB3ZSB3YW50IHRvIGRpc3BsYXkKYW5zd2VyX29uZSA8LSBjb250ZW50KGNoYXRHUFRfcmVzcG9uc2UpJGNob2ljZXNbWzFdXSRtZXNzYWdlJGNvbnRlbnQKIyBjbGVhbmluZyB0aGUgc2VsZWN0ZWQgb3V0cHV0CmFuc3dlcl9vbmUgPC0gc3RyaW5ncjo6c3RyX3RyaW0oYW5zd2VyX29uZSkKIyBQcmludGluZyB0aGUgbWVzc2FnZSBhcyBhIGNoYXJhY3RlciBzdHJpbmcKY2F0KGFuc3dlcl9vbmUpCmBgYAoKCiMjIFVzaW5nIEdQVCB0byBjb2RlIGEgZGF0YXNldAoKVG8gcnVuIHRoYXQgc2FtZSBwcm9jZXNzIHRocm91Z2ggb3VyIGRhdGEsIHdlJ3JlIGdvaW5nIHRvIGNvbnZlcnQgdGhvc2UgdHdvIGZ1bmN0aW9ucyBpbnRvIG9uZSBmdW5jdGlvbi4KCmBgYHtyLCBldmFsPUZBTFNFfQojIEFza2luZyBRdWVzdGlvbnMgdG8gQ2hhdEdQVCwgU2F2aW5nIGFuZCBDbGVhbmluZyBBbnN3ZXIKaGV5X2NoYXRHUFQgPC0gZnVuY3Rpb24oYW5zd2VyX215X3F1ZXN0aW9uKSB7CiAgY2hhdF9HUFRfYW5zd2VyIDwtIFBPU1QoCiAgICB1cmwgPSAiaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS92MS9jaGF0L2NvbXBsZXRpb25zIiwKICAgIGFkZF9oZWFkZXJzKEF1dGhvcml6YXRpb24gPSBwYXN0ZSgiQmVhcmVyIiwgbXlfQVBJKSksCiAgICBjb250ZW50X3R5cGVfanNvbigpLAogICAgZW5jb2RlID0gImpzb24iLAogICAgYm9keSA9IGxpc3QoCiAgICAgIG1vZGVsID0gImdwdC00LTAxMjUtcHJldmlldyIsCiAgICAgIG1lc3NhZ2VzID0gbGlzdCgKICAgICAgICBsaXN0KAogICAgICAgICAgcm9sZSA9ICJ1c2VyIiwKICAgICAgICAgIGNvbnRlbnQgPSBhbnN3ZXJfbXlfcXVlc3Rpb24KICAgICAgICApCiAgICAgICkKICAgICkKICApCiAgc3RyX3RyaW0oY29udGVudChjaGF0X0dQVF9hbnN3ZXIpJGNob2ljZXNbWzFdXSRtZXNzYWdlJGNvbnRlbnQpCn0KCmBgYAoKIyMgVGVzdCBvdXIgbmV3IGZ1bmN0aW9uCgpgYGB7ciwgZXZhbD1GQUxTRX0KcmVzcG9uc2UgPC0gaGV5X2NoYXRHUFQoIldoYXQgYXJlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gUiBhbmQgUHl0aG9uPyIpCmNhdChyZXNwb25zZSkKYGBgCgojIFJlYWwgYW5hbHlzZXMKCkJlY2F1c2UgSSB3YW50IHRvIHVzZSBHUFQgZm9yIGNvZGluZywgSSdtIHNldHRpbmcgdGVtcGVyYXR1cmUgdG8gMC4gVGhlIGhpZ2hlciB0aGUgdGVtcCBpcywgdGhlIG1vcmUgY3JlYXRpdmUgeW91J3JlIGFsbG93aW5nIEdQVCB0byBiZS4gVGhpcyBjYW4gYmUgaGVscGZ1bCBmb3IgZ2VuZXJhdGluZyBpbWFnZXMsIGJ1dCBpdCBoYXNuJ3Qgd29ya2VkIHdlbGwgZm9yIG15IHB1cnBvc2VzLiAKCkZlZWwgZnJlZSB0byBwbGF5IHdpdGggdGhpcy4gU29tZSBwZW9wbGUgcHJlZmVyIDAuMS0wLjIuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY2hhdEdQVF8wdGVtcCA8LSBmdW5jdGlvbihhbnN3ZXJfbXlfcXVlc3Rpb24pIHsKICBjaGF0X0dQVF9hbnN3ZXIgPC0gUE9TVCgKICAgIHVybCA9ICJodHRwczovL2FwaS5vcGVuYWkuY29tL3YxL2NoYXQvY29tcGxldGlvbnMiLAogICAgYWRkX2hlYWRlcnMoQXV0aG9yaXphdGlvbiA9IHBhc3RlKCJCZWFyZXIiLCBteV9BUEkpKSwKICAgIGNvbnRlbnRfdHlwZV9qc29uKCksCiAgICBlbmNvZGUgPSAianNvbiIsCiAgICBib2R5ID0gbGlzdCgKICAgICAgbW9kZWwgPSAiZ3B0LTQtMDEyNS1wcmV2aWV3IiwgIAogICAgICB0ZW1wZXJhdHVyZSA9IDAsICAjIHNwZWNpZnlpbmcgMCB0ZW1wIHRvIGxpbWl0IGdwdCAiY3JlYXRpdml0eSIKCiAgICAgIG1lc3NhZ2VzID0gbGlzdCgKICAgICAgICBsaXN0KAogICAgICAgICAgcm9sZSA9ICJ1c2VyIiwKICAgICAgICAgIGNvbnRlbnQgPSBhbnN3ZXJfbXlfcXVlc3Rpb24KICAgICAgICApCiAgICAgICkKICAgICkKICApCiBzdHJfdHJpbShjb250ZW50KGNoYXRfR1BUX2Fuc3dlcikkY2hvaWNlc1tbMV1dJG1lc3NhZ2UkY29udGVudCkKfQoKCgpgYGAKCiMjIExvYWQgdGhlIGRhdGFmcmFtZQoKRm9yIEdQVCB0byBlbnRlciBuZXcgdmFsdWVzIGludG8geW91ciBkYXRhZnJhbWUsIHlvdSdsbCBuZWVkIHRvIGZpcnN0IGNyZWF0ZSBhIGJsYW5rIHZhcmlhYmxlIHRvIGRpcmVjdCBpdCB3aGVyZSB0byBnby4gSSdtIG1ha2luZyB0aHJlZSBuZXcgdmFyaWFibGVzIHNvIEkgY2FuIGNvbXBhcmUgR1BUJ3MgY29kaW5nIGFnYWluc3QgaXRzZWxmLiBZb3UgY2FuIGFsc28gbWFrZSBtdWx0aXBsZSBjb2x1bW5zIGlmIHlvdSB3YW50IEdQVCB0byBnaXZlIHlvdSBtdWx0aXBsZSByZXNwb25zZXMgd2l0aGluIG9uZSBkYXRhZnJhbWUuCgpgYGB7cn0KZGY8LSByZWFkLmNzdigiL1VzZXJzL2thcmVlbmFkZWxyb3NhcmlvL0Rlc2t0b3AvbW9yYWxpdHkgc3R1ZHkvbWlpc190ZXh0X2FuYWx5c2lzL21paXNfY2xlYW5fZGZfd2l0aGFsbG5lZ290aWF0aW9ub3V0Y29tZXMuY3N2IikKCmRhdGEgPC0gZGYgJT4lIAogIHRyYW5zbXV0ZShEeWFkLklELCB0ZXh0PW5lZ290aWF0aW9uTG9nKSAjIHNlbGVjdGluZyB0aGUgdHdvIG5lY2Vzc2FyeSB2YXJpYWJsZXMgYW5kIHJlbmFtaW5nIHRoZSB0ZXh0IGNvbHVtbgoKIyBDcmVhdGUgYSAiZ3B0IiBjb2x1bW4KZGF0YSRncHQgPC0gTkEKZGF0YSRncHQyIDwtIE5BCmRhdGEkZ3B0MyA8LSBOQQpgYGAKCgojIyBDb2Rpbmcgd2l0aCBjdXN0b20gZnVuY3Rpb24KCkZpcnN0IEknbSBnb2luZyB0byBkbyBhIHRlc3QgcnVuIHdpdGggYSBzdWJzZXQgb2YgbXkgZGF0YS4KCmBgYHtyLCBldmFsPUZBTFNFfQojIGxldCdzIGxpbWl0IHRoaXMgdG8gMzAgZHlhZHMKZGF0YV8zMCA8LSBkYXRhICU+JSAKICBkcGx5cjo6c2xpY2VfbWluKG9yZGVyX2J5ID0gRHlhZC5JRCwgbj02MCkgJT4lIAogIGRpc3RpbmN0KER5YWQuSUQsIC5rZWVwX2FsbCA9IFRSVUUpCgoKIyBjb2RpbmcgZm9yIHBlcnN1YXNpdmVuZXNzIC0gZ3B0IDEgICAgCiAgIGZvciAoaSBpbiAxOm5yb3coZGF0YV8zMCkpIHsKICBwcmludChpKQogIHF1ZXN0aW9uIDwtICJSYXRlIHBlcnN1YXNpdmVuZXNzIG9mIG9ubHkgdGhlIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCAnQTonIGluIHRoZSBuZWdvdGlhdGlvbiBvbiBhIDEgdG8gNyBzY2FsZS4gRWFjaCBsaW5lIHN0YXJ0cyB3aXRoIDxicj4uIEFuc3dlciBPTkxZIHdpdGggYSBudW1iZXIuIElmIHRoZXJlIGFyZSBubyBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggJ0E6JyB3cml0ZSAwLiBUbyBoZWxwIHJhdGUgZnJvbSAxLTcsIHVzZSB0aGVzZSBndWlkZWxpbmVzOgoxIG91dCBvZiA3IHdvdWxkIGJlIHNvbWV0aGluZyBsaWtlLCBvaCBJIHRoaW5rIHdlIHNob3VsZCBnbyBmb3IgdGhpcyBvZmZlciAobm8gcmVhc29uaW5nL2FyZ3VtZW50KQo0IG91dCBvZiA3IHdvdWxkIGJlIHRoZSBwYXJ0aWNpcGFudCByZXN0YXRpbmcgd2hhdCdzIG9uIHRoZWlyIGluZGl2aWR1YWwgcm9sZSBzaGVldC4gRXg6IEkgbGlrZSB0aGUgZ2xvYmFsIGZpcm0gYmVjYXVzZSBzaW5jZSB0aGV5J3JlIGludGVybmF0aW9uYWwsIHRoZXkgY2FuIGxvb2sgYXQgYXBwbGljYW50cyBmcm9tIGFsbCBvdmVyIHRoZSB3b3JsZC4KNyBvdXQgb2YgNyB3b3VsZCBiZSB0aGUgcGFydGljaXBhbnQgaGF2aW5nIHRoZWlyIG93biByZWFzb25pbmcsIGFyZ3VtZW50LCBldGMuIEEgNyBzaG91bGQgbWFrZSB5b3UgdGhpbmssIG9oIHRoYXQncyBhIGdvb2QgYXJndW1lbnQuIApEb250IGJlIGFmcmFpZCB0byB1c2UgYWxsIHRoZSBzY2FsZSBudW1iZXJzIC0tIHBsZWFzZSBkb250IHNoeSBhd2F5IGZyb20gMSBhbmQgNy4gIEhlcmUgaXMgdGhlIHRleHQ6IgogIHRleHQgPC0gZGF0YV8zMFtpLCAyXSAgIyBBc3N1bWluZyB0aGUgdGV4dCBpcyBpbiB0aGUgc2Vjb25kIGNvbHVtbiBvZiB0aGUgZGYKICBjb25jYXQgPC0gcGFzdGUocXVlc3Rpb24sIHRleHQpCiAgcmVzdWx0IDwtIGNoYXRHUFRfMHRlbXAoY29uY2F0KQp3aGlsZShsZW5ndGgocmVzdWx0KSA9PSAwKXsKICAgIHJlc3VsdCA8LSBjaGF0R1BUXzB0ZW1wKGNvbmNhdCkKICAgIHByaW50KHJlc3VsdCkKICB9CiAgIHByaW50KHJlc3VsdCkgCiAgZGF0YV8zMCRncHRbaV0gPC0gcmVzdWx0ICMgbWFrZSBzdXJlIHRoaXMgbWF0Y2hlcyB5b3VyIGRmJHZhcmlhYmxlCiAgIH0KCiMgZ3B0IDIKICAgZm9yIChpIGluIDE6bnJvdyhkYXRhXzMwKSkgewogIHByaW50KGkpCiAgcXVlc3Rpb24gPC0gIlJhdGUgcGVyc3Vhc2l2ZW5lc3Mgb2Ygb25seSB0aGUgbGluZXMgdGhhdCBzdGFydCB3aXRoICdBOicgaW4gdGhlIG5lZ290aWF0aW9uIG9uIGEgMSB0byA3IHNjYWxlLiBFYWNoIGxpbmUgc3RhcnRzIHdpdGggPGJyPi4gQW5zd2VyIE9OTFkgd2l0aCBhIG51bWJlci4gSWYgdGhlcmUgYXJlIG5vIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCAnQTonIHdyaXRlIDAuIFRvIGhlbHAgcmF0ZSBmcm9tIDEtNywgdXNlIHRoZXNlIGd1aWRlbGluZXM6CjEgb3V0IG9mIDcgd291bGQgYmUgc29tZXRoaW5nIGxpa2UsIG9oIEkgdGhpbmsgd2Ugc2hvdWxkIGdvIGZvciB0aGlzIG9mZmVyIChubyByZWFzb25pbmcvYXJndW1lbnQpCjQgb3V0IG9mIDcgd291bGQgYmUgdGhlIHBhcnRpY2lwYW50IHJlc3RhdGluZyB3aGF0J3Mgb24gdGhlaXIgaW5kaXZpZHVhbCByb2xlIHNoZWV0LiBFeDogSSBsaWtlIHRoZSBnbG9iYWwgZmlybSBiZWNhdXNlIHNpbmNlIHRoZXkncmUgaW50ZXJuYXRpb25hbCwgdGhleSBjYW4gbG9vayBhdCBhcHBsaWNhbnRzIGZyb20gYWxsIG92ZXIgdGhlIHdvcmxkLgo3IG91dCBvZiA3IHdvdWxkIGJlIHRoZSBwYXJ0aWNpcGFudCBoYXZpbmcgdGhlaXIgb3duIHJlYXNvbmluZywgYXJndW1lbnQsIGV0Yy4gQSA3IHNob3VsZCBtYWtlIHlvdSB0aGluaywgb2ggdGhhdCdzIGEgZ29vZCBhcmd1bWVudC4gCkRvbnQgYmUgYWZyYWlkIHRvIHVzZSBhbGwgdGhlIHNjYWxlIG51bWJlcnMgLS0gcGxlYXNlIGRvbnQgc2h5IGF3YXkgZnJvbSAxIGFuZCA3LiAgSGVyZSBpcyB0aGUgdGV4dDoiCiAgdGV4dCA8LSBkYXRhXzMwW2ksIDJdICAjIEFzc3VtaW5nIHRoZSB0ZXh0IGlzIGluIHRoZSBzZWNvbmQgY29sdW1uIG9mIHRoZSBkZgogIGNvbmNhdCA8LSBwYXN0ZShxdWVzdGlvbiwgdGV4dCkKICByZXN1bHQgPC0gY2hhdEdQVF8wdGVtcChjb25jYXQpCndoaWxlKGxlbmd0aChyZXN1bHQpID09IDApewogICAgcmVzdWx0IDwtIGNoYXRHUFRfMHRlbXAoY29uY2F0KQogICAgcHJpbnQocmVzdWx0KQogIH0KICAgcHJpbnQocmVzdWx0KSAKICBkYXRhXzMwJGdwdDJbaV0gPC0gcmVzdWx0ICMgbWFrZSBzdXJlIHRoaXMgbWF0Y2hlcyB5b3VyIGRmJHZhcmlhYmxlCiAgIH0KCiMgZ3B0IDMKICAgZm9yIChpIGluIDE6bnJvdyhkYXRhXzMwKSkgewogIHByaW50KGkpCiAgcXVlc3Rpb24gPC0gIlJhdGUgcGVyc3Vhc2l2ZW5lc3Mgb2Ygb25seSB0aGUgbGluZXMgdGhhdCBzdGFydCB3aXRoICdBOicgaW4gdGhlIG5lZ290aWF0aW9uIG9uIGEgMSB0byA3IHNjYWxlLiBFYWNoIGxpbmUgc3RhcnRzIHdpdGggPGJyPi4gQW5zd2VyIE9OTFkgd2l0aCBhIG51bWJlci4gSWYgdGhlcmUgYXJlIG5vIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCAnQTonIHdyaXRlIDAuIFRvIGhlbHAgcmF0ZSBmcm9tIDEtNywgdXNlIHRoZXNlIGd1aWRlbGluZXM6CjEgb3V0IG9mIDcgd291bGQgYmUgc29tZXRoaW5nIGxpa2UsIG9oIEkgdGhpbmsgd2Ugc2hvdWxkIGdvIGZvciB0aGlzIG9mZmVyIChubyByZWFzb25pbmcvYXJndW1lbnQpCjQgb3V0IG9mIDcgd291bGQgYmUgdGhlIHBhcnRpY2lwYW50IHJlc3RhdGluZyB3aGF0J3Mgb24gdGhlaXIgaW5kaXZpZHVhbCByb2xlIHNoZWV0LiBFeDogSSBsaWtlIHRoZSBnbG9iYWwgZmlybSBiZWNhdXNlIHNpbmNlIHRoZXkncmUgaW50ZXJuYXRpb25hbCwgdGhleSBjYW4gbG9vayBhdCBhcHBsaWNhbnRzIGZyb20gYWxsIG92ZXIgdGhlIHdvcmxkLgo3IG91dCBvZiA3IHdvdWxkIGJlIHRoZSBwYXJ0aWNpcGFudCBoYXZpbmcgdGhlaXIgb3duIHJlYXNvbmluZywgYXJndW1lbnQsIGV0Yy4gQSA3IHNob3VsZCBtYWtlIHlvdSB0aGluaywgb2ggdGhhdCdzIGEgZ29vZCBhcmd1bWVudC4gCkRvbnQgYmUgYWZyYWlkIHRvIHVzZSBhbGwgdGhlIHNjYWxlIG51bWJlcnMgLS0gcGxlYXNlIGRvbnQgc2h5IGF3YXkgZnJvbSAxIGFuZCA3LiAgSGVyZSBpcyB0aGUgdGV4dDoiCiAgdGV4dCA8LSBkYXRhXzMwW2ksIDJdICAjIEFzc3VtaW5nIHRoZSB0ZXh0IGlzIGluIHRoZSBzZWNvbmQgY29sdW1uIG9mIHRoZSBkZgogIGNvbmNhdCA8LSBwYXN0ZShxdWVzdGlvbiwgdGV4dCkKICByZXN1bHQgPC0gY2hhdEdQVF8wdGVtcChjb25jYXQpCndoaWxlKGxlbmd0aChyZXN1bHQpID09IDApewogICAgcmVzdWx0IDwtIGNoYXRHUFRfMHRlbXAoY29uY2F0KQogICAgcHJpbnQocmVzdWx0KQogIH0KICAgcHJpbnQocmVzdWx0KSAKICBkYXRhXzMwJGdwdDNbaV0gPC0gcmVzdWx0ICMgbWFrZSBzdXJlIHRoaXMgbWF0Y2hlcyB5b3VyIGRmJHZhcmlhYmxlCiAgIH0KCiN3cml0ZS5jc3YoZGF0YV8zMCwgImdwdGRlbW9fZGF0YV8zMC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgojIyMgTGV0J3MgdGVzdCB0aGUgaW50ZXJyZWxpYWJpbGl0eSB3aXRoaW4gQ2hhdEdQVApgYGB7cn0KIyByZW1vdmUgdGhpcyBsaW5lIGluIHlvdXIgYW5hbHlzZXMKZGF0YV8zMCA8LSByZWFkLmNzdigiZ3B0ZGVtb19kYXRhXzMwLmNzdiIpCgpsaWJyYXJ5KHBzeWNoKQoKZGF0YV8zMCA8LSBkYXRhXzMwICU+JSAKICAgZHBseXI6Om11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoImdwdCIpLCB+YXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoLikpKSkgCgppY2NfZ3B0IDwtIElDQyhkYXRhXzMwWywtMV0pICAjIEFzc3VtaW5nIHRoZSBmaXJzdCBjb2x1bW4gaXMgYER5YWQuSURgCgpwcmludChpY2NfZ3B0KQpgYGAKClJlZmVyZW5jaW5nIElDQzMsIHdlIHNlZSB0aGF0IEdQVCdzIElDQyA9IDAuNjg1LCBwIDwgLjAwMS4KCk5vdywgbGV0J3MgY29tcGFyZSBpdCB0byBodW1hbnMuCgojIyBIdW1hbiB2cyBNYWNoaW5lCgpOb3RlIHRoYXQgdGhlIGh1bWFuIGNvZGVyIHJhdGluZ3MgYXJlIGF2ZXJhZ2VkIGFjcm9zcyB0d28gY29kZXJzLiBXZSdsbCBzZXBhcmF0ZSB0aGVtIGZvciBhIGRpZmZlcmVudCBhbmFseXNpcyBsYXRlci4gCgpgYGB7cn0KaHVtYW5jb2RlcnMgPC0gcmVhZC5jc3YoImh1bWFuY29kZXJfY29tcGFyaXNvbl9tYXIyMDI0LmNzdiIpICMgaHVtYW4gZGYKCiMgd2UncmUgc3RpbGwgdXNpbmcgYSBzdWJzZXQKY29tcGFyaXNvbiA8LSBpbm5lcl9qb2luKGh1bWFuY29kZXJzLCBkYXRhXzMwLCBieSA9ICJEeWFkLklEIikgJT4lICMgY29tYmluZSBkZgogIHNlbGVjdCgtYyhYKSkgJT4lIAogZHBseXI6Om11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoImdwdCIpLCB+YXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoLikpKSkKCmljY19yZXN1bHRzIDwtIElDQyhjb21wYXJpc29uWywtMV0pICAjIEFzc3VtaW5nIHRoZSBmaXJzdCBjb2x1bW4gaXMgYER5YWQuSURgCgpwcmludChpY2NfcmVzdWx0cykKYGBgCgpOb3cgbGV0J3MgdXNlIHRoZSBmdWxsIGRhdGFzZXRzLgoKYGBge3IgZXZhbD1GQUxTRX0KZGF0YV9mdWxsIDwtIGRhdGEgJT4lIAogIGRpc3RpbmN0KER5YWQuSUQsIC5rZWVwX2FsbCA9IFRSVUUpCgogICBmb3IgKGkgaW4gMTpucm93KGRhdGFfZnVsbCkpIHsKICBwcmludChpKQogIHF1ZXN0aW9uIDwtICJSYXRlIHBlcnN1YXNpdmVuZXNzIG9mIG9ubHkgdGhlIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCAnQTonIGluIHRoZSBuZWdvdGlhdGlvbiBvbiBhIDEgdG8gNyBzY2FsZS4gRWFjaCBsaW5lIHN0YXJ0cyB3aXRoIDxicj4uIEFuc3dlciBPTkxZIHdpdGggYSBudW1iZXIuIElmIHRoZXJlIGFyZSBubyBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggJ0E6JyB3cml0ZSAwLiBUbyBoZWxwIHJhdGUgZnJvbSAxLTcsIHVzZSB0aGVzZSBndWlkZWxpbmVzOgoxIG91dCBvZiA3IHdvdWxkIGJlIHNvbWV0aGluZyBsaWtlLCBvaCBJIHRoaW5rIHdlIHNob3VsZCBnbyBmb3IgdGhpcyBvZmZlciAobm8gcmVhc29uaW5nL2FyZ3VtZW50KQo0IG91dCBvZiA3IHdvdWxkIGJlIHRoZSBwYXJ0aWNpcGFudCByZXN0YXRpbmcgd2hhdCdzIG9uIHRoZWlyIGluZGl2aWR1YWwgcm9sZSBzaGVldC4gRXg6IEkgbGlrZSB0aGUgZ2xvYmFsIGZpcm0gYmVjYXVzZSBzaW5jZSB0aGV5J3JlIGludGVybmF0aW9uYWwsIHRoZXkgY2FuIGxvb2sgYXQgYXBwbGljYW50cyBmcm9tIGFsbCBvdmVyIHRoZSB3b3JsZC4KNyBvdXQgb2YgNyB3b3VsZCBiZSB0aGUgcGFydGljaXBhbnQgaGF2aW5nIHRoZWlyIG93biByZWFzb25pbmcsIGFyZ3VtZW50LCBldGMuIEEgNyBzaG91bGQgbWFrZSB5b3UgdGhpbmssIG9oIHRoYXQncyBhIGdvb2QgYXJndW1lbnQuIApEb250IGJlIGFmcmFpZCB0byB1c2UgYWxsIHRoZSBzY2FsZSBudW1iZXJzIC0tIHBsZWFzZSBkb250IHNoeSBhd2F5IGZyb20gMSBhbmQgNy4gIEhlcmUgaXMgdGhlIHRleHQ6IgogIHRleHQgPC0gZGF0YV9mdWxsW2ksIDJdICAjIEFzc3VtaW5nIHRoZSB0ZXh0IGlzIGluIHRoZSBzZWNvbmQgY29sdW1uIG9mIHRoZSBkZgogIGNvbmNhdCA8LSBwYXN0ZShxdWVzdGlvbiwgdGV4dCkKICByZXN1bHQgPC0gY2hhdEdQVF8wdGVtcChjb25jYXQpCndoaWxlKGxlbmd0aChyZXN1bHQpID09IDApewogICAgcmVzdWx0IDwtIGNoYXRHUFRfMHRlbXAoY29uY2F0KQogICAgcHJpbnQocmVzdWx0KQogIH0KICAgcHJpbnQocmVzdWx0KSAKICBkYXRhX2Z1bGwkZ3B0W2ldIDwtIHJlc3VsdCAjIG1ha2Ugc3VyZSB0aGlzIG1hdGNoZXMgeW91ciBkZiR2YXJpYWJsZQogICB9CgojIGJlY2F1c2UgSSdtICJrbml0dGluZyIgdGhpcyBSTUQsIEkgZG9uJ3Qgd2FudCB0byB0b3RhbGx5IHJlcnVuIHRoZXNlIGFuYWx5c2VzLiBJJ20gZ29pbmcgdG8gcmVhZCBpbiBteSBvdXRwdXQgaW5zdGVhZC4gSnVzdCBrbm93IHRoYXQgSSBkaWQgYWN0dWFsbHkgcnVuIHRoaXMuCgojIHdyaXRlLmNzdihkYXRhX2Z1bGwsICJncHRkZW1vX2RhdGFfZnVsbC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgpgYGB7cn0KIyByZW1vdmUgdGhpcyBsaW5lIHdoZW4geW91IGFjdHVhbGx5IHJ1biB0aGUgY29kZS4KZGF0YV9mdWxsIDwtIHJlYWQuY3N2KCJncHRkZW1vX2RhdGFfZnVsbC5jc3YiKQoKIyBjb21iaW5lIHdpdGggaHVtYW5jb2RlciBkYXRhCmNvbXBfZnVsbCA8LSBpbm5lcl9qb2luKGh1bWFuY29kZXJzLCBkYXRhX2Z1bGwsIGJ5ID0gIkR5YWQuSUQiKSAlPiUgIyBjb21iaW5lIGRmCiAgc2VsZWN0KC1jKFgsIHRleHQsIGdwdDIsIGdwdDMpKSAlPiUgCiBkcGx5cjo6bXV0YXRlKGFjcm9zcyhzdGFydHNfd2l0aCgiZ3B0IiksIH5hcy5udW1lcmljKGFzLmNoYXJhY3RlciguKSkpKQoKaWNjX2Z1bGwgPC0gSUNDKGNvbXBfZnVsbFssLTFdKSAgCgpwcmludChpY2NfZnVsbCkKYGBgCgpXaGVuIGxvb2tpbmcgYXQgdGhlIElDQ3MgYmV0d2VlbiBHUFQgYW5kIGh1bWFuIGNvZGVycyBhY3Jvc3MgYWxsIG9mIHRoZSBkYXRhLCB3ZSBnZXQgSUNDID0gMC42MzQsIHAgPCAuMDAxLgo=