QTW 7333 Module 12: Convolutional Neural Networks Study Guide

1. What Is a Convolution?

Mathematical Representation:

For continuous functions:

y(t) = ∫ f(τ) g(t - τ) dτ

For discrete functions (used in images and signals):

y[n] = Σ f[k] * g[n - k]

In neural networks, this becomes:

output = Σ (input * filter)

This is a dot product between the filter and the part of the input it overlaps.


2. Convolution in Neural Networks

Example:

If you convolve a 5x5 image with a 3x3 filter: - Output size becomes (5-3)+1 = 3 (in each dimension), unless you apply padding.


3. Padding

Without Padding: Output size = (W - F + 1)

With Padding (P): Output size = ((W - F + 2P) / S) + 1

Where: - W = input width/height - F = filter size - P = padding - S = stride


4. Stride


5. Filter Examples

Each of these is used to extract different features (edges, textures, etc.).


6. Neural Network Architecture Recap

From Module 11 (transcript):

Each neuron is essentially a regression function:
z = Wx + b → σ(z)

Where: - W = weights - x = inputs - b = bias - σ = activation function (e.g., sigmoid, ReLU)

In CNNs, this regression is replaced with convolution operations.


7. Matrix Dimensions & Output Size (Important)

If: - Input = 28x28 - Filter = 3x3 - Padding = 1 - Stride = 1

Then: - Output = ((28 - 3 + 2×1) / 1) + 1 = 28
→ Output has same dimension as input.


8. Summary of Key Concepts


9. Key Takeaways


10. Practice Questions

  1. What does a convolution operation do in image processing?
  2. Why do we use padding in convolutional neural networks?
  3. How does the stride affect the output size?
  4. What happens when you apply an edge detection filter to an image?
  5. Derive the output size of a convolutional layer given the input size, filter size, padding, and stride.
  6. What is the role of the bias vector in a convolutional layer?

Suggested Reading

From the Elements of Statistical Learning (Hastie, Tibshirani, Friedman):

URL for textbook:
https://web.stanford.edu/~hastie/ElemStatLearn/


🧠 Module 12: Convolutional Neural Networks — Study Guide


1. Core Concept: What is a Convolution?

Definition:
A convolution is the overlap between two functions, calculated by sliding a function (called a filter or kernel) over input data (usually an image), multiplying overlapping values, and summing the results.

This helps extract important features like edges, textures, or color gradients from images.


2. Mathematical Representation

Continuous form:

y(t) = ∫ f(τ)·g(t−τ) dτ

Discrete (for digital images and neural networks):

y[i, j] = Σ Σ input[m+i, n+j] * filter[m, n]

Where: - input is your image matrix - filter is the kernel (e.g., edge detection) - y is the output feature map


3. CNNs in Action: Python Code Example (Beginner-Friendly)

import numpy as np
from scipy.signal import convolve2d
import matplotlib.pyplot as plt

# Example input image (5x5)
image = np.array([
    [1, 2, 3, 0, 1],
    [0, 1, 2, 3, 0],
    [1, 0, 1, 2, 1],
    [2, 1, 0, 1, 2],
    [1, 2, 1, 0, 1]
])

# Example edge detection filter (3x3)
filter_kernel = np.array([
    [0, -1, 0],
    [-1, 4, -1],
    [0, -1, 0]
])

# Apply convolution
output = convolve2d(image, filter_kernel, mode='valid')

print("Output after convolution:")
print(output)

Output: A smaller matrix that highlights where edges or features were detected.


4. CNN Layers Breakdown


5. Visual Example from Class Slides


6. Key Takeaways


7. Relevant Questions to Test Yourself

  1. What does a convolution do in a neural network?
  2. Why is padding used in convolution layers?
  3. What effect does increasing the stride have?
  4. How does an edge detection filter work?
  5. What is the output size if you apply a 3x3 filter to a 5x5 image with no padding and stride 1?

8. Layman’s Explanation – Pizza Cutter Analogy

Imagine you’re cutting a large pizza with a stencil that is 3x3 inches in size. You place your stencil on the pizza, look at just that square, and rate it from 1 to 10 based on how much pepperoni it has.

Then, you move the stencil over a little (by 1 inch = stride), and do it again. You’re scanning the entire pizza, one small patch at a time.

Your stencil is the filter. The pizza is the image. Your rating is the output feature. If you want the edges to also be analyzed (not just the center), you place napkins (zeros) around the edge — that’s padding.

This is what convolution does: it helps see the important parts of an image (like cheese, crust, or pepperoni) and condense it into useful info — which is what a neural network needs to make a decision.


9. Textbook References for Deeper Reading

From “The Elements of Statistical Learning” by Hastie et al.
- Chapter 11: Neural Networks
URL: https://web.stanford.edu/~hastie/ElemStatLearn/

From “The Statistical Sleuth” by Ramsey & Schafer
- Not image-focused but useful for regression background


Here’s your complete study guide for Convolutional Neural Networks (CNNs), integrating all class visuals, transcripts, textbook knowledge, mathematical concepts, Python code, key takeaways, and an easy-to-understand real-world analogy.


QTW 7333 – Module 12
Study Guide: Convolutional Neural Networks (CNNs)


🧠 Overview of CNNs

A Convolutional Neural Network is a type of neural network architecture optimized for processing images. It mimics the human visual cortex where only nearby neurons communicate—meaning filters (kernels) focus on local patterns (edges, corners, etc.) instead of the entire image at once.


🧩 Layers in a CNN

  1. Convolutional Layer
  2. Activation Function (usually ReLU)
  3. Pooling Layer
  4. Flatten Layer
  5. Dense (Fully Connected) Layer
  6. Output Layer (often Softmax for classification)

🧮 Mathematical Concepts

1. Convolution Layer:

Applies a filter (kernel) across the image.

Equation (2D discrete convolution):
Y(i, j) = Σ_m Σ_n X(i+m, j+n) · K(m, n)

Where:
- X = input image
- K = kernel
- Y = output feature map

2. Pooling Layer:

Reduces spatial size by selecting max or average values.

  • Max Pooling (2×2 with stride 2):
    From:
    [[1, 1],
    [5, 6]] → max = 6

3. Flatten Layer:

Converts multi-dimensional tensor into 1D vector for dense layers.

Example: [[0, -1, 0],
[-1, 4, -1],
[0, -1, 0]]
→ [0, -1, 0, -1, 4, -1, 0, -1, 0]


💻 Python Code Example (Simple CNN with Keras)

import tensorflow as tf
from tensorflow.keras import layers, models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

# Flatten and Dense
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

model.summary()

🎨 Layman’s Analogy

Imagine you’re looking at a Where’s Waldo book: - Your eyes scan small sections of the image (like a filter). - You’re checking patterns like hats, glasses, red-white shirts. - When something stands out, your brain stores that info. - Once enough features are collected, you decide where Waldo is.

Similarly: - Convolutional layers scan for patterns. - Pooling summarizes those patterns (shrinks detail). - Flattening stacks everything into a list. - Dense layers make a final decision (e.g., this is Waldo!).


📘 Textbook Support

From Elements of Statistical Learning: - Chapter 11 – Neural Networks
https://web.stanford.edu/~hastie/ElemStatLearn/

Also explore CS231n by Stanford (Visual CNN walkthrough):
http://cs231n.stanford.edu/slides/2019/cs231n_2019_lecture5.pdf


🧠 Key Takeaways


❓ Practice Questions

  1. What is the purpose of a convolutional layer?
  2. What does max pooling do? Why is it useful?
  3. Why do we flatten data before sending it to a dense layer?
  4. In what way is a CNN biologically inspired?
  5. How does increasing the number of filters affect the output?

📘 QTW 7333 – Module 12: Transfer Learning Study Guide


🔍 1. Concept Overview: What Is Transfer Learning?

Definition:
Transfer Learning is a technique where we take a pre-trained model (often trained on large datasets using powerful hardware) and reuse its early layers while retraining only the final layers for a new, often smaller, task.

Instead of training everything from scratch, we “transfer” the learning from one task to another.


🧠 2. Why Does It Work?

From your transcript: - Early layers (top of the network) learn general features (edges, colors, shapes). - Later layers (dense layers) learn specific task-related features. - General features are reusable, even across different tasks (e.g., dogs vs. cats, or cars vs. planes). - Saves time, data, and compute resources.


📊 3. Mathematical Formulation

Let:

We freeze θ_general and retrain only θ_specific.

New output:

y’ = f(x; θ_general, θ’_specific)
(where θ_general is pre-trained and frozen, θ’_specific is newly trained)


💻 4. Python Code Example (Using Keras)

from tensorflow.keras.applications import VGG16
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras import Input

# Load pre-trained VGG16 without top layers
base_model = VGG16(weights='imagenet', include_top=False, input_tensor=Input(shape=(224, 224, 3)))

# Freeze base model layers
for layer in base_model.layers:
    layer.trainable = False

# Add new dense layers on top
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(128, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)  # for 10-class problem

# Final model
model = Model(inputs=base_model.input, outputs=predictions)
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

This uses ImageNet-trained features and trains only the classifier on your custom data.


📚 5. Reference from Textbooks

The Elements of Statistical Learning (Hastie, Tibshirani, Friedman)
- Chapter 11 (Neural Networks): explains how lower-level representations generalize across problems.
Link: https://web.stanford.edu/~hastie/ElemStatLearn/


✅ 6. Key Takeaways


❓ 7. Relevant Questions

  1. Why is transfer learning useful in deep learning?
  2. Which parts of the model are typically frozen and which are retrained?
  3. Can transfer learning work across domains (e.g., vision to language)?
  4. What role does the vanishing gradient play in motivating transfer learning?

🧸 8. Layman Explanation (Real-World Analogy)

Imagine you’re learning to play music.

You don’t need to relearn music theory, rhythm, notes, or timing. You just need to learn how to hold and strum the guitar.

That’s transfer learning: - Your music theory = pre-trained layers (frozen). - Learning guitar specifics = final layers (retrained). - You’re not starting from scratch — you’re adapting.

Just like your brain doesn’t relearn how sound works each time, a CNN doesn’t need to relearn edge detection or textures when classifying new images.


📘 QTW 7333 – Module 12: CNN Part I – Set Up Your Data Study Guide


🔍 1. What Are We Doing?

We’re: - Importing CIFAR-10 image dataset - Visualizing the dataset - Normalizing the pixel values - Preparing everything to feed into a Convolutional Neural Network (CNN)


🧪 2. Why Normalize and Visualize?


📚 3. Textbook Support

From The Elements of Statistical Learning by Hastie, Tibshirani & Friedman:
- Chapter 11 (Neural Networks)
- Chapter 7 (Model Assessment and Selection)
Link: https://web.stanford.edu/~hastie/ElemStatLearn/

Also helpful: TensorFlow official docs:
https://www.tensorflow.org/tutorials/images/cnn


📊 4. Mathematical Explanation


💻 5. Python Code (Beginner-Friendly Setup)

# Step 1: Import libraries
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt

# Step 2: Load CIFAR-10 dataset
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()

# Step 3: Normalize pixel values to [0, 1]
train_images, test_images = train_images / 255.0, test_images / 255.0

# Step 4: Define class names
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

# Step 5: Plot first 25 images
plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])  # Remove x-axis
    plt.yticks([])  # Remove y-axis
    plt.grid(False)
    plt.imshow(train_images[i])
    plt.xlabel(class_names[train_labels[i][0]])
plt.show()

✅ 6. Key Takeaways


❓ 7. Questions to Test Yourself

  1. Why do we divide pixel values by 255 in image datasets?
  2. What are the dimensions of each CIFAR-10 image?
  3. Why is it useful to visualize the images before training a CNN?
  4. What does train_labels[i][0] mean in the plotting loop?

🧸 8. Layman’s Analogy: Filing Photos for an Album

Imagine you’re creating a photo album: - Each photo = an image - The photo label = the name written below it (dog, truck, etc.) - But all your photos are in different formats and brightness levels.

Before pasting them in: 1. You resize all to the same dimensions (32x32 pixels). 2. You adjust brightness (normalization). 3. You label them (class names). 4. Then you lay them out in the album (plotting).

This setup makes your album (dataset) organized and easy to interpret — just like it makes the CNN’s job easier during training.


Here is your complete Study Guide for CNN Part II: Building the Model from QTW 7333, including textbook links, transcript summary, mathematical explanation, Python code, key takeaways, self-test questions, and a beginner analogy.


📘 QTW 7333 – Module 12: CNN Part II – Building the Model


🔍 1. What Are We Doing in This Module?

We’re: - Building a CNN architecture using Keras’ Sequential API. - Adding convolution, pooling, flattening, and dense layers. - Compiling the model with an optimizer and loss function. - Training the model and validating performance. - Making predictions and visualizing results.


🧠 2. Key Concepts


🧮 3. Mathematical Representation

Each Conv2D layer:

output = ReLU(W * input + b)

Where: - * = convolution operation - W = filter/kernel weights - b = bias - ReLU = max(0, x), element-wise activation

MaxPooling2D:

Reduces matrix by selecting the max value in each window (e.g., 2x2).

Dense layer:

output = Softmax(Wx + b)


💻 4. Complete Python Code

import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
import numpy as np

# Load and normalize CIFAR-10
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()
train_images, test_images = train_images / 255.0, test_images / 255.0

# Define CNN model
my_model = models.Sequential()
my_model.add(layers.Conv2D(32, (2, 2), activation='relu', input_shape=(32, 32, 3)))
my_model.add(layers.Conv2D(64, (3, 3), activation='relu'))
my_model.add(layers.MaxPooling2D((2, 2)))
my_model.add(layers.Conv2D(17, (2, 3), activation='relu'))
my_model.add(layers.Conv2D(14, (4, 4), activation='relu'))
my_model.add(layers.MaxPooling2D((2, 2)))
my_model.add(layers.Flatten())
my_model.add(layers.Dense(100, activation='relu'))
my_model.add(layers.Dense(10, activation='softmax'))

# Compile the model
my_model.compile(optimizer='adam',
                 loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
                 metrics=['accuracy'])

# Train the model
history = my_model.fit(train_images, train_labels, epochs=5, batch_size=50,
                       validation_data=(test_images, test_labels))

# Predict and visualize results
results = my_model.predict(test_images)

# Show predictions vs true labels
plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([]); plt.yticks([]); plt.grid(False)
    plt.imshow(test_images[i], cmap=plt.cm.binary)
    true_label = test_labels[i][0]
    pred_label = np.argmax(results[i])
    plt.xlabel(f"True: {true_label}, Pred: {pred_label}")
plt.show()

📊 5. Accuracy Output Example

Training output (sample):


✅ 6. Key Takeaways


❓ 7. Practice Questions

  1. What does the Flatten layer do?
  2. Why do we use MaxPooling in a CNN?
  3. What does the final Dense(10, activation=‘softmax’) layer represent?
  4. Why is the Adam optimizer commonly used?
  5. What shape do the CNN predictions have and why?

🧸 8. Layman Explanation – The “Photo Sorting Machine”

Imagine building a smart photo sorter: - The first machine part looks for edges (where colors change suddenly). - The next part identifies shapes (cars, cats, etc.). - Finally, a decision-maker reads everything and says, “This is a truck!”

Each “part” is a layer in your neural network: - Convolution layers = find patterns (like textures) - Pooling layers = zoom out to reduce clutter - Dense layers = make decisions based on what was found - Softmax = gives the final vote (percent chance it’s each object)

Just like sorting your photos by eye, CNNs do this automatically—but with math and training!


📚 Textbook Support

The Elements of Statistical Learning
- Chapter 11: Neural Networks (pg. 392+)
https://web.stanford.edu/~hastie/ElemStatLearn/

TensorFlow Tutorial (Official)
https://www.tensorflow.org/tutorials/images/cnn

—generate this as: - A Jupyter Notebook for immediate use? - A PDF Study Sheet for printing? - An interactive HTML presentation with sliders?

Perfect. I’ve compiled your materials into a structured study guide that covers the following topics:

  1. Convolutional Layer Fundamentals
  2. Transfer Learning Concepts
  3. CNN Part I: Dataset Setup
  4. CNN Part II: Model Building
  5. Transfer Learning: VGG16 + Custom Classifier

I’ll walk you through each section in an easy-to-follow format:


Convolutional Neural Networks: Beginner Study Guide

Section 1: What is a Convolution?

Concept:
A convolution is when we slide a filter (small matrix) over data (like an image) to extract meaningful features (like edges or patterns). Each overlap is multiplied element-wise and summed into a new output value.

Math:
If I is an image matrix and K is a filter:

S(i,j) = ∑∑ K(m,n) * I(i+m, j+n)

This is done for all valid positions of the filter.

Python Example:

import numpy as np
from scipy.signal import convolve2d

image = np.array([[1, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]])

kernel = np.array([[0, 1],
                   [1, 0]])

result = convolve2d(image, kernel, mode='valid')
print(result)

Key Terms: - Padding: Adds extra borders to maintain original size. - Stride: How many steps the filter moves. - Filter: Also called a kernel.

Takeaways: - Filters are learned during training. - Convolutions detect patterns like edges, color, and textures.

Relevant Questions: - Why do we use padding? - What happens when you increase the stride?

Layman Analogy:
Imagine using a small window to look at different parts of a big painting. Each time, you write down a summary of what you see (like color intensity or sharpness). You repeat this until you’ve scanned the whole picture.


Section 2: Transfer Learning Explained

Concept:
Transfer learning lets us reuse a powerful pretrained model (like VGG16) and just train the final layers on our specific task.

Why?
Advanced models take weeks and lots of compute. Transfer learning lets us use pretrained features and train only the “head” (final layers).

Diagram Summary:
Left = pretrained convolution layers
Right = new dense layers
We keep the left side “frozen” and train only the right.

Mathematics: If F is the feature extractor and W are trainable weights:

Prediction = softmax(W * F(x))

Python Example:

base_model = tf.keras.applications.VGG16(input_shape=(160,160,3),
                                         include_top=False,
                                         weights='imagenet')
