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
- 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.
- 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.
- 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
- Frequent Itemset Generation:
- Generate all itemsets that meet a minimum support threshold, a
process that is computationally intensive.
- Rule Generation:
- Use the frequent itemsets to create association rules, ensuring they
meet both minimum support and confidence thresholds.
- 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
- Association rule mining finds co-occurrence
patterns in transaction data.
- Support and confidence help
quantify how strongly items are associated.
- Apriori Principle and candidate
lattice reduce computational load, enabling scalable
analysis.
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==