This document will attempt to demonstrate the five behaviors caused by epistasis described in the lecture.

Griffing effect

The below script will show how additive-by-additive epistasis contributes to response to selection in an unselected population and how this response diminishes in subsequent generations.

library(AlphaSimR)
Loading required package: R6
founderPop = quickHaplo(nInd=1000, nChr=10, segSites=1000)

# Setting a trait with strong epistasis
SP = SimParam$
  new(founderPop)$
  addTraitAE(1000, relAA=1)$
  setVarE(H2=1)

pop = newPop(founderPop)

varA(pop)
       Trait1
Trait1      1
varAA(pop)
          Trait1
Trait1 0.9394489
pop2 = selectCross(pop, nInd=100, nCrosses=1000)

# Observed response to selection
meanG(pop2) - meanG(pop)
  Trait1 
1.819469 
# Response expected from standard breeders equation
selInt(p=0.1) * varA(pop) / sqrt(varP(pop))
         Trait1
Trait1 1.254025
# Response expected from the Griffing effect and no prior selection
selInt(p=0.1) * (varA(pop) + 0.5*varAA(pop)) / sqrt(varP(pop))
         Trait1
Trait1 1.843072
# Repeat measurement after multiple rounds of selection
for(i in 1:10){
  pop = selectCross(pop, nInd=100, nCrosses=1000)
}

pop2 = selectCross(pop, nInd=100, nCrosses=1000)

# Observed response to selection
meanG(pop2) - meanG(pop)
  Trait1 
1.153263 
# Response expected from standard breeders equation
selInt(p=0.1) * varA(pop) / sqrt(varP(pop))
         Trait1
Trait1 1.264255
# Response expected from the Griffing effect and no prior selection
selInt(p=0.1) * (varA(pop) + 0.5*varAA(pop)) / sqrt(varP(pop))
         Trait1
Trait1 1.842631

“Conversion” of epistatic variance

This script will show how additive genetic variance can increase due to epistatic effects. This property is due to drift, so the experiment will be performed using populations of two different sizes. This example comes from the AlphaSimR_Examples repository on GitHub.

library(ggplot2)

## User set variances
# VarA = 1, always
VarD = 1 
VarAA = 1
VarE = 1
n = 1000 # Population size, must be divisible by 10
# Note that conversion is due to drift, so it occurs
# sooner in smaller populations

## Simulation
founderPop = quickHaplo(nInd=n,nChr=10,segSites=100)

SP = SimParam$
  new(founderPop)$
  addTraitADE(100, varDD=VarD*2, relAA=VarAA)$
  setVarE(varE=VarE)

pop = newPop(founderPop)
MEAN = VARG = VARA = VARD = VARAA = numeric(101)

tmp = genParam(pop)
MEAN[1] = tmp$mu
VARG[1] = tmp$varG
VARA[1] = tmp$varA
VARD[1] = tmp$varD
VARAA[1] = tmp$varAA

for(i in 2:101){
  pop = selectCross(pop,n*0.1,nCrosses=n)
  tmp = genParam(pop)
  MEAN[i] = tmp$mu
  VARG[i] = tmp$varG
  VARA[i] = tmp$varA
  VARD[i] = tmp$varD
  VARAA[i] = tmp$varAA
}

df = data.frame(Cycle=rep(0:100,4),
                Source=rep(c("Vg","Va","Vd","Vaa"),each=101),
                Variance=c(VARG,VARA,VARD,VARAA))

ggplot(df,aes(x=Cycle, y=Variance, color=Source))+
  geom_line(linewidth=1)+
  guides(alpha="none")+
  theme_bw()


df2 = data.frame(Cycle=0:100,
                 Mean=MEAN)

ggplot(df2,aes(x=Cycle,y=Mean))+
  geom_line(linewidth=1)+
  guides(alpha="none")+
  theme_bw()



### Rerunning the simulation with a smaller population

## User set variances
# VarA = 1, always
VarD = 1 
VarAA = 1
VarE = 1
n = 100 # Population size, must be divisible by 10
# Note that conversion is due to drift, so it occurs
# sooner in smaller populations

## Simulation
founderPop = quickHaplo(nInd=n,nChr=10,segSites=100)

SP = SimParam$
  new(founderPop)$
  addTraitADE(100, varDD=VarD*2, relAA=VarAA)$
  setVarE(varE=VarE)

pop = newPop(founderPop)
MEAN = VARG = VARA = VARD = VARAA = numeric(101)

tmp = genParam(pop)
MEAN[1] = tmp$mu
VARG[1] = tmp$varG
VARA[1] = tmp$varA
VARD[1] = tmp$varD
VARAA[1] = tmp$varAA

for(i in 2:101){
  pop = selectCross(pop,n*0.1,nCrosses=n)
  tmp = genParam(pop)
  MEAN[i] = tmp$mu
  VARG[i] = tmp$varG
  VARA[i] = tmp$varA
  VARD[i] = tmp$varD
  VARAA[i] = tmp$varAA
}

