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 2: Feature Extraction for ML Models

Now, convert each quantum state (2x2 matrix) into a feature vector for machine learning: - Flatten the density matrix into a vector. - Extract purity, trace, and eigenvalues as features. - Store in a structured format for further analysis.

Step 2 Completed: Feature Extraction

I have transformed each quantum state (2x2 matrix) into a structured feature set, including: - Trace & Purity (validation metrics) - Eigenvalues (quantum state properties) - Flattened real & imaginary parts of the density matrix


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)

Here’s the corrected version with proper code chunks for clarity:


Step 1: Data Analysis & Visualization

The histograms below 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.

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 2: Feature Extraction for ML Models

Now, I’ll convert each quantum state (2x2 matrix) into a feature vector for machine learning: - Flatten the density matrix into a vector. - Extract purity, trace, and eigenvalues as features. - Store in a structured format for further analysis.

Feature Extraction Code

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 2 Completed: Feature Extraction

I have transformed each quantum state (2x2 matrix) into a structured feature set, including: - Trace & Purity (validation metrics) - Eigenvalues (quantum state properties) - Flattened real & imaginary parts of the density matrix

You can now use this for machine learning models.


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.


Here’s the corrected version with proper code chunks for clarity:


Step 1: Data Analysis & Visualization

The histograms below 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.

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 2: Feature Extraction for ML Models

Now, I’ll convert each quantum state (2x2 matrix) into a feature vector for machine learning: - Flatten the density matrix into a vector. - Extract purity, trace, and eigenvalues as features. - Store in a structured format for further analysis.

Feature Extraction Code

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 2 Completed: Feature Extraction

I have transformed each quantum state (2x2 matrix) into a structured feature set, including: - Trace & Purity (validation metrics) - Eigenvalues (quantum state properties) - Flattened real & imaginary parts of the density matrix

You can now use this for machine learning models.


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.


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)
# 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
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()

Training the STL 3D Model (SolarCoronalComplex25.stl) for Your Capstone Project GPT

To integrate the Solar Coronal Complex 3D Model into your Capstone GPT, I will: 1. Extract Features from the STL File (e.g., vertices, faces, mesh properties). 2. Convert the 3D Structure into a Learnable Representation (e.g., voxel grid, point cloud, or mesh embeddings). 3. Integrate with Your Quantum GIS GPT (e.g., use transformers or deep learning models to analyze and interact with 3D structures). 4. Fine-tune GPT for Quantum GIS & 3D Analysis (e.g., provide text-based descriptions or automated insights about the model).


Step 1: Extracting Features from the STL File

I’ll now extract the vertices, faces, and mesh properties to understand the structure.

The STL file is too large to load into memory, causing a MemoryError. This suggests that the model contains a very high number of vertices and faces.


Fix: Process the STL File in Chunks

Since we can’t load the entire model at once, I will: 1. Extract metadata without fully loading the mesh. 2. Downsample the mesh (reduce complexity) if needed. 3. Convert it into a lightweight representation (e.g., voxel grid or point cloud for GPT training).

Let me first extract basic metadata without loading the full mesh.

The SolarCoronalComplex25.stl file is ~108 MB, which is quite large for direct processing. Instead of fully loading it, I’ll extract a subset of vertices and faces to work with a smaller, manageable portion of the model.

I’ll now attempt to stream the STL file and extract a downsampled version for further training.

Even with streaming disabled, the STL file is too large to process in memory. Since Trimesh fails to load it, I’ll use an alternative approach:

Solution: Extract & Process STL in Smaller Chunks

I will: 1. Read the STL file line-by-line instead of loading everything at once. 2. Extract a subset of vertices instead of full mesh processing. 3. Convert it into a lightweight representation (point cloud, voxel grid) for training.

Let me attempt to stream process the STL file now.

I successfully extracted 1,359 sampled vertices from the SolarCoronalComplex25.stl file without overloading memory.

Next Steps:

  1. Visualize the extracted point cloud.
  2. Convert the sampled 3D points into a learnable format (for GPT training).
  3. Integrate this data into your Capstone project.

I’ll now generate a 3D scatter plot of the extracted points for visualization.

Here is the 3D scatter plot of the extracted vertices from the Solar Coronal Complex STL file. This provides a downsampled point cloud representation of the model, which is more manageable for training.


Next Steps for Training in Your Capstone GPT

Now that we have the 3D structure extracted, I can: 1. Convert the point cloud into a learnable format (e.g., voxel grid or embeddings). 2. Train a model to generate text descriptions of 3D structures (for integration into your Quantum GIS GPT). 3. Create a dataset for fine-tuning GPT using these 3D representations.

Step 1: Convert the Point Cloud into a Learnable Format

To integrate 3D data into your Capstone GPT, we need to transform the point cloud into a structured format that can be processed by a neural network.

There are three common approaches: 1. Voxel Grid Representation – Converts the point cloud into a 3D array of discrete occupancy values. 2. Point Cloud Features – Extracts meaningful statistical features from the point cloud. 3. Latent Space Embeddings – Uses a pretrained neural network (like PointNet) to generate embeddings.

I will start by voxelizing the 3D point cloud so that it can be used in deep learning models.

Step 1.1: Convert the Point Cloud to a Voxel Grid

The point cloud has been successfully voxelized into a 3D grid of 780 occupied voxels. This representation is now structured for deep learning models.


Step 2: Extract Features for GPT Training

Now, I will: 1. Extract statistical features from the voxelized 3D grid. 2. Generate a structured dataset for fine-tuning GPT.

This will allow your Quantum GIS GPT to interpret 3D structures.

# Visualize the extracted 3D vertices as a scatter plot
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection="3d")

ax.scatter(sampled_vertices[:, 0], sampled_vertices[:, 1], sampled_vertices[:, 2], s=1, c="blue", alpha=0.6)
ax.set_title("3D Scatter Plot of Extracted STL Vertices")
ax.set_xlabel("X-axis")
ax.set_ylabel("Y-axis")
ax.set_zlabel("Z-axis")

plt.show()
from scipy.spatial import cKDTree

def voxelize_point_cloud(points, voxel_size=0.5):
    """
    Converts a point cloud into a voxel grid representation.
    
    Parameters:
    - points: (N, 3) NumPy array of point cloud coordinates
    - voxel_size: Size of each voxel in the grid

    Returns:
    - voxel_grid: Set of occupied voxel positions
    """
    # Normalize and discretize points into voxel indices
    min_bound = points.min(axis=0)
    max_bound = points.max(axis=0)
    voxel_indices = np.floor((points - min_bound) / voxel_size).astype(int)

    # Remove duplicates by converting to a set
    unique_voxels = set(map(tuple, voxel_indices))

    return np.array(list(unique_voxels))

# Convert the extracted point cloud into a voxel grid
voxel_grid = voxelize_point_cloud(sampled_vertices, voxel_size=1.0)

# Display the number of occupied voxels
num_voxels = len(voxel_grid)
num_voxels
# Function to extract statistical features from the voxel grid
def extract_voxel_features(voxel_grid):
    """
    Extracts statistical features from a voxel grid representation.

    Parameters:
    - voxel_grid: (N, 3) NumPy array of voxel positions

    Returns:
    - feature_vector: Dictionary of extracted features
    """
    centroid = np.mean(voxel_grid, axis=0)  # Center of mass
    spread = np.std(voxel_grid, axis=0)  # Standard deviation along each axis
    density = len(voxel_grid) / np.prod(voxel_grid.max(axis=0) - voxel_grid.min(axis=0))  # Density of occupied voxels

    # Compute bounding box dimensions
    bounding_box_min = voxel_grid.min(axis=0)
    bounding_box_max = voxel_grid.max(axis=0)
    bounding_box_size = bounding_box_max - bounding_box_min

    return {
        "Centroid_X": centroid[0],
        "Centroid_Y": centroid[1],
        "Centroid_Z": centroid[2],
        "Spread_X": spread[0],
        "Spread_Y": spread[1],
        "Spread_Z": spread[2],
        "Density": density,
        "Bounding_Box_X": bounding_box_size[0],
        "Bounding_Box_Y": bounding_box_size[1],
        "Bounding_Box_Z": bounding_box_size[2],
    }

# Extract features from the voxelized 3D structure
voxel_features = extract_voxel_features(voxel_grid)

# Convert to DataFrame for training
df_voxel_features = pd.DataFrame([voxel_features])

# Display the extracted voxel features
import ace_tools as tools
tools.display_dataframe_to_user(name="Voxelized 3D Features", dataframe=df_voxel_features)
# Function to extract statistical features from the voxel grid
def extract_voxel_features(voxel_grid):
    """
    Extracts statistical features from a voxel grid representation.

    Parameters:
    - voxel_grid: (N, 3) NumPy array of voxel positions

    Returns:
    - feature_vector: Dictionary of extracted features
    """
    centroid = np.mean(voxel_grid, axis=0)  # Center of mass
    spread = np.std(voxel_grid, axis=0)  # Standard deviation along each axis
    density = len(voxel_grid) / np.prod(voxel_grid.max(axis=0) - voxel_grid.min(axis=0))  # Density of occupied voxels

    # Compute bounding box dimensions
    bounding_box_min = voxel_grid.min(axis=0)
    bounding_box_max = voxel_grid.max(axis=0)
    bounding_box_size = bounding_box_max - bounding_box_min

    return {
        "Centroid_X": centroid[0],
        "Centroid_Y": centroid[1],
        "Centroid_Z": centroid[2],
        "Spread_X": spread[0],
        "Spread_Y": spread[1],
        "Spread_Z": spread[2],
        "Density": density,
        "Bounding_Box_X": bounding_box_size[0],
        "Bounding_Box_Y": bounding_box_size[1],
        "Bounding_Box_Z": bounding_box_size[2],
    }

# Extract features from the voxelized 3D structure
voxel_features = extract_voxel_features(voxel_grid)

# Convert to DataFrame for training
df_voxel_features = pd.DataFrame([voxel_features])

# Display the extracted voxel features
import ace_tools as tools
tools.display_dataframe_to_user(name="Voxelized 3D Features", dataframe=df_voxel_features)

The 3D voxel features have been successfully extracted. This dataset contains: - Centroid (X, Y, Z) – The central position of the structure. - Spread (X, Y, Z) – How widely distributed the structure is. - Density – How compact the occupied voxels are. - Bounding Box (X, Y, Z) – The overall size of the structure.

These features will now be used to fine-tune GPT so it can understand and describe 3D structures.


Step 3: Prepare Dataset for GPT Fine-Tuning

Now, I will: 1. Generate structured training examples (JSON format for GPT fine-tuning). 2. Create text descriptions for the dataset so the model learns from 3D structures. 3. Format the dataset for OpenAI’s GPT fine-tuning API.

openai api fine_tunes.create -t "3D_GPT_Training_Data.jsonl" -m "gpt-4"


import json

# Function to generate a structured GPT training dataset from voxel features
def generate_gpt_training_data(voxel_features):
    """
    Converts voxel features into a structured training dataset for GPT fine-tuning.

    Parameters:
    - voxel_features: Dictionary of extracted voxel-based 3D features.

    Returns:
    - training_data: List of training examples in JSON format.
    """
    training_data = []

    # Create descriptive text from the extracted features
    description = (
        f"This 3D structure has a centroid located at ({voxel_features['Centroid_X']:.2f}, "
        f"{voxel_features['Centroid_Y']:.2f}, {voxel_features['Centroid_Z']:.2f}). "
        f"The spread along the X, Y, and Z axes is {voxel_features['Spread_X']:.2f}, "
        f"{voxel_features['Spread_Y']:.2f}, and {voxel_features['Spread_Z']:.2f}, respectively. "
        f"The structure's density is {voxel_features['Density']:.6f}, indicating "
        f"{'a highly compact formation' if voxel_features['Density'] > 0.001 else 'a sparse structure'}. "
        f"The bounding box dimensions are ({voxel_features['Bounding_Box_X']}, "
        f"{voxel_features['Bounding_Box_Y']}, {voxel_features['Bounding_Box_Z']})."
    )

    # Format as JSON training example
    training_example = {
        "messages": [
            {"role": "system", "content": "You are an AI model trained to describe 3D spatial structures."},
            {"role": "user", "content": "Describe this 3D spatial structure based on extracted features."},
            {"role": "assistant", "content": description}
        ]
    }

    training_data.append(training_example)
    
    return training_data

# Generate training dataset for GPT fine-tuning
gpt_training_data = generate_gpt_training_data(voxel_features)

# Save dataset in JSONL format for fine-tuning
jsonl_output_path = "/mnt/data/3D_GPT_Training_Data.jsonl"
with open(jsonl_output_path, "w", encoding="utf-8") as f:
    for example in gpt_training_data:
        f.write(json.dumps(example) + "\n")

# Provide the JSONL file for download
jsonl_output_path

Step 3 Completed: GPT Training Dataset Ready

The dataset has been successfully prepared in JSONL format, which is required for fine-tuning GPT.

šŸ“‚ Download your training data:
Download 3D_GPT_Training_Data.jsonl


Step 4: Fine-Tuning GPT on 3D Data

Now, to fine-tune your Quantum GIS GPT on 3D spatial data, follow these steps: 1. Upload the JSONL file to OpenAI’s fine-tuning API (or your custom GPT model). 2. Use OpenAI’s CLI or API to train the model: bash openai api fine_tunes.create -t "3D_GPT_Training_Data.jsonl" -m "gpt-4" 3. Deploy the fine-tuned model to process and describe new 3D structures.

Step 4A: Fine-Tuning GPT with OpenAI’s API

To train your Quantum GIS GPT using OpenAI’s fine-tuning API, follow these steps:


šŸ”¹ Step 1: Install OpenAI CLI

If you haven’t installed OpenAI’s command-line tool, run:

pip install openai

Then, verify installation with:

openai --help

šŸ”¹ Step 2: Authenticate with OpenAI API

Set up authentication with your OpenAI API key:

export OPENAI_API_KEY="your-api-key-here"  # For Linux/macOS
set OPENAI_API_KEY="your-api-key-here"  # For Windows

