Categorical Features

Most machine learning algorithms cannot handle categorical features (variables) unless they are converted to numerical values. The algorithms that cannot directly use categorical features are those that calculate distances in the feature space. That includes kNN, SVM (Support Vector Machine), ANN (Artificial Neural Network), k-means, and Regression. The Naive Bayes Classifier, Decision Trees, Decision Rules, Association Rules do not require an encoding of categorical variables.

Categorical features can be divided into two categories:

  • Ordinal (some ordering or ranking), e.g., education
  • Nominal (no particular order), e.g., gender, race, ethnicity

Encoding Mechanisms

Ordinal categorical features:

  • Label Encoding
  • Boolean Encoding

Nominal categorical features:

  • One-Hot Encoding
  • Count and Frequency Encoding
  • Weight-of-Evidence Encoding

Encoding Ordinal Features

Ordinal categorical variables can be converted to a numeric scale where the distances between the values reflect the differences in ranks. For example, we could encode education with categorical values (also called class levels) of None, High School, Bachelor, Masters, Doctorate as 0, 1, 3, 6, and 11. Of course, someone else might choose the encoding 1, 2, 3, 5, 8. The actual encoding is more of an art than a science.

This approach is often called “Label Encoding”.

Encoding Binary Features

Binary features are categorical features that have two possible values: true and false. Such features can be encoded with numeric values of 0 (false) and 1 (true).

Encoding Nominal Features

Nominal features are multinomial variables with values from a defined set of values without any ordering or ranking of the values and thus no definition of a “distance”. A simple example is gender where there are pre-defined values including female, male, non-binary, etc. In this situation it does not make sense to assign a numeric value to the class levels.

Note that some data sets might choose to use a numeric encoding of nominal features for performance reasons and to save storage, but that does not imply an ordering. It is critical that the analyst understands that a numeric feature does not always mean that it is numeric; it could be categorical.

Furthermore, all machine learning algorithms will gladly perform their calculations using numerically encoded nominal features and produce a model. However, the results of the model are likely not usable as the assumptions that the algorithm may have made are likely not correct.

For example, kNN needs to calculate distances between observations and thus needs to perform a subtraction of corresponding feature values. What does it mean to subtract female from non-binary? If they were encoded as female = 1, male = 2, non-binary = 3, unspecified = 99, then female - non-binary = -2. What is the semantics of this? It is a meaningless calculation as subtraction is simply not a defined operation on nominal variables.

One-Hot Encoding

A common encoding scheme is “one-hot encoding” which is also often called “dummy coding”. It is quite simple as it converts the nominal feature to several new Boolean features. In essence, the nominal feature is replaced with several Boolean features (this means that the nominal feature is removed from the data set). Naturally, this increases the number of columns which may cause other issues, and should only be used when there are very few categorical variables with a small number of levels (say, three to five).

Note that encoding occurs before splitting the data into subsets for training and testing.

The technique is best explained with an example. Let’s consider a data set with the categorical feature “eye color” with possible values of {amber, blue, brown, gray, green, hazel, red}. There are 𝔩 = 7 levels. So, we need to create 𝔩-1 new columns – we always need one column less than the number of categories.

Then we create (arbitrarily) six new columns labeled by the category label; generally best to pick the first six. We then encode each value of eye color by placing a 1 into the column that matches the eye color and 0 for the other columns. The one value that does not have a column has 0 in all columns (in the example below, it is the color red).

eyecolor amber blue brown gray green hazel
blue 0 1 0 0 0 0
gray 0 0 0 1 0 0
red 0 0 0 0 0 0
amber 1 0 0 0 0 0
brown 0 0 1 0 0 0
green 0 0 0 0 1 0
hazel 0 0 0 0 0 1
red 0 0 0 0 0 0

One-hot encoding can result in a substantial number of additional features (columns) if there are many levels in the categorical variable. Many machine learning algorithms do not perform well if there are too many features, i.e., kNN, k-means, statistical regression, among others. When using such “distance-based” algorithms, one of the other encodings below might be preferable.

Removing Categorical Features

This process is done independently for each nominal feature. This can result in a very large number of additional Boolean features which may overwhelm some algorithms.

Sometimes it may be more beneficial to remove some or all categorical features, particularly for those algorithms that calculate distance such as kNN and SVM.

Count or Frequency Encoding

Replace the categorical feature by the count of the observations that show that category in the dataset or by the frequency – or percentage – of observations in the dataset. For example, if 10 of 100 observations show the eye color green, we would replace any eye color value of green by 10 if doing count encoding, or by 0.1 if doing frequency encoding. The categorical variable is now a single-column numeric variable and can be used by distance-based algorithms such as kNN or regression.

“Weight of Evidence” Encoding

