Signal processing is concerned with the representation, transformation and manipulation of signals and information they contain.

When doing Digital Signal Processing, R is not typically the first computer language to ring a bell. DSP seems to be widely done using languages such as MATLAB, Python and C++. 10 years ago, I probably wouldn’t have checked R's capabilities for DSP, I mean, MATLAB and C++ were the de facto back then for these tasks (Oh! Did I mention I would still be in elementary school? 😄 👩‍🏫). Fast forwarding, there are lots of tools in R’s ecosystem that have been developed.

It’s safe to say that it has never been easier to use R in almost any domain as demonstrated by Jared Lander’s talk at e-Rum 2020. In that same spirit of using R Anywhere and Everywhere, after implementing the lab: Speech in the time and frequency domain using MATLAB (can be found here), the next adventure was doing it in R too and Voila, here it is:

Speech in the time and frequency domain lab: An R Version

In this lab, we analysed a speech signal in the time and frequency domain. The objectives of this experiment were:

  1. To create a two second recording of one of the 10 digits in Kiswahili.
  2. To compute a spectrogram of the recorded speech signal.
  3. To obtain a segment 32ms long from a region with speech and another segment of the same duration from a segment without speech, plot and analyze them in the time domain.
  4. To compute the Discrete Fourier, Transform of both signals in (c) above.

Background: Putting it in very simple terms

Discrete-time signal processing is based on processing of numeric sequences indexed on integer variables rather than functions of a continuous independent variable. In Digital Signal Processing, signals are represented by sequences of finite precision numbers and processing is implemented using digital computation. Although there are many examples in which signals to be processed are inherently discrete-time sequences, most applications involve the use of discrete-time technology for processing signals that originate as continuous-time signals. In this case, a continuous-time signal is typically converted into a sequence of samples. After discrete-time processing, the output sequence is converted back to a continuous-time signal.

The Discrete Fourier Transform (DFT) is considered as the frequency domain representation of the original input sequence.The DFT is itself a sequence rather than a function of a continuous variable, and it corresponds to samples, equally spaced in frequency. A continuous time signal x(t) is first sampled to form the discrete time signal x[n] of length N samples. The transformed signal within the frequency domain is of finite extent but discrete and of the same length as the original signal. The DFT then decomposes a time domain signal defined over a finite range into a sum of weighted complex sinusoids as:

\(X(k) =\sum_{n=0}^{N-1} x[n] {e^{-j\frac{2\pi}Nkn}}\)

This operation is useful in many fields but computing it directly from the definition is often too slow to be practical. A fast Fourier transform (FFT) is an algorithm that computes the discrete Fourier Transform of a sequence or its inverse.

Data Analysis in R

Let’s now take a look at how we can analyze a speech signal in the time and frequency domain. The following table lists common quantities used in signal processing to characterize and interpret signal properties. They will be quite useful in our analysis, so yes, they are worth a look:


Quantity Description
y Sampled data
N <- length(y) Number of samples
Fs Sample frequency (samples per unit time or space)
dt <- 1/Fs Time or space increment per sample
t <- (seq_len(n) - 1)/Fs Time or space range for data
dft <- fft(y) Discrete Fourier transform of data (DFT)
abs(dft) Amplitude of the DFT
(abs(dft)^2)/N Power of the DFT
Fs/N Frequency increment/Frequency resolution
f <- (seq_len(n) - 1)*(Fs/N) Frequency range


A single channel 2s audio signal of the word moja (Swahili word for digit one) sampled at 8 kHz was recorded using a Raspberry Pi and stored as a .wav file. The recording can also be done using a laptop or mobile phone too.

If you would like to follow along using the audio file used in this post, you can download it from here: moja wav

For this analysis, we’ll be requiring the following packages in R:

suppressMessages({
library(seewave)
library(tuneR)
library(tidyverse)
library(audio)
library(plotly) # for adding some interactivity dust
library(fftw)
library(cowplot)
})

You can have them installed as:

`install.packages(c(“tidyverse”, “tuneR”,

“audio”, “plotly”, “oce”, “fftw”, “cowplot”))`

Loading the audio data and some pre-processing

Let’s begin by loading the speech signal recorded, reading it and then rescaling the amplitude of the waveform to a canonical interval [-1,1].

# loading wav file
one = "C:/Users/keras/Documents/DSP_R/moja.wav"

# read and normalize wav file using tuneR
data <- tuneR::readWave(one) %>%
  tuneR::normalize(unit = c("1"), center = FALSE, rescale = FALSE)

The audio signal was recorded for 2 seconds and then reduced to a discrete time signal by sampling at 8 kHz to obtain 16000 samples as below:

\(sampling \, period \, (T_s) =\, \frac{1}{sampling\,frequency \, (F_s)} \, =\, \frac{1}{8000}\)

\(T_s \, = \,0.125\, _{ms} \, (time\,difference\,between\,samples)\)

\(Number\,of\,samples\, = segment\,length(sec) \times sampling\,frequency \,\)

\(Number\,of\,samples\, = \,16000\)

All these summaries of the Wave-object can be obtained as:

summary(data)
## 
## Wave Object
##  Number of Samples:      16000
##  Duration (seconds):     2
##  Samplingrate (Hertz):   8000
##  Channels (Mono/Stereo): Mono
##  PCM (integer format):   TRUE
##  Bit (8/16/24/32/64):    32
## 
## Summary statistics for channel(s):
## 
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## -0.578278 -0.027527  0.003113 -0.001011  0.029671  0.763031

Plotting the audio data

Now we’ll make a plot of the signal’s amplitude vs the corresponding sample number.

# extracting sampled data, y
y = data@left

# extracting the sample rate, Fs
Fs = data@samp.rate

# you can listen to the audio too!
# audio::play.audioSample(y, Fs)

# getting our ggplot on
theme_set(theme_light())
wvfm <- ggplot(mapping = aes(x = seq_len(length(y)), y = y))+
  geom_line(color = 'blue')+
  labs(x = "Sample Number", y = "Amplitude", title = "Speech waveform")+
  theme(plot.title = element_text(hjust = 0.5))

ggplotly(wvfm)

From the Speech waveform plot, one can easily identify the segments that contain speech and those without (very faint speech).

We’ll need these segments for some later analysis, so let’s define sample numbers that contain speech and those without.

# sample index of segments with speech and those without
speech_start <- 5100
silence_start <- 9850

Computing a spectrogram of the signal

A spectrogram is a visual representation of the spectrum of frequencies of a signal as it varies with time. It is usually depicted as a heat map with intensity being shown by varying the colour or brightness.

We will compute the spectrogram by dividing the speech signal into segments 32ms long (256 samples at 8kHz) with 50% ovelap between neighboring segments and taking the FFT of each segment.

We will then plot the magnitude of this FFT for all the segments as a 2-dimensional image showing how the frequency varies with time. ggspectro {seewave} (an alternative to spectro {seewave}) allows us to draw a spectrogram with the package ggplot2. Maybe some few things to note is that I have specified a Fourier Transform window and used the Fast Fourier Transform (by setting fftw = TRUE) to speed up the process.

# defining the window size (number of samples in a 32ms long segment)
N <- 32e-3*Fs

# computing a spectrogram of the audio showing how frequency varies with time
spect <- ggspectro(y, Fs, wl = N, wn = "hamming", ovlp = 50, fftw = T)+
  geom_tile(aes(fill = amplitude))+
  scale_fill_viridis_c()
ggplotly(spect)

The spectrogram appears as a heatmap with the various colours representing power in decibels. High values are represented by yellow, low values by deep blue and intermediate values appear as green. It is evident that the digit was spoken in the interval between 0.4s and 1s where there are high values of frequency.


Now, let’s divide the speech signal into segments 64ms, take the FFT of each segment and whip up a spectrogram.

# defining the window size (number of samples in a 32ms long segment)
N_2 <- 64e-3*Fs

# computing a spectrogram of the audio showing how frequency varies with time
spect <- ggspectro(y, Fs, wl = N_2, wn = "hamming", ovlp = 50, fftw = TRUE)+
  geom_tile(aes(fill = amplitude))+
  scale_fill_viridis_c()
ggplotly(spect)

Increasing the segment length to 64ms consequently increases the window length to 512 samples. This effect of this is that the frequency resolution increases. The frequency resolution is obtained by the ratio Fs/N. So, for a given sampling frequency (Fs), the more samples (N) in the signal, the smaller the frequency increment between successive DFT data points. Since more points are being sampled, the spectral resolution increases.

In what frequency range is most of the energy of the signal?

To answer this questions, we will have to come up with a magnitude spectrum of the speech waveform which shows the magnitude of the signal at a particular frequency. This means we will have to convert the signal from the time domain to a representation in the frequency domain by computing the Discrete Fourier Transform (using a FFT algorithm). Once we have the amplitude of the DFT and the frequency range, we’ll be ready to go!

