WHO SMART Guidelines - HIV
0.4.3 - ci-build
WHO SMART Guidelines - HIV - Local Development build (v0.4.3) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions
Draft as of 2025-02-07 |
{
"resourceType" : "Library",
"id" : "HIVCommon",
"meta" : {
"profile" : [
🔗 "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-shareablelibrary"🔗 ,
"http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-publishablelibrary"🔗 ,
"http://hl7.org/fhir/uv/cql/StructureDefinition/cql-library"🔗 ,
"http://hl7.org/fhir/uv/cql/StructureDefinition/cql-module"
]
},
"text" : {
"status" : "extensions",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n<div>\n <table class=\"grid dict\">\n \n \n <tr>\n <th scope=\"row\"><b>Title: </b></th>\n <td style=\"padding-left: 4px;\">HIVCommon</td>\n </tr>\n \n\n \n \n <tr>\n <th scope=\"row\"><b>Id: </b></th>\n <td style=\"padding-left: 4px;\">HIVCommon</td>\n </tr>\n \n\n \n \n <tr>\n <th scope=\"row\"><b>Version: </b></th>\n <td style=\"padding-left: 4px;\">0.4.3</td>\n </tr>\n \n\n \n <tr>\n <th scope=\"row\"><b>Url: </b></th>\n <td style=\"padding-left: 4px;\"><a href=\"Library-HIVCommon.html\">HIVCommon</a></td>\n </tr>\n \n\n \n\n \n\n \n <tr>\n <th scope=\"row\"><b>Status: </b></th>\n <td style=\"padding-left: 4px;\">draft</td>\n </tr>\n \n\n \n <tr>\n <th scope=\"row\"><b>Experimental: </b></th>\n <td style=\"padding-left: 4px;\">true</td>\n </tr>\n \n\n \n <tr>\n <th scope=\"row\"><b>Type: </b></th>\n <td style=\"padding-left: 4px;\">\n \n \n \n <p style=\"margin-bottom: 5px;\">\n <b>system: </b> <span><a href=\"http://terminology.hl7.org/6.0.2/CodeSystem-library-type.html\">http://terminology.hl7.org/CodeSystem/library-type</a></span>\n </p>\n \n \n <p style=\"margin-bottom: 5px;\">\n <b>code: </b> <span>logic-library</span>\n </p>\n \n \n \n \n \n </td>\n </tr>\n \n\n \n\n \n <tr>\n <th scope=\"row\"><b>Date: </b></th>\n <td style=\"padding-left: 4px;\">2025-02-07 14:15:45+0000</td>\n </tr>\n \n\n \n <tr>\n <th scope=\"row\"><b>Publisher: </b></th>\n <td style=\"padding-left: 4px;\">WHO</td>\n </tr>\n \n\n \n <tr>\n <th scope=\"row\"><b>Description: </b></th>\n <td style=\"padding-left: 4px;\"><div><p>Description not yet available for HIVCommon.</p>\n</div></td>\n </tr>\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n \n <tr>\n <td colspan=\"2\">\n <table>\n <tr><th><a id=\"cql-content\"><b>Content: </b></a> text/cql</th></tr>\n <tr><td><pre><code class=\"language-cql\">library HIVCommon version '0.0.1' \n\nusing FHIR version '4.0.1'\n\ninclude FHIRHelpers version '4.0.1'\ninclude WHOCommon called WCom\n\ninclude HIVConcepts called Concepts\ninclude HIVConceptsCustom called CustomConcepts\n\ncontext Patient\n\n//VERIFIED\n\n\n//DRAFT\n\n//need to add Time to start ART (within 7, 30 or 90 days of diagnosis, as per country guidelines)\n//need to add Disaggregation by time since diagnosis\n \n\n\n/* \n* DAK has codes for HAART misspecified \n* ICD-10 Z92.2\tOther prophylactic chemotherapy\t\t\n* LOINC "54825-5\t"\tOn scheduled pain medication regimen in last 7 days\n*\n* Should discuss \n*/\n\n\n\n\n\n\n\n/*\n* Kenya EMR defined as HIV positive condition\n* Will need to discuss if produce flags through conditions for concepts to use in indicator calculation such as HIV positive and On ART\n* However, we have moved forward with a more prescriptive approach \n* Inevitably our approach requires that certain data elements be available\n*/\n\n/*\n * HIV Treatment during the measurement period\n * uses dosage and dispensation amount to estimate last day of medication\n * medication should be dispensed before end of measurement period\n * medication should last until after 28 days after the end of the measurement period\n * This takes into account lost to follow up\n */\n\n/*\n*define "HIV Treatment during the measurement period":\n* [MedicationDispense] MD\n* where MD.status in { 'final', 'amended', 'corrected' }\n* and MD.medication ~ 'Antiretroviral'\n* and MD.whenHandedOver before end of "Measurement Period"\n* and (MD.whenHandedOver + MD.dosageInstruction[0].timing.repeat.duration.value * MedicationDispense.quantity.value) after (measurementPeriod.end - 28 days)\n*/ \n\n/*\n* Immunization defines HAART as\n* exists([MedicationAdministration] A where ExtractMedicationCode(A.medication) in IMMZc."ARV Drugs" and A.status = 'in-progress')\n* I believe this is the incorrect resource unless they mean to say that medication is given during a medical encounter\n* would like to discuss \n*/ \n\n/** \n * Patient Deceased During Measurement Period\n * Immunization defines this as true when is a boolean. This may have the effect of deleting a person from indicators in all calculations\n * Should intend to use when patient.deceased FHIR boolean was updated to TRUE if no other date is available\n * Kenya EMR example does not account for when deceased is just a boolean\n */\n\n\n/*\ndefine "PREP Prescription Days":\n Sum(\n (\n collapse (\n [MedicationRequest] MR\n where MR.status = 'completed'\n and MR.intent = 'order'\n and MR.medication ~ Concepts."PrEP for HIV prevention"\n return WComV2."Prescription Relevant Period"( MR ) intersect "Measurement Period"\n )\n ) PREPUseInterval\n return days between start of PREPUseInterval and end of PREPUseInterval\n )\n*/\n\n\ndefine function GetDurationInDays(value FHIR.Duration): // returns Decimal:\n case value.code.value\n when 'a' then value.value * 365.0\n when 'mo' then value.value.value * 30.0\n when 'wk' then value.value.value * 7.0\n when 'd' then value.value.value\n when 'h' then value.value.value / 24.0\n when 'min' then value.value.value / 60.0 / 24.0\n when 's' then value.value.value / 60.0 / 60.0 / 24.0\n when 'ms' then value.value.value / 60.0 / 60.0 / 24.0 / 1000.0\n else Message(1000, true, 'Undefined', 'Error', 'Unsupported duration unit ' + value.code.value)\n end\n\ndefine function "Prescription Relevant Period"(prescription FHIR.MedicationRequest):\n if (\n prescription.authoredOn is not null and prescription.dispenseRequest is not null\n and prescription.dispenseRequest.expectedSupplyDuration is not null\n )\n then Interval[\n date from prescription.authoredOn,\n date from prescription.authoredOn + System.Quantity{ value: GetDurationInDays(prescription.dispenseRequest.expectedSupplyDuration), unit: 'days' }\n ]\n else null\n\n//System.Integer\ndefine function ToDaily(frequency System.Integer, period System.Quantity):\n case period.unit\n when 'h' then frequency * (24.0 / period.value)\n when 'min' then frequency * (24.0 / period.value) * 60\n when 's' then frequency * (24.0 / period.value) * 60 * 60\n when 'd' then frequency * (24.0 / period.value) / 24\n when 'wk' then frequency * (24.0 / period.value) / (24 * 7)\n when 'mo' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */\n when 'a' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */\n when 'hour' then frequency * (24.0 / period.value)\n when 'minute' then frequency * (24.0 / period.value) * 60\n when 'second' then frequency * (24.0 / period.value) * 60 * 60\n when 'day' then frequency * (24.0 / period.value) / 24\n when 'week' then frequency * (24.0 / period.value) / (24 * 7)\n when 'month' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */\n when 'year' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */\n when 'hours' then frequency * (24.0 / period.value)\n when 'minutes' then frequency * (24.0 / period.value) * 60\n when 'seconds' then frequency * (24.0 / period.value) * 60 * 60\n when 'days' then frequency * (24.0 / period.value) / 24\n when 'weeks' then frequency * (24.0 / period.value) / (24 * 7)\n when 'months' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */\n when 'years' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */\n else null\n end\n\n define function "HasEnd"(period Interval<DateTime> ):\n not (end of period is null\n or end of period = maximum DateTime\n)\n\n define fluent function MedicationRequestPeriod(Request MedicationRequest):\n Request R\n let\n dosage: singleton from R.dosageInstruction,\n doseAndRate: singleton from dosage.doseAndRate,\n doseRange: doseAndRate.dose as Range,\n doseQuantity: doseAndRate.dose as SimpleQuantity,\n dose: Coalesce(end of doseRange, doseQuantity),\n timing: dosage.timing,\n frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),\n period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },\n dosesPerDay: Coalesce(ToDaily(FHIRHelpers.ToInteger(frequency), period), Count(timing.repeat.timeOfDay), 1.0),\n boundsPeriod: timing.repeat.bounds as Period,\n daysSupply: R.dispenseRequest.expectedSupplyDuration,\n quantity: R.dispenseRequest.quantity,\n refills: Coalesce(R.dispenseRequest.numberOfRepeatsAllowed, 0),\n startDate:\n Coalesce(\n start of boundsPeriod,\n start of R.dispenseRequest.validityPeriod,\n R.authoredOn\n )\n return\n if HasEnd(boundsPeriod) then\n Interval[startDate, end of boundsPeriod]\n else\n (\n Coalesce(daysSupply, quantity / (dose * dosesPerDay))\n * (1 + refills)\n ) durationInDays\n return Interval[startDate, startDate + durationInDays]\n\n\ndefine function "DosesPerDay"(frequency Code):\n\t/*Calculates the cumulative dose per day for each prescription*/\n\tcase\n\t\twhen frequency ~ CustomConcepts."Once daily (qualifier value)" then 1.0\n\t\twhen frequency ~ CustomConcepts."Twice a day (qualifier value)" then 2.0\n\t\twhen frequency ~ CustomConcepts."Three times daily (qualifier value)" then 3.0\n\t\twhen frequency ~ CustomConcepts."Four times daily (qualifier value)" then 4.0\n\t\twhen frequency ~ CustomConcepts."Every twenty four hours (qualifier value)" then 1.0\n\t\twhen frequency ~ CustomConcepts."Every twelve hours (qualifier value)" then 2.0\n\t\twhen frequency ~ CustomConcepts."Every thirty six hours (qualifier value)" then 0.67\n\t\twhen frequency ~ CustomConcepts."Every eight hours (qualifier value)" then 3.0\n\t\twhen frequency ~ CustomConcepts."Every four hours (qualifier value)" then 6.0\n\t\twhen frequency ~ CustomConcepts."Every six hours (qualifier value)" then 4.0\n\t\twhen frequency ~ CustomConcepts."Every seventy two hours (qualifier value)" then 0.34\n\t\twhen frequency ~ CustomConcepts."Every forty eight hours (qualifier value)" then 0.5\n\t\twhen frequency ~ CustomConcepts."Every eight to twelve hours (qualifier value)" then 2.0\n\t\twhen frequency ~ CustomConcepts."Every six to eight hours (qualifier value)" then 3.0\n\t\twhen frequency ~ CustomConcepts."Every three to four hours (qualifier value)" then 6.0\n\t\twhen frequency ~ CustomConcepts."Every three to six hours (qualifier value)" then 4.0\n\t\twhen frequency ~ CustomConcepts."Every two to four hours (qualifier value)" then 6.0\n\t\twhen frequency ~ CustomConcepts."One to four times a day (qualifier value)" then 4.0\n\t\twhen frequency ~ CustomConcepts."One to three times a day (qualifier value)" then 3.0\n\t\twhen frequency ~ CustomConcepts."One to two times a day (qualifier value)" then 2.0\n\t\telse null \n\tend\n\n //define function "GetMedicationDailyDose"(dosage Quantity, dosesPerDay Decimal):\n //dosage * Quantity { value: dosesPerDay, unit: '/d' }\n\n\ndefine fluent function dailyDose(Request "MedicationRequest"):\n Request R\n let\n dosage: singleton from R.dosageInstruction,\n doseAndRate: singleton from dosage.doseAndRate,\n timing: dosage.timing,\n frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),\n period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },\n doseRange: doseAndRate.dose as Range,\n doseQuantity: doseAndRate.dose as SimpleQuantity,\n dose: Coalesce(doseRange.high, doseQuantity),\n dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0),\n quantity: R.dispenseRequest.quantity\n return\n quantity / (dose * dosesPerDay)\n\ndefine fluent function dailyDose(Dispense "MedicationDispense"):\n Dispense D\n let\n dosage: singleton from D.dosageInstruction,\n doseAndRate: singleton from dosage.doseAndRate,\n timing: dosage.timing,\n frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),\n period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },\n doseRange: doseAndRate.dose as Range,\n doseQuantity: doseAndRate.dose as SimpleQuantity,\n dose: Coalesce(doseRange.high, doseQuantity),\n dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0)\n return\n D.quantity / (dose * dosesPerDay)\n\n\n\n\n\n\n\n \n \n\n\n//need to define\n define STI_testing:\n [Procedure] P\n where P.status = 'completed'\n and P.code ~ Concepts."STI testing and treatment services" \n\n\n/**\n * @description Takes a choice between a Medication and a CodeableConcept and returns just the code of the medication\n */\ndefine function ExtractMedicationCode(choice Choice<FHIR.CodeableConcept, FHIR.Reference>):\n case\n\t when choice is FHIR.CodeableConcept then\n \tchoice as FHIR.CodeableConcept\n when choice is FHIR.Reference then\n First([Medication] M \n where M.id = Last(Split(choice.reference, '/'))\n return M.code as FHIR.CodeableConcept)\n else\n Message(null as FHIR.CodeableConcept, true, '1', 'Error', 'Cannot compute a medication code') // TODO: I'm sure that this is supported somehow?\n end\n\n/**\n * @description Returns the code of an Observation CodeableConcept\n */\ndefine fluent function getObservationCode(observation FHIR.Observation):\n case \n when observation.code is FHIR.CodeableConcept then (observation.value as CodeableConcept).coding.code\n else null\n end\n \n/**\n * @description Returns a list of codes of an a list of Observations\n */\ndefine fluent function getObservationCodes(obslist List<FHIR.Observation>):\n obslist obs return obs.getObservationCode()\n\n\n</code></pre></td></tr>\n </table>\n </td>\n </tr>\n \n \n \n </table>\n</div>\n</div>"
},
"extension" : [
{
"url" : "http://hl7.org/fhir/StructureDefinition/cqf-knowledgeCapability",
"valueCode" : "computable"
}
],
"url" : "http://smart.who.int/hiv/Library/HIVCommon",
"version" : "0.4.3",
"name" : "HIVCommon",
"title" : "HIVCommon",
"status" : "draft",
"experimental" : true,
"type" : {
"coding" : [
{
"system" : "http://terminology.hl7.org/CodeSystem/library-type",
"code" : "logic-library"
}
]
},
"date" : "2025-02-07T14:15:45+00:00",
"publisher" : "WHO",
"contact" : [
{
"name" : "WHO",
"telecom" : [
{
"system" : "url",
"value" : "http://who.int"
}
]
}
],
"description" : "Description not yet available for HIVCommon.",
"content" : [
{
"contentType" : "text/cql",
"data" : "bGlicmFyeSBISVZDb21tb24gdmVyc2lvbiAnMC4wLjEnIAoKdXNpbmcgRkhJUiB2ZXJzaW9uICc0LjAuMScKCmluY2x1ZGUgRkhJUkhlbHBlcnMgdmVyc2lvbiAnNC4wLjEnCmluY2x1ZGUgV0hPQ29tbW9uIGNhbGxlZCBXQ29tCgppbmNsdWRlIEhJVkNvbmNlcHRzIGNhbGxlZCBDb25jZXB0cwppbmNsdWRlIEhJVkNvbmNlcHRzQ3VzdG9tIGNhbGxlZCBDdXN0b21Db25jZXB0cwoKY29udGV4dCBQYXRpZW50CgovL1ZFUklGSUVECgoKLy9EUkFGVAoKLy9uZWVkIHRvIGFkZCBUaW1lIHRvIHN0YXJ0IEFSVCAod2l0aGluIDcsIDMwIG9yIDkwIGRheXMgb2YgZGlhZ25vc2lzLCBhcyBwZXIgY291bnRyeSBndWlkZWxpbmVzKQovL25lZWQgdG8gYWRkIERpc2FnZ3JlZ2F0aW9uIGJ5IHRpbWUgc2luY2UgZGlhZ25vc2lzCiAgCgoKLyogCiogREFLIGhhcyBjb2RlcyBmb3IgSEFBUlQgbWlzc3BlY2lmaWVkIAoqIElDRC0xMCBaOTIuMglPdGhlciBwcm9waHlsYWN0aWMgY2hlbW90aGVyYXB5CQkKKiBMT0lOQyAiNTQ4MjUtNQkiCU9uIHNjaGVkdWxlZCBwYWluIG1lZGljYXRpb24gcmVnaW1lbiBpbiBsYXN0IDcgZGF5cwoqCiogU2hvdWxkIGRpc2N1c3MgCiovCgoKCgoKCgovKgoqIEtlbnlhIEVNUiBkZWZpbmVkIGFzIEhJViBwb3NpdGl2ZSBjb25kaXRpb24KKiBXaWxsIG5lZWQgdG8gZGlzY3VzcyBpZiBwcm9kdWNlIGZsYWdzIHRocm91Z2ggY29uZGl0aW9ucyBmb3IgY29uY2VwdHMgdG8gdXNlIGluIGluZGljYXRvciBjYWxjdWxhdGlvbiBzdWNoIGFzIEhJViBwb3NpdGl2ZSBhbmQgT24gQVJUCiogSG93ZXZlciwgd2UgaGF2ZSBtb3ZlZCBmb3J3YXJkIHdpdGggYSBtb3JlIHByZXNjcmlwdGl2ZSBhcHByb2FjaCAKKiBJbmV2aXRhYmx5IG91ciBhcHByb2FjaCByZXF1aXJlcyB0aGF0IGNlcnRhaW4gZGF0YSBlbGVtZW50cyBiZSBhdmFpbGFibGUKKi8KCi8qCiAqIEhJViBUcmVhdG1lbnQgZHVyaW5nIHRoZSBtZWFzdXJlbWVudCBwZXJpb2QKICogdXNlcyBkb3NhZ2UgYW5kIGRpc3BlbnNhdGlvbiBhbW91bnQgdG8gZXN0aW1hdGUgbGFzdCBkYXkgb2YgbWVkaWNhdGlvbgogKiBtZWRpY2F0aW9uIHNob3VsZCBiZSBkaXNwZW5zZWQgYmVmb3JlIGVuZCBvZiBtZWFzdXJlbWVudCBwZXJpb2QKICogbWVkaWNhdGlvbiBzaG91bGQgbGFzdCB1bnRpbCBhZnRlciAyOCBkYXlzIGFmdGVyIHRoZSBlbmQgb2YgdGhlIG1lYXN1cmVtZW50IHBlcmlvZAogKiBUaGlzIHRha2VzIGludG8gYWNjb3VudCBsb3N0IHRvIGZvbGxvdyB1cAogKi8KCi8qCipkZWZpbmUgIkhJViBUcmVhdG1lbnQgZHVyaW5nIHRoZSBtZWFzdXJlbWVudCBwZXJpb2QiOgoqICAgW01lZGljYXRpb25EaXNwZW5zZV0gTUQKKiAgICB3aGVyZSBNRC5zdGF0dXMgaW4geyAnZmluYWwnLCAnYW1lbmRlZCcsICdjb3JyZWN0ZWQnIH0KKiAgICBhbmQgTUQubWVkaWNhdGlvbiB+ICdBbnRpcmV0cm92aXJhbCcKKiAgICBhbmQgTUQud2hlbkhhbmRlZE92ZXIgYmVmb3JlIGVuZCBvZiAiTWVhc3VyZW1lbnQgUGVyaW9kIgoqICAgIGFuZCAoTUQud2hlbkhhbmRlZE92ZXIgKyBNRC5kb3NhZ2VJbnN0cnVjdGlvblswXS50aW1pbmcucmVwZWF0LmR1cmF0aW9uLnZhbHVlICogTWVkaWNhdGlvbkRpc3BlbnNlLnF1YW50aXR5LnZhbHVlKSBhZnRlciAobWVhc3VyZW1lbnRQZXJpb2QuZW5kIC0gMjggZGF5cykKKi8gCgovKgoqIEltbXVuaXphdGlvbiBkZWZpbmVzIEhBQVJUIGFzCiogIGV4aXN0cyhbTWVkaWNhdGlvbkFkbWluaXN0cmF0aW9uXSBBIHdoZXJlIEV4dHJhY3RNZWRpY2F0aW9uQ29kZShBLm1lZGljYXRpb24pIGluIElNTVpjLiJBUlYgRHJ1Z3MiIGFuZCBBLnN0YXR1cyA9ICdpbi1wcm9ncmVzcycpCiogSSBiZWxpZXZlIHRoaXMgaXMgdGhlIGluY29ycmVjdCByZXNvdXJjZSB1bmxlc3MgdGhleSBtZWFuIHRvIHNheSB0aGF0IG1lZGljYXRpb24gaXMgZ2l2ZW4gZHVyaW5nIGEgbWVkaWNhbCBlbmNvdW50ZXIKKiB3b3VsZCBsaWtlIHRvIGRpc2N1c3MgCiovIAoKLyoqIAogKiBQYXRpZW50IERlY2Vhc2VkIER1cmluZyBNZWFzdXJlbWVudCBQZXJpb2QKICogSW1tdW5pemF0aW9uIGRlZmluZXMgdGhpcyBhcyB0cnVlIHdoZW4gaXMgYSBib29sZWFuLiBUaGlzIG1heSBoYXZlIHRoZSBlZmZlY3Qgb2YgZGVsZXRpbmcgYSBwZXJzb24gZnJvbSBpbmRpY2F0b3JzIGluIGFsbCBjYWxjdWxhdGlvbnMKICogU2hvdWxkIGludGVuZCB0byB1c2Ugd2hlbiBwYXRpZW50LmRlY2Vhc2VkIEZISVIgYm9vbGVhbiB3YXMgdXBkYXRlZCB0byBUUlVFIGlmIG5vIG90aGVyIGRhdGUgaXMgYXZhaWxhYmxlCiAqIEtlbnlhIEVNUiBleGFtcGxlIGRvZXMgbm90IGFjY291bnQgZm9yIHdoZW4gZGVjZWFzZWQgaXMganVzdCBhIGJvb2xlYW4KICovCgoKLyoKZGVmaW5lICJQUkVQIFByZXNjcmlwdGlvbiBEYXlzIjoKICBTdW0oCiAgICAoCiAgICAgIGNvbGxhcHNlICgKICAgICAgICBbTWVkaWNhdGlvblJlcXVlc3RdIE1SCiAgICAgICAgICB3aGVyZSBNUi5zdGF0dXMgPSAnY29tcGxldGVkJwogICAgICAgICAgYW5kIE1SLmludGVudCA9ICdvcmRlcicKICAgICAgICAgIGFuZCBNUi5tZWRpY2F0aW9uIH4gQ29uY2VwdHMuIlByRVAgZm9yIEhJViBwcmV2ZW50aW9uIgogICAgICAgIHJldHVybiBXQ29tVjIuIlByZXNjcmlwdGlvbiBSZWxldmFudCBQZXJpb2QiKCBNUiApIGludGVyc2VjdCAiTWVhc3VyZW1lbnQgUGVyaW9kIgogICAgICApCiAgICApIFBSRVBVc2VJbnRlcnZhbAogICAgICByZXR1cm4gZGF5cyBiZXR3ZWVuIHN0YXJ0IG9mIFBSRVBVc2VJbnRlcnZhbCBhbmQgZW5kIG9mIFBSRVBVc2VJbnRlcnZhbAogICkKKi8KCgpkZWZpbmUgZnVuY3Rpb24gR2V0RHVyYXRpb25JbkRheXModmFsdWUgRkhJUi5EdXJhdGlvbik6IC8vIHJldHVybnMgRGVjaW1hbDoKICAgIGNhc2UgdmFsdWUuY29kZS52YWx1ZQogICAgICB3aGVuICdhJyB0aGVuIHZhbHVlLnZhbHVlICogMzY1LjAKICAgICAgd2hlbiAnbW8nIHRoZW4gdmFsdWUudmFsdWUudmFsdWUgKiAzMC4wCiAgICAgIHdoZW4gJ3drJyB0aGVuIHZhbHVlLnZhbHVlLnZhbHVlICogNy4wCiAgICAgIHdoZW4gJ2QnIHRoZW4gdmFsdWUudmFsdWUudmFsdWUKICAgICAgd2hlbiAnaCcgdGhlbiB2YWx1ZS52YWx1ZS52YWx1ZSAvIDI0LjAKICAgICAgd2hlbiAnbWluJyB0aGVuIHZhbHVlLnZhbHVlLnZhbHVlIC8gNjAuMCAvIDI0LjAKICAgICAgd2hlbiAncycgdGhlbiB2YWx1ZS52YWx1ZS52YWx1ZSAvIDYwLjAgLyA2MC4wIC8gMjQuMAogICAgICB3aGVuICdtcycgdGhlbiB2YWx1ZS52YWx1ZS52YWx1ZSAvIDYwLjAgLyA2MC4wIC8gMjQuMCAvIDEwMDAuMAogICAgICBlbHNlIE1lc3NhZ2UoMTAwMCwgdHJ1ZSwgJ1VuZGVmaW5lZCcsICdFcnJvcicsICdVbnN1cHBvcnRlZCBkdXJhdGlvbiB1bml0ICcgKyB2YWx1ZS5jb2RlLnZhbHVlKQogICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gIlByZXNjcmlwdGlvbiBSZWxldmFudCBQZXJpb2QiKHByZXNjcmlwdGlvbiBGSElSLk1lZGljYXRpb25SZXF1ZXN0KToKICBpZiAoCiAgICBwcmVzY3JpcHRpb24uYXV0aG9yZWRPbiBpcyBub3QgbnVsbCBhbmQgcHJlc2NyaXB0aW9uLmRpc3BlbnNlUmVxdWVzdCBpcyBub3QgbnVsbAogICAgICBhbmQgcHJlc2NyaXB0aW9uLmRpc3BlbnNlUmVxdWVzdC5leHBlY3RlZFN1cHBseUR1cmF0aW9uIGlzIG5vdCBudWxsCiAgKQogIHRoZW4gSW50ZXJ2YWxbCiAgICBkYXRlIGZyb20gcHJlc2NyaXB0aW9uLmF1dGhvcmVkT24sCiAgICBkYXRlIGZyb20gcHJlc2NyaXB0aW9uLmF1dGhvcmVkT24gKyBTeXN0ZW0uUXVhbnRpdHl7IHZhbHVlOiBHZXREdXJhdGlvbkluRGF5cyhwcmVzY3JpcHRpb24uZGlzcGVuc2VSZXF1ZXN0LmV4cGVjdGVkU3VwcGx5RHVyYXRpb24pLCB1bml0OiAnZGF5cycgfQogIF0KICBlbHNlIG51bGwKCi8vU3lzdGVtLkludGVnZXIKZGVmaW5lIGZ1bmN0aW9uIFRvRGFpbHkoZnJlcXVlbmN5IFN5c3RlbS5JbnRlZ2VyLCBwZXJpb2QgU3lzdGVtLlF1YW50aXR5KToKICBjYXNlIHBlcmlvZC51bml0CiAgICB3aGVuICdoJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKQogICAgd2hlbiAnbWluJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAqIDYwCiAgICB3aGVuICdzJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAqIDYwICogNjAKICAgIHdoZW4gJ2QnIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpIC8gMjQKICAgIHdoZW4gJ3drJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAvICgyNCAqIDcpCiAgICB3aGVuICdtbycgdGhlbiBmcmVxdWVuY3kgKiAoMjQuMCAvIHBlcmlvZC52YWx1ZSkgLyAoMjQgKiAzMCkgLyogYXNzdW1pbmcgMzAgZGF5cyBpbiBtb250aCAqLwogICAgd2hlbiAnYScgdGhlbiBmcmVxdWVuY3kgKiAoMjQuMCAvIHBlcmlvZC52YWx1ZSkgLyAoMjQgKiAzNjUpIC8qIGFzc3VtaW5nIDM2NSBkYXlzIGluIHllYXIgKi8KICAgIHdoZW4gJ2hvdXInIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpCiAgICB3aGVuICdtaW51dGUnIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpICogNjAKICAgIHdoZW4gJ3NlY29uZCcgdGhlbiBmcmVxdWVuY3kgKiAoMjQuMCAvIHBlcmlvZC52YWx1ZSkgKiA2MCAqIDYwCiAgICB3aGVuICdkYXknIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpIC8gMjQKICAgIHdoZW4gJ3dlZWsnIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpIC8gKDI0ICogNykKICAgIHdoZW4gJ21vbnRoJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAvICgyNCAqIDMwKSAvKiBhc3N1bWluZyAzMCBkYXlzIGluIG1vbnRoICovCiAgICB3aGVuICd5ZWFyJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAvICgyNCAqIDM2NSkgLyogYXNzdW1pbmcgMzY1IGRheXMgaW4geWVhciAqLwogICAgd2hlbiAnaG91cnMnIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpCiAgICB3aGVuICdtaW51dGVzJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAqIDYwCiAgICB3aGVuICdzZWNvbmRzJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAqIDYwICogNjAKICAgIHdoZW4gJ2RheXMnIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpIC8gMjQKICAgIHdoZW4gJ3dlZWtzJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAvICgyNCAqIDcpCiAgICB3aGVuICdtb250aHMnIHRoZW4gZnJlcXVlbmN5ICogKDI0LjAgLyBwZXJpb2QudmFsdWUpIC8gKDI0ICogMzApIC8qIGFzc3VtaW5nIDMwIGRheXMgaW4gbW9udGggKi8KICAgIHdoZW4gJ3llYXJzJyB0aGVuIGZyZXF1ZW5jeSAqICgyNC4wIC8gcGVyaW9kLnZhbHVlKSAvICgyNCAqIDM2NSkgLyogYXNzdW1pbmcgMzY1IGRheXMgaW4geWVhciAqLwogICAgZWxzZSBudWxsCiAgZW5kCgogIGRlZmluZSBmdW5jdGlvbiAiSGFzRW5kIihwZXJpb2QgSW50ZXJ2YWw8RGF0ZVRpbWU+ICk6CiAgbm90IChlbmQgb2YgcGVyaW9kIGlzIG51bGwKICAgIG9yIGVuZCBvZiBwZXJpb2QgPSBtYXhpbXVtIERhdGVUaW1lCikKCiAgZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBNZWRpY2F0aW9uUmVxdWVzdFBlcmlvZChSZXF1ZXN0IE1lZGljYXRpb25SZXF1ZXN0KToKICBSZXF1ZXN0IFIKICAgIGxldAogICAgICBkb3NhZ2U6IHNpbmdsZXRvbiBmcm9tIFIuZG9zYWdlSW5zdHJ1Y3Rpb24sCiAgICAgIGRvc2VBbmRSYXRlOiBzaW5nbGV0b24gZnJvbSBkb3NhZ2UuZG9zZUFuZFJhdGUsCiAgICAgIGRvc2VSYW5nZTogZG9zZUFuZFJhdGUuZG9zZSBhcyBSYW5nZSwKICAgICAgZG9zZVF1YW50aXR5OiBkb3NlQW5kUmF0ZS5kb3NlIGFzIFNpbXBsZVF1YW50aXR5LAogICAgICBkb3NlOiBDb2FsZXNjZShlbmQgb2YgZG9zZVJhbmdlLCBkb3NlUXVhbnRpdHkpLAogICAgICB0aW1pbmc6IGRvc2FnZS50aW1pbmcsCiAgICAgIGZyZXF1ZW5jeTogQ29hbGVzY2UodGltaW5nLnJlcGVhdC5mcmVxdWVuY3lNYXgsIHRpbWluZy5yZXBlYXQuZnJlcXVlbmN5KSwKICAgICAgcGVyaW9kOiBTeXN0ZW0uUXVhbnRpdHkgeyB2YWx1ZTogdGltaW5nLnJlcGVhdC5wZXJpb2QsIHVuaXQ6IHRpbWluZy5yZXBlYXQucGVyaW9kVW5pdC52YWx1ZSB9LAogICAgICBkb3Nlc1BlckRheTogQ29hbGVzY2UoVG9EYWlseShGSElSSGVscGVycy5Ub0ludGVnZXIoZnJlcXVlbmN5KSwgcGVyaW9kKSwgQ291bnQodGltaW5nLnJlcGVhdC50aW1lT2ZEYXkpLCAxLjApLAogICAgICBib3VuZHNQZXJpb2Q6IHRpbWluZy5yZXBlYXQuYm91bmRzIGFzIFBlcmlvZCwKICAgICAgZGF5c1N1cHBseTogUi5kaXNwZW5zZVJlcXVlc3QuZXhwZWN0ZWRTdXBwbHlEdXJhdGlvbiwKICAgICAgcXVhbnRpdHk6IFIuZGlzcGVuc2VSZXF1ZXN0LnF1YW50aXR5LAogICAgICByZWZpbGxzOiBDb2FsZXNjZShSLmRpc3BlbnNlUmVxdWVzdC5udW1iZXJPZlJlcGVhdHNBbGxvd2VkLCAwKSwKICAgICAgc3RhcnREYXRlOgogICAgICAgIENvYWxlc2NlKAogICAgICAgICAgc3RhcnQgb2YgYm91bmRzUGVyaW9kLAogICAgICAgICAgc3RhcnQgb2YgUi5kaXNwZW5zZVJlcXVlc3QudmFsaWRpdHlQZXJpb2QsCiAgICAgICAgICBSLmF1dGhvcmVkT24KICAgICAgICApCiAgICByZXR1cm4KICAgICAgaWYgSGFzRW5kKGJvdW5kc1BlcmlvZCkgdGhlbgogICAgICAgIEludGVydmFsW3N0YXJ0RGF0ZSwgZW5kIG9mIGJvdW5kc1BlcmlvZF0KICAgICAgZWxzZQogICAgICAgICgKICAgICAgICAgIENvYWxlc2NlKGRheXNTdXBwbHksIHF1YW50aXR5IC8gKGRvc2UgKiBkb3Nlc1BlckRheSkpCiAgICAgICAgICAgICogKDEgKyByZWZpbGxzKQogICAgICAgICkgZHVyYXRpb25JbkRheXMKICAgICAgICAgIHJldHVybiBJbnRlcnZhbFtzdGFydERhdGUsIHN0YXJ0RGF0ZSArIGR1cmF0aW9uSW5EYXlzXQoKCmRlZmluZSBmdW5jdGlvbiAiRG9zZXNQZXJEYXkiKGZyZXF1ZW5jeSBDb2RlKToKCS8qQ2FsY3VsYXRlcyB0aGUgY3VtdWxhdGl2ZSBkb3NlIHBlciBkYXkgZm9yIGVhY2ggcHJlc2NyaXB0aW9uKi8KCWNhc2UKCQl3aGVuIGZyZXF1ZW5jeSB+IEN1c3RvbUNvbmNlcHRzLiJPbmNlIGRhaWx5IChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDEuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIlR3aWNlIGEgZGF5IChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDIuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIlRocmVlIHRpbWVzIGRhaWx5IChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDMuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkZvdXIgdGltZXMgZGFpbHkgKHF1YWxpZmllciB2YWx1ZSkiIHRoZW4gNC4wCgkJd2hlbiBmcmVxdWVuY3kgfiBDdXN0b21Db25jZXB0cy4iRXZlcnkgdHdlbnR5IGZvdXIgaG91cnMgKHF1YWxpZmllciB2YWx1ZSkiIHRoZW4gMS4wCgkJd2hlbiBmcmVxdWVuY3kgfiBDdXN0b21Db25jZXB0cy4iRXZlcnkgdHdlbHZlIGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDIuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkV2ZXJ5IHRoaXJ0eSBzaXggaG91cnMgKHF1YWxpZmllciB2YWx1ZSkiIHRoZW4gMC42NwoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkV2ZXJ5IGVpZ2h0IGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDMuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkV2ZXJ5IGZvdXIgaG91cnMgKHF1YWxpZmllciB2YWx1ZSkiIHRoZW4gNi4wCgkJd2hlbiBmcmVxdWVuY3kgfiBDdXN0b21Db25jZXB0cy4iRXZlcnkgc2l4IGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDQuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkV2ZXJ5IHNldmVudHkgdHdvIGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDAuMzQKCQl3aGVuIGZyZXF1ZW5jeSB+IEN1c3RvbUNvbmNlcHRzLiJFdmVyeSBmb3J0eSBlaWdodCBob3VycyAocXVhbGlmaWVyIHZhbHVlKSIgdGhlbiAwLjUKCQl3aGVuIGZyZXF1ZW5jeSB+IEN1c3RvbUNvbmNlcHRzLiJFdmVyeSBlaWdodCB0byB0d2VsdmUgaG91cnMgKHF1YWxpZmllciB2YWx1ZSkiIHRoZW4gMi4wCgkJd2hlbiBmcmVxdWVuY3kgfiBDdXN0b21Db25jZXB0cy4iRXZlcnkgc2l4IHRvIGVpZ2h0IGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDMuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkV2ZXJ5IHRocmVlIHRvIGZvdXIgaG91cnMgKHF1YWxpZmllciB2YWx1ZSkiIHRoZW4gNi4wCgkJd2hlbiBmcmVxdWVuY3kgfiBDdXN0b21Db25jZXB0cy4iRXZlcnkgdGhyZWUgdG8gc2l4IGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDQuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIkV2ZXJ5IHR3byB0byBmb3VyIGhvdXJzIChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDYuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIk9uZSB0byBmb3VyIHRpbWVzIGEgZGF5IChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDQuMAoJCXdoZW4gZnJlcXVlbmN5IH4gQ3VzdG9tQ29uY2VwdHMuIk9uZSB0byB0aHJlZSB0aW1lcyBhIGRheSAocXVhbGlmaWVyIHZhbHVlKSIgdGhlbiAzLjAKCQl3aGVuIGZyZXF1ZW5jeSB+IEN1c3RvbUNvbmNlcHRzLiJPbmUgdG8gdHdvIHRpbWVzIGEgZGF5IChxdWFsaWZpZXIgdmFsdWUpIiB0aGVuIDIuMAoJCWVsc2UgbnVsbCAKCWVuZAoKICAvL2RlZmluZSBmdW5jdGlvbiAiR2V0TWVkaWNhdGlvbkRhaWx5RG9zZSIoZG9zYWdlIFF1YW50aXR5LCBkb3Nlc1BlckRheSBEZWNpbWFsKToKICAvL2Rvc2FnZSAqIFF1YW50aXR5IHsgdmFsdWU6IGRvc2VzUGVyRGF5LCB1bml0OiAnL2QnIH0KCgpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIGRhaWx5RG9zZShSZXF1ZXN0ICJNZWRpY2F0aW9uUmVxdWVzdCIpOgogIFJlcXVlc3QgUgogICAgbGV0CiAgICAgIGRvc2FnZTogc2luZ2xldG9uIGZyb20gUi5kb3NhZ2VJbnN0cnVjdGlvbiwKICAgICAgZG9zZUFuZFJhdGU6IHNpbmdsZXRvbiBmcm9tIGRvc2FnZS5kb3NlQW5kUmF0ZSwKICAgICAgdGltaW5nOiBkb3NhZ2UudGltaW5nLAogICAgICBmcmVxdWVuY3k6IENvYWxlc2NlKHRpbWluZy5yZXBlYXQuZnJlcXVlbmN5TWF4LCB0aW1pbmcucmVwZWF0LmZyZXF1ZW5jeSksCiAgICAgIHBlcmlvZDogU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHRpbWluZy5yZXBlYXQucGVyaW9kLCB1bml0OiB0aW1pbmcucmVwZWF0LnBlcmlvZFVuaXQudmFsdWUgfSwKICAgICAgZG9zZVJhbmdlOiBkb3NlQW5kUmF0ZS5kb3NlIGFzIFJhbmdlLAogICAgICBkb3NlUXVhbnRpdHk6IGRvc2VBbmRSYXRlLmRvc2UgYXMgU2ltcGxlUXVhbnRpdHksCiAgICAgIGRvc2U6IENvYWxlc2NlKGRvc2VSYW5nZS5oaWdoLCBkb3NlUXVhbnRpdHkpLAogICAgICBkb3Nlc1BlckRheTogQ29hbGVzY2UoVG9EYWlseShmcmVxdWVuY3ksIHBlcmlvZCksIENvdW50KHRpbWluZy5yZXBlYXQudGltZU9mRGF5KSwgMS4wKSwKICAgICAgcXVhbnRpdHk6IFIuZGlzcGVuc2VSZXF1ZXN0LnF1YW50aXR5CiAgICByZXR1cm4KICAgICAgcXVhbnRpdHkgLyAoZG9zZSAqIGRvc2VzUGVyRGF5KQoKZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBkYWlseURvc2UoRGlzcGVuc2UgIk1lZGljYXRpb25EaXNwZW5zZSIpOgogIERpc3BlbnNlIEQKICAgIGxldAogICAgICBkb3NhZ2U6IHNpbmdsZXRvbiBmcm9tIEQuZG9zYWdlSW5zdHJ1Y3Rpb24sCiAgICAgIGRvc2VBbmRSYXRlOiBzaW5nbGV0b24gZnJvbSBkb3NhZ2UuZG9zZUFuZFJhdGUsCiAgICAgIHRpbWluZzogZG9zYWdlLnRpbWluZywKICAgICAgZnJlcXVlbmN5OiBDb2FsZXNjZSh0aW1pbmcucmVwZWF0LmZyZXF1ZW5jeU1heCwgdGltaW5nLnJlcGVhdC5mcmVxdWVuY3kpLAogICAgICBwZXJpb2Q6IFN5c3RlbS5RdWFudGl0eSB7IHZhbHVlOiB0aW1pbmcucmVwZWF0LnBlcmlvZCwgdW5pdDogdGltaW5nLnJlcGVhdC5wZXJpb2RVbml0LnZhbHVlIH0sCiAgICAgIGRvc2VSYW5nZTogZG9zZUFuZFJhdGUuZG9zZSBhcyBSYW5nZSwKICAgICAgZG9zZVF1YW50aXR5OiBkb3NlQW5kUmF0ZS5kb3NlIGFzIFNpbXBsZVF1YW50aXR5LAogICAgICBkb3NlOiBDb2FsZXNjZShkb3NlUmFuZ2UuaGlnaCwgZG9zZVF1YW50aXR5KSwKICAgICAgZG9zZXNQZXJEYXk6IENvYWxlc2NlKFRvRGFpbHkoZnJlcXVlbmN5LCBwZXJpb2QpLCBDb3VudCh0aW1pbmcucmVwZWF0LnRpbWVPZkRheSksIDEuMCkKICAgIHJldHVybgogICAgICBELnF1YW50aXR5IC8gKGRvc2UgKiBkb3Nlc1BlckRheSkKCgoKCgoKCiAgCiAgCgoKLy9uZWVkIHRvIGRlZmluZQogIGRlZmluZSBTVElfdGVzdGluZzoKICAgIFtQcm9jZWR1cmVdIFAKICB3aGVyZSBQLnN0YXR1cyA9ICdjb21wbGV0ZWQnCiAgYW5kIFAuY29kZSB+IENvbmNlcHRzLiJTVEkgdGVzdGluZyBhbmQgdHJlYXRtZW50IHNlcnZpY2VzIiAKCgovKioKICogQGRlc2NyaXB0aW9uIFRha2VzIGEgY2hvaWNlIGJldHdlZW4gYSBNZWRpY2F0aW9uIGFuZCBhIENvZGVhYmxlQ29uY2VwdCBhbmQgcmV0dXJucyBqdXN0IHRoZSBjb2RlIG9mIHRoZSBtZWRpY2F0aW9uCiAqLwpkZWZpbmUgZnVuY3Rpb24gRXh0cmFjdE1lZGljYXRpb25Db2RlKGNob2ljZSBDaG9pY2U8RkhJUi5Db2RlYWJsZUNvbmNlcHQsIEZISVIuUmVmZXJlbmNlPik6CiAgY2FzZQoJICB3aGVuIGNob2ljZSBpcyBGSElSLkNvZGVhYmxlQ29uY2VwdCB0aGVuCiAgICAJY2hvaWNlIGFzIEZISVIuQ29kZWFibGVDb25jZXB0CiAgICB3aGVuIGNob2ljZSBpcyBGSElSLlJlZmVyZW5jZSB0aGVuCiAgICAgIEZpcnN0KFtNZWRpY2F0aW9uXSBNIAogICAgICAgIHdoZXJlIE0uaWQgPSBMYXN0KFNwbGl0KGNob2ljZS5yZWZlcmVuY2UsICcvJykpCiAgICAgICAgcmV0dXJuIE0uY29kZSBhcyBGSElSLkNvZGVhYmxlQ29uY2VwdCkKICAgIGVsc2UKICAgICAgTWVzc2FnZShudWxsIGFzIEZISVIuQ29kZWFibGVDb25jZXB0LCB0cnVlLCAnMScsICdFcnJvcicsICdDYW5ub3QgY29tcHV0ZSBhIG1lZGljYXRpb24gY29kZScpIC8vIFRPRE86IEknbSBzdXJlIHRoYXQgdGhpcyBpcyBzdXBwb3J0ZWQgc29tZWhvdz8KICBlbmQKCi8qKgogKiBAZGVzY3JpcHRpb24gUmV0dXJucyB0aGUgY29kZSBvZiBhbiBPYnNlcnZhdGlvbiBDb2RlYWJsZUNvbmNlcHQKICovCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gZ2V0T2JzZXJ2YXRpb25Db2RlKG9ic2VydmF0aW9uIEZISVIuT2JzZXJ2YXRpb24pOgogIGNhc2UgCiAgICB3aGVuIG9ic2VydmF0aW9uLmNvZGUgaXMgRkhJUi5Db2RlYWJsZUNvbmNlcHQgdGhlbiAob2JzZXJ2YXRpb24udmFsdWUgYXMgQ29kZWFibGVDb25jZXB0KS5jb2RpbmcuY29kZQogICAgZWxzZSBudWxsCiAgZW5kCiAgCi8qKgogKiBAZGVzY3JpcHRpb24gUmV0dXJucyBhIGxpc3Qgb2YgY29kZXMgb2YgYW4gYSBsaXN0IG9mIE9ic2VydmF0aW9ucwogKi8KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBnZXRPYnNlcnZhdGlvbkNvZGVzKG9ic2xpc3QgTGlzdDxGSElSLk9ic2VydmF0aW9uPik6CiAgb2JzbGlzdCBvYnMgcmV0dXJuIG9icy5nZXRPYnNlcnZhdGlvbkNvZGUoKQoKCg=="
}
]
}