This analysis evaluates Fonterra’s social-media content and user engagement across its official social-media platforms. The analysis is completed entirely in R, with data preparation, text analysis, topic modelling, exploratory analysis, and bivariate statistical analysis conducted in the order presented below.

Install and load packages and import and preview data

# Set global chunk options so the knitted report is tidy and readable
knitr::opts_chunk$set(message = FALSE, warning = FALSE, fig.width = 8, fig.height = 5)
options(scipen = 999)

# Install and load packages
if(!require("pacman")) install.packages("pacman")
## Loading required package: pacman
pacman::p_load(readr, dplyr, tidyr, stringr, lubridate, purrr, ggplot2, gridExtra, GGally, corrplot)
pacman::p_load(tidytext, textstem, wordcloud, textdata, topicmodels)
pacman::p_load(DescTools, skimr, finalfit, rstatix, huxtable, jtools)

# Import the original dataset. The CSV is not modified manually outside R.
fonterra_raw <- read_csv("fonterra.csv", show_col_types = FALSE)
fonterra <- fonterra_raw

# Preview the structure and a concise sample of the data
glimpse(fonterra)
## Rows: 9,001
## Columns: 9
## $ platform       <chr> "Twitter/X", "Twitter/X", "Twitter/X", "Twitter/X", "Tw…
## $ url            <chr> "https://x.com/Fonterra/status/1891573780557988062", "h…
## $ createdAt      <chr> "2025/02/17 19:41:47", "2024/12/05 22:23:33", "2024/12/…
## $ text           <chr> "Today we announced new incentives to help farms reduce…
## $ media          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ commentCount   <dbl> 66, 1, 1, 303, 8, 2, 2, 0, 2, 0, 1, 1, 1, 0, 0, 1, 0, 0…
## $ shareCount     <dbl> 2, 0, 0, 102, 3, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 1, 0, 0,…
## $ likeCount      <dbl> 15, 0, 0, 704, 16, 1, 6, 0, 19, 0, 16, 1, 3, 18, 2, 14,…
## $ videoplayCount <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
fonterra |>
  mutate(text_preview = str_trunc(text, 80)) |>
  select(platform, createdAt, media, commentCount, shareCount, likeCount,
         videoplayCount, text_preview) |>
  slice_head(n = 6) |>
  knitr::kable(caption = "Preview of the Fonterra social-media dataset")
Preview of the Fonterra social-media dataset
platform createdAt media commentCount shareCount likeCount videoplayCount text_preview
Twitter/X 2025/02/17 19:41:47 NA 66 2 15 NA Today we announced new incentives to help farms reduce emissions.

“We’re gro… | |Twitter/X |2024/12/05 22:23:33 |NA | 1| 0| 0| _ansaar Hi there, please reach out to us at . Our … | |Twitter/X |2024/12/05 21:05:34 |NA | 1| 0| 0| _ansaar Hi there, could you please DM us with some information about wh… | |Twitter/X |2024/12/01 23:04:24 |NA | 303| 102| 704| NA|At Fonterra, food safety and quality is our number one priority.

Bovaer has … | |Twitter/X |2024/11/10 19:39:51 |NA | 8| 3| 16| NA|We’ve lifted our forecast Farmgate Milk Price midpoint for the season by 50c … | |Twitter/X |2024/10/10 19:36:23 |NA | 2| 0| 1| NA|Today we announced a new Appointed Director, Alistair Field, who will join th… |

The dataset contains 9001 social-media records and 9 original variables. The records cover Twitter/X, Facebook, Instagram, YouTube, TikTok, and LinkedIn.

Data preparation

Non-text data

Brief notes on judgements applied

  • The createdAt variable was converted from character format to a UTC date-time so that dates and time-based variables could be analysed correctly.
  • Exact duplicate rows and duplicate URLs were checked before analysis. No exact duplicate rows or duplicate URLs were found, so no records were removed on this basis. Repeated text was retained when it appeared under a different URL or platform because cross-posting and repeated organisational messages can be legitimate social-media activity.
  • The media field used different labels for equivalent formats. Photo, Image, and image were standardised to Image; Video and video were standardised to Video; and Sidecar was retained as Sidecar.
  • Missing media, comment, share, and video-play values were not automatically replaced with zero. Missingness is strongly platform-dependent, so a missing value may indicate that the metric was unavailable or not extracted rather than that the true value was zero. Treating all missing values as zero could bias comparisons.
  • Engagement-count variables were checked for impossible negative values. None were found.
  • Missing or blank text was retained in the main dataset because those records may still be relevant to non-text engagement analysis; such records are excluded only from the text-analysis dataset.
# Summarise missing data before making analytical decisions
missing_summary <- fonterra |>
  summarise(across(everything(), ~ sum(is.na(.)))) |>
  pivot_longer(everything(), names_to = "variable", values_to = "missing_n") |>
  mutate(missing_pct = round(100 * missing_n / nrow(fonterra), 2))

missing_summary |>
  knitr::kable(caption = "Missing values in the original dataset")
Missing values in the original dataset
variable missing_n missing_pct
platform 0 0.00
url 0 0.00
createdAt 0 0.00
text 82 0.91
media 6994 77.70
commentCount 293 3.26
shareCount 951 10.57
likeCount 0 0.00
videoplayCount 8115 90.16
# Check exact duplicates, duplicate URLs, and repeated text/timestamp combinations
duplicate_summary <- tibble::tibble(
  check = c("Exact duplicate rows",
            "Duplicate URLs",
            "Repeated text at the same timestamp"),
  n = c(sum(duplicated(fonterra)),
        sum(duplicated(fonterra$url)),
        sum(duplicated(fonterra[c("createdAt", "text")])))
)

duplicate_summary |>
  knitr::kable(caption = "Duplicate checks")
Duplicate checks
check n
Exact duplicate rows 0
Duplicate URLs 0
Repeated text at the same timestamp 1
# Convert the timestamp to a proper UTC date-time and standardise platform text
fonterra <- fonterra |>
  mutate(
    createdAt = ymd_hms(createdAt, tz = "UTC", quiet = TRUE),
    platform = str_squish(platform),
    media_clean = case_when(
      str_to_lower(media) %in% c("photo", "image") ~ "Image",
      str_to_lower(media) == "video" ~ "Video",
      str_to_lower(media) == "sidecar" ~ "Sidecar",
      TRUE ~ NA_character_
    )
  )

# Check whether engagement counts contain impossible negative values
negative_value_check <- fonterra |>
  summarise(across(c(commentCount, shareCount, likeCount, videoplayCount),
                   ~ sum(. < 0, na.rm = TRUE))) |>
  pivot_longer(everything(), names_to = "variable", values_to = "negative_values")

negative_value_check |>
  knitr::kable(caption = "Check for negative engagement counts")
Check for negative engagement counts
variable negative_values
commentCount 0
shareCount 0
likeCount 0
videoplayCount 0
# Show missing engagement values by platform to assess whether missingness is systematic
missing_by_platform <- fonterra |>
  group_by(platform) |>
  summarise(
    posts = n(),
    missing_comments = sum(is.na(commentCount)),
    missing_shares = sum(is.na(shareCount)),
    missing_video_plays = sum(is.na(videoplayCount)),
    missing_media = sum(is.na(media)),
    .groups = "drop"
  )

missing_by_platform |>
  knitr::kable(caption = "Missing engagement and media data by platform")
Missing engagement and media data by platform
platform posts missing_comments missing_shares missing_video_plays missing_media
Facebook 1433 269 0 1115 680
Instagram 519 0 519 454 0
LinkedIn 326 24 37 326 94
TikTok 108 0 0 0 0
Twitter/X 6220 0 0 6220 6220
YouTube 395 0 395 0 0

The missing-value table confirms that missingness is not random across the variables. For example, share counts are unavailable for some platforms and video-play counts are available only for a subset of posts. Therefore, the analysis uses available observations for each metric and avoids interpreting unavailable values as zero.

New variables created

Brief notes on why the variables could be useful

  • document_id gives every social-media record a unique identifier and is needed to connect topic-modelling results back to the original dataset.
  • post_date, year, month, and day_of_week support time-based analysis and make it possible to examine changes in posting or engagement patterns over time.
  • word_count measures post/caption length and can be used to investigate whether text length is associated with engagement.
  • post_age_days measures the age of a post relative to the most recent post in the dataset. This is useful because engagement counts are cumulative and posts cover a long time period.
  • complete_engagement identifies rows where likes, comments, and shares are all observed. total_engagement is calculated only for these complete rows, preventing unavailable metrics from being incorrectly treated as zero.
  • log_likes is a transformed version of likes used only for visualisation. Social-media engagement is highly right-skewed, so log1p() makes patterns and extreme values easier to display while retaining posts with zero likes.
# Create identifiers, date-based variables, text-length measures, and engagement variables
fonterra <- fonterra |>
  mutate(
    document_id = row_number(),
    post_date = as.Date(createdAt),
    year = year(createdAt),
    month = month(createdAt, label = TRUE, abbr = TRUE),
    day_of_week = wday(createdAt, label = TRUE, abbr = FALSE, week_start = 1),
    word_count = str_count(str_squish(coalesce(text, "")), "\\S+"),
    post_age_days = as.integer(max(post_date, na.rm = TRUE) - post_date),
    complete_engagement = !is.na(commentCount) & !is.na(shareCount),
    total_engagement = if_else(
      complete_engagement,
      likeCount + commentCount + shareCount,
      NA_real_
    ),
    log_likes = log1p(likeCount)
  )

# Summarise the newly created variables
new_variable_summary <- fonterra |>
  summarise(
    earliest_post = min(post_date, na.rm = TRUE),
    latest_post = max(post_date, na.rm = TRUE),
    median_word_count = median(word_count, na.rm = TRUE),
    complete_engagement_rows = sum(complete_engagement),
    rows_with_total_engagement = sum(!is.na(total_engagement))
  )

new_variable_summary |>
  knitr::kable(caption = "Summary of selected new variables")
Summary of selected new variables
earliest_post latest_post median_word_count complete_engagement_rows rows_with_total_engagement
2010-11-08 2026-07-28 22 7763 7763

Text data

For term-frequency analysis and topic modelling, the text was converted into individual word tokens. URLs, punctuation, numbers, standard English stop words, and a small set of high-frequency but low-information organisation, location, temporal, and conversational terms were removed. Words were lemmatised so that common inflected forms were brought closer to a shared base form. This reduces noise and makes the resulting themes easier to interpret.

# Create a small custom stop-word list after reviewing high-frequency low-information terms
custom_stop_words <- tibble::tibble(
  word = c("fonterra", "zealand", "nz", "https", "http", "www", "com", "amp",
           "year", "years", "today", "day", "week", "time", "just", "really",
           "good", "great", "like", "know", "thanks", "yeah")
)

# Combine standard English stop words with the custom list
all_stop_words <- bind_rows(
  tidytext::stop_words |> select(word),
  custom_stop_words
) |>
  distinct(word)

# Prepare text, remove URLs, tokenize words, lemmatise, and remove stop words
text_tokens <- fonterra |>
  filter(!is.na(text), str_squish(text) != "") |>
  select(document_id, platform, text) |>
  mutate(
    text_clean = str_replace_all(text, "https?://\\S+|www\\.\\S+", " "),
    text_clean = str_replace_all(text_clean, "@[[:alnum:]_]+", " ")
  ) |>
  tidytext::unnest_tokens(word, text_clean) |>
  filter(str_detect(word, "^[a-z]+$"), nchar(word) > 2) |>
  mutate(word = textstem::lemmatize_words(word)) |>
  anti_join(all_stop_words, by = "word") |>
  filter(!is.na(word), word != "")

# Check how many documents and tokens remain after cleaning
text_preparation_summary <- text_tokens |>
  summarise(
    documents_with_tokens = n_distinct(document_id),
    cleaned_tokens = n(),
    unique_terms = n_distinct(word)
  )

text_preparation_summary |>
  knitr::kable(caption = "Text-preparation summary")
Text-preparation summary
documents_with_tokens cleaned_tokens unique_terms
8843 155895 11245

Data-informed insights and recommendations

Topics content creators have been posting about

Term frequency analysis

Term frequency provides a first view of the subject matter appearing repeatedly across Fonterra’s content. Because a simple frequency count can be influenced by long captions or transcripts, it is interpreted together with the document-based topic model rather than used alone.

# Count the most frequent cleaned terms
term_frequency <- text_tokens |>
  count(word, sort = TRUE, name = "frequency")

# Display the 20 most frequent terms
top_terms <- term_frequency |>
  slice_head(n = 20)

top_terms |>
  knitr::kable(caption = "Twenty most frequent cleaned terms")
Twenty most frequent cleaned terms
word frequency
milk 2236
farmer 1849
farm 1682
dairy 1519
team 1323
product 822
people 769
support 730
food 698
share 697
business 671
world 606
site 605
community 600
plant 583
price 575
award 554
cheese 524
learn 524
water 511
# Visualise the 20 most frequent terms
top_terms |>
  ggplot(aes(x = reorder(word, frequency), y = frequency)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Most frequent terms in Fonterra social-media content",
    x = "Term",
    y = "Frequency"
  ) +
  theme_minimal(base_size = 11)

The cleaned term-frequency results are led by terms such as milk, farmer, farm, dairy, team, product, people, support. Overall, the vocabulary is strongly centred on dairy production, milk, farmers/farms, food/products, and related organisational or community activity. This suggests that Fonterra’s social-media presence primarily communicates its dairy identity while also covering business, product, farmer, community, and sustainability-related material.

Topic modelling

