1. Introduction

This report presents a descriptive analysis of an auto insurance claims dataset. The data contains customer, policy, vehicle, and insurance-claim information. The analysis focuses on the characteristics of policyholders and vehicles, the reasons claims are filed, the channels through which policies are sold, and the relationship between customer income and claim amounts.

The goal is to summarize the dataset using descriptive statistics and five different visualization types. The visualizations are a scatterplot, treemap, heatmap, sunburst chart, and stacked bar chart. Each visualization answers a different descriptive question.

2. Descriptive Analysis of Auto Insurance Claims

The Auto Insurance Claims dataset contains information about customers, their insurance policies, vehicles, and insurance claims. It includes variables such as customer income, total claim amounts, claim reasons, sales channels, vehicle classes, policy types, and coverage levels. This dataset provides an opportunity to explore patterns in insurance claims, examine relationships between customer characteristics and claim amounts, and compare claim distributions across different categories. The purpose of this analysis is to use descriptive statistics and data visualizations to better understand the characteristics and trends within the auto insurance claims data.

3. Visualizations

Select a tab below to view each visualization and its description.

Individual Visualizations

Vizualization 1. Scatterplot: Customer Income vs. Total Claim Amount

The scatterplot examines the relationship between customer income and total insurance claim amounts. Each point represents an individual customer. Customer income is displayed along the horizontal axis, while total claim amount is displayed along the vertical axis. Different colors represent the reason for the insurance claim.

The regression line illustrates the overall direction of the relationship between income and claim amounts. A downward-sloping line suggests a negative association, while an upward-sloping line suggests a positive association. The spread of the individual points also demonstrates how claim amounts can vary among customers with similar incomes.

This visualization helps determine whether customer income appears to be associated with insurance claim amounts and whether different claim reasons demonstrate different patterns.

import os
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = 'c:/users/bryan/anaconda3/library/plugins/platforms'

# Import required libraries
import wget
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import squarify
import matplotlib.ticker as ticker
import zipfile


URL = "https://www.dropbox.com/scl/fo/va3maecms7cvdnq3ho4b9/AJoG6_GYvREJv32YB3KcDr8/Auto%20Insurance%20Claims/Auto_Insurance_Claims_Sample.csv?rlkey=wfotyhfgb6kutv8jgh028vwxu&st=le5u19c5&dl=0"
path = "C:/Users/bryan/PythonUnitFiles/"

wget.download(URL, path + 'Auto_Insurance_Claims_Sample.csv')
# Set visualization style
sns.set_theme(style="whitegrid", context="notebook")

# Set default figure size and formatting
plt.rcParams["figure.figsize"] = (10, 6)
plt.rcParams["axes.titlesize"] = 15
plt.rcParams["axes.labelsize"] = 12

path = "C:/Users/bryan/PythonUnitFiles/Auto Insurance Claims.zip"

with zipfile.ZipFile(path, "r") as zip_file:
    csv_file = [name for name in zip_file.namelist()
               if name.endswith(".csv")
               ][0]
    with zip_file.open(csv_file) as file:
        df = pd.read_csv(file)

# Display descriptive statistics for numerical variables
descriptive_stats = df.describe().T

# Add missing value counts
descriptive_stats["Missing Values"] = df.isnull().sum()

# Round results to two decimal places
descriptive_stats = descriptive_stats.round(2)

# Examine the frequency of important categorical variables

categorical_variables = [
    "Claim Reason",
    "Sales Channel",
    "Vehicle Class",
    "Policy Type",
    "Coverage"
]

# Scatterplot: Customer Income vs. Total Claim Amount

# Select relevant variables and remove missing values
scatter_data = df[
    ["Income", "Total Claim Amount", "Claim Reason"]
].dropna()

# Remove customers with zero income
scatter_data = scatter_data[scatter_data["Income"] > 0]

# Create scatterplot
plt.figure(figsize=(11, 7))

sns.scatterplot(
    data=scatter_data,
    x="Income",
    y="Total Claim Amount",
    hue="Claim Reason",
    alpha=0.5,
    s=35
)

# Add overall regression line
sns.regplot(
    data=scatter_data,
    x="Income",
    y="Total Claim Amount",
    scatter=False,
    color="black",
    line_kws={"linewidth": 2}
)

# Format dollar amounts with commas
plt.gca().xaxis.set_major_formatter(
    ticker.StrMethodFormatter('${x:,.0f}')
)

plt.gca().yaxis.set_major_formatter(
    ticker.StrMethodFormatter('${x:,.0f}')
)

# Add title and labels
plt.title("Customer Income vs. Total Insurance Claim Amount")
plt.xlabel("Customer Income ($)")
plt.ylabel("Total Claim Amount ($)")

plt.legend(title="Claim Reason")

plt.tight_layout()
plt.show()

Visualization 2. Treemap: Distribution of Recordds by Vehicle Class

The treemap illustrates how the insurance customer records are distributed across different vehicle classes. Each rectangle represents a vehicle category, and the size of each rectangle corresponds to the number of customers belonging to that category. Larger rectangles indicate vehicle classes that represent a greater proportion of the dataset, while smaller rectangles represent less common vehicle classes. Each rectangle also displays the number of records and the percentage of total observations associated with that category. This visualization helps identify which vehicle classes are most common among the insurance customers.

# Treemap: Distribution of Records by Vehicle Class

# Count observations for each vehicle class
vehicle_counts = (
    df["Vehicle Class"]
    .dropna()
    .value_counts()
)

# Create labels showing vehicle class, count, and percentage
labels = [
    f"{vehicle}\n{count:,} records\n({count / vehicle_counts.sum():.1%})"
    for vehicle, count in vehicle_counts.items()
]

# Create treemap
plt.figure(figsize=(11, 7))

squarify.plot(
    sizes=vehicle_counts.values,
    label=labels,
    color=sns.color_palette("pastel", len(vehicle_counts)),
    alpha=0.95,
    pad=True,
    text_kwargs={
        "fontsize": 10,
        "color": "black",
        "fontweight": "bold"
    }
)

plt.title("Distribution of Records by Vehicle Class")
plt.axis("off")
plt.tight_layout()
plt.show()

Vizualization 3. Heatmap: Claim Reason by Sales Channel

The heatmap compares the number of insurance claims across different claim reasons and sales channels. The horizontal axis represents the sales channels, including Agent, Branch, Call Center, and Web. The vertical axis represents the reasons for insurance claims. Each square displays the number of customers associated with a particular combination of claim reason and sales channel. Darker blue squares indicate higher numbers of claims, while lighter-colored squares indicate fewer claims. This visualization helps identify which sales channels are associated with the greatest number of claims and which claim reasons occur most frequently across those channels.

# Heatmap: Claim Reason by Sales Channel

# Select relevant variables and remove missing values
heatmap_data = df[
    ["Claim Reason", "Sales Channel"]
].dropna()

