Gains: linear versus quadratic generators

First, we defined a few functions we use to compare gains in simple cases. They are dedicated to the evaluation of gain in simple parametric models: linear and quadratic generators for the conditional average. At the end of the chunck, we compare the relative gains from one generator to another under an iso-variance constraint. To the left of the vertical dashed line, the quadratic generator wins the contest, to the right, it loses it.

c_plus <- function(b){                          # Split point for quadratic generator (1)
    return((1+6*b+sqrt(17-36*b+36*b^2))/8)
}
c_minus <- function(b){                         # Split point for quadratic generator (2)
    return((1+6*b-sqrt(17-36*b+36*b^2))/8)
}
init_var_lin <- function(a){                    # Total variance from linear generator
    return(a^2/12)
}
init_var_quad <- function(b){                   # Total variance from quadratic generator
    return((15*b^2-15*b+4)/45)
}
V_quad <- function(c, b){                       # Post-split variance for the quadratic generator
    return(  (4 - 15*b + 15*b^2 - 5*c + 30*b*c - 45*b^2*c - 5*c^2 + 45*b^2*c^2 + 5*c^3 - 30*b*c^3 + 5*c^4)/45)
}
gain_quad <- function(b){                       # Gain obtained by the quadratic generator
    return(init_var_quad(b) - V_quad(c_plus(b),b))
}
gain_lin <- function(b){                        # Gain obtained by the linear generator under iso-variance
    return((15*b^2-15*b+4)/60)
}
gain_diff <- function(b){                       # Difference between gains
    gain_lin(b)- gain_quad(b)
}
data.frame(x = c(0,0.49)) %>%                   # Plot
    ggplot(aes(x = x)) + 
    stat_function(fun = gain_diff) +
    geom_segment(aes(x = 0.383, y = -0.0024, xend = 0.383, yend = 0), linetype = 2) +
    ylim(-0.0024,0.00215) 

# ggsave("gain.pdf")

Plots of parametric generators

Below, we define the families of generators that we work with in the simulations (the piecewise constant is nonetheless omitted in the paper).

