Setup

library(extraDistr)

Ignoring basics:

(Note: this is overly complex and could have been done with just a single dhyper, but I didn’t realize at the time and wanted to reuse this code for the more complex cases)

handProbs <- data.frame(matrix(0, nrow=59, ncol=8)) # stores the probability that a specific number of copies of the target card are in the starting hand
prizeProbs <- data.frame(matrix(0, nrow=59, ncol=8)) # stores the probability that the a specific number of target cards are in the prize cards
colnames(prizeProbs) <- paste(0:6, "Prized")
colnames(prizeProbs)[8] <- "Expected Number Prized" 

row.names(prizeProbs) <- paste(1:59, "Copy(s) of Card Total")

colnames(handProbs) <- paste(0:7, "In Hand")
row.names(handProbs) <- paste(1:59, "Copy(s) of Card Total")

for(n in 1:59){ # n is the number of copies of the target card in deck, which must be between 1 and 59
  indivProb <- dhyper(0:(min(n,7)),n,60-n,7) # probabilities that 0 to 7 of the target card are in the starting hand
  for(i in max(n-52,1):min(length(indivProb),8)){ # for every non zero probability of copies of target in hand
    curHandProb <- as.numeric(indivProb[i]) # probability that i copies of the target card are in the starting hand
    handProbs[n,i] <- curHandProb
    curPrizeProb <- dhyper(0:min(n,6),n-i+1,52+i-n,6) # probabilities that 0 to 6 of the target card are in the prize cards (i is 1 when 0 are prized, so it needs to be adjusted by 1)
    for(j in 1:length(curPrizeProb)){ # check every probability that 0 to 6 of the target are prized
      prizeProbs[n,j]<-prizeProbs[n,j]+curHandProb*curPrizeProb[j] # add the probability that that number of target cards are prized AND i target cards are in hand to the probability of having j prized
    }
  }
  for(i in 1:6){
    prizeProbs[n,8] <- prizeProbs[n,8] + prizeProbs[n, (i+1)]*i # calculate expected value of number prized
  }
  
}
write.csv(prizeProbs,file="output/ignoreBasics/prizeProbabilitiesNoBasics.csv")
write.csv(handProbs,file="output/ignoreBasics/handProbabilitiesNoBasics.csv")

The Efficient Method

prizeProbs <- data.frame(matrix(0, nrow=59, ncol=7)) # stores the probability that the a specific number of target cards are in the prize cards
colnames(prizeProbs) <- paste(0:6, "Prized")

row.names(prizeProbs) <- paste(1:59, "Copy(s) of Card Total")

for(n in 1:59){ # n is the number of copies of the target card in deck, which must be between 1 and 59
  prizeProbs[n, ] <- dhyper(0:6,n,60-n,6) # probabilities that 0 to 6 of the target card are in the prize cards
}

Taking basics into account:

