Modified Post: 2018-09-06

Modified Post: 2019-03-24


Tags: fMRI R

These motion variables can be used as control variables (covariates of no interest; as seen in resting-state and task-based functional connectivity analyses).



R Libraries

# data loading, manipulation, and writing
library(tidyverse)



Function

# graph motion parameters and facet by motion type
graph_motion <- function(dataset, title) {
  data_long <- reshape2::melt(dataset, id.var = "Duration") %>%
    mutate(variable = gsub("_", " ", variable))
  figure <- ggplot(data_long, aes(x = Duration, y = value)) +
    geom_line(color = "#106a8c") +
    theme_minimal() +
    facet_wrap(~ variable, scales = "free_y", ncol = 3) +
    labs(title = title,
         y = NULL) +
    theme(plot.title = element_text(hjust = 0.5))
  print(figure)
}



6-Rigid Body Motion Regressors

The first six motion parameters are the standard motion parameters and include:

  1. \(translation_X\), translation in the x-direction
  2. \(translation_Y\), translation in the y-direction
  3. \(translation_Z\), translation in the z-direction
  4. \(rotation_X\), rotation around the x-axis
  5. \(rotation_Y\), rotation around the y-axis
  6. \(rotation_Z\), rotation around the z-axis
# load 6-rigidy body motion parameters
# add duration
motion_file <- "blog_Calculate_Motion_Regressors_files/example_motion_demeand.csv"
motion_demeaned <- read_csv(motion_file, col_names = F) %>%
  mutate(Duration = c(1:nrow(.)))

# rename columns
motion_variables <- c(paste0("Translation_", c("X", "Y", "Z")),
                      paste0("Rotation_", c("X", "Y", "Z")))
colnames(motion_demeaned) <- c(motion_variables, "Duration")

# graph motion parameters
graph_motion(motion_demeaned, "6-Rigid Body Motion Parameters")



12-Motion Regressors

The next six motion parameters are the first temporal derivatives of the original six rigid body motion parameters. Specifically, it is the motion minus the motion from the previous time point.

  1. \(translation_{X_{T-1}}\), first temporal derivative of the translation in the x-direction
  2. \(translation_{Y_{T-1}}\), first temporal derivative of the translation in the y-direction
  3. \(translation_{Z_{T-1}}\), first temporal derivative of the translation in the z-direction
  4. \(rotation_{X_{T-1}}\) first temporal derivative of the rotation around the x-axis
  5. \(rotation_{Y_{T-1}}\), first temporal derivative of the rotation around the y-axis
  6. \(rotation_{Z_{T-1}}\), first temporal derivative of the rotation around the z-axis
# calculate first temporal derivative
motion_demeaned_td1 <- motion_demeaned %>%
  sapply(., FUN = function(x) c(NA, diff(x))) %>%
  as_tibble() %>%
  mutate(Duration = c(1:nrow(.)))
  
# rename columns
colnames(motion_demeaned_td1) <- c(paste0(motion_variables, "_TD1"), "Duration")

# graph motion parameters
graph_motion(motion_demeaned_td1, "First Temporal Derivatives of the 6-Rigid Body Motion Parameters")



18-Motion Regressors

The next six motion parameters are the original six rigid body motion parameters squared:

  1. \(translation_X^2\), translation in the x-direction squared
  2. \(translation_Y^2\), translation in the y-direction squared
  3. \(translation_Z^2\), translation in the z-direction squared
  4. \(rotation_X^2\), rotation around the x-axis squared
  5. \(rotation_Y^2\), rotation around the y-axis squared
  6. \(rotation_Z^2\), rotation around the z-axis squared
# square 6-rigid body motion parameters
motion_demeaned_sq <- motion_demeaned^2 %>%
  as_tibble() %>%
  mutate(Duration = c(1:nrow(.)))
  
# rename columns
colnames(motion_demeaned_sq) <- c(paste0(motion_variables, "_Squared"), "Duration")

# graph motion parameters
graph_motion(motion_demeaned_sq, "6-Rigid Body Motion Parameters Squared")



24-Motion Regressors