A six-topic Latent Dirichlet Allocation (LDA) model was fitted, following the assignment guidance to begin with k = 6. Terms appearing in fewer than five documents were excluded from the model to reduce sparse, highly specific vocabulary. The topic numbers are arbitrary; interpretation is based on each topic’s highest-probability terms.

# Calculate document frequency and retain terms appearing in at least five documents
lda_vocabulary <- text_tokens |>
  distinct(document_id, word) |>
  count(word, name = "document_frequency") |>
  filter(document_frequency >= 5)

# Create document-term counts for the LDA model
lda_counts <- text_tokens |>
  semi_join(lda_vocabulary, by = "word") |>
  count(document_id, word, name = "n")

# Convert tidy term counts into a document-term matrix
lda_dtm <- lda_counts |>
  tidytext::cast_dtm(document_id, word, n)

# Fit a reproducible six-topic LDA model
set.seed(1234)
lda_model <- topicmodels::LDA(
  lda_dtm,
  k = 6,
  control = list(seed = 1234)
)

# Extract word-topic probabilities (beta) and identify the top terms for each topic
topic_terms <- tidytext::tidy(lda_model, matrix = "beta") |>
  group_by(topic) |>
  slice_max(beta, n = 10, with_ties = FALSE) |>
  ungroup()

# Display the key terms by topic
topic_keyword_table <- topic_terms |>
  arrange(topic, desc(beta)) |>
  group_by(topic) |>
  summarise(key_terms = paste(term, collapse = ", "), .groups = "drop")

topic_keyword_table |>
  knitr::kable(caption = "Top terms describing each LDA topic")
Top terms describing each LDA topic
topic key_terms
1 team, product, question, hear, customer, test, safety, call, happen, food
2 farm, farmer, water, plant, cow, dairy, environment, live, emission, sustainability
3 tanker, milk, team, school, farmer, kid, check, site, love, share
4 milk, dairy, world, product, people, food, cheese, protein, learn, music
5 milk, price, farmer, business, result, china, market, million, forecast, strong
6 award, dairy, team, support, community, farmer, link, celebrate, win, winner
# Visualise the ten highest-probability terms within each topic
topic_terms |>
  mutate(term_plot = tidytext::reorder_within(term, beta, topic)) |>
  ggplot(aes(x = term_plot, y = beta)) +
  geom_col() +
  coord_flip() +
  facet_wrap(~ topic, scales = "free") +
  tidytext::scale_x_reordered() +
  labs(
    title = "Highest-probability terms within the six LDA topics",
    x = "Term",
    y = "Word-topic probability (beta)"
  ) +
  theme_minimal(base_size = 10)

# Extract document-topic probabilities (gamma) and keep the most probable topic per post
dominant_topic <- tidytext::tidy(lda_model, matrix = "gamma") |>
  group_by(document) |>
  slice_max(gamma, n = 1, with_ties = FALSE) |>
  ungroup() |>
  transmute(
    document_id = as.integer(as.character(document)),
    dominant_topic = topic,
    topic_probability = gamma
  )

# Join each post's most probable topic back to the main dataset
fonterra <- fonterra |>
  left_join(dominant_topic, by = "document_id") |>
  mutate(topic_factor = factor(dominant_topic))

# Calculate the prevalence of each dominant topic
topic_prevalence <- fonterra |>
  filter(!is.na(dominant_topic)) |>
  count(dominant_topic, name = "posts") |>
  mutate(percent = round(100 * posts / sum(posts), 1)) |>
  left_join(topic_keyword_table, by = c("dominant_topic" = "topic")) |>
  arrange(desc(posts))

topic_prevalence |>
  knitr::kable(caption = "Prevalence and key terms of the dominant LDA topics")
Prevalence and key terms of the dominant LDA topics
dominant_topic posts percent key_terms
3 2717 30.9 tanker, milk, team, school, farmer, kid, check, site, love, share
4 1376 15.6 milk, dairy, world, product, people, food, cheese, protein, learn, music
6 1355 15.4 award, dairy, team, support, community, farmer, link, celebrate, win, winner
2 1231 14.0 farm, farmer, water, plant, cow, dairy, environment, live, emission, sustainability
5 1061 12.1 milk, price, farmer, business, result, china, market, million, forecast, strong
1 1055 12.0 team, product, question, hear, customer, test, safety, call, happen, food

Commentary on topic analysis

  • The term-frequency and LDA results reinforce that milk, dairy, farms/farmers, food/products, and business-related communication are central to Fonterra’s social-media content.
  • The six-topic model separates this broad dairy identity into more specific clusters. Based on the high-probability terms shown above, the content includes themes related to product/milk communication, farming and farmer activity, community or workforce activity, service/support messages, sustainability/global business, and corporate results or pricing communication. Topic labels should be interpreted from the terms rather than from the topic number itself because LDA topic numbers have no inherent meaning.
  • Twitter/X represents approximately 69.1% of the records, so the overall term and topic mix is likely to reflect Twitter/X content strongly. Platform-specific content strategies should therefore be considered when interpreting the pooled topics.
  • Long text, including some video captions or transcripts, contributes more tokens to raw term frequency. LDA complements the frequency analysis by modelling co-occurring terms at document level, but both methods remain descriptive rather than causal.

Level of user engagement and factors that influence user engagement

Exploratory data analysis

The engagement variables have different levels of availability across platforms. likeCount is complete for all records and is therefore used as the main comparable engagement outcome across platforms, media types, and topics. Comments, shares, and video plays are also summarised, but their platform-specific missingness is taken into account.