basicsInHandProbs <- matrix(0, nrow=59, ncol=9)
basicsInHandProbs <- data.frame(basicsInHandProbs)
colnames(basicsInHandProbs) <- paste(1:7, "Basic(s) In Hand")
colnames(basicsInHandProbs)[8] <-"Expected number of hands seen"
colnames(basicsInHandProbs)[9] <-"Expected number of mulligans"
row.names(basicsInHandProbs) <- paste(1:59, "Basic(s) In Deck")
mat <- matrix(0, nrow=28, ncol=3) # needed for the multivariate hypergeometric distribution 
count <- 0
for(b in 1:7){
  for(t in 0:(7-b)){
    count<- count+1
    mat[count,] <- c(7-b-t, b, t) # adds every combination of basic, target, and other cards that adds up to 7 and has at least one basic to the matrix of quantiles for the distribution later
  }
}
for(b in 1:59){ # b is the number of basics in deck
  prizeProbs <- data.frame(matrix(0, nrow=60-b, ncol=7)) # needs to be remade for every number of basics
  handProbs <- data.frame(matrix(0, nrow=60-b, ncol=8)) # stores the probability that a specific number of copies of the target card are in the starting hand
  
  colnames(prizeProbs) <- paste(0:6, "Prized")
  row.names(prizeProbs) <- paste(1:(60-b), "Copy(s) of Card Total")
  
  colnames(handProbs) <- paste(0:7, "In Hand")
  row.names(handProbs) <- paste(1:(60-b), "Copy(s) of Card Total")
  for(n in 1:(60-b)){ # n is the number of copies of the target card in the deck
    curHandSuccessProb<-1-dhyper(0,b,60-b,7)
    handCombos<-dmvhyper(mat, c(60-b-n, b, n), 7)/(curHandSuccessProb) # p(having 1...7 basics and 0...6 targets in starting hand)/(1-p(not having basic in starting hand))
    basicsInHandProbs[b, 1] <- sum(handCombos[1:7])
    basicsInHandProbs[b, 2] <- sum(handCombos[8:13])
    basicsInHandProbs[b, 3] <- sum(handCombos[14:18])
    basicsInHandProbs[b, 4] <- sum(handCombos[19:22])
    basicsInHandProbs[b, 5] <- sum(handCombos[23:25])
    basicsInHandProbs[b, 6] <- sum(handCombos[26:27])
    basicsInHandProbs[b, 7] <- sum(handCombos[28])
    expectedHands <- 1/(curHandSuccessProb) # expected value of a geometric distribution is 1/p, so 1/(1-P(mulligan))
    basicsInHandProbs[b, 8]<- expectedHands
    basicsInHandProbs[b, 9]<- expectedHands-1 # the starting hand itself is one hand, so expected number of mulligans is one fewer than the expected number of hands seen
    for(c in 1:length(mat[,1])){
      if(handCombos[c]!=0){
        h <- mat[c,3] # number of the target card in hand
        handProbs[n,h+1]<- handProbs[n,h+1]+handCombos[c] # adds to the probability of having h copies of the card in the starting hand
        curPrizeProb <- dhyper(0:6,max(0,n-h),min(53+h-n, 53),6) # actual probabilities for having 0...6 copies of the card prized given that h copies are in hand and n total
        
        for(j in 1:length(curPrizeProb)){ 
          prizeProbs[n,j]<-prizeProbs[n,j]+curPrizeProb[j]*handCombos[c] # add the probability of having j copies prized given that we got this hand arrangement AND getting the hand arrangement of mat[c,] to the total probability of having j copies prized
        }
        
      }
    }
    
  }
  write.csv(handProbs,file=paste0("output/notBasic/handProbabilities_", b,"Basics.csv"))
  write.csv(prizeProbs,file=paste0("output/notBasic/prizeProbabilities_", b,"Basics.csv"))
}
write.csv(basicsInHandProbs,file=paste0("output/basicsInHand.csv"))

If target is a basic:



