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