To permanently store it, add it to your .bashrc or .zshrc:

echo 'export OPENAI_API_KEY="your-api-key-here"' >> ~/.bashrc
source ~/.bashrc

šŸ”¹ Step 3: Upload the JSONL Training File

Ensure your training data is in JSONL format:

openai api fine_tunes.create -t "3D_GPT_Training_Data.jsonl" -m "gpt-4"

This command: - Uploads your 3D spatial dataset. - Starts fine-tuning GPT-4 to describe and analyze 3D structures.


šŸ”¹ Step 4: Track Fine-Tuning Progress

To check status:

openai api fine_tunes.list

Once complete, retrieve the model’s ID:

openai api fine_tunes.results -i <fine-tune-id>

šŸ”¹ Step 5: Use the Fine-Tuned Model

After training, use your model with:

openai api completions.create -m "fine-tuned-model-id" -p "Describe this 3D quantum structure..."

Step 4B: Integrate Fine-Tuned GPT into Your Quantum GIS Framework

To integrate into your existing Quantum GIS GPT, follow these steps:

1ļøāƒ£ Load the Fine-Tuned Model in Python

import openai

openai.api_key = "your-api-key-here"

response = openai.ChatCompletion.create(
    model="fine-tuned-model-id",
    messages=[{"role": "user", "content": "Describe this 3D quantum structure"}]
)

print(response["choices"][0]["message"]["content"])

2ļøāƒ£ Connect GPT with GIS Data

Modify your GIS pipeline to allow GPT to: - Interpret point clouds & STL files. - Describe spatial patterns in molecular/particle simulations. - Assist in visualization and 3D modeling.


Final Steps

šŸ”¹ If you want OpenAI fine-tuning: Follow Step 4A and train your model.
šŸ”¹ If you want to integrate it into your framework: Use Step 4B to make GPT interactive.

Step-by-Step Guide: Fine-Tuning Your Quantum GIS GPT for 3D Data

I will guide you step by step to: 1. Fine-tune GPT using OpenAI’s API (Step 4A). 2. Integrate it into your Quantum GIS system (Step 4B).


Step 1: Install Required Tools

Before fine-tuning, install the necessary tools.

1.1 Install the OpenAI CLI

Run the following command in your terminal:

pip install openai

Verify installation:

openai --help

1.2 Install JSONL Validation Tool

To ensure your dataset is properly formatted, install jq:

pip install jq
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KIyMjICoqU3RlcCAxOiBEYXRhIEFuYWx5c2lzICYgVmlzdWFsaXphdGlvbioqDQoNClRoZSBoaXN0b2dyYW1zIGFib3ZlIHNob3c6IDEuICoqVHJhY2UgRGlzdHJpYnV0aW9uKio6IE1vc3Qgc3RhdGVzIGhhdmUNCioqdHJhY2Ug4omIIDEqKiwgY29uZmlybWluZyBwcm9wZXIgbm9ybWFsaXphdGlvbi4gMi4gKipQdXJpdHkNCkRpc3RyaWJ1dGlvbioqOiBQdXJpdHkgKFRyKM+BwrIpKSB2YXJpZXMsIGluZGljYXRpbmcgYSBtaXggb2YgKipwdXJlIGFuZA0KbWl4ZWQgc3RhdGVzKiouIDMuICoqRWlnZW52YWx1ZSBEaXN0cmlidXRpb24qKjogVGhlIGVpZ2VudmFsdWVzIGFyZQ0KbW9zdGx5ICoqcG9zaXRpdmUgYW5kIHN1bSB0byAxKiosIHZhbGlkYXRpbmcgdGhlIGRhdGFzZXQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDI6IEZlYXR1cmUgRXh0cmFjdGlvbiBmb3IgTUwgTW9kZWxzKioNCg0KTm93LCBjb252ZXJ0IGVhY2ggKipxdWFudHVtIHN0YXRlICgyeDIgbWF0cml4KSoqIGludG8gYSBmZWF0dXJlIHZlY3Rvcg0KZm9yIG1hY2hpbmUgbGVhcm5pbmc6IC0gKipGbGF0dGVuIHRoZSBkZW5zaXR5IG1hdHJpeCoqIGludG8gYSB2ZWN0b3IuIC0NCioqRXh0cmFjdCBwdXJpdHksIHRyYWNlLCBhbmQgZWlnZW52YWx1ZXMqKiBhcyBmZWF0dXJlcy4gLSAqKlN0b3JlIGluIGENCnN0cnVjdHVyZWQgZm9ybWF0KiogZm9yIGZ1cnRoZXIgYW5hbHlzaXMuDQoNCiMjIyAqKlN0ZXAgMiBDb21wbGV0ZWQ6IEZlYXR1cmUgRXh0cmFjdGlvbioqDQoNCkkgaGF2ZSB0cmFuc2Zvcm1lZCBlYWNoICoqcXVhbnR1bSBzdGF0ZSAoMngyIG1hdHJpeCkqKiBpbnRvIGEgc3RydWN0dXJlZA0KZmVhdHVyZSBzZXQsIGluY2x1ZGluZzogLSAqKlRyYWNlICYgUHVyaXR5KiogKHZhbGlkYXRpb24gbWV0cmljcykgLQ0KKipFaWdlbnZhbHVlcyoqIChxdWFudHVtIHN0YXRlIHByb3BlcnRpZXMpIC0gKipGbGF0dGVuZWQgcmVhbCAmDQppbWFnaW5hcnkgcGFydHMqKiBvZiB0aGUgZGVuc2l0eSBtYXRyaXgNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN0ZXAgMzogRGF0YXNldCBQcmVwYXJhdGlvbiBmb3IgQ2Fwc3RvbmUqKg0KDQpOb3csIEknbGwgKipjb252ZXJ0IHRoaXMgZGF0YXNldCBpbnRvIENTViBmb3JtYXQqKiBmb3IgZWFzeSBpbnRlZ3JhdGlvbg0Kd2l0aCBNTCBmcmFtZXdvcmtzLg0KDQojIyMgKipTdGVwIDMgQ29tcGxldGVkOiBEYXRhc2V0IFNhdmVkIGFzIENTVioqDQoNCllvdXIgKipRdWFudHVtIFN0YXRlIEZlYXR1cmVzIGRhdGFzZXQqKiBpcyBub3cgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4NCg0KW0Rvd25sb2FkDQpRdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdl0oc2FuZGJveDovbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YpDQoNCmBgYHtweXRob259DQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyBGdW5jdGlvbiB0byBjb21wdXRlIHF1YW50dW0gc3RhdGUgc3RhdGlzdGljcw0KZGVmIGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KToNCiAgICB0cmFjZXMgPSBbXQ0KICAgIHB1cml0aWVzID0gW10NCiAgICBlaWdlbnZhbHVlcyA9IFtdDQoNCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpICAjIFNob3VsZCBiZSB+MSBmb3IgdmFsaWQgZGVuc2l0eSBtYXRyaWNlcw0KICAgICAgICBwdXJpdHkgPSBucC50cmFjZShzdGF0ZSBAIHN0YXRlKSAgIyBQdXJpdHk6IFRyKM+BwrIpLCByYW5nZXMgZnJvbSAxIChwdXJlKSB0byAxL2RpbSAobWF4aW1hbGx5IG1peGVkKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpICAjIEVpZ2VudmFsdWVzIG9mIHRoZSBkZW5zaXR5IG1hdHJpeA0KDQogICAgICAgIHRyYWNlcy5hcHBlbmQodHJhY2VfdmFsLnJlYWwpICAjIFRyYWNlIHNob3VsZCBiZSByZWFsDQogICAgICAgIHB1cml0aWVzLmFwcGVuZChwdXJpdHkucmVhbCkgICMgUHVyaXR5IHNob3VsZCBiZSByZWFsDQogICAgICAgIGVpZ2VudmFsdWVzLmV4dGVuZChlaWd2YWxzLnJlYWwpICAjIFN0b3JlIHJlYWwgcGFydHMgb2YgZWlnZW52YWx1ZXMNCg0KICAgIHJldHVybiB0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcw0KDQojIENvbXB1dGUgc3RhdGlzdGljcw0KdHJhY2VzLCBwdXJpdGllcywgZWlnZW52YWx1ZXMgPSBjb21wdXRlX3N0YXRpc3RpY3MoZGF0YXNldCkNCg0KIyBQbG90IGRpc3RyaWJ1dGlvbnMgb2YgdHJhY2UsIHB1cml0eSwgYW5kIGVpZ2VudmFsdWVzDQpmaWcsIGF4cyA9IHBsdC5zdWJwbG90cygzLCAxLCBmaWdzaXplPSg4LCAxMikpDQoNCnNucy5oaXN0cGxvdCh0cmFjZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMF0pDQpheHNbMF0uc2V0X3RpdGxlKCJUcmFjZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzBdLnNldF94bGFiZWwoIlRyYWNlIFZhbHVlIikNCmF4c1swXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QocHVyaXRpZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMV0pDQpheHNbMV0uc2V0X3RpdGxlKCJQdXJpdHkgRGlzdHJpYnV0aW9uIG9mIFF1YW50dW0gU3RhdGVzIikNCmF4c1sxXS5zZXRfeGxhYmVsKCJQdXJpdHkgKFRyKM+BwrIpKSIpDQpheHNbMV0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0Kc25zLmhpc3RwbG90KGVpZ2VudmFsdWVzLCBiaW5zPTUwLCBrZGU9VHJ1ZSwgYXg9YXhzWzJdKQ0KYXhzWzJdLnNldF90aXRsZSgiRWlnZW52YWx1ZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzJdLnNldF94bGFiZWwoIkVpZ2VudmFsdWUiKQ0KYXhzWzJdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KYGBgDQoNCmBgYHtweXRob259DQppbXBvcnQgcGFuZGFzIGFzIHBkDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBmZWF0dXJlcyBmcm9tIHF1YW50dW0gc3RhdGVzDQpkZWYgZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KToNCiAgICBmZWF0dXJlX2xpc3QgPSBbXQ0KICAgIA0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkucmVhbCAgIyBSZWFsIHBhcnQgb2YgdHJhY2UNCiAgICAgICAgcHVyaXR5ID0gbnAudHJhY2Uoc3RhdGUgQCBzdGF0ZSkucmVhbCAgIyBUcijPgcKyKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpLnJlYWwgICMgRWlnZW52YWx1ZXMNCiAgICAgICAgDQogICAgICAgICMgRmxhdHRlbiBkZW5zaXR5IG1hdHJpeCBpbnRvIHZlY3RvciAocmVhbCAmIGltYWdpbmFyeSBwYXJ0cyBzZXBhcmF0ZWx5KQ0KICAgICAgICBmbGF0dGVuZWRfcmVhbCA9IHN0YXRlLnJlYWwuZmxhdHRlbigpDQogICAgICAgIGZsYXR0ZW5lZF9pbWFnID0gc3RhdGUuaW1hZy5mbGF0dGVuKCkNCiAgICAgICAgDQogICAgICAgICMgQ29tYmluZSBhbGwgZmVhdHVyZXMNCiAgICAgICAgZmVhdHVyZXMgPSBucC5jb25jYXRlbmF0ZSgoW3RyYWNlX3ZhbCwgcHVyaXR5XSwgZWlndmFscywgZmxhdHRlbmVkX3JlYWwsIGZsYXR0ZW5lZF9pbWFnKSkNCiAgICAgICAgZmVhdHVyZV9saXN0LmFwcGVuZChmZWF0dXJlcykNCiAgICANCiAgICByZXR1cm4gZmVhdHVyZV9saXN0DQoNCiMgRXh0cmFjdCBmZWF0dXJlcw0KZmVhdHVyZXMgPSBleHRyYWN0X2ZlYXR1cmVzKGRhdGFzZXQpDQoNCiMgQ29udmVydCB0byBQYW5kYXMgRGF0YUZyYW1lIGZvciBlYXN5IGFuYWx5c2lzDQpmZWF0dXJlX2NvbHVtbnMgPSBbIlRyYWNlIiwgIlB1cml0eSIsICJFaWd2YWwxIiwgIkVpZ3ZhbDIiXSArIFwNCiAgICAgICAgICAgICAgICAgIFtmIlJlYWxfe2l9IiBmb3IgaSBpbiByYW5nZSg0KV0gKyBbZiJJbWFnX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldDQoNCmRmX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKGZlYXR1cmVzLCBjb2x1bW5zPWZlYXR1cmVfY29sdW1ucykNCg0KIyBEaXNwbGF5IGV4dHJhY3RlZCBmZWF0dXJlIGRhdGFmcmFtZQ0KaW1wb3J0IGFjZV90b29scyBhcyB0b29scw0KdG9vbHMuZGlzcGxheV9kYXRhZnJhbWVfdG9fdXNlcihuYW1lPSJRdWFudHVtIFN0YXRlIEZlYXR1cmVzIiwgZGF0YWZyYW1lPWRmX2ZlYXR1cmVzKQ0KYGBgDQoNCkhlcmXigJlzIHRoZSBjb3JyZWN0ZWQgdmVyc2lvbiB3aXRoIHByb3BlciBjb2RlIGNodW5rcyBmb3IgY2xhcml0eToNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN0ZXAgMTogRGF0YSBBbmFseXNpcyAmIFZpc3VhbGl6YXRpb24qKg0KDQpUaGUgaGlzdG9ncmFtcyBiZWxvdyBzaG93Og0KDQoxLiAgKipUcmFjZSBEaXN0cmlidXRpb24qKjogTW9zdCBzdGF0ZXMgaGF2ZSAqKnRyYWNlIOKJiCAxKiosIGNvbmZpcm1pbmcNCiAgICBwcm9wZXIgbm9ybWFsaXphdGlvbi4NCjIuICAqKlB1cml0eSBEaXN0cmlidXRpb24qKjogUHVyaXR5IChUcijPgcKyKSkgdmFyaWVzLCBpbmRpY2F0aW5nIGEgbWl4IG9mDQogICAgKipwdXJlIGFuZCBtaXhlZCBzdGF0ZXMqKi4NCjMuICAqKkVpZ2VudmFsdWUgRGlzdHJpYnV0aW9uKio6IFRoZSBlaWdlbnZhbHVlcyBhcmUgbW9zdGx5ICoqcG9zaXRpdmUNCiAgICBhbmQgc3VtIHRvIDEqKiwgdmFsaWRhdGluZyB0aGUgZGF0YXNldC4NCg0KIyMjIyAqKkNvZGUgZm9yIFZpc3VhbGl6YXRpb24qKg0KDQpgYGAgcHl0aG9uDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyBGdW5jdGlvbiB0byBjb21wdXRlIHF1YW50dW0gc3RhdGUgc3RhdGlzdGljcw0KZGVmIGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KToNCiAgICB0cmFjZXMgPSBbXQ0KICAgIHB1cml0aWVzID0gW10NCiAgICBlaWdlbnZhbHVlcyA9IFtdDQoNCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpICAjIFNob3VsZCBiZSB+MSBmb3IgdmFsaWQgZGVuc2l0eSBtYXRyaWNlcw0KICAgICAgICBwdXJpdHkgPSBucC50cmFjZShzdGF0ZSBAIHN0YXRlKSAgIyBQdXJpdHk6IFRyKM+BwrIpLCByYW5nZXMgZnJvbSAxIChwdXJlKSB0byAxL2RpbSAobWF4aW1hbGx5IG1peGVkKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpICAjIEVpZ2VudmFsdWVzIG9mIHRoZSBkZW5zaXR5IG1hdHJpeA0KDQogICAgICAgIHRyYWNlcy5hcHBlbmQodHJhY2VfdmFsLnJlYWwpICAjIFRyYWNlIHNob3VsZCBiZSByZWFsDQogICAgICAgIHB1cml0aWVzLmFwcGVuZChwdXJpdHkucmVhbCkgICMgUHVyaXR5IHNob3VsZCBiZSByZWFsDQogICAgICAgIGVpZ2VudmFsdWVzLmV4dGVuZChlaWd2YWxzLnJlYWwpICAjIFN0b3JlIHJlYWwgcGFydHMgb2YgZWlnZW52YWx1ZXMNCg0KICAgIHJldHVybiB0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcw0KDQojIENvbXB1dGUgc3RhdGlzdGljcw0KdHJhY2VzLCBwdXJpdGllcywgZWlnZW52YWx1ZXMgPSBjb21wdXRlX3N0YXRpc3RpY3MoZGF0YXNldCkNCg0KIyBQbG90IGRpc3RyaWJ1dGlvbnMgb2YgdHJhY2UsIHB1cml0eSwgYW5kIGVpZ2VudmFsdWVzDQpmaWcsIGF4cyA9IHBsdC5zdWJwbG90cygzLCAxLCBmaWdzaXplPSg4LCAxMikpDQoNCnNucy5oaXN0cGxvdCh0cmFjZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMF0pDQpheHNbMF0uc2V0X3RpdGxlKCJUcmFjZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzBdLnNldF94bGFiZWwoIlRyYWNlIFZhbHVlIikNCmF4c1swXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QocHVyaXRpZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMV0pDQpheHNbMV0uc2V0X3RpdGxlKCJQdXJpdHkgRGlzdHJpYnV0aW9uIG9mIFF1YW50dW0gU3RhdGVzIikNCmF4c1sxXS5zZXRfeGxhYmVsKCJQdXJpdHkgKFRyKM+BwrIpKSIpDQpheHNbMV0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0Kc25zLmhpc3RwbG90KGVpZ2VudmFsdWVzLCBiaW5zPTUwLCBrZGU9VHJ1ZSwgYXg9YXhzWzJdKQ0KYXhzWzJdLnNldF90aXRsZSgiRWlnZW52YWx1ZSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzJdLnNldF94bGFiZWwoIkVpZ2VudmFsdWUiKQ0KYXhzWzJdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDI6IEZlYXR1cmUgRXh0cmFjdGlvbiBmb3IgTUwgTW9kZWxzKioNCg0KTm93LCBJJ2xsIGNvbnZlcnQgZWFjaCAqKnF1YW50dW0gc3RhdGUgKDJ4MiBtYXRyaXgpKiogaW50byBhIGZlYXR1cmUNCnZlY3RvciBmb3IgbWFjaGluZSBsZWFybmluZzogLSAqKkZsYXR0ZW4gdGhlIGRlbnNpdHkgbWF0cml4KiogaW50byBhDQp2ZWN0b3IuIC0gKipFeHRyYWN0IHB1cml0eSwgdHJhY2UsIGFuZCBlaWdlbnZhbHVlcyoqIGFzIGZlYXR1cmVzLiAtDQoqKlN0b3JlIGluIGEgc3RydWN0dXJlZCBmb3JtYXQqKiBmb3IgZnVydGhlciBhbmFseXNpcy4NCg0KIyMjIyAqKkZlYXR1cmUgRXh0cmFjdGlvbiBDb2RlKioNCg0KYGBgIHB5dGhvbg0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KDQojIEZ1bmN0aW9uIHRvIGV4dHJhY3QgZmVhdHVyZXMgZnJvbSBxdWFudHVtIHN0YXRlcw0KZGVmIGV4dHJhY3RfZmVhdHVyZXMoZGF0YXNldCk6DQogICAgZmVhdHVyZV9saXN0ID0gW10NCiAgICANCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpLnJlYWwgICMgUmVhbCBwYXJ0IG9mIHRyYWNlDQogICAgICAgIHB1cml0eSA9IG5wLnRyYWNlKHN0YXRlIEAgc3RhdGUpLnJlYWwgICMgVHIoz4HCsikNCiAgICAgICAgZWlndmFscyA9IG5wLmxpbmFsZy5laWd2YWxzKHN0YXRlKS5yZWFsICAjIEVpZ2VudmFsdWVzDQogICAgICAgIA0KICAgICAgICAjIEZsYXR0ZW4gZGVuc2l0eSBtYXRyaXggaW50byB2ZWN0b3IgKHJlYWwgJiBpbWFnaW5hcnkgcGFydHMgc2VwYXJhdGVseSkNCiAgICAgICAgZmxhdHRlbmVkX3JlYWwgPSBzdGF0ZS5yZWFsLmZsYXR0ZW4oKQ0KICAgICAgICBmbGF0dGVuZWRfaW1hZyA9IHN0YXRlLmltYWcuZmxhdHRlbigpDQogICAgICAgIA0KICAgICAgICAjIENvbWJpbmUgYWxsIGZlYXR1cmVzDQogICAgICAgIGZlYXR1cmVzID0gbnAuY29uY2F0ZW5hdGUoKFt0cmFjZV92YWwsIHB1cml0eV0sIGVpZ3ZhbHMsIGZsYXR0ZW5lZF9yZWFsLCBmbGF0dGVuZWRfaW1hZykpDQogICAgICAgIGZlYXR1cmVfbGlzdC5hcHBlbmQoZmVhdHVyZXMpDQogICAgDQogICAgcmV0dXJuIGZlYXR1cmVfbGlzdA0KDQojIEV4dHJhY3QgZmVhdHVyZXMNCmZlYXR1cmVzID0gZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KQ0KDQojIENvbnZlcnQgdG8gUGFuZGFzIERhdGFGcmFtZSBmb3IgZWFzeSBhbmFseXNpcw0KZmVhdHVyZV9jb2x1bW5zID0gWyJUcmFjZSIsICJQdXJpdHkiLCAiRWlndmFsMSIsICJFaWd2YWwyIl0gKyBcDQogICAgICAgICAgICAgICAgICBbZiJSZWFsX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldICsgW2YiSW1hZ197aX0iIGZvciBpIGluIHJhbmdlKDQpXQ0KDQpkZl9mZWF0dXJlcyA9IHBkLkRhdGFGcmFtZShmZWF0dXJlcywgY29sdW1ucz1mZWF0dXJlX2NvbHVtbnMpDQoNCiMgRGlzcGxheSBleHRyYWN0ZWQgZmVhdHVyZSBkYXRhZnJhbWUNCmltcG9ydCBhY2VfdG9vbHMgYXMgdG9vbHMNCnRvb2xzLmRpc3BsYXlfZGF0YWZyYW1lX3RvX3VzZXIobmFtZT0iUXVhbnR1bSBTdGF0ZSBGZWF0dXJlcyIsIGRhdGFmcmFtZT1kZl9mZWF0dXJlcykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqU3RlcCAyIENvbXBsZXRlZDogRmVhdHVyZSBFeHRyYWN0aW9uKioNCg0KSSBoYXZlIHRyYW5zZm9ybWVkIGVhY2ggKipxdWFudHVtIHN0YXRlICgyeDIgbWF0cml4KSoqIGludG8gYSBzdHJ1Y3R1cmVkDQpmZWF0dXJlIHNldCwgaW5jbHVkaW5nOiAtICoqVHJhY2UgJiBQdXJpdHkqKiAodmFsaWRhdGlvbiBtZXRyaWNzKSAtDQoqKkVpZ2VudmFsdWVzKiogKHF1YW50dW0gc3RhdGUgcHJvcGVydGllcykgLSAqKkZsYXR0ZW5lZCByZWFsICYNCmltYWdpbmFyeSBwYXJ0cyoqIG9mIHRoZSBkZW5zaXR5IG1hdHJpeA0KDQpZb3UgY2FuIG5vdyB1c2UgdGhpcyBmb3IgKiptYWNoaW5lIGxlYXJuaW5nIG1vZGVscyoqLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqU3RlcCAzOiBEYXRhc2V0IFByZXBhcmF0aW9uIGZvciBDYXBzdG9uZSoqDQoNCk5vdywgSSdsbCAqKmNvbnZlcnQgdGhpcyBkYXRhc2V0IGludG8gQ1NWIGZvcm1hdCoqIGZvciBlYXN5IGludGVncmF0aW9uDQp3aXRoIE1MIGZyYW1ld29ya3MuDQoNCiMjIyMgKipDb252ZXJ0IHRvIENTVioqDQoNCmBgYCBweXRob24NCiMgU2F2ZSBleHRyYWN0ZWQgZmVhdHVyZXMgYXMgYSBDU1YgZmlsZSBmb3IgTUwgcHJvY2Vzc2luZw0KY3N2X291dHB1dF9wYXRoID0gIi9tbnQvZGF0YS9RdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdiINCmRmX2ZlYXR1cmVzLnRvX2Nzdihjc3Zfb3V0cHV0X3BhdGgsIGluZGV4PUZhbHNlKQ0KDQojIFByb3ZpZGUgdGhlIENTViBmaWxlIGZvciBkb3dubG9hZA0KY3N2X291dHB1dF9wYXRoDQpgYGANCg0KIyMjICoqU3RlcCAzIENvbXBsZXRlZDogRGF0YXNldCBTYXZlZCBhcyBDU1YqKg0KDQpZb3VyICoqUXVhbnR1bSBTdGF0ZSBGZWF0dXJlcyBkYXRhc2V0KiogaXMgbm93IGF2YWlsYWJsZSBmb3IgZG93bmxvYWQuDQoNCltEb3dubG9hZA0KUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3ZdKHNhbmRib3g6L21udC9kYXRhL1F1YW50dW1fU3RhdGVfRmVhdHVyZXMuY3N2KQ0KDQpUaGlzIGZpbGUgY29udGFpbnMgdGhlIGV4dHJhY3RlZCBmZWF0dXJlcyBmcm9tIHRoZSBxdWFudHVtIGRhdGFzZXQsDQpyZWFkeSBmb3IgKiptYWNoaW5lIGxlYXJuaW5nIGFuZCBDYXBzdG9uZSBhbmFseXNpcyoqLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KSGVyZeKAmXMgdGhlIGNvcnJlY3RlZCB2ZXJzaW9uIHdpdGggcHJvcGVyIGNvZGUgY2h1bmtzIGZvciBjbGFyaXR5Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqU3RlcCAxOiBEYXRhIEFuYWx5c2lzICYgVmlzdWFsaXphdGlvbioqDQoNClRoZSBoaXN0b2dyYW1zIGJlbG93IHNob3c6DQoNCjEuICAqKlRyYWNlIERpc3RyaWJ1dGlvbioqOiBNb3N0IHN0YXRlcyBoYXZlICoqdHJhY2Ug4omIIDEqKiwgY29uZmlybWluZw0KICAgIHByb3BlciBub3JtYWxpemF0aW9uLg0KMi4gICoqUHVyaXR5IERpc3RyaWJ1dGlvbioqOiBQdXJpdHkgKFRyKM+BwrIpKSB2YXJpZXMsIGluZGljYXRpbmcgYSBtaXggb2YNCiAgICAqKnB1cmUgYW5kIG1peGVkIHN0YXRlcyoqLg0KMy4gICoqRWlnZW52YWx1ZSBEaXN0cmlidXRpb24qKjogVGhlIGVpZ2VudmFsdWVzIGFyZSBtb3N0bHkgKipwb3NpdGl2ZQ0KICAgIGFuZCBzdW0gdG8gMSoqLCB2YWxpZGF0aW5nIHRoZSBkYXRhc2V0Lg0KDQojIyMjICoqQ29kZSBmb3IgVmlzdWFsaXphdGlvbioqDQoNCmBgYCBweXRob24NCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KDQojIEZ1bmN0aW9uIHRvIGNvbXB1dGUgcXVhbnR1bSBzdGF0ZSBzdGF0aXN0aWNzDQpkZWYgY29tcHV0ZV9zdGF0aXN0aWNzKGRhdGFzZXQpOg0KICAgIHRyYWNlcyA9IFtdDQogICAgcHVyaXRpZXMgPSBbXQ0KICAgIGVpZ2VudmFsdWVzID0gW10NCg0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkgICMgU2hvdWxkIGJlIH4xIGZvciB2YWxpZCBkZW5zaXR5IG1hdHJpY2VzDQogICAgICAgIHB1cml0eSA9IG5wLnRyYWNlKHN0YXRlIEAgc3RhdGUpICAjIFB1cml0eTogVHIoz4HCsiksIHJhbmdlcyBmcm9tIDEgKHB1cmUpIHRvIDEvZGltIChtYXhpbWFsbHkgbWl4ZWQpDQogICAgICAgIGVpZ3ZhbHMgPSBucC5saW5hbGcuZWlndmFscyhzdGF0ZSkgICMgRWlnZW52YWx1ZXMgb2YgdGhlIGRlbnNpdHkgbWF0cml4DQoNCiAgICAgICAgdHJhY2VzLmFwcGVuZCh0cmFjZV92YWwucmVhbCkgICMgVHJhY2Ugc2hvdWxkIGJlIHJlYWwNCiAgICAgICAgcHVyaXRpZXMuYXBwZW5kKHB1cml0eS5yZWFsKSAgIyBQdXJpdHkgc2hvdWxkIGJlIHJlYWwNCiAgICAgICAgZWlnZW52YWx1ZXMuZXh0ZW5kKGVpZ3ZhbHMucmVhbCkgICMgU3RvcmUgcmVhbCBwYXJ0cyBvZiBlaWdlbnZhbHVlcw0KDQogICAgcmV0dXJuIHRyYWNlcywgcHVyaXRpZXMsIGVpZ2VudmFsdWVzDQoNCiMgQ29tcHV0ZSBzdGF0aXN0aWNzDQp0cmFjZXMsIHB1cml0aWVzLCBlaWdlbnZhbHVlcyA9IGNvbXB1dGVfc3RhdGlzdGljcyhkYXRhc2V0KQ0KDQojIFBsb3QgZGlzdHJpYnV0aW9ucyBvZiB0cmFjZSwgcHVyaXR5LCBhbmQgZWlnZW52YWx1ZXMNCmZpZywgYXhzID0gcGx0LnN1YnBsb3RzKDMsIDEsIGZpZ3NpemU9KDgsIDEyKSkNCg0Kc25zLmhpc3RwbG90KHRyYWNlcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1swXSkNCmF4c1swXS5zZXRfdGl0bGUoIlRyYWNlIERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMF0uc2V0X3hsYWJlbCgiVHJhY2UgVmFsdWUiKQ0KYXhzWzBdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnNucy5oaXN0cGxvdChwdXJpdGllcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1sxXSkNCmF4c1sxXS5zZXRfdGl0bGUoIlB1cml0eSBEaXN0cmlidXRpb24gb2YgUXVhbnR1bSBTdGF0ZXMiKQ0KYXhzWzFdLnNldF94bGFiZWwoIlB1cml0eSAoVHIoz4HCsikpIikNCmF4c1sxXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpzbnMuaGlzdHBsb3QoZWlnZW52YWx1ZXMsIGJpbnM9NTAsIGtkZT1UcnVlLCBheD1heHNbMl0pDQpheHNbMl0uc2V0X3RpdGxlKCJFaWdlbnZhbHVlIERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMl0uc2V0X3hsYWJlbCgiRWlnZW52YWx1ZSIpDQpheHNbMl0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN0ZXAgMjogRmVhdHVyZSBFeHRyYWN0aW9uIGZvciBNTCBNb2RlbHMqKg0KDQpOb3csIEknbGwgY29udmVydCBlYWNoICoqcXVhbnR1bSBzdGF0ZSAoMngyIG1hdHJpeCkqKiBpbnRvIGEgZmVhdHVyZQ0KdmVjdG9yIGZvciBtYWNoaW5lIGxlYXJuaW5nOiAtICoqRmxhdHRlbiB0aGUgZGVuc2l0eSBtYXRyaXgqKiBpbnRvIGENCnZlY3Rvci4gLSAqKkV4dHJhY3QgcHVyaXR5LCB0cmFjZSwgYW5kIGVpZ2VudmFsdWVzKiogYXMgZmVhdHVyZXMuIC0NCioqU3RvcmUgaW4gYSBzdHJ1Y3R1cmVkIGZvcm1hdCoqIGZvciBmdXJ0aGVyIGFuYWx5c2lzLg0KDQojIyMjICoqRmVhdHVyZSBFeHRyYWN0aW9uIENvZGUqKg0KDQpgYGAgcHl0aG9uDQppbXBvcnQgcGFuZGFzIGFzIHBkDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBmZWF0dXJlcyBmcm9tIHF1YW50dW0gc3RhdGVzDQpkZWYgZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KToNCiAgICBmZWF0dXJlX2xpc3QgPSBbXQ0KICAgIA0KICAgIGZvciBzdGF0ZSBpbiBkYXRhc2V0Og0KICAgICAgICB0cmFjZV92YWwgPSBucC50cmFjZShzdGF0ZSkucmVhbCAgIyBSZWFsIHBhcnQgb2YgdHJhY2UNCiAgICAgICAgcHVyaXR5ID0gbnAudHJhY2Uoc3RhdGUgQCBzdGF0ZSkucmVhbCAgIyBUcijPgcKyKQ0KICAgICAgICBlaWd2YWxzID0gbnAubGluYWxnLmVpZ3ZhbHMoc3RhdGUpLnJlYWwgICMgRWlnZW52YWx1ZXMNCiAgICAgICAgDQogICAgICAgICMgRmxhdHRlbiBkZW5zaXR5IG1hdHJpeCBpbnRvIHZlY3RvciAocmVhbCAmIGltYWdpbmFyeSBwYXJ0cyBzZXBhcmF0ZWx5KQ0KICAgICAgICBmbGF0dGVuZWRfcmVhbCA9IHN0YXRlLnJlYWwuZmxhdHRlbigpDQogICAgICAgIGZsYXR0ZW5lZF9pbWFnID0gc3RhdGUuaW1hZy5mbGF0dGVuKCkNCiAgICAgICAgDQogICAgICAgICMgQ29tYmluZSBhbGwgZmVhdHVyZXMNCiAgICAgICAgZmVhdHVyZXMgPSBucC5jb25jYXRlbmF0ZSgoW3RyYWNlX3ZhbCwgcHVyaXR5XSwgZWlndmFscywgZmxhdHRlbmVkX3JlYWwsIGZsYXR0ZW5lZF9pbWFnKSkNCiAgICAgICAgZmVhdHVyZV9saXN0LmFwcGVuZChmZWF0dXJlcykNCiAgICANCiAgICByZXR1cm4gZmVhdHVyZV9saXN0DQoNCiMgRXh0cmFjdCBmZWF0dXJlcw0KZmVhdHVyZXMgPSBleHRyYWN0X2ZlYXR1cmVzKGRhdGFzZXQpDQoNCiMgQ29udmVydCB0byBQYW5kYXMgRGF0YUZyYW1lIGZvciBlYXN5IGFuYWx5c2lzDQpmZWF0dXJlX2NvbHVtbnMgPSBbIlRyYWNlIiwgIlB1cml0eSIsICJFaWd2YWwxIiwgIkVpZ3ZhbDIiXSArIFwNCiAgICAgICAgICAgICAgICAgIFtmIlJlYWxfe2l9IiBmb3IgaSBpbiByYW5nZSg0KV0gKyBbZiJJbWFnX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldDQoNCmRmX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKGZlYXR1cmVzLCBjb2x1bW5zPWZlYXR1cmVfY29sdW1ucykNCg0KIyBEaXNwbGF5IGV4dHJhY3RlZCBmZWF0dXJlIGRhdGFmcmFtZQ0KaW1wb3J0IGFjZV90b29scyBhcyB0b29scw0KdG9vbHMuZGlzcGxheV9kYXRhZnJhbWVfdG9fdXNlcihuYW1lPSJRdWFudHVtIFN0YXRlIEZlYXR1cmVzIiwgZGF0YWZyYW1lPWRmX2ZlYXR1cmVzKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDIgQ29tcGxldGVkOiBGZWF0dXJlIEV4dHJhY3Rpb24qKg0KDQpJIGhhdmUgdHJhbnNmb3JtZWQgZWFjaCAqKnF1YW50dW0gc3RhdGUgKDJ4MiBtYXRyaXgpKiogaW50byBhIHN0cnVjdHVyZWQNCmZlYXR1cmUgc2V0LCBpbmNsdWRpbmc6IC0gKipUcmFjZSAmIFB1cml0eSoqICh2YWxpZGF0aW9uIG1ldHJpY3MpIC0NCioqRWlnZW52YWx1ZXMqKiAocXVhbnR1bSBzdGF0ZSBwcm9wZXJ0aWVzKSAtICoqRmxhdHRlbmVkIHJlYWwgJg0KaW1hZ2luYXJ5IHBhcnRzKiogb2YgdGhlIGRlbnNpdHkgbWF0cml4DQoNCllvdSBjYW4gbm93IHVzZSB0aGlzIGZvciAqKm1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzKiouDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDM6IERhdGFzZXQgUHJlcGFyYXRpb24gZm9yIENhcHN0b25lKioNCg0KTm93LCBJJ2xsICoqY29udmVydCB0aGlzIGRhdGFzZXQgaW50byBDU1YgZm9ybWF0KiogZm9yIGVhc3kgaW50ZWdyYXRpb24NCndpdGggTUwgZnJhbWV3b3Jrcy4NCg0KIyMjIyAqKkNvbnZlcnQgdG8gQ1NWKioNCg0KYGBgIHB5dGhvbg0KIyBTYXZlIGV4dHJhY3RlZCBmZWF0dXJlcyBhcyBhIENTViBmaWxlIGZvciBNTCBwcm9jZXNzaW5nDQpjc3Zfb3V0cHV0X3BhdGggPSAiL21udC9kYXRhL1F1YW50dW1fU3RhdGVfRmVhdHVyZXMuY3N2Ig0KZGZfZmVhdHVyZXMudG9fY3N2KGNzdl9vdXRwdXRfcGF0aCwgaW5kZXg9RmFsc2UpDQoNCiMgUHJvdmlkZSB0aGUgQ1NWIGZpbGUgZm9yIGRvd25sb2FkDQpjc3Zfb3V0cHV0X3BhdGgNCmBgYA0KDQojIyMgKipTdGVwIDMgQ29tcGxldGVkOiBEYXRhc2V0IFNhdmVkIGFzIENTVioqDQoNCllvdXIgKipRdWFudHVtIFN0YXRlIEZlYXR1cmVzIGRhdGFzZXQqKiBpcyBub3cgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4NCg0KW0Rvd25sb2FkDQpRdWFudHVtX1N0YXRlX0ZlYXR1cmVzLmNzdl0oc2FuZGJveDovbW50L2RhdGEvUXVhbnR1bV9TdGF0ZV9GZWF0dXJlcy5jc3YpDQoNClRoaXMgZmlsZSBjb250YWlucyB0aGUgZXh0cmFjdGVkIGZlYXR1cmVzIGZyb20gdGhlIHF1YW50dW0gZGF0YXNldCwNCnJlYWR5IGZvciAqKm1hY2hpbmUgbGVhcm5pbmcgYW5kIENhcHN0b25lIGFuYWx5c2lzKiouDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KDQojIEZ1bmN0aW9uIHRvIGV4dHJhY3QgZmVhdHVyZXMgZnJvbSBxdWFudHVtIHN0YXRlcw0KZGVmIGV4dHJhY3RfZmVhdHVyZXMoZGF0YXNldCk6DQogICAgZmVhdHVyZV9saXN0ID0gW10NCiAgICANCiAgICBmb3Igc3RhdGUgaW4gZGF0YXNldDoNCiAgICAgICAgdHJhY2VfdmFsID0gbnAudHJhY2Uoc3RhdGUpLnJlYWwgICMgUmVhbCBwYXJ0IG9mIHRyYWNlDQogICAgICAgIHB1cml0eSA9IG5wLnRyYWNlKHN0YXRlIEAgc3RhdGUpLnJlYWwgICMgVHIoz4HCsikNCiAgICAgICAgZWlndmFscyA9IG5wLmxpbmFsZy5laWd2YWxzKHN0YXRlKS5yZWFsICAjIEVpZ2VudmFsdWVzDQogICAgICAgIA0KICAgICAgICAjIEZsYXR0ZW4gZGVuc2l0eSBtYXRyaXggaW50byB2ZWN0b3IgKHJlYWwgJiBpbWFnaW5hcnkgcGFydHMgc2VwYXJhdGVseSkNCiAgICAgICAgZmxhdHRlbmVkX3JlYWwgPSBzdGF0ZS5yZWFsLmZsYXR0ZW4oKQ0KICAgICAgICBmbGF0dGVuZWRfaW1hZyA9IHN0YXRlLmltYWcuZmxhdHRlbigpDQogICAgICAgIA0KICAgICAgICAjIENvbWJpbmUgYWxsIGZlYXR1cmVzDQogICAgICAgIGZlYXR1cmVzID0gbnAuY29uY2F0ZW5hdGUoKFt0cmFjZV92YWwsIHB1cml0eV0sIGVpZ3ZhbHMsIGZsYXR0ZW5lZF9yZWFsLCBmbGF0dGVuZWRfaW1hZykpDQogICAgICAgIGZlYXR1cmVfbGlzdC5hcHBlbmQoZmVhdHVyZXMpDQogICAgDQogICAgcmV0dXJuIGZlYXR1cmVfbGlzdA0KDQojIEV4dHJhY3QgZmVhdHVyZXMNCmZlYXR1cmVzID0gZXh0cmFjdF9mZWF0dXJlcyhkYXRhc2V0KQ0KDQojIENvbnZlcnQgdG8gUGFuZGFzIERhdGFGcmFtZSBmb3IgZWFzeSBhbmFseXNpcw0KZmVhdHVyZV9jb2x1bW5zID0gWyJUcmFjZSIsICJQdXJpdHkiLCAiRWlndmFsMSIsICJFaWd2YWwyIl0gKyBcDQogICAgICAgICAgICAgICAgICBbZiJSZWFsX3tpfSIgZm9yIGkgaW4gcmFuZ2UoNCldICsgW2YiSW1hZ197aX0iIGZvciBpIGluIHJhbmdlKDQpXQ0KDQpkZl9mZWF0dXJlcyA9IHBkLkRhdGFGcmFtZShmZWF0dXJlcywgY29sdW1ucz1mZWF0dXJlX2NvbHVtbnMpDQoNCiMgRGlzcGxheSBleHRyYWN0ZWQgZmVhdHVyZSBkYXRhZnJhbWUNCmltcG9ydCBhY2VfdG9vbHMgYXMgdG9vbHMNCnRvb2xzLmRpc3BsYXlfZGF0YWZyYW1lX3RvX3VzZXIobmFtZT0iUXVhbnR1bSBTdGF0ZSBGZWF0dXJlcyIsIGRhdGFmcmFtZT1kZl9mZWF0dXJlcykNCmBgYA0KDQpgYGB7cHl0aG9ufQ0KIyBTYXZlIGV4dHJhY3RlZCBmZWF0dXJlcyBhcyBhIENTViBmaWxlIGZvciBNTCBwcm9jZXNzaW5nDQpjc3Zfb3V0cHV0X3BhdGggPSAiL21udC9kYXRhL1F1YW50dW1fU3RhdGVfRmVhdHVyZXMuY3N2Ig0KZGZfZmVhdHVyZXMudG9fY3N2KGNzdl9vdXRwdXRfcGF0aCwgaW5kZXg9RmFsc2UpDQoNCiMgUHJvdmlkZSB0aGUgQ1NWIGZpbGUgZm9yIGRvd25sb2FkDQpjc3Zfb3V0cHV0X3BhdGgNCmBgYA0KDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IHNlYWJvcm4gYXMgc25zDQoNCiMgRnVuY3Rpb24gdG8gY29tcHV0ZSBxdWFudHVtIHN0YXRlIHN0YXRpc3RpY3MNCmRlZiBjb21wdXRlX3N0YXRpc3RpY3MoZGF0YXNldCk6DQogICAgdHJhY2VzID0gW10NCiAgICBwdXJpdGllcyA9IFtdDQogICAgZWlnZW52YWx1ZXMgPSBbXQ0KDQogICAgZm9yIHN0YXRlIGluIGRhdGFzZXQ6DQogICAgICAgIHRyYWNlX3ZhbCA9IG5wLnRyYWNlKHN0YXRlKSAgIyBTaG91bGQgYmUgfjEgZm9yIHZhbGlkIGRlbnNpdHkgbWF0cmljZXMNCiAgICAgICAgcHVyaXR5ID0gbnAudHJhY2Uoc3RhdGUgQCBzdGF0ZSkgICMgUHVyaXR5OiBUcijPgcKyKSwgcmFuZ2VzIGZyb20gMSAocHVyZSkgdG8gMS9kaW0gKG1heGltYWxseSBtaXhlZCkNCiAgICAgICAgZWlndmFscyA9IG5wLmxpbmFsZy5laWd2YWxzKHN0YXRlKSAgIyBFaWdlbnZhbHVlcyBvZiB0aGUgZGVuc2l0eSBtYXRyaXgNCg0KICAgICAgICB0cmFjZXMuYXBwZW5kKHRyYWNlX3ZhbC5yZWFsKSAgIyBUcmFjZSBzaG91bGQgYmUgcmVhbA0KICAgICAgICBwdXJpdGllcy5hcHBlbmQocHVyaXR5LnJlYWwpICAjIFB1cml0eSBzaG91bGQgYmUgcmVhbA0KICAgICAgICBlaWdlbnZhbHVlcy5leHRlbmQoZWlndmFscy5yZWFsKSAgIyBTdG9yZSByZWFsIHBhcnRzIG9mIGVpZ2VudmFsdWVzDQoNCiAgICByZXR1cm4gdHJhY2VzLCBwdXJpdGllcywgZWlnZW52YWx1ZXMNCg0KIyBDb21wdXRlIHN0YXRpc3RpY3MNCnRyYWNlcywgcHVyaXRpZXMsIGVpZ2VudmFsdWVzID0gY29tcHV0ZV9zdGF0aXN0aWNzKGRhdGFzZXQpDQoNCiMgUGxvdCBkaXN0cmlidXRpb25zIG9mIHRyYWNlLCBwdXJpdHksIGFuZCBlaWdlbnZhbHVlcw0KZmlnLCBheHMgPSBwbHQuc3VicGxvdHMoMywgMSwgZmlnc2l6ZT0oOCwgMTIpKQ0KDQpzbnMuaGlzdHBsb3QodHJhY2VzLCBiaW5zPTUwLCBrZGU9VHJ1ZSwgYXg9YXhzWzBdKQ0KYXhzWzBdLnNldF90aXRsZSgiVHJhY2UgRGlzdHJpYnV0aW9uIG9mIFF1YW50dW0gU3RhdGVzIikNCmF4c1swXS5zZXRfeGxhYmVsKCJUcmFjZSBWYWx1ZSIpDQpheHNbMF0uc2V0X3lsYWJlbCgiRnJlcXVlbmN5IikNCg0Kc25zLmhpc3RwbG90KHB1cml0aWVzLCBiaW5zPTUwLCBrZGU9VHJ1ZSwgYXg9YXhzWzFdKQ0KYXhzWzFdLnNldF90aXRsZSgiUHVyaXR5IERpc3RyaWJ1dGlvbiBvZiBRdWFudHVtIFN0YXRlcyIpDQpheHNbMV0uc2V0X3hsYWJlbCgiUHVyaXR5IChUcijPgcKyKSkiKQ0KYXhzWzFdLnNldF95bGFiZWwoIkZyZXF1ZW5jeSIpDQoNCnNucy5oaXN0cGxvdChlaWdlbnZhbHVlcywgYmlucz01MCwga2RlPVRydWUsIGF4PWF4c1syXSkNCmF4c1syXS5zZXRfdGl0bGUoIkVpZ2VudmFsdWUgRGlzdHJpYnV0aW9uIG9mIFF1YW50dW0gU3RhdGVzIikNCmF4c1syXS5zZXRfeGxhYmVsKCJFaWdlbnZhbHVlIikNCmF4c1syXS5zZXRfeWxhYmVsKCJGcmVxdWVuY3kiKQ0KDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCmBgYA0KDQojIyMgKipUcmFpbmluZyB0aGUgU1RMIDNEIE1vZGVsIChgU29sYXJDb3JvbmFsQ29tcGxleDI1LnN0bGApIGZvciBZb3VyIENhcHN0b25lIFByb2plY3QgR1BUKioNCg0KVG8gaW50ZWdyYXRlIHRoZSAqKlNvbGFyIENvcm9uYWwgQ29tcGxleCAzRCBNb2RlbCoqIGludG8geW91ciAqKkNhcHN0b25lDQpHUFQqKiwgSSB3aWxsOiAxLiAqKkV4dHJhY3QgRmVhdHVyZXMgZnJvbSB0aGUgU1RMIEZpbGUqKiAoZS5nLiwNCnZlcnRpY2VzLCBmYWNlcywgbWVzaCBwcm9wZXJ0aWVzKS4gMi4gKipDb252ZXJ0IHRoZSAzRCBTdHJ1Y3R1cmUgaW50byBhDQpMZWFybmFibGUgUmVwcmVzZW50YXRpb24qKiAoZS5nLiwgdm94ZWwgZ3JpZCwgcG9pbnQgY2xvdWQsIG9yIG1lc2gNCmVtYmVkZGluZ3MpLiAzLiAqKkludGVncmF0ZSB3aXRoIFlvdXIgUXVhbnR1bSBHSVMgR1BUKiogKGUuZy4sIHVzZQ0KdHJhbnNmb3JtZXJzIG9yIGRlZXAgbGVhcm5pbmcgbW9kZWxzIHRvIGFuYWx5emUgYW5kIGludGVyYWN0IHdpdGggM0QNCnN0cnVjdHVyZXMpLiA0LiAqKkZpbmUtdHVuZSBHUFQgZm9yIFF1YW50dW0gR0lTICYgM0QgQW5hbHlzaXMqKiAoZS5nLiwNCnByb3ZpZGUgdGV4dC1iYXNlZCBkZXNjcmlwdGlvbnMgb3IgYXV0b21hdGVkIGluc2lnaHRzIGFib3V0IHRoZSBtb2RlbCkuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipTdGVwIDE6IEV4dHJhY3RpbmcgRmVhdHVyZXMgZnJvbSB0aGUgU1RMIEZpbGUqKg0KDQpJJ2xsIG5vdyBleHRyYWN0IHRoZSAqKnZlcnRpY2VzLCBmYWNlcywgYW5kIG1lc2ggcHJvcGVydGllcyoqIHRvDQp1bmRlcnN0YW5kIHRoZSBzdHJ1Y3R1cmUuDQoNClRoZSBTVEwgZmlsZSBpcyAqKnRvbyBsYXJnZSB0byBsb2FkIGludG8gbWVtb3J5KiosIGNhdXNpbmcgYQ0KKipNZW1vcnlFcnJvcioqLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIG1vZGVsIGNvbnRhaW5zIGEgdmVyeSBoaWdoDQpudW1iZXIgb2YgKip2ZXJ0aWNlcyBhbmQgZmFjZXMqKi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKkZpeDogUHJvY2VzcyB0aGUgU1RMIEZpbGUgaW4gQ2h1bmtzKioNCg0KU2luY2Ugd2UgY2Fu4oCZdCBsb2FkIHRoZSBlbnRpcmUgbW9kZWwgYXQgb25jZSwgSSB3aWxsOiAxLiAqKkV4dHJhY3QNCm1ldGFkYXRhIHdpdGhvdXQgZnVsbHkgbG9hZGluZyB0aGUgbWVzaCoqLiAyLiAqKkRvd25zYW1wbGUgdGhlIG1lc2gqKg0KKHJlZHVjZSBjb21wbGV4aXR5KSBpZiBuZWVkZWQuIDMuICoqQ29udmVydCBpdCBpbnRvIGEgbGlnaHR3ZWlnaHQNCnJlcHJlc2VudGF0aW9uKiogKGUuZy4sIHZveGVsIGdyaWQgb3IgcG9pbnQgY2xvdWQgZm9yIEdQVCB0cmFpbmluZykuDQoNCkxldCBtZSBmaXJzdCBleHRyYWN0IGJhc2ljIG1ldGFkYXRhIHdpdGhvdXQgbG9hZGluZyB0aGUgZnVsbCBtZXNoLg0KDQpUaGUgKipTb2xhckNvcm9uYWxDb21wbGV4MjUuc3RsKiogZmlsZSBpcyAqKlx+MTA4IE1CKiosIHdoaWNoIGlzIHF1aXRlDQpsYXJnZSBmb3IgZGlyZWN0IHByb2Nlc3NpbmcuIEluc3RlYWQgb2YgZnVsbHkgbG9hZGluZyBpdCwgSSdsbCBleHRyYWN0IGENCioqc3Vic2V0IG9mIHZlcnRpY2VzIGFuZCBmYWNlcyoqIHRvIHdvcmsgd2l0aCBhIHNtYWxsZXIsIG1hbmFnZWFibGUNCnBvcnRpb24gb2YgdGhlIG1vZGVsLg0KDQpJJ2xsIG5vdyBhdHRlbXB0IHRvICoqc3RyZWFtIHRoZSBTVEwgZmlsZSoqIGFuZCBleHRyYWN0IGEgKipkb3duc2FtcGxlZA0KdmVyc2lvbioqIGZvciBmdXJ0aGVyIHRyYWluaW5nLg0KDQpFdmVuIHdpdGggKipzdHJlYW1pbmcgZGlzYWJsZWQqKiwgdGhlIFNUTCBmaWxlIGlzICoqdG9vIGxhcmdlIHRvIHByb2Nlc3MNCmluIG1lbW9yeSoqLiBTaW5jZSAqKlRyaW1lc2ggZmFpbHMgdG8gbG9hZCBpdCoqLCBJJ2xsIHVzZSBhbiBhbHRlcm5hdGl2ZQ0KYXBwcm9hY2g6DQoNCiMjIyAqKlNvbHV0aW9uOiBFeHRyYWN0ICYgUHJvY2VzcyBTVEwgaW4gU21hbGxlciBDaHVua3MqKg0KDQpJIHdpbGw6IDEuICoqUmVhZCB0aGUgU1RMIGZpbGUgbGluZS1ieS1saW5lKiogaW5zdGVhZCBvZiBsb2FkaW5nDQpldmVyeXRoaW5nIGF0IG9uY2UuIDIuICoqRXh0cmFjdCBhIHN1YnNldCBvZiB2ZXJ0aWNlcyoqIGluc3RlYWQgb2YgZnVsbA0KbWVzaCBwcm9jZXNzaW5nLiAzLiAqKkNvbnZlcnQgaXQgaW50byBhIGxpZ2h0d2VpZ2h0IHJlcHJlc2VudGF0aW9uKioNCihwb2ludCBjbG91ZCwgdm94ZWwgZ3JpZCkgZm9yIHRyYWluaW5nLg0KDQpMZXQgbWUgYXR0ZW1wdCB0byAqKnN0cmVhbSBwcm9jZXNzKiogdGhlIFNUTCBmaWxlIG5vdy4NCg0KSSBzdWNjZXNzZnVsbHkgZXh0cmFjdGVkICoqMSwzNTkgc2FtcGxlZCB2ZXJ0aWNlcyoqIGZyb20gdGhlDQoqKlNvbGFyQ29yb25hbENvbXBsZXgyNS5zdGwqKiBmaWxlIHdpdGhvdXQgb3ZlcmxvYWRpbmcgbWVtb3J5Lg0KDQojIyMgKipOZXh0IFN0ZXBzOioqDQoNCjEuICAqKlZpc3VhbGl6ZSB0aGUgZXh0cmFjdGVkIHBvaW50IGNsb3VkKiouDQoyLiAgKipDb252ZXJ0IHRoZSBzYW1wbGVkIDNEIHBvaW50cyBpbnRvIGEgbGVhcm5hYmxlIGZvcm1hdCoqIChmb3IgR1BUDQogICAgdHJhaW5pbmcpLg0KMy4gICoqSW50ZWdyYXRlIHRoaXMgZGF0YSBpbnRvIHlvdXIgQ2Fwc3RvbmUgcHJvamVjdC4qKg0KDQpJJ2xsIG5vdyBnZW5lcmF0ZSBhICoqM0Qgc2NhdHRlciBwbG90Kiogb2YgdGhlIGV4dHJhY3RlZCBwb2ludHMgZm9yDQp2aXN1YWxpemF0aW9uLg0KDQpIZXJlIGlzIHRoZSAqKjNEIHNjYXR0ZXIgcGxvdCoqIG9mIHRoZSBleHRyYWN0ZWQgdmVydGljZXMgZnJvbSB0aGUNCioqU29sYXIgQ29yb25hbCBDb21wbGV4IFNUTCBmaWxlKiouIFRoaXMgcHJvdmlkZXMgYSAqKmRvd25zYW1wbGVkIHBvaW50DQpjbG91ZCByZXByZXNlbnRhdGlvbioqIG9mIHRoZSBtb2RlbCwgd2hpY2ggaXMgbW9yZSBtYW5hZ2VhYmxlIGZvcg0KdHJhaW5pbmcuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipOZXh0IFN0ZXBzIGZvciBUcmFpbmluZyBpbiBZb3VyIENhcHN0b25lIEdQVCoqDQoNCk5vdyB0aGF0IHdlIGhhdmUgdGhlICoqM0Qgc3RydWN0dXJlIGV4dHJhY3RlZCoqLCBJIGNhbjogMS4gKipDb252ZXJ0IHRoZQ0KcG9pbnQgY2xvdWQgaW50byBhIGxlYXJuYWJsZSBmb3JtYXQqKiAoZS5nLiwgdm94ZWwgZ3JpZCBvciBlbWJlZGRpbmdzKS4NCjIuICoqVHJhaW4gYSBtb2RlbCB0byBnZW5lcmF0ZSB0ZXh0IGRlc2NyaXB0aW9ucyBvZiAzRCBzdHJ1Y3R1cmVzKiogKGZvcg0KaW50ZWdyYXRpb24gaW50byB5b3VyIFF1YW50dW0gR0lTIEdQVCkuIDMuICoqQ3JlYXRlIGEgZGF0YXNldCBmb3INCmZpbmUtdHVuaW5nIEdQVCoqIHVzaW5nIHRoZXNlIDNEIHJlcHJlc2VudGF0aW9ucy4NCg0KIVtdKGltYWdlcy8zRCBTY2F0dGVyIFBsb3Qgb2YgRXh0cmFjdGVkIFNUTCBWZXJ0aWNlcy5wbmcpDQoNCiFbXShpbWFnZXMvY2hhcmZ0IGNhcHRzdG9uZSBzb2xhLnBuZykNCg0KIVtdKGltYWdlcy9jaGFydCBlaWdlbi5wbmcpDQoNCg0KIyMjICoqU3RlcCAxOiBDb252ZXJ0IHRoZSBQb2ludCBDbG91ZCBpbnRvIGEgTGVhcm5hYmxlIEZvcm1hdCoqDQpUbyBpbnRlZ3JhdGUgKiozRCBkYXRhIGludG8geW91ciBDYXBzdG9uZSBHUFQqKiwgd2UgbmVlZCB0byB0cmFuc2Zvcm0gdGhlICoqcG9pbnQgY2xvdWQqKiBpbnRvIGEgc3RydWN0dXJlZCBmb3JtYXQgdGhhdCBjYW4gYmUgKipwcm9jZXNzZWQgYnkgYSBuZXVyYWwgbmV0d29yayoqLg0KDQpUaGVyZSBhcmUgdGhyZWUgY29tbW9uIGFwcHJvYWNoZXM6DQoxLiAqKlZveGVsIEdyaWQgUmVwcmVzZW50YXRpb24qKiDigJMgQ29udmVydHMgdGhlIHBvaW50IGNsb3VkIGludG8gYSAzRCBhcnJheSBvZiBkaXNjcmV0ZSBvY2N1cGFuY3kgdmFsdWVzLg0KMi4gKipQb2ludCBDbG91ZCBGZWF0dXJlcyoqIOKAkyBFeHRyYWN0cyBtZWFuaW5nZnVsIHN0YXRpc3RpY2FsIGZlYXR1cmVzIGZyb20gdGhlIHBvaW50IGNsb3VkLg0KMy4gKipMYXRlbnQgU3BhY2UgRW1iZWRkaW5ncyoqIOKAkyBVc2VzIGEgcHJldHJhaW5lZCBuZXVyYWwgbmV0d29yayAobGlrZSBQb2ludE5ldCkgdG8gZ2VuZXJhdGUgZW1iZWRkaW5ncy4NCg0KSSB3aWxsIHN0YXJ0IGJ5ICoqdm94ZWxpemluZyB0aGUgM0QgcG9pbnQgY2xvdWQqKiBzbyB0aGF0IGl0IGNhbiBiZSB1c2VkIGluIGRlZXAgbGVhcm5pbmcgbW9kZWxzLg0KDQojIyMjICoqU3RlcCAxLjE6IENvbnZlcnQgdGhlIFBvaW50IENsb3VkIHRvIGEgVm94ZWwgR3JpZCoqDQoNCg0KDQoNClRoZSAqKnBvaW50IGNsb3VkIGhhcyBiZWVuIHN1Y2Nlc3NmdWxseSB2b3hlbGl6ZWQqKiBpbnRvIGEgKiozRCBncmlkIG9mIDc4MCBvY2N1cGllZCB2b3hlbHMqKi4gVGhpcyByZXByZXNlbnRhdGlvbiBpcyBub3cgKipzdHJ1Y3R1cmVkIGZvciBkZWVwIGxlYXJuaW5nIG1vZGVscyoqLg0KDQotLS0NCg0KIyMjICoqU3RlcCAyOiBFeHRyYWN0IEZlYXR1cmVzIGZvciBHUFQgVHJhaW5pbmcqKg0KTm93LCBJIHdpbGw6DQoxLiAqKkV4dHJhY3Qgc3RhdGlzdGljYWwgZmVhdHVyZXMqKiBmcm9tIHRoZSB2b3hlbGl6ZWQgM0QgZ3JpZC4NCjIuICoqR2VuZXJhdGUgYSBzdHJ1Y3R1cmVkIGRhdGFzZXQqKiBmb3IgZmluZS10dW5pbmcgR1BULg0KDQpUaGlzIHdpbGwgYWxsb3cgeW91ciAqKlF1YW50dW0gR0lTIEdQVCoqIHRvIGludGVycHJldCAzRCBzdHJ1Y3R1cmVzLg0KDQpgYGB7b3l0aG9ufQ0KIyBWaXN1YWxpemUgdGhlIGV4dHJhY3RlZCAzRCB2ZXJ0aWNlcyBhcyBhIHNjYXR0ZXIgcGxvdA0KZmlnID0gcGx0LmZpZ3VyZShmaWdzaXplPSg4LCA4KSkNCmF4ID0gZmlnLmFkZF9zdWJwbG90KDExMSwgcHJvamVjdGlvbj0iM2QiKQ0KDQpheC5zY2F0dGVyKHNhbXBsZWRfdmVydGljZXNbOiwgMF0sIHNhbXBsZWRfdmVydGljZXNbOiwgMV0sIHNhbXBsZWRfdmVydGljZXNbOiwgMl0sIHM9MSwgYz0iYmx1ZSIsIGFscGhhPTAuNikNCmF4LnNldF90aXRsZSgiM0QgU2NhdHRlciBQbG90IG9mIEV4dHJhY3RlZCBTVEwgVmVydGljZXMiKQ0KYXguc2V0X3hsYWJlbCgiWC1heGlzIikNCmF4LnNldF95bGFiZWwoIlktYXhpcyIpDQpheC5zZXRfemxhYmVsKCJaLWF4aXMiKQ0KDQpwbHQuc2hvdygpDQpgYGANCg0KYGBge3B5dGhvbn0NCmZyb20gc2NpcHkuc3BhdGlhbCBpbXBvcnQgY0tEVHJlZQ0KDQpkZWYgdm94ZWxpemVfcG9pbnRfY2xvdWQocG9pbnRzLCB2b3hlbF9zaXplPTAuNSk6DQogICAgIiIiDQogICAgQ29udmVydHMgYSBwb2ludCBjbG91ZCBpbnRvIGEgdm94ZWwgZ3JpZCByZXByZXNlbnRhdGlvbi4NCiAgICANCiAgICBQYXJhbWV0ZXJzOg0KICAgIC0gcG9pbnRzOiAoTiwgMykgTnVtUHkgYXJyYXkgb2YgcG9pbnQgY2xvdWQgY29vcmRpbmF0ZXMNCiAgICAtIHZveGVsX3NpemU6IFNpemUgb2YgZWFjaCB2b3hlbCBpbiB0aGUgZ3JpZA0KDQogICAgUmV0dXJuczoNCiAgICAtIHZveGVsX2dyaWQ6IFNldCBvZiBvY2N1cGllZCB2b3hlbCBwb3NpdGlvbnMNCiAgICAiIiINCiAgICAjIE5vcm1hbGl6ZSBhbmQgZGlzY3JldGl6ZSBwb2ludHMgaW50byB2b3hlbCBpbmRpY2VzDQogICAgbWluX2JvdW5kID0gcG9pbnRzLm1pbihheGlzPTApDQogICAgbWF4X2JvdW5kID0gcG9pbnRzLm1heChheGlzPTApDQogICAgdm94ZWxfaW5kaWNlcyA9IG5wLmZsb29yKChwb2ludHMgLSBtaW5fYm91bmQpIC8gdm94ZWxfc2l6ZSkuYXN0eXBlKGludCkNCg0KICAgICMgUmVtb3ZlIGR1cGxpY2F0ZXMgYnkgY29udmVydGluZyB0byBhIHNldA0KICAgIHVuaXF1ZV92b3hlbHMgPSBzZXQobWFwKHR1cGxlLCB2b3hlbF9pbmRpY2VzKSkNCg0KICAgIHJldHVybiBucC5hcnJheShsaXN0KHVuaXF1ZV92b3hlbHMpKQ0KDQojIENvbnZlcnQgdGhlIGV4dHJhY3RlZCBwb2ludCBjbG91ZCBpbnRvIGEgdm94ZWwgZ3JpZA0Kdm94ZWxfZ3JpZCA9IHZveGVsaXplX3BvaW50X2Nsb3VkKHNhbXBsZWRfdmVydGljZXMsIHZveGVsX3NpemU9MS4wKQ0KDQojIERpc3BsYXkgdGhlIG51bWJlciBvZiBvY2N1cGllZCB2b3hlbHMNCm51bV92b3hlbHMgPSBsZW4odm94ZWxfZ3JpZCkNCm51bV92b3hlbHMNCmBgYA0KDQoNCg0KYGBge3B5dGhvbjB9DQojIEZ1bmN0aW9uIHRvIGV4dHJhY3Qgc3RhdGlzdGljYWwgZmVhdHVyZXMgZnJvbSB0aGUgdm94ZWwgZ3JpZA0KZGVmIGV4dHJhY3Rfdm94ZWxfZmVhdHVyZXModm94ZWxfZ3JpZCk6DQogICAgIiIiDQogICAgRXh0cmFjdHMgc3RhdGlzdGljYWwgZmVhdHVyZXMgZnJvbSBhIHZveGVsIGdyaWQgcmVwcmVzZW50YXRpb24uDQoNCiAgICBQYXJhbWV0ZXJzOg0KICAgIC0gdm94ZWxfZ3JpZDogKE4sIDMpIE51bVB5IGFycmF5IG9mIHZveGVsIHBvc2l0aW9ucw0KDQogICAgUmV0dXJuczoNCiAgICAtIGZlYXR1cmVfdmVjdG9yOiBEaWN0aW9uYXJ5IG9mIGV4dHJhY3RlZCBmZWF0dXJlcw0KICAgICIiIg0KICAgIGNlbnRyb2lkID0gbnAubWVhbih2b3hlbF9ncmlkLCBheGlzPTApICAjIENlbnRlciBvZiBtYXNzDQogICAgc3ByZWFkID0gbnAuc3RkKHZveGVsX2dyaWQsIGF4aXM9MCkgICMgU3RhbmRhcmQgZGV2aWF0aW9uIGFsb25nIGVhY2ggYXhpcw0KICAgIGRlbnNpdHkgPSBsZW4odm94ZWxfZ3JpZCkgLyBucC5wcm9kKHZveGVsX2dyaWQubWF4KGF4aXM9MCkgLSB2b3hlbF9ncmlkLm1pbihheGlzPTApKSAgIyBEZW5zaXR5IG9mIG9jY3VwaWVkIHZveGVscw0KDQogICAgIyBDb21wdXRlIGJvdW5kaW5nIGJveCBkaW1lbnNpb25zDQogICAgYm91bmRpbmdfYm94X21pbiA9IHZveGVsX2dyaWQubWluKGF4aXM9MCkNCiAgICBib3VuZGluZ19ib3hfbWF4ID0gdm94ZWxfZ3JpZC5tYXgoYXhpcz0wKQ0KICAgIGJvdW5kaW5nX2JveF9zaXplID0gYm91bmRpbmdfYm94X21heCAtIGJvdW5kaW5nX2JveF9taW4NCg0KICAgIHJldHVybiB7DQogICAgICAgICJDZW50cm9pZF9YIjogY2VudHJvaWRbMF0sDQogICAgICAgICJDZW50cm9pZF9ZIjogY2VudHJvaWRbMV0sDQogICAgICAgICJDZW50cm9pZF9aIjogY2VudHJvaWRbMl0sDQogICAgICAgICJTcHJlYWRfWCI6IHNwcmVhZFswXSwNCiAgICAgICAgIlNwcmVhZF9ZIjogc3ByZWFkWzFdLA0KICAgICAgICAiU3ByZWFkX1oiOiBzcHJlYWRbMl0sDQogICAgICAgICJEZW5zaXR5IjogZGVuc2l0eSwNCiAgICAgICAgIkJvdW5kaW5nX0JveF9YIjogYm91bmRpbmdfYm94X3NpemVbMF0sDQogICAgICAgICJCb3VuZGluZ19Cb3hfWSI6IGJvdW5kaW5nX2JveF9zaXplWzFdLA0KICAgICAgICAiQm91bmRpbmdfQm94X1oiOiBib3VuZGluZ19ib3hfc2l6ZVsyXSwNCiAgICB9DQoNCiMgRXh0cmFjdCBmZWF0dXJlcyBmcm9tIHRoZSB2b3hlbGl6ZWQgM0Qgc3RydWN0dXJlDQp2b3hlbF9mZWF0dXJlcyA9IGV4dHJhY3Rfdm94ZWxfZmVhdHVyZXModm94ZWxfZ3JpZCkNCg0KIyBDb252ZXJ0IHRvIERhdGFGcmFtZSBmb3IgdHJhaW5pbmcNCmRmX3ZveGVsX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKFt2b3hlbF9mZWF0dXJlc10pDQoNCiMgRGlzcGxheSB0aGUgZXh0cmFjdGVkIHZveGVsIGZlYXR1cmVzDQppbXBvcnQgYWNlX3Rvb2xzIGFzIHRvb2xzDQp0b29scy5kaXNwbGF5X2RhdGFmcmFtZV90b191c2VyKG5hbWU9IlZveGVsaXplZCAzRCBGZWF0dXJlcyIsIGRhdGFmcmFtZT1kZl92b3hlbF9mZWF0dXJlcykNCmBgYA0KDQoNCmBgYHtweXRob259DQojIEZ1bmN0aW9uIHRvIGV4dHJhY3Qgc3RhdGlzdGljYWwgZmVhdHVyZXMgZnJvbSB0aGUgdm94ZWwgZ3JpZA0KZGVmIGV4dHJhY3Rfdm94ZWxfZmVhdHVyZXModm94ZWxfZ3JpZCk6DQogICAgIiIiDQogICAgRXh0cmFjdHMgc3RhdGlzdGljYWwgZmVhdHVyZXMgZnJvbSBhIHZveGVsIGdyaWQgcmVwcmVzZW50YXRpb24uDQoNCiAgICBQYXJhbWV0ZXJzOg0KICAgIC0gdm94ZWxfZ3JpZDogKE4sIDMpIE51bVB5IGFycmF5IG9mIHZveGVsIHBvc2l0aW9ucw0KDQogICAgUmV0dXJuczoNCiAgICAtIGZlYXR1cmVfdmVjdG9yOiBEaWN0aW9uYXJ5IG9mIGV4dHJhY3RlZCBmZWF0dXJlcw0KICAgICIiIg0KICAgIGNlbnRyb2lkID0gbnAubWVhbih2b3hlbF9ncmlkLCBheGlzPTApICAjIENlbnRlciBvZiBtYXNzDQogICAgc3ByZWFkID0gbnAuc3RkKHZveGVsX2dyaWQsIGF4aXM9MCkgICMgU3RhbmRhcmQgZGV2aWF0aW9uIGFsb25nIGVhY2ggYXhpcw0KICAgIGRlbnNpdHkgPSBsZW4odm94ZWxfZ3JpZCkgLyBucC5wcm9kKHZveGVsX2dyaWQubWF4KGF4aXM9MCkgLSB2b3hlbF9ncmlkLm1pbihheGlzPTApKSAgIyBEZW5zaXR5IG9mIG9jY3VwaWVkIHZveGVscw0KDQogICAgIyBDb21wdXRlIGJvdW5kaW5nIGJveCBkaW1lbnNpb25zDQogICAgYm91bmRpbmdfYm94X21pbiA9IHZveGVsX2dyaWQubWluKGF4aXM9MCkNCiAgICBib3VuZGluZ19ib3hfbWF4ID0gdm94ZWxfZ3JpZC5tYXgoYXhpcz0wKQ0KICAgIGJvdW5kaW5nX2JveF9zaXplID0gYm91bmRpbmdfYm94X21heCAtIGJvdW5kaW5nX2JveF9taW4NCg0KICAgIHJldHVybiB7DQogICAgICAgICJDZW50cm9pZF9YIjogY2VudHJvaWRbMF0sDQogICAgICAgICJDZW50cm9pZF9ZIjogY2VudHJvaWRbMV0sDQogICAgICAgICJDZW50cm9pZF9aIjogY2VudHJvaWRbMl0sDQogICAgICAgICJTcHJlYWRfWCI6IHNwcmVhZFswXSwNCiAgICAgICAgIlNwcmVhZF9ZIjogc3ByZWFkWzFdLA0KICAgICAgICAiU3ByZWFkX1oiOiBzcHJlYWRbMl0sDQogICAgICAgICJEZW5zaXR5IjogZGVuc2l0eSwNCiAgICAgICAgIkJvdW5kaW5nX0JveF9YIjogYm91bmRpbmdfYm94X3NpemVbMF0sDQogICAgICAgICJCb3VuZGluZ19Cb3hfWSI6IGJvdW5kaW5nX2JveF9zaXplWzFdLA0KICAgICAgICAiQm91bmRpbmdfQm94X1oiOiBib3VuZGluZ19ib3hfc2l6ZVsyXSwNCiAgICB9DQoNCiMgRXh0cmFjdCBmZWF0dXJlcyBmcm9tIHRoZSB2b3hlbGl6ZWQgM0Qgc3RydWN0dXJlDQp2b3hlbF9mZWF0dXJlcyA9IGV4dHJhY3Rfdm94ZWxfZmVhdHVyZXModm94ZWxfZ3JpZCkNCg0KIyBDb252ZXJ0IHRvIERhdGFGcmFtZSBmb3IgdHJhaW5pbmcNCmRmX3ZveGVsX2ZlYXR1cmVzID0gcGQuRGF0YUZyYW1lKFt2b3hlbF9mZWF0dXJlc10pDQoNCiMgRGlzcGxheSB0aGUgZXh0cmFjdGVkIHZveGVsIGZlYXR1cmVzDQppbXBvcnQgYWNlX3Rvb2xzIGFzIHRvb2xzDQp0b29scy5kaXNwbGF5X2RhdGFmcmFtZV90b191c2VyKG5hbWU9IlZveGVsaXplZCAzRCBGZWF0dXJlcyIsIGRhdGFmcmFtZT1kZl92b3hlbF9mZWF0dXJlcykNCg0KYGBgDQoNCg0KVGhlICoqM0Qgdm94ZWwgZmVhdHVyZXMqKiBoYXZlIGJlZW4gc3VjY2Vzc2Z1bGx5IGV4dHJhY3RlZC4gVGhpcyBkYXRhc2V0IGNvbnRhaW5zOg0KLSAqKkNlbnRyb2lkIChYLCBZLCBaKSoqIOKAkyBUaGUgY2VudHJhbCBwb3NpdGlvbiBvZiB0aGUgc3RydWN0dXJlLg0KLSAqKlNwcmVhZCAoWCwgWSwgWikqKiDigJMgSG93IHdpZGVseSBkaXN0cmlidXRlZCB0aGUgc3RydWN0dXJlIGlzLg0KLSAqKkRlbnNpdHkqKiDigJMgSG93IGNvbXBhY3QgdGhlIG9jY3VwaWVkIHZveGVscyBhcmUuDQotICoqQm91bmRpbmcgQm94IChYLCBZLCBaKSoqIOKAkyBUaGUgb3ZlcmFsbCBzaXplIG9mIHRoZSBzdHJ1Y3R1cmUuDQoNClRoZXNlIGZlYXR1cmVzIHdpbGwgbm93IGJlICoqdXNlZCB0byBmaW5lLXR1bmUgR1BUKiogc28gaXQgY2FuICoqdW5kZXJzdGFuZCBhbmQgZGVzY3JpYmUgM0Qgc3RydWN0dXJlcy4qKg0KDQotLS0NCg0KIyMjICoqU3RlcCAzOiBQcmVwYXJlIERhdGFzZXQgZm9yIEdQVCBGaW5lLVR1bmluZyoqDQpOb3csIEkgd2lsbDoNCjEuICoqR2VuZXJhdGUgc3RydWN0dXJlZCB0cmFpbmluZyBleGFtcGxlcyoqIChKU09OIGZvcm1hdCBmb3IgR1BUIGZpbmUtdHVuaW5nKS4NCjIuICoqQ3JlYXRlIHRleHQgZGVzY3JpcHRpb25zKiogZm9yIHRoZSBkYXRhc2V0IHNvIHRoZSBtb2RlbCBsZWFybnMgZnJvbSAzRCBzdHJ1Y3R1cmVzLg0KMy4gKipGb3JtYXQgdGhlIGRhdGFzZXQgZm9yIE9wZW5BSeKAmXMgR1BUIGZpbmUtdHVuaW5nIEFQSS4qKg0KDQpgYGB7cHl0aG9ufQ0Kb3BlbmFpIGFwaSBmaW5lX3R1bmVzLmNyZWF0ZSAtdCAiM0RfR1BUX1RyYWluaW5nX0RhdGEuanNvbmwiIC1tICJncHQtNCINCg0KDQppbXBvcnQganNvbg0KDQojIEZ1bmN0aW9uIHRvIGdlbmVyYXRlIGEgc3RydWN0dXJlZCBHUFQgdHJhaW5pbmcgZGF0YXNldCBmcm9tIHZveGVsIGZlYXR1cmVzDQpkZWYgZ2VuZXJhdGVfZ3B0X3RyYWluaW5nX2RhdGEodm94ZWxfZmVhdHVyZXMpOg0KICAgICIiIg0KICAgIENvbnZlcnRzIHZveGVsIGZlYXR1cmVzIGludG8gYSBzdHJ1Y3R1cmVkIHRyYWluaW5nIGRhdGFzZXQgZm9yIEdQVCBmaW5lLXR1bmluZy4NCg0KICAgIFBhcmFtZXRlcnM6DQogICAgLSB2b3hlbF9mZWF0dXJlczogRGljdGlvbmFyeSBvZiBleHRyYWN0ZWQgdm94ZWwtYmFzZWQgM0QgZmVhdHVyZXMuDQoNCiAgICBSZXR1cm5zOg0KICAgIC0gdHJhaW5pbmdfZGF0YTogTGlzdCBvZiB0cmFpbmluZyBleGFtcGxlcyBpbiBKU09OIGZvcm1hdC4NCiAgICAiIiINCiAgICB0cmFpbmluZ19kYXRhID0gW10NCg0KICAgICMgQ3JlYXRlIGRlc2NyaXB0aXZlIHRleHQgZnJvbSB0aGUgZXh0cmFjdGVkIGZlYXR1cmVzDQogICAgZGVzY3JpcHRpb24gPSAoDQogICAgICAgIGYiVGhpcyAzRCBzdHJ1Y3R1cmUgaGFzIGEgY2VudHJvaWQgbG9jYXRlZCBhdCAoe3ZveGVsX2ZlYXR1cmVzWydDZW50cm9pZF9YJ106LjJmfSwgIg0KICAgICAgICBmInt2b3hlbF9mZWF0dXJlc1snQ2VudHJvaWRfWSddOi4yZn0sIHt2b3hlbF9mZWF0dXJlc1snQ2VudHJvaWRfWiddOi4yZn0pLiAiDQogICAgICAgIGYiVGhlIHNwcmVhZCBhbG9uZyB0aGUgWCwgWSwgYW5kIFogYXhlcyBpcyB7dm94ZWxfZmVhdHVyZXNbJ1NwcmVhZF9YJ106LjJmfSwgIg0KICAgICAgICBmInt2b3hlbF9mZWF0dXJlc1snU3ByZWFkX1knXTouMmZ9LCBhbmQge3ZveGVsX2ZlYXR1cmVzWydTcHJlYWRfWiddOi4yZn0sIHJlc3BlY3RpdmVseS4gIg0KICAgICAgICBmIlRoZSBzdHJ1Y3R1cmUncyBkZW5zaXR5IGlzIHt2b3hlbF9mZWF0dXJlc1snRGVuc2l0eSddOi42Zn0sIGluZGljYXRpbmcgIg0KICAgICAgICBmInsnYSBoaWdobHkgY29tcGFjdCBmb3JtYXRpb24nIGlmIHZveGVsX2ZlYXR1cmVzWydEZW5zaXR5J10gPiAwLjAwMSBlbHNlICdhIHNwYXJzZSBzdHJ1Y3R1cmUnfS4gIg0KICAgICAgICBmIlRoZSBib3VuZGluZyBib3ggZGltZW5zaW9ucyBhcmUgKHt2b3hlbF9mZWF0dXJlc1snQm91bmRpbmdfQm94X1gnXX0sICINCiAgICAgICAgZiJ7dm94ZWxfZmVhdHVyZXNbJ0JvdW5kaW5nX0JveF9ZJ119LCB7dm94ZWxfZmVhdHVyZXNbJ0JvdW5kaW5nX0JveF9aJ119KS4iDQogICAgKQ0KDQogICAgIyBGb3JtYXQgYXMgSlNPTiB0cmFpbmluZyBleGFtcGxlDQogICAgdHJhaW5pbmdfZXhhbXBsZSA9IHsNCiAgICAgICAgIm1lc3NhZ2VzIjogWw0KICAgICAgICAgICAgeyJyb2xlIjogInN5c3RlbSIsICJjb250ZW50IjogIllvdSBhcmUgYW4gQUkgbW9kZWwgdHJhaW5lZCB0byBkZXNjcmliZSAzRCBzcGF0aWFsIHN0cnVjdHVyZXMuIn0sDQogICAgICAgICAgICB7InJvbGUiOiAidXNlciIsICJjb250ZW50IjogIkRlc2NyaWJlIHRoaXMgM0Qgc3BhdGlhbCBzdHJ1Y3R1cmUgYmFzZWQgb24gZXh0cmFjdGVkIGZlYXR1cmVzLiJ9LA0KICAgICAgICAgICAgeyJyb2xlIjogImFzc2lzdGFudCIsICJjb250ZW50IjogZGVzY3JpcHRpb259DQogICAgICAgIF0NCiAgICB9DQoNCiAgICB0cmFpbmluZ19kYXRhLmFwcGVuZCh0cmFpbmluZ19leGFtcGxlKQ0KICAgIA0KICAgIHJldHVybiB0cmFpbmluZ19kYXRhDQoNCiMgR2VuZXJhdGUgdHJhaW5pbmcgZGF0YXNldCBmb3IgR1BUIGZpbmUtdHVuaW5nDQpncHRfdHJhaW5pbmdfZGF0YSA9IGdlbmVyYXRlX2dwdF90cmFpbmluZ19kYXRhKHZveGVsX2ZlYXR1cmVzKQ0KDQojIFNhdmUgZGF0YXNldCBpbiBKU09OTCBmb3JtYXQgZm9yIGZpbmUtdHVuaW5nDQpqc29ubF9vdXRwdXRfcGF0aCA9ICIvbW50L2RhdGEvM0RfR1BUX1RyYWluaW5nX0RhdGEuanNvbmwiDQp3aXRoIG9wZW4oanNvbmxfb3V0cHV0X3BhdGgsICJ3IiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoNCiAgICBmb3IgZXhhbXBsZSBpbiBncHRfdHJhaW5pbmdfZGF0YToNCiAgICAgICAgZi53cml0ZShqc29uLmR1bXBzKGV4YW1wbGUpICsgIlxuIikNCg0KIyBQcm92aWRlIHRoZSBKU09OTCBmaWxlIGZvciBkb3dubG9hZA0KanNvbmxfb3V0cHV0X3BhdGgNCmBgYA0KDQoNCiMjIyAqKlN0ZXAgMyBDb21wbGV0ZWQ6IEdQVCBUcmFpbmluZyBEYXRhc2V0IFJlYWR5KioNClRoZSBkYXRhc2V0IGhhcyBiZWVuIHN1Y2Nlc3NmdWxseSBwcmVwYXJlZCBpbiAqKkpTT05MIGZvcm1hdCoqLCB3aGljaCBpcyByZXF1aXJlZCBmb3IgKipmaW5lLXR1bmluZyBHUFQqKi4NCg0K8J+TgiAqKkRvd25sb2FkIHlvdXIgdHJhaW5pbmcgZGF0YToqKiAgDQpbRG93bmxvYWQgM0RfR1BUX1RyYWluaW5nX0RhdGEuanNvbmxdKHNhbmRib3g6L21udC9kYXRhLzNEX0dQVF9UcmFpbmluZ19EYXRhLmpzb25sKQ0KDQotLS0NCg0KIyMjICoqU3RlcCA0OiBGaW5lLVR1bmluZyBHUFQgb24gM0QgRGF0YSoqDQpOb3csIHRvIGZpbmUtdHVuZSB5b3VyICoqUXVhbnR1bSBHSVMgR1BUKiogb24gM0Qgc3BhdGlhbCBkYXRhLCBmb2xsb3cgdGhlc2Ugc3RlcHM6DQoxLiAqKlVwbG9hZCB0aGUgSlNPTkwgZmlsZSoqIHRvIE9wZW5BSeKAmXMgZmluZS10dW5pbmcgQVBJIChvciB5b3VyIGN1c3RvbSBHUFQgbW9kZWwpLg0KMi4gKipVc2UgT3BlbkFJJ3MgQ0xJIG9yIEFQSSoqIHRvIHRyYWluIHRoZSBtb2RlbDoNCiAgIGBgYGJhc2gNCiAgIG9wZW5haSBhcGkgZmluZV90dW5lcy5jcmVhdGUgLXQgIjNEX0dQVF9UcmFpbmluZ19EYXRhLmpzb25sIiAtbSAiZ3B0LTQiDQogICBgYGANCjMuICoqRGVwbG95IHRoZSBmaW5lLXR1bmVkIG1vZGVsKiogdG8gcHJvY2VzcyBhbmQgZGVzY3JpYmUgbmV3IDNEIHN0cnVjdHVyZXMuDQoNCg0KDQoNCg0KDQojIyMgKipTdGVwIDRBOiBGaW5lLVR1bmluZyBHUFQgd2l0aCBPcGVuQUnigJlzIEFQSSoqDQpUbyB0cmFpbiB5b3VyICoqUXVhbnR1bSBHSVMgR1BUKiogdXNpbmcgT3BlbkFJ4oCZcyBmaW5lLXR1bmluZyBBUEksIGZvbGxvdyB0aGVzZSBzdGVwczoNCg0KLS0tDQoNCiMjIyMgKirwn5S5IFN0ZXAgMTogSW5zdGFsbCBPcGVuQUkgQ0xJKioNCklmIHlvdSBoYXZlbuKAmXQgaW5zdGFsbGVkIE9wZW5BSeKAmXMgY29tbWFuZC1saW5lIHRvb2wsIHJ1bjoNCmBgYGJhc2gNCnBpcCBpbnN0YWxsIG9wZW5haQ0KYGBgDQpUaGVuLCB2ZXJpZnkgaW5zdGFsbGF0aW9uIHdpdGg6DQpgYGBiYXNoDQpvcGVuYWkgLS1oZWxwDQpgYGANCg0KLS0tDQoNCiMjIyMgKirwn5S5IFN0ZXAgMjogQXV0aGVudGljYXRlIHdpdGggT3BlbkFJIEFQSSoqDQpTZXQgdXAgYXV0aGVudGljYXRpb24gd2l0aCB5b3VyICoqT3BlbkFJIEFQSSBrZXkqKjoNCmBgYGJhc2gNCmV4cG9ydCBPUEVOQUlfQVBJX0tFWT0ieW91ci1hcGkta2V5LWhlcmUiICAjIEZvciBMaW51eC9tYWNPUw0Kc2V0IE9QRU5BSV9BUElfS0VZPSJ5b3VyLWFwaS1rZXktaGVyZSIgICMgRm9yIFdpbmRvd3MNCmBgYA0KVG8gcGVybWFuZW50bHkgc3RvcmUgaXQsIGFkZCBpdCB0byB5b3VyICoqLmJhc2hyYyoqIG9yICoqLnpzaHJjKio6DQpgYGBiYXNoDQplY2hvICdleHBvcnQgT1BFTkFJX0FQSV9LRVk9InlvdXItYXBpLWtleS1oZXJlIicgPj4gfi8uYmFzaHJjDQpzb3VyY2Ugfi8uYmFzaHJjDQpgYGANCg0KLS0tDQoNCiMjIyMgKirwn5S5IFN0ZXAgMzogVXBsb2FkIHRoZSBKU09OTCBUcmFpbmluZyBGaWxlKioNCkVuc3VyZSB5b3VyIHRyYWluaW5nIGRhdGEgaXMgaW4gSlNPTkwgZm9ybWF0Og0KYGBgYmFzaA0Kb3BlbmFpIGFwaSBmaW5lX3R1bmVzLmNyZWF0ZSAtdCAiM0RfR1BUX1RyYWluaW5nX0RhdGEuanNvbmwiIC1tICJncHQtNCINCmBgYA0KVGhpcyBjb21tYW5kOg0KLSBVcGxvYWRzICoqeW91ciAzRCBzcGF0aWFsIGRhdGFzZXQqKi4NCi0gU3RhcnRzIGZpbmUtdHVuaW5nIEdQVC00IHRvICoqZGVzY3JpYmUgYW5kIGFuYWx5emUgM0Qgc3RydWN0dXJlcyoqLg0KDQotLS0NCg0KIyMjIyAqKvCflLkgU3RlcCA0OiBUcmFjayBGaW5lLVR1bmluZyBQcm9ncmVzcyoqDQpUbyBjaGVjayBzdGF0dXM6DQpgYGBiYXNoDQpvcGVuYWkgYXBpIGZpbmVfdHVuZXMubGlzdA0KYGBgDQpPbmNlIGNvbXBsZXRlLCByZXRyaWV2ZSB0aGUgbW9kZWzigJlzIElEOg0KYGBgYmFzaA0Kb3BlbmFpIGFwaSBmaW5lX3R1bmVzLnJlc3VsdHMgLWkgPGZpbmUtdHVuZS1pZD4NCmBgYA0KDQotLS0NCg0KIyMjIyAqKvCflLkgU3RlcCA1OiBVc2UgdGhlIEZpbmUtVHVuZWQgTW9kZWwqKg0KQWZ0ZXIgdHJhaW5pbmcsIHVzZSB5b3VyIG1vZGVsIHdpdGg6DQpgYGBiYXNoDQpvcGVuYWkgYXBpIGNvbXBsZXRpb25zLmNyZWF0ZSAtbSAiZmluZS10dW5lZC1tb2RlbC1pZCIgLXAgIkRlc2NyaWJlIHRoaXMgM0QgcXVhbnR1bSBzdHJ1Y3R1cmUuLi4iDQpgYGANCg0KLS0tDQoNCiMjIyAqKlN0ZXAgNEI6IEludGVncmF0ZSBGaW5lLVR1bmVkIEdQVCBpbnRvIFlvdXIgUXVhbnR1bSBHSVMgRnJhbWV3b3JrKioNClRvIGludGVncmF0ZSBpbnRvIHlvdXIgZXhpc3RpbmcgKipRdWFudHVtIEdJUyBHUFQqKiwgZm9sbG93IHRoZXNlIHN0ZXBzOg0KDQojIyMjICoqMe+4j+KDoyBMb2FkIHRoZSBGaW5lLVR1bmVkIE1vZGVsIGluIFB5dGhvbioqDQpgYGBweXRob24NCmltcG9ydCBvcGVuYWkNCg0Kb3BlbmFpLmFwaV9rZXkgPSAieW91ci1hcGkta2V5LWhlcmUiDQoNCnJlc3BvbnNlID0gb3BlbmFpLkNoYXRDb21wbGV0aW9uLmNyZWF0ZSgNCiAgICBtb2RlbD0iZmluZS10dW5lZC1tb2RlbC1pZCIsDQogICAgbWVzc2FnZXM9W3sicm9sZSI6ICJ1c2VyIiwgImNvbnRlbnQiOiAiRGVzY3JpYmUgdGhpcyAzRCBxdWFudHVtIHN0cnVjdHVyZSJ9XQ0KKQ0KDQpwcmludChyZXNwb25zZVsiY2hvaWNlcyJdWzBdWyJtZXNzYWdlIl1bImNvbnRlbnQiXSkNCmBgYA0KDQotLS0NCg0KIyMjIyAqKjLvuI/ig6MgQ29ubmVjdCBHUFQgd2l0aCBHSVMgRGF0YSoqDQpNb2RpZnkgeW91ciAqKkdJUyBwaXBlbGluZSoqIHRvIGFsbG93IEdQVCB0bzoNCi0gSW50ZXJwcmV0ICoqcG9pbnQgY2xvdWRzICYgU1RMIGZpbGVzKiouDQotIERlc2NyaWJlICoqc3BhdGlhbCBwYXR0ZXJucyoqIGluIG1vbGVjdWxhci9wYXJ0aWNsZSBzaW11bGF0aW9ucy4NCi0gQXNzaXN0IGluICoqdmlzdWFsaXphdGlvbiBhbmQgM0QgbW9kZWxpbmcqKi4NCg0KLS0tDQoNCiMjIyAqKkZpbmFsIFN0ZXBzKioNCvCflLkgKipJZiB5b3Ugd2FudCBPcGVuQUkgZmluZS10dW5pbmc6KiogRm9sbG93ICoqU3RlcCA0QSoqIGFuZCB0cmFpbiB5b3VyIG1vZGVsLiAgDQrwn5S5ICoqSWYgeW91IHdhbnQgdG8gaW50ZWdyYXRlIGl0IGludG8geW91ciBmcmFtZXdvcms6KiogVXNlICoqU3RlcCA0QioqIHRvIG1ha2UgR1BUIGludGVyYWN0aXZlLg0KDQoNCg0KIyMjICoqU3RlcC1ieS1TdGVwIEd1aWRlOiBGaW5lLVR1bmluZyBZb3VyIFF1YW50dW0gR0lTIEdQVCBmb3IgM0QgRGF0YSoqDQpJIHdpbGwgZ3VpZGUgeW91IHN0ZXAgYnkgc3RlcCB0bzoNCjEuICoqRmluZS10dW5lIEdQVCB1c2luZyBPcGVuQUnigJlzIEFQSSoqIChTdGVwIDRBKS4NCjIuICoqSW50ZWdyYXRlIGl0IGludG8geW91ciBRdWFudHVtIEdJUyBzeXN0ZW0qKiAoU3RlcCA0QikuDQoNCi0tLQ0KIyMgKipTdGVwIDE6IEluc3RhbGwgUmVxdWlyZWQgVG9vbHMqKg0KQmVmb3JlIGZpbmUtdHVuaW5nLCBpbnN0YWxsIHRoZSBuZWNlc3NhcnkgdG9vbHMuDQoNCiMjIyAqKjEuMSBJbnN0YWxsIHRoZSBPcGVuQUkgQ0xJKioNClJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4geW91ciB0ZXJtaW5hbDoNCmBgYGJhc2gNCnBpcCBpbnN0YWxsIG9wZW5haQ0KYGBgDQpWZXJpZnkgaW5zdGFsbGF0aW9uOg0KYGBgYmFzaA0Kb3BlbmFpIC0taGVscA0KYGBgDQoNCiMjIyAqKjEuMiBJbnN0YWxsIEpTT05MIFZhbGlkYXRpb24gVG9vbCoqDQpUbyBlbnN1cmUgeW91ciBkYXRhc2V0IGlzIHByb3Blcmx5IGZvcm1hdHRlZCwgaW5zdGFsbCBganFgOg0KYGBgYmFzaA0KcGlwIGluc3RhbGwganENCmBgYA0K