# Calculate the number of claims by category and sales channel
heatmap_counts = pd.crosstab(
    heatmap_data["Claim Reason"],
    heatmap_data["Sales Channel"]
)

# Create heatmap
plt.figure(figsize=(10, 6))

ax = sns.heatmap(
    heatmap_counts,
    annot=True,
    fmt=",",
    cmap="YlGnBu",
    linewidths=0.5,
    cbar_kws={
        "format": ticker.FuncFormatter(
            lambda x, pos: f"{x:,.0f}"
        )
    }
)

# Add title and labels
plt.title("Claim Reason by Sales Channel")
plt.xlabel("Sales Channel")
plt.ylabel("Claim Reason")

plt.tight_layout()
plt.show()

Vizualization 4. Nested Pie Chart: Claim Reason by Vehicle Class

The nested pie chart illustrates the distribution of insurance claims by claim reason and vehicle class. The inner sections represent the different claim reasons, while the outer sections break each claim reason down by vehicle class. The size of each section corresponds to the number of claims within that category, with larger sections representing a greater number of claims. This visualization makes it easier to identify which vehicle classes account for the largest portions of each claim reason. It also provides a clearer understanding of how claim reasons are distributed across different vehicle classes. By examining both rings, we can compare the overall frequency of claim reasons and the vehicle classes contributing to those totals.

import pandas as pd
import plotly.express as px

# Count claims and create sunburst chart with chained updates
nested_counts = (
    df[["Claim Reason", "Vehicle Class"]]
    .dropna()
    .groupby(["Claim Reason", "Vehicle Class"])
    .size()
    .reset_index(name="Number of Claims")
)

fig = px.sunburst(
    nested_counts,
    path=["Claim Reason", "Vehicle Class"],
    values="Number of Claims",
    color="Claim Reason",
    color_discrete_sequence=px.colors.qualitative.Safe,
    title="Distribution of Insurance Claims by Reason and Vehicle Class"
).update_traces(
    textinfo="label",
    insidetextorientation="horizontal",
    insidetextfont=dict(size=13),
    hovertemplate=(
        "<b>%{label}</b><br>"
        "Claims: %{value:,}<br>"
        "Share of Total: %{percentRoot:.1%}<br>"
        "Percentage of Parent: %{percentParent:.1%}"
        "<extra></extra>"
    ),
    maxdepth=2
).update_layout(
    width=950, height=750,
    title=dict(text="Distribution of Insurance Claims by Reason and Vehicle Class", x=0.5, xanchor="center", font=dict(size=20)),
    margin=dict(t=100, l=30, r=30, b=30),
    uniformtext=dict(minsize=11, mode="hide")
)

fig

Vizualization 5. Stacked Bar Chart: Insurance Claims by Sales Channel and Claim Reason

The stacked bar chart compares the number of insurance claims across different sales channels and shows how each sales channel is divided by claim reason. Each bar represents a sales channel, while the colored sections represent the different claim reasons. The total height of each bar indicates the overall number of claims associated with that sales channel. This visualization helps identify which sales channels account for the greatest number of claims and which claim reasons occur most frequently within each channel. Comparing the colored sections also makes it possible to observe similarities and differences in claim patterns across sales channels. These patterns provide insight into how claim reasons are distributed among the company’s different methods of selling insurance.

# Stacked Bar Chart: Claim Reason by Sales Channel

import matplotlib.pyplot as plt
import seaborn as sns

# Remove rows with missing values
stacked_data = df[["Sales Channel", "Claim Reason"]].dropna()

# Count claims by sales channel and claim reason
stacked_counts = pd.crosstab(
    stacked_data["Sales Channel"],
    stacked_data["Claim Reason"]
)

# Create stacked bar chart
ax = stacked_counts.plot(
    kind="bar",
    stacked=True,
    figsize=(11, 7),
    colormap="Set2",
    edgecolor="black"
)

plt.title(
    "Insurance Claims by Sales Channel and Claim Reason",
    fontsize=16,
    fontweight="bold",
    pad=15
)

plt.xlabel("Sales Channel", fontsize=12)
plt.ylabel("Number of Claims", fontsize=12)

plt.xticks(rotation=15)
plt.legend(title="Claim Reason", bbox_to_anchor=(1.02, 1), loc="upper left")

plt.tight_layout()
plt.show()

4. Key Findings

Several interesting patterns emerged from the initial analysis of the auto insurance claims dataset. Collision was the most common reason for filing a claim, followed by hail damage. Four-door cars represented the largest vehicle category, accounting for approximately half of the observations. Additionally, agents were the most frequently used sales channel, while web sales represented the smallest category. Personal auto policies and basic coverage were also significantly more common than the other policy and coverage options.

The numerical statistics revealed that the average customer income was approximately $37,657, compared to a median income of $33,890. The average total claim amount was approximately $434, with a median of $384. The difference between these averages and medians suggests some variation in customer income and claim amounts, potentially influenced by higher-value observations. Overall, these findings provide an initial understanding of the customers, vehicles, and claims represented in the dataset and help establish the foundation for the visual analysis.

5. Conclusion

Overall, the analysis of the auto insurance claims dataset revealed several patterns in customer demographics, vehicle types, insurance policies, and claim activity. Collisions were the most common reason for claims, four-door cars represented the largest vehicle category, and personal auto policies with basic coverage were the most common. The visualizations also provided insight into relationships between customer income, claim amounts, and different insurance categories. Overall, this analysis demonstrates how data visualization and descriptive statistics can help identify trends and better understand patterns within the auto insurance industry.

