Pre-requirements
The easiest way to install the core bupaR packages is by installing
the bupaverse-package.
install.packages("bupaverse")
install.packages("processanimateR")
install.packages("psmineR")
You can then load the packages using library().
library(bupaverse)
print(R.version.string)
In case R needs to be updated to version 4.4.2, update the packages
after installation.
update.packages("bupaR")
update.packages("eventdataR")
update.packages("xesreadR")
update.packages("edeaR")
update.packages("processmapR")
update.packages("processmonitR")
library(bupaverse)
library(dplyr)
library(processanimateR)
library(bpmnR)
library(processcheckR)
Loading an event log
log_xes <- read_xes("C:/Users/Admin/Downloads/Sepsis Cases - Event Log.xes")
log_xes
# Log of 15214 events consisting of:
846 traces
1050 cases
15214 instances of 16 activities
1 resource
Events occurred from 2013-11-07 07:18:29 until 2015-06-05 10:25:11
# Variables were mapped as follows:
Case identifier: CASE_concept_name
Activity identifier: activity_id
Resource identifier: resource_id
Activity instance identifier: activity_instance_id
Timestamp: timestamp
Lifecycle transition: lifecycle_id
The mapping function can be used to retrieve all the meta data from a
log object, i.e. the relation between log identifiers and the
corresponding data fields.
log_xes %>% activities()
#log_xes %>% cases()
#log_xes %>% resources()
#log_xes %>% traces()
Discovery
Log statistics
An analysis of the control flow can be done by using metrics on
activities and traces.
activity_presence <- log_xes %>% activity_presence()
activity_presence %>% plot()

The trace coverage metric shows the relationship between the number
of different activity sequences (i.e. traces) and the number of cases
they cover.
trace_coverage <- log_xes %>% trace_coverage("trace")
trace_coverage %>% plot()

The trace length metric describes the length of traces, i.e. the
number of activity instances for each case. It can be computed at the
levels case, trace and log.
trace_length <- log_xes %>% trace_length("log")
trace_length %>% plot()

Process models
Frequency maps
A process map of a log can be created using process_map(). A process
map is a directly-follows graph, where each distinct activity is
represented by a node, and each directly-follows relationship between
activities is shown by directed edges, i.e. arrows between the
nodes.
frequency <- log_xes %>% filter_trace_frequency(percentage = 0.8)
frequency %>% process_map(frequency("absolute"))
Animation
It is possible to determine the aesthetics of tokens regardless of
the timestamps at which activities occurred. This could be useful if
some measurements were taken throughout a process, but the measurement
event itself should not be included in the process map.
For example, the lacticacid measurements could be used in that
way:
# Extract only the lacticacid measurements
lactic <- log_xes %>%
mutate(LacticAcid = as.numeric(LacticAcid)) %>%
filter_activity(c("LacticAcid")) %>%
as.data.frame() %>%
select("case" = CASE_concept_name,
"time" = timestamp,
value = LacticAcid) # format needs to be 'case,time,value'
# Remove the measurement events from the sepsis log
sepsisBase <- log_xes %>%
filter_activity(c("LacticAcid", "CRP", "Leucocytes", "Return ER",
"IV Liquid", "IV Antibiotics"), reverse = T) %>%
filter_trace_frequency(percentage = 0.95)
# Animate with the secondary data frame `lactic`
animate_process(sepsisBase,
mode = "relative",
duration = 300,
legend = "color",
mapping = token_aes(color = token_scale(lactic,
scale = "linear",
range = c("#fff5eb","#7f2704"))))
Process visualizations
Process matrix
A process matrix is a two-dimensional matrix showing the flows
between activities. Its configuration is exactly the same as that used
by process_map().
matrix_frequency <- log_xes %>% process_matrix(frequency("absolute"))
matrix_frequency %>% plot()

Dotted chart
Dotted charts can be made with dotted_chart(). A dotted chart is a
graph in which each activity instance is displayed by a dot. The x-axis
refers to the time aspect, while the y-axis refers to cases.
dotted_chart <- log_xes %>% dotted_chart(x = "absolute")
dotted_chart

Trace explorer
Different activity sequences in the log can be visualized with
trace_explorer(). It can be used to explore frequent as well as
infrequent traces.
trace_explorer <- log_xes %>% trace_explorer()
Warning: No `coverage` or `n_traces` set.
! Defaulting to `coverage` = 0.2 for `type` = "frequent" traces.
trace_explorer

