Study Guide: Support Vector Machines (SVMs) – Module 9

1. Introduction to Support Vector Machines

Support Vector Machines (SVMs) are powerful supervised learning algorithms primarily used for classification tasks. The goal of SVM is to find an optimal separating hyperplane that best divides different classes of data points.

Key Concepts:

  • Linear Separability: When a dataset can be perfectly divided using a straight line (in 2D) or a hyperplane (in higher dimensions).
  • Hyperplane: A decision boundary separating different classes.
  • Margin: The distance between the closest data points (support vectors) and the hyperplane.
  • Support Vectors: Data points that lie closest to the hyperplane and influence its orientation.
  • Maximal Margin Classifier: The hyperplane that maximizes the margin.
  • Soft Margin: Introduces slack variables to allow misclassification in cases where perfect separation is impossible.

2. Vector Algebra and Hyperplanes

Understanding vector algebra is essential for SVMs.

Hyperplane Equation:

A hyperplane in \(n\)-dimensional space is defined as: \[ \beta_0 + \beta^T x = 0 \] Where: - \(\beta\) is the normal vector (perpendicular to the hyperplane). - \(x\) represents the feature vectors. - \(\beta_0\) is the bias term.

For two classes labeled \(y_i \in \{-1, 1\}\), the SVM decision boundary follows: \[ y_i (\beta^T x_i + \beta_0) \geq M \] where \(M\) is the margin.

Finding the Optimal Hyperplane

The objective is to maximize the margin, which translates to minimizing \(||\beta||\) subject to: \[ y_i (\beta^T x_i + \beta_0) \geq 1 \] This is a convex optimization problem solved using Lagrange multipliers.

3. Handling Non-Separable Data

SVM introduces slack variables \(\xi_i\) to allow some misclassification: \[ y_i (\beta^T x_i + \beta_0) \geq 1 - \xi_i \] where \(\xi_i \geq 0\) represents the margin violation amount.

New optimization objective: \[ \min_{\beta, \beta_0, \xi} \frac{1}{2} ||\beta||^2 + C \sum \xi_i \] where \(C\) is a regularization parameter controlling the trade-off between margin maximization and classification errors.

4. Kernel Trick for Non-Linear Data

If a dataset is not linearly separable, we map it to a higher-dimensional space using kernels: \[ K(x_i, x_j) = \phi(x_i)^T \phi(x_j) \]

Common Kernels:

  • Linear Kernel: \(K(x_i, x_j) = x_i^T x_j\)
  • Polynomial Kernel: \(K(x_i, x_j) = (x_i^T x_j + c)^d\)
  • Radial Basis Function (RBF) Kernel: \(K(x_i, x_j) = \exp(-\gamma ||x_i - x_j||^2)\)

The kernel trick enables SVM to operate in higher-dimensional spaces without explicit transformations.

5. Mathematical Formulation of SVM

Hard Margin SVM (Linearly Separable)

\[ \min_{\beta, \beta_0} \frac{1}{2} ||\beta||^2 \] subject to: \[ y_i (\beta^T x_i + \beta_0) \geq 1 \]

Soft Margin SVM (Allowing Misclassification)

\[ \min_{\beta, \beta_0, \xi} \frac{1}{2} ||\beta||^2 + C \sum \xi_i \] subject to: \[ y_i (\beta^T x_i + \beta_0) \geq 1 - \xi_i, \quad \xi_i \geq 0 \]

6. Implementing SVM in Python

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# Generate synthetic dataset
X, y = make_classification(n_samples=100, n_features=2, n_classes=2, n_redundant=0, random_state=42)
y = np.where(y == 0, -1, 1)  # Convert labels to {-1, 1}

# Split into train-test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Train SVM classifier
svm = SVC(kernel='linear', C=1.0)
svm.fit(X_train, y_train)

# Predictions
y_pred = svm.predict(X_test)
print(f'Accuracy: {accuracy_score(y_test, y_pred):.2f}')

# Plot decision boundary
def plot_svm_decision_boundary(X, y, model):
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='k')
    
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    
    xx, yy = np.meshgrid(np.linspace(xlim[0], xlim[1], 50),
                         np.linspace(ylim[0], ylim[1], 50))
    
    Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    ax.contour(xx, yy, Z, colors='k', levels=[-1, 0, 1], linestyles=['--', '-', '--'])
    
    plt.title("SVM Decision Boundary")
    plt.show()

plot_svm_decision_boundary(X_train, y_train, svm)

7. Key Takeaways

  • SVM finds the optimal hyperplane that maximizes margin.
  • Slack variables \(\xi\) allow for soft-margin classification.
  • Kernels enable non-linear classification by transforming data into higher dimensions.
  • SVM is effective for both linear and non-linear classification problems.
  • The regularization parameter \(C\) controls the trade-off between margin maximization and misclassification tolerance.

