Association Rule Mining Study Guide

Overview of Association Analysis

Association analysis examines relationships between items in a dataset to determine patterns of co-occurrence. In association rule mining, these patterns are used to generate rules that predict when certain items will appear together.

For example, in a retail setting, this analysis might reveal that “if a customer buys bread and milk, they are likely to also buy eggs.”

Key Concepts

  1. Association Rule Mining:
    • Definition: Given a set of transactions, association rule mining seeks to find patterns or rules that explain when items co-occur in transactions.
    • Applications: Common in recommendation systems (e.g., Amazon’s “Customers who bought this also bought”) and in healthcare data (e.g., high blood pressure often co-occurs with stress).

1. Association Rule Mining Recap

Association rule mining aims to uncover relationships between variables in transaction data. Each transaction is a collection of items, and the objective is to determine if certain items frequently co-occur. These relationships can be represented as association rules of the form \(X \rightarrow Y\), where \(X\) implies \(Y\) with a certain degree of confidence and support.

  1. Frequent Itemsets:
    • Definition: Collections of one or more items that appear frequently together in the dataset. For example, {bread, milk} could be a frequent itemset.
    • Support Count: The frequency of occurrence of an itemset within the dataset.
    • Support: The proportion of transactions that contain the itemset, calculated as: \[ \text{Support} = \frac{\text{Support Count}}{\text{Total Transactions}} \]

2. Frequent Itemset Generation

Generating frequent itemsets involves identifying groups of items that often appear together in transactions. The candidate itemset lattice is used to systematically evaluate itemset combinations, starting from single items and progressing to larger groups. Key to reducing computational load is the Apriori principle, which helps to avoid evaluating all possible combinations by eliminating itemsets that do not meet the minimum support threshold.

Example: For items like {milk, bread, diapers}, frequent itemsets are those that appear in a sufficient number of transactions. Calculating support for itemsets and generating candidate lattices enables structured exploration of co-occurrence relationships.

  1. Association Rules:
    • Definition: Expressed as \(X \rightarrow Y\), where \(X\) and \(Y\) are itemsets, meaning “If \(X\) occurs, \(Y\) is likely to occur.”
    • Support for a Rule: The fraction of transactions that contain both \(X\) and \(Y\).
    • Confidence for a Rule: Indicates how often \(Y\) appears in transactions that contain \(X\), given by: \[ \text{Confidence}(X \rightarrow Y) = \frac{\text{Support of } X \cup Y}{\text{Support of } X} \]

Rule Evaluation Metrics

  • Support: Helps in identifying the overall frequency of an itemset.
  • Confidence: Measures the reliability of an inference; higher confidence indicates a stronger association.
  • Lift (optional): Measures how much more likely \(Y\) is to occur when \(X\) is present compared to when \(X\) is absent.

3. Rule Generation

Once frequent itemsets are identified, the next step is creating association rules from these sets, ensuring each rule meets minimum support and confidence requirements. These rules are derived from itemsets by dividing them into antecedents (if-part) and consequents (then-part), with metrics calculated for each rule.

Rule Metrics: - Support: Measures how often \(X \cup Y\) appears in the dataset. - Confidence: Measures the likelihood of \(Y\) appearing when \(X\) is present, calculated as: \[ \text{Confidence}(X \rightarrow Y) = \frac{\text{Support of } X \cup Y}{\text{Support of } X} \] - Lift: Evaluates the strength of an association rule relative to random chance, providing insight into the rule’s significance.

Steps in Association Rule Mining

  1. Frequent Itemset Generation:
    • Generate all itemsets that meet a minimum support threshold, a process that is computationally intensive.
  2. Rule Generation:
    • Use the frequent itemsets to create association rules, ensuring they meet both minimum support and confidence thresholds.
  3. Optimizing Computation:
    • Candidate Lattice: Visualizes all potential itemsets for frequent set mining.
    • Apriori Principle: If an itemset is infrequent, all supersets are also infrequent, helping to reduce computation by pruning.

Example Calculations

Suppose a dataset has 5 transactions, and {milk, bread, diapers} appears in 2 of them: - Support Count for {milk, bread, diapers}: 2 - Support for {milk, bread, diapers}: \[ \text{Support} = \frac{2}{5} = 0.4 \]

If {milk, diapers} appears in 3 transactions, the confidence that {milk, diapers} \rightarrow beer is: \[ \text{Confidence} = \frac{\text{Support of } \{milk, diapers, beer\}}{\text{Support of } \{milk, diapers\}} \]

Python Code for Support and Confidence Calculation

from itertools import combinations

# Example transaction data
transactions = [
    {'milk', 'bread', 'beer'},
    {'milk', 'diapers', 'beer'},
    {'milk', 'bread', 'diapers'},
    {'bread', 'diapers', 'beer'},
    {'milk', 'bread'}
]

def support(itemset, transactions):
    count = sum(1 for transaction in transactions if itemset.issubset(transaction))
    return count / len(transactions)

def confidence(itemset_X, itemset_Y, transactions):
    return support(itemset_X | itemset_Y, transactions) / support(itemset_X, transactions)

# Calculate support and confidence for a rule
itemset_X = {'milk', 'diapers'}
itemset_Y = {'beer'}

rule_support = support(itemset_X | itemset_Y, transactions)
rule_confidence = confidence(itemset_X, itemset_Y, transactions)

print("Support:", rule_support)
print("Confidence:", rule_confidence)

Visualizing the Candidate Lattice

To visualize a candidate lattice and optimize computation: 1. List items in a hierarchy (e.g., single items, pairs, triples). 2. Use the Apriori Principle to prune infrequent itemsets.

import networkx as nx
import matplotlib.pyplot as plt

# Example of candidate lattice visualization
G = nx.DiGraph()
G.add_edges_from([
    ("A", "AB"), ("A", "AC"), ("B", "AB"), 
    ("B", "BC"), ("C", "AC"), ("C", "BC"),
    ("AB", "ABC"), ("AC", "ABC"), ("BC", "ABC")
])

plt.figure(figsize=(8, 6))
nx.draw(G, with_labels=True, node_size=3000, node_color="lightblue", font_size=10, font_weight="bold")
plt.show()

Summary

4. Efficient Computation with the Apriori Principle

The Apriori algorithm optimizes frequent itemset generation by eliminating non-frequent itemsets and their supersets early in the process. This reduces the search space and makes rule generation computationally feasible for large datasets, often visualized as a candidate lattice to track the hierarchical progression of item combinations.

5. Implementation Example in Python

To compute support and confidence for specific rules in Python:

from itertools import combinations

# Example transactions
transactions = [
    {'milk', 'bread', 'beer'},
    {'milk', 'diapers', 'beer'},
    {'milk', 'bread', 'diapers'},
    {'bread', 'diapers', 'beer'},
    {'milk', 'bread'}
]

# Function to calculate support
def support(itemset, transactions):
    count = sum(1 for transaction in transactions if itemset.issubset(transaction))
    return count / len(transactions)

# Function to calculate confidence
def confidence(itemset_X, itemset_Y, transactions):
    return support(itemset_X | itemset_Y, transactions) / support(itemset_X, transactions)

# Calculate support and confidence for a rule
itemset_X = {'milk', 'diapers'}
itemset_Y = {'beer'}

rule_support = support(itemset_X | itemset_Y, transactions)
rule_confidence = confidence(itemset_X, itemset_Y, transactions)

print("Support:", rule_support)
print("Confidence:", rule_confidence)

This code provides a structured way to calculate support and confidence for a rule such as {milk, diapers} → beer, helping identify meaningful associations in transaction data.

6. Visualizing Candidate Lattices

The candidate lattice organizes itemsets by their frequency, aiding in the application of the Apriori principle. Visualizing it with NetworkX:

import networkx as nx
import matplotlib.pyplot as plt

G = nx.DiGraph()
G.add_edges_from([
    ("A", "AB"), ("A", "AC"), ("B", "AB"), 
    ("B", "BC"), ("C", "AC"), ("C", "BC"),
    ("AB", "ABC"), ("AC", "ABC"), ("BC", "ABC")
])

plt.figure(figsize=(8, 6))
nx.draw(G, with_labels=True, node_size=3000, font_size=10, font_weight="bold")
plt.show()

Recap

Association rule mining, supported by efficient itemset generation through the Apriori principle and candidate lattices, enables scalable analysis of transactional data, revealing patterns with practical applications across industries.

These concepts can be used to identify valuable patterns that can be applied in business, healthcare, and other domains requiring co-occurrence analysis.

LS0tDQp0aXRsZTogIk1MNzMzMSBNb2R1bGUgMTIiDQphdXRob3I6ICJKZXNzaWNhIE1jUGhhdWwiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgQXNzb2NpYXRpb24gUnVsZSBNaW5pbmcgU3R1ZHkgR3VpZGUNCg0KIyMjIyBPdmVydmlldyBvZiBBc3NvY2lhdGlvbiBBbmFseXNpcw0KQXNzb2NpYXRpb24gYW5hbHlzaXMgZXhhbWluZXMgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGl0ZW1zIGluIGEgZGF0YXNldCB0byBkZXRlcm1pbmUgcGF0dGVybnMgb2YgY28tb2NjdXJyZW5jZS4gSW4gYXNzb2NpYXRpb24gcnVsZSBtaW5pbmcsIHRoZXNlIHBhdHRlcm5zIGFyZSB1c2VkIHRvIGdlbmVyYXRlICoqcnVsZXMqKiB0aGF0IHByZWRpY3Qgd2hlbiBjZXJ0YWluIGl0ZW1zIHdpbGwgYXBwZWFyIHRvZ2V0aGVyLg0KDQpGb3IgZXhhbXBsZSwgaW4gYSByZXRhaWwgc2V0dGluZywgdGhpcyBhbmFseXNpcyBtaWdodCByZXZlYWwgdGhhdCAiaWYgYSBjdXN0b21lciBidXlzIGJyZWFkIGFuZCBtaWxrLCB0aGV5IGFyZSBsaWtlbHkgdG8gYWxzbyBidXkgZWdncy4iDQoNCiMjIyMgS2V5IENvbmNlcHRzDQoNCjEuICoqQXNzb2NpYXRpb24gUnVsZSBNaW5pbmcqKjoNCiAgIC0gKipEZWZpbml0aW9uKio6IEdpdmVuIGEgc2V0IG9mIHRyYW5zYWN0aW9ucywgYXNzb2NpYXRpb24gcnVsZSBtaW5pbmcgc2Vla3MgdG8gZmluZCBwYXR0ZXJucyBvciBydWxlcyB0aGF0IGV4cGxhaW4gd2hlbiBpdGVtcyBjby1vY2N1ciBpbiB0cmFuc2FjdGlvbnMuDQogICAtICoqQXBwbGljYXRpb25zKio6IENvbW1vbiBpbiByZWNvbW1lbmRhdGlvbiBzeXN0ZW1zIChlLmcuLCBBbWF6b27igJlzICJDdXN0b21lcnMgd2hvIGJvdWdodCB0aGlzIGFsc28gYm91Z2h0IikgYW5kIGluIGhlYWx0aGNhcmUgZGF0YSAoZS5nLiwgaGlnaCBibG9vZCBwcmVzc3VyZSBvZnRlbiBjby1vY2N1cnMgd2l0aCBzdHJlc3MpLg0KDQojIyMjIDEuIEFzc29jaWF0aW9uIFJ1bGUgTWluaW5nIFJlY2FwDQpBc3NvY2lhdGlvbiBydWxlIG1pbmluZyBhaW1zIHRvIHVuY292ZXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBpbiB0cmFuc2FjdGlvbiBkYXRhLiBFYWNoIHRyYW5zYWN0aW9uIGlzIGEgY29sbGVjdGlvbiBvZiBpdGVtcywgYW5kIHRoZSBvYmplY3RpdmUgaXMgdG8gZGV0ZXJtaW5lIGlmIGNlcnRhaW4gaXRlbXMgZnJlcXVlbnRseSBjby1vY2N1ci4gVGhlc2UgcmVsYXRpb25zaGlwcyBjYW4gYmUgcmVwcmVzZW50ZWQgYXMgKiphc3NvY2lhdGlvbiBydWxlcyoqIG9mIHRoZSBmb3JtIFwoIFggXHJpZ2h0YXJyb3cgWSBcKSwgd2hlcmUgXCggWCBcKSBpbXBsaWVzIFwoIFkgXCkgd2l0aCBhIGNlcnRhaW4gZGVncmVlIG9mICoqY29uZmlkZW5jZSoqIGFuZCAqKnN1cHBvcnQqKi4NCg0KDQoyLiAqKkZyZXF1ZW50IEl0ZW1zZXRzKio6DQogICAtICoqRGVmaW5pdGlvbioqOiBDb2xsZWN0aW9ucyBvZiBvbmUgb3IgbW9yZSBpdGVtcyB0aGF0IGFwcGVhciBmcmVxdWVudGx5IHRvZ2V0aGVyIGluIHRoZSBkYXRhc2V0LiBGb3IgZXhhbXBsZSwgYHticmVhZCwgbWlsa31gIGNvdWxkIGJlIGEgZnJlcXVlbnQgaXRlbXNldC4NCiAgIC0gKipTdXBwb3J0IENvdW50Kio6IFRoZSBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZSBvZiBhbiBpdGVtc2V0IHdpdGhpbiB0aGUgZGF0YXNldC4NCiAgIC0gKipTdXBwb3J0Kio6IFRoZSBwcm9wb3J0aW9uIG9mIHRyYW5zYWN0aW9ucyB0aGF0IGNvbnRhaW4gdGhlIGl0ZW1zZXQsIGNhbGN1bGF0ZWQgYXM6DQogICAgIFxbDQogICAgIFx0ZXh0e1N1cHBvcnR9ID0gXGZyYWN7XHRleHR7U3VwcG9ydCBDb3VudH19e1x0ZXh0e1RvdGFsIFRyYW5zYWN0aW9uc319DQogICAgIFxdDQogICAgIA0KICAgICANCiMjIyMgMi4gRnJlcXVlbnQgSXRlbXNldCBHZW5lcmF0aW9uDQpHZW5lcmF0aW5nIGZyZXF1ZW50IGl0ZW1zZXRzIGludm9sdmVzIGlkZW50aWZ5aW5nIGdyb3VwcyBvZiBpdGVtcyB0aGF0IG9mdGVuIGFwcGVhciB0b2dldGhlciBpbiB0cmFuc2FjdGlvbnMuIFRoZSAqKmNhbmRpZGF0ZSBpdGVtc2V0IGxhdHRpY2UqKiBpcyB1c2VkIHRvIHN5c3RlbWF0aWNhbGx5IGV2YWx1YXRlIGl0ZW1zZXQgY29tYmluYXRpb25zLCBzdGFydGluZyBmcm9tIHNpbmdsZSBpdGVtcyBhbmQgcHJvZ3Jlc3NpbmcgdG8gbGFyZ2VyIGdyb3Vwcy4gS2V5IHRvIHJlZHVjaW5nIGNvbXB1dGF0aW9uYWwgbG9hZCBpcyB0aGUgKipBcHJpb3JpIHByaW5jaXBsZSoqLCB3aGljaCBoZWxwcyB0byBhdm9pZCBldmFsdWF0aW5nIGFsbCBwb3NzaWJsZSBjb21iaW5hdGlvbnMgYnkgZWxpbWluYXRpbmcgaXRlbXNldHMgdGhhdCBkbyBub3QgbWVldCB0aGUgbWluaW11bSBzdXBwb3J0IHRocmVzaG9sZC4NCg0KKipFeGFtcGxlOioqDQpGb3IgaXRlbXMgbGlrZSBge21pbGssIGJyZWFkLCBkaWFwZXJzfWAsIGZyZXF1ZW50IGl0ZW1zZXRzIGFyZSB0aG9zZSB0aGF0IGFwcGVhciBpbiBhIHN1ZmZpY2llbnQgbnVtYmVyIG9mIHRyYW5zYWN0aW9ucy4gQ2FsY3VsYXRpbmcgc3VwcG9ydCBmb3IgaXRlbXNldHMgYW5kIGdlbmVyYXRpbmcgY2FuZGlkYXRlIGxhdHRpY2VzIGVuYWJsZXMgc3RydWN0dXJlZCBleHBsb3JhdGlvbiBvZiBjby1vY2N1cnJlbmNlIHJlbGF0aW9uc2hpcHMuDQoNCg0KMy4gKipBc3NvY2lhdGlvbiBSdWxlcyoqOg0KICAgLSAqKkRlZmluaXRpb24qKjogRXhwcmVzc2VkIGFzIFwoIFggXHJpZ2h0YXJyb3cgWSBcKSwgd2hlcmUgXCggWCBcKSBhbmQgXCggWSBcKSBhcmUgaXRlbXNldHMsIG1lYW5pbmcgIklmIFwoIFggXCkgb2NjdXJzLCBcKCBZIFwpIGlzIGxpa2VseSB0byBvY2N1ci4iDQogICAtICoqU3VwcG9ydCBmb3IgYSBSdWxlKio6IFRoZSBmcmFjdGlvbiBvZiB0cmFuc2FjdGlvbnMgdGhhdCBjb250YWluIGJvdGggXCggWCBcKSBhbmQgXCggWSBcKS4NCiAgIC0gKipDb25maWRlbmNlIGZvciBhIFJ1bGUqKjogSW5kaWNhdGVzIGhvdyBvZnRlbiBcKCBZIFwpIGFwcGVhcnMgaW4gdHJhbnNhY3Rpb25zIHRoYXQgY29udGFpbiBcKCBYIFwpLCBnaXZlbiBieToNCiAgICAgXFsNCiAgICAgXHRleHR7Q29uZmlkZW5jZX0oWCBccmlnaHRhcnJvdyBZKSA9IFxmcmFje1x0ZXh0e1N1cHBvcnQgb2YgfSBYIFxjdXAgWX17XHRleHR7U3VwcG9ydCBvZiB9IFh9DQogICAgIFxdDQogICAgIA0KICAgICANCg0KIyMjIyBSdWxlIEV2YWx1YXRpb24gTWV0cmljcw0KDQotICoqU3VwcG9ydCoqOiBIZWxwcyBpbiBpZGVudGlmeWluZyB0aGUgb3ZlcmFsbCBmcmVxdWVuY3kgb2YgYW4gaXRlbXNldC4NCi0gKipDb25maWRlbmNlKio6IE1lYXN1cmVzIHRoZSByZWxpYWJpbGl0eSBvZiBhbiBpbmZlcmVuY2U7IGhpZ2hlciBjb25maWRlbmNlIGluZGljYXRlcyBhIHN0cm9uZ2VyIGFzc29jaWF0aW9uLg0KLSAqKkxpZnQgKG9wdGlvbmFsKSoqOiBNZWFzdXJlcyBob3cgbXVjaCBtb3JlIGxpa2VseSBcKCBZIFwpIGlzIHRvIG9jY3VyIHdoZW4gXCggWCBcKSBpcyBwcmVzZW50IGNvbXBhcmVkIHRvIHdoZW4gXCggWCBcKSBpcyBhYnNlbnQuDQoNCiMjIyMgMy4gUnVsZSBHZW5lcmF0aW9uDQpPbmNlIGZyZXF1ZW50IGl0ZW1zZXRzIGFyZSBpZGVudGlmaWVkLCB0aGUgbmV4dCBzdGVwIGlzIGNyZWF0aW5nIGFzc29jaWF0aW9uIHJ1bGVzIGZyb20gdGhlc2Ugc2V0cywgZW5zdXJpbmcgZWFjaCBydWxlIG1lZXRzIG1pbmltdW0gc3VwcG9ydCBhbmQgY29uZmlkZW5jZSByZXF1aXJlbWVudHMuIFRoZXNlIHJ1bGVzIGFyZSBkZXJpdmVkIGZyb20gaXRlbXNldHMgYnkgZGl2aWRpbmcgdGhlbSBpbnRvIGFudGVjZWRlbnRzIChpZi1wYXJ0KSBhbmQgY29uc2VxdWVudHMgKHRoZW4tcGFydCksIHdpdGggbWV0cmljcyBjYWxjdWxhdGVkIGZvciBlYWNoIHJ1bGUuDQoNCioqUnVsZSBNZXRyaWNzKio6DQotICoqU3VwcG9ydCoqOiBNZWFzdXJlcyBob3cgb2Z0ZW4gXCggWCBcY3VwIFkgXCkgYXBwZWFycyBpbiB0aGUgZGF0YXNldC4NCi0gKipDb25maWRlbmNlKio6IE1lYXN1cmVzIHRoZSBsaWtlbGlob29kIG9mIFwoIFkgXCkgYXBwZWFyaW5nIHdoZW4gXCggWCBcKSBpcyBwcmVzZW50LCBjYWxjdWxhdGVkIGFzOg0KICBcWw0KICBcdGV4dHtDb25maWRlbmNlfShYIFxyaWdodGFycm93IFkpID0gXGZyYWN7XHRleHR7U3VwcG9ydCBvZiB9IFggXGN1cCBZfXtcdGV4dHtTdXBwb3J0IG9mIH0gWH0NCiAgXF0NCi0gKipMaWZ0Kio6IEV2YWx1YXRlcyB0aGUgc3RyZW5ndGggb2YgYW4gYXNzb2NpYXRpb24gcnVsZSByZWxhdGl2ZSB0byByYW5kb20gY2hhbmNlLCBwcm92aWRpbmcgaW5zaWdodCBpbnRvIHRoZSBydWxl4oCZcyBzaWduaWZpY2FuY2UuDQoNCiMjIyMgU3RlcHMgaW4gQXNzb2NpYXRpb24gUnVsZSBNaW5pbmcNCg0KMS4gKipGcmVxdWVudCBJdGVtc2V0IEdlbmVyYXRpb24qKjoNCiAgIC0gR2VuZXJhdGUgYWxsIGl0ZW1zZXRzIHRoYXQgbWVldCBhIG1pbmltdW0gc3VwcG9ydCB0aHJlc2hvbGQsIGEgcHJvY2VzcyB0aGF0IGlzIGNvbXB1dGF0aW9uYWxseSBpbnRlbnNpdmUuDQoNCjIuICoqUnVsZSBHZW5lcmF0aW9uKio6DQogICAtIFVzZSB0aGUgZnJlcXVlbnQgaXRlbXNldHMgdG8gY3JlYXRlIGFzc29jaWF0aW9uIHJ1bGVzLCBlbnN1cmluZyB0aGV5IG1lZXQgYm90aCBtaW5pbXVtIHN1cHBvcnQgYW5kIGNvbmZpZGVuY2UgdGhyZXNob2xkcy4NCg0KMy4gKipPcHRpbWl6aW5nIENvbXB1dGF0aW9uKio6DQogICAtICoqQ2FuZGlkYXRlIExhdHRpY2UqKjogVmlzdWFsaXplcyBhbGwgcG90ZW50aWFsIGl0ZW1zZXRzIGZvciBmcmVxdWVudCBzZXQgbWluaW5nLg0KICAgLSAqKkFwcmlvcmkgUHJpbmNpcGxlKio6IElmIGFuIGl0ZW1zZXQgaXMgaW5mcmVxdWVudCwgYWxsIHN1cGVyc2V0cyBhcmUgYWxzbyBpbmZyZXF1ZW50LCBoZWxwaW5nIHRvIHJlZHVjZSBjb21wdXRhdGlvbiBieSBwcnVuaW5nLg0KDQojIyMjIEV4YW1wbGUgQ2FsY3VsYXRpb25zDQoNClN1cHBvc2UgYSBkYXRhc2V0IGhhcyA1IHRyYW5zYWN0aW9ucywgYW5kIGB7bWlsaywgYnJlYWQsIGRpYXBlcnN9YCBhcHBlYXJzIGluIDIgb2YgdGhlbToNCi0gKipTdXBwb3J0IENvdW50IGZvciBge21pbGssIGJyZWFkLCBkaWFwZXJzfWAqKjogMg0KLSAqKlN1cHBvcnQgZm9yIGB7bWlsaywgYnJlYWQsIGRpYXBlcnN9YCoqOg0KICBcWw0KICBcdGV4dHtTdXBwb3J0fSA9IFxmcmFjezJ9ezV9ID0gMC40DQogIFxdDQoNCklmIGB7bWlsaywgZGlhcGVyc31gIGFwcGVhcnMgaW4gMyB0cmFuc2FjdGlvbnMsIHRoZSAqKmNvbmZpZGVuY2UqKiB0aGF0IGB7bWlsaywgZGlhcGVyc30gXHJpZ2h0YXJyb3cgYmVlcmAgaXM6DQpcWw0KXHRleHR7Q29uZmlkZW5jZX0gPSBcZnJhY3tcdGV4dHtTdXBwb3J0IG9mIH0gXHttaWxrLCBkaWFwZXJzLCBiZWVyXH19e1x0ZXh0e1N1cHBvcnQgb2YgfSBce21pbGssIGRpYXBlcnNcfX0NClxdDQoNCiMjIyMgUHl0aG9uIENvZGUgZm9yIFN1cHBvcnQgYW5kIENvbmZpZGVuY2UgQ2FsY3VsYXRpb24NCg0KYGBgcHl0aG9uDQpmcm9tIGl0ZXJ0b29scyBpbXBvcnQgY29tYmluYXRpb25zDQoNCiMgRXhhbXBsZSB0cmFuc2FjdGlvbiBkYXRhDQp0cmFuc2FjdGlvbnMgPSBbDQogICAgeydtaWxrJywgJ2JyZWFkJywgJ2JlZXInfSwNCiAgICB7J21pbGsnLCAnZGlhcGVycycsICdiZWVyJ30sDQogICAgeydtaWxrJywgJ2JyZWFkJywgJ2RpYXBlcnMnfSwNCiAgICB7J2JyZWFkJywgJ2RpYXBlcnMnLCAnYmVlcid9LA0KICAgIHsnbWlsaycsICdicmVhZCd9DQpdDQoNCmRlZiBzdXBwb3J0KGl0ZW1zZXQsIHRyYW5zYWN0aW9ucyk6DQogICAgY291bnQgPSBzdW0oMSBmb3IgdHJhbnNhY3Rpb24gaW4gdHJhbnNhY3Rpb25zIGlmIGl0ZW1zZXQuaXNzdWJzZXQodHJhbnNhY3Rpb24pKQ0KICAgIHJldHVybiBjb3VudCAvIGxlbih0cmFuc2FjdGlvbnMpDQoNCmRlZiBjb25maWRlbmNlKGl0ZW1zZXRfWCwgaXRlbXNldF9ZLCB0cmFuc2FjdGlvbnMpOg0KICAgIHJldHVybiBzdXBwb3J0KGl0ZW1zZXRfWCB8IGl0ZW1zZXRfWSwgdHJhbnNhY3Rpb25zKSAvIHN1cHBvcnQoaXRlbXNldF9YLCB0cmFuc2FjdGlvbnMpDQoNCiMgQ2FsY3VsYXRlIHN1cHBvcnQgYW5kIGNvbmZpZGVuY2UgZm9yIGEgcnVsZQ0KaXRlbXNldF9YID0geydtaWxrJywgJ2RpYXBlcnMnfQ0KaXRlbXNldF9ZID0geydiZWVyJ30NCg0KcnVsZV9zdXBwb3J0ID0gc3VwcG9ydChpdGVtc2V0X1ggfCBpdGVtc2V0X1ksIHRyYW5zYWN0aW9ucykNCnJ1bGVfY29uZmlkZW5jZSA9IGNvbmZpZGVuY2UoaXRlbXNldF9YLCBpdGVtc2V0X1ksIHRyYW5zYWN0aW9ucykNCg0KcHJpbnQoIlN1cHBvcnQ6IiwgcnVsZV9zdXBwb3J0KQ0KcHJpbnQoIkNvbmZpZGVuY2U6IiwgcnVsZV9jb25maWRlbmNlKQ0KYGBgDQoNCiMjIyMgVmlzdWFsaXppbmcgdGhlIENhbmRpZGF0ZSBMYXR0aWNlDQoNClRvIHZpc3VhbGl6ZSBhICoqY2FuZGlkYXRlIGxhdHRpY2UqKiBhbmQgb3B0aW1pemUgY29tcHV0YXRpb246DQoxLiBMaXN0IGl0ZW1zIGluIGEgaGllcmFyY2h5IChlLmcuLCBzaW5nbGUgaXRlbXMsIHBhaXJzLCB0cmlwbGVzKS4NCjIuIFVzZSB0aGUgKipBcHJpb3JpIFByaW5jaXBsZSoqIHRvIHBydW5lIGluZnJlcXVlbnQgaXRlbXNldHMuDQoNCmBgYHB5dGhvbg0KaW1wb3J0IG5ldHdvcmt4IGFzIG54DQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgRXhhbXBsZSBvZiBjYW5kaWRhdGUgbGF0dGljZSB2aXN1YWxpemF0aW9uDQpHID0gbnguRGlHcmFwaCgpDQpHLmFkZF9lZGdlc19mcm9tKFsNCiAgICAoIkEiLCAiQUIiKSwgKCJBIiwgIkFDIiksICgiQiIsICJBQiIpLCANCiAgICAoIkIiLCAiQkMiKSwgKCJDIiwgIkFDIiksICgiQyIsICJCQyIpLA0KICAgICgiQUIiLCAiQUJDIiksICgiQUMiLCAiQUJDIiksICgiQkMiLCAiQUJDIikNCl0pDQoNCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCwgNikpDQpueC5kcmF3KEcsIHdpdGhfbGFiZWxzPVRydWUsIG5vZGVfc2l6ZT0zMDAwLCBub2RlX2NvbG9yPSJsaWdodGJsdWUiLCBmb250X3NpemU9MTAsIGZvbnRfd2VpZ2h0PSJib2xkIikNCnBsdC5zaG93KCkNCmBgYA0KDQojIyMgU3VtbWFyeQ0KLSAqKkFzc29jaWF0aW9uIHJ1bGUgbWluaW5nKiogZmluZHMgY28tb2NjdXJyZW5jZSBwYXR0ZXJucyBpbiB0cmFuc2FjdGlvbiBkYXRhLg0KLSAqKlN1cHBvcnQqKiBhbmQgKipjb25maWRlbmNlKiogaGVscCBxdWFudGlmeSBob3cgc3Ryb25nbHkgaXRlbXMgYXJlIGFzc29jaWF0ZWQuDQotICoqQXByaW9yaSBQcmluY2lwbGUqKiBhbmQgKipjYW5kaWRhdGUgbGF0dGljZSoqIHJlZHVjZSBjb21wdXRhdGlvbmFsIGxvYWQsIGVuYWJsaW5nIHNjYWxhYmxlIGFuYWx5c2lzLiANCg0KDQojIyMjIDQuIEVmZmljaWVudCBDb21wdXRhdGlvbiB3aXRoIHRoZSBBcHJpb3JpIFByaW5jaXBsZQ0KVGhlIEFwcmlvcmkgYWxnb3JpdGhtIG9wdGltaXplcyBmcmVxdWVudCBpdGVtc2V0IGdlbmVyYXRpb24gYnkgZWxpbWluYXRpbmcgbm9uLWZyZXF1ZW50IGl0ZW1zZXRzIGFuZCB0aGVpciBzdXBlcnNldHMgZWFybHkgaW4gdGhlIHByb2Nlc3MuIFRoaXMgcmVkdWNlcyB0aGUgc2VhcmNoIHNwYWNlIGFuZCBtYWtlcyBydWxlIGdlbmVyYXRpb24gY29tcHV0YXRpb25hbGx5IGZlYXNpYmxlIGZvciBsYXJnZSBkYXRhc2V0cywgb2Z0ZW4gdmlzdWFsaXplZCBhcyBhICoqY2FuZGlkYXRlIGxhdHRpY2UqKiB0byB0cmFjayB0aGUgaGllcmFyY2hpY2FsIHByb2dyZXNzaW9uIG9mIGl0ZW0gY29tYmluYXRpb25zLg0KDQojIyMjIDUuIEltcGxlbWVudGF0aW9uIEV4YW1wbGUgaW4gUHl0aG9uDQoNClRvIGNvbXB1dGUgc3VwcG9ydCBhbmQgY29uZmlkZW5jZSBmb3Igc3BlY2lmaWMgcnVsZXMgaW4gUHl0aG9uOg0KDQpgYGBweXRob24NCmZyb20gaXRlcnRvb2xzIGltcG9ydCBjb21iaW5hdGlvbnMNCg0KIyBFeGFtcGxlIHRyYW5zYWN0aW9ucw0KdHJhbnNhY3Rpb25zID0gWw0KICAgIHsnbWlsaycsICdicmVhZCcsICdiZWVyJ30sDQogICAgeydtaWxrJywgJ2RpYXBlcnMnLCAnYmVlcid9LA0KICAgIHsnbWlsaycsICdicmVhZCcsICdkaWFwZXJzJ30sDQogICAgeydicmVhZCcsICdkaWFwZXJzJywgJ2JlZXInfSwNCiAgICB7J21pbGsnLCAnYnJlYWQnfQ0KXQ0KDQojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBzdXBwb3J0DQpkZWYgc3VwcG9ydChpdGVtc2V0LCB0cmFuc2FjdGlvbnMpOg0KICAgIGNvdW50ID0gc3VtKDEgZm9yIHRyYW5zYWN0aW9uIGluIHRyYW5zYWN0aW9ucyBpZiBpdGVtc2V0Lmlzc3Vic2V0KHRyYW5zYWN0aW9uKSkNCiAgICByZXR1cm4gY291bnQgLyBsZW4odHJhbnNhY3Rpb25zKQ0KDQojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBjb25maWRlbmNlDQpkZWYgY29uZmlkZW5jZShpdGVtc2V0X1gsIGl0ZW1zZXRfWSwgdHJhbnNhY3Rpb25zKToNCiAgICByZXR1cm4gc3VwcG9ydChpdGVtc2V0X1ggfCBpdGVtc2V0X1ksIHRyYW5zYWN0aW9ucykgLyBzdXBwb3J0KGl0ZW1zZXRfWCwgdHJhbnNhY3Rpb25zKQ0KDQojIENhbGN1bGF0ZSBzdXBwb3J0IGFuZCBjb25maWRlbmNlIGZvciBhIHJ1bGUNCml0ZW1zZXRfWCA9IHsnbWlsaycsICdkaWFwZXJzJ30NCml0ZW1zZXRfWSA9IHsnYmVlcid9DQoNCnJ1bGVfc3VwcG9ydCA9IHN1cHBvcnQoaXRlbXNldF9YIHwgaXRlbXNldF9ZLCB0cmFuc2FjdGlvbnMpDQpydWxlX2NvbmZpZGVuY2UgPSBjb25maWRlbmNlKGl0ZW1zZXRfWCwgaXRlbXNldF9ZLCB0cmFuc2FjdGlvbnMpDQoNCnByaW50KCJTdXBwb3J0OiIsIHJ1bGVfc3VwcG9ydCkNCnByaW50KCJDb25maWRlbmNlOiIsIHJ1bGVfY29uZmlkZW5jZSkNCmBgYA0KDQpUaGlzIGNvZGUgcHJvdmlkZXMgYSBzdHJ1Y3R1cmVkIHdheSB0byBjYWxjdWxhdGUgc3VwcG9ydCBhbmQgY29uZmlkZW5jZSBmb3IgYSBydWxlIHN1Y2ggYXMgYHttaWxrLCBkaWFwZXJzfSDihpIgYmVlcmAsIGhlbHBpbmcgaWRlbnRpZnkgbWVhbmluZ2Z1bCBhc3NvY2lhdGlvbnMgaW4gdHJhbnNhY3Rpb24gZGF0YS4NCg0KIyMjIyA2LiBWaXN1YWxpemluZyBDYW5kaWRhdGUgTGF0dGljZXMNCg0KVGhlIGNhbmRpZGF0ZSBsYXR0aWNlIG9yZ2FuaXplcyBpdGVtc2V0cyBieSB0aGVpciBmcmVxdWVuY3ksIGFpZGluZyBpbiB0aGUgYXBwbGljYXRpb24gb2YgdGhlIEFwcmlvcmkgcHJpbmNpcGxlLiBWaXN1YWxpemluZyBpdCB3aXRoIE5ldHdvcmtYOg0KDQpgYGBweXRob24NCmltcG9ydCBuZXR3b3JreCBhcyBueA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQpHID0gbnguRGlHcmFwaCgpDQpHLmFkZF9lZGdlc19mcm9tKFsNCiAgICAoIkEiLCAiQUIiKSwgKCJBIiwgIkFDIiksICgiQiIsICJBQiIpLCANCiAgICAoIkIiLCAiQkMiKSwgKCJDIiwgIkFDIiksICgiQyIsICJCQyIpLA0KICAgICgiQUIiLCAiQUJDIiksICgiQUMiLCAiQUJDIiksICgiQkMiLCAiQUJDIikNCl0pDQoNCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCwgNikpDQpueC5kcmF3KEcsIHdpdGhfbGFiZWxzPVRydWUsIG5vZGVfc2l6ZT0zMDAwLCBmb250X3NpemU9MTAsIGZvbnRfd2VpZ2h0PSJib2xkIikNCnBsdC5zaG93KCkNCmBgYA0KDQoNCiMjIyBSZWNhcA0KQXNzb2NpYXRpb24gcnVsZSBtaW5pbmcsIHN1cHBvcnRlZCBieSBlZmZpY2llbnQgaXRlbXNldCBnZW5lcmF0aW9uIHRocm91Z2ggdGhlIEFwcmlvcmkgcHJpbmNpcGxlIGFuZCBjYW5kaWRhdGUgbGF0dGljZXMsIGVuYWJsZXMgc2NhbGFibGUgYW5hbHlzaXMgb2YgdHJhbnNhY3Rpb25hbCBkYXRhLCByZXZlYWxpbmcgcGF0dGVybnMgd2l0aCBwcmFjdGljYWwgYXBwbGljYXRpb25zIGFjcm9zcyBpbmR1c3RyaWVzLg0KDQpUaGVzZSBjb25jZXB0cyBjYW4gYmUgdXNlZCB0byBpZGVudGlmeSB2YWx1YWJsZSBwYXR0ZXJucyB0aGF0IGNhbiBiZSBhcHBsaWVkIGluIGJ1c2luZXNzLCBoZWFsdGhjYXJlLCBhbmQgb3RoZXIgZG9tYWlucyByZXF1aXJpbmcgY28tb2NjdXJyZW5jZSBhbmFseXNpcy4NCg==