base_model.trainable = False

Takeaways: - Saves compute and time. - Works with few samples. - Can be used across domains.

Relevant Questions: - What layers should you freeze? - Why is softmax used in the final layer?

Layman Analogy:
Imagine you’re learning to bake cakes. Instead of learning everything from scratch, you buy a cake mix (pretrained model) and just focus on decorating it to your style (custom layers).


Section 3: CNN, Part I: Set Up Your Data

Code Snippet:

import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt

(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()
train_images = train_images / 255.0  # normalize pixel values
test_images = test_images / 255.0

Visualization:

class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([]), plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i][0]])
plt.show()

Takeaways: - Normalizing image data (0–255 → 0–1) is crucial. - Visualizing helps verify your labels and structure.

Relevant Questions: - Why normalize pixel values? - What does CIFAR-10 contain?


Section 4: CNN, Part II: Build the Model

Architecture Overview:

model = models.Sequential()
model.add(layers.Conv2D(32, (2,2), activation='relu', input_shape=(32,32,3)))
model.add(layers.Conv2D(64, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(128, (4,3), activation='relu'))
model.add(layers.Conv2D(128, (4,4), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Flatten())
model.add(layers.Dense(100, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))  # For 10 classes

Compile and Train:

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['accuracy'])

model.fit(train_images, train_labels, epochs=5, batch_size=50, validation_data=(test_images, test_labels))

Takeaways: - Conv2D layers extract features. - MaxPooling2D reduces size and keeps important features. - Dense layers make final predictions.

Relevant Questions: - What does the flatten layer do? - Why is softmax good for multiclass classification?

Layman Analogy:
Imagine your model is like a detective. The convolutional layers are like the detective gathering clues (edges, colors), the flatten layer organizes all the clues in a single file, and the dense layers use that file to figure out: “Is this a cat or a truck?”


Section 5: Transfer Learning in Practice (VGG16 + Cats vs Dogs)

Steps Summary: 1. Load dataset with image_dataset_from_directory 2. Normalize with Rescaling 3. Load VGG16 base (exclude top) 4. Freeze base model 5. Add global average pooling, dropout, and dense output 6. Compile and train

Key Code:

base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet', input_shape=(160,160,3))
base_model.trainable = False

model = tf.keras.Sequential([
    tf.keras.layers.Rescaling(1./127.5, offset=-1),
    base_model,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(1)  # binary classification
])

model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

model.fit(train_dataset, epochs=10, validation_data=validation_dataset)

Takeaways: - VGG16 has 14 million+ parameters; we only train a few hundred. - Fast training, even on small datasets. - Accuracy improves in just a few epochs.

Relevant Questions: - What is the difference between categorical and binary crossentropy? - What does dropout do?

Layman Analogy:
Think of the VGG16 model as an expert art critic who has studied millions of paintings. You’re training a small assistant to only tell apart cats and dogs. You don’t retrain the expert, just use their opinions (features) and teach your assistant how to interpret them for a very specific task.


Here’s a distilled synthesis from everything we’ve done today—including the screenshots, class transcripts, and the concepts in your PDF materials—into three general takeaways and three thoughtful questions for Dr. Slater:


Three General Takeaways

1. Transfer Learning is a Game-Changer for Resource-Limited Training
By freezing the convolutional base of pretrained models (like VGG16) and training only the classifier head, we unlock the power of highly complex models without needing massive compute. This approach significantly accelerates training while maintaining strong performance, even with limited data.

2. CNNs Rely Heavily on Proper Data Handling and Architectural Balance
Performance is strongly influenced by seemingly simple preprocessing steps (e.g., normalization by dividing by 255, resizing images, proper label encoding). Additionally, the depth of convolution and pooling layers must be balanced with memory constraints and task complexity.

3. The Shift from Dense to Convolutional Networks Reflects a Change in How Models “See” Data
Dense networks treat input as flat vectors; CNNs treat it spatially. This is crucial for images, where structure and locality matter. CNNs learn hierarchical features—from edges in early layers to complex shapes in deeper ones—mimicking how visual cortex neurons operate.


Three Thought-Provoking Questions for Dr. Slater

1. Given the benefits of transfer learning, how do we assess when it’s more appropriate to fine-tune versus freeze layers, especially when working with scientific or niche datasets like medical scans or satellite imagery?

2. In real-world deployment, how do we mitigate the risks of CNN misclassification (e.g., frog vs. dog) in high-stakes applications such as autonomous vehicles, military surveillance, or healthcare diagnostics?

3. From a pedagogical or research standpoint, where do you see the biggest conceptual gaps in student understanding when transitioning from dense to convolutional neural networks—and how can we better bridge that leap, perhaps with visual tools or hands-on demos?


\section*{General Takeaways from CNNs and Transfer Learning}

\begin{enumerate}
    \item \textbf{Transfer Learning is a Game-Changer for Resource-Limited Training} \\
    Freezing the convolutional base of pretrained models like VGG16 and training only the dense classifier head allows us to leverage powerful models without the need for massive compute resources. This greatly reduces training time while maintaining strong performance, especially when working with smaller datasets.

    \item \textbf{CNNs Rely Heavily on Proper Data Handling and Architectural Balance} \\
    Performance is significantly influenced by preprocessing steps such as normalization (e.g., dividing pixel values by 255), image resizing, and label formatting. The architecture’s depth (number and size of convolution and pooling layers) must be tuned in relation to the complexity of the task and available memory.

    \item \textbf{The Shift from Dense to Convolutional Networks Reflects a Change in How Models "See" Data} \\
    Dense networks treat data as flat vectors; CNNs process it spatially. This is essential for image tasks where spatial locality matters. CNNs build hierarchical feature maps—from simple edges to complex objects—similar to the human visual system.
\end{enumerate}

\vspace{0.5cm}
\section*{Thought-Provoking Questions for Dr. Slater}

\begin{enumerate}
    \item \textbf{How should we decide whether to freeze or fine-tune pretrained convolutional layers when working with domain-specific datasets (e.g., medical imaging, satellite data, or environmental analysis)?}

    \item \textbf{What safeguards or model evaluation strategies would you recommend when deploying CNNs in high-stakes environments (e.g., defense, transportation, healthcare) to reduce risks from misclassifications like mistaking a frog for a dog?}

    \item \textbf{From your experience teaching deep learning, where do students typically struggle most when transitioning from dense to convolutional networks, and how could we better support that shift using visual or interactive resources?}
\end{enumerate}

Jessica McPhaul 7333 – QTW Module 12 Presession Week 12 ________________________________________ Three General Takeaways 1. Transfer Learning is a Game-Changer for Resource-Limited Training By freezing the convolutional base of pretrained models (like VGG16) and training only the classifier head, we unlock the power of highly complex models without needing massive compute. This approach significantly accelerates training while maintaining strong performance, even with limited data. 2. CNNs Rely Heavily on Proper Data Handling and Architectural Balance Performance is strongly influenced by seemingly simple preprocessing steps (e.g., normalization by dividing by 255, resizing images, proper label encoding). Additionally, the depth of convolution and pooling layers must be balanced with memory constraints and task complexity. 3. The Shift from Dense to Convolutional Networks Reflects a Change in How Models “See” Data Dense networks treat input as flat vectors; CNNs treat it spatially. This is crucial for images, where structure and locality matter. CNNs learn hierarchical features—from edges in early layers to complex shapes in deeper ones—mimicking how visual cortex neurons operate. ________________________________________ 3 Questiosn 1. Given the benefits of transfer learning, how do we assess when it’s more appropriate to fine-tune versus freeze layers, especially when working with scientific or niche datasets like medical scans or satellite imagery? 2. In real-world deployment, how do we mitigate the risks of CNN misclassification (e.g., frog vs. dog) in high-stakes applications such as autonomous vehicles, military surveillance, or healthcare diagnostics? 3. From a pedagogical or research standpoint, where do you see the biggest conceptual gaps in student understanding when transitioning from dense to convolutional neural networks—and how can we better bridge that leap, perhaps with visual tools or hands-on demos? ________________________________________

LS0tDQp0aXRsZTogIjczMzMgTW9kdWxlIDEyIENOTiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KKipRVFcgNzMzMyBNb2R1bGUgMTI6IENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmtzIFN0dWR5IEd1aWRlKioNCg0KKioxLiBXaGF0IElzIGEgQ29udm9sdXRpb24/KioNCg0KLSBBICoqY29udm9sdXRpb24qKiBpcyB0aGUgaW50ZWdyYWwgb2YgdGhlIHByb2R1Y3Qgb2YgdHdvIGZ1bmN0aW9ucyBhZnRlciBvbmUgaXMgcmV2ZXJzZWQgYW5kIHNoaWZ0ZWQuDQotIEluIGRpc2NyZXRlIHRlcm1zLCB3ZSAic2xpZGUiIGEgc21hbGwgZnVuY3Rpb24gKGNhbGxlZCBhICoqZmlsdGVyKiogb3IgKiprZXJuZWwqKikgYWNyb3NzIGFuIGlucHV0IChsaWtlIGFuIGltYWdlIG9yIHNpZ25hbCksIGFuZCBjb21wdXRlIGFuIG91dHB1dCB2YWx1ZSBiYXNlZCBvbiB0aGVpciBvdmVybGFwLg0KDQoqKk1hdGhlbWF0aWNhbCBSZXByZXNlbnRhdGlvbjoqKg0KDQpGb3IgY29udGludW91cyBmdW5jdGlvbnM6DQogIA0KICB5KHQpID0g4oirIGYoz4QpIGcodCAtIM+EKSBkz4QNCg0KRm9yIGRpc2NyZXRlIGZ1bmN0aW9ucyAodXNlZCBpbiBpbWFnZXMgYW5kIHNpZ25hbHMpOg0KDQogIHlbbl0gPSDOoyBmW2tdICogZ1tuIC0ga10NCg0KKipJbiBuZXVyYWwgbmV0d29ya3MsIHRoaXMgYmVjb21lczoqKg0KDQogIG91dHB1dCA9IM6jIChpbnB1dCAqIGZpbHRlcikNCg0KVGhpcyBpcyBhICoqZG90IHByb2R1Y3QqKiBiZXR3ZWVuIHRoZSBmaWx0ZXIgYW5kIHRoZSBwYXJ0IG9mIHRoZSBpbnB1dCBpdCBvdmVybGFwcy4NCg0KLS0tDQoNCioqMi4gQ29udm9sdXRpb24gaW4gTmV1cmFsIE5ldHdvcmtzKioNCg0KLSBXZSBzbGlkZSB0aGUgZmlsdGVyIChlLmcuLCBhIDN4MyBtYXRyaXgpIG92ZXIgdGhlIGlucHV0IChlLmcuLCBhbiBpbWFnZSBtYXRyaXgpLg0KLSBBdCBlYWNoIHBvc2l0aW9uLCB3ZSB0YWtlIGFuIGVsZW1lbnQtd2lzZSBtdWx0aXBsaWNhdGlvbiBvZiBvdmVybGFwcGluZyB2YWx1ZXMsIHRoZW4gc3VtIHRvIGdldCBhIHNpbmdsZSBudW1iZXIuDQotIFRoYXQgbnVtYmVyIGJlY29tZXMgYSAqKmZlYXR1cmUqKiBpbiB0aGUgb3V0cHV0IChhbHNvIGNhbGxlZCBhICoqZmVhdHVyZSBtYXAqKiBvciAqKmFjdGl2YXRpb24gbWFwKiopLg0KDQoqKkV4YW1wbGU6KioNCiAgDQpJZiB5b3UgY29udm9sdmUgYSA1eDUgaW1hZ2Ugd2l0aCBhIDN4MyBmaWx0ZXI6DQotIE91dHB1dCBzaXplIGJlY29tZXMgKDUtMykrMSA9IDMgKGluIGVhY2ggZGltZW5zaW9uKSwgdW5sZXNzIHlvdSBhcHBseSAqKnBhZGRpbmcqKi4NCg0KLS0tDQoNCioqMy4gUGFkZGluZyoqDQoNCi0gKipQYWRkaW5nKiogaGVscHMgbWFpbnRhaW4gdGhlIG9yaWdpbmFsIHNpemUgb2YgdGhlIGltYWdlIGFmdGVyIGNvbnZvbHV0aW9uLg0KLSBXZSBhZGQgZXh0cmEgcGl4ZWxzICh1c3VhbGx5IDBzKSBhcm91bmQgdGhlIGJvcmRlci4NCg0KKipXaXRob3V0IFBhZGRpbmc6KioNCiAgT3V0cHV0IHNpemUgPSAoVyAtIEYgKyAxKQ0KDQoqKldpdGggUGFkZGluZyAoUCk6KioNCiAgT3V0cHV0IHNpemUgPSAoKFcgLSBGICsgMlApIC8gUykgKyAxDQoNCldoZXJlOg0KLSBXID0gaW5wdXQgd2lkdGgvaGVpZ2h0DQotIEYgPSBmaWx0ZXIgc2l6ZQ0KLSBQID0gcGFkZGluZw0KLSBTID0gc3RyaWRlDQoNCi0tLQ0KDQoqKjQuIFN0cmlkZSoqDQoNCi0gU3RyaWRlIGNvbnRyb2xzIGhvdyBtYW55IHBpeGVscyB3ZSBtb3ZlIHRoZSBmaWx0ZXIgYXQgYSB0aW1lLg0KLSAqKlN0cmlkZSA9IDEqKiDihpIgbW92ZSBmaWx0ZXIgMSBwaXhlbCBhdCBhIHRpbWUuDQotICoqTGFyZ2VyIHN0cmlkZSoqIOKGkiBzbWFsbGVyIG91dHB1dCBhbmQgbGVzcyBvdmVybGFwLg0KDQotLS0NCg0KKio1LiBGaWx0ZXIgRXhhbXBsZXMqKg0KDQotICoqQXZlcmFnaW5nIEZpbHRlcioqIChzbW9vdGhpbmcpOg0KDQogIDEvMjUgw5cgIA0KICBbMSAxIDEgIA0KICAgMSAxIDEgIA0KICAgMSAxIDFdDQoNCi0gKipFZGdlIERldGVjdGlvbiBGaWx0ZXIqKjoNCg0KICBbIDAgIC0xICAgMCAgDQogICAtMSAgNCAgLTEgIA0KICAgIDAgIC0xICAwXQ0KDQpFYWNoIG9mIHRoZXNlIGlzIHVzZWQgdG8gZXh0cmFjdCBkaWZmZXJlbnQgZmVhdHVyZXMgKGVkZ2VzLCB0ZXh0dXJlcywgZXRjLikuDQoNCi0tLQ0KDQoqKjYuIE5ldXJhbCBOZXR3b3JrIEFyY2hpdGVjdHVyZSBSZWNhcCoqDQoNCkZyb20gTW9kdWxlIDExICh0cmFuc2NyaXB0KToNCg0KRWFjaCAqKm5ldXJvbioqIGlzIGVzc2VudGlhbGx5IGEgcmVncmVzc2lvbiBmdW5jdGlvbjogIA0KICB6ID0gV3ggKyBiIOKGkiDPgyh6KQ0KDQpXaGVyZToNCi0gVyA9IHdlaWdodHMNCi0geCA9IGlucHV0cw0KLSBiID0gYmlhcw0KLSDPgyA9IGFjdGl2YXRpb24gZnVuY3Rpb24gKGUuZy4sIHNpZ21vaWQsIFJlTFUpDQoNCkluIENOTnMsIHRoaXMgcmVncmVzc2lvbiBpcyByZXBsYWNlZCB3aXRoICoqY29udm9sdXRpb24gb3BlcmF0aW9ucyoqLg0KDQotLS0NCg0KKio3LiBNYXRyaXggRGltZW5zaW9ucyAmIE91dHB1dCBTaXplIChJbXBvcnRhbnQpKioNCg0KSWY6DQotIElucHV0ID0gMjh4MjgNCi0gRmlsdGVyID0gM3gzDQotIFBhZGRpbmcgPSAxDQotIFN0cmlkZSA9IDENCg0KVGhlbjoNCi0gT3V0cHV0ID0gKCgyOCAtIDMgKyAyw5cxKSAvIDEpICsgMSA9IDI4ICANCuKGkiBPdXRwdXQgaGFzIHNhbWUgZGltZW5zaW9uIGFzIGlucHV0Lg0KDQotLS0NCg0KKio4LiBTdW1tYXJ5IG9mIEtleSBDb25jZXB0cyoqDQoNCi0gKipDb252b2x1dGlvbioqOiBPdmVybGFwcGluZyBvZiBpbnB1dCBhbmQgZmlsdGVyIHRvIGV4dHJhY3QgZmVhdHVyZXMuDQotICoqUGFkZGluZyoqOiBBZGRzIGJvcmRlciB0byBjb250cm9sIG91dHB1dCBzaXplLg0KLSAqKlN0cmlkZSoqOiBDb250cm9scyBzdGVwIHNpemUgb2YgdGhlIGZpbHRlciBtb3ZlbWVudC4NCi0gKipGaWx0ZXJzL0tlcm5lbHMqKjogRGV0ZWN0IGVkZ2VzLCB0ZXh0dXJlcywgcGF0dGVybnMuDQotICoqT3V0cHV0Kio6IEZlYXR1cmUgbWFwcyB1c2VkIGluIGRvd25zdHJlYW0gbGF5ZXJzLg0KLSAqKk11bHRpcGxlIGZpbHRlcnMqKjogVXNlZCB0byBleHRyYWN0IGRpZmZlcmVudCBmZWF0dXJlIHR5cGVzLg0KDQotLS0NCg0KKio5LiBLZXkgVGFrZWF3YXlzKioNCg0KLSBDb252b2x1dGlvbnMgYXJlIHBvd2VyZnVsIGJlY2F1c2UgdGhleSAqKnByZXNlcnZlIHNwYXRpYWwgcmVsYXRpb25zaGlwcyoqLg0KLSBDTk5zIGxlYXJuIGZpbHRlcnMgYXV0b21hdGljYWxseSBkdXJpbmcgdHJhaW5pbmcuDQotICoqUGFkZGluZyArIHN0cmlkZSoqIGxldHMgeW91IGNvbnRyb2wgdGhlIHNpemUgb2YgeW91ciBvdXRwdXQgZmVhdHVyZSBtYXBzLg0KLSAqKkVkZ2UgZGV0ZWN0aW9uKiogYW5kICoqYmx1cnJpbmcqKiBhcmUgc2ltcGxlIHJlYWwtd29ybGQgZXhhbXBsZXMgb2YgY29udm9sdXRpb24gZmlsdGVycy4NCg0KLS0tDQoNCioqMTAuIFByYWN0aWNlIFF1ZXN0aW9ucyoqDQoNCjEuIFdoYXQgZG9lcyBhIGNvbnZvbHV0aW9uIG9wZXJhdGlvbiBkbyBpbiBpbWFnZSBwcm9jZXNzaW5nPw0KMi4gV2h5IGRvIHdlIHVzZSBwYWRkaW5nIGluIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzPw0KMy4gSG93IGRvZXMgdGhlIHN0cmlkZSBhZmZlY3QgdGhlIG91dHB1dCBzaXplPw0KNC4gV2hhdCBoYXBwZW5zIHdoZW4geW91IGFwcGx5IGFuIGVkZ2UgZGV0ZWN0aW9uIGZpbHRlciB0byBhbiBpbWFnZT8NCjUuIERlcml2ZSB0aGUgb3V0cHV0IHNpemUgb2YgYSBjb252b2x1dGlvbmFsIGxheWVyIGdpdmVuIHRoZSBpbnB1dCBzaXplLCBmaWx0ZXIgc2l6ZSwgcGFkZGluZywgYW5kIHN0cmlkZS4NCjYuIFdoYXQgaXMgdGhlIHJvbGUgb2YgdGhlIGJpYXMgdmVjdG9yIGluIGEgY29udm9sdXRpb25hbCBsYXllcj8NCg0KLS0tDQoNCioqU3VnZ2VzdGVkIFJlYWRpbmcqKg0KDQpGcm9tIHRoZSBFbGVtZW50cyBvZiBTdGF0aXN0aWNhbCBMZWFybmluZyAoSGFzdGllLCBUaWJzaGlyYW5pLCBGcmllZG1hbik6DQoNCi0gQ2hhcHRlciAxMTogTmV1cmFsIE5ldHdvcmtzICANCi0gQ2hhcHRlciA1OiBCYXNpcyBFeHBhbnNpb25zIGFuZCBSZWd1bGFyaXphdGlvbiAoZm9yIHVuZGVyc3RhbmRpbmcgY29udm9sdXRpb24tbGlrZSBvcGVyYXRpb25zKQ0KDQpVUkwgZm9yIHRleHRib29rOiAgDQpodHRwczovL3dlYi5zdGFuZm9yZC5lZHUvfmhhc3RpZS9FbGVtU3RhdExlYXJuLw0KDQotLS0NCg0KDQojIyMg8J+noCBNb2R1bGUgMTI6IENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmtzIOKAlCBTdHVkeSBHdWlkZQ0KDQotLS0NCg0KIyMjIDEuIENvcmUgQ29uY2VwdDogV2hhdCBpcyBhIENvbnZvbHV0aW9uPw0KDQoqKkRlZmluaXRpb246KiogIA0KQSAqKmNvbnZvbHV0aW9uKiogaXMgdGhlIG92ZXJsYXAgYmV0d2VlbiB0d28gZnVuY3Rpb25zLCBjYWxjdWxhdGVkIGJ5IHNsaWRpbmcgYSBmdW5jdGlvbiAoY2FsbGVkIGEgKipmaWx0ZXIqKiBvciAqKmtlcm5lbCoqKSBvdmVyIGlucHV0IGRhdGEgKHVzdWFsbHkgYW4gaW1hZ2UpLCBtdWx0aXBseWluZyBvdmVybGFwcGluZyB2YWx1ZXMsIGFuZCBzdW1taW5nIHRoZSByZXN1bHRzLg0KDQpUaGlzIGhlbHBzIGV4dHJhY3QgaW1wb3J0YW50IGZlYXR1cmVzIGxpa2UgZWRnZXMsIHRleHR1cmVzLCBvciBjb2xvciBncmFkaWVudHMgZnJvbSBpbWFnZXMuDQoNCi0tLQ0KDQojIyMgMi4gTWF0aGVtYXRpY2FsIFJlcHJlc2VudGF0aW9uDQoNCioqQ29udGludW91cyBmb3JtOioqDQogIA0KICB5KHQpID0g4oirIGYoz4QpwrdnKHTiiJLPhCkgZM+EDQoNCioqRGlzY3JldGUgKGZvciBkaWdpdGFsIGltYWdlcyBhbmQgbmV1cmFsIG5ldHdvcmtzKToqKg0KDQogIHlbaSwgal0gPSDOoyDOoyBpbnB1dFttK2ksIG4ral0gKiBmaWx0ZXJbbSwgbl0NCg0KV2hlcmU6DQotIGBpbnB1dGAgaXMgeW91ciBpbWFnZSBtYXRyaXgNCi0gYGZpbHRlcmAgaXMgdGhlIGtlcm5lbCAoZS5nLiwgZWRnZSBkZXRlY3Rpb24pDQotIGB5YCBpcyB0aGUgb3V0cHV0IGZlYXR1cmUgbWFwDQoNCi0tLQ0KDQojIyMgMy4gQ05OcyBpbiBBY3Rpb246IFB5dGhvbiBDb2RlIEV4YW1wbGUgKEJlZ2lubmVyLUZyaWVuZGx5KQ0KDQpgYGBweXRob24NCmltcG9ydCBudW1weSBhcyBucA0KZnJvbSBzY2lweS5zaWduYWwgaW1wb3J0IGNvbnZvbHZlMmQNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KIyBFeGFtcGxlIGlucHV0IGltYWdlICg1eDUpDQppbWFnZSA9IG5wLmFycmF5KFsNCiAgICBbMSwgMiwgMywgMCwgMV0sDQogICAgWzAsIDEsIDIsIDMsIDBdLA0KICAgIFsxLCAwLCAxLCAyLCAxXSwNCiAgICBbMiwgMSwgMCwgMSwgMl0sDQogICAgWzEsIDIsIDEsIDAsIDFdDQpdKQ0KDQojIEV4YW1wbGUgZWRnZSBkZXRlY3Rpb24gZmlsdGVyICgzeDMpDQpmaWx0ZXJfa2VybmVsID0gbnAuYXJyYXkoWw0KICAgIFswLCAtMSwgMF0sDQogICAgWy0xLCA0LCAtMV0sDQogICAgWzAsIC0xLCAwXQ0KXSkNCg0KIyBBcHBseSBjb252b2x1dGlvbg0Kb3V0cHV0ID0gY29udm9sdmUyZChpbWFnZSwgZmlsdGVyX2tlcm5lbCwgbW9kZT0ndmFsaWQnKQ0KDQpwcmludCgiT3V0cHV0IGFmdGVyIGNvbnZvbHV0aW9uOiIpDQpwcmludChvdXRwdXQpDQpgYGANCg0KKipPdXRwdXQ6KiogQSBzbWFsbGVyIG1hdHJpeCB0aGF0IGhpZ2hsaWdodHMgd2hlcmUgZWRnZXMgb3IgZmVhdHVyZXMgd2VyZSBkZXRlY3RlZC4NCg0KLS0tDQoNCiMjIyA0LiBDTk4gTGF5ZXJzIEJyZWFrZG93bg0KDQotICoqQ29udm9sdXRpb25hbCBMYXllcioqOiBBcHBsaWVzIGZpbHRlcnMgdG8gZXh0cmFjdCBmZWF0dXJlcy4NCi0gKipQYWRkaW5nKio6IEFkZHMgemVyb3MgYXJvdW5kIHRoZSBpbWFnZSB0byBwcmVzZXJ2ZSBzaXplLg0KLSAqKlN0cmlkZSoqOiBIb3cgbWFueSBzdGVwcyB0aGUgZmlsdGVyIG1vdmVzLg0KLSAqKkFjdGl2YXRpb24gKFJlTFUpKio6IEFwcGxpZXMgYSBmdW5jdGlvbiBsaWtlIGBtYXgoMCwgeClgIHRvIG1ha2UgdGhlIG1vZGVsIG5vbi1saW5lYXIuDQotICoqUG9vbGluZyoqOiBSZWR1Y2VzIGRpbWVuc2lvbmFsaXR5IChNYXhQb29sLCBBdmdQb29sKS4NCg0KLS0tDQoNCiMjIyA1LiBWaXN1YWwgRXhhbXBsZSBmcm9tIENsYXNzIFNsaWRlcw0KDQotICoqQXZlcmFnaW5nIGZpbHRlcioqIHNtb290aHMgdGhlIGltYWdlIHVzaW5nIGEgdW5pZm9ybSBrZXJuZWwuDQotICoqRWRnZSBkZXRlY3Rpb24gZmlsdGVyKiogaGlnaGxpZ2h0cyBib3VuZGFyaWVzIHVzaW5nOg0KICANCiAgYGBgDQogIFtbIDAsIC0xLCAgMF0sDQogICBbLTEsICA0LCAtMV0sDQogICBbIDAsIC0xLCAgMF1dDQogIGBgYA0KDQotLS0NCg0KIyMjIDYuIEtleSBUYWtlYXdheXMNCg0KLSBDb252b2x1dGlvbiA9IFNsaWRpbmcgKyBNdWx0aXBseWluZyArIFN1bW1pbmcNCi0gUGFkZGluZyBoZWxwcyBtYWludGFpbiB0aGUgaW5wdXQgc2l6ZQ0KLSBTdHJpZGUgYWZmZWN0cyBob3cgbXVjaCB3ZSByZWR1Y2UgZGltZW5zaW9uYWxpdHkNCi0gRmlsdGVycyBhcmUgKipsZWFybmVkKiogZHVyaW5nIHRyYWluaW5nIHRvIGV4dHJhY3QgdXNlZnVsIGZlYXR1cmVzDQotIENvbnZvbHV0aW9ucyBhcmUgKip3aHkgQ05OcyBvdXRwZXJmb3JtIHRyYWRpdGlvbmFsIG1ldGhvZHMqKiBpbiBpbWFnZSB0YXNrcw0KDQotLS0NCg0KIyMjIDcuIFJlbGV2YW50IFF1ZXN0aW9ucyB0byBUZXN0IFlvdXJzZWxmDQoNCjEuIFdoYXQgZG9lcyBhIGNvbnZvbHV0aW9uIGRvIGluIGEgbmV1cmFsIG5ldHdvcms/DQoyLiBXaHkgaXMgcGFkZGluZyB1c2VkIGluIGNvbnZvbHV0aW9uIGxheWVycz8NCjMuIFdoYXQgZWZmZWN0IGRvZXMgaW5jcmVhc2luZyB0aGUgc3RyaWRlIGhhdmU/DQo0LiBIb3cgZG9lcyBhbiBlZGdlIGRldGVjdGlvbiBmaWx0ZXIgd29yaz8NCjUuIFdoYXQgaXMgdGhlIG91dHB1dCBzaXplIGlmIHlvdSBhcHBseSBhIDN4MyBmaWx0ZXIgdG8gYSA1eDUgaW1hZ2Ugd2l0aCBubyBwYWRkaW5nIGFuZCBzdHJpZGUgMT8NCg0KLS0tDQoNCiMjIyA4LiBMYXltYW7igJlzIEV4cGxhbmF0aW9uIOKAkyBQaXp6YSBDdXR0ZXIgQW5hbG9neQ0KDQpJbWFnaW5lIHlvdeKAmXJlIGN1dHRpbmcgYSBsYXJnZSBwaXp6YSB3aXRoIGEgKipzdGVuY2lsKiogdGhhdCBpcyAzeDMgaW5jaGVzIGluIHNpemUuIFlvdSBwbGFjZSB5b3VyIHN0ZW5jaWwgb24gdGhlIHBpenphLCBsb29rIGF0IGp1c3QgdGhhdCBzcXVhcmUsIGFuZCByYXRlIGl0IGZyb20gMSB0byAxMCBiYXNlZCBvbiBob3cgbXVjaCBwZXBwZXJvbmkgaXQgaGFzLg0KDQpUaGVuLCB5b3UgbW92ZSB0aGUgc3RlbmNpbCBvdmVyIGEgbGl0dGxlIChieSAxIGluY2ggPSBzdHJpZGUpLCBhbmQgZG8gaXQgYWdhaW4uIFlvdSdyZSBzY2FubmluZyB0aGUgZW50aXJlIHBpenphLCBvbmUgc21hbGwgcGF0Y2ggYXQgYSB0aW1lLg0KDQpZb3VyIHN0ZW5jaWwgaXMgdGhlICoqZmlsdGVyKiouDQpUaGUgcGl6emEgaXMgdGhlICoqaW1hZ2UqKi4NCllvdXIgcmF0aW5nIGlzIHRoZSAqKm91dHB1dCBmZWF0dXJlKiouDQpJZiB5b3Ugd2FudCB0aGUgZWRnZXMgdG8gYWxzbyBiZSBhbmFseXplZCAobm90IGp1c3QgdGhlIGNlbnRlciksIHlvdSBwbGFjZSBuYXBraW5zICh6ZXJvcykgYXJvdW5kIHRoZSBlZGdlIOKAlCB0aGF0J3MgKipwYWRkaW5nKiouDQoNClRoaXMgaXMgd2hhdCBjb252b2x1dGlvbiBkb2VzOiBpdCBoZWxwcyAqKnNlZSB0aGUgaW1wb3J0YW50IHBhcnRzKiogb2YgYW4gaW1hZ2UgKGxpa2UgY2hlZXNlLCBjcnVzdCwgb3IgcGVwcGVyb25pKSBhbmQgY29uZGVuc2UgaXQgaW50byB1c2VmdWwgaW5mbyDigJQgd2hpY2ggaXMgd2hhdCBhIG5ldXJhbCBuZXR3b3JrIG5lZWRzIHRvIG1ha2UgYSBkZWNpc2lvbi4NCg0KLS0tDQoNCiMjIyA5LiBUZXh0Ym9vayBSZWZlcmVuY2VzIGZvciBEZWVwZXIgUmVhZGluZw0KDQoqKkZyb20g4oCcVGhlIEVsZW1lbnRzIG9mIFN0YXRpc3RpY2FsIExlYXJuaW5n4oCdIGJ5IEhhc3RpZSBldCBhbC4qKiAgDQotIENoYXB0ZXIgMTE6IE5ldXJhbCBOZXR3b3JrcyAgDQogIFVSTDogaHR0cHM6Ly93ZWIuc3RhbmZvcmQuZWR1L35oYXN0aWUvRWxlbVN0YXRMZWFybi8NCg0KKipGcm9tIOKAnFRoZSBTdGF0aXN0aWNhbCBTbGV1dGjigJ0gYnkgUmFtc2V5ICYgU2NoYWZlcioqICANCi0gTm90IGltYWdlLWZvY3VzZWQgYnV0IHVzZWZ1bCBmb3IgcmVncmVzc2lvbiBiYWNrZ3JvdW5kDQoNCi0tLQ0KDQoNCg0KDQoNCkhlcmXigJlzIHlvdXIgKipjb21wbGV0ZSBzdHVkeSBndWlkZSBmb3IgQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29ya3MgKENOTnMpKiosIGludGVncmF0aW5nIGFsbCBjbGFzcyB2aXN1YWxzLCB0cmFuc2NyaXB0cywgdGV4dGJvb2sga25vd2xlZGdlLCBtYXRoZW1hdGljYWwgY29uY2VwdHMsIFB5dGhvbiBjb2RlLCBrZXkgdGFrZWF3YXlzLCBhbmQgYW4gZWFzeS10by11bmRlcnN0YW5kIHJlYWwtd29ybGQgYW5hbG9neS4NCg0KLS0tDQoNClFUVyA3MzMzIOKAkyBNb2R1bGUgMTIgIA0KKipTdHVkeSBHdWlkZTogQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29ya3MgKENOTnMpKioNCg0KLS0tDQoNCioq8J+noCBPdmVydmlldyBvZiBDTk5zKioNCg0KQSBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrIGlzIGEgdHlwZSBvZiBuZXVyYWwgbmV0d29yayBhcmNoaXRlY3R1cmUgKipvcHRpbWl6ZWQgZm9yIHByb2Nlc3NpbmcgaW1hZ2VzKiouIEl0IG1pbWljcyB0aGUgKipodW1hbiB2aXN1YWwgY29ydGV4Kiogd2hlcmUgKipvbmx5IG5lYXJieSBuZXVyb25zIGNvbW11bmljYXRlKirigJRtZWFuaW5nICoqZmlsdGVycyAoa2VybmVscykqKiBmb2N1cyBvbiBsb2NhbCBwYXR0ZXJucyAoZWRnZXMsIGNvcm5lcnMsIGV0Yy4pIGluc3RlYWQgb2YgdGhlIGVudGlyZSBpbWFnZSBhdCBvbmNlLg0KDQotLS0NCg0KKirwn6epIExheWVycyBpbiBhIENOTioqDQoNCjEuICoqQ29udm9sdXRpb25hbCBMYXllcioqDQoyLiAqKkFjdGl2YXRpb24gRnVuY3Rpb24gKHVzdWFsbHkgUmVMVSkqKg0KMy4gKipQb29saW5nIExheWVyKioNCjQuICoqRmxhdHRlbiBMYXllcioqDQo1LiAqKkRlbnNlIChGdWxseSBDb25uZWN0ZWQpIExheWVyKioNCjYuICoqT3V0cHV0IExheWVyIChvZnRlbiBTb2Z0bWF4IGZvciBjbGFzc2lmaWNhdGlvbikqKg0KDQotLS0NCg0KIyMjIPCfp64gTWF0aGVtYXRpY2FsIENvbmNlcHRzDQoNCiMjIyMgMS4gQ29udm9sdXRpb24gTGF5ZXI6DQpBcHBsaWVzIGEgZmlsdGVyIChrZXJuZWwpIGFjcm9zcyB0aGUgaW1hZ2UuDQoNCioqRXF1YXRpb24gKDJEIGRpc2NyZXRlIGNvbnZvbHV0aW9uKToqKiAgDQpZKGksIGopID0gzqNfbSDOo19uIFgoaSttLCBqK24pIMK3IEsobSwgbikNCg0KV2hlcmU6ICANCi0gWCA9IGlucHV0IGltYWdlICANCi0gSyA9IGtlcm5lbCAgDQotIFkgPSBvdXRwdXQgZmVhdHVyZSBtYXANCg0KIyMjIyAyLiBQb29saW5nIExheWVyOg0KUmVkdWNlcyBzcGF0aWFsIHNpemUgYnkgc2VsZWN0aW5nICoqbWF4Kiogb3IgKiphdmVyYWdlKiogdmFsdWVzLg0KDQotICoqTWF4IFBvb2xpbmcgKDLDlzIgd2l0aCBzdHJpZGUgMikqKjogIA0KRnJvbTogIA0KW1sxLCAxXSwgIA0KIFs1LCA2XV0g4oaSIG1heCA9IDYNCg0KIyMjIyAzLiBGbGF0dGVuIExheWVyOg0KQ29udmVydHMgbXVsdGktZGltZW5zaW9uYWwgdGVuc29yIGludG8gMUQgdmVjdG9yIGZvciBkZW5zZSBsYXllcnMuDQoNCkV4YW1wbGU6DQpbWzAsIC0xLCAwXSwgIA0KIFstMSwgNCwgLTFdLCAgDQogWzAsIC0xLCAwXV0gIA0K4oaSIFswLCAtMSwgMCwgLTEsIDQsIC0xLCAwLCAtMSwgMF0NCg0KLS0tDQoNCiMjIyDwn5K7IFB5dGhvbiBDb2RlIEV4YW1wbGUgKFNpbXBsZSBDTk4gd2l0aCBLZXJhcykNCg0KYGBgcHl0aG9uDQppbXBvcnQgdGVuc29yZmxvdyBhcyB0Zg0KZnJvbSB0ZW5zb3JmbG93LmtlcmFzIGltcG9ydCBsYXllcnMsIG1vZGVscw0KDQptb2RlbCA9IG1vZGVscy5TZXF1ZW50aWFsKCkNCm1vZGVsLmFkZChsYXllcnMuQ29udjJEKDMyLCAoMywgMyksIGFjdGl2YXRpb249J3JlbHUnLCBpbnB1dF9zaGFwZT0oMjgsIDI4LCAxKSkpDQptb2RlbC5hZGQobGF5ZXJzLk1heFBvb2xpbmcyRCgoMiwgMikpKQ0KbW9kZWwuYWRkKGxheWVycy5Db252MkQoNjQsICgzLCAzKSwgYWN0aXZhdGlvbj0ncmVsdScpKQ0KbW9kZWwuYWRkKGxheWVycy5NYXhQb29saW5nMkQoKDIsIDIpKSkNCm1vZGVsLmFkZChsYXllcnMuQ29udjJEKDY0LCAoMywgMyksIGFjdGl2YXRpb249J3JlbHUnKSkNCg0KIyBGbGF0dGVuIGFuZCBEZW5zZQ0KbW9kZWwuYWRkKGxheWVycy5GbGF0dGVuKCkpDQptb2RlbC5hZGQobGF5ZXJzLkRlbnNlKDY0LCBhY3RpdmF0aW9uPSdyZWx1JykpDQptb2RlbC5hZGQobGF5ZXJzLkRlbnNlKDEwLCBhY3RpdmF0aW9uPSdzb2Z0bWF4JykpDQoNCm1vZGVsLnN1bW1hcnkoKQ0KYGBgDQoNCi0tLQ0KDQojIyMg8J+OqCBMYXltYW7igJlzIEFuYWxvZ3kNCg0KSW1hZ2luZSB5b3XigJlyZSBsb29raW5nIGF0IGEgKipXaGVyZeKAmXMgV2FsZG8qKiBib29rOg0KLSBZb3VyICoqZXllcyBzY2FuIHNtYWxsIHNlY3Rpb25zKiogb2YgdGhlIGltYWdlIChsaWtlIGEgZmlsdGVyKS4NCi0gWW914oCZcmUgY2hlY2tpbmcgKipwYXR0ZXJucyBsaWtlIGhhdHMsIGdsYXNzZXMsIHJlZC13aGl0ZSBzaGlydHMqKi4NCi0gV2hlbiBzb21ldGhpbmcgc3RhbmRzIG91dCwgeW91ciBicmFpbiBzdG9yZXMgdGhhdCBpbmZvLg0KLSBPbmNlIGVub3VnaCBmZWF0dXJlcyBhcmUgY29sbGVjdGVkLCB5b3UgKipkZWNpZGUgd2hlcmUgV2FsZG8gaXMqKi4NCg0KU2ltaWxhcmx5Og0KLSAqKkNvbnZvbHV0aW9uYWwgbGF5ZXJzKiogc2NhbiBmb3IgcGF0dGVybnMuDQotICoqUG9vbGluZyoqIHN1bW1hcml6ZXMgdGhvc2UgcGF0dGVybnMgKHNocmlua3MgZGV0YWlsKS4NCi0gKipGbGF0dGVuaW5nKiogc3RhY2tzIGV2ZXJ5dGhpbmcgaW50byBhIGxpc3QuDQotICoqRGVuc2UgbGF5ZXJzKiogbWFrZSBhIGZpbmFsIGRlY2lzaW9uIChlLmcuLCB0aGlzIGlzIFdhbGRvISkuDQoNCi0tLQ0KDQojIyMg8J+TmCBUZXh0Ym9vayBTdXBwb3J0DQoNCkZyb20gKipFbGVtZW50cyBvZiBTdGF0aXN0aWNhbCBMZWFybmluZyoqOg0KLSAqKkNoYXB0ZXIgMTEg4oCTIE5ldXJhbCBOZXR3b3JrcyoqICANCmh0dHBzOi8vd2ViLnN0YW5mb3JkLmVkdS9+aGFzdGllL0VsZW1TdGF0TGVhcm4vDQoNCkFsc28gZXhwbG9yZSAqKkNTMjMxbiBieSBTdGFuZm9yZCoqIChWaXN1YWwgQ05OIHdhbGt0aHJvdWdoKTogIA0KaHR0cDovL2NzMjMxbi5zdGFuZm9yZC5lZHUvc2xpZGVzLzIwMTkvY3MyMzFuXzIwMTlfbGVjdHVyZTUucGRmDQoNCi0tLQ0KDQojIyMg8J+noCBLZXkgVGFrZWF3YXlzDQoNCi0gQ05OcyBhcmUgaWRlYWwgZm9yIGltYWdlIGNsYXNzaWZpY2F0aW9uIGR1ZSB0byBzcGF0aWFsIGF3YXJlbmVzcy4NCi0gQ29udm9sdXRpb25hbCBsYXllcnMgZXh0cmFjdCAqKmxvY2FsIHBhdHRlcm5zKiouDQotIFBvb2xpbmcgbGF5ZXJzIHJlZHVjZSAqKnNwYXRpYWwgZGltZW5zaW9ucyoqLg0KLSBGbGF0dGVuaW5nIHJlc2hhcGVzIHRoZSBpbWFnZSBpbnRvIGEgMUQgdmVjdG9yIGZvciBEZW5zZSBsYXllcnMuDQotIENOTnMgKipyZWR1Y2UgcGFyYW1ldGVycyoqIGNvbXBhcmVkIHRvIHRyYWRpdGlvbmFsIGRlbnNlIG5ldHdvcmtzIHdoaWxlIGltcHJvdmluZyBhY2N1cmFjeS4NCg0KLS0tDQoNCiMjIyDinZMgUHJhY3RpY2UgUXVlc3Rpb25zDQoNCjEuIFdoYXQgaXMgdGhlIHB1cnBvc2Ugb2YgYSBjb252b2x1dGlvbmFsIGxheWVyPw0KMi4gV2hhdCBkb2VzIG1heCBwb29saW5nIGRvPyBXaHkgaXMgaXQgdXNlZnVsPw0KMy4gV2h5IGRvIHdlIGZsYXR0ZW4gZGF0YSBiZWZvcmUgc2VuZGluZyBpdCB0byBhIGRlbnNlIGxheWVyPw0KNC4gSW4gd2hhdCB3YXkgaXMgYSBDTk4gYmlvbG9naWNhbGx5IGluc3BpcmVkPw0KNS4gSG93IGRvZXMgaW5jcmVhc2luZyB0aGUgbnVtYmVyIG9mIGZpbHRlcnMgYWZmZWN0IHRoZSBvdXRwdXQ/DQoNCg0KLS0tDQoNCioq8J+TmCBRVFcgNzMzMyDigJMgTW9kdWxlIDEyOiBUcmFuc2ZlciBMZWFybmluZyBTdHVkeSBHdWlkZSoqDQoNCi0tLQ0KDQojIyMg8J+UjSAxLiBDb25jZXB0IE92ZXJ2aWV3OiBXaGF0IElzIFRyYW5zZmVyIExlYXJuaW5nPw0KDQoqKkRlZmluaXRpb246KiogIA0KVHJhbnNmZXIgTGVhcm5pbmcgaXMgYSB0ZWNobmlxdWUgd2hlcmUgd2UgdGFrZSBhICoqcHJlLXRyYWluZWQgbW9kZWwqKiAob2Z0ZW4gdHJhaW5lZCBvbiBsYXJnZSBkYXRhc2V0cyB1c2luZyBwb3dlcmZ1bCBoYXJkd2FyZSkgYW5kICoqcmV1c2UgaXRzIGVhcmx5IGxheWVycyoqIHdoaWxlICoqcmV0cmFpbmluZyBvbmx5IHRoZSBmaW5hbCBsYXllcnMqKiBmb3IgYSBuZXcsIG9mdGVuIHNtYWxsZXIsIHRhc2suDQoNCkluc3RlYWQgb2YgdHJhaW5pbmcgZXZlcnl0aGluZyBmcm9tIHNjcmF0Y2gsIHdlICoq4oCcdHJhbnNmZXLigJ0gdGhlIGxlYXJuaW5nKiogZnJvbSBvbmUgdGFzayB0byBhbm90aGVyLg0KDQotLS0NCg0KIyMjIPCfp6AgMi4gV2h5IERvZXMgSXQgV29yaz8NCg0KRnJvbSB5b3VyIHRyYW5zY3JpcHQ6DQotIEVhcmx5IGxheWVycyAodG9wIG9mIHRoZSBuZXR3b3JrKSBsZWFybiAqKmdlbmVyYWwgZmVhdHVyZXMqKiAoZWRnZXMsIGNvbG9ycywgc2hhcGVzKS4NCi0gTGF0ZXIgbGF5ZXJzIChkZW5zZSBsYXllcnMpIGxlYXJuICoqc3BlY2lmaWMgdGFzay1yZWxhdGVkIGZlYXR1cmVzKiouDQotIEdlbmVyYWwgZmVhdHVyZXMgYXJlICoqcmV1c2FibGUqKiwgZXZlbiBhY3Jvc3MgZGlmZmVyZW50IHRhc2tzIChlLmcuLCBkb2dzIHZzLiBjYXRzLCBvciBjYXJzIHZzLiBwbGFuZXMpLg0KLSBTYXZlcyB0aW1lLCBkYXRhLCBhbmQgY29tcHV0ZSByZXNvdXJjZXMuDQoNCi0tLQ0KDQojIyMg8J+TiiAzLiBNYXRoZW1hdGljYWwgRm9ybXVsYXRpb24NCg0KTGV0Og0KDQotICoqZih4OyDOuCkqKiBiZSB0aGUgb3JpZ2luYWwgbmV1cmFsIG5ldHdvcmsNCi0gzrggPSBbzrhfZ2VuZXJhbCwgzrhfc3BlY2lmaWNdDQoNCldlIGZyZWV6ZSDOuF9nZW5lcmFsIGFuZCByZXRyYWluIG9ubHkgzrhfc3BlY2lmaWMuDQoNCioqTmV3IG91dHB1dDoqKg0KICANCiAgeScgPSBmKHg7IM64X2dlbmVyYWwsIM64J19zcGVjaWZpYykgIA0KICAod2hlcmUgzrhfZ2VuZXJhbCBpcyBwcmUtdHJhaW5lZCBhbmQgZnJvemVuLCDOuCdfc3BlY2lmaWMgaXMgbmV3bHkgdHJhaW5lZCkNCg0KLS0tDQoNCiMjIyDwn5K7IDQuIFB5dGhvbiBDb2RlIEV4YW1wbGUgKFVzaW5nIEtlcmFzKQ0KDQpgYGBweXRob24NCmZyb20gdGVuc29yZmxvdy5rZXJhcy5hcHBsaWNhdGlvbnMgaW1wb3J0IFZHRzE2DQpmcm9tIHRlbnNvcmZsb3cua2VyYXMubW9kZWxzIGltcG9ydCBNb2RlbA0KZnJvbSB0ZW5zb3JmbG93LmtlcmFzLmxheWVycyBpbXBvcnQgRGVuc2UsIEdsb2JhbEF2ZXJhZ2VQb29saW5nMkQNCmZyb20gdGVuc29yZmxvdy5rZXJhcyBpbXBvcnQgSW5wdXQNCg0KIyBMb2FkIHByZS10cmFpbmVkIFZHRzE2IHdpdGhvdXQgdG9wIGxheWVycw0KYmFzZV9tb2RlbCA9IFZHRzE2KHdlaWdodHM9J2ltYWdlbmV0JywgaW5jbHVkZV90b3A9RmFsc2UsIGlucHV0X3RlbnNvcj1JbnB1dChzaGFwZT0oMjI0LCAyMjQsIDMpKSkNCg0KIyBGcmVlemUgYmFzZSBtb2RlbCBsYXllcnMNCmZvciBsYXllciBpbiBiYXNlX21vZGVsLmxheWVyczoNCiAgICBsYXllci50cmFpbmFibGUgPSBGYWxzZQ0KDQojIEFkZCBuZXcgZGVuc2UgbGF5ZXJzIG9uIHRvcA0KeCA9IGJhc2VfbW9kZWwub3V0cHV0DQp4ID0gR2xvYmFsQXZlcmFnZVBvb2xpbmcyRCgpKHgpDQp4ID0gRGVuc2UoMTI4LCBhY3RpdmF0aW9uPSdyZWx1JykoeCkNCnByZWRpY3Rpb25zID0gRGVuc2UoMTAsIGFjdGl2YXRpb249J3NvZnRtYXgnKSh4KSAgIyBmb3IgMTAtY2xhc3MgcHJvYmxlbQ0KDQojIEZpbmFsIG1vZGVsDQptb2RlbCA9IE1vZGVsKGlucHV0cz1iYXNlX21vZGVsLmlucHV0LCBvdXRwdXRzPXByZWRpY3Rpb25zKQ0KbW9kZWwuY29tcGlsZShvcHRpbWl6ZXI9J2FkYW0nLCBsb3NzPSdjYXRlZ29yaWNhbF9jcm9zc2VudHJvcHknLCBtZXRyaWNzPVsnYWNjdXJhY3knXSkNCmBgYA0KDQpUaGlzIHVzZXMgKipJbWFnZU5ldC10cmFpbmVkIGZlYXR1cmVzKiogYW5kIHRyYWlucyBvbmx5IHRoZSBjbGFzc2lmaWVyIG9uIHlvdXIgY3VzdG9tIGRhdGEuDQoNCi0tLQ0KDQojIyMg8J+TmiA1LiBSZWZlcmVuY2UgZnJvbSBUZXh0Ym9va3MNCg0KKipfVGhlIEVsZW1lbnRzIG9mIFN0YXRpc3RpY2FsIExlYXJuaW5nXyoqIChIYXN0aWUsIFRpYnNoaXJhbmksIEZyaWVkbWFuKSAgDQotIENoYXB0ZXIgMTEgKE5ldXJhbCBOZXR3b3Jrcyk6IGV4cGxhaW5zIGhvdyBsb3dlci1sZXZlbCByZXByZXNlbnRhdGlvbnMgZ2VuZXJhbGl6ZSBhY3Jvc3MgcHJvYmxlbXMuICANCiAgTGluazogaHR0cHM6Ly93ZWIuc3RhbmZvcmQuZWR1L35oYXN0aWUvRWxlbVN0YXRMZWFybi8NCg0KLS0tDQoNCiMjIyDinIUgNi4gS2V5IFRha2Vhd2F5cw0KDQotIFlvdSBjYW4gcmV1c2UgZXhwZW5zaXZlLCBwcmUtdHJhaW5lZCBtb2RlbHMgdG8gc29sdmUgeW91ciBvd24gcHJvYmxlbS4NCi0gWW91IG9ubHkgbmVlZCB0byB0cmFpbiBhIGZldyBsYXllcnMgKHVzdWFsbHkgdGhlIGxhc3QgZGVuc2UgbGF5ZXJzKS4NCi0gR3JlYXQgZm9yICoqbGltaXRlZCBkYXRhKiosICoqZmFzdGVyIHRyYWluaW5nKiosIGFuZCAqKmNyb3NzLWRvbWFpbiB0YXNrcyoqLg0KLSBQcmUtdHJhaW5lZCBtb2RlbHMgYXJlIGF2YWlsYWJsZSB2aWEgKipUZW5zb3JGbG93IEh1YioqLCAqKlB5VG9yY2ggSHViKiosICoqSHVnZ2luZyBGYWNlKiosIGV0Yy4NCg0KLS0tDQoNCiMjIyDinZMgNy4gUmVsZXZhbnQgUXVlc3Rpb25zDQoNCjEuIFdoeSBpcyB0cmFuc2ZlciBsZWFybmluZyB1c2VmdWwgaW4gZGVlcCBsZWFybmluZz8NCjIuIFdoaWNoIHBhcnRzIG9mIHRoZSBtb2RlbCBhcmUgdHlwaWNhbGx5IGZyb3plbiBhbmQgd2hpY2ggYXJlIHJldHJhaW5lZD8NCjMuIENhbiB0cmFuc2ZlciBsZWFybmluZyB3b3JrIGFjcm9zcyBkb21haW5zIChlLmcuLCB2aXNpb24gdG8gbGFuZ3VhZ2UpPw0KNC4gV2hhdCByb2xlIGRvZXMgdGhlIHZhbmlzaGluZyBncmFkaWVudCBwbGF5IGluIG1vdGl2YXRpbmcgdHJhbnNmZXIgbGVhcm5pbmc/DQoNCi0tLQ0KDQojIyMg8J+nuCA4LiBMYXltYW4gRXhwbGFuYXRpb24gKFJlYWwtV29ybGQgQW5hbG9neSkNCg0KKipJbWFnaW5lIHlvdeKAmXJlIGxlYXJuaW5nIHRvIHBsYXkgbXVzaWMuKioNCg0KLSBZb3UgdHJhaW5lZCAxMCB5ZWFycyBvbiB0aGUgcGlhbm8uDQotIE5vdyB5b3Ugd2FudCB0byBsZWFybiB0aGUgZ3VpdGFyLg0KDQpZb3UgZG9u4oCZdCBuZWVkIHRvIHJlbGVhcm4gKiptdXNpYyB0aGVvcnksIHJoeXRobSwgbm90ZXMsIG9yIHRpbWluZyoqLiBZb3UganVzdCBuZWVkIHRvIGxlYXJuICoqaG93IHRvIGhvbGQgYW5kIHN0cnVtIHRoZSBndWl0YXIqKi4NCg0KVGhhdOKAmXMgdHJhbnNmZXIgbGVhcm5pbmc6DQotIFlvdXIgbXVzaWMgdGhlb3J5ID0gcHJlLXRyYWluZWQgbGF5ZXJzIChmcm96ZW4pLg0KLSBMZWFybmluZyBndWl0YXIgc3BlY2lmaWNzID0gZmluYWwgbGF5ZXJzIChyZXRyYWluZWQpLg0KLSBZb3XigJlyZSBub3Qgc3RhcnRpbmcgZnJvbSBzY3JhdGNoIOKAlCB5b3XigJlyZSBhZGFwdGluZy4NCg0KSnVzdCBsaWtlIHlvdXIgYnJhaW4gZG9lc27igJl0IHJlbGVhcm4gaG93IHNvdW5kIHdvcmtzIGVhY2ggdGltZSwgYSBDTk4gZG9lc27igJl0IG5lZWQgdG8gcmVsZWFybiBlZGdlIGRldGVjdGlvbiBvciB0ZXh0dXJlcyB3aGVuIGNsYXNzaWZ5aW5nIG5ldyBpbWFnZXMuDQoNCi0tLQ0KDQoqKvCfk5ggUVRXIDczMzMg4oCTIE1vZHVsZSAxMjogQ05OIFBhcnQgSSDigJMgU2V0IFVwIFlvdXIgRGF0YSBTdHVkeSBHdWlkZSoqDQoNCi0tLQ0KDQojIyMg8J+UjSAxLiBXaGF0IEFyZSBXZSBEb2luZz8NCg0KV2XigJlyZToNCi0gSW1wb3J0aW5nIENJRkFSLTEwIGltYWdlIGRhdGFzZXQNCi0gVmlzdWFsaXppbmcgdGhlIGRhdGFzZXQNCi0gTm9ybWFsaXppbmcgdGhlIHBpeGVsIHZhbHVlcw0KLSBQcmVwYXJpbmcgZXZlcnl0aGluZyB0byBmZWVkIGludG8gYSBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrIChDTk4pDQoNCi0tLQ0KDQojIyMg8J+nqiAyLiBXaHkgTm9ybWFsaXplIGFuZCBWaXN1YWxpemU/DQoNCi0gKipOb3JtYWxpemF0aW9uKiogc2NhbGVzIHBpeGVsIHZhbHVlcyBmcm9tIGBbMCwgMjU1XWAgdG8gYFswLjAsIDEuMF1gIOKGkiBoZWxwcyB0aGUgbmV1cmFsIG5ldHdvcmsgY29udmVyZ2UgZmFzdGVyLg0KLSAqKlZpc3VhbGl6YXRpb24qKiBsZXRzIHVzIHZlcmlmeSB0aGF0IHRoZSBpbWFnZXMgYW5kIGxhYmVscyBhcmUgY29ycmVjdGx5IGFsaWduZWQgYmVmb3JlIHRyYWluaW5nLg0KDQotLS0NCg0KIyMjIPCfk5ogMy4gVGV4dGJvb2sgU3VwcG9ydA0KDQpGcm9tIF9UaGUgRWxlbWVudHMgb2YgU3RhdGlzdGljYWwgTGVhcm5pbmdfIGJ5IEhhc3RpZSwgVGlic2hpcmFuaSAmIEZyaWVkbWFuOiAgDQotIENoYXB0ZXIgMTEgKE5ldXJhbCBOZXR3b3JrcykgIA0KLSBDaGFwdGVyIDcgKE1vZGVsIEFzc2Vzc21lbnQgYW5kIFNlbGVjdGlvbikgIA0KTGluazogaHR0cHM6Ly93ZWIuc3RhbmZvcmQuZWR1L35oYXN0aWUvRWxlbVN0YXRMZWFybi8NCg0KQWxzbyBoZWxwZnVsOiBUZW5zb3JGbG93IG9mZmljaWFsIGRvY3M6ICANCmh0dHBzOi8vd3d3LnRlbnNvcmZsb3cub3JnL3R1dG9yaWFscy9pbWFnZXMvY25uDQoNCi0tLQ0KDQojIyMg8J+TiiA0LiBNYXRoZW1hdGljYWwgRXhwbGFuYXRpb24NCg0KLSBFYWNoIGltYWdlIGlzIGEgKip0ZW5zb3Igb2Ygc2hhcGUgKDMyLCAzMiwgMykqKiAoaGVpZ2h0LCB3aWR0aCwgUkdCIGNoYW5uZWxzKS4NCi0gTm9ybWFsaXphdGlvbjoNCiAgDQogIEZvciBlYWNoIHBpeGVsIHZhbHVlIFwoIHggXCkgaW4gdGhlIGltYWdlOiAgDQogIFwoIHhfe1x0ZXh0e25vcm1hbGl6ZWR9fSA9IFxmcmFje3h9ezI1NS4wfSBcKQ0KDQotIExhYmVscyBhcmUgaW50ZWdlci1lbmNvZGVkOiAgDQogIGUuZy4sIDAgPSBhaXJwbGFuZSwgMSA9IGNhciwgLi4uLCA5ID0gdHJ1Y2sNCg0KLS0tDQoNCiMjIyDwn5K7IDUuIFB5dGhvbiBDb2RlIChCZWdpbm5lci1GcmllbmRseSBTZXR1cCkNCg0KYGBgcHl0aG9uDQojIFN0ZXAgMTogSW1wb3J0IGxpYnJhcmllcw0KaW1wb3J0IHRlbnNvcmZsb3cgYXMgdGYNCmZyb20gdGVuc29yZmxvdy5rZXJhcyBpbXBvcnQgZGF0YXNldHMsIGxheWVycywgbW9kZWxzDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgU3RlcCAyOiBMb2FkIENJRkFSLTEwIGRhdGFzZXQNCih0cmFpbl9pbWFnZXMsIHRyYWluX2xhYmVscyksICh0ZXN0X2ltYWdlcywgdGVzdF9sYWJlbHMpID0gZGF0YXNldHMuY2lmYXIxMC5sb2FkX2RhdGEoKQ0KDQojIFN0ZXAgMzogTm9ybWFsaXplIHBpeGVsIHZhbHVlcyB0byBbMCwgMV0NCnRyYWluX2ltYWdlcywgdGVzdF9pbWFnZXMgPSB0cmFpbl9pbWFnZXMgLyAyNTUuMCwgdGVzdF9pbWFnZXMgLyAyNTUuMA0KDQojIFN0ZXAgNDogRGVmaW5lIGNsYXNzIG5hbWVzDQpjbGFzc19uYW1lcyA9IFsnYWlycGxhbmUnLCAnYXV0b21vYmlsZScsICdiaXJkJywgJ2NhdCcsICdkZWVyJywNCiAgICAgICAgICAgICAgICdkb2cnLCAnZnJvZycsICdob3JzZScsICdzaGlwJywgJ3RydWNrJ10NCg0KIyBTdGVwIDU6IFBsb3QgZmlyc3QgMjUgaW1hZ2VzDQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLDEwKSkNCmZvciBpIGluIHJhbmdlKDI1KToNCiAgICBwbHQuc3VicGxvdCg1LDUsaSsxKQ0KICAgIHBsdC54dGlja3MoW10pICAjIFJlbW92ZSB4LWF4aXMNCiAgICBwbHQueXRpY2tzKFtdKSAgIyBSZW1vdmUgeS1heGlzDQogICAgcGx0LmdyaWQoRmFsc2UpDQogICAgcGx0Lmltc2hvdyh0cmFpbl9pbWFnZXNbaV0pDQogICAgcGx0LnhsYWJlbChjbGFzc19uYW1lc1t0cmFpbl9sYWJlbHNbaV1bMF1dKQ0KcGx0LnNob3coKQ0KYGBgDQoNCi0tLQ0KDQojIyMg4pyFIDYuIEtleSBUYWtlYXdheXMNCg0KLSAqKkNJRkFSLTEwKiogaXMgYSBncmVhdCBzdGFydGVyIGRhdGFzZXQgZm9yIGltYWdlIGNsYXNzaWZpY2F0aW9uIHdpdGggMTAgY29tbW9uIG9iamVjdCBjbGFzc2VzLg0KLSAqKk5vcm1hbGl6YXRpb24qKiBpcyBlc3NlbnRpYWwgaW4gbmV1cmFsIG5ldHdvcmtzIHRvIGVuc3VyZSBwaXhlbCB2YWx1ZXMgYXJlIGluIGEgc3RhbmRhcmQgcmFuZ2UuDQotIFBsb3R0aW5nIGltYWdlcyBoZWxwcyB2YWxpZGF0ZSBkYXRhIHF1YWxpdHkgYmVmb3JlIHRyYWluaW5nLg0KLSBUaGlzIHNldHVwIHBoYXNlIGlzIGNydWNpYWwuIEdhcmJhZ2UgaW4gPSBnYXJiYWdlIG91dC4NCg0KLS0tDQoNCiMjIyDinZMgNy4gUXVlc3Rpb25zIHRvIFRlc3QgWW91cnNlbGYNCg0KMS4gV2h5IGRvIHdlIGRpdmlkZSBwaXhlbCB2YWx1ZXMgYnkgMjU1IGluIGltYWdlIGRhdGFzZXRzPw0KMi4gV2hhdCBhcmUgdGhlIGRpbWVuc2lvbnMgb2YgZWFjaCBDSUZBUi0xMCBpbWFnZT8NCjMuIFdoeSBpcyBpdCB1c2VmdWwgdG8gdmlzdWFsaXplIHRoZSBpbWFnZXMgYmVmb3JlIHRyYWluaW5nIGEgQ05OPw0KNC4gV2hhdCBkb2VzIGB0cmFpbl9sYWJlbHNbaV1bMF1gIG1lYW4gaW4gdGhlIHBsb3R0aW5nIGxvb3A/DQoNCi0tLQ0KDQojIyMg8J+nuCA4LiBMYXltYW7igJlzIEFuYWxvZ3k6IEZpbGluZyBQaG90b3MgZm9yIGFuIEFsYnVtDQoNCkltYWdpbmUgeW914oCZcmUgY3JlYXRpbmcgYSBwaG90byBhbGJ1bToNCi0gKipFYWNoIHBob3RvKiogPSBhbiBpbWFnZQ0KLSAqKlRoZSBwaG90byBsYWJlbCoqID0gdGhlIG5hbWUgd3JpdHRlbiBiZWxvdyBpdCAoZG9nLCB0cnVjaywgZXRjLikNCi0gQnV0IGFsbCB5b3VyIHBob3RvcyBhcmUgaW4gKipkaWZmZXJlbnQgZm9ybWF0cyBhbmQgYnJpZ2h0bmVzcyoqIGxldmVscy4NCg0KQmVmb3JlIHBhc3RpbmcgdGhlbSBpbjoNCjEuIFlvdSByZXNpemUgYWxsIHRvIHRoZSBzYW1lIGRpbWVuc2lvbnMgKDMyeDMyIHBpeGVscykuDQoyLiBZb3UgYWRqdXN0IGJyaWdodG5lc3MgKG5vcm1hbGl6YXRpb24pLg0KMy4gWW91IGxhYmVsIHRoZW0gKGNsYXNzIG5hbWVzKS4NCjQuIFRoZW4geW91IGxheSB0aGVtIG91dCBpbiB0aGUgYWxidW0gKHBsb3R0aW5nKS4NCg0KVGhpcyBzZXR1cCBtYWtlcyB5b3VyIGFsYnVtIChkYXRhc2V0KSBvcmdhbml6ZWQgYW5kIGVhc3kgdG8gaW50ZXJwcmV0IOKAlCBqdXN0IGxpa2UgaXQgbWFrZXMgdGhlIENOTuKAmXMgam9iIGVhc2llciBkdXJpbmcgdHJhaW5pbmcuDQoNCi0tLQ0KDQoNCg0KSGVyZSBpcyB5b3VyIGNvbXBsZXRlICoqU3R1ZHkgR3VpZGUgZm9yIENOTiBQYXJ0IElJOiBCdWlsZGluZyB0aGUgTW9kZWwqKiBmcm9tIFFUVyA3MzMzLCBpbmNsdWRpbmcgdGV4dGJvb2sgbGlua3MsIHRyYW5zY3JpcHQgc3VtbWFyeSwgbWF0aGVtYXRpY2FsIGV4cGxhbmF0aW9uLCBQeXRob24gY29kZSwga2V5IHRha2Vhd2F5cywgc2VsZi10ZXN0IHF1ZXN0aW9ucywgYW5kIGEgYmVnaW5uZXIgYW5hbG9neS4NCg0KLS0tDQoNCioq8J+TmCBRVFcgNzMzMyDigJMgTW9kdWxlIDEyOiBDTk4gUGFydCBJSSDigJMgQnVpbGRpbmcgdGhlIE1vZGVsKioNCg0KLS0tDQoNCiMjIyDwn5SNIDEuIFdoYXQgQXJlIFdlIERvaW5nIGluIFRoaXMgTW9kdWxlPw0KDQpXZeKAmXJlOg0KLSBCdWlsZGluZyBhIENOTiBhcmNoaXRlY3R1cmUgdXNpbmcgS2VyYXMnIGBTZXF1ZW50aWFsYCBBUEkuDQotIEFkZGluZyBjb252b2x1dGlvbiwgcG9vbGluZywgZmxhdHRlbmluZywgYW5kIGRlbnNlIGxheWVycy4NCi0gQ29tcGlsaW5nIHRoZSBtb2RlbCB3aXRoIGFuIG9wdGltaXplciBhbmQgbG9zcyBmdW5jdGlvbi4NCi0gVHJhaW5pbmcgdGhlIG1vZGVsIGFuZCB2YWxpZGF0aW5nIHBlcmZvcm1hbmNlLg0KLSBNYWtpbmcgcHJlZGljdGlvbnMgYW5kIHZpc3VhbGl6aW5nIHJlc3VsdHMuDQoNCi0tLQ0KDQojIyMg8J+noCAyLiBLZXkgQ29uY2VwdHMNCg0KLSAqKkNvbnYyRCoqOiBBcHBsaWVzIGNvbnZvbHV0aW9uYWwgZmlsdGVycyB0byBleHRyYWN0IHBhdHRlcm5zLg0KLSAqKk1heFBvb2xpbmcyRCoqOiBSZWR1Y2VzIHNwYXRpYWwgZGltZW5zaW9ucywgcmV0YWlucyBpbXBvcnRhbnQgZmVhdHVyZXMuDQotICoqRmxhdHRlbioqOiBDb252ZXJ0cyAyRCBvdXRwdXQgdG8gMUQgZm9yIGRlbnNlIGxheWVyIGlucHV0Lg0KLSAqKkRlbnNlIChmdWxseSBjb25uZWN0ZWQgbGF5ZXIpKio6IEZpbmFsIGRlY2lzaW9uIGxheWVycy4NCi0gKipTb2Z0bWF4Kio6IE91dHB1dCBsYXllciBmb3IgY2xhc3NpZmljYXRpb24gYWNyb3NzIG11bHRpcGxlIGNsYXNzZXMuDQoNCi0tLQ0KDQojIyMg8J+nriAzLiBNYXRoZW1hdGljYWwgUmVwcmVzZW50YXRpb24NCg0KRWFjaCAqKkNvbnYyRCoqIGxheWVyOg0KICANCiAgb3V0cHV0ID0gUmVMVShXICogaW5wdXQgKyBiKQ0KDQpXaGVyZToNCi0gYCpgID0gY29udm9sdXRpb24gb3BlcmF0aW9uDQotIGBXYCA9IGZpbHRlci9rZXJuZWwgd2VpZ2h0cw0KLSBgYmAgPSBiaWFzDQotIGBSZUxVYCA9IG1heCgwLCB4KSwgZWxlbWVudC13aXNlIGFjdGl2YXRpb24NCg0KKipNYXhQb29saW5nMkQqKjoNCiAgDQogIFJlZHVjZXMgbWF0cml4IGJ5IHNlbGVjdGluZyB0aGUgbWF4IHZhbHVlIGluIGVhY2ggd2luZG93IChlLmcuLCAyeDIpLg0KDQoqKkRlbnNlIGxheWVyKio6DQogIA0KICBvdXRwdXQgPSBTb2Z0bWF4KFd4ICsgYikNCg0KLS0tDQoNCiMjIyDwn5K7IDQuIENvbXBsZXRlIFB5dGhvbiBDb2RlDQoNCmBgYHB5dGhvbg0KaW1wb3J0IHRlbnNvcmZsb3cgYXMgdGYNCmZyb20gdGVuc29yZmxvdy5rZXJhcyBpbXBvcnQgZGF0YXNldHMsIGxheWVycywgbW9kZWxzDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgbnVtcHkgYXMgbnANCg0KIyBMb2FkIGFuZCBub3JtYWxpemUgQ0lGQVItMTANCih0cmFpbl9pbWFnZXMsIHRyYWluX2xhYmVscyksICh0ZXN0X2ltYWdlcywgdGVzdF9sYWJlbHMpID0gZGF0YXNldHMuY2lmYXIxMC5sb2FkX2RhdGEoKQ0KdHJhaW5faW1hZ2VzLCB0ZXN0X2ltYWdlcyA9IHRyYWluX2ltYWdlcyAvIDI1NS4wLCB0ZXN0X2ltYWdlcyAvIDI1NS4wDQoNCiMgRGVmaW5lIENOTiBtb2RlbA0KbXlfbW9kZWwgPSBtb2RlbHMuU2VxdWVudGlhbCgpDQpteV9tb2RlbC5hZGQobGF5ZXJzLkNvbnYyRCgzMiwgKDIsIDIpLCBhY3RpdmF0aW9uPSdyZWx1JywgaW5wdXRfc2hhcGU9KDMyLCAzMiwgMykpKQ0KbXlfbW9kZWwuYWRkKGxheWVycy5Db252MkQoNjQsICgzLCAzKSwgYWN0aXZhdGlvbj0ncmVsdScpKQ0KbXlfbW9kZWwuYWRkKGxheWVycy5NYXhQb29saW5nMkQoKDIsIDIpKSkNCm15X21vZGVsLmFkZChsYXllcnMuQ29udjJEKDE3LCAoMiwgMyksIGFjdGl2YXRpb249J3JlbHUnKSkNCm15X21vZGVsLmFkZChsYXllcnMuQ29udjJEKDE0LCAoNCwgNCksIGFjdGl2YXRpb249J3JlbHUnKSkNCm15X21vZGVsLmFkZChsYXllcnMuTWF4UG9vbGluZzJEKCgyLCAyKSkpDQpteV9tb2RlbC5hZGQobGF5ZXJzLkZsYXR0ZW4oKSkNCm15X21vZGVsLmFkZChsYXllcnMuRGVuc2UoMTAwLCBhY3RpdmF0aW9uPSdyZWx1JykpDQpteV9tb2RlbC5hZGQobGF5ZXJzLkRlbnNlKDEwLCBhY3RpdmF0aW9uPSdzb2Z0bWF4JykpDQoNCiMgQ29tcGlsZSB0aGUgbW9kZWwNCm15X21vZGVsLmNvbXBpbGUob3B0aW1pemVyPSdhZGFtJywNCiAgICAgICAgICAgICAgICAgbG9zcz10Zi5rZXJhcy5sb3NzZXMuU3BhcnNlQ2F0ZWdvcmljYWxDcm9zc2VudHJvcHkoZnJvbV9sb2dpdHM9RmFsc2UpLA0KICAgICAgICAgICAgICAgICBtZXRyaWNzPVsnYWNjdXJhY3knXSkNCg0KIyBUcmFpbiB0aGUgbW9kZWwNCmhpc3RvcnkgPSBteV9tb2RlbC5maXQodHJhaW5faW1hZ2VzLCB0cmFpbl9sYWJlbHMsIGVwb2Nocz01LCBiYXRjaF9zaXplPTUwLA0KICAgICAgICAgICAgICAgICAgICAgICB2YWxpZGF0aW9uX2RhdGE9KHRlc3RfaW1hZ2VzLCB0ZXN0X2xhYmVscykpDQoNCiMgUHJlZGljdCBhbmQgdmlzdWFsaXplIHJlc3VsdHMNCnJlc3VsdHMgPSBteV9tb2RlbC5wcmVkaWN0KHRlc3RfaW1hZ2VzKQ0KDQojIFNob3cgcHJlZGljdGlvbnMgdnMgdHJ1ZSBsYWJlbHMNCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTAsMTApKQ0KZm9yIGkgaW4gcmFuZ2UoMjUpOg0KICAgIHBsdC5zdWJwbG90KDUsNSxpKzEpDQogICAgcGx0Lnh0aWNrcyhbXSk7IHBsdC55dGlja3MoW10pOyBwbHQuZ3JpZChGYWxzZSkNCiAgICBwbHQuaW1zaG93KHRlc3RfaW1hZ2VzW2ldLCBjbWFwPXBsdC5jbS5iaW5hcnkpDQogICAgdHJ1ZV9sYWJlbCA9IHRlc3RfbGFiZWxzW2ldWzBdDQogICAgcHJlZF9sYWJlbCA9IG5wLmFyZ21heChyZXN1bHRzW2ldKQ0KICAgIHBsdC54bGFiZWwoZiJUcnVlOiB7dHJ1ZV9sYWJlbH0sIFByZWQ6IHtwcmVkX2xhYmVsfSIpDQpwbHQuc2hvdygpDQpgYGANCg0KLS0tDQoNCiMjIyDwn5OKIDUuIEFjY3VyYWN5IE91dHB1dCBFeGFtcGxlDQoNClRyYWluaW5nIG91dHB1dCAoc2FtcGxlKToNCg0KLSB2YWxfYWNjdXJhY3k6IDAuNjExMiDihpIgMC42MzY4IOKGkiAwLjY3NzYgIA0KLSBFYWNoIGVwb2NoIGltcHJvdmVzIGFjY3VyYWN5Lg0KLSBGaW5hbCBwcmVkaWN0aW9uIHByb2JhYmlsaXRpZXMgaGF2ZSBzaGFwZSAoMTAwMDAsIDEwKSwgb25lIHJvdyBwZXIgdGVzdCBpbWFnZSwgMTAgY29sdW1ucyBwZXIgY2xhc3MuDQoNCi0tLQ0KDQojIyMg4pyFIDYuIEtleSBUYWtlYXdheXMNCg0KLSBDTk5zIGV4Y2VsIGF0IGltYWdlIHJlY29nbml0aW9uIGJlY2F1c2UgdGhleSBsZWFybiAqKnNwYXRpYWwgaGllcmFyY2hpZXMqKiAoZWRnZXMg4oaSIHNoYXBlcyDihpIgb2JqZWN0cykuDQotIFJlTFUgaXMgdGhlIGRlZmF1bHQgYWN0aXZhdGlvbiBmb3IgY29udm9sdXRpb25hbCBsYXllcnMuDQotIFNvZnRtYXggdHVybnMgcmF3IG91dHB1dHMgaW50byBwcm9iYWJpbGl0aWVzIGZvciBtdWx0aS1jbGFzcyBjbGFzc2lmaWNhdGlvbi4NCi0gWW91IG11c3QgKipmbGF0dGVuKiogdGhlIGNvbnZvbHV0aW9uYWwgb3V0cHV0IGJlZm9yZSBjb25uZWN0aW5nIGl0IHRvIGEgZGVuc2UgbGF5ZXIuDQotIE1vZGVsIHBlcmZvcm1hbmNlIGltcHJvdmVzIHdpdGggKipkZWVwZXIgYXJjaGl0ZWN0dXJlcyoqLCAqKmdvb2Qgbm9ybWFsaXphdGlvbioqLCBhbmQgKiptb3JlIGVwb2NocyoqLg0KDQotLS0NCg0KIyMjIOKdkyA3LiBQcmFjdGljZSBRdWVzdGlvbnMNCg0KMS4gV2hhdCBkb2VzIHRoZSBGbGF0dGVuIGxheWVyIGRvPw0KMi4gV2h5IGRvIHdlIHVzZSBNYXhQb29saW5nIGluIGEgQ05OPw0KMy4gV2hhdCBkb2VzIHRoZSBmaW5hbCBEZW5zZSgxMCwgYWN0aXZhdGlvbj0nc29mdG1heCcpIGxheWVyIHJlcHJlc2VudD8NCjQuIFdoeSBpcyB0aGUgQWRhbSBvcHRpbWl6ZXIgY29tbW9ubHkgdXNlZD8NCjUuIFdoYXQgc2hhcGUgZG8gdGhlIENOTiBwcmVkaWN0aW9ucyBoYXZlIGFuZCB3aHk/DQoNCi0tLQ0KDQojIyMg8J+nuCA4LiBMYXltYW4gRXhwbGFuYXRpb24g4oCTIFRoZSAiUGhvdG8gU29ydGluZyBNYWNoaW5lIg0KDQpJbWFnaW5lIGJ1aWxkaW5nIGEgc21hcnQgKipwaG90byBzb3J0ZXIqKjoNCi0gVGhlIGZpcnN0IG1hY2hpbmUgcGFydCBsb29rcyBmb3IgKiplZGdlcyoqICh3aGVyZSBjb2xvcnMgY2hhbmdlIHN1ZGRlbmx5KS4NCi0gVGhlIG5leHQgcGFydCBpZGVudGlmaWVzICoqc2hhcGVzKiogKGNhcnMsIGNhdHMsIGV0Yy4pLg0KLSBGaW5hbGx5LCBhIGRlY2lzaW9uLW1ha2VyIHJlYWRzIGV2ZXJ5dGhpbmcgYW5kIHNheXMsIOKAnFRoaXMgaXMgYSB0cnVjayHigJ0NCg0KRWFjaCAicGFydCIgaXMgYSAqKmxheWVyKiogaW4geW91ciBuZXVyYWwgbmV0d29yazoNCi0gKipDb252b2x1dGlvbiBsYXllcnMqKiA9IGZpbmQgcGF0dGVybnMgKGxpa2UgdGV4dHVyZXMpDQotICoqUG9vbGluZyBsYXllcnMqKiA9IHpvb20gb3V0IHRvIHJlZHVjZSBjbHV0dGVyDQotICoqRGVuc2UgbGF5ZXJzKiogPSBtYWtlIGRlY2lzaW9ucyBiYXNlZCBvbiB3aGF0IHdhcyBmb3VuZA0KLSAqKlNvZnRtYXgqKiA9IGdpdmVzIHRoZSBmaW5hbCB2b3RlIChwZXJjZW50IGNoYW5jZSBpdCdzIGVhY2ggb2JqZWN0KQ0KDQpKdXN0IGxpa2Ugc29ydGluZyB5b3VyIHBob3RvcyBieSBleWUsIENOTnMgZG8gdGhpcyBhdXRvbWF0aWNhbGx54oCUYnV0IHdpdGggbWF0aCBhbmQgdHJhaW5pbmchDQoNCi0tLQ0KDQojIyMg8J+TmiBUZXh0Ym9vayBTdXBwb3J0DQoNCioqX1RoZSBFbGVtZW50cyBvZiBTdGF0aXN0aWNhbCBMZWFybmluZ18qKiAgDQotIENoYXB0ZXIgMTE6IE5ldXJhbCBOZXR3b3JrcyAocGcuIDM5MispICANCmh0dHBzOi8vd2ViLnN0YW5mb3JkLmVkdS9+aGFzdGllL0VsZW1TdGF0TGVhcm4vDQoNCioqX1RlbnNvckZsb3cgVHV0b3JpYWwgKE9mZmljaWFsKV8qKiAgDQpodHRwczovL3d3dy50ZW5zb3JmbG93Lm9yZy90dXRvcmlhbHMvaW1hZ2VzL2Nubg0KDQotLS1nZW5lcmF0ZSB0aGlzIGFzOg0KLSBBICoqSnVweXRlciBOb3RlYm9vayoqIGZvciBpbW1lZGlhdGUgdXNlPw0KLSBBICoqUERGIFN0dWR5IFNoZWV0KiogZm9yIHByaW50aW5nPw0KLSBBbiAqKmludGVyYWN0aXZlIEhUTUwgcHJlc2VudGF0aW9uKiogd2l0aCBzbGlkZXJzPw0KDQpQZXJmZWN0LiBJ4oCZdmUgY29tcGlsZWQgeW91ciBtYXRlcmlhbHMgaW50byBhIHN0cnVjdHVyZWQgc3R1ZHkgZ3VpZGUgdGhhdCBjb3ZlcnMgdGhlIGZvbGxvd2luZyB0b3BpY3M6DQoNCjEuICoqQ29udm9sdXRpb25hbCBMYXllciBGdW5kYW1lbnRhbHMqKg0KMi4gKipUcmFuc2ZlciBMZWFybmluZyBDb25jZXB0cyoqDQozLiAqKkNOTiBQYXJ0IEk6IERhdGFzZXQgU2V0dXAqKg0KNC4gKipDTk4gUGFydCBJSTogTW9kZWwgQnVpbGRpbmcqKg0KNS4gKipUcmFuc2ZlciBMZWFybmluZzogVkdHMTYgKyBDdXN0b20gQ2xhc3NpZmllcioqDQoNCkknbGwgd2FsayB5b3UgdGhyb3VnaCBlYWNoIHNlY3Rpb24gaW4gYW4gZWFzeS10by1mb2xsb3cgZm9ybWF0Og0KDQotLS0NCg0KQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29ya3M6IEJlZ2lubmVyIFN0dWR5IEd1aWRlICANCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNClNlY3Rpb24gMTogKipXaGF0IGlzIGEgQ29udm9sdXRpb24/KiogIA0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKkNvbmNlcHQqKjogIA0KQSBjb252b2x1dGlvbiBpcyB3aGVuIHdlIHNsaWRlIGEgZmlsdGVyIChzbWFsbCBtYXRyaXgpIG92ZXIgZGF0YSAobGlrZSBhbiBpbWFnZSkgdG8gZXh0cmFjdCBtZWFuaW5nZnVsIGZlYXR1cmVzIChsaWtlIGVkZ2VzIG9yIHBhdHRlcm5zKS4gRWFjaCBvdmVybGFwIGlzIG11bHRpcGxpZWQgZWxlbWVudC13aXNlIGFuZCBzdW1tZWQgaW50byBhIG5ldyBvdXRwdXQgdmFsdWUuDQoNCioqTWF0aCoqOiAgDQpJZiAqKkkqKiBpcyBhbiBpbWFnZSBtYXRyaXggYW5kICoqSyoqIGlzIGEgZmlsdGVyOg0KDQogICAgUyhpLGopID0g4oiR4oiRIEsobSxuKSAqIEkoaSttLCBqK24pDQoNClRoaXMgaXMgZG9uZSBmb3IgYWxsIHZhbGlkIHBvc2l0aW9ucyBvZiB0aGUgZmlsdGVyLg0KDQoqKlB5dGhvbiBFeGFtcGxlKio6DQpgYGBweXRob24NCmltcG9ydCBudW1weSBhcyBucA0KZnJvbSBzY2lweS5zaWduYWwgaW1wb3J0IGNvbnZvbHZlMmQNCg0KaW1hZ2UgPSBucC5hcnJheShbWzEsIDIsIDNdLA0KICAgICAgICAgICAgICAgICAgWzQsIDUsIDZdLA0KICAgICAgICAgICAgICAgICAgWzcsIDgsIDldXSkNCg0Ka2VybmVsID0gbnAuYXJyYXkoW1swLCAxXSwNCiAgICAgICAgICAgICAgICAgICBbMSwgMF1dKQ0KDQpyZXN1bHQgPSBjb252b2x2ZTJkKGltYWdlLCBrZXJuZWwsIG1vZGU9J3ZhbGlkJykNCnByaW50KHJlc3VsdCkNCmBgYA0KDQoqKktleSBUZXJtcyoqOg0KLSAqKlBhZGRpbmcqKjogQWRkcyBleHRyYSBib3JkZXJzIHRvIG1haW50YWluIG9yaWdpbmFsIHNpemUuDQotICoqU3RyaWRlKio6IEhvdyBtYW55IHN0ZXBzIHRoZSBmaWx0ZXIgbW92ZXMuDQotICoqRmlsdGVyKio6IEFsc28gY2FsbGVkIGEga2VybmVsLg0KDQoqKlRha2Vhd2F5cyoqOg0KLSBGaWx0ZXJzIGFyZSBsZWFybmVkIGR1cmluZyB0cmFpbmluZy4NCi0gQ29udm9sdXRpb25zIGRldGVjdCBwYXR0ZXJucyBsaWtlIGVkZ2VzLCBjb2xvciwgYW5kIHRleHR1cmVzLg0KDQoqKlJlbGV2YW50IFF1ZXN0aW9ucyoqOg0KLSBXaHkgZG8gd2UgdXNlIHBhZGRpbmc/DQotIFdoYXQgaGFwcGVucyB3aGVuIHlvdSBpbmNyZWFzZSB0aGUgc3RyaWRlPw0KDQoqKkxheW1hbiBBbmFsb2d5Kio6ICANCkltYWdpbmUgdXNpbmcgYSBzbWFsbCB3aW5kb3cgdG8gbG9vayBhdCBkaWZmZXJlbnQgcGFydHMgb2YgYSBiaWcgcGFpbnRpbmcuIEVhY2ggdGltZSwgeW91IHdyaXRlIGRvd24gYSBzdW1tYXJ5IG9mIHdoYXQgeW91IHNlZSAobGlrZSBjb2xvciBpbnRlbnNpdHkgb3Igc2hhcnBuZXNzKS4gWW91IHJlcGVhdCB0aGlzIHVudGlsIHlvdSd2ZSBzY2FubmVkIHRoZSB3aG9sZSBwaWN0dXJlLg0KDQotLS0NCg0KU2VjdGlvbiAyOiAqKlRyYW5zZmVyIExlYXJuaW5nIEV4cGxhaW5lZCoqICANCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKkNvbmNlcHQqKjogIA0KVHJhbnNmZXIgbGVhcm5pbmcgbGV0cyB1cyByZXVzZSBhIHBvd2VyZnVsIHByZXRyYWluZWQgbW9kZWwgKGxpa2UgVkdHMTYpIGFuZCBqdXN0IHRyYWluIHRoZSBmaW5hbCBsYXllcnMgb24gb3VyIHNwZWNpZmljIHRhc2suDQoNCioqV2h5PyoqICANCkFkdmFuY2VkIG1vZGVscyB0YWtlIHdlZWtzIGFuZCBsb3RzIG9mIGNvbXB1dGUuIFRyYW5zZmVyIGxlYXJuaW5nIGxldHMgdXMgdXNlIHByZXRyYWluZWQgZmVhdHVyZXMgYW5kIHRyYWluIG9ubHkgdGhlICJoZWFkIiAoZmluYWwgbGF5ZXJzKS4NCg0KKipEaWFncmFtIFN1bW1hcnkqKjogIA0KTGVmdCA9IHByZXRyYWluZWQgY29udm9sdXRpb24gbGF5ZXJzICANClJpZ2h0ID0gbmV3IGRlbnNlIGxheWVycyAgDQpXZSBrZWVwIHRoZSBsZWZ0IHNpZGUgImZyb3plbiIgYW5kIHRyYWluIG9ubHkgdGhlIHJpZ2h0Lg0KDQoqKk1hdGhlbWF0aWNzKio6DQpJZiAqKkYqKiBpcyB0aGUgZmVhdHVyZSBleHRyYWN0b3IgYW5kICoqVyoqIGFyZSB0cmFpbmFibGUgd2VpZ2h0czoNCg0KICAgIFByZWRpY3Rpb24gPSBzb2Z0bWF4KFcgKiBGKHgpKQ0KDQoqKlB5dGhvbiBFeGFtcGxlKio6DQpgYGBweXRob24NCmJhc2VfbW9kZWwgPSB0Zi5rZXJhcy5hcHBsaWNhdGlvbnMuVkdHMTYoaW5wdXRfc2hhcGU9KDE2MCwxNjAsMyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdG9wPUZhbHNlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzPSdpbWFnZW5ldCcpDQpiYXNlX21vZGVsLnRyYWluYWJsZSA9IEZhbHNlDQpgYGANCg0KKipUYWtlYXdheXMqKjoNCi0gU2F2ZXMgY29tcHV0ZSBhbmQgdGltZS4NCi0gV29ya3Mgd2l0aCBmZXcgc2FtcGxlcy4NCi0gQ2FuIGJlIHVzZWQgYWNyb3NzIGRvbWFpbnMuDQoNCioqUmVsZXZhbnQgUXVlc3Rpb25zKio6DQotIFdoYXQgbGF5ZXJzIHNob3VsZCB5b3UgZnJlZXplPw0KLSBXaHkgaXMgc29mdG1heCB1c2VkIGluIHRoZSBmaW5hbCBsYXllcj8NCg0KKipMYXltYW4gQW5hbG9neSoqOiAgDQpJbWFnaW5lIHlvdeKAmXJlIGxlYXJuaW5nIHRvIGJha2UgY2FrZXMuIEluc3RlYWQgb2YgbGVhcm5pbmcgZXZlcnl0aGluZyBmcm9tIHNjcmF0Y2gsIHlvdSBidXkgYSBjYWtlIG1peCAocHJldHJhaW5lZCBtb2RlbCkgYW5kIGp1c3QgZm9jdXMgb24gZGVjb3JhdGluZyBpdCB0byB5b3VyIHN0eWxlIChjdXN0b20gbGF5ZXJzKS4NCg0KLS0tDQoNClNlY3Rpb24gMzogKipDTk4sIFBhcnQgSTogU2V0IFVwIFlvdXIgRGF0YSoqICANCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCioqQ29kZSBTbmlwcGV0Kio6DQpgYGBweXRob24NCmltcG9ydCB0ZW5zb3JmbG93IGFzIHRmDQpmcm9tIHRlbnNvcmZsb3cua2VyYXMgaW1wb3J0IGRhdGFzZXRzLCBsYXllcnMsIG1vZGVscw0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQoodHJhaW5faW1hZ2VzLCB0cmFpbl9sYWJlbHMpLCAodGVzdF9pbWFnZXMsIHRlc3RfbGFiZWxzKSA9IGRhdGFzZXRzLmNpZmFyMTAubG9hZF9kYXRhKCkNCnRyYWluX2ltYWdlcyA9IHRyYWluX2ltYWdlcyAvIDI1NS4wICAjIG5vcm1hbGl6ZSBwaXhlbCB2YWx1ZXMNCnRlc3RfaW1hZ2VzID0gdGVzdF9pbWFnZXMgLyAyNTUuMA0KYGBgDQoNCioqVmlzdWFsaXphdGlvbioqOg0KYGBgcHl0aG9uDQpjbGFzc19uYW1lcyA9IFsnYWlycGxhbmUnLCAnYXV0b21vYmlsZScsICdiaXJkJywgJ2NhdCcsICdkZWVyJywNCiAgICAgICAgICAgICAgICdkb2cnLCAnZnJvZycsICdob3JzZScsICdzaGlwJywgJ3RydWNrJ10NCg0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMCwxMCkpDQpmb3IgaSBpbiByYW5nZSgyNSk6DQogICAgcGx0LnN1YnBsb3QoNSw1LGkrMSkNCiAgICBwbHQueHRpY2tzKFtdKSwgcGx0Lnl0aWNrcyhbXSkNCiAgICBwbHQuZ3JpZChGYWxzZSkNCiAgICBwbHQuaW1zaG93KHRyYWluX2ltYWdlc1tpXSwgY21hcD1wbHQuY20uYmluYXJ5KQ0KICAgIHBsdC54bGFiZWwoY2xhc3NfbmFtZXNbdHJhaW5fbGFiZWxzW2ldWzBdXSkNCnBsdC5zaG93KCkNCmBgYA0KDQoqKlRha2Vhd2F5cyoqOg0KLSBOb3JtYWxpemluZyBpbWFnZSBkYXRhICgw4oCTMjU1IOKGkiAw4oCTMSkgaXMgY3J1Y2lhbC4NCi0gVmlzdWFsaXppbmcgaGVscHMgdmVyaWZ5IHlvdXIgbGFiZWxzIGFuZCBzdHJ1Y3R1cmUuDQoNCioqUmVsZXZhbnQgUXVlc3Rpb25zKio6DQotIFdoeSBub3JtYWxpemUgcGl4ZWwgdmFsdWVzPw0KLSBXaGF0IGRvZXMgQ0lGQVItMTAgY29udGFpbj8NCg0KLS0tDQoNClNlY3Rpb24gNDogKipDTk4sIFBhcnQgSUk6IEJ1aWxkIHRoZSBNb2RlbCoqICANCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCioqQXJjaGl0ZWN0dXJlIE92ZXJ2aWV3Kio6DQpgYGBweXRob24NCm1vZGVsID0gbW9kZWxzLlNlcXVlbnRpYWwoKQ0KbW9kZWwuYWRkKGxheWVycy5Db252MkQoMzIsICgyLDIpLCBhY3RpdmF0aW9uPSdyZWx1JywgaW5wdXRfc2hhcGU9KDMyLDMyLDMpKSkNCm1vZGVsLmFkZChsYXllcnMuQ29udjJEKDY0LCAoMywzKSwgYWN0aXZhdGlvbj0ncmVsdScpKQ0KbW9kZWwuYWRkKGxheWVycy5NYXhQb29saW5nMkQoKDIsMikpKQ0KDQptb2RlbC5hZGQobGF5ZXJzLkNvbnYyRCgxMjgsICg0LDMpLCBhY3RpdmF0aW9uPSdyZWx1JykpDQptb2RlbC5hZGQobGF5ZXJzLkNvbnYyRCgxMjgsICg0LDQpLCBhY3RpdmF0aW9uPSdyZWx1JykpDQptb2RlbC5hZGQobGF5ZXJzLk1heFBvb2xpbmcyRCgoMiwyKSkpDQoNCm1vZGVsLmFkZChsYXllcnMuRmxhdHRlbigpKQ0KbW9kZWwuYWRkKGxheWVycy5EZW5zZSgxMDAsIGFjdGl2YXRpb249J3JlbHUnKSkNCm1vZGVsLmFkZChsYXllcnMuRGVuc2UoMTAsIGFjdGl2YXRpb249J3NvZnRtYXgnKSkgICMgRm9yIDEwIGNsYXNzZXMNCmBgYA0KDQoqKkNvbXBpbGUgYW5kIFRyYWluKio6DQpgYGBweXRob24NCm1vZGVsLmNvbXBpbGUob3B0aW1pemVyPSdhZGFtJywNCiAgICAgICAgICAgICAgbG9zcz10Zi5rZXJhcy5sb3NzZXMuU3BhcnNlQ2F0ZWdvcmljYWxDcm9zc2VudHJvcHkoZnJvbV9sb2dpdHM9RmFsc2UpLA0KICAgICAgICAgICAgICBtZXRyaWNzPVsnYWNjdXJhY3knXSkNCg0KbW9kZWwuZml0KHRyYWluX2ltYWdlcywgdHJhaW5fbGFiZWxzLCBlcG9jaHM9NSwgYmF0Y2hfc2l6ZT01MCwgdmFsaWRhdGlvbl9kYXRhPSh0ZXN0X2ltYWdlcywgdGVzdF9sYWJlbHMpKQ0KYGBgDQoNCioqVGFrZWF3YXlzKio6DQotIGBDb252MkRgIGxheWVycyBleHRyYWN0IGZlYXR1cmVzLg0KLSBgTWF4UG9vbGluZzJEYCByZWR1Y2VzIHNpemUgYW5kIGtlZXBzIGltcG9ydGFudCBmZWF0dXJlcy4NCi0gYERlbnNlYCBsYXllcnMgbWFrZSBmaW5hbCBwcmVkaWN0aW9ucy4NCg0KKipSZWxldmFudCBRdWVzdGlvbnMqKjoNCi0gV2hhdCBkb2VzIHRoZSBmbGF0dGVuIGxheWVyIGRvPw0KLSBXaHkgaXMgc29mdG1heCBnb29kIGZvciBtdWx0aWNsYXNzIGNsYXNzaWZpY2F0aW9uPw0KDQoqKkxheW1hbiBBbmFsb2d5Kio6ICANCkltYWdpbmUgeW91ciBtb2RlbCBpcyBsaWtlIGEgZGV0ZWN0aXZlLiBUaGUgY29udm9sdXRpb25hbCBsYXllcnMgYXJlIGxpa2UgdGhlIGRldGVjdGl2ZSBnYXRoZXJpbmcgY2x1ZXMgKGVkZ2VzLCBjb2xvcnMpLCB0aGUgZmxhdHRlbiBsYXllciBvcmdhbml6ZXMgYWxsIHRoZSBjbHVlcyBpbiBhIHNpbmdsZSBmaWxlLCBhbmQgdGhlIGRlbnNlIGxheWVycyB1c2UgdGhhdCBmaWxlIHRvIGZpZ3VyZSBvdXQ6IOKAnElzIHRoaXMgYSBjYXQgb3IgYSB0cnVjaz/igJ0NCg0KLS0tDQoNClNlY3Rpb24gNTogKipUcmFuc2ZlciBMZWFybmluZyBpbiBQcmFjdGljZSAoVkdHMTYgKyBDYXRzIHZzIERvZ3MpKiogIA0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCioqU3RlcHMgU3VtbWFyeSoqOg0KMS4gTG9hZCBkYXRhc2V0IHdpdGggYGltYWdlX2RhdGFzZXRfZnJvbV9kaXJlY3RvcnlgDQoyLiBOb3JtYWxpemUgd2l0aCBgUmVzY2FsaW5nYA0KMy4gTG9hZCBWR0cxNiBiYXNlIChleGNsdWRlIHRvcCkNCjQuIEZyZWV6ZSBiYXNlIG1vZGVsDQo1LiBBZGQgZ2xvYmFsIGF2ZXJhZ2UgcG9vbGluZywgZHJvcG91dCwgYW5kIGRlbnNlIG91dHB1dA0KNi4gQ29tcGlsZSBhbmQgdHJhaW4NCg0KKipLZXkgQ29kZSoqOg0KYGBgcHl0aG9uDQpiYXNlX21vZGVsID0gdGYua2VyYXMuYXBwbGljYXRpb25zLlZHRzE2KGluY2x1ZGVfdG9wPUZhbHNlLCB3ZWlnaHRzPSdpbWFnZW5ldCcsIGlucHV0X3NoYXBlPSgxNjAsMTYwLDMpKQ0KYmFzZV9tb2RlbC50cmFpbmFibGUgPSBGYWxzZQ0KDQptb2RlbCA9IHRmLmtlcmFzLlNlcXVlbnRpYWwoWw0KICAgIHRmLmtlcmFzLmxheWVycy5SZXNjYWxpbmcoMS4vMTI3LjUsIG9mZnNldD0tMSksDQogICAgYmFzZV9tb2RlbCwNCiAgICB0Zi5rZXJhcy5sYXllcnMuR2xvYmFsQXZlcmFnZVBvb2xpbmcyRCgpLA0KICAgIHRmLmtlcmFzLmxheWVycy5Ecm9wb3V0KDAuMiksDQogICAgdGYua2VyYXMubGF5ZXJzLkRlbnNlKDEpICAjIGJpbmFyeSBjbGFzc2lmaWNhdGlvbg0KXSkNCg0KbW9kZWwuY29tcGlsZShvcHRpbWl6ZXI9dGYua2VyYXMub3B0aW1pemVycy5BZGFtKGxlYXJuaW5nX3JhdGU9MC4wMDAxKSwNCiAgICAgICAgICAgICAgbG9zcz10Zi5rZXJhcy5sb3NzZXMuQmluYXJ5Q3Jvc3NlbnRyb3B5KGZyb21fbG9naXRzPVRydWUpLA0KICAgICAgICAgICAgICBtZXRyaWNzPVsnYWNjdXJhY3knXSkNCg0KbW9kZWwuZml0KHRyYWluX2RhdGFzZXQsIGVwb2Nocz0xMCwgdmFsaWRhdGlvbl9kYXRhPXZhbGlkYXRpb25fZGF0YXNldCkNCmBgYA0KDQoqKlRha2Vhd2F5cyoqOg0KLSBWR0cxNiBoYXMgMTQgbWlsbGlvbisgcGFyYW1ldGVyczsgd2Ugb25seSB0cmFpbiBhIGZldyBodW5kcmVkLg0KLSBGYXN0IHRyYWluaW5nLCBldmVuIG9uIHNtYWxsIGRhdGFzZXRzLg0KLSBBY2N1cmFjeSBpbXByb3ZlcyBpbiBqdXN0IGEgZmV3IGVwb2Nocy4NCg0KKipSZWxldmFudCBRdWVzdGlvbnMqKjoNCi0gV2hhdCBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGNhdGVnb3JpY2FsIGFuZCBiaW5hcnkgY3Jvc3NlbnRyb3B5Pw0KLSBXaGF0IGRvZXMgZHJvcG91dCBkbz8NCg0KKipMYXltYW4gQW5hbG9neSoqOiAgDQpUaGluayBvZiB0aGUgVkdHMTYgbW9kZWwgYXMgYW4gZXhwZXJ0IGFydCBjcml0aWMgd2hvIGhhcyBzdHVkaWVkIG1pbGxpb25zIG9mIHBhaW50aW5ncy4gWW914oCZcmUgdHJhaW5pbmcgYSBzbWFsbCBhc3Npc3RhbnQgdG8gb25seSB0ZWxsIGFwYXJ0IGNhdHMgYW5kIGRvZ3MuIFlvdSBkb27igJl0IHJldHJhaW4gdGhlIGV4cGVydCwganVzdCB1c2UgdGhlaXIgb3BpbmlvbnMgKGZlYXR1cmVzKSBhbmQgdGVhY2ggeW91ciBhc3Npc3RhbnQgaG93IHRvIGludGVycHJldCB0aGVtIGZvciBhIHZlcnkgc3BlY2lmaWMgdGFzay4NCg0KLS0tDQoNCkhlcmXigJlzIGEgZGlzdGlsbGVkIHN5bnRoZXNpcyBmcm9tIGV2ZXJ5dGhpbmcgd2XigJl2ZSBkb25lIHRvZGF54oCUaW5jbHVkaW5nIHRoZSBzY3JlZW5zaG90cywgY2xhc3MgdHJhbnNjcmlwdHMsIGFuZCB0aGUgY29uY2VwdHMgaW4geW91ciBQREYgbWF0ZXJpYWxz4oCUaW50byAqKnRocmVlIGdlbmVyYWwgdGFrZWF3YXlzKiogYW5kICoqdGhyZWUgdGhvdWdodGZ1bCBxdWVzdGlvbnMgZm9yIERyLiBTbGF0ZXIqKjoNCg0KLS0tDQoNCioqVGhyZWUgR2VuZXJhbCBUYWtlYXdheXMqKiAgDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KKioxLiBUcmFuc2ZlciBMZWFybmluZyBpcyBhIEdhbWUtQ2hhbmdlciBmb3IgUmVzb3VyY2UtTGltaXRlZCBUcmFpbmluZyoqICANCkJ5IGZyZWV6aW5nIHRoZSBjb252b2x1dGlvbmFsIGJhc2Ugb2YgcHJldHJhaW5lZCBtb2RlbHMgKGxpa2UgVkdHMTYpIGFuZCB0cmFpbmluZyBvbmx5IHRoZSBjbGFzc2lmaWVyIGhlYWQsIHdlIHVubG9jayB0aGUgcG93ZXIgb2YgaGlnaGx5IGNvbXBsZXggbW9kZWxzIHdpdGhvdXQgbmVlZGluZyBtYXNzaXZlIGNvbXB1dGUuIFRoaXMgYXBwcm9hY2ggc2lnbmlmaWNhbnRseSBhY2NlbGVyYXRlcyB0cmFpbmluZyB3aGlsZSBtYWludGFpbmluZyBzdHJvbmcgcGVyZm9ybWFuY2UsIGV2ZW4gd2l0aCBsaW1pdGVkIGRhdGEuDQoNCioqMi4gQ05OcyBSZWx5IEhlYXZpbHkgb24gUHJvcGVyIERhdGEgSGFuZGxpbmcgYW5kIEFyY2hpdGVjdHVyYWwgQmFsYW5jZSoqICANClBlcmZvcm1hbmNlIGlzIHN0cm9uZ2x5IGluZmx1ZW5jZWQgYnkgc2VlbWluZ2x5IHNpbXBsZSBwcmVwcm9jZXNzaW5nIHN0ZXBzIChlLmcuLCBub3JtYWxpemF0aW9uIGJ5IGRpdmlkaW5nIGJ5IDI1NSwgcmVzaXppbmcgaW1hZ2VzLCBwcm9wZXIgbGFiZWwgZW5jb2RpbmcpLiBBZGRpdGlvbmFsbHksIHRoZSBkZXB0aCBvZiBjb252b2x1dGlvbiBhbmQgcG9vbGluZyBsYXllcnMgbXVzdCBiZSBiYWxhbmNlZCB3aXRoIG1lbW9yeSBjb25zdHJhaW50cyBhbmQgdGFzayBjb21wbGV4aXR5Lg0KDQoqKjMuIFRoZSBTaGlmdCBmcm9tIERlbnNlIHRvIENvbnZvbHV0aW9uYWwgTmV0d29ya3MgUmVmbGVjdHMgYSBDaGFuZ2UgaW4gSG93IE1vZGVscyAiU2VlIiBEYXRhKiogIA0KRGVuc2UgbmV0d29ya3MgdHJlYXQgaW5wdXQgYXMgZmxhdCB2ZWN0b3JzOyBDTk5zIHRyZWF0IGl0IHNwYXRpYWxseS4gVGhpcyBpcyBjcnVjaWFsIGZvciBpbWFnZXMsIHdoZXJlIHN0cnVjdHVyZSBhbmQgbG9jYWxpdHkgbWF0dGVyLiBDTk5zIGxlYXJuIGhpZXJhcmNoaWNhbCBmZWF0dXJlc+KAlGZyb20gZWRnZXMgaW4gZWFybHkgbGF5ZXJzIHRvIGNvbXBsZXggc2hhcGVzIGluIGRlZXBlciBvbmVz4oCUbWltaWNraW5nIGhvdyB2aXN1YWwgY29ydGV4IG5ldXJvbnMgb3BlcmF0ZS4NCg0KLS0tDQoNCioqVGhyZWUgVGhvdWdodC1Qcm92b2tpbmcgUXVlc3Rpb25zIGZvciBEci4gU2xhdGVyKiogIA0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQoqKjEuIEdpdmVuIHRoZSBiZW5lZml0cyBvZiB0cmFuc2ZlciBsZWFybmluZywgaG93IGRvIHdlIGFzc2VzcyB3aGVuIGl0J3MgbW9yZSBhcHByb3ByaWF0ZSB0byBmaW5lLXR1bmUgdmVyc3VzIGZyZWV6ZSBsYXllcnMsIGVzcGVjaWFsbHkgd2hlbiB3b3JraW5nIHdpdGggc2NpZW50aWZpYyBvciBuaWNoZSBkYXRhc2V0cyBsaWtlIG1lZGljYWwgc2NhbnMgb3Igc2F0ZWxsaXRlIGltYWdlcnk/KioNCg0KKioyLiBJbiByZWFsLXdvcmxkIGRlcGxveW1lbnQsIGhvdyBkbyB3ZSBtaXRpZ2F0ZSB0aGUgcmlza3Mgb2YgQ05OIG1pc2NsYXNzaWZpY2F0aW9uIChlLmcuLCBmcm9nIHZzLiBkb2cpIGluIGhpZ2gtc3Rha2VzIGFwcGxpY2F0aW9ucyBzdWNoIGFzIGF1dG9ub21vdXMgdmVoaWNsZXMsIG1pbGl0YXJ5IHN1cnZlaWxsYW5jZSwgb3IgaGVhbHRoY2FyZSBkaWFnbm9zdGljcz8qKg0KDQoqKjMuIEZyb20gYSBwZWRhZ29naWNhbCBvciByZXNlYXJjaCBzdGFuZHBvaW50LCB3aGVyZSBkbyB5b3Ugc2VlIHRoZSBiaWdnZXN0IGNvbmNlcHR1YWwgZ2FwcyBpbiBzdHVkZW50IHVuZGVyc3RhbmRpbmcgd2hlbiB0cmFuc2l0aW9uaW5nIGZyb20gZGVuc2UgdG8gY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3PigJRhbmQgaG93IGNhbiB3ZSBiZXR0ZXIgYnJpZGdlIHRoYXQgbGVhcCwgcGVyaGFwcyB3aXRoIHZpc3VhbCB0b29scyBvciBoYW5kcy1vbiBkZW1vcz8qKg0KDQotLS0NCg0KDQpgYGBsYXRleA0KXHNlY3Rpb24qe0dlbmVyYWwgVGFrZWF3YXlzIGZyb20gQ05OcyBhbmQgVHJhbnNmZXIgTGVhcm5pbmd9DQoNClxiZWdpbntlbnVtZXJhdGV9DQogICAgXGl0ZW0gXHRleHRiZntUcmFuc2ZlciBMZWFybmluZyBpcyBhIEdhbWUtQ2hhbmdlciBmb3IgUmVzb3VyY2UtTGltaXRlZCBUcmFpbmluZ30gXFwNCiAgICBGcmVlemluZyB0aGUgY29udm9sdXRpb25hbCBiYXNlIG9mIHByZXRyYWluZWQgbW9kZWxzIGxpa2UgVkdHMTYgYW5kIHRyYWluaW5nIG9ubHkgdGhlIGRlbnNlIGNsYXNzaWZpZXIgaGVhZCBhbGxvd3MgdXMgdG8gbGV2ZXJhZ2UgcG93ZXJmdWwgbW9kZWxzIHdpdGhvdXQgdGhlIG5lZWQgZm9yIG1hc3NpdmUgY29tcHV0ZSByZXNvdXJjZXMuIFRoaXMgZ3JlYXRseSByZWR1Y2VzIHRyYWluaW5nIHRpbWUgd2hpbGUgbWFpbnRhaW5pbmcgc3Ryb25nIHBlcmZvcm1hbmNlLCBlc3BlY2lhbGx5IHdoZW4gd29ya2luZyB3aXRoIHNtYWxsZXIgZGF0YXNldHMuDQoNCiAgICBcaXRlbSBcdGV4dGJme0NOTnMgUmVseSBIZWF2aWx5IG9uIFByb3BlciBEYXRhIEhhbmRsaW5nIGFuZCBBcmNoaXRlY3R1cmFsIEJhbGFuY2V9IFxcDQogICAgUGVyZm9ybWFuY2UgaXMgc2lnbmlmaWNhbnRseSBpbmZsdWVuY2VkIGJ5IHByZXByb2Nlc3Npbmcgc3RlcHMgc3VjaCBhcyBub3JtYWxpemF0aW9uIChlLmcuLCBkaXZpZGluZyBwaXhlbCB2YWx1ZXMgYnkgMjU1KSwgaW1hZ2UgcmVzaXppbmcsIGFuZCBsYWJlbCBmb3JtYXR0aW5nLiBUaGUgYXJjaGl0ZWN0dXJl4oCZcyBkZXB0aCAobnVtYmVyIGFuZCBzaXplIG9mIGNvbnZvbHV0aW9uIGFuZCBwb29saW5nIGxheWVycykgbXVzdCBiZSB0dW5lZCBpbiByZWxhdGlvbiB0byB0aGUgY29tcGxleGl0eSBvZiB0aGUgdGFzayBhbmQgYXZhaWxhYmxlIG1lbW9yeS4NCg0KICAgIFxpdGVtIFx0ZXh0YmZ7VGhlIFNoaWZ0IGZyb20gRGVuc2UgdG8gQ29udm9sdXRpb25hbCBOZXR3b3JrcyBSZWZsZWN0cyBhIENoYW5nZSBpbiBIb3cgTW9kZWxzICJTZWUiIERhdGF9IFxcDQogICAgRGVuc2UgbmV0d29ya3MgdHJlYXQgZGF0YSBhcyBmbGF0IHZlY3RvcnM7IENOTnMgcHJvY2VzcyBpdCBzcGF0aWFsbHkuIFRoaXMgaXMgZXNzZW50aWFsIGZvciBpbWFnZSB0YXNrcyB3aGVyZSBzcGF0aWFsIGxvY2FsaXR5IG1hdHRlcnMuIENOTnMgYnVpbGQgaGllcmFyY2hpY2FsIGZlYXR1cmUgbWFwc+KAlGZyb20gc2ltcGxlIGVkZ2VzIHRvIGNvbXBsZXggb2JqZWN0c+KAlHNpbWlsYXIgdG8gdGhlIGh1bWFuIHZpc3VhbCBzeXN0ZW0uDQpcZW5ke2VudW1lcmF0ZX0NCg0KXHZzcGFjZXswLjVjbX0NClxzZWN0aW9uKntUaG91Z2h0LVByb3Zva2luZyBRdWVzdGlvbnMgZm9yIERyLiBTbGF0ZXJ9DQoNClxiZWdpbntlbnVtZXJhdGV9DQogICAgXGl0ZW0gXHRleHRiZntIb3cgc2hvdWxkIHdlIGRlY2lkZSB3aGV0aGVyIHRvIGZyZWV6ZSBvciBmaW5lLXR1bmUgcHJldHJhaW5lZCBjb252b2x1dGlvbmFsIGxheWVycyB3aGVuIHdvcmtpbmcgd2l0aCBkb21haW4tc3BlY2lmaWMgZGF0YXNldHMgKGUuZy4sIG1lZGljYWwgaW1hZ2luZywgc2F0ZWxsaXRlIGRhdGEsIG9yIGVudmlyb25tZW50YWwgYW5hbHlzaXMpP30NCg0KICAgIFxpdGVtIFx0ZXh0YmZ7V2hhdCBzYWZlZ3VhcmRzIG9yIG1vZGVsIGV2YWx1YXRpb24gc3RyYXRlZ2llcyB3b3VsZCB5b3UgcmVjb21tZW5kIHdoZW4gZGVwbG95aW5nIENOTnMgaW4gaGlnaC1zdGFrZXMgZW52aXJvbm1lbnRzIChlLmcuLCBkZWZlbnNlLCB0cmFuc3BvcnRhdGlvbiwgaGVhbHRoY2FyZSkgdG8gcmVkdWNlIHJpc2tzIGZyb20gbWlzY2xhc3NpZmljYXRpb25zIGxpa2UgbWlzdGFraW5nIGEgZnJvZyBmb3IgYSBkb2c/fQ0KDQogICAgXGl0ZW0gXHRleHRiZntGcm9tIHlvdXIgZXhwZXJpZW5jZSB0ZWFjaGluZyBkZWVwIGxlYXJuaW5nLCB3aGVyZSBkbyBzdHVkZW50cyB0eXBpY2FsbHkgc3RydWdnbGUgbW9zdCB3aGVuIHRyYW5zaXRpb25pbmcgZnJvbSBkZW5zZSB0byBjb252b2x1dGlvbmFsIG5ldHdvcmtzLCBhbmQgaG93IGNvdWxkIHdlIGJldHRlciBzdXBwb3J0IHRoYXQgc2hpZnQgdXNpbmcgdmlzdWFsIG9yIGludGVyYWN0aXZlIHJlc291cmNlcz99DQpcZW5ke2VudW1lcmF0ZX0NCmBgYA0KDQoNCg0KSmVzc2ljYSBNY1BoYXVsDQo3MzMzIOKAkyBRVFcgTW9kdWxlIDEyDQpQcmVzZXNzaW9uIFdlZWsgMTINCl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18NClRocmVlIEdlbmVyYWwgVGFrZWF3YXlzDQoxLiBUcmFuc2ZlciBMZWFybmluZyBpcyBhIEdhbWUtQ2hhbmdlciBmb3IgUmVzb3VyY2UtTGltaXRlZCBUcmFpbmluZw0KQnkgZnJlZXppbmcgdGhlIGNvbnZvbHV0aW9uYWwgYmFzZSBvZiBwcmV0cmFpbmVkIG1vZGVscyAobGlrZSBWR0cxNikgYW5kIHRyYWluaW5nIG9ubHkgdGhlIGNsYXNzaWZpZXIgaGVhZCwgd2UgdW5sb2NrIHRoZSBwb3dlciBvZiBoaWdobHkgY29tcGxleCBtb2RlbHMgd2l0aG91dCBuZWVkaW5nIG1hc3NpdmUgY29tcHV0ZS4gVGhpcyBhcHByb2FjaCBzaWduaWZpY2FudGx5IGFjY2VsZXJhdGVzIHRyYWluaW5nIHdoaWxlIG1haW50YWluaW5nIHN0cm9uZyBwZXJmb3JtYW5jZSwgZXZlbiB3aXRoIGxpbWl0ZWQgZGF0YS4NCjIuIENOTnMgUmVseSBIZWF2aWx5IG9uIFByb3BlciBEYXRhIEhhbmRsaW5nIGFuZCBBcmNoaXRlY3R1cmFsIEJhbGFuY2UNClBlcmZvcm1hbmNlIGlzIHN0cm9uZ2x5IGluZmx1ZW5jZWQgYnkgc2VlbWluZ2x5IHNpbXBsZSBwcmVwcm9jZXNzaW5nIHN0ZXBzIChlLmcuLCBub3JtYWxpemF0aW9uIGJ5IGRpdmlkaW5nIGJ5IDI1NSwgcmVzaXppbmcgaW1hZ2VzLCBwcm9wZXIgbGFiZWwgZW5jb2RpbmcpLiBBZGRpdGlvbmFsbHksIHRoZSBkZXB0aCBvZiBjb252b2x1dGlvbiBhbmQgcG9vbGluZyBsYXllcnMgbXVzdCBiZSBiYWxhbmNlZCB3aXRoIG1lbW9yeSBjb25zdHJhaW50cyBhbmQgdGFzayBjb21wbGV4aXR5Lg0KMy4gVGhlIFNoaWZ0IGZyb20gRGVuc2UgdG8gQ29udm9sdXRpb25hbCBOZXR3b3JrcyBSZWZsZWN0cyBhIENoYW5nZSBpbiBIb3cgTW9kZWxzICJTZWUiIERhdGENCkRlbnNlIG5ldHdvcmtzIHRyZWF0IGlucHV0IGFzIGZsYXQgdmVjdG9yczsgQ05OcyB0cmVhdCBpdCBzcGF0aWFsbHkuIFRoaXMgaXMgY3J1Y2lhbCBmb3IgaW1hZ2VzLCB3aGVyZSBzdHJ1Y3R1cmUgYW5kIGxvY2FsaXR5IG1hdHRlci4gQ05OcyBsZWFybiBoaWVyYXJjaGljYWwgZmVhdHVyZXPigJRmcm9tIGVkZ2VzIGluIGVhcmx5IGxheWVycyB0byBjb21wbGV4IHNoYXBlcyBpbiBkZWVwZXIgb25lc+KAlG1pbWlja2luZyBob3cgdmlzdWFsIGNvcnRleCBuZXVyb25zIG9wZXJhdGUuDQpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQozIFF1ZXN0aW9zbg0KMS4gR2l2ZW4gdGhlIGJlbmVmaXRzIG9mIHRyYW5zZmVyIGxlYXJuaW5nLCBob3cgZG8gd2UgYXNzZXNzIHdoZW4gaXQncyBtb3JlIGFwcHJvcHJpYXRlIHRvIGZpbmUtdHVuZSB2ZXJzdXMgZnJlZXplIGxheWVycywgZXNwZWNpYWxseSB3aGVuIHdvcmtpbmcgd2l0aCBzY2llbnRpZmljIG9yIG5pY2hlIGRhdGFzZXRzIGxpa2UgbWVkaWNhbCBzY2FucyBvciBzYXRlbGxpdGUgaW1hZ2VyeT8NCjIuIEluIHJlYWwtd29ybGQgZGVwbG95bWVudCwgaG93IGRvIHdlIG1pdGlnYXRlIHRoZSByaXNrcyBvZiBDTk4gbWlzY2xhc3NpZmljYXRpb24gKGUuZy4sIGZyb2cgdnMuIGRvZykgaW4gaGlnaC1zdGFrZXMgYXBwbGljYXRpb25zIHN1Y2ggYXMgYXV0b25vbW91cyB2ZWhpY2xlcywgbWlsaXRhcnkgc3VydmVpbGxhbmNlLCBvciBoZWFsdGhjYXJlIGRpYWdub3N0aWNzPw0KMy4gRnJvbSBhIHBlZGFnb2dpY2FsIG9yIHJlc2VhcmNoIHN0YW5kcG9pbnQsIHdoZXJlIGRvIHlvdSBzZWUgdGhlIGJpZ2dlc3QgY29uY2VwdHVhbCBnYXBzIGluIHN0dWRlbnQgdW5kZXJzdGFuZGluZyB3aGVuIHRyYW5zaXRpb25pbmcgZnJvbSBkZW5zZSB0byBjb252b2x1dGlvbmFsIG5ldXJhbCBuZXR3b3Jrc+KAlGFuZCBob3cgY2FuIHdlIGJldHRlciBicmlkZ2UgdGhhdCBsZWFwLCBwZXJoYXBzIHdpdGggdmlzdWFsIHRvb2xzIG9yIGhhbmRzLW9uIGRlbW9zPw0KX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXw0KDQo=