Below, we analyze the long term behavior of system under three
policies using a stochastic model to make better decisions on what
policy to employ.
First let us begin by loading relevant libraries:
library(expm)
Warning messages:
1: In formals(fun) : argument is not a function
2: In formals(fun) : argument is not a function
3: In formals(fun) : argument is not a function
library(reshape2)
In this example, we consider a situation where we have a viral
infection spreading through a community. We have three possible
policies:
-
Policy 0: Baseline case, we do nothing
-
Policy I: Education + early screening
-
Policy II: Late stage treatment focus
We have three different transition matrices corresponding to the
probability of transitioning between states in the three different
situations.
Warning message:
In formals(fun) : argument is not a function
P.control <- matrix(c(0.85, 0.1, 0.05, 0, 0,
0.2, 0.5, 0.2, 0.1, 0,
0, 0.1, 0.6, 0.2, 0.1,
0, 0.05, 0.15, 0.7, 0.1,
0, 0, 0.1, 0.3, 0.6), nrow=5, byrow=TRUE)
P.policy.i <- matrix(c(0.9, 0.08, 0.02, 0, 0,
0.15, 0.6, 0.15, 0.1, 0,
0, 0.05, 0.65, 0.25, 0.05,
0, 0.02, 0.1, 0.8, 0.08,
0, 0, 0.05, 0.2, 0.75), nrow=5, byrow=TRUE)
P.policy.ii <- matrix(c(0.85, 0.1, 0.05, 0, 0,
0.2, 0.5, 0.2, 0.1, 0,
0, 0.05, 0.6, 0.25, 0.1,
0, 0.02, 0.1, 0.75, 0.13,
0, 0, 0.1, 0.25, 0.65), nrow=5, byrow=TRUE)
We can track the evolution of the system under each policy by
multiplying the transition matrix raised to the power of the iteration
by the initial distribution (\(\pi_0\)):
\(\pi_i = \pi_0 P^i\)
The steady state will be reached at \(\pi_\infty\) when \(\pi_\infty P = \pi_\infty\). In this
example, we simply calculate the distribution of the system after
sufficiently enough time has passed.
To find the steady state probability we have to find:
\(\pi_\infty(P-I)=0\)
Below we define the initial population:
Initial.pop <- c(300000, 100000, 50000, 30000, 20000)
Define parameters for solution:
num.itr <- 100
Baseline
control <- matrix(nrow=num.itr, ncol=5)
for(i in 1:num.itr){
control[i,] <- Initial.pop %*% (P.control %^% i)
}
control_df <- as.data.frame(control)
colnames(control_df) <- c('Healthy', 'At-risk', 'Infected', 'Recovered', 'Chronic Condition')
control_df$Iteration <- 1:num.itr
control_long <- melt(control_df, id.vars = "Iteration", variable.name = "State", value.name = "Population")
ggplot(control_long, aes(x = Iteration, y = Population, color = State, group = State)) +
geom_line(size = 1.2) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Distribution of population - baseline",
x = "Iteration",
y = "Population",
color = "State") +
theme(text = element_text(size=14))+
scale_color_manual(values = c('Healthy'='#27445D', 'At-risk'='#780C28', 'Infected'='#71BBB2', 'Recovered'='#3F4F44', 'Chronic Condition'='#ADB2D4'))

NA
NA
n <- nrow(P.control)
A <- t(P.control - diag(n))
A <- rbind(A, rep(1, n))
b <- c(rep(0, n), 1)
steady_state <- ginv(A) %*% b
steady_state <- steady_state / sum(steady_state)
print(steady_state*500000)
[,1]
[1,] 75520.83
[2,] 56640.62
[3,] 121093.75
[4,] 173177.08
[5,] 73567.71
Great, now let us plot the average cost for the population over
time.
ggplot()+geom_line(aes(x = 1:length(control%*%c(0, 200, 8000, 500, 25000)), y = control%*%c(0, 200, 8000, 500, 25000)), color = '#ADB2D4', size=1.5) +
geom_line(size = 1.2) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Cost over time",
x = "Iteration",
y = "Cost") +
theme(text = element_text(size=14))

Okay, let’s see how our system behaves under the two other
policies.
Policy II
policy.i <- matrix(nrow=num.itr, ncol=5)
for(i in 1:num.itr){
policy.i[i,] <- Initial.pop %*% (P.policy.i %^% i)
}
policy.i_df <- as.data.frame(policy.i)
colnames(policy.i_df) <- c('Healthy', 'At-risk', 'Infected', 'Recovered', 'Chronic Condition')
policy.i_df$Iteration <- 1:num.itr
policy.i_long <- melt(policy.i_df, id.vars = "Iteration", variable.name = "State", value.name = "Population")
ggplot(policy.i_long, aes(x = Iteration, y = Population, color = State, group = State)) +
geom_line(size = 1.2) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Distribution of population - policy i",
x = "Iteration",
y = "Population",
color = "State") +
theme(text = element_text(size=14))+
scale_color_manual(values = c('Healthy'='#27445D', 'At-risk'='#780C28', 'Infected'='#71BBB2', 'Recovered'='#3F4F44', 'Chronic Condition'='#ADB2D4'))

