Instructions

Consider the file A0001.mat from the PhysioNet Challenge https://physionetchallenges.github.io/2020/. Using R:

  1. Plot the histogram of all the 12 ECG leads with the respective density curve.
  2. Using the second lead inverted, apply the expectation-maximization algorithm with 2 and 3 latent classes.
  3. Plot the densities for each of the latent classes.
  4. Which point in the ECG belongs to each latent class? Plot the ECG in which each point has a color corresponding to the class to which it belongs.
  5. Use the difference between the averages of class distribution as the convergence criterion.
  6. Generate 1000 data points at random according to a single distribution fitted to the original data.
  7. Generate 1000 data points at random according to a mixture of distributions fitted using one of the previous EM computed in line b.

Submitted code file should include comments to improve readability.

Setup

First, load the libraries:

Import data using readMat from R.matlab package. The data comes in a List container, so we drop it to work directly with the matrix:

Histograms

  1. Plot the histogram of all the 12 ECG leads with the respective density curve.

Expectation-Maximization

  1. Using the second lead inverted, apply the expectation-maximization algorithm with 2 and 3 latent classes.
  2. Plot the densities for each of the latent classes.
# expectation maximization function ----
expmax <- function(data, k, eps = 1e-5, crisp = FALSE, plot = TRUE) {
  set.seed(2020)
  sample_mean <- sample(data, k)
  sample_sd <- rep(sd(data) / k, k)
  max_iterations <- 10000

  for (i in seq_len(max_iterations)) {
    # expectation ----
    dens <- sapply(seq_len(k), function(x) {
      dnorm(data, mean = sample_mean[x], sd = sample_sd[x])
    })

    rel <- t(apply(dens, 1, function(x) {
      x / sum(x)
    }))

    # maximization ----
    attrib <- t(apply(rel, 1, function(x) {
      replace(rep(0, k), which.max(x)[1], 1)
    }))

    if (crisp) {
      # crisp ----
      sample_mean.new <- sapply(seq_len(k), function(x) {
        mean(data[attrib[, x] == 1])
      })

      sample_sd.new <- sapply(seq_len(k), function(x) {
        sd(data[attrib[, x] == 1])
      })
    } else {
      # soft ----
      sample_mean.new <- sapply(seq_len(k), function(x) {
        sum(data * rel[, x]) / sum(rel[, x])
      })

      sample_sd.new <- sapply(seq_len(k), function(x) {
        sqrt(sum(rel[, x] * (data - sample_mean[x])^2) / sum(rel[, x]))
      })
    }

    dif <- sum(abs(sample_mean.new - sample_mean))

    if (dif < eps) {
      message("Finished in ", i, " iterations.")
      if (plot) {
        gen_data_dens <- list()
        max_y <- -Inf
        for (i in seq_len(k)) {
          set.seed(2020)
          gen_data_dens[[i]] <- density(rnorm(100000, mean = sample_mean[i], sd = sample_sd[i]))
          max <- max(gen_data_dens[[i]]$y)
          if (max > max_y) {
            max_y <- max
          }
        }

        hist(data,
          main = paste("Expectation-Maximization with", k, "classes"),
          freq = FALSE, xlab = "Value", ylim = c(0, max_y)
        )

        for (i in seq_len(k)) {
          lines(gen_data_dens[[i]], lwd = 1, col = i + 1)
        }
      }

      return(list(mean = sample_mean, sd = sample_sd, attrib = attrib, rel = rel, dens = dens))
    }

    sample_mean <- sample_mean.new
    sample_sd <- sample_sd.new
  }
  stop("Algorithm did not converge.")
}

# data ----
lead <- data[[2]] * -1

# using 2 classes ----
result1 <- expmax(lead, 2, plot = TRUE)
Finished in 56 iterations.

Finished in 71 iterations.

