Motivation

Approach

  1. Identify and extract relevant variables from Fingertips via fingertipsR
  2. Preprocess - missing data, convert to wide format, scale
  3. Hierarchical clustering and kmeans
  4. Statistical tests to confirm cluster numbers
  5. Calculate summary statistics for each cluster
  6. Assign practices to clusters
  7. Map
  8. Other…?

Getting data from Fingertips

We’ll use the practice profiles and other profiles with GP data (e.g. diabetes):

  • Get ProfileID for the profile
  • Get AreaTypeID for GPs
  • Identify indicators available for GP IndicatorTypeID
  • Filter relevant indicators
  • Extract data

Getting IndicatorIDs

(Note, indicator names for identical indicators vary across Fingertips data)

Filtering on indicator names

For this analysis we are interested in extracting prevalence estimates, deprivation scores and demographic variables (prevalence estimates are not age-adjusted)

We have a list of 40 indicator IDs for which we will extract data

Data extraction

We can now extract the data (NB takes a few minutes)

Summary

What have we got:

  • A data set with 2109837 rows and 26 columns
  • A data set with 57 unique indicators
  • A data set with these variables Learning disability: QOF prevalence, Stroke: QOF prevalence (all ages), Hypertension: QOF prevalence (all ages), Epilepsy: QOF prevalence (18+), Diabetes: QOF prevalence (17+), Dementia: QOF prevalence (all ages), COPD: QOF prevalence (all ages), CKD: QOF prevalence (18+), Heart Failure: QOF prevalence (all ages), CHD: QOF prevalence (all ages), Cancer: QOF prevalence (all ages), Atrial fibrillation: QOF prevalence, Asthma: QOF prevalence (all ages), Palliative/supportive care: QOF prevalence (all ages), % aged 65+ years, IDACI (Income Depr. - Children), IDAOPI (Income Depr. - Older People), % aged 0 to 4 years, % aged 5 to 14 years, % aged 75+ years, % aged 85+ years, % aged under 18 years, Depression: Recorded prevalence (aged 18+), Heart failure w LVD: QOF prevalence (all ages), Osteoporosis: QOF prevalence (50+), GP patient survey: smoking prevalence, GP patient survey: ex-smoking prevalence, Mental Health: QOF prevalence (all ages), Depression: QOF incidence (18+) - new diagnosis, CS002: Women, aged 25-64, with a record of cervical screening (last 5 yrs) , BP002: Patients, aged 45+, who have a record of blood pressure (last 5yrs), Rheumatoid Arthritis: QOF prevalence (16+), Estimated smoking prevalence (QOF), Estimated prevalence of atrial fibrillation, Deprivation score (IMD 2015), Obesity: QOF prevalence (18+), CVD-PP: QOF prevalence (30-74) , PAD: QOF prevalence (all ages), Contact with mental health or learning disability services: rate per 1,000 patients on GP practice list aged 18+ , Estimated prevalence of COPD (all ages), Estimated prevalence of diagnosed hypertension (16+), Estimated prevalence of undiagnosed hypertension (16+), Estimated prevalence of depression (all ages), Estimated prevalence of stroke (55-79 yrs), Estimated prevalence of Heart failure (16+), Estimated prevalence of CHD (55-79 yrs), Estimated prevalence of peripheral arterial disease (PAD) (55-79 yrs), Percentage of people with type 1 diabetes aged under 40 , Percentage of people with type 1 diabetes aged 40 to 64 , Percentage of people with type 1 diabetes aged 65 to 79, Percentage of people with type 1 diabetes aged 80 and over, Percentage of people with type 2 diabetes aged under 40 , Percentage of people with type 2 diabetes aged 40 to 64 , Percentage of people with type 2 diabetes aged 65 to 79, Percentage of people with type 2 diabetes aged 80 and over, % reporting a long-term MSK problem , % reporting a long-term mental health problem

Cleaning

  • We’ll remove the retired indicators
  • select latest data
  • Check for and impute missing data
  • Convert to wide and scale

Now have 57 variables for 7265 GP practices.

Correlation matrix

Can look at the relationship between variables

Cluster analysis

  • Use pam (partition around mediods) model

Cluster summaries

Geography of practice clusters

Source : https://maps.googleapis.com/maps/api/staticmap?center=London&zoom=10&size=640x640&scale=2&maptype=terrain&key=xxx-eIDkZLfLH4
Source : https://maps.googleapis.com/maps/api/geocode/json?address=London&key=xxx-eIDkZLfLH4
Saving 7 x 7 in image

Source : https://maps.googleapis.com/maps/api/staticmap?center=England&zoom=6&size=640x640&scale=2&maptype=terrain&key=xxx-eIDkZLfLH4
Source : https://maps.googleapis.com/maps/api/geocode/json?address=England&key=xxx-eIDkZLfLH4

Cluster descriptions

Cluster No Size Description Geography
1 801 Relatively deprived, “middle-aged”, higher levels of obesity and smoking, high prevalence of diabetes in younger age groups, high prevalence of cvd, msk, epilepsy, respiratory disease) Urban NE/ NW/ Midlands/ some coastal/ East London
2 687 Most deprived, “middle-aged”, highest levels of obesity and smoking, high prevalence of diabetes in younger age groups, high prevalence of mental health problems and respiratory disease Similar to cluster 1: Urban NE/ NW/ Midlands/ some coastal/ East London
3 1632 Average - “middle age structure”, not deprived, average levels of obesity, smoking, average prevalence for most disease Suburban NE/ NW/ Midlands/ some coastal/London/ South coast
4 296 Relatively deprived, most “middle-aged”, low levels of obesity but higher smoking rates, high prevalence of diabetes in younger age groups, low prevalence of cvd, msk, epilepsy, respiratory disease), higher levels of mental health problems Mostly suburban London
5 433 Deprived, youngest population, higher levels of obesity and smoking, high prevalence of diabetes in younger age groups, high prevalence of cvd, msk, epilepsy, respiratory disease) Similar to cluster 4
6 537 Not deprived, oldest population, lower levels of obesity and smoking, high prevalence of diabetes in younger age groups, high prevalence of cvd, msk, epilepsy, respiratory disease) Largely coastal areas across England
7 594 Similar to 5.Deprived, youngest population, higher levels of obesity and smoking, high prevalence of diabetes in younger age groups, high prevalence of cvd, msk, epilepsy, respiratory disease Geography similar to 1
8 442 Similar to 4 but less deprived, more middle-aged population, lower levels of obesity and smoking, high prevalence of diabetes in older age groups, lower prevalence of cvd, msk, epilepsy, respiratory disease Largely central London
9 1121 Similar to 6. Not Deprived, older population, lower levels of obesity but higher rate of smoking, high prevalence of diabetes in younger age groups, high prevalence of cvd, msk, epilepsy, respiratory disease Geography widespread across the country
10 731 Not deprived, younger population, low rates of risk factors and prevalence across range of diseases South East England

Conclusions

  • Through simple clustering approaches we have been able define a number of distinct GP population phenotypes. For the purposes of this analysis we use co-morbidity to mean the co-existence of high prevalence across a range of diseases in the same population. Using this definition we can distinguish 4 groups of practice populations with high levels of co-morbidity:

  • Cluster 1 which is deprived, with high rates of smoking and obesity and is geographically concentrated in urban NE, NW and West Midlands, and East London
  • Cluster 2 which is similar to Cluster 1 but more deprived, with high rates of smoking and obesity and is geographically concentrated in urban NE, NW and West Midlands, and East London. The main difference from Cluster 1 is higher rates of mental health disorders, and lower rates of CVD
  • Cluster 6 which consists of largely coastal, older, non-deprived populations
  • Cluster 9 which is similar to Cluster 6 but has a different geography

  • Note that we have used crude prevalence estimates from QOF - age adjusted estimates could change the clustering. Also we could improve demographic profiling by including ethnicity estimates. Additional risk factor estimates (e.g. alcohol) could also change the clustering. The choice of 10 cluster groups is somewhat arbitrary and it is clear that some are subsets of others.

  • From an inequality perspective Cluster 1 is of most concern which appears to have high levels of co-morbidity despite having a relatively young population profile

  • London has a different set of phenotypes to the rest of the country.