Due to mathematical convenience, the arrangement of the FFT output has positive and negative frequencies side by side in a slightly non-intuitive order (the frequency increases from 0 until N/2 and then it decreases for negative frequencies). As the positive and negative frequency magnitudes are identical for a real signal it is common just to plot the positive frequencies from 0 to Fs/2 (in our case 0 to 4000 Hz).

# Discrete Fourier transform of data (DFT)
dft_speech <- fftw::FFT(y)

# amplitude of DFT
dft_amp <- abs(dft_speech) # DFT is a sequence of complex sinusoids 

# number of samples
n <- length(y)

# frequency range
f <- (seq_len(n)-1)*Fs/n

freq_plot <- ggplot(mapping = aes(x = f[1:Fs], y = dft_amp[1:Fs]))+
  geom_line(color = 'blue')+
  labs(x = "Frequency (Hz)", y = "Magnitude",
title = "Frequency domain representation of the entire speech sequence")+
  theme(plot.title = element_text(hjust = 0.5))

ggplotly(freq_plot)

From the magnitude spectrum of the Discrete Fourier Transform, we can observe that most of the energy is concentrated in the frequency range 0 to 1 KHz.

Time domain, Frequency domain and Energy comparison of a segment containing speech and one without

The time domain representation of a speech signal shows how the sound changes in intensity over time

Earlier on in this analysis, we defined some sample numbers in regions containing speech and those without. First, we’ll obtain a segment 32ms long from a region with speech and another segment of the same duration from a segment without speech and plot these time domain signals side by side with time in seconds as the x-axis. A 32ms long segment corresponds to 256 samples:

\(Number\,of\,samples\, = \,segment\,length(sec) \times sampling\,frequency \,\)

\(= 0.032 \times 8000 = 256\)

# sample length
sample_len = 32e-3*Fs

# obtaining a 32ms long segment/256 samples without speech
moja_sl <- y[silence_start : (silence_start + sample_len - 1)]

# obtaining a 32ms long segment/256 samples with speech
moja_sp <- y[speech_start : (speech_start + sample_len - 1)]

# corresponding time-length of the sample (32ms long)
t <- (seq_len(sample_len) - 1) * 1/Fs

# plotting the waveform of the silent segment
wvfm_sl <- ggplot(mapping = aes(x = t, y = moja_sl)) +
  geom_line(color = 'blue', lwd = 1.1)+
  labs(x = "Time (s)", y = "Amplitude",
       title = "Time domain of silent segment")+
  theme(plot.title = element_text(hjust = 0.5))

# ggplotly(wvfm_sl)

# plotting the waveform of the segment with speech
wvfm_sp <- ggplot(mapping = aes(x = t, y = moja_sp)) +
  geom_line(color = 'blue', lwd = 1.1)+
  labs(x = "Time (s)", y = "Amplitude",
       title = "Time domain of speech segment")+
  theme(plot.title = element_text(hjust = 0.5))

# ggplotly(wvfm_sp)

cowplot::plot_grid(wvfm_sl, wvfm_sp)

From the above plots above, the time domain plot of the segment containing speech has higher intensity (amplitude ranging from -0.27 to 0.24) than that without speech/faint speech due to noise (amplitude ranging from -0.02 to 0.06).

The time domain plot of the segment containing speech is also more erratic due to more fluctuations in amplitude of sound.

One common metric that can be easily computed from the sample values is a signal's energy.

The energy of a discrete time signal is defined as the sum of the square of the sampled signal values: \(E(y) =\sum_{n=-\infty}^{\infty} y^2\)

# Energy of silent segment
E_sl <- sum(moja_sl^2)
E_sl
## [1] 0.1733198
# Energy of segment with speech
E_sp <- sum(moja_sp^2)
E_sp
## [1] 3.440872

The silent segment has an energy of 0.1733 whereas the segment containing speech has an energy of 3.4409. Due to higher intensities of sound in the region containing speech, the sample values in this region have higher amplitude values which in turn account for the higher energy. The energy in the silent segment could be attributed to noise and can be filtered out using an appropriate filter.


The time domain analysis can also be done in the frequency domain by computing the Discrete Fourier Transform (using a FFT algorithm) of the two segments.

# DFT of silent segment
dft_sl <- fftw::FFT(moja_sl)

# DFT of speech segment
dft_sp <- fftw::FFT(moja_sp)

# frequency range
fs <- (seq_len(sample_len)-1)*Fs/sample_len

# plotting the waveform of the silent segment in freq domain
freqd_sl <- ggplot(mapping = aes(x = fs[1 : (sample_len)/2],
  y = abs(dft_sl[1: (sample_len)/2]))) +
  geom_line(color = 'blue', lwd = 1.1)+
  labs(x = "Frequency (Hz)", y = "Amplitude",
       title = "Frequency domain of silent segment")+
  theme(plot.title = element_text(hjust = 0.5))

# ggplotly(freqd_sl)

# plotting the waveform of the speech segment in freq domain
freqd_sp <- ggplot(mapping = aes(x = fs[1 : (sample_len)/2], 
  y = abs(dft_sp[1: (sample_len)/2]))) +
  geom_line(color = 'blue', lwd = 1.1)+
  labs(x = "Frequency (Hz)", y = "Amplitude",
       title = "Frequency domain of speech segment")+
  theme(plot.title = element_text(hjust = 0.5))

# ggplotly(freqd_sp)

cowplot::plot_grid(freqd_sl, freqd_sp)

When the two segments are converted from the time domain to a representation in the frequency domain the same inferences made earlier still hold. The segment containing speech has higher magnitude than that without speech at various frequencies.

Signal power as a function of frequency is another common metric used in signal processing. Power is the squared magnitude of a signal’s Fourier transform, normalized by the number of frequency samples:

\(P_{dft}\, =\, [\mid {(dft)}\mid^2]/N\)

Let’s plot a power spectrum of the segment containing speech, shall we?

# power of the dft
power <-  ((abs(dft_sp))^2)/sample_len

# frequency range
fp <- (seq_len(sample_len)-1)*Fs/sample_len

# power spectrum
power_sp <- ggplot(mapping = aes(x = fp[1 : (sample_len)/2],
  y = power[1: (sample_len)/2])) +
  geom_line(color = 'blue', lwd = 1.1)+
  labs(x = "Frequency (Hz)", y = "Power",
       title = "Power Spectrum of speech segment")+
  theme(plot.title = element_text(hjust = 0.5))

ggplotly(power_sp)

The plot indicates that the speech segment consists of a fundamental frequency around 156.25 Hz and a sequence of harmonics, where the third harmonic is emphasized.

Time to it a wrap! It’s been quite the adventure, right 😊? Anyhow, we got to analyze a speech signal and at the same time cover some fundamental concepts in Digital Signal Processing.

This is by no means a thorough walk-through but I hope it will ignite some R-DSP spark or something.

Hoping to cover some more exciting concepts … soon!

Be sure to check out great blogs, tutorials and other formats of R resources coming out every day at RWeekly.org!

Till then,

Happy Learning 👩🏽‍💻 👨‍💻 👨🏾‍💻 👩‍💻 ,

Eric (R_ic), Microsoft Learn Student Ambassador.

Reference Material

  • Andrew O. Finley, Jeffrey W. Doser, Vince Melfi, “Digital Signal Processing”, in R Programming for Data Sciences. 2020

  • I. V. McLoughlin, Speech and Audio Processing: A MATLAB®-based approach. 2016.

  • MATLAB Documentation, Basic Spectral Analysis

  • A. V Oppenheim and R. W. Schafer, “Discrete Time Signal Processing 2nd Edition,” Book. 1998.

  • Eric Wanjau, Samuel Kariuki, Ian Muchiri, Joshua Ndemenge, Analysis of Speech in the Time and Frequency Domain using MATLAB.

LS0tDQp0aXRsZTogIioqQSBnZW50bGUgaW50cm9kdWN0aW9uOiBSIGluIERpZ2l0YWwgU2lnbmFsIFByb2Nlc3NpbmcqKiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjc3M6IHN0eWxlXzMuY3NzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgaW5jbHVkZXM6DQogICAgICBhZnRlcl9ib2R5OiBmb290ZXIuaHRtbA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQotLS0NCg0KU2lnbmFsIHByb2Nlc3NpbmcgaXMgY29uY2VybmVkIHdpdGggdGhlIHJlcHJlc2VudGF0aW9uLCB0cmFuc2Zvcm1hdGlvbiBhbmQgbWFuaXB1bGF0aW9uIG9mIHNpZ25hbHMgYW5kIGluZm9ybWF0aW9uIHRoZXkgY29udGFpbi4gDQoNCldoZW4gZG9pbmcgRGlnaXRhbCBTaWduYWwgUHJvY2Vzc2luZywgYFJgIGlzIG5vdCB0eXBpY2FsbHkgdGhlIGZpcnN0IGNvbXB1dGVyIGxhbmd1YWdlIHRvIHJpbmcgYSBiZWxsLiBEU1Agc2VlbXMgdG8gYmUgd2lkZWx5IGRvbmUgdXNpbmcgbGFuZ3VhZ2VzIHN1Y2ggYXMgYE1BVExBQmAsIGBQeXRob25gIGFuZCBgQysrYC4gMTAgeWVhcnMgYWdvLCBJIHByb2JhYmx5IHdvdWxkbid0IGhhdmUgY2hlY2tlZCBgUidzYCBjYXBhYmlsaXRpZXMgZm9yIERTUCwgSSBtZWFuLCBNQVRMQUIgYW5kIEMrKyB3ZXJlIHRoZSBkZSBmYWN0byBiYWNrIHRoZW4gZm9yIHRoZXNlIHRhc2tzIChPaCEgRGlkIEkgbWVudGlvbiBJIHdvdWxkIHN0aWxsIGJlIGluIGVsZW1lbnRhcnkgc2Nob29sPyDwn5iEIPCfkanigI3wn4+rKS4gRmFzdCBmb3J3YXJkaW5nLCB0aGVyZSBhcmUgbG90cyBvZiB0b29scyBpbiBSJ3MgZWNvc3lzdGVtIHRoYXQgaGF2ZSBiZWVuIGRldmVsb3BlZC4gDQoNCkl0J3Mgc2FmZSB0byBzYXkgdGhhdCBpdCBoYXMgbmV2ZXIgYmVlbiBlYXNpZXIgdG8gdXNlIFIgaW4gYWxtb3N0IGFueSBkb21haW4gYXMgZGVtb25zdHJhdGVkIGJ5IFtKYXJlZCBMYW5kZXIncyB0YWxrXShodHRwczovL2phcmVkbGFuZGVyLmNvbS9jb250ZW50LzIwMjAvMDYvQXBwbHlpbmdSQXRXb3JrLmh0bWwjMSkgYXQgW2UtUnVtIDIwMjBdKGh0dHBzOi8vMjAyMC5lcnVtLmlvLykuIEluIHRoYXQgc2FtZSBzcGlyaXQgb2YgYHVzaW5nIFIgQW55d2hlcmUgYW5kIEV2ZXJ5d2hlcmVgLCBhZnRlciBpbXBsZW1lbnRpbmcgdGhlIGxhYjogYFNwZWVjaCBpbiB0aGUgdGltZSBhbmQgZnJlcXVlbmN5IGRvbWFpbmAgdXNpbmcgTUFUTEFCIChjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9SLWljbnRheS9EU1BfTUFUTEFCKSksIHRoZSBuZXh0IGFkdmVudHVyZSB3YXMgZG9pbmcgaXQgaW4gUiB0b28gYW5kIFZvaWxhLCBoZXJlIGl0IGlzOg0KDQojICoqU3BlZWNoIGluIHRoZSB0aW1lIGFuZCBmcmVxdWVuY3kgZG9tYWluIGxhYjoqKiBBbiBSIFZlcnNpb24NCg0KSW4gdGhpcyBsYWIsIHdlIGFuYWx5c2VkIGEgc3BlZWNoIHNpZ25hbCBpbiB0aGUgdGltZSBhbmQgZnJlcXVlbmN5IGRvbWFpbi4gVGhlIG9iamVjdGl2ZXMgb2YgdGhpcyBleHBlcmltZW50IHdlcmU6DQoNCmEuIFRvIGNyZWF0ZSBhIHR3byBzZWNvbmQgcmVjb3JkaW5nIG9mIG9uZSBvZiB0aGUgMTAgZGlnaXRzIGluIEtpc3dhaGlsaS4NCmIuIFRvIGNvbXB1dGUgYSBzcGVjdHJvZ3JhbSBvZiB0aGUgcmVjb3JkZWQgc3BlZWNoIHNpZ25hbC4NCmMuIFRvIG9idGFpbiBhIHNlZ21lbnQgMzJtcyBsb25nIGZyb20gYSByZWdpb24gd2l0aCBzcGVlY2ggYW5kIGFub3RoZXIgc2VnbWVudCBvZiB0aGUgc2FtZSBkdXJhdGlvbiBmcm9tIGEgc2VnbWVudCB3aXRob3V0IHNwZWVjaCwgcGxvdCBhbmQgYW5hbHl6ZSB0aGVtIGluIHRoZSB0aW1lIGRvbWFpbi4NCmQuIFRvIGNvbXB1dGUgdGhlIERpc2NyZXRlIEZvdXJpZXIsIFRyYW5zZm9ybSBvZiBib3RoIHNpZ25hbHMgaW4gKGMpIGFib3ZlLg0KDQoNCiMgKipCYWNrZ3JvdW5kOioqIFB1dHRpbmcgaXQgaW4gdmVyeSBzaW1wbGUgdGVybXMNCg0KRGlzY3JldGUtdGltZSBzaWduYWwgcHJvY2Vzc2luZyBpcyBiYXNlZCBvbiBwcm9jZXNzaW5nIG9mIG51bWVyaWMgc2VxdWVuY2VzIGluZGV4ZWQgb24gaW50ZWdlciB2YXJpYWJsZXMgcmF0aGVyIHRoYW4gZnVuY3Rpb25zIG9mIGEgY29udGludW91cyBpbmRlcGVuZGVudCB2YXJpYWJsZS4gSW4gRGlnaXRhbCBTaWduYWwgUHJvY2Vzc2luZywgc2lnbmFscyBhcmUgcmVwcmVzZW50ZWQgYnkgc2VxdWVuY2VzIG9mIGZpbml0ZSBwcmVjaXNpb24gbnVtYmVycyBhbmQgcHJvY2Vzc2luZyBpcyBpbXBsZW1lbnRlZCB1c2luZyBkaWdpdGFsIGNvbXB1dGF0aW9uLiBBbHRob3VnaCB0aGVyZSBhcmUgbWFueSBleGFtcGxlcyBpbiB3aGljaCBzaWduYWxzIHRvIGJlIHByb2Nlc3NlZCBhcmUgaW5oZXJlbnRseSBkaXNjcmV0ZS10aW1lIHNlcXVlbmNlcywgbW9zdCBhcHBsaWNhdGlvbnMgaW52b2x2ZSB0aGUgdXNlIG9mIGRpc2NyZXRlLXRpbWUgdGVjaG5vbG9neSBmb3IgcHJvY2Vzc2luZyBzaWduYWxzIHRoYXQgb3JpZ2luYXRlIGFzIGNvbnRpbnVvdXMtdGltZSBzaWduYWxzLiBJbiB0aGlzIGNhc2UsIGEgY29udGludW91cy10aW1lIHNpZ25hbCBpcyB0eXBpY2FsbHkgY29udmVydGVkIGludG8gYSBzZXF1ZW5jZSBvZiBzYW1wbGVzLiBBZnRlciBkaXNjcmV0ZS10aW1lIHByb2Nlc3NpbmcsIHRoZSBvdXRwdXQgc2VxdWVuY2UgaXMgY29udmVydGVkIGJhY2sgdG8gYSBjb250aW51b3VzLXRpbWUgc2lnbmFsLg0KDQpUaGUgRGlzY3JldGUgRm91cmllciBUcmFuc2Zvcm0gKERGVCkgaXMgY29uc2lkZXJlZCBhcyB0aGUgZnJlcXVlbmN5IGRvbWFpbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgaW5wdXQgc2VxdWVuY2UuVGhlIERGVCBpcyBpdHNlbGYgYSBzZXF1ZW5jZSByYXRoZXIgdGhhbiBhIGZ1bmN0aW9uIG9mIGEgY29udGludW91cyB2YXJpYWJsZSwgYW5kIGl0IGNvcnJlc3BvbmRzIHRvIHNhbXBsZXMsIGVxdWFsbHkgc3BhY2VkIGluIGZyZXF1ZW5jeS4gQSBjb250aW51b3VzIHRpbWUgc2lnbmFsIHgodCkgaXMgZmlyc3Qgc2FtcGxlZCB0byBmb3JtIHRoZSBkaXNjcmV0ZSB0aW1lIHNpZ25hbCB4W25dIG9mIGxlbmd0aCBOIHNhbXBsZXMuIFRoZSB0cmFuc2Zvcm1lZCBzaWduYWwgd2l0aGluIHRoZSBmcmVxdWVuY3kgZG9tYWluIGlzIG9mIGZpbml0ZSBleHRlbnQgYnV0IGRpc2NyZXRlIGFuZCBvZiB0aGUgc2FtZSBsZW5ndGggYXMgdGhlIG9yaWdpbmFsIHNpZ25hbC4gVGhlIERGVCB0aGVuIGRlY29tcG9zZXMgYSB0aW1lIGRvbWFpbiBzaWduYWwgZGVmaW5lZCBvdmVyIGEgZmluaXRlIHJhbmdlIGludG8gYSBzdW0gb2Ygd2VpZ2h0ZWQgY29tcGxleCBzaW51c29pZHMgYXM6DQoNCiRYKGspID1cc3VtX3tuPTB9XntOLTF9IHhbbl0ge2Veey1qXGZyYWN7MlxwaX1Oa259fSQNCg0KVGhpcyBvcGVyYXRpb24gaXMgdXNlZnVsIGluIG1hbnkgZmllbGRzIGJ1dCBjb21wdXRpbmcgaXQgZGlyZWN0bHkgZnJvbSB0aGUgZGVmaW5pdGlvbiBpcyBvZnRlbiB0b28gc2xvdyB0byBiZSBwcmFjdGljYWwuDQpBIGZhc3QgRm91cmllciB0cmFuc2Zvcm0gKEZGVCkgaXMgYW4gYWxnb3JpdGhtIHRoYXQgY29tcHV0ZXMgdGhlIGRpc2NyZXRlIEZvdXJpZXIgVHJhbnNmb3JtIG9mIGEgc2VxdWVuY2Ugb3IgaXRzIGludmVyc2UuIA0KDQoNCiMgKipEYXRhIEFuYWx5c2lzIGluIFIqKg0KDQpMZXQncyBub3cgdGFrZSBhIGxvb2sgYXQgaG93IHdlIGNhbiBhbmFseXplIGEgc3BlZWNoIHNpZ25hbCBpbiB0aGUgdGltZSBhbmQgZnJlcXVlbmN5IGRvbWFpbi4gVGhlIGZvbGxvd2luZyB0YWJsZSBsaXN0cyBjb21tb24gcXVhbnRpdGllcyB1c2VkIGluIHNpZ25hbCBwcm9jZXNzaW5nIHRvIGNoYXJhY3Rlcml6ZSBhbmQgaW50ZXJwcmV0IHNpZ25hbCBwcm9wZXJ0aWVzLiBUaGV5IHdpbGwgYmUgcXVpdGUgdXNlZnVsIGluIG91ciBhbmFseXNpcywgc28geWVzLCB0aGV5IGFyZSB3b3J0aCBhIGxvb2s6DQoNCjxicj4NCg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmRmIDwtIHRpYmJsZSgNCiAgUXVhbnRpdHkgPSBjKCJ5IiwgIk4gPC0gIGxlbmd0aCh5KSIsICJGcyIsICJkdCA8LSAxL0ZzIiwgInQgPC0gKHNlcV9sZW4obikgLSAxKS9GcyIsICJkZnQgPC0gZmZ0KHkpIiwgImFicyhkZnQpIiwgIihhYnMoZGZ0KV4yKS9OIiAsICJGcy9OIiwgImYgPC0gKHNlcV9sZW4obikgLSAxKSooRnMvTikiKSwNCiAgDQogIA0KICBEZXNjcmlwdGlvbiA9IGMoIlNhbXBsZWQgZGF0YSIsICJOdW1iZXIgb2Ygc2FtcGxlcyIsICJTYW1wbGUgZnJlcXVlbmN5IChzYW1wbGVzIHBlciB1bml0IHRpbWUgb3Igc3BhY2UpIiwgIlRpbWUgb3Igc3BhY2UgaW5jcmVtZW50IHBlciBzYW1wbGUiLCAiVGltZSBvciBzcGFjZSByYW5nZSBmb3IgZGF0YSIsICJEaXNjcmV0ZSBGb3VyaWVyIHRyYW5zZm9ybSBvZiBkYXRhIChERlQpIiwgIkFtcGxpdHVkZSBvZiB0aGUgREZUIiwgIlBvd2VyIG9mIHRoZSBERlQiLCAiRnJlcXVlbmN5IGluY3JlbWVudC9GcmVxdWVuY3kgcmVzb2x1dGlvbiIsICJGcmVxdWVuY3kgcmFuZ2UiICApDQopDQoNCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KZGYgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9tYXRlcmlhbF9kYXJrKCkNCg0KI2xpYnJhcnkoZm9ybWF0dGFibGUpDQojZm9ybWF0dGFibGUoZGYsIGxpc3QoDQogIyBRdWFudGl0eSA9IGNvbG9yX3RpbGUoIndoaXRlIiwgImdyZXkiKSwNCiAjIERlc2NyaXB0aW9uID0gY29sb3JfdGlsZSgid2hpdGUiLCAib3JhbmdlIikNCiMpKQ0KDQpgYGANCg0KPGJyPg0KDQpBIHNpbmdsZSBjaGFubmVsIDJzIGF1ZGlvIHNpZ25hbCBvZiB0aGUgd29yZCBgbW9qYWAgKFN3YWhpbGkgd29yZCBmb3IgZGlnaXQgb25lKSBzYW1wbGVkIGF0IDgga0h6IHdhcyByZWNvcmRlZCB1c2luZyBhIFJhc3BiZXJyeSBQaSBhbmQgc3RvcmVkIGFzIGEgYC53YXZgIGZpbGUuIFRoZSByZWNvcmRpbmcgY2FuIGFsc28gYmUgZG9uZSB1c2luZyBhIGxhcHRvcCBvciBtb2JpbGUgcGhvbmUgdG9vLg0KDQpJZiB5b3Ugd291bGQgbGlrZSB0byBmb2xsb3cgYWxvbmcgdXNpbmcgdGhlIGF1ZGlvIGZpbGUgdXNlZCBpbiB0aGlzIHBvc3QsIHlvdSBjYW4gZG93bmxvYWQgaXQgZnJvbSBoZXJlOiBbbW9qYSB3YXZdKGh0dHBzOi8vc3RkbnRwYXJ0bmVycy1teS5zaGFyZXBvaW50LmNvbS86dTovZy9wZXJzb25hbC9lcmljX3dhbmphdV9zdHVkZW50YW1iYXNzYWRvcnNfY29tL0VYcEUydjBNaThsTHJMUkJYQ0hCWnRzQkdqT1lUd2xXaWw1dllzMXBSOVJSX1E/ZT1zWmZ2cGQpDQoNCkZvciB0aGlzIGFuYWx5c2lzLCB3ZSdsbCBiZSByZXF1aXJpbmcgdGhlIGZvbGxvd2luZyBwYWNrYWdlcyBpbiBSOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCnN1cHByZXNzTWVzc2FnZXMoew0KbGlicmFyeShzZWV3YXZlKQ0KbGlicmFyeSh0dW5lUikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShhdWRpbykNCmxpYnJhcnkocGxvdGx5KSAjIGZvciBhZGRpbmcgc29tZSBpbnRlcmFjdGl2aXR5IGR1c3QNCmxpYnJhcnkoZmZ0dykNCmxpYnJhcnkoY293cGxvdCkNCn0pDQpgYGANCg0KWW91IGNhbiBoYXZlIHRoZW0gaW5zdGFsbGVkIGFzOiANCg0KYGluc3RhbGwucGFja2FnZXMoYygidGlkeXZlcnNlIiwgInR1bmVSIiwNCg0KImF1ZGlvIiwgInBsb3RseSIsICJvY2UiLCAiZmZ0dyIsICJjb3dwbG90IikpYA0KDQojIyMgKipMb2FkaW5nIHRoZSBhdWRpbyBkYXRhIGFuZCBzb21lIHByZS1wcm9jZXNzaW5nKioNCg0KTGV0J3MgYmVnaW4gYnkgbG9hZGluZyB0aGUgc3BlZWNoIHNpZ25hbCByZWNvcmRlZCwgcmVhZGluZyBpdCBhbmQgdGhlbiByZXNjYWxpbmcgdGhlIGFtcGxpdHVkZSBvZiB0aGUgd2F2ZWZvcm0gdG8gYSBjYW5vbmljYWwgaW50ZXJ2YWwgWy0xLDFdLg0KDQpgYGB7cn0NCiMgbG9hZGluZyB3YXYgZmlsZQ0Kb25lID0gIkM6L1VzZXJzL2tlcmFzL0RvY3VtZW50cy9EU1BfUi9tb2phLndhdiINCg0KIyByZWFkIGFuZCBub3JtYWxpemUgd2F2IGZpbGUgdXNpbmcgdHVuZVINCmRhdGEgPC0gdHVuZVI6OnJlYWRXYXZlKG9uZSkgJT4lDQogIHR1bmVSOjpub3JtYWxpemUodW5pdCA9IGMoIjEiKSwgY2VudGVyID0gRkFMU0UsIHJlc2NhbGUgPSBGQUxTRSkNCg0KYGBgDQoNClRoZSBhdWRpbyBzaWduYWwgd2FzIHJlY29yZGVkIGZvciAyIHNlY29uZHMgYW5kIHRoZW4gcmVkdWNlZCB0byBhIGRpc2NyZXRlIHRpbWUgc2lnbmFsIGJ5IHNhbXBsaW5nIGF0IDgga0h6IHRvIG9idGFpbiAxNjAwMCBzYW1wbGVzIGFzIGJlbG93Og0KDQokc2FtcGxpbmcgXCwgcGVyaW9kIFwsIChUX3MpID1cLCBcZnJhY3sxfXtzYW1wbGluZ1wsZnJlcXVlbmN5IFwsIChGX3MpfSBcLCA9XCwgXGZyYWN7MX17ODAwMH0kDQoNCiRUX3MgXCwgPSBcLDAuMTI1XCwgX3ttc30gXCwgKHRpbWVcLGRpZmZlcmVuY2VcLGJldHdlZW5cLHNhbXBsZXMpJA0KDQokTnVtYmVyXCxvZlwsc2FtcGxlc1wsID0gc2VnbWVudFwsbGVuZ3RoKHNlYykgXHRpbWVzIHNhbXBsaW5nXCxmcmVxdWVuY3kgXCwkDQoNCiROdW1iZXJcLG9mXCxzYW1wbGVzXCwgPSBcLDE2MDAwJA0KDQpBbGwgdGhlc2Ugc3VtbWFyaWVzIG9mIHRoZSBXYXZlLW9iamVjdCBjYW4gYmUgb2J0YWluZWQgYXM6DQoNCmBgYHtyfQ0Kc3VtbWFyeShkYXRhKQ0KYGBgDQoNCg0KIyMjICoqUGxvdHRpbmcgdGhlIGF1ZGlvIGRhdGEqKg0KDQpOb3cgd2UnbGwgbWFrZSBhIHBsb3Qgb2YgdGhlIHNpZ25hbCdzIGBhbXBsaXR1ZGVgIHZzIHRoZSBjb3JyZXNwb25kaW5nIGBzYW1wbGUgbnVtYmVyYC4NCg0KYGBge3J9DQojIGV4dHJhY3Rpbmcgc2FtcGxlZCBkYXRhLCB5DQp5ID0gZGF0YUBsZWZ0DQoNCiMgZXh0cmFjdGluZyB0aGUgc2FtcGxlIHJhdGUsIEZzDQpGcyA9IGRhdGFAc2FtcC5yYXRlDQoNCiMgeW91IGNhbiBsaXN0ZW4gdG8gdGhlIGF1ZGlvIHRvbyENCiMgYXVkaW86OnBsYXkuYXVkaW9TYW1wbGUoeSwgRnMpDQoNCiMgZ2V0dGluZyBvdXIgZ2dwbG90IG9uDQp0aGVtZV9zZXQodGhlbWVfbGlnaHQoKSkNCnd2Zm0gPC0gZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHNlcV9sZW4obGVuZ3RoKHkpKSwgeSA9IHkpKSsNCiAgZ2VvbV9saW5lKGNvbG9yID0gJ2JsdWUnKSsNCiAgbGFicyh4ID0gIlNhbXBsZSBOdW1iZXIiLCB5ID0gIkFtcGxpdHVkZSIsIHRpdGxlID0gIlNwZWVjaCB3YXZlZm9ybSIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KZ2dwbG90bHkod3ZmbSkNCg0KYGBgDQoNCg0KRnJvbSB0aGUgU3BlZWNoIHdhdmVmb3JtIHBsb3QsIG9uZSBjYW4gZWFzaWx5IGlkZW50aWZ5IHRoZSBzZWdtZW50cyB0aGF0IGNvbnRhaW4gc3BlZWNoIGFuZCB0aG9zZSB3aXRob3V0ICh2ZXJ5IGZhaW50IHNwZWVjaCkuDQoNCldlJ2xsIG5lZWQgdGhlc2Ugc2VnbWVudHMgZm9yIHNvbWUgbGF0ZXIgYW5hbHlzaXMsIHNvIGxldCdzIGRlZmluZSBzYW1wbGUgbnVtYmVycyB0aGF0IGNvbnRhaW4gc3BlZWNoIGFuZCB0aG9zZSB3aXRob3V0Lg0KDQpgYGB7cn0NCiMgc2FtcGxlIGluZGV4IG9mIHNlZ21lbnRzIHdpdGggc3BlZWNoIGFuZCB0aG9zZSB3aXRob3V0DQpzcGVlY2hfc3RhcnQgPC0gNTEwMA0Kc2lsZW5jZV9zdGFydCA8LSA5ODUwDQpgYGANCg0KDQojIyMgKipDb21wdXRpbmcgYSBzcGVjdHJvZ3JhbSBvZiB0aGUgc2lnbmFsKioNCg0KQSBzcGVjdHJvZ3JhbSBpcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgc3BlY3RydW0gb2YgZnJlcXVlbmNpZXMgb2YgYSBzaWduYWwgYXMgaXQgdmFyaWVzIHdpdGggdGltZS4gSXQgaXMgdXN1YWxseSBkZXBpY3RlZCBhcyBhIGhlYXQgbWFwIHdpdGggaW50ZW5zaXR5IGJlaW5nIHNob3duIGJ5IHZhcnlpbmcgdGhlIGNvbG91ciBvciBicmlnaHRuZXNzLg0KDQpXZSB3aWxsIGNvbXB1dGUgdGhlIHNwZWN0cm9ncmFtIGJ5IGRpdmlkaW5nIHRoZSBzcGVlY2ggc2lnbmFsIGludG8gc2VnbWVudHMgMzJtcyBsb25nICgyNTYgc2FtcGxlcyBhdCA4a0h6KSB3aXRoIDUwJSBvdmVsYXAgYmV0d2VlbiBuZWlnaGJvcmluZyBzZWdtZW50cyBhbmQgdGFraW5nIHRoZSBGRlQgb2YgZWFjaCBzZWdtZW50Lg0KDQoNCg0KV2Ugd2lsbCB0aGVuIHBsb3QgdGhlIG1hZ25pdHVkZSBvZiB0aGlzIEZGVCBmb3IgYWxsIHRoZSBzZWdtZW50cyBhcyBhIDItZGltZW5zaW9uYWwgaW1hZ2Ugc2hvd2luZyBob3cgdGhlIGZyZXF1ZW5jeSB2YXJpZXMgd2l0aCB0aW1lLiBgZ2dzcGVjdHJvIHtzZWV3YXZlfWAgKGFuIGFsdGVybmF0aXZlIHRvIGBzcGVjdHJvIHtzZWV3YXZlfWApIGFsbG93cyB1cyB0byBkcmF3IGEgc3BlY3Ryb2dyYW0gd2l0aCB0aGUgcGFja2FnZSBgZ2dwbG90MmAuIE1heWJlIHNvbWUgZmV3IHRoaW5ncyB0byBub3RlIGlzIHRoYXQgSSBoYXZlIHNwZWNpZmllZCBhIFtGb3VyaWVyIFRyYW5zZm9ybSB3aW5kb3ddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dpbmRvd19mdW5jdGlvbikgYW5kIHVzZWQgdGhlIFtGYXN0IEZvdXJpZXIgVHJhbnNmb3JtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GYXN0X0ZvdXJpZXJfdHJhbnNmb3JtKSAoYnkgc2V0dGluZyBmZnR3ID0gVFJVRSkgdG8gc3BlZWQgdXAgdGhlIHByb2Nlc3MuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQojIGRlZmluaW5nIHRoZSB3aW5kb3cgc2l6ZSAobnVtYmVyIG9mIHNhbXBsZXMgaW4gYSAzMm1zIGxvbmcgc2VnbWVudCkNCk4gPC0gMzJlLTMqRnMNCg0KIyBjb21wdXRpbmcgYSBzcGVjdHJvZ3JhbSBvZiB0aGUgYXVkaW8gc2hvd2luZyBob3cgZnJlcXVlbmN5IHZhcmllcyB3aXRoIHRpbWUNCnNwZWN0IDwtIGdnc3BlY3Rybyh5LCBGcywgd2wgPSBOLCB3biA9ICJoYW1taW5nIiwgb3ZscCA9IDUwLCBmZnR3ID0gVCkrDQogIGdlb21fdGlsZShhZXMoZmlsbCA9IGFtcGxpdHVkZSkpKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpDQpnZ3Bsb3RseShzcGVjdCkNCiAgDQpgYGANCg0KDQpUaGUgc3BlY3Ryb2dyYW0gYXBwZWFycyBhcyBhIGhlYXRtYXAgd2l0aCB0aGUgdmFyaW91cyBjb2xvdXJzIHJlcHJlc2VudGluZw0KcG93ZXIgaW4gZGVjaWJlbHMuIEhpZ2ggdmFsdWVzIGFyZSByZXByZXNlbnRlZCBieSB5ZWxsb3csIGxvdyB2YWx1ZXMgYnkgZGVlcCBibHVlIGFuZCBpbnRlcm1lZGlhdGUgdmFsdWVzIGFwcGVhciBhcyBncmVlbi4gSXQgaXMgZXZpZGVudCB0aGF0IHRoZSBkaWdpdCB3YXMgc3Bva2VuIGluIHRoZSBpbnRlcnZhbCBiZXR3ZWVuICoqMC40cyoqIGFuZCAqKjFzKiogd2hlcmUgdGhlcmUgYXJlIGhpZ2ggdmFsdWVzIG9mIGZyZXF1ZW5jeS4NCg0KPGJyPg0KDQpOb3csIGxldCdzIGRpdmlkZSB0aGUgc3BlZWNoIHNpZ25hbCBpbnRvIHNlZ21lbnRzIDY0bXMsIHRha2UgdGhlIEZGVCBvZiBlYWNoIHNlZ21lbnQgYW5kIHdoaXAgdXAgYSBzcGVjdHJvZ3JhbS4gDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQojIGRlZmluaW5nIHRoZSB3aW5kb3cgc2l6ZSAobnVtYmVyIG9mIHNhbXBsZXMgaW4gYSAzMm1zIGxvbmcgc2VnbWVudCkNCk5fMiA8LSA2NGUtMypGcw0KDQojIGNvbXB1dGluZyBhIHNwZWN0cm9ncmFtIG9mIHRoZSBhdWRpbyBzaG93aW5nIGhvdyBmcmVxdWVuY3kgdmFyaWVzIHdpdGggdGltZQ0Kc3BlY3QgPC0gZ2dzcGVjdHJvKHksIEZzLCB3bCA9IE5fMiwgd24gPSAiaGFtbWluZyIsIG92bHAgPSA1MCwgZmZ0dyA9IFRSVUUpKw0KICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBhbXBsaXR1ZGUpKSsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKQ0KZ2dwbG90bHkoc3BlY3QpDQpgYGANCg0KDQpJbmNyZWFzaW5nIHRoZSBzZWdtZW50IGxlbmd0aCB0byBgNjRtc2AgY29uc2VxdWVudGx5IGluY3JlYXNlcyB0aGUgYHdpbmRvdyBsZW5ndGhgIHRvIGA1MTIgc2FtcGxlc2AuIFRoaXMgZWZmZWN0IG9mIHRoaXMgaXMgdGhhdCB0aGUgYGZyZXF1ZW5jeSByZXNvbHV0aW9uYCBpbmNyZWFzZXMuIFRoZSBmcmVxdWVuY3kgcmVzb2x1dGlvbiBpcyBvYnRhaW5lZCBieSB0aGUgcmF0aW8gYEZzL05gLiAgU28sIGZvciBhIGdpdmVuIHNhbXBsaW5nIGZyZXF1ZW5jeSAoRnMpLCB0aGUgbW9yZSBzYW1wbGVzIChOKSBpbiB0aGUgc2lnbmFsLCB0aGUgc21hbGxlciB0aGUgZnJlcXVlbmN5IGluY3JlbWVudCBiZXR3ZWVuIHN1Y2Nlc3NpdmUgREZUIGRhdGEgcG9pbnRzLiBTaW5jZSBtb3JlIHBvaW50cyBhcmUgYmVpbmcgc2FtcGxlZCwgdGhlIHNwZWN0cmFsIHJlc29sdXRpb24gaW5jcmVhc2VzLg0KDQoNCiMjIyAqKkluIHdoYXQgZnJlcXVlbmN5IHJhbmdlIGlzIG1vc3Qgb2YgdGhlIGVuZXJneSBvZiB0aGUgc2lnbmFsPyoqDQoNClRvIGFuc3dlciB0aGlzIHF1ZXN0aW9ucywgd2Ugd2lsbCBoYXZlIHRvIGNvbWUgdXAgd2l0aCBhIG1hZ25pdHVkZSBzcGVjdHJ1bSBvZiB0aGUgc3BlZWNoIHdhdmVmb3JtIHdoaWNoIHNob3dzIHRoZSBtYWduaXR1ZGUgb2YgdGhlIHNpZ25hbCBhdCBhIHBhcnRpY3VsYXIgZnJlcXVlbmN5LiBUaGlzIG1lYW5zIHdlIHdpbGwgaGF2ZSB0byBjb252ZXJ0IHRoZSBzaWduYWwgZnJvbSB0aGUgdGltZSBkb21haW4gdG8gYSByZXByZXNlbnRhdGlvbiBpbiB0aGUgYGZyZXF1ZW5jeSBkb21haW5gIGJ5IGNvbXB1dGluZyB0aGUgYERpc2NyZXRlIEZvdXJpZXIgVHJhbnNmb3JtYCAodXNpbmcgYSBGRlQgYWxnb3JpdGhtKS4gT25jZSB3ZSBoYXZlIHRoZSBhbXBsaXR1ZGUgb2YgdGhlIERGVCBhbmQgdGhlIGZyZXF1ZW5jeSByYW5nZSwgd2UnbGwgYmUgcmVhZHkgdG8gZ28hDQoNCg0KRHVlIHRvIG1hdGhlbWF0aWNhbCBjb252ZW5pZW5jZSwgdGhlIGFycmFuZ2VtZW50IG9mIHRoZSBGRlQgb3V0cHV0IGhhcw0KcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGZyZXF1ZW5jaWVzIHNpZGUgYnkgc2lkZSBpbiBhIHNsaWdodGx5IG5vbi1pbnR1aXRpdmUgb3JkZXIgKHRoZSBmcmVxdWVuY3kgaW5jcmVhc2VzIGZyb20gMCB1bnRpbCBOLzIgYW5kIHRoZW4gaXQgZGVjcmVhc2VzIGZvciBuZWdhdGl2ZSBmcmVxdWVuY2llcykuIEFzIHRoZSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgZnJlcXVlbmN5IG1hZ25pdHVkZXMgYXJlIGlkZW50aWNhbCBmb3IgYSByZWFsIHNpZ25hbCBpdCBpcyBjb21tb24ganVzdCB0byBwbG90IHRoZSBwb3NpdGl2ZSBmcmVxdWVuY2llcyBmcm9tIGAwIHRvIEZzLzJgIChpbiBvdXIgY2FzZSBgMCB0byA0MDAwIEh6YCkuDQoNCg0KDQpgYGB7cn0NCiMgRGlzY3JldGUgRm91cmllciB0cmFuc2Zvcm0gb2YgZGF0YSAoREZUKQ0KZGZ0X3NwZWVjaCA8LSBmZnR3OjpGRlQoeSkNCg0KIyBhbXBsaXR1ZGUgb2YgREZUDQpkZnRfYW1wIDwtIGFicyhkZnRfc3BlZWNoKSAjIERGVCBpcyBhIHNlcXVlbmNlIG9mIGNvbXBsZXggc2ludXNvaWRzIA0KDQojIG51bWJlciBvZiBzYW1wbGVzDQpuIDwtIGxlbmd0aCh5KQ0KDQojIGZyZXF1ZW5jeSByYW5nZQ0KZiA8LSAoc2VxX2xlbihuKS0xKSpGcy9uDQoNCmZyZXFfcGxvdCA8LSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZlsxOkZzXSwgeSA9IGRmdF9hbXBbMTpGc10pKSsNCiAgZ2VvbV9saW5lKGNvbG9yID0gJ2JsdWUnKSsNCiAgbGFicyh4ID0gIkZyZXF1ZW5jeSAoSHopIiwgeSA9ICJNYWduaXR1ZGUiLA0KdGl0bGUgPSAiRnJlcXVlbmN5IGRvbWFpbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgZW50aXJlIHNwZWVjaCBzZXF1ZW5jZSIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KZ2dwbG90bHkoZnJlcV9wbG90KQ0KYGBgDQoNCg0KRnJvbSB0aGUgbWFnbml0dWRlIHNwZWN0cnVtIG9mIHRoZSBEaXNjcmV0ZSBGb3VyaWVyIFRyYW5zZm9ybSwgd2UgY2FuIG9ic2VydmUgdGhhdCBtb3N0IG9mIHRoZSBlbmVyZ3kgaXMgY29uY2VudHJhdGVkIGluIHRoZSBmcmVxdWVuY3kgcmFuZ2UgKiowKiogdG8gKioxIEtIeioqLg0KDQoNCiMjIyAqKlRpbWUgZG9tYWluLCBGcmVxdWVuY3kgZG9tYWluIGFuZCBFbmVyZ3kgY29tcGFyaXNvbiBvZiBhIHNlZ21lbnQgY29udGFpbmluZyBzcGVlY2ggYW5kIG9uZSB3aXRob3V0KioNCg0KVGhlIHRpbWUgZG9tYWluIHJlcHJlc2VudGF0aW9uIG9mIGEgc3BlZWNoIHNpZ25hbCBzaG93cyBob3cgdGhlIHNvdW5kIGNoYW5nZXMgaW4gaW50ZW5zaXR5IG92ZXIgdGltZQ0KDQoNCkVhcmxpZXIgb24gaW4gdGhpcyBhbmFseXNpcywgd2UgZGVmaW5lZCBzb21lIHNhbXBsZSBudW1iZXJzIGluIHJlZ2lvbnMgY29udGFpbmluZyBzcGVlY2ggYW5kIHRob3NlIHdpdGhvdXQuIEZpcnN0LCB3ZSdsbCBvYnRhaW4gYSBzZWdtZW50IGAzMm1zYCBsb25nIGZyb20gYSByZWdpb24gd2l0aCBzcGVlY2ggYW5kIGFub3RoZXIgc2VnbWVudCBvZiB0aGUgc2FtZSBkdXJhdGlvbiBmcm9tIGEgc2VnbWVudCB3aXRob3V0IHNwZWVjaCBhbmQgcGxvdCB0aGVzZSB0aW1lIGRvbWFpbiBzaWduYWxzIHNpZGUNCmJ5IHNpZGUgd2l0aCB0aW1lIGluIHNlY29uZHMgYXMgdGhlIHgtYXhpcy4gQSBgMzJtc2AgbG9uZyBzZWdtZW50IGNvcnJlc3BvbmRzIHRvIGAyNTYgc2FtcGxlc2A6DQoNCiROdW1iZXJcLG9mXCxzYW1wbGVzXCwgPSBcLHNlZ21lbnRcLGxlbmd0aChzZWMpIFx0aW1lcyBzYW1wbGluZ1wsZnJlcXVlbmN5IFwsJA0KDQokPSAwLjAzMiBcdGltZXMgODAwMCA9IDI1NiQNCg0KYGBge3J9DQojIHNhbXBsZSBsZW5ndGgNCnNhbXBsZV9sZW4gPSAzMmUtMypGcw0KDQojIG9idGFpbmluZyBhIDMybXMgbG9uZyBzZWdtZW50LzI1NiBzYW1wbGVzIHdpdGhvdXQgc3BlZWNoDQptb2phX3NsIDwtIHlbc2lsZW5jZV9zdGFydCA6IChzaWxlbmNlX3N0YXJ0ICsgc2FtcGxlX2xlbiAtIDEpXQ0KDQojIG9idGFpbmluZyBhIDMybXMgbG9uZyBzZWdtZW50LzI1NiBzYW1wbGVzIHdpdGggc3BlZWNoDQptb2phX3NwIDwtIHlbc3BlZWNoX3N0YXJ0IDogKHNwZWVjaF9zdGFydCArIHNhbXBsZV9sZW4gLSAxKV0NCg0KIyBjb3JyZXNwb25kaW5nIHRpbWUtbGVuZ3RoIG9mIHRoZSBzYW1wbGUgKDMybXMgbG9uZykNCnQgPC0gKHNlcV9sZW4oc2FtcGxlX2xlbikgLSAxKSAqIDEvRnMNCg0KIyBwbG90dGluZyB0aGUgd2F2ZWZvcm0gb2YgdGhlIHNpbGVudCBzZWdtZW50DQp3dmZtX3NsIDwtIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSB0LCB5ID0gbW9qYV9zbCkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gJ2JsdWUnLCBsd2QgPSAxLjEpKw0KICBsYWJzKHggPSAiVGltZSAocykiLCB5ID0gIkFtcGxpdHVkZSIsDQogICAgICAgdGl0bGUgPSAiVGltZSBkb21haW4gb2Ygc2lsZW50IHNlZ21lbnQiKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgZ2dwbG90bHkod3ZmbV9zbCkNCg0KIyBwbG90dGluZyB0aGUgd2F2ZWZvcm0gb2YgdGhlIHNlZ21lbnQgd2l0aCBzcGVlY2gNCnd2Zm1fc3AgPC0gZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHQsIHkgPSBtb2phX3NwKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAnYmx1ZScsIGx3ZCA9IDEuMSkrDQogIGxhYnMoeCA9ICJUaW1lIChzKSIsIHkgPSAiQW1wbGl0dWRlIiwNCiAgICAgICB0aXRsZSA9ICJUaW1lIGRvbWFpbiBvZiBzcGVlY2ggc2VnbWVudCIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBnZ3Bsb3RseSh3dmZtX3NwKQ0KDQpjb3dwbG90OjpwbG90X2dyaWQod3ZmbV9zbCwgd3ZmbV9zcCkNCg0KYGBgDQoNCkZyb20gdGhlIGFib3ZlIHBsb3RzIGFib3ZlLCB0aGUgdGltZSBkb21haW4gcGxvdCBvZiB0aGUgc2VnbWVudCBjb250YWluaW5nIHNwZWVjaCBoYXMgaGlnaGVyIGludGVuc2l0eSAoYW1wbGl0dWRlIHJhbmdpbmcgZnJvbSAtMC4yNyB0byAwLjI0KSB0aGFuIHRoYXQgd2l0aG91dCBzcGVlY2gvZmFpbnQgc3BlZWNoIGR1ZSB0byBub2lzZSAoYW1wbGl0dWRlIHJhbmdpbmcgZnJvbSAtMC4wMiB0byAwLjA2KS4gDQoNClRoZSB0aW1lIGRvbWFpbiBwbG90IG9mIHRoZSBzZWdtZW50IGNvbnRhaW5pbmcgc3BlZWNoIGlzIGFsc28gbW9yZSBlcnJhdGljIGR1ZSB0byBtb3JlIGZsdWN0dWF0aW9ucyBpbiBhbXBsaXR1ZGUgb2Ygc291bmQuDQoNCk9uZSBjb21tb24gbWV0cmljIHRoYXQgY2FuIGJlIGVhc2lseSBjb21wdXRlZCBmcm9tIHRoZSBzYW1wbGUgdmFsdWVzIGlzIGEgYHNpZ25hbCdzIGVuZXJneWAuDQoNClRoZSBlbmVyZ3kgb2YgYSBkaXNjcmV0ZSB0aW1lIHNpZ25hbCBpcyBkZWZpbmVkIGFzIHRoZSBzdW0gb2YgdGhlIHNxdWFyZSBvZiB0aGUgc2FtcGxlZCBzaWduYWwgdmFsdWVzOg0KJEUoeSkgPVxzdW1fe249LVxpbmZ0eX1ee1xpbmZ0eX0geV4yJA0KDQpgYGB7cn0NCiMgRW5lcmd5IG9mIHNpbGVudCBzZWdtZW50DQpFX3NsIDwtIHN1bShtb2phX3NsXjIpDQpFX3NsDQoNCiMgRW5lcmd5IG9mIHNlZ21lbnQgd2l0aCBzcGVlY2gNCkVfc3AgPC0gc3VtKG1vamFfc3BeMikNCkVfc3ANCmBgYA0KDQpUaGUgc2lsZW50IHNlZ21lbnQgaGFzIGFuIGVuZXJneSBvZiAqKjAuMTczMyoqIHdoZXJlYXMgdGhlIHNlZ21lbnQgY29udGFpbmluZyBzcGVlY2ggaGFzIGFuIGVuZXJneSBvZiAqKjMuNDQwOSoqLiBEdWUgdG8gaGlnaGVyIGludGVuc2l0aWVzIG9mIHNvdW5kIGluIHRoZSByZWdpb24gY29udGFpbmluZyBzcGVlY2gsIHRoZSBzYW1wbGUgdmFsdWVzIGluIHRoaXMgcmVnaW9uIGhhdmUgaGlnaGVyIGFtcGxpdHVkZSB2YWx1ZXMgd2hpY2ggaW4gdHVybiBhY2NvdW50IGZvciB0aGUgaGlnaGVyIGVuZXJneS4gVGhlIGVuZXJneSBpbiB0aGUgc2lsZW50IHNlZ21lbnQgY291bGQgYmUgYXR0cmlidXRlZCB0byBub2lzZSBhbmQgY2FuIGJlIGZpbHRlcmVkIG91dCB1c2luZyBhbiBhcHByb3ByaWF0ZSBmaWx0ZXIuDQoNCjxicj4NCg0KVGhlIHRpbWUgZG9tYWluIGFuYWx5c2lzIGNhbiBhbHNvIGJlIGRvbmUgaW4gdGhlIGZyZXF1ZW5jeSBkb21haW4gYnkgY29tcHV0aW5nIHRoZSBgRGlzY3JldGUgRm91cmllciBUcmFuc2Zvcm1gICh1c2luZyBhIEZGVCBhbGdvcml0aG0pIG9mIHRoZSB0d28gc2VnbWVudHMuDQoNCmBgYHtyfQ0KIyBERlQgb2Ygc2lsZW50IHNlZ21lbnQNCmRmdF9zbCA8LSBmZnR3OjpGRlQobW9qYV9zbCkNCg0KIyBERlQgb2Ygc3BlZWNoIHNlZ21lbnQNCmRmdF9zcCA8LSBmZnR3OjpGRlQobW9qYV9zcCkNCg0KIyBmcmVxdWVuY3kgcmFuZ2UNCmZzIDwtIChzZXFfbGVuKHNhbXBsZV9sZW4pLTEpKkZzL3NhbXBsZV9sZW4NCg0KIyBwbG90dGluZyB0aGUgd2F2ZWZvcm0gb2YgdGhlIHNpbGVudCBzZWdtZW50IGluIGZyZXEgZG9tYWluDQpmcmVxZF9zbCA8LSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZnNbMSA6IChzYW1wbGVfbGVuKS8yXSwNCiAgeSA9IGFicyhkZnRfc2xbMTogKHNhbXBsZV9sZW4pLzJdKSkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gJ2JsdWUnLCBsd2QgPSAxLjEpKw0KICBsYWJzKHggPSAiRnJlcXVlbmN5IChIeikiLCB5ID0gIkFtcGxpdHVkZSIsDQogICAgICAgdGl0bGUgPSAiRnJlcXVlbmN5IGRvbWFpbiBvZiBzaWxlbnQgc2VnbWVudCIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBnZ3Bsb3RseShmcmVxZF9zbCkNCg0KIyBwbG90dGluZyB0aGUgd2F2ZWZvcm0gb2YgdGhlIHNwZWVjaCBzZWdtZW50IGluIGZyZXEgZG9tYWluDQpmcmVxZF9zcCA8LSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZnNbMSA6IChzYW1wbGVfbGVuKS8yXSwgDQogIHkgPSBhYnMoZGZ0X3NwWzE6IChzYW1wbGVfbGVuKS8yXSkpKSArDQogIGdlb21fbGluZShjb2xvciA9ICdibHVlJywgbHdkID0gMS4xKSsNCiAgbGFicyh4ID0gIkZyZXF1ZW5jeSAoSHopIiwgeSA9ICJBbXBsaXR1ZGUiLA0KICAgICAgIHRpdGxlID0gIkZyZXF1ZW5jeSBkb21haW4gb2Ygc3BlZWNoIHNlZ21lbnQiKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgZ2dwbG90bHkoZnJlcWRfc3ApDQoNCmNvd3Bsb3Q6OnBsb3RfZ3JpZChmcmVxZF9zbCwgZnJlcWRfc3ApDQoNCmBgYA0KDQpXaGVuIHRoZSB0d28gc2VnbWVudHMgYXJlIGNvbnZlcnRlZCBmcm9tIHRoZSB0aW1lIGRvbWFpbiB0byBhIHJlcHJlc2VudGF0aW9uIGluIHRoZSBmcmVxdWVuY3kgZG9tYWluIHRoZSBzYW1lIGluZmVyZW5jZXMgbWFkZSBlYXJsaWVyIHN0aWxsIGhvbGQuIFRoZSBzZWdtZW50IGNvbnRhaW5pbmcgc3BlZWNoIGhhcyBoaWdoZXIgbWFnbml0dWRlIHRoYW4gdGhhdCB3aXRob3V0IHNwZWVjaCBhdCB2YXJpb3VzIGZyZXF1ZW5jaWVzLg0KDQpgU2lnbmFsIHBvd2VyYCBhcyBhIGZ1bmN0aW9uIG9mIGZyZXF1ZW5jeSBpcyBhbm90aGVyIGNvbW1vbiBtZXRyaWMgdXNlZCBpbiBzaWduYWwgcHJvY2Vzc2luZy4gUG93ZXIgaXMgdGhlIHNxdWFyZWQgbWFnbml0dWRlIG9mIGEgc2lnbmFsJ3MgRm91cmllciB0cmFuc2Zvcm0sIG5vcm1hbGl6ZWQgYnkgdGhlIG51bWJlciBvZiBmcmVxdWVuY3kgc2FtcGxlczoNCg0KJFBfe2RmdH1cLCA9XCwgW1xtaWQgeyhkZnQpfVxtaWReMl0vTiQNCg0KTGV0J3MgcGxvdCBhIHBvd2VyIHNwZWN0cnVtIG9mIHRoZSBzZWdtZW50IGNvbnRhaW5pbmcgc3BlZWNoLCBzaGFsbCB3ZT8NCg0KYGBge3J9DQojIHBvd2VyIG9mIHRoZSBkZnQNCnBvd2VyIDwtICAoKGFicyhkZnRfc3ApKV4yKS9zYW1wbGVfbGVuDQoNCiMgZnJlcXVlbmN5IHJhbmdlDQpmcCA8LSAoc2VxX2xlbihzYW1wbGVfbGVuKS0xKSpGcy9zYW1wbGVfbGVuDQoNCiMgcG93ZXIgc3BlY3RydW0NCnBvd2VyX3NwIDwtIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBmcFsxIDogKHNhbXBsZV9sZW4pLzJdLA0KICB5ID0gcG93ZXJbMTogKHNhbXBsZV9sZW4pLzJdKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAnYmx1ZScsIGx3ZCA9IDEuMSkrDQogIGxhYnMoeCA9ICJGcmVxdWVuY3kgKEh6KSIsIHkgPSAiUG93ZXIiLA0KICAgICAgIHRpdGxlID0gIlBvd2VyIFNwZWN0cnVtIG9mIHNwZWVjaCBzZWdtZW50IikrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpnZ3Bsb3RseShwb3dlcl9zcCkNCmBgYA0KDQoNClRoZSBwbG90IGluZGljYXRlcyB0aGF0IHRoZSBzcGVlY2ggc2VnbWVudCBjb25zaXN0cyBvZiBhIGZ1bmRhbWVudGFsIGZyZXF1ZW5jeSBhcm91bmQgMTU2LjI1IEh6IGFuZCBhIHNlcXVlbmNlIG9mIGhhcm1vbmljcywgd2hlcmUgdGhlIHRoaXJkIGhhcm1vbmljIGlzIGVtcGhhc2l6ZWQuDQoNCg0KVGltZSB0byBpdCBhIHdyYXAhIEl0J3MgYmVlbiBxdWl0ZSB0aGUgYWR2ZW50dXJlLCByaWdodCDwn5iKPyANCkFueWhvdywgd2UgZ290IHRvIGFuYWx5emUgYSBzcGVlY2ggc2lnbmFsIGFuZCBhdCB0aGUgc2FtZSB0aW1lIGNvdmVyIHNvbWUgZnVuZGFtZW50YWwgY29uY2VwdHMgaW4gRGlnaXRhbCBTaWduYWwgUHJvY2Vzc2luZy4NCg0KVGhpcyBpcyBieSBubyBtZWFucyBhIHRob3JvdWdoIHdhbGstdGhyb3VnaCBidXQgSSBob3BlIGl0IHdpbGwgaWduaXRlIHNvbWUgYFItRFNQIHNwYXJrYCBvciBzb21ldGhpbmcuDQoNCkhvcGluZyB0byBjb3ZlciBzb21lIG1vcmUgZXhjaXRpbmcgY29uY2VwdHMgLi4uIHNvb24hDQoNCkJlIHN1cmUgdG8gY2hlY2sgb3V0IGdyZWF0IGJsb2dzLCB0dXRvcmlhbHMgYW5kIG90aGVyIGZvcm1hdHMgb2YgUiByZXNvdXJjZXMgY29taW5nIG91dCBldmVyeSBkYXkgYXQgW1JXZWVrbHkub3JnXShodHRwczovL3J3ZWVrbHkub3JnLykhDQoNCg0KVGlsbCB0aGVuLCANCg0KSGFwcHkgTGVhcm5pbmcg8J+RqfCfj73igI3wn5K7IPCfkajigI3wn5K7IPCfkajwn4++4oCN8J+SuyDwn5Gp4oCN8J+SuyAsDQoNCltFcmljIChSX2ljKV0oaHR0cHM6Ly90d2l0dGVyLmNvbS9lcmljbnRheSksIE1pY3Jvc29mdCBMZWFybiBTdHVkZW50IEFtYmFzc2Fkb3IuDQoNCg0KIyAqKlJlZmVyZW5jZSBNYXRlcmlhbCoqDQoNCiogQW5kcmV3IE8uIEZpbmxleSwgSmVmZnJleSBXLiBEb3NlciwgVmluY2UgTWVsZmksICJEaWdpdGFsIFNpZ25hbCBQcm9jZXNzaW5nIiwgaW4gKlIgUHJvZ3JhbW1pbmcgZm9yIERhdGEgU2NpZW5jZXMqLiAyMDIwDQoNCiogSS4gVi4gTWNMb3VnaGxpbiwgKlNwZWVjaCBhbmQgQXVkaW8gUHJvY2Vzc2luZzogQSBNQVRMQULCri1iYXNlZCBhcHByb2FjaCouIDIwMTYuDQoNCiogTUFUTEFCIERvY3VtZW50YXRpb24sICpbQmFzaWMgU3BlY3RyYWwgQW5hbHlzaXNdKGh0dHBzOi8vd3d3Lm1hdGh3b3Jrcy5jb20vaGVscC9tYXRsYWIvbWF0aC9iYXNpYy1zcGVjdHJhbC1hbmFseXNpcy5odG1sKSoNCg0KKiBBLiBWIE9wcGVuaGVpbSBhbmQgUi4gVy4gU2NoYWZlciwg4oCcRGlzY3JldGUgVGltZSBTaWduYWwgUHJvY2Vzc2luZyAybmQNCkVkaXRpb24s4oCdIEJvb2suIDE5OTguDQoNCiogRXJpYyBXYW5qYXUsIFNhbXVlbCBLYXJpdWtpLCBJYW4gTXVjaGlyaSwgSm9zaHVhIE5kZW1lbmdlLCAqW0FuYWx5c2lzIG9mIFNwZWVjaCBpbiB0aGUgVGltZSBhbmQgRnJlcXVlbmN5IERvbWFpbiB1c2luZyBNQVRMQUJdKGh0dHBzOi8vZ2l0aHViLmNvbS9SLWljbnRheS9EU1BfTUFUTEFCKSouDQoNCg==