Step 1: Data Analysis & Visualization
The histograms above show:
1. Trace Distribution: Most states have trace ≈
1, confirming proper normalization.
2. Purity Distribution: Purity (Tr(ρ²)) varies,
indicating a mix of pure and mixed states.
3. Eigenvalue Distribution: The eigenvalues are mostly
positive and sum to 1, validating the dataset.
Step 3: Dataset Preparation for Capstone
Now, I’ll convert this dataset into CSV format for
easy integration with ML frameworks.
Step 1: Data Analysis & Visualization
The histograms above show: 1. Trace Distribution:
Most states have trace ≈ 1, confirming proper
normalization. 2. Purity Distribution: Purity (Tr(ρ²))
varies, indicating a mix of pure and mixed states. 3.
Eigenvalue Distribution: The eigenvalues are mostly
positive and sum to 1, validating the dataset.
Step 3: Dataset Preparation for Capstone
Now, I’ll convert this dataset into CSV format for
easy integration with ML frameworks.
Step 3 Completed: Dataset Saved as CSV
Your Quantum State Features dataset is now available
for download.
Download
Quantum_State_Features.csv
import matplotlib.pyplot as plt
import seaborn as sns
# Function to compute quantum state statistics
def compute_statistics(dataset):
traces = []
purities = []
eigenvalues = []
for state in dataset:
trace_val = np.trace(state) # Should be ~1 for valid density matrices
purity = np.trace(state @ state) # Purity: Tr(ρ²), ranges from 1 (pure) to 1/dim (maximally mixed)
eigvals = np.linalg.eigvals(state) # Eigenvalues of the density matrix
traces.append(trace_val.real) # Trace should be real
purities.append(purity.real) # Purity should be real
eigenvalues.extend(eigvals.real) # Store real parts of eigenvalues
return traces, purities, eigenvalues
# Compute statistics
traces, purities, eigenvalues = compute_statistics(dataset)
# Plot distributions of trace, purity, and eigenvalues
fig, axs = plt.subplots(3, 1, figsize=(8, 12))
sns.histplot(traces, bins=50, kde=True, ax=axs[0])
axs[0].set_title("Trace Distribution of Quantum States")
axs[0].set_xlabel("Trace Value")
axs[0].set_ylabel("Frequency")
sns.histplot(purities, bins=50, kde=True, ax=axs[1])
axs[1].set_title("Purity Distribution of Quantum States")
axs[1].set_xlabel("Purity (Tr(ρ²))")
axs[1].set_ylabel("Frequency")
sns.histplot(eigenvalues, bins=50, kde=True, ax=axs[2])
axs[2].set_title("Eigenvalue Distribution of Quantum States")
axs[2].set_xlabel("Eigenvalue")
axs[2].set_ylabel("Frequency")
plt.tight_layout()
plt.show()
import pandas as pd
# Function to extract features from quantum states
def extract_features(dataset):
feature_list = []
for state in dataset:
trace_val = np.trace(state).real # Real part of trace
purity = np.trace(state @ state).real # Tr(ρ²)
eigvals = np.linalg.eigvals(state).real # Eigenvalues
# Flatten density matrix into vector (real & imaginary parts separately)
flattened_real = state.real.flatten()
flattened_imag = state.imag.flatten()
# Combine all features
features = np.concatenate(([trace_val, purity], eigvals, flattened_real, flattened_imag))
feature_list.append(features)
return feature_list
# Extract features
features = extract_features(dataset)
# Convert to Pandas DataFrame for easy analysis
feature_columns = ["Trace", "Purity", "Eigval1", "Eigval2"] + \
[f"Real_{i}" for i in range(4)] + [f"Imag_{i}" for i in range(4)]
df_features = pd.DataFrame(features, columns=feature_columns)
# Display extracted feature dataframe
import ace_tools as tools
tools.display_dataframe_to_user(name="Quantum State Features", dataframe=df_features)
Step 1: Data Analysis & Visualization
The histograms below show:
- Trace Distribution: Most states have trace
≈ 1, confirming proper normalization.
- Purity Distribution: Purity (Tr(ρ²)) varies,
indicating a mix of pure and mixed states.
- Eigenvalue Distribution: The eigenvalues are mostly
positive and sum to 1, validating the dataset.
Code for Visualization
import matplotlib.pyplot as plt
import seaborn as sns
# Function to compute quantum state statistics
def compute_statistics(dataset):
traces = []
purities = []
eigenvalues = []
for state in dataset:
trace_val = np.trace(state) # Should be ~1 for valid density matrices
purity = np.trace(state @ state) # Purity: Tr(ρ²), ranges from 1 (pure) to 1/dim (maximally mixed)
eigvals = np.linalg.eigvals(state) # Eigenvalues of the density matrix
traces.append(trace_val.real) # Trace should be real
purities.append(purity.real) # Purity should be real
eigenvalues.extend(eigvals.real) # Store real parts of eigenvalues
return traces, purities, eigenvalues
# Compute statistics
traces, purities, eigenvalues = compute_statistics(dataset)
# Plot distributions of trace, purity, and eigenvalues
fig, axs = plt.subplots(3, 1, figsize=(8, 12))
sns.histplot(traces, bins=50, kde=True, ax=axs[0])
axs[0].set_title("Trace Distribution of Quantum States")
axs[0].set_xlabel("Trace Value")
axs[0].set_ylabel("Frequency")
sns.histplot(purities, bins=50, kde=True, ax=axs[1])
axs[1].set_title("Purity Distribution of Quantum States")
axs[1].set_xlabel("Purity (Tr(ρ²))")
axs[1].set_ylabel("Frequency")
sns.histplot(eigenvalues, bins=50, kde=True, ax=axs[2])
axs[2].set_title("Eigenvalue Distribution of Quantum States")
axs[2].set_xlabel("Eigenvalue")
axs[2].set_ylabel("Frequency")
plt.tight_layout()
plt.show()
Step 3: Dataset Preparation for Capstone
Now, I’ll convert this dataset into CSV format for
easy integration with ML frameworks.
Convert to CSV
# Save extracted features as a CSV file for ML processing
csv_output_path = "/mnt/data/Quantum_State_Features.csv"
df_features.to_csv(csv_output_path, index=False)
# Provide the CSV file for download
csv_output_path
Step 3 Completed: Dataset Saved as CSV
Your Quantum State Features dataset is now available
for download.
Download
Quantum_State_Features.csv
This file contains the extracted features from the quantum dataset,
ready for machine learning and Capstone analysis.
Step 1: Data Analysis & Visualization
The histograms below show:
- Trace Distribution: Most states have trace
≈ 1, confirming proper normalization.
- Purity Distribution: Purity (Tr(ρ²)) varies,
indicating a mix of pure and mixed states.
- Eigenvalue Distribution: The eigenvalues are mostly
positive and sum to 1, validating the dataset.
Code for Visualization
import matplotlib.pyplot as plt
import seaborn as sns
# Function to compute quantum state statistics
def compute_statistics(dataset):
traces = []
purities = []
eigenvalues = []
for state in dataset:
trace_val = np.trace(state) # Should be ~1 for valid density matrices
purity = np.trace(state @ state) # Purity: Tr(ρ²), ranges from 1 (pure) to 1/dim (maximally mixed)
eigvals = np.linalg.eigvals(state) # Eigenvalues of the density matrix
traces.append(trace_val.real) # Trace should be real
purities.append(purity.real) # Purity should be real
eigenvalues.extend(eigvals.real) # Store real parts of eigenvalues
return traces, purities, eigenvalues
# Compute statistics
traces, purities, eigenvalues = compute_statistics(dataset)
# Plot distributions of trace, purity, and eigenvalues
fig, axs = plt.subplots(3, 1, figsize=(8, 12))
sns.histplot(traces, bins=50, kde=True, ax=axs[0])
axs[0].set_title("Trace Distribution of Quantum States")
axs[0].set_xlabel("Trace Value")
axs[0].set_ylabel("Frequency")
sns.histplot(purities, bins=50, kde=True, ax=axs[1])
axs[1].set_title("Purity Distribution of Quantum States")
axs[1].set_xlabel("Purity (Tr(ρ²))")
axs[1].set_ylabel("Frequency")
sns.histplot(eigenvalues, bins=50, kde=True, ax=axs[2])
axs[2].set_title("Eigenvalue Distribution of Quantum States")
axs[2].set_xlabel("Eigenvalue")
axs[2].set_ylabel("Frequency")
plt.tight_layout()
plt.show()
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCi0tLQ0KDQojIyMgKipTdGVwIDE6IERhdGEgQW5hbHlzaXMgJiBWaXN1YWxpemF0aW9uKioNCg0KVGhlIGhpc3RvZ3JhbXMgYWJvdmUgc2hvdzogIA0KMS4gKipUcmFjZSBEaXN0cmlidXRpb24qKjogTW9zdCBzdGF0ZXMgaGF2ZSAqKnRyYWNlIOKJiCAxKiosIGNvbmZpcm1pbmcgcHJvcGVyIG5vcm1hbGl6YXRpb24uICANCjIuICoqUHVyaXR5IERpc3RyaWJ1dGlvbioqOiBQdXJpdHkgKFRyKM+BwrIpKSB2YXJpZXMsIGluZGljYXRpbmcgYSBtaXggb2YgKipwdXJlIGFuZCBtaXhlZCBzdGF0ZXMqKi4gIA0KMy4gKipFaWdlbnZhbHVlIERpc3RyaWJ1dGlvbioqOiBUaGUgZWlnZW52YWx1ZXMgYXJlIG1vc3RseSAqKnBvc2l0aXZlIGFuZCBzdW0gdG8gMSoqLCB2YWxpZGF0aW5nIHRoZSBkYXRhc2V0LiAgDQoNCi0tLQ0KDQojIyMgKipTdGVwIDI6IEZlYXR1cmUgRXh0cmFjdGlvbiBmb3IgTUwgTW9kZWxzKioNCg0KTm93LCBjb252ZXJ0IGVhY2ggKipxdWFudHVtIHN0YXRlICgyeDIgbWF0cml4KSoqIGludG8gYSBmZWF0dXJlIHZlY3RvciBmb3IgbWFjaGluZSBsZWFybmluZzoNCi0gKipGbGF0dGVuIHRoZSBkZW5zaXR5IG1hdHJpeCoqIGludG8gYSB2ZWN0b3IuICANCi0gKipFeHRyYWN0IHB1cml0eSwgdHJhY2UsIGFuZCBlaWdlbnZhbHVlcyoqIGFzIGZlYXR1cmVzLiAgDQotICoqU3RvcmUgaW4gYSBzdHJ1Y3R1cmVkIGZvcm1hdCoqIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiAgDQoNCi0tLQ0KDQojIyMgKipTdGVwIDIgQ29tcGxldGVkOiBGZWF0dXJlIEV4dHJhY3Rpb24qKg0KDQpJIGhhdmUgdHJhbnNmb3JtZWQgZWFjaCAqKnF1YW50dW0gc3RhdGUgKDJ4MiBtYXRyaXgpKiogaW50byBhIHN0cnVjdHVyZWQgZmVhdHVyZSBzZXQsIGluY2x1ZGluZzoNCi0gKipUcmFjZSAmIFB1cml0eSoqICh2YWxpZGF0aW9uIG1ldHJpY3MpICANCi0gKipFaWdlbnZhbHVlcyoqIChxdWFudHVtIHN0YXRlIHByb3BlcnRpZXMpICANCi0gKipGbGF0dGVuZWQgcmVhbCAmIGltYWdpbmFyeSBwYXJ0cyoqIG9mIHRoZSBkZW5zaXR5IG1hdHJpeCAgDQoNCi0tLQ0KDQojIyMgKipTdGVwIDM6IERhdGFzZXQgUHJlcGFyYXRpb24gZm9yIENhcHN0b25lKioNCg0KTm93LCBJJ2xsICoqY29udmVydCB0aGlzIGRhdGFzZXQgaW50byBDU1YgZm9ybWF0KiogZm9yIGVhc3kgaW50ZWdyYXRpb24gd2l0aCBNTCBmcmFtZXdvcmtzLiAgDQoNCi0tLQ0KDQojIyMgKipTdGVwIDMgQ29tcGxldGVkOiBEYXRhc2V0IFNhdmVkIGFzIENTVioqDQoNCllvdXIgKipRdWFudHVtIFN0YXRlIEZlYXR1cmVzIGRhdGFzZXQqKiBpcyBub3cgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4NCg0K8J+TgiAqKltEb3dubG9hZCBRdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdl0oc2FuZGJveDovbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YpKiogIA0KDQotLS0NCg0KDQoNCg0KIyMjICoqU3RlcCAxOiBEYXRhIEFuYWx5c2lzICYgVmlzdWFsaXphdGlvbioqDQoNClRoZSBoaXN0b2dyYW1zIGFib3ZlIHNob3c6IDEuICoqVHJhY2UgRGlzdHJpYnV0aW9uKio6IE1vc3Qgc3RhdGVzIGhhdmUNCioqdHJhY2Ug4omIIDEqKiwgY29uZmlybWluZyBwcm9wZXIgbm9ybWFsaXphdGlvbi4gMi4gKipQdXJpdHkNCkRpc3RyaWJ1dGlvbioqOiBQdXJpdHkgKFRyKM+BwrIpKSB2YXJpZXMsIGluZGljYXRpbmcgYSBtaXggb2YgKipwdXJlIGFuZA0KbWl4ZWQgc3RhdGVzKiouIDMuICoqRWlnZW52YWx1ZSBEaXN0cmlidXRpb24qKjogVGhlIGVpZ2VudmFsdWVzIGFyZQ0KbW9zdGx5ICoqcG9zaXRpdmUgYW5kIHN1bSB0byAxKiosIHZhbGlkYXRpbmcgdGhlIGRhdGFzZXQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDI6IEZlYXR1cmUgRXh0cmFjdGlvbiBmb3IgTUwgTW9kZWxzKioNCg0KTm93LCBjb252ZXJ0IGVhY2ggKipxdWFudHVtIHN0YXRlICgyeDIgbWF0cml4KSoqIGludG8gYSBmZWF0dXJlIHZlY3Rvcg0KZm9yIG1hY2hpbmUgbGVhcm5pbmc6IC0gKipGbGF0dGVuIHRoZSBkZW5zaXR5IG1hdHJpeCoqIGludG8gYSB2ZWN0b3IuIC0NCioqRXh0cmFjdCBwdXJpdHksIHRyYWNlLCBhbmQgZWlnZW52YWx1ZXMqKiBhcyBmZWF0dXJlcy4gLSAqKlN0b3JlIGluIGENCnN0cnVjdHVyZWQgZm9ybWF0KiogZm9yIGZ1cnRoZXIgYW5hbHlzaXMuDQoNCiMjIyAqKlN0ZXAgMiBDb21wbGV0ZWQ6IEZlYXR1cmUgRXh0cmFjdGlvbioqDQoNCkkgaGF2ZSB0cmFuc2Zvcm1lZCBlYWNoICoqcXVhbnR1bSBzdGF0ZSAoMngyIG1hdHJpeCkqKiBpbnRvIGEgc3RydWN0dXJlZA0KZmVhdHVyZSBzZXQsIGluY2x1ZGluZzogLSAqKlRyYWNlICYgUHVyaXR5KiogKHZhbGlkYXRpb24gbWV0cmljcykgLQ0KKipFaWdlbnZhbHVlcyoqIChxdWFudHVtIHN0YXRlIHByb3BlcnRpZXMpIC0gKipGbGF0dGVuZWQgcmVhbCAmDQppbWFnaW5hcnkgcGFydHMqKiBvZiB0aGUgZGVuc2l0eSBtYXRyaXgNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN0ZXAgMzogRGF0YXNldCBQcmVwYXJhdGlvbiBmb3IgQ2Fwc3RvbmUqKg0KDQpOb3csIEknbGwgKipjb252ZXJ0IHRoaXMgZGF0YXNldCBpbnRvIENTViBmb3JtYXQqKiBmb3IgZWFzeSBpbnRlZ3JhdGlvbg0Kd2l0aCBNTCBmcmFtZXdvcmtzLg0KDQojIyMgKipTdGVwIDMgQ29tcGxldGVkOiBEYXRhc2V0IFNhdmVkIGFzIENTVioqDQoNCllvdXIgKipRdWFudHVtIFN0YXRlIEZlYXR1cmVzIGRhdGFzZXQqKiBpcyBub3cgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4NCg0KW0Rvd25sb2FkDQpRdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdl0oc2FuZGJveDovbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YpDQoNCmBgYHtweXRob259DQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyBGdW5jdGlvbiB0byBjb21wdXRlIHF1YW50dW0gc3RhdGUgc3RhdGlzdGljcw0KZGVmIGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KToNCiAgICB0cmFjZXMgPSBbXQ0KICAgIHB1cml0aWVzID0gW10NCiAgICBlaWdlbnZhbHVlcyA9IFtdDQoNCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpICAjIFNob3VsZCBiZSB+MSBmb3IgdmFsaWQgZGVuc2l0eSBtYXRyaWNlcw0KICAgICAgICBwdXJpdHkgPSBucC50cmFjZShzdGF0ZSBAIHN0YXRlKSAgIyBQdXJpdHk6IFRyKM+BwrIpLCByYW5nZXMgZnJvbSAxIChwdXJlKSB0byAxL2RpbSAobWF4aW1hbGx5IG1peGVkKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpICAjIEVpZ2VudmFsdWVzIG9mIHRoZSBkZW5zaXR5IG1hdHJpeA0KDQogICAgICAgIHRyYWNlcy5hcHBlbmQodHJhY2VfdmFsLnJlYWwpICAjIFRyYWNlIHNob3VsZCBiZSByZWFsDQogICAgICAgIHB1cml0aWVzLmFwcGVuZChwdXJpdHkucmVhbCkgICMgUHVyaXR5IHNob3VsZCBiZSByZWFsDQogICAgICAgIGVpZ2VudmFsdWVzLmV4dGVuZChlaWd2YWxzLnJlYWwpICAjIFN0b3JlIHJlYWwgcGFydHMgb2YgZWlnZW52YWx1ZXMNCg0KICAgIHJldHVybiB0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcw0KDQojIENvbXB1dGUgc3RhdGlzdGljcw0KdHJhY2VzLCBwdXJpdGllcywgZWlnZW52YWx1ZXMgPSBjb21wdXRlX3N0YXRpc3RpY3MoZGF0YXNldCkNCg0KIyBQbG90IGRpc3RyaWJ1dGlvbnMgb2YgdHJhY2UsIHB1cml0eSwgYW5kIGVpZ2VudmFsdWVzDQpmaWcsIGF4cyA9IHBsdC5zdWJwbG90cygzLCAxLCBmaWdzaXplPSg4LCAxMikpDQoNCnNucy5oaXN0cGxvdCh0cmFjZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMF0pDQpheHNbMF0uc2V0X3RpdGxlKCJUcmFjZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzBdLnNldF94bGFiZWwoIlRyYWNlIFZhbHVlIikNCmF4c1swXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QocHVyaXRpZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMV0pDQpheHNbMV0uc2V0X3RpdGxlKCJQdXJpdHkgRGlzdHJpYnV0aW9uIG9mIFF1YW50dW0gU3RhdGVzIikNCmF4c1sxXS5zZXRfeGxhYmVsKCJQdXJpdHkgKFRyKM+BwrIpKSIpDQpheHNbMV0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0Kc25zLmhpc3RwbG90KGVpZ2VudmFsdWVzLCBiaW5zPTUwLCBrZGU9VHJ1ZSwgYXg9YXhzWzJdKQ0KYXhzWzJdLnNldF90aXRsZSgiRWlnZW52YWx1ZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzJdLnNldF94bGFiZWwoIkVpZ2VudmFsdWUiKQ0KYXhzWzJdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCmBgYHtweXRob259DQppbXBvcnQgcGFuZGFzIGFzIHBkDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBmZWF0dXJlcyBmcm9tIHF1YW50dW0gc3RhdGVzDQpkZWYgZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KToNCiAgICBmZWF0dXJlX2xpc3QgPSBbXQ0KICAgIA0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkucmVhbCAgIyBSZWFsIHBhcnQgb2YgdHJhY2UNCiAgICAgICAgcHVyaXR5ID0gbnAudHJhY2Uoc3RhdGUgQCBzdGF0ZSkucmVhbCAgIyBUcijPgcKyKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpLnJlYWwgICMgRWlnZW52YWx1ZXMNCiAgICAgICAgDQogICAgICAgICMgRmxhdHRlbiBkZW5zaXR5IG1hdHJpeCBpbnRvIHZlY3RvciAocmVhbCAmIGltYWdpbmFyeSBwYXJ0cyBzZXBhcmF0ZWx5KQ0KICAgICAgICBmbGF0dGVuZWRfcmVhbCA9IHN0YXRlLnJlYWwuZmxhdHRlbigpDQogICAgICAgIGZsYXR0ZW5lZF9pbWFnID0gc3RhdGUuaW1hZy5mbGF0dGVuKCkNCiAgICAgICAgDQogICAgICAgICMgQ29tYmluZSBhbGwgZmVhdHVyZXMNCiAgICAgICAgZmVhdHVyZXMgPSBucC5jb25jYXRlbmF0ZSgoW3RyYWNlX3ZhbCwgcHVyaXR5XSwgZWlndmFscywgZmxhdHRlbmVkX3JlYWwsIGZsYXR0ZW5lZF9pbWFnKSkNCiAgICAgICAgZmVhdHVyZV9saXN0LmFwcGVuZChmZWF0dXJlcykNCiAgICANCiAgICByZXR1cm4gZmVhdHVyZV9saXN0DQoNCiMgRXh0cmFjdCBmZWF0dXJlcw0KZmVhdHVyZXMgPSBleHRyYWN0X2ZlYXR1cmVzKGRhdGFzZXQpDQoNCiMgQ29udmVydCB0byBQYW5kYXMgRGF0YUZyYW1lIGZvciBlYXN5IGFuYWx5c2lzDQpmZWF0dXJlX2NvbHVtbnMgPSBbIlRyYWNlIiwgIlB1cml0eSIsICJFaWd2YWwxIiwgIkVpZ3ZhbDIiXSArIFwNCiAgICAgICAgICAgICAgICAgIFtmIlJlYWxfe2l9IiBmb3IgaSBpbiByYW5nZSg0KV0gKyBbZiJJbWFnX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldDQoNCmRmX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKGZlYXR1cmVzLCBjb2x1bW5zPWZlYXR1cmVfY29sdW1ucykNCg0KIyBEaXNwbGF5IGV4dHJhY3RlZCBmZWF0dXJlIGRhdGFmcmFtZQ0KaW1wb3J0IGFjZV90b29scyBhcyB0b29scw0KdG9vbHMuZGlzcGxheV9kYXRhZnJhbWVfdG9fdXNlcihuYW1lPSJRdWFudHVtIFN0YXRlIEZlYXR1cmVzIiwgZGF0YWZyYW1lPWRmX2ZlYXR1cmVzKQ0KYGBgDQoNCg0KIyMjICoqU3RlcCAxOiBEYXRhIEFuYWx5c2lzICYgVmlzdWFsaXphdGlvbioqDQoNClRoZSBoaXN0b2dyYW1zIGJlbG93IHNob3c6DQoNCjEuICAqKlRyYWNlIERpc3RyaWJ1dGlvbioqOiBNb3N0IHN0YXRlcyBoYXZlICoqdHJhY2Ug4omIIDEqKiwgY29uZmlybWluZw0KICAgIHByb3BlciBub3JtYWxpemF0aW9uLg0KMi4gICoqUHVyaXR5IERpc3RyaWJ1dGlvbioqOiBQdXJpdHkgKFRyKM+BwrIpKSB2YXJpZXMsIGluZGljYXRpbmcgYSBtaXggb2YNCiAgICAqKnB1cmUgYW5kIG1peGVkIHN0YXRlcyoqLg0KMy4gICoqRWlnZW52YWx1ZSBEaXN0cmlidXRpb24qKjogVGhlIGVpZ2VudmFsdWVzIGFyZSBtb3N0bHkgKipwb3NpdGl2ZQ0KICAgIGFuZCBzdW0gdG8gMSoqLCB2YWxpZGF0aW5nIHRoZSBkYXRhc2V0Lg0KDQojIyMjICoqQ29kZSBmb3IgVmlzdWFsaXphdGlvbioqDQoNCmBgYCBweXRob24NCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KDQojIEZ1bmN0aW9uIHRvIGNvbXB1dGUgcXVhbnR1bSBzdGF0ZSBzdGF0aXN0aWNzDQpkZWYgY29tcHV0ZV9zdGF0aXN0aWNzKGRhdGFzZXQpOg0KICAgIHRyYWNlcyA9IFtdDQogICAgcHVyaXRpZXMgPSBbXQ0KICAgIGVpZ2VudmFsdWVzID0gW10NCg0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkgICMgU2hvdWxkIGJlIH4xIGZvciB2YWxpZCBkZW5zaXR5IG1hdHJpY2VzDQogICAgICAgIHB1cml0eSA9IG5wLnRyYWNlKHN0YXRlIEAgc3RhdGUpICAjIFB1cml0eTogVHIoz4HCsiksIHJhbmdlcyBmcm9tIDEgKHB1cmUpIHRvIDEvZGltIChtYXhpbWFsbHkgbWl4ZWQpDQogICAgICAgIGVpZ3ZhbHMgPSBucC5saW5hbGcuZWlndmFscyhzdGF0ZSkgICMgRWlnZW52YWx1ZXMgb2YgdGhlIGRlbnNpdHkgbWF0cml4DQoNCiAgICAgICAgdHJhY2VzLmFwcGVuZCh0cmFjZV92YWwucmVhbCkgICMgVHJhY2Ugc2hvdWxkIGJlIHJlYWwNCiAgICAgICAgcHVyaXRpZXMuYXBwZW5kKHB1cml0eS5yZWFsKSAgIyBQdXJpdHkgc2hvdWxkIGJlIHJlYWwNCiAgICAgICAgZWlnZW52YWx1ZXMuZXh0ZW5kKGVpZ3ZhbHMucmVhbCkgICMgU3RvcmUgcmVhbCBwYXJ0cyBvZiBlaWdlbnZhbHVlcw0KDQogICAgcmV0dXJuIHRyYWNlcywgcHVyaXRpZXMsIGVpZ2VudmFsdWVzDQoNCiMgQ29tcHV0ZSBzdGF0aXN0aWNzDQp0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcyA9IGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KQ0KDQojIFBsb3QgZGlzdHJpYnV0aW9ucyBvZiB0cmFjZSwgcHVyaXR5LCBhbmQgZWlnZW52YWx1ZXMNCmZpZywgYXhzID0gcGx0LnN1YnBsb3RzKDMsIDEsIGZpZ3NpemU9KDgsIDEyKSkNCg0Kc25zLmhpc3RwbG90KHRyYWNlcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1swXSkNCmF4c1swXS5zZXRfdGl0bGUoIlRyYWNlIERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMF0uc2V0X3hsYWJlbCgiVHJhY2UgVmFsdWUiKQ0KYXhzWzBdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnNucy5oaXN0cGxvdChwdXJpdGllcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1sxXSkNCmF4c1sxXS5zZXRfdGl0bGUoIlB1cml0eSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzFdLnNldF94bGFiZWwoIlB1cml0eSAoVHIoz4HCsikpIikNCmF4c1sxXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QoZWlnZW52YWx1ZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMl0pDQpheHNbMl0uc2V0X3RpdGxlKCJFaWdlbnZhbHVlIERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMl0uc2V0X3hsYWJlbCgiRWlnZW52YWx1ZSIpDQpheHNbMl0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN0ZXAgMjogRmVhdHVyZSBFeHRyYWN0aW9uIGZvciBNTCBNb2RlbHMqKg0KDQpOb3csIEknbGwgY29udmVydCBlYWNoICoqcXVhbnR1bSBzdGF0ZSAoMngyIG1hdHJpeCkqKiBpbnRvIGEgZmVhdHVyZQ0KdmVjdG9yIGZvciBtYWNoaW5lIGxlYXJuaW5nOiAtICoqRmxhdHRlbiB0aGUgZGVuc2l0eSBtYXRyaXgqKiBpbnRvIGENCnZlY3Rvci4gLSAqKkV4dHJhY3QgcHVyaXR5LCB0cmFjZSwgYW5kIGVpZ2VudmFsdWVzKiogYXMgZmVhdHVyZXMuIC0NCioqU3RvcmUgaW4gYSBzdHJ1Y3R1cmVkIGZvcm1hdCoqIGZvciBmdXJ0aGVyIGFuYWx5c2lzLg0KDQojIyMjICoqRmVhdHVyZSBFeHRyYWN0aW9uIENvZGUqKg0KDQpgYGAgcHl0aG9uDQppbXBvcnQgcGFuZGFzIGFzIHBkDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBmZWF0dXJlcyBmcm9tIHF1YW50dW0gc3RhdGVzDQpkZWYgZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KToNCiAgICBmZWF0dXJlX2xpc3QgPSBbXQ0KICAgIA0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkucmVhbCAgIyBSZWFsIHBhcnQgb2YgdHJhY2UNCiAgICAgICAgcHVyaXR5ID0gbnAudHJhY2Uoc3RhdGUgQCBzdGF0ZSkucmVhbCAgIyBUcijPgcKyKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpLnJlYWwgICMgRWlnZW52YWx1ZXMNCiAgICAgICAgDQogICAgICAgICMgRmxhdHRlbiBkZW5zaXR5IG1hdHJpeCBpbnRvIHZlY3RvciAocmVhbCAmIGltYWdpbmFyeSBwYXJ0cyBzZXBhcmF0ZWx5KQ0KICAgICAgICBmbGF0dGVuZWRfcmVhbCA9IHN0YXRlLnJlYWwuZmxhdHRlbigpDQogICAgICAgIGZsYXR0ZW5lZF9pbWFnID0gc3RhdGUuaW1hZy5mbGF0dGVuKCkNCiAgICAgICAgDQogICAgICAgICMgQ29tYmluZSBhbGwgZmVhdHVyZXMNCiAgICAgICAgZmVhdHVyZXMgPSBucC5jb25jYXRlbmF0ZSgoW3RyYWNlX3ZhbCwgcHVyaXR5XSwgZWlndmFscywgZmxhdHRlbmVkX3JlYWwsIGZsYXR0ZW5lZF9pbWFnKSkNCiAgICAgICAgZmVhdHVyZV9saXN0LmFwcGVuZChmZWF0dXJlcykNCiAgICANCiAgICByZXR1cm4gZmVhdHVyZV9saXN0DQoNCiMgRXh0cmFjdCBmZWF0dXJlcw0KZmVhdHVyZXMgPSBleHRyYWN0X2ZlYXR1cmVzKGRhdGFzZXQpDQoNCiMgQ29udmVydCB0byBQYW5kYXMgRGF0YUZyYW1lIGZvciBlYXN5IGFuYWx5c2lzDQpmZWF0dXJlX2NvbHVtbnMgPSBbIlRyYWNlIiwgIlB1cml0eSIsICJFaWd2YWwxIiwgIkVpZ3ZhbDIiXSArIFwNCiAgICAgICAgICAgICAgICAgIFtmIlJlYWxfe2l9IiBmb3IgaSBpbiByYW5nZSg0KV0gKyBbZiJJbWFnX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldDQoNCmRmX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKGZlYXR1cmVzLCBjb2x1bW5zPWZlYXR1cmVfY29sdW1ucykNCg0KIyBEaXNwbGF5IGV4dHJhY3RlZCBmZWF0dXJlIGRhdGFmcmFtZQ0KaW1wb3J0IGFjZV90b29scyBhcyB0b29scw0KdG9vbHMuZGlzcGxheV9kYXRhZnJhbWVfdG9fdXNlcihuYW1lPSJRdWFudHVtIFN0YXRlIEZlYXR1cmVzIiwgZGF0YWZyYW1lPWRmX2ZlYXR1cmVzKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDIgQ29tcGxldGVkOiBGZWF0dXJlIEV4dHJhY3Rpb24qKg0KDQpJIGhhdmUgdHJhbnNmb3JtZWQgZWFjaCAqKnF1YW50dW0gc3RhdGUgKDJ4MiBtYXRyaXgpKiogaW50byBhIHN0cnVjdHVyZWQNCmZlYXR1cmUgc2V0LCBpbmNsdWRpbmc6IC0gKipUcmFjZSAmIFB1cml0eSoqICh2YWxpZGF0aW9uIG1ldHJpY3MpIC0NCioqRWlnZW52YWx1ZXMqKiAocXVhbnR1bSBzdGF0ZSBwcm9wZXJ0aWVzKSAtICoqRmxhdHRlbmVkIHJlYWwgJg0KaW1hZ2luYXJ5IHBhcnRzKiogb2YgdGhlIGRlbnNpdHkgbWF0cml4DQoNCllvdSBjYW4gbm93IHVzZSB0aGlzIGZvciAqKm1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzKiouDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDM6IERhdGFzZXQgUHJlcGFyYXRpb24gZm9yIENhcHN0b25lKioNCg0KTm93LCBJJ2xsICoqY29udmVydCB0aGlzIGRhdGFzZXQgaW50byBDU1YgZm9ybWF0KiogZm9yIGVhc3kgaW50ZWdyYXRpb24NCndpdGggTUwgZnJhbWV3b3Jrcy4NCg0KIyMjIyAqKkNvbnZlcnQgdG8gQ1NWKioNCg0KYGBgIHB5dGhvbg0KIyBTYXZlIGV4dHJhY3RlZCBmZWF0dXJlcyBhcyBhIENTViBmaWxlIGZvciBNTCBwcm9jZXNzaW5nDQpjc3Zfb3V0cHV0X3BhdGggPSAiL21udC9kYXRhL1F1YW50dW1fU3RhdGVfRmVhdHVyZXMuY3N2Ig0KZGZfZmVhdHVyZXMudG9fY3N2KGNzdl9vdXRwdXRfcGF0aCwgaW5kZXg9RmFsc2UpDQoNCiMgUHJvdmlkZSB0aGUgQ1NWIGZpbGUgZm9yIGRvd25sb2FkDQpjc3Zfb3V0cHV0X3BhdGgNCmBgYA0KDQojIyMgKipTdGVwIDMgQ29tcGxldGVkOiBEYXRhc2V0IFNhdmVkIGFzIENTVioqDQoNCllvdXIgKipRdWFudHVtIFN0YXRlIEZlYXR1cmVzIGRhdGFzZXQqKiBpcyBub3cgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4NCg0KW0Rvd25sb2FkDQpRdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdl0oc2FuZGJveDovbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YpDQoNClRoaXMgZmlsZSBjb250YWlucyB0aGUgZXh0cmFjdGVkIGZlYXR1cmVzIGZyb20gdGhlIHF1YW50dW0gZGF0YXNldCwNCnJlYWR5IGZvciAqKm1hY2hpbmUgbGVhcm5pbmcgYW5kIENhcHN0b25lIGFuYWx5c2lzKiouDQoNCg0KDQojIyMgKipTdGVwIDE6IERhdGEgQW5hbHlzaXMgJiBWaXN1YWxpemF0aW9uKioNCg0KVGhlIGhpc3RvZ3JhbXMgYmVsb3cgc2hvdzoNCg0KMS4gICoqVHJhY2UgRGlzdHJpYnV0aW9uKio6IE1vc3Qgc3RhdGVzIGhhdmUgKip0cmFjZSDiiYggMSoqLCBjb25maXJtaW5nDQogICAgcHJvcGVyIG5vcm1hbGl6YXRpb24uDQoyLiAgKipQdXJpdHkgRGlzdHJpYnV0aW9uKio6IFB1cml0eSAoVHIoz4HCsikpIHZhcmllcywgaW5kaWNhdGluZyBhIG1peCBvZg0KICAgICoqcHVyZSBhbmQgbWl4ZWQgc3RhdGVzKiouDQozLiAgKipFaWdlbnZhbHVlIERpc3RyaWJ1dGlvbioqOiBUaGUgZWlnZW52YWx1ZXMgYXJlIG1vc3RseSAqKnBvc2l0aXZlDQogICAgYW5kIHN1bSB0byAxKiosIHZhbGlkYXRpbmcgdGhlIGRhdGFzZXQuDQoNCiMjIyMgKipDb2RlIGZvciBWaXN1YWxpemF0aW9uKioNCg0KDQoNCmBgYCBweXRob24NCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KDQojIEZ1bmN0aW9uIHRvIGNvbXB1dGUgcXVhbnR1bSBzdGF0ZSBzdGF0aXN0aWNzDQpkZWYgY29tcHV0ZV9zdGF0aXN0aWNzKGRhdGFzZXQpOg0KICAgIHRyYWNlcyA9IFtdDQogICAgcHVyaXRpZXMgPSBbXQ0KICAgIGVpZ2VudmFsdWVzID0gW10NCg0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkgICMgU2hvdWxkIGJlIH4xIGZvciB2YWxpZCBkZW5zaXR5IG1hdHJpY2VzDQogICAgICAgIHB1cml0eSA9IG5wLnRyYWNlKHN0YXRlIEAgc3RhdGUpICAjIFB1cml0eTogVHIoz4HCsiksIHJhbmdlcyBmcm9tIDEgKHB1cmUpIHRvIDEvZGltIChtYXhpbWFsbHkgbWl4ZWQpDQogICAgICAgIGVpZ3ZhbHMgPSBucC5saW5hbGcuZWlndmFscyhzdGF0ZSkgICMgRWlnZW52YWx1ZXMgb2YgdGhlIGRlbnNpdHkgbWF0cml4DQoNCiAgICAgICAgdHJhY2VzLmFwcGVuZCh0cmFjZV92YWwucmVhbCkgICMgVHJhY2Ugc2hvdWxkIGJlIHJlYWwNCiAgICAgICAgcHVyaXRpZXMuYXBwZW5kKHB1cml0eS5yZWFsKSAgIyBQdXJpdHkgc2hvdWxkIGJlIHJlYWwNCiAgICAgICAgZWlnZW52YWx1ZXMuZXh0ZW5kKGVpZ3ZhbHMucmVhbCkgICMgU3RvcmUgcmVhbCBwYXJ0cyBvZiBlaWdlbnZhbHVlcw0KDQogICAgcmV0dXJuIHRyYWNlcywgcHVyaXRpZXMsIGVpZ2VudmFsdWVzDQoNCiMgQ29tcHV0ZSBzdGF0aXN0aWNzDQp0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcyA9IGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KQ0KDQojIFBsb3QgZGlzdHJpYnV0aW9ucyBvZiB0cmFjZSwgcHVyaXR5LCBhbmQgZWlnZW52YWx1ZXMNCmZpZywgYXhzID0gcGx0LnN1YnBsb3RzKDMsIDEsIGZpZ3NpemU9KDgsIDEyKSkNCg0Kc25zLmhpc3RwbG90KHRyYWNlcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1swXSkNCmF4c1swXS5zZXRfdGl0bGUoIlRyYWNlIERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMF0uc2V0X3hsYWJlbCgiVHJhY2UgVmFsdWUiKQ0KYXhzWzBdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnNucy5oaXN0cGxvdChwdXJpdGllcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1sxXSkNCmF4c1sxXS5zZXRfdGl0bGUoIlB1cml0eSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzFdLnNldF94bGFiZWwoIlB1cml0eSAoVHIoz4HCsikpIikNCmF4c1sxXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QoZWlnZW52YWx1ZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMl0pDQpheHNbMl0uc2V0X3RpdGxlKCJFaWdlbnZhbHVlIERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMl0uc2V0X3hsYWJlbCgiRWlnZW52YWx1ZSIpDQpheHNbMl0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KDQojIyAqKlN0ZXAgMjogRmVhdHVyZSBFeHRyYWN0aW9uIGZvciBNTCBNb2RlbHMqKg0KDQpOb3csIEknbGwgY29udmVydCBlYWNoICoqcXVhbnR1bSBzdGF0ZSAoMngyIG1hdHJpeCkqKiBpbnRvIGEgZmVhdHVyZQ0KdmVjdG9yIGZvciBtYWNoaW5lIGxlYXJuaW5nOiAtICoqRmxhdHRlbiB0aGUgZGVuc2l0eSBtYXRyaXgqKiBpbnRvIGENCnZlY3Rvci4gLSAqKkV4dHJhY3QgcHVyaXR5LCB0cmFjZSwgYW5kIGVpZ2VudmFsdWVzKiogYXMgZmVhdHVyZXMuIC0NCioqU3RvcmUgaW4gYSBzdHJ1Y3R1cmVkIGZvcm1hdCoqIGZvciBmdXJ0aGVyIGFuYWx5c2lzLg0KDQojIyMjICoqRmVhdHVyZSBFeHRyYWN0aW9uIENvZGUqKg0KDQpgYGAgcHl0aG9uDQppbXBvcnQgcGFuZGFzIGFzIHBkDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBmZWF0dXJlcyBmcm9tIHF1YW50dW0gc3RhdGVzDQpkZWYgZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KToNCiAgICBmZWF0dXJlX2xpc3QgPSBbXQ0KICAgIA0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkucmVhbCAgIyBSZWFsIHBhcnQgb2YgdHJhY2UNCiAgICAgICAgcHVyaXR5ID0gbnAudHJhY2Uoc3RhdGUgQCBzdGF0ZSkucmVhbCAgIyBUcijPgcKyKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpLnJlYWwgICMgRWlnZW52YWx1ZXMNCiAgICAgICAgDQogICAgICAgICMgRmxhdHRlbiBkZW5zaXR5IG1hdHJpeCBpbnRvIHZlY3RvciAocmVhbCAmIGltYWdpbmFyeSBwYXJ0cyBzZXBhcmF0ZWx5KQ0KICAgICAgICBmbGF0dGVuZWRfcmVhbCA9IHN0YXRlLnJlYWwuZmxhdHRlbigpDQogICAgICAgIGZsYXR0ZW5lZF9pbWFnID0gc3RhdGUuaW1hZy5mbGF0dGVuKCkNCiAgICAgICAgDQogICAgICAgICMgQ29tYmluZSBhbGwgZmVhdHVyZXMNCiAgICAgICAgZmVhdHVyZXMgPSBucC5jb25jYXRlbmF0ZSgoW3RyYWNlX3ZhbCwgcHVyaXR5XSwgZWlndmFscywgZmxhdHRlbmVkX3JlYWwsIGZsYXR0ZW5lZF9pbWFnKSkNCiAgICAgICAgZmVhdHVyZV9saXN0LmFwcGVuZChmZWF0dXJlcykNCiAgICANCiAgICByZXR1cm4gZmVhdHVyZV9saXN0DQoNCiMgRXh0cmFjdCBmZWF0dXJlcw0KZmVhdHVyZXMgPSBleHRyYWN0X2ZlYXR1cmVzKGRhdGFzZXQpDQoNCiMgQ29udmVydCB0byBQYW5kYXMgRGF0YUZyYW1lIGZvciBlYXN5IGFuYWx5c2lzDQpmZWF0dXJlX2NvbHVtbnMgPSBbIlRyYWNlIiwgIlB1cml0eSIsICJFaWd2YWwxIiwgIkVpZ3ZhbDIiXSArIFwNCiAgICAgICAgICAgICAgICAgIFtmIlJlYWxfe2l9IiBmb3IgaSBpbiByYW5nZSg0KV0gKyBbZiJJbWFnX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldDQoNCmRmX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKGZlYXR1cmVzLCBjb2x1bW5zPWZlYXR1cmVfY29sdW1ucykNCg0KIyBEaXNwbGF5IGV4dHJhY3RlZCBmZWF0dXJlIGRhdGFmcmFtZQ0KaW1wb3J0IGFjZV90b29scyBhcyB0b29scw0KdG9vbHMuZGlzcGxheV9kYXRhZnJhbWVfdG9fdXNlcihuYW1lPSJRdWFudHVtIFN0YXRlIEZlYXR1cmVzIiwgZGF0YWZyYW1lPWRmX2ZlYXR1cmVzKQ0KYGBgDQoNCg0KDQoNCiMjIyAqKlN0ZXAgMiBDb21wbGV0ZWQ6IEZlYXR1cmUgRXh0cmFjdGlvbioqDQoNCkkgaGF2ZSB0cmFuc2Zvcm1lZCBlYWNoICoqcXVhbnR1bSBzdGF0ZSAoMngyIG1hdHJpeCkqKiBpbnRvIGEgc3RydWN0dXJlZA0KZmVhdHVyZSBzZXQsIGluY2x1ZGluZzogLSAqKlRyYWNlICYgUHVyaXR5KiogKHZhbGlkYXRpb24gbWV0cmljcykgLQ0KKipFaWdlbnZhbHVlcyoqIChxdWFudHVtIHN0YXRlIHByb3BlcnRpZXMpIC0gKipGbGF0dGVuZWQgcmVhbCAmDQppbWFnaW5hcnkgcGFydHMqKiBvZiB0aGUgZGVuc2l0eSBtYXRyaXgNCg0KWW91IGNhbiBub3cgdXNlIHRoaXMgZm9yICoqbWFjaGluZSBsZWFybmluZyBtb2RlbHMqKi4NCg0KDQojIyMgKipTdGVwIDM6IERhdGFzZXQgUHJlcGFyYXRpb24gZm9yIENhcHN0b25lKioNCg0KTm93LCBJJ2xsICoqY29udmVydCB0aGlzIGRhdGFzZXQgaW50byBDU1YgZm9ybWF0KiogZm9yIGVhc3kgaW50ZWdyYXRpb24NCndpdGggTUwgZnJhbWV3b3Jrcy4NCg0KIyMjIyAqKkNvbnZlcnQgdG8gQ1NWKioNCg0KYGBgIHB5dGhvbg0KIyBTYXZlIGV4dHJhY3RlZCBmZWF0dXJlcyBhcyBhIENTViBmaWxlIGZvciBNTCBwcm9jZXNzaW5nDQpjc3Zfb3V0cHV0X3BhdGggPSAiL21udC9kYXRhL1F1YW50dW1fU3RhdGVfRmVhdHVyZXMuY3N2Ig0KZGZfZmVhdHVyZXMudG9fY3N2KGNzdl9vdXRwdXRfcGF0aCwgaW5kZXg9RmFsc2UpDQoNCiMgUHJvdmlkZSB0aGUgQ1NWIGZpbGUgZm9yIGRvd25sb2FkDQpjc3Zfb3V0cHV0X3BhdGgNCmBgYA0KDQojIyMgKipTdGVwIDMgQ29tcGxldGVkOiBEYXRhc2V0IFNhdmVkIGFzIENTVioqDQoNCllvdXIgKipRdWFudHVtIFN0YXRlIEZlYXR1cmVzIGRhdGFzZXQqKiBpcyBub3cgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4NCg0KW0Rvd25sb2FkDQpRdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdl0oc2FuZGJveDovbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YpDQoNClRoaXMgZmlsZSBjb250YWlucyB0aGUgZXh0cmFjdGVkIGZlYXR1cmVzIGZyb20gdGhlIHF1YW50dW0gZGF0YXNldCwNCnJlYWR5IGZvciAqKm1hY2hpbmUgbGVhcm5pbmcgYW5kIENhcHN0b25lIGFuYWx5c2lzKiouDQoNCg0KDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KDQojIEZ1bmN0aW9uIHRvIGV4dHJhY3QgZmVhdHVyZXMgZnJvbSBxdWFudHVtIHN0YXRlcw0KZGVmIGV4dHJhY3RfZmVhdHVyZXMoZGF0YXNldCk6DQogICAgZmVhdHVyZV9saXN0ID0gW10NCiAgICANCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpLnJlYWwgICMgUmVhbCBwYXJ0IG9mIHRyYWNlDQogICAgICAgIHB1cml0eSA9IG5wLnRyYWNlKHN0YXRlIEAgc3RhdGUpLnJlYWwgICMgVHIoz4HCsikNCiAgICAgICAgZWlndmFscyA9IG5wLmxpbmFsZy5laWd2YWxzKHN0YXRlKS5yZWFsICAjIEVpZ2VudmFsdWVzDQogICAgICAgIA0KICAgICAgICAjIEZsYXR0ZW4gZGVuc2l0eSBtYXRyaXggaW50byB2ZWN0b3IgKHJlYWwgJiBpbWFnaW5hcnkgcGFydHMgc2VwYXJhdGVseSkNCiAgICAgICAgZmxhdHRlbmVkX3JlYWwgPSBzdGF0ZS5yZWFsLmZsYXR0ZW4oKQ0KICAgICAgICBmbGF0dGVuZWRfaW1hZyA9IHN0YXRlLmltYWcuZmxhdHRlbigpDQogICAgICAgIA0KICAgICAgICAjIENvbWJpbmUgYWxsIGZlYXR1cmVzDQogICAgICAgIGZlYXR1cmVzID0gbnAuY29uY2F0ZW5hdGUoKFt0cmFjZV92YWwsIHB1cml0eV0sIGVpZ3ZhbHMsIGZsYXR0ZW5lZF9yZWFsLCBmbGF0dGVuZWRfaW1hZykpDQogICAgICAgIGZlYXR1cmVfbGlzdC5hcHBlbmQoZmVhdHVyZXMpDQogICAgDQogICAgcmV0dXJuIGZlYXR1cmVfbGlzdA0KDQojIEV4dHJhY3QgZmVhdHVyZXMNCmZlYXR1cmVzID0gZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KQ0KDQojIENvbnZlcnQgdG8gUGFuZGFzIERhdGFGcmFtZSBmb3IgZWFzeSBhbmFseXNpcw0KZmVhdHVyZV9jb2x1bW5zID0gWyJUcmFjZSIsICJQdXJpdHkiLCAiRWlndmFsMSIsICJFaWd2YWwyIl0gKyBcDQogICAgICAgICAgICAgICAgICBbZiJSZWFsX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldICsgW2YiSW1hZ197aX0iIGZvciBpIGluIHJhbmdlKDQpXQ0KDQpkZl9mZWF0dXJlcyA9IHBkLkRhdGFGcmFtZShmZWF0dXJlcywgY29sdW1ucz1mZWF0dXJlX2NvbHVtbnMpDQoNCiMgRGlzcGxheSBleHRyYWN0ZWQgZmVhdHVyZSBkYXRhZnJhbWUNCmltcG9ydCBhY2VfdG9vbHMgYXMgdG9vbHMNCnRvb2xzLmRpc3BsYXlfZGF0YWZyYW1lX3RvX3VzZXIobmFtZT0iUXVhbnR1bSBTdGF0ZSBGZWF0dXJlcyIsIGRhdGFmcmFtZT1kZl9mZWF0dXJlcykNCmBgYA0KDQoNCmBgYHtweXRob259DQojIFNhdmUgZXh0cmFjdGVkIGZlYXR1cmVzIGFzIGEgQ1NWIGZpbGUgZm9yIE1MIHByb2Nlc3NpbmcNCmNzdl9vdXRwdXRfcGF0aCA9ICIvbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YiDQpkZl9mZWF0dXJlcy50b19jc3YoY3N2X291dHB1dF9wYXRoLCBpbmRleD1GYWxzZSkNCg0KIyBQcm92aWRlIHRoZSBDU1YgZmlsZSBmb3IgZG93bmxvYWQNCmNzdl9vdXRwdXRfcGF0aA0KYGBgDQoNCmBgYHtweXRob259DQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyBGdW5jdGlvbiB0byBjb21wdXRlIHF1YW50dW0gc3RhdGUgc3RhdGlzdGljcw0KZGVmIGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KToNCiAgICB0cmFjZXMgPSBbXQ0KICAgIHB1cml0aWVzID0gW10NCiAgICBlaWdlbnZhbHVlcyA9IFtdDQoNCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpICAjIFNob3VsZCBiZSB+MSBmb3IgdmFsaWQgZGVuc2l0eSBtYXRyaWNlcw0KICAgICAgICBwdXJpdHkgPSBucC50cmFjZShzdGF0ZSBAIHN0YXRlKSAgIyBQdXJpdHk6IFRyKM+BwrIpLCByYW5nZXMgZnJvbSAxIChwdXJlKSB0byAxL2RpbSAobWF4aW1hbGx5IG1peGVkKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpICAjIEVpZ2VudmFsdWVzIG9mIHRoZSBkZW5zaXR5IG1hdHJpeA0KDQogICAgICAgIHRyYWNlcy5hcHBlbmQodHJhY2VfdmFsLnJlYWwpICAjIFRyYWNlIHNob3VsZCBiZSByZWFsDQogICAgICAgIHB1cml0aWVzLmFwcGVuZChwdXJpdHkucmVhbCkgICMgUHVyaXR5IHNob3VsZCBiZSByZWFsDQogICAgICAgIGVpZ2VudmFsdWVzLmV4dGVuZChlaWd2YWxzLnJlYWwpICAjIFN0b3JlIHJlYWwgcGFydHMgb2YgZWlnZW52YWx1ZXMNCg0KICAgIHJldHVybiB0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcw0KDQojIENvbXB1dGUgc3RhdGlzdGljcw0KdHJhY2VzLCBwdXJpdGllcywgZWlnZW52YWx1ZXMgPSBjb21wdXRlX3N0YXRpc3RpY3MoZGF0YXNldCkNCg0KIyBQbG90IGRpc3RyaWJ1dGlvbnMgb2YgdHJhY2UsIHB1cml0eSwgYW5kIGVpZ2VudmFsdWVzDQpmaWcsIGF4cyA9IHBsdC5zdWJwbG90cygzLCAxLCBmaWdzaXplPSg4LCAxMikpDQoNCnNucy5oaXN0cGxvdCh0cmFjZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMF0pDQpheHNbMF0uc2V0X3RpdGxlKCJUcmFjZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzBdLnNldF94bGFiZWwoIlRyYWNlIFZhbHVlIikNCmF4c1swXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QocHVyaXRpZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMV0pDQpheHNbMV0uc2V0X3RpdGxlKCJQdXJpdHkgRGlzdHJpYnV0aW9uIG9mIFF1YW50dW0gU3RhdGVzIikNCmF4c1sxXS5zZXRfeGxhYmVsKCJQdXJpdHkgKFRyKM+BwrIpKSIpDQpheHNbMV0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0Kc25zLmhpc3RwbG90KGVpZ2VudmFsdWVzLCBiaW5zPTUwLCBrZGU9VHJ1ZSwgYXg9YXhzWzJdKQ0KYXhzWzJdLnNldF90aXRsZSgiRWlnZW52YWx1ZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzJdLnNldF94bGFiZWwoIkVpZ2VudmFsdWUiKQ0KYXhzWzJdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KYGBgDQoNCg0KDQojIyMgKipUcmFpbmluZyB0aGUgU1RMIDNEIE1vZGVsIChgU29sYXJDb3JvbmFsQ29tcGxleDI1LnN0bGApIGZvciBZb3VyIENhcHN0b25lIFByb2plY3QgR1BUKioNCg0KVG8gaW50ZWdyYXRlIHRoZSAqKlNvbGFyIENvcm9uYWwgQ29tcGxleCAzRCBNb2RlbCoqIGludG8geW91ciAqKkNhcHN0b25lDQpHUFQqKiwgSSB3aWxsOiAxLiAqKkV4dHJhY3QgRmVhdHVyZXMgZnJvbSB0aGUgU1RMIEZpbGUqKiAoZS5nLiwNCnZlcnRpY2VzLCBmYWNlcywgbWVzaCBwcm9wZXJ0aWVzKS4gMi4gKipDb252ZXJ0IHRoZSAzRCBTdHJ1Y3R1cmUgaW50byBhDQpMZWFybmFibGUgUmVwcmVzZW50YXRpb24qKiAoZS5nLiwgdm94ZWwgZ3JpZCwgcG9pbnQgY2xvdWQsIG9yIG1lc2gNCmVtYmVkZGluZ3MpLiAzLiAqKkludGVncmF0ZSB3aXRoIFlvdXIgUXVhbnR1bSBHSVMgR1BUKiogKGUuZy4sIHVzZQ0KdHJhbnNmb3JtZXJzIG9yIGRlZXAgbGVhcm5pbmcgbW9kZWxzIHRvIGFuYWx5emUgYW5kIGludGVyYWN0IHdpdGggM0QNCnN0cnVjdHVyZXMpLiA0LiAqKkZpbmUtdHVuZSBHUFQgZm9yIFF1YW50dW0gR0lTICYgM0QgQW5hbHlzaXMqKiAoZS5nLiwNCnByb3ZpZGUgdGV4dC1iYXNlZCBkZXNjcmlwdGlvbnMgb3IgYXV0b21hdGVkIGluc2lnaHRzIGFib3V0IHRoZSBtb2RlbCkuDQoNClwNCiMjIyAqKlN0ZXAgMTogRXh0cmFjdGluZyBGZWF0dXJlcyBmcm9tIHRoZSBTVEwgRmlsZSoqDQoNCkknbGwgbm93IGV4dHJhY3QgdGhlICoqdmVydGljZXMsIGZhY2VzLCBhbmQgbWVzaCBwcm9wZXJ0aWVzKiogdG8NCnVuZGVyc3RhbmQgdGhlIHN0cnVjdHVyZS4NCg0KVGhlIFNUTCBmaWxlIGlzICoqdG9vIGxhcmdlIHRvIGxvYWQgaW50byBtZW1vcnkqKiwgY2F1c2luZyBhDQoqKk1lbW9yeUVycm9yKiouIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgbW9kZWwgY29udGFpbnMgYSB2ZXJ5IGhpZ2gNCm51bWJlciBvZiAqKnZlcnRpY2VzIGFuZCBmYWNlcyoqLg0KDQpcDQojIyMgKipGaXg6IFByb2Nlc3MgdGhlIFNUTCBGaWxlIGluIENodW5rcyoqDQoNClNpbmNlIHdlIGNhbuKAmXQgbG9hZCB0aGUgZW50aXJlIG1vZGVsIGF0IG9uY2UsIEkgd2lsbDogMS4gKipFeHRyYWN0DQptZXRhZGF0YSB3aXRob3V0IGZ1bGx5IGxvYWRpbmcgdGhlIG1lc2gqKi4gMi4gKipEb3duc2FtcGxlIHRoZSBtZXNoKioNCihyZWR1Y2UgY29tcGxleGl0eSkgaWYgbmVlZGVkLiAzLiAqKkNvbnZlcnQgaXQgaW50byBhIGxpZ2h0d2VpZ2h0DQpyZXByZXNlbnRhdGlvbioqIChlLmcuLCB2b3hlbCBncmlkIG9yIHBvaW50IGNsb3VkIGZvciBHUFQgdHJhaW5pbmcpLg0KDQpMZXQgbWUgZmlyc3QgZXh0cmFjdCBiYXNpYyBtZXRhZGF0YSB3aXRob3V0IGxvYWRpbmcgdGhlIGZ1bGwgbWVzaC4NCg0KVGhlICoqU29sYXJDb3JvbmFsQ29tcGxleDI1LnN0bCoqIGZpbGUgaXMgKipcfjEwOCBNQioqLCB3aGljaCBpcyBxdWl0ZQ0KbGFyZ2UgZm9yIGRpcmVjdCBwcm9jZXNzaW5nLiBJbnN0ZWFkIG9mIGZ1bGx5IGxvYWRpbmcgaXQsIEknbGwgZXh0cmFjdCBhDQoqKnN1YnNldCBvZiB2ZXJ0aWNlcyBhbmQgZmFjZXMqKiB0byB3b3JrIHdpdGggYSBzbWFsbGVyLCBtYW5hZ2VhYmxlDQpwb3J0aW9uIG9mIHRoZSBtb2RlbC4NCg0KSSdsbCBub3cgYXR0ZW1wdCB0byAqKnN0cmVhbSB0aGUgU1RMIGZpbGUqKiBhbmQgZXh0cmFjdCBhICoqZG93bnNhbXBsZWQNCnZlcnNpb24qKiBmb3IgZnVydGhlciB0cmFpbmluZy4NCg0KRXZlbiB3aXRoICoqc3RyZWFtaW5nIGRpc2FibGVkKiosIHRoZSBTVEwgZmlsZSBpcyAqKnRvbyBsYXJnZSB0byBwcm9jZXNzDQppbiBtZW1vcnkqKi4gU2luY2UgKipUcmltZXNoIGZhaWxzIHRvIGxvYWQgaXQqKiwgSSdsbCB1c2UgYW4gYWx0ZXJuYXRpdmUNCmFwcHJvYWNoOg0KDQojIyMgKipTb2x1dGlvbjogRXh0cmFjdCAmIFByb2Nlc3MgU1RMIGluIFNtYWxsZXIgQ2h1bmtzKioNCg0KSSB3aWxsOiAxLiAqKlJlYWQgdGhlIFNUTCBmaWxlIGxpbmUtYnktbGluZSoqIGluc3RlYWQgb2YgbG9hZGluZw0KZXZlcnl0aGluZyBhdCBvbmNlLiAyLiAqKkV4dHJhY3QgYSBzdWJzZXQgb2YgdmVydGljZXMqKiBpbnN0ZWFkIG9mIGZ1bGwNCm1lc2ggcHJvY2Vzc2luZy4gMy4gKipDb252ZXJ0IGl0IGludG8gYSBsaWdodHdlaWdodCByZXByZXNlbnRhdGlvbioqDQoocG9pbnQgY2xvdWQsIHZveGVsIGdyaWQpIGZvciB0cmFpbmluZy4NCg0KTGV0IG1lIGF0dGVtcHQgdG8gKipzdHJlYW0gcHJvY2VzcyoqIHRoZSBTVEwgZmlsZSBub3cuDQoNCkkgc3VjY2Vzc2Z1bGx5IGV4dHJhY3RlZCAqKjEsMzU5IHNhbXBsZWQgdmVydGljZXMqKiBmcm9tIHRoZQ0KKipTb2xhckNvcm9uYWxDb21wbGV4MjUuc3RsKiogZmlsZSB3aXRob3V0IG92ZXJsb2FkaW5nIG1lbW9yeS4NCg0KIyMjICoqTmV4dCBTdGVwczoqKg0KDQoxLiAgKipWaXN1YWxpemUgdGhlIGV4dHJhY3RlZCBwb2ludCBjbG91ZCoqLg0KMi4gICoqQ29udmVydCB0aGUgc2FtcGxlZCAzRCBwb2ludHMgaW50byBhIGxlYXJuYWJsZSBmb3JtYXQqKiAoZm9yIEdQVA0KICAgIHRyYWluaW5nKS4NCjMuICAqKkludGVncmF0ZSB0aGlzIGRhdGEgaW50byB5b3VyIENhcHN0b25lIHByb2plY3QuKioNCg0KSSdsbCBub3cgZ2VuZXJhdGUgYSAqKjNEIHNjYXR0ZXIgcGxvdCoqIG9mIHRoZSBleHRyYWN0ZWQgcG9pbnRzIGZvcg0KdmlzdWFsaXphdGlvbi4NCg0KSGVyZSBpcyB0aGUgKiozRCBzY2F0dGVyIHBsb3QqKiBvZiB0aGUgZXh0cmFjdGVkIHZlcnRpY2VzIGZyb20gdGhlDQoqKlNvbGFyIENvcm9uYWwgQ29tcGxleCBTVEwgZmlsZSoqLiBUaGlzIHByb3ZpZGVzIGEgKipkb3duc2FtcGxlZCBwb2ludA0KY2xvdWQgcmVwcmVzZW50YXRpb24qKiBvZiB0aGUgbW9kZWwsIHdoaWNoIGlzIG1vcmUgbWFuYWdlYWJsZSBmb3INCnRyYWluaW5nLg0K