This approach is only applicable for binary classification models where the target variable is binary (positive or negative.) For example, in a disease classification scenario, the target variable has the value positive (1) if the disease is present and negative (0) otherwise. Each categorical feature is replaced by

\(\log_2\left(\frac{P\left(pos\right)}{P\left(neg\right)}\right)\)

where \(P(pos)\) is the probability of positive value for the target variable and \(P(neg)\) is the probability of negative target variable of each category in categorical variable.

Using the ubiquitous “titanic” dataset as an example, assuming “Survived” as target variable, one of the categorical variables is “Cabin”, which can be encoded using Weight of Evidence as shown below.

Weight of Evidence Example Using Titanic Dataset
Weight of Evidence Example Using Titanic Dataset

This encoding mechanism is particularly well suited for logistic regression because the logit function is the odds ratio of \(P(1)/P(0)\).

If it isn’t already clear, naturally you will need to remove the original categorical value column from the dataset.

Weight-of-Evidence vs Frequency Encoding

Both weight of evidence (WOE) encoding and frequency encoding are common techniques for encoding categorical variables in the context of machine learning, and their effectiveness can vary depending on the specific dataset and the nature of the categorical variables.

Weight of evidence encoding is commonly used in credit risk modeling and is helpful when dealing with binary classification problems. It calculates the relationship between the categories and the target variable by measuring the odds of the target variable being positive (or negative) for each category. This technique can handle imbalanced datasets and is useful when the categories have a strong association with the target variable.

Frequency encoding, on the other hand, replaces each category with the frequency of its occurrences in the dataset. This can be particularly useful for dealing with high cardinality categorical variables. It simplifies the data representation and can help capture information about the categories based on their frequency.

Ultimately, the choice between WOE encoding and frequency encoding depends on the characteristics of the dataset and the specific machine learning problem at hand. It may often be an appropriate practice to experiment with different encoding methods and compare their performance using cross-validation or other evaluation metrics to determine which one works better for a given modeling effort.

Conclusion

The handling of categorical features before applying any kind of machine learning algorithm is a critical step in feature engineering. While most of the encoding techniques can be applied, some work better for some algorithms. Weight-of-Evidence works best with Logistic Regression and with some non-linear models such as Decision Tree. If there are only a few categorical features with a small number of class levels then one-hot encoding works just fine. Frequency Count is useful when there are many nominal categorical variables. For ordinal categorical data, simple Label Encoding can be used.


Files & Resources

All Files for Lesson 3.207

References

No references.

Errata

None collected yet. Let us know.