Next steps

  1. This analysis identifies two kinds of practice groups with high levels of co-morbidity - one identified by virtue of its older age profile, and another by virtue of its levels of deprivation and risk factors. There are sub-clusters these groups with different geographical profiles. These practices are identifiable and may require different approaches to tackling co-morbidity or marketing initiatives to local populations.
  2. This is only a preliminary analysis - more variables could be included which could add additional insight
  3. Diseases and risk factors tend to cluster in populations - for example mental health issues in Cluster 2
  4. Can estimate approximate sizes of co-comorbid populations from practize size, QOF numerators and denominators
  5. We can use this information to cluster practices within large primary care datasets like CPRD where practices are anonymised, to try and train better prevalence models, define and refine practice population phenotypes
  6. The analysis could be further refined, and clustering over time could be assessed
  7. This could help in refining interventions to tackle inequality or specific populations

Annex

Search term

(“unsupervised machine learning”[MeSH Terms] OR (“unsupervised”[All Fields] AND “machine”[All Fields] AND “learning”[All Fields]) OR “unsupervised machine learning”[All Fields]) OR ((“cluster analysis”[MeSH Terms] OR (“cluster”[All Fields] AND “analysis”[All Fields]) OR “cluster analysis”[All Fields]) AND (“population”[MeSH Terms] OR “population”[All Fields] OR “population groups”[MeSH Terms] OR (“population”[All Fields] AND “groups”[All Fields]) OR “population groups”[All Fields]) AND segmentation[All Fields]) AND 2010[PDAT] : 2018[PDAT]

1.Yan S, Kwan YH, Tan CS, Thumboo J, Low LL. A systematic review of the clinical application of data-driven population segmentation analysis. BMC Med Res Methodol. 2018;18(1):121., 2.Leslie HH, Zhou X, Spiegelman D, Kruk ME. Health system measurement: Harnessing machine learning to advance global health. PLoS One. 2018;13(10):e0204958., 3.Cleret de Langavant L, Bayen E, Yaffe K. Unsupervised Machine Learning to Identify High Likelihood of Dementia in Population-Based Surveys: Development and Validation Study. J Med Internet Res. 2018;20(7):e10493., 4.Vuik SI, Mayer E, Darzi A. A quantitative evidence base for population health: applying utilization-based cluster analysis to segment a patient population. Popul Health Metr. 2016;14:44., 5.Demydas T. Consumer segmentation based on the level and structure of fruit and vegetable intake: an empirical evidence for US adults from the National Health and Nutrition Examination Survey (NHANES) 2005-2006. Public Health Nutr. 2011;14(6):1088-95.