mat <- matrix(0, nrow=36, ncol=3) # needed for the multivariate hypergeometric distribution 
count <- 0
for(b in 0:7){ # since the target card is a basic now, we can also have 0 basics but one target
  for(t in max(0, 1-b):(7-b)){
    count<- count+1
    mat[count,] <- c(7-b-t, b, t) 
  }
}
for(b in 0:59){ # b is the number of other basics in deck
  prizeProbs <- data.frame(matrix(0, nrow=min(4,60-b), ncol=7)) # needs to be remade for every number of basics
  handProbs <- data.frame(matrix(0, nrow=min(4,60-b), ncol=8)) # stores the probability that a specific number of copies of the target card are in the starting hand
  
  colnames(prizeProbs) <- paste(0:6, "Prized")
  row.names(prizeProbs) <- paste(1:min(4,60-b), "Copy(s) of Card Total")
  
  colnames(handProbs) <- paste(0:7, "In Hand")
  row.names(handProbs) <- paste(1:min(4,60-b), "Copy(s) of Card Total")
  for(n in 1:min(4,60-b)){ # n is the number of copies of the target card in the deck
    curHandSuccessProb<-1-dhyper(0,b+n,60-b-n,7)
    handCombos<-dmvhyper(mat, c(60-b-n, b, n), 7)/(curHandSuccessProb) # p(having 1...7 basics and 0...7 targets in starting hand)/(1-p(not having basic in starting hand))
    
    for(c in 1:length(mat[,1])){
      if(handCombos[c]!=0){
        h <- mat[c,3] # number of the target card in hand
        handProbs[n,h+1]<- handProbs[n,h+1]+handCombos[c] # adds to the probability of having h copies of the card in the starting hand
        curPrizeProb <- dhyper(0:6,max(0,n-h),min(53+h-n, 53),6) # actual probabilities for having 0...6 copies of the card prized given that h copies are in hand and there are n total
        
        for(j in 1:length(curPrizeProb)){ 
          prizeProbs[n,j]<-prizeProbs[n,j]+curPrizeProb[j]*handCombos[c] # add the probability of having j copies prized given that we got this hand arrangement AND getting the hand arrangement of mat[c,] to the total probability of having j copies prized
        }
        
      }
    }
    
  }
  write.csv(handProbs,file=paste0("output/isBasic/handProbabilitiesB_", b,"Basics.csv"))
  write.csv(prizeProbs,file=paste0("output/isBasic/prizeProbabilitiesB_", b,"Basics.csv"))
}
LS0tDQp0aXRsZTogIlByaXplIFByb2JhYmlsaXRpZXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogImxhc3RsZWd1bWUiDQotLS0NCiMgU2V0dXANCg0KYGBge3J9DQpsaWJyYXJ5KGV4dHJhRGlzdHIpDQpgYGANCg0KIyBJZ25vcmluZyBiYXNpY3M6ICAgDQooTm90ZTogdGhpcyBpcyBvdmVybHkgY29tcGxleCBhbmQgY291bGQgaGF2ZSBiZWVuIGRvbmUgd2l0aCBqdXN0IGEgc2luZ2xlIGRoeXBlciwgYnV0IEkgZGlkbid0IHJlYWxpemUgYXQgdGhlIHRpbWUgYW5kIHdhbnRlZCB0byByZXVzZSB0aGlzIGNvZGUgZm9yIHRoZSBtb3JlIGNvbXBsZXggY2FzZXMpDQoNCmBgYHtyfQ0KaGFuZFByb2JzIDwtIGRhdGEuZnJhbWUobWF0cml4KDAsIG5yb3c9NTksIG5jb2w9OCkpICMgc3RvcmVzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgc3BlY2lmaWMgbnVtYmVyIG9mIGNvcGllcyBvZiB0aGUgdGFyZ2V0IGNhcmQgYXJlIGluIHRoZSBzdGFydGluZyBoYW5kDQpwcml6ZVByb2JzIDwtIGRhdGEuZnJhbWUobWF0cml4KDAsIG5yb3c9NTksIG5jb2w9OCkpICMgc3RvcmVzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBhIHNwZWNpZmljIG51bWJlciBvZiB0YXJnZXQgY2FyZHMgYXJlIGluIHRoZSBwcml6ZSBjYXJkcw0KY29sbmFtZXMocHJpemVQcm9icykgPC0gcGFzdGUoMDo2LCAiUHJpemVkIikNCmNvbG5hbWVzKHByaXplUHJvYnMpWzhdIDwtICJFeHBlY3RlZCBOdW1iZXIgUHJpemVkIiANCg0Kcm93Lm5hbWVzKHByaXplUHJvYnMpIDwtIHBhc3RlKDE6NTksICJDb3B5KHMpIG9mIENhcmQgVG90YWwiKQ0KDQpjb2xuYW1lcyhoYW5kUHJvYnMpIDwtIHBhc3RlKDA6NywgIkluIEhhbmQiKQ0Kcm93Lm5hbWVzKGhhbmRQcm9icykgPC0gcGFzdGUoMTo1OSwgIkNvcHkocykgb2YgQ2FyZCBUb3RhbCIpDQoNCmZvcihuIGluIDE6NTkpeyAjIG4gaXMgdGhlIG51bWJlciBvZiBjb3BpZXMgb2YgdGhlIHRhcmdldCBjYXJkIGluIGRlY2ssIHdoaWNoIG11c3QgYmUgYmV0d2VlbiAxIGFuZCA1OQ0KICBpbmRpdlByb2IgPC0gZGh5cGVyKDA6KG1pbihuLDcpKSxuLDYwLW4sNykgIyBwcm9iYWJpbGl0aWVzIHRoYXQgMCB0byA3IG9mIHRoZSB0YXJnZXQgY2FyZCBhcmUgaW4gdGhlIHN0YXJ0aW5nIGhhbmQNCiAgZm9yKGkgaW4gbWF4KG4tNTIsMSk6bWluKGxlbmd0aChpbmRpdlByb2IpLDgpKXsgIyBmb3IgZXZlcnkgbm9uIHplcm8gcHJvYmFiaWxpdHkgb2YgY29waWVzIG9mIHRhcmdldCBpbiBoYW5kDQogICAgY3VySGFuZFByb2IgPC0gYXMubnVtZXJpYyhpbmRpdlByb2JbaV0pICMgcHJvYmFiaWxpdHkgdGhhdCBpIGNvcGllcyBvZiB0aGUgdGFyZ2V0IGNhcmQgYXJlIGluIHRoZSBzdGFydGluZyBoYW5kDQogICAgaGFuZFByb2JzW24saV0gPC0gY3VySGFuZFByb2INCiAgICBjdXJQcml6ZVByb2IgPC0gZGh5cGVyKDA6bWluKG4sNiksbi1pKzEsNTIraS1uLDYpICMgcHJvYmFiaWxpdGllcyB0aGF0IDAgdG8gNiBvZiB0aGUgdGFyZ2V0IGNhcmQgYXJlIGluIHRoZSBwcml6ZSBjYXJkcyAoaSBpcyAxIHdoZW4gMCBhcmUgcHJpemVkLCBzbyBpdCBuZWVkcyB0byBiZSBhZGp1c3RlZCBieSAxKQ0KICAgIGZvcihqIGluIDE6bGVuZ3RoKGN1clByaXplUHJvYikpeyAjIGNoZWNrIGV2ZXJ5IHByb2JhYmlsaXR5IHRoYXQgMCB0byA2IG9mIHRoZSB0YXJnZXQgYXJlIHByaXplZA0KICAgICAgcHJpemVQcm9ic1tuLGpdPC1wcml6ZVByb2JzW24sal0rY3VySGFuZFByb2IqY3VyUHJpemVQcm9iW2pdICMgYWRkIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoYXQgbnVtYmVyIG9mIHRhcmdldCBjYXJkcyBhcmUgcHJpemVkIEFORCBpIHRhcmdldCBjYXJkcyBhcmUgaW4gaGFuZCB0byB0aGUgcHJvYmFiaWxpdHkgb2YgaGF2aW5nIGogcHJpemVkDQogICAgfQ0KICB9DQogIGZvcihpIGluIDE6Nil7DQogICAgcHJpemVQcm9ic1tuLDhdIDwtIHByaXplUHJvYnNbbiw4XSArIHByaXplUHJvYnNbbiwgKGkrMSldKmkgIyBjYWxjdWxhdGUgZXhwZWN0ZWQgdmFsdWUgb2YgbnVtYmVyIHByaXplZA0KICB9DQogIA0KfQ0Kd3JpdGUuY3N2KHByaXplUHJvYnMsZmlsZT0ib3V0cHV0L2lnbm9yZUJhc2ljcy9wcml6ZVByb2JhYmlsaXRpZXNOb0Jhc2ljcy5jc3YiKQ0Kd3JpdGUuY3N2KGhhbmRQcm9icyxmaWxlPSJvdXRwdXQvaWdub3JlQmFzaWNzL2hhbmRQcm9iYWJpbGl0aWVzTm9CYXNpY3MuY3N2IikNCg0KDQpgYGANCg0KIyMgVGhlIEVmZmljaWVudCBNZXRob2QNCg0KYGBge3J9DQpwcml6ZVByb2JzIDwtIGRhdGEuZnJhbWUobWF0cml4KDAsIG5yb3c9NTksIG5jb2w9NykpICMgc3RvcmVzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBhIHNwZWNpZmljIG51bWJlciBvZiB0YXJnZXQgY2FyZHMgYXJlIGluIHRoZSBwcml6ZSBjYXJkcw0KY29sbmFtZXMocHJpemVQcm9icykgPC0gcGFzdGUoMDo2LCAiUHJpemVkIikNCg0Kcm93Lm5hbWVzKHByaXplUHJvYnMpIDwtIHBhc3RlKDE6NTksICJDb3B5KHMpIG9mIENhcmQgVG90YWwiKQ0KDQpmb3IobiBpbiAxOjU5KXsgIyBuIGlzIHRoZSBudW1iZXIgb2YgY29waWVzIG9mIHRoZSB0YXJnZXQgY2FyZCBpbiBkZWNrLCB3aGljaCBtdXN0IGJlIGJldHdlZW4gMSBhbmQgNTkNCiAgcHJpemVQcm9ic1tuLCBdIDwtIGRoeXBlcigwOjYsbiw2MC1uLDYpICMgcHJvYmFiaWxpdGllcyB0aGF0IDAgdG8gNiBvZiB0aGUgdGFyZ2V0IGNhcmQgYXJlIGluIHRoZSBwcml6ZSBjYXJkcw0KfQ0KYGBgDQoNCiMgVGFraW5nIGJhc2ljcyBpbnRvIGFjY291bnQ6DQoNCmBgYHtyfQ0KYmFzaWNzSW5IYW5kUHJvYnMgPC0gbWF0cml4KDAsIG5yb3c9NTksIG5jb2w9OSkNCmJhc2ljc0luSGFuZFByb2JzIDwtIGRhdGEuZnJhbWUoYmFzaWNzSW5IYW5kUHJvYnMpDQpjb2xuYW1lcyhiYXNpY3NJbkhhbmRQcm9icykgPC0gcGFzdGUoMTo3LCAiQmFzaWMocykgSW4gSGFuZCIpDQpjb2xuYW1lcyhiYXNpY3NJbkhhbmRQcm9icylbOF0gPC0iRXhwZWN0ZWQgbnVtYmVyIG9mIGhhbmRzIHNlZW4iDQpjb2xuYW1lcyhiYXNpY3NJbkhhbmRQcm9icylbOV0gPC0iRXhwZWN0ZWQgbnVtYmVyIG9mIG11bGxpZ2FucyINCnJvdy5uYW1lcyhiYXNpY3NJbkhhbmRQcm9icykgPC0gcGFzdGUoMTo1OSwgIkJhc2ljKHMpIEluIERlY2siKQ0KbWF0IDwtIG1hdHJpeCgwLCBucm93PTI4LCBuY29sPTMpICMgbmVlZGVkIGZvciB0aGUgbXVsdGl2YXJpYXRlIGh5cGVyZ2VvbWV0cmljIGRpc3RyaWJ1dGlvbiANCmNvdW50IDwtIDANCmZvcihiIGluIDE6Nyl7DQogIGZvcih0IGluIDA6KDctYikpew0KICAgIGNvdW50PC0gY291bnQrMQ0KICAgIG1hdFtjb3VudCxdIDwtIGMoNy1iLXQsIGIsIHQpICMgYWRkcyBldmVyeSBjb21iaW5hdGlvbiBvZiBiYXNpYywgdGFyZ2V0LCBhbmQgb3RoZXIgY2FyZHMgdGhhdCBhZGRzIHVwIHRvIDcgYW5kIGhhcyBhdCBsZWFzdCBvbmUgYmFzaWMgdG8gdGhlIG1hdHJpeCBvZiBxdWFudGlsZXMgZm9yIHRoZSBkaXN0cmlidXRpb24gbGF0ZXINCiAgfQ0KfQ0KZm9yKGIgaW4gMTo1OSl7ICMgYiBpcyB0aGUgbnVtYmVyIG9mIGJhc2ljcyBpbiBkZWNrDQogIHByaXplUHJvYnMgPC0gZGF0YS5mcmFtZShtYXRyaXgoMCwgbnJvdz02MC1iLCBuY29sPTcpKSAjIG5lZWRzIHRvIGJlIHJlbWFkZSBmb3IgZXZlcnkgbnVtYmVyIG9mIGJhc2ljcw0KICBoYW5kUHJvYnMgPC0gZGF0YS5mcmFtZShtYXRyaXgoMCwgbnJvdz02MC1iLCBuY29sPTgpKSAjIHN0b3JlcyB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIHNwZWNpZmljIG51bWJlciBvZiBjb3BpZXMgb2YgdGhlIHRhcmdldCBjYXJkIGFyZSBpbiB0aGUgc3RhcnRpbmcgaGFuZA0KICANCiAgY29sbmFtZXMocHJpemVQcm9icykgPC0gcGFzdGUoMDo2LCAiUHJpemVkIikNCiAgcm93Lm5hbWVzKHByaXplUHJvYnMpIDwtIHBhc3RlKDE6KDYwLWIpLCAiQ29weShzKSBvZiBDYXJkIFRvdGFsIikNCiAgDQogIGNvbG5hbWVzKGhhbmRQcm9icykgPC0gcGFzdGUoMDo3LCAiSW4gSGFuZCIpDQogIHJvdy5uYW1lcyhoYW5kUHJvYnMpIDwtIHBhc3RlKDE6KDYwLWIpLCAiQ29weShzKSBvZiBDYXJkIFRvdGFsIikNCiAgZm9yKG4gaW4gMTooNjAtYikpeyAjIG4gaXMgdGhlIG51bWJlciBvZiBjb3BpZXMgb2YgdGhlIHRhcmdldCBjYXJkIGluIHRoZSBkZWNrDQogICAgY3VySGFuZFN1Y2Nlc3NQcm9iPC0xLWRoeXBlcigwLGIsNjAtYiw3KQ0KICAgIGhhbmRDb21ib3M8LWRtdmh5cGVyKG1hdCwgYyg2MC1iLW4sIGIsIG4pLCA3KS8oY3VySGFuZFN1Y2Nlc3NQcm9iKSAjIHAoaGF2aW5nIDEuLi43IGJhc2ljcyBhbmQgMC4uLjYgdGFyZ2V0cyBpbiBzdGFydGluZyBoYW5kKS8oMS1wKG5vdCBoYXZpbmcgYmFzaWMgaW4gc3RhcnRpbmcgaGFuZCkpDQogICAgYmFzaWNzSW5IYW5kUHJvYnNbYiwgMV0gPC0gc3VtKGhhbmRDb21ib3NbMTo3XSkNCiAgICBiYXNpY3NJbkhhbmRQcm9ic1tiLCAyXSA8LSBzdW0oaGFuZENvbWJvc1s4OjEzXSkNCiAgICBiYXNpY3NJbkhhbmRQcm9ic1tiLCAzXSA8LSBzdW0oaGFuZENvbWJvc1sxNDoxOF0pDQogICAgYmFzaWNzSW5IYW5kUHJvYnNbYiwgNF0gPC0gc3VtKGhhbmRDb21ib3NbMTk6MjJdKQ0KICAgIGJhc2ljc0luSGFuZFByb2JzW2IsIDVdIDwtIHN1bShoYW5kQ29tYm9zWzIzOjI1XSkNCiAgICBiYXNpY3NJbkhhbmRQcm9ic1tiLCA2XSA8LSBzdW0oaGFuZENvbWJvc1syNjoyN10pDQogICAgYmFzaWNzSW5IYW5kUHJvYnNbYiwgN10gPC0gc3VtKGhhbmRDb21ib3NbMjhdKQ0KICAgIGV4cGVjdGVkSGFuZHMgPC0gMS8oY3VySGFuZFN1Y2Nlc3NQcm9iKSAjIGV4cGVjdGVkIHZhbHVlIG9mIGEgZ2VvbWV0cmljIGRpc3RyaWJ1dGlvbiBpcyAxL3AsIHNvIDEvKDEtUChtdWxsaWdhbikpDQogICAgYmFzaWNzSW5IYW5kUHJvYnNbYiwgOF08LSBleHBlY3RlZEhhbmRzDQogICAgYmFzaWNzSW5IYW5kUHJvYnNbYiwgOV08LSBleHBlY3RlZEhhbmRzLTEgIyB0aGUgc3RhcnRpbmcgaGFuZCBpdHNlbGYgaXMgb25lIGhhbmQsIHNvIGV4cGVjdGVkIG51bWJlciBvZiBtdWxsaWdhbnMgaXMgb25lIGZld2VyIHRoYW4gdGhlIGV4cGVjdGVkIG51bWJlciBvZiBoYW5kcyBzZWVuDQogICAgZm9yKGMgaW4gMTpsZW5ndGgobWF0WywxXSkpew0KICAgICAgaWYoaGFuZENvbWJvc1tjXSE9MCl7DQogICAgICAgIGggPC0gbWF0W2MsM10gIyBudW1iZXIgb2YgdGhlIHRhcmdldCBjYXJkIGluIGhhbmQNCiAgICAgICAgaGFuZFByb2JzW24saCsxXTwtIGhhbmRQcm9ic1tuLGgrMV0raGFuZENvbWJvc1tjXSAjIGFkZHMgdG8gdGhlIHByb2JhYmlsaXR5IG9mIGhhdmluZyBoIGNvcGllcyBvZiB0aGUgY2FyZCBpbiB0aGUgc3RhcnRpbmcgaGFuZA0KICAgICAgICBjdXJQcml6ZVByb2IgPC0gZGh5cGVyKDA6NixtYXgoMCxuLWgpLG1pbig1MytoLW4sIDUzKSw2KSAjIGFjdHVhbCBwcm9iYWJpbGl0aWVzIGZvciBoYXZpbmcgMC4uLjYgY29waWVzIG9mIHRoZSBjYXJkIHByaXplZCBnaXZlbiB0aGF0IGggY29waWVzIGFyZSBpbiBoYW5kIGFuZCBuIHRvdGFsDQogICAgICAgIA0KICAgICAgICBmb3IoaiBpbiAxOmxlbmd0aChjdXJQcml6ZVByb2IpKXsgDQogICAgICAgICAgcHJpemVQcm9ic1tuLGpdPC1wcml6ZVByb2JzW24sal0rY3VyUHJpemVQcm9iW2pdKmhhbmRDb21ib3NbY10gIyBhZGQgdGhlIHByb2JhYmlsaXR5IG9mIGhhdmluZyBqIGNvcGllcyBwcml6ZWQgZ2l2ZW4gdGhhdCB3ZSBnb3QgdGhpcyBoYW5kIGFycmFuZ2VtZW50IEFORCBnZXR0aW5nIHRoZSBoYW5kIGFycmFuZ2VtZW50IG9mIG1hdFtjLF0gdG8gdGhlIHRvdGFsIHByb2JhYmlsaXR5IG9mIGhhdmluZyBqIGNvcGllcyBwcml6ZWQNCiAgICAgICAgfQ0KICAgICAgICANCiAgICAgIH0NCiAgICB9DQogICAgDQogIH0NCiAgd3JpdGUuY3N2KGhhbmRQcm9icyxmaWxlPXBhc3RlMCgib3V0cHV0L25vdEJhc2ljL2hhbmRQcm9iYWJpbGl0aWVzXyIsIGIsIkJhc2ljcy5jc3YiKSkNCiAgd3JpdGUuY3N2KHByaXplUHJvYnMsZmlsZT1wYXN0ZTAoIm91dHB1dC9ub3RCYXNpYy9wcml6ZVByb2JhYmlsaXRpZXNfIiwgYiwiQmFzaWNzLmNzdiIpKQ0KfQ0Kd3JpdGUuY3N2KGJhc2ljc0luSGFuZFByb2JzLGZpbGU9cGFzdGUwKCJvdXRwdXQvYmFzaWNzSW5IYW5kLmNzdiIpKQ0KYGBgDQoNCiMgSWYgdGFyZ2V0IGlzIGEgYmFzaWM6DQoNCmBgYHtyfQ0KDQoNCm1hdCA8LSBtYXRyaXgoMCwgbnJvdz0zNiwgbmNvbD0zKSAjIG5lZWRlZCBmb3IgdGhlIG11bHRpdmFyaWF0ZSBoeXBlcmdlb21ldHJpYyBkaXN0cmlidXRpb24gDQpjb3VudCA8LSAwDQpmb3IoYiBpbiAwOjcpeyAjIHNpbmNlIHRoZSB0YXJnZXQgY2FyZCBpcyBhIGJhc2ljIG5vdywgd2UgY2FuIGFsc28gaGF2ZSAwIGJhc2ljcyBidXQgb25lIHRhcmdldA0KICBmb3IodCBpbiBtYXgoMCwgMS1iKTooNy1iKSl7DQogICAgY291bnQ8LSBjb3VudCsxDQogICAgbWF0W2NvdW50LF0gPC0gYyg3LWItdCwgYiwgdCkgDQogIH0NCn0NCmZvcihiIGluIDA6NTkpeyAjIGIgaXMgdGhlIG51bWJlciBvZiBvdGhlciBiYXNpY3MgaW4gZGVjaw0KICBwcml6ZVByb2JzIDwtIGRhdGEuZnJhbWUobWF0cml4KDAsIG5yb3c9bWluKDQsNjAtYiksIG5jb2w9NykpICMgbmVlZHMgdG8gYmUgcmVtYWRlIGZvciBldmVyeSBudW1iZXIgb2YgYmFzaWNzDQogIGhhbmRQcm9icyA8LSBkYXRhLmZyYW1lKG1hdHJpeCgwLCBucm93PW1pbig0LDYwLWIpLCBuY29sPTgpKSAjIHN0b3JlcyB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIHNwZWNpZmljIG51bWJlciBvZiBjb3BpZXMgb2YgdGhlIHRhcmdldCBjYXJkIGFyZSBpbiB0aGUgc3RhcnRpbmcgaGFuZA0KICANCiAgY29sbmFtZXMocHJpemVQcm9icykgPC0gcGFzdGUoMDo2LCAiUHJpemVkIikNCiAgcm93Lm5hbWVzKHByaXplUHJvYnMpIDwtIHBhc3RlKDE6bWluKDQsNjAtYiksICJDb3B5KHMpIG9mIENhcmQgVG90YWwiKQ0KICANCiAgY29sbmFtZXMoaGFuZFByb2JzKSA8LSBwYXN0ZSgwOjcsICJJbiBIYW5kIikNCiAgcm93Lm5hbWVzKGhhbmRQcm9icykgPC0gcGFzdGUoMTptaW4oNCw2MC1iKSwgIkNvcHkocykgb2YgQ2FyZCBUb3RhbCIpDQogIGZvcihuIGluIDE6bWluKDQsNjAtYikpeyAjIG4gaXMgdGhlIG51bWJlciBvZiBjb3BpZXMgb2YgdGhlIHRhcmdldCBjYXJkIGluIHRoZSBkZWNrDQogICAgY3VySGFuZFN1Y2Nlc3NQcm9iPC0xLWRoeXBlcigwLGIrbiw2MC1iLW4sNykNCiAgICBoYW5kQ29tYm9zPC1kbXZoeXBlcihtYXQsIGMoNjAtYi1uLCBiLCBuKSwgNykvKGN1ckhhbmRTdWNjZXNzUHJvYikgIyBwKGhhdmluZyAxLi4uNyBiYXNpY3MgYW5kIDAuLi43IHRhcmdldHMgaW4gc3RhcnRpbmcgaGFuZCkvKDEtcChub3QgaGF2aW5nIGJhc2ljIGluIHN0YXJ0aW5nIGhhbmQpKQ0KICAgIA0KICAgIGZvcihjIGluIDE6bGVuZ3RoKG1hdFssMV0pKXsNCiAgICAgIGlmKGhhbmRDb21ib3NbY10hPTApew0KICAgICAgICBoIDwtIG1hdFtjLDNdICMgbnVtYmVyIG9mIHRoZSB0YXJnZXQgY2FyZCBpbiBoYW5kDQogICAgICAgIGhhbmRQcm9ic1tuLGgrMV08LSBoYW5kUHJvYnNbbixoKzFdK2hhbmRDb21ib3NbY10gIyBhZGRzIHRvIHRoZSBwcm9iYWJpbGl0eSBvZiBoYXZpbmcgaCBjb3BpZXMgb2YgdGhlIGNhcmQgaW4gdGhlIHN0YXJ0aW5nIGhhbmQNCiAgICAgICAgY3VyUHJpemVQcm9iIDwtIGRoeXBlcigwOjYsbWF4KDAsbi1oKSxtaW4oNTMraC1uLCA1MyksNikgIyBhY3R1YWwgcHJvYmFiaWxpdGllcyBmb3IgaGF2aW5nIDAuLi42IGNvcGllcyBvZiB0aGUgY2FyZCBwcml6ZWQgZ2l2ZW4gdGhhdCBoIGNvcGllcyBhcmUgaW4gaGFuZCBhbmQgdGhlcmUgYXJlIG4gdG90YWwNCiAgICAgICAgDQogICAgICAgIGZvcihqIGluIDE6bGVuZ3RoKGN1clByaXplUHJvYikpeyANCiAgICAgICAgICBwcml6ZVByb2JzW24sal08LXByaXplUHJvYnNbbixqXStjdXJQcml6ZVByb2Jbal0qaGFuZENvbWJvc1tjXSAjIGFkZCB0aGUgcHJvYmFiaWxpdHkgb2YgaGF2aW5nIGogY29waWVzIHByaXplZCBnaXZlbiB0aGF0IHdlIGdvdCB0aGlzIGhhbmQgYXJyYW5nZW1lbnQgQU5EIGdldHRpbmcgdGhlIGhhbmQgYXJyYW5nZW1lbnQgb2YgbWF0W2MsXSB0byB0aGUgdG90YWwgcHJvYmFiaWxpdHkgb2YgaGF2aW5nIGogY29waWVzIHByaXplZA0KICAgICAgICB9DQogICAgICAgIA0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgfQ0KICB3cml0ZS5jc3YoaGFuZFByb2JzLGZpbGU9cGFzdGUwKCJvdXRwdXQvaXNCYXNpYy9oYW5kUHJvYmFiaWxpdGllc0JfIiwgYiwiQmFzaWNzLmNzdiIpKQ0KICB3cml0ZS5jc3YocHJpemVQcm9icyxmaWxlPXBhc3RlMCgib3V0cHV0L2lzQmFzaWMvcHJpemVQcm9iYWJpbGl0aWVzQl8iLCBiLCJCYXNpY3MuY3N2IikpDQp9DQoNCg0KYGBgDQo=