Sections by class

  1. Which point in the ECG belongs to each latent class? Plot the ECG in which each point has a color corresponding to the class to which it belongs.
  2. Use the difference between the averages of class distribution as the convergence criterion.

Generating data

  1. Generate 1000 data points at random according to a single distribution fitted to the original data.
  2. Generate 1000 data points at random according to a mixture of distributions fitted using one of the previous EM computed in line b.

LS0tCnRpdGxlOiAiSEVBRFMgLSBISURBIC0gQ29tcFN0YXQ6IEFzc2lnbm1lbnQgMiIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCmF1dGhvcjogRnJhbmNpc2NvIEJpc2Nob2ZmCi0tLQoKIyMgSW5zdHJ1Y3Rpb25zCgpDb25zaWRlciB0aGUgZmlsZSBBMDAwMS5tYXQgZnJvbSB0aGUgUGh5c2lvTmV0IENoYWxsZW5nZSBodHRwczovL3BoeXNpb25ldGNoYWxsZW5nZXMuZ2l0aHViLmlvLzIwMjAvLiBVc2luZyBSOgoKYSkgUGxvdCB0aGUgaGlzdG9ncmFtIG9mIGFsbCB0aGUgMTIgRUNHIGxlYWRzIHdpdGggdGhlIHJlc3BlY3RpdmUgZGVuc2l0eSBjdXJ2ZS4KYikgVXNpbmcgdGhlIHNlY29uZCBsZWFkIGludmVydGVkLCBhcHBseSB0aGUgZXhwZWN0YXRpb24tbWF4aW1pemF0aW9uIGFsZ29yaXRobSB3aXRoIDIgYW5kIDMgbGF0ZW50IGNsYXNzZXMuCmMpIFBsb3QgdGhlIGRlbnNpdGllcyBmb3IgZWFjaCBvZiB0aGUgbGF0ZW50IGNsYXNzZXMuCmQpIFdoaWNoIHBvaW50IGluIHRoZSBFQ0cgYmVsb25ncyB0byBlYWNoIGxhdGVudCBjbGFzcz8gUGxvdCB0aGUgRUNHIGluIHdoaWNoIGVhY2ggcG9pbnQgaGFzIGEgY29sb3IgY29ycmVzcG9uZGluZyB0byB0aGUgY2xhc3MgdG8gd2hpY2ggaXQgYmVsb25ncy4KZSkgVXNlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGF2ZXJhZ2VzIG9mIGNsYXNzIGRpc3RyaWJ1dGlvbiBhcyB0aGUgY29udmVyZ2VuY2UgY3JpdGVyaW9uLgpmKSBHZW5lcmF0ZSAxMDAwIGRhdGEgcG9pbnRzIGF0IHJhbmRvbSBhY2NvcmRpbmcgdG8gYSBzaW5nbGUgZGlzdHJpYnV0aW9uIGZpdHRlZCB0byB0aGUgb3JpZ2luYWwgZGF0YS4KZykgR2VuZXJhdGUgMTAwMCBkYXRhIHBvaW50cyBhdCByYW5kb20gYWNjb3JkaW5nIHRvIGEgbWl4dHVyZSBvZiBkaXN0cmlidXRpb25zIGZpdHRlZCB1c2luZyBvbmUgb2YgdGhlIHByZXZpb3VzIEVNIGNvbXB1dGVkIGluIGxpbmUgYi4gCgpTdWJtaXR0ZWQgY29kZSBmaWxlIHNob3VsZCBpbmNsdWRlIGNvbW1lbnRzIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkuCgojIyBTZXR1cAoKRmlyc3QsIGxvYWQgdGhlIGxpYnJhcmllczoKCmBgYHtyIHNldHVwLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoUi5tYXRsYWIpCmBgYAoKSW1wb3J0IGRhdGEgdXNpbmcgYHJlYWRNYXRgIGZyb20gYFIubWF0bGFiYCBwYWNrYWdlLiBUaGUgZGF0YSBjb21lcyBpbiBhIGBMaXN0YCBjb250YWluZXIsIHNvIHdlIGRyb3AgaXQgdG8gd29yayBkaXJlY3RseSB3aXRoIHRoZSBgbWF0cml4YDoKCmBgYHtyIGltcG9ydH0KIyBJbXBvcnQgdGhlIGRhdGEgLS0tLQpkYXRhIDwtIHJlYWRNYXQoIkEwMDAxLm1hdCIpCmRhdGEgPC0gZGF0YVtbMV1dICMgZ2V0IHJpZCBvZiBMaXN0CmRhdGEgPC0gdChkYXRhKSAjIHRyYW5zcG9zZSB0aGUgbWF0cml4LCBzbyBlYWNoIGNvbHVtbiByZXByZXNlbnRzIG9uZSBsZWFkLgpkYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGF0YSkKY29sbmFtZXMoZGF0YSkgPC0gYygiSSIsICJJSSIsICJJSUkiLCAiYVZSIiwgImFWTCIsICJhVkYiLCAiVjEiLCAiVjIiLCAiVjMiLCAiVjQiLCAiVjUiLCAiVjYiKQpgYGAKCiMjIEhpc3RvZ3JhbXMKCmEpIFBsb3QgdGhlIGhpc3RvZ3JhbSBvZiBhbGwgdGhlIDEyIEVDRyBsZWFkcyB3aXRoIHRoZSByZXNwZWN0aXZlIGRlbnNpdHkgY3VydmUuCgpgYGB7ciBoaXN0b2dyYW1zLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0Kb2xkcGFyIDwtIHBhcihtZnJvdyA9IGMoMywgNCkpCgpmb3IgKGkgaW4gMToxMikgewogIGhpc3QoZGF0YVtbaV1dLCBtYWluID0gbmFtZXMoZGF0YVtpXSksIGZyZXEgPSBGQUxTRSwgeGxhYiA9ICJWYWx1ZSIsIHlsaW0gPSBjKDAsIDAuMDExKSkKICBsaW5lcyhkZW5zaXR5KGRhdGFbW2ldXSksIGx3ZCA9IDEsIGNvbCA9IDQpCn0KCnBhcihvbGRwYXIpCmBgYAoKIyMgRXhwZWN0YXRpb24tTWF4aW1pemF0aW9uCgpiKSBVc2luZyB0aGUgc2Vjb25kIGxlYWQgaW52ZXJ0ZWQsIGFwcGx5IHRoZSBleHBlY3RhdGlvbi1tYXhpbWl6YXRpb24gYWxnb3JpdGhtIHdpdGggMiBhbmQgMyBsYXRlbnQgY2xhc3Nlcy4KYykgUGxvdCB0aGUgZGVuc2l0aWVzIGZvciBlYWNoIG9mIHRoZSBsYXRlbnQgY2xhc3Nlcy4KCmBgYHtyIGVtLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBleHBlY3RhdGlvbiBtYXhpbWl6YXRpb24gZnVuY3Rpb24gLS0tLQpleHBtYXggPC0gZnVuY3Rpb24oZGF0YSwgaywgZXBzID0gMWUtNSwgY3Jpc3AgPSBGQUxTRSwgcGxvdCA9IFRSVUUpIHsKICBzZXQuc2VlZCgyMDIwKQogIHNhbXBsZV9tZWFuIDwtIHNhbXBsZShkYXRhLCBrKQogIHNhbXBsZV9zZCA8LSByZXAoc2QoZGF0YSkgLyBrLCBrKQogIG1heF9pdGVyYXRpb25zIDwtIDEwMDAwCgogIGZvciAoaSBpbiBzZXFfbGVuKG1heF9pdGVyYXRpb25zKSkgewogICAgIyBleHBlY3RhdGlvbiAtLS0tCiAgICBkZW5zIDwtIHNhcHBseShzZXFfbGVuKGspLCBmdW5jdGlvbih4KSB7CiAgICAgIGRub3JtKGRhdGEsIG1lYW4gPSBzYW1wbGVfbWVhblt4XSwgc2QgPSBzYW1wbGVfc2RbeF0pCiAgICB9KQoKICAgIHJlbCA8LSB0KGFwcGx5KGRlbnMsIDEsIGZ1bmN0aW9uKHgpIHsKICAgICAgeCAvIHN1bSh4KQogICAgfSkpCgogICAgIyBtYXhpbWl6YXRpb24gLS0tLQogICAgYXR0cmliIDwtIHQoYXBwbHkocmVsLCAxLCBmdW5jdGlvbih4KSB7CiAgICAgIHJlcGxhY2UocmVwKDAsIGspLCB3aGljaC5tYXgoeClbMV0sIDEpCiAgICB9KSkKCiAgICBpZiAoY3Jpc3ApIHsKICAgICAgIyBjcmlzcCAtLS0tCiAgICAgIHNhbXBsZV9tZWFuLm5ldyA8LSBzYXBwbHkoc2VxX2xlbihrKSwgZnVuY3Rpb24oeCkgewogICAgICAgIG1lYW4oZGF0YVthdHRyaWJbLCB4XSA9PSAxXSkKICAgICAgfSkKCiAgICAgIHNhbXBsZV9zZC5uZXcgPC0gc2FwcGx5KHNlcV9sZW4oayksIGZ1bmN0aW9uKHgpIHsKICAgICAgICBzZChkYXRhW2F0dHJpYlssIHhdID09IDFdKQogICAgICB9KQogICAgfSBlbHNlIHsKICAgICAgIyBzb2Z0IC0tLS0KICAgICAgc2FtcGxlX21lYW4ubmV3IDwtIHNhcHBseShzZXFfbGVuKGspLCBmdW5jdGlvbih4KSB7CiAgICAgICAgc3VtKGRhdGEgKiByZWxbLCB4XSkgLyBzdW0ocmVsWywgeF0pCiAgICAgIH0pCgogICAgICBzYW1wbGVfc2QubmV3IDwtIHNhcHBseShzZXFfbGVuKGspLCBmdW5jdGlvbih4KSB7CiAgICAgICAgc3FydChzdW0ocmVsWywgeF0gKiAoZGF0YSAtIHNhbXBsZV9tZWFuW3hdKV4yKSAvIHN1bShyZWxbLCB4XSkpCiAgICAgIH0pCiAgICB9CgogICAgZGlmIDwtIHN1bShhYnMoc2FtcGxlX21lYW4ubmV3IC0gc2FtcGxlX21lYW4pKQoKICAgIGlmIChkaWYgPCBlcHMpIHsKICAgICAgbWVzc2FnZSgiRmluaXNoZWQgaW4gIiwgaSwgIiBpdGVyYXRpb25zLiIpCiAgICAgIGlmIChwbG90KSB7CiAgICAgICAgZ2VuX2RhdGFfZGVucyA8LSBsaXN0KCkKICAgICAgICBtYXhfeSA8LSAtSW5mCiAgICAgICAgZm9yIChpIGluIHNlcV9sZW4oaykpIHsKICAgICAgICAgIHNldC5zZWVkKDIwMjApCiAgICAgICAgICBnZW5fZGF0YV9kZW5zW1tpXV0gPC0gZGVuc2l0eShybm9ybSgxMDAwMDAsIG1lYW4gPSBzYW1wbGVfbWVhbltpXSwgc2QgPSBzYW1wbGVfc2RbaV0pKQogICAgICAgICAgbWF4IDwtIG1heChnZW5fZGF0YV9kZW5zW1tpXV0keSkKICAgICAgICAgIGlmIChtYXggPiBtYXhfeSkgewogICAgICAgICAgICBtYXhfeSA8LSBtYXgKICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGhpc3QoZGF0YSwKICAgICAgICAgIG1haW4gPSBwYXN0ZSgiRXhwZWN0YXRpb24tTWF4aW1pemF0aW9uIHdpdGgiLCBrLCAiY2xhc3NlcyIpLAogICAgICAgICAgZnJlcSA9IEZBTFNFLCB4bGFiID0gIlZhbHVlIiwgeWxpbSA9IGMoMCwgbWF4X3kpCiAgICAgICAgKQoKICAgICAgICBmb3IgKGkgaW4gc2VxX2xlbihrKSkgewogICAgICAgICAgbGluZXMoZ2VuX2RhdGFfZGVuc1tbaV1dLCBsd2QgPSAxLCBjb2wgPSBpICsgMSkKICAgICAgICB9CiAgICAgIH0KCiAgICAgIHJldHVybihsaXN0KG1lYW4gPSBzYW1wbGVfbWVhbiwgc2QgPSBzYW1wbGVfc2QsIGF0dHJpYiA9IGF0dHJpYiwgcmVsID0gcmVsLCBkZW5zID0gZGVucykpCiAgICB9CgogICAgc2FtcGxlX21lYW4gPC0gc2FtcGxlX21lYW4ubmV3CiAgICBzYW1wbGVfc2QgPC0gc2FtcGxlX3NkLm5ldwogIH0KICBzdG9wKCJBbGdvcml0aG0gZGlkIG5vdCBjb252ZXJnZS4iKQp9CgojIGRhdGEgLS0tLQpsZWFkIDwtIGRhdGFbWzJdXSAqIC0xCgojIHVzaW5nIDIgY2xhc3NlcyAtLS0tCnJlc3VsdDEgPC0gZXhwbWF4KGxlYWQsIDIsIHBsb3QgPSBUUlVFKQoKIyB1c2luZyAzIGNsYXNzZXMgLS0tLQpyZXN1bHQyIDwtIGV4cG1heChsZWFkLCAzLCBwbG90ID0gVFJVRSkKYGBgCgojIyBTZWN0aW9ucyBieSBjbGFzcwoKZCkgV2hpY2ggcG9pbnQgaW4gdGhlIEVDRyBiZWxvbmdzIHRvIGVhY2ggbGF0ZW50IGNsYXNzPyBQbG90IHRoZSBFQ0cgaW4gd2hpY2ggZWFjaCBwb2ludCBoYXMgYSBjb2xvciBjb3JyZXNwb25kaW5nIHRvIHRoZSBjbGFzcyB0byB3aGljaCBpdCBiZWxvbmdzLgplKSBVc2UgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgYXZlcmFnZXMgb2YgY2xhc3MgZGlzdHJpYnV0aW9uIGFzIHRoZSBjb252ZXJnZW5jZSBjcml0ZXJpb24uCgpgYGB7ciBjbGFzc2VzMiwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTAsIGNvbGxhcHNlPVRSVUV9CgpzcGxpdHMgPC0gc29ydChyZXN1bHQxJG1lYW4pCnNwbGl0MSA8LSBtZWFuKHNwbGl0cykKCmNsYXNzMSA8LSBpZmVsc2UocmVzdWx0MSRhdHRyaWJbLCAxXSA9PSAxLCAtbGVhZCwgTkEpCmNsYXNzMiA8LSBpZmVsc2UocmVzdWx0MSRhdHRyaWJbLCAyXSA9PSAxLCAtbGVhZCwgTkEpCgp7CiAgcGxvdChjbGFzczEsCiAgICB0eXBlID0gImwiLCBjb2wgPSAyLCBtYWluID0gIlNlY3Rpb25zIGJ5IGNsYXNzIC0gMiIsIHlsYWIgPSAidmFsdWUiLAogICAgeWxpbSA9IGMoLTcyMCwgNzIwKQogICkKICBsaW5lcyhjbGFzczIsIGNvbCA9IDMpCiAgbGVnZW5kKCJ0b3ByaWdodCIsCiAgICBsZWdlbmQgPSBjKCIxIiwgIjIiKSwKICAgIGx0eSA9IDEsIGNvbCA9IDI6MywKICAgIHRpdGxlID0gIkNsYXNzZXMiCiAgKQp9CmBgYAoKYGBge3IgY2xhc3NlczMsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwLCBjb2xsYXBzZT1UUlVFfQoKc3BsaXRzIDwtIHNvcnQocmVzdWx0MiRtZWFuKQpzcGxpdDEgPC0gbWVhbihzcGxpdHNbMToyXSkKc3BsaXQyIDwtIG1lYW4oc3BsaXRzWzI6M10pCgpjbGFzczEgPC0gaWZlbHNlKHJlc3VsdDIkYXR0cmliWywgMV0gPT0gMSwgLWxlYWQsIE5BKQpjbGFzczIgPC0gaWZlbHNlKHJlc3VsdDIkYXR0cmliWywgMl0gPT0gMSwgLWxlYWQsIE5BKQpjbGFzczMgPC0gaWZlbHNlKHJlc3VsdDIkYXR0cmliWywgM10gPT0gMSwgLWxlYWQsIE5BKQoKCnsKICBwbG90KGNsYXNzMSwKICAgIHR5cGUgPSAibCIsIGNvbCA9IDIsIG1haW4gPSAiU2VjdGlvbnMgYnkgY2xhc3MgLSAzIiwgeWxhYiA9ICJ2YWx1ZSIsCiAgICB5bGltID0gYygtNzIwLCA3MjApCiAgKQogIGxpbmVzKGNsYXNzMiwgY29sID0gMykKICBsaW5lcyhjbGFzczMsIGNvbCA9IDQpCiAgbGVnZW5kKCJ0b3ByaWdodCIsCiAgICBsZWdlbmQgPSBjKCIxIiwgIjIiLCAiMyIpLAogICAgbHR5ID0gMSwgY29sID0gMjo0LAogICAgdGl0bGUgPSAiQ2xhc3NlcyIKICApCn0KYGBgCgojIyBHZW5lcmF0aW5nIGRhdGEKCmYpIEdlbmVyYXRlIDEwMDAgZGF0YSBwb2ludHMgYXQgcmFuZG9tIGFjY29yZGluZyB0byBhIHNpbmdsZSBkaXN0cmlidXRpb24gZml0dGVkIHRvIHRoZSBvcmlnaW5hbCBkYXRhLgpnKSBHZW5lcmF0ZSAxMDAwIGRhdGEgcG9pbnRzIGF0IHJhbmRvbSBhY2NvcmRpbmcgdG8gYSBtaXh0dXJlIG9mIGRpc3RyaWJ1dGlvbnMgZml0dGVkIHVzaW5nIG9uZSBvZiB0aGUgcHJldmlvdXMgRU0gY29tcHV0ZWQgaW4gbGluZSBiLiAKCmBgYHtyIGdlbmVyYXRlZCwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpuIDwtIDEwMDAKY2xhc3NlcyA8LSBhcHBseShyZXN1bHQxJGF0dHJpYiwgMSwgd2hpY2gubWF4KQpjbGFzc19wcm9wIDwtIHByb3AudGFibGUodGFibGUoY2xhc3NlcykpCnNldC5zZWVkKDEwMDApCnNhbXBsZV9jbGFzc2VzIDwtIHNhbXBsZShzZXFfYWxvbmcocmVzdWx0MSRtZWFuKSwgc2l6ZSA9IG4sIHJlcGxhY2UgPSBULCBwcm9iID0gY2xhc3NfcHJvcCkKc2FtcGxlX3Byb3AgPC0gcHJvcC50YWJsZSh0YWJsZShzYW1wbGVfY2xhc3NlcykpCm1peHR1cmUxX2dlbiA8LSByb3VuZCh1bmxpc3Qoc2FwcGx5KHNlcV9hbG9uZyhyZXN1bHQxJG1lYW4pLCBmdW5jdGlvbih4KSB7CiAgcm5vcm0obiAqIHNhbXBsZV9wcm9wW3hdLCBtZWFuID0gcmVzdWx0MSRtZWFuW3hdLCBzZCA9IHJlc3VsdDEkc2RbeF0pCn0pKSwgMCkKCmNsYXNzZXMgPC0gYXBwbHkocmVzdWx0MiRhdHRyaWIsIDEsIHdoaWNoLm1heCkKY2xhc3NfcHJvcCA8LSBwcm9wLnRhYmxlKHRhYmxlKGNsYXNzZXMpKQpzZXQuc2VlZCgxMDAwKQpzYW1wbGVfY2xhc3NlcyA8LSBzYW1wbGUoc2VxX2Fsb25nKHJlc3VsdDIkbWVhbiksIHNpemUgPSBuLCByZXBsYWNlID0gVCwgcHJvYiA9IGNsYXNzX3Byb3ApCnNhbXBsZV9wcm9wIDwtIHByb3AudGFibGUodGFibGUoc2FtcGxlX2NsYXNzZXMpKQptaXh0dXJlMl9nZW4gPC0gcm91bmQodW5saXN0KHNhcHBseShzZXFfYWxvbmcocmVzdWx0MiRtZWFuKSwgZnVuY3Rpb24oeCkgewogIHJub3JtKG4gKiBzYW1wbGVfcHJvcFt4XSwgbWVhbiA9IHJlc3VsdDIkbWVhblt4XSwgc2QgPSByZXN1bHQyJHNkW3hdKQp9KSksIDApCgpvcmlnaW5hbF9nZW4gPC0gcm5vcm0obiwgbWVhbiA9IG1lYW4obGVhZCksIHNkID0gc2QobGVhZCkpCgpvbGRwYXIgPC0gcGFyKG1mcm93ID0gYygyLCAyKSkKYnJlYWtzIDwtIDEwCnsKICBoaXN0KGxlYWQsCiAgICBicmVha3MgPSBicmVha3MsIG1haW4gPSAiT3JpZ2luYWwgZGF0YXNldCIsIGZyZXEgPSBGQUxTRSwKICAgIHhsaW0gPSBjKC04MDAsIDgwMCksIHhsYWIgPSAidmFsdWUiCiAgKQogIGhpc3Qob3JpZ2luYWxfZ2VuLAogICAgYnJlYWtzID0gYnJlYWtzLCBtYWluID0gIkdlbmVyYXRlZCBmcm9tIG9yaWdpbmFsIiwgZnJlcSA9IEZBTFNFLAogICAgeGxpbSA9IGMoLTgwMCwgODAwKSwgeGxhYiA9ICJ2YWx1ZSIKICApCiAgaGlzdChtaXh0dXJlMV9nZW4sCiAgICBicmVha3MgPSBicmVha3MsIG1haW4gPSAiR2VuZXJhdGVkIGZyb20gMiBjbGFzc2VzIiwgZnJlcSA9IEZBTFNFLAogICAgeGxpbSA9IGMoLTgwMCwgODAwKSwgeGxhYiA9ICJ2YWx1ZSIKICApCiAgaGlzdChtaXh0dXJlMl9nZW4sCiAgICBicmVha3MgPSBicmVha3MsIG1haW4gPSAiR2VuZXJhdGVkIGZyb20gMyBjbGFzc2VzIiwgZnJlcSA9IEZBTFNFLAogICAgeGxpbSA9IGMoLTgwMCwgODAwKSwgeGxhYiA9ICJ2YWx1ZSIKICApCn0KcGFyKG9sZHBhcikKYGBgCg==