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=