Filtering
Infrequent flows
Filtering infrequent flows allows us to select a set of cases in
which every directly-follows flow has a minimum frequency. For example,
consider the process map below.
log_xes %>% process_map()
In this map, we can observe several unique directly follows
relations, as well as flows occurring less than 30 times. Using the
filter, we can remove the cases that lead to these flows as follows:
log_xes <- log_xes %>%
mutate(activity_instance_id = as.character(activity_instance_id))
infrequent_flows <- log_xes %>% filter_infrequent_flows(min_n = 30) %>% process_map()
infrequent_flows
Time period
Filtering cases by time period can be done using the
filter_time_period(). There are four different filter_method’s that act
as case filters:
- “start”: all cases started in an interval.
- “complete”: all cases completed in an interval.
- “contained”: all cases contained in an interval.
- “intersecting”: all cases with some activity in an interval.
Using the interval January 2015, you can compare the results of
different filtering methods below using dotted charts.
time_period <- log_xes %>% filter_time_period(interval = ymd(c(20150101, 20150131)), filter_method = "start") %>% dotted_chart()
time_period

Case condition
filter_case_condition() can be used to select cases for which a
condition holds. This condition can be related to any of the variables
in the log.
For example, select all cases where age higher than 85 is
involved.
age_85 <- log_xes %>% filter(!is.na(Age)) %>% filter_case_condition(Age >= 85)
age_85
# Log of 304 events consisting of:
6 traces
304 cases
304 instances of 6 activities
1 resource
Events occurred from 2013-11-07 07:18:29 until 2015-02-19 17:15:45
# Variables were mapped as follows:
Case identifier: CASE_concept_name
Activity identifier: activity_id
Resource identifier: resource_id
Activity instance identifier: activity_instance_id
Timestamp: timestamp
Lifecycle transition: lifecycle_id
process_map(age_85)
Precedence
The filter_precedence() allows us to filter cases based on flows
between activities.
If there is more than one antecedent or consequent activity, the
filter will test all possible pairs. The filter_method will tell the
filter whether all of the rules should hold, at least one, or none are
allowed.
The following filter takes only cases where Triage and Assessment is
directly followed by Blood test.
precedence <- log_xes %>%
filter_precedence(antecedents = "ER Triage",
consequents = "Leucocytes",
precedence_type = "directly_follows") %>%
traces()
head(precedence)
LS0tDQp0aXRsZTogIkJ1cGFSIHR1dG9yaWFsIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgUmVzb3VyY2VzDQoNCiogRG9jdW1lbnRhdGlvbjogaHR0cHM6Ly9idXBhci5uZXQvDQoqIENoZWV0IHNoZWV0Omh0dHBzOi8vd3d3LmJ1cGFyLm5ldC9tYXRlcmlhbHMvMjAxNzA5MDQlMjBwb3N0ZXIlMjBidXBhUi5wZGYNCiogR2l0SHViOiBodHRwczovL2dpdGh1Yi5jb20vYnVwYXZlcnNlLw0KDQojIyBQcmUtcmVxdWlyZW1lbnRzDQoNClRoZSBlYXNpZXN0IHdheSB0byBpbnN0YWxsIHRoZSBjb3JlIGJ1cGFSIHBhY2thZ2VzIGlzIGJ5IGluc3RhbGxpbmcgdGhlIGJ1cGF2ZXJzZS1wYWNrYWdlLg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImJ1cGF2ZXJzZSIpDQpgYGANCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJwcm9jZXNzYW5pbWF0ZVIiKQ0KaW5zdGFsbC5wYWNrYWdlcygicHNtaW5lUiIpDQpgYGANCg0KWW91IGNhbiB0aGVuIGxvYWQgdGhlIHBhY2thZ2VzIHVzaW5nIGxpYnJhcnkoKS4NCg0KYGBge3J9DQpsaWJyYXJ5KGJ1cGF2ZXJzZSkNCnByaW50KFIudmVyc2lvbi5zdHJpbmcpDQpgYGANCg0KSW4gY2FzZSBSIG5lZWRzIHRvIGJlIHVwZGF0ZWQgdG8gdmVyc2lvbiA0LjQuMiwgdXBkYXRlIHRoZSBwYWNrYWdlcyBhZnRlciBpbnN0YWxsYXRpb24uDQoNCmBgYHtyfQ0KdXBkYXRlLnBhY2thZ2VzKCJidXBhUiIpDQp1cGRhdGUucGFja2FnZXMoImV2ZW50ZGF0YVIiKQ0KdXBkYXRlLnBhY2thZ2VzKCJ4ZXNyZWFkUiIpDQp1cGRhdGUucGFja2FnZXMoImVkZWFSIikNCnVwZGF0ZS5wYWNrYWdlcygicHJvY2Vzc21hcFIiKQ0KdXBkYXRlLnBhY2thZ2VzKCJwcm9jZXNzbW9uaXRSIikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoYnVwYXZlcnNlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocHJvY2Vzc2FuaW1hdGVSKQ0KbGlicmFyeShicG1uUikNCmxpYnJhcnkocHJvY2Vzc2NoZWNrUikNCmBgYA0KDQojIyBMb2FkaW5nIGFuIGV2ZW50IGxvZw0KDQpgYGB7cn0NCmxvZ194ZXMgPC0gcmVhZF94ZXMoIkM6L1VzZXJzL0FkbWluL0Rvd25sb2Fkcy9TZXBzaXMgQ2FzZXMgLSBFdmVudCBMb2cueGVzIikNCmBgYA0KDQpgYGB7cn0NCmxvZ194ZXMNCmBgYA0KVGhlIG1hcHBpbmcgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gcmV0cmlldmUgYWxsIHRoZSBtZXRhIGRhdGEgZnJvbSBhIGxvZyBvYmplY3QsIGkuZS4gdGhlIHJlbGF0aW9uIGJldHdlZW4gbG9nIGlkZW50aWZpZXJzIGFuZCB0aGUgY29ycmVzcG9uZGluZyBkYXRhIGZpZWxkcy4NCg0KYGBge3J9DQpsb2dfeGVzICU+JSBhY3Rpdml0aWVzKCkNCiNsb2dfeGVzICU+JSBjYXNlcygpDQojbG9nX3hlcyAlPiUgcmVzb3VyY2VzKCkNCiNsb2dfeGVzICU+JSB0cmFjZXMoKQ0KYGBgDQoNCiMjIERpc2NvdmVyeQ0KDQojIyMgTG9nIHN0YXRpc3RpY3MNCg0KQW4gYW5hbHlzaXMgb2YgdGhlIGNvbnRyb2wgZmxvdyBjYW4gYmUgZG9uZSBieSB1c2luZyBtZXRyaWNzIG9uIGFjdGl2aXRpZXMgYW5kIHRyYWNlcy4NCg0KYGBge3J9DQphY3Rpdml0eV9wcmVzZW5jZSA8LSBsb2dfeGVzICU+JSBhY3Rpdml0eV9wcmVzZW5jZSgpDQphY3Rpdml0eV9wcmVzZW5jZSAlPiUgcGxvdCgpDQpgYGANClRoZSB0cmFjZSBjb3ZlcmFnZSBtZXRyaWMgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50IGFjdGl2aXR5IHNlcXVlbmNlcyAoaS5lLiB0cmFjZXMpIGFuZCB0aGUgbnVtYmVyIG9mIGNhc2VzIHRoZXkgY292ZXIuDQoNCmBgYHtyfQ0KdHJhY2VfY292ZXJhZ2UgPC0gbG9nX3hlcyAlPiUgdHJhY2VfY292ZXJhZ2UoInRyYWNlIikNCnRyYWNlX2NvdmVyYWdlICU+JSBwbG90KCkNCmBgYA0KVGhlIHRyYWNlIGxlbmd0aCBtZXRyaWMgZGVzY3JpYmVzIHRoZSBsZW5ndGggb2YgdHJhY2VzLCBpLmUuIHRoZSBudW1iZXIgb2YgYWN0aXZpdHkgaW5zdGFuY2VzIGZvciBlYWNoIGNhc2UuIEl0IGNhbiBiZSBjb21wdXRlZCBhdCB0aGUgbGV2ZWxzIGNhc2UsIHRyYWNlIGFuZCBsb2cuDQoNCmBgYHtyfQ0KdHJhY2VfbGVuZ3RoIDwtIGxvZ194ZXMgJT4lIHRyYWNlX2xlbmd0aCgibG9nIikNCnRyYWNlX2xlbmd0aCAlPiUgcGxvdCgpDQpgYGANCg0KIyMjIFByb2Nlc3MgbW9kZWxzDQoNCiMjIyMgRnJlcXVlbmN5IG1hcHMNCg0KQSBwcm9jZXNzIG1hcCBvZiBhIGxvZyBjYW4gYmUgY3JlYXRlZCB1c2luZyBwcm9jZXNzX21hcCgpLiBBIHByb2Nlc3MgbWFwIGlzIGEgZGlyZWN0bHktZm9sbG93cyBncmFwaCwgd2hlcmUgZWFjaCBkaXN0aW5jdCBhY3Rpdml0eSBpcyByZXByZXNlbnRlZCBieSBhIG5vZGUsIGFuZCBlYWNoIGRpcmVjdGx5LWZvbGxvd3MgcmVsYXRpb25zaGlwIGJldHdlZW4gYWN0aXZpdGllcyBpcyBzaG93biBieSBkaXJlY3RlZCBlZGdlcywgaS5lLiBhcnJvd3MgYmV0d2VlbiB0aGUgbm9kZXMuDQoNCmBgYHtyfQ0KZnJlcXVlbmN5IDwtIGxvZ194ZXMgJT4lIGZpbHRlcl90cmFjZV9mcmVxdWVuY3kocGVyY2VudGFnZSA9IDAuOCkNCmBgYA0KDQpgYGB7cn0NCmZyZXF1ZW5jeSAlPiUgcHJvY2Vzc19tYXAoZnJlcXVlbmN5KCJhYnNvbHV0ZSIpKQ0KYGBgDQoNCiMjIyMgUGVyZm9ybWFuY2UgbWFwcw0KDQpJbnN0ZWFkIG9mIGEgZnJlcXVlbmNpZXMsIHByb2Nlc3MgbWFwcyBjYW4gYWxzbyBiZSB1c2VkIHRvIHZpc3VhbGl6ZSBwZXJmb3JtYW5jZSBvZiB0aGUgcHJvY2VzcywgYnkgdXNpbmcgcGVyZm9ybWFuY2UoKSB0byBjb25maWd1cmUgdGhlIG1hcCwgaW5zdGVhZCBvZiBmcmVxdWVuY3koKS4NCg0KYGBge3J9DQpwZXJmb3JtYW5jZSA8LSBsb2dfeGVzICU+JSBwcm9jZXNzX21hcChwZXJmb3JtYW5jZSgpKQ0KcGVyZm9ybWFuY2UNCmBgYA0KDQpJbmZvcm1hdGlvbiBhYm91dCBmcmVxdWVuY2llcyBhbmQgcGVyZm9ybWFuY2UsIG9yIGFueSBvdGhlciB2YWx1ZSwgY2FuIGJlIGNvbWJpbmVkIGluIHRoZSBzYW1lIGdyYXBoLg0KDQpgYGB7cn0NCmZyZXF1ZW5jeV9wZXJmb3JtYW5jZSA8LSBsb2dfeGVzICU+JSBwcm9jZXNzX21hcCh0eXBlX25vZGVzID0gZnJlcXVlbmN5KCJyZWxhdGl2ZV9jYXNlIiksIHR5cGVfZWRnZXMgPSBwZXJmb3JtYW5jZShtZWFuKSkNCmZyZXF1ZW5jeV9wZXJmb3JtYW5jZQ0KYGBgDQoNCiMjIyMgQW5pbWF0aW9uDQoNCkl0IGlzIHBvc3NpYmxlIHRvIGRldGVybWluZSB0aGUgYWVzdGhldGljcyBvZiB0b2tlbnMgcmVnYXJkbGVzcyBvZiB0aGUgdGltZXN0YW1wcyBhdCB3aGljaCBhY3Rpdml0aWVzIG9jY3VycmVkLiBUaGlzIGNvdWxkIGJlIHVzZWZ1bCBpZiBzb21lIG1lYXN1cmVtZW50cyB3ZXJlIHRha2VuIHRocm91Z2hvdXQgYSBwcm9jZXNzLCBidXQgdGhlIG1lYXN1cmVtZW50IGV2ZW50IGl0c2VsZiBzaG91bGQgbm90IGJlIGluY2x1ZGVkIGluIHRoZSBwcm9jZXNzIG1hcC4NCg0KRm9yIGV4YW1wbGUsIHRoZSBsYWN0aWNhY2lkIG1lYXN1cmVtZW50cyBjb3VsZCBiZSB1c2VkIGluIHRoYXQgd2F5Og0KDQpgYGB7cn0NCiMgRXh0cmFjdCBvbmx5IHRoZSBsYWN0aWNhY2lkIG1lYXN1cmVtZW50cw0KbGFjdGljIDwtIGxvZ194ZXMgJT4lDQogICAgbXV0YXRlKExhY3RpY0FjaWQgPSBhcy5udW1lcmljKExhY3RpY0FjaWQpKSAlPiUNCiAgICBmaWx0ZXJfYWN0aXZpdHkoYygiTGFjdGljQWNpZCIpKSAlPiUNCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogICAgc2VsZWN0KCJjYXNlIiA9IENBU0VfY29uY2VwdF9uYW1lLCANCiAgICAgICAgICAgICJ0aW1lIiA9ICB0aW1lc3RhbXAsIA0KICAgICAgICAgICAgdmFsdWUgPSBMYWN0aWNBY2lkKSAjIGZvcm1hdCBuZWVkcyB0byBiZSAnY2FzZSx0aW1lLHZhbHVlJw0KDQojIFJlbW92ZSB0aGUgbWVhc3VyZW1lbnQgZXZlbnRzIGZyb20gdGhlIHNlcHNpcyBsb2cNCnNlcHNpc0Jhc2UgPC0gbG9nX3hlcyAlPiUNCiAgICBmaWx0ZXJfYWN0aXZpdHkoYygiTGFjdGljQWNpZCIsICJDUlAiLCAiTGV1Y29jeXRlcyIsICJSZXR1cm4gRVIiLA0KICAgICAgICAgICAgICAgICAgICAgICJJViBMaXF1aWQiLCAiSVYgQW50aWJpb3RpY3MiKSwgcmV2ZXJzZSA9IFQpICU+JQ0KICAgIGZpbHRlcl90cmFjZV9mcmVxdWVuY3kocGVyY2VudGFnZSA9IDAuOTUpDQoNCiMgQW5pbWF0ZSB3aXRoIHRoZSBzZWNvbmRhcnkgZGF0YSBmcmFtZSBgbGFjdGljYA0KYW5pbWF0ZV9wcm9jZXNzKHNlcHNpc0Jhc2UsIA0KICAgICAgICAgICAgICAgIG1vZGUgPSAicmVsYXRpdmUiLCANCiAgICAgICAgICAgICAgICBkdXJhdGlvbiA9IDMwMCwNCiAgICAgICAgICAgICAgICBsZWdlbmQgPSAiY29sb3IiLCANCiAgICAgICAgICAgICAgICBtYXBwaW5nID0gdG9rZW5fYWVzKGNvbG9yID0gdG9rZW5fc2NhbGUobGFjdGljLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUgPSAibGluZWFyIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygiI2ZmZjVlYiIsIiM3ZjI3MDQiKSkpKSANCmBgYA0KDQojIyMgUHJvY2VzcyB2aXN1YWxpemF0aW9ucw0KDQojIyMjIFByb2Nlc3MgbWF0cml4DQoNCkEgcHJvY2VzcyBtYXRyaXggaXMgYSB0d28tZGltZW5zaW9uYWwgbWF0cml4IHNob3dpbmcgdGhlIGZsb3dzIGJldHdlZW4gYWN0aXZpdGllcy4gSXRzIGNvbmZpZ3VyYXRpb24gaXMgZXhhY3RseSB0aGUgc2FtZSBhcyB0aGF0IHVzZWQgYnkgcHJvY2Vzc19tYXAoKS4NCg0KYGBge3J9DQptYXRyaXhfZnJlcXVlbmN5IDwtIGxvZ194ZXMgJT4lIHByb2Nlc3NfbWF0cml4KGZyZXF1ZW5jeSgiYWJzb2x1dGUiKSkgDQptYXRyaXhfZnJlcXVlbmN5ICU+JSBwbG90KCkNCmBgYA0KDQojIyMjIERvdHRlZCBjaGFydA0KDQpEb3R0ZWQgY2hhcnRzIGNhbiBiZSBtYWRlIHdpdGggZG90dGVkX2NoYXJ0KCkuIEEgZG90dGVkIGNoYXJ0IGlzIGEgZ3JhcGggaW4gd2hpY2ggZWFjaCBhY3Rpdml0eSBpbnN0YW5jZSBpcyBkaXNwbGF5ZWQgYnkgYSBkb3QuIFRoZSB4LWF4aXMgcmVmZXJzIHRvIHRoZSB0aW1lIGFzcGVjdCwgd2hpbGUgdGhlIHktYXhpcyByZWZlcnMgdG8gY2FzZXMuDQoNCmBgYHtyfQ0KZG90dGVkX2NoYXJ0IDwtIGxvZ194ZXMgJT4lIGRvdHRlZF9jaGFydCh4ID0gImFic29sdXRlIikNCmRvdHRlZF9jaGFydA0KYGBgDQoNCiMjIyMgVHJhY2UgZXhwbG9yZXINCg0KRGlmZmVyZW50IGFjdGl2aXR5IHNlcXVlbmNlcyBpbiB0aGUgbG9nIGNhbiBiZSB2aXN1YWxpemVkIHdpdGggdHJhY2VfZXhwbG9yZXIoKS4gSXQgY2FuIGJlIHVzZWQgdG8gZXhwbG9yZSBmcmVxdWVudCBhcyB3ZWxsIGFzIGluZnJlcXVlbnQgdHJhY2VzLg0KDQpgYGB7cn0NCnRyYWNlX2V4cGxvcmVyIDwtIGxvZ194ZXMgJT4lIHRyYWNlX2V4cGxvcmVyKCkNCnRyYWNlX2V4cGxvcmVyDQpgYGANCg0KIyMjIyBQZXJmb3JtYW5jZSBzcGVjdHJ1bQ0KDQpCb3RoIGRldGFpbGVkIGFuZCBhZ2dyZWdhdGVkIHBlcmZvcm1hbmNlIHNwZWN0cnVtIGNhbiBiZSBjcmVhdGVkIHVzaW5nIHBzX2RldGFpbGVkKCkgYW5kIHBzX2FnZ3JlZ2F0ZWQoKSwgcmVzcGVjdGl2ZWx5Lg0KDQpgYGB7cn0NCmxpYnJhcnkocHNtaW5lUikNCg0Kc3BlY3RydW1fZGV0YWlsZWQgPC0gbG9nX3hlcyAlPiUgcHNfZGV0YWlsZWQoKQ0Kc3BlY3RydW1fZGV0YWlsZWQNCmBgYA0KDQojIyBGaWx0ZXJpbmcNCg0KIyMjIEluZnJlcXVlbnQgZmxvd3MNCg0KRmlsdGVyaW5nIGluZnJlcXVlbnQgZmxvd3MgYWxsb3dzIHVzIHRvIHNlbGVjdCBhIHNldCBvZiBjYXNlcyBpbiB3aGljaCBldmVyeSBkaXJlY3RseS1mb2xsb3dzIGZsb3cgaGFzIGEgbWluaW11bSBmcmVxdWVuY3kuIEZvciBleGFtcGxlLCBjb25zaWRlciB0aGUgcHJvY2VzcyBtYXAgYmVsb3cuDQoNCmBgYHtyfQ0KbG9nX3hlcyAlPiUgcHJvY2Vzc19tYXAoKQ0KYGBgDQoNCkluIHRoaXMgbWFwLCB3ZSBjYW4gb2JzZXJ2ZSBzZXZlcmFsIHVuaXF1ZSBkaXJlY3RseSBmb2xsb3dzIHJlbGF0aW9ucywgYXMgd2VsbCBhcyBmbG93cyBvY2N1cnJpbmcgbGVzcyB0aGFuIDMwIHRpbWVzLiBVc2luZyB0aGUgZmlsdGVyLCB3ZSBjYW4gcmVtb3ZlIHRoZSBjYXNlcyB0aGF0IGxlYWQgdG8gdGhlc2UgZmxvd3MgYXMgZm9sbG93czoNCg0KYGBge3J9DQpsb2dfeGVzIDwtIGxvZ194ZXMgJT4lDQogIG11dGF0ZShhY3Rpdml0eV9pbnN0YW5jZV9pZCA9IGFzLmNoYXJhY3RlcihhY3Rpdml0eV9pbnN0YW5jZV9pZCkpDQoNCmluZnJlcXVlbnRfZmxvd3MgPC0gbG9nX3hlcyAlPiUgZmlsdGVyX2luZnJlcXVlbnRfZmxvd3MobWluX24gPSAzMCkgJT4lIHByb2Nlc3NfbWFwKCkNCmluZnJlcXVlbnRfZmxvd3MNCmBgYA0KDQojIyMgVGltZSBwZXJpb2QNCg0KRmlsdGVyaW5nIGNhc2VzIGJ5IHRpbWUgcGVyaW9kIGNhbiBiZSBkb25lIHVzaW5nIHRoZSBmaWx0ZXJfdGltZV9wZXJpb2QoKS4gVGhlcmUgYXJlIGZvdXIgZGlmZmVyZW50IGZpbHRlcl9tZXRob2TigJlzIHRoYXQgYWN0IGFzIGNhc2UgZmlsdGVyczoNCg0KKiDigJxzdGFydOKAnTogYWxsIGNhc2VzIHN0YXJ0ZWQgaW4gYW4gaW50ZXJ2YWwuDQoqIOKAnGNvbXBsZXRl4oCdOiBhbGwgY2FzZXMgY29tcGxldGVkIGluIGFuIGludGVydmFsLg0KKiDigJxjb250YWluZWTigJ06IGFsbCBjYXNlcyBjb250YWluZWQgaW4gYW4gaW50ZXJ2YWwuDQoqIOKAnGludGVyc2VjdGluZ+KAnTogYWxsIGNhc2VzIHdpdGggc29tZSBhY3Rpdml0eSBpbiBhbiBpbnRlcnZhbC4NCg0KVXNpbmcgdGhlIGludGVydmFsIEphbnVhcnkgMjAxNSwgeW91IGNhbiBjb21wYXJlIHRoZSByZXN1bHRzIG9mIGRpZmZlcmVudCBmaWx0ZXJpbmcgbWV0aG9kcyBiZWxvdyB1c2luZyBkb3R0ZWQgY2hhcnRzLg0KDQpgYGB7cn0NCnRpbWVfcGVyaW9kIDwtIGxvZ194ZXMgJT4lIGZpbHRlcl90aW1lX3BlcmlvZChpbnRlcnZhbCA9IHltZChjKDIwMTUwMTAxLCAyMDE1MDEzMSkpLCBmaWx0ZXJfbWV0aG9kID0gInN0YXJ0IikgJT4lIGRvdHRlZF9jaGFydCgpIA0KdGltZV9wZXJpb2QNCmBgYA0KDQojIyMgQ2FzZSBjb25kaXRpb24NCg0KZmlsdGVyX2Nhc2VfY29uZGl0aW9uKCkgY2FuIGJlIHVzZWQgdG8gc2VsZWN0IGNhc2VzIGZvciB3aGljaCBhIGNvbmRpdGlvbiBob2xkcy4gVGhpcyBjb25kaXRpb24gY2FuIGJlIHJlbGF0ZWQgdG8gYW55IG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGxvZy4NCg0KRm9yIGV4YW1wbGUsIHNlbGVjdCBhbGwgY2FzZXMgd2hlcmUgYWdlIGhpZ2hlciB0aGFuIDg1IGlzIGludm9sdmVkLg0KDQpgYGB7cn0NCmFnZV84NSA8LSBsb2dfeGVzICU+JSBmaWx0ZXIoIWlzLm5hKEFnZSkpICU+JSBmaWx0ZXJfY2FzZV9jb25kaXRpb24oQWdlID49IDg1KQ0KYWdlXzg1DQpwcm9jZXNzX21hcChhZ2VfODUpDQpgYGANCg0KIyMjIFByZWNlZGVuY2UNCg0KVGhlIGZpbHRlcl9wcmVjZWRlbmNlKCkgYWxsb3dzIHVzIHRvIGZpbHRlciBjYXNlcyBiYXNlZCBvbiBmbG93cyBiZXR3ZWVuIGFjdGl2aXRpZXMuDQoNCklmIHRoZXJlIGlzIG1vcmUgdGhhbiBvbmUgYW50ZWNlZGVudCBvciBjb25zZXF1ZW50IGFjdGl2aXR5LCB0aGUgZmlsdGVyIHdpbGwgdGVzdCBhbGwgcG9zc2libGUgcGFpcnMuIFRoZSBmaWx0ZXJfbWV0aG9kIHdpbGwgdGVsbCB0aGUgZmlsdGVyIHdoZXRoZXIgYWxsIG9mIHRoZSBydWxlcyBzaG91bGQgaG9sZCwgYXQgbGVhc3Qgb25lLCBvciBub25lIGFyZSBhbGxvd2VkLg0KDQpUaGUgZm9sbG93aW5nIGZpbHRlciB0YWtlcyBvbmx5IGNhc2VzIHdoZXJlIFRyaWFnZSBhbmQgQXNzZXNzbWVudCBpcyBkaXJlY3RseSBmb2xsb3dlZCBieSBCbG9vZCB0ZXN0Lg0KDQpgYGB7cn0NCnByZWNlZGVuY2UgPC0gbG9nX3hlcyAlPiUNCiAgICBmaWx0ZXJfcHJlY2VkZW5jZShhbnRlY2VkZW50cyA9ICJFUiBUcmlhZ2UiLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbnNlcXVlbnRzID0gIkxldWNvY3l0ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgIHByZWNlZGVuY2VfdHlwZSA9ICJkaXJlY3RseV9mb2xsb3dzIikgJT4lDQogICAgdHJhY2VzKCkNCmhlYWQocHJlY2VkZW5jZSkNCmBgYA0KDQojIyBDb25mb3JtYW5jZSBjaGVja2luZw0KDQojIyMgUnVsZS1iYXNlZCBjb25mb3JtYW5jZQ0KDQpVc2luZyB0aGUgcGFja2FnZXMgcHJvY2Vzc2NoZWNrciBwcm9kZWN1cmFsIHJ1bGVzIGNhbiBiZSBjaGVja2VkIGluIGFuIGV2ZW50IGxvZy4gQ2hlY2tpbmcgcnVsZXMgd2lsbCBhZGQgYSBib29sZWFuIGNhc2UgYXR0cmlidXRlLCB3aGljaCBjYW4gYmUgdXNlZCBmb3IgZmlsdGVyaW5nIG9yIGluIGFuYWx5c2lzLg0KDQpSdWxlcyBjYW4gYmUgY2hlY2tlZCB1c2luZyB0aGUgY2hlY2tfcnVsZSBmdW5jdGlvbiAoc2VlIGV4YW1wbGUgYmVsb3cpLiBJdCB3aWxsIGNyZWF0ZSBhIG5ldyBsb2dpY2FsIHZhcmlhYmxlIHRvIGluZGljYXRlIGZvciB3aGljaCBjYXNlcyB0aGUgcnVsZSBob2xkcy4gVGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIGNhbiBiZSBjb25maWd1cmVkIHVzaW5nIHRoZSBsYWJlbCBhcmd1bWVudCBpbiBjaGVja19ydWxlLg0KDQpJbiB0aGUgZm9sbG93aW5nIGV4YW1wbGUsIHRoZSBmaXJzdCBydWxlIGNoZWNrcyB0aGUgc3RhcnRpbmcgYWN0aXZpdHksIHdoaWxlIHRoZSBzZWNvbmQgcnVsZSBjaGVja3Mgd2hldGhlciBDUlAgYW5kIExhY3RpY0FjaWQgb2NjdXIgdG9nZXRoZXIuDQoNCmBgYHtyfQ0KbG9nX3hlcyAlPiUNCiAgIyBjaGVjayBpZiBjYXNlcyBzdGFydHMgd2l0aCAiRVIgUmVnaXN0cmF0aW9uIg0KICBjaGVja19ydWxlKHN0YXJ0cygiRVIgUmVnaXN0cmF0aW9uIiksIGxhYmVsID0gInIxIikgJT4lDQogICMgY2hlY2sgaWYgYWN0aXZpdGllcyAiQ1JQIiBhbmQgIkxhY3RpY0FjaWQiIG9jY3VyIHRvZ2V0aGVyDQogIGNoZWNrX3J1bGUoYW5kKCJDUlAiLCJMYWN0aWNBY2lkIiksIGxhYmVsID0gInIyIikgJT4lDQogIGdyb3VwX2J5KHIxLCByMikgJT4lDQogIG5fY2FzZXMoKQ0KYGBgDQoNCiMjIyBBbGlnbm1lbnRzDQoNCkFsaWdubWVudHMgaXMgdW5kZXIgZGV2ZWxvcG1lbnQgYW5kIGNhbiBiZSB1c2VkIHdpdGggdGhlIGJ1cGFSbWluZXIgbGlicmFyeS4gTW9yZSBpbmZvcm1hdGlvbiBjYW4gYmUgZm91bmQgaW4gdGhlIEdpdEh1YjogaHR0cHM6Ly9naXRodWIuY29tL2J1cGF2ZXJzZS9idXBhUm1pbmVyLg0KDQoNCg0KDQo=