Q1
##Multi-Period: A Simple Inventory Model
#Holding cost per item per day
h.val=0.6
unit.price=400
unit.cost=250
#Profit margin per TV sold
unit.profit=unit.price-unit.cost
#Parameters of (r, q) inventory policy
r=30
q=130
#cost per delivery from supplier to the dealer
shipping.cost=500
#The number of simulation runs
M=5000
#The number of days
T_value=360
#The experiment will be repeated for
# m in 1:M repetitions
# t in 1:T duration
#Vector initialization
#SL: service level
SL=rep(NA,M)
daily.profit=rep(NA,M)
set.seed(5566)
start.time <- Sys.time()
for(m in 1:M){
#Initialization: Set inventory on-hand=q in day 0
inv.onhand=c(q,rep(NA,T_value-1))
sales=rep(NA,T_value)
orderQ=rep(0,T_value)
#Keep track of inventory on-order but not arrived yet
inv.onorder=c()
inv.onorder.stamp=c()
#The number of deliveries from the supplier
delivery=0
#The total amount of lost sales
loss=0
#The number of stockouts in a repetition
stockout=0
#Simulate Normal demand for T periods
d=rnorm(T_value, mean = 12, sd = 2)
#
for(t in 1:T_value){
if(t==1){
#Selling process
sales[t]=min(inv.onhand[t],d[t])
#Check stockout
if(d[t]>sales[t]){
stockout=stockout+1
loss=loss+(d[t]-sales[t])
}
#Compute inventory position
inv.position=inv.onhand[1]
#Ordering mechanism of (r, q)
if(inv.position<=r){
#Compute order quantity
Q=q-inv.position
#Update inventory on-order
inv.onorder=c(inv.onorder,Q)
#Simulate stochastic lead time
time.to.arrive=t+sample(c(4,5),1,prob=c(0.5,0.5))+1
#Update time stamp for invenory on-order
inv.onorder.stamp=c(inv.onorder.stamp,time.to.arrive)
#Record order quantity
orderQ[t]=Q
}
#Update inventory on-hand in the end of each day
inv.onhand[t+1]=inv.onhand[t]-sales[t]
}
#
if(t>1){
#Check if any inventory on-order should arrive
if(any(inv.onorder.stamp==t)){
#Update the number of deliveries
delivery=delivery+1
#Compute the total of arrived inventories
index=which(inv.onorder.stamp==t)
arrival=sum(inv.onorder[index])
#Update inventory on-hand before starting the day
inv.onhand[t]=inv.onhand[t]+arrival
#Removed those just arrived from inventory on-order
inv.onorder=inv.onorder[-index]
inv.onorder.stamp=inv.onorder.stamp[-index]
#cat("arrival time:",t)
} #end t==1
#Record sales
sales[t]=min(inv.onhand[t],d[t])
#Check if any stockout takes place
if(d[t]>sales[t]){
stockout=stockout+1
loss=loss+d[t]-sales[t]
}
#Update inventory position
inv.position=inv.onhand[t]+sum(inv.onorder)
#Ordering mechanism of (r, q) inventory policy
if(inv.position<=r){
Q=q-inv.position
inv.onorder=c(inv.onorder,Q)
time.to.arrive=t+sample(c(4,5),1,prob=c(0.5,0.5))+1
inv.onorder.stamp=c(inv.onorder.stamp,time.to.arrive)
orderQ[t]=Q
}
#Update inventory on-hand in the end of the day
inv.onhand[t+1]=inv.onhand[t]-sales[t]
} #end t>1
#cat("day:",t,";sales:",sales[t],";onhand:",inv.onhand[t],";onorder:",
# inv.onorder,";order:",orderQ[t],"\n")
} #end for t
SL[m]=(T_value-stockout)/T_value #Calculate the service rate;
#Calculate the average daily profit
daily.profit[m]=(unit.profit*sum(sales)-sum(h.val*inv.onhand)-
shipping.cost*delivery-unit.profit*loss)/T_value
#Simulation progress marker
if(m%%100==0){cat("Repetitions:",m,"\n")}
} #end for m
Repetitions: 100
Repetitions: 200
Repetitions: 300
Repetitions: 400
Repetitions: 500
Repetitions: 600
Repetitions: 700
Repetitions: 800
Repetitions: 900
Repetitions: 1000
Repetitions: 1100
Repetitions: 1200
Repetitions: 1300
Repetitions: 1400
Repetitions: 1500
Repetitions: 1600
Repetitions: 1700
Repetitions: 1800
Repetitions: 1900
Repetitions: 2000
Repetitions: 2100
Repetitions: 2200
Repetitions: 2300
Repetitions: 2400
Repetitions: 2500
Repetitions: 2600
Repetitions: 2700
Repetitions: 2800
Repetitions: 2900
Repetitions: 3000
Repetitions: 3100
Repetitions: 3200
Repetitions: 3300
Repetitions: 3400
Repetitions: 3500
Repetitions: 3600
Repetitions: 3700
Repetitions: 3800
Repetitions: 3900
Repetitions: 4000
Repetitions: 4100
Repetitions: 4200
Repetitions: 4300
Repetitions: 4400
Repetitions: 4500
Repetitions: 4600
Repetitions: 4700
Repetitions: 4800
Repetitions: 4900
Repetitions: 5000
end.time <- Sys.time()
time.taken <- end.time - start.time
time.taken
Time difference of 4.741276 secs
summary(daily.profit)
Min. 1st Qu. Median Mean 3rd Qu. Max.
647.4 714.7 728.8 729.4 743.2 818.7
summary(SL)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.6528 0.6778 0.6806 0.6818 0.6861 0.7139
##Search for optimal (R*, Q*)
R=seq(0,60,5)
Q=seq(90,360,10)
search.val=as.matrix(expand.grid(R,Q))
dim(search.val)
[1] 364 2
#The (r, q) model as a function for simulation
InvModel=function(i){
r=search.val[i,1]
q=search.val[i,2]
#Vector initialization
SL=rep(NA,M)
daily.profit=rep(NA,M)
for(m in 1:M){
#Initialization: Set inventory on-hand=q in day 0
inv.onhand=c(q,rep(NA,T_value-1))
sales=rep(NA,T_value)
orderQ=rep(0,T_value)
#Keep track of inventory on-order but not arrived yet
inv.onorder=c()
inv.onorder.stamp=c()
#The number of deliveries from the supplier
delivery=0
#The total amount of lost sales
loss=0
#The number of stockouts in a repetition
stockout=0
#Simulate Normal demand for T periods
d=rnorm(T_value, mean = 12, sd = 2)
#
for(t in 1:T_value){
if(t==1){
#Selling process
sales[t]=min(inv.onhand[t],d[t])
#Check stockout
if(d[t]>sales[t]){
stockout=stockout+1
loss=loss+(d[t]-sales[t])
}
#Compute inventory position
inv.position=inv.onhand[1]
#Ordering mechanism of (r, q)
if(inv.position<=r){
#Compute order quantity
Q=q-inv.position
#Update inventory on-order
inv.onorder=c(inv.onorder,Q)
#Simulate stochastic lead time
time.to.arrive=t+sample(c(4,5),1,prob=c(0.5,0.5))+1
#Update time stamp for invenory on-order
inv.onorder.stamp=c(inv.onorder.stamp,time.to.arrive)
#Record order quantity
orderQ[t]=Q
}
#Update inventory on-hand in the end of each day
inv.onhand[t+1]=inv.onhand[t]-sales[t]
}
#
if(t>1){
#Check if any inventory on-order should arrive
if(any(inv.onorder.stamp==t)){
#Update the number of deliveries
delivery=delivery+1
#Compute the total of arrived inventories
index=which(inv.onorder.stamp==t)
arrival=sum(inv.onorder[index])
#Update inventory on-hand before starting the day
inv.onhand[t]=inv.onhand[t]+arrival
#Removed those just arrived from inventory on-order
inv.onorder=inv.onorder[-index]
inv.onorder.stamp=inv.onorder.stamp[-index]
#cat("arrival time:",t)
} #end t==1
#Record sales
sales[t]=min(inv.onhand[t],d[t])
#Check if any stockout takes place
if(d[t]>sales[t]){
stockout=stockout+1
loss=loss+d[t]-sales[t]
}
#Update inventory position
inv.position=inv.onhand[t]+sum(inv.onorder)
#Ordering mechanism of (r, q) inventory policy
if(inv.position<=r){
Q=q-inv.position
inv.onorder=c(inv.onorder,Q)
time.to.arrive=t+sample(c(4,5),1,prob=c(0.5,0.5))+1
inv.onorder.stamp=c(inv.onorder.stamp,time.to.arrive)
orderQ[t]=Q
}
#Update inventory on-hand in the end of the day
inv.onhand[t+1]=inv.onhand[t]-sales[t]
} #end t>1
#cat("day:",t,";sales:",sales[t],";onhand:",inv.onhand[t],";onorder:",
# inv.onorder,";order:",orderQ[t],"\n")
} #end for t
SL[m]=(T_value-stockout)/T_value #Calculate the service rate;
#Calculate the average daily profit
daily.profit[m]=(unit.profit*sum(sales)-sum(h.val*inv.onhand)-
shipping.cost*delivery-unit.profit*loss)/T_value
} #end for m
c(mean(SL),mean(daily.profit),sd(daily.profit))
}
InvModel(1)
##Use parallel CPU computing for optimal search
library(foreach)
library(doParallel)
registerDoParallel(cores=6)
getDoParWorkers()
[1] 6
start.time <- Sys.time()
sim.results=foreach(i=1:nrow(search.val),
.combine=rbind,.verbose=F) %dopar% InvModel(i)
##Apply the Hooke-Jeeves search algorithm
InvModel=function(par){
r=par[1]
q=par[2]
#Vector initialization
SL=rep(NA,M)
daily.profit=rep(NA,M)
for(m in 1:M){
#Initialization: Set inventory on-hand=q in day 0
inv.onhand=c(q,rep(NA,T_value-1))
sales=rep(NA,T_value)
orderQ=rep(0,T_value)
#Keep track of inventory on-order but not arrived yet
inv.onorder=c()
inv.onorder.stamp=c()
#The number of deliveries from the supplier
delivery=0
#The total amount of lost sales
loss=0
#The number of stockouts in a repetition
stockout=0
#Simulate Normal demand for T periods
d=rnorm(T_value, mean = 12, sd = 2)
#
for(t in 1:T_value){
if(t==1){
#Selling process
sales[t]=min(inv.onhand[t],d[t])
#Check stockout
if(d[t]>sales[t]){
stockout=stockout+1
loss=loss+(d[t]-sales[t])
}
#Compute inventory position
inv.position=inv.onhand[1]
#Ordering mechanism of (r, q)
if(inv.position<=r){
#Compute order quantity
Q=q-inv.position
#Update inventory on-order
inv.onorder=c(inv.onorder,Q)
#Simulate stochastic lead time
time.to.arrive=t+sample(c(4,5),1,prob=c(0.5,0.5))+1
#Update time stamp for invenory on-order
inv.onorder.stamp=c(inv.onorder.stamp,time.to.arrive)
#Record order quantity
orderQ[t]=Q
}
#Update inventory on-hand in the end of each day
inv.onhand[t+1]=inv.onhand[t]-sales[t]
}
#
if(t>1){
#Check if any inventory on-order should arrive
if(any(inv.onorder.stamp==t)){
#Update the number of deliveries
delivery=delivery+1
#Compute the total of arrived inventories
index=which(inv.onorder.stamp==t)
arrival=sum(inv.onorder[index])
#Update inventory on-hand before starting the day
inv.onhand[t]=inv.onhand[t]+arrival
#Removed those just arrived from inventory on-order
inv.onorder=inv.onorder[-index]
inv.onorder.stamp=inv.onorder.stamp[-index]
#cat("arrival time:",t)
} #end t==1
#Record sales
sales[t]=min(inv.onhand[t],d[t])
#Check if any stockout takes place
if(d[t]>sales[t]){
stockout=stockout+1
loss=loss+d[t]-sales[t]
}
#Update inventory position
inv.position=inv.onhand[t]+sum(inv.onorder)
#Ordering mechanism of (r, q) inventory policy
if(inv.position<=r){
Q=q-inv.position
inv.onorder=c(inv.onorder,Q)
time.to.arrive=t+sample(c(4,5),1,prob=c(0.5,0.5))+1
inv.onorder.stamp=c(inv.onorder.stamp,time.to.arrive)
orderQ[t]=Q
}
#Update inventory on-hand in the end of the day
inv.onhand[t+1]=inv.onhand[t]-sales[t]
} #end t>1
#cat("day:",t,";sales:",sales[t],";onhand:",inv.onhand[t],";onorder:",
# inv.onorder,";order:",orderQ[t],"\n")
} #end for t
SL[m]=(T_value-stockout)/T_value #Calculate the service rate;
#Calculate the average daily profit
daily.profit[m]=(unit.profit*sum(sales)-sum(h.val*inv.onhand)-
shipping.cost*delivery-unit.profit*loss)/T_value
} #end for m
#c(mean(SL),mean(daily.profit),sd(daily.profit))
mean(daily.profit)
}
library(pracma)
res=fminsearch(InvModel,c(30,130),
lower=c(20,80),
upper=c(150,250),
method=c("Hooke-Jeeves"),
minimize=FALSE)
res
$xmin
[1] 87.63159 215.07648
$fmin
[1] 1700.547
$count
[1] 479
$convergence
[1] 0
$info
$info$solver
[1] "Hooke-Jeeves"
$info$iterations
[1] 26
LS0tCnRpdGxlOiAiSFc0X0dyb3VwMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoK57WE5ZOh77ya5ZSQ5oCd55Cq44CB5p6X5pu46ZyG44CB5p+v54Kv5ZCN44CB57Ch5L2R6Ie744CB546L5am344CB6IOh56WQ6YqTCgojIyBRMQoKYGBge3J9CiMjTXVsdGktUGVyaW9kOiBBIFNpbXBsZSBJbnZlbnRvcnkgTW9kZWwKCiNIb2xkaW5nIGNvc3QgcGVyIGl0ZW0gcGVyIGRheQpoLnZhbD0wLjYKCnVuaXQucHJpY2U9NDAwCnVuaXQuY29zdD0yNTAKI1Byb2ZpdCBtYXJnaW4gcGVyIFRWIHNvbGQKdW5pdC5wcm9maXQ9dW5pdC5wcmljZS11bml0LmNvc3QKCiNQYXJhbWV0ZXJzIG9mIChyLCBxKSBpbnZlbnRvcnkgcG9saWN5CnI9MzAKcT0xMzAKCiNjb3N0IHBlciBkZWxpdmVyeSBmcm9tIHN1cHBsaWVyIHRvIHRoZSBkZWFsZXIKc2hpcHBpbmcuY29zdD01MDAKCiNUaGUgbnVtYmVyIG9mIHNpbXVsYXRpb24gcnVucwpNPTUwMDAKI1RoZSBudW1iZXIgb2YgZGF5cwpUX3ZhbHVlPTM2MAoKI1RoZSBleHBlcmltZW50IHdpbGwgYmUgcmVwZWF0ZWQgZm9yIAojIG0gaW4gMTpNIHJlcGV0aXRpb25zCiMgdCBpbiAxOlQgZHVyYXRpb24KCiNWZWN0b3IgaW5pdGlhbGl6YXRpb24KI1NMOiBzZXJ2aWNlIGxldmVsClNMPXJlcChOQSxNKQpkYWlseS5wcm9maXQ9cmVwKE5BLE0pCnNldC5zZWVkKDU1NjYpCgpzdGFydC50aW1lIDwtIFN5cy50aW1lKCkKZm9yKG0gaW4gMTpNKXsKICAjSW5pdGlhbGl6YXRpb246IFNldCBpbnZlbnRvcnkgb24taGFuZD1xIGluIGRheSAwCiAgaW52Lm9uaGFuZD1jKHEscmVwKE5BLFRfdmFsdWUtMSkpCiAgc2FsZXM9cmVwKE5BLFRfdmFsdWUpCiAgb3JkZXJRPXJlcCgwLFRfdmFsdWUpCiAgI0tlZXAgdHJhY2sgb2YgaW52ZW50b3J5IG9uLW9yZGVyIGJ1dCBub3QgYXJyaXZlZCB5ZXQKICBpbnYub25vcmRlcj1jKCkKICBpbnYub25vcmRlci5zdGFtcD1jKCkKICAjVGhlIG51bWJlciBvZiBkZWxpdmVyaWVzIGZyb20gdGhlIHN1cHBsaWVyCiAgZGVsaXZlcnk9MAogICNUaGUgdG90YWwgYW1vdW50IG9mIGxvc3Qgc2FsZXMgCiAgbG9zcz0wCiAgI1RoZSBudW1iZXIgb2Ygc3RvY2tvdXRzIGluIGEgcmVwZXRpdGlvbgogIHN0b2Nrb3V0PTAKICAjU2ltdWxhdGUgTm9ybWFsIGRlbWFuZCBmb3IgVCBwZXJpb2RzCiAgZD1ybm9ybShUX3ZhbHVlLCBtZWFuID0gMTIsIHNkID0gMikKICAjCiAgZm9yKHQgaW4gMTpUX3ZhbHVlKXsKICAgICBpZih0PT0xKXsKICAgICAgICNTZWxsaW5nIHByb2Nlc3MKICAgICAgIHNhbGVzW3RdPW1pbihpbnYub25oYW5kW3RdLGRbdF0pCiAgICAgICAjQ2hlY2sgc3RvY2tvdXQKICAgICAgIGlmKGRbdF0+c2FsZXNbdF0pewogICAgICAgICBzdG9ja291dD1zdG9ja291dCsxCiAgICAgICAgIGxvc3M9bG9zcysoZFt0XS1zYWxlc1t0XSkKICAgICAgIH0KICAgICAgICNDb21wdXRlIGludmVudG9yeSBwb3NpdGlvbgogICAgICAgaW52LnBvc2l0aW9uPWludi5vbmhhbmRbMV0KICAgICAgICNPcmRlcmluZyBtZWNoYW5pc20gb2YgKHIsIHEpCiAgICAgICBpZihpbnYucG9zaXRpb248PXIpewogICAgICAgICAjQ29tcHV0ZSBvcmRlciBxdWFudGl0eQogICAgICAgICBRPXEtaW52LnBvc2l0aW9uCiAgICAgICAgICNVcGRhdGUgaW52ZW50b3J5IG9uLW9yZGVyCiAgICAgICAgIGludi5vbm9yZGVyPWMoaW52Lm9ub3JkZXIsUSkKICAgICAgICAgI1NpbXVsYXRlIHN0b2NoYXN0aWMgbGVhZCB0aW1lCiAgICAgICAgIHRpbWUudG8uYXJyaXZlPXQrc2FtcGxlKGMoNCw1KSwxLHByb2I9YygwLjUsMC41KSkrMQogICAgICAgICAjVXBkYXRlIHRpbWUgc3RhbXAgZm9yIGludmVub3J5IG9uLW9yZGVyCiAgICAgICAgIGludi5vbm9yZGVyLnN0YW1wPWMoaW52Lm9ub3JkZXIuc3RhbXAsdGltZS50by5hcnJpdmUpCiAgICAgICAgICNSZWNvcmQgb3JkZXIgcXVhbnRpdHkKICAgICAgICAgb3JkZXJRW3RdPVEKICAgICAgIH0KICAgICAgICNVcGRhdGUgaW52ZW50b3J5IG9uLWhhbmQgaW4gdGhlIGVuZCBvZiBlYWNoIGRheQogICAgICAgaW52Lm9uaGFuZFt0KzFdPWludi5vbmhhbmRbdF0tc2FsZXNbdF0KICAgICB9CiAgICAgIwogICAgIGlmKHQ+MSl7CiAgICAgICAgI0NoZWNrIGlmIGFueSBpbnZlbnRvcnkgb24tb3JkZXIgc2hvdWxkIGFycml2ZQogICAgICAgIGlmKGFueShpbnYub25vcmRlci5zdGFtcD09dCkpewogICAgICAgICAgICNVcGRhdGUgdGhlIG51bWJlciBvZiBkZWxpdmVyaWVzCiAgICAgICAgICAgZGVsaXZlcnk9ZGVsaXZlcnkrMQogICAgICAgICAgICNDb21wdXRlIHRoZSB0b3RhbCBvZiBhcnJpdmVkIGludmVudG9yaWVzCiAgICAgICAgICAgaW5kZXg9d2hpY2goaW52Lm9ub3JkZXIuc3RhbXA9PXQpCiAgICAgICAgICAgYXJyaXZhbD1zdW0oaW52Lm9ub3JkZXJbaW5kZXhdKQogICAgICAgICAgICNVcGRhdGUgaW52ZW50b3J5IG9uLWhhbmQgYmVmb3JlIHN0YXJ0aW5nIHRoZSBkYXkKICAgICAgICAgICBpbnYub25oYW5kW3RdPWludi5vbmhhbmRbdF0rYXJyaXZhbAogICAgICAgICAgICNSZW1vdmVkIHRob3NlIGp1c3QgYXJyaXZlZCBmcm9tIGludmVudG9yeSBvbi1vcmRlcgogICAgICAgICAgIGludi5vbm9yZGVyPWludi5vbm9yZGVyWy1pbmRleF0KICAgICAgICAgICBpbnYub25vcmRlci5zdGFtcD1pbnYub25vcmRlci5zdGFtcFstaW5kZXhdCiAgICAgICAgICAgI2NhdCgiYXJyaXZhbCB0aW1lOiIsdCkKICAgICAgICB9ICNlbmQgdD09MQogICAgICAgI1JlY29yZCBzYWxlcwogICAgICAgc2FsZXNbdF09bWluKGludi5vbmhhbmRbdF0sZFt0XSkKICAgICAgICNDaGVjayBpZiBhbnkgc3RvY2tvdXQgdGFrZXMgcGxhY2UKICAgICAgIGlmKGRbdF0+c2FsZXNbdF0pewogICAgICAgICBzdG9ja291dD1zdG9ja291dCsxCiAgICAgICAgIGxvc3M9bG9zcytkW3RdLXNhbGVzW3RdCiAgICAgICB9CiAgICAgICAjVXBkYXRlIGludmVudG9yeSBwb3NpdGlvbgogICAgICAgaW52LnBvc2l0aW9uPWludi5vbmhhbmRbdF0rc3VtKGludi5vbm9yZGVyKQogICAgICAgI09yZGVyaW5nIG1lY2hhbmlzbSBvZiAociwgcSkgaW52ZW50b3J5IHBvbGljeQogICAgICAgaWYoaW52LnBvc2l0aW9uPD1yKXsKICAgICAgICAgUT1xLWludi5wb3NpdGlvbgogICAgICAgICBpbnYub25vcmRlcj1jKGludi5vbm9yZGVyLFEpCiAgICAgICAgIHRpbWUudG8uYXJyaXZlPXQrc2FtcGxlKGMoNCw1KSwxLHByb2I9YygwLjUsMC41KSkrMQogICAgICAgICBpbnYub25vcmRlci5zdGFtcD1jKGludi5vbm9yZGVyLnN0YW1wLHRpbWUudG8uYXJyaXZlKQogICAgICAgICBvcmRlclFbdF09UQogICAgICAgfQogICAgICAgI1VwZGF0ZSBpbnZlbnRvcnkgb24taGFuZCBpbiB0aGUgZW5kIG9mIHRoZSBkYXkKICAgICAgIGludi5vbmhhbmRbdCsxXT1pbnYub25oYW5kW3RdLXNhbGVzW3RdCiAgICAgfSAjZW5kIHQ+MQogICAgICNjYXQoImRheToiLHQsIjtzYWxlczoiLHNhbGVzW3RdLCI7b25oYW5kOiIsaW52Lm9uaGFuZFt0XSwiO29ub3JkZXI6IiwKICAgICAjICAgICBpbnYub25vcmRlciwiO29yZGVyOiIsb3JkZXJRW3RdLCJcbiIpIAogIH0gI2VuZCBmb3IgdAogIAogIFNMW21dPShUX3ZhbHVlLXN0b2Nrb3V0KS9UX3ZhbHVlICAjQ2FsY3VsYXRlIHRoZSBzZXJ2aWNlIHJhdGU7IAogICAgCiAgI0NhbGN1bGF0ZSB0aGUgYXZlcmFnZSBkYWlseSBwcm9maXQKICBkYWlseS5wcm9maXRbbV09KHVuaXQucHJvZml0KnN1bShzYWxlcyktc3VtKGgudmFsKmludi5vbmhhbmQpLQogICAgICAgICAgICAgICAgICAgc2hpcHBpbmcuY29zdCpkZWxpdmVyeS11bml0LnByb2ZpdCpsb3NzKS9UX3ZhbHVlCiAgI1NpbXVsYXRpb24gcHJvZ3Jlc3MgbWFya2VyCiAgaWYobSUlMTAwPT0wKXtjYXQoIlJlcGV0aXRpb25zOiIsbSwiXG4iKX0gCgp9ICNlbmQgZm9yIG0KCmVuZC50aW1lIDwtIFN5cy50aW1lKCkKdGltZS50YWtlbiA8LSBlbmQudGltZSAtIHN0YXJ0LnRpbWUKdGltZS50YWtlbgoKCnN1bW1hcnkoZGFpbHkucHJvZml0KQpzdW1tYXJ5KFNMKQpgYGAKCgpgYGB7cn0KIyNTZWFyY2ggZm9yIG9wdGltYWwgKFIqLCBRKikKUj1zZXEoMCw2MCw1KQpRPXNlcSg5MCwzNjAsMTApCgpzZWFyY2gudmFsPWFzLm1hdHJpeChleHBhbmQuZ3JpZChSLFEpKQpkaW0oc2VhcmNoLnZhbCkKCiNUaGUgKHIsIHEpIG1vZGVsIGFzIGEgZnVuY3Rpb24gZm9yIHNpbXVsYXRpb24KSW52TW9kZWw9ZnVuY3Rpb24oaSl7CiAgcj1zZWFyY2gudmFsW2ksMV0KICBxPXNlYXJjaC52YWxbaSwyXQogICNWZWN0b3IgaW5pdGlhbGl6YXRpb24KICBTTD1yZXAoTkEsTSkKICBkYWlseS5wcm9maXQ9cmVwKE5BLE0pCiAgZm9yKG0gaW4gMTpNKXsKICAgICNJbml0aWFsaXphdGlvbjogU2V0IGludmVudG9yeSBvbi1oYW5kPXEgaW4gZGF5IDAKICAgIGludi5vbmhhbmQ9YyhxLHJlcChOQSxUX3ZhbHVlLTEpKQogICAgc2FsZXM9cmVwKE5BLFRfdmFsdWUpCiAgICBvcmRlclE9cmVwKDAsVF92YWx1ZSkKICAgICNLZWVwIHRyYWNrIG9mIGludmVudG9yeSBvbi1vcmRlciBidXQgbm90IGFycml2ZWQgeWV0CiAgICBpbnYub25vcmRlcj1jKCkKICAgIGludi5vbm9yZGVyLnN0YW1wPWMoKQogICAgI1RoZSBudW1iZXIgb2YgZGVsaXZlcmllcyBmcm9tIHRoZSBzdXBwbGllcgogICAgZGVsaXZlcnk9MAogICAgI1RoZSB0b3RhbCBhbW91bnQgb2YgbG9zdCBzYWxlcyAKICAgIGxvc3M9MAogICAgI1RoZSBudW1iZXIgb2Ygc3RvY2tvdXRzIGluIGEgcmVwZXRpdGlvbgogICAgc3RvY2tvdXQ9MAogICAgI1NpbXVsYXRlIE5vcm1hbCBkZW1hbmQgZm9yIFQgcGVyaW9kcwogICAgZD1ybm9ybShUX3ZhbHVlLCBtZWFuID0gMTIsIHNkID0gMikKICAgICMKICAgIGZvcih0IGluIDE6VF92YWx1ZSl7CiAgICAgIGlmKHQ9PTEpewogICAgICAgICNTZWxsaW5nIHByb2Nlc3MKICAgICAgICBzYWxlc1t0XT1taW4oaW52Lm9uaGFuZFt0XSxkW3RdKQogICAgICAgICNDaGVjayBzdG9ja291dAogICAgICAgIGlmKGRbdF0+c2FsZXNbdF0pewogICAgICAgICAgc3RvY2tvdXQ9c3RvY2tvdXQrMQogICAgICAgICAgbG9zcz1sb3NzKyhkW3RdLXNhbGVzW3RdKQogICAgICAgIH0KICAgICAgICAjQ29tcHV0ZSBpbnZlbnRvcnkgcG9zaXRpb24KICAgICAgICBpbnYucG9zaXRpb249aW52Lm9uaGFuZFsxXQogICAgICAgICNPcmRlcmluZyBtZWNoYW5pc20gb2YgKHIsIHEpCiAgICAgICAgaWYoaW52LnBvc2l0aW9uPD1yKXsKICAgICAgICAgICNDb21wdXRlIG9yZGVyIHF1YW50aXR5CiAgICAgICAgICBRPXEtaW52LnBvc2l0aW9uCiAgICAgICAgICAjVXBkYXRlIGludmVudG9yeSBvbi1vcmRlcgogICAgICAgICAgaW52Lm9ub3JkZXI9YyhpbnYub25vcmRlcixRKQogICAgICAgICAgI1NpbXVsYXRlIHN0b2NoYXN0aWMgbGVhZCB0aW1lCiAgICAgICAgICB0aW1lLnRvLmFycml2ZT10K3NhbXBsZShjKDQsNSksMSxwcm9iPWMoMC41LDAuNSkpKzEKICAgICAgICAgICNVcGRhdGUgdGltZSBzdGFtcCBmb3IgaW52ZW5vcnkgb24tb3JkZXIKICAgICAgICAgIGludi5vbm9yZGVyLnN0YW1wPWMoaW52Lm9ub3JkZXIuc3RhbXAsdGltZS50by5hcnJpdmUpCiAgICAgICAgICAjUmVjb3JkIG9yZGVyIHF1YW50aXR5CiAgICAgICAgICBvcmRlclFbdF09UQogICAgICAgIH0KICAgICAgICAjVXBkYXRlIGludmVudG9yeSBvbi1oYW5kIGluIHRoZSBlbmQgb2YgZWFjaCBkYXkKICAgICAgICBpbnYub25oYW5kW3QrMV09aW52Lm9uaGFuZFt0XS1zYWxlc1t0XQogICAgICB9CiAgICAgICMKICAgICAgaWYodD4xKXsKICAgICAgICAjQ2hlY2sgaWYgYW55IGludmVudG9yeSBvbi1vcmRlciBzaG91bGQgYXJyaXZlCiAgICAgICAgaWYoYW55KGludi5vbm9yZGVyLnN0YW1wPT10KSl7CiAgICAgICAgICAjVXBkYXRlIHRoZSBudW1iZXIgb2YgZGVsaXZlcmllcwogICAgICAgICAgZGVsaXZlcnk9ZGVsaXZlcnkrMQogICAgICAgICAgI0NvbXB1dGUgdGhlIHRvdGFsIG9mIGFycml2ZWQgaW52ZW50b3JpZXMKICAgICAgICAgIGluZGV4PXdoaWNoKGludi5vbm9yZGVyLnN0YW1wPT10KQogICAgICAgICAgYXJyaXZhbD1zdW0oaW52Lm9ub3JkZXJbaW5kZXhdKQogICAgICAgICAgI1VwZGF0ZSBpbnZlbnRvcnkgb24taGFuZCBiZWZvcmUgc3RhcnRpbmcgdGhlIGRheQogICAgICAgICAgaW52Lm9uaGFuZFt0XT1pbnYub25oYW5kW3RdK2Fycml2YWwKICAgICAgICAgICNSZW1vdmVkIHRob3NlIGp1c3QgYXJyaXZlZCBmcm9tIGludmVudG9yeSBvbi1vcmRlcgogICAgICAgICAgaW52Lm9ub3JkZXI9aW52Lm9ub3JkZXJbLWluZGV4XQogICAgICAgICAgaW52Lm9ub3JkZXIuc3RhbXA9aW52Lm9ub3JkZXIuc3RhbXBbLWluZGV4XQogICAgICAgICAgI2NhdCgiYXJyaXZhbCB0aW1lOiIsdCkKICAgICAgICB9ICNlbmQgdD09MQogICAgICAgICNSZWNvcmQgc2FsZXMKICAgICAgICBzYWxlc1t0XT1taW4oaW52Lm9uaGFuZFt0XSxkW3RdKQogICAgICAgICNDaGVjayBpZiBhbnkgc3RvY2tvdXQgdGFrZXMgcGxhY2UKICAgICAgICBpZihkW3RdPnNhbGVzW3RdKXsKICAgICAgICAgIHN0b2Nrb3V0PXN0b2Nrb3V0KzEKICAgICAgICAgIGxvc3M9bG9zcytkW3RdLXNhbGVzW3RdCiAgICAgICAgfQogICAgICAgICNVcGRhdGUgaW52ZW50b3J5IHBvc2l0aW9uCiAgICAgICAgaW52LnBvc2l0aW9uPWludi5vbmhhbmRbdF0rc3VtKGludi5vbm9yZGVyKQogICAgICAgICNPcmRlcmluZyBtZWNoYW5pc20gb2YgKHIsIHEpIGludmVudG9yeSBwb2xpY3kKICAgICAgICBpZihpbnYucG9zaXRpb248PXIpewogICAgICAgICAgUT1xLWludi5wb3NpdGlvbgogICAgICAgICAgaW52Lm9ub3JkZXI9YyhpbnYub25vcmRlcixRKQogICAgICAgICAgdGltZS50by5hcnJpdmU9dCtzYW1wbGUoYyg0LDUpLDEscHJvYj1jKDAuNSwwLjUpKSsxCiAgICAgICAgICBpbnYub25vcmRlci5zdGFtcD1jKGludi5vbm9yZGVyLnN0YW1wLHRpbWUudG8uYXJyaXZlKQogICAgICAgICAgb3JkZXJRW3RdPVEKICAgICAgICB9CiAgICAgICAgI1VwZGF0ZSBpbnZlbnRvcnkgb24taGFuZCBpbiB0aGUgZW5kIG9mIHRoZSBkYXkKICAgICAgICBpbnYub25oYW5kW3QrMV09aW52Lm9uaGFuZFt0XS1zYWxlc1t0XQogICAgICB9ICNlbmQgdD4xCiAgICAgICNjYXQoImRheToiLHQsIjtzYWxlczoiLHNhbGVzW3RdLCI7b25oYW5kOiIsaW52Lm9uaGFuZFt0XSwiO29ub3JkZXI6IiwKICAgICAgIyAgICAgaW52Lm9ub3JkZXIsIjtvcmRlcjoiLG9yZGVyUVt0XSwiXG4iKSAKICAgIH0gI2VuZCBmb3IgdAogICAgCiAgICBTTFttXT0oVF92YWx1ZS1zdG9ja291dCkvVF92YWx1ZSAgI0NhbGN1bGF0ZSB0aGUgc2VydmljZSByYXRlOyAKICAgIAogICAgI0NhbGN1bGF0ZSB0aGUgYXZlcmFnZSBkYWlseSBwcm9maXQKICAgIGRhaWx5LnByb2ZpdFttXT0odW5pdC5wcm9maXQqc3VtKHNhbGVzKS1zdW0oaC52YWwqaW52Lm9uaGFuZCktCiAgICAgICAgICAgICAgICAgICAgICAgc2hpcHBpbmcuY29zdCpkZWxpdmVyeS11bml0LnByb2ZpdCpsb3NzKS9UX3ZhbHVlCiAgICAKICB9ICNlbmQgZm9yIG0KICBjKG1lYW4oU0wpLG1lYW4oZGFpbHkucHJvZml0KSxzZChkYWlseS5wcm9maXQpKQp9CgoKSW52TW9kZWwoMSkKYGBgCgoKYGBge3J9CiMjVXNlIHBhcmFsbGVsIENQVSBjb21wdXRpbmcgZm9yIG9wdGltYWwgc2VhcmNoCmxpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShkb1BhcmFsbGVsKQpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXM9NikKZ2V0RG9QYXJXb3JrZXJzKCkKCnN0YXJ0LnRpbWUgPC0gU3lzLnRpbWUoKQpzaW0ucmVzdWx0cz1mb3JlYWNoKGk9MTpucm93KHNlYXJjaC52YWwpLAogICAgICAgICAgICAgLmNvbWJpbmU9cmJpbmQsLnZlcmJvc2U9RikgJWRvcGFyJSBJbnZNb2RlbChpKQplbmQudGltZSA8LSBTeXMudGltZSgpCnRpbWUudGFrZW4gPC0gZW5kLnRpbWUgLSBzdGFydC50aW1lCnRpbWUudGFrZW4KCgojVXNlIG5vbi1wYXJhbGxlbCBjb21wdXRpbmcgZm9yIG9wdGltYWwgc2VhcmNoCnN0YXJ0LnRpbWUgPC0gU3lzLnRpbWUoKQpzaW0ucmVzdWx0c0lJPW1hdHJpeChOQSxuY29sPTMsbnJvdz1ucm93KHNlYXJjaC52YWwpKQpmb3IoaSBpbiAxOm5yb3coc2VhcmNoLnZhbCkpewogICBzaW0ucmVzdWx0c0lJW2ksXT1JbnZNb2RlbChpKQp9CmVuZC50aW1lIDwtIFN5cy50aW1lKCkKdGltZS50YWtlbiA8LSBlbmQudGltZSAtIHN0YXJ0LnRpbWUKdGltZS50YWtlbgoKI3NpbS5yZXN1bHRzSUkKc3VtbWFyeShzaW0ucmVzdWx0c0lJWywxXSkKc3VtbWFyeShzaW0ucmVzdWx0c0lJWywyXSkKd2hpY2gubWF4KHNpbS5yZXN1bHRzSUlbLDJdKQpzZWFyY2gudmFsW3doaWNoLm1heChzaW0ucmVzdWx0c0lJWywyXSksXQpgYGAKCgpgYGB7cn0KIyNBcHBseSB0aGUgSG9va2UtSmVldmVzIHNlYXJjaCBhbGdvcml0aG0KSW52TW9kZWw9ZnVuY3Rpb24ocGFyKXsKICByPXBhclsxXQogIHE9cGFyWzJdCiAgI1ZlY3RvciBpbml0aWFsaXphdGlvbgogIFNMPXJlcChOQSxNKQogIGRhaWx5LnByb2ZpdD1yZXAoTkEsTSkKICBmb3IobSBpbiAxOk0pewogICAgI0luaXRpYWxpemF0aW9uOiBTZXQgaW52ZW50b3J5IG9uLWhhbmQ9cSBpbiBkYXkgMAogICAgaW52Lm9uaGFuZD1jKHEscmVwKE5BLFRfdmFsdWUtMSkpCiAgICBzYWxlcz1yZXAoTkEsVF92YWx1ZSkKICAgIG9yZGVyUT1yZXAoMCxUX3ZhbHVlKQogICAgI0tlZXAgdHJhY2sgb2YgaW52ZW50b3J5IG9uLW9yZGVyIGJ1dCBub3QgYXJyaXZlZCB5ZXQKICAgIGludi5vbm9yZGVyPWMoKQogICAgaW52Lm9ub3JkZXIuc3RhbXA9YygpCiAgICAjVGhlIG51bWJlciBvZiBkZWxpdmVyaWVzIGZyb20gdGhlIHN1cHBsaWVyCiAgICBkZWxpdmVyeT0wCiAgICAjVGhlIHRvdGFsIGFtb3VudCBvZiBsb3N0IHNhbGVzIAogICAgbG9zcz0wCiAgICAjVGhlIG51bWJlciBvZiBzdG9ja291dHMgaW4gYSByZXBldGl0aW9uCiAgICBzdG9ja291dD0wCiAgICAjU2ltdWxhdGUgTm9ybWFsIGRlbWFuZCBmb3IgVCBwZXJpb2RzCiAgICBkPXJub3JtKFRfdmFsdWUsIG1lYW4gPSAxMiwgc2QgPSAyKQogICAgIwogICAgZm9yKHQgaW4gMTpUX3ZhbHVlKXsKICAgICAgaWYodD09MSl7CiAgICAgICAgI1NlbGxpbmcgcHJvY2VzcwogICAgICAgIHNhbGVzW3RdPW1pbihpbnYub25oYW5kW3RdLGRbdF0pCiAgICAgICAgI0NoZWNrIHN0b2Nrb3V0CiAgICAgICAgaWYoZFt0XT5zYWxlc1t0XSl7CiAgICAgICAgICBzdG9ja291dD1zdG9ja291dCsxCiAgICAgICAgICBsb3NzPWxvc3MrKGRbdF0tc2FsZXNbdF0pCiAgICAgICAgfQogICAgICAgICNDb21wdXRlIGludmVudG9yeSBwb3NpdGlvbgogICAgICAgIGludi5wb3NpdGlvbj1pbnYub25oYW5kWzFdCiAgICAgICAgI09yZGVyaW5nIG1lY2hhbmlzbSBvZiAociwgcSkKICAgICAgICBpZihpbnYucG9zaXRpb248PXIpewogICAgICAgICAgI0NvbXB1dGUgb3JkZXIgcXVhbnRpdHkKICAgICAgICAgIFE9cS1pbnYucG9zaXRpb24KICAgICAgICAgICNVcGRhdGUgaW52ZW50b3J5IG9uLW9yZGVyCiAgICAgICAgICBpbnYub25vcmRlcj1jKGludi5vbm9yZGVyLFEpCiAgICAgICAgICAjU2ltdWxhdGUgc3RvY2hhc3RpYyBsZWFkIHRpbWUKICAgICAgICAgIHRpbWUudG8uYXJyaXZlPXQrc2FtcGxlKGMoNCw1KSwxLHByb2I9YygwLjUsMC41KSkrMQogICAgICAgICAgI1VwZGF0ZSB0aW1lIHN0YW1wIGZvciBpbnZlbm9yeSBvbi1vcmRlcgogICAgICAgICAgaW52Lm9ub3JkZXIuc3RhbXA9YyhpbnYub25vcmRlci5zdGFtcCx0aW1lLnRvLmFycml2ZSkKICAgICAgICAgICNSZWNvcmQgb3JkZXIgcXVhbnRpdHkKICAgICAgICAgIG9yZGVyUVt0XT1RCiAgICAgICAgfQogICAgICAgICNVcGRhdGUgaW52ZW50b3J5IG9uLWhhbmQgaW4gdGhlIGVuZCBvZiBlYWNoIGRheQogICAgICAgIGludi5vbmhhbmRbdCsxXT1pbnYub25oYW5kW3RdLXNhbGVzW3RdCiAgICAgIH0KICAgICAgIwogICAgICBpZih0PjEpewogICAgICAgICNDaGVjayBpZiBhbnkgaW52ZW50b3J5IG9uLW9yZGVyIHNob3VsZCBhcnJpdmUKICAgICAgICBpZihhbnkoaW52Lm9ub3JkZXIuc3RhbXA9PXQpKXsKICAgICAgICAgICNVcGRhdGUgdGhlIG51bWJlciBvZiBkZWxpdmVyaWVzCiAgICAgICAgICBkZWxpdmVyeT1kZWxpdmVyeSsxCiAgICAgICAgICAjQ29tcHV0ZSB0aGUgdG90YWwgb2YgYXJyaXZlZCBpbnZlbnRvcmllcwogICAgICAgICAgaW5kZXg9d2hpY2goaW52Lm9ub3JkZXIuc3RhbXA9PXQpCiAgICAgICAgICBhcnJpdmFsPXN1bShpbnYub25vcmRlcltpbmRleF0pCiAgICAgICAgICAjVXBkYXRlIGludmVudG9yeSBvbi1oYW5kIGJlZm9yZSBzdGFydGluZyB0aGUgZGF5CiAgICAgICAgICBpbnYub25oYW5kW3RdPWludi5vbmhhbmRbdF0rYXJyaXZhbAogICAgICAgICAgI1JlbW92ZWQgdGhvc2UganVzdCBhcnJpdmVkIGZyb20gaW52ZW50b3J5IG9uLW9yZGVyCiAgICAgICAgICBpbnYub25vcmRlcj1pbnYub25vcmRlclstaW5kZXhdCiAgICAgICAgICBpbnYub25vcmRlci5zdGFtcD1pbnYub25vcmRlci5zdGFtcFstaW5kZXhdCiAgICAgICAgICAjY2F0KCJhcnJpdmFsIHRpbWU6Iix0KQogICAgICAgIH0gI2VuZCB0PT0xCiAgICAgICAgI1JlY29yZCBzYWxlcwogICAgICAgIHNhbGVzW3RdPW1pbihpbnYub25oYW5kW3RdLGRbdF0pCiAgICAgICAgI0NoZWNrIGlmIGFueSBzdG9ja291dCB0YWtlcyBwbGFjZQogICAgICAgIGlmKGRbdF0+c2FsZXNbdF0pewogICAgICAgICAgc3RvY2tvdXQ9c3RvY2tvdXQrMQogICAgICAgICAgbG9zcz1sb3NzK2RbdF0tc2FsZXNbdF0KICAgICAgICB9CiAgICAgICAgI1VwZGF0ZSBpbnZlbnRvcnkgcG9zaXRpb24KICAgICAgICBpbnYucG9zaXRpb249aW52Lm9uaGFuZFt0XStzdW0oaW52Lm9ub3JkZXIpCiAgICAgICAgI09yZGVyaW5nIG1lY2hhbmlzbSBvZiAociwgcSkgaW52ZW50b3J5IHBvbGljeQogICAgICAgIGlmKGludi5wb3NpdGlvbjw9cil7CiAgICAgICAgICBRPXEtaW52LnBvc2l0aW9uCiAgICAgICAgICBpbnYub25vcmRlcj1jKGludi5vbm9yZGVyLFEpCiAgICAgICAgICB0aW1lLnRvLmFycml2ZT10K3NhbXBsZShjKDQsNSksMSxwcm9iPWMoMC41LDAuNSkpKzEKICAgICAgICAgIGludi5vbm9yZGVyLnN0YW1wPWMoaW52Lm9ub3JkZXIuc3RhbXAsdGltZS50by5hcnJpdmUpCiAgICAgICAgICBvcmRlclFbdF09UQogICAgICAgIH0KICAgICAgICAjVXBkYXRlIGludmVudG9yeSBvbi1oYW5kIGluIHRoZSBlbmQgb2YgdGhlIGRheQogICAgICAgIGludi5vbmhhbmRbdCsxXT1pbnYub25oYW5kW3RdLXNhbGVzW3RdCiAgICAgIH0gI2VuZCB0PjEKICAgICAgI2NhdCgiZGF5OiIsdCwiO3NhbGVzOiIsc2FsZXNbdF0sIjtvbmhhbmQ6IixpbnYub25oYW5kW3RdLCI7b25vcmRlcjoiLAogICAgICAjICAgICBpbnYub25vcmRlciwiO29yZGVyOiIsb3JkZXJRW3RdLCJcbiIpIAogICAgfSAjZW5kIGZvciB0CiAgICAKICAgIFNMW21dPShUX3ZhbHVlLXN0b2Nrb3V0KS9UX3ZhbHVlICAjQ2FsY3VsYXRlIHRoZSBzZXJ2aWNlIHJhdGU7IAogICAgCiAgICAjQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGRhaWx5IHByb2ZpdAogICAgZGFpbHkucHJvZml0W21dPSh1bml0LnByb2ZpdCpzdW0oc2FsZXMpLXN1bShoLnZhbCppbnYub25oYW5kKS0KICAgICAgICAgICAgICAgICAgICAgICBzaGlwcGluZy5jb3N0KmRlbGl2ZXJ5LXVuaXQucHJvZml0Kmxvc3MpL1RfdmFsdWUKICAgIAogIH0gI2VuZCBmb3IgbQogICNjKG1lYW4oU0wpLG1lYW4oZGFpbHkucHJvZml0KSxzZChkYWlseS5wcm9maXQpKQogIG1lYW4oZGFpbHkucHJvZml0KQp9CgpgYGAKCgpgYGB7cn0KbGlicmFyeShwcmFjbWEpCnJlcz1mbWluc2VhcmNoKEludk1vZGVsLGMoMzAsMTMwKSwKICAgICAgICAgbG93ZXI9YygyMCw4MCksIAogICAgICAgICB1cHBlcj1jKDE1MCwyNTApLAogICAgICAgICBtZXRob2Q9YygiSG9va2UtSmVldmVzIiksCiAgICAgICAgIG1pbmltaXplPUZBTFNFKQoKcmVzCmBgYAo=