# Summarise the distribution and availability of each engagement metric
engagement_summary <- fonterra |>
  select(likeCount, commentCount, shareCount, videoplayCount) |>
  pivot_longer(everything(), names_to = "metric", values_to = "value") |>
  group_by(metric) |>
  summarise(
    available_n = sum(!is.na(value)),
    missing_n = sum(is.na(value)),
    mean = mean(value, na.rm = TRUE),
    median = median(value, na.rm = TRUE),
    IQR = IQR(value, na.rm = TRUE),
    maximum = max(value, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(across(c(mean, median, IQR, maximum), ~ round(., 2)))

engagement_summary |>
  knitr::kable(caption = "Descriptive statistics for user-engagement metrics")
Descriptive statistics for user-engagement metrics
metric available_n missing_n mean median IQR maximum
commentCount 8708 293 7.69 0.0 1.00 4265
likeCount 9001 0 48.42 3.0 20.00 9528
shareCount 8050 951 4.41 1.0 3.00 1621
videoplayCount 886 8115 32997.64 2783.5 11026.75 1199468
# Summarise likes by social-media platform
platform_like_summary <- fonterra |>
  group_by(platform) |>
  summarise(
    posts = n(),
    mean_likes = mean(likeCount, na.rm = TRUE),
    median_likes = median(likeCount, na.rm = TRUE),
    IQR_likes = IQR(likeCount, na.rm = TRUE),
    maximum_likes = max(likeCount, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(across(c(mean_likes, median_likes, IQR_likes, maximum_likes), ~ round(., 2))) |>
  arrange(desc(median_likes))

platform_like_summary |>
  knitr::kable(caption = "Like engagement by platform")
Like engagement by platform
platform posts mean_likes median_likes IQR_likes maximum_likes
LinkedIn 326 196.44 141 141.25 1967
Instagram 519 146.94 132 117.50 1164
Facebook 1433 182.22 61 134.00 9528
TikTok 108 36.89 7 8.25 1582
Twitter/X 6220 4.40 1 5.00 704
YouTube 395 7.86 0 4.00 493
# Visualise the highly skewed like distributions using a log transformation for display only
fonterra |>
  ggplot(aes(x = reorder(platform, log_likes, FUN = median), y = log_likes)) +
  geom_boxplot(outlier.alpha = 0.2) +
  coord_flip() +
  labs(
    title = "Distribution of likes by social-media platform",
    x = "Platform",
    y = "log(1 + likes)"
  ) +
  theme_minimal(base_size = 11)

# Summarise likes by recorded media type; rows with unrecorded media are excluded
media_like_summary <- fonterra |>
  filter(!is.na(media_clean)) |>
  group_by(media_clean) |>
  summarise(
    posts = n(),
    mean_likes = mean(likeCount),
    median_likes = median(likeCount),
    IQR_likes = IQR(likeCount),
    .groups = "drop"
  ) |>
  mutate(across(c(mean_likes, median_likes, IQR_likes), ~ round(., 2))) |>
  arrange(desc(median_likes))

media_like_summary |>
  knitr::kable(caption = "Like engagement by recorded media type")
Like engagement by recorded media type
media_clean posts mean_likes median_likes IQR_likes
Sidecar 95 142.74 134.0 98.00
Image 930 199.29 113.5 154.00
Video 982 104.79 20.0 102.75
# Connect each model-derived topic with its observed like engagement
topic_engagement <- fonterra |>
  filter(!is.na(dominant_topic)) |>
  group_by(dominant_topic) |>
  summarise(
    posts = n(),
    mean_likes = mean(likeCount),
    median_likes = median(likeCount),
    IQR_likes = IQR(likeCount),
    .groups = "drop"
  ) |>
  left_join(topic_keyword_table, by = c("dominant_topic" = "topic")) |>
  mutate(across(c(mean_likes, median_likes, IQR_likes), ~ round(., 2))) |>
  arrange(desc(median_likes))

topic_engagement |>
  knitr::kable(caption = "Like engagement by dominant LDA topic")
Like engagement by dominant LDA topic
dominant_topic posts mean_likes median_likes IQR_likes key_terms
6 1355 48.38 10 49 award, dairy, team, support, community, farmer, link, celebrate, win, winner
2 1231 53.62 6 45 farm, farmer, water, plant, cow, dairy, environment, live, emission, sustainability
4 1376 52.77 6 33 milk, dairy, world, product, people, food, cheese, protein, learn, music
3 2717 68.50 3 23 tanker, milk, team, school, farmer, kid, check, site, love, share
5 1061 28.70 1 6 milk, price, farmer, business, result, china, market, million, forecast, strong
1 1055 9.83 0 2 team, product, question, hear, customer, test, safety, call, happen, food
# Create a log-scale scatter plot for posts where video-play counts are available
video_data <- fonterra |>
  filter(!is.na(videoplayCount))

ggplot(video_data, aes(x = log1p(videoplayCount), y = log1p(likeCount))) +
  geom_point(alpha = 0.35) +
  geom_smooth(method = "loess", se = FALSE) +
  labs(
    title = "Association between video plays and likes",
    x = "log(1 + video plays)",
    y = "log(1 + likes)"
  ) +
  theme_minimal(base_size = 11)

The large differences between means and medians show that engagement is highly right-skewed and includes a small number of very high-engagement posts. For example, the overall median is 3 likes while the mean is 48.42. Therefore, medians, IQRs, log-scale visualisations, and non-parametric tests are emphasised rather than relying on means alone.

The platform table shows substantial differences in raw like counts. The highest median-like platform in this dataset is LinkedIn, with a median of 141 likes per post. However, these are absolute counts rather than engagement rates: follower numbers, impressions, and reach are not available, and the platforms also cover different historical periods. The platform comparison is therefore descriptive evidence of different engagement distributions, not proof that one platform is intrinsically more effective.

For posts where media type was recorded, the highest median-like format is Sidecar, with a median of 134 likes. This result should be interpreted cautiously because media availability is platform-dependent and media type is therefore partly confounded with platform.

The topic-engagement table connects the LDA themes with audience response. In this model, Topic 6 has the highest median like count (10) and is characterised by the terms award, dairy, team, support, community, farmer, link, celebrate, win, winner. This provides a useful direction for future content testing, but pooled topic differences may also reflect platform, posting period, or audience-size differences.

Bivariate analysis

Because likes are extremely right-skewed with many low values and several extreme observations, Kruskal-Wallis tests are used as the non-parametric alternative to one-way ANOVA when comparing likes across more than two groups. Spearman’s rank correlation is used to assess the relationship between video plays and likes without assuming a normal linear relationship.

# Test whether like distributions differ across social-media platforms
kw_platform <- kruskal.test(likeCount ~ platform, data = fonterra)

# Test whether like distributions differ across recorded media types
media_test_data <- fonterra |>
  filter(!is.na(media_clean))
kw_media <- kruskal.test(likeCount ~ media_clean, data = media_test_data)

# Test whether like distributions differ across the six model-derived topics
topic_test_data <- fonterra |>
  filter(!is.na(topic_factor))
kw_topic <- kruskal.test(likeCount ~ topic_factor, data = topic_test_data)

# Put the Kruskal-Wallis results into one concise table
kruskal_results <- tibble::tibble(
  analysis = c("Likes by platform", "Likes by recorded media type", "Likes by LDA topic"),
  statistic = c(unname(kw_platform$statistic),
                unname(kw_media$statistic),
                unname(kw_topic$statistic)),
  df = c(unname(kw_platform$parameter),
         unname(kw_media$parameter),
         unname(kw_topic$parameter)),
  p_value = c(kw_platform$p.value, kw_media$p.value, kw_topic$p.value)
) |>
  mutate(
    statistic = round(statistic, 2),
    p_display = format.pval(p_value, digits = 3, eps = 0.001)
  ) |>
  select(analysis, statistic, df, p_display)

kruskal_results |>
  knitr::kable(caption = "Kruskal-Wallis tests of factors associated with like engagement")
Kruskal-Wallis tests of factors associated with like engagement
analysis statistic df p_display
Likes by platform 4728.39 5 <0.001
Likes by recorded media type 327.80 2 <0.001
Likes by LDA topic 967.02 5 <0.001
# Assess the association between video plays and likes for posts with video-play data
video_like_correlation <- cor.test(
  video_data$videoplayCount,
  video_data$likeCount,
  method = "spearman",
  exact = FALSE
)

video_correlation_result <- tibble::tibble(
  observations = nrow(video_data),
  spearman_rho = round(unname(video_like_correlation$estimate), 3),
  p_value = video_like_correlation$p.value,
  p_display = format.pval(video_like_correlation$p.value, digits = 3, eps = 0.001)
)

video_correlation_result |>
  select(observations, spearman_rho, p_display) |>
  knitr::kable(caption = "Spearman correlation between video plays and likes")
Spearman correlation between video plays and likes
observations spearman_rho p_display
886 0.731 <0.001

Commentary on insights and recommendations

  • The Kruskal-Wallis results show statistically significant differences in like distributions across platforms, recorded media types, and model-derived content topics (p < .001 for each comparison). These tests establish association, not causation. In particular, platform audience size, historical coverage, and the uneven availability of media information could contribute to the observed differences.
  • Video plays and likes show a positive Spearman association of approximately 0.73 across 886 posts with video-play data (p < .001). Posts that receive more video plays also tend to receive more likes, although this does not show that video plays cause likes and video-play data are available only for selected platforms/posts.
  • Fonterra should use platform-specific benchmarks rather than comparing raw like counts as though all platforms have the same audience size, reach, age, and content mix. The current data do not contain followers, impressions, or reach, so a true engagement rate cannot be calculated.
  • The higher-median media format identified in the media table should be treated as a testable content hypothesis, not as a universal rule. A practical next step would be controlled platform-level testing of image, sidecar/carousel, and video formats while collecting reach/impression data.
  • The topic-engagement table provides a data-informed way to identify themes that currently receive stronger audience response. Fonterra should test a larger share of the higher-median topic(s) within the same platform and time period while monitoring whether the advantage remains after accounting for audience reach.
  • For video content, Fonterra should track not only video plays but also play-to-like, play-to-comment, and play-to-share conversion where the relevant metrics are available. This would distinguish exposure from active engagement.
  • Future social-media extraction should collect consistent engagement fields, impressions/reach, follower counts, and media type across all platforms. These additions would allow fairer engagement-rate comparisons and stronger analysis of which content characteristics are associated with engagement.
  • Finally, the dataset spans from 08 November 2010 to 28 July 2026. Platform usage, audience size, algorithms, and content strategy can change materially over such a long period. Future decision-making should therefore repeat the analysis on a recent common time window and compare like-for-like periods within each platform.

GenAI use declaration

ChatGPT was used as a guided supporting tool to help interpret the assignment requirements, structure the R Markdown workflow, review R syntax, and identify suitable approaches for data preparation, text analysis, topic modelling, exploratory analysis, and bivariate statistical testing. GenAI was not used to manually alter the source dataset. Data-dependent statements were cross-checked against the supplied fonterra.csv, including the dataset structure, missing-value patterns, engagement summaries, and the suitability of the selected statistical methods.

LS0tDQp0aXRsZTogIkluZGl2aWR1YWwgVGFzayAxIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCmVkaXRvcl9vcHRpb25zOg0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNClRoaXMgYW5hbHlzaXMgZXZhbHVhdGVzIEZvbnRlcnJhJ3Mgc29jaWFsLW1lZGlhIGNvbnRlbnQgYW5kIHVzZXIgZW5nYWdlbWVudCBhY3Jvc3MgaXRzIG9mZmljaWFsIHNvY2lhbC1tZWRpYSBwbGF0Zm9ybXMuIFRoZSBhbmFseXNpcyBpcyBjb21wbGV0ZWQgZW50aXJlbHkgaW4gUiwgd2l0aCBkYXRhIHByZXBhcmF0aW9uLCB0ZXh0IGFuYWx5c2lzLCB0b3BpYyBtb2RlbGxpbmcsIGV4cGxvcmF0b3J5IGFuYWx5c2lzLCBhbmQgYml2YXJpYXRlIHN0YXRpc3RpY2FsIGFuYWx5c2lzIGNvbmR1Y3RlZCBpbiB0aGUgb3JkZXIgcHJlc2VudGVkIGJlbG93Lg0KDQojIEluc3RhbGwgYW5kIGxvYWQgcGFja2FnZXMgYW5kIGltcG9ydCBhbmQgcHJldmlldyBkYXRhDQoNCmBgYHtyIGluaXRpYWx9DQojIFNldCBnbG9iYWwgY2h1bmsgb3B0aW9ucyBzbyB0aGUga25pdHRlZCByZXBvcnQgaXMgdGlkeSBhbmQgcmVhZGFibGUNCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDUpDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkNCg0KIyBJbnN0YWxsIGFuZCBsb2FkIHBhY2thZ2VzDQppZighcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpwYWNtYW46OnBfbG9hZChyZWFkciwgZHBseXIsIHRpZHlyLCBzdHJpbmdyLCBsdWJyaWRhdGUsIHB1cnJyLCBnZ3Bsb3QyLCBncmlkRXh0cmEsIEdHYWxseSwgY29ycnBsb3QpDQpwYWNtYW46OnBfbG9hZCh0aWR5dGV4dCwgdGV4dHN0ZW0sIHdvcmRjbG91ZCwgdGV4dGRhdGEsIHRvcGljbW9kZWxzKQ0KcGFjbWFuOjpwX2xvYWQoRGVzY1Rvb2xzLCBza2ltciwgZmluYWxmaXQsIHJzdGF0aXgsIGh1eHRhYmxlLCBqdG9vbHMpDQoNCiMgSW1wb3J0IHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBUaGUgQ1NWIGlzIG5vdCBtb2RpZmllZCBtYW51YWxseSBvdXRzaWRlIFIuDQpmb250ZXJyYV9yYXcgPC0gcmVhZF9jc3YoImZvbnRlcnJhLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpDQpmb250ZXJyYSA8LSBmb250ZXJyYV9yYXcNCg0KIyBQcmV2aWV3IHRoZSBzdHJ1Y3R1cmUgYW5kIGEgY29uY2lzZSBzYW1wbGUgb2YgdGhlIGRhdGENCmdsaW1wc2UoZm9udGVycmEpDQoNCmZvbnRlcnJhIHw+DQogIG11dGF0ZSh0ZXh0X3ByZXZpZXcgPSBzdHJfdHJ1bmModGV4dCwgODApKSB8Pg0KICBzZWxlY3QocGxhdGZvcm0sIGNyZWF0ZWRBdCwgbWVkaWEsIGNvbW1lbnRDb3VudCwgc2hhcmVDb3VudCwgbGlrZUNvdW50LA0KICAgICAgICAgdmlkZW9wbGF5Q291bnQsIHRleHRfcHJldmlldykgfD4NCiAgc2xpY2VfaGVhZChuID0gNikgfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiUHJldmlldyBvZiB0aGUgRm9udGVycmEgc29jaWFsLW1lZGlhIGRhdGFzZXQiKQ0KYGBgDQoNClRoZSBkYXRhc2V0IGNvbnRhaW5zIGByIG5yb3coZm9udGVycmEpYCBzb2NpYWwtbWVkaWEgcmVjb3JkcyBhbmQgYHIgbmNvbChmb250ZXJyYSlgIG9yaWdpbmFsIHZhcmlhYmxlcy4gVGhlIHJlY29yZHMgY292ZXIgVHdpdHRlci9YLCBGYWNlYm9vaywgSW5zdGFncmFtLCBZb3VUdWJlLCBUaWtUb2ssIGFuZCBMaW5rZWRJbi4NCg0KIyBEYXRhIHByZXBhcmF0aW9uDQoNCiMjIE5vbi10ZXh0IGRhdGENCg0KIyMjIEJyaWVmIG5vdGVzIG9uIGp1ZGdlbWVudHMgYXBwbGllZA0KDQotIFRoZSBgY3JlYXRlZEF0YCB2YXJpYWJsZSB3YXMgY29udmVydGVkIGZyb20gY2hhcmFjdGVyIGZvcm1hdCB0byBhIFVUQyBkYXRlLXRpbWUgc28gdGhhdCBkYXRlcyBhbmQgdGltZS1iYXNlZCB2YXJpYWJsZXMgY291bGQgYmUgYW5hbHlzZWQgY29ycmVjdGx5Lg0KLSBFeGFjdCBkdXBsaWNhdGUgcm93cyBhbmQgZHVwbGljYXRlIFVSTHMgd2VyZSBjaGVja2VkIGJlZm9yZSBhbmFseXNpcy4gTm8gZXhhY3QgZHVwbGljYXRlIHJvd3Mgb3IgZHVwbGljYXRlIFVSTHMgd2VyZSBmb3VuZCwgc28gbm8gcmVjb3JkcyB3ZXJlIHJlbW92ZWQgb24gdGhpcyBiYXNpcy4gUmVwZWF0ZWQgdGV4dCB3YXMgcmV0YWluZWQgd2hlbiBpdCBhcHBlYXJlZCB1bmRlciBhIGRpZmZlcmVudCBVUkwgb3IgcGxhdGZvcm0gYmVjYXVzZSBjcm9zcy1wb3N0aW5nIGFuZCByZXBlYXRlZCBvcmdhbmlzYXRpb25hbCBtZXNzYWdlcyBjYW4gYmUgbGVnaXRpbWF0ZSBzb2NpYWwtbWVkaWEgYWN0aXZpdHkuDQotIFRoZSBgbWVkaWFgIGZpZWxkIHVzZWQgZGlmZmVyZW50IGxhYmVscyBmb3IgZXF1aXZhbGVudCBmb3JtYXRzLiBgUGhvdG9gLCBgSW1hZ2VgLCBhbmQgYGltYWdlYCB3ZXJlIHN0YW5kYXJkaXNlZCB0byAqKkltYWdlKio7IGBWaWRlb2AgYW5kIGB2aWRlb2Agd2VyZSBzdGFuZGFyZGlzZWQgdG8gKipWaWRlbyoqOyBhbmQgYFNpZGVjYXJgIHdhcyByZXRhaW5lZCBhcyAqKlNpZGVjYXIqKi4NCi0gTWlzc2luZyBgbWVkaWFgLCBjb21tZW50LCBzaGFyZSwgYW5kIHZpZGVvLXBsYXkgdmFsdWVzIHdlcmUgKipub3QgYXV0b21hdGljYWxseSByZXBsYWNlZCB3aXRoIHplcm8qKi4gTWlzc2luZ25lc3MgaXMgc3Ryb25nbHkgcGxhdGZvcm0tZGVwZW5kZW50LCBzbyBhIG1pc3NpbmcgdmFsdWUgbWF5IGluZGljYXRlIHRoYXQgdGhlIG1ldHJpYyB3YXMgdW5hdmFpbGFibGUgb3Igbm90IGV4dHJhY3RlZCByYXRoZXIgdGhhbiB0aGF0IHRoZSB0cnVlIHZhbHVlIHdhcyB6ZXJvLiBUcmVhdGluZyBhbGwgbWlzc2luZyB2YWx1ZXMgYXMgemVybyBjb3VsZCBiaWFzIGNvbXBhcmlzb25zLg0KLSBFbmdhZ2VtZW50LWNvdW50IHZhcmlhYmxlcyB3ZXJlIGNoZWNrZWQgZm9yIGltcG9zc2libGUgbmVnYXRpdmUgdmFsdWVzLiBOb25lIHdlcmUgZm91bmQuDQotIE1pc3Npbmcgb3IgYmxhbmsgdGV4dCB3YXMgcmV0YWluZWQgaW4gdGhlIG1haW4gZGF0YXNldCBiZWNhdXNlIHRob3NlIHJlY29yZHMgbWF5IHN0aWxsIGJlIHJlbGV2YW50IHRvIG5vbi10ZXh0IGVuZ2FnZW1lbnQgYW5hbHlzaXM7IHN1Y2ggcmVjb3JkcyBhcmUgZXhjbHVkZWQgb25seSBmcm9tIHRoZSB0ZXh0LWFuYWx5c2lzIGRhdGFzZXQuDQoNCmBgYHtyIHJlcV8yYX0NCiMgU3VtbWFyaXNlIG1pc3NpbmcgZGF0YSBiZWZvcmUgbWFraW5nIGFuYWx5dGljYWwgZGVjaXNpb25zDQptaXNzaW5nX3N1bW1hcnkgPC0gZm9udGVycmEgfD4NCiAgc3VtbWFyaXNlKGFjcm9zcyhldmVyeXRoaW5nKCksIH4gc3VtKGlzLm5hKC4pKSkpIHw+DQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gIm1pc3NpbmdfbiIpIHw+DQogIG11dGF0ZShtaXNzaW5nX3BjdCA9IHJvdW5kKDEwMCAqIG1pc3NpbmdfbiAvIG5yb3coZm9udGVycmEpLCAyKSkNCg0KbWlzc2luZ19zdW1tYXJ5IHw+DQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIk1pc3NpbmcgdmFsdWVzIGluIHRoZSBvcmlnaW5hbCBkYXRhc2V0IikNCg0KIyBDaGVjayBleGFjdCBkdXBsaWNhdGVzLCBkdXBsaWNhdGUgVVJMcywgYW5kIHJlcGVhdGVkIHRleHQvdGltZXN0YW1wIGNvbWJpbmF0aW9ucw0KZHVwbGljYXRlX3N1bW1hcnkgPC0gdGliYmxlOjp0aWJibGUoDQogIGNoZWNrID0gYygiRXhhY3QgZHVwbGljYXRlIHJvd3MiLA0KICAgICAgICAgICAgIkR1cGxpY2F0ZSBVUkxzIiwNCiAgICAgICAgICAgICJSZXBlYXRlZCB0ZXh0IGF0IHRoZSBzYW1lIHRpbWVzdGFtcCIpLA0KICBuID0gYyhzdW0oZHVwbGljYXRlZChmb250ZXJyYSkpLA0KICAgICAgICBzdW0oZHVwbGljYXRlZChmb250ZXJyYSR1cmwpKSwNCiAgICAgICAgc3VtKGR1cGxpY2F0ZWQoZm9udGVycmFbYygiY3JlYXRlZEF0IiwgInRleHQiKV0pKSkNCikNCg0KZHVwbGljYXRlX3N1bW1hcnkgfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiRHVwbGljYXRlIGNoZWNrcyIpDQoNCiMgQ29udmVydCB0aGUgdGltZXN0YW1wIHRvIGEgcHJvcGVyIFVUQyBkYXRlLXRpbWUgYW5kIHN0YW5kYXJkaXNlIHBsYXRmb3JtIHRleHQNCmZvbnRlcnJhIDwtIGZvbnRlcnJhIHw+DQogIG11dGF0ZSgNCiAgICBjcmVhdGVkQXQgPSB5bWRfaG1zKGNyZWF0ZWRBdCwgdHogPSAiVVRDIiwgcXVpZXQgPSBUUlVFKSwNCiAgICBwbGF0Zm9ybSA9IHN0cl9zcXVpc2gocGxhdGZvcm0pLA0KICAgIG1lZGlhX2NsZWFuID0gY2FzZV93aGVuKA0KICAgICAgc3RyX3RvX2xvd2VyKG1lZGlhKSAlaW4lIGMoInBob3RvIiwgImltYWdlIikgfiAiSW1hZ2UiLA0KICAgICAgc3RyX3RvX2xvd2VyKG1lZGlhKSA9PSAidmlkZW8iIH4gIlZpZGVvIiwNCiAgICAgIHN0cl90b19sb3dlcihtZWRpYSkgPT0gInNpZGVjYXIiIH4gIlNpZGVjYXIiLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCg0KIyBDaGVjayB3aGV0aGVyIGVuZ2FnZW1lbnQgY291bnRzIGNvbnRhaW4gaW1wb3NzaWJsZSBuZWdhdGl2ZSB2YWx1ZXMNCm5lZ2F0aXZlX3ZhbHVlX2NoZWNrIDwtIGZvbnRlcnJhIHw+DQogIHN1bW1hcmlzZShhY3Jvc3MoYyhjb21tZW50Q291bnQsIHNoYXJlQ291bnQsIGxpa2VDb3VudCwgdmlkZW9wbGF5Q291bnQpLA0KICAgICAgICAgICAgICAgICAgIH4gc3VtKC4gPCAwLCBuYS5ybSA9IFRSVUUpKSkgfD4NCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAibmVnYXRpdmVfdmFsdWVzIikNCg0KbmVnYXRpdmVfdmFsdWVfY2hlY2sgfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiQ2hlY2sgZm9yIG5lZ2F0aXZlIGVuZ2FnZW1lbnQgY291bnRzIikNCg0KIyBTaG93IG1pc3NpbmcgZW5nYWdlbWVudCB2YWx1ZXMgYnkgcGxhdGZvcm0gdG8gYXNzZXNzIHdoZXRoZXIgbWlzc2luZ25lc3MgaXMgc3lzdGVtYXRpYw0KbWlzc2luZ19ieV9wbGF0Zm9ybSA8LSBmb250ZXJyYSB8Pg0KICBncm91cF9ieShwbGF0Zm9ybSkgfD4NCiAgc3VtbWFyaXNlKA0KICAgIHBvc3RzID0gbigpLA0KICAgIG1pc3NpbmdfY29tbWVudHMgPSBzdW0oaXMubmEoY29tbWVudENvdW50KSksDQogICAgbWlzc2luZ19zaGFyZXMgPSBzdW0oaXMubmEoc2hhcmVDb3VudCkpLA0KICAgIG1pc3NpbmdfdmlkZW9fcGxheXMgPSBzdW0oaXMubmEodmlkZW9wbGF5Q291bnQpKSwNCiAgICBtaXNzaW5nX21lZGlhID0gc3VtKGlzLm5hKG1lZGlhKSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCm1pc3NpbmdfYnlfcGxhdGZvcm0gfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiTWlzc2luZyBlbmdhZ2VtZW50IGFuZCBtZWRpYSBkYXRhIGJ5IHBsYXRmb3JtIikNCmBgYA0KDQpUaGUgbWlzc2luZy12YWx1ZSB0YWJsZSBjb25maXJtcyB0aGF0IG1pc3NpbmduZXNzIGlzIG5vdCByYW5kb20gYWNyb3NzIHRoZSB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBzaGFyZSBjb3VudHMgYXJlIHVuYXZhaWxhYmxlIGZvciBzb21lIHBsYXRmb3JtcyBhbmQgdmlkZW8tcGxheSBjb3VudHMgYXJlIGF2YWlsYWJsZSBvbmx5IGZvciBhIHN1YnNldCBvZiBwb3N0cy4gVGhlcmVmb3JlLCB0aGUgYW5hbHlzaXMgdXNlcyBhdmFpbGFibGUgb2JzZXJ2YXRpb25zIGZvciBlYWNoIG1ldHJpYyBhbmQgYXZvaWRzIGludGVycHJldGluZyB1bmF2YWlsYWJsZSB2YWx1ZXMgYXMgemVyby4NCg0KIyMgTmV3IHZhcmlhYmxlcyBjcmVhdGVkDQoNCiMjIyBCcmllZiBub3RlcyBvbiB3aHkgdGhlIHZhcmlhYmxlcyBjb3VsZCBiZSB1c2VmdWwNCg0KLSBgZG9jdW1lbnRfaWRgIGdpdmVzIGV2ZXJ5IHNvY2lhbC1tZWRpYSByZWNvcmQgYSB1bmlxdWUgaWRlbnRpZmllciBhbmQgaXMgbmVlZGVkIHRvIGNvbm5lY3QgdG9waWMtbW9kZWxsaW5nIHJlc3VsdHMgYmFjayB0byB0aGUgb3JpZ2luYWwgZGF0YXNldC4NCi0gYHBvc3RfZGF0ZWAsIGB5ZWFyYCwgYG1vbnRoYCwgYW5kIGBkYXlfb2Zfd2Vla2Agc3VwcG9ydCB0aW1lLWJhc2VkIGFuYWx5c2lzIGFuZCBtYWtlIGl0IHBvc3NpYmxlIHRvIGV4YW1pbmUgY2hhbmdlcyBpbiBwb3N0aW5nIG9yIGVuZ2FnZW1lbnQgcGF0dGVybnMgb3ZlciB0aW1lLg0KLSBgd29yZF9jb3VudGAgbWVhc3VyZXMgcG9zdC9jYXB0aW9uIGxlbmd0aCBhbmQgY2FuIGJlIHVzZWQgdG8gaW52ZXN0aWdhdGUgd2hldGhlciB0ZXh0IGxlbmd0aCBpcyBhc3NvY2lhdGVkIHdpdGggZW5nYWdlbWVudC4NCi0gYHBvc3RfYWdlX2RheXNgIG1lYXN1cmVzIHRoZSBhZ2Ugb2YgYSBwb3N0IHJlbGF0aXZlIHRvIHRoZSBtb3N0IHJlY2VudCBwb3N0IGluIHRoZSBkYXRhc2V0LiBUaGlzIGlzIHVzZWZ1bCBiZWNhdXNlIGVuZ2FnZW1lbnQgY291bnRzIGFyZSBjdW11bGF0aXZlIGFuZCBwb3N0cyBjb3ZlciBhIGxvbmcgdGltZSBwZXJpb2QuDQotIGBjb21wbGV0ZV9lbmdhZ2VtZW50YCBpZGVudGlmaWVzIHJvd3Mgd2hlcmUgbGlrZXMsIGNvbW1lbnRzLCBhbmQgc2hhcmVzIGFyZSBhbGwgb2JzZXJ2ZWQuIGB0b3RhbF9lbmdhZ2VtZW50YCBpcyBjYWxjdWxhdGVkIG9ubHkgZm9yIHRoZXNlIGNvbXBsZXRlIHJvd3MsIHByZXZlbnRpbmcgdW5hdmFpbGFibGUgbWV0cmljcyBmcm9tIGJlaW5nIGluY29ycmVjdGx5IHRyZWF0ZWQgYXMgemVyby4NCi0gYGxvZ19saWtlc2AgaXMgYSB0cmFuc2Zvcm1lZCB2ZXJzaW9uIG9mIGxpa2VzIHVzZWQgb25seSBmb3IgdmlzdWFsaXNhdGlvbi4gU29jaWFsLW1lZGlhIGVuZ2FnZW1lbnQgaXMgaGlnaGx5IHJpZ2h0LXNrZXdlZCwgc28gYGxvZzFwKClgIG1ha2VzIHBhdHRlcm5zIGFuZCBleHRyZW1lIHZhbHVlcyBlYXNpZXIgdG8gZGlzcGxheSB3aGlsZSByZXRhaW5pbmcgcG9zdHMgd2l0aCB6ZXJvIGxpa2VzLg0KDQpgYGB7ciByZXFfMmJ9DQojIENyZWF0ZSBpZGVudGlmaWVycywgZGF0ZS1iYXNlZCB2YXJpYWJsZXMsIHRleHQtbGVuZ3RoIG1lYXN1cmVzLCBhbmQgZW5nYWdlbWVudCB2YXJpYWJsZXMNCmZvbnRlcnJhIDwtIGZvbnRlcnJhIHw+DQogIG11dGF0ZSgNCiAgICBkb2N1bWVudF9pZCA9IHJvd19udW1iZXIoKSwNCiAgICBwb3N0X2RhdGUgPSBhcy5EYXRlKGNyZWF0ZWRBdCksDQogICAgeWVhciA9IHllYXIoY3JlYXRlZEF0KSwNCiAgICBtb250aCA9IG1vbnRoKGNyZWF0ZWRBdCwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSksDQogICAgZGF5X29mX3dlZWsgPSB3ZGF5KGNyZWF0ZWRBdCwgbGFiZWwgPSBUUlVFLCBhYmJyID0gRkFMU0UsIHdlZWtfc3RhcnQgPSAxKSwNCiAgICB3b3JkX2NvdW50ID0gc3RyX2NvdW50KHN0cl9zcXVpc2goY29hbGVzY2UodGV4dCwgIiIpKSwgIlxcUysiKSwNCiAgICBwb3N0X2FnZV9kYXlzID0gYXMuaW50ZWdlcihtYXgocG9zdF9kYXRlLCBuYS5ybSA9IFRSVUUpIC0gcG9zdF9kYXRlKSwNCiAgICBjb21wbGV0ZV9lbmdhZ2VtZW50ID0gIWlzLm5hKGNvbW1lbnRDb3VudCkgJiAhaXMubmEoc2hhcmVDb3VudCksDQogICAgdG90YWxfZW5nYWdlbWVudCA9IGlmX2Vsc2UoDQogICAgICBjb21wbGV0ZV9lbmdhZ2VtZW50LA0KICAgICAgbGlrZUNvdW50ICsgY29tbWVudENvdW50ICsgc2hhcmVDb3VudCwNCiAgICAgIE5BX3JlYWxfDQogICAgKSwNCiAgICBsb2dfbGlrZXMgPSBsb2cxcChsaWtlQ291bnQpDQogICkNCg0KIyBTdW1tYXJpc2UgdGhlIG5ld2x5IGNyZWF0ZWQgdmFyaWFibGVzDQpuZXdfdmFyaWFibGVfc3VtbWFyeSA8LSBmb250ZXJyYSB8Pg0KICBzdW1tYXJpc2UoDQogICAgZWFybGllc3RfcG9zdCA9IG1pbihwb3N0X2RhdGUsIG5hLnJtID0gVFJVRSksDQogICAgbGF0ZXN0X3Bvc3QgPSBtYXgocG9zdF9kYXRlLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lZGlhbl93b3JkX2NvdW50ID0gbWVkaWFuKHdvcmRfY291bnQsIG5hLnJtID0gVFJVRSksDQogICAgY29tcGxldGVfZW5nYWdlbWVudF9yb3dzID0gc3VtKGNvbXBsZXRlX2VuZ2FnZW1lbnQpLA0KICAgIHJvd3Nfd2l0aF90b3RhbF9lbmdhZ2VtZW50ID0gc3VtKCFpcy5uYSh0b3RhbF9lbmdhZ2VtZW50KSkNCiAgKQ0KDQpuZXdfdmFyaWFibGVfc3VtbWFyeSB8Pg0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJTdW1tYXJ5IG9mIHNlbGVjdGVkIG5ldyB2YXJpYWJsZXMiKQ0KYGBgDQoNCiMjIFRleHQgZGF0YQ0KDQpGb3IgdGVybS1mcmVxdWVuY3kgYW5hbHlzaXMgYW5kIHRvcGljIG1vZGVsbGluZywgdGhlIHRleHQgd2FzIGNvbnZlcnRlZCBpbnRvIGluZGl2aWR1YWwgd29yZCB0b2tlbnMuIFVSTHMsIHB1bmN0dWF0aW9uLCBudW1iZXJzLCBzdGFuZGFyZCBFbmdsaXNoIHN0b3Agd29yZHMsIGFuZCBhIHNtYWxsIHNldCBvZiBoaWdoLWZyZXF1ZW5jeSBidXQgbG93LWluZm9ybWF0aW9uIG9yZ2FuaXNhdGlvbiwgbG9jYXRpb24sIHRlbXBvcmFsLCBhbmQgY29udmVyc2F0aW9uYWwgdGVybXMgd2VyZSByZW1vdmVkLiBXb3JkcyB3ZXJlIGxlbW1hdGlzZWQgc28gdGhhdCBjb21tb24gaW5mbGVjdGVkIGZvcm1zIHdlcmUgYnJvdWdodCBjbG9zZXIgdG8gYSBzaGFyZWQgYmFzZSBmb3JtLiBUaGlzIHJlZHVjZXMgbm9pc2UgYW5kIG1ha2VzIHRoZSByZXN1bHRpbmcgdGhlbWVzIGVhc2llciB0byBpbnRlcnByZXQuDQoNCmBgYHtyIHJlcV8yY30NCiMgQ3JlYXRlIGEgc21hbGwgY3VzdG9tIHN0b3Atd29yZCBsaXN0IGFmdGVyIHJldmlld2luZyBoaWdoLWZyZXF1ZW5jeSBsb3ctaW5mb3JtYXRpb24gdGVybXMNCmN1c3RvbV9zdG9wX3dvcmRzIDwtIHRpYmJsZTo6dGliYmxlKA0KICB3b3JkID0gYygiZm9udGVycmEiLCAiemVhbGFuZCIsICJueiIsICJodHRwcyIsICJodHRwIiwgInd3dyIsICJjb20iLCAiYW1wIiwNCiAgICAgICAgICAgInllYXIiLCAieWVhcnMiLCAidG9kYXkiLCAiZGF5IiwgIndlZWsiLCAidGltZSIsICJqdXN0IiwgInJlYWxseSIsDQogICAgICAgICAgICJnb29kIiwgImdyZWF0IiwgImxpa2UiLCAia25vdyIsICJ0aGFua3MiLCAieWVhaCIpDQopDQoNCiMgQ29tYmluZSBzdGFuZGFyZCBFbmdsaXNoIHN0b3Agd29yZHMgd2l0aCB0aGUgY3VzdG9tIGxpc3QNCmFsbF9zdG9wX3dvcmRzIDwtIGJpbmRfcm93cygNCiAgdGlkeXRleHQ6OnN0b3Bfd29yZHMgfD4gc2VsZWN0KHdvcmQpLA0KICBjdXN0b21fc3RvcF93b3Jkcw0KKSB8Pg0KICBkaXN0aW5jdCh3b3JkKQ0KDQojIFByZXBhcmUgdGV4dCwgcmVtb3ZlIFVSTHMsIHRva2VuaXplIHdvcmRzLCBsZW1tYXRpc2UsIGFuZCByZW1vdmUgc3RvcCB3b3Jkcw0KdGV4dF90b2tlbnMgPC0gZm9udGVycmEgfD4NCiAgZmlsdGVyKCFpcy5uYSh0ZXh0KSwgc3RyX3NxdWlzaCh0ZXh0KSAhPSAiIikgfD4NCiAgc2VsZWN0KGRvY3VtZW50X2lkLCBwbGF0Zm9ybSwgdGV4dCkgfD4NCiAgbXV0YXRlKA0KICAgIHRleHRfY2xlYW4gPSBzdHJfcmVwbGFjZV9hbGwodGV4dCwgImh0dHBzPzovL1xcUyt8d3d3XFwuXFxTKyIsICIgIiksDQogICAgdGV4dF9jbGVhbiA9IHN0cl9yZXBsYWNlX2FsbCh0ZXh0X2NsZWFuLCAiQFtbOmFsbnVtOl1fXSsiLCAiICIpDQogICkgfD4NCiAgdGlkeXRleHQ6OnVubmVzdF90b2tlbnMod29yZCwgdGV4dF9jbGVhbikgfD4NCiAgZmlsdGVyKHN0cl9kZXRlY3Qod29yZCwgIl5bYS16XSskIiksIG5jaGFyKHdvcmQpID4gMikgfD4NCiAgbXV0YXRlKHdvcmQgPSB0ZXh0c3RlbTo6bGVtbWF0aXplX3dvcmRzKHdvcmQpKSB8Pg0KICBhbnRpX2pvaW4oYWxsX3N0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKSB8Pg0KICBmaWx0ZXIoIWlzLm5hKHdvcmQpLCB3b3JkICE9ICIiKQ0KDQojIENoZWNrIGhvdyBtYW55IGRvY3VtZW50cyBhbmQgdG9rZW5zIHJlbWFpbiBhZnRlciBjbGVhbmluZw0KdGV4dF9wcmVwYXJhdGlvbl9zdW1tYXJ5IDwtIHRleHRfdG9rZW5zIHw+DQogIHN1bW1hcmlzZSgNCiAgICBkb2N1bWVudHNfd2l0aF90b2tlbnMgPSBuX2Rpc3RpbmN0KGRvY3VtZW50X2lkKSwNCiAgICBjbGVhbmVkX3Rva2VucyA9IG4oKSwNCiAgICB1bmlxdWVfdGVybXMgPSBuX2Rpc3RpbmN0KHdvcmQpDQogICkNCg0KdGV4dF9wcmVwYXJhdGlvbl9zdW1tYXJ5IHw+DQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIlRleHQtcHJlcGFyYXRpb24gc3VtbWFyeSIpDQpgYGANCg0KIyBEYXRhLWluZm9ybWVkIGluc2lnaHRzIGFuZCByZWNvbW1lbmRhdGlvbnMNCg0KIyMgVG9waWNzIGNvbnRlbnQgY3JlYXRvcnMgaGF2ZSBiZWVuIHBvc3RpbmcgYWJvdXQNCg0KIyMjIFRlcm0gZnJlcXVlbmN5IGFuYWx5c2lzDQoNClRlcm0gZnJlcXVlbmN5IHByb3ZpZGVzIGEgZmlyc3QgdmlldyBvZiB0aGUgc3ViamVjdCBtYXR0ZXIgYXBwZWFyaW5nIHJlcGVhdGVkbHkgYWNyb3NzIEZvbnRlcnJhJ3MgY29udGVudC4gQmVjYXVzZSBhIHNpbXBsZSBmcmVxdWVuY3kgY291bnQgY2FuIGJlIGluZmx1ZW5jZWQgYnkgbG9uZyBjYXB0aW9ucyBvciB0cmFuc2NyaXB0cywgaXQgaXMgaW50ZXJwcmV0ZWQgdG9nZXRoZXIgd2l0aCB0aGUgZG9jdW1lbnQtYmFzZWQgdG9waWMgbW9kZWwgcmF0aGVyIHRoYW4gdXNlZCBhbG9uZS4NCg0KYGBge3IgcmVxXzNhaX0NCiMgQ291bnQgdGhlIG1vc3QgZnJlcXVlbnQgY2xlYW5lZCB0ZXJtcw0KdGVybV9mcmVxdWVuY3kgPC0gdGV4dF90b2tlbnMgfD4NCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUsIG5hbWUgPSAiZnJlcXVlbmN5IikNCg0KIyBEaXNwbGF5IHRoZSAyMCBtb3N0IGZyZXF1ZW50IHRlcm1zDQp0b3BfdGVybXMgPC0gdGVybV9mcmVxdWVuY3kgfD4NCiAgc2xpY2VfaGVhZChuID0gMjApDQoNCnRvcF90ZXJtcyB8Pg0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJUd2VudHkgbW9zdCBmcmVxdWVudCBjbGVhbmVkIHRlcm1zIikNCg0KIyBWaXN1YWxpc2UgdGhlIDIwIG1vc3QgZnJlcXVlbnQgdGVybXMNCnRvcF90ZXJtcyB8Pg0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHdvcmQsIGZyZXF1ZW5jeSksIHkgPSBmcmVxdWVuY3kpKSArDQogIGdlb21fY29sKCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vc3QgZnJlcXVlbnQgdGVybXMgaW4gRm9udGVycmEgc29jaWFsLW1lZGlhIGNvbnRlbnQiLA0KICAgIHggPSAiVGVybSIsDQogICAgeSA9ICJGcmVxdWVuY3kiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKQ0KYGBgDQoNClRoZSBjbGVhbmVkIHRlcm0tZnJlcXVlbmN5IHJlc3VsdHMgYXJlIGxlZCBieSB0ZXJtcyBzdWNoIGFzIGByIHBhc3RlKGhlYWQodG9wX3Rlcm1zJHdvcmQsIDgpLCBjb2xsYXBzZSA9ICIsICIpYC4gT3ZlcmFsbCwgdGhlIHZvY2FidWxhcnkgaXMgc3Ryb25nbHkgY2VudHJlZCBvbiBkYWlyeSBwcm9kdWN0aW9uLCBtaWxrLCBmYXJtZXJzL2Zhcm1zLCBmb29kL3Byb2R1Y3RzLCBhbmQgcmVsYXRlZCBvcmdhbmlzYXRpb25hbCBvciBjb21tdW5pdHkgYWN0aXZpdHkuIFRoaXMgc3VnZ2VzdHMgdGhhdCBGb250ZXJyYSdzIHNvY2lhbC1tZWRpYSBwcmVzZW5jZSBwcmltYXJpbHkgY29tbXVuaWNhdGVzIGl0cyBkYWlyeSBpZGVudGl0eSB3aGlsZSBhbHNvIGNvdmVyaW5nIGJ1c2luZXNzLCBwcm9kdWN0LCBmYXJtZXIsIGNvbW11bml0eSwgYW5kIHN1c3RhaW5hYmlsaXR5LXJlbGF0ZWQgbWF0ZXJpYWwuDQoNCiMjIyBUb3BpYyBtb2RlbGxpbmcNCg0KQSBzaXgtdG9waWMgTGF0ZW50IERpcmljaGxldCBBbGxvY2F0aW9uIChMREEpIG1vZGVsIHdhcyBmaXR0ZWQsIGZvbGxvd2luZyB0aGUgYXNzaWdubWVudCBndWlkYW5jZSB0byBiZWdpbiB3aXRoIGBrID0gNmAuIFRlcm1zIGFwcGVhcmluZyBpbiBmZXdlciB0aGFuIGZpdmUgZG9jdW1lbnRzIHdlcmUgZXhjbHVkZWQgZnJvbSB0aGUgbW9kZWwgdG8gcmVkdWNlIHNwYXJzZSwgaGlnaGx5IHNwZWNpZmljIHZvY2FidWxhcnkuIFRoZSB0b3BpYyBudW1iZXJzIGFyZSBhcmJpdHJhcnk7IGludGVycHJldGF0aW9uIGlzIGJhc2VkIG9uIGVhY2ggdG9waWMncyBoaWdoZXN0LXByb2JhYmlsaXR5IHRlcm1zLg0KDQpgYGB7ciByZXFfM2FpaX0NCiMgQ2FsY3VsYXRlIGRvY3VtZW50IGZyZXF1ZW5jeSBhbmQgcmV0YWluIHRlcm1zIGFwcGVhcmluZyBpbiBhdCBsZWFzdCBmaXZlIGRvY3VtZW50cw0KbGRhX3ZvY2FidWxhcnkgPC0gdGV4dF90b2tlbnMgfD4NCiAgZGlzdGluY3QoZG9jdW1lbnRfaWQsIHdvcmQpIHw+DQogIGNvdW50KHdvcmQsIG5hbWUgPSAiZG9jdW1lbnRfZnJlcXVlbmN5IikgfD4NCiAgZmlsdGVyKGRvY3VtZW50X2ZyZXF1ZW5jeSA+PSA1KQ0KDQojIENyZWF0ZSBkb2N1bWVudC10ZXJtIGNvdW50cyBmb3IgdGhlIExEQSBtb2RlbA0KbGRhX2NvdW50cyA8LSB0ZXh0X3Rva2VucyB8Pg0KICBzZW1pX2pvaW4obGRhX3ZvY2FidWxhcnksIGJ5ID0gIndvcmQiKSB8Pg0KICBjb3VudChkb2N1bWVudF9pZCwgd29yZCwgbmFtZSA9ICJuIikNCg0KIyBDb252ZXJ0IHRpZHkgdGVybSBjb3VudHMgaW50byBhIGRvY3VtZW50LXRlcm0gbWF0cml4DQpsZGFfZHRtIDwtIGxkYV9jb3VudHMgfD4NCiAgdGlkeXRleHQ6OmNhc3RfZHRtKGRvY3VtZW50X2lkLCB3b3JkLCBuKQ0KDQojIEZpdCBhIHJlcHJvZHVjaWJsZSBzaXgtdG9waWMgTERBIG1vZGVsDQpzZXQuc2VlZCgxMjM0KQ0KbGRhX21vZGVsIDwtIHRvcGljbW9kZWxzOjpMREEoDQogIGxkYV9kdG0sDQogIGsgPSA2LA0KICBjb250cm9sID0gbGlzdChzZWVkID0gMTIzNCkNCikNCg0KIyBFeHRyYWN0IHdvcmQtdG9waWMgcHJvYmFiaWxpdGllcyAoYmV0YSkgYW5kIGlkZW50aWZ5IHRoZSB0b3AgdGVybXMgZm9yIGVhY2ggdG9waWMNCnRvcGljX3Rlcm1zIDwtIHRpZHl0ZXh0Ojp0aWR5KGxkYV9tb2RlbCwgbWF0cml4ID0gImJldGEiKSB8Pg0KICBncm91cF9ieSh0b3BpYykgfD4NCiAgc2xpY2VfbWF4KGJldGEsIG4gPSAxMCwgd2l0aF90aWVzID0gRkFMU0UpIHw+DQogIHVuZ3JvdXAoKQ0KDQojIERpc3BsYXkgdGhlIGtleSB0ZXJtcyBieSB0b3BpYw0KdG9waWNfa2V5d29yZF90YWJsZSA8LSB0b3BpY190ZXJtcyB8Pg0KICBhcnJhbmdlKHRvcGljLCBkZXNjKGJldGEpKSB8Pg0KICBncm91cF9ieSh0b3BpYykgfD4NCiAgc3VtbWFyaXNlKGtleV90ZXJtcyA9IHBhc3RlKHRlcm0sIGNvbGxhcHNlID0gIiwgIiksIC5ncm91cHMgPSAiZHJvcCIpDQoNCnRvcGljX2tleXdvcmRfdGFibGUgfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiVG9wIHRlcm1zIGRlc2NyaWJpbmcgZWFjaCBMREEgdG9waWMiKQ0KDQojIFZpc3VhbGlzZSB0aGUgdGVuIGhpZ2hlc3QtcHJvYmFiaWxpdHkgdGVybXMgd2l0aGluIGVhY2ggdG9waWMNCnRvcGljX3Rlcm1zIHw+DQogIG11dGF0ZSh0ZXJtX3Bsb3QgPSB0aWR5dGV4dDo6cmVvcmRlcl93aXRoaW4odGVybSwgYmV0YSwgdG9waWMpKSB8Pg0KICBnZ3Bsb3QoYWVzKHggPSB0ZXJtX3Bsb3QsIHkgPSBiZXRhKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgdGlkeXRleHQ6OnNjYWxlX3hfcmVvcmRlcmVkKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkhpZ2hlc3QtcHJvYmFiaWxpdHkgdGVybXMgd2l0aGluIHRoZSBzaXggTERBIHRvcGljcyIsDQogICAgeCA9ICJUZXJtIiwNCiAgICB5ID0gIldvcmQtdG9waWMgcHJvYmFiaWxpdHkgKGJldGEpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMCkNCg0KIyBFeHRyYWN0IGRvY3VtZW50LXRvcGljIHByb2JhYmlsaXRpZXMgKGdhbW1hKSBhbmQga2VlcCB0aGUgbW9zdCBwcm9iYWJsZSB0b3BpYyBwZXIgcG9zdA0KZG9taW5hbnRfdG9waWMgPC0gdGlkeXRleHQ6OnRpZHkobGRhX21vZGVsLCBtYXRyaXggPSAiZ2FtbWEiKSB8Pg0KICBncm91cF9ieShkb2N1bWVudCkgfD4NCiAgc2xpY2VfbWF4KGdhbW1hLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpIHw+DQogIHVuZ3JvdXAoKSB8Pg0KICB0cmFuc211dGUoDQogICAgZG9jdW1lbnRfaWQgPSBhcy5pbnRlZ2VyKGFzLmNoYXJhY3Rlcihkb2N1bWVudCkpLA0KICAgIGRvbWluYW50X3RvcGljID0gdG9waWMsDQogICAgdG9waWNfcHJvYmFiaWxpdHkgPSBnYW1tYQ0KICApDQoNCiMgSm9pbiBlYWNoIHBvc3QncyBtb3N0IHByb2JhYmxlIHRvcGljIGJhY2sgdG8gdGhlIG1haW4gZGF0YXNldA0KZm9udGVycmEgPC0gZm9udGVycmEgfD4NCiAgbGVmdF9qb2luKGRvbWluYW50X3RvcGljLCBieSA9ICJkb2N1bWVudF9pZCIpIHw+DQogIG11dGF0ZSh0b3BpY19mYWN0b3IgPSBmYWN0b3IoZG9taW5hbnRfdG9waWMpKQ0KDQojIENhbGN1bGF0ZSB0aGUgcHJldmFsZW5jZSBvZiBlYWNoIGRvbWluYW50IHRvcGljDQp0b3BpY19wcmV2YWxlbmNlIDwtIGZvbnRlcnJhIHw+DQogIGZpbHRlcighaXMubmEoZG9taW5hbnRfdG9waWMpKSB8Pg0KICBjb3VudChkb21pbmFudF90b3BpYywgbmFtZSA9ICJwb3N0cyIpIHw+DQogIG11dGF0ZShwZXJjZW50ID0gcm91bmQoMTAwICogcG9zdHMgLyBzdW0ocG9zdHMpLCAxKSkgfD4NCiAgbGVmdF9qb2luKHRvcGljX2tleXdvcmRfdGFibGUsIGJ5ID0gYygiZG9taW5hbnRfdG9waWMiID0gInRvcGljIikpIHw+DQogIGFycmFuZ2UoZGVzYyhwb3N0cykpDQoNCnRvcGljX3ByZXZhbGVuY2UgfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiUHJldmFsZW5jZSBhbmQga2V5IHRlcm1zIG9mIHRoZSBkb21pbmFudCBMREEgdG9waWNzIikNCmBgYA0KDQojIyMgQ29tbWVudGFyeSBvbiB0b3BpYyBhbmFseXNpcw0KDQotIFRoZSB0ZXJtLWZyZXF1ZW5jeSBhbmQgTERBIHJlc3VsdHMgcmVpbmZvcmNlIHRoYXQgKiptaWxrLCBkYWlyeSwgZmFybXMvZmFybWVycywgZm9vZC9wcm9kdWN0cywgYW5kIGJ1c2luZXNzLXJlbGF0ZWQgY29tbXVuaWNhdGlvbioqIGFyZSBjZW50cmFsIHRvIEZvbnRlcnJhJ3Mgc29jaWFsLW1lZGlhIGNvbnRlbnQuDQotIFRoZSBzaXgtdG9waWMgbW9kZWwgc2VwYXJhdGVzIHRoaXMgYnJvYWQgZGFpcnkgaWRlbnRpdHkgaW50byBtb3JlIHNwZWNpZmljIGNsdXN0ZXJzLiBCYXNlZCBvbiB0aGUgaGlnaC1wcm9iYWJpbGl0eSB0ZXJtcyBzaG93biBhYm92ZSwgdGhlIGNvbnRlbnQgaW5jbHVkZXMgdGhlbWVzIHJlbGF0ZWQgdG8gcHJvZHVjdC9taWxrIGNvbW11bmljYXRpb24sIGZhcm1pbmcgYW5kIGZhcm1lciBhY3Rpdml0eSwgY29tbXVuaXR5IG9yIHdvcmtmb3JjZSBhY3Rpdml0eSwgc2VydmljZS9zdXBwb3J0IG1lc3NhZ2VzLCBzdXN0YWluYWJpbGl0eS9nbG9iYWwgYnVzaW5lc3MsIGFuZCBjb3Jwb3JhdGUgcmVzdWx0cyBvciBwcmljaW5nIGNvbW11bmljYXRpb24uIFRvcGljIGxhYmVscyBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgZnJvbSB0aGUgdGVybXMgcmF0aGVyIHRoYW4gZnJvbSB0aGUgdG9waWMgbnVtYmVyIGl0c2VsZiBiZWNhdXNlIExEQSB0b3BpYyBudW1iZXJzIGhhdmUgbm8gaW5oZXJlbnQgbWVhbmluZy4NCi0gVHdpdHRlci9YIHJlcHJlc2VudHMgYXBwcm94aW1hdGVseSBgciByb3VuZCgxMDAgKiBtZWFuKGZvbnRlcnJhJHBsYXRmb3JtID09ICJUd2l0dGVyL1giKSwgMSlgJSBvZiB0aGUgcmVjb3Jkcywgc28gdGhlIG92ZXJhbGwgdGVybSBhbmQgdG9waWMgbWl4IGlzIGxpa2VseSB0byByZWZsZWN0IFR3aXR0ZXIvWCBjb250ZW50IHN0cm9uZ2x5LiBQbGF0Zm9ybS1zcGVjaWZpYyBjb250ZW50IHN0cmF0ZWdpZXMgc2hvdWxkIHRoZXJlZm9yZSBiZSBjb25zaWRlcmVkIHdoZW4gaW50ZXJwcmV0aW5nIHRoZSBwb29sZWQgdG9waWNzLg0KLSBMb25nIHRleHQsIGluY2x1ZGluZyBzb21lIHZpZGVvIGNhcHRpb25zIG9yIHRyYW5zY3JpcHRzLCBjb250cmlidXRlcyBtb3JlIHRva2VucyB0byByYXcgdGVybSBmcmVxdWVuY3kuIExEQSBjb21wbGVtZW50cyB0aGUgZnJlcXVlbmN5IGFuYWx5c2lzIGJ5IG1vZGVsbGluZyBjby1vY2N1cnJpbmcgdGVybXMgYXQgZG9jdW1lbnQgbGV2ZWwsIGJ1dCBib3RoIG1ldGhvZHMgcmVtYWluIGRlc2NyaXB0aXZlIHJhdGhlciB0aGFuIGNhdXNhbC4NCg0KIyMgTGV2ZWwgb2YgdXNlciBlbmdhZ2VtZW50IGFuZCBmYWN0b3JzIHRoYXQgaW5mbHVlbmNlIHVzZXIgZW5nYWdlbWVudA0KDQojIyMgRXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcw0KDQpUaGUgZW5nYWdlbWVudCB2YXJpYWJsZXMgaGF2ZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIGF2YWlsYWJpbGl0eSBhY3Jvc3MgcGxhdGZvcm1zLiBgbGlrZUNvdW50YCBpcyBjb21wbGV0ZSBmb3IgYWxsIHJlY29yZHMgYW5kIGlzIHRoZXJlZm9yZSB1c2VkIGFzIHRoZSBtYWluIGNvbXBhcmFibGUgZW5nYWdlbWVudCBvdXRjb21lIGFjcm9zcyBwbGF0Zm9ybXMsIG1lZGlhIHR5cGVzLCBhbmQgdG9waWNzLiBDb21tZW50cywgc2hhcmVzLCBhbmQgdmlkZW8gcGxheXMgYXJlIGFsc28gc3VtbWFyaXNlZCwgYnV0IHRoZWlyIHBsYXRmb3JtLXNwZWNpZmljIG1pc3NpbmduZXNzIGlzIHRha2VuIGludG8gYWNjb3VudC4NCg0KYGBge3IgcmVxXzNifQ0KIyBTdW1tYXJpc2UgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgYXZhaWxhYmlsaXR5IG9mIGVhY2ggZW5nYWdlbWVudCBtZXRyaWMNCmVuZ2FnZW1lbnRfc3VtbWFyeSA8LSBmb250ZXJyYSB8Pg0KICBzZWxlY3QobGlrZUNvdW50LCBjb21tZW50Q291bnQsIHNoYXJlQ291bnQsIHZpZGVvcGxheUNvdW50KSB8Pg0KICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJtZXRyaWMiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSB8Pg0KICBncm91cF9ieShtZXRyaWMpIHw+DQogIHN1bW1hcmlzZSgNCiAgICBhdmFpbGFibGVfbiA9IHN1bSghaXMubmEodmFsdWUpKSwNCiAgICBtaXNzaW5nX24gPSBzdW0oaXMubmEodmFsdWUpKSwNCiAgICBtZWFuID0gbWVhbih2YWx1ZSwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWRpYW4gPSBtZWRpYW4odmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgSVFSID0gSVFSKHZhbHVlLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1heGltdW0gPSBtYXgodmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIHw+DQogIG11dGF0ZShhY3Jvc3MoYyhtZWFuLCBtZWRpYW4sIElRUiwgbWF4aW11bSksIH4gcm91bmQoLiwgMikpKQ0KDQplbmdhZ2VtZW50X3N1bW1hcnkgfD4NCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiRGVzY3JpcHRpdmUgc3RhdGlzdGljcyBmb3IgdXNlci1lbmdhZ2VtZW50IG1ldHJpY3MiKQ0KDQojIFN1bW1hcmlzZSBsaWtlcyBieSBzb2NpYWwtbWVkaWEgcGxhdGZvcm0NCnBsYXRmb3JtX2xpa2Vfc3VtbWFyeSA8LSBmb250ZXJyYSB8Pg0KICBncm91cF9ieShwbGF0Zm9ybSkgfD4NCiAgc3VtbWFyaXNlKA0KICAgIHBvc3RzID0gbigpLA0KICAgIG1lYW5fbGlrZXMgPSBtZWFuKGxpa2VDb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWRpYW5fbGlrZXMgPSBtZWRpYW4obGlrZUNvdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIElRUl9saWtlcyA9IElRUihsaWtlQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgbWF4aW11bV9saWtlcyA9IG1heChsaWtlQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIHw+DQogIG11dGF0ZShhY3Jvc3MoYyhtZWFuX2xpa2VzLCBtZWRpYW5fbGlrZXMsIElRUl9saWtlcywgbWF4aW11bV9saWtlcyksIH4gcm91bmQoLiwgMikpKSB8Pg0KICBhcnJhbmdlKGRlc2MobWVkaWFuX2xpa2VzKSkNCg0KcGxhdGZvcm1fbGlrZV9zdW1tYXJ5IHw+DQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIkxpa2UgZW5nYWdlbWVudCBieSBwbGF0Zm9ybSIpDQoNCiMgVmlzdWFsaXNlIHRoZSBoaWdobHkgc2tld2VkIGxpa2UgZGlzdHJpYnV0aW9ucyB1c2luZyBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBmb3IgZGlzcGxheSBvbmx5DQpmb250ZXJyYSB8Pg0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHBsYXRmb3JtLCBsb2dfbGlrZXMsIEZVTiA9IG1lZGlhbiksIHkgPSBsb2dfbGlrZXMpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLmFscGhhID0gMC4yKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGxpa2VzIGJ5IHNvY2lhbC1tZWRpYSBwbGF0Zm9ybSIsDQogICAgeCA9ICJQbGF0Zm9ybSIsDQogICAgeSA9ICJsb2coMSArIGxpa2VzKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTEpDQoNCiMgU3VtbWFyaXNlIGxpa2VzIGJ5IHJlY29yZGVkIG1lZGlhIHR5cGU7IHJvd3Mgd2l0aCB1bnJlY29yZGVkIG1lZGlhIGFyZSBleGNsdWRlZA0KbWVkaWFfbGlrZV9zdW1tYXJ5IDwtIGZvbnRlcnJhIHw+DQogIGZpbHRlcighaXMubmEobWVkaWFfY2xlYW4pKSB8Pg0KICBncm91cF9ieShtZWRpYV9jbGVhbikgfD4NCiAgc3VtbWFyaXNlKA0KICAgIHBvc3RzID0gbigpLA0KICAgIG1lYW5fbGlrZXMgPSBtZWFuKGxpa2VDb3VudCksDQogICAgbWVkaWFuX2xpa2VzID0gbWVkaWFuKGxpa2VDb3VudCksDQogICAgSVFSX2xpa2VzID0gSVFSKGxpa2VDb3VudCksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIHw+DQogIG11dGF0ZShhY3Jvc3MoYyhtZWFuX2xpa2VzLCBtZWRpYW5fbGlrZXMsIElRUl9saWtlcyksIH4gcm91bmQoLiwgMikpKSB8Pg0KICBhcnJhbmdlKGRlc2MobWVkaWFuX2xpa2VzKSkNCg0KbWVkaWFfbGlrZV9zdW1tYXJ5IHw+DQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIkxpa2UgZW5nYWdlbWVudCBieSByZWNvcmRlZCBtZWRpYSB0eXBlIikNCg0KIyBDb25uZWN0IGVhY2ggbW9kZWwtZGVyaXZlZCB0b3BpYyB3aXRoIGl0cyBvYnNlcnZlZCBsaWtlIGVuZ2FnZW1lbnQNCnRvcGljX2VuZ2FnZW1lbnQgPC0gZm9udGVycmEgfD4NCiAgZmlsdGVyKCFpcy5uYShkb21pbmFudF90b3BpYykpIHw+DQogIGdyb3VwX2J5KGRvbWluYW50X3RvcGljKSB8Pg0KICBzdW1tYXJpc2UoDQogICAgcG9zdHMgPSBuKCksDQogICAgbWVhbl9saWtlcyA9IG1lYW4obGlrZUNvdW50KSwNCiAgICBtZWRpYW5fbGlrZXMgPSBtZWRpYW4obGlrZUNvdW50KSwNCiAgICBJUVJfbGlrZXMgPSBJUVIobGlrZUNvdW50KSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgfD4NCiAgbGVmdF9qb2luKHRvcGljX2tleXdvcmRfdGFibGUsIGJ5ID0gYygiZG9taW5hbnRfdG9waWMiID0gInRvcGljIikpIHw+DQogIG11dGF0ZShhY3Jvc3MoYyhtZWFuX2xpa2VzLCBtZWRpYW5fbGlrZXMsIElRUl9saWtlcyksIH4gcm91bmQoLiwgMikpKSB8Pg0KICBhcnJhbmdlKGRlc2MobWVkaWFuX2xpa2VzKSkNCg0KdG9waWNfZW5nYWdlbWVudCB8Pg0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJMaWtlIGVuZ2FnZW1lbnQgYnkgZG9taW5hbnQgTERBIHRvcGljIikNCg0KIyBDcmVhdGUgYSBsb2ctc2NhbGUgc2NhdHRlciBwbG90IGZvciBwb3N0cyB3aGVyZSB2aWRlby1wbGF5IGNvdW50cyBhcmUgYXZhaWxhYmxlDQp2aWRlb19kYXRhIDwtIGZvbnRlcnJhIHw+DQogIGZpbHRlcighaXMubmEodmlkZW9wbGF5Q291bnQpKQ0KDQpnZ3Bsb3QodmlkZW9fZGF0YSwgYWVzKHggPSBsb2cxcCh2aWRlb3BsYXlDb3VudCksIHkgPSBsb2cxcChsaWtlQ291bnQpKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zNSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQXNzb2NpYXRpb24gYmV0d2VlbiB2aWRlbyBwbGF5cyBhbmQgbGlrZXMiLA0KICAgIHggPSAibG9nKDEgKyB2aWRlbyBwbGF5cykiLA0KICAgIHkgPSAibG9nKDEgKyBsaWtlcykiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKQ0KYGBgDQoNClRoZSBsYXJnZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIG1lYW5zIGFuZCBtZWRpYW5zIHNob3cgdGhhdCBlbmdhZ2VtZW50IGlzIGhpZ2hseSByaWdodC1za2V3ZWQgYW5kIGluY2x1ZGVzIGEgc21hbGwgbnVtYmVyIG9mIHZlcnkgaGlnaC1lbmdhZ2VtZW50IHBvc3RzLiBGb3IgZXhhbXBsZSwgdGhlIG92ZXJhbGwgbWVkaWFuIGlzIGByIG1lZGlhbihmb250ZXJyYSRsaWtlQ291bnQpYCBsaWtlcyB3aGlsZSB0aGUgbWVhbiBpcyBgciByb3VuZChtZWFuKGZvbnRlcnJhJGxpa2VDb3VudCksIDIpYC4gVGhlcmVmb3JlLCBtZWRpYW5zLCBJUVJzLCBsb2ctc2NhbGUgdmlzdWFsaXNhdGlvbnMsIGFuZCBub24tcGFyYW1ldHJpYyB0ZXN0cyBhcmUgZW1waGFzaXNlZCByYXRoZXIgdGhhbiByZWx5aW5nIG9uIG1lYW5zIGFsb25lLg0KDQpUaGUgcGxhdGZvcm0gdGFibGUgc2hvd3Mgc3Vic3RhbnRpYWwgZGlmZmVyZW5jZXMgaW4gcmF3IGxpa2UgY291bnRzLiBUaGUgaGlnaGVzdCBtZWRpYW4tbGlrZSBwbGF0Zm9ybSBpbiB0aGlzIGRhdGFzZXQgaXMgKipgciBwbGF0Zm9ybV9saWtlX3N1bW1hcnkkcGxhdGZvcm1bMV1gKiosIHdpdGggYSBtZWRpYW4gb2YgYHIgcGxhdGZvcm1fbGlrZV9zdW1tYXJ5JG1lZGlhbl9saWtlc1sxXWAgbGlrZXMgcGVyIHBvc3QuIEhvd2V2ZXIsIHRoZXNlIGFyZSAqKmFic29sdXRlIGNvdW50cyByYXRoZXIgdGhhbiBlbmdhZ2VtZW50IHJhdGVzKio6IGZvbGxvd2VyIG51bWJlcnMsIGltcHJlc3Npb25zLCBhbmQgcmVhY2ggYXJlIG5vdCBhdmFpbGFibGUsIGFuZCB0aGUgcGxhdGZvcm1zIGFsc28gY292ZXIgZGlmZmVyZW50IGhpc3RvcmljYWwgcGVyaW9kcy4gVGhlIHBsYXRmb3JtIGNvbXBhcmlzb24gaXMgdGhlcmVmb3JlIGRlc2NyaXB0aXZlIGV2aWRlbmNlIG9mIGRpZmZlcmVudCBlbmdhZ2VtZW50IGRpc3RyaWJ1dGlvbnMsIG5vdCBwcm9vZiB0aGF0IG9uZSBwbGF0Zm9ybSBpcyBpbnRyaW5zaWNhbGx5IG1vcmUgZWZmZWN0aXZlLg0KDQpGb3IgcG9zdHMgd2hlcmUgbWVkaWEgdHlwZSB3YXMgcmVjb3JkZWQsIHRoZSBoaWdoZXN0IG1lZGlhbi1saWtlIGZvcm1hdCBpcyAqKmByIG1lZGlhX2xpa2Vfc3VtbWFyeSRtZWRpYV9jbGVhblsxXWAqKiwgd2l0aCBhIG1lZGlhbiBvZiBgciBtZWRpYV9saWtlX3N1bW1hcnkkbWVkaWFuX2xpa2VzWzFdYCBsaWtlcy4gVGhpcyByZXN1bHQgc2hvdWxkIGJlIGludGVycHJldGVkIGNhdXRpb3VzbHkgYmVjYXVzZSBtZWRpYSBhdmFpbGFiaWxpdHkgaXMgcGxhdGZvcm0tZGVwZW5kZW50IGFuZCBtZWRpYSB0eXBlIGlzIHRoZXJlZm9yZSBwYXJ0bHkgY29uZm91bmRlZCB3aXRoIHBsYXRmb3JtLg0KDQpUaGUgdG9waWMtZW5nYWdlbWVudCB0YWJsZSBjb25uZWN0cyB0aGUgTERBIHRoZW1lcyB3aXRoIGF1ZGllbmNlIHJlc3BvbnNlLiBJbiB0aGlzIG1vZGVsLCBUb3BpYyBgciB0b3BpY19lbmdhZ2VtZW50JGRvbWluYW50X3RvcGljWzFdYCBoYXMgdGhlIGhpZ2hlc3QgbWVkaWFuIGxpa2UgY291bnQgKGByIHRvcGljX2VuZ2FnZW1lbnQkbWVkaWFuX2xpa2VzWzFdYCkgYW5kIGlzIGNoYXJhY3RlcmlzZWQgYnkgdGhlIHRlcm1zICoqYHIgdG9waWNfZW5nYWdlbWVudCRrZXlfdGVybXNbMV1gKiouIFRoaXMgcHJvdmlkZXMgYSB1c2VmdWwgZGlyZWN0aW9uIGZvciBmdXR1cmUgY29udGVudCB0ZXN0aW5nLCBidXQgcG9vbGVkIHRvcGljIGRpZmZlcmVuY2VzIG1heSBhbHNvIHJlZmxlY3QgcGxhdGZvcm0sIHBvc3RpbmcgcGVyaW9kLCBvciBhdWRpZW5jZS1zaXplIGRpZmZlcmVuY2VzLg0KDQojIyMgQml2YXJpYXRlIGFuYWx5c2lzDQoNCkJlY2F1c2UgbGlrZXMgYXJlIGV4dHJlbWVseSByaWdodC1za2V3ZWQgd2l0aCBtYW55IGxvdyB2YWx1ZXMgYW5kIHNldmVyYWwgZXh0cmVtZSBvYnNlcnZhdGlvbnMsIEtydXNrYWwtV2FsbGlzIHRlc3RzIGFyZSB1c2VkIGFzIHRoZSBub24tcGFyYW1ldHJpYyBhbHRlcm5hdGl2ZSB0byBvbmUtd2F5IEFOT1ZBIHdoZW4gY29tcGFyaW5nIGxpa2VzIGFjcm9zcyBtb3JlIHRoYW4gdHdvIGdyb3Vwcy4gU3BlYXJtYW4ncyByYW5rIGNvcnJlbGF0aW9uIGlzIHVzZWQgdG8gYXNzZXNzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2aWRlbyBwbGF5cyBhbmQgbGlrZXMgd2l0aG91dCBhc3N1bWluZyBhIG5vcm1hbCBsaW5lYXIgcmVsYXRpb25zaGlwLg0KDQpgYGB7ciByZXFfM2JfYml2YXJpYXRlfQ0KIyBUZXN0IHdoZXRoZXIgbGlrZSBkaXN0cmlidXRpb25zIGRpZmZlciBhY3Jvc3Mgc29jaWFsLW1lZGlhIHBsYXRmb3Jtcw0Ka3dfcGxhdGZvcm0gPC0ga3J1c2thbC50ZXN0KGxpa2VDb3VudCB+IHBsYXRmb3JtLCBkYXRhID0gZm9udGVycmEpDQoNCiMgVGVzdCB3aGV0aGVyIGxpa2UgZGlzdHJpYnV0aW9ucyBkaWZmZXIgYWNyb3NzIHJlY29yZGVkIG1lZGlhIHR5cGVzDQptZWRpYV90ZXN0X2RhdGEgPC0gZm9udGVycmEgfD4NCiAgZmlsdGVyKCFpcy5uYShtZWRpYV9jbGVhbikpDQprd19tZWRpYSA8LSBrcnVza2FsLnRlc3QobGlrZUNvdW50IH4gbWVkaWFfY2xlYW4sIGRhdGEgPSBtZWRpYV90ZXN0X2RhdGEpDQoNCiMgVGVzdCB3aGV0aGVyIGxpa2UgZGlzdHJpYnV0aW9ucyBkaWZmZXIgYWNyb3NzIHRoZSBzaXggbW9kZWwtZGVyaXZlZCB0b3BpY3MNCnRvcGljX3Rlc3RfZGF0YSA8LSBmb250ZXJyYSB8Pg0KICBmaWx0ZXIoIWlzLm5hKHRvcGljX2ZhY3RvcikpDQprd190b3BpYyA8LSBrcnVza2FsLnRlc3QobGlrZUNvdW50IH4gdG9waWNfZmFjdG9yLCBkYXRhID0gdG9waWNfdGVzdF9kYXRhKQ0KDQojIFB1dCB0aGUgS3J1c2thbC1XYWxsaXMgcmVzdWx0cyBpbnRvIG9uZSBjb25jaXNlIHRhYmxlDQprcnVza2FsX3Jlc3VsdHMgPC0gdGliYmxlOjp0aWJibGUoDQogIGFuYWx5c2lzID0gYygiTGlrZXMgYnkgcGxhdGZvcm0iLCAiTGlrZXMgYnkgcmVjb3JkZWQgbWVkaWEgdHlwZSIsICJMaWtlcyBieSBMREEgdG9waWMiKSwNCiAgc3RhdGlzdGljID0gYyh1bm5hbWUoa3dfcGxhdGZvcm0kc3RhdGlzdGljKSwNCiAgICAgICAgICAgICAgICB1bm5hbWUoa3dfbWVkaWEkc3RhdGlzdGljKSwNCiAgICAgICAgICAgICAgICB1bm5hbWUoa3dfdG9waWMkc3RhdGlzdGljKSksDQogIGRmID0gYyh1bm5hbWUoa3dfcGxhdGZvcm0kcGFyYW1ldGVyKSwNCiAgICAgICAgIHVubmFtZShrd19tZWRpYSRwYXJhbWV0ZXIpLA0KICAgICAgICAgdW5uYW1lKGt3X3RvcGljJHBhcmFtZXRlcikpLA0KICBwX3ZhbHVlID0gYyhrd19wbGF0Zm9ybSRwLnZhbHVlLCBrd19tZWRpYSRwLnZhbHVlLCBrd190b3BpYyRwLnZhbHVlKQ0KKSB8Pg0KICBtdXRhdGUoDQogICAgc3RhdGlzdGljID0gcm91bmQoc3RhdGlzdGljLCAyKSwNCiAgICBwX2Rpc3BsYXkgPSBmb3JtYXQucHZhbChwX3ZhbHVlLCBkaWdpdHMgPSAzLCBlcHMgPSAwLjAwMSkNCiAgKSB8Pg0KICBzZWxlY3QoYW5hbHlzaXMsIHN0YXRpc3RpYywgZGYsIHBfZGlzcGxheSkNCg0Ka3J1c2thbF9yZXN1bHRzIHw+DQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIktydXNrYWwtV2FsbGlzIHRlc3RzIG9mIGZhY3RvcnMgYXNzb2NpYXRlZCB3aXRoIGxpa2UgZW5nYWdlbWVudCIpDQoNCiMgQXNzZXNzIHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIHZpZGVvIHBsYXlzIGFuZCBsaWtlcyBmb3IgcG9zdHMgd2l0aCB2aWRlby1wbGF5IGRhdGENCnZpZGVvX2xpa2VfY29ycmVsYXRpb24gPC0gY29yLnRlc3QoDQogIHZpZGVvX2RhdGEkdmlkZW9wbGF5Q291bnQsDQogIHZpZGVvX2RhdGEkbGlrZUNvdW50LA0KICBtZXRob2QgPSAic3BlYXJtYW4iLA0KICBleGFjdCA9IEZBTFNFDQopDQoNCnZpZGVvX2NvcnJlbGF0aW9uX3Jlc3VsdCA8LSB0aWJibGU6OnRpYmJsZSgNCiAgb2JzZXJ2YXRpb25zID0gbnJvdyh2aWRlb19kYXRhKSwNCiAgc3BlYXJtYW5fcmhvID0gcm91bmQodW5uYW1lKHZpZGVvX2xpa2VfY29ycmVsYXRpb24kZXN0aW1hdGUpLCAzKSwNCiAgcF92YWx1ZSA9IHZpZGVvX2xpa2VfY29ycmVsYXRpb24kcC52YWx1ZSwNCiAgcF9kaXNwbGF5ID0gZm9ybWF0LnB2YWwodmlkZW9fbGlrZV9jb3JyZWxhdGlvbiRwLnZhbHVlLCBkaWdpdHMgPSAzLCBlcHMgPSAwLjAwMSkNCikNCg0KdmlkZW9fY29ycmVsYXRpb25fcmVzdWx0IHw+DQogIHNlbGVjdChvYnNlcnZhdGlvbnMsIHNwZWFybWFuX3JobywgcF9kaXNwbGF5KSB8Pg0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJTcGVhcm1hbiBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZpZGVvIHBsYXlzIGFuZCBsaWtlcyIpDQpgYGANCg0KIyMjIENvbW1lbnRhcnkgb24gaW5zaWdodHMgYW5kIHJlY29tbWVuZGF0aW9ucw0KDQotIFRoZSBLcnVza2FsLVdhbGxpcyByZXN1bHRzIHNob3cgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBpbiBsaWtlIGRpc3RyaWJ1dGlvbnMgYWNyb3NzICoqcGxhdGZvcm1zKiosICoqcmVjb3JkZWQgbWVkaWEgdHlwZXMqKiwgYW5kICoqbW9kZWwtZGVyaXZlZCBjb250ZW50IHRvcGljcyoqIChgcCA8IC4wMDFgIGZvciBlYWNoIGNvbXBhcmlzb24pLiBUaGVzZSB0ZXN0cyBlc3RhYmxpc2ggYXNzb2NpYXRpb24sIG5vdCBjYXVzYXRpb24uIEluIHBhcnRpY3VsYXIsIHBsYXRmb3JtIGF1ZGllbmNlIHNpemUsIGhpc3RvcmljYWwgY292ZXJhZ2UsIGFuZCB0aGUgdW5ldmVuIGF2YWlsYWJpbGl0eSBvZiBtZWRpYSBpbmZvcm1hdGlvbiBjb3VsZCBjb250cmlidXRlIHRvIHRoZSBvYnNlcnZlZCBkaWZmZXJlbmNlcy4NCi0gVmlkZW8gcGxheXMgYW5kIGxpa2VzIHNob3cgYSBwb3NpdGl2ZSBTcGVhcm1hbiBhc3NvY2lhdGlvbiBvZiBhcHByb3hpbWF0ZWx5ICoqYHIgcm91bmQodW5uYW1lKHZpZGVvX2xpa2VfY29ycmVsYXRpb24kZXN0aW1hdGUpLCAyKWAqKiBhY3Jvc3MgYHIgbnJvdyh2aWRlb19kYXRhKWAgcG9zdHMgd2l0aCB2aWRlby1wbGF5IGRhdGEgKGBwIDwgLjAwMWApLiBQb3N0cyB0aGF0IHJlY2VpdmUgbW9yZSB2aWRlbyBwbGF5cyBhbHNvIHRlbmQgdG8gcmVjZWl2ZSBtb3JlIGxpa2VzLCBhbHRob3VnaCB0aGlzIGRvZXMgbm90IHNob3cgdGhhdCB2aWRlbyBwbGF5cyBjYXVzZSBsaWtlcyBhbmQgdmlkZW8tcGxheSBkYXRhIGFyZSBhdmFpbGFibGUgb25seSBmb3Igc2VsZWN0ZWQgcGxhdGZvcm1zL3Bvc3RzLg0KLSBGb250ZXJyYSBzaG91bGQgdXNlICoqcGxhdGZvcm0tc3BlY2lmaWMgYmVuY2htYXJrcyoqIHJhdGhlciB0aGFuIGNvbXBhcmluZyByYXcgbGlrZSBjb3VudHMgYXMgdGhvdWdoIGFsbCBwbGF0Zm9ybXMgaGF2ZSB0aGUgc2FtZSBhdWRpZW5jZSBzaXplLCByZWFjaCwgYWdlLCBhbmQgY29udGVudCBtaXguIFRoZSBjdXJyZW50IGRhdGEgZG8gbm90IGNvbnRhaW4gZm9sbG93ZXJzLCBpbXByZXNzaW9ucywgb3IgcmVhY2gsIHNvIGEgdHJ1ZSBlbmdhZ2VtZW50IHJhdGUgY2Fubm90IGJlIGNhbGN1bGF0ZWQuDQotIFRoZSBoaWdoZXItbWVkaWFuIG1lZGlhIGZvcm1hdCBpZGVudGlmaWVkIGluIHRoZSBtZWRpYSB0YWJsZSBzaG91bGQgYmUgdHJlYXRlZCBhcyBhICoqdGVzdGFibGUgY29udGVudCBoeXBvdGhlc2lzKiosIG5vdCBhcyBhIHVuaXZlcnNhbCBydWxlLiBBIHByYWN0aWNhbCBuZXh0IHN0ZXAgd291bGQgYmUgY29udHJvbGxlZCBwbGF0Zm9ybS1sZXZlbCB0ZXN0aW5nIG9mIGltYWdlLCBzaWRlY2FyL2Nhcm91c2VsLCBhbmQgdmlkZW8gZm9ybWF0cyB3aGlsZSBjb2xsZWN0aW5nIHJlYWNoL2ltcHJlc3Npb24gZGF0YS4NCi0gVGhlIHRvcGljLWVuZ2FnZW1lbnQgdGFibGUgcHJvdmlkZXMgYSBkYXRhLWluZm9ybWVkIHdheSB0byBpZGVudGlmeSB0aGVtZXMgdGhhdCBjdXJyZW50bHkgcmVjZWl2ZSBzdHJvbmdlciBhdWRpZW5jZSByZXNwb25zZS4gRm9udGVycmEgc2hvdWxkIHRlc3QgYSBsYXJnZXIgc2hhcmUgb2YgdGhlIGhpZ2hlci1tZWRpYW4gdG9waWMocykgd2l0aGluIHRoZSBzYW1lIHBsYXRmb3JtIGFuZCB0aW1lIHBlcmlvZCB3aGlsZSBtb25pdG9yaW5nIHdoZXRoZXIgdGhlIGFkdmFudGFnZSByZW1haW5zIGFmdGVyIGFjY291bnRpbmcgZm9yIGF1ZGllbmNlIHJlYWNoLg0KLSBGb3IgdmlkZW8gY29udGVudCwgRm9udGVycmEgc2hvdWxkIHRyYWNrIG5vdCBvbmx5IHZpZGVvIHBsYXlzIGJ1dCBhbHNvICoqcGxheS10by1saWtlLCBwbGF5LXRvLWNvbW1lbnQsIGFuZCBwbGF5LXRvLXNoYXJlIGNvbnZlcnNpb24qKiB3aGVyZSB0aGUgcmVsZXZhbnQgbWV0cmljcyBhcmUgYXZhaWxhYmxlLiBUaGlzIHdvdWxkIGRpc3Rpbmd1aXNoIGV4cG9zdXJlIGZyb20gYWN0aXZlIGVuZ2FnZW1lbnQuDQotIEZ1dHVyZSBzb2NpYWwtbWVkaWEgZXh0cmFjdGlvbiBzaG91bGQgY29sbGVjdCAqKmNvbnNpc3RlbnQgZW5nYWdlbWVudCBmaWVsZHMsIGltcHJlc3Npb25zL3JlYWNoLCBmb2xsb3dlciBjb3VudHMsIGFuZCBtZWRpYSB0eXBlIGFjcm9zcyBhbGwgcGxhdGZvcm1zKiouIFRoZXNlIGFkZGl0aW9ucyB3b3VsZCBhbGxvdyBmYWlyZXIgZW5nYWdlbWVudC1yYXRlIGNvbXBhcmlzb25zIGFuZCBzdHJvbmdlciBhbmFseXNpcyBvZiB3aGljaCBjb250ZW50IGNoYXJhY3RlcmlzdGljcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGVuZ2FnZW1lbnQuDQotIEZpbmFsbHksIHRoZSBkYXRhc2V0IHNwYW5zIGZyb20gYHIgZm9ybWF0KG1pbihmb250ZXJyYSRwb3N0X2RhdGUpLCAiJWQgJUIgJVkiKWAgdG8gYHIgZm9ybWF0KG1heChmb250ZXJyYSRwb3N0X2RhdGUpLCAiJWQgJUIgJVkiKWAuIFBsYXRmb3JtIHVzYWdlLCBhdWRpZW5jZSBzaXplLCBhbGdvcml0aG1zLCBhbmQgY29udGVudCBzdHJhdGVneSBjYW4gY2hhbmdlIG1hdGVyaWFsbHkgb3ZlciBzdWNoIGEgbG9uZyBwZXJpb2QuIEZ1dHVyZSBkZWNpc2lvbi1tYWtpbmcgc2hvdWxkIHRoZXJlZm9yZSByZXBlYXQgdGhlIGFuYWx5c2lzIG9uIGEgcmVjZW50IGNvbW1vbiB0aW1lIHdpbmRvdyBhbmQgY29tcGFyZSBsaWtlLWZvci1saWtlIHBlcmlvZHMgd2l0aGluIGVhY2ggcGxhdGZvcm0uDQoNCiMgR2VuQUkgdXNlIGRlY2xhcmF0aW9uDQoNCkNoYXRHUFQgd2FzIHVzZWQgYXMgYSBndWlkZWQgc3VwcG9ydGluZyB0b29sIHRvIGhlbHAgaW50ZXJwcmV0IHRoZSBhc3NpZ25tZW50IHJlcXVpcmVtZW50cywgc3RydWN0dXJlIHRoZSBSIE1hcmtkb3duIHdvcmtmbG93LCByZXZpZXcgUiBzeW50YXgsIGFuZCBpZGVudGlmeSBzdWl0YWJsZSBhcHByb2FjaGVzIGZvciBkYXRhIHByZXBhcmF0aW9uLCB0ZXh0IGFuYWx5c2lzLCB0b3BpYyBtb2RlbGxpbmcsIGV4cGxvcmF0b3J5IGFuYWx5c2lzLCBhbmQgYml2YXJpYXRlIHN0YXRpc3RpY2FsIHRlc3RpbmcuIEdlbkFJIHdhcyBub3QgdXNlZCB0byBtYW51YWxseSBhbHRlciB0aGUgc291cmNlIGRhdGFzZXQuIERhdGEtZGVwZW5kZW50IHN0YXRlbWVudHMgd2VyZSBjcm9zcy1jaGVja2VkIGFnYWluc3QgdGhlIHN1cHBsaWVkIGBmb250ZXJyYS5jc3ZgLCBpbmNsdWRpbmcgdGhlIGRhdGFzZXQgc3RydWN0dXJlLCBtaXNzaW5nLXZhbHVlIHBhdHRlcm5zLCBlbmdhZ2VtZW50IHN1bW1hcmllcywgYW5kIHRoZSBzdWl0YWJpbGl0eSBvZiB0aGUgc2VsZWN0ZWQgc3RhdGlzdGljYWwgbWV0aG9kcy4NCg0K