8. Practical Considerations

  • Choose an appropriate kernel based on dataset complexity.
  • Hyperparameter tuning (\(C\), \(\gamma\) for RBF kernel) is crucial and can be optimized using grid search and cross-validation.
  • SVMs scale poorly with very large datasets (due to quadratic complexity); consider alternative classifiers like Random Forests or Deep Learning for big data.

Additional Resources

Study Guide: Support Vector Machines (SVMs)

1. Introduction to Support Vector Machines (SVMs)

Support Vector Machines (SVMs) are supervised learning models used for classification and regression tasks. They work by identifying the optimal hyperplane that best separates classes in a given dataset.

Mathematical Representation:

Given a dataset \((x_i, y_i)\) where \(x_i\) represents the feature vectors and \(y_i\) the class labels (\(\pm1\)), the decision boundary is defined as: \[ w \cdot x + b = 0 \] where \(w\) is the weight vector and \(b\) is the bias term.

The margin is given by: \[ \frac{2}{\|w\|} \] which should be maximized while ensuring correct classification.

Python Implementation:

from sklearn.svm import SVC
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

X, y = make_classification(n_samples=100, n_features=2, n_classes=2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = SVC(kernel='linear', C=1.0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

2. Margins and Hinge Loss

The hinge loss function is defined as: \[ L(y, f(x)) = \max(0, 1 - y f(x)) \] This loss penalizes misclassified points and points close to the margin, ensuring better generalization.

Comparison with Other Loss Functions:

  • Logistic Loss (Binomial Deviance): \(\log(1 + e^{-yf(x)})\)
  • Squared Error Loss: \((y - f(x))^2\)
  • Huberized Square Hinge Loss: Hybrid between hinge loss and squared loss【104:0†ESLII_print12_toc.pdf】.

3. Kernel Trick

For non-linearly separable data, SVMs use the kernel trick to transform input space into a higher-dimensional feature space where a linear decision boundary can be applied.

Common Kernels:

  • Linear Kernel: \(K(x, x') = x \cdot x'\)
  • Polynomial Kernel: \(K(x, x') = (x \cdot x' + c)^d\)
  • Radial Basis Function (RBF) Kernel: \(K(x, x') = \exp(-\gamma \|x - x'\|^2)\)
  • Sigmoid Kernel: \(K(x, x') = \tanh(\alpha x \cdot x' + c)\)
# Example of using an RBF kernel
model = SVC(kernel='rbf', gamma=0.1, C=1.0)
model.fit(X_train, y_train)

4. Scaling and Performance

SVMs have an O(n^2) complexity, making them inefficient for large datasets. Performance optimization techniques include: - Using LinearSVC (which implements SVM as an optimization problem) - Reducing features via PCA - Using approximate methods such as SGDClassifier

from sklearn.svm import LinearSVC
model = LinearSVC()
model.fit(X_train, y_train)

5. Comparison with Naive Bayes

While SVMs optimize a margin, Naive Bayes estimates class probabilities. - SVMs perform better in high-dimensional spaces. - Naive Bayes is computationally faster and works well when feature independence assumptions hold.

Key Takeaways:

  1. SVMs find the optimal hyperplane by maximizing the margin.
  2. The kernel trick allows SVMs to handle non-linearly separable data.
  3. Hinge loss is used for classification to penalize misclassified points.
  4. Scaling issues limit SVMs on large datasets due to quadratic complexity.
  5. SVMs outperform Naive Bayes in high-dimensional spaces, but are computationally expensive.

Discussion Questions:

  1. How does the choice of the kernel affect the performance of an SVM model?
  2. In what scenarios is SVM preferable over logistic regression?
  3. How can we handle imbalanced data while using SVMs?
  4. Why does increasing the regularization parameter C reduce the margin?
  5. What strategies can improve SVM performance on large datasets?
LS0tDQp0aXRsZTogIlFUVyAtIDczMzMgTW9kdWxlIDkgLSBWZWN0b3IgTWFjaGlvbmVzIC0gY29uZGVuc2VkIg0KYXV0aG9yOiAiSmVzc2ljYSBNY1BoYXVsIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQoNCiMgU3R1ZHkgR3VpZGU6IFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzIChTVk1zKSDigJMgTW9kdWxlIDkNCg0KIyMgKioxLiBJbnRyb2R1Y3Rpb24gdG8gU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMqKg0KU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMgKFNWTXMpIGFyZSBwb3dlcmZ1bCBzdXBlcnZpc2VkIGxlYXJuaW5nIGFsZ29yaXRobXMgcHJpbWFyaWx5IHVzZWQgZm9yIGNsYXNzaWZpY2F0aW9uIHRhc2tzLiBUaGUgZ29hbCBvZiBTVk0gaXMgdG8gZmluZCBhbiBvcHRpbWFsIHNlcGFyYXRpbmcgaHlwZXJwbGFuZSB0aGF0IGJlc3QgZGl2aWRlcyBkaWZmZXJlbnQgY2xhc3NlcyBvZiBkYXRhIHBvaW50cy4NCg0KIyMjICoqS2V5IENvbmNlcHRzOioqDQotICoqTGluZWFyIFNlcGFyYWJpbGl0eToqKiBXaGVuIGEgZGF0YXNldCBjYW4gYmUgcGVyZmVjdGx5IGRpdmlkZWQgdXNpbmcgYSBzdHJhaWdodCBsaW5lIChpbiAyRCkgb3IgYSBoeXBlcnBsYW5lIChpbiBoaWdoZXIgZGltZW5zaW9ucykuDQotICoqSHlwZXJwbGFuZToqKiBBIGRlY2lzaW9uIGJvdW5kYXJ5IHNlcGFyYXRpbmcgZGlmZmVyZW50IGNsYXNzZXMuDQotICoqTWFyZ2luOioqIFRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBjbG9zZXN0IGRhdGEgcG9pbnRzIChzdXBwb3J0IHZlY3RvcnMpIGFuZCB0aGUgaHlwZXJwbGFuZS4NCi0gKipTdXBwb3J0IFZlY3RvcnM6KiogRGF0YSBwb2ludHMgdGhhdCBsaWUgY2xvc2VzdCB0byB0aGUgaHlwZXJwbGFuZSBhbmQgaW5mbHVlbmNlIGl0cyBvcmllbnRhdGlvbi4NCi0gKipNYXhpbWFsIE1hcmdpbiBDbGFzc2lmaWVyOioqIFRoZSBoeXBlcnBsYW5lIHRoYXQgbWF4aW1pemVzIHRoZSBtYXJnaW4uDQotICoqU29mdCBNYXJnaW46KiogSW50cm9kdWNlcyBzbGFjayB2YXJpYWJsZXMgdG8gYWxsb3cgbWlzY2xhc3NpZmljYXRpb24gaW4gY2FzZXMgd2hlcmUgcGVyZmVjdCBzZXBhcmF0aW9uIGlzIGltcG9zc2libGUuDQoNCiMjICoqMi4gVmVjdG9yIEFsZ2VicmEgYW5kIEh5cGVycGxhbmVzKioNClVuZGVyc3RhbmRpbmcgdmVjdG9yIGFsZ2VicmEgaXMgZXNzZW50aWFsIGZvciBTVk1zLg0KDQojIyMgKipIeXBlcnBsYW5lIEVxdWF0aW9uOioqDQpBIGh5cGVycGxhbmUgaW4gJG4kLWRpbWVuc2lvbmFsIHNwYWNlIGlzIGRlZmluZWQgYXM6DQpcWyBcYmV0YV8wICsgXGJldGFeVCB4ID0gMCBcXQ0KV2hlcmU6DQotICRcYmV0YSQgaXMgdGhlIG5vcm1hbCB2ZWN0b3IgKHBlcnBlbmRpY3VsYXIgdG8gdGhlIGh5cGVycGxhbmUpLg0KLSAkeCQgcmVwcmVzZW50cyB0aGUgZmVhdHVyZSB2ZWN0b3JzLg0KLSAkXGJldGFfMCQgaXMgdGhlIGJpYXMgdGVybS4NCg0KRm9yIHR3byBjbGFzc2VzIGxhYmVsZWQgJHlfaSBcaW4gXHstMSwgMVx9JCwgdGhlIFNWTSBkZWNpc2lvbiBib3VuZGFyeSBmb2xsb3dzOg0KXFsgeV9pIChcYmV0YV5UIHhfaSArIFxiZXRhXzApIFxnZXEgTSBcXQ0Kd2hlcmUgJE0kIGlzIHRoZSBtYXJnaW4uDQoNCiMjIyAqKkZpbmRpbmcgdGhlIE9wdGltYWwgSHlwZXJwbGFuZSoqDQpUaGUgb2JqZWN0aXZlIGlzIHRvIG1heGltaXplIHRoZSBtYXJnaW4sIHdoaWNoIHRyYW5zbGF0ZXMgdG8gbWluaW1pemluZyAkfHxcYmV0YXx8JCBzdWJqZWN0IHRvOg0KXFsgeV9pIChcYmV0YV5UIHhfaSArIFxiZXRhXzApIFxnZXEgMSBcXQ0KVGhpcyBpcyBhIGNvbnZleCBvcHRpbWl6YXRpb24gcHJvYmxlbSBzb2x2ZWQgdXNpbmcgTGFncmFuZ2UgbXVsdGlwbGllcnMuDQoNCiMjICoqMy4gSGFuZGxpbmcgTm9uLVNlcGFyYWJsZSBEYXRhKioNClNWTSBpbnRyb2R1Y2VzIHNsYWNrIHZhcmlhYmxlcyAkXHhpX2kkIHRvIGFsbG93IHNvbWUgbWlzY2xhc3NpZmljYXRpb246DQpcWyB5X2kgKFxiZXRhXlQgeF9pICsgXGJldGFfMCkgXGdlcSAxIC0gXHhpX2kgXF0NCndoZXJlICRceGlfaSBcZ2VxIDAkIHJlcHJlc2VudHMgdGhlIG1hcmdpbiB2aW9sYXRpb24gYW1vdW50Lg0KDQpOZXcgb3B0aW1pemF0aW9uIG9iamVjdGl2ZToNClxbIFxtaW5fe1xiZXRhLCBcYmV0YV8wLCBceGl9IFxmcmFjezF9ezJ9IHx8XGJldGF8fF4yICsgQyBcc3VtIFx4aV9pIFxdDQp3aGVyZSAkQyQgaXMgYSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgY29udHJvbGxpbmcgdGhlIHRyYWRlLW9mZiBiZXR3ZWVuIG1hcmdpbiBtYXhpbWl6YXRpb24gYW5kIGNsYXNzaWZpY2F0aW9uIGVycm9ycy4NCg0KIyMgKio0LiBLZXJuZWwgVHJpY2sgZm9yIE5vbi1MaW5lYXIgRGF0YSoqDQpJZiBhIGRhdGFzZXQgaXMgbm90IGxpbmVhcmx5IHNlcGFyYWJsZSwgd2UgbWFwIGl0IHRvIGEgaGlnaGVyLWRpbWVuc2lvbmFsIHNwYWNlIHVzaW5nIGtlcm5lbHM6DQpcWyBLKHhfaSwgeF9qKSA9IFxwaGkoeF9pKV5UIFxwaGkoeF9qKSBcXQ0KDQojIyMgKipDb21tb24gS2VybmVsczoqKg0KLSAqKkxpbmVhciBLZXJuZWw6KiogJEsoeF9pLCB4X2opID0geF9pXlQgeF9qJA0KLSAqKlBvbHlub21pYWwgS2VybmVsOioqICRLKHhfaSwgeF9qKSA9ICh4X2leVCB4X2ogKyBjKV5kJA0KLSAqKlJhZGlhbCBCYXNpcyBGdW5jdGlvbiAoUkJGKSBLZXJuZWw6KiogJEsoeF9pLCB4X2opID0gXGV4cCgtXGdhbW1hIHx8eF9pIC0geF9qfHxeMikkDQoNClRoZSBrZXJuZWwgdHJpY2sgZW5hYmxlcyBTVk0gdG8gb3BlcmF0ZSBpbiBoaWdoZXItZGltZW5zaW9uYWwgc3BhY2VzIHdpdGhvdXQgZXhwbGljaXQgdHJhbnNmb3JtYXRpb25zLg0KDQojIyAqKjUuIE1hdGhlbWF0aWNhbCBGb3JtdWxhdGlvbiBvZiBTVk0qKg0KIyMjICoqSGFyZCBNYXJnaW4gU1ZNIChMaW5lYXJseSBTZXBhcmFibGUpKioNClxbIFxtaW5fe1xiZXRhLCBcYmV0YV8wfSBcZnJhY3sxfXsyfSB8fFxiZXRhfHxeMiBcXQ0Kc3ViamVjdCB0bzoNClxbIHlfaSAoXGJldGFeVCB4X2kgKyBcYmV0YV8wKSBcZ2VxIDEgXF0NCg0KIyMjICoqU29mdCBNYXJnaW4gU1ZNIChBbGxvd2luZyBNaXNjbGFzc2lmaWNhdGlvbikqKg0KXFsgXG1pbl97XGJldGEsIFxiZXRhXzAsIFx4aX0gXGZyYWN7MX17Mn0gfHxcYmV0YXx8XjIgKyBDIFxzdW0gXHhpX2kgXF0NCnN1YmplY3QgdG86DQpcWyB5X2kgKFxiZXRhXlQgeF9pICsgXGJldGFfMCkgXGdlcSAxIC0gXHhpX2ksIFxxdWFkIFx4aV9pIFxnZXEgMCBcXQ0KDQojIyAqKjYuIEltcGxlbWVudGluZyBTVk0gaW4gUHl0aG9uKioNCmBgYHB5dGhvbg0KaW1wb3J0IG51bXB5IGFzIG5wDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQpmcm9tIHNrbGVhcm4uc3ZtIGltcG9ydCBTVkMNCmZyb20gc2tsZWFybi5kYXRhc2V0cyBpbXBvcnQgbWFrZV9jbGFzc2lmaWNhdGlvbg0KZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdA0KZnJvbSBza2xlYXJuLm1ldHJpY3MgaW1wb3J0IGFjY3VyYWN5X3Njb3JlDQoNCiMgR2VuZXJhdGUgc3ludGhldGljIGRhdGFzZXQNClgsIHkgPSBtYWtlX2NsYXNzaWZpY2F0aW9uKG5fc2FtcGxlcz0xMDAsIG5fZmVhdHVyZXM9Miwgbl9jbGFzc2VzPTIsIG5fcmVkdW5kYW50PTAsIHJhbmRvbV9zdGF0ZT00MikNCnkgPSBucC53aGVyZSh5ID09IDAsIC0xLCAxKSAgIyBDb252ZXJ0IGxhYmVscyB0byB7LTEsIDF9DQoNCiMgU3BsaXQgaW50byB0cmFpbi10ZXN0IHNldHMNClhfdHJhaW4sIFhfdGVzdCwgeV90cmFpbiwgeV90ZXN0ID0gdHJhaW5fdGVzdF9zcGxpdChYLCB5LCB0ZXN0X3NpemU9MC4zLCByYW5kb21fc3RhdGU9NDIpDQoNCiMgVHJhaW4gU1ZNIGNsYXNzaWZpZXINCnN2bSA9IFNWQyhrZXJuZWw9J2xpbmVhcicsIEM9MS4wKQ0Kc3ZtLmZpdChYX3RyYWluLCB5X3RyYWluKQ0KDQojIFByZWRpY3Rpb25zDQp5X3ByZWQgPSBzdm0ucHJlZGljdChYX3Rlc3QpDQpwcmludChmJ0FjY3VyYWN5OiB7YWNjdXJhY3lfc2NvcmUoeV90ZXN0LCB5X3ByZWQpOi4yZn0nKQ0KDQojIFBsb3QgZGVjaXNpb24gYm91bmRhcnkNCmRlZiBwbG90X3N2bV9kZWNpc2lvbl9ib3VuZGFyeShYLCB5LCBtb2RlbCk6DQogICAgcGx0LnNjYXR0ZXIoWFs6LCAwXSwgWFs6LCAxXSwgYz15LCBjbWFwPSdjb29sd2FybScsIGVkZ2Vjb2xvcnM9J2snKQ0KICAgIA0KICAgIGF4ID0gcGx0LmdjYSgpDQogICAgeGxpbSA9IGF4LmdldF94bGltKCkNCiAgICB5bGltID0gYXguZ2V0X3lsaW0oKQ0KICAgIA0KICAgIHh4LCB5eSA9IG5wLm1lc2hncmlkKG5wLmxpbnNwYWNlKHhsaW1bMF0sIHhsaW1bMV0sIDUwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBucC5saW5zcGFjZSh5bGltWzBdLCB5bGltWzFdLCA1MCkpDQogICAgDQogICAgWiA9IG1vZGVsLmRlY2lzaW9uX2Z1bmN0aW9uKG5wLmNfW3h4LnJhdmVsKCksIHl5LnJhdmVsKCldKQ0KICAgIFogPSBaLnJlc2hhcGUoeHguc2hhcGUpDQogICAgDQogICAgYXguY29udG91cih4eCwgeXksIFosIGNvbG9ycz0naycsIGxldmVscz1bLTEsIDAsIDFdLCBsaW5lc3R5bGVzPVsnLS0nLCAnLScsICctLSddKQ0KICAgIA0KICAgIHBsdC50aXRsZSgiU1ZNIERlY2lzaW9uIEJvdW5kYXJ5IikNCiAgICBwbHQuc2hvdygpDQoNCnBsb3Rfc3ZtX2RlY2lzaW9uX2JvdW5kYXJ5KFhfdHJhaW4sIHlfdHJhaW4sIHN2bSkNCmBgYA0KDQojIyAqKjcuIEtleSBUYWtlYXdheXMqKg0KLSBTVk0gZmluZHMgdGhlIG9wdGltYWwgaHlwZXJwbGFuZSB0aGF0IG1heGltaXplcyBtYXJnaW4uDQotIFNsYWNrIHZhcmlhYmxlcyAkXHhpJCBhbGxvdyBmb3Igc29mdC1tYXJnaW4gY2xhc3NpZmljYXRpb24uDQotIEtlcm5lbHMgZW5hYmxlIG5vbi1saW5lYXIgY2xhc3NpZmljYXRpb24gYnkgdHJhbnNmb3JtaW5nIGRhdGEgaW50byBoaWdoZXIgZGltZW5zaW9ucy4NCi0gU1ZNIGlzIGVmZmVjdGl2ZSBmb3IgYm90aCBsaW5lYXIgYW5kIG5vbi1saW5lYXIgY2xhc3NpZmljYXRpb24gcHJvYmxlbXMuDQotIFRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgJEMkIGNvbnRyb2xzIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiBtYXJnaW4gbWF4aW1pemF0aW9uIGFuZCBtaXNjbGFzc2lmaWNhdGlvbiB0b2xlcmFuY2UuDQoNCiMjICoqOC4gUHJhY3RpY2FsIENvbnNpZGVyYXRpb25zKioNCi0gQ2hvb3NlIGFuIGFwcHJvcHJpYXRlIGtlcm5lbCBiYXNlZCBvbiBkYXRhc2V0IGNvbXBsZXhpdHkuDQotIEh5cGVycGFyYW1ldGVyIHR1bmluZyAoJEMkLCAkXGdhbW1hJCBmb3IgUkJGIGtlcm5lbCkgaXMgY3J1Y2lhbCBhbmQgY2FuIGJlIG9wdGltaXplZCB1c2luZyBncmlkIHNlYXJjaCBhbmQgY3Jvc3MtdmFsaWRhdGlvbi4NCi0gU1ZNcyBzY2FsZSBwb29ybHkgd2l0aCB2ZXJ5IGxhcmdlIGRhdGFzZXRzIChkdWUgdG8gcXVhZHJhdGljIGNvbXBsZXhpdHkpOyBjb25zaWRlciBhbHRlcm5hdGl2ZSBjbGFzc2lmaWVycyBsaWtlIFJhbmRvbSBGb3Jlc3RzIG9yIERlZXAgTGVhcm5pbmcgZm9yIGJpZyBkYXRhLg0KDQotLS0NCiMjIyAqKkFkZGl0aW9uYWwgUmVzb3VyY2VzKioNCi0gKioiVGhlIEVsZW1lbnRzIG9mIFN0YXRpc3RpY2FsIExlYXJuaW5nIioqIOKAkyBIYXN0aWUsIFRpYnNoaXJhbmksIGFuZCBGcmllZG1hbi4NCi0gKipTY2lraXQtTGVhcm4gRG9jdW1lbnRhdGlvbjoqKiBodHRwczovL3NjaWtpdC1sZWFybi5vcmcvc3RhYmxlL21vZHVsZXMvc3ZtLmh0bWwNCg0KDQoNCioqU3R1ZHkgR3VpZGU6IFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzIChTVk1zKSoqDQoNCiMjIyAqKjEuIEludHJvZHVjdGlvbiB0byBTdXBwb3J0IFZlY3RvciBNYWNoaW5lcyAoU1ZNcykqKg0KU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMgKFNWTXMpIGFyZSBzdXBlcnZpc2VkIGxlYXJuaW5nIG1vZGVscyB1c2VkIGZvciBjbGFzc2lmaWNhdGlvbiBhbmQgcmVncmVzc2lvbiB0YXNrcy4gVGhleSB3b3JrIGJ5IGlkZW50aWZ5aW5nIHRoZSBvcHRpbWFsIGh5cGVycGxhbmUgdGhhdCBiZXN0IHNlcGFyYXRlcyBjbGFzc2VzIGluIGEgZ2l2ZW4gZGF0YXNldC4NCg0KIyMjIyAqKk1hdGhlbWF0aWNhbCBSZXByZXNlbnRhdGlvbjoqKg0KR2l2ZW4gYSBkYXRhc2V0IFwoKHhfaSwgeV9pKVwpIHdoZXJlIFwoeF9pXCkgcmVwcmVzZW50cyB0aGUgZmVhdHVyZSB2ZWN0b3JzIGFuZCBcKHlfaVwpIHRoZSBjbGFzcyBsYWJlbHMgKFwoXHBtMVwpKSwgdGhlIGRlY2lzaW9uIGJvdW5kYXJ5IGlzIGRlZmluZWQgYXM6DQpcWw0KICAgIHcgXGNkb3QgeCArIGIgPSAwDQpcXQ0Kd2hlcmUgXCh3XCkgaXMgdGhlIHdlaWdodCB2ZWN0b3IgYW5kIFwoYlwpIGlzIHRoZSBiaWFzIHRlcm0uDQoNClRoZSBtYXJnaW4gaXMgZ2l2ZW4gYnk6DQpcWw0KICAgIFxmcmFjezJ9e1x8d1x8fQ0KXF0NCndoaWNoIHNob3VsZCBiZSBtYXhpbWl6ZWQgd2hpbGUgZW5zdXJpbmcgY29ycmVjdCBjbGFzc2lmaWNhdGlvbi4NCg0KIyMjIyAqKlB5dGhvbiBJbXBsZW1lbnRhdGlvbjoqKg0KYGBgcHl0aG9uDQpmcm9tIHNrbGVhcm4uc3ZtIGltcG9ydCBTVkMNCmZyb20gc2tsZWFybi5kYXRhc2V0cyBpbXBvcnQgbWFrZV9jbGFzc2lmaWNhdGlvbg0KZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdA0KDQpYLCB5ID0gbWFrZV9jbGFzc2lmaWNhdGlvbihuX3NhbXBsZXM9MTAwLCBuX2ZlYXR1cmVzPTIsIG5fY2xhc3Nlcz0yLCByYW5kb21fc3RhdGU9NDIpDQpYX3RyYWluLCBYX3Rlc3QsIHlfdHJhaW4sIHlfdGVzdCA9IHRyYWluX3Rlc3Rfc3BsaXQoWCwgeSwgdGVzdF9zaXplPTAuMiwgcmFuZG9tX3N0YXRlPTQyKQ0KDQptb2RlbCA9IFNWQyhrZXJuZWw9J2xpbmVhcicsIEM9MS4wKQ0KbW9kZWwuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQp5X3ByZWQgPSBtb2RlbC5wcmVkaWN0KFhfdGVzdCkNCmBgYA0KDQojIyMgKioyLiBNYXJnaW5zIGFuZCBIaW5nZSBMb3NzKioNClRoZSBoaW5nZSBsb3NzIGZ1bmN0aW9uIGlzIGRlZmluZWQgYXM6DQpcWw0KICAgIEwoeSwgZih4KSkgPSBcbWF4KDAsIDEgLSB5IGYoeCkpDQpcXQ0KVGhpcyBsb3NzIHBlbmFsaXplcyBtaXNjbGFzc2lmaWVkIHBvaW50cyBhbmQgcG9pbnRzIGNsb3NlIHRvIHRoZSBtYXJnaW4sIGVuc3VyaW5nIGJldHRlciBnZW5lcmFsaXphdGlvbi4NCg0KIyMjIyAqKkNvbXBhcmlzb24gd2l0aCBPdGhlciBMb3NzIEZ1bmN0aW9uczoqKg0KLSBMb2dpc3RpYyBMb3NzIChCaW5vbWlhbCBEZXZpYW5jZSk6IFwoXGxvZygxICsgZV57LXlmKHgpfSlcKQ0KLSBTcXVhcmVkIEVycm9yIExvc3M6IFwoKHkgLSBmKHgpKV4yXCkNCi0gSHViZXJpemVkIFNxdWFyZSBIaW5nZSBMb3NzOiBIeWJyaWQgYmV0d2VlbiBoaW5nZSBsb3NzIGFuZCBzcXVhcmVkIGxvc3PjgJAxMDQ6MOKAoEVTTElJX3ByaW50MTJfdG9jLnBkZuOAkS4NCg0KIyMjICoqMy4gS2VybmVsIFRyaWNrKioNCkZvciBub24tbGluZWFybHkgc2VwYXJhYmxlIGRhdGEsIFNWTXMgdXNlIHRoZSAqKmtlcm5lbCB0cmljayoqIHRvIHRyYW5zZm9ybSBpbnB1dCBzcGFjZSBpbnRvIGEgaGlnaGVyLWRpbWVuc2lvbmFsIGZlYXR1cmUgc3BhY2Ugd2hlcmUgYSBsaW5lYXIgZGVjaXNpb24gYm91bmRhcnkgY2FuIGJlIGFwcGxpZWQuDQoNCiMjIyMgKipDb21tb24gS2VybmVsczoqKg0KLSAqKkxpbmVhciBLZXJuZWw6KiogXCggSyh4LCB4JykgPSB4IFxjZG90IHgnIFwpDQotICoqUG9seW5vbWlhbCBLZXJuZWw6KiogXCggSyh4LCB4JykgPSAoeCBcY2RvdCB4JyArIGMpXmQgXCkNCi0gKipSYWRpYWwgQmFzaXMgRnVuY3Rpb24gKFJCRikgS2VybmVsOioqIFwoIEsoeCwgeCcpID0gXGV4cCgtXGdhbW1hIFx8eCAtIHgnXHxeMikgXCkNCi0gKipTaWdtb2lkIEtlcm5lbDoqKiBcKCBLKHgsIHgnKSA9IFx0YW5oKFxhbHBoYSB4IFxjZG90IHgnICsgYykgXCkNCg0KYGBgcHl0aG9uDQojIEV4YW1wbGUgb2YgdXNpbmcgYW4gUkJGIGtlcm5lbA0KbW9kZWwgPSBTVkMoa2VybmVsPSdyYmYnLCBnYW1tYT0wLjEsIEM9MS4wKQ0KbW9kZWwuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQpgYGANCg0KIyMjICoqNC4gU2NhbGluZyBhbmQgUGVyZm9ybWFuY2UqKg0KU1ZNcyBoYXZlIGFuICoqTyhuXjIpIGNvbXBsZXhpdHkqKiwgbWFraW5nIHRoZW0gaW5lZmZpY2llbnQgZm9yIGxhcmdlIGRhdGFzZXRzLiBQZXJmb3JtYW5jZSBvcHRpbWl6YXRpb24gdGVjaG5pcXVlcyBpbmNsdWRlOg0KLSBVc2luZyAqKkxpbmVhclNWQyoqICh3aGljaCBpbXBsZW1lbnRzIFNWTSBhcyBhbiBvcHRpbWl6YXRpb24gcHJvYmxlbSkNCi0gUmVkdWNpbmcgZmVhdHVyZXMgdmlhIFBDQQ0KLSBVc2luZyBhcHByb3hpbWF0ZSBtZXRob2RzIHN1Y2ggYXMgU0dEQ2xhc3NpZmllcg0KDQpgYGBweXRob24NCmZyb20gc2tsZWFybi5zdm0gaW1wb3J0IExpbmVhclNWQw0KbW9kZWwgPSBMaW5lYXJTVkMoKQ0KbW9kZWwuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQpgYGANCg0KIyMjICoqNS4gQ29tcGFyaXNvbiB3aXRoIE5haXZlIEJheWVzKioNCldoaWxlIFNWTXMgb3B0aW1pemUgYSBtYXJnaW4sIE5haXZlIEJheWVzIGVzdGltYXRlcyBjbGFzcyBwcm9iYWJpbGl0aWVzLg0KLSAqKlNWTXMqKiBwZXJmb3JtIGJldHRlciBpbiAqKmhpZ2gtZGltZW5zaW9uYWwgc3BhY2VzKiouDQotICoqTmFpdmUgQmF5ZXMqKiBpcyBjb21wdXRhdGlvbmFsbHkgKipmYXN0ZXIqKiBhbmQgd29ya3Mgd2VsbCB3aGVuIGZlYXR1cmUgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb25zIGhvbGQuDQoNCiMjIyAqKktleSBUYWtlYXdheXM6KioNCjEuIFNWTXMgZmluZCB0aGUgb3B0aW1hbCBoeXBlcnBsYW5lIGJ5IG1heGltaXppbmcgdGhlIG1hcmdpbi4NCjIuIFRoZSAqKmtlcm5lbCB0cmljayoqIGFsbG93cyBTVk1zIHRvIGhhbmRsZSBub24tbGluZWFybHkgc2VwYXJhYmxlIGRhdGEuDQozLiAqKkhpbmdlIGxvc3MqKiBpcyB1c2VkIGZvciBjbGFzc2lmaWNhdGlvbiB0byBwZW5hbGl6ZSBtaXNjbGFzc2lmaWVkIHBvaW50cy4NCjQuICoqU2NhbGluZyBpc3N1ZXMqKiBsaW1pdCBTVk1zIG9uIGxhcmdlIGRhdGFzZXRzIGR1ZSB0byBxdWFkcmF0aWMgY29tcGxleGl0eS4NCjUuIFNWTXMgb3V0cGVyZm9ybSBOYWl2ZSBCYXllcyBpbiAqKmhpZ2gtZGltZW5zaW9uYWwgc3BhY2VzKiosIGJ1dCBhcmUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZS4NCg0KIyMjICoqRGlzY3Vzc2lvbiBRdWVzdGlvbnM6KioNCjEuIEhvdyBkb2VzIHRoZSBjaG9pY2Ugb2YgdGhlIGtlcm5lbCBhZmZlY3QgdGhlIHBlcmZvcm1hbmNlIG9mIGFuIFNWTSBtb2RlbD8NCjIuIEluIHdoYXQgc2NlbmFyaW9zIGlzIFNWTSBwcmVmZXJhYmxlIG92ZXIgbG9naXN0aWMgcmVncmVzc2lvbj8NCjMuIEhvdyBjYW4gd2UgaGFuZGxlIGltYmFsYW5jZWQgZGF0YSB3aGlsZSB1c2luZyBTVk1zPw0KNC4gV2h5IGRvZXMgaW5jcmVhc2luZyB0aGUgcmVndWxhcml6YXRpb24gcGFyYW1ldGVyIEMgcmVkdWNlIHRoZSBtYXJnaW4/DQo1LiBXaGF0IHN0cmF0ZWdpZXMgY2FuIGltcHJvdmUgU1ZNIHBlcmZvcm1hbmNlIG9uIGxhcmdlIGRhdGFzZXRzPw0KDQo=