df = data.frame(Cycle=rep(0:100,4),
                Source=rep(c("Vg","Va","Vd","Vaa"),each=101),
                Variance=c(VARG,VARA,VARD,VARAA))

ggplot(df,aes(x=Cycle, y=Variance, color=Source))+
  geom_line(linewidth=1)+
  guides(alpha="none")+
  theme_bw()


df2 = data.frame(Cycle=0:100,
                 Mean=MEAN)

ggplot(df2,aes(x=Cycle,y=Mean))+
  geom_line(linewidth=1)+
  guides(alpha="none")+
  theme_bw()

Hybrid depression

This script will show how epistasis can contribute to hybrid depression. Hybrid depression is when the mean of the hybrid is less the two parental populations. The simulation will create favorable conditions for this phenomenon by breeding two population separately in the presence of strong epistasis and the absence of dominance. Favorable epistatic combinations will build up in each population that will only partially be transmitted to the hybrids.

founderPop = runMacs(nInd=200, nChr=10, segSites=1000,
                     split=100, inbred=TRUE)

SP = SimParam$
  new(founderPop[1:100])$
  addTraitAE(1000, relAA=1)$
  setVarE(H2=1)

A = newPop(founderPop[1:100])
B = newPop(founderPop[101:200])

A = makeDH( randCross(A, 1000) )
B = makeDH( randCross(B, 1000) )
F1 = randCross2(A, B, nCrosses=10000)

# Create variable for midparent and F1 means
meanMidPar = meanF1 = numeric(20)

meanMidPar[1] = (meanG(A)+meanG(B))/2
meanF1[1] = meanG(F1)

# Reciprical recurrent selection
for(i in 2:20){
  A = makeDH( selectCross(A, nInd=100, nCrosses=1000) )
  B = makeDH( selectCross(B, nInd=100, nCrosses=1000) )
  F1 = randCross2(A, B, nCrosses=10000)
  
  meanMidPar[i] = (meanG(A)+meanG(B))/2
  meanF1[i] = meanG(F1)
}

# Plot midparent (black) and F1 (red) means over time
plot(1:20, meanMidPar, type="l",
     xlab="Generation",
     ylab="Genetic Value")
lines(1:20, meanF1, col="red")

Hybrid depression

The below script will attempt (and fail) to show heterosis due to epistatic interactions. It is similar to the above script, but uses reciprocal recurrent selection to attempt to build favorable epistatic interaction between the populations.

founderPop = runMacs(nInd=200, nChr=10, segSites=1000,
                     split=100, inbred=TRUE)

SP = SimParam$
  new(founderPop[1:100])$
  addTraitAE(1000, relAA=1)$
  setVarE(H2=1)

A = newPop(founderPop[1:100])
B = newPop(founderPop[101:200])

A = makeDH( randCross(A, 1000) )
B = makeDH( randCross(B, 1000) )
F1 = randCross2(A, B, nCrosses=10000)

# Create variable for midparent and F1 means
meanMidPar = meanF1 = numeric(20)

meanMidPar[1] = (meanG(A)+meanG(B))/2
meanF1[1] = meanG(F1)

# Reciprocal recurrent selection using random testers
for(i in 2:20){
  A = setPhenoGCA(A, testers=B[sample(1000,10)], inbred=TRUE)
  B = setPhenoGCA(B, testers=A[sample(1000,10)], inbred=TRUE)
  
  A = makeDH( selectCross(A, nInd=100, nCrosses=1000) )
  B = makeDH( selectCross(B, nInd=100, nCrosses=1000) )
  F1 = randCross2(A, B, nCrosses=10000)
  
  meanMidPar[i] = (meanG(A)+meanG(B))/2
  meanF1[i] = meanG(F1)
}

# Plot midparent (black) and F1 (red) means over time
plot(1:20, meanMidPar, type="l",
     xlab="Generation",
     ylab="Genetic Value")
lines(1:20, meanF1, col="red")

Epistatic decay

This script will show how stabilizing selection on component traits can explain progeny means being below the midparent values for elite parents. This script also shows how complex epistasis can be modeled using nonlinear interactions between component traits.

# Function for modeling epistasis as one component trait under
# directional selection and the rest under stabilizing selection.
specialSelect = function(X){
  Y = X[,1] - rowSums( abs(X[,-1]) )
}

founderPop = quickHaplo(nInd=1000, nChr=10, segSites=1000, 
                        inbred=TRUE)

# Simulating multiple traits to serve as component traits
SP = SimParam$
  new(founderPop)$
  addTraitA(1000, 
            mean = c(100, rep(0,10)),
            var = rep(1,11),
            corA = diag(11))$
  setVarE(H2 = rep(1,11))

pop = newPop(founderPop)

hist(specialSelect( gv(pop) ),
     main = "Histogram of Special Trait",
     xlab = "Special Trait")


# Five generations of breeding
for(i in 1:5){
  pop = selectCross(pop, nInd=100, nCrosses=1000,
                    trait=specialSelect)
  pop = makeDH(pop)
}

