♫ Bells are ringing, children singing, all is merry and bright. Santa’s elves made a big mistake, now he needs your help tonight ♫
Helping Santa filling his baggs.
Libraries
library(stringr)
library(ggplot2)
library(triangle)
library(data.table)
Data Load
gifts <- read.csv('input/gifts.csv')
#Tomo la priper palabra de cada regalo
gifts$Toy <- factor(str_extract(gifts$GiftId, pattern = "[a-z]+"))
#Genero un gráfico para mostrar la distribución de regalos Santa
ggplot(data= gifts, aes(x= reorder(Toy, -table(Toy)[Toy]) , fill =Toy ))+
geom_bar(stat="count") +
theme_classic() +
xlab("Toy")+
guides(fill = FALSE)

Gift Weight Probability Distribution
We compute the weight distribution for each gift
sample_horse
function(n) pmax(0, rnorm(n=n, mean=5, sd=2))
Let’s visualizate the weights distributions
ggplot(data= gifts, aes (x= Weight, fill=Toy)) +
geom_density() +
facet_wrap(~Toy, ncol= 3, scales="free")+
theme_bw() +
guides(fill=FALSE) +
ggtitle("Density plots of the toy's weight by toy type")

Let’s visualizate a boxplot with weights distributions
ggplot (data= gifts, aes (y= Weight, x=Toy, fill=Toy))+
geom_boxplot()+
guides(fill=FALSE) +
ggtitle("Boxplots of weight distribution by toy type")

Evaluation
As the Evaluation tab tells us “Submissions are evaluated on the total amount of weight you fit into Santa’s 1000 bags”, so we need to maximize the total weight of the bags. The important key here is that our primary objective is to make same carry as much weight as possible, without taking into account the total number of gifts.
We have then, a maximization function for all the gifts of all the bags: \[\sum_{i=1}^{n}\left( \sum_{j=1}^{m}\ Weight_i*X_{i,j} \right)\] Where each \(Weight_i\) represents the Weight of each gift and each \(X_{i,j}\) represents if the gift \(i\) goes into the bag \(j\).
\(i\) goes from gift \(1\) to gift \(n\). \(j\) goes from bag \(1\) to bag \(m\).
Constraint Number 1: -Each bag has to weight less that 50 lb. \[ \sum_{j=1}^{m}\ Weight_i*X_{i,j} \le 50 kg\ \forall i = 1,..,n \] Constraint Number 2: -Each bag has to carry at least 3 items.
\[\sum_{i=1}^{n}\ X_{i,j} \ge 3\ \forall j = 1,..,m \]
Constraint Number 3: An item can be only in one bag.
\[ \sum_{j=1}^{m}\ X_{i,j} \le 1 \ \forall i = 1,..,n \] Contraint Number 4: \(X_{i,j}\) is binary \[X_{i,j} \in\ \{ {0,1} \} \ \forall i = 1,..,n \ j = 1,..,m \]
First Approach: Random path
In this first approach I’ll generate a random list of all the gifts and cut this list into bags when they are about to get 50 kg.
set.seed(2223)
sample_gifts<- data.frame(GiftId=integer(),Toy=character(),Weight=double, Bag=integer())
sampleGiftId<-sample(gifts$GiftId)
sampleToy <- factor(str_extract(sampleGiftId, pattern = "[a-z]+"))
sampleWeight<-sapply(sampleToy,function(x) do.call(paste("sample",x, sep="_"), as.list(1)))
sampleBag<-rep(0,length(sampleToy))
sample_gifts<-data.frame(GiftId=sampleGiftId,Toy=sampleToy,Weight=sampleWeight,Bag=sampleBag)
bag_weight<-NULL
bag_items<-NULL
bag_name<-NULL
bag_name[1]<-1
bag_items[1]<-0;
bagweight=0
j=-1
for (i in 1:length(sample_gifts$Toy))
{
if (sample_gifts$Weight[i]<50)
{
bagweight = bagweight + sample_gifts$Weight[i]
if (bagweight>48) {
j = j + 1
bag_weight[j]<-bagweight - sample_gifts$Weight[i]
bag_items[j]<-0
bag_name[j]<-j
bagweight = sample_gifts$Weight[i]
}
sample_gifts$Bag[i]=j
bag_items[j]<-bag_items[j]+1
}
}
summary(bag_weight)
Min. 1st Qu. Median Mean 3rd Qu. Max.
6.692 38.260 43.260 40.760 45.990 48.000
length(bag_weight)
[1] 1243
summary(bag_name)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.0 311.5 622.0 622.0 932.5 1243.0
We take out bags that do not respect the constraints. (Less than 3)
Now I show the weight distribution per bag.
d<-data.frame(bag_weight,bag_items,bag_name)
f1<-cut(bag_items,3)
levels(f1)<-c("Low","Medium","High")
d$bag_item_q<-f1
ggplot(d, aes(bag_weight, ..count..,)) +
geom_density() +
ggtitle("Count of bags by weight")