NA
NA
Let’s evaluate steady states
n <- nrow(P.policy.i)
A <- t(P.policy.i - diag(n))
A <- rbind(A, rep(1, n))
b <- c(rep(0, n), 1)
steady_state <- ginv(A) %*% b
steady_state <- steady_state / sum(steady_state)
print(steady_state*500000)
[,1]
[1,] 50111.36
[2,] 33407.57
[3,] 95662.32
[4,] 228550.22
[5,] 92268.53
Average cost over time for policy i:
ggplot()+geom_line(aes(x = 1:length(policy.i%*%c(0, 200, 8000, 500, 25000)), y = policy.i%*%c(0, 200, 8000, 500, 25000)), color = '#ADB2D4', size=1.5) +
geom_line(size = 1.2) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Cost over time",
x = "Iteration",
y = "Cost") +
theme(text = element_text(size=14))

Policy ii
policy.ii <- matrix(nrow=num.itr, ncol=5)
for(i in 1:num.itr){
policy.ii[i,] <- Initial.pop %*% (P.policy.ii %^% i)
}
policy.ii_df <- as.data.frame(policy.ii)
colnames(policy.ii_df) <- c('Healthy', 'At-risk', 'Infected', 'Recovered', 'Chronic Condition')
policy.ii_df$Iteration <- 1:num.itr
policy.ii_long <- melt(policy.ii_df, id.vars = "Iteration", variable.name = "State", value.name = "Population")
ggplot(policy.ii_long, aes(x = Iteration, y = Population, color = State, group = State)) +
geom_line(size = 1.2) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Distribution of population - policy ii",
x = "Iteration",
y = "Population",
color = "State") +
theme(text = element_text(size=14))+
scale_color_manual(values = c('Healthy'='#27445D', 'At-risk'='#780C28', 'Infected'='#71BBB2', 'Recovered'='#3F4F44', 'Chronic Condition'='#ADB2D4'))

NA
NA
Steady states:
n <- nrow(P.policy.ii)
A <- t(P.policy.ii - diag(n))
A <- rbind(A, rep(1, n))
b <- c(rep(0, n), 1)
steady_state <- ginv(A) %*% b
steady_state <- steady_state / sum(steady_state)
print(steady_state*500000)
[,1]
[1,] 34843.21
[2,] 26132.40
[3,] 101742.16
[4,] 224738.68
[5,] 112543.55
Average cost over time for policy ii:
ggplot()+geom_line(aes(x = 1:length(policy.i%*%c(0, 200, 8000, 500, 25000)), y = policy.i%*%c(0, 200, 8000, 500, 25000)), color = '#ADB2D4', size=1.5) +
geom_line(size = 1.2) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Cost over time",
x = "Iteration",
y = "Cost") +
theme(text = element_text(size=14))

As demonstrated all three Markov chains are ergodic and reach a
steady state distribution after some time.
Comparison of costs under different policies
df <- data.frame(
Iteration = 1:length(control %*% c(0, 200, 8000, 500, 25000)),
Control = as.vector(control %*% c(0, 200, 8000, 500, 25000)),
Policy_I = as.vector(policy.i %*% c(0, 200, 8000, 500, 25000)),
Policy_II = as.vector(policy.ii %*% c(0, 200, 8000, 500, 25000))
)
# Convert data to long format for ggplot
library(reshape2)
df_long <- melt(df, id.vars = "Iteration", variable.name = "Policy", value.name = "Cost")
# Plot with legend
ggplot(df_long, aes(x = Iteration, y = Cost, color = Policy, group = Policy)) +
geom_line(size=1.5) +
geom_point(size = 2) +
theme_minimal() +
labs(title = "Cost over Time",
x = "Iteration",
y = "Cost",
color = "Policy") + # Legend label
scale_color_manual(values = c("Control" = "#ADB2D4", "Policy_I" = "darkred", "Policy_II" = "#27445D")) +
theme(text = element_text(size=14))