generator <- function(x, type, c){
    if(type == "linear"){return(c*x)}
    if(type == "quad"){return((x-c)^2)}
    if(type == "exp"){return(exp(-x*c))}
    if(type == "log"){return(log(x+c))}
    if(type == "sin"){return(sin(x*c)/c)}
    if(type == "outlier"){return(c*(x-0.5)^3)}
    if(type == "piece"){                            # Piecewise constant
        weightz <- arima.sim(list(ar = c(0.99)), c)
        div <- length(weightz)
        weightz <- (weightz - mean(weightz)) / sd(weightz) / runif(1, min = 8, max = 13)
        if(length(x) == 1){                         # Real input (dim = 1)
            inter <- seq(0, 1-1/div, by = 1/div)    # Intervals
            ind <-  sum(x > inter)
            return(weightz[ind] - mean(weightz))
        } else {                                    # Vector input
            inter <- rep(seq(0, 1-1/div, by = 1/div), length(x)) %>%
                matrix(ncol = div, byrow = T)
            comp <- rep(x, div) %>%
                matrix(ncol = div, byrow = F)
            ind <- rowSums(x > inter)
            return(weightz[ind])
        }
    }
    if(type == "poly"){                              # Polynomial generator
        pars <- rnorm(c) 
        pars <- pars - mean(pars) # To make sure that the behavior is not too montonous
        m <- sum(pars/(2:(c+1)))
        x <- sapply(x, `^`, 1:c)
        p <- matrix(rep(pars, ncol(x)), nrow = c, byrow = F)
        out <- colSums(p*x) - m
        return(out / sd(out) / runif(1, min = 12, max = 21))
    }
}
m_gen <- function(type, c){                           # Average values
    if(type == "linear") return(c/2) 
    if(type == "quad") return(1/3-c+c^2)
    if(type == "exp") return((1-exp(-c))/c)
    if(type == "log") return(-1 - c*log(c)+(1+c)*log(1+c))
    if(type == "sin") return((1-cos(c))/c^2)
    if(type == "outlier"){return(0)}                  # A true zero
    if(type == "piece") {return(0)}                   # Not defined (parameter dependent)
    if(type == "poly") {return(0)}                    # Not defined (parameter dependent)
}
fin_gen2 <- function(x, type,cc){                     # Final generator: with mean retrieved
    return(generator(x, type, cc) - m_gen(type, cc))
}
ggplot(data.frame(x = c(0.01,1)), aes(x = x)) +
    stat_function(fun = fin_gen2, args = list(type = "linear", c = 0.1), aes(color = "linear")) +
    stat_function(fun = fin_gen2, args = list(type = "linear", c = 0.2), aes(color = "linear")) +
    stat_function(fun = fin_gen2, args = list(type = "linear", c = 0.3), aes(color = "linear")) +
    stat_function(fun = fin_gen2, args = list(type = "quad", c = 0.51), aes(color = "quad")) +
    stat_function(fun = fin_gen2, args = list(type = "quad", c = 0.55), aes(color = "quad")) +
    stat_function(fun = fin_gen2, args = list(type = "quad", c = 0.59), aes(color = "quad")) +
    stat_function(fun = fin_gen2, args = list(type = "exp", c = 0.1), aes(color = "exp")) +
    stat_function(fun = fin_gen2, args = list(type = "exp", c = 0.2), aes(color = "exp")) +
    stat_function(fun = fin_gen2, args = list(type = "exp", c = 0.3), aes(color = "exp")) +
    stat_function(fun = fin_gen2, args = list(type = "log", c = 3), aes(color = "log")) +
    stat_function(fun = fin_gen2, args = list(type = "log", c = 8), aes(color = "log")) +
    stat_function(fun = fin_gen2, args = list(type = "log", c = 13), aes(color = "log")) +
    stat_function(fun = fin_gen2, args = list(type = "sin", c = 7), aes(color = "sin")) +
    stat_function(fun = fin_gen2, args = list(type = "sin", c = 10), aes(color = "sin")) +
    stat_function(fun = fin_gen2, args = list(type = "sin", c = 14), aes(color = "sin")) +
    stat_function(fun = fin_gen2, args = list(type = "poly", c = 6), aes(color = "poly")) +
    stat_function(fun = fin_gen2, args = list(type = "poly", c = 6), aes(color = "poly")) +
    stat_function(fun = fin_gen2, args = list(type = "poly", c = 6), aes(color = "poly")) 

#ggsave("generators.pdf")

Counterexamples

Finally, some counterexamples. One is ok, the second is pathological.

library(gridExtra)
g1 <- ggplot(data.frame(x = c(0,1)), aes(x = x)) + stat_function(fun = function(x) (x-0.5)^3) 
g2 <- ggplot(data.frame(x=1:8, y = c(1,1,2,6,-5,-1,-2,-2))) + geom_bar(aes(x = x, y = y/10), stat = "identity")
grid.arrange(g1, g2, ncol=2) #%>% ggsave(filename = "counter.pdf")