ggplot(d, aes(bag_items, ..count..,)) +
geom_density() +
ggtitle("Count of bags by weight")

ggplot(d, aes(bag_weight, ..count..,fill=bag_item_q)) +
geom_density() +
facet_wrap(~bag_item_q, ncol= 3, scales="free")+
ggtitle("Count of bags by weight diviving by number of items in the bag")

Now we choose the 1000 bags with more weight in them.
d1<-d[bag_items>=3,]
mt <- head(d1[order(d1$bag_weight,decreasing=TRUE), ],1000)
summary(mt)
bag_weight bag_items bag_name bag_item_q
Min. :32.90 Min. : 3.000 Min. : 1.0 Low :541
1st Qu.:40.47 1st Qu.: 5.000 1st Qu.: 307.8 Medium:429
Median :44.13 Median : 6.000 Median : 614.5 High : 30
Mean :43.12 Mean : 6.524 Mean : 617.8
3rd Qu.:46.34 3rd Qu.: 8.000 3rd Qu.: 923.2
Max. :48.00 Max. :16.000 Max. :1242.0
Total_Weight<-sum(mt$bag_weight)
Total_Weight
[1] 43122.35
The total Weight for this solutions is 43122 lb.
Exporting the solution
summary(sample_gifts_f)
GiftId Toy Weight Bag
ball_0 : 1 book :1123 Min. : 0.000 Min. : 1.0
ball_1 : 1 ball :1023 1st Qu.: 2.044 1st Qu.: 303.0
ball_10 : 1 doll : 939 Median : 4.564 Median : 608.0
ball_100 : 1 horse : 938 Mean : 6.351 Mean : 615.2
ball_1000: 1 blocks : 930 3rd Qu.: 9.322 3rd Qu.: 924.0
ball_1001: 1 train : 926 Max. :39.386 Max. :1242.0
(Other) :6518 (Other): 645
LS0tDQp0aXRsZTogIlNBTlRBUyBHSUZUUyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQrimasgQmVsbHMgYXJlIHJpbmdpbmcsIGNoaWxkcmVuIHNpbmdpbmcsIGFsbCBpcyBtZXJyeSBhbmQgYnJpZ2h0LiBTYW50YSdzIGVsdmVzIG1hZGUgYSBiaWcgbWlzdGFrZSwgbm93IGhlIG5lZWRzIHlvdXIgaGVscCB0b25pZ2h0IOKZqw0KDQpIZWxwaW5nIFNhbnRhIGZpbGxpbmcgaGlzIGJhZ2dzLg0KDQojTGlicmFyaWVzIw0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRyaWFuZ2xlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KYGBgDQoNCiNEYXRhIExvYWQjIA0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpnaWZ0cyAgIDwtIHJlYWQuY3N2KCdpbnB1dC9naWZ0cy5jc3YnKQ0KI1RvbW8gbGEgcHJpcGVyIHBhbGFicmEgZGUgY2FkYSByZWdhbG8NCmdpZnRzJFRveSA8LSBmYWN0b3Ioc3RyX2V4dHJhY3QoZ2lmdHMkR2lmdElkLCBwYXR0ZXJuID0gIlthLXpdKyIpKSANCg0KI0dlbmVybyB1biBncsOhZmljbyBwYXJhIG1vc3RyYXIgbGEgZGlzdHJpYnVjacOzbiBkZSByZWdhbG9zIFNhbnRhDQpnZ3Bsb3QoZGF0YT0gZ2lmdHMsIGFlcyh4PSByZW9yZGVyKFRveSwgLXRhYmxlKFRveSlbVG95XSkgLCBmaWxsID1Ub3kgKSkrDQogIGdlb21fYmFyKHN0YXQ9ImNvdW50IikgKyANCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgeGxhYigiVG95IikrDQogIGd1aWRlcyhmaWxsID0gRkFMU0UpDQoNCmBgYA0KDQojR2lmdCBXZWlnaHQgUHJvYmFiaWxpdHkgRGlzdHJpYnV0aW9uIw0KIyNXZSBjb21wdXRlIHRoZSB3ZWlnaHQgZGlzdHJpYnV0aW9uIGZvciBlYWNoIGdpZnQjIw0KYGBge3J9DQojIFRha2VuIGZyb20gQmVuIEdvcm1hbidzIGtlcm5lbC4gVGhhbmsgeW91Lg0KIyBodHRwczovL3d3dy5rYWdnbGUuY29tL2JlbjUxOS9zYW50YXMtdW5jZXJ0YWluLWJhZ3MvbWVycnktY2hyaXN0bWFzLXktYWxsDQoNCiNob3JzZSA9IG1heCgwLCBucC5yYW5kb20ubm9ybWFsKDUsMiwxKVswXSkNCnNhbXBsZV9ob3JzZSA8LSBmdW5jdGlvbihuKSBwbWF4KDAsIHJub3JtKG49biwgbWVhbj01LCBzZD0yKSkNCiNiYWxsID0gbWF4KDAsIDEgKyBucC5yYW5kb20ubm9ybWFsKDEsMC4zLDEpWzBdKQ0Kc2FtcGxlX2JhbGwgPC0gZnVuY3Rpb24gKG4pIHBtYXgoMCwgMSArIHJub3JtKG49bixtZWFuPTEsc2Q9MC4zKSkNCiNiaWtlID0gbWF4KDAsIG5wLnJhbmRvbS5ub3JtYWwoMjAsMTAsMSlbMF0pDQpzYW1wbGVfYmlrZSA8LSBmdW5jdGlvbiAobikgcG1heCgwLCBybm9ybShuPW4sbWVhbj0yMCwgc2Q9MTApKQ0KI3RyYWluID0gbWF4KDAsIG5wLnJhbmRvbS5ub3JtYWwoMTAsNSwxKVswXSkNCnNhbXBsZV90cmFpbiA8LSBmdW5jdGlvbiAobikgcG1heCgwLCBybm9ybShuPW4sIG1lYW49MTAsIHNkPTUpKQ0KI2NvYWwgPSA0NyAqIG5wLnJhbmRvbS5iZXRhKDAuNSwwLjUsMSlbMF0NCnNhbXBsZV9jb2FsIDwtIGZ1bmN0aW9uIChuKSA0NyAqIHJiZXRhKG49biwgc2hhcGUxPTAuNSwgc2hhcGUyPTAuNSkNCiNib29rID0gbnAucmFuZG9tLmNoaXNxdWFyZSgyLDEpWzBdDQpzYW1wbGVfYm9vayA8LSBmdW5jdGlvbiAobikgcmNoaXNxKG4gPSBuLCBkZiA9IDIpDQojZG9sbCA9IG5wLnJhbmRvbS5nYW1tYSg1LDEsMSlbMF0NCnNhbXBsZV9kb2xsIDwtIGZ1bmN0aW9uIChuKSByZ2FtbWEobj1uLCBzaGFwZT01LCByYXRlPTEpDQojYmxvY2sgPSBucC5yYW5kb20udHJpYW5ndWxhcig1LDEwLDIwLDEpWzBdDQpzYW1wbGVfYmxvY2tzPC0gZnVuY3Rpb24obikgaWYobiA9PSAwKSByZXR1cm4obnVtZXJpYygwKSkgZWxzZSByZXR1cm4ocnRyaWFuZ2xlKG49biwgYT01LCBjPTEwLCBiPTIwKSkNCiNnbG92ZXMgPSAzLjAgKyBucC5yYW5kb20ucmFuZCgxKVswXSBpZiBucC5yYW5kb20ucmFuZCgxKSA8IDAuMyBlbHNlIG5wLnJhbmRvbS5yYW5kKDEpWzBdDQpzYW1wbGVfZ2xvdmVzIDwtIGZ1bmN0aW9uKG4pIHJ1bmlmKG49biwgbWluPTAsIG1heD0xKSArIChydW5pZihuPW4sIG1pbj0wLCBtYXg9MSkgPCAuMykgKiAzDQoNCnNldC5zZWVkKDEyMzQpDQpnaWZ0cyRXZWlnaHQ8LXNhcHBseShnaWZ0cyRUb3ksZnVuY3Rpb24oeCkgZG8uY2FsbChwYXN0ZSgic2FtcGxlIix4LCBzZXA9Il8iKSwgYXMubGlzdCgxKSkpDQoNCmBgYA0KI0xldCdzIHZpc3VhbGl6YXRlIHRoZSB3ZWlnaHRzIGRpc3RyaWJ1dGlvbnMjDQpgYGB7cn0NCmdncGxvdChkYXRhPSBnaWZ0cywgYWVzICh4PSBXZWlnaHQsIGZpbGw9VG95KSkgKw0KIGdlb21fZGVuc2l0eSgpICsNCiAgZmFjZXRfd3JhcCh+VG95LCBuY29sPSAzLCBzY2FsZXM9ImZyZWUiKSsNCiAgdGhlbWVfYncoKSArIA0KICBndWlkZXMoZmlsbD1GQUxTRSkgKw0KICBnZ3RpdGxlKCJEZW5zaXR5IHBsb3RzIG9mIHRoZSB0b3kncyB3ZWlnaHQgYnkgdG95IHR5cGUiKQ0KYGBgDQoNCiNMZXQncyB2aXN1YWxpemF0ZSBhIGJveHBsb3Qgd2l0aCB3ZWlnaHRzIGRpc3RyaWJ1dGlvbnMjDQpgYGB7cn0NCmdncGxvdCAoZGF0YT0gZ2lmdHMsIGFlcyAoeT0gV2VpZ2h0LCB4PVRveSwgZmlsbD1Ub3kpKSsNCiAgZ2VvbV9ib3hwbG90KCkrDQogICBndWlkZXMoZmlsbD1GQUxTRSkgKw0KICBnZ3RpdGxlKCJCb3hwbG90cyBvZiB3ZWlnaHQgZGlzdHJpYnV0aW9uIGJ5IHRveSB0eXBlIikNCmBgYA0KDQojRXZhbHVhdGlvbiMNCg0KQXMgdGhlIEV2YWx1YXRpb24gdGFiIHRlbGxzIHVzICJTdWJtaXNzaW9ucyBhcmUgZXZhbHVhdGVkIG9uIHRoZSB0b3RhbCBhbW91bnQgb2Ygd2VpZ2h0IHlvdSBmaXQgaW50byBTYW50YSdzIDEwMDAgYmFncyIsIHNvIHdlIG5lZWQgdG8gbWF4aW1pemUgdGhlIHRvdGFsIHdlaWdodCBvZiB0aGUgYmFncy4gVGhlIGltcG9ydGFudCBrZXkgaGVyZSBpcyB0aGF0IG91ciBwcmltYXJ5IG9iamVjdGl2ZSBpcyB0byBtYWtlIHNhbWUgY2FycnkgYXMgbXVjaCB3ZWlnaHQgYXMgcG9zc2libGUsIHdpdGhvdXQgdGFraW5nIGludG8gYWNjb3VudCB0aGUgdG90YWwgbnVtYmVyIG9mIGdpZnRzLg0KDQpXZSBoYXZlIHRoZW4sIGEgbWF4aW1pemF0aW9uIGZ1bmN0aW9uIGZvciBhbGwgdGhlIGdpZnRzIG9mIGFsbCB0aGUgYmFnczoNCiQkXHN1bV97aT0xfV57bn1cbGVmdCggXHN1bV97aj0xfV57bX1cIFdlaWdodF9pKlhfe2ksan0gXHJpZ2h0KSQkDQpXaGVyZSBlYWNoICRXZWlnaHRfaSQgcmVwcmVzZW50cyB0aGUgV2VpZ2h0IG9mIGVhY2ggZ2lmdCBhbmQgZWFjaCAkWF97aSxqfSQgcmVwcmVzZW50cyBpZiB0aGUgZ2lmdCAkaSQgZ29lcyBpbnRvIHRoZSBiYWcgJGokLg0KDQokaSQgZ29lcyBmcm9tIGdpZnQgJDEkIHRvIGdpZnQgJG4kLg0KJGokIGdvZXMgZnJvbSBiYWcgJDEkIHRvIGJhZyAkbSQuDQoNCkNvbnN0cmFpbnQgTnVtYmVyIDE6IC1FYWNoIGJhZyBoYXMgdG8gd2VpZ2h0IGxlc3MgdGhhdCA1MCBsYi4NCiQkIFxzdW1fe2o9MX1ee219XCBXZWlnaHRfaSpYX3tpLGp9IFxsZSA1MCBrZ1wgIFxmb3JhbGwgaSA9IDEsLi4sbiAkJA0KQ29uc3RyYWludCBOdW1iZXIgMjogLUVhY2ggYmFnIGhhcyB0byBjYXJyeSBhdCBsZWFzdCAzIGl0ZW1zLg0KDQokJFxzdW1fe2k9MX1ee259XCBYX3tpLGp9IFxnZSAzXCAgICBcZm9yYWxsIGogPSAxLC4uLG0gJCQNCg0KQ29uc3RyYWludCBOdW1iZXIgMzogQW4gaXRlbSBjYW4gYmUgb25seSBpbiBvbmUgYmFnLg0KDQokJCBcc3VtX3tqPTF9XnttfVwgWF97aSxqfSBcbGUgMSBcICBcZm9yYWxsIGkgPSAxLC4uLG4gJCQNCkNvbnRyYWludCBOdW1iZXIgNDogJFhfe2ksan0kIGlzIGJpbmFyeSANCiQkWF97aSxqfSBcaW5cIFx7IHswLDF9IFx9IFwgXGZvcmFsbCBpID0gMSwuLixuIFwgaiA9IDEsLi4sbSAkJCANCg0KI0ZpcnN0IEFwcHJvYWNoOiBSYW5kb20gcGF0aCMNCg0KSW4gdGhpcyBmaXJzdCBhcHByb2FjaCBJJ2xsIGdlbmVyYXRlIGEgcmFuZG9tIGxpc3Qgb2YgYWxsIHRoZSBnaWZ0cyBhbmQgY3V0IHRoaXMgbGlzdCBpbnRvIGJhZ3Mgd2hlbiB0aGV5IGFyZSBhYm91dCB0byBnZXQgNTAga2cuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjIyMykNCnNhbXBsZV9naWZ0czwtIGRhdGEuZnJhbWUoR2lmdElkPWludGVnZXIoKSxUb3k9Y2hhcmFjdGVyKCksV2VpZ2h0PWRvdWJsZSwgQmFnPWludGVnZXIoKSkNCnNhbXBsZUdpZnRJZDwtc2FtcGxlKGdpZnRzJEdpZnRJZCkNCnNhbXBsZVRveSA8LSBmYWN0b3Ioc3RyX2V4dHJhY3Qoc2FtcGxlR2lmdElkLCBwYXR0ZXJuID0gIlthLXpdKyIpKSANCnNhbXBsZVdlaWdodDwtc2FwcGx5KHNhbXBsZVRveSxmdW5jdGlvbih4KSBkby5jYWxsKHBhc3RlKCJzYW1wbGUiLHgsIHNlcD0iXyIpLCBhcy5saXN0KDEpKSkNCnNhbXBsZUJhZzwtcmVwKDAsbGVuZ3RoKHNhbXBsZVRveSkpDQoNCnNhbXBsZV9naWZ0czwtZGF0YS5mcmFtZShHaWZ0SWQ9c2FtcGxlR2lmdElkLFRveT1zYW1wbGVUb3ksV2VpZ2h0PXNhbXBsZVdlaWdodCxCYWc9c2FtcGxlQmFnKQ0KYmFnX3dlaWdodDwtTlVMTA0KYmFnX2l0ZW1zPC1OVUxMDQpiYWdfbmFtZTwtTlVMTA0KYmFnX25hbWVbMV08LTENCmJhZ19pdGVtc1sxXTwtMDsNCmJhZ3dlaWdodD0wDQpqPS0xDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzYW1wbGVfZ2lmdHMkVG95KSkNCnsNCiAgaWYgKHNhbXBsZV9naWZ0cyRXZWlnaHRbaV08NTApDQogIHsNCiAgICBiYWd3ZWlnaHQgPSBiYWd3ZWlnaHQgKyBzYW1wbGVfZ2lmdHMkV2VpZ2h0W2ldDQogICAgaWYgKGJhZ3dlaWdodD40OCkgew0KICAgICAgDQogICAgICBqID0gaiArIDENCiAgICAgIGJhZ193ZWlnaHRbal08LWJhZ3dlaWdodCAtIHNhbXBsZV9naWZ0cyRXZWlnaHRbaV0NCiAgICAgIGJhZ19pdGVtc1tqXTwtMA0KICAgICAgYmFnX25hbWVbal08LWoNCiAgICAgIGJhZ3dlaWdodCA9IHNhbXBsZV9naWZ0cyRXZWlnaHRbaV0NCiAgICB9DQogICBzYW1wbGVfZ2lmdHMkQmFnW2ldPWoNCiAgIGJhZ19pdGVtc1tqXTwtYmFnX2l0ZW1zW2pdKzENCiAgfQ0KfQ0KDQoNCnN1bW1hcnkoYmFnX3dlaWdodCkNCmxlbmd0aChiYWdfd2VpZ2h0KQ0Kc3VtbWFyeShiYWdfbmFtZSkNCiAgICAgDQpgYGANCldlIHRha2Ugb3V0IGJhZ3MgdGhhdCBkbyBub3QgcmVzcGVjdCB0aGUgY29uc3RyYWludHMuIChMZXNzIHRoYW4gMykNCg0KDQpOb3cgSSBzaG93IHRoZSB3ZWlnaHQgZGlzdHJpYnV0aW9uIHBlciBiYWcuDQoNCmBgYHtyfQ0KZDwtZGF0YS5mcmFtZShiYWdfd2VpZ2h0LGJhZ19pdGVtcyxiYWdfbmFtZSkNCmYxPC1jdXQoYmFnX2l0ZW1zLDMpDQpsZXZlbHMoZjEpPC1jKCJMb3ciLCJNZWRpdW0iLCJIaWdoIikNCg0KZCRiYWdfaXRlbV9xPC1mMQ0KDQogIGdncGxvdChkLCBhZXMoYmFnX3dlaWdodCwgLi5jb3VudC4uLCkpICsNCiAgZ2VvbV9kZW5zaXR5KCkgKw0KICBnZ3RpdGxlKCJDb3VudCBvZiBiYWdzIGJ5IHdlaWdodCIpDQogICAgDQogIGdncGxvdChkLCBhZXMoYmFnX2l0ZW1zLCAuLmNvdW50Li4sKSkgKw0KICBnZW9tX2RlbnNpdHkoKSArDQogIGdndGl0bGUoIkNvdW50IG9mIGJhZ3MgYnkgd2VpZ2h0IikNCiAgICANCiAgIGdncGxvdChkLCBhZXMoYmFnX3dlaWdodCwgLi5jb3VudC4uLGZpbGw9YmFnX2l0ZW1fcSkpICsNCiAgZ2VvbV9kZW5zaXR5KCkgKw0KICBmYWNldF93cmFwKH5iYWdfaXRlbV9xLCBuY29sPSAzLCBzY2FsZXM9ImZyZWUiKSsNCiAgZ2d0aXRsZSgiQ291bnQgb2YgYmFncyBieSB3ZWlnaHQgZGl2aXZpbmcgYnkgbnVtYmVyIG9mIGl0ZW1zIGluIHRoZSBiYWciKQ0KDQoNCmBgYA0KDQoNCg0KTm93IHdlIGNob29zZSB0aGUgMTAwMCBiYWdzIHdpdGggbW9yZSB3ZWlnaHQgaW4gdGhlbS4NCmBgYHtyfQ0KZDE8LWRbYmFnX2l0ZW1zPj0zLF0NCm10IDwtIGhlYWQoZDFbb3JkZXIoZDEkYmFnX3dlaWdodCxkZWNyZWFzaW5nPVRSVUUpLCBdLDEwMDApDQpzdW1tYXJ5KG10KQ0KVG90YWxfV2VpZ2h0PC1zdW0obXQkYmFnX3dlaWdodCkNClRvdGFsX1dlaWdodA0KDQoNCg0KYGBgDQpUaGUgdG90YWwgV2VpZ2h0IGZvciB0aGlzIHNvbHV0aW9ucyBpcyBgciBmb3JtYXQoVG90YWxfV2VpZ2h0LGRpZ2l0cz0xKWAgIGxiLg0KDQojIEV4cG9ydGluZyB0aGUgc29sdXRpb24NCmBgYHtyfQ0KI0kgbmVlZCB0byBjaGVjayBpbiB0aGUgb3JpZ2luYWwgZ2lmdCB3aGljaCBnaWZ0cyBhcmUgaW4gdGhlIGJhZ3MgdGhhdCAgZXhpc3QgaW4gdGhlIHNvbHV0aW9uIA0KIyBhbmQgIGZvciBlYWggYmFnIGdlbmVyYXRlIGEgbGluZSB3aXRoIHRoZSBnaWZ0cw0KDQptdDwtbXRbb3JkZXIobXQkYmFnX25hbWUsZGVjcmVhc2luZz1GQUxTRSksXQ0Kc2FtcGxlX2dpZnRzMjwtc2FtcGxlX2dpZnRzW29yZGVyKHNhbXBsZV9naWZ0cyRCYWcsZGVjcmVhc2luZz1GQUxTRSksXQ0Kc2FtcGxlX2dpZnRzX2Y8LXNhbXBsZV9naWZ0c1tzYW1wbGVfZ2lmdHMkQmFnICVpbiUgbXQkYmFnX25hbWUsXQ0Kc2FtcGxlX2dpZnRzX2Y8LWRhdGEuZnJhbWUoc2FtcGxlX2dpZnRzX2YpDQpzdW1tYXJ5KHNhbXBsZV9naWZ0c19mKQ0KDQp0b3RfcmVzdWx0IDwtIGFnZ3JlZ2F0ZShHaWZ0SWR+QmFnLCBkYXRhID0gc2FtcGxlX2dpZnRzX2YsIHBhc3RlLCBjb2xsYXBzZSA9ICIgIikNCnJlc3VsdDwtbGlzdCh0b3RfcmVzdWx0JEdpZnRJZCkgDQpmd3JpdGUocmVzdWx0LCBmaWxlID0icmVzdWx0LmNzdiIpDQoNCmBgYA0KDQoNCg0KDQo=