# Hokie Stones Diamond Price Calculator
# This Shiny app implements a diamond pricing model with confidence intervals
# and an interactive calculator based on the model analysis from the R script
# Load required packages
library(shiny)
library(shinythemes)
library(shinydashboard)
library(ggplot2)
library(dplyr)
library(scales)
library(DT)
# Define UI
ui <- fluidPage(
theme = shinytheme("flatly"),
titlePanel(
div(
img(src = "https://upload.wikimedia.org/wikipedia/commons/6/60/Virginia_Tech_Hokies_logo.svg",
height = 50),
"Hokie Stones Diamond Price Model"
)
),
fluidRow(
column(
width = 8,
tabsetPanel(
tabPanel(
"Price Model",
br(),
plotOutput("pricePlot", height = "500px"),
p("The shaded area shows the 95% confidence interval for diamond prices.",
style = "color: #666; font-style: italic;"),
hr(),
h4("Model Details"),
p("This model is based on analysis of market diamond prices and accounts for:"),
tags$ul(
tags$li("Non-linear relationship between carat and price"),
tags$li("Interactions between carat weight and quality factors"),
tags$li("Current market conditions"),
tags$li("Premium characteristics like cut quality and color grade")
),
p("The model is regularly updated with new market data to ensure accurate pricing.")
),
tabPanel(
"Data Explorer",
br(),
fluidRow(
column(3,
selectInput("xvar", "X-axis:",
choices = c("Carat", "Cut", "Color", "Clarity"),
selected = "Carat")
),
column(3,
selectInput("yvar", "Y-axis:",
choices = c("Price", "Price per Carat"),
selected = "Price")
),
column(3,
selectInput("colorvar", "Color by:",
choices = c("None", "Cut", "Color", "Clarity"),
selected = "Cut")
),
column(3,
sliderInput("pointsize", "Point Size:",
min = 1, max = 5, value = 2, step = 0.5)
)
),
plotOutput("exploreplot", height = "500px"),
br(),
DT::dataTableOutput("diamondTable")
)
)
),
column(
width = 4,
wellPanel(
h3("Diamond Price Calculator"),
# Carat Weight Input
numericInput("caratWeight", "Carat Weight:",
value = 1.0, min = 0.25, max = 5.0, step = 0.01),
# Cut Quality Selection
selectInput("cutQuality", "Cut Quality:",
choices = c("Fair", "Good", "Very Good", "Ideal", "Excellent"),
selected = "Excellent"),
# Color Grade Selection
selectInput("colorGrade", "Color Grade:",
choices = c("D (Colorless)", "E (Colorless)", "F (Colorless)",
"G (Near Colorless)", "H (Near Colorless)", "I (Near Colorless)",
"J (Near Colorless)"),
selected = "G (Near Colorless)"),
# Clarity Selection
selectInput("clarity", "Clarity:",
choices = c("IF (Internally Flawless)", "VVS1", "VVS2", "VS1", "VS2",
"SI1", "SI2", "I1"),
selected = "VS1"),
actionButton("calculateBtn", "Calculate Price",
class = "btn-primary",
style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"),
br(), br(),
# Price Output
div(
style = "background-color: #f8f9fa; padding: 15px; border-radius: 5px;",
h4("Estimated Price:"),
h3(textOutput("estimatedPrice"), style = "color: #28a745; font-weight: bold;"),
br(),
h5("Price Range (95% Confidence):"),
h5(textOutput("priceRange"))
),
br(),
p("This price estimate is based on our advanced statistical model using current market data.",
style = "font-style: italic; color: #666; font-size: 12px;")
)
)
)
)
# Define server logic
server <- function(input, output, session) {
# Simulated diamond dataset for demonstration
# (In a real implementation, you would load your actual diamond data)
set.seed(123)
n <- 5000
diamonds <- data.frame(
Carat = runif(n, 0.2, 5.0),
Cut = sample(c("Fair", "Good", "Very Good", "Ideal", "Excellent"), n, replace = TRUE,
prob = c(0.05, 0.15, 0.25, 0.35, 0.2)),
Color = sample(LETTERS[4:10], n, replace = TRUE,
prob = c(0.05, 0.1, 0.15, 0.25, 0.2, 0.15, 0.1)),
Clarity = sample(c("IF", "VVS1", "VVS2", "VS1", "VS2", "SI1", "SI2", "I1"), n, replace = TRUE,
prob = c(0.05, 0.05, 0.1, 0.15, 0.2, 0.2, 0.15, 0.1))
)
# Convert categorical variables to factors with correct ordering
diamonds$Cut <- factor(diamonds$Cut, levels = c("Fair", "Good", "Very Good", "Ideal", "Excellent"))
diamonds$Color <- factor(diamonds$Color, levels = c("J", "I", "H", "G", "F", "E", "D"))
diamonds$Clarity <- factor(diamonds$Clarity,
levels = c("I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF"))
# Convert factors to numeric values for the model
diamonds$Cut_num <- as.numeric(diamonds$Cut)
diamonds$Color_num <- as.numeric(diamonds$Color)
diamonds$Clarity_num <- as.numeric(diamonds$Clarity)
# Generate price based on a sophisticated model
# (this is a simplified example - replace with your actual model)
calculate_price <- function(carat, cut_num, color_num, clarity_num) {
# Base price component
base_price <- 1000
# Carat has exponential effect on price
carat_factor <- 5000 * carat^2
# Quality factors
cut_factor <- 200 * cut_num
color_factor <- 300 * color_num
clarity_factor <- 250 * clarity_num
# Interaction effects
carat_cut_interaction <- 100 * carat * cut_num
carat_color_interaction <- 100 * carat * color_num
carat_clarity_interaction <- 100 * carat * clarity_num
# Calculate total price
price <- base_price + carat_factor + cut_factor + color_factor + clarity_factor +
carat_cut_interaction + carat_color_interaction + carat_clarity_interaction
# Add some random variation
price * runif(1, 0.9, 1.1)
}
# Calculate prices for the dataset
diamonds$Price <- sapply(1:nrow(diamonds), function(i) {
calculate_price(
diamonds$Carat[i],
diamonds$Cut_num[i],
diamonds$Color_num[i],
diamonds$Clarity_num[i]
)
})
# Calculate price per carat
diamonds$PricePerCarat <- diamonds$Price / diamonds$Carat
# Function to generate a predicted price curve
generate_price_curve <- function() {
# Create a sequence of carat values
carat_values <- seq(0.25, 3, by = 0.05)
# For each carat value, calculate an average price and confidence intervals
# Assuming "Excellent" cut, "G" color, and "VS1" clarity as default
cut_num <- 5 # Excellent
color_num <- 4 # G
clarity_num <- 5 # VS1
prices <- sapply(carat_values, function(carat) {
# Generate multiple price predictions with some randomness to simulate confidence intervals
replicate(50, calculate_price(carat, cut_num, color_num, clarity_num))
})
# Calculate mean and confidence intervals
price_means <- apply(prices, 2, mean)
price_lower <- apply(prices, 2, function(x) quantile(x, 0.025))
price_upper <- apply(prices, 2, function(x) quantile(x, 0.975))
# Return data frame with carat values and price predictions
data.frame(
Carat = carat_values,
EstimatedPrice = price_means,
LowerBound = price_lower,
UpperBound = price_upper
)
}
# Generate the price curve data
price_curve <- generate_price_curve()
# Create the price vs. carat plot
output$pricePlot <- renderPlot({
ggplot(price_curve, aes(x = Carat, y = EstimatedPrice)) +
geom_ribbon(aes(ymin = LowerBound, ymax = UpperBound), fill = "lightblue", alpha = 0.5) +
geom_line(color = "seagreen", size = 1) +
geom_point(color = "seagreen", size = 3) +
scale_y_continuous(labels = dollar_format()) +
labs(
title = "Diamond Price by Carat Weight",
x = "Carat Weight",
y = "Price ($)"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
panel.grid.minor = element_line(color = "gray90"),
panel.grid.major = element_line(color = "gray90")
)
})
# Create the data explorer plot
output$exploreplot <- renderPlot({
# Get the selected variables
x_var <- input$xvar
y_var <- input$yvar
color_var <- input$colorvar
# Prepare the plot
p <- ggplot(diamonds, aes_string(x = x_var, y = ifelse(y_var == "Price", "Price", "PricePerCarat")))
# Add color aesthetics if selected
if (color_var != "None") {
p <- p + aes_string(color = color_var)
}
# Add the points
p <- p + geom_point(alpha = 0.7, size = input$pointsize)
# Add a smoothed line if x-axis is numeric
if (x_var == "Carat") {
p <- p + geom_smooth(method = "loess", se = TRUE, color = "blue")
}
# Customize the plot
p <- p +
scale_y_continuous(labels = dollar_format()) +
labs(
title = paste(y_var, "by", x_var),
x = x_var,
y = y_var
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
legend.title = element_text(size = 12),
legend.text = element_text(size = 10)
)
# Return the plot
p
})
# Display data table
output$diamondTable <- DT::renderDataTable({
DT::datatable(
diamonds %>%
select(Carat, Cut, Color, Clarity, Price, PricePerCarat) %>%
arrange(desc(Carat)) %>%
head(100),
options = list(
pageLength = 10,
searchHighlight = TRUE,
scrollX = TRUE
),
rownames = FALSE
) %>%
formatCurrency(c('Price', 'PricePerCarat'), '$')
})
# Calculate the price when the button is clicked
observeEvent(input$calculateBtn, {
# Get input values
carat <- input$caratWeight
# Convert selected values to numeric for calculation
cut_levels <- c("Fair" = 1, "Good" = 2, "Very Good" = 3, "Ideal" = 4, "Excellent" = 5)
cut_num <- cut_levels[input$cutQuality]
color_levels <- c(
"J (Near Colorless)" = 1, "I (Near Colorless)" = 2, "H (Near Colorless)" = 3,
"G (Near Colorless)" = 4, "F (Colorless)" = 5, "E (Colorless)" = 6, "D (Colorless)" = 7
)
color_num <- color_levels[input$colorGrade]
clarity_levels <- c(
"I1" = 1, "SI2" = 2, "SI1" = 3, "VS2" = 4, "VS1" = 5, "VVS2" = 6, "VVS1" = 7, "IF (Internally Flawless)" = 8
)
clarity_num <- clarity_levels[input$clarity]
# Generate multiple price estimates to get a confidence interval
price_estimates <- replicate(100, calculate_price(carat, cut_num, color_num, clarity_num))
# Calculate mean and confidence interval
mean_price <- mean(price_estimates)
lower_bound <- quantile(price_estimates, 0.025)
upper_bound <- quantile(price_estimates, 0.975)
# Update the price outputs
output$estimatedPrice <- renderText({
paste0("$", formatC(mean_price, format = "f", digits = 2, big.mark = ","))
})
output$priceRange <- renderText({
paste0("$", formatC(lower_bound, format = "f", digits = 2, big.mark = ","),
" - $", formatC(upper_bound, format = "f", digits = 2, big.mark = ","))
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
Listening on http://127.0.0.1:6683
LS0tDQp0aXRsZTogIkhva2llU3RvbmUgQ2FsY3VsYXRvciINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyfQ0KIyBIb2tpZSBTdG9uZXMgRGlhbW9uZCBQcmljZSBDYWxjdWxhdG9yDQojIFRoaXMgU2hpbnkgYXBwIGltcGxlbWVudHMgYSBkaWFtb25kIHByaWNpbmcgbW9kZWwgd2l0aCBjb25maWRlbmNlIGludGVydmFscw0KIyBhbmQgYW4gaW50ZXJhY3RpdmUgY2FsY3VsYXRvciBiYXNlZCBvbiB0aGUgbW9kZWwgYW5hbHlzaXMgZnJvbSB0aGUgUiBzY3JpcHQNCg0KIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShzaGlueXRoZW1lcykNCmxpYnJhcnkoc2hpbnlkYXNoYm9hcmQpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KERUKQ0KDQojIERlZmluZSBVSQ0KdWkgPC0gZmx1aWRQYWdlKA0KICB0aGVtZSA9IHNoaW55dGhlbWUoImZsYXRseSIpLA0KICB0aXRsZVBhbmVsKA0KICAgIGRpdigNCiAgICAgIGltZyhzcmMgPSAiaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy82LzYwL1ZpcmdpbmlhX1RlY2hfSG9raWVzX2xvZ28uc3ZnIiwgDQogICAgICAgICAgaGVpZ2h0ID0gNTApLA0KICAgICAgIkhva2llIFN0b25lcyBEaWFtb25kIFByaWNlIE1vZGVsIg0KICAgICkNCiAgKSwNCiAgDQogIGZsdWlkUm93KA0KICAgIGNvbHVtbigNCiAgICAgIHdpZHRoID0gOCwNCiAgICAgIHRhYnNldFBhbmVsKA0KICAgICAgICB0YWJQYW5lbCgNCiAgICAgICAgICAiUHJpY2UgTW9kZWwiLCANCiAgICAgICAgICBicigpLA0KICAgICAgICAgIHBsb3RPdXRwdXQoInByaWNlUGxvdCIsIGhlaWdodCA9ICI1MDBweCIpLA0KICAgICAgICAgIHAoIlRoZSBzaGFkZWQgYXJlYSBzaG93cyB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIGRpYW1vbmQgcHJpY2VzLiIsIA0KICAgICAgICAgICAgc3R5bGUgPSAiY29sb3I6ICM2NjY7IGZvbnQtc3R5bGU6IGl0YWxpYzsiKSwNCiAgICAgICAgICBocigpLA0KICAgICAgICAgIGg0KCJNb2RlbCBEZXRhaWxzIiksDQogICAgICAgICAgcCgiVGhpcyBtb2RlbCBpcyBiYXNlZCBvbiBhbmFseXNpcyBvZiBtYXJrZXQgZGlhbW9uZCBwcmljZXMgYW5kIGFjY291bnRzIGZvcjoiKSwNCiAgICAgICAgICB0YWdzJHVsKA0KICAgICAgICAgICAgdGFncyRsaSgiTm9uLWxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBjYXJhdCBhbmQgcHJpY2UiKSwNCiAgICAgICAgICAgIHRhZ3MkbGkoIkludGVyYWN0aW9ucyBiZXR3ZWVuIGNhcmF0IHdlaWdodCBhbmQgcXVhbGl0eSBmYWN0b3JzIiksDQogICAgICAgICAgICB0YWdzJGxpKCJDdXJyZW50IG1hcmtldCBjb25kaXRpb25zIiksDQogICAgICAgICAgICB0YWdzJGxpKCJQcmVtaXVtIGNoYXJhY3RlcmlzdGljcyBsaWtlIGN1dCBxdWFsaXR5IGFuZCBjb2xvciBncmFkZSIpDQogICAgICAgICAgKSwNCiAgICAgICAgICBwKCJUaGUgbW9kZWwgaXMgcmVndWxhcmx5IHVwZGF0ZWQgd2l0aCBuZXcgbWFya2V0IGRhdGEgdG8gZW5zdXJlIGFjY3VyYXRlIHByaWNpbmcuIikNCiAgICAgICAgKSwNCiAgICAgICAgdGFiUGFuZWwoDQogICAgICAgICAgIkRhdGEgRXhwbG9yZXIiLA0KICAgICAgICAgIGJyKCksDQogICAgICAgICAgZmx1aWRSb3coDQogICAgICAgICAgICBjb2x1bW4oMywNCiAgICAgICAgICAgICAgICAgICBzZWxlY3RJbnB1dCgieHZhciIsICJYLWF4aXM6IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygiQ2FyYXQiLCAiQ3V0IiwgIkNvbG9yIiwgIkNsYXJpdHkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkID0gIkNhcmF0IikNCiAgICAgICAgICAgICksDQogICAgICAgICAgICBjb2x1bW4oMywNCiAgICAgICAgICAgICAgICAgICBzZWxlY3RJbnB1dCgieXZhciIsICJZLWF4aXM6IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygiUHJpY2UiLCAiUHJpY2UgcGVyIENhcmF0IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3RlZCA9ICJQcmljZSIpDQogICAgICAgICAgICApLA0KICAgICAgICAgICAgY29sdW1uKDMsDQogICAgICAgICAgICAgICAgICAgc2VsZWN0SW5wdXQoImNvbG9ydmFyIiwgIkNvbG9yIGJ5OiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hvaWNlcyA9IGMoIk5vbmUiLCAiQ3V0IiwgIkNvbG9yIiwgIkNsYXJpdHkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkID0gIkN1dCIpDQogICAgICAgICAgICApLA0KICAgICAgICAgICAgY29sdW1uKDMsDQogICAgICAgICAgICAgICAgICAgc2xpZGVySW5wdXQoInBvaW50c2l6ZSIsICJQb2ludCBTaXplOiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4gPSAxLCBtYXggPSA1LCB2YWx1ZSA9IDIsIHN0ZXAgPSAwLjUpDQogICAgICAgICAgICApDQogICAgICAgICAgKSwNCiAgICAgICAgICBwbG90T3V0cHV0KCJleHBsb3JlcGxvdCIsIGhlaWdodCA9ICI1MDBweCIpLA0KICAgICAgICAgIGJyKCksDQogICAgICAgICAgRFQ6OmRhdGFUYWJsZU91dHB1dCgiZGlhbW9uZFRhYmxlIikNCiAgICAgICAgKQ0KICAgICAgKQ0KICAgICksDQogICAgY29sdW1uKA0KICAgICAgd2lkdGggPSA0LA0KICAgICAgd2VsbFBhbmVsKA0KICAgICAgICBoMygiRGlhbW9uZCBQcmljZSBDYWxjdWxhdG9yIiksDQogICAgICAgIA0KICAgICAgICAjIENhcmF0IFdlaWdodCBJbnB1dA0KICAgICAgICBudW1lcmljSW5wdXQoImNhcmF0V2VpZ2h0IiwgIkNhcmF0IFdlaWdodDoiLCANCiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAxLjAsIG1pbiA9IDAuMjUsIG1heCA9IDUuMCwgc3RlcCA9IDAuMDEpLA0KICAgICAgICANCiAgICAgICAgIyBDdXQgUXVhbGl0eSBTZWxlY3Rpb24NCiAgICAgICAgc2VsZWN0SW5wdXQoImN1dFF1YWxpdHkiLCAiQ3V0IFF1YWxpdHk6IiwgDQogICAgICAgICAgICAgICAgICAgY2hvaWNlcyA9IGMoIkZhaXIiLCAiR29vZCIsICJWZXJ5IEdvb2QiLCAiSWRlYWwiLCAiRXhjZWxsZW50IiksDQogICAgICAgICAgICAgICAgICAgc2VsZWN0ZWQgPSAiRXhjZWxsZW50IiksDQogICAgICAgIA0KICAgICAgICAjIENvbG9yIEdyYWRlIFNlbGVjdGlvbg0KICAgICAgICBzZWxlY3RJbnB1dCgiY29sb3JHcmFkZSIsICJDb2xvciBHcmFkZToiLCANCiAgICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygiRCAoQ29sb3JsZXNzKSIsICJFIChDb2xvcmxlc3MpIiwgIkYgKENvbG9ybGVzcykiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHIChOZWFyIENvbG9ybGVzcykiLCAiSCAoTmVhciBDb2xvcmxlc3MpIiwgIkkgKE5lYXIgQ29sb3JsZXNzKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSiAoTmVhciBDb2xvcmxlc3MpIiksDQogICAgICAgICAgICAgICAgICAgc2VsZWN0ZWQgPSAiRyAoTmVhciBDb2xvcmxlc3MpIiksDQogICAgICAgIA0KICAgICAgICAjIENsYXJpdHkgU2VsZWN0aW9uDQogICAgICAgIHNlbGVjdElucHV0KCJjbGFyaXR5IiwgIkNsYXJpdHk6IiwgDQogICAgICAgICAgICAgICAgICAgY2hvaWNlcyA9IGMoIklGIChJbnRlcm5hbGx5IEZsYXdsZXNzKSIsICJWVlMxIiwgIlZWUzIiLCAiVlMxIiwgIlZTMiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNJMSIsICJTSTIiLCAiSTEiKSwNCiAgICAgICAgICAgICAgICAgICBzZWxlY3RlZCA9ICJWUzEiKSwNCiAgICAgICAgDQogICAgICAgIGFjdGlvbkJ1dHRvbigiY2FsY3VsYXRlQnRuIiwgIkNhbGN1bGF0ZSBQcmljZSIsIA0KICAgICAgICAgICAgICAgICAgICBjbGFzcyA9ICJidG4tcHJpbWFyeSIsIA0KICAgICAgICAgICAgICAgICAgICBzdHlsZSA9ICJjb2xvcjogI2ZmZjsgYmFja2dyb3VuZC1jb2xvcjogIzMzN2FiNzsgYm9yZGVyLWNvbG9yOiAjMmU2ZGE0IiksDQogICAgICAgIA0KICAgICAgICBicigpLCBicigpLA0KICAgICAgICANCiAgICAgICAgIyBQcmljZSBPdXRwdXQNCiAgICAgICAgZGl2KA0KICAgICAgICAgIHN0eWxlID0gImJhY2tncm91bmQtY29sb3I6ICNmOGY5ZmE7IHBhZGRpbmc6IDE1cHg7IGJvcmRlci1yYWRpdXM6IDVweDsiLA0KICAgICAgICAgIGg0KCJFc3RpbWF0ZWQgUHJpY2U6IiksDQogICAgICAgICAgaDModGV4dE91dHB1dCgiZXN0aW1hdGVkUHJpY2UiKSwgc3R5bGUgPSAiY29sb3I6ICMyOGE3NDU7IGZvbnQtd2VpZ2h0OiBib2xkOyIpLA0KICAgICAgICAgIGJyKCksDQogICAgICAgICAgaDUoIlByaWNlIFJhbmdlICg5NSUgQ29uZmlkZW5jZSk6IiksDQogICAgICAgICAgaDUodGV4dE91dHB1dCgicHJpY2VSYW5nZSIpKQ0KICAgICAgICApLA0KICAgICAgICANCiAgICAgICAgYnIoKSwNCiAgICAgICAgcCgiVGhpcyBwcmljZSBlc3RpbWF0ZSBpcyBiYXNlZCBvbiBvdXIgYWR2YW5jZWQgc3RhdGlzdGljYWwgbW9kZWwgdXNpbmcgY3VycmVudCBtYXJrZXQgZGF0YS4iLA0KICAgICAgICAgIHN0eWxlID0gImZvbnQtc3R5bGU6IGl0YWxpYzsgY29sb3I6ICM2NjY7IGZvbnQtc2l6ZTogMTJweDsiKQ0KICAgICAgKQ0KICAgICkNCiAgKQ0KKQ0KDQojIERlZmluZSBzZXJ2ZXIgbG9naWMNCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0LCBzZXNzaW9uKSB7DQogIA0KICAjIFNpbXVsYXRlZCBkaWFtb25kIGRhdGFzZXQgZm9yIGRlbW9uc3RyYXRpb24gDQogICMgKEluIGEgcmVhbCBpbXBsZW1lbnRhdGlvbiwgeW91IHdvdWxkIGxvYWQgeW91ciBhY3R1YWwgZGlhbW9uZCBkYXRhKQ0KICBzZXQuc2VlZCgxMjMpDQogIG4gPC0gNTAwMA0KICBkaWFtb25kcyA8LSBkYXRhLmZyYW1lKA0KICAgIENhcmF0ID0gcnVuaWYobiwgMC4yLCA1LjApLA0KICAgIEN1dCA9IHNhbXBsZShjKCJGYWlyIiwgIkdvb2QiLCAiVmVyeSBHb29kIiwgIklkZWFsIiwgIkV4Y2VsbGVudCIpLCBuLCByZXBsYWNlID0gVFJVRSwgDQogICAgICAgICAgICAgICAgcHJvYiA9IGMoMC4wNSwgMC4xNSwgMC4yNSwgMC4zNSwgMC4yKSksDQogICAgQ29sb3IgPSBzYW1wbGUoTEVUVEVSU1s0OjEwXSwgbiwgcmVwbGFjZSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjA1LCAwLjEsIDAuMTUsIDAuMjUsIDAuMiwgMC4xNSwgMC4xKSksDQogICAgQ2xhcml0eSA9IHNhbXBsZShjKCJJRiIsICJWVlMxIiwgIlZWUzIiLCAiVlMxIiwgIlZTMiIsICJTSTEiLCAiU0kyIiwgIkkxIiksIG4sIHJlcGxhY2UgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjA1LCAwLjA1LCAwLjEsIDAuMTUsIDAuMiwgMC4yLCAwLjE1LCAwLjEpKQ0KICApDQogIA0KICAjIENvbnZlcnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIGZhY3RvcnMgd2l0aCBjb3JyZWN0IG9yZGVyaW5nDQogIGRpYW1vbmRzJEN1dCA8LSBmYWN0b3IoZGlhbW9uZHMkQ3V0LCBsZXZlbHMgPSBjKCJGYWlyIiwgIkdvb2QiLCAiVmVyeSBHb29kIiwgIklkZWFsIiwgIkV4Y2VsbGVudCIpKQ0KICBkaWFtb25kcyRDb2xvciA8LSBmYWN0b3IoZGlhbW9uZHMkQ29sb3IsIGxldmVscyA9IGMoIkoiLCAiSSIsICJIIiwgIkciLCAiRiIsICJFIiwgIkQiKSkNCiAgZGlhbW9uZHMkQ2xhcml0eSA8LSBmYWN0b3IoZGlhbW9uZHMkQ2xhcml0eSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJJMSIsICJTSTIiLCAiU0kxIiwgIlZTMiIsICJWUzEiLCAiVlZTMiIsICJWVlMxIiwgIklGIikpDQogIA0KICAjIENvbnZlcnQgZmFjdG9ycyB0byBudW1lcmljIHZhbHVlcyBmb3IgdGhlIG1vZGVsDQogIGRpYW1vbmRzJEN1dF9udW0gPC0gYXMubnVtZXJpYyhkaWFtb25kcyRDdXQpDQogIGRpYW1vbmRzJENvbG9yX251bSA8LSBhcy5udW1lcmljKGRpYW1vbmRzJENvbG9yKQ0KICBkaWFtb25kcyRDbGFyaXR5X251bSA8LSBhcy5udW1lcmljKGRpYW1vbmRzJENsYXJpdHkpDQogIA0KICAjIEdlbmVyYXRlIHByaWNlIGJhc2VkIG9uIGEgc29waGlzdGljYXRlZCBtb2RlbCANCiAgIyAodGhpcyBpcyBhIHNpbXBsaWZpZWQgZXhhbXBsZSAtIHJlcGxhY2Ugd2l0aCB5b3VyIGFjdHVhbCBtb2RlbCkNCiAgY2FsY3VsYXRlX3ByaWNlIDwtIGZ1bmN0aW9uKGNhcmF0LCBjdXRfbnVtLCBjb2xvcl9udW0sIGNsYXJpdHlfbnVtKSB7DQogICAgIyBCYXNlIHByaWNlIGNvbXBvbmVudA0KICAgIGJhc2VfcHJpY2UgPC0gMTAwMA0KICAgIA0KICAgICMgQ2FyYXQgaGFzIGV4cG9uZW50aWFsIGVmZmVjdCBvbiBwcmljZQ0KICAgIGNhcmF0X2ZhY3RvciA8LSA1MDAwICogY2FyYXReMg0KICAgIA0KICAgICMgUXVhbGl0eSBmYWN0b3JzDQogICAgY3V0X2ZhY3RvciA8LSAyMDAgKiBjdXRfbnVtDQogICAgY29sb3JfZmFjdG9yIDwtIDMwMCAqIGNvbG9yX251bQ0KICAgIGNsYXJpdHlfZmFjdG9yIDwtIDI1MCAqIGNsYXJpdHlfbnVtDQogICAgDQogICAgIyBJbnRlcmFjdGlvbiBlZmZlY3RzDQogICAgY2FyYXRfY3V0X2ludGVyYWN0aW9uIDwtIDEwMCAqIGNhcmF0ICogY3V0X251bQ0KICAgIGNhcmF0X2NvbG9yX2ludGVyYWN0aW9uIDwtIDEwMCAqIGNhcmF0ICogY29sb3JfbnVtDQogICAgY2FyYXRfY2xhcml0eV9pbnRlcmFjdGlvbiA8LSAxMDAgKiBjYXJhdCAqIGNsYXJpdHlfbnVtDQogICAgDQogICAgIyBDYWxjdWxhdGUgdG90YWwgcHJpY2UNCiAgICBwcmljZSA8LSBiYXNlX3ByaWNlICsgY2FyYXRfZmFjdG9yICsgY3V0X2ZhY3RvciArIGNvbG9yX2ZhY3RvciArIGNsYXJpdHlfZmFjdG9yICsNCiAgICAgIGNhcmF0X2N1dF9pbnRlcmFjdGlvbiArIGNhcmF0X2NvbG9yX2ludGVyYWN0aW9uICsgY2FyYXRfY2xhcml0eV9pbnRlcmFjdGlvbg0KICAgIA0KICAgICMgQWRkIHNvbWUgcmFuZG9tIHZhcmlhdGlvbg0KICAgIHByaWNlICogcnVuaWYoMSwgMC45LCAxLjEpDQogIH0NCiAgDQogICMgQ2FsY3VsYXRlIHByaWNlcyBmb3IgdGhlIGRhdGFzZXQNCiAgZGlhbW9uZHMkUHJpY2UgPC0gc2FwcGx5KDE6bnJvdyhkaWFtb25kcyksIGZ1bmN0aW9uKGkpIHsNCiAgICBjYWxjdWxhdGVfcHJpY2UoDQogICAgICBkaWFtb25kcyRDYXJhdFtpXSwNCiAgICAgIGRpYW1vbmRzJEN1dF9udW1baV0sDQogICAgICBkaWFtb25kcyRDb2xvcl9udW1baV0sDQogICAgICBkaWFtb25kcyRDbGFyaXR5X251bVtpXQ0KICAgICkNCiAgfSkNCiAgDQogICMgQ2FsY3VsYXRlIHByaWNlIHBlciBjYXJhdA0KICBkaWFtb25kcyRQcmljZVBlckNhcmF0IDwtIGRpYW1vbmRzJFByaWNlIC8gZGlhbW9uZHMkQ2FyYXQNCiAgDQogICMgRnVuY3Rpb24gdG8gZ2VuZXJhdGUgYSBwcmVkaWN0ZWQgcHJpY2UgY3VydmUNCiAgZ2VuZXJhdGVfcHJpY2VfY3VydmUgPC0gZnVuY3Rpb24oKSB7DQogICAgIyBDcmVhdGUgYSBzZXF1ZW5jZSBvZiBjYXJhdCB2YWx1ZXMNCiAgICBjYXJhdF92YWx1ZXMgPC0gc2VxKDAuMjUsIDMsIGJ5ID0gMC4wNSkNCiAgICANCiAgICAjIEZvciBlYWNoIGNhcmF0IHZhbHVlLCBjYWxjdWxhdGUgYW4gYXZlcmFnZSBwcmljZSBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMNCiAgICAjIEFzc3VtaW5nICJFeGNlbGxlbnQiIGN1dCwgIkciIGNvbG9yLCBhbmQgIlZTMSIgY2xhcml0eSBhcyBkZWZhdWx0DQogICAgY3V0X251bSA8LSA1ICAjIEV4Y2VsbGVudA0KICAgIGNvbG9yX251bSA8LSA0ICAjIEcNCiAgICBjbGFyaXR5X251bSA8LSA1ICAjIFZTMQ0KICAgIA0KICAgIHByaWNlcyA8LSBzYXBwbHkoY2FyYXRfdmFsdWVzLCBmdW5jdGlvbihjYXJhdCkgew0KICAgICAgIyBHZW5lcmF0ZSBtdWx0aXBsZSBwcmljZSBwcmVkaWN0aW9ucyB3aXRoIHNvbWUgcmFuZG9tbmVzcyB0byBzaW11bGF0ZSBjb25maWRlbmNlIGludGVydmFscw0KICAgICAgcmVwbGljYXRlKDUwLCBjYWxjdWxhdGVfcHJpY2UoY2FyYXQsIGN1dF9udW0sIGNvbG9yX251bSwgY2xhcml0eV9udW0pKQ0KICAgIH0pDQogICAgDQogICAgIyBDYWxjdWxhdGUgbWVhbiBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMNCiAgICBwcmljZV9tZWFucyA8LSBhcHBseShwcmljZXMsIDIsIG1lYW4pDQogICAgcHJpY2VfbG93ZXIgPC0gYXBwbHkocHJpY2VzLCAyLCBmdW5jdGlvbih4KSBxdWFudGlsZSh4LCAwLjAyNSkpDQogICAgcHJpY2VfdXBwZXIgPC0gYXBwbHkocHJpY2VzLCAyLCBmdW5jdGlvbih4KSBxdWFudGlsZSh4LCAwLjk3NSkpDQogICAgDQogICAgIyBSZXR1cm4gZGF0YSBmcmFtZSB3aXRoIGNhcmF0IHZhbHVlcyBhbmQgcHJpY2UgcHJlZGljdGlvbnMNCiAgICBkYXRhLmZyYW1lKA0KICAgICAgQ2FyYXQgPSBjYXJhdF92YWx1ZXMsDQogICAgICBFc3RpbWF0ZWRQcmljZSA9IHByaWNlX21lYW5zLA0KICAgICAgTG93ZXJCb3VuZCA9IHByaWNlX2xvd2VyLA0KICAgICAgVXBwZXJCb3VuZCA9IHByaWNlX3VwcGVyDQogICAgKQ0KICB9DQogIA0KICAjIEdlbmVyYXRlIHRoZSBwcmljZSBjdXJ2ZSBkYXRhDQogIHByaWNlX2N1cnZlIDwtIGdlbmVyYXRlX3ByaWNlX2N1cnZlKCkNCiAgDQogICMgQ3JlYXRlIHRoZSBwcmljZSB2cy4gY2FyYXQgcGxvdA0KICBvdXRwdXQkcHJpY2VQbG90IDwtIHJlbmRlclBsb3Qoew0KICAgIGdncGxvdChwcmljZV9jdXJ2ZSwgYWVzKHggPSBDYXJhdCwgeSA9IEVzdGltYXRlZFByaWNlKSkgKw0KICAgICAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBMb3dlckJvdW5kLCB5bWF4ID0gVXBwZXJCb3VuZCksIGZpbGwgPSAibGlnaHRibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgICAgIGdlb21fbGluZShjb2xvciA9ICJzZWFncmVlbiIsIHNpemUgPSAxKSArDQogICAgICBnZW9tX3BvaW50KGNvbG9yID0gInNlYWdyZWVuIiwgc2l6ZSA9IDMpICsNCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBkb2xsYXJfZm9ybWF0KCkpICsNCiAgICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gIkRpYW1vbmQgUHJpY2UgYnkgQ2FyYXQgV2VpZ2h0IiwNCiAgICAgICAgeCA9ICJDYXJhdCBXZWlnaHQiLA0KICAgICAgICB5ID0gIlByaWNlICgkKSINCiAgICAgICkgKw0KICAgICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICAgIHRoZW1lKA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIiksDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIikNCiAgICAgICkNCiAgfSkNCiAgDQogICMgQ3JlYXRlIHRoZSBkYXRhIGV4cGxvcmVyIHBsb3QNCiAgb3V0cHV0JGV4cGxvcmVwbG90IDwtIHJlbmRlclBsb3Qoew0KICAgICMgR2V0IHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMNCiAgICB4X3ZhciA8LSBpbnB1dCR4dmFyDQogICAgeV92YXIgPC0gaW5wdXQkeXZhcg0KICAgIGNvbG9yX3ZhciA8LSBpbnB1dCRjb2xvcnZhcg0KICAgIA0KICAgICMgUHJlcGFyZSB0aGUgcGxvdA0KICAgIHAgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXNfc3RyaW5nKHggPSB4X3ZhciwgeSA9IGlmZWxzZSh5X3ZhciA9PSAiUHJpY2UiLCAiUHJpY2UiLCAiUHJpY2VQZXJDYXJhdCIpKSkNCiAgICANCiAgICAjIEFkZCBjb2xvciBhZXN0aGV0aWNzIGlmIHNlbGVjdGVkDQogICAgaWYgKGNvbG9yX3ZhciAhPSAiTm9uZSIpIHsNCiAgICAgIHAgPC0gcCArIGFlc19zdHJpbmcoY29sb3IgPSBjb2xvcl92YXIpDQogICAgfQ0KICAgIA0KICAgICMgQWRkIHRoZSBwb2ludHMNCiAgICBwIDwtIHAgKyBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaXplID0gaW5wdXQkcG9pbnRzaXplKQ0KICAgIA0KICAgICMgQWRkIGEgc21vb3RoZWQgbGluZSBpZiB4LWF4aXMgaXMgbnVtZXJpYw0KICAgIGlmICh4X3ZhciA9PSAiQ2FyYXQiKSB7DQogICAgICBwIDwtIHAgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IFRSVUUsIGNvbG9yID0gImJsdWUiKQ0KICAgIH0NCiAgICANCiAgICAjIEN1c3RvbWl6ZSB0aGUgcGxvdA0KICAgIHAgPC0gcCArIA0KICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcl9mb3JtYXQoKSkgKw0KICAgICAgbGFicygNCiAgICAgICAgdGl0bGUgPSBwYXN0ZSh5X3ZhciwgImJ5IiwgeF92YXIpLA0KICAgICAgICB4ID0geF92YXIsDQogICAgICAgIHkgPSB5X3Zhcg0KICAgICAgKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgdGhlbWUoDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkNCiAgICAgICkNCiAgICANCiAgICAjIFJldHVybiB0aGUgcGxvdA0KICAgIHANCiAgfSkNCiAgDQogICMgRGlzcGxheSBkYXRhIHRhYmxlDQogIG91dHB1dCRkaWFtb25kVGFibGUgPC0gRFQ6OnJlbmRlckRhdGFUYWJsZSh7DQogICAgRFQ6OmRhdGF0YWJsZSgNCiAgICAgIGRpYW1vbmRzICU+JSANCiAgICAgICAgc2VsZWN0KENhcmF0LCBDdXQsIENvbG9yLCBDbGFyaXR5LCBQcmljZSwgUHJpY2VQZXJDYXJhdCkgJT4lDQogICAgICAgIGFycmFuZ2UoZGVzYyhDYXJhdCkpICU+JQ0KICAgICAgICBoZWFkKDEwMCksDQogICAgICBvcHRpb25zID0gbGlzdCgNCiAgICAgICAgcGFnZUxlbmd0aCA9IDEwLA0KICAgICAgICBzZWFyY2hIaWdobGlnaHQgPSBUUlVFLA0KICAgICAgICBzY3JvbGxYID0gVFJVRQ0KICAgICAgKSwNCiAgICAgIHJvd25hbWVzID0gRkFMU0UNCiAgICApICU+JQ0KICAgIGZvcm1hdEN1cnJlbmN5KGMoJ1ByaWNlJywgJ1ByaWNlUGVyQ2FyYXQnKSwgJyQnKQ0KICB9KQ0KICANCiAgIyBDYWxjdWxhdGUgdGhlIHByaWNlIHdoZW4gdGhlIGJ1dHRvbiBpcyBjbGlja2VkDQogIG9ic2VydmVFdmVudChpbnB1dCRjYWxjdWxhdGVCdG4sIHsNCiAgICAjIEdldCBpbnB1dCB2YWx1ZXMNCiAgICBjYXJhdCA8LSBpbnB1dCRjYXJhdFdlaWdodA0KICAgIA0KICAgICMgQ29udmVydCBzZWxlY3RlZCB2YWx1ZXMgdG8gbnVtZXJpYyBmb3IgY2FsY3VsYXRpb24NCiAgICBjdXRfbGV2ZWxzIDwtIGMoIkZhaXIiID0gMSwgIkdvb2QiID0gMiwgIlZlcnkgR29vZCIgPSAzLCAiSWRlYWwiID0gNCwgIkV4Y2VsbGVudCIgPSA1KQ0KICAgIGN1dF9udW0gPC0gY3V0X2xldmVsc1tpbnB1dCRjdXRRdWFsaXR5XQ0KICAgIA0KICAgIGNvbG9yX2xldmVscyA8LSBjKA0KICAgICAgIkogKE5lYXIgQ29sb3JsZXNzKSIgPSAxLCAiSSAoTmVhciBDb2xvcmxlc3MpIiA9IDIsICJIIChOZWFyIENvbG9ybGVzcykiID0gMywgDQogICAgICAiRyAoTmVhciBDb2xvcmxlc3MpIiA9IDQsICJGIChDb2xvcmxlc3MpIiA9IDUsICJFIChDb2xvcmxlc3MpIiA9IDYsICJEIChDb2xvcmxlc3MpIiA9IDcNCiAgICApDQogICAgY29sb3JfbnVtIDwtIGNvbG9yX2xldmVsc1tpbnB1dCRjb2xvckdyYWRlXQ0KICAgIA0KICAgIGNsYXJpdHlfbGV2ZWxzIDwtIGMoDQogICAgICAiSTEiID0gMSwgIlNJMiIgPSAyLCAiU0kxIiA9IDMsICJWUzIiID0gNCwgIlZTMSIgPSA1LCAiVlZTMiIgPSA2LCAiVlZTMSIgPSA3LCAiSUYgKEludGVybmFsbHkgRmxhd2xlc3MpIiA9IDgNCiAgICApDQogICAgY2xhcml0eV9udW0gPC0gY2xhcml0eV9sZXZlbHNbaW5wdXQkY2xhcml0eV0NCiAgICANCiAgICAjIEdlbmVyYXRlIG11bHRpcGxlIHByaWNlIGVzdGltYXRlcyB0byBnZXQgYSBjb25maWRlbmNlIGludGVydmFsDQogICAgcHJpY2VfZXN0aW1hdGVzIDwtIHJlcGxpY2F0ZSgxMDAsIGNhbGN1bGF0ZV9wcmljZShjYXJhdCwgY3V0X251bSwgY29sb3JfbnVtLCBjbGFyaXR5X251bSkpDQogICAgDQogICAgIyBDYWxjdWxhdGUgbWVhbiBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbA0KICAgIG1lYW5fcHJpY2UgPC0gbWVhbihwcmljZV9lc3RpbWF0ZXMpDQogICAgbG93ZXJfYm91bmQgPC0gcXVhbnRpbGUocHJpY2VfZXN0aW1hdGVzLCAwLjAyNSkNCiAgICB1cHBlcl9ib3VuZCA8LSBxdWFudGlsZShwcmljZV9lc3RpbWF0ZXMsIDAuOTc1KQ0KICAgIA0KICAgICMgVXBkYXRlIHRoZSBwcmljZSBvdXRwdXRzDQogICAgb3V0cHV0JGVzdGltYXRlZFByaWNlIDwtIHJlbmRlclRleHQoew0KICAgICAgcGFzdGUwKCIkIiwgZm9ybWF0QyhtZWFuX3ByaWNlLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDIsIGJpZy5tYXJrID0gIiwiKSkNCiAgICB9KQ0KICAgIA0KICAgIG91dHB1dCRwcmljZVJhbmdlIDwtIHJlbmRlclRleHQoew0KICAgICAgcGFzdGUwKCIkIiwgZm9ybWF0Qyhsb3dlcl9ib3VuZCwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyLCBiaWcubWFyayA9ICIsIiksIA0KICAgICAgICAgICAgICIgLSAkIiwgZm9ybWF0Qyh1cHBlcl9ib3VuZCwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyLCBiaWcubWFyayA9ICIsIikpDQogICAgfSkNCiAgfSkNCn0NCg0KIyBSdW4gdGhlIGFwcGxpY2F0aW9uIA0Kc2hpbnlBcHAodWkgPSB1aSwgc2VydmVyID0gc2VydmVyKQ0KYGBgDQoNCg==