LS0tCnRpdGxlOiAiRW5jb2RpbmcgQ2F0ZWdvcmljYWwgRmVhdHVyZXMiCnBhcmFtczoKICBjYXRlZ29yeTogMwogIHN0YWNrczogMAogIG51bWJlcjogMjA3CiAgdGltZTogNDUKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiBjYXRlZ29yaWNhbCBmZWF0dXJlcyxmZWF0dXJlIGVuZ2luZWVyaW5nLG9uZS1ob3QsZnJlcXVlbmN5CiAgZGVzY3JpcHRpb246ICJFeHBsYWlucyB0aGUgdmFyaW91cyBlbmNvZGluZyBzY2hlbWVzIGZvciBjYXRlZ29yaWNhbAogICAgICAgICAgICAgICAgdmFyaWFibGVzLCBpbmNsdWRpbmcgb25lLWhvdCwgZnJlcXVlbmN5LCBhbmQgd2VpZ2h0IG9mCiAgICAgICAgICAgICAgICBldmlkZW5jZS4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIENhdGVnb3JpY2FsIEZlYXR1cmVzCgpNb3N0IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyBjYW5ub3QgaGFuZGxlIGNhdGVnb3JpY2FsIGZlYXR1cmVzICh2YXJpYWJsZXMpIHVubGVzcyB0aGV5IGFyZSBjb252ZXJ0ZWQgdG8gbnVtZXJpY2FsIHZhbHVlcy4gVGhlIGFsZ29yaXRobXMgdGhhdCBjYW5ub3QgZGlyZWN0bHkgdXNlIGNhdGVnb3JpY2FsIGZlYXR1cmVzIGFyZSB0aG9zZSB0aGF0IGNhbGN1bGF0ZSBkaXN0YW5jZXMgaW4gdGhlIGZlYXR1cmUgc3BhY2UuIFRoYXQgaW5jbHVkZXMga05OLCBTVk0gKFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUpLCBBTk4gKEFydGlmaWNpYWwgTmV1cmFsIE5ldHdvcmspLCBrLW1lYW5zLCBhbmQgUmVncmVzc2lvbi4gVGhlIE5haXZlIEJheWVzIENsYXNzaWZpZXIsIERlY2lzaW9uIFRyZWVzLCBEZWNpc2lvbiBSdWxlcywgQXNzb2NpYXRpb24gUnVsZXMgZG8gbm90IHJlcXVpcmUgYW4gZW5jb2Rpbmcgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzLgoKQ2F0ZWdvcmljYWwgZmVhdHVyZXMgY2FuIGJlIGRpdmlkZWQgaW50byB0d28gY2F0ZWdvcmllczoKCi0gICBPcmRpbmFsIChzb21lIG9yZGVyaW5nIG9yIHJhbmtpbmcpLCAqZS5nLiosIGVkdWNhdGlvbgotICAgTm9taW5hbCAobm8gcGFydGljdWxhciBvcmRlciksICplLmcuKiwgZ2VuZGVyLCByYWNlLCBldGhuaWNpdHkKCiMjIyBFbmNvZGluZyBNZWNoYW5pc21zCgpPcmRpbmFsIGNhdGVnb3JpY2FsIGZlYXR1cmVzOgoKLSAgIExhYmVsIEVuY29kaW5nCi0gICBCb29sZWFuIEVuY29kaW5nCgpOb21pbmFsIGNhdGVnb3JpY2FsIGZlYXR1cmVzOgoKLSAgIE9uZS1Ib3QgRW5jb2RpbmcKLSAgIENvdW50IGFuZCBGcmVxdWVuY3kgRW5jb2RpbmcKLSAgIFdlaWdodC1vZi1FdmlkZW5jZSBFbmNvZGluZwoKIyMgRW5jb2RpbmcgT3JkaW5hbCBGZWF0dXJlcwoKT3JkaW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgY2FuIGJlIGNvbnZlcnRlZCB0byBhIG51bWVyaWMgc2NhbGUgd2hlcmUgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZSB2YWx1ZXMgcmVmbGVjdCB0aGUgZGlmZmVyZW5jZXMgaW4gcmFua3MuIEZvciBleGFtcGxlLCB3ZSBjb3VsZCBlbmNvZGUgZWR1Y2F0aW9uIHdpdGggY2F0ZWdvcmljYWwgdmFsdWVzIChhbHNvIGNhbGxlZCAqY2xhc3MgbGV2ZWxzKikgb2YgKk5vbmUqLCAqSGlnaCBTY2hvb2wqLCAqQmFjaGVsb3IqLCAqTWFzdGVycyosICpEb2N0b3JhdGUqIGFzIDAsIDEsIDMsIDYsIGFuZCAxMS4gT2YgY291cnNlLCBzb21lb25lIGVsc2UgbWlnaHQgY2hvb3NlIHRoZSBlbmNvZGluZyAxLCAyLCAzLCA1LCA4LiBUaGUgYWN0dWFsIGVuY29kaW5nIGlzIG1vcmUgb2YgYW4gYXJ0IHRoYW4gYSBzY2llbmNlLgoKVGhpcyBhcHByb2FjaCBpcyBvZnRlbiBjYWxsZWQgIkxhYmVsIEVuY29kaW5nIi4KCiMjIyBFbmNvZGluZyBCaW5hcnkgRmVhdHVyZXMKCkJpbmFyeSBmZWF0dXJlcyBhcmUgY2F0ZWdvcmljYWwgZmVhdHVyZXMgdGhhdCBoYXZlIHR3byBwb3NzaWJsZSB2YWx1ZXM6ICp0cnVlKiBhbmQgKmZhbHNlKi4gU3VjaCBmZWF0dXJlcyBjYW4gYmUgZW5jb2RlZCB3aXRoIG51bWVyaWMgdmFsdWVzIG9mIDAgKCpmYWxzZSopIGFuZCAxICgqdHJ1ZSopLgoKIyMgRW5jb2RpbmcgTm9taW5hbCBGZWF0dXJlcwoKTm9taW5hbCBmZWF0dXJlcyBhcmUgbXVsdGlub21pYWwgdmFyaWFibGVzIHdpdGggdmFsdWVzIGZyb20gYSBkZWZpbmVkIHNldCBvZiB2YWx1ZXMgd2l0aG91dCBhbnkgb3JkZXJpbmcgb3IgcmFua2luZyBvZiB0aGUgdmFsdWVzIGFuZCB0aHVzIG5vIGRlZmluaXRpb24gb2YgYSAiZGlzdGFuY2UiLiBBIHNpbXBsZSBleGFtcGxlIGlzIGdlbmRlciB3aGVyZSB0aGVyZSBhcmUgcHJlLWRlZmluZWQgdmFsdWVzIGluY2x1ZGluZyBmZW1hbGUsIG1hbGUsIG5vbi1iaW5hcnksIGV0Yy4gSW4gdGhpcyBzaXR1YXRpb24gaXQgZG9lcyBub3QgbWFrZSBzZW5zZSB0byBhc3NpZ24gYSBudW1lcmljIHZhbHVlIHRvIHRoZSBjbGFzcyBsZXZlbHMuCgpOb3RlIHRoYXQgc29tZSBkYXRhIHNldHMgbWlnaHQgY2hvb3NlIHRvIHVzZSBhIG51bWVyaWMgZW5jb2Rpbmcgb2Ygbm9taW5hbCBmZWF0dXJlcyBmb3IgcGVyZm9ybWFuY2UgcmVhc29ucyBhbmQgdG8gc2F2ZSBzdG9yYWdlLCBidXQgdGhhdCBkb2VzIG5vdCBpbXBseSBhbiBvcmRlcmluZy4gSXQgaXMgY3JpdGljYWwgdGhhdCB0aGUgYW5hbHlzdCB1bmRlcnN0YW5kcyB0aGF0IGEgbnVtZXJpYyBmZWF0dXJlIGRvZXMgbm90IGFsd2F5cyBtZWFuIHRoYXQgaXQgaXMgbnVtZXJpYzsgaXQgY291bGQgYmUgY2F0ZWdvcmljYWwuCgpGdXJ0aGVybW9yZSwgYWxsIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyB3aWxsIGdsYWRseSBwZXJmb3JtIHRoZWlyIGNhbGN1bGF0aW9ucyB1c2luZyBudW1lcmljYWxseSBlbmNvZGVkIG5vbWluYWwgZmVhdHVyZXMgYW5kIHByb2R1Y2UgYSBtb2RlbC4gSG93ZXZlciwgdGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVsIGFyZSBsaWtlbHkgbm90IHVzYWJsZSBhcyB0aGUgYXNzdW1wdGlvbnMgdGhhdCB0aGUgYWxnb3JpdGhtIG1heSBoYXZlIG1hZGUgYXJlIGxpa2VseSBub3QgY29ycmVjdC4KCkZvciBleGFtcGxlLCBrTk4gbmVlZHMgdG8gY2FsY3VsYXRlIGRpc3RhbmNlcyBiZXR3ZWVuIG9ic2VydmF0aW9ucyBhbmQgdGh1cyBuZWVkcyB0byBwZXJmb3JtIGEgc3VidHJhY3Rpb24gb2YgY29ycmVzcG9uZGluZyBmZWF0dXJlIHZhbHVlcy4gV2hhdCBkb2VzIGl0IG1lYW4gdG8gc3VidHJhY3QgZmVtYWxlIGZyb20gbm9uLWJpbmFyeT8gSWYgdGhleSB3ZXJlIGVuY29kZWQgYXMgZmVtYWxlID0gMSwgbWFsZSA9IDIsIG5vbi1iaW5hcnkgPSAzLCB1bnNwZWNpZmllZCA9IDk5LCB0aGVuIGZlbWFsZSAtIG5vbi1iaW5hcnkgPSAtMi4gV2hhdCBpcyB0aGUgc2VtYW50aWNzIG9mIHRoaXM/IEl0IGlzIGEgbWVhbmluZ2xlc3MgY2FsY3VsYXRpb24gYXMgc3VidHJhY3Rpb24gaXMgc2ltcGx5IG5vdCBhIGRlZmluZWQgb3BlcmF0aW9uIG9uIG5vbWluYWwgdmFyaWFibGVzLgoKIyMjIE9uZS1Ib3QgRW5jb2RpbmcKCkEgY29tbW9uIGVuY29kaW5nIHNjaGVtZSBpcyAib25lLWhvdCBlbmNvZGluZyIgd2hpY2ggaXMgYWxzbyBvZnRlbiBjYWxsZWQgImR1bW15IGNvZGluZyIuIEl0IGlzIHF1aXRlIHNpbXBsZSBhcyBpdCBjb252ZXJ0cyB0aGUgbm9taW5hbCBmZWF0dXJlIHRvIHNldmVyYWwgbmV3IEJvb2xlYW4gZmVhdHVyZXMuIEluIGVzc2VuY2UsIHRoZSBub21pbmFsIGZlYXR1cmUgaXMgcmVwbGFjZWQgd2l0aCBzZXZlcmFsIEJvb2xlYW4gZmVhdHVyZXMgKHRoaXMgbWVhbnMgdGhhdCB0aGUgbm9taW5hbCBmZWF0dXJlIGlzIHJlbW92ZWQgZnJvbSB0aGUgZGF0YSBzZXQpLiBOYXR1cmFsbHksIHRoaXMgaW5jcmVhc2VzIHRoZSBudW1iZXIgb2YgY29sdW1ucyB3aGljaCBtYXkgY2F1c2Ugb3RoZXIgaXNzdWVzLCBhbmQgc2hvdWxkIG9ubHkgYmUgdXNlZCB3aGVuIHRoZXJlIGFyZSB2ZXJ5IGZldyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBhIHNtYWxsIG51bWJlciBvZiBsZXZlbHMgKHNheSwgdGhyZWUgdG8gZml2ZSkuCgo+IE5vdGUgdGhhdCBlbmNvZGluZyBvY2N1cnMgYmVmb3JlIHNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIHN1YnNldHMgZm9yIHRyYWluaW5nIGFuZCB0ZXN0aW5nLgoKVGhlIHRlY2huaXF1ZSBpcyBiZXN0IGV4cGxhaW5lZCB3aXRoIGFuIGV4YW1wbGUuIExldCdzIGNvbnNpZGVyIGEgZGF0YSBzZXQgd2l0aCB0aGUgY2F0ZWdvcmljYWwgZmVhdHVyZSAiZXllIGNvbG9yIiB3aXRoIHBvc3NpYmxlIHZhbHVlcyBvZiB7YW1iZXIsIGJsdWUsIGJyb3duLCBncmF5LCBncmVlbiwgaGF6ZWwsIHJlZH0uIFRoZXJlIGFyZSDwnZSpID0gNyBsZXZlbHMuIFNvLCB3ZSBuZWVkIHRvIGNyZWF0ZSDwnZSpLTEgbmV3IGNvbHVtbnMgLS0gd2UgYWx3YXlzIG5lZWQgb25lIGNvbHVtbiBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBjYXRlZ29yaWVzLgoKVGhlbiB3ZSBjcmVhdGUgKGFyYml0cmFyaWx5KSBzaXggbmV3IGNvbHVtbnMgbGFiZWxlZCBieSB0aGUgY2F0ZWdvcnkgbGFiZWw7IGdlbmVyYWxseSBiZXN0IHRvIHBpY2sgdGhlIGZpcnN0IHNpeC4gV2UgdGhlbiBlbmNvZGUgZWFjaCB2YWx1ZSBvZiBleWUgY29sb3IgYnkgcGxhY2luZyBhIDEgaW50byB0aGUgY29sdW1uIHRoYXQgbWF0Y2hlcyB0aGUgZXllIGNvbG9yIGFuZCAwIGZvciB0aGUgb3RoZXIgY29sdW1ucy4gVGhlIG9uZSB2YWx1ZSB0aGF0IGRvZXMgbm90IGhhdmUgYSBjb2x1bW4gaGFzIDAgaW4gYWxsIGNvbHVtbnMgKGluIHRoZSBleGFtcGxlIGJlbG93LCBpdCBpcyB0aGUgY29sb3IgKnJlZCopLgoKfCBleWVjb2xvciB8IGFtYmVyIHwgYmx1ZSB8IGJyb3duIHwgZ3JheSB8IGdyZWVuIHwgaGF6ZWwgfAp8Oi0tLS0tLS0tOnw6LS0tLS06fDotLS0tOnw6LS0tLS06fDotLS0tOnw6LS0tLS06fDotLS0tLTp8CnwgICBibHVlICAgfCAgIDAgICB8ICAxICAgfCAgIDAgICB8ICAwICAgfCAgIDAgICB8ICAgMCAgIHwKfCAgIGdyYXkgICB8ICAgMCAgIHwgIDAgICB8ICAgMCAgIHwgIDEgICB8ICAgMCAgIHwgICAwICAgfAp8ICAgcmVkICAgIHwgICAwICAgfCAgMCAgIHwgICAwICAgfCAgMCAgIHwgICAwICAgfCAgIDAgICB8CnwgIGFtYmVyICAgfCAgIDEgICB8ICAwICAgfCAgIDAgICB8ICAwICAgfCAgIDAgICB8ICAgMCAgIHwKfCAgYnJvd24gICB8ICAgMCAgIHwgIDAgICB8ICAgMSAgIHwgIDAgICB8ICAgMCAgIHwgICAwICAgfAp8ICBncmVlbiAgIHwgICAwICAgfCAgMCAgIHwgICAwICAgfCAgMCAgIHwgICAxICAgfCAgIDAgICB8CnwgIGhhemVsICAgfCAgIDAgICB8ICAwICAgfCAgIDAgICB8ICAwICAgfCAgIDAgICB8ICAgMSAgIHwKfCAgIHJlZCAgICB8ICAgMCAgIHwgIDAgICB8ICAgMCAgIHwgIDAgICB8ICAgMCAgIHwgICAwICAgfAoKT25lLWhvdCBlbmNvZGluZyBjYW4gcmVzdWx0IGluIGEgc3Vic3RhbnRpYWwgbnVtYmVyIG9mIGFkZGl0aW9uYWwgZmVhdHVyZXMgKGNvbHVtbnMpIGlmIHRoZXJlIGFyZSBtYW55IGxldmVscyBpbiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUuIE1hbnkgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIGRvIG5vdCBwZXJmb3JtIHdlbGwgaWYgdGhlcmUgYXJlIHRvbyBtYW55IGZlYXR1cmVzLCAqaS5lLiosIGtOTiwgay1tZWFucywgc3RhdGlzdGljYWwgcmVncmVzc2lvbiwgYW1vbmcgb3RoZXJzLiBXaGVuIHVzaW5nIHN1Y2ggImRpc3RhbmNlLWJhc2VkIiBhbGdvcml0aG1zLCBvbmUgb2YgdGhlIG90aGVyIGVuY29kaW5ncyBiZWxvdyBtaWdodCBiZSBwcmVmZXJhYmxlLgoKIyMjIFJlbW92aW5nIENhdGVnb3JpY2FsIEZlYXR1cmVzCgpUaGlzIHByb2Nlc3MgaXMgZG9uZSBpbmRlcGVuZGVudGx5IGZvciBlYWNoIG5vbWluYWwgZmVhdHVyZS4gVGhpcyBjYW4gcmVzdWx0IGluIGEgdmVyeSBsYXJnZSBudW1iZXIgb2YgYWRkaXRpb25hbCBCb29sZWFuIGZlYXR1cmVzIHdoaWNoIG1heSBvdmVyd2hlbG0gc29tZSBhbGdvcml0aG1zLgoKU29tZXRpbWVzIGl0IG1heSBiZSBtb3JlIGJlbmVmaWNpYWwgdG8gcmVtb3ZlIHNvbWUgb3IgYWxsIGNhdGVnb3JpY2FsIGZlYXR1cmVzLCBwYXJ0aWN1bGFybHkgZm9yIHRob3NlIGFsZ29yaXRobXMgdGhhdCBjYWxjdWxhdGUgZGlzdGFuY2Ugc3VjaCBhcyAqa05OKiBhbmQgKlNWTSouCgojIyMgQ291bnQgb3IgRnJlcXVlbmN5IEVuY29kaW5nCgpSZXBsYWNlIHRoZSBjYXRlZ29yaWNhbCBmZWF0dXJlIGJ5IHRoZSBjb3VudCBvZiB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgc2hvdyB0aGF0IGNhdGVnb3J5IGluIHRoZSBkYXRhc2V0IG9yIGJ5IHRoZSBmcmVxdWVuY3kgLS0gb3IgcGVyY2VudGFnZSAtLSBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIGRhdGFzZXQuIEZvciBleGFtcGxlLCBpZiAxMCBvZiAxMDAgb2JzZXJ2YXRpb25zIHNob3cgdGhlIGV5ZSBjb2xvciAqZ3JlZW4qLCB3ZSB3b3VsZCByZXBsYWNlIGFueSBleWUgY29sb3IgdmFsdWUgb2YgKmdyZWVuKiBieSAxMCBpZiBkb2luZyBjb3VudCBlbmNvZGluZywgb3IgYnkgMC4xIGlmIGRvaW5nIGZyZXF1ZW5jeSBlbmNvZGluZy4gVGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIG5vdyBhIHNpbmdsZS1jb2x1bW4gbnVtZXJpYyB2YXJpYWJsZSBhbmQgY2FuIGJlIHVzZWQgYnkgZGlzdGFuY2UtYmFzZWQgYWxnb3JpdGhtcyBzdWNoIGFzICprTk4qIG9yICpyZWdyZXNzaW9uKi4KCiMjIyAiV2VpZ2h0IG9mIEV2aWRlbmNlIiBFbmNvZGluZwoKVGhpcyBhcHByb2FjaCBpcyBvbmx5IGFwcGxpY2FibGUgZm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgd2hlcmUgdGhlIHRhcmdldCB2YXJpYWJsZSBpcyBiaW5hcnkgKHBvc2l0aXZlIG9yIG5lZ2F0aXZlLikgRm9yIGV4YW1wbGUsIGluIGEgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiBzY2VuYXJpbywgdGhlIHRhcmdldCB2YXJpYWJsZSBoYXMgdGhlIHZhbHVlIHBvc2l0aXZlICgxKSBpZiB0aGUgZGlzZWFzZSBpcyBwcmVzZW50IGFuZCBuZWdhdGl2ZSAoMCkgb3RoZXJ3aXNlLiBFYWNoIGNhdGVnb3JpY2FsIGZlYXR1cmUgaXMgcmVwbGFjZWQgYnkKCiRcbG9nXzJcbGVmdChcZnJhY3tQXGxlZnQocG9zXHJpZ2h0KX17UFxsZWZ0KG5lZ1xyaWdodCl9XHJpZ2h0KSQKCndoZXJlICRQKHBvcykkIGlzIHRoZSBwcm9iYWJpbGl0eSBvZiBwb3NpdGl2ZSB2YWx1ZSBmb3IgdGhlIHRhcmdldCB2YXJpYWJsZSBhbmQgJFAobmVnKSQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIG5lZ2F0aXZlIHRhcmdldCB2YXJpYWJsZSBvZiBlYWNoIGNhdGVnb3J5IGluIGNhdGVnb3JpY2FsIHZhcmlhYmxlLgoKVXNpbmcgdGhlIHViaXF1aXRvdXMgInRpdGFuaWMiIGRhdGFzZXQgYXMgYW4gZXhhbXBsZSwgYXNzdW1pbmcgIlN1cnZpdmVkIiBhcyB0YXJnZXQgdmFyaWFibGUsIG9uZSBvZiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGlzICJDYWJpbiIsIHdoaWNoIGNhbiBiZSBlbmNvZGVkIHVzaW5nICpXZWlnaHQgb2YgRXZpZGVuY2UqIGFzIHNob3duIGJlbG93LgoKIVtXZWlnaHQgb2YgRXZpZGVuY2UgRXhhbXBsZSBVc2luZyBUaXRhbmljIERhdGFzZXRdKGwtMy0yMDctV29FVGFibGUucG5nKXt3aWR0aD0iNTAlIn0KClRoaXMgZW5jb2RpbmcgbWVjaGFuaXNtIGlzIHBhcnRpY3VsYXJseSB3ZWxsIHN1aXRlZCBmb3IgbG9naXN0aWMgcmVncmVzc2lvbiBiZWNhdXNlIHRoZSBsb2dpdCBmdW5jdGlvbiBpcyB0aGUgb2RkcyByYXRpbyBvZiAkUCgxKS9QKDApJC4KCj4gSWYgaXQgaXNuJ3QgYWxyZWFkeSBjbGVhciwgbmF0dXJhbGx5IHlvdSB3aWxsIG5lZWQgdG8gcmVtb3ZlIHRoZSBvcmlnaW5hbCBjYXRlZ29yaWNhbCB2YWx1ZSBjb2x1bW4gZnJvbSB0aGUgZGF0YXNldC4KCiMjIyBXZWlnaHQtb2YtRXZpZGVuY2UgdnMgRnJlcXVlbmN5IEVuY29kaW5nCgpCb3RoIHdlaWdodCBvZiBldmlkZW5jZSAoV09FKSBlbmNvZGluZyBhbmQgZnJlcXVlbmN5IGVuY29kaW5nIGFyZSBjb21tb24gdGVjaG5pcXVlcyBmb3IgZW5jb2RpbmcgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGluIHRoZSBjb250ZXh0IG9mIG1hY2hpbmUgbGVhcm5pbmcsIGFuZCB0aGVpciBlZmZlY3RpdmVuZXNzIGNhbiB2YXJ5IGRlcGVuZGluZyBvbiB0aGUgc3BlY2lmaWMgZGF0YXNldCBhbmQgdGhlIG5hdHVyZSBvZiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzLgoKV2VpZ2h0IG9mIGV2aWRlbmNlIGVuY29kaW5nIGlzIGNvbW1vbmx5IHVzZWQgaW4gY3JlZGl0IHJpc2sgbW9kZWxpbmcgYW5kIGlzIGhlbHBmdWwgd2hlbiBkZWFsaW5nIHdpdGggYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW1zLiBJdCBjYWxjdWxhdGVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgY2F0ZWdvcmllcyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZSBieSBtZWFzdXJpbmcgdGhlIG9kZHMgb2YgdGhlIHRhcmdldCB2YXJpYWJsZSBiZWluZyBwb3NpdGl2ZSAob3IgbmVnYXRpdmUpIGZvciBlYWNoIGNhdGVnb3J5LiBUaGlzIHRlY2huaXF1ZSBjYW4gaGFuZGxlIGltYmFsYW5jZWQgZGF0YXNldHMgYW5kIGlzIHVzZWZ1bCB3aGVuIHRoZSBjYXRlZ29yaWVzIGhhdmUgYSBzdHJvbmcgYXNzb2NpYXRpb24gd2l0aCB0aGUgdGFyZ2V0IHZhcmlhYmxlLgoKRnJlcXVlbmN5IGVuY29kaW5nLCBvbiB0aGUgb3RoZXIgaGFuZCwgcmVwbGFjZXMgZWFjaCBjYXRlZ29yeSB3aXRoIHRoZSBmcmVxdWVuY3kgb2YgaXRzIG9jY3VycmVuY2VzIGluIHRoZSBkYXRhc2V0LiBUaGlzIGNhbiBiZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBkZWFsaW5nIHdpdGggaGlnaCBjYXJkaW5hbGl0eSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEl0IHNpbXBsaWZpZXMgdGhlIGRhdGEgcmVwcmVzZW50YXRpb24gYW5kIGNhbiBoZWxwIGNhcHR1cmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNhdGVnb3JpZXMgYmFzZWQgb24gdGhlaXIgZnJlcXVlbmN5LgoKVWx0aW1hdGVseSwgdGhlIGNob2ljZSBiZXR3ZWVuIFdPRSBlbmNvZGluZyBhbmQgZnJlcXVlbmN5IGVuY29kaW5nIGRlcGVuZHMgb24gdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgZGF0YXNldCBhbmQgdGhlIHNwZWNpZmljIG1hY2hpbmUgbGVhcm5pbmcgcHJvYmxlbSBhdCBoYW5kLiBJdCBtYXkgb2Z0ZW4gYmUgYW4gYXBwcm9wcmlhdGUgcHJhY3RpY2UgdG8gZXhwZXJpbWVudCB3aXRoIGRpZmZlcmVudCBlbmNvZGluZyBtZXRob2RzIGFuZCBjb21wYXJlIHRoZWlyIHBlcmZvcm1hbmNlIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24gb3Igb3RoZXIgZXZhbHVhdGlvbiBtZXRyaWNzIHRvIGRldGVybWluZSB3aGljaCBvbmUgd29ya3MgYmV0dGVyIGZvciBhIGdpdmVuIG1vZGVsaW5nIGVmZm9ydC4KCiMjIENvbmNsdXNpb24KClRoZSBoYW5kbGluZyBvZiBjYXRlZ29yaWNhbCBmZWF0dXJlcyBiZWZvcmUgYXBwbHlpbmcgYW55IGtpbmQgb2YgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG0gaXMgYSBjcml0aWNhbCBzdGVwIGluIGZlYXR1cmUgZW5naW5lZXJpbmcuIFdoaWxlIG1vc3Qgb2YgdGhlIGVuY29kaW5nIHRlY2huaXF1ZXMgY2FuIGJlIGFwcGxpZWQsIHNvbWUgd29yayBiZXR0ZXIgZm9yIHNvbWUgYWxnb3JpdGhtcy4gV2VpZ2h0LW9mLUV2aWRlbmNlIHdvcmtzIGJlc3Qgd2l0aCBMb2dpc3RpYyBSZWdyZXNzaW9uIGFuZCB3aXRoIHNvbWUgbm9uLWxpbmVhciBtb2RlbHMgc3VjaCBhcyBEZWNpc2lvbiBUcmVlLiBJZiB0aGVyZSBhcmUgb25seSBhIGZldyBjYXRlZ29yaWNhbCBmZWF0dXJlcyB3aXRoIGEgc21hbGwgbnVtYmVyIG9mIGNsYXNzIGxldmVscyB0aGVuIG9uZS1ob3QgZW5jb2Rpbmcgd29ya3MganVzdCBmaW5lLiBGcmVxdWVuY3kgQ291bnQgaXMgdXNlZnVsIHdoZW4gdGhlcmUgYXJlIG1hbnkgbm9taW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEZvciBvcmRpbmFsIGNhdGVnb3JpY2FsIGRhdGEsIHNpbXBsZSBMYWJlbCBFbmNvZGluZyBjYW4gYmUgdXNlZC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmlsZXMgJiBSZXNvdXJjZXMKCmBgYHtyIHppcEZpbGVzLCBlY2hvPUZBTFNFfQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LAogICAgICAgICAgICAgICAgIHBhcmFtcyRudW1iZXIpCgp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIAogICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksIi4iLHBhcmFtcyRudW1iZXIpCgojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlIKa25pdHI6OnJhd19odG1sKGRvd25sb2FkRmlsZXNMaW5rKCIuIiwgemlwTmFtZSwgdGV4dEFMaW5rKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFJlZmVyZW5jZXMKCk5vIHJlZmVyZW5jZXMuCgojIyBFcnJhdGEKCk5vbmUgY29sbGVjdGVkIHlldC4gW0xldCB1cyBrbm93XShodHRwczovL2Zvcm0uam90Zm9ybS5jb20vMjEyMTg3MDcyNzg0MTU3KXt0YXJnZXQ9Il9ibGFuayJ9LgoKYGBge3IgY29kZT14ZnVuOjpyZWFkX3V0ZjgocGFzdGUwKGhlcmU6OmhlcmUoKSwnL1IvX2RlcGxveUtuaXQuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAK