Non-Linear Fit Types

Dont ask, “What is the best model for the job”. Rather, “What model is most theoretically sound.” (Singer & Willet, 2003, p.233)

  • Goodness of fits & examining residuals, improvement on fits are not what always drive these approaches. You must really think about what is driving the change you see.

Major Types

  • Polynomials (Power & Orthogonal) [useful generalizable approach to approximate complex growth curves]
    • You add predictors that reflect shapes (quadric = Arch/U, Cubic = S-shaped, Quartic = W/M-shaped, etc).
      • You must always have 1 data point more than the shape above a line (2 points for a line, 3 points for U, 4 points for W, etc).

Growth Curves: - Inverse Polynomials (1/Polynomials) & Hyperbola [Such as in learning theory] - Exponential (Positive & Negative Exponential, logarithmic, Power law) [seen in biological growth or perception studies] - These often can be accomplished by either transforming the DV or our measure of time - We will cover the bold ones today - Always keep in mind your cadence when you transform and remember that time can be in any unit.

Polynomials

Study Design

You have 10 participants. Five are assigned to a standard treatment of desensitization therapy, and the other five undergo a radically new type of treatment. In early sessions of desensitization therapy, clients know that they are going to be exposed to their phobia and often get anxious as their therapy time approaches. You try a radical new approach in trying to increase the degree of anxiety the experience before the actual therapy session begins. You have participants waiting outside of the door 12 minutes before the start of the therapy time, and you have a mock therapy session going on inside of the room. In the standard condition, participants do not hear what’s going into the therapy room, but they can hear voices (but not the words). In the radical condition, participants hear nothing but the voices for the first six minutes and then they hear loud yelling as a mock patient tells the therapist to keep that giant nasty spider away from them. You expect that after they hear the yelling start, they will work themselves up into anxiety frenzy just before the therapy session starts. You record from the participant a physiological measurement of anxiety once a minute which is normalized.

Before moving on to the main study, you want to make sure your manipulation worked.

Visualize Raw Data

Visualize your fit before you model is a good way to avoid problems down the line in terms of non-sensical fits: DV =Anxiety score (0 = Low to 11 High), IV = Time (0-11).

Loess Line

  • Locally Weighted Scatterplot Smoothing
  • Does lots of regressions on small sections of the scatter plot to get the best fit (curvy) line
  • Lets you visualize complex relationships, but it can overfit the relationship (so be careful)
  • Good diagnostic tool to plot what is happening on “average.” Download Data
HighAx1<-read.csv("Mixed/NonLinearTime1.csv")
HighAx1$Type<-factor(HighAx1$Treatment, 
                     levels=c(0,1),
                     labels=c("Standard", "Radical"))
HighAx1$Sound.F<-factor(HighAx1$Sound, 
                     levels=c(1,0),
                     labels=c("Screaming","Voices"))
HighAx1$Subject<-as.factor(HighAx1$Subject)

Speg<-ggplot(data = HighAx1, aes(x=Time,y=Anxiety,  color=Type))+
  geom_smooth(aes(group=Type),method='loess', se=FALSE, color='black', size=2)+
    geom_point(aes(group=Subject))+
  geom_line(aes(group=Subject))+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(0,11,1))+
  ggtitle("By Subject") +
  theme(legend.position = "top")+
  theme_bw()
Speg

Power Polynomials

  • Models that simply curves, such as quadratic or cubic effects
  • Linear: \(Y =B_{1}X + B_0\)
  • Quadratic: \(Y = B_{1}X + B_{2}X^2 + B_0\)
  • Cubic: \(Y = B_{1}X + B_{2}X^2 + B_{3}X^3 + B_0\)

Visualize Subject Level

  • We can add smoother per subject with each Power Polynomials (method='lm')
  • This looks Linear formula=y~x or Quadratic formula=y~poly(x,2) per subject

Discontinuity

  • The radical therapy does not differ from the standard therapy until 6 minutes in, so you will need to account for that, but quadratic seems to be more logical fit.

Fitting Power Polynomials

  • Forward Selection Approach
    • Model 1: Linear fit
    • Model 2: Quadratic fit
    • Model i: keep going up until you are satisfied,
    • R Code you can add them manually, Anxiety + I(Anxiety^2) or with the poly command, poly(Anxiety, 2, raw=T)
  • Do we want to use Type of treatment (Standard or radical) or Sound (voice whole-time vs. voice-to-screaming) [between subject condition ways of thinking about it]
  • Given that standard therapy is identical to radical therapy up to 6 mins, you really want to model how the screaming changes their anxiety level, AFTER the screaming starts. We will use sound factor and not of Type therapy factor.

Linear Model Fit

  • Time will not be centered (Yet)
Linear.PP<-lmer(Anxiety ~ Time*Sound.F+ (1+Time|Subject), data=HighAx1, REML=FALSE)
summary(Linear.PP, correlation=FALSE)
## Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's
##   method [lmerModLmerTest]
## Formula: Anxiety ~ Time * Sound.F + (1 + Time | Subject)
##    Data: HighAx1
## 
##      AIC      BIC   logLik deviance df.resid 
##    250.6    272.9   -117.3    234.6      112 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.79325 -0.59057 -0.02637  0.57932  2.86639 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr
##  Subject  (Intercept) 0.032526 0.18035      
##           Time        0.003536 0.05947  1.00
##  Residual             0.337005 0.58052      
## Number of obs: 120, groups:  Subject, 10
## 
## Fixed effects:
##                     Estimate Std. Error        df t value Pr(>|t|)    
## (Intercept)         -2.04852    0.54188 110.78271  -3.780 0.000254 ***
## Time                 0.90607    0.06558  99.84501  13.815  < 2e-16 ***
## Sound.FVoices        3.88930    0.54790 109.84025   7.099 1.31e-10 ***
## Time:Sound.FVoices  -0.71173    0.06924  99.35035 -10.280  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## optimizer (nloptwrap) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')
  • Note the random correlation would need to be removed

  • Check the residuals of the model fit

plot(Linear.PP)

  • Those are very odd. So clearly linear fit is not correct
library(effects)
Linear.PP.Fixed<-Effect(c("Time","Sound.F"),Linear.PP,
                        xlevels=list(Time=seq(0,11,1)))

Linear.PP.Fixed<-as.data.frame(Linear.PP.Fixed)

L.PP.Plot <-ggplot(data = Linear.PP.Fixed, 
                            aes(x = Time, y =fit,group=Sound.F))+
  geom_line(aes(color=Sound.F),size=2)+
  geom_ribbon(aes(ymin=lower, ymax=upper),alpha=.2)+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(0,11,1))+
  ggtitle("By Subject: Linear Power Poly Fit") +
  theme(legend.position = "bottom")+theme_bw()
L.PP.Plot

  • We should not force the model to predict screaming condition BEFORE 6 minutes because that never occurs
  • We can manually remove that false prediction and replot
library(dplyr)
Linear.PP.Fixed.Cleaned<-Linear.PP.Fixed %>%
  dplyr::filter(Sound.F=="Voices" | (Sound.F!="Voices" & Time>=6))

L.PP.Plot.2 <-ggplot(data = Linear.PP.Fixed.Cleaned, 
                            aes(x = Time, y =fit,group=Sound.F))+
  geom_line(aes(color=Sound.F),size=2)+
  geom_ribbon(aes(ymin=lower, ymax=upper),alpha=.2)+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(0,11,1))+
  ggtitle("By Subject: Linear Power Poly Fit") +
  theme(legend.position = "bottom")+theme_bw()
L.PP.Plot.2

Quadratic Model Fit

  • Time will not be centered (Yet)
Quad.PP<-lmer(Anxiety ~ stats::poly(Time,2,raw=TRUE)*Sound.F+ (1+stats::poly(Time,2,raw=TRUE)|Subject), data=HighAx1, REML=FALSE)
summary(Quad.PP, correlation=FALSE)
## Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's
##   method [lmerModLmerTest]
## Formula: 
## Anxiety ~ stats::poly(Time, 2, raw = TRUE) * Sound.F + (1 + stats::poly(Time,  
##     2, raw = TRUE) | Subject)
##    Data: HighAx1
## 
##      AIC      BIC   logLik deviance df.resid 
##     19.3     55.5      3.4     -6.7      107 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.38917 -0.57582 -0.07594  0.60019  2.81510 
## 
## Random effects:
##  Groups   Name                              Variance Std.Dev. Corr       
##  Subject  (Intercept)                       0.855058 0.92469             
##           stats::poly(Time, 2, raw = TRUE)1 0.141643 0.37635  -1.00      
##           stats::poly(Time, 2, raw = TRUE)2 0.001485 0.03854   0.99 -1.00
##  Residual                                   0.031598 0.17776             
## Number of obs: 120, groups:  Subject, 10
## 
## Fixed effects:
##                                                  Estimate Std. Error        df
## (Intercept)                                       7.10066    0.96348 109.71735
## stats::poly(Time, 2, raw = TRUE)1                -1.31541    0.25254  96.39103
## stats::poly(Time, 2, raw = TRUE)2                 0.13077    0.01797  48.64731
## Sound.FVoices                                    -5.64655    0.91925  98.94311
## stats::poly(Time, 2, raw = TRUE)1:Sound.FVoices   1.76599    0.22433  99.33480
## stats::poly(Time, 2, raw = TRUE)2:Sound.FVoices  -0.15638    0.01374 102.57485
##                                                 t value Pr(>|t|)    
## (Intercept)                                       7.370 3.40e-11 ***
## stats::poly(Time, 2, raw = TRUE)1                -5.209 1.08e-06 ***
## stats::poly(Time, 2, raw = TRUE)2                 7.278 2.56e-09 ***
## Sound.FVoices                                    -6.143 1.70e-08 ***
## stats::poly(Time, 2, raw = TRUE)1:Sound.FVoices   7.872 4.42e-12 ***
## stats::poly(Time, 2, raw = TRUE)2:Sound.FVoices -11.379  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## optimizer (nloptwrap) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')
  • Note the random correlations would need to be removed

  • Notice they are all significant

  • Check the residuals of the model fit

plot(Quad.PP)

  • Much better than the linear fit

Visualize Models

  • Left plot = False Prediction before 6 Minutes, Right plot remove False Prediction
Quad.PP.Fixed<-Effect(c("Time","Sound.F"),Quad.PP,
                        xlevels=list(Time=seq(0,11,1)))

Quad.PP.Fixed.Cleaned<-Quad.PP.Fixed %>%
  dplyr::filter(Sound.F=="Voices" | (Sound.F!="Voices" & Time>=6))

Test Improvement

  • The quadratic fit is better
anova(Linear.PP,Quad.PP)
## Data: HighAx1
## Models:
## Linear.PP: Anxiety ~ Time * Sound.F + (1 + Time | Subject)
## Quad.PP: Anxiety ~ stats::poly(Time, 2, raw = TRUE) * Sound.F + (1 + stats::poly(Time, 2, raw = TRUE) | Subject)
##           npar    AIC     BIC   logLik deviance  Chisq Df Pr(>Chisq)    
## Linear.PP    8 250.59 272.895 -117.298   234.59                         
## Quad.PP     13  19.27  55.508    3.365    -6.73 241.33  5  < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Orthogonal Polynomials

  • Problem with power polynomials is interpreting them independently
  • The higher order terms depend on, the lower order terms
  • Thus linear term can be significant cause the higher order term is significant, but there is no linear trend in the data
    • This is because power polynomials correlate with each other (they are not unique predictors)
    • For Example:
LinearTerms<-seq(0,11,1)
QuadTerms<-LinearTerms^2
  • Correlation between linear and quadratic is r = 0.964. Thus they are multicollinear!
  • Technically, it’s OK that they are multicollinear, but you cannot interpret each term without looking at the other (the p-values will be problematic)
  • The solution to make the linear and quadratic terms correlate with each at zero (just as we did with ANOVA)
  • we can use the poly code in R, but set raw = F (which is actually the default. This is also centering the data)
  • Here is an example of what R is going to do:
O.Poly<-stats::poly(LinearTerms,2)
  • Correlation between linear and quadratic is now r = 0. Meaning they are no longer multicollinear. Now you can see if the linear and quadratic are independently significant.

Quadratic Model Fit

  • Time will not be centered (Yet)
Linear.OP<-lmer(Anxiety ~ stats::poly(Time,1)*Sound.F+ 
                  (1+stats::poly(Time,1)|Subject), data=HighAx1, REML=FALSE)

Quad.OP<-lmer(Anxiety ~ stats::poly(Time,2)*Sound.F+ 
                (1+stats::poly(Time,2)|Subject), data=HighAx1, REML=FALSE)

