♫ 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=