LS0tDQp0aXRsZTogIlByZWNpc2lvbiBwdWJsaWMgaGVhbHRoOiBjcmVhdGluZyBHUCBtdWx0aW1vcmJpZGl0eSBjbHVzdGVycyINCnN1YnRpdGxlOiAiRXhwbG9yYXRvcnkgYW5hbHlzaXMgdXNpbmcgdW5zdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmciDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQphdXRob3I6ICJKdWxpYW4gRmxvd2VycyINCmFmZmlsaWF0aW9uOiAiUHVibGljIEhlYWx0aCBFbmdsYW5kIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQ0KaWYgKCFyZXF1aXJlKCJwaGVjaGFydHMiKSkgZGV2dG9vbHM6Omluc3RhbGxfZ2l0KCdodHRwczovL2dpdGxhYi5waGUuZ292LnVrL3BhY2thZ2VzL3BoZWNoYXJ0cycpDQoNCg0KbGlicmFyeShwYWNtYW4pDQojaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiAgICMgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQ0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJDb21wbGV4SGVhdG1hcCIsIHZlcnNpb24gPSAiMy44IikNCnBfbG9hZChmaW5nZXJ0aXBzUiwgZmluZ2VydGlwc2NoYXJ0cywgcmdpc3dzLCB0aWR5dmVyc2UsIE5iQ2x1c3QsIGZhY3RvZXh0cmEsIEZhY3RvTWluZVIsIHNraW1yLCBuYW5pYXIsIHBoZWNoYXJ0cywgIGNsdXN0ZXIpDQp0aGVtZV9zZXQodGhlbWVfcGhlKCkpDQoNCg0KYGBgDQoNCg0KDQojIE1vdGl2YXRpb24NCg0KKiBJbmNyZWFzaW5nIGltcG9ydGFuY2Ugb2YgTU0gLSBjb3N0cywgaW5lcXVhbGl0eSwgaW5jcmVhc2luZyBwcmV2YWxlbmNlDQoqIExpbWl0ZWQgZGF0YXNldHMNCiogUHJldmFsZW5jZSBlc3RpbWF0ZXMgYXQgcHJhY3RpY2UgbGV2ZWwgZnJvbSBRT0YgYXQgcHJhY3RpY2UgbGV2ZWwgYW5kIG92ZXIgdGltZQ0KKiBVc2UgdW5zdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgdG8gbG9vayBhdCBhc3NvY2lhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyA9IHByZXZhbGVuY2UsIGRlcHJpdmF0aW9uLCBzb2Npb2RlbW9ncmFwaHk/DQoNCiMjIEFwcHJvYWNoDQoNCjEuIElkZW50aWZ5IGFuZCBleHRyYWN0IHJlbGV2YW50IHZhcmlhYmxlcyBmcm9tIEZpbmdlcnRpcHMgdmlhIGBmaW5nZXJ0aXBzUmANCjIuIFByZXByb2Nlc3MgLSBtaXNzaW5nIGRhdGEsIGNvbnZlcnQgdG8gd2lkZSBmb3JtYXQsIHNjYWxlDQozLiBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhbmQga21lYW5zDQo0LiBTdGF0aXN0aWNhbCB0ZXN0cyB0byBjb25maXJtIGNsdXN0ZXIgbnVtYmVycw0KNS4gQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgZWFjaCBjbHVzdGVyDQo2LiBBc3NpZ24gcHJhY3RpY2VzIHRvIGNsdXN0ZXJzDQo3LiBNYXANCjguIE90aGVyLi4uPw0KDQohW10oUHJldmlldy5qcGcpDQoNCg0KIyMgR2V0dGluZyBkYXRhIGZyb20gRmluZ2VydGlwcw0KDQpXZSdsbCB1c2UgdGhlIHByYWN0aWNlIHByb2ZpbGVzIGFuZCBvdGhlciBwcm9maWxlcyB3aXRoIEdQIGRhdGEgKGUuZy4gZGlhYmV0ZXMpOg0KDQoqIEdldCBQcm9maWxlSUQgZm9yIHRoZSBwcm9maWxlDQoqIEdldCBBcmVhVHlwZUlEIGZvciBHUHMNCiogSWRlbnRpZnkgaW5kaWNhdG9ycyBhdmFpbGFibGUgZm9yIEdQIEluZGljYXRvclR5cGVJRA0KKiBGaWx0ZXIgcmVsZXZhbnQgaW5kaWNhdG9ycw0KKiBFeHRyYWN0IGRhdGENCg0KIyMjIEdldHRpbmcgSW5kaWNhdG9ySURzDQoNCmBgYHtyfQ0KDQojIyBQcm9maWxlcw0KcHJvZiA8LSBwcm9maWxlcygpICMjIDIwDQphcmVhcyA8LSBhcmVhX3R5cGVzKCkgIyMgNw0KDQppbmRzIDwtIGluZGljYXRvcl9hcmVhdHlwZXMoKSAlPiUNCiAgZmlsdGVyKEFyZWFUeXBlSUQgPT0gNykNCg0KaW5kczEgPC0gaW5kaWNhdG9ycygpDQoNCmluZHMyIDwtIGluZHMgJT4lDQogIGxlZnRfam9pbihpbmRzMSkgJT4lDQogIHNlbGVjdChJbmRpY2F0b3JJRDpJbmRpY2F0b3JOYW1lKSANCg0KaGVhZChpbmRzMikNCg0KDQpgYGANCg0KKE5vdGUsIGluZGljYXRvciBuYW1lcyBmb3IgaWRlbnRpY2FsIGluZGljYXRvcnMgdmFyeSBhY3Jvc3MgRmluZ2VydGlwcyBkYXRhKQ0KDQojIyMgRmlsdGVyaW5nIG9uIGluZGljYXRvciBuYW1lcw0KDQpGb3IgdGhpcyBhbmFseXNpcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBleHRyYWN0aW5nIHByZXZhbGVuY2UgZXN0aW1hdGVzLCBkZXByaXZhdGlvbiBzY29yZXMgYW5kIGRlbW9ncmFwaGljIHZhcmlhYmxlcyAocHJldmFsZW5jZSBlc3RpbWF0ZXMgYXJlIG5vdCBhZ2UtYWRqdXN0ZWQpDQoNCmBgYHtyfQ0KDQoNCmlkcyA8LSBpbmRzMiAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoSW5kaWNhdG9yTmFtZSwgInByZXZhbGVuY2V8W0RkXWVwcml8bG9uZy10ZXJtfG51cnNpbmd8YWdlZCIpKSAlPiUNCiAgcHVsbChJbmRpY2F0b3JJRCkgJT4lDQogIHVuaXF1ZSgpDQoNCg0KDQpgYGANCg0KV2UgaGF2ZSBhIGxpc3Qgb2YgNDAgaW5kaWNhdG9yIElEcyBmb3Igd2hpY2ggd2Ugd2lsbCBleHRyYWN0IGRhdGENCg0KIyMjICBEYXRhIGV4dHJhY3Rpb24NCg0KV2UgY2FuIG5vdyBleHRyYWN0IHRoZSBkYXRhIChOQiB0YWtlcyBhIGZldyBtaW51dGVzKQ0KDQpgYGB7cn0NCg0KZGF0YSA8LSBmaW5nZXJ0aXBzX2RhdGEoSW5kaWNhdG9ySUQgPSBpZHMsIEFyZWFUeXBlSUQgPSA3KQ0KDQpkYXRhICU+JSB3cml0ZV9yZHMoImdwX2NsdXN0ZXJfZGF0YS5yZHMiLCBjb21wcmVzcyA9ICJneiIpDQoNCmBgYA0KDQoNCiMjIyBTdW1tYXJ5DQoNCldoYXQgaGF2ZSB3ZSBnb3Q6DQoNCiogQSBkYXRhIHNldCB3aXRoIGByIG5yb3coZGF0YSlgIHJvd3MgYW5kIGByIG5jb2woZGF0YSlgIGNvbHVtbnMNCiogQSBkYXRhIHNldCB3aXRoIGByIGxlbmd0aCh1bmlxdWUoZGF0YSRJbmRpY2F0b3JJRCkpYCB1bmlxdWUgaW5kaWNhdG9ycw0KKiBBIGRhdGEgc2V0IHdpdGggdGhlc2UgdmFyaWFibGVzIGByIGRhdGEkSW5kaWNhdG9yTmFtZSAlPiUgdW5pcXVlKClgDQoNCiMjIyBDbGVhbmluZw0KDQoqIFdlJ2xsIHJlbW92ZSB0aGUgcmV0aXJlZCBpbmRpY2F0b3JzDQoNCmBgYHtyfQ0KDQpkYXRhIDwtIHJlYWRfcmRzKCJncF9jbHVzdGVyX2RhdGEucmRzIikNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBmaWx0ZXIoIXN0cl9kZXRlY3QoSW5kaWNhdG9yTmFtZSwgInJldGlyZWQiKSkNCg0KDQpgYGANCg0KKiBzZWxlY3QgbGF0ZXN0IGRhdGENCg0KYGBge3J9DQoNCmxhdGVzdF9kYXRhIDwtIGRhdGEgJT4lDQogIGZpbHRlcihBcmVhVHlwZSA9PSAiR1AiKSAlPiUNCiAgZ3JvdXBfYnkoSW5kaWNhdG9ySUQsIEFnZSwgU2V4KSAlPiUNCiAgZmlsdGVyKFRpbWVwZXJpb2RTb3J0YWJsZSA9PSBtYXgoVGltZXBlcmlvZFNvcnRhYmxlKSkNCg0KIyMgdmlzdWFsaXNlDQoNCg0KbGF0ZXN0X2RhdGEgJT4lDQogIGZpbHRlcihBcmVhVHlwZSA9PSAiR1AiKSAlPiUNCiAgZ2dwbG90KGFlcyhmY3RfcmV2KEluZGljYXRvck5hbWUpLCBUaW1lcGVyaW9kKSwgZmlsbCA9ICJyZWQiKSArDQogIGdlb21fdGlsZSgpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJMYXRlc3QgcGVyaW9kIGZvciBlYWNoIGluZGljYXRvciIsIA0KICAgICAgIHkgPSAiIiwgDQogICAgICAgeCA9ICIiKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKC43KSkpDQogIA0KYGBgDQoNCg0KDQoNCiogQ2hlY2sgZm9yIGFuZCBpbXB1dGUgbWlzc2luZyBkYXRhDQoNCmBgYHtyfQ0KDQpsYXRlc3RfZGF0YSA8LSBsYXRlc3RfZGF0YSAlPiUNCiAgZmlsdGVyKEFyZWFUeXBlID09ICJHUCIpICU+JQ0KICBzZWxlY3QoSW5kaWNhdG9ySUQsIEluZGljYXRvck5hbWUsIEFyZWFOYW1lLCBBcmVhQ29kZSwgQXJlYVR5cGUsICBUaW1lcGVyaW9kLCBWYWx1ZSkNCg0KbGF0ZXN0X2RhdGFfaW1wdXRlZCA8LSBsYXRlc3RfZGF0YSAlPiUNCiAgZmlsdGVyKEFyZWFUeXBlID09ICJHUCIpICU+JQ0KICBncm91cF9ieShJbmRpY2F0b3JJRCwgVGltZXBlcmlvZCkgJT4lDQogIG11dGF0ZShWYWx1ZSA9IGlmZWxzZShpcy5uYShWYWx1ZSksIG1lZGlhbihWYWx1ZSwgbmEucm0gPSBUUlVFKSwgVmFsdWUpKSANCg0KDQpgYGANCg0KKiBDb252ZXJ0IHRvIHdpZGUgYW5kIHNjYWxlDQoNCmBgYHtyfQ0KDQpzY2FsZWQgPC0gbGF0ZXN0X2RhdGFfaW1wdXRlZCAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoaW5kZXggPSBwYXN0ZShJbmRpY2F0b3JOYW1lLCBUaW1lcGVyaW9kKSkgJT4lDQogIHNlbGVjdChpbmRleCwgVmFsdWUsIEFyZWFOYW1lLCBBcmVhQ29kZSkgJT4lDQogIHNwcmVhZChpbmRleCwgVmFsdWUpICU+JQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkgaWZlbHNlKGlzLm5hKHgpLCBtZWRpYW4oeCwgbmEucm0gPSBUUlVFKSwgeCkpICU+JQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgc2NhbGUpDQoNCnNjYWxlZA0KDQoNCg0KYGBgDQoNCk5vdyBoYXZlIGByIG5jb2woc2NhbGVkKSAtMmAgIHZhcmlhYmxlcyBmb3IgYHIgbnJvdyhzY2FsZWQpYCBHUCBwcmFjdGljZXMuDQoNCg0KIyMgQ29ycmVsYXRpb24gbWF0cml4DQoNCkNhbiBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMNCg0KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KDQpzY2FsZWRbLCAtYygxOjIpXSAlPiUNCiAgY29yKCkgJT4lDQogIGNvcnJwbG90Ojpjb3JycGxvdCh0bC5jZXggPSAuNSwgdGwuY29sID0gImJsYWNrIiwgb3JkZXIgPSAiaGNsdXN0IiwgbWV0aG9kID0gImVsbGlwc2UiLCBhZGRyZWN0ID0gNikNCg0KYGBgDQoNCg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KDQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChzY2FsZWQpDQoNCmBgYA0KDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQoNCmZ2aXpfbmJjbHVzdChzY2FsZWRbLCAtYygxOjIpXSwga21lYW5zLCBrLm1heCA9IDEwLCBtZXRob2QgPSAgIndzcyIpIA0KDQoNCmBgYA0KDQoNCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCg0KaGMgPC0gaGN1dChzY2FsZWRbLCAtYygxOjIpXSkNCg0KcDEgPC0gZnZpel9kZW5kKGhjLCBzaG93X2xhYmVscyA9IEZBTFNFLCByZWN0ID0gVFJVRSkNCnAyIDwtIGZ2aXpfY2x1c3RlcihoYywgZWxsaXBzZS50eXBlID0gImNvbnZleCIpDQoNCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIpDQoNCmBgYA0KDQojIyBDbHVzdGVyIGFuYWx5c2lzDQoNCiogVXNlIFtwYW1dKGh0dHBzOi8vZW4ud2lraWJvb2tzLm9yZy93aWtpL0RhdGFfTWluaW5nX0FsZ29yaXRobXNfSW5fUi9DbHVzdGVyaW5nL1BhcnRpdGlvbmluZ19Bcm91bmRfTWVkb2lkc18oUEFNKSkgKHBhcnRpdGlvbiBhcm91bmQgbWVkaW9kcykgbW9kZWwgDQoNCmBgYHtyfQ0KDQpzZXQuc2VlZCg0MikNCmsgPC0gMTANCg0Kcm93bmFtZXMoc2NhbGVkKSA8LSBzY2FsZWQkQXJlYUNvZGUNCg0Ka20gPC0gcGFtKHNjYWxlZFssIC1jKDE6MildLCBrID0gaykNCg0KZSA8LSBmdml6X2NsdXN0ZXIoa20sIHNjYWxlZFssIC1jKDE6MildLCBlbGxpcHNlID0gVFJVRSwgZWxsaXBzZS50eXBlID0gImNvbnZleCIsIGVsbGlwc2UubGV2ZWwgPSAuOTUsIGxhYmVsc2l6ZSA9IDYsIHBhbGV0dGUgPSB2aXJpZGlzOjp2aXJpZGlzKDEwKSkNCg0KZQ0KDQpzY2FsZWRfY2x1c3RlciA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBrbSRjbHVzdGVyLCBzY2FsZWQpDQpzY2FsZV9tZWFucyA8LSBkYXRhLmZyYW1lKGttJGNlbnRlcnMpICU+JSByb3duYW1lc190b19jb2x1bW4oImNsdXN0ZXIiKQ0KDQpgYGANCg0KIyMgQ2x1c3RlciBzdW1tYXJpZXMNCg0KYGBge3IgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTEyfQ0KDQpvcHRpb25zKGRpZ2l0cyA9IDMpDQoNCmNsdXN0ZXJfc2l6ZSA8LSBrbSRjbHVzaW5mb1ssMV0NCg0KDQoNCmttX21lZCA8LSBrbSRtZWRvaWRzICU+JQ0KICBkYXRhLmZyYW1lKCkgJT4lDQogIG11dGF0ZShjbHVzdGVyID0gMToxMCkgJT4lDQogIHNlbGVjdChjbHVzdGVyLCBldmVyeXRoaW5nKCkpICU+JQ0KICBnYXRoZXIobWV0cmljLCB2YWx1ZSwgMjpuY29sKC4pKSAlPiUNCiAgbXV0YXRlKGNhdGVnb3J5ID0gY2FzZV93aGVuKHN0cl9kZXRlY3QobWV0cmljLCAiWC4uYWdlZCIpIH4gImRlbW9ncmFwaHkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QobWV0cmljLCAiW0RkXWVwciIpICYgIXN0cl9kZXRlY3QobWV0cmljLCAiW0RkXWVwcmVzcyIpIH4gImRlcHJpdmF0aW9uIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KG1ldHJpYywgInNtb2t8T2Jlc2kiKSB+ICJyaXNrX2ZhY3RvcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QobWV0cmljLCAiW0RkXWlhYmV0ZXMiKSB+ICJkaWFiZXRlcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChtZXRyaWMsICJDSER8RGVtZW50aWF8SGVhcnR8QlB8W1NzXXRyb2tlfFBBRHxbSGhdeXBlciIpIH4gImN2ZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChtZXRyaWMsICJbRGRdZXByZXNzfFtNbV1lbnRhbCIpIH4gIm1lbnRhbF9oZWFsdGgiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QobWV0cmljLCAiTVNLfEFydGh8T3N0ZW8iKSB+ICJtc2siLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QobWV0cmljLCAiQ09QRHxBc3RobWEiKSB+ICJyZXNwX2Rpc2Vhc2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAicHJldmFsZW5jZSIpKSAlPiUNCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KG1ldHJpYywgInVuZGVyLjE4IikpDQogIA0KICANCmttMSA8LSBrbV9tZWQgJT4lDQogIGZpbHRlcihjYXRlZ29yeSA9PSAiZGVtb2dyYXBoeSIpICU+JQ0KICBnZ3Bsb3QoYWVzKGZjdF9yZXYobWV0cmljKSwgZmFjdG9yKGNsdXN0ZXIpLCBmaWxsID0gdmFsdWUpKSArDQogIGdlb21fdGlsZSggY29sb3VyID0gImdyZXkiKSArIA0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikgKw0KICBmYWNldF93cmFwKH5jYXRlZ29yeSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKC44KSkpDQoNCmttMiA8LSBrbV9tZWQgJT4lDQogIGZpbHRlcihjYXRlZ29yeSA9PSAiZGVwcml2YXRpb24iKSAlPiUNCiAgZ2dwbG90KGFlcyhmY3RfcmV2KG1ldHJpYyksIGZhY3RvcihjbHVzdGVyKSwgZmlsbCA9IHZhbHVlKSkgKw0KICBnZW9tX3RpbGUoY29sb3VyID0gImdyZXkiKSArIA0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikgKw0KICBmYWNldF93cmFwKH5jYXRlZ29yeSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKC44KSkpDQoNCmttMyA8LSBrbV9tZWQgJT4lDQogIGZpbHRlcihjYXRlZ29yeSA9PSAicmlza19mYWN0b3JzIikgJT4lDQogIGdncGxvdChhZXMobWV0cmljLCBmYWN0b3IoY2x1c3RlciksIGZpbGwgPSB2YWx1ZSkpICsNCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJncmV5IikgKyANCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIpICsNCiAgZmFjZXRfd3JhcCh+Y2F0ZWdvcnkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCguOCkpKQ0KDQprbTQgPC0ga21fbWVkICU+JQ0KICBmaWx0ZXIoY2F0ZWdvcnkgPT0gImRpYWJldGVzIikgJT4lDQogIGdncGxvdChhZXMobWV0cmljLCBmYWN0b3IoY2x1c3RlciksIGZpbGwgPSB2YWx1ZSkpICsNCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJncmV5IikgKyANCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIpICsNCiAgZmFjZXRfd3JhcCh+Y2F0ZWdvcnkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCguOCkpKQ0KDQprbTUgPC0ga21fbWVkICU+JQ0KICBmaWx0ZXIoY2F0ZWdvcnkgPT0gImN2ZCIpICU+JQ0KICBnZ3Bsb3QoYWVzKG1ldHJpYywgZmFjdG9yKGNsdXN0ZXIpLCBmaWxsID0gdmFsdWUpKSArDQogIGdlb21fdGlsZShjb2xvdXIgPSAiZ3JleSIpICsgDQogIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiKSArDQogIGZhY2V0X3dyYXAofmNhdGVnb3J5KSArDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoLjgpKSkNCg0KDQprbTYgPC0ga21fbWVkICU+JQ0KICBmaWx0ZXIoY2F0ZWdvcnkgPT0gInByZXZhbGVuY2UiKSAlPiUNCiAgZ2dwbG90KGFlcyhtZXRyaWMsIGZhY3RvcihjbHVzdGVyKSwgZmlsbCA9IHZhbHVlKSkgKw0KICBnZW9tX3RpbGUoY29sb3VyID0gImdyZXkiKSArIA0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikgKw0KICBmYWNldF93cmFwKH5jYXRlZ29yeSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKC44KSkpDQoNCmttNyA8LSBrbV9tZWQgJT4lDQogIGZpbHRlcihjYXRlZ29yeSA9PSAibWVudGFsX2hlYWx0aCIpICU+JQ0KICBnZ3Bsb3QoYWVzKG1ldHJpYywgZmFjdG9yKGNsdXN0ZXIpLCBmaWxsID0gdmFsdWUpKSArDQogIGdlb21fdGlsZShjb2xvdXIgPSAiZ3JleSIpICsgDQogIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiKSArDQogIGZhY2V0X3dyYXAofmNhdGVnb3J5KSArDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoLjgpKSkNCg0Ka204IDwtIGttX21lZCAlPiUNCiAgZmlsdGVyKGNhdGVnb3J5ID09ICJtc2siKSAlPiUNCiAgZ2dwbG90KGFlcyhtZXRyaWMsIGZhY3RvcihjbHVzdGVyKSwgZmlsbCA9IHZhbHVlKSkgKw0KICBnZW9tX3RpbGUoY29sb3VyID0gImdyZXkiKSArIA0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikgKw0KICBmYWNldF93cmFwKH5jYXRlZ29yeSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKC44KSkpDQoNCmttOSA8LSBrbV9tZWQgJT4lDQogIGZpbHRlcihjYXRlZ29yeSA9PSAicmVzcF9kaXNlYXNlIikgJT4lDQogIGdncGxvdChhZXMobWV0cmljLCBmYWN0b3IoY2x1c3RlciksIGZpbGwgPSB2YWx1ZSkpICsNCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJncmV5IikgKyANCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIpICsNCiAgZmFjZXRfd3JhcCh+Y2F0ZWdvcnkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCguOCkpKQ0KDQoNCmNvd3Bsb3Q6OnBsb3RfZ3JpZChrbTEsIGttMiwga20zLCBrbTQsIGttNSwga202LCBrbTcsIGttOCwga205LCAgIG5jb2wgPSAxLCBhbGlnbiA9ICJ2IiwgcmVsX2hlaWdodHMgPSAyLCBheGlzID0gImwiKQ0KDQoNCmBgYA0KDQoNCg0KYGBge3J9DQoNCmxhdGVzdF9kYXRhX3dpZGUgPC0gbGF0ZXN0X2RhdGFfaW1wdXRlZCAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoaW5kZXggPSBwYXN0ZShJbmRpY2F0b3JOYW1lLCBUaW1lcGVyaW9kKSkgJT4lDQogIHNlbGVjdChpbmRleCwgVmFsdWUsIEFyZWFOYW1lLCBBcmVhQ29kZSkgJT4lDQogIHNwcmVhZChpbmRleCwgVmFsdWUpICU+JQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkgaWZlbHNlKGlzLm5hKHgpLCBtZWRpYW4oeCwgbmEucm0gPSBUUlVFKSwgeCkpDQoNCmdwX2NsdXN0ZXJzIDwtIGRhdGEuZnJhbWUobGF0ZXN0X2RhdGFfd2lkZSwgY2x1c3RlciA9IGttJGNsdXN0ZXJpbmcpDQoNCmdwX2NsdXN0ZXJzICU+JQ0KICBmaWx0ZXIoY2x1c3RlciAlaW4lIGMoMiw5KSkgJT4lDQogIGdhdGhlcihtZXRyaWMsIHZhbHVlLCAzOjU5KSAgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIsIG1ldHJpYykgJT4lDQogIHN1bW1hcmlzZSh2YWx1ZSA9IGxpc3QodmFsdWUpKSAlPiUNCiAgc3ByZWFkKGNsdXN0ZXIsIHZhbHVlKSAlPiUNCiAgZ3JvdXBfYnkobWV0cmljKSAlPiUNCiAgbXV0YXRlKHBfdmFsdWUgPSB0LnRlc3QodW5saXN0KGAyYCksIHVubGlzdChgOWApKSRwLnZhbHVlLCANCiAgICAgICAgICB0X3ZhbHVlID0gdC50ZXN0KHVubGlzdChgMmApLCB1bmxpc3QoYDlgKSkkc3RhdGlzdGljLCANCiAgICAgICAgIG1lYW5fZGlmZiA9IG1lYW4odW5saXN0KGAyYCksIG5hLnJtID0gVFJVRSkvIG1lYW4odW5saXN0KGA5YCksIG5hLnJtID0gVFJVRSkpICU+JQ0KICBmaWx0ZXIocF92YWx1ZSA8IDAuMDAxKSAlPiUNCiAgYXJyYW5nZSgtbWVhbl9kaWZmKSAlPiUNCiAgc2VsZWN0KG1ldHJpYywgbWVhbl9kaWZmKSAlPiUNCiAgZ2dwbG90KGFlcyhtZXRyaWMsIG1lYW5fZGlmZikpICsNCiAgZ2VvbV9jb2woKSArDQogIGNvb3JkX2ZsaXAoKQ0KDQogIA0KDQpgYGANCg0KDQoNCg0KIyMgR2VvZ3JhcGh5IG9mIHByYWN0aWNlIGNsdXN0ZXJzDQoNCmBgYHtyfQ0KDQoNCiMjIHByYWN0aWNlIHBvc3Rjb2Rlcw0KcHJhY3RpY2VfZGV0YWlscyA8LSByZWFkX2NzdigiQzovVXNlcnMvanVsaWFuLmZsb3dlcnMvRG93bmxvYWRzL2dwX2NvZGVzLmNzdiIpDQoNCiMjIHByYWN0aWNlIGdlb2NvZGVzDQoNCg0KYGBgDQoNCg0KYGBge3J9DQoNCmdwX2NsdXN0ZXJzIDwtIGdwX2NsdXN0ZXJzICU+JQ0KICBsZWZ0X2pvaW4ocHJhY3RpY2VfZ2VvY29kZSwgYnkgPSBjKCJBcmVhQ29kZSIgPSAiT3JnSWQiKSkgDQoNCmdwX2NsdXMgPC0gZ3BfY2x1c3RlcnMgDQoNCmBgYA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9OH0NCmxpYnJhcnkoZ2dtYXApDQoNCmdnbWFwOjpyZWdpc3Rlcl9nb29nbGUoa2V5ID0gIkFJemFTeUNEMWZtVHBTNWVwNjZ3THpkeTVhbXotZUlEa1pMZkxINCIpDQoNCg0KZ2VvIDwtIHByYWN0aWNlX2RldGFpbHMgJT4lDQogIG11dGF0ZShnZW8gPSBwdXJycjo6bWFwKFBvc3RDb2RlLCB+KGdlb2NvZGUoLngpKSkpDQoNCmdlbyA8LSBnZW8gJT4lIHVubmVzdCgpDQoNCmdlbyAlPiUgd3JpdGVfcmRzKCJncF9sYXRfbG9uLnJkcyIpDQoNCg0KYGBgDQoNCg0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9DQoNCmdlbzEgPC0gcmVhZF9yZHMoImdwX2xhdF9sb24ucmRzIikgJT4lIHVubmVzdCgpICU+JSBzZWxlY3QoT3JnSWQsIE5hbWUsIGxhdCwgbG9uKQ0KDQpncF9jbHVzX2EgPC0gZ3BfY2x1c1ssIDE6NjFdICU+JQ0KICBsZWZ0X2pvaW4oZ2VvMSwgYnkgPSBjKCJBcmVhQ29kZSIgPSAiT3JnSWQiKSkNCg0KcmVhZF8NCg0KDQpwIDwtIGdnbWFwKGdldF9nb29nbGVtYXAoIkxvbmRvbiIsIHpvb20gPSAxMCkpDQoNCnAgKyBnZW9tX3BvaW50KGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBjb2xvdXIgPSBmYWN0b3IoY2x1c3RlcikpLCBzaXplID0gMS42LCBkYXRhID0gZ3BfY2x1c19hLCBzaG93LmxlZ2VuZCA9IFRSVUUgKSsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdmlyaWRpczo6c2NhbGVfY29sb3VyX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSwgb3B0aW9uID0gIkMiKSArDQogIGdnc2F2ZSgibG9uZG9uX2dwcy5wbmciLCBzY2FsZSA9IDIpDQoNCnEgPC0gIGdnbWFwKGdldF9nb29nbGVtYXAoIkVuZ2xhbmQiLCB6b29tID0gNikpDQoNCg0KcSArIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSwgc2l6ZSA9IC40LCBkYXRhID0gZ3BfY2x1c19hLCBzaG93LmxlZ2VuZCA9IFRSVUUgKSsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdmlyaWRpczo6c2NhbGVfY29sb3VyX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBvcHRpb24gPSAiQyIsIG5hbWUgPSAiQ2x1c3RlciIpICsNCiAgZmFjZXRfd3JhcCh+Y2x1c3RlciwgbnJvdyA9IDIpDQoNCg0KYGBgDQoNCg0KIyMgQ2x1c3RlciBkZXNjcmlwdGlvbnMNCg0KQ2x1c3RlciBObyB8IFNpemUgfCBEZXNjcmlwdGlvbiB8IEdlb2dyYXBoeQ0KLS0tLS0tLS0tLS18LS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLQ0KMSB8IGByIG5yb3coZ3BfY2x1c19hW2dwX2NsdXNfYSRjbHVzdGVyID09IDEsIF0pYCB8IFJlbGF0aXZlbHkgZGVwcml2ZWQsICJtaWRkbGUtYWdlZCIsIGhpZ2hlciBsZXZlbHMgb2Ygb2Jlc2l0eSBhbmQgc21va2luZywgaGlnaCBwcmV2YWxlbmNlIG9mIGRpYWJldGVzIGluIHlvdW5nZXIgYWdlIGdyb3VwcywgaGlnaCBwcmV2YWxlbmNlIG9mIGN2ZCwgbXNrLCBlcGlsZXBzeSwgcmVzcGlyYXRvcnkgZGlzZWFzZSkgfCBVcmJhbiBORS8gTlcvIE1pZGxhbmRzLyBzb21lIGNvYXN0YWwvIEVhc3QgTG9uZG9uDQoyIHwgYHIgbnJvdyhncF9jbHVzX2FbZ3BfY2x1c19hJGNsdXN0ZXIgPT0gMiwgXSlgIHwgTW9zdCBkZXByaXZlZCwgIm1pZGRsZS1hZ2VkIiwgaGlnaGVzdCBsZXZlbHMgb2Ygb2Jlc2l0eSBhbmQgc21va2luZywgaGlnaCBwcmV2YWxlbmNlIG9mIGRpYWJldGVzIGluIHlvdW5nZXIgYWdlIGdyb3VwcywgaGlnaCBwcmV2YWxlbmNlIG9mIG1lbnRhbCBoZWFsdGggcHJvYmxlbXMgYW5kIHJlc3BpcmF0b3J5IGRpc2Vhc2UgfCBTaW1pbGFyIHRvIGNsdXN0ZXIgMTogVXJiYW4gTkUvIE5XLyBNaWRsYW5kcy8gc29tZSBjb2FzdGFsLyBFYXN0IExvbmRvbg0KMyB8IGByIG5yb3coZ3BfY2x1c19hW2dwX2NsdXNfYSRjbHVzdGVyID09IDMsIF0pYCB8IEF2ZXJhZ2UgLSAibWlkZGxlIGFnZSBzdHJ1Y3R1cmUiLCBub3QgZGVwcml2ZWQsIGF2ZXJhZ2UgbGV2ZWxzIG9mIG9iZXNpdHksIHNtb2tpbmcsIGF2ZXJhZ2UgcHJldmFsZW5jZSBmb3IgbW9zdCBkaXNlYXNlIHwgU3VidXJiYW4gTkUvIE5XLyBNaWRsYW5kcy8gc29tZSBjb2FzdGFsL0xvbmRvbi8gU291dGggY29hc3QNCjQgfCBgciBucm93KGdwX2NsdXNfYVtncF9jbHVzX2EkY2x1c3RlciA9PSA0LCBdKWAgfCBSZWxhdGl2ZWx5IGRlcHJpdmVkLCBtb3N0ICJtaWRkbGUtYWdlZCIsIGxvdyBsZXZlbHMgb2Ygb2Jlc2l0eSBidXQgaGlnaGVyIHNtb2tpbmcgcmF0ZXMsIGhpZ2ggcHJldmFsZW5jZSBvZiBkaWFiZXRlcyBpbiB5b3VuZ2VyIGFnZSBncm91cHMsIGxvdyBwcmV2YWxlbmNlIG9mIGN2ZCwgbXNrLCBlcGlsZXBzeSwgcmVzcGlyYXRvcnkgZGlzZWFzZSksIGhpZ2hlciBsZXZlbHMgb2YgbWVudGFsIGhlYWx0aCBwcm9ibGVtcyB8IE1vc3RseSBzdWJ1cmJhbiBMb25kb24NCjUgfCBgciBucm93KGdwX2NsdXNfYVtncF9jbHVzX2EkY2x1c3RlciA9PSA1LCBdKWAgfCBEZXByaXZlZCwgeW91bmdlc3QgcG9wdWxhdGlvbiwgaGlnaGVyIGxldmVscyBvZiBvYmVzaXR5IGFuZCBzbW9raW5nLCBoaWdoIHByZXZhbGVuY2Ugb2YgZGlhYmV0ZXMgaW4geW91bmdlciBhZ2UgZ3JvdXBzLCBoaWdoIHByZXZhbGVuY2Ugb2YgY3ZkLCBtc2ssIGVwaWxlcHN5LCByZXNwaXJhdG9yeSBkaXNlYXNlKSB8IFNpbWlsYXIgdG8gY2x1c3RlciA0DQo2IHwgYHIgbnJvdyhncF9jbHVzX2FbZ3BfY2x1c19hJGNsdXN0ZXIgPT0gNiwgXSlgIHwgTm90IGRlcHJpdmVkLCBvbGRlc3QgcG9wdWxhdGlvbiwgbG93ZXIgbGV2ZWxzIG9mIG9iZXNpdHkgYW5kIHNtb2tpbmcsIGhpZ2ggcHJldmFsZW5jZSBvZiBkaWFiZXRlcyBpbiB5b3VuZ2VyIGFnZSBncm91cHMsIGhpZ2ggcHJldmFsZW5jZSBvZiBjdmQsIG1zaywgZXBpbGVwc3ksIHJlc3BpcmF0b3J5IGRpc2Vhc2UpIHwgTGFyZ2VseSBjb2FzdGFsIGFyZWFzIGFjcm9zcyBFbmdsYW5kDQo3IHwgYHIgbnJvdyhncF9jbHVzX2FbZ3BfY2x1c19hJGNsdXN0ZXIgPT0gNywgXSlgIHwgU2ltaWxhciB0byA1LkRlcHJpdmVkLCB5b3VuZ2VzdCBwb3B1bGF0aW9uLCBoaWdoZXIgbGV2ZWxzIG9mIG9iZXNpdHkgYW5kIHNtb2tpbmcsIGhpZ2ggcHJldmFsZW5jZSBvZiBkaWFiZXRlcyBpbiB5b3VuZ2VyIGFnZSBncm91cHMsIGhpZ2ggcHJldmFsZW5jZSBvZiBjdmQsIG1zaywgZXBpbGVwc3ksIHJlc3BpcmF0b3J5IGRpc2Vhc2UgfCBHZW9ncmFwaHkgc2ltaWxhciB0byAxDQo4IHwgYHIgbnJvdyhncF9jbHVzX2FbZ3BfY2x1c19hJGNsdXN0ZXIgPT0gOCwgXSlgIHwgU2ltaWxhciB0byA0IGJ1dCBsZXNzIGRlcHJpdmVkLCBtb3JlIG1pZGRsZS1hZ2VkIHBvcHVsYXRpb24sIGxvd2VyIGxldmVscyBvZiBvYmVzaXR5IGFuZCBzbW9raW5nLCBoaWdoIHByZXZhbGVuY2Ugb2YgZGlhYmV0ZXMgaW4gb2xkZXIgYWdlIGdyb3VwcywgbG93ZXIgcHJldmFsZW5jZSBvZiBjdmQsIG1zaywgZXBpbGVwc3ksIHJlc3BpcmF0b3J5IGRpc2Vhc2UgfCBMYXJnZWx5IGNlbnRyYWwgTG9uZG9uDQo5IHwgYHIgbnJvdyhncF9jbHVzX2FbZ3BfY2x1c19hJGNsdXN0ZXIgPT0gOSwgXSlgIHwgU2ltaWxhciB0byA2LiBOb3QgRGVwcml2ZWQsIG9sZGVyIHBvcHVsYXRpb24sIGxvd2VyIGxldmVscyBvZiBvYmVzaXR5IGJ1dCBoaWdoZXIgcmF0ZSBvZiBzbW9raW5nLCBoaWdoIHByZXZhbGVuY2Ugb2YgZGlhYmV0ZXMgaW4geW91bmdlciBhZ2UgZ3JvdXBzLCBoaWdoIHByZXZhbGVuY2Ugb2YgY3ZkLCBtc2ssIGVwaWxlcHN5LCByZXNwaXJhdG9yeSBkaXNlYXNlIHwgR2VvZ3JhcGh5IHdpZGVzcHJlYWQgYWNyb3NzIHRoZSBjb3VudHJ5DQoxMCB8IGByIG5yb3coZ3BfY2x1c19hW2dwX2NsdXNfYSRjbHVzdGVyID09IDEwLCBdKWAgfCBOb3QgZGVwcml2ZWQsIHlvdW5nZXIgcG9wdWxhdGlvbiwgbG93IHJhdGVzIG9mIHJpc2sgZmFjdG9ycyBhbmQgcHJldmFsZW5jZSBhY3Jvc3MgcmFuZ2Ugb2YgZGlzZWFzZXMgfCBTb3V0aCBFYXN0IEVuZ2xhbmQNCg0KIyMgQ29uY2x1c2lvbnMNCg0KKiBUaHJvdWdoIHNpbXBsZSBjbHVzdGVyaW5nIGFwcHJvYWNoZXMgd2UgaGF2ZSBiZWVuIGFibGUgZGVmaW5lIGEgbnVtYmVyIG9mIGRpc3RpbmN0IEdQIHBvcHVsYXRpb24gcGhlbm90eXBlcy4gRm9yIHRoZSBwdXJwb3NlcyBvZiB0aGlzIGFuYWx5c2lzIHdlIHVzZSAqY28tbW9yYmlkaXR5KiB0byBtZWFuIHRoZSBjby1leGlzdGVuY2Ugb2YgaGlnaCBwcmV2YWxlbmNlIGFjcm9zcyBhIHJhbmdlIG9mIA0KZGlzZWFzZXMgaW4gdGhlIHNhbWUgcG9wdWxhdGlvbi4gVXNpbmcgdGhpcyBkZWZpbml0aW9uIHdlIGNhbiBkaXN0aW5ndWlzaCA0IGdyb3VwcyBvZiBwcmFjdGljZSBwb3B1bGF0aW9ucyB3aXRoIGhpZ2ggbGV2ZWxzIG9mICpjby1tb3JiaWRpdHkqOg0KDQoqICoqQ2x1c3RlciAxKiogd2hpY2ggaXMgZGVwcml2ZWQsIHdpdGggaGlnaCByYXRlcyBvZiBzbW9raW5nIGFuZCBvYmVzaXR5IGFuZCBpcyBnZW9ncmFwaGljYWxseSBjb25jZW50cmF0ZWQgaW4gdXJiYW4gTkUsIE5XIGFuZCBXZXN0IE1pZGxhbmRzLCBhbmQgRWFzdCBMb25kb24NCiogKipDbHVzdGVyIDIqKiB3aGljaCBpcyBzaW1pbGFyIHRvICoqQ2x1c3RlciAxKiogYnV0IG1vcmUgZGVwcml2ZWQsIHdpdGggaGlnaCByYXRlcyBvZiBzbW9raW5nIGFuZCBvYmVzaXR5IGFuZCBpcyBnZW9ncmFwaGljYWxseSBjb25jZW50cmF0ZWQgaW4gdXJiYW4gTkUsIE5XIGFuZCBXZXN0IE1pZGxhbmRzLCBhbmQgRWFzdCBMb25kb24uIFRoZSBtYWluIGRpZmZlcmVuY2UgZnJvbSAqKkNsdXN0ZXIgMSoqIGlzIGhpZ2hlciByYXRlcyBvZiBtZW50YWwgaGVhbHRoIGRpc29yZGVycywgYW5kIGxvd2VyIHJhdGVzIG9mIENWRA0KKiAqKkNsdXN0ZXIgNioqIHdoaWNoIGNvbnNpc3RzIG9mIGxhcmdlbHkgY29hc3RhbCwgb2xkZXIsIG5vbi1kZXByaXZlZCBwb3B1bGF0aW9ucw0KKiAqKkNsdXN0ZXIgOSoqIHdoaWNoIGlzIHNpbWlsYXIgdG8gKipDbHVzdGVyIDYqKiBidXQgaGFzIGEgZGlmZmVyZW50IGdlb2dyYXBoeQ0KDQoqIE5vdGUgdGhhdCB3ZSBoYXZlIHVzZWQgY3J1ZGUgcHJldmFsZW5jZSBlc3RpbWF0ZXMgZnJvbSBRT0YgLSBhZ2UgYWRqdXN0ZWQgZXN0aW1hdGVzIGNvdWxkIGNoYW5nZSB0aGUgY2x1c3RlcmluZy4gQWxzbyB3ZSBjb3VsZCBpbXByb3ZlIGRlbW9ncmFwaGljIHByb2ZpbGluZyBieSBpbmNsdWRpbmcgZXRobmljaXR5IGVzdGltYXRlcy4gQWRkaXRpb25hbCByaXNrIGZhY3RvciBlc3RpbWF0ZXMgKGUuZy4gYWxjb2hvbCkgY291bGQgYWxzbyBjaGFuZ2UgdGhlIGNsdXN0ZXJpbmcuIFRoZSBjaG9pY2Ugb2YgMTAgY2x1c3RlciBncm91cHMgaXMgc29tZXdoYXQgYXJiaXRyYXJ5IGFuZCBpdCBpcyBjbGVhciB0aGF0IHNvbWUgYXJlIHN1YnNldHMgb2Ygb3RoZXJzLg0KDQoqIEZyb20gYW4gaW5lcXVhbGl0eSBwZXJzcGVjdGl2ZSAqKkNsdXN0ZXIgMSoqIGlzIG9mIG1vc3QgY29uY2VybiB3aGljaCBhcHBlYXJzIHRvIGhhdmUgaGlnaCBsZXZlbHMgb2YgY28tbW9yYmlkaXR5IGRlc3BpdGUgaGF2aW5nIGEgcmVsYXRpdmVseSB5b3VuZyBwb3B1bGF0aW9uIHByb2ZpbGUNCg0KKiBMb25kb24gaGFzIGEgZGlmZmVyZW50IHNldCBvZiBwaGVub3R5cGVzIHRvIHRoZSByZXN0IG9mIHRoZSBjb3VudHJ5Lg0KDQoNCiMjIE5leHQgc3RlcHMNCg0KMS4gVGhpcyBhbmFseXNpcyBpZGVudGlmaWVzIHR3byBraW5kcyBvZiBwcmFjdGljZSBncm91cHMgd2l0aCBoaWdoIGxldmVscyBvZiBjby1tb3JiaWRpdHkgLSBvbmUgaWRlbnRpZmllZCBieSB2aXJ0dWUgb2YgaXRzIG9sZGVyIGFnZSBwcm9maWxlLCBhbmQgYW5vdGhlciBieSB2aXJ0dWUgb2YgaXRzIGxldmVscyBvZiBkZXByaXZhdGlvbiBhbmQgcmlzayBmYWN0b3JzLiBUaGVyZSBhcmUgc3ViLWNsdXN0ZXJzICB0aGVzZSBncm91cHMgd2l0aCBkaWZmZXJlbnQgZ2VvZ3JhcGhpY2FsIHByb2ZpbGVzLiBUaGVzZSBwcmFjdGljZXMgYXJlIGlkZW50aWZpYWJsZSBhbmQgbWF5IHJlcXVpcmUgZGlmZmVyZW50IGFwcHJvYWNoZXMgdG8gdGFja2xpbmcgY28tbW9yYmlkaXR5IG9yIG1hcmtldGluZyBpbml0aWF0aXZlcyB0byBsb2NhbCBwb3B1bGF0aW9ucy4NCjIuIFRoaXMgaXMgb25seSBhIHByZWxpbWluYXJ5IGFuYWx5c2lzICAtIG1vcmUgdmFyaWFibGVzIGNvdWxkIGJlIGluY2x1ZGVkIHdoaWNoIGNvdWxkIGFkZCBhZGRpdGlvbmFsIGluc2lnaHQNCjMuIERpc2Vhc2VzIGFuZCByaXNrIGZhY3RvcnMgdGVuZCB0byBjbHVzdGVyIGluIHBvcHVsYXRpb25zIC0gZm9yIGV4YW1wbGUgbWVudGFsIGhlYWx0aCBpc3N1ZXMgaW4gKipDbHVzdGVyIDIqKg0KNC4gQ2FuIGVzdGltYXRlIGFwcHJveGltYXRlIHNpemVzIG9mIGNvLWNvbW9yYmlkIHBvcHVsYXRpb25zIGZyb20gcHJhY3RpemUgc2l6ZSwgUU9GIG51bWVyYXRvcnMgYW5kIGRlbm9taW5hdG9ycw0KNC4gV2UgY2FuIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIGNsdXN0ZXIgcHJhY3RpY2VzIHdpdGhpbiBsYXJnZSBwcmltYXJ5IGNhcmUgZGF0YXNldHMgbGlrZSBDUFJEIHdoZXJlIHByYWN0aWNlcyBhcmUgYW5vbnltaXNlZCwgdG8gdHJ5IGFuZCB0cmFpbiBiZXR0ZXIgcHJldmFsZW5jZSBtb2RlbHMsIGRlZmluZSBhbmQgcmVmaW5lIHByYWN0aWNlIHBvcHVsYXRpb24gcGhlbm90eXBlcw0KNS4gVGhlIGFuYWx5c2lzIGNvdWxkIGJlIGZ1cnRoZXIgcmVmaW5lZCwgYW5kIGNsdXN0ZXJpbmcgb3ZlciB0aW1lIGNvdWxkIGJlIGFzc2Vzc2VkDQo2LiBUaGlzIGNvdWxkIGhlbHAgaW4gcmVmaW5pbmcgaW50ZXJ2ZW50aW9ucyB0byB0YWNrbGUgaW5lcXVhbGl0eSBvciBzcGVjaWZpYyBwb3B1bGF0aW9ucw0KDQoNCiMjIEFubmV4DQoNCiMjIyBTZWFyY2ggdGVybQ0KDQooInVuc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIltNZVNIIFRlcm1zXSBPUiAoInVuc3VwZXJ2aXNlZCJbQWxsIEZpZWxkc10gQU5EICJtYWNoaW5lIltBbGwgRmllbGRzXSBBTkQgImxlYXJuaW5nIltBbGwgRmllbGRzXSkgT1IgInVuc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIltBbGwgRmllbGRzXSkgT1IgKCgiY2x1c3RlciBhbmFseXNpcyJbTWVTSCBUZXJtc10gT1IgKCJjbHVzdGVyIltBbGwgRmllbGRzXSBBTkQgImFuYWx5c2lzIltBbGwgRmllbGRzXSkgT1IgImNsdXN0ZXIgYW5hbHlzaXMiW0FsbCBGaWVsZHNdKSBBTkQgKCJwb3B1bGF0aW9uIltNZVNIIFRlcm1zXSBPUiAicG9wdWxhdGlvbiJbQWxsIEZpZWxkc10gT1IgInBvcHVsYXRpb24gZ3JvdXBzIltNZVNIIFRlcm1zXSBPUiAoInBvcHVsYXRpb24iW0FsbCBGaWVsZHNdIEFORCAiZ3JvdXBzIltBbGwgRmllbGRzXSkgT1IgInBvcHVsYXRpb24gZ3JvdXBzIltBbGwgRmllbGRzXSkgQU5EIHNlZ21lbnRhdGlvbltBbGwgRmllbGRzXSkgQU5EIDIwMTBbUERBVF0gOiAyMDE4W1BEQVRdDQoNCmByIHJlYWRfbGluZXMoInByZWNfbWVkLnR4dCIpICU+JSBzdHJfcmVtb3ZlKC4sICJcXHQiKSAlPiUgc3RyX3JlcGxhY2VfYWxsKC4sICJcXC4sIiwgIlxcbiIpYA0K