Analog vs Digital Transmission

In this notebook we will explore the potential advantages of digital transmission over analog transmission. We will consider the case of transmission over a long (e.g. transoceanic) cable in which several repeaters are used to compensate for the attenuation introduced by the transmission.

Remember that if each cable segment introduces an attenuation of \(1/G\), we can recover the original amplitude by boosting the signal with a repeater with gain \(G\). However, if the signal has accumulated additive noise, the noise will be amplified as well so that, after \(N\) repeaters, the noise will have been amplified \(N\) times:

\[ \hat{x}_N(t) = x(t) + NG\sigma(t) \]

If we use a digital signal, on the other hand, we can threshold the signal after each repeater and virtually eliminate the noise at each stage, so that even after several repeaters the trasmission is still noise-free.

Let’s start with the standard initial bookkeeping…

# https://www.coursera.org/learn/dsp1/ungradedLab/h92pa/transoceanic-signal-transmission/lab?path=%2Fnotebooks%2FTransoceanic%2FSignalTransmission.ipynb


# Load required libraries
library(tidyverse) # For data manipulation and plotting
library(tuneR) # For audio file handling
library(seewave) # For additional audio processing
# Set default plot size (in R Markdown or similar environments)
options(repr.plot.width = 14, repr.plot.height = 4)

Now we can read in an audio file from disk; we can plot it and play it back. The readWave() function returns the audio data and the playback rate, which we will need to pass to the playback functions.

# Read the audio file
speech_wav <- readWave("speech.wav")
rate <- speech_wav@samp.rate
s <- speech_wav@left # Assuming mono audio

# Plot the audio signal using ggplot2
audio_df <- tibble(sample = 1:length(s), amplitude = s)
ggplot(audio_df, aes(x = sample, y = amplitude)) +
  geom_line() +
  theme_minimal() +
  labs(x = "Sample", y = "Amplitude", title = "Audio Signal")


# For audio playback in R
# listen(speech_wav)

The “Analog” and “Digital” Signals

We will now create two version of the audio signal, an “analog” version and a “digital” version. Obviously the analog version is just a simulation, since we’re using a digital computer; we will assume that, by using floating point values, we’re in fact close enough to infinite precision. In the digital version of the signal, on the other hand, the audio samples will only take integer values between -100 and +100 (i.e. we will use approximately 8 bits per audio sample).

# The analog signal is rescaled between -100 and +100
# Find largest element in magnitude
norm <- 1.0 / max(abs(c(min(s), max(s))))
sA <- 100.0 * s * norm

# The digital version is rounded to integers
sD <- round(sA)

Rememeber that there is no free lunch and quantization implies a loss of quality; this initial loss (that we can minimize by using more bits per sample) is the price to pay for digital transmission. We can plot the error and compute the Signal to Noise Ratio (SNR) of the quantized signal

# Plot the error between analog and digital signals
error_df <- tibble(sample = 1:length(sA), error = sA - sD)
ggplot(error_df, aes(x = sample, y = error)) +
  geom_line() +
  theme_minimal() +
  labs(x = "Sample", y = "Error", title = "Quantization Error")

as expected, the error is between -0.5 and +0.5, since in the “analog” signal the values are real-valued, whereas in the “digital” version they can only take integer values. As for the SNR,

# Define SNR function
SNR <- function(noisy, original) {
  # Power of the error
  err <- sqrt(sum((original - noisy)^2))
  # Power of the signal
  sig <- sqrt(sum(original^2))
  # SNR in dBs
  return(10 * log10(sig / err))
}

# Calculate initial SNR
cat(sprintf("SNR = %f dB", SNR(sD, sA)))
SNR = 17.124344 dB

Can we hear the 17dB difference? A bit…

# Create and play analog version
play_analog <- function() {
  analog_wav <- speech_wav
  analog_wav@left <- sA # Keep as floating point values
  listen(analog_wav)
}

# Create and play digital version
play_digital <- function() {
  digital_wav <- speech_wav
  digital_wav@left <- sD # Already integer-like from rounding
  listen(digital_wav)
}

# Uncomment to play
# play_analog()
# play_digital()

How to produce a quieter version of the audio clip

# First normalize to full range (-1 to 1)
normalized <- s / max(abs(s))

# Then create two versions with different amplitudes
full_volume <- normalized
half_volume <- normalized * 0.5

# Create combined audio
combined <- speech_wav
combined@left <- c(full_volume * 32767, rep(0, 8000), half_volume * 32767)

audio_df <- tibble(sample = 1:length(combined@left), amplitude = combined@left)
ggplot(audio_df, aes(x = sample, y = amplitude)) +
  geom_line() +
  theme_minimal() +
  labs(x = "Sample", y = "Amplitude", title = "Audio Signal")


listen(combined)

Transmission

Let’s now define a function that represents the net effect of transmitting audio over a cable segment terminated by a repeater: * the signal is attenuated * the signal is accumulates additive noise as it propagates through the cable * the signal is amplified to the original amplitude by the repeater

# Define repeater function
repeater <- function(x, noise_amplitude, attenuation) {
  # Create uniform noise
  noise <- runif(length(x), min = -noise_amplitude, max = noise_amplitude)
  # Attenuation
  x <- x * attenuation
  # Add noise
  x <- x + noise
  # Gain compensation
  return(x / attenuation)
}

we can use the repeater for both analog and digital signals. Transmission of the analog signal is simply a sequence of repeaters:

# Define analog transmission function
analog_tx <- function(x, num_repeaters, noise_amplitude, attenuation) {
  for (n in 1:num_repeaters) {
    x <- repeater(x, noise_amplitude, attenuation)
  }
  return(x)
}

For digital signals, however, we can rectify the signal after each repeater, because we know that values should only be integer-valued:

# Define digital transmission function
digital_tx <- function(x, num_repeaters, noise_amplitude, attenuation) {
  for (n in 1:num_repeaters) {
    x <- round(repeater(x, noise_amplitude, attenuation))
  }
  return(x)
}

Let’s compare transmission schemes. As you can see, the SNR after digital transmission has not changed! Now the difference between audio clips should be easy to hear:

# Set parameters
NUM_REPEATERS <- 70
NOISE_AMPLITUDE <- 0.2
ATTENUATION <- 0.5

# Run analog transmission
yA <- analog_tx(sA, NUM_REPEATERS, NOISE_AMPLITUDE, ATTENUATION)
cat(sprintf("Analog transmission: SNR = %f dB\n", SNR(yA, sA)))
Analog transmission: SNR = 8.741513 dB
# Run digital transmission
yD <- digital_tx(sD, NUM_REPEATERS, NOISE_AMPLITUDE, ATTENUATION)
cat(sprintf("Digital transmission: SNR = %f dB\n", SNR(yD, sA)))
Digital transmission: SNR = 17.124344 dB
combined <- speech_wav
combined@left <- c(yA, rep(0, 8000), yD)

audio_df <- tibble(sample = 1:length(combined@left), amplitude = combined@left)
ggplot(audio_df, aes(x = sample, y = amplitude)) +
  geom_line() +
  theme_minimal() +
  labs(x = "Sample", y = "Amplitude", title = "Audio Signal")


listen(combined)

Note however that, if the noise amplitude exceeds a certain value, digital transmission degrades even less gracefully than analog transmission:

# Set higher noise amplitude
NOISE_AMPLITUDE <- 0.3

# Run analog transmission with higher noise
yA <- analog_tx(sA, NUM_REPEATERS, NOISE_AMPLITUDE, ATTENUATION)
cat(sprintf("Analog transmission: SNR = %f dB\n", SNR(yA, sA)))
Analog transmission: SNR = 6.977521 dB
# Run digital transmission with higher noise
yD <- digital_tx(sD, NUM_REPEATERS, NOISE_AMPLITUDE, ATTENUATION)
cat(sprintf("Digital transmission: SNR = %f dB\n", SNR(yD, sA)))
Digital transmission: SNR = 6.215226 dB

Playing Transmitted Signals

combined2 <- speech_wav
combined2@left <- c(yA, rep(0, 8000), yD)

audio_df <- tibble(sample = 1:length(combined2@left), amplitude = combined2@left)
ggplot(audio_df, aes(x = sample, y = amplitude)) +
  geom_line() +
  theme_minimal() +
  labs(x = "Sample", y = "Amplitude", title = "Audio Signal")

listen(combined2)

Understanding Digital Signal Degradation with Noise

The significant degradation in digital transmission when increasing NOISE_AMPLITUDE from 0.2 to 0.3 is a great example of what’s called the “cliff effect” in digital communications. Let me explain why this happens:

Why Digital Signals Degrade Suddenly

In the digital transmission system, we’re relying on the ability to correctly recover the original integer values by rounding after each repeater. This works well as long as the noise doesn’t push the signal across a decision threshold. Let’s analyze the critical threshold mathematically:

  1. Each digital value has decision boundaries at ±0.5 from the integer value (e.g., for the value 42, correct recovery happens as long as the noisy signal is between 41.5 and 42.5)

  2. In each repeater:

    • Signal is attenuated: x * ATTENUATION (x * 0.5)
    • Noise is added: [-NOISE_AMPLITUDE, +NOISE_AMPLITUDE]
    • Signal is amplified: (x * ATTENUATION + noise) / ATTENUATION
    • After simplifying: x + noise/ATTENUATION
  3. The effective noise range after amplification becomes: NOISE_AMPLITUDE * (1/ATTENUATION) = NOISE_AMPLITUDE * 2

Calculating the Critical Threshold

For reliable digital transmission, we need: NOISE_AMPLITUDE * (1/ATTENUATION) < 0.5

With ATTENUATION = 0.5: NOISE_AMPLITUDE * 2 < 0.5 NOISE_AMPLITUDE < 0.25

This explains why:

  • At NOISE_AMPLITUDE = 0.2: The effective noise (0.4) is still below the critical threshold of 0.5
  • At NOISE_AMPLITUDE = 0.3: The effective noise (0.6) exceeds the threshold, causing incorrect rounding decisions

Estimating the Upper Limit

Implement this in R to visualize the effect:

# Function to estimate threshold and error probability
estimate_digital_reliability <- function(attenuation_value = 0.5, 
                                        noise_range = seq(0.1, 0.5, by = 0.01), 
                                        num_repeaters = 70,
                                        samples = 10000) {
  
  results <- tibble(
    noise_amplitude = noise_range,
    effective_noise = noise_range * (1/attenuation_value),
    theoretical_threshold = 0.5,
    error_probability = pmap_dbl(list(noise_range), function(noise_val) {
      # Simulate a single repeater stage with different noise levels
      # Start with perfect integer values
      original <- sample(-100:100, samples, replace = TRUE)
      # Pass through one repeater
      noisy <- original + runif(samples, -noise_val, noise_val) * (1/attenuation_value)
      # Check error rate after rounding
      mean(round(noisy) != original)
    }),
    cumulative_error_prob = 1 - (1 - error_probability)^num_repeaters
  )
  
  return(results)
}

# Calculate and plot
threshold_data <- estimate_digital_reliability()

# Plot the results
ggplot(threshold_data, aes(x = noise_amplitude)) +
  geom_line(aes(y = error_probability, color = "Single Repeater Error Prob.")) +
  geom_line(aes(y = cumulative_error_prob, color = "System Error Prob.")) +
  geom_vline(xintercept = 0.25, linetype = "dashed", color = "red") +
  annotate("text", x = 0.26, y = 0.5, label = "Critical\nThreshold", hjust = 0) +
  scale_color_manual(values = c("blue", "darkred")) +
  labs(
    title = "Digital Transmission Reliability vs. Noise Amplitude",
    x = "Noise Amplitude",
    y = "Probability of Error",
    color = "Measure"
  ) +
  theme_minimal()

Why Digital Degrades Worse Than Analog Above Threshold

When noise exceeds the critical threshold in digital transmission:

  1. Each error causes a complete misclassification of the signal value
  2. These errors accumulate and cascade through multiple repeaters
  3. Once errors start occurring, the subsequent repeaters amplify these errors

In contrast, analog signals degrade gracefully - the noise accumulates linearly across repeaters but there are no sudden “jumps” between discrete values. This demonstrates the classic tradeoff in digital communications: excellent performance up to a critical threshold, followed by rapid degradation beyond that point.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEFuYWxvZyB2cyBEaWdpdGFsIFRyYW5zbWlzc2lvbg0KDQpJbiB0aGlzIG5vdGVib29rIHdlIHdpbGwgZXhwbG9yZSB0aGUgcG90ZW50aWFsIGFkdmFudGFnZXMgb2YgZGlnaXRhbCB0cmFuc21pc3Npb24gb3ZlciBhbmFsb2cgdHJhbnNtaXNzaW9uLiBXZSB3aWxsIGNvbnNpZGVyIHRoZSBjYXNlIG9mIHRyYW5zbWlzc2lvbiBvdmVyIGEgbG9uZyAoZS5nLiB0cmFuc29jZWFuaWMpIGNhYmxlIGluIHdoaWNoIHNldmVyYWwgcmVwZWF0ZXJzIGFyZSB1c2VkIHRvIGNvbXBlbnNhdGUgZm9yIHRoZSBhdHRlbnVhdGlvbiBpbnRyb2R1Y2VkIGJ5IHRoZSB0cmFuc21pc3Npb24uDQoNClJlbWVtYmVyIHRoYXQgaWYgZWFjaCBjYWJsZSBzZWdtZW50IGludHJvZHVjZXMgYW4gYXR0ZW51YXRpb24gb2YgJDEvRyQsIHdlIGNhbiByZWNvdmVyIHRoZSBvcmlnaW5hbCBhbXBsaXR1ZGUgYnkgYm9vc3RpbmcgdGhlIHNpZ25hbCB3aXRoIGEgcmVwZWF0ZXIgd2l0aCBnYWluICRHJC4gSG93ZXZlciwgaWYgdGhlIHNpZ25hbCBoYXMgYWNjdW11bGF0ZWQgYWRkaXRpdmUgbm9pc2UsIHRoZSBub2lzZSB3aWxsIGJlIGFtcGxpZmllZCBhcyB3ZWxsIHNvIHRoYXQsIGFmdGVyICROJCByZXBlYXRlcnMsIHRoZSBub2lzZSB3aWxsIGhhdmUgYmVlbiBhbXBsaWZpZWQgJE4kIHRpbWVzOg0KDQokJA0KICAgIFxoYXR7eH1fTih0KSAgPSB4KHQpICsgTkdcc2lnbWEodCkNCiQkDQoNCklmIHdlIHVzZSBhIGRpZ2l0YWwgc2lnbmFsLCBvbiB0aGUgb3RoZXIgaGFuZCwgd2UgY2FuIHRocmVzaG9sZCB0aGUgc2lnbmFsIGFmdGVyIGVhY2ggcmVwZWF0ZXIgYW5kIHZpcnR1YWxseSBlbGltaW5hdGUgdGhlIG5vaXNlIGF0IGVhY2ggc3RhZ2UsIHNvIHRoYXQgZXZlbiBhZnRlciBzZXZlcmFsIHJlcGVhdGVycyB0aGUgdHJhc21pc3Npb24gaXMgc3RpbGwgbm9pc2UtZnJlZS4NCg0KTGV0J3Mgc3RhcnQgd2l0aCB0aGUgc3RhbmRhcmQgaW5pdGlhbCBib29ra2VlcGluZy4uLg0KDQpgYGB7cn0NCiMgaHR0cHM6Ly93d3cuY291cnNlcmEub3JnL2xlYXJuL2RzcDEvdW5ncmFkZWRMYWIvaDkycGEvdHJhbnNvY2VhbmljLXNpZ25hbC10cmFuc21pc3Npb24vbGFiP3BhdGg9JTJGbm90ZWJvb2tzJTJGVHJhbnNvY2VhbmljJTJGU2lnbmFsVHJhbnNtaXNzaW9uLmlweW5iDQoNCg0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcw0KbGlicmFyeSh0aWR5dmVyc2UpICMgRm9yIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBwbG90dGluZw0KbGlicmFyeSh0dW5lUikgIyBGb3IgYXVkaW8gZmlsZSBoYW5kbGluZw0KbGlicmFyeShzZWV3YXZlKSAjIEZvciBhZGRpdGlvbmFsIGF1ZGlvIHByb2Nlc3NpbmcNCmBgYA0KDQpgYGB7cn0NCiMgU2V0IGRlZmF1bHQgcGxvdCBzaXplIChpbiBSIE1hcmtkb3duIG9yIHNpbWlsYXIgZW52aXJvbm1lbnRzKQ0Kb3B0aW9ucyhyZXByLnBsb3Qud2lkdGggPSAxNCwgcmVwci5wbG90LmhlaWdodCA9IDQpDQpgYGANCg0KTm93IHdlIGNhbiByZWFkIGluIGFuIGF1ZGlvIGZpbGUgZnJvbSBkaXNrOyB3ZSBjYW4gcGxvdCBpdCBhbmQgcGxheSBpdCBiYWNrLiBUaGUgYHJlYWRXYXZlKClgIGZ1bmN0aW9uIHJldHVybnMgdGhlIGF1ZGlvIGRhdGEgYW5kIHRoZSBwbGF5YmFjayByYXRlLCB3aGljaCB3ZSB3aWxsIG5lZWQgdG8gcGFzcyB0byB0aGUgcGxheWJhY2sgZnVuY3Rpb25zLg0KDQpgYGB7cn0NCiMgUmVhZCB0aGUgYXVkaW8gZmlsZQ0Kc3BlZWNoX3dhdiA8LSByZWFkV2F2ZSgic3BlZWNoLndhdiIpDQpyYXRlIDwtIHNwZWVjaF93YXZAc2FtcC5yYXRlDQpzIDwtIHNwZWVjaF93YXZAbGVmdCAjIEFzc3VtaW5nIG1vbm8gYXVkaW8NCg0KIyBQbG90IHRoZSBhdWRpbyBzaWduYWwgdXNpbmcgZ2dwbG90Mg0KYXVkaW9fZGYgPC0gdGliYmxlKHNhbXBsZSA9IDE6bGVuZ3RoKHMpLCBhbXBsaXR1ZGUgPSBzKQ0KZ2dwbG90KGF1ZGlvX2RmLCBhZXMoeCA9IHNhbXBsZSwgeSA9IGFtcGxpdHVkZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiU2FtcGxlIiwgeSA9ICJBbXBsaXR1ZGUiLCB0aXRsZSA9ICJBdWRpbyBTaWduYWwiKQ0KDQojIEZvciBhdWRpbyBwbGF5YmFjayBpbiBSDQojIGxpc3RlbihzcGVlY2hfd2F2KQ0KYGBgDQoNCiMjIFRoZSAiQW5hbG9nIiBhbmQgIkRpZ2l0YWwiIFNpZ25hbHMNCg0KV2Ugd2lsbCBub3cgY3JlYXRlIHR3byB2ZXJzaW9uIG9mIHRoZSBhdWRpbyBzaWduYWwsIGFuICJhbmFsb2ciIHZlcnNpb24gYW5kIGEgImRpZ2l0YWwiIHZlcnNpb24uIE9idmlvdXNseSB0aGUgYW5hbG9nIHZlcnNpb24gaXMganVzdCBhIHNpbXVsYXRpb24sIHNpbmNlIHdlJ3JlIHVzaW5nIGEgZGlnaXRhbCBjb21wdXRlcjsgd2Ugd2lsbCBhc3N1bWUgdGhhdCwgYnkgdXNpbmcgZmxvYXRpbmcgcG9pbnQgdmFsdWVzLCB3ZSdyZSBpbiBmYWN0IGNsb3NlIGVub3VnaCB0byBpbmZpbml0ZSBwcmVjaXNpb24uIEluIHRoZSBkaWdpdGFsIHZlcnNpb24gb2YgdGhlIHNpZ25hbCwgb24gdGhlIG90aGVyIGhhbmQsIHRoZSBhdWRpbyBzYW1wbGVzIHdpbGwgb25seSB0YWtlIGludGVnZXIgdmFsdWVzIGJldHdlZW4gLTEwMCBhbmQgKzEwMCAoaS5lLiB3ZSB3aWxsIHVzZSBhcHByb3hpbWF0ZWx5IDggYml0cyBwZXIgYXVkaW8gc2FtcGxlKS4NCg0KYGBge3J9DQojIFRoZSBhbmFsb2cgc2lnbmFsIGlzIHJlc2NhbGVkIGJldHdlZW4gLTEwMCBhbmQgKzEwMA0KIyBGaW5kIGxhcmdlc3QgZWxlbWVudCBpbiBtYWduaXR1ZGUNCm5vcm0gPC0gMS4wIC8gbWF4KGFicyhjKG1pbihzKSwgbWF4KHMpKSkpDQpzQSA8LSAxMDAuMCAqIHMgKiBub3JtDQoNCiMgVGhlIGRpZ2l0YWwgdmVyc2lvbiBpcyByb3VuZGVkIHRvIGludGVnZXJzDQpzRCA8LSByb3VuZChzQSkNCmBgYA0KDQpSZW1lbWViZXIgdGhhdCB0aGVyZSBpcyBubyBmcmVlIGx1bmNoIGFuZCBxdWFudGl6YXRpb24gaW1wbGllcyBhIGxvc3Mgb2YgcXVhbGl0eTsgdGhpcyBpbml0aWFsIGxvc3MgKHRoYXQgd2UgY2FuIG1pbmltaXplIGJ5IHVzaW5nIG1vcmUgYml0cyBwZXIgc2FtcGxlKSBpcyB0aGUgcHJpY2UgdG8gcGF5IGZvciBkaWdpdGFsIHRyYW5zbWlzc2lvbi4gV2UgY2FuIHBsb3QgdGhlIGVycm9yIGFuZCBjb21wdXRlIHRoZSBTaWduYWwgdG8gTm9pc2UgUmF0aW8gKFNOUikgb2YgdGhlIHF1YW50aXplZCBzaWduYWwNCg0KYGBge3J9DQojIFBsb3QgdGhlIGVycm9yIGJldHdlZW4gYW5hbG9nIGFuZCBkaWdpdGFsIHNpZ25hbHMNCmVycm9yX2RmIDwtIHRpYmJsZShzYW1wbGUgPSAxOmxlbmd0aChzQSksIGVycm9yID0gc0EgLSBzRCkNCmdncGxvdChlcnJvcl9kZiwgYWVzKHggPSBzYW1wbGUsIHkgPSBlcnJvcikpICsNCiAgZ2VvbV9saW5lKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiU2FtcGxlIiwgeSA9ICJFcnJvciIsIHRpdGxlID0gIlF1YW50aXphdGlvbiBFcnJvciIpDQpgYGANCg0KYXMgZXhwZWN0ZWQsIHRoZSBlcnJvciBpcyBiZXR3ZWVuIC0wLjUgYW5kICswLjUsIHNpbmNlIGluIHRoZSAiYW5hbG9nIiBzaWduYWwgdGhlIHZhbHVlcyBhcmUgcmVhbC12YWx1ZWQsIHdoZXJlYXMgaW4gdGhlICJkaWdpdGFsIiB2ZXJzaW9uIHRoZXkgY2FuIG9ubHkgdGFrZSBpbnRlZ2VyIHZhbHVlcy4gQXMgZm9yIHRoZSBTTlIsDQoNCmBgYHtyfQ0KIyBEZWZpbmUgU05SIGZ1bmN0aW9uDQpTTlIgPC0gZnVuY3Rpb24obm9pc3ksIG9yaWdpbmFsKSB7DQogICMgUG93ZXIgb2YgdGhlIGVycm9yDQogIGVyciA8LSBzcXJ0KHN1bSgob3JpZ2luYWwgLSBub2lzeSleMikpDQogICMgUG93ZXIgb2YgdGhlIHNpZ25hbA0KICBzaWcgPC0gc3FydChzdW0ob3JpZ2luYWxeMikpDQogICMgU05SIGluIGRCcw0KICByZXR1cm4oMTAgKiBsb2cxMChzaWcgLyBlcnIpKQ0KfQ0KDQojIENhbGN1bGF0ZSBpbml0aWFsIFNOUg0KY2F0KHNwcmludGYoIlNOUiA9ICVmIGRCIiwgU05SKHNELCBzQSkpKQ0KYGBgDQoNCkNhbiB3ZSBoZWFyIHRoZSAxN2RCIGRpZmZlcmVuY2U/IEEgYml0Li4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYW5kIHBsYXkgYW5hbG9nIHZlcnNpb24NCnBsYXlfYW5hbG9nIDwtIGZ1bmN0aW9uKCkgew0KICBhbmFsb2dfd2F2IDwtIHNwZWVjaF93YXYNCiAgYW5hbG9nX3dhdkBsZWZ0IDwtIHNBICMgS2VlcCBhcyBmbG9hdGluZyBwb2ludCB2YWx1ZXMNCiAgbGlzdGVuKGFuYWxvZ193YXYpDQp9DQoNCiMgQ3JlYXRlIGFuZCBwbGF5IGRpZ2l0YWwgdmVyc2lvbg0KcGxheV9kaWdpdGFsIDwtIGZ1bmN0aW9uKCkgew0KICBkaWdpdGFsX3dhdiA8LSBzcGVlY2hfd2F2DQogIGRpZ2l0YWxfd2F2QGxlZnQgPC0gc0QgIyBBbHJlYWR5IGludGVnZXItbGlrZSBmcm9tIHJvdW5kaW5nDQogIGxpc3RlbihkaWdpdGFsX3dhdikNCn0NCg0KIyBVbmNvbW1lbnQgdG8gcGxheQ0KIyBwbGF5X2FuYWxvZygpDQojIHBsYXlfZGlnaXRhbCgpDQpgYGANCg0KIyMjIEhvdyB0byBwcm9kdWNlIGEgcXVpZXRlciB2ZXJzaW9uIG9mIHRoZSBhdWRpbyBjbGlwDQoNCmBgYHtyfQ0KIyBGaXJzdCBub3JtYWxpemUgdG8gZnVsbCByYW5nZSAoLTEgdG8gMSkNCm5vcm1hbGl6ZWQgPC0gcyAvIG1heChhYnMocykpDQoNCiMgVGhlbiBjcmVhdGUgdHdvIHZlcnNpb25zIHdpdGggZGlmZmVyZW50IGFtcGxpdHVkZXMNCmZ1bGxfdm9sdW1lIDwtIG5vcm1hbGl6ZWQNCmhhbGZfdm9sdW1lIDwtIG5vcm1hbGl6ZWQgKiAwLjUNCg0KIyBDcmVhdGUgY29tYmluZWQgYXVkaW8NCmNvbWJpbmVkIDwtIHNwZWVjaF93YXYNCmNvbWJpbmVkQGxlZnQgPC0gYyhmdWxsX3ZvbHVtZSAqIDMyNzY3LCByZXAoMCwgODAwMCksIGhhbGZfdm9sdW1lICogMzI3NjcpDQoNCmF1ZGlvX2RmIDwtIHRpYmJsZShzYW1wbGUgPSAxOmxlbmd0aChjb21iaW5lZEBsZWZ0KSwgYW1wbGl0dWRlID0gY29tYmluZWRAbGVmdCkNCmdncGxvdChhdWRpb19kZiwgYWVzKHggPSBzYW1wbGUsIHkgPSBhbXBsaXR1ZGUpKSArDQogIGdlb21fbGluZSgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh4ID0gIlNhbXBsZSIsIHkgPSAiQW1wbGl0dWRlIiwgdGl0bGUgPSAiQXVkaW8gU2lnbmFsIikNCg0KbGlzdGVuKGNvbWJpbmVkKQ0KYGBgDQoNCiMjIFRyYW5zbWlzc2lvbg0KDQpMZXQncyBub3cgZGVmaW5lIGEgZnVuY3Rpb24gdGhhdCByZXByZXNlbnRzIHRoZSBuZXQgZWZmZWN0IG9mIHRyYW5zbWl0dGluZyBhdWRpbyBvdmVyIGEgY2FibGUgc2VnbWVudCB0ZXJtaW5hdGVkIGJ5IGEgcmVwZWF0ZXI6IFwqIHRoZSBzaWduYWwgaXMgYXR0ZW51YXRlZCBcKiB0aGUgc2lnbmFsIGlzIGFjY3VtdWxhdGVzIGFkZGl0aXZlIG5vaXNlIGFzIGl0IHByb3BhZ2F0ZXMgdGhyb3VnaCB0aGUgY2FibGUgXCogdGhlIHNpZ25hbCBpcyBhbXBsaWZpZWQgdG8gdGhlIG9yaWdpbmFsIGFtcGxpdHVkZSBieSB0aGUgcmVwZWF0ZXINCg0KYGBge3J9DQojIERlZmluZSByZXBlYXRlciBmdW5jdGlvbg0KcmVwZWF0ZXIgPC0gZnVuY3Rpb24oeCwgbm9pc2VfYW1wbGl0dWRlLCBhdHRlbnVhdGlvbikgew0KICAjIENyZWF0ZSB1bmlmb3JtIG5vaXNlDQogIG5vaXNlIDwtIHJ1bmlmKGxlbmd0aCh4KSwgbWluID0gLW5vaXNlX2FtcGxpdHVkZSwgbWF4ID0gbm9pc2VfYW1wbGl0dWRlKQ0KICAjIEF0dGVudWF0aW9uDQogIHggPC0geCAqIGF0dGVudWF0aW9uDQogICMgQWRkIG5vaXNlDQogIHggPC0geCArIG5vaXNlDQogICMgR2FpbiBjb21wZW5zYXRpb24NCiAgcmV0dXJuKHggLyBhdHRlbnVhdGlvbikNCn0NCmBgYA0KDQp3ZSBjYW4gdXNlIHRoZSByZXBlYXRlciBmb3IgYm90aCBhbmFsb2cgYW5kIGRpZ2l0YWwgc2lnbmFscy4gVHJhbnNtaXNzaW9uIG9mIHRoZSBhbmFsb2cgc2lnbmFsIGlzIHNpbXBseSBhIHNlcXVlbmNlIG9mIHJlcGVhdGVyczoNCg0KYGBge3J9DQojIERlZmluZSBhbmFsb2cgdHJhbnNtaXNzaW9uIGZ1bmN0aW9uDQphbmFsb2dfdHggPC0gZnVuY3Rpb24oeCwgbnVtX3JlcGVhdGVycywgbm9pc2VfYW1wbGl0dWRlLCBhdHRlbnVhdGlvbikgew0KICBmb3IgKG4gaW4gMTpudW1fcmVwZWF0ZXJzKSB7DQogICAgeCA8LSByZXBlYXRlcih4LCBub2lzZV9hbXBsaXR1ZGUsIGF0dGVudWF0aW9uKQ0KICB9DQogIHJldHVybih4KQ0KfQ0KYGBgDQoNCkZvciBkaWdpdGFsIHNpZ25hbHMsIGhvd2V2ZXIsIHdlIGNhbiByZWN0aWZ5IHRoZSBzaWduYWwgYWZ0ZXIgZWFjaCByZXBlYXRlciwgYmVjYXVzZSB3ZSBrbm93IHRoYXQgdmFsdWVzIHNob3VsZCBvbmx5IGJlIGludGVnZXItdmFsdWVkOg0KDQpgYGB7cn0NCiMgRGVmaW5lIGRpZ2l0YWwgdHJhbnNtaXNzaW9uIGZ1bmN0aW9uDQpkaWdpdGFsX3R4IDwtIGZ1bmN0aW9uKHgsIG51bV9yZXBlYXRlcnMsIG5vaXNlX2FtcGxpdHVkZSwgYXR0ZW51YXRpb24pIHsNCiAgZm9yIChuIGluIDE6bnVtX3JlcGVhdGVycykgew0KICAgIHggPC0gcm91bmQocmVwZWF0ZXIoeCwgbm9pc2VfYW1wbGl0dWRlLCBhdHRlbnVhdGlvbikpDQogIH0NCiAgcmV0dXJuKHgpDQp9DQpgYGANCg0KTGV0J3MgY29tcGFyZSB0cmFuc21pc3Npb24gc2NoZW1lcy4gQXMgeW91IGNhbiBzZWUsIHRoZSBTTlIgYWZ0ZXIgZGlnaXRhbCB0cmFuc21pc3Npb24gaGFzIG5vdCBjaGFuZ2VkISBOb3cgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhdWRpbyBjbGlwcyBzaG91bGQgYmUgZWFzeSB0byBoZWFyOg0KDQpgYGB7cn0NCiMgU2V0IHBhcmFtZXRlcnMNCk5VTV9SRVBFQVRFUlMgPC0gNzANCk5PSVNFX0FNUExJVFVERSA8LSAwLjINCkFUVEVOVUFUSU9OIDwtIDAuNQ0KDQojIFJ1biBhbmFsb2cgdHJhbnNtaXNzaW9uDQp5QSA8LSBhbmFsb2dfdHgoc0EsIE5VTV9SRVBFQVRFUlMsIE5PSVNFX0FNUExJVFVERSwgQVRURU5VQVRJT04pDQpjYXQoc3ByaW50ZigiQW5hbG9nIHRyYW5zbWlzc2lvbjogU05SID0gJWYgZEJcbiIsIFNOUih5QSwgc0EpKSkNCg0KIyBSdW4gZGlnaXRhbCB0cmFuc21pc3Npb24NCnlEIDwtIGRpZ2l0YWxfdHgoc0QsIE5VTV9SRVBFQVRFUlMsIE5PSVNFX0FNUExJVFVERSwgQVRURU5VQVRJT04pDQpjYXQoc3ByaW50ZigiRGlnaXRhbCB0cmFuc21pc3Npb246IFNOUiA9ICVmIGRCXG4iLCBTTlIoeUQsIHNBKSkpDQoNCg0KY29tYmluZWQgPC0gc3BlZWNoX3dhdg0KY29tYmluZWRAbGVmdCA8LSBjKHlBLCByZXAoMCwgODAwMCksIHlEKQ0KDQphdWRpb19kZiA8LSB0aWJibGUoc2FtcGxlID0gMTpsZW5ndGgoY29tYmluZWRAbGVmdCksIGFtcGxpdHVkZSA9IGNvbWJpbmVkQGxlZnQpDQpnZ3Bsb3QoYXVkaW9fZGYsIGFlcyh4ID0gc2FtcGxlLCB5ID0gYW1wbGl0dWRlKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoeCA9ICJTYW1wbGUiLCB5ID0gIkFtcGxpdHVkZSIsIHRpdGxlID0gIkF1ZGlvIFNpZ25hbCIpDQoNCmxpc3Rlbihjb21iaW5lZCkNCmBgYA0KDQpOb3RlIGhvd2V2ZXIgdGhhdCwgaWYgdGhlIG5vaXNlIGFtcGxpdHVkZSBleGNlZWRzIGEgY2VydGFpbiB2YWx1ZSwgZGlnaXRhbCB0cmFuc21pc3Npb24gZGVncmFkZXMgZXZlbiBsZXNzIGdyYWNlZnVsbHkgdGhhbiBhbmFsb2cgdHJhbnNtaXNzaW9uOg0KDQpgYGB7cn0NCiMgU2V0IGhpZ2hlciBub2lzZSBhbXBsaXR1ZGUNCk5PSVNFX0FNUExJVFVERSA8LSAwLjMNCg0KIyBSdW4gYW5hbG9nIHRyYW5zbWlzc2lvbiB3aXRoIGhpZ2hlciBub2lzZQ0KeUEgPC0gYW5hbG9nX3R4KHNBLCBOVU1fUkVQRUFURVJTLCBOT0lTRV9BTVBMSVRVREUsIEFUVEVOVUFUSU9OKQ0KY2F0KHNwcmludGYoIkFuYWxvZyB0cmFuc21pc3Npb246IFNOUiA9ICVmIGRCXG4iLCBTTlIoeUEsIHNBKSkpDQoNCiMgUnVuIGRpZ2l0YWwgdHJhbnNtaXNzaW9uIHdpdGggaGlnaGVyIG5vaXNlDQp5RCA8LSBkaWdpdGFsX3R4KHNELCBOVU1fUkVQRUFURVJTLCBOT0lTRV9BTVBMSVRVREUsIEFUVEVOVUFUSU9OKQ0KY2F0KHNwcmludGYoIkRpZ2l0YWwgdHJhbnNtaXNzaW9uOiBTTlIgPSAlZiBkQlxuIiwgU05SKHlELCBzQSkpKQ0KYGBgDQoNCiMjIFBsYXlpbmcgVHJhbnNtaXR0ZWQgU2lnbmFscw0KDQpgYGB7cn0NCmNvbWJpbmVkMiA8LSBzcGVlY2hfd2F2DQpjb21iaW5lZDJAbGVmdCA8LSBjKHlBLCByZXAoMCwgODAwMCksIHlEKQ0KDQphdWRpb19kZiA8LSB0aWJibGUoc2FtcGxlID0gMTpsZW5ndGgoY29tYmluZWQyQGxlZnQpLCBhbXBsaXR1ZGUgPSBjb21iaW5lZDJAbGVmdCkNCmdncGxvdChhdWRpb19kZiwgYWVzKHggPSBzYW1wbGUsIHkgPSBhbXBsaXR1ZGUpKSArDQogIGdlb21fbGluZSgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh4ID0gIlNhbXBsZSIsIHkgPSAiQW1wbGl0dWRlIiwgdGl0bGUgPSAiQXVkaW8gU2lnbmFsIikNCg0KbGlzdGVuKGNvbWJpbmVkMikNCg0KYGBgDQoNCiMjIFVuZGVyc3RhbmRpbmcgRGlnaXRhbCBTaWduYWwgRGVncmFkYXRpb24gd2l0aCBOb2lzZQ0KDQpUaGUgc2lnbmlmaWNhbnQgZGVncmFkYXRpb24gaW4gZGlnaXRhbCB0cmFuc21pc3Npb24gd2hlbiBpbmNyZWFzaW5nIE5PSVNFX0FNUExJVFVERSBmcm9tIDAuMiB0byAwLjMgaXMgYSBncmVhdCBleGFtcGxlIG9mIHdoYXQncyBjYWxsZWQgdGhlICJjbGlmZiBlZmZlY3QiIGluIGRpZ2l0YWwgY29tbXVuaWNhdGlvbnMuIExldCBtZSBleHBsYWluIHdoeSB0aGlzIGhhcHBlbnM6DQoNCiMjIyBXaHkgRGlnaXRhbCBTaWduYWxzIERlZ3JhZGUgU3VkZGVubHkNCg0KSW4gdGhlIGRpZ2l0YWwgdHJhbnNtaXNzaW9uIHN5c3RlbSwgd2UncmUgcmVseWluZyBvbiB0aGUgYWJpbGl0eSB0byBjb3JyZWN0bHkgcmVjb3ZlciB0aGUgb3JpZ2luYWwgaW50ZWdlciB2YWx1ZXMgYnkgcm91bmRpbmcgYWZ0ZXIgZWFjaCByZXBlYXRlci4gVGhpcyB3b3JrcyB3ZWxsIGFzIGxvbmcgYXMgdGhlIG5vaXNlIGRvZXNuJ3QgcHVzaCB0aGUgc2lnbmFsIGFjcm9zcyBhIGRlY2lzaW9uIHRocmVzaG9sZC4gTGV0J3MgYW5hbHl6ZSB0aGUgY3JpdGljYWwgdGhyZXNob2xkIG1hdGhlbWF0aWNhbGx5Og0KDQoxLiAgRWFjaCBkaWdpdGFsIHZhbHVlIGhhcyBkZWNpc2lvbiBib3VuZGFyaWVzIGF0IMKxMC41IGZyb20gdGhlIGludGVnZXIgdmFsdWUgKGUuZy4sIGZvciB0aGUgdmFsdWUgNDIsIGNvcnJlY3QgcmVjb3ZlcnkgaGFwcGVucyBhcyBsb25nIGFzIHRoZSBub2lzeSBzaWduYWwgaXMgYmV0d2VlbiA0MS41IGFuZCA0Mi41KQ0KDQoyLiAgSW4gZWFjaCByZXBlYXRlcjoNCg0KICAgIC0gICBTaWduYWwgaXMgYXR0ZW51YXRlZDogYHggKiBBVFRFTlVBVElPTmAgKHggXCogMC41KQ0KICAgIC0gICBOb2lzZSBpcyBhZGRlZDogWy1OT0lTRV9BTVBMSVRVREUsICtOT0lTRV9BTVBMSVRVREVdDQogICAgLSAgIFNpZ25hbCBpcyBhbXBsaWZpZWQ6IGAoeCAqIEFUVEVOVUFUSU9OICsgbm9pc2UpIC8gQVRURU5VQVRJT05gDQogICAgLSAgIEFmdGVyIHNpbXBsaWZ5aW5nOiBgeCArIG5vaXNlL0FUVEVOVUFUSU9OYA0KDQozLiAgVGhlIGVmZmVjdGl2ZSBub2lzZSByYW5nZSBhZnRlciBhbXBsaWZpY2F0aW9uIGJlY29tZXM6IGBOT0lTRV9BTVBMSVRVREUgKiAoMS9BVFRFTlVBVElPTikgPSBOT0lTRV9BTVBMSVRVREUgKiAyYA0KDQojIyMgQ2FsY3VsYXRpbmcgdGhlIENyaXRpY2FsIFRocmVzaG9sZA0KDQpGb3IgcmVsaWFibGUgZGlnaXRhbCB0cmFuc21pc3Npb24sIHdlIG5lZWQ6IGBOT0lTRV9BTVBMSVRVREUgKiAoMS9BVFRFTlVBVElPTikgPCAwLjVgDQoNCldpdGggQVRURU5VQVRJT04gPSAwLjU6IGBOT0lTRV9BTVBMSVRVREUgKiAyIDwgMC41YCBgTk9JU0VfQU1QTElUVURFIDwgMC4yNWANCg0KVGhpcyBleHBsYWlucyB3aHk6DQoNCi0gICBBdCBOT0lTRV9BTVBMSVRVREUgPSAwLjI6IFRoZSBlZmZlY3RpdmUgbm9pc2UgKDAuNCkgaXMgc3RpbGwgYmVsb3cgdGhlIGNyaXRpY2FsIHRocmVzaG9sZCBvZiAwLjUNCi0gICBBdCBOT0lTRV9BTVBMSVRVREUgPSAwLjM6IFRoZSBlZmZlY3RpdmUgbm9pc2UgKDAuNikgZXhjZWVkcyB0aGUgdGhyZXNob2xkLCBjYXVzaW5nIGluY29ycmVjdCByb3VuZGluZyBkZWNpc2lvbnMNCg0KIyMgRXN0aW1hdGluZyB0aGUgVXBwZXIgTGltaXQNCg0KSW1wbGVtZW50IHRoaXMgaW4gUiB0byB2aXN1YWxpemUgdGhlIGVmZmVjdDoNCg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGVzdGltYXRlIHRocmVzaG9sZCBhbmQgZXJyb3IgcHJvYmFiaWxpdHkNCmVzdGltYXRlX2RpZ2l0YWxfcmVsaWFiaWxpdHkgPC0gZnVuY3Rpb24oYXR0ZW51YXRpb25fdmFsdWUgPSAwLjUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vaXNlX3JhbmdlID0gc2VxKDAuMSwgMC41LCBieSA9IDAuMDEpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1fcmVwZWF0ZXJzID0gNzAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IDEwMDAwKSB7DQogIA0KICByZXN1bHRzIDwtIHRpYmJsZSgNCiAgICBub2lzZV9hbXBsaXR1ZGUgPSBub2lzZV9yYW5nZSwNCiAgICBlZmZlY3RpdmVfbm9pc2UgPSBub2lzZV9yYW5nZSAqICgxL2F0dGVudWF0aW9uX3ZhbHVlKSwNCiAgICB0aGVvcmV0aWNhbF90aHJlc2hvbGQgPSAwLjUsDQogICAgZXJyb3JfcHJvYmFiaWxpdHkgPSBwbWFwX2RibChsaXN0KG5vaXNlX3JhbmdlKSwgZnVuY3Rpb24obm9pc2VfdmFsKSB7DQogICAgICAjIFNpbXVsYXRlIGEgc2luZ2xlIHJlcGVhdGVyIHN0YWdlIHdpdGggZGlmZmVyZW50IG5vaXNlIGxldmVscw0KICAgICAgIyBTdGFydCB3aXRoIHBlcmZlY3QgaW50ZWdlciB2YWx1ZXMNCiAgICAgIG9yaWdpbmFsIDwtIHNhbXBsZSgtMTAwOjEwMCwgc2FtcGxlcywgcmVwbGFjZSA9IFRSVUUpDQogICAgICAjIFBhc3MgdGhyb3VnaCBvbmUgcmVwZWF0ZXINCiAgICAgIG5vaXN5IDwtIG9yaWdpbmFsICsgcnVuaWYoc2FtcGxlcywgLW5vaXNlX3ZhbCwgbm9pc2VfdmFsKSAqICgxL2F0dGVudWF0aW9uX3ZhbHVlKQ0KICAgICAgIyBDaGVjayBlcnJvciByYXRlIGFmdGVyIHJvdW5kaW5nDQogICAgICBtZWFuKHJvdW5kKG5vaXN5KSAhPSBvcmlnaW5hbCkNCiAgICB9KSwNCiAgICBjdW11bGF0aXZlX2Vycm9yX3Byb2IgPSAxIC0gKDEgLSBlcnJvcl9wcm9iYWJpbGl0eSlebnVtX3JlcGVhdGVycw0KICApDQogIA0KICByZXR1cm4ocmVzdWx0cykNCn0NCg0KIyBDYWxjdWxhdGUgYW5kIHBsb3QNCnRocmVzaG9sZF9kYXRhIDwtIGVzdGltYXRlX2RpZ2l0YWxfcmVsaWFiaWxpdHkoKQ0KDQojIFBsb3QgdGhlIHJlc3VsdHMNCmdncGxvdCh0aHJlc2hvbGRfZGF0YSwgYWVzKHggPSBub2lzZV9hbXBsaXR1ZGUpKSArDQogIGdlb21fbGluZShhZXMoeSA9IGVycm9yX3Byb2JhYmlsaXR5LCBjb2xvciA9ICJTaW5nbGUgUmVwZWF0ZXIgRXJyb3IgUHJvYi4iKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBjdW11bGF0aXZlX2Vycm9yX3Byb2IsIGNvbG9yID0gIlN5c3RlbSBFcnJvciBQcm9iLiIpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuMjUsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMC4yNiwgeSA9IDAuNSwgbGFiZWwgPSAiQ3JpdGljYWxcblRocmVzaG9sZCIsIGhqdXN0ID0gMCkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsICJkYXJrcmVkIikpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEaWdpdGFsIFRyYW5zbWlzc2lvbiBSZWxpYWJpbGl0eSB2cy4gTm9pc2UgQW1wbGl0dWRlIiwNCiAgICB4ID0gIk5vaXNlIEFtcGxpdHVkZSIsDQogICAgeSA9ICJQcm9iYWJpbGl0eSBvZiBFcnJvciIsDQogICAgY29sb3IgPSAiTWVhc3VyZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIFdoeSBEaWdpdGFsIERlZ3JhZGVzIFdvcnNlIFRoYW4gQW5hbG9nIEFib3ZlIFRocmVzaG9sZA0KDQpXaGVuIG5vaXNlIGV4Y2VlZHMgdGhlIGNyaXRpY2FsIHRocmVzaG9sZCBpbiBkaWdpdGFsIHRyYW5zbWlzc2lvbjoNCg0KMS4gRWFjaCBlcnJvciBjYXVzZXMgYSBjb21wbGV0ZSBtaXNjbGFzc2lmaWNhdGlvbiBvZiB0aGUgc2lnbmFsIHZhbHVlDQoyLiBUaGVzZSBlcnJvcnMgYWNjdW11bGF0ZSBhbmQgY2FzY2FkZSB0aHJvdWdoIG11bHRpcGxlIHJlcGVhdGVycw0KMy4gT25jZSBlcnJvcnMgc3RhcnQgb2NjdXJyaW5nLCB0aGUgc3Vic2VxdWVudCByZXBlYXRlcnMgYW1wbGlmeSB0aGVzZSBlcnJvcnMNCg0KSW4gY29udHJhc3QsIGFuYWxvZyBzaWduYWxzIGRlZ3JhZGUgZ3JhY2VmdWxseSAtIHRoZSBub2lzZSBhY2N1bXVsYXRlcyBsaW5lYXJseSBhY3Jvc3MgcmVwZWF0ZXJzIGJ1dCB0aGVyZSBhcmUgbm8gc3VkZGVuICJqdW1wcyIgYmV0d2VlbiBkaXNjcmV0ZSB2YWx1ZXMuDQpUaGlzIGRlbW9uc3RyYXRlcyB0aGUgY2xhc3NpYyB0cmFkZW9mZiBpbiBkaWdpdGFsIGNvbW11bmljYXRpb25zOiBleGNlbGxlbnQgcGVyZm9ybWFuY2UgdXAgdG8gYSBjcml0aWNhbCB0aHJlc2hvbGQsIGZvbGxvd2VkIGJ5IHJhcGlkIGRlZ3JhZGF0aW9uIGJleW9uZCB0aGF0IHBvaW50Lg==