WHO Immunization Implementation Guide
1.0.0 - release

WHO Immunization Implementation Guide, published by WHO. This guide is not an authorized publication; it is the continuous build for version 1.0.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/WorldHealthOrganization/smart-immunizations/tree/132/merge and changes regularly. See the Directory of published versions

: WHOCommon - TTL Representation

Draft as of 2026-03-31

Raw ttl | Download

@prefix fhir: <http://hl7.org/fhir/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

# - resource -------------------------------------------------------------------

 a fhir:Library ;
  fhir:nodeRole fhir:treeRoot ;
  fhir:id [ fhir:v "WHOCommon"] ; # 
  fhir:meta [
    ( fhir:profile [
fhir:v "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-shareablelibrary"^^xsd:anyURI ;
fhir:l <http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-shareablelibrary>     ] [
fhir:v "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-publishablelibrary"^^xsd:anyURI ;
fhir:l <http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-publishablelibrary>     ] [
fhir:v "http://hl7.org/fhir/uv/cql/StructureDefinition/cql-library"^^xsd:anyURI ;
fhir:l <http://hl7.org/fhir/uv/cql/StructureDefinition/cql-library>     ] [
fhir:v "http://hl7.org/fhir/uv/cql/StructureDefinition/cql-module"^^xsd:anyURI ;
fhir:l <http://hl7.org/fhir/uv/cql/StructureDefinition/cql-module>     ] )
  ] ; # 
  fhir:text [
fhir:status [ fhir:v "extensions" ] ;
fhir:div [ fhir:v "<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;\">WHOCommon</td>\n        </tr>\n        \n\n        \n        \n        <tr>\n            <th scope=\"row\"><b>Id: </b></th>\n            <td style=\"padding-left: 4px;\">WHOCommon</td>\n        </tr>\n        \n\n        \n        \n        <tr>\n            <th scope=\"row\"><b>Version: </b></th>\n            <td style=\"padding-left: 4px;\">1.0.0</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-WHOCommon.html\">WHOCommon</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\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/7.1.0/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;\">2026-03-31 13:54:17+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>This library defines common terminologies and functions used throughout WHO SMART Guidelines content</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        <tr>\n            <th scope=\"row\"><b>Related Artifacts: </b></th>\n            <td style=\"padding-left: 4px;\">\n                \n                \n                \n                <p><b>Dependencies</b></p>\n                <ul>\n                  \n                    <li><a href=\"http://terminology.hl7.org/7.1.0/CodeSystem-condition-clinical.html\">Condition Clinical Status Codes</a></li>\n                  \n                </ul>\n                \n                \n                \n                \n                \n            </td>\n        </tr>\n        \n\n        \n        <tr>\n          <th scope=\"row\"><b>Parameters: </b></th>\n          <td style=\"padding-left: 4px;\">\n            <table class=\"grid-dict\">\n              <tr><th><b>Name</b></th><th><b>Type</b></th><th><b>Min</b></th><th><b>Max</b></th><th><b>In/Out</b></th></tr>\n              \n                <tr><th>Patient</th><th>Patient</th><th>0</th><th>1</th><th>Out</th></tr>\n              \n            </table>\n          </td>\n        </tr>\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 WHOCommon#xD;\n#xD;\nusing FHIR version '4.0.1'#xD;\n#xD;\ninclude FHIRHelpers version '4.0.1'#xD;\n#xD;\ncodesystem \"ConditionClinicalStatusCodes\": 'http://terminology.hl7.org/CodeSystem/condition-clinical'#xD;\n#xD;\n// Condition Clinical Status Codes - Consider value sets for these#xD;\ncode \"active\": 'active' from \"ConditionClinicalStatusCodes\"#xD;\ncode \"recurrence\": 'recurrence' from \"ConditionClinicalStatusCodes\"#xD;\ncode \"relapse\": 'relapse' from \"ConditionClinicalStatusCodes\"#xD;\ncode \"inactive\": 'inactive' from \"ConditionClinicalStatusCodes\"#xD;\ncode \"remission\": 'remission' from \"ConditionClinicalStatusCodes\"#xD;\ncode \"resolved\": 'resolved' from \"ConditionClinicalStatusCodes\"#xD;\n#xD;\ncontext Patient#xD;\n#xD;\ndefine fluent function official(identifiers List&lt;Identifier&gt;):#xD;\n  singleton from (identifiers I where I.use = 'official')#xD;\n#xD;\ndefine fluent function official(addresses List&lt;Address&gt;):#xD;\n  singleton from (addresses A where A.use = 'official')#xD;\n#xD;\ndefine fluent function official(names List&lt;HumanName&gt;):#xD;\n  singleton from (names N where N.use = 'official')#xD;\n#xD;\ndefine fluent function mobile(contactPoints List&lt;ContactPoint&gt;):#xD;\n  singleton from (contactPoints P where P.use = 'mobile')#xD;\n#xD;\ndefine fluent function only(allergies List&lt;AllergyIntolerance&gt;):#xD;\n  singleton from allergies#xD;\n#xD;\ndefine fluent function only(appointments List&lt;Appointment&gt;):#xD;\n  singleton from appointments#xD;\n#xD;\ndefine fluent function only(careplans List&lt;CarePlan&gt;):#xD;\n  singleton from careplans#xD;\n#xD;\ndefine fluent function only(conditions List&lt;Condition&gt;):#xD;\n  singleton from conditions#xD;\n#xD;\ndefine fluent function only(encounters List&lt;Encounter&gt;):#xD;\n  singleton from encounters#xD;\n#xD;\ndefine fluent function only(immunizations List&lt;Immunization&gt;):#xD;\n  singleton from immunizations#xD;\n#xD;\ndefine fluent function only(locations List&lt;Location&gt;):#xD;\n  singleton from locations#xD;\n#xD;\ndefine fluent function only(medicationrequests List&lt;MedicationRequest&gt;):#xD;\n  singleton from medicationrequests#xD;\n#xD;\ndefine fluent function only(observations List&lt;Observation&gt;):#xD;\n  singleton from observations#xD;\n#xD;\ndefine fluent function only(procedures List&lt;Procedure&gt;):#xD;\n  singleton from procedures#xD;\n#xD;\ndefine fluent function only(serviceRequests List&lt;ServiceRequest&gt;):#xD;\n  singleton from serviceRequests#xD;\n#xD;\ndefine fluent function only(dosages List&lt;Dosage&gt;):#xD;\n  singleton from dosages#xD;\n#xD;\ndefine fluent function only(doses List&lt;FHIR.Dosage.DoseAndRate&gt;):#xD;\n  singleton from doses#xD;\n#xD;\ndefine fluent function earliest(observations List&lt;Observation&gt;):#xD;\n  First(#xD;\n    observations O#xD;\n      sort by issued#xD;\n  )#xD;\n#xD;\ndefine fluent function earliest(immunizations List&lt;Immunization&gt;):#xD;\n  First(#xD;\n    immunizations I#xD;\n      sort by start of occurrence.toInterval()#xD;\n  )#xD;\n#xD;\n#xD;\ndefine fluent function latest(observations List&lt;Observation&gt;):#xD;\n  Last(#xD;\n    observations O#xD;\n      sort by issued#xD;\n  )#xD;\n#xD;\ndefine fluent function mostRecent(observations List&lt;Observation&gt;):#xD;\n  Last(#xD;\n    observations O#xD;\n      sort by issued#xD;\n  )#xD;\n#xD;\ndefine fluent function mostRecent(procedures List&lt;Procedure&gt;):#xD;\n  Last(#xD;\n    procedures P#xD;\n      sort by start of performed.toInterval()#xD;\n  )#xD;\n#xD;\ndefine fluent function mostRecent(immunizations List&lt;Immunization&gt;):#xD;\n  Last(#xD;\n    immunizations I#xD;\n      sort by start of occurrence.toInterval()#xD;\n  )#xD;\n#xD;\ndefine fluent function mostRecent(medicationRequests List&lt;MedicationRequest&gt;):#xD;\n  Last(#xD;\n    medicationRequests MR#xD;\n      sort by authoredOn#xD;\n  )#xD;\n#xD;\ndefine fluent function lowest(observations List&lt;Observation&gt;):#xD;\n  First(#xD;\n    observations O#xD;\n      sort by FHIRHelpers.ToQuantity(value as FHIR.Quantity)#xD;\n  )#xD;\n#xD;\ndefine fluent function highest(observations List&lt;Observation&gt;):#xD;\n  Last(#xD;\n    observations O#xD;\n      sort by FHIRHelpers.ToQuantity(value as FHIR.Quantity)#xD;\n  )#xD;\n#xD;\ndefine fluent function complete(observations List&lt;Observation&gt;):#xD;\n  observations O where O.status in { 'final', 'amended', 'corrected' }#xD;\n#xD;\ndefine function EarliestOf(dates List&lt;Date&gt;):#xD;\n  Min(dates)#xD;\n#xD;\ndefine function LatestOf(dates List&lt;Date&gt;):#xD;\n  Max(dates)#xD;\n#xD;\n/*#xD;\nFrom FHIRCommon 4.1.0:#xD;\n*/#xD;\n#xD;\n/*#xD;\n@description: Normalizes a value that is a choice of timing-valued types to an equivalent interval#xD;\n@comment: Normalizes a choice type of FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instance, FHIR.string, FHIR.Age, or FHIR.Range types#xD;\nto an equivalent interval. This selection of choice types is a superset of the majority of choice types that are used as possible#xD;\nrepresentations for timing-valued elements in FHIR, allowing this function to be used across any resource.#xD;\n#xD;\nThe input can be provided as a dateTime, Period, Timing, instant, string, Age, or Range.#xD;\nThe intent of this function is to provide a clear and concise mechanism to treat single#xD;\nelements that have multiple possible representations as intervals so that logic doesn't have to account#xD;\nfor the variability. More complex calculations (such as medication request period or dispense period#xD;\ncalculation) need specific guidance and consideration. That guidance may make use of this function, but#xD;\nthe focus of this function is on single element calculations where the semantics are unambiguous.#xD;\nIf the input is a dateTime, the result a DateTime Interval beginning and ending on that dateTime.#xD;\nIf the input is a Period, the result is a DateTime Interval.#xD;\nIf the input is a Timing, an error is raised indicating a single interval cannot be computed from a Timing.#xD;\nIf the input is an instant, the result is a DateTime Interval beginning and ending on that instant.#xD;\nIf the input is a string, an error is raised indicating a single interval cannot be computed from a string.#xD;\nIf the input is an Age, the result is a DateTime Interval beginning when the patient was the given Age,#xD;\nand ending immediately prior to when the patient was the given Age plus one year.#xD;\nIf the input is a Range, the result is a DateTime Interval beginning when the patient was the Age given#xD;\nby the low end of the Range, and ending immediately prior to when the patient was the Age given by the#xD;\nhigh end of the Range plus one year.#xD;\n#xD;\nNOTE: Due to the#xD;\ncomplexity of determining a single interval from a Timing or String type, this function will throw a run-time exception if it is used#xD;\nwith a Timing or String.#xD;\n*/#xD;\ndefine fluent function toInterval(choice Choice&lt;FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string, FHIR.Age, FHIR.Range&gt;):#xD;\n  case#xD;\n    when choice is FHIR.dateTime then#xD;\n      Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]#xD;\n    when choice is FHIR.Period then#xD;\n      FHIRHelpers.ToInterval(choice as FHIR.Period)#xD;\n    when choice is FHIR.instant then#xD;\n      Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as FHIR.instant)]#xD;\n    when choice is FHIR.Age then#xD;\n      Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),#xD;\n        FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1 year)#xD;\n    when choice is FHIR.Range then#xD;\n      Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).low),#xD;\n        FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).high) + 1 year)#xD;\n    when choice is FHIR.Timing then#xD;\n      Message(null as Interval&lt;DateTime&gt;, true, 'NOT_IMPLEMENTED', 'Error', 'Calculation of an interval from a Timing value is not supported')#xD;\n    when choice is FHIR.string then#xD;\n      Message(null as Interval&lt;DateTime&gt;, true, 'NOT_IMPLEMENTED', 'Error', 'Calculation of an interval from a String value is not supported')#xD;\n    else#xD;\n      null as Interval&lt;DateTime&gt;#xD;\n  end#xD;\n#xD;\n// Was getting error on this for this particular choice set not being found#xD;\n#xD;\n#xD;\n/*#xD;\n@description: Returns an interval representing the normalized Abatement of a given Condition resource.#xD;\n@comment: NOTE: Due to the complexity of determining an interval from a String, this function will throw#xD;\na run-time exception if used with a Condition instance that has a String as the abatement value.#xD;\n*/#xD;\ndefine fluent function abatementInterval(condition Condition):#xD;\n  if condition.abatement is FHIR.dateTime then#xD;\n    Interval[FHIRHelpers.ToDateTime(condition.abatement as FHIR.dateTime), FHIRHelpers.ToDateTime(condition.abatement as FHIR.dateTime)]#xD;\n  else if condition.abatement is FHIR.Period then#xD;\n    FHIRHelpers.ToInterval(condition.abatement as FHIR.Period)#xD;\n  else if condition.abatement is FHIR.string then#xD;\n    Message(null as Interval&lt;DateTime&gt;, true, 'NOT_IMPLEMENTED', 'Error', 'Calculation of an interval from a String value is not supported')#xD;\n  else if condition.abatement is FHIR.Age then#xD;\n    Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(condition.abatement as FHIR.Age),#xD;\n      FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(condition.abatement as FHIR.Age) + 1 year)#xD;\n  else if condition.abatement is FHIR.Range then#xD;\n    Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((condition.abatement as FHIR.Range).low),#xD;\n      FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((condition.abatement as FHIR.Range).high) + 1 year)#xD;\n  else if condition.abatement is FHIR.boolean then#xD;\n    Interval[end of condition.onset.toInterval(), condition.recordedDate)#xD;\n  else #xD;\n    null#xD;\n#xD;\n/*#xD;\n@description: Returns an interval representing the normalized prevalence period of a given Condition resource.#xD;\n@comment: Uses the toInterval and toAbatementInterval functions to determine the widest potential interval from#xD;\nonset to abatement as specified in the given Condition.#xD;\n*/#xD;\ndefine fluent function prevalenceInterval(condition Condition):#xD;\nif condition.clinicalStatus ~ \"active\"#xD;\n  or condition.clinicalStatus ~ \"recurrence\"#xD;\n  or condition.clinicalStatus ~ \"relapse\" then#xD;\n  Interval[start of condition.onset.toInterval(), end of condition.abatementInterval()]#xD;\nelse#xD;\n  (end of condition.abatementInterval()) abatementDate#xD;\n    return #xD;\n	  if abatementDate is null then#xD;\n        Interval[start of condition.onset.toInterval(), abatementDate)#xD;\n      else#xD;\n        Interval[start of condition.onset.toInterval(), abatementDate]#xD;\n#xD;\n/*#xD;\n@description: Returns true if the given reference is to the given resource#xD;\n@comment: Returns true if the `id` element of the given resource exactly equals the tail of the given reference.#xD;\nNOTE: This function assumes resources from the same source server.#xD;\n*/#xD;\ndefine fluent function references(reference FHIR.Reference, resource FHIR.Resource):#xD;\n  resource.id = Last(Split(reference.reference, '/'))  #xD;\n#xD;\n/*#xD;\n@description: Returns true if the given reference is to the given resourceId#xD;\n@comment: Returns true if the `resourceId` parameter exactly equals the tail of the given reference.#xD;\nNOTE: This function assumes resources from the same source server.#xD;\n*/#xD;\ndefine fluent function references(reference FHIR.Reference, resourceId String):#xD;\n  resourceId = Last(Split(reference.reference, '/'))#xD;\n#xD;\n/*#xD;\n@description: Returns true if any of the given references are to the given resource#xD;\n@comment: Returns true if the `id` element of the given resource exactly equals the tail of any of the given references.#xD;\nNOTE: This function assumes resources from the same source server.#xD;\n*/#xD;\ndefine fluent function references(references List&lt;FHIR.Reference&gt;, resource FHIR.Resource):#xD;\n  exists (references R where R.references(resource))#xD;\n  #xD;\n/*#xD;\n@description: Returns true if any of the given references are to the given resourceId#xD;\n@comment: Returns true if the `resourceId` parameter exactly equals the tail of any of the given references.#xD;\nNOTE: This function assumes resources from the same source server.#xD;\n*/#xD;\ndefine fluent function references(references List&lt;FHIR.Reference&gt;, resourceId String):#xD;\n  exists (references R where R.references(resourceId))#xD;\n</code></pre></td></tr>\n            </table>\n          </td>\n        </tr>\n        \n        \n        \n    </table>\n</div>\n</div>"^^rdf:XMLLiteral ]
  ] ; # 
  fhir:extension ( [
fhir:url [
fhir:v "http://hl7.org/fhir/StructureDefinition/cqf-knowledgeCapability"^^xsd:anyURI ;
fhir:l <http://hl7.org/fhir/StructureDefinition/cqf-knowledgeCapability>     ] ;
fhir:value [
a fhir:Code ;
fhir:v "computable"     ]
  ] ) ; # 
  fhir:url [
fhir:v "http://smart.who.int/immunizations/Library/WHOCommon"^^xsd:anyURI ;
fhir:l <http://smart.who.int/immunizations/Library/WHOCommon>
  ] ; # 
  fhir:version [ fhir:v "1.0.0"] ; # 
  fhir:name [ fhir:v "WHOCommon"] ; # 
  fhir:title [ fhir:v "WHOCommon"] ; # 
  fhir:status [ fhir:v "draft"] ; # 
  fhir:experimental [ fhir:v false] ; # 
  fhir:type [
    ( fhir:coding [
fhir:system [
fhir:v "http://terminology.hl7.org/CodeSystem/library-type"^^xsd:anyURI ;
fhir:l <http://terminology.hl7.org/CodeSystem/library-type>       ] ;
fhir:code [ fhir:v "logic-library" ]     ] )
  ] ; # 
  fhir:date [ fhir:v "2026-03-31T13:54:17+00:00"^^xsd:dateTime] ; # 
  fhir:publisher [ fhir:v "WHO"] ; # 
  fhir:contact ( [
fhir:name [ fhir:v "WHO" ] ;
    ( fhir:telecom [
fhir:system [ fhir:v "url" ] ;
fhir:value [ fhir:v "http://who.int" ]     ] )
  ] ) ; # 
  fhir:description [ fhir:v "This library defines common terminologies and functions used throughout WHO SMART Guidelines content"] ; # 
  fhir:relatedArtifact ( [
fhir:type [ fhir:v "depends-on" ] ;
fhir:display [ fhir:v "Code System ConditionClinicalStatusCodes" ] ;
fhir:resource [
fhir:v "http://terminology.hl7.org/CodeSystem/condition-clinical"^^xsd:anyURI ;
fhir:l <http://terminology.hl7.org/CodeSystem/condition-clinical>     ]
  ] ) ; # 
  fhir:parameter ( [
fhir:name [ fhir:v "Patient" ] ;
fhir:use [ fhir:v "out" ] ;
fhir:min [ fhir:v 0 ] ;
fhir:max [ fhir:v "1" ] ;
fhir:type [ fhir:v "Patient" ]
  ] ) ; # 
  fhir:content ( [
fhir:contentType [ fhir:v "text/cql" ] ;
fhir:data [ fhir:v "bGlicmFyeSBXSE9Db21tb24NCg0KdXNpbmcgRkhJUiB2ZXJzaW9uICc0LjAuMScNCg0KaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMScNCg0KY29kZXN5c3RlbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2NvbmRpdGlvbi1jbGluaWNhbCcNCg0KLy8gQ29uZGl0aW9uIENsaW5pY2FsIFN0YXR1cyBDb2RlcyAtIENvbnNpZGVyIHZhbHVlIHNldHMgZm9yIHRoZXNlDQpjb2RlICJhY3RpdmUiOiAnYWN0aXZlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIg0KY29kZSAicmVjdXJyZW5jZSI6ICdyZWN1cnJlbmNlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIg0KY29kZSAicmVsYXBzZSI6ICdyZWxhcHNlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIg0KY29kZSAiaW5hY3RpdmUiOiAnaW5hY3RpdmUnIGZyb20gIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiDQpjb2RlICJyZW1pc3Npb24iOiAncmVtaXNzaW9uJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIg0KY29kZSAicmVzb2x2ZWQiOiAncmVzb2x2ZWQnIGZyb20gIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiDQoNCmNvbnRleHQgUGF0aWVudA0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIG9mZmljaWFsKGlkZW50aWZpZXJzIExpc3Q8SWRlbnRpZmllcj4pOg0KICBzaW5nbGV0b24gZnJvbSAoaWRlbnRpZmllcnMgSSB3aGVyZSBJLnVzZSA9ICdvZmZpY2lhbCcpDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gb2ZmaWNpYWwoYWRkcmVzc2VzIExpc3Q8QWRkcmVzcz4pOg0KICBzaW5nbGV0b24gZnJvbSAoYWRkcmVzc2VzIEEgd2hlcmUgQS51c2UgPSAnb2ZmaWNpYWwnKQ0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIG9mZmljaWFsKG5hbWVzIExpc3Q8SHVtYW5OYW1lPik6DQogIHNpbmdsZXRvbiBmcm9tIChuYW1lcyBOIHdoZXJlIE4udXNlID0gJ29mZmljaWFsJykNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBtb2JpbGUoY29udGFjdFBvaW50cyBMaXN0PENvbnRhY3RQb2ludD4pOg0KICBzaW5nbGV0b24gZnJvbSAoY29udGFjdFBvaW50cyBQIHdoZXJlIFAudXNlID0gJ21vYmlsZScpDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gb25seShhbGxlcmdpZXMgTGlzdDxBbGxlcmd5SW50b2xlcmFuY2U+KToNCiAgc2luZ2xldG9uIGZyb20gYWxsZXJnaWVzDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gb25seShhcHBvaW50bWVudHMgTGlzdDxBcHBvaW50bWVudD4pOg0KICBzaW5nbGV0b24gZnJvbSBhcHBvaW50bWVudHMNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBvbmx5KGNhcmVwbGFucyBMaXN0PENhcmVQbGFuPik6DQogIHNpbmdsZXRvbiBmcm9tIGNhcmVwbGFucw0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIG9ubHkoY29uZGl0aW9ucyBMaXN0PENvbmRpdGlvbj4pOg0KICBzaW5nbGV0b24gZnJvbSBjb25kaXRpb25zDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gb25seShlbmNvdW50ZXJzIExpc3Q8RW5jb3VudGVyPik6DQogIHNpbmdsZXRvbiBmcm9tIGVuY291bnRlcnMNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBvbmx5KGltbXVuaXphdGlvbnMgTGlzdDxJbW11bml6YXRpb24+KToNCiAgc2luZ2xldG9uIGZyb20gaW1tdW5pemF0aW9ucw0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIG9ubHkobG9jYXRpb25zIExpc3Q8TG9jYXRpb24+KToNCiAgc2luZ2xldG9uIGZyb20gbG9jYXRpb25zDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gb25seShtZWRpY2F0aW9ucmVxdWVzdHMgTGlzdDxNZWRpY2F0aW9uUmVxdWVzdD4pOg0KICBzaW5nbGV0b24gZnJvbSBtZWRpY2F0aW9ucmVxdWVzdHMNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBvbmx5KG9ic2VydmF0aW9ucyBMaXN0PE9ic2VydmF0aW9uPik6DQogIHNpbmdsZXRvbiBmcm9tIG9ic2VydmF0aW9ucw0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIG9ubHkocHJvY2VkdXJlcyBMaXN0PFByb2NlZHVyZT4pOg0KICBzaW5nbGV0b24gZnJvbSBwcm9jZWR1cmVzDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gb25seShzZXJ2aWNlUmVxdWVzdHMgTGlzdDxTZXJ2aWNlUmVxdWVzdD4pOg0KICBzaW5nbGV0b24gZnJvbSBzZXJ2aWNlUmVxdWVzdHMNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBvbmx5KGRvc2FnZXMgTGlzdDxEb3NhZ2U+KToNCiAgc2luZ2xldG9uIGZyb20gZG9zYWdlcw0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIG9ubHkoZG9zZXMgTGlzdDxGSElSLkRvc2FnZS5Eb3NlQW5kUmF0ZT4pOg0KICBzaW5nbGV0b24gZnJvbSBkb3Nlcw0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIGVhcmxpZXN0KG9ic2VydmF0aW9ucyBMaXN0PE9ic2VydmF0aW9uPik6DQogIEZpcnN0KA0KICAgIG9ic2VydmF0aW9ucyBPDQogICAgICBzb3J0IGJ5IGlzc3VlZA0KICApDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gZWFybGllc3QoaW1tdW5pemF0aW9ucyBMaXN0PEltbXVuaXphdGlvbj4pOg0KICBGaXJzdCgNCiAgICBpbW11bml6YXRpb25zIEkNCiAgICAgIHNvcnQgYnkgc3RhcnQgb2Ygb2NjdXJyZW5jZS50b0ludGVydmFsKCkNCiAgKQ0KDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gbGF0ZXN0KG9ic2VydmF0aW9ucyBMaXN0PE9ic2VydmF0aW9uPik6DQogIExhc3QoDQogICAgb2JzZXJ2YXRpb25zIE8NCiAgICAgIHNvcnQgYnkgaXNzdWVkDQogICkNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBtb3N0UmVjZW50KG9ic2VydmF0aW9ucyBMaXN0PE9ic2VydmF0aW9uPik6DQogIExhc3QoDQogICAgb2JzZXJ2YXRpb25zIE8NCiAgICAgIHNvcnQgYnkgaXNzdWVkDQogICkNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBtb3N0UmVjZW50KHByb2NlZHVyZXMgTGlzdDxQcm9jZWR1cmU+KToNCiAgTGFzdCgNCiAgICBwcm9jZWR1cmVzIFANCiAgICAgIHNvcnQgYnkgc3RhcnQgb2YgcGVyZm9ybWVkLnRvSW50ZXJ2YWwoKQ0KICApDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gbW9zdFJlY2VudChpbW11bml6YXRpb25zIExpc3Q8SW1tdW5pemF0aW9uPik6DQogIExhc3QoDQogICAgaW1tdW5pemF0aW9ucyBJDQogICAgICBzb3J0IGJ5IHN0YXJ0IG9mIG9jY3VycmVuY2UudG9JbnRlcnZhbCgpDQogICkNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBtb3N0UmVjZW50KG1lZGljYXRpb25SZXF1ZXN0cyBMaXN0PE1lZGljYXRpb25SZXF1ZXN0Pik6DQogIExhc3QoDQogICAgbWVkaWNhdGlvblJlcXVlc3RzIE1SDQogICAgICBzb3J0IGJ5IGF1dGhvcmVkT24NCiAgKQ0KDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIGxvd2VzdChvYnNlcnZhdGlvbnMgTGlzdDxPYnNlcnZhdGlvbj4pOg0KICBGaXJzdCgNCiAgICBvYnNlcnZhdGlvbnMgTw0KICAgICAgc29ydCBieSBGSElSSGVscGVycy5Ub1F1YW50aXR5KHZhbHVlIGFzIEZISVIuUXVhbnRpdHkpDQogICkNCg0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBoaWdoZXN0KG9ic2VydmF0aW9ucyBMaXN0PE9ic2VydmF0aW9uPik6DQogIExhc3QoDQogICAgb2JzZXJ2YXRpb25zIE8NCiAgICAgIHNvcnQgYnkgRkhJUkhlbHBlcnMuVG9RdWFudGl0eSh2YWx1ZSBhcyBGSElSLlF1YW50aXR5KQ0KICApDQoNCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gY29tcGxldGUob2JzZXJ2YXRpb25zIExpc3Q8T2JzZXJ2YXRpb24+KToNCiAgb2JzZXJ2YXRpb25zIE8gd2hlcmUgTy5zdGF0dXMgaW4geyAnZmluYWwnLCAnYW1lbmRlZCcsICdjb3JyZWN0ZWQnIH0NCg0KZGVmaW5lIGZ1bmN0aW9uIEVhcmxpZXN0T2YoZGF0ZXMgTGlzdDxEYXRlPik6DQogIE1pbihkYXRlcykNCg0KZGVmaW5lIGZ1bmN0aW9uIExhdGVzdE9mKGRhdGVzIExpc3Q8RGF0ZT4pOg0KICBNYXgoZGF0ZXMpDQoNCi8qDQpGcm9tIEZISVJDb21tb24gNC4xLjA6DQoqLw0KDQovKg0KQGRlc2NyaXB0aW9uOiBOb3JtYWxpemVzIGEgdmFsdWUgdGhhdCBpcyBhIGNob2ljZSBvZiB0aW1pbmctdmFsdWVkIHR5cGVzIHRvIGFuIGVxdWl2YWxlbnQgaW50ZXJ2YWwNCkBjb21tZW50OiBOb3JtYWxpemVzIGEgY2hvaWNlIHR5cGUgb2YgRkhJUi5kYXRlVGltZSwgRkhJUi5QZXJpb2QsIEZISVIuVGltaW5nLCBGSElSLmluc3RhbmNlLCBGSElSLnN0cmluZywgRkhJUi5BZ2UsIG9yIEZISVIuUmFuZ2UgdHlwZXMNCnRvIGFuIGVxdWl2YWxlbnQgaW50ZXJ2YWwuIFRoaXMgc2VsZWN0aW9uIG9mIGNob2ljZSB0eXBlcyBpcyBhIHN1cGVyc2V0IG9mIHRoZSBtYWpvcml0eSBvZiBjaG9pY2UgdHlwZXMgdGhhdCBhcmUgdXNlZCBhcyBwb3NzaWJsZQ0KcmVwcmVzZW50YXRpb25zIGZvciB0aW1pbmctdmFsdWVkIGVsZW1lbnRzIGluIEZISVIsIGFsbG93aW5nIHRoaXMgZnVuY3Rpb24gdG8gYmUgdXNlZCBhY3Jvc3MgYW55IHJlc291cmNlLg0KDQpUaGUgaW5wdXQgY2FuIGJlIHByb3ZpZGVkIGFzIGEgZGF0ZVRpbWUsIFBlcmlvZCwgVGltaW5nLCBpbnN0YW50LCBzdHJpbmcsIEFnZSwgb3IgUmFuZ2UuDQpUaGUgaW50ZW50IG9mIHRoaXMgZnVuY3Rpb24gaXMgdG8gcHJvdmlkZSBhIGNsZWFyIGFuZCBjb25jaXNlIG1lY2hhbmlzbSB0byB0cmVhdCBzaW5nbGUNCmVsZW1lbnRzIHRoYXQgaGF2ZSBtdWx0aXBsZSBwb3NzaWJsZSByZXByZXNlbnRhdGlvbnMgYXMgaW50ZXJ2YWxzIHNvIHRoYXQgbG9naWMgZG9lc24ndCBoYXZlIHRvIGFjY291bnQNCmZvciB0aGUgdmFyaWFiaWxpdHkuIE1vcmUgY29tcGxleCBjYWxjdWxhdGlvbnMgKHN1Y2ggYXMgbWVkaWNhdGlvbiByZXF1ZXN0IHBlcmlvZCBvciBkaXNwZW5zZSBwZXJpb2QNCmNhbGN1bGF0aW9uKSBuZWVkIHNwZWNpZmljIGd1aWRhbmNlIGFuZCBjb25zaWRlcmF0aW9uLiBUaGF0IGd1aWRhbmNlIG1heSBtYWtlIHVzZSBvZiB0aGlzIGZ1bmN0aW9uLCBidXQNCnRoZSBmb2N1cyBvZiB0aGlzIGZ1bmN0aW9uIGlzIG9uIHNpbmdsZSBlbGVtZW50IGNhbGN1bGF0aW9ucyB3aGVyZSB0aGUgc2VtYW50aWNzIGFyZSB1bmFtYmlndW91cy4NCklmIHRoZSBpbnB1dCBpcyBhIGRhdGVUaW1lLCB0aGUgcmVzdWx0IGEgRGF0ZVRpbWUgSW50ZXJ2YWwgYmVnaW5uaW5nIGFuZCBlbmRpbmcgb24gdGhhdCBkYXRlVGltZS4NCklmIHRoZSBpbnB1dCBpcyBhIFBlcmlvZCwgdGhlIHJlc3VsdCBpcyBhIERhdGVUaW1lIEludGVydmFsLg0KSWYgdGhlIGlucHV0IGlzIGEgVGltaW5nLCBhbiBlcnJvciBpcyByYWlzZWQgaW5kaWNhdGluZyBhIHNpbmdsZSBpbnRlcnZhbCBjYW5ub3QgYmUgY29tcHV0ZWQgZnJvbSBhIFRpbWluZy4NCklmIHRoZSBpbnB1dCBpcyBhbiBpbnN0YW50LCB0aGUgcmVzdWx0IGlzIGEgRGF0ZVRpbWUgSW50ZXJ2YWwgYmVnaW5uaW5nIGFuZCBlbmRpbmcgb24gdGhhdCBpbnN0YW50Lg0KSWYgdGhlIGlucHV0IGlzIGEgc3RyaW5nLCBhbiBlcnJvciBpcyByYWlzZWQgaW5kaWNhdGluZyBhIHNpbmdsZSBpbnRlcnZhbCBjYW5ub3QgYmUgY29tcHV0ZWQgZnJvbSBhIHN0cmluZy4NCklmIHRoZSBpbnB1dCBpcyBhbiBBZ2UsIHRoZSByZXN1bHQgaXMgYSBEYXRlVGltZSBJbnRlcnZhbCBiZWdpbm5pbmcgd2hlbiB0aGUgcGF0aWVudCB3YXMgdGhlIGdpdmVuIEFnZSwNCmFuZCBlbmRpbmcgaW1tZWRpYXRlbHkgcHJpb3IgdG8gd2hlbiB0aGUgcGF0aWVudCB3YXMgdGhlIGdpdmVuIEFnZSBwbHVzIG9uZSB5ZWFyLg0KSWYgdGhlIGlucHV0IGlzIGEgUmFuZ2UsIHRoZSByZXN1bHQgaXMgYSBEYXRlVGltZSBJbnRlcnZhbCBiZWdpbm5pbmcgd2hlbiB0aGUgcGF0aWVudCB3YXMgdGhlIEFnZSBnaXZlbg0KYnkgdGhlIGxvdyBlbmQgb2YgdGhlIFJhbmdlLCBhbmQgZW5kaW5nIGltbWVkaWF0ZWx5IHByaW9yIHRvIHdoZW4gdGhlIHBhdGllbnQgd2FzIHRoZSBBZ2UgZ2l2ZW4gYnkgdGhlDQpoaWdoIGVuZCBvZiB0aGUgUmFuZ2UgcGx1cyBvbmUgeWVhci4NCg0KTk9URTogRHVlIHRvIHRoZQ0KY29tcGxleGl0eSBvZiBkZXRlcm1pbmluZyBhIHNpbmdsZSBpbnRlcnZhbCBmcm9tIGEgVGltaW5nIG9yIFN0cmluZyB0eXBlLCB0aGlzIGZ1bmN0aW9uIHdpbGwgdGhyb3cgYSBydW4tdGltZSBleGNlcHRpb24gaWYgaXQgaXMgdXNlZA0Kd2l0aCBhIFRpbWluZyBvciBTdHJpbmcuDQoqLw0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiB0b0ludGVydmFsKGNob2ljZSBDaG9pY2U8RkhJUi5kYXRlVGltZSwgRkhJUi5QZXJpb2QsIEZISVIuVGltaW5nLCBGSElSLmluc3RhbnQsIEZISVIuc3RyaW5nLCBGSElSLkFnZSwgRkhJUi5SYW5nZT4pOg0KICBjYXNlDQogICAgd2hlbiBjaG9pY2UgaXMgRkhJUi5kYXRlVGltZSB0aGVuDQogICAgICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNob2ljZSBhcyBGSElSLmRhdGVUaW1lKSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5kYXRlVGltZSldDQogICAgd2hlbiBjaG9pY2UgaXMgRkhJUi5QZXJpb2QgdGhlbg0KICAgICAgRkhJUkhlbHBlcnMuVG9JbnRlcnZhbChjaG9pY2UgYXMgRkhJUi5QZXJpb2QpDQogICAgd2hlbiBjaG9pY2UgaXMgRkhJUi5pbnN0YW50IHRoZW4NCiAgICAgIEludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZVRpbWUoY2hvaWNlIGFzIEZISVIuaW5zdGFudCksIEZISVJIZWxwZXJzLlRvRGF0ZVRpbWUoY2hvaWNlIGFzIEZISVIuaW5zdGFudCldDQogICAgd2hlbiBjaG9pY2UgaXMgRkhJUi5BZ2UgdGhlbg0KICAgICAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoY2hvaWNlIGFzIEZISVIuQWdlKSwNCiAgICAgICAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoY2hvaWNlIGFzIEZISVIuQWdlKSArIDEgeWVhcikNCiAgICB3aGVuIGNob2ljZSBpcyBGSElSLlJhbmdlIHRoZW4NCiAgICAgIEludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KChjaG9pY2UgYXMgRkhJUi5SYW5nZSkubG93KSwNCiAgICAgICAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNob2ljZSBhcyBGSElSLlJhbmdlKS5oaWdoKSArIDEgeWVhcikNCiAgICB3aGVuIGNob2ljZSBpcyBGSElSLlRpbWluZyB0aGVuDQogICAgICBNZXNzYWdlKG51bGwgYXMgSW50ZXJ2YWw8RGF0ZVRpbWU+LCB0cnVlLCAnTk9UX0lNUExFTUVOVEVEJywgJ0Vycm9yJywgJ0NhbGN1bGF0aW9uIG9mIGFuIGludGVydmFsIGZyb20gYSBUaW1pbmcgdmFsdWUgaXMgbm90IHN1cHBvcnRlZCcpDQogICAgd2hlbiBjaG9pY2UgaXMgRkhJUi5zdHJpbmcgdGhlbg0KICAgICAgTWVzc2FnZShudWxsIGFzIEludGVydmFsPERhdGVUaW1lPiwgdHJ1ZSwgJ05PVF9JTVBMRU1FTlRFRCcsICdFcnJvcicsICdDYWxjdWxhdGlvbiBvZiBhbiBpbnRlcnZhbCBmcm9tIGEgU3RyaW5nIHZhbHVlIGlzIG5vdCBzdXBwb3J0ZWQnKQ0KICAgIGVsc2UNCiAgICAgIG51bGwgYXMgSW50ZXJ2YWw8RGF0ZVRpbWU+DQogIGVuZA0KDQovLyBXYXMgZ2V0dGluZyBlcnJvciBvbiB0aGlzIGZvciB0aGlzIHBhcnRpY3VsYXIgY2hvaWNlIHNldCBub3QgYmVpbmcgZm91bmQNCg0KDQovKg0KQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuDQpAY29tbWVudDogTk9URTogRHVlIHRvIHRoZSBjb21wbGV4aXR5IG9mIGRldGVybWluaW5nIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcsIHRoaXMgZnVuY3Rpb24gd2lsbCB0aHJvdw0KYSBydW4tdGltZSBleGNlcHRpb24gaWYgdXNlZCB3aXRoIGEgQ29uZGl0aW9uIGluc3RhbmNlIHRoYXQgaGFzIGEgU3RyaW5nIGFzIHRoZSBhYmF0ZW1lbnQgdmFsdWUuDQoqLw0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiBhYmF0ZW1lbnRJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToNCiAgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLmRhdGVUaW1lIHRoZW4NCiAgICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5kYXRlVGltZSksIEZISVJIZWxwZXJzLlRvRGF0ZVRpbWUoY29uZGl0aW9uLmFiYXRlbWVudCBhcyBGSElSLmRhdGVUaW1lKV0NCiAgZWxzZSBpZiBjb25kaXRpb24uYWJhdGVtZW50IGlzIEZISVIuUGVyaW9kIHRoZW4NCiAgICBGSElSSGVscGVycy5Ub0ludGVydmFsKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5QZXJpb2QpDQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLnN0cmluZyB0aGVuDQogICAgTWVzc2FnZShudWxsIGFzIEludGVydmFsPERhdGVUaW1lPiwgdHJ1ZSwgJ05PVF9JTVBMRU1FTlRFRCcsICdFcnJvcicsICdDYWxjdWxhdGlvbiBvZiBhbiBpbnRlcnZhbCBmcm9tIGEgU3RyaW5nIHZhbHVlIGlzIG5vdCBzdXBwb3J0ZWQnKQ0KICBlbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5BZ2UgdGhlbg0KICAgIEludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5BZ2UpLA0KICAgICAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoY29uZGl0aW9uLmFiYXRlbWVudCBhcyBGSElSLkFnZSkgKyAxIHllYXIpDQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLlJhbmdlIHRoZW4NCiAgICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eSgoY29uZGl0aW9uLmFiYXRlbWVudCBhcyBGSElSLlJhbmdlKS5sb3cpLA0KICAgICAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5SYW5nZSkuaGlnaCkgKyAxIHllYXIpDQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLmJvb2xlYW4gdGhlbg0KICAgIEludGVydmFsW2VuZCBvZiBjb25kaXRpb24ub25zZXQudG9JbnRlcnZhbCgpLCBjb25kaXRpb24ucmVjb3JkZWREYXRlKQ0KICBlbHNlIA0KICAgIG51bGwNCg0KLyoNCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuDQpAY29tbWVudDogVXNlcyB0aGUgdG9JbnRlcnZhbCBhbmQgdG9BYmF0ZW1lbnRJbnRlcnZhbCBmdW5jdGlvbnMgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsIGZyb20NCm9uc2V0IHRvIGFiYXRlbWVudCBhcyBzcGVjaWZpZWQgaW4gdGhlIGdpdmVuIENvbmRpdGlvbi4NCiovDQpkZWZpbmUgZmx1ZW50IGZ1bmN0aW9uIHByZXZhbGVuY2VJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToNCmlmIGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJhY3RpdmUiDQogIG9yIGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJyZWN1cnJlbmNlIg0KICBvciBjb25kaXRpb24uY2xpbmljYWxTdGF0dXMgfiAicmVsYXBzZSIgdGhlbg0KICBJbnRlcnZhbFtzdGFydCBvZiBjb25kaXRpb24ub25zZXQudG9JbnRlcnZhbCgpLCBlbmQgb2YgY29uZGl0aW9uLmFiYXRlbWVudEludGVydmFsKCldDQplbHNlDQogIChlbmQgb2YgY29uZGl0aW9uLmFiYXRlbWVudEludGVydmFsKCkpIGFiYXRlbWVudERhdGUNCiAgICByZXR1cm4gDQoJICBpZiBhYmF0ZW1lbnREYXRlIGlzIG51bGwgdGhlbg0KICAgICAgICBJbnRlcnZhbFtzdGFydCBvZiBjb25kaXRpb24ub25zZXQudG9JbnRlcnZhbCgpLCBhYmF0ZW1lbnREYXRlKQ0KICAgICAgZWxzZQ0KICAgICAgICBJbnRlcnZhbFtzdGFydCBvZiBjb25kaXRpb24ub25zZXQudG9JbnRlcnZhbCgpLCBhYmF0ZW1lbnREYXRlXQ0KDQovKg0KQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuIHJlZmVyZW5jZSBpcyB0byB0aGUgZ2l2ZW4gcmVzb3VyY2UNCkBjb21tZW50OiBSZXR1cm5zIHRydWUgaWYgdGhlIGBpZGAgZWxlbWVudCBvZiB0aGUgZ2l2ZW4gcmVzb3VyY2UgZXhhY3RseSBlcXVhbHMgdGhlIHRhaWwgb2YgdGhlIGdpdmVuIHJlZmVyZW5jZS4NCk5PVEU6IFRoaXMgZnVuY3Rpb24gYXNzdW1lcyByZXNvdXJjZXMgZnJvbSB0aGUgc2FtZSBzb3VyY2Ugc2VydmVyLg0KKi8NCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gcmVmZXJlbmNlcyhyZWZlcmVuY2UgRkhJUi5SZWZlcmVuY2UsIHJlc291cmNlIEZISVIuUmVzb3VyY2UpOg0KICByZXNvdXJjZS5pZCA9IExhc3QoU3BsaXQocmVmZXJlbmNlLnJlZmVyZW5jZSwgJy8nKSkgIA0KDQovKg0KQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuIHJlZmVyZW5jZSBpcyB0byB0aGUgZ2l2ZW4gcmVzb3VyY2VJZA0KQGNvbW1lbnQ6IFJldHVybnMgdHJ1ZSBpZiB0aGUgYHJlc291cmNlSWRgIHBhcmFtZXRlciBleGFjdGx5IGVxdWFscyB0aGUgdGFpbCBvZiB0aGUgZ2l2ZW4gcmVmZXJlbmNlLg0KTk9URTogVGhpcyBmdW5jdGlvbiBhc3N1bWVzIHJlc291cmNlcyBmcm9tIHRoZSBzYW1lIHNvdXJjZSBzZXJ2ZXIuDQoqLw0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiByZWZlcmVuY2VzKHJlZmVyZW5jZSBGSElSLlJlZmVyZW5jZSwgcmVzb3VyY2VJZCBTdHJpbmcpOg0KICByZXNvdXJjZUlkID0gTGFzdChTcGxpdChyZWZlcmVuY2UucmVmZXJlbmNlLCAnLycpKQ0KDQovKg0KQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRydWUgaWYgYW55IG9mIHRoZSBnaXZlbiByZWZlcmVuY2VzIGFyZSB0byB0aGUgZ2l2ZW4gcmVzb3VyY2UNCkBjb21tZW50OiBSZXR1cm5zIHRydWUgaWYgdGhlIGBpZGAgZWxlbWVudCBvZiB0aGUgZ2l2ZW4gcmVzb3VyY2UgZXhhY3RseSBlcXVhbHMgdGhlIHRhaWwgb2YgYW55IG9mIHRoZSBnaXZlbiByZWZlcmVuY2VzLg0KTk9URTogVGhpcyBmdW5jdGlvbiBhc3N1bWVzIHJlc291cmNlcyBmcm9tIHRoZSBzYW1lIHNvdXJjZSBzZXJ2ZXIuDQoqLw0KZGVmaW5lIGZsdWVudCBmdW5jdGlvbiByZWZlcmVuY2VzKHJlZmVyZW5jZXMgTGlzdDxGSElSLlJlZmVyZW5jZT4sIHJlc291cmNlIEZISVIuUmVzb3VyY2UpOg0KICBleGlzdHMgKHJlZmVyZW5jZXMgUiB3aGVyZSBSLnJlZmVyZW5jZXMocmVzb3VyY2UpKQ0KICANCi8qDQpAZGVzY3JpcHRpb246IFJldHVybnMgdHJ1ZSBpZiBhbnkgb2YgdGhlIGdpdmVuIHJlZmVyZW5jZXMgYXJlIHRvIHRoZSBnaXZlbiByZXNvdXJjZUlkDQpAY29tbWVudDogUmV0dXJucyB0cnVlIGlmIHRoZSBgcmVzb3VyY2VJZGAgcGFyYW1ldGVyIGV4YWN0bHkgZXF1YWxzIHRoZSB0YWlsIG9mIGFueSBvZiB0aGUgZ2l2ZW4gcmVmZXJlbmNlcy4NCk5PVEU6IFRoaXMgZnVuY3Rpb24gYXNzdW1lcyByZXNvdXJjZXMgZnJvbSB0aGUgc2FtZSBzb3VyY2Ugc2VydmVyLg0KKi8NCmRlZmluZSBmbHVlbnQgZnVuY3Rpb24gcmVmZXJlbmNlcyhyZWZlcmVuY2VzIExpc3Q8RkhJUi5SZWZlcmVuY2U+LCByZXNvdXJjZUlkIFN0cmluZyk6DQogIGV4aXN0cyAocmVmZXJlbmNlcyBSIHdoZXJlIFIucmVmZXJlbmNlcyhyZXNvdXJjZUlkKSkNCg=="^^xsd:base64Binary ]
  ] ) . #