LS0tDQp0aXRsZTogIkRlc2NyaXB0aXZlIEFuYWx5c2lzIG9mIEF1dG8gSW5zdXJhbmNlIENsYWltcyINCmF1dGhvcjogIk1vcmdhbiBCcnlhbnQiDQpkYXRlOiAiMjAyNi0xMS0xMiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6ICcyJw0KLS0tDQoNCmBgYCB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KICAgIA0KICAgIGxpYnJhcnkocmV0aWN1bGF0ZSkNCiAgICB1c2VfcHl0aG9uKCJDOi9Vc2Vycy9icnlhbi9hbmFjb25kYTMvcHl0aG9uLmV4ZSIpDQpgYGANCiMgMS4gSW50cm9kdWN0aW9uDQoNClRoaXMgcmVwb3J0IHByZXNlbnRzIGEgZGVzY3JpcHRpdmUgYW5hbHlzaXMgb2YgYW4gYXV0byBpbnN1cmFuY2UgY2xhaW1zIGRhdGFzZXQuIFRoZSBkYXRhIGNvbnRhaW5zIGN1c3RvbWVyLCBwb2xpY3ksIHZlaGljbGUsIGFuZCBpbnN1cmFuY2UtY2xhaW0gaW5mb3JtYXRpb24uIFRoZSBhbmFseXNpcyBmb2N1c2VzIG9uIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgcG9saWN5aG9sZGVycyBhbmQgdmVoaWNsZXMsIHRoZSByZWFzb25zIGNsYWltcyBhcmUgZmlsZWQsIHRoZSBjaGFubmVscyB0aHJvdWdoIHdoaWNoIHBvbGljaWVzIGFyZSBzb2xkLCBhbmQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGN1c3RvbWVyIGluY29tZSBhbmQgY2xhaW0gYW1vdW50cy4NCg0KVGhlIGdvYWwgaXMgdG8gc3VtbWFyaXplIHRoZSBkYXRhc2V0IHVzaW5nIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgYW5kIGZpdmUgZGlmZmVyZW50IHZpc3VhbGl6YXRpb24gdHlwZXMuIFRoZSB2aXN1YWxpemF0aW9ucyBhcmUgYSBzY2F0dGVycGxvdCwgdHJlZW1hcCwgaGVhdG1hcCwgc3VuYnVyc3QgY2hhcnQsIGFuZCBzdGFja2VkIGJhciBjaGFydC4gRWFjaCB2aXN1YWxpemF0aW9uIGFuc3dlcnMgYSBkaWZmZXJlbnQgZGVzY3JpcHRpdmUgcXVlc3Rpb24uDQoNCiMgMi4gRGVzY3JpcHRpdmUgQW5hbHlzaXMgb2YgQXV0byBJbnN1cmFuY2UgQ2xhaW1zDQoNClRoZSBBdXRvIEluc3VyYW5jZSBDbGFpbXMgZGF0YXNldCBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBjdXN0b21lcnMsIHRoZWlyIGluc3VyYW5jZSBwb2xpY2llcywgdmVoaWNsZXMsIGFuZCBpbnN1cmFuY2UgY2xhaW1zLiBJdCBpbmNsdWRlcyB2YXJpYWJsZXMgc3VjaCBhcyBjdXN0b21lciBpbmNvbWUsIHRvdGFsIGNsYWltIGFtb3VudHMsIGNsYWltIHJlYXNvbnMsIHNhbGVzIGNoYW5uZWxzLCB2ZWhpY2xlIGNsYXNzZXMsIHBvbGljeSB0eXBlcywgYW5kIGNvdmVyYWdlIGxldmVscy4gVGhpcyBkYXRhc2V0IHByb3ZpZGVzIGFuIG9wcG9ydHVuaXR5IHRvIGV4cGxvcmUgcGF0dGVybnMgaW4gaW5zdXJhbmNlIGNsYWltcywgZXhhbWluZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gY3VzdG9tZXIgY2hhcmFjdGVyaXN0aWNzIGFuZCBjbGFpbSBhbW91bnRzLCBhbmQgY29tcGFyZSBjbGFpbSBkaXN0cmlidXRpb25zIGFjcm9zcyBkaWZmZXJlbnQgY2F0ZWdvcmllcy4gVGhlIHB1cnBvc2Ugb2YgdGhpcyBhbmFseXNpcyBpcyB0byB1c2UgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBhbmQgZGF0YSB2aXN1YWxpemF0aW9ucyB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgY2hhcmFjdGVyaXN0aWNzIGFuZCB0cmVuZHMgd2l0aGluIHRoZSBhdXRvIGluc3VyYW5jZSBjbGFpbXMgZGF0YS4NCg0KIyAzLiBWaXN1YWxpemF0aW9ucw0KDQpTZWxlY3QgYSB0YWIgYmVsb3cgdG8gdmlldyBlYWNoIHZpc3VhbGl6YXRpb24gYW5kIGl0cyBkZXNjcmlwdGlvbi4NCg0KIyMgSW5kaXZpZHVhbCBWaXN1YWxpemF0aW9ucyB7LnRhYnNldH0NCg0KIyMjIFZpenVhbGl6YXRpb24gMS4gU2NhdHRlcnBsb3Q6IEN1c3RvbWVyIEluY29tZSB2cy4gVG90YWwgQ2xhaW0gQW1vdW50DQoNClRoZSBzY2F0dGVycGxvdCBleGFtaW5lcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY3VzdG9tZXIgaW5jb21lIGFuZCB0b3RhbCBpbnN1cmFuY2UgY2xhaW0gYW1vdW50cy4gRWFjaCBwb2ludCByZXByZXNlbnRzIGFuIGluZGl2aWR1YWwgY3VzdG9tZXIuIEN1c3RvbWVyIGluY29tZSBpcyBkaXNwbGF5ZWQgYWxvbmcgdGhlIGhvcml6b250YWwgYXhpcywgd2hpbGUgdG90YWwgY2xhaW0gYW1vdW50IGlzIGRpc3BsYXllZCBhbG9uZyB0aGUgdmVydGljYWwgYXhpcy4gRGlmZmVyZW50IGNvbG9ycyByZXByZXNlbnQgdGhlIHJlYXNvbiBmb3IgdGhlIGluc3VyYW5jZSBjbGFpbS4NCg0KVGhlIHJlZ3Jlc3Npb24gbGluZSBpbGx1c3RyYXRlcyB0aGUgb3ZlcmFsbCBkaXJlY3Rpb24gb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGluY29tZSBhbmQgY2xhaW0gYW1vdW50cy4gQSBkb3dud2FyZC1zbG9waW5nIGxpbmUgc3VnZ2VzdHMgYSBuZWdhdGl2ZSBhc3NvY2lhdGlvbiwgd2hpbGUgYW4gdXB3YXJkLXNsb3BpbmcgbGluZSBzdWdnZXN0cyBhIHBvc2l0aXZlIGFzc29jaWF0aW9uLiBUaGUgc3ByZWFkIG9mIHRoZSBpbmRpdmlkdWFsIHBvaW50cyBhbHNvIGRlbW9uc3RyYXRlcyBob3cgY2xhaW0gYW1vdW50cyBjYW4gdmFyeSBhbW9uZyBjdXN0b21lcnMgd2l0aCBzaW1pbGFyIGluY29tZXMuDQoNClRoaXMgdmlzdWFsaXphdGlvbiBoZWxwcyBkZXRlcm1pbmUgd2hldGhlciBjdXN0b21lciBpbmNvbWUgYXBwZWFycyB0byBiZSBhc3NvY2lhdGVkIHdpdGggaW5zdXJhbmNlIGNsYWltIGFtb3VudHMgYW5kIHdoZXRoZXIgZGlmZmVyZW50IGNsYWltIHJlYXNvbnMgZGVtb25zdHJhdGUgZGlmZmVyZW50IHBhdHRlcm5zLg0KDQoNCmBgYHtweXRob24sIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30NCmltcG9ydCBvcw0Kb3MuZW52aXJvblsnUVRfUVBBX1BMQVRGT1JNX1BMVUdJTl9QQVRIJ10gPSAnYzovdXNlcnMvYnJ5YW4vYW5hY29uZGEzL2xpYnJhcnkvcGx1Z2lucy9wbGF0Zm9ybXMnDQoNCiMgSW1wb3J0IHJlcXVpcmVkIGxpYnJhcmllcw0KaW1wb3J0IHdnZXQNCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBudW1weSBhcyBucA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IHNlYWJvcm4gYXMgc25zDQppbXBvcnQgc3F1YXJpZnkNCmltcG9ydCBtYXRwbG90bGliLnRpY2tlciBhcyB0aWNrZXINCmltcG9ydCB6aXBmaWxlDQoNCg0KVVJMID0gImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3NjbC9mby92YTNtYWVjbXM3Y3ZkbnEzaG80YjkvQUpvRzZfR1l2UkVKdjMyWUIzS2NEcjgvQXV0byUyMEluc3VyYW5jZSUyMENsYWltcy9BdXRvX0luc3VyYW5jZV9DbGFpbXNfU2FtcGxlLmNzdj9ybGtleT13Zm90eWhmZ2I2a3V0djhqZ2gwMjh2d3h1JnN0PWxlNXUxOWM1JmRsPTAiDQpwYXRoID0gIkM6L1VzZXJzL2JyeWFuL1B5dGhvblVuaXRGaWxlcy8iDQoNCndnZXQuZG93bmxvYWQoVVJMLCBwYXRoICsgJ0F1dG9fSW5zdXJhbmNlX0NsYWltc19TYW1wbGUuY3N2JykNCg0KIyBTZXQgdmlzdWFsaXphdGlvbiBzdHlsZQ0Kc25zLnNldF90aGVtZShzdHlsZT0id2hpdGVncmlkIiwgY29udGV4dD0ibm90ZWJvb2siKQ0KDQojIFNldCBkZWZhdWx0IGZpZ3VyZSBzaXplIGFuZCBmb3JtYXR0aW5nDQpwbHQucmNQYXJhbXNbImZpZ3VyZS5maWdzaXplIl0gPSAoMTAsIDYpDQpwbHQucmNQYXJhbXNbImF4ZXMudGl0bGVzaXplIl0gPSAxNQ0KcGx0LnJjUGFyYW1zWyJheGVzLmxhYmVsc2l6ZSJdID0gMTINCg0KcGF0aCA9ICJDOi9Vc2Vycy9icnlhbi9QeXRob25Vbml0RmlsZXMvQXV0byBJbnN1cmFuY2UgQ2xhaW1zLnppcCINCg0Kd2l0aCB6aXBmaWxlLlppcEZpbGUocGF0aCwgInIiKSBhcyB6aXBfZmlsZToNCiAgICBjc3ZfZmlsZSA9IFtuYW1lIGZvciBuYW1lIGluIHppcF9maWxlLm5hbWVsaXN0KCkNCiAgICAgICAgICAgICAgIGlmIG5hbWUuZW5kc3dpdGgoIi5jc3YiKQ0KICAgICAgICAgICAgICAgXVswXQ0KICAgIHdpdGggemlwX2ZpbGUub3Blbihjc3ZfZmlsZSkgYXMgZmlsZToNCiAgICAgICAgZGYgPSBwZC5yZWFkX2NzdihmaWxlKQ0KDQojIERpc3BsYXkgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBmb3IgbnVtZXJpY2FsIHZhcmlhYmxlcw0KZGVzY3JpcHRpdmVfc3RhdHMgPSBkZi5kZXNjcmliZSgpLlQNCg0KIyBBZGQgbWlzc2luZyB2YWx1ZSBjb3VudHMNCmRlc2NyaXB0aXZlX3N0YXRzWyJNaXNzaW5nIFZhbHVlcyJdID0gZGYuaXNudWxsKCkuc3VtKCkNCg0KIyBSb3VuZCByZXN1bHRzIHRvIHR3byBkZWNpbWFsIHBsYWNlcw0KZGVzY3JpcHRpdmVfc3RhdHMgPSBkZXNjcmlwdGl2ZV9zdGF0cy5yb3VuZCgyKQ0KDQojIEV4YW1pbmUgdGhlIGZyZXF1ZW5jeSBvZiBpbXBvcnRhbnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCmNhdGVnb3JpY2FsX3ZhcmlhYmxlcyA9IFsNCiAgICAiQ2xhaW0gUmVhc29uIiwNCiAgICAiU2FsZXMgQ2hhbm5lbCIsDQogICAgIlZlaGljbGUgQ2xhc3MiLA0KICAgICJQb2xpY3kgVHlwZSIsDQogICAgIkNvdmVyYWdlIg0KXQ0KDQojIFNjYXR0ZXJwbG90OiBDdXN0b21lciBJbmNvbWUgdnMuIFRvdGFsIENsYWltIEFtb3VudA0KDQojIFNlbGVjdCByZWxldmFudCB2YXJpYWJsZXMgYW5kIHJlbW92ZSBtaXNzaW5nIHZhbHVlcw0Kc2NhdHRlcl9kYXRhID0gZGZbDQogICAgWyJJbmNvbWUiLCAiVG90YWwgQ2xhaW0gQW1vdW50IiwgIkNsYWltIFJlYXNvbiJdDQpdLmRyb3BuYSgpDQoNCiMgUmVtb3ZlIGN1c3RvbWVycyB3aXRoIHplcm8gaW5jb21lDQpzY2F0dGVyX2RhdGEgPSBzY2F0dGVyX2RhdGFbc2NhdHRlcl9kYXRhWyJJbmNvbWUiXSA+IDBdDQoNCiMgQ3JlYXRlIHNjYXR0ZXJwbG90DQpwbHQuZmlndXJlKGZpZ3NpemU9KDExLCA3KSkNCg0Kc25zLnNjYXR0ZXJwbG90KA0KICAgIGRhdGE9c2NhdHRlcl9kYXRhLA0KICAgIHg9IkluY29tZSIsDQogICAgeT0iVG90YWwgQ2xhaW0gQW1vdW50IiwNCiAgICBodWU9IkNsYWltIFJlYXNvbiIsDQogICAgYWxwaGE9MC41LA0KICAgIHM9MzUNCikNCg0KIyBBZGQgb3ZlcmFsbCByZWdyZXNzaW9uIGxpbmUNCnNucy5yZWdwbG90KA0KICAgIGRhdGE9c2NhdHRlcl9kYXRhLA0KICAgIHg9IkluY29tZSIsDQogICAgeT0iVG90YWwgQ2xhaW0gQW1vdW50IiwNCiAgICBzY2F0dGVyPUZhbHNlLA0KICAgIGNvbG9yPSJibGFjayIsDQogICAgbGluZV9rd3M9eyJsaW5ld2lkdGgiOiAyfQ0KKQ0KDQojIEZvcm1hdCBkb2xsYXIgYW1vdW50cyB3aXRoIGNvbW1hcw0KcGx0LmdjYSgpLnhheGlzLnNldF9tYWpvcl9mb3JtYXR0ZXIoDQogICAgdGlja2VyLlN0ck1ldGhvZEZvcm1hdHRlcignJHt4OiwuMGZ9JykNCikNCg0KcGx0LmdjYSgpLnlheGlzLnNldF9tYWpvcl9mb3JtYXR0ZXIoDQogICAgdGlja2VyLlN0ck1ldGhvZEZvcm1hdHRlcignJHt4OiwuMGZ9JykNCikNCg0KIyBBZGQgdGl0bGUgYW5kIGxhYmVscw0KcGx0LnRpdGxlKCJDdXN0b21lciBJbmNvbWUgdnMuIFRvdGFsIEluc3VyYW5jZSBDbGFpbSBBbW91bnQiKQ0KcGx0LnhsYWJlbCgiQ3VzdG9tZXIgSW5jb21lICgkKSIpDQpwbHQueWxhYmVsKCJUb3RhbCBDbGFpbSBBbW91bnQgKCQpIikNCg0KcGx0LmxlZ2VuZCh0aXRsZT0iQ2xhaW0gUmVhc29uIikNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMjIFZpc3VhbGl6YXRpb24gMi4gVHJlZW1hcDogRGlzdHJpYnV0aW9uIG9mIFJlY29yZGRzIGJ5IFZlaGljbGUgQ2xhc3MNCg0KVGhlIHRyZWVtYXAgaWxsdXN0cmF0ZXMgaG93IHRoZSBpbnN1cmFuY2UgY3VzdG9tZXIgcmVjb3JkcyBhcmUgZGlzdHJpYnV0ZWQgYWNyb3NzIGRpZmZlcmVudCB2ZWhpY2xlIGNsYXNzZXMuIEVhY2ggcmVjdGFuZ2xlIHJlcHJlc2VudHMgYSB2ZWhpY2xlIGNhdGVnb3J5LCBhbmQgdGhlIHNpemUgb2YgZWFjaCByZWN0YW5nbGUgY29ycmVzcG9uZHMgdG8gdGhlIG51bWJlciBvZiBjdXN0b21lcnMgYmVsb25naW5nIHRvIHRoYXQgY2F0ZWdvcnkuDQpMYXJnZXIgcmVjdGFuZ2xlcyBpbmRpY2F0ZSB2ZWhpY2xlIGNsYXNzZXMgdGhhdCByZXByZXNlbnQgYSBncmVhdGVyIHByb3BvcnRpb24gb2YgdGhlIGRhdGFzZXQsIHdoaWxlIHNtYWxsZXIgcmVjdGFuZ2xlcyByZXByZXNlbnQgbGVzcyBjb21tb24gdmVoaWNsZSBjbGFzc2VzLiBFYWNoIHJlY3RhbmdsZSBhbHNvIGRpc3BsYXlzIHRoZSBudW1iZXIgb2YgcmVjb3JkcyBhbmQgdGhlIHBlcmNlbnRhZ2Ugb2YgdG90YWwgb2JzZXJ2YXRpb25zIGFzc29jaWF0ZWQgd2l0aCB0aGF0IGNhdGVnb3J5Lg0KVGhpcyB2aXN1YWxpemF0aW9uIGhlbHBzIGlkZW50aWZ5IHdoaWNoIHZlaGljbGUgY2xhc3NlcyBhcmUgbW9zdCBjb21tb24gYW1vbmcgdGhlIGluc3VyYW5jZSBjdXN0b21lcnMuDQoNCmBgYHtweXRob24sIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30NCiMgVHJlZW1hcDogRGlzdHJpYnV0aW9uIG9mIFJlY29yZHMgYnkgVmVoaWNsZSBDbGFzcw0KDQojIENvdW50IG9ic2VydmF0aW9ucyBmb3IgZWFjaCB2ZWhpY2xlIGNsYXNzDQp2ZWhpY2xlX2NvdW50cyA9ICgNCiAgICBkZlsiVmVoaWNsZSBDbGFzcyJdDQogICAgLmRyb3BuYSgpDQogICAgLnZhbHVlX2NvdW50cygpDQopDQoNCiMgQ3JlYXRlIGxhYmVscyBzaG93aW5nIHZlaGljbGUgY2xhc3MsIGNvdW50LCBhbmQgcGVyY2VudGFnZQ0KbGFiZWxzID0gWw0KICAgIGYie3ZlaGljbGV9XG57Y291bnQ6LH0gcmVjb3Jkc1xuKHtjb3VudCAvIHZlaGljbGVfY291bnRzLnN1bSgpOi4xJX0pIg0KICAgIGZvciB2ZWhpY2xlLCBjb3VudCBpbiB2ZWhpY2xlX2NvdW50cy5pdGVtcygpDQpdDQoNCiMgQ3JlYXRlIHRyZWVtYXANCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTEsIDcpKQ0KDQpzcXVhcmlmeS5wbG90KA0KICAgIHNpemVzPXZlaGljbGVfY291bnRzLnZhbHVlcywNCiAgICBsYWJlbD1sYWJlbHMsDQogICAgY29sb3I9c25zLmNvbG9yX3BhbGV0dGUoInBhc3RlbCIsIGxlbih2ZWhpY2xlX2NvdW50cykpLA0KICAgIGFscGhhPTAuOTUsDQogICAgcGFkPVRydWUsDQogICAgdGV4dF9rd2FyZ3M9ew0KICAgICAgICAiZm9udHNpemUiOiAxMCwNCiAgICAgICAgImNvbG9yIjogImJsYWNrIiwNCiAgICAgICAgImZvbnR3ZWlnaHQiOiAiYm9sZCINCiAgICB9DQopDQoNCnBsdC50aXRsZSgiRGlzdHJpYnV0aW9uIG9mIFJlY29yZHMgYnkgVmVoaWNsZSBDbGFzcyIpDQpwbHQuYXhpcygib2ZmIikNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMjIFZpenVhbGl6YXRpb24gMy4gSGVhdG1hcDogQ2xhaW0gUmVhc29uIGJ5IFNhbGVzIENoYW5uZWwNCg0KVGhlIGhlYXRtYXAgY29tcGFyZXMgdGhlIG51bWJlciBvZiBpbnN1cmFuY2UgY2xhaW1zIGFjcm9zcyBkaWZmZXJlbnQgY2xhaW0gcmVhc29ucyBhbmQgc2FsZXMgY2hhbm5lbHMuIFRoZSBob3Jpem9udGFsIGF4aXMgcmVwcmVzZW50cyB0aGUgc2FsZXMgY2hhbm5lbHMsIGluY2x1ZGluZyBBZ2VudCwgQnJhbmNoLCBDYWxsIENlbnRlciwgYW5kIFdlYi4gVGhlIHZlcnRpY2FsIGF4aXMgcmVwcmVzZW50cyB0aGUgcmVhc29ucyBmb3IgaW5zdXJhbmNlIGNsYWltcy4NCkVhY2ggc3F1YXJlIGRpc3BsYXlzIHRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIGFzc29jaWF0ZWQgd2l0aCBhIHBhcnRpY3VsYXIgY29tYmluYXRpb24gb2YgY2xhaW0gcmVhc29uIGFuZCBzYWxlcyBjaGFubmVsLiBEYXJrZXIgYmx1ZSBzcXVhcmVzIGluZGljYXRlIGhpZ2hlciBudW1iZXJzIG9mIGNsYWltcywgd2hpbGUgbGlnaHRlci1jb2xvcmVkIHNxdWFyZXMgaW5kaWNhdGUgZmV3ZXIgY2xhaW1zLg0KVGhpcyB2aXN1YWxpemF0aW9uIGhlbHBzIGlkZW50aWZ5IHdoaWNoIHNhbGVzIGNoYW5uZWxzIGFyZSBhc3NvY2lhdGVkIHdpdGggdGhlIGdyZWF0ZXN0IG51bWJlciBvZiBjbGFpbXMgYW5kIHdoaWNoIGNsYWltIHJlYXNvbnMgb2NjdXIgbW9zdCBmcmVxdWVudGx5IGFjcm9zcyB0aG9zZSBjaGFubmVscy4NCg0KYGBge3B5dGhvbiwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzID0gJ2hpZGUnfQ0KIyBIZWF0bWFwOiBDbGFpbSBSZWFzb24gYnkgU2FsZXMgQ2hhbm5lbA0KDQojIFNlbGVjdCByZWxldmFudCB2YXJpYWJsZXMgYW5kIHJlbW92ZSBtaXNzaW5nIHZhbHVlcw0KaGVhdG1hcF9kYXRhID0gZGZbDQogICAgWyJDbGFpbSBSZWFzb24iLCAiU2FsZXMgQ2hhbm5lbCJdDQpdLmRyb3BuYSgpDQoNCiMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgY2xhaW1zIGJ5IGNhdGVnb3J5IGFuZCBzYWxlcyBjaGFubmVsDQpoZWF0bWFwX2NvdW50cyA9IHBkLmNyb3NzdGFiKA0KICAgIGhlYXRtYXBfZGF0YVsiQ2xhaW0gUmVhc29uIl0sDQogICAgaGVhdG1hcF9kYXRhWyJTYWxlcyBDaGFubmVsIl0NCikNCg0KIyBDcmVhdGUgaGVhdG1hcA0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMCwgNikpDQoNCmF4ID0gc25zLmhlYXRtYXAoDQogICAgaGVhdG1hcF9jb3VudHMsDQogICAgYW5ub3Q9VHJ1ZSwNCiAgICBmbXQ9IiwiLA0KICAgIGNtYXA9IllsR25CdSIsDQogICAgbGluZXdpZHRocz0wLjUsDQogICAgY2Jhcl9rd3M9ew0KICAgICAgICAiZm9ybWF0IjogdGlja2VyLkZ1bmNGb3JtYXR0ZXIoDQogICAgICAgICAgICBsYW1iZGEgeCwgcG9zOiBmInt4OiwuMGZ9Ig0KICAgICAgICApDQogICAgfQ0KKQ0KDQojIEFkZCB0aXRsZSBhbmQgbGFiZWxzDQpwbHQudGl0bGUoIkNsYWltIFJlYXNvbiBieSBTYWxlcyBDaGFubmVsIikNCnBsdC54bGFiZWwoIlNhbGVzIENoYW5uZWwiKQ0KcGx0LnlsYWJlbCgiQ2xhaW0gUmVhc29uIikNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMjIFZpenVhbGl6YXRpb24gNC4gTmVzdGVkIFBpZSBDaGFydDogQ2xhaW0gUmVhc29uIGJ5IFZlaGljbGUgQ2xhc3MNCg0KVGhlIG5lc3RlZCBwaWUgY2hhcnQgaWxsdXN0cmF0ZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBpbnN1cmFuY2UgY2xhaW1zIGJ5IGNsYWltIHJlYXNvbiBhbmQgdmVoaWNsZSBjbGFzcy4gVGhlIGlubmVyIHNlY3Rpb25zIHJlcHJlc2VudCB0aGUgZGlmZmVyZW50IGNsYWltIHJlYXNvbnMsIHdoaWxlIHRoZSBvdXRlciBzZWN0aW9ucyBicmVhayBlYWNoIGNsYWltIHJlYXNvbiBkb3duIGJ5IHZlaGljbGUgY2xhc3MuIFRoZSBzaXplIG9mIGVhY2ggc2VjdGlvbiBjb3JyZXNwb25kcyB0byB0aGUgbnVtYmVyIG9mIGNsYWltcyB3aXRoaW4gdGhhdCBjYXRlZ29yeSwgd2l0aCBsYXJnZXIgc2VjdGlvbnMgcmVwcmVzZW50aW5nIGEgZ3JlYXRlciBudW1iZXIgb2YgY2xhaW1zLg0KVGhpcyB2aXN1YWxpemF0aW9uIG1ha2VzIGl0IGVhc2llciB0byBpZGVudGlmeSB3aGljaCB2ZWhpY2xlIGNsYXNzZXMgYWNjb3VudCBmb3IgdGhlIGxhcmdlc3QgcG9ydGlvbnMgb2YgZWFjaCBjbGFpbSByZWFzb24uIEl0IGFsc28gcHJvdmlkZXMgYSBjbGVhcmVyIHVuZGVyc3RhbmRpbmcgb2YgaG93IGNsYWltIHJlYXNvbnMgYXJlIGRpc3RyaWJ1dGVkIGFjcm9zcyBkaWZmZXJlbnQgdmVoaWNsZSBjbGFzc2VzLiBCeSBleGFtaW5pbmcgYm90aCByaW5ncywgd2UgY2FuIGNvbXBhcmUgdGhlIG92ZXJhbGwgZnJlcXVlbmN5IG9mIGNsYWltIHJlYXNvbnMgYW5kIHRoZSB2ZWhpY2xlIGNsYXNzZXMgY29udHJpYnV0aW5nIHRvIHRob3NlIHRvdGFscy4NCg0KYGBge3B5dGhvbiwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzID0gJ2hpZGUnfQ0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KaW1wb3J0IHBsb3RseS5leHByZXNzIGFzIHB4DQoNCiMgQ291bnQgY2xhaW1zIGFuZCBjcmVhdGUgc3VuYnVyc3QgY2hhcnQgd2l0aCBjaGFpbmVkIHVwZGF0ZXMNCm5lc3RlZF9jb3VudHMgPSAoDQogICAgZGZbWyJDbGFpbSBSZWFzb24iLCAiVmVoaWNsZSBDbGFzcyJdXQ0KICAgIC5kcm9wbmEoKQ0KICAgIC5ncm91cGJ5KFsiQ2xhaW0gUmVhc29uIiwgIlZlaGljbGUgQ2xhc3MiXSkNCiAgICAuc2l6ZSgpDQogICAgLnJlc2V0X2luZGV4KG5hbWU9Ik51bWJlciBvZiBDbGFpbXMiKQ0KKQ0KDQpmaWcgPSBweC5zdW5idXJzdCgNCiAgICBuZXN0ZWRfY291bnRzLA0KICAgIHBhdGg9WyJDbGFpbSBSZWFzb24iLCAiVmVoaWNsZSBDbGFzcyJdLA0KICAgIHZhbHVlcz0iTnVtYmVyIG9mIENsYWltcyIsDQogICAgY29sb3I9IkNsYWltIFJlYXNvbiIsDQogICAgY29sb3JfZGlzY3JldGVfc2VxdWVuY2U9cHguY29sb3JzLnF1YWxpdGF0aXZlLlNhZmUsDQogICAgdGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBJbnN1cmFuY2UgQ2xhaW1zIGJ5IFJlYXNvbiBhbmQgVmVoaWNsZSBDbGFzcyINCikudXBkYXRlX3RyYWNlcygNCiAgICB0ZXh0aW5mbz0ibGFiZWwiLA0KICAgIGluc2lkZXRleHRvcmllbnRhdGlvbj0iaG9yaXpvbnRhbCIsDQogICAgaW5zaWRldGV4dGZvbnQ9ZGljdChzaXplPTEzKSwNCiAgICBob3ZlcnRlbXBsYXRlPSgNCiAgICAgICAgIjxiPiV7bGFiZWx9PC9iPjxicj4iDQogICAgICAgICJDbGFpbXM6ICV7dmFsdWU6LH08YnI+Ig0KICAgICAgICAiU2hhcmUgb2YgVG90YWw6ICV7cGVyY2VudFJvb3Q6LjElfTxicj4iDQogICAgICAgICJQZXJjZW50YWdlIG9mIFBhcmVudDogJXtwZXJjZW50UGFyZW50Oi4xJX0iDQogICAgICAgICI8ZXh0cmE+PC9leHRyYT4iDQogICAgKSwNCiAgICBtYXhkZXB0aD0yDQopLnVwZGF0ZV9sYXlvdXQoDQogICAgd2lkdGg9OTUwLCBoZWlnaHQ9NzUwLA0KICAgIHRpdGxlPWRpY3QodGV4dD0iRGlzdHJpYnV0aW9uIG9mIEluc3VyYW5jZSBDbGFpbXMgYnkgUmVhc29uIGFuZCBWZWhpY2xlIENsYXNzIiwgeD0wLjUsIHhhbmNob3I9ImNlbnRlciIsIGZvbnQ9ZGljdChzaXplPTIwKSksDQogICAgbWFyZ2luPWRpY3QodD0xMDAsIGw9MzAsIHI9MzAsIGI9MzApLA0KICAgIHVuaWZvcm10ZXh0PWRpY3QobWluc2l6ZT0xMSwgbW9kZT0iaGlkZSIpDQopDQoNCmZpZw0KYGBgDQoNCiMjIyBWaXp1YWxpemF0aW9uIDUuIFN0YWNrZWQgQmFyIENoYXJ0OiBJbnN1cmFuY2UgQ2xhaW1zIGJ5IFNhbGVzIENoYW5uZWwgYW5kIENsYWltIFJlYXNvbg0KDQpUaGUgc3RhY2tlZCBiYXIgY2hhcnQgY29tcGFyZXMgdGhlIG51bWJlciBvZiBpbnN1cmFuY2UgY2xhaW1zIGFjcm9zcyBkaWZmZXJlbnQgc2FsZXMgY2hhbm5lbHMgYW5kIHNob3dzIGhvdyBlYWNoIHNhbGVzIGNoYW5uZWwgaXMgZGl2aWRlZCBieSBjbGFpbSByZWFzb24uIEVhY2ggYmFyIHJlcHJlc2VudHMgYSBzYWxlcyBjaGFubmVsLCB3aGlsZSB0aGUgY29sb3JlZCBzZWN0aW9ucyByZXByZXNlbnQgdGhlIGRpZmZlcmVudCBjbGFpbSByZWFzb25zLiBUaGUgdG90YWwgaGVpZ2h0IG9mIGVhY2ggYmFyIGluZGljYXRlcyB0aGUgb3ZlcmFsbCBudW1iZXIgb2YgY2xhaW1zIGFzc29jaWF0ZWQgd2l0aCB0aGF0IHNhbGVzIGNoYW5uZWwuDQpUaGlzIHZpc3VhbGl6YXRpb24gaGVscHMgaWRlbnRpZnkgd2hpY2ggc2FsZXMgY2hhbm5lbHMgYWNjb3VudCBmb3IgdGhlIGdyZWF0ZXN0IG51bWJlciBvZiBjbGFpbXMgYW5kIHdoaWNoIGNsYWltIHJlYXNvbnMgb2NjdXIgbW9zdCBmcmVxdWVudGx5IHdpdGhpbiBlYWNoIGNoYW5uZWwuIENvbXBhcmluZyB0aGUgY29sb3JlZCBzZWN0aW9ucyBhbHNvIG1ha2VzIGl0IHBvc3NpYmxlIHRvIG9ic2VydmUgc2ltaWxhcml0aWVzIGFuZCBkaWZmZXJlbmNlcyBpbiBjbGFpbSBwYXR0ZXJucyBhY3Jvc3Mgc2FsZXMgY2hhbm5lbHMuIFRoZXNlIHBhdHRlcm5zIHByb3ZpZGUgaW5zaWdodCBpbnRvIGhvdyBjbGFpbSByZWFzb25zIGFyZSBkaXN0cmlidXRlZCBhbW9uZyB0aGUgY29tcGFueSdzIGRpZmZlcmVudCBtZXRob2RzIG9mIHNlbGxpbmcgaW5zdXJhbmNlLg0KDQpgYGB7cHl0aG9uLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9DQojIFN0YWNrZWQgQmFyIENoYXJ0OiBDbGFpbSBSZWFzb24gYnkgU2FsZXMgQ2hhbm5lbA0KDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyBSZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzDQpzdGFja2VkX2RhdGEgPSBkZltbIlNhbGVzIENoYW5uZWwiLCAiQ2xhaW0gUmVhc29uIl1dLmRyb3BuYSgpDQoNCiMgQ291bnQgY2xhaW1zIGJ5IHNhbGVzIGNoYW5uZWwgYW5kIGNsYWltIHJlYXNvbg0Kc3RhY2tlZF9jb3VudHMgPSBwZC5jcm9zc3RhYigNCiAgICBzdGFja2VkX2RhdGFbIlNhbGVzIENoYW5uZWwiXSwNCiAgICBzdGFja2VkX2RhdGFbIkNsYWltIFJlYXNvbiJdDQopDQoNCiMgQ3JlYXRlIHN0YWNrZWQgYmFyIGNoYXJ0DQpheCA9IHN0YWNrZWRfY291bnRzLnBsb3QoDQogICAga2luZD0iYmFyIiwNCiAgICBzdGFja2VkPVRydWUsDQogICAgZmlnc2l6ZT0oMTEsIDcpLA0KICAgIGNvbG9ybWFwPSJTZXQyIiwNCiAgICBlZGdlY29sb3I9ImJsYWNrIg0KKQ0KDQpwbHQudGl0bGUoDQogICAgIkluc3VyYW5jZSBDbGFpbXMgYnkgU2FsZXMgQ2hhbm5lbCBhbmQgQ2xhaW0gUmVhc29uIiwNCiAgICBmb250c2l6ZT0xNiwNCiAgICBmb250d2VpZ2h0PSJib2xkIiwNCiAgICBwYWQ9MTUNCikNCg0KcGx0LnhsYWJlbCgiU2FsZXMgQ2hhbm5lbCIsIGZvbnRzaXplPTEyKQ0KcGx0LnlsYWJlbCgiTnVtYmVyIG9mIENsYWltcyIsIGZvbnRzaXplPTEyKQ0KDQpwbHQueHRpY2tzKHJvdGF0aW9uPTE1KQ0KcGx0LmxlZ2VuZCh0aXRsZT0iQ2xhaW0gUmVhc29uIiwgYmJveF90b19hbmNob3I9KDEuMDIsIDEpLCBsb2M9InVwcGVyIGxlZnQiKQ0KDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCmBgYA0KDQojIDQuIEtleSBGaW5kaW5ncw0KDQpTZXZlcmFsIGludGVyZXN0aW5nIHBhdHRlcm5zIGVtZXJnZWQgZnJvbSB0aGUgaW5pdGlhbCBhbmFseXNpcyBvZiB0aGUgYXV0byBpbnN1cmFuY2UgY2xhaW1zIGRhdGFzZXQuIENvbGxpc2lvbiB3YXMgdGhlIG1vc3QgY29tbW9uIHJlYXNvbiBmb3IgZmlsaW5nIGEgY2xhaW0sIGZvbGxvd2VkIGJ5IGhhaWwgZGFtYWdlLiBGb3VyLWRvb3IgY2FycyByZXByZXNlbnRlZCB0aGUgbGFyZ2VzdCB2ZWhpY2xlIGNhdGVnb3J5LCBhY2NvdW50aW5nIGZvciBhcHByb3hpbWF0ZWx5IGhhbGYgb2YgdGhlIG9ic2VydmF0aW9ucy4gQWRkaXRpb25hbGx5LCBhZ2VudHMgd2VyZSB0aGUgbW9zdCBmcmVxdWVudGx5IHVzZWQgc2FsZXMgY2hhbm5lbCwgd2hpbGUgd2ViIHNhbGVzIHJlcHJlc2VudGVkIHRoZSBzbWFsbGVzdCBjYXRlZ29yeS4gUGVyc29uYWwgYXV0byBwb2xpY2llcyBhbmQgYmFzaWMgY292ZXJhZ2Ugd2VyZSBhbHNvIHNpZ25pZmljYW50bHkgbW9yZSBjb21tb24gdGhhbiB0aGUgb3RoZXIgcG9saWN5IGFuZCBjb3ZlcmFnZSBvcHRpb25zLg0KDQpUaGUgbnVtZXJpY2FsIHN0YXRpc3RpY3MgcmV2ZWFsZWQgdGhhdCB0aGUgYXZlcmFnZSBjdXN0b21lciBpbmNvbWUgd2FzIGFwcHJveGltYXRlbHkgJDM3LDY1NywgY29tcGFyZWQgdG8gYSBtZWRpYW4gaW5jb21lIG9mICQzMyw4OTAuIFRoZSBhdmVyYWdlIHRvdGFsIGNsYWltIGFtb3VudCB3YXMgYXBwcm94aW1hdGVseSAkNDM0LCB3aXRoIGEgbWVkaWFuIG9mICQzODQuIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlc2UgYXZlcmFnZXMgYW5kIG1lZGlhbnMgc3VnZ2VzdHMgc29tZSB2YXJpYXRpb24gaW4gY3VzdG9tZXIgaW5jb21lIGFuZCBjbGFpbSBhbW91bnRzLCBwb3RlbnRpYWxseSBpbmZsdWVuY2VkIGJ5IGhpZ2hlci12YWx1ZSBvYnNlcnZhdGlvbnMuIE92ZXJhbGwsIHRoZXNlIGZpbmRpbmdzIHByb3ZpZGUgYW4gaW5pdGlhbCB1bmRlcnN0YW5kaW5nIG9mIHRoZSBjdXN0b21lcnMsIHZlaGljbGVzLCBhbmQgY2xhaW1zIHJlcHJlc2VudGVkIGluIHRoZSBkYXRhc2V0IGFuZCBoZWxwIGVzdGFibGlzaCB0aGUgZm91bmRhdGlvbiBmb3IgdGhlIHZpc3VhbCBhbmFseXNpcy4NCg0KIyA1LiBDb25jbHVzaW9uDQoNCk92ZXJhbGwsIHRoZSBhbmFseXNpcyBvZiB0aGUgYXV0byBpbnN1cmFuY2UgY2xhaW1zIGRhdGFzZXQgcmV2ZWFsZWQgc2V2ZXJhbCBwYXR0ZXJucyBpbiBjdXN0b21lciBkZW1vZ3JhcGhpY3MsIHZlaGljbGUgdHlwZXMsIGluc3VyYW5jZSBwb2xpY2llcywgYW5kIGNsYWltIGFjdGl2aXR5LiBDb2xsaXNpb25zIHdlcmUgdGhlIG1vc3QgY29tbW9uIHJlYXNvbiBmb3IgY2xhaW1zLCBmb3VyLWRvb3IgY2FycyByZXByZXNlbnRlZCB0aGUgbGFyZ2VzdCB2ZWhpY2xlIGNhdGVnb3J5LCBhbmQgcGVyc29uYWwgYXV0byBwb2xpY2llcyB3aXRoIGJhc2ljIGNvdmVyYWdlIHdlcmUgdGhlIG1vc3QgY29tbW9uLiBUaGUgdmlzdWFsaXphdGlvbnMgYWxzbyBwcm92aWRlZCBpbnNpZ2h0IGludG8gcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGN1c3RvbWVyIGluY29tZSwgY2xhaW0gYW1vdW50cywgYW5kIGRpZmZlcmVudCBpbnN1cmFuY2UgY2F0ZWdvcmllcy4gT3ZlcmFsbCwgdGhpcyBhbmFseXNpcyBkZW1vbnN0cmF0ZXMgaG93IGRhdGEgdmlzdWFsaXphdGlvbiBhbmQgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBjYW4gaGVscCBpZGVudGlmeSB0cmVuZHMgYW5kIGJldHRlciB1bmRlcnN0YW5kIHBhdHRlcm5zIHdpdGhpbiB0aGUgYXV0byBpbnN1cmFuY2UgaW5kdXN0cnkuDQoNCg==