# Select parents and make biparental crosses
parents = selectInd(pop, nInd=3, trait=specialSelect)

progeny12 = makeCross(parents, crossPlan=cbind(1,2))
progeny12 = makeDH(progeny12, nDH=100)

progeny13 = makeCross(parents, crossPlan=cbind(1,3))
progeny13 = makeDH(progeny13, nDH=100)

progeny23 = makeCross(parents, crossPlan=cbind(2,3))
progeny23 = makeDH(progeny23, nDH=100)

# Measure GV
gvPar = specialSelect(gv(parents))
gvProg12 = specialSelect(gv(progeny12))
gvProg13 = specialSelect(gv(progeny13))
gvProg23 = specialSelect(gv(progeny23))

# Measure difference between midparent and progeny mean
mean(gvPar[1:2]) - mean(gvProg12)
[1] 3.042156
mean(gvPar[c(1,3)]) - mean(gvProg13)
[1] 2.481761
mean(gvPar[2:3]) - mean(gvProg23)
[1] 2.799389
LS0tDQp0aXRsZTogIlF1YW50aXRhdGl2ZSBHZW5ldGljczogRXBpc3RhdGljIEVmZmVjdHMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGRvY3VtZW50IHdpbGwgYXR0ZW1wdCB0byBkZW1vbnN0cmF0ZSB0aGUgZml2ZSBiZWhhdmlvcnMgY2F1c2VkIGJ5IGVwaXN0YXNpcyBkZXNjcmliZWQgaW4gdGhlIGxlY3R1cmUuDQoNCg0KIyMgR3JpZmZpbmcgZWZmZWN0DQoNClRoZSBiZWxvdyBzY3JpcHQgd2lsbCBzaG93IGhvdyBhZGRpdGl2ZS1ieS1hZGRpdGl2ZSBlcGlzdGFzaXMgY29udHJpYnV0ZXMgdG8gcmVzcG9uc2UgdG8gc2VsZWN0aW9uIGluIGFuIHVuc2VsZWN0ZWQgcG9wdWxhdGlvbiBhbmQgaG93IHRoaXMgcmVzcG9uc2UgZGltaW5pc2hlcyBpbiBzdWJzZXF1ZW50IGdlbmVyYXRpb25zLg0KDQpgYGB7cn0NCmxpYnJhcnkoQWxwaGFTaW1SKQ0KDQpmb3VuZGVyUG9wID0gcXVpY2tIYXBsbyhuSW5kPTEwMDAsIG5DaHI9MTAsIHNlZ1NpdGVzPTEwMDApDQoNCiMgU2V0dGluZyBhIHRyYWl0IHdpdGggc3Ryb25nIGVwaXN0YXNpcw0KU1AgPSBTaW1QYXJhbSQNCiAgbmV3KGZvdW5kZXJQb3ApJA0KICBhZGRUcmFpdEFFKDEwMDAsIHJlbEFBPTEpJA0KICBzZXRWYXJFKEgyPTEpDQoNCnBvcCA9IG5ld1BvcChmb3VuZGVyUG9wKQ0KDQp2YXJBKHBvcCkNCnZhckFBKHBvcCkNCg0KcG9wMiA9IHNlbGVjdENyb3NzKHBvcCwgbkluZD0xMDAsIG5Dcm9zc2VzPTEwMDApDQoNCiMgT2JzZXJ2ZWQgcmVzcG9uc2UgdG8gc2VsZWN0aW9uDQptZWFuRyhwb3AyKSAtIG1lYW5HKHBvcCkNCg0KIyBSZXNwb25zZSBleHBlY3RlZCBmcm9tIHN0YW5kYXJkIGJyZWVkZXJzIGVxdWF0aW9uDQpzZWxJbnQocD0wLjEpICogdmFyQShwb3ApIC8gc3FydCh2YXJQKHBvcCkpDQoNCiMgUmVzcG9uc2UgZXhwZWN0ZWQgZnJvbSB0aGUgR3JpZmZpbmcgZWZmZWN0IGFuZCBubyBwcmlvciBzZWxlY3Rpb24NCnNlbEludChwPTAuMSkgKiAodmFyQShwb3ApICsgMC41KnZhckFBKHBvcCkpIC8gc3FydCh2YXJQKHBvcCkpDQoNCiMgUmVwZWF0IG1lYXN1cmVtZW50IGFmdGVyIG11bHRpcGxlIHJvdW5kcyBvZiBzZWxlY3Rpb24NCmZvcihpIGluIDE6MTApew0KICBwb3AgPSBzZWxlY3RDcm9zcyhwb3AsIG5JbmQ9MTAwLCBuQ3Jvc3Nlcz0xMDAwKQ0KfQ0KDQpwb3AyID0gc2VsZWN0Q3Jvc3MocG9wLCBuSW5kPTEwMCwgbkNyb3NzZXM9MTAwMCkNCg0KIyBPYnNlcnZlZCByZXNwb25zZSB0byBzZWxlY3Rpb24NCm1lYW5HKHBvcDIpIC0gbWVhbkcocG9wKQ0KDQojIFJlc3BvbnNlIGV4cGVjdGVkIGZyb20gc3RhbmRhcmQgYnJlZWRlcnMgZXF1YXRpb24NCnNlbEludChwPTAuMSkgKiB2YXJBKHBvcCkgLyBzcXJ0KHZhclAocG9wKSkNCg0KIyBSZXNwb25zZSBleHBlY3RlZCBmcm9tIHRoZSBHcmlmZmluZyBlZmZlY3QgYW5kIG5vIHByaW9yIHNlbGVjdGlvbg0Kc2VsSW50KHA9MC4xKSAqICh2YXJBKHBvcCkgKyAwLjUqdmFyQUEocG9wKSkgLyBzcXJ0KHZhclAocG9wKSkNCmBgYA0KDQojIyAiQ29udmVyc2lvbiIgb2YgZXBpc3RhdGljIHZhcmlhbmNlDQoNClRoaXMgc2NyaXB0IHdpbGwgc2hvdyBob3cgYWRkaXRpdmUgZ2VuZXRpYyB2YXJpYW5jZSBjYW4gaW5jcmVhc2UgZHVlIHRvIGVwaXN0YXRpYyBlZmZlY3RzLiBUaGlzIHByb3BlcnR5IGlzIGR1ZSB0byBkcmlmdCwgc28gdGhlIGV4cGVyaW1lbnQgd2lsbCBiZSBwZXJmb3JtZWQgdXNpbmcgcG9wdWxhdGlvbnMgb2YgdHdvIGRpZmZlcmVudCBzaXplcy4gVGhpcyBleGFtcGxlIGNvbWVzIGZyb20gdGhlIEFscGhhU2ltUl9FeGFtcGxlcyByZXBvc2l0b3J5IG9uIEdpdEh1Yi4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMjIFVzZXIgc2V0IHZhcmlhbmNlcw0KIyBWYXJBID0gMSwgYWx3YXlzDQpWYXJEID0gMSANClZhckFBID0gMQ0KVmFyRSA9IDENCm4gPSAxMDAwICMgUG9wdWxhdGlvbiBzaXplLCBtdXN0IGJlIGRpdmlzaWJsZSBieSAxMA0KIyBOb3RlIHRoYXQgY29udmVyc2lvbiBpcyBkdWUgdG8gZHJpZnQsIHNvIGl0IG9jY3Vycw0KIyBzb29uZXIgaW4gc21hbGxlciBwb3B1bGF0aW9ucw0KDQojIyBTaW11bGF0aW9uDQpmb3VuZGVyUG9wID0gcXVpY2tIYXBsbyhuSW5kPW4sbkNocj0xMCxzZWdTaXRlcz0xMDApDQoNClNQID0gU2ltUGFyYW0kDQogIG5ldyhmb3VuZGVyUG9wKSQNCiAgYWRkVHJhaXRBREUoMTAwLCB2YXJERD1WYXJEKjIsIHJlbEFBPVZhckFBKSQNCiAgc2V0VmFyRSh2YXJFPVZhckUpDQoNCnBvcCA9IG5ld1BvcChmb3VuZGVyUG9wKQ0KTUVBTiA9IFZBUkcgPSBWQVJBID0gVkFSRCA9IFZBUkFBID0gbnVtZXJpYygxMDEpDQoNCnRtcCA9IGdlblBhcmFtKHBvcCkNCk1FQU5bMV0gPSB0bXAkbXUNClZBUkdbMV0gPSB0bXAkdmFyRw0KVkFSQVsxXSA9IHRtcCR2YXJBDQpWQVJEWzFdID0gdG1wJHZhckQNClZBUkFBWzFdID0gdG1wJHZhckFBDQoNCmZvcihpIGluIDI6MTAxKXsNCiAgcG9wID0gc2VsZWN0Q3Jvc3MocG9wLG4qMC4xLG5Dcm9zc2VzPW4pDQogIHRtcCA9IGdlblBhcmFtKHBvcCkNCiAgTUVBTltpXSA9IHRtcCRtdQ0KICBWQVJHW2ldID0gdG1wJHZhckcNCiAgVkFSQVtpXSA9IHRtcCR2YXJBDQogIFZBUkRbaV0gPSB0bXAkdmFyRA0KICBWQVJBQVtpXSA9IHRtcCR2YXJBQQ0KfQ0KDQpkZiA9IGRhdGEuZnJhbWUoQ3ljbGU9cmVwKDA6MTAwLDQpLA0KICAgICAgICAgICAgICAgIFNvdXJjZT1yZXAoYygiVmciLCJWYSIsIlZkIiwiVmFhIiksZWFjaD0xMDEpLA0KICAgICAgICAgICAgICAgIFZhcmlhbmNlPWMoVkFSRyxWQVJBLFZBUkQsVkFSQUEpKQ0KDQpnZ3Bsb3QoZGYsYWVzKHg9Q3ljbGUsIHk9VmFyaWFuY2UsIGNvbG9yPVNvdXJjZSkpKw0KICBnZW9tX2xpbmUobGluZXdpZHRoPTEpKw0KICBndWlkZXMoYWxwaGE9Im5vbmUiKSsNCiAgdGhlbWVfYncoKQ0KDQpkZjIgPSBkYXRhLmZyYW1lKEN5Y2xlPTA6MTAwLA0KICAgICAgICAgICAgICAgICBNZWFuPU1FQU4pDQoNCmdncGxvdChkZjIsYWVzKHg9Q3ljbGUseT1NZWFuKSkrDQogIGdlb21fbGluZShsaW5ld2lkdGg9MSkrDQogIGd1aWRlcyhhbHBoYT0ibm9uZSIpKw0KICB0aGVtZV9idygpDQoNCg0KIyMjIFJlcnVubmluZyB0aGUgc2ltdWxhdGlvbiB3aXRoIGEgc21hbGxlciBwb3B1bGF0aW9uDQoNCiMjIFVzZXIgc2V0IHZhcmlhbmNlcw0KIyBWYXJBID0gMSwgYWx3YXlzDQpWYXJEID0gMSANClZhckFBID0gMQ0KVmFyRSA9IDENCm4gPSAxMDAgIyBQb3B1bGF0aW9uIHNpemUsIG11c3QgYmUgZGl2aXNpYmxlIGJ5IDEwDQojIE5vdGUgdGhhdCBjb252ZXJzaW9uIGlzIGR1ZSB0byBkcmlmdCwgc28gaXQgb2NjdXJzDQojIHNvb25lciBpbiBzbWFsbGVyIHBvcHVsYXRpb25zDQoNCiMjIFNpbXVsYXRpb24NCmZvdW5kZXJQb3AgPSBxdWlja0hhcGxvKG5JbmQ9bixuQ2hyPTEwLHNlZ1NpdGVzPTEwMCkNCg0KU1AgPSBTaW1QYXJhbSQNCiAgbmV3KGZvdW5kZXJQb3ApJA0KICBhZGRUcmFpdEFERSgxMDAsIHZhckREPVZhckQqMiwgcmVsQUE9VmFyQUEpJA0KICBzZXRWYXJFKHZhckU9VmFyRSkNCg0KcG9wID0gbmV3UG9wKGZvdW5kZXJQb3ApDQpNRUFOID0gVkFSRyA9IFZBUkEgPSBWQVJEID0gVkFSQUEgPSBudW1lcmljKDEwMSkNCg0KdG1wID0gZ2VuUGFyYW0ocG9wKQ0KTUVBTlsxXSA9IHRtcCRtdQ0KVkFSR1sxXSA9IHRtcCR2YXJHDQpWQVJBWzFdID0gdG1wJHZhckENClZBUkRbMV0gPSB0bXAkdmFyRA0KVkFSQUFbMV0gPSB0bXAkdmFyQUENCg0KZm9yKGkgaW4gMjoxMDEpew0KICBwb3AgPSBzZWxlY3RDcm9zcyhwb3AsbiowLjEsbkNyb3NzZXM9bikNCiAgdG1wID0gZ2VuUGFyYW0ocG9wKQ0KICBNRUFOW2ldID0gdG1wJG11DQogIFZBUkdbaV0gPSB0bXAkdmFyRw0KICBWQVJBW2ldID0gdG1wJHZhckENCiAgVkFSRFtpXSA9IHRtcCR2YXJEDQogIFZBUkFBW2ldID0gdG1wJHZhckFBDQp9DQoNCmRmID0gZGF0YS5mcmFtZShDeWNsZT1yZXAoMDoxMDAsNCksDQogICAgICAgICAgICAgICAgU291cmNlPXJlcChjKCJWZyIsIlZhIiwiVmQiLCJWYWEiKSxlYWNoPTEwMSksDQogICAgICAgICAgICAgICAgVmFyaWFuY2U9YyhWQVJHLFZBUkEsVkFSRCxWQVJBQSkpDQoNCmdncGxvdChkZixhZXMoeD1DeWNsZSwgeT1WYXJpYW5jZSwgY29sb3I9U291cmNlKSkrDQogIGdlb21fbGluZShsaW5ld2lkdGg9MSkrDQogIGd1aWRlcyhhbHBoYT0ibm9uZSIpKw0KICB0aGVtZV9idygpDQoNCmRmMiA9IGRhdGEuZnJhbWUoQ3ljbGU9MDoxMDAsDQogICAgICAgICAgICAgICAgIE1lYW49TUVBTikNCg0KZ2dwbG90KGRmMixhZXMoeD1DeWNsZSx5PU1lYW4pKSsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aD0xKSsNCiAgZ3VpZGVzKGFscGhhPSJub25lIikrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQoNCiMjIEh5YnJpZCBkZXByZXNzaW9uDQoNClRoaXMgc2NyaXB0IHdpbGwgc2hvdyBob3cgZXBpc3Rhc2lzIGNhbiBjb250cmlidXRlIHRvIGh5YnJpZCBkZXByZXNzaW9uLiBIeWJyaWQgZGVwcmVzc2lvbiBpcyB3aGVuIHRoZSBtZWFuIG9mIHRoZSBoeWJyaWQgaXMgbGVzcyB0aGUgdHdvIHBhcmVudGFsIHBvcHVsYXRpb25zLiBUaGUgc2ltdWxhdGlvbiB3aWxsIGNyZWF0ZSBmYXZvcmFibGUgY29uZGl0aW9ucyBmb3IgdGhpcyBwaGVub21lbm9uIGJ5IGJyZWVkaW5nIHR3byBwb3B1bGF0aW9uIHNlcGFyYXRlbHkgaW4gdGhlIHByZXNlbmNlIG9mIHN0cm9uZyBlcGlzdGFzaXMgYW5kIHRoZSBhYnNlbmNlIG9mIGRvbWluYW5jZS4gRmF2b3JhYmxlIGVwaXN0YXRpYyBjb21iaW5hdGlvbnMgd2lsbCBidWlsZCB1cCBpbiBlYWNoIHBvcHVsYXRpb24gdGhhdCB3aWxsIG9ubHkgcGFydGlhbGx5IGJlIHRyYW5zbWl0dGVkIHRvIHRoZSBoeWJyaWRzLg0KDQoNCmBgYHtyfQ0KZm91bmRlclBvcCA9IHJ1bk1hY3MobkluZD0yMDAsIG5DaHI9MTAsIHNlZ1NpdGVzPTEwMDAsDQogICAgICAgICAgICAgICAgICAgICBzcGxpdD0xMDAsIGluYnJlZD1UUlVFKQ0KDQpTUCA9IFNpbVBhcmFtJA0KICBuZXcoZm91bmRlclBvcFsxOjEwMF0pJA0KICBhZGRUcmFpdEFFKDEwMDAsIHJlbEFBPTEpJA0KICBzZXRWYXJFKEgyPTEpDQoNCkEgPSBuZXdQb3AoZm91bmRlclBvcFsxOjEwMF0pDQpCID0gbmV3UG9wKGZvdW5kZXJQb3BbMTAxOjIwMF0pDQoNCkEgPSBtYWtlREgoIHJhbmRDcm9zcyhBLCAxMDAwKSApDQpCID0gbWFrZURIKCByYW5kQ3Jvc3MoQiwgMTAwMCkgKQ0KRjEgPSByYW5kQ3Jvc3MyKEEsIEIsIG5Dcm9zc2VzPTEwMDAwKQ0KDQojIENyZWF0ZSB2YXJpYWJsZSBmb3IgbWlkcGFyZW50IGFuZCBGMSBtZWFucw0KbWVhbk1pZFBhciA9IG1lYW5GMSA9IG51bWVyaWMoMjApDQoNCm1lYW5NaWRQYXJbMV0gPSAobWVhbkcoQSkrbWVhbkcoQikpLzINCm1lYW5GMVsxXSA9IG1lYW5HKEYxKQ0KDQojIFJlY2lwcmljYWwgcmVjdXJyZW50IHNlbGVjdGlvbg0KZm9yKGkgaW4gMjoyMCl7DQogIEEgPSBtYWtlREgoIHNlbGVjdENyb3NzKEEsIG5JbmQ9MTAwLCBuQ3Jvc3Nlcz0xMDAwKSApDQogIEIgPSBtYWtlREgoIHNlbGVjdENyb3NzKEIsIG5JbmQ9MTAwLCBuQ3Jvc3Nlcz0xMDAwKSApDQogIEYxID0gcmFuZENyb3NzMihBLCBCLCBuQ3Jvc3Nlcz0xMDAwMCkNCiAgDQogIG1lYW5NaWRQYXJbaV0gPSAobWVhbkcoQSkrbWVhbkcoQikpLzINCiAgbWVhbkYxW2ldID0gbWVhbkcoRjEpDQp9DQoNCiMgUGxvdCBtaWRwYXJlbnQgKGJsYWNrKSBhbmQgRjEgKHJlZCkgbWVhbnMgb3ZlciB0aW1lDQpwbG90KDE6MjAsIG1lYW5NaWRQYXIsIHR5cGU9ImwiLA0KICAgICB4bGFiPSJHZW5lcmF0aW9uIiwNCiAgICAgeWxhYj0iR2VuZXRpYyBWYWx1ZSIpDQpsaW5lcygxOjIwLCBtZWFuRjEsIGNvbD0icmVkIikNCmBgYA0KDQoNCiMjIEh5YnJpZCBkZXByZXNzaW9uDQoNClRoZSBiZWxvdyBzY3JpcHQgd2lsbCBhdHRlbXB0IChhbmQgZmFpbCkgdG8gc2hvdyBoZXRlcm9zaXMgZHVlIHRvIGVwaXN0YXRpYyBpbnRlcmFjdGlvbnMuIEl0IGlzIHNpbWlsYXIgdG8gdGhlIGFib3ZlIHNjcmlwdCwgYnV0IHVzZXMgcmVjaXByb2NhbCByZWN1cnJlbnQgc2VsZWN0aW9uIHRvIGF0dGVtcHQgdG8gYnVpbGQgZmF2b3JhYmxlIGVwaXN0YXRpYyBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSBwb3B1bGF0aW9ucy4NCg0KYGBge3J9DQpmb3VuZGVyUG9wID0gcnVuTWFjcyhuSW5kPTIwMCwgbkNocj0xMCwgc2VnU2l0ZXM9MTAwMCwNCiAgICAgICAgICAgICAgICAgICAgIHNwbGl0PTEwMCwgaW5icmVkPVRSVUUpDQoNClNQID0gU2ltUGFyYW0kDQogIG5ldyhmb3VuZGVyUG9wWzE6MTAwXSkkDQogIGFkZFRyYWl0QUUoMTAwMCwgcmVsQUE9MSkkDQogIHNldFZhckUoSDI9MSkNCg0KQSA9IG5ld1BvcChmb3VuZGVyUG9wWzE6MTAwXSkNCkIgPSBuZXdQb3AoZm91bmRlclBvcFsxMDE6MjAwXSkNCg0KQSA9IG1ha2VESCggcmFuZENyb3NzKEEsIDEwMDApICkNCkIgPSBtYWtlREgoIHJhbmRDcm9zcyhCLCAxMDAwKSApDQpGMSA9IHJhbmRDcm9zczIoQSwgQiwgbkNyb3NzZXM9MTAwMDApDQoNCiMgQ3JlYXRlIHZhcmlhYmxlIGZvciBtaWRwYXJlbnQgYW5kIEYxIG1lYW5zDQptZWFuTWlkUGFyID0gbWVhbkYxID0gbnVtZXJpYygyMCkNCg0KbWVhbk1pZFBhclsxXSA9IChtZWFuRyhBKSttZWFuRyhCKSkvMg0KbWVhbkYxWzFdID0gbWVhbkcoRjEpDQoNCiMgUmVjaXByb2NhbCByZWN1cnJlbnQgc2VsZWN0aW9uIHVzaW5nIHJhbmRvbSB0ZXN0ZXJzDQpmb3IoaSBpbiAyOjIwKXsNCiAgQSA9IHNldFBoZW5vR0NBKEEsIHRlc3RlcnM9QltzYW1wbGUoMTAwMCwxMCldLCBpbmJyZWQ9VFJVRSkNCiAgQiA9IHNldFBoZW5vR0NBKEIsIHRlc3RlcnM9QVtzYW1wbGUoMTAwMCwxMCldLCBpbmJyZWQ9VFJVRSkNCiAgDQogIEEgPSBtYWtlREgoIHNlbGVjdENyb3NzKEEsIG5JbmQ9MTAwLCBuQ3Jvc3Nlcz0xMDAwKSApDQogIEIgPSBtYWtlREgoIHNlbGVjdENyb3NzKEIsIG5JbmQ9MTAwLCBuQ3Jvc3Nlcz0xMDAwKSApDQogIEYxID0gcmFuZENyb3NzMihBLCBCLCBuQ3Jvc3Nlcz0xMDAwMCkNCiAgDQogIG1lYW5NaWRQYXJbaV0gPSAobWVhbkcoQSkrbWVhbkcoQikpLzINCiAgbWVhbkYxW2ldID0gbWVhbkcoRjEpDQp9DQoNCiMgUGxvdCBtaWRwYXJlbnQgKGJsYWNrKSBhbmQgRjEgKHJlZCkgbWVhbnMgb3ZlciB0aW1lDQpwbG90KDE6MjAsIG1lYW5NaWRQYXIsIHR5cGU9ImwiLA0KICAgICB4bGFiPSJHZW5lcmF0aW9uIiwNCiAgICAgeWxhYj0iR2VuZXRpYyBWYWx1ZSIpDQpsaW5lcygxOjIwLCBtZWFuRjEsIGNvbD0icmVkIikNCmBgYA0KDQojIyBFcGlzdGF0aWMgZGVjYXkNCg0KVGhpcyBzY3JpcHQgd2lsbCBzaG93IGhvdyBzdGFiaWxpemluZyBzZWxlY3Rpb24gb24gY29tcG9uZW50IHRyYWl0cyBjYW4gZXhwbGFpbiBwcm9nZW55IG1lYW5zIGJlaW5nIGJlbG93IHRoZSBtaWRwYXJlbnQgdmFsdWVzIGZvciBlbGl0ZSBwYXJlbnRzLiBUaGlzIHNjcmlwdCBhbHNvIHNob3dzIGhvdyBjb21wbGV4IGVwaXN0YXNpcyBjYW4gYmUgbW9kZWxlZCB1c2luZyBub25saW5lYXIgaW50ZXJhY3Rpb25zIGJldHdlZW4gY29tcG9uZW50IHRyYWl0cy4NCg0KDQoNCmBgYHtyfQ0KIyBGdW5jdGlvbiBmb3IgbW9kZWxpbmcgZXBpc3Rhc2lzIGFzIG9uZSBjb21wb25lbnQgdHJhaXQgdW5kZXINCiMgZGlyZWN0aW9uYWwgc2VsZWN0aW9uIGFuZCB0aGUgcmVzdCB1bmRlciBzdGFiaWxpemluZyBzZWxlY3Rpb24uDQpzcGVjaWFsU2VsZWN0ID0gZnVuY3Rpb24oWCl7DQogIFkgPSBYWywxXSAtIHJvd1N1bXMoIGFicyhYWywtMV0pICkNCn0NCg0KZm91bmRlclBvcCA9IHF1aWNrSGFwbG8obkluZD0xMDAwLCBuQ2hyPTEwLCBzZWdTaXRlcz0xMDAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGluYnJlZD1UUlVFKQ0KDQojIFNpbXVsYXRpbmcgbXVsdGlwbGUgdHJhaXRzIHRvIHNlcnZlIGFzIGNvbXBvbmVudCB0cmFpdHMNClNQID0gU2ltUGFyYW0kDQogIG5ldyhmb3VuZGVyUG9wKSQNCiAgYWRkVHJhaXRBKDEwMDAsIA0KICAgICAgICAgICAgbWVhbiA9IGMoMTAwLCByZXAoMCwxMCkpLA0KICAgICAgICAgICAgdmFyID0gcmVwKDEsMTEpLA0KICAgICAgICAgICAgY29yQSA9IGRpYWcoMTEpKSQNCiAgc2V0VmFyRShIMiA9IHJlcCgxLDExKSkNCg0KcG9wID0gbmV3UG9wKGZvdW5kZXJQb3ApDQoNCmhpc3Qoc3BlY2lhbFNlbGVjdCggZ3YocG9wKSApLA0KICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBTcGVjaWFsIFRyYWl0IiwNCiAgICAgeGxhYiA9ICJTcGVjaWFsIFRyYWl0IikNCg0KIyBGaXZlIGdlbmVyYXRpb25zIG9mIGJyZWVkaW5nDQpmb3IoaSBpbiAxOjUpew0KICBwb3AgPSBzZWxlY3RDcm9zcyhwb3AsIG5JbmQ9MTAwLCBuQ3Jvc3Nlcz0xMDAwLA0KICAgICAgICAgICAgICAgICAgICB0cmFpdD1zcGVjaWFsU2VsZWN0KQ0KICBwb3AgPSBtYWtlREgocG9wKQ0KfQ0KDQojIFNlbGVjdCBwYXJlbnRzIGFuZCBtYWtlIGJpcGFyZW50YWwgY3Jvc3Nlcw0KcGFyZW50cyA9IHNlbGVjdEluZChwb3AsIG5JbmQ9MywgdHJhaXQ9c3BlY2lhbFNlbGVjdCkNCg0KcHJvZ2VueTEyID0gbWFrZUNyb3NzKHBhcmVudHMsIGNyb3NzUGxhbj1jYmluZCgxLDIpKQ0KcHJvZ2VueTEyID0gbWFrZURIKHByb2dlbnkxMiwgbkRIPTEwMCkNCg0KcHJvZ2VueTEzID0gbWFrZUNyb3NzKHBhcmVudHMsIGNyb3NzUGxhbj1jYmluZCgxLDMpKQ0KcHJvZ2VueTEzID0gbWFrZURIKHByb2dlbnkxMywgbkRIPTEwMCkNCg0KcHJvZ2VueTIzID0gbWFrZUNyb3NzKHBhcmVudHMsIGNyb3NzUGxhbj1jYmluZCgyLDMpKQ0KcHJvZ2VueTIzID0gbWFrZURIKHByb2dlbnkyMywgbkRIPTEwMCkNCg0KIyBNZWFzdXJlIEdWDQpndlBhciA9IHNwZWNpYWxTZWxlY3QoZ3YocGFyZW50cykpDQpndlByb2cxMiA9IHNwZWNpYWxTZWxlY3QoZ3YocHJvZ2VueTEyKSkNCmd2UHJvZzEzID0gc3BlY2lhbFNlbGVjdChndihwcm9nZW55MTMpKQ0KZ3ZQcm9nMjMgPSBzcGVjaWFsU2VsZWN0KGd2KHByb2dlbnkyMykpDQoNCiMgTWVhc3VyZSBkaWZmZXJlbmNlIGJldHdlZW4gbWlkcGFyZW50IGFuZCBwcm9nZW55IG1lYW4NCm1lYW4oZ3ZQYXJbMToyXSkgLSBtZWFuKGd2UHJvZzEyKQ0KbWVhbihndlBhcltjKDEsMyldKSAtIG1lYW4oZ3ZQcm9nMTMpDQptZWFuKGd2UGFyWzI6M10pIC0gbWVhbihndlByb2cyMykNCg0KYGBgDQoNCg==