summary(Quad.OP, correlation=FALSE)
## Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's
##   method [lmerModLmerTest]
## Formula: Anxiety ~ stats::poly(Time, 2) * Sound.F + (1 + stats::poly(Time,  
##     2) | Subject)
##    Data: HighAx1
## 
##      AIC      BIC   logLik deviance df.resid 
##     19.1     55.3      3.4     -6.9      107 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.40751 -0.58027 -0.07731  0.60156  2.84083 
## 
## Random effects:
##  Groups   Name                  Variance Std.Dev. Corr     
##  Subject  (Intercept)            0.27596 0.5253            
##           stats::poly(Time, 2)1  5.39867 2.3235   0.91     
##           stats::poly(Time, 2)2 23.35501 4.8327   0.99 0.85
##  Residual                        0.03103 0.1762            
## Number of obs: 120, groups:  Subject, 10
## 
## Fixed effects:
##                                     Estimate Std. Error       df t value
## (Intercept)                           5.3797     0.2959  68.0749  18.183
## stats::poly(Time, 2)1                 4.6504     3.1277 110.7452   1.487
## stats::poly(Time, 2)2                15.1064     2.1496  34.9048   7.028
## Sound.FVoices                        -2.5275     0.2547 109.3145  -9.922
## stats::poly(Time, 2)1:Sound.FVoices   1.7345     3.1431 107.5724   0.552
## stats::poly(Time, 2)2:Sound.FVoices -18.0650     1.5740 103.4921 -11.477
##                                     Pr(>|t|)    
## (Intercept)                          < 2e-16 ***
## stats::poly(Time, 2)1                  0.140    
## stats::poly(Time, 2)2               3.57e-08 ***
## Sound.FVoices                        < 2e-16 ***
## stats::poly(Time, 2)1:Sound.FVoices    0.582    
## stats::poly(Time, 2)2:Sound.FVoices  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## optimizer (nloptwrap) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')
  • Note the random correlations are probably too high and we could remove them
  • Notice they are not all significant!
    • In both cases the quadradic effect is only significant. This is different from the power polynomials.
  • The one problem is the estimates have lost meaning (really only the sign is meaningful)
  • The effect package will read that we used poly and we can predict as normal, you will notice there is no difference in final plot. The difference is we interpret the pattern of t-values.
Quad.OP.Fixed<-Effect(c("Time","Sound.F"),Quad.OP,
                        xlevels=list(Time=seq(0,11,1)))
Quad.OP.Fixed<-as.data.frame(Quad.OP.Fixed)

Quad.OP.Fixed.Cleaned<-Quad.OP.Fixed %>%
  dplyr::filter(Sound.F=="Voices" | (Sound.F!="Voices" & Time>=6))

Q.OP.Plot <-ggplot(data = Quad.OP.Fixed.Cleaned, 
                            aes(x = Time, y =fit,group=Sound.F))+
  geom_line(aes(color=Sound.F),size=2)+
  geom_ribbon(aes(ymin=lower, ymax=upper),alpha=.2)+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(0,11,1))+
  ggtitle("By Subject: Quad Otho Poly Fit") +
  theme(legend.position = "bottom")+theme_bw()
Q.OP.Plot

Results

The results show an overall significant positive quadratic effect on anxiety (arch-shape) for the standard treatment condition (where participants are waiting outside of the therapy room with no intervention). This suggests that as participants wait outside of the room, the increase as time goes on in the amount of anxiety feel relative to the treatment they know they’re about to undergo, but that anxiety starts to level off and even drops down a bit just before their scheduled therapy time. However, those assigned to the condition where the mock participant starts screaming at around six minutes, we see a significant discontinuity (the effect of the sound condition) where there is an immediate jump up in their anxiety level and we see a significantly negative quadratic effect (U-shape) where they get more and more anxious as their scheduled therapy time approaches. In other words, your manipulation was quite successful.

Visualizing Complete Final Model Fit

  • Since this is a balanced study, we can overall model fit and the per subject fit (with dots = raw subject data)
HighAx1$QuadModelFit<-predict(Quad.OP)
Speg.Final<-ggplot()+
  geom_point(data = HighAx1, aes(x=Time,y=Anxiety, color=Sound.F))+
  geom_line(data = HighAx1, aes(x=Time,y=QuadModelFit, color=Sound.F, group=Subject))+
  geom_line(data = Quad.OP.Fixed.Cleaned, aes(x = Time, y =fit,group=Sound.F),color='blue', size=2)+
  ylab("Model Fitted Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(0,11,1))+
  ggtitle("By Subject Model Fit") +
  theme(legend.position = "none")+theme_bw()
Speg.Final

Other Types of Growth Curves

  • These are models that cannot be fit with polynomials
  • Logarithmic (These functions are often physical; These look more sigmoidal)
    • \(c^Y = dX\)
    • These are look more sigmoidal
  • Exponential growth (like unchecked population growth)
    • \(Y = ce^{dx}\)
      • Note: Euler’s number, \(e =\) 2.7182818, is an irrational math constant and the base value of the natural log
  • Power Law growth (Common function in motor control and perception experiments)
    • \(Y = cX^d\)
  • In all these cases the solution is often to transform DV or Time. In power-law, you might need to transform both

Growth Curve Example

Now that you have sufficiently made half of your client’s anxiety levels go to “11” you hypothesize that given that they start out more anxious they will respond to the treatment more given that you’re putting them in the state of their maximal level of anxiety in which for you to conduct the therapy/exposure session. Thus, you expect those who came in more anxious will have the lowest levels of anxiety at the end of treatment versus those who started out with normal levels of anxiety preceding treatment. You take their Anxiety score every 3.3 mins throughout a 60-minute session

Visualize Raw Data

  • DV =Anxiety score (0 = Low to 11 High)
  • IV1 = Time (1-18)
  • IV2 = Therapy Type (Standard or Radical)
HighAx2<-read.csv("Mixed/GrowthCurve.csv")
HighAx2$Type<-factor(HighAx2$Treatment, 
                     levels=c(0,1),
                     labels=c("Standard", "Radical"))
HighAx2$Subject<-as.factor(HighAx2$Subject)

Speg.3<-ggplot(data = HighAx2, aes(x=Time,y=Anxiety,  color=Type))+
  geom_smooth(aes(group=Type),method='loess', se=FALSE, color='black', size=2)+
    geom_point(aes(group=Subject))+
  geom_line(aes(group=Subject))+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(1,18,1))+
  ggtitle("By Subject") +
  theme(legend.position = "bottom")+theme_bw()
Speg.3

Transforms

  • Polynomials might not be the best fit. Let’s try to straighten the data out as this looks somewhat exponential: Log DV | Log IV

  • Taking the log of the IV (Time) works best
Linear.Study2<-lmer(Anxiety ~ Time*Type+ (1+Time|Subject), data=HighAx2, REML=FALSE)
Quad.Study2<-lmer(Anxiety ~ stats::poly(Time,2)*Type+ (1+stats::poly(Time,2)|Subject), data=HighAx2, REML=FALSE)
  • Check the residuals of the model fit

  • The linear fit is terrible. The quad fit has weirdness in the tails

Log-Linear Model Fit

  • Time must start at 1 [as log(0) = inf]
Linear.Log<-lmer(Anxiety ~ log(Time)*Type+ (1+log(Time)|Subject), data=HighAx2, REML=FALSE)
  • Residuals look very good
plot(Linear.Log, correlation=FALSE)

  • Model Fit
summary(Linear.Log, correlation=FALSE)
## Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's
##   method [lmerModLmerTest]
## Formula: Anxiety ~ log(Time) * Type + (1 + log(Time) | Subject)
##    Data: HighAx2
## 
##      AIC      BIC   logLik deviance df.resid 
##    187.1    212.7    -85.6    171.1      172 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.95201 -0.70486  0.00995  0.74344  1.99901 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr 
##  Subject  (Intercept) 0.03067  0.1751        
##           log(Time)   0.05376  0.2319   -1.00
##  Residual             0.12920  0.3594        
## Number of obs: 180, groups:  Subject, 10
## 
## Fixed effects:
##                       Estimate Std. Error      df t value Pr(>|t|)    
## (Intercept)             7.3780     0.1313 16.0136  56.200  < 2e-16 ***
## log(Time)              -0.6472     0.1145 10.2174  -5.652 0.000196 ***
## TypeRadical             3.1273     0.1857 16.0136  16.844 1.31e-11 ***
## log(Time):TypeRadical  -2.5155     0.1620 10.2174 -15.532 1.94e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## optimizer (nloptwrap) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')
  • Notice the random correlations; they would need to be removed.
Linear.Log.Fixed<-Effect(c("Time","Type"),Linear.Log,
                        xlevels=list(Time=seq(1,18,1)))

Linear.Log.Fixed<-as.data.frame(Linear.Log.Fixed)

L.Log.Plot <-ggplot(data = Linear.Log.Fixed, 
                            aes(x = Time, y =fit,group=Type))+
  geom_line(aes(color=Type),size=2)+
  geom_ribbon(aes(ymin=lower, ymax=upper),alpha=.2)+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(1,18,1))+
  ggtitle("By Subject: Linear Log Fit") +
  theme(legend.position = "bottom")+theme_bw()
L.Log.Plot

Results

While those in the standard therapy condition did show reduced anxiety by the end of the session, those in the radical therapy groups showed both significantly higher starting anxiety level than the standard group, and they showed significantly more decay in their anxiety level as indicated by the significant negative interaction.

Visualizing Complete Final Model Fit

  • Plot the overall model fit and the per subject fit (with dots = raw subject data)
HighAx2$LogModelFit<-predict(Linear.Log)
Speg.Final.Study.2<-ggplot()+
  geom_point(data = HighAx2, aes(x=Time,y=Anxiety, color=Type))+
  geom_line(data = HighAx2, aes(x=Time,y=LogModelFit, color=Type, group=Subject), linetype=2)+
  geom_line(data = Linear.Log.Fixed, aes(x = Time, y =fit, color=Type,group=Type), size=2)+
  ylab("Model Fitted Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(1,18,1))+
  ggtitle("By Subject Model Log Fit") +
  theme(legend.position = "none")+theme_bw()
Speg.Final.Study.2

Growth Curve with a Discontinuity

You want to replicate and extend study 2 from above, but you make the treatment more radical. You know that those in the high anxiety condition (those that went to “11”) will start without more anxious and they-they will respond to the treatment more, but the might also respond to rapid onset of exposure better and you can cure them faster. Clients in both groups (standard and radical) undergo rapid exposure. You pull a lever at roughly the 35 min mark and they are dropped into a ball-pit fill with tarantulas. You expect those who in the standard group to respond poorly to their new situation, while those people in the radical group were prepared for the worst and will improve. Again, you take their Anxiety score every 3.3 mins throughout a 60-minute session.

Visualize Raw Data

  • DV =Anxiety score (0 = Low to 11 High)
  • IV1 = Time (1-18)
  • IV2 = Therapy Type (Standard or Radical)
  • IV3 =Spider pit (from Time 10-18)
HighAx3<-read.csv("Mixed/Study3.csv")
HighAx3$Type<-factor(HighAx3$Treatment, 
                     levels=c(0,1),
                     labels=c("Standard", "Radical"))
HighAx3$Location<-factor(HighAx3$Pit, 
                     levels=c(0,1),
                     labels=c("Office", "Spider Pit"))
HighAx3$Subject<-as.factor(HighAx3$Subject)

Speg.Study3<-ggplot(data = HighAx3, aes(x=Time,y=Anxiety,  color=Location))+
  facet_grid(~Type)+
  geom_smooth(aes(group=Location),method='loess', se=FALSE, color='black', size=2)+
    geom_point(aes(group=Subject))+
  geom_line(aes(group=Subject))+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(1,18,1))+
  ggtitle("By Subject") +
  theme(legend.position = "bottom")+theme_bw()
Speg.Study3

Transforms

  • Like before lets log Time
Speg.Study3.log<-ggplot(data = HighAx3, aes(x=log(Time),y=Anxiety,  color=Location))+
  facet_grid(~Type)+
  geom_smooth(aes(group=Location),method='loess', se=FALSE, color='black', size=2)+
    geom_point(aes(group=Subject))+
  geom_line(aes(group=Subject))+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(1,18,1))+
  ggtitle("By Subject") +
  theme(legend.position = "bottom")+theme_bw()
Speg.Study3.log

  • Taking the log again makes sense

Log-Linear Model Fit with Discontinuity

  • In this case Location was a within-subject variable!
Linear.Log.Study3<-lmer(Anxiety ~ log(Time)*Type*Location+ 
                          (1+log(Time)*Location|Subject), data=HighAx3, REML=FALSE)
summary(Linear.Log.Study3, correlation=FALSE)
## Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's
##   method [lmerModLmerTest]
## Formula: Anxiety ~ log(Time) * Type * Location + (1 + log(Time) * Location |  
##     Subject)
##    Data: HighAx3
## 
##      AIC      BIC   logLik deviance df.resid 
##    252.8    313.5   -107.4    214.8      161 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.37617 -0.63871 -0.06243  0.57808  2.67636 
## 
## Random effects:
##  Groups   Name                         Variance Std.Dev. Corr             
##  Subject  (Intercept)                  0.15304  0.3912                    
##           log(Time)                    0.01339  0.1157   -0.43            
##           LocationSpider Pit           4.77592  2.1854   -0.93  0.06      
##           log(Time):LocationSpider Pit 0.86557  0.9304    0.85  0.12 -0.98
##  Residual                              0.14288  0.3780                    
## Number of obs: 180, groups:  Subject, 10
## 
## Fixed effects:
##                                          Estimate Std. Error       df t value
## (Intercept)                               5.92448    0.21667 10.65102  27.343
## log(Time)                                -0.61109    0.09267 16.65659  -6.594
## TypeRadical                               2.55865    0.30642 10.65102   8.350
## LocationSpider Pit                        0.82939    1.39768 12.92357   0.593
## log(Time):TypeRadical                    -2.63274    0.13105 16.65659 -20.089
## log(Time):LocationSpider Pit              1.25798    0.56317 12.22484   2.234
## TypeRadical:LocationSpider Pit            6.59739    1.97662 12.92357   3.338
## log(Time):TypeRadical:LocationSpider Pit -2.58070    0.79644 12.22485  -3.240
##                                          Pr(>|t|)    
## (Intercept)                              3.28e-11 ***
## log(Time)                                5.04e-06 ***
## TypeRadical                              5.36e-06 ***
## LocationSpider Pit                        0.56315    
## log(Time):TypeRadical                    4.14e-13 ***
## log(Time):LocationSpider Pit              0.04492 *  
## TypeRadical:LocationSpider Pit            0.00538 ** 
## log(Time):TypeRadical:LocationSpider Pit  0.00693 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
  • We could reduce the random structure by removing the correlations or by running parsmonous approach, but we will leave this for now.
Linear.Log.Fixed.S3<-Effect(c("Time","Type","Location"),Linear.Log.Study3,
                        xlevels=list(Time=seq(1,18,1)))

Linear.Log.Fixed.S3<-as.data.frame(Linear.Log.Fixed.S3)
Linear.Log.Fixed.S3$Type<-factor(Linear.Log.Fixed.S3$Type, 
                     levels=c("Standard", "Radical"),
                     labels=c("Standard", "Radical"))

Linear.Log.Fixed.S3.Cleaned<-Linear.Log.Fixed.S3 %>%
  dplyr::filter(Location=="Office" & Time<=10| (Location!="Office" & Time>=10))


L.Log.Plot.S3 <-ggplot(data = Linear.Log.Fixed.S3.Cleaned, 
                            aes(x = Time, y =fit))+
  facet_grid(~Type)+
  geom_line(size=1, color='red')+
  geom_ribbon(aes(ymin=lower, ymax=upper),alpha=.2)+
  ylab("Anxiety")+xlab("Time")+
  scale_y_continuous(breaks=seq(0,11,1))+
  scale_x_continuous(breaks=seq(1,18,1))+
  ggtitle("By Subject: Linear Log Fit") +
  geom_vline(xintercept=10)+
  annotate("text", x = 13, y = 9, label = "Spider Pit")+
  theme(legend.position = "bottom")+theme_bw()
L.Log.Plot.S3

Results

In both standard and radical therapy, dropping them into the spider pit caused them to have increased anxiety, but only the those in radical therapy were able to calm down.

Notes

Always try to follow what you see and remember you are modeling at the level of the individual subject (when the subject is the level 2). These types of model are easy to over-fit (for example too many polynomials). However, underfitting can be equally as bad. Many people argue if the linear effect does not change the “story” don’t bother with the higher order. While that may be true sometimes, you can cause type II error (as the linear fit maybe weak) or even type S issues. Imagine you have a cubic effect and trying to run a line through it as the first and last data points yield the sign (but that might miss where the bulk of the data fall). Proper growth curve modeling requires careful graphing and stepwise modeling.

Generalized Additive (Mixed) Models

GAMMs are quickly becoming a new way to merge more powerful non-linear smoothing (such as using splines) to fit complex functions in the mixed model framework. They are useful for things where the shapes are rather crazy (like neuron firing). These are more bleeding edge methods and work on them is advancing quickly. Soon they will probably replace more simplistic growth curve mixed models, but as of today, they are not as flexible in the types of random structures they can handle.