The next six motion parameters are the first temporal derivatives squared.

  1. \(translation_{X_{T-1}}^2\), first temporal derivative of the translation in the x-direction squared
  2. \(translation_{Y_{T-1}}^2\), first temporal derivative of the translation in the y-direction squared
  3. \(translation_{Z_{T-1}}^2\), first temporal derivative of the translation in the z-direction squared
  4. \(rotation_{X_{T-1}}^2\), first temporal derivative of the rotation around the x-axis squared
  5. \(rotation_{Y_{T-1}}^2\) first temporal derivative of the rotation around the y-axis squared
  6. \(rotation_{Z_{T-1}}^2\), first temporal derivative of the rotation around the z-axis squared
# square the first temporal derivative
motion_demeaned_td1_sq <- motion_demeaned_td1^2 %>%
  as_tibble() %>%
  mutate(Duration = 1:nrow(.)) 

# rename columns
colnames(motion_demeaned_td1_sq) <- c(paste0(motion_variables, "_TD1_Squared"), "Duration")

# plot motion.td1.sq into one figure
graph_motion(motion_demeaned_td1_sq, "First Temporal Derivatives of the 6-Rigid Body Motion Parameters Squared")



R Code Example

# function to create 24 motion variables =======================================
create_24_motion_variables <- function(dataset) {
  # mean-center (demean) ----
  # subtract each score from its respective column mean
  dataset_motion_demeaned <- scale(x = dataset, center = T, scale = F)

  # first temporal derivatives ----
  # subtract motion from prior motion time point
  dataset_motion_td1 <- sapply(dataset_motion_demeaned, FUN = function(x) c(NA, diff(x)))

  # combine the variables into one dataset
  dataset_motion <- data.frame(dataset_motion_demeaned, dataset_motion_td1)

  # squares ----
  # square 6-rigid body motion parameters and its temporal derivatives
  dataset_motion_squared <- dataset_motion^2

  # combine the variables into one dataset
  dataset_motion <- data.frame(dataset_motion, dataset_motion_squared)

  return(dataset_motion)
}

# load 6-rigid body motion parameters file =====================================
# assign to dataset_motion
dataset_motion <- read_csv(file.choose())

# create 24 motion variables using function ====================================
# assign output to dataset_motion_24
dataset_motion_24 <- create_24_motion_variables(dataset = dataset_motion)

# rename columns ===============================================================
# assign original motion variables
motion_variables <- c(
  "translation_x", "translation_y", "translation_z",
  "rotation_x", "rotation_y", "rotation_z"
)

# rename columns
colnames(dataset_motion_24) <- c(
  motion_variables,
  paste0(motion_variables, "_td1"),
  paste0(motion_variables, "_squared"),
  paste0(motion_variables, "_td1_squared")
)

# export 24-motion variables ===================================================
write_csv(x = dataset_motion_24, file = "dataset-motion-24.csv")



References

Friston, K. J., Williams, S., Howard, R., Frackowiak, R. S. J., & Turner, R. (1996). Movement-related effects in fMRI time-series. Magnetic Resonance in Medicine, 35(3), 346–355. http://doi.org/10.1002/mrm.1910350312

Yan, C.-G., Cheung, B., Kelly, C., Colcombe, S., Craddock, R. C., Di Martino, A., … Milham, M. P. (2013). A comprehensive assessment of regional variation in the impact of head micromovements on functional connectomics. NeuroImage, 76, 183–201. http://doi.org/10.1016/j.neuroimage.2013.03.004