LS0tCnRpdGxlOiAiVG9UIEZvcm1hbCBwbG90cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMgR2FpbnM6IGxpbmVhciB2ZXJzdXMgcXVhZHJhdGljIGdlbmVyYXRvcnMKCkZpcnN0LCB3ZSBkZWZpbmVkIGEgZmV3IGZ1bmN0aW9ucyB3ZSB1c2UgdG8gY29tcGFyZSBnYWlucyBpbiBzaW1wbGUgY2FzZXMuIFRoZXkgYXJlIGRlZGljYXRlZCB0byB0aGUgZXZhbHVhdGlvbiBvZiBnYWluIGluIHNpbXBsZSBwYXJhbWV0cmljIG1vZGVsczogbGluZWFyIGFuZCBxdWFkcmF0aWMgZ2VuZXJhdG9ycyBmb3IgdGhlIGNvbmRpdGlvbmFsIGF2ZXJhZ2UuIEF0IHRoZSBlbmQgb2YgdGhlIGNodW5jaywgd2UgY29tcGFyZSB0aGUgcmVsYXRpdmUgZ2FpbnMgZnJvbSBvbmUgZ2VuZXJhdG9yIHRvIGFub3RoZXIgdW5kZXIgYW4gaXNvLXZhcmlhbmNlIGNvbnN0cmFpbnQuIFRvIHRoZSBsZWZ0IG9mIHRoZSB2ZXJ0aWNhbCBkYXNoZWQgbGluZSwgdGhlIHF1YWRyYXRpYyBnZW5lcmF0b3Igd2lucyB0aGUgY29udGVzdCwgdG8gdGhlIHJpZ2h0LCBpdCBsb3NlcyBpdC4KCmBgYHtyIGZ1bmN0aW9uc30KY19wbHVzIDwtIGZ1bmN0aW9uKGIpeyAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTcGxpdCBwb2ludCBmb3IgcXVhZHJhdGljIGdlbmVyYXRvciAoMSkKICAgIHJldHVybigoMSs2KmIrc3FydCgxNy0zNipiKzM2KmJeMikpLzgpCn0KY19taW51cyA8LSBmdW5jdGlvbihiKXsgICAgICAgICAgICAgICAgICAgICAgICAgIyBTcGxpdCBwb2ludCBmb3IgcXVhZHJhdGljIGdlbmVyYXRvciAoMikKICAgIHJldHVybigoMSs2KmItc3FydCgxNy0zNipiKzM2KmJeMikpLzgpCn0KaW5pdF92YXJfbGluIDwtIGZ1bmN0aW9uKGEpeyAgICAgICAgICAgICAgICAgICAgIyBUb3RhbCB2YXJpYW5jZSBmcm9tIGxpbmVhciBnZW5lcmF0b3IKICAgIHJldHVybihhXjIvMTIpCn0KaW5pdF92YXJfcXVhZCA8LSBmdW5jdGlvbihiKXsgICAgICAgICAgICAgICAgICAgIyBUb3RhbCB2YXJpYW5jZSBmcm9tIHF1YWRyYXRpYyBnZW5lcmF0b3IKICAgIHJldHVybigoMTUqYl4yLTE1KmIrNCkvNDUpCn0KVl9xdWFkIDwtIGZ1bmN0aW9uKGMsIGIpeyAgICAgICAgICAgICAgICAgICAgICAgIyBQb3N0LXNwbGl0IHZhcmlhbmNlIGZvciB0aGUgcXVhZHJhdGljIGdlbmVyYXRvcgogICAgcmV0dXJuKCAgKDQgLSAxNSpiICsgMTUqYl4yIC0gNSpjICsgMzAqYipjIC0gNDUqYl4yKmMgLSA1KmNeMiArIDQ1KmJeMipjXjIgKyA1KmNeMyAtIDMwKmIqY14zICsgNSpjXjQpLzQ1KQp9CmdhaW5fcXVhZCA8LSBmdW5jdGlvbihiKXsgICAgICAgICAgICAgICAgICAgICAgICMgR2FpbiBvYnRhaW5lZCBieSB0aGUgcXVhZHJhdGljIGdlbmVyYXRvcgogICAgcmV0dXJuKGluaXRfdmFyX3F1YWQoYikgLSBWX3F1YWQoY19wbHVzKGIpLGIpKQp9CmdhaW5fbGluIDwtIGZ1bmN0aW9uKGIpeyAgICAgICAgICAgICAgICAgICAgICAgICMgR2FpbiBvYnRhaW5lZCBieSB0aGUgbGluZWFyIGdlbmVyYXRvciB1bmRlciBpc28tdmFyaWFuY2UKICAgIHJldHVybigoMTUqYl4yLTE1KmIrNCkvNjApCn0KZ2Fpbl9kaWZmIDwtIGZ1bmN0aW9uKGIpeyAgICAgICAgICAgICAgICAgICAgICAgIyBEaWZmZXJlbmNlIGJldHdlZW4gZ2FpbnMKICAgIGdhaW5fbGluKGIpLSBnYWluX3F1YWQoYikKfQoKZGF0YS5mcmFtZSh4ID0gYygwLDAuNDkpKSAlPiUgICAgICAgICAgICAgICAgICAgIyBQbG90CiAgICBnZ3Bsb3QoYWVzKHggPSB4KSkgKyAKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZ2Fpbl9kaWZmKSArCiAgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLjM4MywgeSA9IC0wLjAwMjQsIHhlbmQgPSAwLjM4MywgeWVuZCA9IDApLCBsaW5ldHlwZSA9IDIpICsKICAgIHlsaW0oLTAuMDAyNCwwLjAwMjE1KSAKIyBnZ3NhdmUoImdhaW4ucGRmIikKYGBgCgoKIyBQbG90cyBvZiBwYXJhbWV0cmljIGdlbmVyYXRvcnMKCkJlbG93LCB3ZSBkZWZpbmUgdGhlIGZhbWlsaWVzIG9mIGdlbmVyYXRvcnMgdGhhdCB3ZSB3b3JrIHdpdGggaW4gdGhlIHNpbXVsYXRpb25zICh0aGUgcGllY2V3aXNlIGNvbnN0YW50IGlzIG5vbmV0aGVsZXNzIG9taXR0ZWQgaW4gdGhlIHBhcGVyKS4KCmBgYHtyIGdyYXBocywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZW5lcmF0b3IgPC0gZnVuY3Rpb24oeCwgdHlwZSwgYyl7CiAgICBpZih0eXBlID09ICJsaW5lYXIiKXtyZXR1cm4oYyp4KX0KICAgIGlmKHR5cGUgPT0gInF1YWQiKXtyZXR1cm4oKHgtYyleMil9CiAgICBpZih0eXBlID09ICJleHAiKXtyZXR1cm4oZXhwKC14KmMpKX0KICAgIGlmKHR5cGUgPT0gImxvZyIpe3JldHVybihsb2coeCtjKSl9CiAgICBpZih0eXBlID09ICJzaW4iKXtyZXR1cm4oc2luKHgqYykvYyl9CiAgICBpZih0eXBlID09ICJvdXRsaWVyIil7cmV0dXJuKGMqKHgtMC41KV4zKX0KICAgIGlmKHR5cGUgPT0gInBpZWNlIil7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUGllY2V3aXNlIGNvbnN0YW50CiAgICAgICAgd2VpZ2h0eiA8LSBhcmltYS5zaW0obGlzdChhciA9IGMoMC45OSkpLCBjKQogICAgICAgIGRpdiA8LSBsZW5ndGgod2VpZ2h0eikKICAgICAgICB3ZWlnaHR6IDwtICh3ZWlnaHR6IC0gbWVhbih3ZWlnaHR6KSkgLyBzZCh3ZWlnaHR6KSAvIHJ1bmlmKDEsIG1pbiA9IDgsIG1heCA9IDEzKQogICAgICAgIGlmKGxlbmd0aCh4KSA9PSAxKXsgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZWFsIGlucHV0IChkaW0gPSAxKQogICAgICAgICAgICBpbnRlciA8LSBzZXEoMCwgMS0xL2RpdiwgYnkgPSAxL2RpdikgICAgIyBJbnRlcnZhbHMKICAgICAgICAgICAgaW5kIDwtICBzdW0oeCA+IGludGVyKQogICAgICAgICAgICByZXR1cm4od2VpZ2h0eltpbmRdIC0gbWVhbih3ZWlnaHR6KSkKICAgICAgICB9IGVsc2UgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVmVjdG9yIGlucHV0CiAgICAgICAgICAgIGludGVyIDwtIHJlcChzZXEoMCwgMS0xL2RpdiwgYnkgPSAxL2RpdiksIGxlbmd0aCh4KSkgJT4lCiAgICAgICAgICAgICAgICBtYXRyaXgobmNvbCA9IGRpdiwgYnlyb3cgPSBUKQogICAgICAgICAgICBjb21wIDwtIHJlcCh4LCBkaXYpICU+JQogICAgICAgICAgICAgICAgbWF0cml4KG5jb2wgPSBkaXYsIGJ5cm93ID0gRikKICAgICAgICAgICAgaW5kIDwtIHJvd1N1bXMoeCA+IGludGVyKQogICAgICAgICAgICByZXR1cm4od2VpZ2h0eltpbmRdKQogICAgICAgIH0KICAgIH0KICAgIGlmKHR5cGUgPT0gInBvbHkiKXsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFBvbHlub21pYWwgZ2VuZXJhdG9yCiAgICAgICAgcGFycyA8LSBybm9ybShjKSAKICAgICAgICBwYXJzIDwtIHBhcnMgLSBtZWFuKHBhcnMpICMgVG8gbWFrZSBzdXJlIHRoYXQgdGhlIGJlaGF2aW9yIGlzIG5vdCB0b28gbW9udG9ub3VzCiAgICAgICAgbSA8LSBzdW0ocGFycy8oMjooYysxKSkpCiAgICAgICAgeCA8LSBzYXBwbHkoeCwgYF5gLCAxOmMpCiAgICAgICAgcCA8LSBtYXRyaXgocmVwKHBhcnMsIG5jb2woeCkpLCBucm93ID0gYywgYnlyb3cgPSBGKQogICAgICAgIG91dCA8LSBjb2xTdW1zKHAqeCkgLSBtCiAgICAgICAgcmV0dXJuKG91dCAvIHNkKG91dCkgLyBydW5pZigxLCBtaW4gPSAxMiwgbWF4ID0gMjEpKQogICAgfQp9CgptX2dlbiA8LSBmdW5jdGlvbih0eXBlLCBjKXsgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEF2ZXJhZ2UgdmFsdWVzCiAgICBpZih0eXBlID09ICJsaW5lYXIiKSByZXR1cm4oYy8yKSAKICAgIGlmKHR5cGUgPT0gInF1YWQiKSByZXR1cm4oMS8zLWMrY14yKQogICAgaWYodHlwZSA9PSAiZXhwIikgcmV0dXJuKCgxLWV4cCgtYykpL2MpCiAgICBpZih0eXBlID09ICJsb2ciKSByZXR1cm4oLTEgLSBjKmxvZyhjKSsoMStjKSpsb2coMStjKSkKICAgIGlmKHR5cGUgPT0gInNpbiIpIHJldHVybigoMS1jb3MoYykpL2NeMikKICAgIGlmKHR5cGUgPT0gIm91dGxpZXIiKXtyZXR1cm4oMCl9ICAgICAgICAgICAgICAgICAgIyBBIHRydWUgemVybwogICAgaWYodHlwZSA9PSAicGllY2UiKSB7cmV0dXJuKDApfSAgICAgICAgICAgICAgICAgICAjIE5vdCBkZWZpbmVkIChwYXJhbWV0ZXIgZGVwZW5kZW50KQogICAgaWYodHlwZSA9PSAicG9seSIpIHtyZXR1cm4oMCl9ICAgICAgICAgICAgICAgICAgICAjIE5vdCBkZWZpbmVkIChwYXJhbWV0ZXIgZGVwZW5kZW50KQp9CgpmaW5fZ2VuMiA8LSBmdW5jdGlvbih4LCB0eXBlLGNjKXsgICAgICAgICAgICAgICAgICAgICAjIEZpbmFsIGdlbmVyYXRvcjogd2l0aCBtZWFuIHJldHJpZXZlZAogICAgcmV0dXJuKGdlbmVyYXRvcih4LCB0eXBlLCBjYykgLSBtX2dlbih0eXBlLCBjYykpCn0KCmdncGxvdChkYXRhLmZyYW1lKHggPSBjKDAuMDEsMSkpLCBhZXMoeCA9IHgpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gImxpbmVhciIsIGMgPSAwLjEpLCBhZXMoY29sb3IgPSAibGluZWFyIikpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZmluX2dlbjIsIGFyZ3MgPSBsaXN0KHR5cGUgPSAibGluZWFyIiwgYyA9IDAuMiksIGFlcyhjb2xvciA9ICJsaW5lYXIiKSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBmaW5fZ2VuMiwgYXJncyA9IGxpc3QodHlwZSA9ICJsaW5lYXIiLCBjID0gMC4zKSwgYWVzKGNvbG9yID0gImxpbmVhciIpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gInF1YWQiLCBjID0gMC41MSksIGFlcyhjb2xvciA9ICJxdWFkIikpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZmluX2dlbjIsIGFyZ3MgPSBsaXN0KHR5cGUgPSAicXVhZCIsIGMgPSAwLjU1KSwgYWVzKGNvbG9yID0gInF1YWQiKSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBmaW5fZ2VuMiwgYXJncyA9IGxpc3QodHlwZSA9ICJxdWFkIiwgYyA9IDAuNTkpLCBhZXMoY29sb3IgPSAicXVhZCIpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gImV4cCIsIGMgPSAwLjEpLCBhZXMoY29sb3IgPSAiZXhwIikpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZmluX2dlbjIsIGFyZ3MgPSBsaXN0KHR5cGUgPSAiZXhwIiwgYyA9IDAuMiksIGFlcyhjb2xvciA9ICJleHAiKSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBmaW5fZ2VuMiwgYXJncyA9IGxpc3QodHlwZSA9ICJleHAiLCBjID0gMC4zKSwgYWVzKGNvbG9yID0gImV4cCIpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gImxvZyIsIGMgPSAzKSwgYWVzKGNvbG9yID0gImxvZyIpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gImxvZyIsIGMgPSA4KSwgYWVzKGNvbG9yID0gImxvZyIpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gImxvZyIsIGMgPSAxMyksIGFlcyhjb2xvciA9ICJsb2ciKSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBmaW5fZ2VuMiwgYXJncyA9IGxpc3QodHlwZSA9ICJzaW4iLCBjID0gNyksIGFlcyhjb2xvciA9ICJzaW4iKSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBmaW5fZ2VuMiwgYXJncyA9IGxpc3QodHlwZSA9ICJzaW4iLCBjID0gMTApLCBhZXMoY29sb3IgPSAic2luIikpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZmluX2dlbjIsIGFyZ3MgPSBsaXN0KHR5cGUgPSAic2luIiwgYyA9IDE0KSwgYWVzKGNvbG9yID0gInNpbiIpKSArCiAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZpbl9nZW4yLCBhcmdzID0gbGlzdCh0eXBlID0gInBvbHkiLCBjID0gNiksIGFlcyhjb2xvciA9ICJwb2x5IikpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZmluX2dlbjIsIGFyZ3MgPSBsaXN0KHR5cGUgPSAicG9seSIsIGMgPSA2KSwgYWVzKGNvbG9yID0gInBvbHkiKSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBmaW5fZ2VuMiwgYXJncyA9IGxpc3QodHlwZSA9ICJwb2x5IiwgYyA9IDYpLCBhZXMoY29sb3IgPSAicG9seSIpKSAKI2dnc2F2ZSgiZ2VuZXJhdG9ycy5wZGYiKQpgYGAKCgojIENvdW50ZXJleGFtcGxlcwoKRmluYWxseSwgc29tZSBjb3VudGVyZXhhbXBsZXMuIE9uZSBpcyBvaywgdGhlIHNlY29uZCBpcyBwYXRob2xvZ2ljYWwuCgpgYGB7ciBjb3VudGVyLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9CmxpYnJhcnkoZ3JpZEV4dHJhKQpnMSA8LSBnZ3Bsb3QoZGF0YS5mcmFtZSh4ID0gYygwLDEpKSwgYWVzKHggPSB4KSkgKyBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZ1bmN0aW9uKHgpICh4LTAuNSleMykgCmcyIDwtIGdncGxvdChkYXRhLmZyYW1lKHg9MTo4LCB5ID0gYygxLDEsMiw2LC01LC0xLC0yLC0yKSkpICsgZ2VvbV9iYXIoYWVzKHggPSB4LCB5ID0geS8xMCksIHN0YXQgPSAiaWRlbnRpdHkiKQpncmlkLmFycmFuZ2UoZzEsIGcyLCBuY29sPTIpICMlPiUgZ2dzYXZlKGZpbGVuYW1lID0gImNvdW50ZXIucGRmIikKYGBgCgo=