LS0tDQp0aXRsZTogJ05vbi1MaW5lYXIgVGltZSAmIERpc2NvbnRpbnVpdGllcycNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBmb250c2l6ZTogOHB0DQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQotLS0NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZSA9IFRSVUUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSAgRkFMU0UpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTQuMjUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD00LjApDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduPSdjZW50ZXInKSANCmtuaXRyOjpvcHRzX2NodW5rJHNldChyZXN1bHRzPSdob2xkJykgDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGxtZTQpDQp0aGVtZV9zZXQodGhlbWVfYncoKSkNCmBgYA0KDQpccGFnZWJyZWFrDQoNCiMgTm9uLUxpbmVhciBGaXQgVHlwZXMgDQo+IERvbnQgYXNrLCAiV2hhdCBpcyB0aGUgYmVzdCBtb2RlbCBmb3IgdGhlIGpvYiIuIFJhdGhlciwgIldoYXQgbW9kZWwgaXMgbW9zdCB0aGVvcmV0aWNhbGx5IHNvdW5kLiIgKFNpbmdlciAmIFdpbGxldCwgMjAwMywgcC4yMzMpDQoNCi0gR29vZG5lc3Mgb2YgZml0cyAmIGV4YW1pbmluZyByZXNpZHVhbHMsIGltcHJvdmVtZW50IG9uIGZpdHMgYXJlIG5vdCB3aGF0IGFsd2F5cyBkcml2ZSB0aGVzZSBhcHByb2FjaGVzLiBZb3UgbXVzdCByZWFsbHkgdGhpbmsgYWJvdXQgd2hhdCBpcyBkcml2aW5nIHRoZSBjaGFuZ2UgeW91IHNlZS4gIA0KDQojIyBNYWpvciBUeXBlcw0KLSBQb2x5bm9taWFscyAoUG93ZXIgJiBPcnRob2dvbmFsKSBbdXNlZnVsIGdlbmVyYWxpemFibGUgYXBwcm9hY2ggdG8gYXBwcm94aW1hdGUgY29tcGxleCBncm93dGggY3VydmVzXQ0KICAgIC0gWW91IGFkZCBwcmVkaWN0b3JzIHRoYXQgcmVmbGVjdCBzaGFwZXMgKHF1YWRyaWMgPSBBcmNoL1UsIEN1YmljID0gUy1zaGFwZWQsIFF1YXJ0aWMgPSBXL00tc2hhcGVkLCBldGMpLiANCiAgICAgICAgLSBZb3UgbXVzdCBhbHdheXMgaGF2ZSAxIGRhdGEgcG9pbnQgbW9yZSB0aGFuIHRoZSBzaGFwZSBhYm92ZSBhIGxpbmUgKDIgcG9pbnRzIGZvciBhIGxpbmUsIDMgcG9pbnRzIGZvciBVLCA0IHBvaW50cyBmb3IgVywgZXRjKS4gDQoNCkdyb3d0aCBDdXJ2ZXM6DQotIEludmVyc2UgUG9seW5vbWlhbHMgKDEvUG9seW5vbWlhbHMpICYgSHlwZXJib2xhIFtTdWNoIGFzIGluIGxlYXJuaW5nIHRoZW9yeV0NCi0gRXhwb25lbnRpYWwgKFBvc2l0aXZlICYgKk5lZ2F0aXZlIEV4cG9uZW50aWFsKiwgbG9nYXJpdGhtaWMsIFBvd2VyIGxhdykgW3NlZW4gaW4gYmlvbG9naWNhbCBncm93dGggb3IgcGVyY2VwdGlvbiBzdHVkaWVzXQ0KICAgIC0gVGhlc2Ugb2Z0ZW4gY2FuIGJlIGFjY29tcGxpc2hlZCBieSBlaXRoZXIgdHJhbnNmb3JtaW5nIHRoZSBEViBvciBvdXIgbWVhc3VyZSBvZiB0aW1lDQotIFdlIHdpbGwgY292ZXIgdGhlIGJvbGQgb25lcyB0b2RheQ0KLSBBbHdheXMga2VlcCBpbiBtaW5kIHlvdXIgY2FkZW5jZSB3aGVuIHlvdSB0cmFuc2Zvcm0gYW5kIHJlbWVtYmVyIHRoYXQgdGltZSBjYW4gYmUgaW4gYW55IHVuaXQuDQoNCg0KIyBQb2x5bm9taWFscw0KIyMgU3R1ZHkgRGVzaWduDQo+IFlvdSBoYXZlIDEwIHBhcnRpY2lwYW50cy4gRml2ZSBhcmUgYXNzaWduZWQgdG8gYSBzdGFuZGFyZCB0cmVhdG1lbnQgb2YgZGVzZW5zaXRpemF0aW9uIHRoZXJhcHksIGFuZCB0aGUgb3RoZXIgZml2ZSB1bmRlcmdvIGEgcmFkaWNhbGx5IG5ldyB0eXBlIG9mIHRyZWF0bWVudC4gSW4gZWFybHkgc2Vzc2lvbnMgb2YgZGVzZW5zaXRpemF0aW9uIHRoZXJhcHksIGNsaWVudHMga25vdyB0aGF0IHRoZXkgYXJlIGdvaW5nIHRvIGJlIGV4cG9zZWQgdG8gdGhlaXIgcGhvYmlhIGFuZCBvZnRlbiBnZXQgYW54aW91cyBhcyB0aGVpciB0aGVyYXB5IHRpbWUgYXBwcm9hY2hlcy4gWW91IHRyeSBhIHJhZGljYWwgbmV3IGFwcHJvYWNoIGluIHRyeWluZyB0byBpbmNyZWFzZSB0aGUgZGVncmVlIG9mIGFueGlldHkgdGhlIGV4cGVyaWVuY2UgYmVmb3JlIHRoZSBhY3R1YWwgdGhlcmFweSBzZXNzaW9uIGJlZ2lucy4gIFlvdSBoYXZlIHBhcnRpY2lwYW50cyB3YWl0aW5nIG91dHNpZGUgb2YgdGhlIGRvb3IgMTIgbWludXRlcyBiZWZvcmUgdGhlIHN0YXJ0IG9mIHRoZSB0aGVyYXB5IHRpbWUsIGFuZCB5b3UgaGF2ZSBhIG1vY2sgdGhlcmFweSBzZXNzaW9uIGdvaW5nIG9uIGluc2lkZSBvZiB0aGUgcm9vbS4gSW4gdGhlIHN0YW5kYXJkIGNvbmRpdGlvbiwgcGFydGljaXBhbnRzIGRvIG5vdCBoZWFyIHdoYXQncyBnb2luZyBpbnRvIHRoZSB0aGVyYXB5IHJvb20sIGJ1dCB0aGV5IGNhbiBoZWFyIHZvaWNlcyAoYnV0IG5vdCB0aGUgd29yZHMpLiBJbiB0aGUgcmFkaWNhbCBjb25kaXRpb24sIHBhcnRpY2lwYW50cyBoZWFyIG5vdGhpbmcgYnV0IHRoZSB2b2ljZXMgZm9yIHRoZSBmaXJzdCBzaXggbWludXRlcyBhbmQgdGhlbiB0aGV5IGhlYXIgbG91ZCB5ZWxsaW5nIGFzIGEgbW9jayBwYXRpZW50IHRlbGxzIHRoZSB0aGVyYXBpc3QgdG8ga2VlcCB0aGF0IGdpYW50IG5hc3R5IHNwaWRlciBhd2F5IGZyb20gdGhlbS4gWW91IGV4cGVjdCB0aGF0IGFmdGVyIHRoZXkgaGVhciB0aGUgeWVsbGluZyBzdGFydCwgdGhleSB3aWxsIHdvcmsgdGhlbXNlbHZlcyB1cCBpbnRvIGFueGlldHkgZnJlbnp5IGp1c3QgYmVmb3JlIHRoZSB0aGVyYXB5IHNlc3Npb24gc3RhcnRzLiBZb3UgcmVjb3JkIGZyb20gdGhlIHBhcnRpY2lwYW50IGEgcGh5c2lvbG9naWNhbCBtZWFzdXJlbWVudCBvZiBhbnhpZXR5IG9uY2UgYSBtaW51dGUgd2hpY2ggaXMgbm9ybWFsaXplZC4gDQoNCkJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIG1haW4gc3R1ZHksIHlvdSB3YW50IHRvIG1ha2Ugc3VyZSB5b3VyIG1hbmlwdWxhdGlvbiB3b3JrZWQuIA0KDQojIyMgVmlzdWFsaXplIFJhdyBEYXRhDQpWaXN1YWxpemUgeW91ciBmaXQgYmVmb3JlIHlvdSBtb2RlbCBpcyBhIGdvb2Qgd2F5IHRvIGF2b2lkIHByb2JsZW1zIGRvd24gdGhlIGxpbmUgaW4gdGVybXMgb2Ygbm9uLXNlbnNpY2FsIGZpdHM6IERWID0qKkFueGlldHkgc2NvcmUqKiAoMCA9IExvdyB0byAxMSBIaWdoKSwgSVYgPSAqKlRpbWUqKiAoMC0xMSkuIA0KDQojIyMjIExvZXNzIExpbmUNCi0gKipMbyoqY2FsbHkgVyoqZSoqaWdodGVkICoqUyoqY2F0dGVycGxvdCAqKlMqKm1vb3RoaW5nDQotIERvZXMgbG90cyBvZiByZWdyZXNzaW9ucyBvbiBzbWFsbCBzZWN0aW9ucyBvZiB0aGUgc2NhdHRlciBwbG90IHRvIGdldCB0aGUgYmVzdCBmaXQgKGN1cnZ5KSBsaW5lDQotIExldHMgeW91IHZpc3VhbGl6ZSBjb21wbGV4IHJlbGF0aW9uc2hpcHMsIGJ1dCBpdCBjYW4gKm92ZXJmaXQqIHRoZSByZWxhdGlvbnNoaXAgKHNvIGJlIGNhcmVmdWwpDQotIEdvb2QgZGlhZ25vc3RpYyB0b29sIHRvIHBsb3Qgd2hhdCBpcyBoYXBwZW5pbmcgb24gImF2ZXJhZ2UuIg0KW0Rvd25sb2FkIERhdGFdKC9NaXhlZC9Ob25MaW5lYXJUaW1lMS5jc3YpDQoNCmBgYHtyLCBmaWcud2lkdGg9Ni4yNSxmaWcuaGVpZ2h0PTQuMH0NCkhpZ2hBeDE8LXJlYWQuY3N2KCJNaXhlZC9Ob25MaW5lYXJUaW1lMS5jc3YiKQ0KSGlnaEF4MSRUeXBlPC1mYWN0b3IoSGlnaEF4MSRUcmVhdG1lbnQsIA0KICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoMCwxKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJTdGFuZGFyZCIsICJSYWRpY2FsIikpDQpIaWdoQXgxJFNvdW5kLkY8LWZhY3RvcihIaWdoQXgxJFNvdW5kLCANCiAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKDEsMCksDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiU2NyZWFtaW5nIiwiVm9pY2VzIikpDQpIaWdoQXgxJFN1YmplY3Q8LWFzLmZhY3RvcihIaWdoQXgxJFN1YmplY3QpDQoNClNwZWc8LWdncGxvdChkYXRhID0gSGlnaEF4MSwgYWVzKHg9VGltZSx5PUFueGlldHksICBjb2xvcj1UeXBlKSkrDQogIGdlb21fc21vb3RoKGFlcyhncm91cD1UeXBlKSxtZXRob2Q9J2xvZXNzJywgc2U9RkFMU0UsIGNvbG9yPSdibGFjaycsIHNpemU9MikrDQogICAgZ2VvbV9wb2ludChhZXMoZ3JvdXA9U3ViamVjdCkpKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPVN1YmplY3QpKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikrDQogIHRoZW1lX2J3KCkNClNwZWcNCmBgYA0KDQojIyBQb3dlciBQb2x5bm9taWFscyANCi0gTW9kZWxzIHRoYXQgc2ltcGx5IGN1cnZlcywgc3VjaCBhcyBxdWFkcmF0aWMgb3IgY3ViaWMgZWZmZWN0cyAgDQotIExpbmVhcjogJFkgPUJfezF9WCArIEJfMCQNCi0gUXVhZHJhdGljOiAkWSA9IEJfezF9WCArIEJfezJ9WF4yICsgQl8wJA0KLSBDdWJpYzogJFkgPSBCX3sxfVggKyBCX3syfVheMiArIEJfezN9WF4zICsgQl8wJA0KDQojIyMgVmlzdWFsaXplIFN1YmplY3QgTGV2ZWwNCi0gV2UgY2FuIGFkZCBzbW9vdGhlciBwZXIgc3ViamVjdCB3aXRoIGVhY2ggUG93ZXIgUG9seW5vbWlhbHMgKGBtZXRob2Q9J2xtJ2ApDQotIFRoaXMgbG9va3MgTGluZWFyIGBmb3JtdWxhPXl+eGAgb3IgUXVhZHJhdGljIGBmb3JtdWxhPXl+cG9seSh4LDIpYCBwZXIgc3ViamVjdA0KDQpgYGB7ciwgZWNobz1GQUxTRSxvdXQud2lkdGg9JzEwMCUnLCBmaWcuaGVpZ2h0PTIsZmlnLnNob3c9J2hvbGQnLGZpZy5hbGlnbj0nY2VudGVyJ30NCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KDQp0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplID0gNywgYmFzZV9mYW1pbHkgPSAiIikpDQoNClNwZWcubGluZWFyPC1nZ3Bsb3QoZGF0YSA9IEhpZ2hBeDEsIGFlcyh4PVRpbWUseT1BbnhpZXR5LCBncm91cD1TdWJqZWN0LGNvbG9yPVR5cGUpKSsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGZvcm11bGE9eX54LCBzZT1GQUxTRSkrDQogIGdlb21fcG9pbnQoYWVzKGdyb3VwPVN1YmplY3QpKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0OiBMaW5lYXIgRml0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KDQpTcGVnLlF1YWQ8LWdncGxvdChkYXRhID0gSGlnaEF4MSwgYWVzKHg9VGltZSx5PUFueGlldHksIGdyb3VwPVN1YmplY3QsY29sb3I9VHlwZSkpKw0KICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgZm9ybXVsYT15fnN0YXRzOjpwb2x5KHgsMiksIHNlPUZBTFNFKSsNCiAgZ2VvbV9wb2ludChhZXMoZ3JvdXA9U3ViamVjdCkpKw0KICB5bGFiKCJBbnhpZXR5IikreGxhYigiVGltZSIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIGdndGl0bGUoIkJ5IFN1YmplY3Q6IFF1YWRyYXRpYyBGaXQiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQpncmlkLmFycmFuZ2UoU3BlZy5RdWFkLCBTcGVnLmxpbmVhciwgbmNvbD0yKQ0KYGBgDQoNCiMjIyBEaXNjb250aW51aXR5ICANCi0gVGhlIHJhZGljYWwgdGhlcmFweSBkb2VzIG5vdCBkaWZmZXIgZnJvbSB0aGUgc3RhbmRhcmQgdGhlcmFweSB1bnRpbCA2IG1pbnV0ZXMgaW4sIHNvIHlvdSB3aWxsIG5lZWQgdG8gYWNjb3VudCBmb3IgdGhhdCwgYnV0IHF1YWRyYXRpYyBzZWVtcyB0byBiZSBtb3JlIGxvZ2ljYWwgZml0Lg0KDQpgYGB7ciwgZWNobz1GQUxTRSxvdXQud2lkdGg9JzEwMCUnLCBmaWcuaGVpZ2h0PTIsZmlnLnNob3c9J2hvbGQnLGZpZy5hbGlnbj0nY2VudGVyJ30NCg0KdGhlbWVfc2V0KHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDcsIGJhc2VfZmFtaWx5ID0gIiIpKQ0KDQpTcGVnLmxpbmVhci4yPC1nZ3Bsb3QoZGF0YSA9IEhpZ2hBeDEsIGFlcyh4PVRpbWUseT1BbnhpZXR5KSkrDQogIGZhY2V0X2dyaWQoVHlwZX4uKSsNCiAgICBnZW9tX3Ntb290aChhZXMoZ3JvdXA9U3ViamVjdCxsaW5ldHlwZT1UeXBlKSwNCiAgICAgICAgICAgICAgbWV0aG9kPSdsbScsIGZvcm11bGE9eX54LCBzZT1GQUxTRSxzaG93LmxlZ2VuZCA9IEZBTFNFKSsNCiAgZ2VvbV9wb2ludChhZXMoZ3JvdXA9U3ViamVjdCxjb2xvcj1Tb3VuZC5GKSkrDQogICAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0OiBMaW5lYXIgRml0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KU3BlZy5RdWFkLjI8LWdncGxvdChkYXRhID0gSGlnaEF4MSwgYWVzKHg9VGltZSx5PUFueGlldHkpKSsNCiAgZmFjZXRfZ3JpZChUeXBlfi4pKw0KICAgIGdlb21fc21vb3RoKGFlcyhncm91cD1TdWJqZWN0LGxpbmV0eXBlPVR5cGUpLA0KICAgICAgICAgICAgICBtZXRob2Q9J2xtJywgZm9ybXVsYT15fnN0YXRzOjpwb2x5KHgsMiksIHNlPUZBTFNFLHNob3cubGVnZW5kID0gRkFMU0UpKw0KICBnZW9tX3BvaW50KGFlcyhncm91cD1TdWJqZWN0LGNvbG9yPVNvdW5kLkYpKSsNCiAgICB5bGFiKCJBbnhpZXR5IikreGxhYigiVGltZSIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIGdndGl0bGUoIkJ5IFN1YmplY3Q6IFF1YWRyYXRpYyBGaXQiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQpncmlkLmFycmFuZ2UoU3BlZy5saW5lYXIuMiwgU3BlZy5RdWFkLjIsIG5jb2w9MikNCmBgYA0KDQoNCiMjIyBGaXR0aW5nIFBvd2VyIFBvbHlub21pYWxzIA0KLSBGb3J3YXJkIFNlbGVjdGlvbiBBcHByb2FjaA0KICAgIC0gTW9kZWwgMTogTGluZWFyIGZpdA0KICAgIC0gTW9kZWwgMjogUXVhZHJhdGljIGZpdA0KICAgIC0gTW9kZWwgaToga2VlcCBnb2luZyB1cCB1bnRpbCB5b3UgYXJlIHNhdGlzZmllZCwgDQogICAgLSBSIENvZGUgeW91IGNhbiBhZGQgdGhlbSBtYW51YWxseSwgYEFueGlldHkgKyBJKEFueGlldHleMilgIG9yIHdpdGggdGhlIHBvbHkgY29tbWFuZCwgYHBvbHkoQW54aWV0eSwgMiwgcmF3PVQpYA0KLSBEbyB3ZSB3YW50IHRvIHVzZSBUeXBlIG9mIHRyZWF0bWVudCAoU3RhbmRhcmQgb3IgcmFkaWNhbCkgb3IgU291bmQgKHZvaWNlIHdob2xlLXRpbWUgdnMuIHZvaWNlLXRvLXNjcmVhbWluZykgW2JldHdlZW4gc3ViamVjdCBjb25kaXRpb24gd2F5cyBvZiB0aGlua2luZyBhYm91dCBpdF0NCi0gR2l2ZW4gdGhhdCBzdGFuZGFyZCB0aGVyYXB5IGlzIGlkZW50aWNhbCB0byByYWRpY2FsIHRoZXJhcHkgdXAgdG8gNiBtaW5zLCB5b3UgcmVhbGx5IHdhbnQgdG8gbW9kZWwgaG93IHRoZSBzY3JlYW1pbmcgY2hhbmdlcyB0aGVpciBhbnhpZXR5IGxldmVsLCBBRlRFUiB0aGUgc2NyZWFtaW5nIHN0YXJ0cy4gV2Ugd2lsbCB1c2UgYHNvdW5kYCBmYWN0b3IgYW5kIG5vdCBvZiBUeXBlIHRoZXJhcHkgZmFjdG9yLg0KDQojIyMgTGluZWFyIE1vZGVsIEZpdA0KLSBUaW1lIHdpbGwgbm90IGJlIGNlbnRlcmVkIChZZXQpDQoNCmBgYHtyfQ0KTGluZWFyLlBQPC1sbWVyKEFueGlldHkgfiBUaW1lKlNvdW5kLkYrICgxK1RpbWV8U3ViamVjdCksIGRhdGE9SGlnaEF4MSwgUkVNTD1GQUxTRSkNCnN1bW1hcnkoTGluZWFyLlBQLCBjb3JyZWxhdGlvbj1GQUxTRSkNCmBgYA0KLSBOb3RlIHRoZSByYW5kb20gY29ycmVsYXRpb24gd291bGQgbmVlZCB0byBiZSByZW1vdmVkDQoNCi0gIENoZWNrIHRoZSByZXNpZHVhbHMgb2YgdGhlIG1vZGVsIGZpdA0KYGBge3J9DQpwbG90KExpbmVhci5QUCkNCmBgYA0KDQotIFRob3NlIGFyZSB2ZXJ5IG9kZC4gU28gY2xlYXJseSBsaW5lYXIgZml0IGlzIG5vdCBjb3JyZWN0DQoNCmBgYHtyfQ0KbGlicmFyeShlZmZlY3RzKQ0KTGluZWFyLlBQLkZpeGVkPC1FZmZlY3QoYygiVGltZSIsIlNvdW5kLkYiKSxMaW5lYXIuUFAsDQogICAgICAgICAgICAgICAgICAgICAgICB4bGV2ZWxzPWxpc3QoVGltZT1zZXEoMCwxMSwxKSkpDQoNCkxpbmVhci5QUC5GaXhlZDwtYXMuZGF0YS5mcmFtZShMaW5lYXIuUFAuRml4ZWQpDQoNCkwuUFAuUGxvdCA8LWdncGxvdChkYXRhID0gTGluZWFyLlBQLkZpeGVkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRpbWUsIHkgPWZpdCxncm91cD1Tb3VuZC5GKSkrDQogIGdlb21fbGluZShhZXMoY29sb3I9U291bmQuRiksc2l6ZT0yKSsNCiAgZ2VvbV9yaWJib24oYWVzKHltaW49bG93ZXIsIHltYXg9dXBwZXIpLGFscGhhPS4yKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0OiBMaW5lYXIgUG93ZXIgUG9seSBGaXQiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSt0aGVtZV9idygpDQpMLlBQLlBsb3QNCmBgYA0KDQotIFdlIHNob3VsZCBub3QgZm9yY2UgdGhlIG1vZGVsIHRvIHByZWRpY3Qgc2NyZWFtaW5nIGNvbmRpdGlvbiBCRUZPUkUgNiBtaW51dGVzIGJlY2F1c2UgdGhhdCBuZXZlciBvY2N1cnMNCi0gV2UgY2FuIG1hbnVhbGx5IHJlbW92ZSB0aGF0IGZhbHNlIHByZWRpY3Rpb24gYW5kIHJlcGxvdA0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpMaW5lYXIuUFAuRml4ZWQuQ2xlYW5lZDwtTGluZWFyLlBQLkZpeGVkICU+JQ0KICBkcGx5cjo6ZmlsdGVyKFNvdW5kLkY9PSJWb2ljZXMiIHwgKFNvdW5kLkYhPSJWb2ljZXMiICYgVGltZT49NikpDQoNCkwuUFAuUGxvdC4yIDwtZ2dwbG90KGRhdGEgPSBMaW5lYXIuUFAuRml4ZWQuQ2xlYW5lZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBUaW1lLCB5ID1maXQsZ3JvdXA9U291bmQuRikpKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPVNvdW5kLkYpLHNpemU9MikrDQogIGdlb21fcmliYm9uKGFlcyh5bWluPWxvd2VyLCB5bWF4PXVwcGVyKSxhbHBoYT0uMikrDQogIHlsYWIoIkFueGlldHkiKSt4bGFiKCJUaW1lIikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgZ2d0aXRsZSgiQnkgU3ViamVjdDogTGluZWFyIFBvd2VyIFBvbHkgRml0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrdGhlbWVfYncoKQ0KTC5QUC5QbG90LjINCmBgYA0KDQoNCiMjIyBRdWFkcmF0aWMgTW9kZWwgRml0DQotIFRpbWUgd2lsbCBub3QgYmUgY2VudGVyZWQgKFlldCkNCg0KYGBge3J9DQpRdWFkLlBQPC1sbWVyKEFueGlldHkgfiBzdGF0czo6cG9seShUaW1lLDIscmF3PVRSVUUpKlNvdW5kLkYrICgxK3N0YXRzOjpwb2x5KFRpbWUsMixyYXc9VFJVRSl8U3ViamVjdCksIGRhdGE9SGlnaEF4MSwgUkVNTD1GQUxTRSkNCnN1bW1hcnkoUXVhZC5QUCwgY29ycmVsYXRpb249RkFMU0UpDQpgYGANCg0KLSBOb3RlIHRoZSByYW5kb20gY29ycmVsYXRpb25zIHdvdWxkIG5lZWQgdG8gYmUgcmVtb3ZlZA0KLSBOb3RpY2UgdGhleSBhcmUgYWxsIHNpZ25pZmljYW50DQoNCi0gIENoZWNrIHRoZSByZXNpZHVhbHMgb2YgdGhlIG1vZGVsIGZpdA0KYGBge3J9DQpwbG90KFF1YWQuUFApDQpgYGANCg0KLSBNdWNoIGJldHRlciB0aGFuIHRoZSBsaW5lYXIgZml0DQoNCiMjIyMgVmlzdWFsaXplIE1vZGVscw0KLSBMZWZ0IHBsb3QgPSBGYWxzZSBQcmVkaWN0aW9uIGJlZm9yZSA2IE1pbnV0ZXMsIFJpZ2h0IHBsb3QgcmVtb3ZlIEZhbHNlIFByZWRpY3Rpb24NCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpRdWFkLlBQLkZpeGVkPC1FZmZlY3QoYygiVGltZSIsIlNvdW5kLkYiKSxRdWFkLlBQLA0KICAgICAgICAgICAgICAgICAgICAgICAgeGxldmVscz1saXN0KFRpbWU9c2VxKDAsMTEsMSkpKQ0KDQpRdWFkLlBQLkZpeGVkLkNsZWFuZWQ8LVF1YWQuUFAuRml4ZWQgJT4lDQogIGRwbHlyOjpmaWx0ZXIoU291bmQuRj09IlZvaWNlcyIgfCAoU291bmQuRiE9IlZvaWNlcyIgJiBUaW1lPj02KSkNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFLG91dC53aWR0aD0nMTAwJScsIGZpZy5oZWlnaHQ9MixmaWcuc2hvdz0naG9sZCcsZmlnLmFsaWduPSdjZW50ZXInfQ0KdGhlbWVfc2V0KHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDcsIGJhc2VfZmFtaWx5ID0gIiIpKQ0KDQpRdWFkLlBQLkZpeGVkPC1FZmZlY3QoYygiVGltZSIsIlNvdW5kLkYiKSxRdWFkLlBQLA0KICAgICAgICAgICAgICAgICAgICAgICAgeGxldmVscz1saXN0KFRpbWU9c2VxKDAsMTEsMSkpKQ0KDQpRdWFkLlBQLkZpeGVkPC1hcy5kYXRhLmZyYW1lKFF1YWQuUFAuRml4ZWQpDQoNClEuUFAuUGxvdCA8LWdncGxvdChkYXRhID0gUXVhZC5QUC5GaXhlZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBUaW1lLCB5ID1maXQsZ3JvdXA9U291bmQuRikpKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPVNvdW5kLkYpLHNpemU9MikrDQogIGdlb21fcmliYm9uKGFlcyh5bWluPWxvd2VyLCB5bWF4PXVwcGVyKSxhbHBoYT0uMikrDQogIHlsYWIoIkFueGlldHkiKSt4bGFiKCJUaW1lIikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgZ2d0aXRsZSgiQnkgU3ViamVjdDogUXVhZCBQb3dlciBQb2x5IEZpdCIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNClF1YWQuUFAuRml4ZWQuQ2xlYW5lZDwtUXVhZC5QUC5GaXhlZCAlPiUNCiAgZHBseXI6OmZpbHRlcihTb3VuZC5GPT0iVm9pY2VzIiB8IChTb3VuZC5GIT0iVm9pY2VzIiAmIFRpbWU+PTYpKQ0KDQpRLlBQLlBsb3QuMiA8LWdncGxvdChkYXRhID0gUXVhZC5QUC5GaXhlZC5DbGVhbmVkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRpbWUsIHkgPWZpdCxncm91cD1Tb3VuZC5GKSkrDQogIGdlb21fbGluZShhZXMoY29sb3I9U291bmQuRiksc2l6ZT0yKSsNCiAgZ2VvbV9yaWJib24oYWVzKHltaW49bG93ZXIsIHltYXg9dXBwZXIpLGFscGhhPS4yKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0OiBRdWFkIFBvd2VyIFBvbHkgRml0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KZ3JpZC5hcnJhbmdlKFEuUFAuUGxvdCwgUS5QUC5QbG90LjIsIG5jb2w9MikNCmBgYA0KDQojIyMjIFRlc3QgSW1wcm92ZW1lbnQNCi0gVGhlIHF1YWRyYXRpYyBmaXQgaXMgYmV0dGVyDQpgYGB7cn0NCmFub3ZhKExpbmVhci5QUCxRdWFkLlBQKQ0KYGBgDQoNCiMjIE9ydGhvZ29uYWwgUG9seW5vbWlhbHMNCi0gUHJvYmxlbSB3aXRoIHBvd2VyIHBvbHlub21pYWxzIGlzIGludGVycHJldGluZyB0aGVtIGluZGVwZW5kZW50bHkNCi0gVGhlIGhpZ2hlciBvcmRlciB0ZXJtcyBkZXBlbmQgb24sIHRoZSBsb3dlciBvcmRlciB0ZXJtcw0KLSBUaHVzIGxpbmVhciB0ZXJtIGNhbiBiZSBzaWduaWZpY2FudCBjYXVzZSB0aGUgaGlnaGVyIG9yZGVyIHRlcm0gaXMgc2lnbmlmaWNhbnQsIGJ1dCB0aGVyZSBpcyBubyBsaW5lYXIgdHJlbmQgaW4gdGhlIGRhdGEgDQogICAgLSBUaGlzIGlzIGJlY2F1c2UgcG93ZXIgcG9seW5vbWlhbHMgY29ycmVsYXRlIHdpdGggZWFjaCBvdGhlciAodGhleSBhcmUgbm90IHVuaXF1ZSBwcmVkaWN0b3JzKQ0KICAgIC0gRm9yIEV4YW1wbGU6DQoNCmBgYHtyfQ0KTGluZWFyVGVybXM8LXNlcSgwLDExLDEpDQpRdWFkVGVybXM8LUxpbmVhclRlcm1zXjINCmBgYA0KDQotIENvcnJlbGF0aW9uIGJldHdlZW4gbGluZWFyIGFuZCBxdWFkcmF0aWMgaXMgciA9IGByIHJvdW5kKGNvcihMaW5lYXJUZXJtcyxRdWFkVGVybXMpLDMpYC4gKipUaHVzIHRoZXkgYXJlIG11bHRpY29sbGluZWFyISoqDQotIFRlY2huaWNhbGx5LCBpdCdzIE9LIHRoYXQgdGhleSBhcmUgbXVsdGljb2xsaW5lYXIsIGJ1dCB5b3UgY2Fubm90IGludGVycHJldCBlYWNoIHRlcm0gd2l0aG91dCBsb29raW5nIGF0IHRoZSBvdGhlciAodGhlIHAtdmFsdWVzIHdpbGwgYmUgcHJvYmxlbWF0aWMpDQotIFRoZSBzb2x1dGlvbiB0byBtYWtlIHRoZSBsaW5lYXIgYW5kIHF1YWRyYXRpYyB0ZXJtcyBjb3JyZWxhdGUgd2l0aCBlYWNoIGF0IHplcm8gKGp1c3QgYXMgd2UgZGlkIHdpdGggQU5PVkEpDQotIHdlIGNhbiB1c2UgdGhlIGBwb2x5YCBjb2RlIGluIFIsIGJ1dCBzZXQgcmF3ID0gRiAod2hpY2ggaXMgYWN0dWFsbHkgdGhlIGRlZmF1bHQuIFRoaXMgaXMgYWxzbyBjZW50ZXJpbmcgdGhlIGRhdGEpIA0KLSBIZXJlIGlzIGFuIGV4YW1wbGUgb2Ygd2hhdCBSIGlzIGdvaW5nIHRvIGRvOiANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCk8uUG9seTwtc3RhdHM6OnBvbHkoTGluZWFyVGVybXMsMikNCmBgYA0KDQotIENvcnJlbGF0aW9uIGJldHdlZW4gbGluZWFyIGFuZCBxdWFkcmF0aWMgaXMgbm93IHIgPSBgciByb3VuZChjb3IoTy5Qb2x5WywxXSxPLlBvbHlbLDJdKSwzKWAuIE1lYW5pbmcgdGhleSBhcmUgbm8gbG9uZ2VyIG11bHRpY29sbGluZWFyLiBOb3cgeW91IGNhbiBzZWUgaWYgdGhlIGxpbmVhciBhbmQgcXVhZHJhdGljIGFyZSBpbmRlcGVuZGVudGx5IHNpZ25pZmljYW50LiAgDQoNCiMjIyBRdWFkcmF0aWMgTW9kZWwgRml0DQotIFRpbWUgd2lsbCBub3QgYmUgY2VudGVyZWQgKFlldCkNCg0KYGBge3J9DQpMaW5lYXIuT1A8LWxtZXIoQW54aWV0eSB+IHN0YXRzOjpwb2x5KFRpbWUsMSkqU291bmQuRisgDQogICAgICAgICAgICAgICAgICAoMStzdGF0czo6cG9seShUaW1lLDEpfFN1YmplY3QpLCBkYXRhPUhpZ2hBeDEsIFJFTUw9RkFMU0UpDQoNClF1YWQuT1A8LWxtZXIoQW54aWV0eSB+IHN0YXRzOjpwb2x5KFRpbWUsMikqU291bmQuRisgDQogICAgICAgICAgICAgICAgKDErc3RhdHM6OnBvbHkoVGltZSwyKXxTdWJqZWN0KSwgZGF0YT1IaWdoQXgxLCBSRU1MPUZBTFNFKQ0KDQpzdW1tYXJ5KFF1YWQuT1AsIGNvcnJlbGF0aW9uPUZBTFNFKQ0KYGBgDQoNCi0gTm90ZSB0aGUgcmFuZG9tIGNvcnJlbGF0aW9ucyBhcmUgcHJvYmFibHkgdG9vIGhpZ2ggYW5kIHdlIGNvdWxkIHJlbW92ZSB0aGVtDQotICpOb3RpY2UgdGhleSBhcmUgbm90IGFsbCBzaWduaWZpY2FudCEqIA0KICAgIC0gKipJbiBib3RoIGNhc2VzIHRoZSBxdWFkcmFkaWMgZWZmZWN0IGlzIG9ubHkgc2lnbmlmaWNhbnQqKi4gVGhpcyBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgcG93ZXIgcG9seW5vbWlhbHMuIA0KLSBUaGUgb25lIHByb2JsZW0gaXMgdGhlIGVzdGltYXRlcyBoYXZlIGxvc3QgbWVhbmluZyAocmVhbGx5IG9ubHkgdGhlIHNpZ24gaXMgbWVhbmluZ2Z1bCkNCi0gVGhlIGVmZmVjdCBwYWNrYWdlIHdpbGwgcmVhZCB0aGF0IHdlIHVzZWQgYHBvbHlgIGFuZCB3ZSBjYW4gcHJlZGljdCBhcyBub3JtYWwsIHlvdSB3aWxsIG5vdGljZSB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIGZpbmFsIHBsb3QuIFRoZSBkaWZmZXJlbmNlIGlzIHdlIGludGVycHJldCB0aGUgcGF0dGVybiBvZiB0LXZhbHVlcy4gDQoNCmBgYHtyfQ0KUXVhZC5PUC5GaXhlZDwtRWZmZWN0KGMoIlRpbWUiLCJTb3VuZC5GIiksUXVhZC5PUCwNCiAgICAgICAgICAgICAgICAgICAgICAgIHhsZXZlbHM9bGlzdChUaW1lPXNlcSgwLDExLDEpKSkNClF1YWQuT1AuRml4ZWQ8LWFzLmRhdGEuZnJhbWUoUXVhZC5PUC5GaXhlZCkNCg0KUXVhZC5PUC5GaXhlZC5DbGVhbmVkPC1RdWFkLk9QLkZpeGVkICU+JQ0KICBkcGx5cjo6ZmlsdGVyKFNvdW5kLkY9PSJWb2ljZXMiIHwgKFNvdW5kLkYhPSJWb2ljZXMiICYgVGltZT49NikpDQoNClEuT1AuUGxvdCA8LWdncGxvdChkYXRhID0gUXVhZC5PUC5GaXhlZC5DbGVhbmVkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRpbWUsIHkgPWZpdCxncm91cD1Tb3VuZC5GKSkrDQogIGdlb21fbGluZShhZXMoY29sb3I9U291bmQuRiksc2l6ZT0yKSsNCiAgZ2VvbV9yaWJib24oYWVzKHltaW49bG93ZXIsIHltYXg9dXBwZXIpLGFscGhhPS4yKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0OiBRdWFkIE90aG8gUG9seSBGaXQiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSt0aGVtZV9idygpDQpRLk9QLlBsb3QNCmBgYA0KDQojIyBSZXN1bHRzDQo+IFRoZSByZXN1bHRzIHNob3cgYW4gb3ZlcmFsbCBzaWduaWZpY2FudCBwb3NpdGl2ZSBxdWFkcmF0aWMgZWZmZWN0IG9uIGFueGlldHkgKGFyY2gtc2hhcGUpIGZvciB0aGUgc3RhbmRhcmQgdHJlYXRtZW50IGNvbmRpdGlvbiAod2hlcmUgcGFydGljaXBhbnRzIGFyZSB3YWl0aW5nIG91dHNpZGUgb2YgdGhlIHRoZXJhcHkgcm9vbSB3aXRoIG5vIGludGVydmVudGlvbikuIFRoaXMgc3VnZ2VzdHMgdGhhdCBhcyBwYXJ0aWNpcGFudHMgd2FpdCBvdXRzaWRlIG9mIHRoZSByb29tLCB0aGUgaW5jcmVhc2UgYXMgdGltZSBnb2VzIG9uIGluIHRoZSBhbW91bnQgb2YgYW54aWV0eSBmZWVsIHJlbGF0aXZlIHRvIHRoZSB0cmVhdG1lbnQgdGhleSBrbm93IHRoZXkncmUgYWJvdXQgdG8gdW5kZXJnbywgYnV0IHRoYXQgYW54aWV0eSBzdGFydHMgdG8gbGV2ZWwgb2ZmIGFuZCBldmVuIGRyb3BzIGRvd24gYSBiaXQganVzdCBiZWZvcmUgdGhlaXIgc2NoZWR1bGVkIHRoZXJhcHkgdGltZS4gSG93ZXZlciwgdGhvc2UgYXNzaWduZWQgdG8gdGhlIGNvbmRpdGlvbiB3aGVyZSB0aGUgbW9jayBwYXJ0aWNpcGFudCBzdGFydHMgc2NyZWFtaW5nIGF0IGFyb3VuZCBzaXggbWludXRlcywgd2Ugc2VlIGEgc2lnbmlmaWNhbnQgZGlzY29udGludWl0eSAodGhlIGVmZmVjdCBvZiB0aGUgc291bmQgY29uZGl0aW9uKSB3aGVyZSB0aGVyZSBpcyBhbiBpbW1lZGlhdGUganVtcCB1cCBpbiB0aGVpciBhbnhpZXR5IGxldmVsIGFuZCB3ZSBzZWUgYSBzaWduaWZpY2FudGx5IG5lZ2F0aXZlIHF1YWRyYXRpYyBlZmZlY3QgKFUtc2hhcGUpIHdoZXJlIHRoZXkgZ2V0IG1vcmUgYW5kIG1vcmUgYW54aW91cyBhcyB0aGVpciBzY2hlZHVsZWQgdGhlcmFweSB0aW1lIGFwcHJvYWNoZXMuIEluIG90aGVyIHdvcmRzLCB5b3VyIG1hbmlwdWxhdGlvbiB3YXMgcXVpdGUgc3VjY2Vzc2Z1bC4NCg0KIyMjIFZpc3VhbGl6aW5nIENvbXBsZXRlIEZpbmFsIE1vZGVsIEZpdA0KLSBTaW5jZSB0aGlzIGlzIGEgYmFsYW5jZWQgc3R1ZHksIHdlIGNhbiBvdmVyYWxsIG1vZGVsIGZpdCBhbmQgdGhlIHBlciBzdWJqZWN0IGZpdCAod2l0aCBkb3RzID0gcmF3IHN1YmplY3QgZGF0YSkNCg0KYGBge3IsIGZpZy53aWR0aD02LjI1LGZpZy5oZWlnaHQ9NC4wfQ0KSGlnaEF4MSRRdWFkTW9kZWxGaXQ8LXByZWRpY3QoUXVhZC5PUCkNClNwZWcuRmluYWw8LWdncGxvdCgpKw0KICBnZW9tX3BvaW50KGRhdGEgPSBIaWdoQXgxLCBhZXMoeD1UaW1lLHk9QW54aWV0eSwgY29sb3I9U291bmQuRikpKw0KICBnZW9tX2xpbmUoZGF0YSA9IEhpZ2hBeDEsIGFlcyh4PVRpbWUseT1RdWFkTW9kZWxGaXQsIGNvbG9yPVNvdW5kLkYsIGdyb3VwPVN1YmplY3QpKSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBRdWFkLk9QLkZpeGVkLkNsZWFuZWQsIGFlcyh4ID0gVGltZSwgeSA9Zml0LGdyb3VwPVNvdW5kLkYpLGNvbG9yPSdibHVlJywgc2l6ZT0yKSsNCiAgeWxhYigiTW9kZWwgRml0dGVkIEFueGlldHkiKSt4bGFiKCJUaW1lIikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgZ2d0aXRsZSgiQnkgU3ViamVjdCBNb2RlbCBGaXQiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrdGhlbWVfYncoKQ0KU3BlZy5GaW5hbA0KYGBgDQoNCg0KDQojIE90aGVyIFR5cGVzIG9mIEdyb3d0aCBDdXJ2ZXMNCi0gVGhlc2UgYXJlIG1vZGVscyB0aGF0IGNhbm5vdCBiZSBmaXQgd2l0aCBwb2x5bm9taWFscw0KLSBMb2dhcml0aG1pYyAoVGhlc2UgZnVuY3Rpb25zIGFyZSBvZnRlbiBwaHlzaWNhbDsgVGhlc2UgbG9vayBtb3JlIHNpZ21vaWRhbCkNCiAgICAtICRjXlkgPSBkWCQNCiAgICAtIFRoZXNlIGFyZSBsb29rIG1vcmUgc2lnbW9pZGFsIA0KLSBFeHBvbmVudGlhbCBncm93dGggKGxpa2UgdW5jaGVja2VkIHBvcHVsYXRpb24gZ3Jvd3RoKSANCiAgICAtICRZID0gY2Vee2R4fSQNCiAgICAgICAgLSBOb3RlOiBFdWxlcidzIG51bWJlciwgJGUgPSQgYHIgIGV4cCgxKWAsIGlzIGFuIGlycmF0aW9uYWwgbWF0aCBjb25zdGFudCBhbmQgdGhlIGJhc2UgdmFsdWUgb2YgdGhlIG5hdHVyYWwgbG9nDQotIFBvd2VyIExhdyBncm93dGggKENvbW1vbiBmdW5jdGlvbiBpbiBtb3RvciBjb250cm9sIGFuZCBwZXJjZXB0aW9uIGV4cGVyaW1lbnRzKQ0KICAgIC0gJFkgPSBjWF5kJA0KLSBJbiBhbGwgdGhlc2UgY2FzZXMgdGhlIHNvbHV0aW9uIGlzIG9mdGVuIHRvIHRyYW5zZm9ybSBEViBvciBUaW1lLiBJbiBwb3dlci1sYXcsIHlvdSBtaWdodCBuZWVkIHRvIHRyYW5zZm9ybSBib3RoDQoNCiMgR3Jvd3RoIEN1cnZlIEV4YW1wbGUNCj4gTm93IHRoYXQgeW91IGhhdmUgc3VmZmljaWVudGx5IG1hZGUgaGFsZiBvZiB5b3VyIGNsaWVudCdzIGFueGlldHkgbGV2ZWxzIGdvIHRvICIxMSIgeW91IGh5cG90aGVzaXplIHRoYXQgZ2l2ZW4gdGhhdCB0aGV5IHN0YXJ0IG91dCBtb3JlIGFueGlvdXMgdGhleSB3aWxsIHJlc3BvbmQgdG8gdGhlIHRyZWF0bWVudCAqKm1vcmUqKiBnaXZlbiB0aGF0IHlvdSdyZSBwdXR0aW5nIHRoZW0gaW4gdGhlIHN0YXRlIG9mIHRoZWlyIG1heGltYWwgbGV2ZWwgb2YgYW54aWV0eSBpbiB3aGljaCBmb3IgeW91IHRvIGNvbmR1Y3QgdGhlIHRoZXJhcHkvZXhwb3N1cmUgc2Vzc2lvbi4gVGh1cywgeW91IGV4cGVjdCB0aG9zZSB3aG8gY2FtZSBpbiBtb3JlIGFueGlvdXMgd2lsbCBoYXZlIHRoZSBsb3dlc3QgbGV2ZWxzIG9mIGFueGlldHkgYXQgdGhlIGVuZCBvZiB0cmVhdG1lbnQgdmVyc3VzIHRob3NlIHdobyBzdGFydGVkIG91dCB3aXRoIG5vcm1hbCBsZXZlbHMgb2YgYW54aWV0eSBwcmVjZWRpbmcgdHJlYXRtZW50LiBZb3UgdGFrZSB0aGVpciBBbnhpZXR5IHNjb3JlIGV2ZXJ5IDMuMyBtaW5zIHRocm91Z2hvdXQgYSA2MC1taW51dGUgc2Vzc2lvbg0KDQojIyBWaXN1YWxpemUgUmF3IERhdGENCi0gRFYgPSoqQW54aWV0eSBzY29yZSoqICgwID0gTG93IHRvIDExIEhpZ2gpDQotIElWMSA9ICoqVGltZSoqICgxLTE4KQ0KLSBJVjIgPSAqKlRoZXJhcHkgVHlwZSoqIChTdGFuZGFyZCBvciBSYWRpY2FsKQ0KDQpgYGB7ciwgZmlnLndpZHRoPTYuMjUsZmlnLmhlaWdodD00LjB9DQpIaWdoQXgyPC1yZWFkLmNzdigiTWl4ZWQvR3Jvd3RoQ3VydmUuY3N2IikNCkhpZ2hBeDIkVHlwZTwtZmFjdG9yKEhpZ2hBeDIkVHJlYXRtZW50LCANCiAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKDAsMSksDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiU3RhbmRhcmQiLCAiUmFkaWNhbCIpKQ0KSGlnaEF4MiRTdWJqZWN0PC1hcy5mYWN0b3IoSGlnaEF4MiRTdWJqZWN0KQ0KDQpTcGVnLjM8LWdncGxvdChkYXRhID0gSGlnaEF4MiwgYWVzKHg9VGltZSx5PUFueGlldHksICBjb2xvcj1UeXBlKSkrDQogIGdlb21fc21vb3RoKGFlcyhncm91cD1UeXBlKSxtZXRob2Q9J2xvZXNzJywgc2U9RkFMU0UsIGNvbG9yPSdibGFjaycsIHNpemU9MikrDQogICAgZ2VvbV9wb2ludChhZXMoZ3JvdXA9U3ViamVjdCkpKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPVN1YmplY3QpKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDEsMTgsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrdGhlbWVfYncoKQ0KU3BlZy4zDQpgYGANCg0KIyMjIFRyYW5zZm9ybXMNCi0gUG9seW5vbWlhbHMgbWlnaHQgbm90IGJlIHRoZSBiZXN0IGZpdC4gTGV0J3MgdHJ5IHRvIHN0cmFpZ2h0ZW4gdGhlIGRhdGEgb3V0IGFzIHRoaXMgbG9va3Mgc29tZXdoYXQgZXhwb25lbnRpYWw6IGBMb2cgRFYgfCBMb2cgSVZgDQoNCmBgYHtyLCBlY2hvPUZBTFNFLG91dC53aWR0aD0nMTAwJScsIGZpZy5oZWlnaHQ9MixmaWcuc2hvdz0naG9sZCcsZmlnLmFsaWduPSdjZW50ZXInfQ0KdGhlbWVfc2V0KHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDcsIGJhc2VfZmFtaWx5ID0gIiIpKQ0KDQpTcGVnLjQ8LWdncGxvdChkYXRhID0gSGlnaEF4MiwgYWVzKHg9bG9nKFRpbWUpLHk9QW54aWV0eSwgIGNvbG9yPVR5cGUpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwPVR5cGUpLG1ldGhvZD0nbG9lc3MnLCBzZT1GQUxTRSwgY29sb3I9J2JsYWNrJywgc2l6ZT0yKSsNCiAgICBnZW9tX3BvaW50KGFlcyhncm91cD1TdWJqZWN0KSkrDQogIGdlb21fbGluZShhZXMoZ3JvdXA9U3ViamVjdCkpKw0KICB5bGFiKCJBbnhpZXR5IikreGxhYigiTG9nKFRpbWUpIikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxLDE4LDEpKSsNCiAgZ2d0aXRsZSgiTG9nIElWIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpTcGVnLjQNCg0KU3BlZy41PC1nZ3Bsb3QoZGF0YSA9IEhpZ2hBeDIsIGFlcyh4PVRpbWUsIHk9bG9nKEFueGlldHkpLGNvbG9yPVR5cGUpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwPVR5cGUpLG1ldGhvZD0nbG9lc3MnLCBzZT1GQUxTRSwgY29sb3I9J2JsYWNrJywgc2l6ZT0yKSsNCiAgICBnZW9tX3BvaW50KGFlcyhncm91cD1TdWJqZWN0KSkrDQogIGdlb21fbGluZShhZXMoZ3JvdXA9U3ViamVjdCkpKw0KICB5bGFiKCJMb2coQW54aWV0eSkiKSt4bGFiKCJUaW1lIikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxLDE4LDEpKSsNCiAgZ2d0aXRsZSgiTG9nIERWIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmdyaWQuYXJyYW5nZShTcGVnLjQsIFNwZWcuNSwgbmNvbD0yKQ0KYGBgDQoNCi0gVGFraW5nIHRoZSBsb2cgb2YgdGhlIElWIChUaW1lKSB3b3JrcyBiZXN0DQoNCmBgYHtyfQ0KTGluZWFyLlN0dWR5MjwtbG1lcihBbnhpZXR5IH4gVGltZSpUeXBlKyAoMStUaW1lfFN1YmplY3QpLCBkYXRhPUhpZ2hBeDIsIFJFTUw9RkFMU0UpDQpRdWFkLlN0dWR5MjwtbG1lcihBbnhpZXR5IH4gc3RhdHM6OnBvbHkoVGltZSwyKSpUeXBlKyAoMStzdGF0czo6cG9seShUaW1lLDIpfFN1YmplY3QpLCBkYXRhPUhpZ2hBeDIsIFJFTUw9RkFMU0UpDQpgYGANCg0KLSAgQ2hlY2sgdGhlIHJlc2lkdWFscyBvZiB0aGUgbW9kZWwgZml0DQpgYGB7ciwgZWNobz1GQUxTRSxvdXQud2lkdGg9JzQwJScsIGZpZy5oZWlnaHQ9Mi41LGZpZy5zaG93PSdob2xkJyxmaWcuYWxpZ249J2NlbnRlcid9DQpwbG90KExpbmVhci5TdHVkeTIpDQpwbG90KFF1YWQuU3R1ZHkyKQ0KYGBgDQoNCi0gVGhlIGxpbmVhciBmaXQgaXMgdGVycmlibGUuICBUaGUgcXVhZCBmaXQgaGFzIHdlaXJkbmVzcyBpbiB0aGUgdGFpbHMNCg0KDQojIyBMb2ctTGluZWFyIE1vZGVsIEZpdA0KLSBUaW1lIG11c3Qgc3RhcnQgYXQgMSBbYXMgbG9nKDApID0gaW5mXQ0KDQpgYGB7cn0NCkxpbmVhci5Mb2c8LWxtZXIoQW54aWV0eSB+IGxvZyhUaW1lKSpUeXBlKyAoMStsb2coVGltZSl8U3ViamVjdCksIGRhdGE9SGlnaEF4MiwgUkVNTD1GQUxTRSkNCmBgYA0KDQotIFJlc2lkdWFscyBsb29rIHZlcnkgZ29vZA0KYGBge3J9DQpwbG90KExpbmVhci5Mb2csIGNvcnJlbGF0aW9uPUZBTFNFKQ0KYGBgDQoNCi0gTW9kZWwgRml0DQoNCmBgYHtyfQ0Kc3VtbWFyeShMaW5lYXIuTG9nLCBjb3JyZWxhdGlvbj1GQUxTRSkNCmBgYA0KLSBOb3RpY2UgdGhlIHJhbmRvbSBjb3JyZWxhdGlvbnM7IHRoZXkgd291bGQgbmVlZCB0byBiZSByZW1vdmVkLg0KDQpgYGB7ciwgZmlnLndpZHRoPTYuMjUsZmlnLmhlaWdodD00LjB9DQpMaW5lYXIuTG9nLkZpeGVkPC1FZmZlY3QoYygiVGltZSIsIlR5cGUiKSxMaW5lYXIuTG9nLA0KICAgICAgICAgICAgICAgICAgICAgICAgeGxldmVscz1saXN0KFRpbWU9c2VxKDEsMTgsMSkpKQ0KDQpMaW5lYXIuTG9nLkZpeGVkPC1hcy5kYXRhLmZyYW1lKExpbmVhci5Mb2cuRml4ZWQpDQoNCkwuTG9nLlBsb3QgPC1nZ3Bsb3QoZGF0YSA9IExpbmVhci5Mb2cuRml4ZWQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gVGltZSwgeSA9Zml0LGdyb3VwPVR5cGUpKSsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj1UeXBlKSxzaXplPTIpKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbj1sb3dlciwgeW1heD11cHBlciksYWxwaGE9LjIpKw0KICB5bGFiKCJBbnhpZXR5IikreGxhYigiVGltZSIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMSwxOCwxKSkrDQogIGdndGl0bGUoIkJ5IFN1YmplY3Q6IExpbmVhciBMb2cgRml0IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrdGhlbWVfYncoKQ0KTC5Mb2cuUGxvdA0KYGBgDQoNCiMjIFJlc3VsdHMNCj4gV2hpbGUgdGhvc2UgaW4gdGhlIHN0YW5kYXJkIHRoZXJhcHkgY29uZGl0aW9uIGRpZCBzaG93IHJlZHVjZWQgYW54aWV0eSBieSB0aGUgZW5kIG9mIHRoZSBzZXNzaW9uLCB0aG9zZSAgaW4gdGhlIHJhZGljYWwgdGhlcmFweSBncm91cHMgc2hvd2VkIGJvdGggc2lnbmlmaWNhbnRseSBoaWdoZXIgc3RhcnRpbmcgYW54aWV0eSBsZXZlbCB0aGFuIHRoZSBzdGFuZGFyZCBncm91cCwgYW5kIHRoZXkgc2hvd2VkIHNpZ25pZmljYW50bHkgbW9yZSBkZWNheSBpbiB0aGVpciBhbnhpZXR5IGxldmVsIGFzIGluZGljYXRlZCBieSB0aGUgc2lnbmlmaWNhbnQgbmVnYXRpdmUgaW50ZXJhY3Rpb24uDQoNCiMjIyBWaXN1YWxpemluZyBDb21wbGV0ZSBGaW5hbCBNb2RlbCBGaXQNCi0gUGxvdCB0aGUgb3ZlcmFsbCBtb2RlbCBmaXQgYW5kIHRoZSBwZXIgc3ViamVjdCBmaXQgKHdpdGggZG90cyA9IHJhdyBzdWJqZWN0IGRhdGEpDQoNCmBgYHtyLCBmaWcud2lkdGg9Ni4yNSxmaWcuaGVpZ2h0PTQuMH0NCkhpZ2hBeDIkTG9nTW9kZWxGaXQ8LXByZWRpY3QoTGluZWFyLkxvZykNClNwZWcuRmluYWwuU3R1ZHkuMjwtZ2dwbG90KCkrDQogIGdlb21fcG9pbnQoZGF0YSA9IEhpZ2hBeDIsIGFlcyh4PVRpbWUseT1BbnhpZXR5LCBjb2xvcj1UeXBlKSkrDQogIGdlb21fbGluZShkYXRhID0gSGlnaEF4MiwgYWVzKHg9VGltZSx5PUxvZ01vZGVsRml0LCBjb2xvcj1UeXBlLCBncm91cD1TdWJqZWN0KSwgbGluZXR5cGU9MikrDQogIGdlb21fbGluZShkYXRhID0gTGluZWFyLkxvZy5GaXhlZCwgYWVzKHggPSBUaW1lLCB5ID1maXQsIGNvbG9yPVR5cGUsZ3JvdXA9VHlwZSksIHNpemU9MikrDQogIHlsYWIoIk1vZGVsIEZpdHRlZCBBbnhpZXR5IikreGxhYigiVGltZSIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMSwxOCwxKSkrDQogIGdndGl0bGUoIkJ5IFN1YmplY3QgTW9kZWwgTG9nIEZpdCIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSt0aGVtZV9idygpDQpTcGVnLkZpbmFsLlN0dWR5LjINCmBgYA0KDQoNCg0KIyBHcm93dGggQ3VydmUgd2l0aCBhIERpc2NvbnRpbnVpdHkgDQo+IFlvdSB3YW50IHRvIHJlcGxpY2F0ZSBhbmQgZXh0ZW5kIHN0dWR5IDIgZnJvbSBhYm92ZSwgYnV0IHlvdSBtYWtlIHRoZSB0cmVhdG1lbnQgbW9yZSByYWRpY2FsLiBZb3Uga25vdyB0aGF0IHRob3NlIGluIHRoZSBoaWdoIGFueGlldHkgY29uZGl0aW9uICh0aG9zZSB0aGF0IHdlbnQgdG8gIjExIikgd2lsbCBzdGFydCB3aXRob3V0IG1vcmUgYW54aW91cyBhbmQgdGhleS10aGV5IHdpbGwgcmVzcG9uZCB0byB0aGUgdHJlYXRtZW50ICoqbW9yZSoqLCBidXQgdGhlIG1pZ2h0IGFsc28gcmVzcG9uZCB0byByYXBpZCBvbnNldCBvZiBleHBvc3VyZSBiZXR0ZXIgYW5kIHlvdSBjYW4gY3VyZSB0aGVtIGZhc3Rlci4gQ2xpZW50cyBpbiBib3RoIGdyb3VwcyAoc3RhbmRhcmQgYW5kIHJhZGljYWwpIHVuZGVyZ28gcmFwaWQgZXhwb3N1cmUuIFlvdSBwdWxsIGEgbGV2ZXIgYXQgcm91Z2hseSB0aGUgMzUgbWluIG1hcmsgYW5kIHRoZXkgYXJlIGRyb3BwZWQgaW50byBhIGJhbGwtcGl0IGZpbGwgd2l0aCB0YXJhbnR1bGFzLiBZb3UgZXhwZWN0IHRob3NlIHdobyBpbiB0aGUgc3RhbmRhcmQgZ3JvdXAgdG8gcmVzcG9uZCBwb29ybHkgdG8gdGhlaXIgbmV3IHNpdHVhdGlvbiwgd2hpbGUgdGhvc2UgcGVvcGxlIGluIHRoZSByYWRpY2FsIGdyb3VwIHdlcmUgcHJlcGFyZWQgZm9yIHRoZSB3b3JzdCBhbmQgd2lsbCBpbXByb3ZlLiBBZ2FpbiwgeW91IHRha2UgdGhlaXIgQW54aWV0eSBzY29yZSBldmVyeSAzLjMgbWlucyB0aHJvdWdob3V0IGEgNjAtbWludXRlIHNlc3Npb24uDQoNCiMjIFZpc3VhbGl6ZSBSYXcgRGF0YQ0KLSBEViA9KipBbnhpZXR5IHNjb3JlKiogKDAgPSBMb3cgdG8gMTEgSGlnaCkNCi0gSVYxID0gKipUaW1lKiogKDEtMTgpDQotIElWMiA9ICoqVGhlcmFweSBUeXBlKiogKFN0YW5kYXJkIG9yIFJhZGljYWwpDQotIElWMyA9U3BpZGVyIHBpdCAoZnJvbSBUaW1lIDEwLTE4KQ0KDQpgYGB7ciwgZmlnLndpZHRoPTYuMjUsZmlnLmhlaWdodD00LjB9DQpIaWdoQXgzPC1yZWFkLmNzdigiTWl4ZWQvU3R1ZHkzLmNzdiIpDQpIaWdoQXgzJFR5cGU8LWZhY3RvcihIaWdoQXgzJFRyZWF0bWVudCwgDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlN0YW5kYXJkIiwgIlJhZGljYWwiKSkNCkhpZ2hBeDMkTG9jYXRpb248LWZhY3RvcihIaWdoQXgzJFBpdCwgDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk9mZmljZSIsICJTcGlkZXIgUGl0IikpDQpIaWdoQXgzJFN1YmplY3Q8LWFzLmZhY3RvcihIaWdoQXgzJFN1YmplY3QpDQoNClNwZWcuU3R1ZHkzPC1nZ3Bsb3QoZGF0YSA9IEhpZ2hBeDMsIGFlcyh4PVRpbWUseT1BbnhpZXR5LCAgY29sb3I9TG9jYXRpb24pKSsNCiAgZmFjZXRfZ3JpZCh+VHlwZSkrDQogIGdlb21fc21vb3RoKGFlcyhncm91cD1Mb2NhdGlvbiksbWV0aG9kPSdsb2VzcycsIHNlPUZBTFNFLCBjb2xvcj0nYmxhY2snLCBzaXplPTIpKw0KICAgIGdlb21fcG9pbnQoYWVzKGdyb3VwPVN1YmplY3QpKSsNCiAgZ2VvbV9saW5lKGFlcyhncm91cD1TdWJqZWN0KSkrDQogIHlsYWIoIkFueGlldHkiKSt4bGFiKCJUaW1lIikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTEsMSkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxLDE4LDEpKSsNCiAgZ2d0aXRsZSgiQnkgU3ViamVjdCIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpK3RoZW1lX2J3KCkNClNwZWcuU3R1ZHkzDQpgYGANCg0KIyMjIFRyYW5zZm9ybXMNCi0gTGlrZSBiZWZvcmUgbGV0cyBsb2cgVGltZQ0KDQpgYGB7ciwgZmlnLndpZHRoPTYuMjUsZmlnLmhlaWdodD00LjB9DQpTcGVnLlN0dWR5My5sb2c8LWdncGxvdChkYXRhID0gSGlnaEF4MywgYWVzKHg9bG9nKFRpbWUpLHk9QW54aWV0eSwgIGNvbG9yPUxvY2F0aW9uKSkrDQogIGZhY2V0X2dyaWQoflR5cGUpKw0KICBnZW9tX3Ntb290aChhZXMoZ3JvdXA9TG9jYXRpb24pLG1ldGhvZD0nbG9lc3MnLCBzZT1GQUxTRSwgY29sb3I9J2JsYWNrJywgc2l6ZT0yKSsNCiAgICBnZW9tX3BvaW50KGFlcyhncm91cD1TdWJqZWN0KSkrDQogIGdlb21fbGluZShhZXMoZ3JvdXA9U3ViamVjdCkpKw0KICB5bGFiKCJBbnhpZXR5IikreGxhYigiVGltZSIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDExLDEpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMSwxOCwxKSkrDQogIGdndGl0bGUoIkJ5IFN1YmplY3QiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSt0aGVtZV9idygpDQpTcGVnLlN0dWR5My5sb2cNCmBgYA0KDQotIFRha2luZyB0aGUgbG9nIGFnYWluIG1ha2VzIHNlbnNlDQoNCiMjIExvZy1MaW5lYXIgTW9kZWwgRml0IHdpdGggRGlzY29udGludWl0eQ0KLSBJbiB0aGlzIGNhc2UgTG9jYXRpb24gd2FzIGEgd2l0aGluLXN1YmplY3QgdmFyaWFibGUhDQoNCmBgYHtyfQ0KTGluZWFyLkxvZy5TdHVkeTM8LWxtZXIoQW54aWV0eSB+IGxvZyhUaW1lKSpUeXBlKkxvY2F0aW9uKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgKDErbG9nKFRpbWUpKkxvY2F0aW9ufFN1YmplY3QpLCBkYXRhPUhpZ2hBeDMsIFJFTUw9RkFMU0UpDQpzdW1tYXJ5KExpbmVhci5Mb2cuU3R1ZHkzLCBjb3JyZWxhdGlvbj1GQUxTRSkNCmBgYA0KDQotIFdlIGNvdWxkIHJlZHVjZSB0aGUgcmFuZG9tIHN0cnVjdHVyZSBieSByZW1vdmluZyB0aGUgY29ycmVsYXRpb25zIG9yIGJ5IHJ1bm5pbmcgcGFyc21vbm91cyBhcHByb2FjaCwgYnV0IHdlIHdpbGwgbGVhdmUgdGhpcyBmb3Igbm93LiANCg0KYGBge3IsIGZpZy53aWR0aD02LjI1LGZpZy5oZWlnaHQ9My4yNX0NCkxpbmVhci5Mb2cuRml4ZWQuUzM8LUVmZmVjdChjKCJUaW1lIiwiVHlwZSIsIkxvY2F0aW9uIiksTGluZWFyLkxvZy5TdHVkeTMsDQogICAgICAgICAgICAgICAgICAgICAgICB4bGV2ZWxzPWxpc3QoVGltZT1zZXEoMSwxOCwxKSkpDQoNCkxpbmVhci5Mb2cuRml4ZWQuUzM8LWFzLmRhdGEuZnJhbWUoTGluZWFyLkxvZy5GaXhlZC5TMykNCkxpbmVhci5Mb2cuRml4ZWQuUzMkVHlwZTwtZmFjdG9yKExpbmVhci5Mb2cuRml4ZWQuUzMkVHlwZSwgDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiU3RhbmRhcmQiLCAiUmFkaWNhbCIpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlN0YW5kYXJkIiwgIlJhZGljYWwiKSkNCg0KTGluZWFyLkxvZy5GaXhlZC5TMy5DbGVhbmVkPC1MaW5lYXIuTG9nLkZpeGVkLlMzICU+JQ0KICBkcGx5cjo6ZmlsdGVyKExvY2F0aW9uPT0iT2ZmaWNlIiAmIFRpbWU8PTEwfCAoTG9jYXRpb24hPSJPZmZpY2UiICYgVGltZT49MTApKQ0KDQoNCkwuTG9nLlBsb3QuUzMgPC1nZ3Bsb3QoZGF0YSA9IExpbmVhci5Mb2cuRml4ZWQuUzMuQ2xlYW5lZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBUaW1lLCB5ID1maXQpKSsNCiAgZmFjZXRfZ3JpZCh+VHlwZSkrDQogIGdlb21fbGluZShzaXplPTEsIGNvbG9yPSdyZWQnKSsNCiAgZ2VvbV9yaWJib24oYWVzKHltaW49bG93ZXIsIHltYXg9dXBwZXIpLGFscGhhPS4yKSsNCiAgeWxhYigiQW54aWV0eSIpK3hsYWIoIlRpbWUiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMSwxKSkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDEsMTgsMSkpKw0KICBnZ3RpdGxlKCJCeSBTdWJqZWN0OiBMaW5lYXIgTG9nIEZpdCIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTEwKSsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTMsIHkgPSA5LCBsYWJlbCA9ICJTcGlkZXIgUGl0IikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSt0aGVtZV9idygpDQpMLkxvZy5QbG90LlMzDQpgYGANCg0KIyMgUmVzdWx0cw0KPiAgSW4gYm90aCBzdGFuZGFyZCBhbmQgcmFkaWNhbCB0aGVyYXB5LCBkcm9wcGluZyB0aGVtIGludG8gdGhlIHNwaWRlciBwaXQgY2F1c2VkIHRoZW0gdG8gaGF2ZSBpbmNyZWFzZWQgYW54aWV0eSwgYnV0IG9ubHkgdGhlIHRob3NlIGluIHJhZGljYWwgdGhlcmFweSB3ZXJlIGFibGUgdG8gY2FsbSBkb3duLiAgICANCg0KDQojIE5vdGVzDQpBbHdheXMgdHJ5IHRvIGZvbGxvdyB3aGF0IHlvdSBzZWUgYW5kIHJlbWVtYmVyIHlvdSBhcmUgbW9kZWxpbmcgYXQgdGhlIGxldmVsIG9mIHRoZSBpbmRpdmlkdWFsIHN1YmplY3QgKHdoZW4gdGhlIHN1YmplY3QgaXMgdGhlIGxldmVsIDIpLiBUaGVzZSB0eXBlcyBvZiBtb2RlbCBhcmUgZWFzeSB0byBvdmVyLWZpdCAoZm9yIGV4YW1wbGUgdG9vIG1hbnkgcG9seW5vbWlhbHMpLiBIb3dldmVyLCB1bmRlcmZpdHRpbmcgY2FuIGJlIGVxdWFsbHkgYXMgYmFkLiBNYW55IHBlb3BsZSBhcmd1ZSBpZiB0aGUgbGluZWFyIGVmZmVjdCBkb2VzIG5vdCBjaGFuZ2UgdGhlICJzdG9yeSIgZG9uJ3QgYm90aGVyIHdpdGggdGhlIGhpZ2hlciBvcmRlci4gIFdoaWxlIHRoYXQgbWF5IGJlIHRydWUgc29tZXRpbWVzLCB5b3UgY2FuIGNhdXNlIHR5cGUgSUkgZXJyb3IgKGFzIHRoZSBsaW5lYXIgZml0IG1heWJlIHdlYWspIG9yIGV2ZW4gdHlwZSBTIGlzc3Vlcy4gIEltYWdpbmUgeW91IGhhdmUgYSBjdWJpYyBlZmZlY3QgYW5kIHRyeWluZyB0byBydW4gYSBsaW5lIHRocm91Z2ggaXQgYXMgdGhlIGZpcnN0IGFuZCBsYXN0IGRhdGEgcG9pbnRzIHlpZWxkIHRoZSBzaWduIChidXQgdGhhdCBtaWdodCBtaXNzIHdoZXJlIHRoZSBidWxrIG9mIHRoZSBkYXRhIGZhbGwpLiBQcm9wZXIgZ3Jvd3RoIGN1cnZlIG1vZGVsaW5nIHJlcXVpcmVzIGNhcmVmdWwgZ3JhcGhpbmcgYW5kIHN0ZXB3aXNlIG1vZGVsaW5nLiANCiANCiMjIEdlbmVyYWxpemVkIEFkZGl0aXZlIChNaXhlZCkgTW9kZWxzDQpHQU1NcyBhcmUgcXVpY2tseSBiZWNvbWluZyBhIG5ldyB3YXkgdG8gbWVyZ2UgbW9yZSBwb3dlcmZ1bCBub24tbGluZWFyIHNtb290aGluZyAoc3VjaCBhcyB1c2luZyBzcGxpbmVzKSB0byBmaXQgY29tcGxleCBmdW5jdGlvbnMgaW4gdGhlIG1peGVkIG1vZGVsIGZyYW1ld29yay4gIFRoZXkgYXJlIHVzZWZ1bCBmb3IgdGhpbmdzIHdoZXJlIHRoZSBzaGFwZXMgYXJlIHJhdGhlciBjcmF6eSAobGlrZSBuZXVyb24gZmlyaW5nKS4gVGhlc2UgYXJlIG1vcmUgYmxlZWRpbmcgZWRnZSBtZXRob2RzIGFuZCB3b3JrIG9uIHRoZW0gaXMgYWR2YW5jaW5nIHF1aWNrbHkuIFNvb24gdGhleSB3aWxsIHByb2JhYmx5IHJlcGxhY2UgbW9yZSBzaW1wbGlzdGljIGdyb3d0aCBjdXJ2ZSBtaXhlZCBtb2RlbHMsIGJ1dCBhcyBvZiB0b2RheSwgdGhleSBhcmUgbm90IGFzIGZsZXhpYmxlIGluIHRoZSB0eXBlcyBvZiByYW5kb20gc3RydWN0dXJlcyB0aGV5IGNhbiBoYW5kbGUuDQoNCjxzY3JpcHQ+DQogIChmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCiAgKGlbcl0ucT1pW3JdLnF8fFtdKS5wdXNoKGFyZ3VtZW50cyl9LGlbcl0ubD0xKm5ldyBEYXRlKCk7YT1zLmNyZWF0ZUVsZW1lbnQobyksDQogIG09cy5nZXRFbGVtZW50c0J5VGFnTmFtZShvKVswXTthLmFzeW5jPTE7YS5zcmM9ZzttLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsbSkNCiAgfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCdodHRwczovL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KDQogIGdhKCdjcmVhdGUnLCAnVUEtOTA0MTUxNjAtMScsICdhdXRvJyk7DQogIGdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQoNCjwvc2NyaXB0Pg0K