LS0tCnRpdGxlOiAiQ2FsY3VsYXRlIDI0LU1vdGlvbiBWYXJpYWJsZXMiCnN1YnRpdGxlOiAiQSBxdWljayBndWlkZSB0byBjYWxjdWxhdGluZyAyNC1tb3Rpb24gdmFyaWFibGVzIgpkYXRlOiAiT3JpZ2luYWwgUG9zdDogMjAxOC0wMy0yMCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdGhlbWU6IGx1bWVuCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKICAgICAgc21vb3RoX3Njcm9sbDogeWVzCi0tLQoKPGg0PjxpPk1vZGlmaWVkIFBvc3Q6IDIwMTgtMDktMDY8L2k+PC9oND4KPGg0PjxpPk1vZGlmaWVkIFBvc3Q6IDIwMTktMDMtMjQ8L2k+PC9oND4KPGJyPipUYWdzOiogPHNwYW4gY2xhc3M9ImJhZGdlIGJhZGdlLXBpbGwgYmFkZ2UtZGFyayI+Zk1SSTwvc3Bhbj4KPHNwYW4gY2xhc3M9ImJhZGdlIGJhZGdlLXBpbGwgYmFkZ2UtcHJpbWFyeSI+Ujwvc3Bhbj4KClRoZXNlIG1vdGlvbiB2YXJpYWJsZXMgY2FuIGJlIHVzZWQgYXMgY29udHJvbCB2YXJpYWJsZXMgKGNvdmFyaWF0ZXMgb2Ygbm8gaW50ZXJlc3Q7IGFzIHNlZW4gaW4gcmVzdGluZy1zdGF0ZSBhbmQgdGFzay1iYXNlZCBmdW5jdGlvbmFsIGNvbm5lY3Rpdml0eSBhbmFseXNlcykuIAoKPEJSPjxCUj4KCiMjIFIgTGlicmFyaWVzCmBgYHtyLCBtZXNzYWdlID0gRn0KIyBkYXRhIGxvYWRpbmcsIG1hbmlwdWxhdGlvbiwgYW5kIHdyaXRpbmcKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKPEJSPjxCUj4KCiMjIEZ1bmN0aW9uCmBgYHtyfQojIGdyYXBoIG1vdGlvbiBwYXJhbWV0ZXJzIGFuZCBmYWNldCBieSBtb3Rpb24gdHlwZQpncmFwaF9tb3Rpb24gPC0gZnVuY3Rpb24oZGF0YXNldCwgdGl0bGUpIHsKICBkYXRhX2xvbmcgPC0gcmVzaGFwZTI6Om1lbHQoZGF0YXNldCwgaWQudmFyID0gIkR1cmF0aW9uIikgJT4lCiAgICBtdXRhdGUodmFyaWFibGUgPSBnc3ViKCJfIiwgIiAiLCB2YXJpYWJsZSkpCiAgZmlndXJlIDwtIGdncGxvdChkYXRhX2xvbmcsIGFlcyh4ID0gRHVyYXRpb24sIHkgPSB2YWx1ZSkpICsKICAgIGdlb21fbGluZShjb2xvciA9ICIjMTA2YThjIikgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGZhY2V0X3dyYXAofiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAzKSArCiAgICBsYWJzKHRpdGxlID0gdGl0bGUsCiAgICAgICAgIHkgPSBOVUxMKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKICBwcmludChmaWd1cmUpCn0KYGBgCgo8QlI+PEJSPgoKIyMgNi1SaWdpZCBCb2R5IE1vdGlvbiBSZWdyZXNzb3JzCgpUaGUgZmlyc3Qgc2l4IG1vdGlvbiBwYXJhbWV0ZXJzIGFyZSB0aGUgc3RhbmRhcmQgbW90aW9uIHBhcmFtZXRlcnMgYW5kIGluY2x1ZGU6CgoxLiAkdHJhbnNsYXRpb25fWCQsIHRyYW5zbGF0aW9uIGluIHRoZSB4LWRpcmVjdGlvbgoyLiAkdHJhbnNsYXRpb25fWSQsIHRyYW5zbGF0aW9uIGluIHRoZSB5LWRpcmVjdGlvbgozLiAkdHJhbnNsYXRpb25fWiQsIHRyYW5zbGF0aW9uIGluIHRoZSB6LWRpcmVjdGlvbgo0LiAkcm90YXRpb25fWCQsIHJvdGF0aW9uIGFyb3VuZCB0aGUgeC1heGlzCjUuICRyb3RhdGlvbl9ZJCwgcm90YXRpb24gYXJvdW5kIHRoZSB5LWF4aXMKNi4gJHJvdGF0aW9uX1okLCByb3RhdGlvbiBhcm91bmQgdGhlIHotYXhpcwoKYGBge3IgbTYsIGNvbGxhcHNlZCA9IFRSVUUsIGRldiA9ICJzdmciLCBtZXNzYWdlID0gRn0KIyBsb2FkIDYtcmlnaWR5IGJvZHkgbW90aW9uIHBhcmFtZXRlcnMKIyBhZGQgZHVyYXRpb24KbW90aW9uX2ZpbGUgPC0gImJsb2dfQ2FsY3VsYXRlX01vdGlvbl9SZWdyZXNzb3JzX2ZpbGVzL2V4YW1wbGVfbW90aW9uX2RlbWVhbmQuY3N2Igptb3Rpb25fZGVtZWFuZWQgPC0gcmVhZF9jc3YobW90aW9uX2ZpbGUsIGNvbF9uYW1lcyA9IEYpICU+JQogIG11dGF0ZShEdXJhdGlvbiA9IGMoMTpucm93KC4pKSkKCiMgcmVuYW1lIGNvbHVtbnMKbW90aW9uX3ZhcmlhYmxlcyA8LSBjKHBhc3RlMCgiVHJhbnNsYXRpb25fIiwgYygiWCIsICJZIiwgIloiKSksCiAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIlJvdGF0aW9uXyIsIGMoIlgiLCAiWSIsICJaIikpKQpjb2xuYW1lcyhtb3Rpb25fZGVtZWFuZWQpIDwtIGMobW90aW9uX3ZhcmlhYmxlcywgIkR1cmF0aW9uIikKCiMgZ3JhcGggbW90aW9uIHBhcmFtZXRlcnMKZ3JhcGhfbW90aW9uKG1vdGlvbl9kZW1lYW5lZCwgIjYtUmlnaWQgQm9keSBNb3Rpb24gUGFyYW1ldGVycyIpCmBgYAoKPEJSPjxCUj4KCiMjIDEyLU1vdGlvbiBSZWdyZXNzb3JzCgpUaGUgbmV4dCBzaXggbW90aW9uIHBhcmFtZXRlcnMgYXJlIHRoZSBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlcyBvZiB0aGUgb3JpZ2luYWwgc2l4IHJpZ2lkIGJvZHkgbW90aW9uIHBhcmFtZXRlcnMuIFNwZWNpZmljYWxseSwgaXQgaXMgdGhlIG1vdGlvbiBtaW51cyB0aGUgbW90aW9uIGZyb20gdGhlIHByZXZpb3VzIHRpbWUgcG9pbnQuCgo3LiAkdHJhbnNsYXRpb25fe1hfe1QtMX19JCwgZmlyc3QgdGVtcG9yYWwgZGVyaXZhdGl2ZSBvZiB0aGUgdHJhbnNsYXRpb24gaW4gdGhlIHgtZGlyZWN0aW9uCjguICR0cmFuc2xhdGlvbl97WV97VC0xfX0kLCBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlIG9mIHRoZSB0cmFuc2xhdGlvbiBpbiB0aGUgeS1kaXJlY3Rpb24KOS4gJHRyYW5zbGF0aW9uX3taX3tULTF9fSQsIGZpcnN0IHRlbXBvcmFsIGRlcml2YXRpdmUgb2YgdGhlIHRyYW5zbGF0aW9uIGluIHRoZSB6LWRpcmVjdGlvbgoxMC4gJHJvdGF0aW9uX3tYX3tULTF9fSQgZmlyc3QgdGVtcG9yYWwgZGVyaXZhdGl2ZSBvZiB0aGUgcm90YXRpb24gYXJvdW5kIHRoZSB4LWF4aXMKMTEuICRyb3RhdGlvbl97WV97VC0xfX0kLCBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlIG9mIHRoZSByb3RhdGlvbiBhcm91bmQgdGhlIHktYXhpcwoxMi4gJHJvdGF0aW9uX3taX3tULTF9fSQsIGZpcnN0IHRlbXBvcmFsIGRlcml2YXRpdmUgb2YgdGhlIHJvdGF0aW9uIGFyb3VuZCB0aGUgei1heGlzCgpgYGB7ciBtMTIsIGNvbGxhcHNlZCA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgZGV2ID0gInN2ZyJ9CiMgY2FsY3VsYXRlIGZpcnN0IHRlbXBvcmFsIGRlcml2YXRpdmUKbW90aW9uX2RlbWVhbmVkX3RkMSA8LSBtb3Rpb25fZGVtZWFuZWQgJT4lCiAgc2FwcGx5KC4sIEZVTiA9IGZ1bmN0aW9uKHgpIGMoTkEsIGRpZmYoeCkpKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBtdXRhdGUoRHVyYXRpb24gPSBjKDE6bnJvdyguKSkpCiAgCiMgcmVuYW1lIGNvbHVtbnMKY29sbmFtZXMobW90aW9uX2RlbWVhbmVkX3RkMSkgPC0gYyhwYXN0ZTAobW90aW9uX3ZhcmlhYmxlcywgIl9URDEiKSwgIkR1cmF0aW9uIikKCiMgZ3JhcGggbW90aW9uIHBhcmFtZXRlcnMKZ3JhcGhfbW90aW9uKG1vdGlvbl9kZW1lYW5lZF90ZDEsICJGaXJzdCBUZW1wb3JhbCBEZXJpdmF0aXZlcyBvZiB0aGUgNi1SaWdpZCBCb2R5IE1vdGlvbiBQYXJhbWV0ZXJzIikKYGBgCgo8QlI+PEJSPgoKIyMgMTgtTW90aW9uIFJlZ3Jlc3NvcnMKClRoZSBuZXh0IHNpeCBtb3Rpb24gcGFyYW1ldGVycyBhcmUgdGhlIG9yaWdpbmFsIHNpeCByaWdpZCBib2R5IG1vdGlvbiBwYXJhbWV0ZXJzIHNxdWFyZWQ6CgoxMy4gJHRyYW5zbGF0aW9uX1heMiQsIHRyYW5zbGF0aW9uIGluIHRoZSB4LWRpcmVjdGlvbiBzcXVhcmVkCjE0LiAkdHJhbnNsYXRpb25fWV4yJCwgdHJhbnNsYXRpb24gaW4gdGhlIHktZGlyZWN0aW9uIHNxdWFyZWQKMTUuICR0cmFuc2xhdGlvbl9aXjIkLCB0cmFuc2xhdGlvbiBpbiB0aGUgei1kaXJlY3Rpb24gc3F1YXJlZAoxNi4gJHJvdGF0aW9uX1heMiQsIHJvdGF0aW9uIGFyb3VuZCB0aGUgeC1heGlzIHNxdWFyZWQKMTcuICRyb3RhdGlvbl9ZXjIkLCByb3RhdGlvbiBhcm91bmQgdGhlIHktYXhpcyBzcXVhcmVkCjE4LiAkcm90YXRpb25fWl4yJCwgcm90YXRpb24gYXJvdW5kIHRoZSB6LWF4aXMgc3F1YXJlZAoKYGBge3IgbTE4LCBjb2xsYXBzZWQgPSBUUlVFLCBkZXYgPSAic3ZnIn0KIyBzcXVhcmUgNi1yaWdpZCBib2R5IG1vdGlvbiBwYXJhbWV0ZXJzCm1vdGlvbl9kZW1lYW5lZF9zcSA8LSBtb3Rpb25fZGVtZWFuZWReMiAlPiUKICBhc190aWJibGUoKSAlPiUKICBtdXRhdGUoRHVyYXRpb24gPSBjKDE6bnJvdyguKSkpCiAgCiMgcmVuYW1lIGNvbHVtbnMKY29sbmFtZXMobW90aW9uX2RlbWVhbmVkX3NxKSA8LSBjKHBhc3RlMChtb3Rpb25fdmFyaWFibGVzLCAiX1NxdWFyZWQiKSwgIkR1cmF0aW9uIikKCiMgZ3JhcGggbW90aW9uIHBhcmFtZXRlcnMKZ3JhcGhfbW90aW9uKG1vdGlvbl9kZW1lYW5lZF9zcSwgIjYtUmlnaWQgQm9keSBNb3Rpb24gUGFyYW1ldGVycyBTcXVhcmVkIikKYGBgCgo8QlI+PEJSPgoKIyMgMjQtTW90aW9uIFJlZ3Jlc3NvcnMKClRoZSBuZXh0IHNpeCBtb3Rpb24gcGFyYW1ldGVycyBhcmUgdGhlIGZpcnN0IHRlbXBvcmFsIGRlcml2YXRpdmVzIHNxdWFyZWQuCgoxOS4gJHRyYW5zbGF0aW9uX3tYX3tULTF9fV4yJCwgZmlyc3QgdGVtcG9yYWwgZGVyaXZhdGl2ZSBvZiB0aGUgdHJhbnNsYXRpb24gaW4gdGhlIHgtZGlyZWN0aW9uIHNxdWFyZWQKMjAuICR0cmFuc2xhdGlvbl97WV97VC0xfX1eMiQsIGZpcnN0IHRlbXBvcmFsIGRlcml2YXRpdmUgb2YgdGhlIHRyYW5zbGF0aW9uIGluIHRoZSB5LWRpcmVjdGlvbiBzcXVhcmVkCjIxLiAkdHJhbnNsYXRpb25fe1pfe1QtMX19XjIkLCBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlIG9mIHRoZSB0cmFuc2xhdGlvbiBpbiB0aGUgei1kaXJlY3Rpb24gc3F1YXJlZAoyMi4gJHJvdGF0aW9uX3tYX3tULTF9fV4yJCwgZmlyc3QgdGVtcG9yYWwgZGVyaXZhdGl2ZSBvZiB0aGUgcm90YXRpb24gYXJvdW5kIHRoZSB4LWF4aXMgc3F1YXJlZAoyMy4gJHJvdGF0aW9uX3tZX3tULTF9fV4yJCBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlIG9mIHRoZSByb3RhdGlvbiBhcm91bmQgdGhlIHktYXhpcyBzcXVhcmVkCjI0LiAkcm90YXRpb25fe1pfe1QtMX19XjIkLCBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlIG9mIHRoZSByb3RhdGlvbiBhcm91bmQgdGhlIHotYXhpcyBzcXVhcmVkCgpgYGB7ciBtMjQsIGNvbGxhcHNlZCA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgZGV2ID0gInN2ZyJ9CgojIHNxdWFyZSB0aGUgZmlyc3QgdGVtcG9yYWwgZGVyaXZhdGl2ZQptb3Rpb25fZGVtZWFuZWRfdGQxX3NxIDwtIG1vdGlvbl9kZW1lYW5lZF90ZDFeMiAlPiUKICBhc190aWJibGUoKSAlPiUKICBtdXRhdGUoRHVyYXRpb24gPSAxOm5yb3coLikpIAoKIyByZW5hbWUgY29sdW1ucwpjb2xuYW1lcyhtb3Rpb25fZGVtZWFuZWRfdGQxX3NxKSA8LSBjKHBhc3RlMChtb3Rpb25fdmFyaWFibGVzLCAiX1REMV9TcXVhcmVkIiksICJEdXJhdGlvbiIpCgojIHBsb3QgbW90aW9uLnRkMS5zcSBpbnRvIG9uZSBmaWd1cmUKZ3JhcGhfbW90aW9uKG1vdGlvbl9kZW1lYW5lZF90ZDFfc3EsICJGaXJzdCBUZW1wb3JhbCBEZXJpdmF0aXZlcyBvZiB0aGUgNi1SaWdpZCBCb2R5IE1vdGlvbiBQYXJhbWV0ZXJzIFNxdWFyZWQiKQpgYGAKCjxCUj48QlI+CgojIyBSIENvZGUgRXhhbXBsZQpgYGB7ciBzY3JpcHQsIGV2YWwgPSBGQUxTRSwgY29sbGFwc2UgPSBGQUxTRSwgaW5jbHVkZSA9IFRSVUUsIGVjaG8gPSBUUlVFfQoKIyBmdW5jdGlvbiB0byBjcmVhdGUgMjQgbW90aW9uIHZhcmlhYmxlcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY3JlYXRlXzI0X21vdGlvbl92YXJpYWJsZXMgPC0gZnVuY3Rpb24oZGF0YXNldCkgewogICMgbWVhbi1jZW50ZXIgKGRlbWVhbikgLS0tLQogICMgc3VidHJhY3QgZWFjaCBzY29yZSBmcm9tIGl0cyByZXNwZWN0aXZlIGNvbHVtbiBtZWFuCiAgZGF0YXNldF9tb3Rpb25fZGVtZWFuZWQgPC0gc2NhbGUoeCA9IGRhdGFzZXQsIGNlbnRlciA9IFQsIHNjYWxlID0gRikKCiAgIyBmaXJzdCB0ZW1wb3JhbCBkZXJpdmF0aXZlcyAtLS0tCiAgIyBzdWJ0cmFjdCBtb3Rpb24gZnJvbSBwcmlvciBtb3Rpb24gdGltZSBwb2ludAogIGRhdGFzZXRfbW90aW9uX3RkMSA8LSBzYXBwbHkoZGF0YXNldF9tb3Rpb25fZGVtZWFuZWQsIEZVTiA9IGZ1bmN0aW9uKHgpIGMoTkEsIGRpZmYoeCkpKQoKICAjIGNvbWJpbmUgdGhlIHZhcmlhYmxlcyBpbnRvIG9uZSBkYXRhc2V0CiAgZGF0YXNldF9tb3Rpb24gPC0gZGF0YS5mcmFtZShkYXRhc2V0X21vdGlvbl9kZW1lYW5lZCwgZGF0YXNldF9tb3Rpb25fdGQxKQoKICAjIHNxdWFyZXMgLS0tLQogICMgc3F1YXJlIDYtcmlnaWQgYm9keSBtb3Rpb24gcGFyYW1ldGVycyBhbmQgaXRzIHRlbXBvcmFsIGRlcml2YXRpdmVzCiAgZGF0YXNldF9tb3Rpb25fc3F1YXJlZCA8LSBkYXRhc2V0X21vdGlvbl4yCgogICMgY29tYmluZSB0aGUgdmFyaWFibGVzIGludG8gb25lIGRhdGFzZXQKICBkYXRhc2V0X21vdGlvbiA8LSBkYXRhLmZyYW1lKGRhdGFzZXRfbW90aW9uLCBkYXRhc2V0X21vdGlvbl9zcXVhcmVkKQoKICByZXR1cm4oZGF0YXNldF9tb3Rpb24pCn0KCiMgbG9hZCA2LXJpZ2lkIGJvZHkgbW90aW9uIHBhcmFtZXRlcnMgZmlsZSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgYXNzaWduIHRvIGRhdGFzZXRfbW90aW9uCmRhdGFzZXRfbW90aW9uIDwtIHJlYWRfY3N2KGZpbGUuY2hvb3NlKCkpCgojIGNyZWF0ZSAyNCBtb3Rpb24gdmFyaWFibGVzIHVzaW5nIGZ1bmN0aW9uID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIGFzc2lnbiBvdXRwdXQgdG8gZGF0YXNldF9tb3Rpb25fMjQKZGF0YXNldF9tb3Rpb25fMjQgPC0gY3JlYXRlXzI0X21vdGlvbl92YXJpYWJsZXMoZGF0YXNldCA9IGRhdGFzZXRfbW90aW9uKQoKIyByZW5hbWUgY29sdW1ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBhc3NpZ24gb3JpZ2luYWwgbW90aW9uIHZhcmlhYmxlcwptb3Rpb25fdmFyaWFibGVzIDwtIGMoCiAgInRyYW5zbGF0aW9uX3giLCAidHJhbnNsYXRpb25feSIsICJ0cmFuc2xhdGlvbl96IiwKICAicm90YXRpb25feCIsICJyb3RhdGlvbl95IiwgInJvdGF0aW9uX3oiCikKCiMgcmVuYW1lIGNvbHVtbnMKY29sbmFtZXMoZGF0YXNldF9tb3Rpb25fMjQpIDwtIGMoCiAgbW90aW9uX3ZhcmlhYmxlcywKICBwYXN0ZTAobW90aW9uX3ZhcmlhYmxlcywgIl90ZDEiKSwKICBwYXN0ZTAobW90aW9uX3ZhcmlhYmxlcywgIl9zcXVhcmVkIiksCiAgcGFzdGUwKG1vdGlvbl92YXJpYWJsZXMsICJfdGQxX3NxdWFyZWQiKQopCgojIGV4cG9ydCAyNC1tb3Rpb24gdmFyaWFibGVzID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQp3cml0ZV9jc3YoeCA9IGRhdGFzZXRfbW90aW9uXzI0LCBmaWxlID0gImRhdGFzZXQtbW90aW9uLTI0LmNzdiIpCmBgYAoKPEJSPjxCUj4KCiMjIFJlZmVyZW5jZXMKCjxkaXYgY2xhc3M9ImRpdlJlZmVyZW5jZXMiPgpGcmlzdG9uLCBLLiBKLiwgV2lsbGlhbXMsIFMuLCBIb3dhcmQsIFIuLCBGcmFja293aWFrLCBSLiBTLiBKLiwgJiBUdXJuZXIsIFIuICgxOTk2KS4gTW92ZW1lbnQtcmVsYXRlZCBlZmZlY3RzIGluIGZNUkkgdGltZS1zZXJpZXMuICpNYWduZXRpYyBSZXNvbmFuY2UgaW4gTWVkaWNpbmUsIDM1KigzKSwgMzQ24oCTMzU1LiBodHRwOi8vZG9pLm9yZy8xMC4xMDAyL21ybS4xOTEwMzUwMzEyCgpZYW4sIEMuLUcuLCBDaGV1bmcsIEIuLCBLZWxseSwgQy4sIENvbGNvbWJlLCBTLiwgQ3JhZGRvY2ssIFIuIEMuLCBEaSBNYXJ0aW5vLCBBLiwg4oCmIE1pbGhhbSwgTS4gUC4gKDIwMTMpLiBBIGNvbXByZWhlbnNpdmUgYXNzZXNzbWVudCBvZiByZWdpb25hbCB2YXJpYXRpb24gaW4gdGhlIGltcGFjdCBvZiBoZWFkIG1pY3JvbW92ZW1lbnRzIG9uIGZ1bmN0aW9uYWwgY29ubmVjdG9taWNzLiAqTmV1cm9JbWFnZSwgNzYqLCAxODPigJMyMDEuIGh0dHA6Ly9kb2kub3JnLzEwLjEwMTYvai5uZXVyb2ltYWdlLjIwMTMuMDMuMDA0CjwvZGl2PgoKPCEtLSBkaXNxdXMgU1RBUlQgLS0+Cjxicj4KCjxocj4KCjxicj4KCjxkaXYgaWQ9ImRpc3F1c190aHJlYWQiPjwvZGl2Pgo8c2NyaXB0PgovKioKKiAgUkVDT01NRU5ERUQgQ09ORklHVVJBVElPTiBWQVJJQUJMRVM6IEVESVQgQU5EIFVOQ09NTUVOVCBUSEUgU0VDVElPTiBCRUxPVyBUTyBJTlNFUlQgRFlOQU1JQyBWQUxVRVMgRlJPTSBZT1VSIFBMQVRGT1JNIE9SIENNUy4KKiAgTEVBUk4gV0hZIERFRklOSU5HIFRIRVNFIFZBUklBQkxFUyBJUyBJTVBPUlRBTlQ6IGh0dHBzOi8vZGlzcXVzLmNvbS9hZG1pbi91bml2ZXJzYWxjb2RlLyNjb25maWd1cmF0aW9uLXZhcmlhYmxlcyovCi8qCnZhciBkaXNxdXNfY29uZmlnID0gZnVuY3Rpb24gKCkgewp0aGlzLnBhZ2UudXJsID0gJ2h0dHBzOi8vZWthcmlucG9uZ3BpcGF0LmNvbS9ibG9nX0NhbGN1bGF0ZV9Nb3Rpb25fUmVncmVzc29ycy5odG1sJzsgIC8vIFJlcGxhY2UgUEFHRV9VUkwgd2l0aCB5b3VyIHBhZ2UncyBjYW5vbmljYWwgVVJMIHZhcmlhYmxlCnRoaXMucGFnZS5pZGVudGlmaWVyID0gJ2Jsb2dfQ2FsY3VsYXRlX01vdGlvbl9SZWdyZXNzb3JzJzsgLy8gUmVwbGFjZSBQQUdFX0lERU5USUZJRVIgd2l0aCB5b3VyIHBhZ2UncyB1bmlxdWUgaWRlbnRpZmllciB2YXJpYWJsZQp9OwoqLwooZnVuY3Rpb24oKSB7IC8vIERPTidUIEVESVQgQkVMT1cgVEhJUyBMSU5FCnZhciBkID0gZG9jdW1lbnQsIHMgPSBkLmNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpOwpzLnNyYyA9ICdodHRwczovL2Vwb25ncGlwYXQuZGlzcXVzLmNvbS9lbWJlZC5qcyc7CnMuc2V0QXR0cmlidXRlKCdkYXRhLXRpbWVzdGFtcCcsICtuZXcgRGF0ZSgpKTsKKGQuaGVhZCB8fCBkLmJvZHkpLmFwcGVuZENoaWxkKHMpOwp9KSgpOwo8L3NjcmlwdD4KPG5vc2NyaXB0PlBsZWFzZSBlbmFibGUgSmF2YVNjcmlwdCB0byB2aWV3IHRoZSA8YSBocmVmPSJodHRwczovL2Rpc3F1cy5jb20vP3JlZl9ub3NjcmlwdCI+Y29tbWVudHMgcG93ZXJlZCBieSBEaXNxdXMuPC9hPjwvbm9zY3JpcHQ+CjwhLS0gZGlzcXVzIEVORCAtLT4K