It seems a little bit counter intuitive and strange to see that long
term it appears that the control case with no intervention is less
expensive compared to the other two policies long-term. However, upon
further inspection we realize that having patients with chronic
condition is the most costly. Interestingly, under the two policies the
probability of staying in the chronic condition state starting from the
chronic condition state is higher than the control case. The chronic
condition cases appear to be much lower under the control condition
compared to the other two policies. However, the number of infected
individuals remain much higher in the control case compared to the two
policies. There is a trade-off to this and that is the fact that the
number of at-risk people is significantly higher in the control case
compared to the other conditions involving intervention.
Mean First Passage Time (MFPT)
find.MFPT <- function(P) {
n <- nrow(P)
MFPT <- matrix(0, n, n)
for (i in 1:n) {
for (j in 1:n) {
if (i != j) {
A <- diag(n) - P
A[j, ] <- 0
A[j, j] <- 1
b <- rep(1, n)
b[j] <- 0
m <- solve(A, b)
MFPT[i, j] <- m[i]
}
}
}
return(MFPT)
}
Let’s find the overall accessibility of each state using the MFPT
matrix and compare the different conditions:
print(paste0("Control: ", colSums(find.MFPT(P.control))))
[1] "Control: 184.137931034483" "Control: 69.6551724137931" "Control: 36.1290322580645"
[4] "Control: 37.2180451127819" "Control: 76.1946902654867"
print(paste0("Policy I: ", colSums(find.MFPT(P.policy.i))))
[1] "Policy I: 464.444444444447" "Policy I: 158.666666666667"
[3] "Policy I: 58.4811529933481" "Policy I: 43.7470997679815"
[5] "Policy I: 99.6206896551725"
print(paste0("Policy II: ", colSums(find.MFPT(P.policy.ii))))
[1] "Policy II: 439.000000000002" "Policy II: 164.666666666667"
[3] "Policy II: 41.5582191780822" "Policy II: 33.6821705426357"
[5] "Policy II: 58.0185758513932"
It seems like based on this analysis, the healthy state is much
easier to reach in the control case than the two other conditions.
LS0tCnRpdGxlOiAiU3RlYWR5IFN0YXRlcyBpbiBNYXJrb3YgQ2hhaW5zIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpCZWxvdywgd2UgYW5hbHl6ZSB0aGUgbG9uZyB0ZXJtIGJlaGF2aW9yIG9mIHN5c3RlbSB1bmRlciB0aHJlZSBwb2xpY2llcyB1c2luZyBhIHN0b2NoYXN0aWMgbW9kZWwgdG8gbWFrZSBiZXR0ZXIgZGVjaXNpb25zIG9uIHdoYXQgcG9saWN5IHRvIGVtcGxveS4KCkZpcnN0IGxldCB1cyBiZWdpbiBieSBsb2FkaW5nIHJlbGV2YW50IGxpYnJhcmllczoKCmBgYHtyfQpsaWJyYXJ5KGV4cG0pCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoTUFTUykKYGBgCgpJbiB0aGlzIGV4YW1wbGUsIHdlIGNvbnNpZGVyIGEgc2l0dWF0aW9uIHdoZXJlIHdlIGhhdmUgYSB2aXJhbCBpbmZlY3Rpb24gc3ByZWFkaW5nIHRocm91Z2ggYSBjb21tdW5pdHkuIFdlIGhhdmUgdGhyZWUgcG9zc2libGUgcG9saWNpZXM6Cgo8dWw+CjxsaT5Qb2xpY3kgMDogQmFzZWxpbmUgY2FzZSwgd2UgZG8gbm90aGluZzwvbGk+CjxsaT5Qb2xpY3kgSTogRWR1Y2F0aW9uICsgZWFybHkgc2NyZWVuaW5nPC9saT4KPGxpPlBvbGljeSBJSTogTGF0ZSBzdGFnZSB0cmVhdG1lbnQgZm9jdXM8L2xpPgo8L3VsPgoKCldlIGhhdmUgdGhyZWUgZGlmZmVyZW50IHRyYW5zaXRpb24gbWF0cmljZXMgY29ycmVzcG9uZGluZyB0byB0aGUgcHJvYmFiaWxpdHkgb2YgdHJhbnNpdGlvbmluZyBiZXR3ZWVuIHN0YXRlcyBpbiB0aGUgdGhyZWUgZGlmZmVyZW50IHNpdHVhdGlvbnMuCgpgYGB7cn0KClAuY29udHJvbCA8LSBtYXRyaXgoYygwLjg1LAkwLjEsCTAuMDUsCTAsCTAsCjAuMiwJMC41LAkwLjIsCTAuMSwJMCwKMCwJMC4xLAkwLjYsCTAuMiwJMC4xLAowLAkwLjA1LAkwLjE1LAkwLjcsCTAuMSwKMCwJMCwJMC4xLAkwLjMsCTAuNiksIG5yb3c9NSwgYnlyb3c9VFJVRSkKCgpQLnBvbGljeS5pIDwtIG1hdHJpeChjKDAuOSwJMC4wOCwJMC4wMiwJMCwJMCwKMC4xNSwJMC42LAkwLjE1LAkwLjEsCTAsCjAsCTAuMDUsCTAuNjUsCTAuMjUsCTAuMDUsCjAsCTAuMDIsCTAuMSwJMC44LAkwLjA4LAowLAkwLAkwLjA1LAkwLjIsCTAuNzUpLCBucm93PTUsIGJ5cm93PVRSVUUpCgpQLnBvbGljeS5paSA8LSBtYXRyaXgoYygwLjg1LAkwLjEsCTAuMDUsCTAsCTAsCjAuMiwJMC41LAkwLjIsCTAuMSwJMCwKMCwJMC4wNSwJMC42LAkwLjI1LAkwLjEsCjAsCTAuMDIsCTAuMSwJMC43NSwJMC4xMywKMCwJMCwJMC4xLAkwLjI1LAkwLjY1KSwgbnJvdz01LCBieXJvdz1UUlVFKQoKYGBgCgoKV2UgY2FuIHRyYWNrIHRoZSBldm9sdXRpb24gb2YgdGhlIHN5c3RlbSB1bmRlciBlYWNoIHBvbGljeSBieSBtdWx0aXBseWluZyB0aGUgdHJhbnNpdGlvbiBtYXRyaXggcmFpc2VkIHRvIHRoZSBwb3dlciBvZiB0aGUgaXRlcmF0aW9uIGJ5IHRoZSBpbml0aWFsIGRpc3RyaWJ1dGlvbiAoJFxwaV8wJCk6CgokXHBpX2kgPSBccGlfMCBQXmkkIAoKVGhlIHN0ZWFkeSBzdGF0ZSB3aWxsIGJlIHJlYWNoZWQgYXQgJFxwaV9caW5mdHkkIHdoZW4gJFxwaV9caW5mdHkgUCA9IFxwaV9caW5mdHkkLiBJbiB0aGlzIGV4YW1wbGUsIHdlIHNpbXBseSBjYWxjdWxhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc3lzdGVtIGFmdGVyIHN1ZmZpY2llbnRseSBlbm91Z2ggdGltZSBoYXMgcGFzc2VkLiAKClRvIGZpbmQgdGhlIHN0ZWFkeSBzdGF0ZSBwcm9iYWJpbGl0eSB3ZSBoYXZlIHRvIGZpbmQ6CgokXHBpX1xpbmZ0eShQLUkpPTAkCgpCZWxvdyB3ZSBkZWZpbmUgdGhlIGluaXRpYWwgcG9wdWxhdGlvbjoKCmBgYHtyfQpJbml0aWFsLnBvcCA8LSBjKDMwMDAwMCwgMTAwMDAwLCA1MDAwMCwgMzAwMDAsIDIwMDAwKQoKYGBgCgpEZWZpbmUgcGFyYW1ldGVycyBmb3Igc29sdXRpb246CgpgYGB7cn0KbnVtLml0ciA8LSAxMDAKCmBgYAoKPGg0PkJhc2VsaW5lPC9oND4KCmBgYHtyfQpjb250cm9sIDwtIG1hdHJpeChucm93PW51bS5pdHIsIG5jb2w9NSkKCmZvcihpIGluIDE6bnVtLml0cil7CiAgY29udHJvbFtpLF0gPC0gSW5pdGlhbC5wb3AgJSolIChQLmNvbnRyb2wgJV4lIGkpCn0KCgpjb250cm9sX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udHJvbCkKY29sbmFtZXMoY29udHJvbF9kZikgPC0gYygnSGVhbHRoeScsICdBdC1yaXNrJywgJ0luZmVjdGVkJywgJ1JlY292ZXJlZCcsICdDaHJvbmljIENvbmRpdGlvbicpCmNvbnRyb2xfZGYkSXRlcmF0aW9uIDwtIDE6bnVtLml0cgoKCmNvbnRyb2xfbG9uZyA8LSBtZWx0KGNvbnRyb2xfZGYsIGlkLnZhcnMgPSAiSXRlcmF0aW9uIiwgdmFyaWFibGUubmFtZSA9ICJTdGF0ZSIsIHZhbHVlLm5hbWUgPSAiUG9wdWxhdGlvbiIpCgpgYGAKCgoKYGBge3J9CmdncGxvdChjb250cm9sX2xvbmcsIGFlcyh4ID0gSXRlcmF0aW9uLCB5ID0gUG9wdWxhdGlvbiwgY29sb3IgPSBTdGF0ZSwgZ3JvdXAgPSBTdGF0ZSkpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHBvcHVsYXRpb24gLSBiYXNlbGluZSIsCiAgICAgICB4ID0gIkl0ZXJhdGlvbiIsCiAgICAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgICAgY29sb3IgPSAiU3RhdGUiKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ0hlYWx0aHknPScjMjc0NDVEJywgJ0F0LXJpc2snPScjNzgwQzI4JywgJ0luZmVjdGVkJz0nIzcxQkJCMicsICdSZWNvdmVyZWQnPScjM0Y0RjQ0JywgJ0Nocm9uaWMgQ29uZGl0aW9uJz0nI0FEQjJENCcpKQoKCmBgYAoKYGBge3J9Cm4gPC0gbnJvdyhQLmNvbnRyb2wpCkEgPC0gdChQLmNvbnRyb2wgLSBkaWFnKG4pKSAKQSA8LSByYmluZChBLCByZXAoMSwgbikpICAKYiA8LSBjKHJlcCgwLCBuKSwgMSkgIAoKCnN0ZWFkeV9zdGF0ZSA8LSBnaW52KEEpICUqJSBiIApzdGVhZHlfc3RhdGUgPC0gc3RlYWR5X3N0YXRlIC8gc3VtKHN0ZWFkeV9zdGF0ZSkgIAoKCnByaW50KHN0ZWFkeV9zdGF0ZSo1MDAwMDApCmBgYAoKR3JlYXQsIG5vdyBsZXQgdXMgcGxvdCB0aGUgYXZlcmFnZSBjb3N0IGZvciB0aGUgcG9wdWxhdGlvbiBvdmVyIHRpbWUuCgpgYGB7cn0KZ2dwbG90KCkrZ2VvbV9saW5lKGFlcyh4ID0gMTpsZW5ndGgoY29udHJvbCUqJWMoMCwgMjAwLCA4MDAwLCA1MDAsIDI1MDAwKSksIHkgPSBjb250cm9sJSolYygwLCAyMDAsIDgwMDAsIDUwMCwgMjUwMDApKSwgY29sb3IgPSAnI0FEQjJENCcsIHNpemU9MS41KSArCiAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkNvc3Qgb3ZlciB0aW1lIiwKICAgICAgIHggPSAiSXRlcmF0aW9uIiwKICAgICAgIHkgPSAiQ29zdCIpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpKQoKYGBgCgoKCgpPa2F5LCBsZXQncyBzZWUgaG93IG91ciBzeXN0ZW0gYmVoYXZlcyB1bmRlciB0aGUgdHdvIG90aGVyIHBvbGljaWVzLiAKCjxoND5Qb2xpY3kgSUk8L2g0PgoKCgpgYGB7cn0KcG9saWN5LmkgPC0gbWF0cml4KG5yb3c9bnVtLml0ciwgbmNvbD01KQoKZm9yKGkgaW4gMTpudW0uaXRyKXsKICBwb2xpY3kuaVtpLF0gPC0gSW5pdGlhbC5wb3AgJSolIChQLnBvbGljeS5pICVeJSBpKQp9CgoKcG9saWN5LmlfZGYgPC0gYXMuZGF0YS5mcmFtZShwb2xpY3kuaSkKY29sbmFtZXMocG9saWN5LmlfZGYpIDwtIGMoJ0hlYWx0aHknLCAnQXQtcmlzaycsICdJbmZlY3RlZCcsICdSZWNvdmVyZWQnLCAnQ2hyb25pYyBDb25kaXRpb24nKQpwb2xpY3kuaV9kZiRJdGVyYXRpb24gPC0gMTpudW0uaXRyCgoKcG9saWN5LmlfbG9uZyA8LSBtZWx0KHBvbGljeS5pX2RmLCBpZC52YXJzID0gIkl0ZXJhdGlvbiIsIHZhcmlhYmxlLm5hbWUgPSAiU3RhdGUiLCB2YWx1ZS5uYW1lID0gIlBvcHVsYXRpb24iKQoKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QocG9saWN5LmlfbG9uZywgYWVzKHggPSBJdGVyYXRpb24sIHkgPSBQb3B1bGF0aW9uLCBjb2xvciA9IFN0YXRlLCBncm91cCA9IFN0YXRlKSkgKwogIGdlb21fbGluZShzaXplID0gMS4yKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgcG9wdWxhdGlvbiAtIHBvbGljeSBpIiwKICAgICAgIHggPSAiSXRlcmF0aW9uIiwKICAgICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICAgICBjb2xvciA9ICJTdGF0ZSIpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnSGVhbHRoeSc9JyMyNzQ0NUQnLCAnQXQtcmlzayc9JyM3ODBDMjgnLCAnSW5mZWN0ZWQnPScjNzFCQkIyJywgJ1JlY292ZXJlZCc9JyMzRjRGNDQnLCAnQ2hyb25pYyBDb25kaXRpb24nPScjQURCMkQ0JykpCgoKYGBgCgoKTGV0J3MgZXZhbHVhdGUgc3RlYWR5IHN0YXRlcwoKYGBge3J9CgpuIDwtIG5yb3coUC5wb2xpY3kuaSkKQSA8LSB0KFAucG9saWN5LmkgLSBkaWFnKG4pKSAKQSA8LSByYmluZChBLCByZXAoMSwgbikpICAKYiA8LSBjKHJlcCgwLCBuKSwgMSkgIAoKCnN0ZWFkeV9zdGF0ZSA8LSBnaW52KEEpICUqJSBiIApzdGVhZHlfc3RhdGUgPC0gc3RlYWR5X3N0YXRlIC8gc3VtKHN0ZWFkeV9zdGF0ZSkgIAoKCnByaW50KHN0ZWFkeV9zdGF0ZSo1MDAwMDApCmBgYAoKQXZlcmFnZSBjb3N0IG92ZXIgdGltZSBmb3IgcG9saWN5IGk6CgpgYGB7cn0KZ2dwbG90KCkrZ2VvbV9saW5lKGFlcyh4ID0gMTpsZW5ndGgocG9saWN5LmklKiVjKDAsIDIwMCwgODAwMCwgNTAwLCAyNTAwMCkpLCB5ID0gcG9saWN5LmklKiVjKDAsIDIwMCwgODAwMCwgNTAwLCAyNTAwMCkpLCBjb2xvciA9ICcjQURCMkQ0Jywgc2l6ZT0xLjUpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiQ29zdCBvdmVyIHRpbWUiLAogICAgICAgeCA9ICJJdGVyYXRpb24iLAogICAgICAgeSA9ICJDb3N0IikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkpCgpgYGAKCjxoND5Qb2xpY3kgaWk8L2g0PgoKCgpgYGB7cn0KcG9saWN5LmlpIDwtIG1hdHJpeChucm93PW51bS5pdHIsIG5jb2w9NSkKCmZvcihpIGluIDE6bnVtLml0cil7CiAgcG9saWN5LmlpW2ksXSA8LSBJbml0aWFsLnBvcCAlKiUgKFAucG9saWN5LmlpICVeJSBpKQp9CgoKcG9saWN5LmlpX2RmIDwtIGFzLmRhdGEuZnJhbWUocG9saWN5LmlpKQpjb2xuYW1lcyhwb2xpY3kuaWlfZGYpIDwtIGMoJ0hlYWx0aHknLCAnQXQtcmlzaycsICdJbmZlY3RlZCcsICdSZWNvdmVyZWQnLCAnQ2hyb25pYyBDb25kaXRpb24nKQpwb2xpY3kuaWlfZGYkSXRlcmF0aW9uIDwtIDE6bnVtLml0cgoKCnBvbGljeS5paV9sb25nIDwtIG1lbHQocG9saWN5LmlpX2RmLCBpZC52YXJzID0gIkl0ZXJhdGlvbiIsIHZhcmlhYmxlLm5hbWUgPSAiU3RhdGUiLCB2YWx1ZS5uYW1lID0gIlBvcHVsYXRpb24iKQoKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QocG9saWN5LmlpX2xvbmcsIGFlcyh4ID0gSXRlcmF0aW9uLCB5ID0gUG9wdWxhdGlvbiwgY29sb3IgPSBTdGF0ZSwgZ3JvdXAgPSBTdGF0ZSkpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHBvcHVsYXRpb24gLSBwb2xpY3kgaWkiLAogICAgICAgeCA9ICJJdGVyYXRpb24iLAogICAgICAgeSA9ICJQb3B1bGF0aW9uIiwKICAgICAgIGNvbG9yID0gIlN0YXRlIikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdIZWFsdGh5Jz0nIzI3NDQ1RCcsICdBdC1yaXNrJz0nIzc4MEMyOCcsICdJbmZlY3RlZCc9JyM3MUJCQjInLCAnUmVjb3ZlcmVkJz0nIzNGNEY0NCcsICdDaHJvbmljIENvbmRpdGlvbic9JyNBREIyRDQnKSkKCgpgYGAKCgpTdGVhZHkgc3RhdGVzOgoKYGBge3J9CgpuIDwtIG5yb3coUC5wb2xpY3kuaWkpCkEgPC0gdChQLnBvbGljeS5paSAtIGRpYWcobikpIApBIDwtIHJiaW5kKEEsIHJlcCgxLCBuKSkgIApiIDwtIGMocmVwKDAsIG4pLCAxKSAgCgoKc3RlYWR5X3N0YXRlIDwtIGdpbnYoQSkgJSolIGIgCnN0ZWFkeV9zdGF0ZSA8LSBzdGVhZHlfc3RhdGUgLyBzdW0oc3RlYWR5X3N0YXRlKSAgCgoKcHJpbnQoc3RlYWR5X3N0YXRlKjUwMDAwMCkKYGBgCgpBdmVyYWdlIGNvc3Qgb3ZlciB0aW1lIGZvciBwb2xpY3kgaWk6CgpgYGB7cn0KZ2dwbG90KCkrZ2VvbV9saW5lKGFlcyh4ID0gMTpsZW5ndGgocG9saWN5LmklKiVjKDAsIDIwMCwgODAwMCwgNTAwLCAyNTAwMCkpLCB5ID0gcG9saWN5LmklKiVjKDAsIDIwMCwgODAwMCwgNTAwLCAyNTAwMCkpLCBjb2xvciA9ICcjQURCMkQ0Jywgc2l6ZT0xLjUpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiQ29zdCBvdmVyIHRpbWUiLAogICAgICAgeCA9ICJJdGVyYXRpb24iLAogICAgICAgeSA9ICJDb3N0IikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkpCgpgYGAKCkFzIGRlbW9uc3RyYXRlZCBhbGwgdGhyZWUgTWFya292IGNoYWlucyBhcmUgZXJnb2RpYyBhbmQgcmVhY2ggYSBzdGVhZHkgc3RhdGUgZGlzdHJpYnV0aW9uIGFmdGVyIHNvbWUgdGltZS4gCgoKPGg0PkNvbXBhcmlzb24gb2YgY29zdHMgdW5kZXIgZGlmZmVyZW50IHBvbGljaWVzPC9oND4KCgoKCmBgYHtyfQoKZGYgPC0gZGF0YS5mcmFtZSgKICBJdGVyYXRpb24gPSAxOmxlbmd0aChjb250cm9sICUqJSBjKDAsIDIwMCwgODAwMCwgNTAwLCAyNTAwMCkpLAogIENvbnRyb2wgPSBhcy52ZWN0b3IoY29udHJvbCAlKiUgYygwLCAyMDAsIDgwMDAsIDUwMCwgMjUwMDApKSwKICBQb2xpY3lfSSA9IGFzLnZlY3Rvcihwb2xpY3kuaSAlKiUgYygwLCAyMDAsIDgwMDAsIDUwMCwgMjUwMDApKSwKICBQb2xpY3lfSUkgPSBhcy52ZWN0b3IocG9saWN5LmlpICUqJSBjKDAsIDIwMCwgODAwMCwgNTAwLCAyNTAwMCkpCikKCgpsaWJyYXJ5KHJlc2hhcGUyKQpkZl9sb25nIDwtIG1lbHQoZGYsIGlkLnZhcnMgPSAiSXRlcmF0aW9uIiwgdmFyaWFibGUubmFtZSA9ICJQb2xpY3kiLCB2YWx1ZS5uYW1lID0gIkNvc3QiKQoKCmdncGxvdChkZl9sb25nLCBhZXMoeCA9IEl0ZXJhdGlvbiwgeSA9IENvc3QsIGNvbG9yID0gUG9saWN5LCBncm91cCA9IFBvbGljeSkpICsKICBnZW9tX2xpbmUoc2l6ZT0xLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkNvc3Qgb3ZlciBUaW1lIiwKICAgICAgIHggPSAiSXRlcmF0aW9uIiwKICAgICAgIHkgPSAiQ29zdCIsCiAgICAgICBjb2xvciA9ICJQb2xpY3kiKSArICAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQ29udHJvbCIgPSAiI0FEQjJENCIsICJQb2xpY3lfSSIgPSAiZGFya3JlZCIsICJQb2xpY3lfSUkiID0gIiMyNzQ0NUQiKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkpCgpgYGAKCgpJdCBzZWVtcyBhIGxpdHRsZSBiaXQgY291bnRlciBpbnR1aXRpdmUgYW5kIHN0cmFuZ2UgdG8gc2VlIHRoYXQgbG9uZyB0ZXJtIGl0IGFwcGVhcnMgdGhhdCB0aGUgY29udHJvbCBjYXNlIHdpdGggbm8gaW50ZXJ2ZW50aW9uIGlzIGxlc3MgZXhwZW5zaXZlIGNvbXBhcmVkIHRvIHRoZSBvdGhlciB0d28gcG9saWNpZXMgbG9uZy10ZXJtLiBIb3dldmVyLCB1cG9uIGZ1cnRoZXIgaW5zcGVjdGlvbiB3ZSByZWFsaXplIHRoYXQgaGF2aW5nIHBhdGllbnRzIHdpdGggY2hyb25pYyBjb25kaXRpb24gaXMgdGhlIG1vc3QgY29zdGx5LiBJbnRlcmVzdGluZ2x5LCB1bmRlciB0aGUgdHdvIHBvbGljaWVzIHRoZSBwcm9iYWJpbGl0eSBvZiBzdGF5aW5nIGluIHRoZSBjaHJvbmljIGNvbmRpdGlvbiBzdGF0ZSBzdGFydGluZyBmcm9tIHRoZSBjaHJvbmljIGNvbmRpdGlvbiBzdGF0ZSBpcyBoaWdoZXIgdGhhbiB0aGUgY29udHJvbCBjYXNlLiBUaGUgY2hyb25pYyBjb25kaXRpb24gY2FzZXMgYXBwZWFyIHRvIGJlIG11Y2ggbG93ZXIgdW5kZXIgdGhlIGNvbnRyb2wgY29uZGl0aW9uIGNvbXBhcmVkIHRvIHRoZSBvdGhlciB0d28gcG9saWNpZXMuIEhvd2V2ZXIsIHRoZSBudW1iZXIgb2YgaW5mZWN0ZWQgaW5kaXZpZHVhbHMgcmVtYWluIG11Y2ggaGlnaGVyIGluIHRoZSBjb250cm9sIGNhc2UgY29tcGFyZWQgdG8gdGhlIHR3byBwb2xpY2llcy4gVGhlcmUgaXMgYSB0cmFkZS1vZmYgdG8gdGhpcyBhbmQgdGhhdCBpcyB0aGUgZmFjdCB0aGF0IHRoZSBudW1iZXIgb2YgYXQtcmlzayBwZW9wbGUgaXMgc2lnbmlmaWNhbnRseSBoaWdoZXIgaW4gdGhlIGNvbnRyb2wgY2FzZSBjb21wYXJlZCB0byB0aGUgb3RoZXIgY29uZGl0aW9ucyBpbnZvbHZpbmcgaW50ZXJ2ZW50aW9uLiAKCgoKPGg0Pk1lYW4gRmlyc3QgUGFzc2FnZSBUaW1lIChNRlBUKTwvaDQ+CgoKCmBgYHtyfQpmaW5kLk1GUFQgPC0gZnVuY3Rpb24oUCkgewogIG4gPC0gbnJvdyhQKQogIE1GUFQgPC0gbWF0cml4KDAsIG4sIG4pICAKICAKICBmb3IgKGkgaW4gMTpuKSB7CiAgICBmb3IgKGogaW4gMTpuKSB7CiAgICAgIGlmIChpICE9IGopIHsKICAgICAgICAKICAgICAgICBBIDwtIGRpYWcobikgLSBQCiAgICAgICAgQVtqLCBdIDwtIDAgIAogICAgICAgIEFbaiwgal0gPC0gMSAgCiAgICAgICAgYiA8LSByZXAoMSwgbikKICAgICAgICBiW2pdIDwtIDAgCiAgICAgICAgCiAgICAKICAgICAgICBtIDwtIHNvbHZlKEEsIGIpCiAgICAgICAgTUZQVFtpLCBqXSA8LSBtW2ldICAKICAgICAgfQogICAgfQogIH0KICAKICByZXR1cm4oTUZQVCkKfQoKYGBgCgoKTGV0J3MgZmluZCB0aGUgb3ZlcmFsbCBhY2Nlc3NpYmlsaXR5IG9mIGVhY2ggc3RhdGUgdXNpbmcgdGhlIE1GUFQgbWF0cml4IGFuZCBjb21wYXJlIHRoZSBkaWZmZXJlbnQgY29uZGl0aW9uczoKCmBgYHtyfQoKcHJpbnQocGFzdGUwKCJDb250cm9sOiAiLCBjb2xTdW1zKGZpbmQuTUZQVChQLmNvbnRyb2wpKSkpCnByaW50KHBhc3RlMCgiUG9saWN5IEk6ICIsIGNvbFN1bXMoZmluZC5NRlBUKFAucG9saWN5LmkpKSkpCnByaW50KHBhc3RlMCgiUG9saWN5IElJOiAiLCBjb2xTdW1zKGZpbmQuTUZQVChQLnBvbGljeS5paSkpKSkKCgpgYGAKSXQgc2VlbXMgbGlrZSBiYXNlZCBvbiB0aGlzIGFuYWx5c2lzLCB0aGUgaGVhbHRoeSBzdGF0ZSBpcyBtdWNoIGVhc2llciB0byByZWFjaCBpbiB0aGUgY29udHJvbCBjYXNlIHRoYW4gdGhlIHR3byBvdGhlciBjb25kaXRpb25zLiAKCgoKCgoKCgoKCg==