Capturing useful R concepts in the context of fooling around with bits of trigonometry and numerology. Nothing beats a good \(\pi\). You know what I golden mean.

Though disuse, laziness and sloth I often forget R concepts and methods, so I mess about when the weather is bad. I aim for ad hoc clarity in my code rather than generalization. This makes it a little easier for me to remember what I did. There is nothing like picking up your old code and having to reverse engineer the thing. Even more horrid for someone else. At least it aint Perl. Or SAS.

Set up the R environment

The libraries used.

require(tidyverse)    # for graphing capabilities, data manipulation
require(MASS)         # for converting decimal fractions to common fractions
require(plotly)       # for animating graphs

Circling points

The ratio of any circle’s circumference \(C\) to its diameter \(d\) is \(\pi\), an irrational number with the approximate value of \(3.1415926535897931\). \(\pi = \frac{C}{d}\). Since \(d\) is equal to twice the circle’s radius \(r\), \(\pi = \frac{C}{2r}\)

The equation can be rearranged to calculate \(C\) from \(r\) and \(r\) from \(C\). \(C = 2\pi r\), \(r = \frac{C}{2\pi}\).

The relationship is used to define angles. The circumference of the circle is a series of arcs of length \(r\). The length of the entire circle (ie. \(C\)) is \(2\pi\) times \(r\). In addition to being the radius of the circle, \(r\) measures the length of an arc on the circle. An arc length \(r\) defines an angle formed by the radius intersecting the end of the arc. This angle is the unit radian \(rad\).

An angle \(\theta\) is the ration of the length of the arc \(s\) to the radius \(r\): \(\theta = \frac{s}{r}\). As a circle grows or contracts, \(r\) and \(s\) also grow and contract. An angle defined by a radian on any sized circle is always the same value of \(\theta\).

\[\frac{s\ units}{r\ units} = \frac{s}{r} = \theta\] The units cancel out, making \(\theta\) dimensionless.

Traveling around the circumference of the circle requires \(2\pi\ rad\), so values of \(\theta\) varies from 0 to \(2\pi\) around that path. Half way around the circle \(\theta = \pi\). A quarter way around the circle \(\theta = \frac{\pi}{2}\), and an eighth around the circle \(\theta = \frac{\pi}{4}\). These values correspond to \(360^\circ\), \(180^\circ\), \(90^\circ\), and \(45^\circ\).

Polar coordinates

A polar coordinate defines a point by distance from the center of the circle (the length of \(r\)) and by the angle, \(\theta\), formed between the radius and a base location (where \(\theta = 0\). The polar coordinate is in the form \((r,\theta)\).

Relating polar to Cartesian coordinates

The trigonometric functions \(cosine\) and \(sine\) can be used to convert polar coordinates to \((x,y)\) Cartesian coordinates. \[x = r\times cos(\theta)\] \[y = r\times sin(\theta)\]

Plotting points on the circle

Here points on a unit circle are generated and graphed on a plot. We want points around the entire circle, but we want the points separated so the pattern is clear.

First we generate a series of fractions from 0 to 2 in steps of 1/8. These will be multiplied by \(pi\) to create angles from \(\frac{1}{8}\pi\) to \(2\pi\), a complete turn around the circle in steps of \(\frac{1}{8}\pi\).

These are multiplied by \(\pi\) to generate a sequence of values of \(\theta\) around the circle. The radius of the unit circle \(r = 1\) is combined with the angles to create a data frame of polar coordinates.

r <- 1
steps <- seq(from = 0, to = 2, by = 1/8)
thetas <- steps * pi
unit_circle <- data.frame("r" = 1,         # the unit circle, where radius is 1
                          "theta" = thetas
                      )
unit_circle  # a collection of polar coordinates on a circle

The data frame is expanded to add the Cartesian coordinate of each of the polar coordinates.

unit_circle$x <- unit_circle$r * cos(unit_circle$theta)
unit_circle$y <- unit_circle$r * sin(unit_circle$theta)
unit_circle

Graph (plot) the points on a circle

The defined Cartesian \((x,y)\) coordinate points of unit_circle are graphed on a plot.

ggplot(unit_circle, aes(x, y))+
  theme_minimal() +
  geom_point(color = "blue") +
  coord_fixed()

We can do some things to create labels as fractions of pi.

steps_asfractions <- fractions(steps)
unit_circle$label <- paste(as.character(steps_asfractions), "pi")
unit_circle
label_scaler <- 1.17    # this is used to move the label locations further out,
                        # i.e., puts the labels on a slightly larger cicle
unit_circle$label_x <- label_scaler * unit_circle$x
unit_circle$label_y <- label_scaler * unit_circle$y
unit_circle

Now the graph can be drawn with labels for the angles. The labels help relate pi (\(\pi\)) to angles in degrees. \(2\pi = 360^\circ\), \(\pi=180^\circ\), \(\frac{\pi}{2}=90^\circ\), \(\frac{\pi}{4}=45^\circ\), \(\frac{3\pi}{2} = 270^\circ\).

ggplot(unit_circle, aes(x, y))+
  theme_minimal() +
  geom_point(color = "blue") +
  geom_text(aes(x=label_x,
                y=label_y,
                label = label),
            size = 4
            ) +
  coord_fixed()

Changing circles

Changing position

A circle’s position is changed by adding or subtracting a constant value to the x and y coordinates.

steps <- seq(from = 0, to = 2, by = 1/32)
thetas <- steps * pi
unit_circle <- data.frame(r = 1,
                          theta = thetas,
                          x = 1 * cos(thetas),
                          y = 1 * sin(thetas)
                          )

unit_plot <- ggplot(unit_circle,aes(x, y)) +
  theme_minimal() +
  geom_point(color = "#7570b3") +
  xlim(-4, 4) +
  ylim(-4, 4) +
  coord_fixed()

unit_plot  # display the plot

The unit circle is moved on the plot by adjusting its coordinates by addition or substraction. Here the circle is moved three units right and three units left.

ggplot(unit_circle, 
       aes(x + 3, y + 3)) +
  theme_minimal() +
  geom_point(color = "#7570b3") +
  xlim(-4, 4) +
  ylim(-4, 4) +
  coord_fixed()

Show the circle at multiple new positions at once

The unit_plot graph is built on by adding new unit circles shifted to various locations on the plane.

unit_plot +
  theme_minimal() +
  xlim(-4, 4) +
  ylim(-4, 4) +
  geom_point(aes(x = x + 3, y = y),  # circle to right by adding to x coordinates
             color = "#1b9e77") +
  geom_point(aes(x = x, y = y + 3),  # circle up by adding to y coordinates
             color = "#1b9e77") +
  geom_point(aes(x = x + 3, y = y + 3),  # circle right and up
             color = "#1b9e77") +
  geom_point(aes(x = x - 3, y = y - 3),  # circle down and left
             color = "#1b9e77")
Scale for 'x' is already present. Adding another scale for 'x', which will replace the
existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the
existing scale.

Changing size

The unit circle is used as a template for circles or other size. The plot assigned to unit_plot is built on by adding new sets of x,y coordinates. The new coordinate sets are created by scaling the original unit circle coordinates, which are part of unit_plot.

unit_plot +  
  theme_minimal() +
  xlim(-4, 4) +
  ylim(-4, 4) +
  geom_point(aes(x = 2 * x, y = 2 * y),      # add a bigger circle
             color = "#d95f02") +
  geom_point(aes(x = x / 2, y = y / 2),      # add a smaller circle
             color = "#d95f02")
Scale for 'x' is already present. Adding another scale for 'x', which will replace the
existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the
existing scale.

Distort

Multiplication and division are changes to the distance from the center of the plot: changes to the radius.

varr <- (1:nrow(unit_circle))
unit_plot +
  xlim(-4, 4) +
  ylim(-4, 4) +
  theme_minimal() +
  geom_point(aes(x = 2*x, y),  # expand on the x axis
             color = "#d95f02") +
  geom_point(aes(x = x, 2*y),  # expand on the x axis
             color = "#d95f02")
Scale for 'x' is already present. Adding another scale for 'x', which will replace the
existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the
existing scale.

Concentric circles

Concentric circles share the same center. Points on concentric circles can be generating by combining a collection of radii of different length with angles around the circle.

The Cartesian cross product is an outcome of combining all members of one set with all members of another set. A cross product of a set of radii and a set of angles creates multiple polar coordinates.

#  create a collection of radii starting from the 0 origin
r = seq(from = 0, to = 5, by = 1)


#  create a set of angles from 0 to pi (equivalent to 0 to 180 degrees)
theta = seq(from = 0, to = 2, by = 1/6) * pi


#  The application of the function crossing maps each radius 
# (there are 6 radii in the set) to 
#  all rows in the angles data frame (there are 9 rows).  
# The product has 54 rows. 
#  Each row defines a polar coordinate (r,theta)
half_circles <- crossing(r, theta)
half_circles


#  Convert polar coordinates to Cartesian coordinates for plotting in x,y
half_circles$x <- half_circles$r * cos(half_circles$theta)
half_circles$y <- half_circles$r * sin(half_circles$theta) 

#  Add the equivalent angle in degrees to each theta
half_circles$degrees <- half_circles$theta * 180 / pi
half_circles
NA

Plot the points. The point is labeled with the radius of its polar coordinate. The point is colored by the equivalent degrees of theta.

ggplot(half_circles, aes(x, y)) +
  theme_minimal() +
  geom_point(aes(color = as.factor(degrees))) +
  geom_text(aes(label = r, color = as.factor(degrees)), nudge_y = 0.3) +
  geom_path(aes(color= as.factor(r))) +
  geom_path(aes(color= as.factor(degrees))) +
  coord_fixed()

Spiral

A spiral is created by increasing the radius for each point generated. Using seq_along(theta) creates a vector from 1 to the number that is the length of theta in increments of 1. We want the spriral to start at the origin, so 1 is subtracted from the generated vector, which efectively shifts the sequence to the left by 1.

theta <- seq(0, 6*pi, 1/4)  # three times around in 1/4 steps
r <- seq_along(theta)       # ever increasing radius
r <- r - 1                  # shift to start at 0

spiral <- data.frame(r = r,
                     theta = theta)
# show the polar coordinates
spiral

# derive the cartesian coordinates
spiral$x <- r * cos(theta)
spiral$y <- r * sin(theta)

# plot the spiral
ggplot(spiral, aes(x, y)) +
  theme_minimal() +
  geom_point(color = "#d95f02") +
  coord_fixed()

Animating the spiral, a bit of a tangent

Fun with spiral animation. Focus is on the creation of plot frames.

Rather than rendering the points all at once, the plot of the spiral can be animated using ggplotly() from the package plotly. This function animates the plot by drawing a series of plot frames, which are identified in the data frame.

The mechanics of generating the points of a spiral guarantees that the data frame is sequenced by radius. This makes it possible to reason about the process. Without the guarantee, some extra work (lazily and slovenly left out of this work) needs to be done. Don’t get complacent.

The spiral as a moving point

In this section, each plot frame identifies a single point. The radius of the point is used as the plot frame variable. The mechanics of the generation of the points making

Note that each frame (which is a point indexed by spiral$r) is a new plot. As the animation plays, a single point seems to move on the path of the spiral. In reality, each point is separate from the others. The preceding plots are replaced, not added to.

ggplotly(                           # the new function
  ggplot(spiral, aes(x, y)) +
  theme_minimal() +
  geom_point(aes(frame = r),        # the frame variable, which is part of spiral
             color = "#d95f02") +
  coord_fixed()
)
Warning: Ignoring unknown aesthetics: frame

The growing spiral

Rather than an apparently moving point on a spiral, I want to show a growing spiral by retaining rather than losing previous points.

In the version of plotly used, retaining points throughout the animation is not an option. We have to perform a bit of finagling.

During animation, each frame replaces the next. Retaining plotted points requires that each frame has its own point and all of the preceding points.

The process:

  1. create a vector, framenumbers, that indexes each row in spiral
  2. iterate over framenumbers capturing each element as framenumber
  3. at each, copy the rows of spiral from 1 to framenumber, add a new column, plotframe to the copied rows and set it to framenumber, and add the copied rows as a data frame item to a growing list
  4. when complete, collapse the list of data frame items into a single data frame

The new data frame contains plot frames with progressively more rows. The plot frame rows are identified in the new data frame as plotframe, which is used to step through the data to animate the plot using the function ggplotly.

framenumbers <- seq_along(spiral$r)

listofexpandedframes <- 
  lapply(framenumbers,           # elements of framenumbers are fed one at a time to the function
         function(framenumber){
           cbind(
             spiral[c(1:framenumber), ],
             plotframe = framenumber
           )
         })

growingspiral <- dplyr::bind_rows(listofexpandedframes) 

head(growingspiral,20)

ggplotly(
  ggplot(growingspiral, aes(x, y)) +
    theme_minimal() +
    geom_point(aes(frame = plotframe), 
               color = "#d95f02") +
    coord_fixed()
)
Warning: Ignoring unknown aesthetics: frame

Sunflower head patterns

See https://archive.bridgesmathart.org/2010/bridges2010-483.pdf and https://math.okstate.edu/people/segerman/talks/sunflower_spiral_talk.pdf for a discussion on the sunflower spiral. The algorithms captured here are from these sources.

Trips around the circle

A set of angles created by multiplying an angle by a set of integers results in a set of coterminal angles. In the case below, the angle is \(2\pi\) and the integers are the set from 1 to 50. \(2\pi\) is equal to \(360^\circ\). Although the fifty angles are all different angles, they all terminate with the same vertex, but with different trips around the circle to get there.

Te coterminal angles are later transformed such that they are no longer coterminal.

The radii are transformed by the square root function. The has the effect of making the points more evenly distributed. Taking the square root has more impact to large numbers than to small. For example, the square root of 100 is 10 and the square root of 1 is 1. Replacing the radii of 1 to n with the square root of 1 to n makes the radii more uniformly distributed in space, resulting in better point spacing when the points are plotted.

n <- seq(1,50)

theta <- 2*pi * n

r <- sqrt(n)

sunflower <- data.frame(n = n,
                        r = r,
                        theta = theta,
                        trips = theta / (2 * pi), 
                        x = r * cos(theta),
                        y = r * sin(theta)
                   )
sunflower

ggplot(sunflower, aes(x, round(y))) + 
  theme_minimal() +
  geom_point(color = "#d95f02") +
  ggtitle("Coterminal angles with different radius lengths")

Henry Segerman’s model

More interesting is when theta is adjusted by the golden ratio. This changes each angle theta by multiplying theta by phi, the golden ratio.
\[\theta = \theta * \phi\] \[\phi = \frac{1 + \sqrt 5}{2} \approx 1.618034\] After the transformation, the angles are multiples of the golden ratio. This have been found to have an arrangement found in nature, including the way florets and later seeds are packed into the head of a sunflower. This is the algorithm used by Henry Segerman.

The labels are the values of n in the data, which is the ordering of the points in the data.

n <- seq(1,90)

phi <- (1 + sqrt(5)) / 2.0
theta <- (2*pi * n) * phi

r <- sqrt(n)

sunflower <- data.frame(n = n,
                        r = r,
                        theta = theta,
                        x = r * cos(theta),
                        y = r * sin(theta)
                   )
sunflower

ggplot(sunflower, aes(x, y, label = n)) + 
  theme_minimal() +
  geom_point(color = "#d95f02") +
  geom_text() +
  ggtitle("Henry Segerman sunflower model") +
  coord_fixed()

Even more interesting when we greatly increase the number of angles. The result is a set of points that is a fairly good model of a sunflower head.

n <- seq(1,959)

r <- sqrt(n) 

phi <- (1 + sqrt(5)) / 2.0    # phi is the golden ratio
phi
[1] 1.618034
theta <- 2*pi * n
theta <- phi * theta          # adjust angle  by the golden ratio
                              

ssunflower <- data.frame(n = n,
                        r = r,
                        theta = theta,
                        phi_count = (theta / (2 * pi)) / phi,
                        x = r * cos(theta),
                        y = r * sin(theta),
                        style = "segerman"
                   )
ggplot(ssunflower, aes(x, y, color = style)) + 
  ggtitle("Henry Segerman sunflower model") +
  theme_minimal() +
  geom_point() +
  coord_fixed()

Helmut Vogel’s model

Proposed by Vogel in 1979. https://en.wikipedia.org/wiki/Fibonacci_number#Nature and reference to paper.
\[\theta = \frac{2\pi}{\phi^2}n,\ r = c\sqrt n\]

n <- seq(1,959)

r <- sqrt(n)

phi <- (1 + sqrt(5)) / 2.0    # phi is the golden ratio
phi
[1] 1.618034
theta <- ((2*pi) / phi^2) * n


vsunflower <- data.frame(n = n,
                         r = r,
                         theta = theta,
                         phi_count = (theta / (2 * pi)) / phi,
                         x = r * cos(theta),
                         y = r * sin(theta),
                         style = "vogel"
                   )
ggplot(vsunflower, aes(x, y, color = style)) + 
  theme_minimal() +
  geom_point() +
  ggtitle("Helmut Vogel sunflower model") +
  coord_fixed()

Comparing the two results visually

The two models are combined to plot both at the same time. This is done by the rbind function, which combines the data frame rows into a single data frame. The combined data frame is the object processed by the ggplot function.

gg <- ggplot(rbind(ssunflower, vsunflower), 
       aes(x, y, color = style)) + 
  theme_minimal() +
  geom_point() +
  ggtitle("Overlay models for comparison") +
  coord_fixed()

gg

ggplotly for richer user experience on the web

Wrapping a ggplot with ggplotly makes it possible to interact with graphs on the web even where there is no animation.

ggplotly(gg)
LS0tDQp0aXRsZTogIkNpcmNsZXMsIGFuaW1hdGlvbnMsIGFuZCBzdW5mbG93ZXJzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCkNhcHR1cmluZyB1c2VmdWwgUiBjb25jZXB0cyBpbiB0aGUgY29udGV4dCBvZiBmb29saW5nIGFyb3VuZCB3aXRoIGJpdHMgb2YgdHJpZ29ub21ldHJ5IGFuZCBudW1lcm9sb2d5LiBOb3RoaW5nIGJlYXRzIGEgZ29vZCAkXHBpJC4gIFlvdSBrbm93IHdoYXQgSSBnb2xkZW4gbWVhbi4NCg0KVGhvdWdoIGRpc3VzZSwgbGF6aW5lc3MgYW5kIHNsb3RoIEkgb2Z0ZW4gZm9yZ2V0IFIgY29uY2VwdHMgYW5kIG1ldGhvZHMsIHNvIEkgbWVzcyBhYm91dCB3aGVuIHRoZSB3ZWF0aGVyIGlzIGJhZC4gSSBhaW0gZm9yIGFkIGhvYyBjbGFyaXR5IGluIG15IGNvZGUgcmF0aGVyIHRoYW4gZ2VuZXJhbGl6YXRpb24uICBUaGlzIG1ha2VzIGl0IGEgbGl0dGxlIGVhc2llciBmb3IgbWUgdG8gcmVtZW1iZXIgd2hhdCBJIGRpZC4gIFRoZXJlIGlzIG5vdGhpbmcgbGlrZSBwaWNraW5nIHVwIHlvdXIgb2xkIGNvZGUgYW5kIGhhdmluZyB0byByZXZlcnNlIGVuZ2luZWVyIHRoZSB0aGluZy4gIEV2ZW4gbW9yZSBob3JyaWQgZm9yIHNvbWVvbmUgZWxzZS4gIEF0IGxlYXN0IGl0IGFpbnQgUGVybC4gT3IgU0FTLg0KDQojIyMgU2V0IHVwIHRoZSBSIGVudmlyb25tZW50DQpUaGUgbGlicmFyaWVzIHVzZWQuDQpgYGB7ciwgcmVzdWx0cyA9IEZBTFNFfQ0KcmVxdWlyZSh0aWR5dmVyc2UpICAgICMgZ3JhcGhpbmcsIGRhdGEgbWFuaXB1bGF0aW9uLCBwaXBpbmcsIGV0Yy4gaW5kaXNwZW5zYWJsZSENCnJlcXVpcmUoTUFTUykgICAgICAgICAjIGNvbnZlcnRpbmcgZGVjaW1hbCBmcmFjdGlvbnMgdG8gY29tbW9uIGZyYWN0aW9ucw0KcmVxdWlyZShwbG90bHkpICAgICAgICMgYW5pbWF0aW5nIGdyYXBocw0KYGBgDQoNCiMjIENpcmNsaW5nIHBvaW50cw0KVGhlIHJhdGlvIG9mIGFueSBjaXJjbGUncyBjaXJjdW1mZXJlbmNlICRDJCB0byBpdHMgZGlhbWV0ZXIgJGQkIGlzICRccGkkLCBhbiBpcnJhdGlvbmFsIG51bWJlciB3aXRoIHRoZSBhcHByb3hpbWF0ZSB2YWx1ZSBvZiAkMy4xNDE1OTI2NTM1ODk3OTMxJC4gICRccGkgPSBcZnJhY3tDfXtkfSQuICBTaW5jZSAkZCQgaXMgZXF1YWwgdG8gdHdpY2UgdGhlIGNpcmNsZSdzIHJhZGl1cyAkciQsICRccGkgPSBcZnJhY3tDfXsycn0kDQoNClRoZSBlcXVhdGlvbiBjYW4gYmUgcmVhcnJhbmdlZCB0byBjYWxjdWxhdGUgJEMkIGZyb20gJHIkIGFuZCAkciQgZnJvbSAkQyQuICRDID0gMlxwaSByJCwgJHIgPSBcZnJhY3tDfXsyXHBpfSQuICANCg0KVGhlIHJlbGF0aW9uc2hpcCBpcyB1c2VkIHRvIGRlZmluZSBhbmdsZXMuICBUaGUgY2lyY3VtZmVyZW5jZSBvZiB0aGUgY2lyY2xlIGlzIGEgc2VyaWVzIG9mIGFyY3Mgb2YgbGVuZ3RoICRyJC4gIFRoZSBsZW5ndGggb2YgdGhlIGVudGlyZSBjaXJjbGUgKGllLiAkQyQpIGlzICQyXHBpJCB0aW1lcyAkciQuICBJbiBhZGRpdGlvbiB0byBiZWluZyB0aGUgcmFkaXVzIG9mIHRoZSBjaXJjbGUsICRyJCBtZWFzdXJlcyB0aGUgbGVuZ3RoIG9mIGFuIGFyYyBvbiB0aGUgY2lyY2xlLiAgQW4gYXJjIGxlbmd0aCAkciQgZGVmaW5lcyBhbiBhbmdsZSBmb3JtZWQgYnkgdGhlIHJhZGl1cyBpbnRlcnNlY3RpbmcgdGhlIGVuZCBvZiB0aGUgYXJjLiBUaGlzIGFuZ2xlIGlzIHRoZSB1bml0IHJhZGlhbiAkcmFkJC4NCg0KQW4gYW5nbGUgJFx0aGV0YSQgaXMgdGhlIHJhdGlvbiBvZiB0aGUgbGVuZ3RoIG9mIHRoZSBhcmMgJHMkIHRvIHRoZSByYWRpdXMgJHIkOiAkXHRoZXRhID0gXGZyYWN7c317cn0kLiAgQXMgYSBjaXJjbGUgZ3Jvd3Mgb3IgY29udHJhY3RzLCAkciQgYW5kICRzJCBhbHNvIGdyb3cgYW5kIGNvbnRyYWN0LiAgQW4gYW5nbGUgZGVmaW5lZCBieSBhIHJhZGlhbiBvbiBhbnkgc2l6ZWQgY2lyY2xlIGlzIGFsd2F5cyB0aGUgc2FtZSB2YWx1ZSBvZiAkXHRoZXRhJC4gIA0KDQokJFxmcmFje3NcIHVuaXRzfXtyXCB1bml0c30gPSBcZnJhY3tzfXtyfSA9IFx0aGV0YSQkDQpUaGUgdW5pdHMgY2FuY2VsIG91dCwgbWFraW5nICRcdGhldGEkIGRpbWVuc2lvbmxlc3MuICANCg0KVHJhdmVsaW5nIGFyb3VuZCB0aGUgY2lyY3VtZmVyZW5jZSBvZiB0aGUgY2lyY2xlIHJlcXVpcmVzICQyXHBpXCByYWQkLCBzbyB2YWx1ZXMgb2YgJFx0aGV0YSQgdmFyaWVzIGZyb20gMCB0byAkMlxwaSQgYXJvdW5kIHRoYXQgcGF0aC4gIEhhbGYgd2F5IGFyb3VuZCB0aGUgY2lyY2xlICRcdGhldGEgPSBccGkkLiAgQSBxdWFydGVyIHdheSBhcm91bmQgdGhlIGNpcmNsZSAkXHRoZXRhID0gXGZyYWN7XHBpfXsyfSQsIGFuZCBhbiBlaWdodGggYXJvdW5kIHRoZSBjaXJjbGUgJFx0aGV0YSA9IFxmcmFje1xwaX17NH0kLiAgVGhlc2UgdmFsdWVzIGNvcnJlc3BvbmQgdG8gJDM2MF5cY2lyYyQsICQxODBeXGNpcmMkLCAkOTBeXGNpcmMkLCBhbmQgJDQ1XlxjaXJjJC4NCg0KIyMjIFBvbGFyIGNvb3JkaW5hdGVzDQpBIHBvbGFyIGNvb3JkaW5hdGUgZGVmaW5lcyBhIHBvaW50IGJ5IGRpc3RhbmNlIGZyb20gdGhlIGNlbnRlciBvZiB0aGUgY2lyY2xlICh0aGUgbGVuZ3RoIG9mICRyJCkgYW5kIGJ5IHRoZSBhbmdsZSwgJFx0aGV0YSQsIGZvcm1lZCBiZXR3ZWVuIHRoZSByYWRpdXMgYW5kIGEgYmFzZSBsb2NhdGlvbiAod2hlcmUgJFx0aGV0YSA9IDAkLiAgVGhlIHBvbGFyIGNvb3JkaW5hdGUgaXMgaW4gdGhlIGZvcm0gJChyLFx0aGV0YSkkLiANCg0KIyMjIFJlbGF0aW5nIHBvbGFyIHRvIENhcnRlc2lhbiBjb29yZGluYXRlcw0KVGhlIHRyaWdvbm9tZXRyaWMgZnVuY3Rpb25zICRjb3NpbmUkIGFuZCAkc2luZSQgY2FuIGJlIHVzZWQgdG8gY29udmVydCBwb2xhciBjb29yZGluYXRlcyB0byAkKHgseSkkIENhcnRlc2lhbiBjb29yZGluYXRlcy4NCiQkeCA9IHJcdGltZXMgY29zKFx0aGV0YSkkJCANCiQkeSA9IHJcdGltZXMgc2luKFx0aGV0YSkkJA0KDQoNCiMjIyBQbG90dGluZyBwb2ludHMgb24gdGhlIGNpcmNsZQ0KSGVyZSBwb2ludHMgb24gYSB1bml0IGNpcmNsZSBhcmUgZ2VuZXJhdGVkIGFuZCBncmFwaGVkIG9uIGEgcGxvdC4gIFdlIHdhbnQgcG9pbnRzIGFyb3VuZCB0aGUgZW50aXJlIGNpcmNsZSwgYnV0IHdlIHdhbnQgdGhlIHBvaW50cyBzZXBhcmF0ZWQgc28gdGhlIHBhdHRlcm4gaXMgY2xlYXIuICANCg0KRmlyc3Qgd2UgZ2VuZXJhdGUgYSBzZXJpZXMgb2YgZnJhY3Rpb25zIGZyb20gMCB0byAyIGluIHN0ZXBzIG9mIDEvOC4gIFRoZXNlIHdpbGwgYmUgbXVsdGlwbGllZCBieSAkcGkkIHRvIGNyZWF0ZSBhbmdsZXMgZnJvbSAkXGZyYWN7MX17OH1ccGkkIHRvICQyXHBpJCwgYSBjb21wbGV0ZSB0dXJuIGFyb3VuZCB0aGUgY2lyY2xlIGluIHN0ZXBzIG9mICRcZnJhY3sxfXs4fVxwaSQuIA0KDQpUaGVzZSBhcmUgbXVsdGlwbGllZCBieSAkXHBpJCB0byBnZW5lcmF0ZSBhIHNlcXVlbmNlIG9mIHZhbHVlcyBvZiAkXHRoZXRhJCBhcm91bmQgdGhlIGNpcmNsZS4gIFRoZSByYWRpdXMgb2YgdGhlIHVuaXQgY2lyY2xlICRyID0gMSQgaXMgY29tYmluZWQgd2l0aCB0aGUgYW5nbGVzIHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgb2YgcG9sYXIgY29vcmRpbmF0ZXMuDQpgYGB7cn0NCnIgPC0gMQ0Kc3RlcHMgPC0gc2VxKGZyb20gPSAwLCB0byA9IDIsIGJ5ID0gMS84KQ0KdGhldGFzIDwtIHN0ZXBzICogcGkNCnVuaXRfY2lyY2xlIDwtIGRhdGEuZnJhbWUoInIiID0gMSwgICAgICAgICAjIHRoZSB1bml0IGNpcmNsZSwgd2hlcmUgcmFkaXVzIGlzIDENCiAgICAgICAgICAgICAgICAgICAgICAgICAgInRoZXRhIiA9IHRoZXRhcw0KICAgICAgICAgICAgICAgICAgICAgICkNCnVuaXRfY2lyY2xlICAjIGEgY29sbGVjdGlvbiBvZiBwb2xhciBjb29yZGluYXRlcyBvbiBhIGNpcmNsZQ0KYGBgDQpUaGUgZGF0YSBmcmFtZSBpcyBleHBhbmRlZCB0byBhZGQgdGhlIENhcnRlc2lhbiBjb29yZGluYXRlIG9mIGVhY2ggb2YgdGhlIHBvbGFyIGNvb3JkaW5hdGVzLg0KYGBge3J9DQp1bml0X2NpcmNsZSR4IDwtIHVuaXRfY2lyY2xlJHIgKiBjb3ModW5pdF9jaXJjbGUkdGhldGEpDQp1bml0X2NpcmNsZSR5IDwtIHVuaXRfY2lyY2xlJHIgKiBzaW4odW5pdF9jaXJjbGUkdGhldGEpDQp1bml0X2NpcmNsZQ0KYGBgDQoNCiMjIyBHcmFwaCAocGxvdCkgdGhlIHBvaW50cyBvbiBhIGNpcmNsZQ0KVGhlIGRlZmluZWQgQ2FydGVzaWFuICQoeCx5KSQgY29vcmRpbmF0ZSBwb2ludHMgb2YgYHVuaXRfY2lyY2xlYCBhcmUgZ3JhcGhlZCBvbiBhIHBsb3QuDQoNCmBgYHtyfQ0KZ2dwbG90KHVuaXRfY2lyY2xlLCBhZXMoeCwgeSkpKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KV2UgY2FuIGRvIHNvbWUgdGhpbmdzIHRvIGNyZWF0ZSBsYWJlbHMgYXMgZnJhY3Rpb25zIG9mIHBpLg0KYGBge3J9DQpzdGVwc19hc2ZyYWN0aW9ucyA8LSBmcmFjdGlvbnMoc3RlcHMpDQp1bml0X2NpcmNsZSRsYWJlbCA8LSBwYXN0ZShhcy5jaGFyYWN0ZXIoc3RlcHNfYXNmcmFjdGlvbnMpLCAicGkiKQ0KdW5pdF9jaXJjbGUNCmBgYA0KDQoNCmBgYHtyfQ0KbGFiZWxfc2NhbGVyIDwtIDEuMTcgICAgIyB0aGlzIGlzIHVzZWQgdG8gbW92ZSB0aGUgbGFiZWwgbG9jYXRpb25zIGZ1cnRoZXIgb3V0LA0KICAgICAgICAgICAgICAgICAgICAgICAgIyBpLmUuLCBwdXRzIHRoZSBsYWJlbHMgb24gYSBzbGlnaHRseSBsYXJnZXIgY2ljbGUNCnVuaXRfY2lyY2xlJGxhYmVsX3ggPC0gbGFiZWxfc2NhbGVyICogdW5pdF9jaXJjbGUkeA0KdW5pdF9jaXJjbGUkbGFiZWxfeSA8LSBsYWJlbF9zY2FsZXIgKiB1bml0X2NpcmNsZSR5DQp1bml0X2NpcmNsZQ0KYGBgDQpOb3cgdGhlIGdyYXBoIGNhbiBiZSBkcmF3biB3aXRoIGxhYmVscyBmb3IgdGhlIGFuZ2xlcy4gIFRoZSBsYWJlbHMgaGVscCByZWxhdGUgcGkgKCRccGkkKSB0byBhbmdsZXMgaW4gZGVncmVlcy4gICQyXHBpID0gMzYwXlxjaXJjJCwgJFxwaT0xODBeXGNpcmMkLCAkXGZyYWN7XHBpfXsyfT05MF5cY2lyYyQsICRcZnJhY3tccGl9ezR9PTQ1XlxjaXJjJCwgJFxmcmFjezNccGl9ezJ9ID0gMjcwXlxjaXJjJC4NCmBgYHtyfQ0KZ2dwbG90KHVuaXRfY2lyY2xlLCBhZXMoeCwgeSkpKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArDQogIGdlb21fdGV4dChhZXMoeD1sYWJlbF94LA0KICAgICAgICAgICAgICAgIHk9bGFiZWxfeSwNCiAgICAgICAgICAgICAgICBsYWJlbCA9IGxhYmVsKSwNCiAgICAgICAgICAgIHNpemUgPSA0DQogICAgICAgICAgICApICsNCiAgY29vcmRfZml4ZWQoKQ0KYGBgDQojIyBDaGFuZ2luZyBjaXJjbGVzDQoNCiMjIyBDaGFuZ2luZyBwb3NpdGlvbg0KDQpBIGNpcmNsZSdzIHBvc2l0aW9uIGlzIGNoYW5nZWQgYnkgYWRkaW5nIG9yIHN1YnRyYWN0aW5nIGEgY29uc3RhbnQgdmFsdWUgdG8gdGhlIHggYW5kIHkgY29vcmRpbmF0ZXMuDQoNCmBgYHtyfQ0Kc3RlcHMgPC0gc2VxKGZyb20gPSAwLCB0byA9IDIsIGJ5ID0gMS8zMikNCnRoZXRhcyA8LSBzdGVwcyAqIHBpDQp1bml0X2NpcmNsZSA8LSBkYXRhLmZyYW1lKHIgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB0aGV0YSA9IHRoZXRhcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEgKiBjb3ModGhldGFzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IDEgKiBzaW4odGhldGFzKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICApDQoNCnVuaXRfcGxvdCA8LSBnZ3Bsb3QodW5pdF9jaXJjbGUsYWVzKHgsIHkpKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiIzc1NzBiMyIpICsNCiAgeGxpbSgtNCwgNCkgKw0KICB5bGltKC00LCA0KSArDQogIGNvb3JkX2ZpeGVkKCkNCg0KdW5pdF9wbG90ICAjIGRpc3BsYXkgdGhlIHBsb3QNCmBgYA0KVGhlIHVuaXQgY2lyY2xlIGlzIG1vdmVkIG9uIHRoZSBwbG90IGJ5IGFkanVzdGluZyBpdHMgY29vcmRpbmF0ZXMgYnkgYWRkaXRpb24gb3Igc3Vic3RyYWN0aW9uLiBIZXJlIHRoZSBjaXJjbGUgaXMgbW92ZWQgdGhyZWUgdW5pdHMgcmlnaHQgYW5kIHRocmVlIHVuaXRzIGxlZnQuDQpgYGB7cn0NCmdncGxvdCh1bml0X2NpcmNsZSwgDQogICAgICAgYWVzKHggKyAzLCB5ICsgMykpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjNzU3MGIzIikgKw0KICB4bGltKC00LCA0KSArDQogIHlsaW0oLTQsIDQpICsNCiAgY29vcmRfZml4ZWQoKQ0KYGBgDQojIyMgU2hvdyB0aGUgY2lyY2xlIGF0IG11bHRpcGxlIG5ldyBwb3NpdGlvbnMgYXQgb25jZQ0KVGhlIGB1bml0X3Bsb3RgIGdyYXBoIGlzIGJ1aWx0IG9uIGJ5IGFkZGluZyBuZXcgdW5pdCBjaXJjbGVzIHNoaWZ0ZWQgdG8gdmFyaW91cyBsb2NhdGlvbnMgb24gdGhlIHBsYW5lLg0KYGBge3J9DQp1bml0X3Bsb3QgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB4bGltKC00LCA0KSArDQogIHlsaW0oLTQsIDQpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHggKyAzLCB5ID0geSksICAjIGNpcmNsZSB0byByaWdodCBieSBhZGRpbmcgdG8geCBjb29yZGluYXRlcw0KICAgICAgICAgICAgIGNvbG9yID0gIiMxYjllNzciKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSB4LCB5ID0geSArIDMpLCAgIyBjaXJjbGUgdXAgYnkgYWRkaW5nIHRvIHkgY29vcmRpbmF0ZXMNCiAgICAgICAgICAgICBjb2xvciA9ICIjMWI5ZTc3IikgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0geCArIDMsIHkgPSB5ICsgMyksICAjIGNpcmNsZSByaWdodCBhbmQgdXANCiAgICAgICAgICAgICBjb2xvciA9ICIjMWI5ZTc3IikgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0geCAtIDMsIHkgPSB5IC0gMyksICAjIGNpcmNsZSBkb3duIGFuZCBsZWZ0DQogICAgICAgICAgICAgY29sb3IgPSAiIzFiOWU3NyIpDQpgYGANCg0KIyMjIENoYW5naW5nIHNpemUNCg0KVGhlIHVuaXQgY2lyY2xlIGlzIHVzZWQgYXMgYSB0ZW1wbGF0ZSBmb3IgY2lyY2xlcyBvciBvdGhlciBzaXplLiAgVGhlIHBsb3QgYXNzaWduZWQgdG8gYHVuaXRfcGxvdGAgaXMgYnVpbHQgb24gYnkgYWRkaW5nIG5ldyBzZXRzIG9mIHgseSBjb29yZGluYXRlcy4gIFRoZSBuZXcgY29vcmRpbmF0ZSBzZXRzIGFyZSBjcmVhdGVkIGJ5IHNjYWxpbmcgdGhlIG9yaWdpbmFsIHVuaXQgY2lyY2xlIGNvb3JkaW5hdGVzLCB3aGljaCBhcmUgcGFydCBvZiBgdW5pdF9wbG90YC4NCmBgYHtyfQ0KdW5pdF9wbG90ICsgIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB4bGltKC00LCA0KSArDQogIHlsaW0oLTQsIDQpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IDIgKiB4LCB5ID0gMiAqIHkpLCAgICAgICMgYWRkIGEgYmlnZ2VyIGNpcmNsZQ0KICAgICAgICAgICAgIGNvbG9yID0gIiNkOTVmMDIiKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSB4IC8gMiwgeSA9IHkgLyAyKSwgICAgICAjIGFkZCBhIHNtYWxsZXIgY2lyY2xlDQogICAgICAgICAgICAgY29sb3IgPSAiI2Q5NWYwMiIpDQpgYGANCg0KIyMjIERpc3RvcnQNCk11bHRpcGxpY2F0aW9uIGFuZCBkaXZpc2lvbiBhcmUgY2hhbmdlcyB0byB0aGUgZGlzdGFuY2UgZnJvbSB0aGUgY2VudGVyIG9mIHRoZSBwbG90OiBjaGFuZ2VzIHRvIHRoZSByYWRpdXMuICANCg0KYGBge3J9DQp2YXJyIDwtICgxOm5yb3codW5pdF9jaXJjbGUpKQ0KdW5pdF9wbG90ICsNCiAgeGxpbSgtNCwgNCkgKw0KICB5bGltKC00LCA0KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSAyKngsIHkpLCAgIyBleHBhbmQgb24gdGhlIHggYXhpcw0KICAgICAgICAgICAgIGNvbG9yID0gIiNkOTVmMDIiKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSB4LCAyKnkpLCAgIyBleHBhbmQgb24gdGhlIHggYXhpcw0KICAgICAgICAgICAgIGNvbG9yID0gIiNkOTVmMDIiKQ0KYGBgDQoNCiMjIyBDb25jZW50cmljIGNpcmNsZXMNCkNvbmNlbnRyaWMgY2lyY2xlcyBzaGFyZSB0aGUgc2FtZSBjZW50ZXIuICBQb2ludHMgb24gY29uY2VudHJpYyBjaXJjbGVzIGNhbiBiZSBnZW5lcmF0aW5nIGJ5IGNvbWJpbmluZyBhIGNvbGxlY3Rpb24gb2YgcmFkaWkgb2YgZGlmZmVyZW50IGxlbmd0aCB3aXRoIGFuZ2xlcyBhcm91bmQgdGhlIGNpcmNsZS4NCg0KVGhlIENhcnRlc2lhbiBjcm9zcyBwcm9kdWN0IGlzIGFuIG91dGNvbWUgb2YgY29tYmluaW5nIGFsbCBtZW1iZXJzIG9mIG9uZSBzZXQgd2l0aCBhbGwgbWVtYmVycyBvZiBhbm90aGVyIHNldC4gIEEgY3Jvc3MgcHJvZHVjdCBvZiBhIHNldCBvZiByYWRpaSBhbmQgYSBzZXQgb2YgYW5nbGVzIGNyZWF0ZXMgbXVsdGlwbGUgcG9sYXIgY29vcmRpbmF0ZXMuDQpgYGB7cn0NCiMgIGNyZWF0ZSBhIGNvbGxlY3Rpb24gb2YgcmFkaWkgc3RhcnRpbmcgZnJvbSB0aGUgMCBvcmlnaW4NCnIgPSBzZXEoZnJvbSA9IDAsIHRvID0gNSwgYnkgPSAxKQ0KDQoNCiMgIGNyZWF0ZSBhIHNldCBvZiBhbmdsZXMgZnJvbSAwIHRvIHBpIChlcXVpdmFsZW50IHRvIDAgdG8gMTgwIGRlZ3JlZXMpDQp0aGV0YSA9IHNlcShmcm9tID0gMCwgdG8gPSAyLCBieSA9IDEvNikgKiBwaQ0KDQoNCiMgIFRoZSBhcHBsaWNhdGlvbiBvZiB0aGUgZnVuY3Rpb24gY3Jvc3NpbmcgbWFwcyBlYWNoIHJhZGl1cyANCiMgKHRoZXJlIGFyZSA2IHJhZGlpIGluIHRoZSBzZXQpIHRvIA0KIyAgYWxsIHJvd3MgaW4gdGhlIGFuZ2xlcyBkYXRhIGZyYW1lICh0aGVyZSBhcmUgOSByb3dzKS4gIA0KIyBUaGUgcHJvZHVjdCBoYXMgNTQgcm93cy4gDQojICBFYWNoIHJvdyBkZWZpbmVzIGEgcG9sYXIgY29vcmRpbmF0ZSAocix0aGV0YSkNCmhhbGZfY2lyY2xlcyA8LSBjcm9zc2luZyhyLCB0aGV0YSkNCmhhbGZfY2lyY2xlcw0KDQoNCiMgIENvbnZlcnQgcG9sYXIgY29vcmRpbmF0ZXMgdG8gQ2FydGVzaWFuIGNvb3JkaW5hdGVzIGZvciBwbG90dGluZyBpbiB4LHkNCmhhbGZfY2lyY2xlcyR4IDwtIGhhbGZfY2lyY2xlcyRyICogY29zKGhhbGZfY2lyY2xlcyR0aGV0YSkNCmhhbGZfY2lyY2xlcyR5IDwtIGhhbGZfY2lyY2xlcyRyICogc2luKGhhbGZfY2lyY2xlcyR0aGV0YSkgDQoNCiMgIEFkZCB0aGUgZXF1aXZhbGVudCBhbmdsZSBpbiBkZWdyZWVzIHRvIGVhY2ggdGhldGENCmhhbGZfY2lyY2xlcyRkZWdyZWVzIDwtIGhhbGZfY2lyY2xlcyR0aGV0YSAqIDE4MCAvIHBpDQpoYWxmX2NpcmNsZXMNCg0KYGBgDQpQbG90IHRoZSBwb2ludHMuICBUaGUgcG9pbnQgaXMgbGFiZWxlZCB3aXRoIHRoZSByYWRpdXMgb2YgaXRzIHBvbGFyIGNvb3JkaW5hdGUuICBUaGUgcG9pbnQgaXMgY29sb3JlZCBieSB0aGUgZXF1aXZhbGVudCBkZWdyZWVzIG9mIHRoZXRhLiANCg0KYGBge3J9DQpnZ3Bsb3QoaGFsZl9jaXJjbGVzLCBhZXMoeCwgeSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhcy5mYWN0b3IoZGVncmVlcykpKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByLCBjb2xvciA9IGFzLmZhY3RvcihkZWdyZWVzKSksIG51ZGdlX3kgPSAwLjMpICsNCiAgZ2VvbV9wYXRoKGFlcyhjb2xvcj0gYXMuZmFjdG9yKHIpKSkgKw0KICBnZW9tX3BhdGgoYWVzKGNvbG9yPSBhcy5mYWN0b3IoZGVncmVlcykpKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KDQoNCg0KDQojIyMgU3BpcmFsDQpBIHNwaXJhbCBpcyBjcmVhdGVkIGJ5IGluY3JlYXNpbmcgdGhlIHJhZGl1cyBmb3IgZWFjaCBwb2ludCBnZW5lcmF0ZWQuICBVc2luZyBgYGBzZXFfYWxvbmcodGhldGEpYGBgIGNyZWF0ZXMgYSB2ZWN0b3IgZnJvbSAxIHRvIHRoZSBudW1iZXIgdGhhdCBpcyB0aGUgbGVuZ3RoIG9mIHRoZXRhIGluIGluY3JlbWVudHMgb2YgMS4gIFdlIHdhbnQgdGhlIHNwcmlyYWwgdG8gc3RhcnQgYXQgdGhlIG9yaWdpbiwgc28gMSBpcyBzdWJ0cmFjdGVkIGZyb20gdGhlIGdlbmVyYXRlZCB2ZWN0b3IsIHdoaWNoIGVmZWN0aXZlbHkgc2hpZnRzIHRoZSBzZXF1ZW5jZSB0byB0aGUgbGVmdCBieSAxLg0KYGBge3J9DQp0aGV0YSA8LSBzZXEoMCwgNipwaSwgMS80KSAgIyB0aHJlZSB0aW1lcyBhcm91bmQgaW4gMS80IHN0ZXBzDQpyIDwtIHNlcV9hbG9uZyh0aGV0YSkgICAgICAgIyBldmVyIGluY3JlYXNpbmcgcmFkaXVzDQpyIDwtIHIgLSAxICAgICAgICAgICAgICAgICAgIyBzaGlmdCB0byBzdGFydCBhdCAwDQoNCnNwaXJhbCA8LSBkYXRhLmZyYW1lKHIgPSByLA0KICAgICAgICAgICAgICAgICAgICAgdGhldGEgPSB0aGV0YSkNCiMgc2hvdyB0aGUgcG9sYXIgY29vcmRpbmF0ZXMNCnNwaXJhbA0KDQojIGRlcml2ZSB0aGUgY2FydGVzaWFuIGNvb3JkaW5hdGVzDQpzcGlyYWwkeCA8LSByICogY29zKHRoZXRhKQ0Kc3BpcmFsJHkgPC0gciAqIHNpbih0aGV0YSkNCg0KIyBwbG90IHRoZSBzcGlyYWwNCmdncGxvdChzcGlyYWwsIGFlcyh4LCB5KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiNkOTVmMDIiKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KDQojIyBBbmltYXRpbmcgdGhlIHNwaXJhbCwgYSBiaXQgb2YgYSB0YW5nZW50DQpGdW4gd2l0aCBzcGlyYWwgYW5pbWF0aW9uLiAgRm9jdXMgaXMgb24gdGhlIGNyZWF0aW9uIG9mIHBsb3QgZnJhbWVzLg0KDQpSYXRoZXIgdGhhbiByZW5kZXJpbmcgdGhlIHBvaW50cyBhbGwgYXQgb25jZSwgdGhlIHBsb3Qgb2YgdGhlIHNwaXJhbCBjYW4gYmUgYW5pbWF0ZWQgdXNpbmcgYGBgZ2dwbG90bHkoKWBgYCBmcm9tIHRoZSBwYWNrYWdlIGBgYHBsb3RseWBgYC4gIFRoaXMgZnVuY3Rpb24gYW5pbWF0ZXMgdGhlIHBsb3QgYnkgZHJhd2luZyBhIHNlcmllcyBvZiBwbG90IGZyYW1lcywgd2hpY2ggYXJlIGlkZW50aWZpZWQgaW4gdGhlIGRhdGEgZnJhbWUuDQoNClRoZSBtZWNoYW5pY3Mgb2YgZ2VuZXJhdGluZyB0aGUgcG9pbnRzIG9mIGEgc3BpcmFsIGd1YXJhbnRlZXMgdGhhdCB0aGUgZGF0YSBmcmFtZSBpcyBzZXF1ZW5jZWQgYnkgcmFkaXVzLiAgVGhpcyBtYWtlcyBpdCBwb3NzaWJsZSB0byByZWFzb24gYWJvdXQgdGhlIHByb2Nlc3MuICBXaXRob3V0IHRoZSBndWFyYW50ZWUsIHNvbWUgZXh0cmEgd29yayAobGF6aWx5IGFuZCBzbG92ZW5seSBsZWZ0IG91dCBvZiB0aGlzIHdvcmspIG5lZWRzIHRvIGJlIGRvbmUuICBEb24ndCBnZXQgY29tcGxhY2VudC4NCg0KIyMjIFRoZSBzcGlyYWwgYXMgYSBtb3ZpbmcgcG9pbnQNCkluIHRoaXMgc2VjdGlvbiwgZWFjaCBwbG90IGZyYW1lIGlkZW50aWZpZXMgYSBzaW5nbGUgcG9pbnQuICBUaGUgcmFkaXVzIG9mIHRoZSBwb2ludCBpcyB1c2VkIGFzIHRoZSBwbG90IGZyYW1lIHZhcmlhYmxlLiAgVGhlIG1lY2hhbmljcyBvZiB0aGUgZ2VuZXJhdGlvbiBvZiB0aGUgcG9pbnRzIG1ha2luZyAgDQoNCk5vdGUgdGhhdCBlYWNoIGZyYW1lICh3aGljaCBpcyBhIHBvaW50IGluZGV4ZWQgYnkgYGBgc3BpcmFsJHJgYGApIGlzIGEgbmV3IHBsb3QuICBBcyB0aGUgYW5pbWF0aW9uIHBsYXlzLCBhIHNpbmdsZSBwb2ludCBzZWVtcyB0byBtb3ZlIG9uIHRoZSBwYXRoIG9mIHRoZSBzcGlyYWwuICBJbiByZWFsaXR5LCBlYWNoIHBvaW50IGlzIHNlcGFyYXRlIGZyb20gdGhlIG90aGVycy4gIFRoZSBwcmVjZWRpbmcgcGxvdHMgYXJlIHJlcGxhY2VkLCBub3QgYWRkZWQgdG8uDQpgYGB7cn0NCmdncGxvdGx5KCAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG5ldyBmdW5jdGlvbg0KICBnZ3Bsb3Qoc3BpcmFsLCBhZXMoeCwgeSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV9wb2ludChhZXMoZnJhbWUgPSByKSwgICAgICAgICMgdGhlIGZyYW1lIHZhcmlhYmxlLCB3aGljaCBpcyBwYXJ0IG9mIHNwaXJhbA0KICAgICAgICAgICAgIGNvbG9yID0gIiNkOTVmMDIiKSArDQogIGNvb3JkX2ZpeGVkKCkNCikNCmBgYA0KDQoNCg0KIyMjIFRoZSBncm93aW5nIHNwaXJhbA0KUmF0aGVyIHRoYW4gYW4gYXBwYXJlbnRseSBtb3ZpbmcgcG9pbnQgb24gYSBzcGlyYWwsIEkgd2FudCB0byBzaG93IGEgZ3Jvd2luZyBzcGlyYWwgYnkgcmV0YWluaW5nIHJhdGhlciB0aGFuIGxvc2luZyBwcmV2aW91cyBwb2ludHMuICANCg0KSW4gdGhlIHZlcnNpb24gb2YgYGBgcGxvdGx5YGBgIHVzZWQsIHJldGFpbmluZyBwb2ludHMgdGhyb3VnaG91dCB0aGUgYW5pbWF0aW9uIGlzIG5vdCBhbiBvcHRpb24uICBXZSBoYXZlIHRvIHBlcmZvcm0gYSBiaXQgb2YgZmluYWdsaW5nLg0KDQpEdXJpbmcgYW5pbWF0aW9uLCBlYWNoIGZyYW1lIHJlcGxhY2VzIHRoZSBuZXh0LiAgUmV0YWluaW5nIHBsb3R0ZWQgcG9pbnRzIHJlcXVpcmVzIHRoYXQgZWFjaCBmcmFtZSBoYXMgaXRzIG93biBwb2ludCBhbmQgYWxsIG9mIHRoZSBwcmVjZWRpbmcgcG9pbnRzLg0KDQpUaGUgcHJvY2VzczoNCg0KMS4gY3JlYXRlIGEgdmVjdG9yLCBgYGBmcmFtZW51bWJlcnNgYGAsIHRoYXQgaW5kZXhlcyBlYWNoIHJvdyBpbiBgYGBzcGlyYWxgYGANCjEuIGl0ZXJhdGUgb3ZlciBgYGBmcmFtZW51bWJlcnNgYGAgY2FwdHVyaW5nIGVhY2ggZWxlbWVudCBhcyBgYGBmcmFtZW51bWJlcmBgYA0KMS4gYXQgZWFjaCwgY29weSB0aGUgcm93cyBvZiBgYGBzcGlyYWxgYGAgZnJvbSAxIHRvIGBgYGZyYW1lbnVtYmVyYGBgLCBhZGQgYSBuZXcgY29sdW1uLCBgYGBwbG90ZnJhbWVgYGAgdG8gdGhlIGNvcGllZCByb3dzIGFuZCBzZXQgaXQgdG8gYGBgZnJhbWVudW1iZXJgYGAsIGFuZCBhZGQgdGhlIGNvcGllZCByb3dzIGFzIGEgZGF0YSBmcmFtZSBpdGVtIHRvIGEgZ3Jvd2luZyBsaXN0DQoxLiB3aGVuIGNvbXBsZXRlLCBjb2xsYXBzZSB0aGUgbGlzdCBvZiBkYXRhIGZyYW1lIGl0ZW1zIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQ0KDQpUaGUgbmV3IGRhdGEgZnJhbWUgY29udGFpbnMgcGxvdCBmcmFtZXMgd2l0aCBwcm9ncmVzc2l2ZWx5IG1vcmUgcm93cy4gIFRoZSBwbG90IGZyYW1lIHJvd3MgYXJlIGlkZW50aWZpZWQgaW4gdGhlIG5ldyBkYXRhIGZyYW1lIGFzIGBgYHBsb3RmcmFtZWBgYCwgd2hpY2ggaXMgdXNlZCB0byBzdGVwIHRocm91Z2ggdGhlIGRhdGEgdG8gYW5pbWF0ZSB0aGUgcGxvdCB1c2luZyB0aGUgZnVuY3Rpb24gYGBgZ2dwbG90bHlgYGAuICANCg0KYGBge3J9DQpmcmFtZW51bWJlcnMgPC0gc2VxX2Fsb25nKHNwaXJhbCRyKQ0KDQpsaXN0b2ZleHBhbmRlZGZyYW1lcyA8LSANCiAgbGFwcGx5KGZyYW1lbnVtYmVycywgICAgICAgICAgICMgZWxlbWVudHMgb2YgZnJhbWVudW1iZXJzIGFyZSBmZWQgb25lIGF0IGEgdGltZSB0byB0aGUgZnVuY3Rpb24NCiAgICAgICAgIGZ1bmN0aW9uKGZyYW1lbnVtYmVyKXsNCiAgICAgICAgICAgY2JpbmQoDQogICAgICAgICAgICAgc3BpcmFsW2MoMTpmcmFtZW51bWJlciksIF0sDQogICAgICAgICAgICAgcGxvdGZyYW1lID0gZnJhbWVudW1iZXINCiAgICAgICAgICAgKQ0KICAgICAgICAgfSkNCg0KZ3Jvd2luZ3NwaXJhbCA8LSBkcGx5cjo6YmluZF9yb3dzKGxpc3RvZmV4cGFuZGVkZnJhbWVzKSANCg0KaGVhZChncm93aW5nc3BpcmFsLDIwKQ0KDQpnZ3Bsb3RseSgNCiAgZ2dwbG90KGdyb3dpbmdzcGlyYWwsIGFlcyh4LCB5KSkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgZ2VvbV9wb2ludChhZXMoZnJhbWUgPSBwbG90ZnJhbWUpLCANCiAgICAgICAgICAgICAgIGNvbG9yID0gIiNkOTVmMDIiKSArDQogICAgY29vcmRfZml4ZWQoKQ0KKQ0KYGBgDQoNCg0KIyMgU3VuZmxvd2VyIGhlYWQgcGF0dGVybnMNClNlZSBodHRwczovL2FyY2hpdmUuYnJpZGdlc21hdGhhcnQub3JnLzIwMTAvYnJpZGdlczIwMTAtNDgzLnBkZiBhbmQgaHR0cHM6Ly9tYXRoLm9rc3RhdGUuZWR1L3Blb3BsZS9zZWdlcm1hbi90YWxrcy9zdW5mbG93ZXJfc3BpcmFsX3RhbGsucGRmIGZvciBhIGRpc2N1c3Npb24gb24gdGhlIHN1bmZsb3dlciBzcGlyYWwuICBUaGUgYWxnb3JpdGhtcyBjYXB0dXJlZCBoZXJlIGFyZSBmcm9tIHRoZXNlIHNvdXJjZXMuDQoNCiMjIyBUcmlwcyBhcm91bmQgdGhlIGNpcmNsZQ0KQSBzZXQgb2YgYW5nbGVzIGNyZWF0ZWQgYnkgbXVsdGlwbHlpbmcgYW4gYW5nbGUgYnkgYSBzZXQgb2YgaW50ZWdlcnMgcmVzdWx0cyBpbiBhIHNldCBvZiBjb3Rlcm1pbmFsIGFuZ2xlcy4gIEluIHRoZSBjYXNlIGJlbG93LCB0aGUgYW5nbGUgaXMgJDJccGkkIGFuZCB0aGUgaW50ZWdlcnMgYXJlIHRoZSBzZXQgZnJvbSAxIHRvIDUwLiAgJDJccGkkIGlzIGVxdWFsIHRvICQzNjBeXGNpcmMkLiAgQWx0aG91Z2ggdGhlIGZpZnR5IGFuZ2xlcyBhcmUgYWxsIGRpZmZlcmVudCBhbmdsZXMsIHRoZXkgYWxsIHRlcm1pbmF0ZSB3aXRoIHRoZSBzYW1lIHZlcnRleCwgYnV0IHdpdGggZGlmZmVyZW50IHRyaXBzIGFyb3VuZCB0aGUgY2lyY2xlIHRvIGdldCB0aGVyZS4NCg0KVGUgY290ZXJtaW5hbCBhbmdsZXMgYXJlIGxhdGVyIHRyYW5zZm9ybWVkIHN1Y2ggdGhhdCB0aGV5IGFyZSBubyBsb25nZXIgY290ZXJtaW5hbC4NCg0KVGhlIHJhZGlpIGFyZSB0cmFuc2Zvcm1lZCBieSB0aGUgc3F1YXJlIHJvb3QgZnVuY3Rpb24uICBUaGUgaGFzIHRoZSBlZmZlY3Qgb2YgbWFraW5nIHRoZSBwb2ludHMgbW9yZSBldmVubHkgZGlzdHJpYnV0ZWQuICBUYWtpbmcgdGhlIHNxdWFyZSByb290IGhhcyBtb3JlIGltcGFjdCB0byBsYXJnZSBudW1iZXJzIHRoYW4gdG8gc21hbGwuICBGb3IgZXhhbXBsZSwgdGhlIHNxdWFyZSByb290IG9mIDEwMCBpcyAxMCBhbmQgdGhlIHNxdWFyZSByb290IG9mIDEgaXMgMS4gUmVwbGFjaW5nIHRoZSByYWRpaSBvZiAxIHRvIG4gd2l0aCB0aGUgc3F1YXJlIHJvb3Qgb2YgMSB0byBuIG1ha2VzIHRoZSByYWRpaSBtb3JlIHVuaWZvcm1seSBkaXN0cmlidXRlZCBpbiBzcGFjZSwgcmVzdWx0aW5nIGluIGJldHRlciBwb2ludCBzcGFjaW5nIHdoZW4gdGhlIHBvaW50cyBhcmUgcGxvdHRlZC4NCmBgYHtyfQ0KbiA8LSBzZXEoMSw1MCkNCg0KdGhldGEgPC0gMipwaSAqIG4NCg0KciA8LSBzcXJ0KG4pDQoNCnN1bmZsb3dlciA8LSBkYXRhLmZyYW1lKG4gPSBuLA0KICAgICAgICAgICAgICAgICAgICAgICAgciA9IHIsDQogICAgICAgICAgICAgICAgICAgICAgICB0aGV0YSA9IHRoZXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgdHJpcHMgPSB0aGV0YSAvICgyICogcGkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSByICogY29zKHRoZXRhKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByICogc2luKHRoZXRhKQ0KICAgICAgICAgICAgICAgICAgICkNCnN1bmZsb3dlcg0KDQpnZ3Bsb3Qoc3VuZmxvd2VyLCBhZXMoeCwgcm91bmQoeSkpKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiNkOTVmMDIiKSArDQogIGdndGl0bGUoIkNvdGVybWluYWwgYW5nbGVzIHdpdGggZGlmZmVyZW50IHJhZGl1cyBsZW5ndGhzIikNCmBgYA0KIyMjIEhlbnJ5IFNlZ2VybWFuJ3MgbW9kZWwNCk1vcmUgaW50ZXJlc3RpbmcgaXMgd2hlbiB0aGV0YSBpcyBhZGp1c3RlZCBieSB0aGUgZ29sZGVuIHJhdGlvLiAgVGhpcyBjaGFuZ2VzIGVhY2ggYW5nbGUgdGhldGEgYnkgbXVsdGlwbHlpbmcgdGhldGEgYnkgcGhpLCB0aGUgZ29sZGVuIHJhdGlvLiAgDQokJFx0aGV0YSA9IFx0aGV0YSAqIFxwaGkkJA0KJCRccGhpID0gXGZyYWN7MSArIFxzcXJ0IDV9ezJ9IFxhcHByb3ggMS42MTgwMzQkJA0KQWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uLCB0aGUgYW5nbGVzIGFyZSBtdWx0aXBsZXMgb2YgdGhlIGdvbGRlbiByYXRpby4gIFRoaXMgaGF2ZSBiZWVuIGZvdW5kIHRvIGhhdmUgYW4gYXJyYW5nZW1lbnQgZm91bmQgaW4gbmF0dXJlLCBpbmNsdWRpbmcgdGhlIHdheSBmbG9yZXRzIGFuZCBsYXRlciBzZWVkcyBhcmUgcGFja2VkIGludG8gdGhlIGhlYWQgb2YgYSBzdW5mbG93ZXIuICBUaGlzIGlzIHRoZSBhbGdvcml0aG0gdXNlZCBieSBIZW5yeSBTZWdlcm1hbi4NCg0KVGhlIGxhYmVscyBhcmUgdGhlIHZhbHVlcyBvZiBgYGBuYGBgIGluIHRoZSBkYXRhLCB3aGljaCBpcyB0aGUgb3JkZXJpbmcgb2YgdGhlIHBvaW50cyBpbiB0aGUgZGF0YS4NCg0KYGBge3J9DQpuIDwtIHNlcSgxLDkwKQ0KDQpwaGkgPC0gKDEgKyBzcXJ0KDUpKSAvIDIuMA0KdGhldGEgPC0gKDIqcGkgKiBuKSAqIHBoaQ0KDQpyIDwtIHNxcnQobikNCg0Kc3VuZmxvd2VyIDwtIGRhdGEuZnJhbWUobiA9IG4sDQogICAgICAgICAgICAgICAgICAgICAgICByID0gciwNCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZXRhID0gdGhldGEsDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gciAqIGNvcyh0aGV0YSksDQogICAgICAgICAgICAgICAgICAgICAgICB5ID0gciAqIHNpbih0aGV0YSkNCiAgICAgICAgICAgICAgICAgICApDQpzdW5mbG93ZXINCg0KZ2dwbG90KHN1bmZsb3dlciwgYWVzKHgsIHksIGxhYmVsID0gbikpICsgDQogIHRoZW1lX21pbmltYWwoKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiI2Q5NWYwMiIpICsNCiAgZ2VvbV90ZXh0KCkgKw0KICBnZ3RpdGxlKCJIZW5yeSBTZWdlcm1hbiBzdW5mbG93ZXIgbW9kZWwiKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KDQpFdmVuIG1vcmUgaW50ZXJlc3Rpbmcgd2hlbiB3ZSBncmVhdGx5IGluY3JlYXNlIHRoZSBudW1iZXIgb2YgYW5nbGVzLiAgVGhlIHJlc3VsdCBpcyBhIHNldCBvZiBwb2ludHMgdGhhdCBpcyBhIGZhaXJseSBnb29kIG1vZGVsIG9mIGEgc3VuZmxvd2VyIGhlYWQuDQoNCmBgYHtyfQ0KbiA8LSBzZXEoMSw5NTkpDQoNCnIgPC0gc3FydChuKSANCg0KcGhpIDwtICgxICsgc3FydCg1KSkgLyAyLjAgICAgIyBwaGkgaXMgdGhlIGdvbGRlbiByYXRpbw0KcGhpDQoNCnRoZXRhIDwtIDIqcGkgKiBuDQp0aGV0YSA8LSBwaGkgKiB0aGV0YSAgICAgICAgICAjIGFkanVzdCBhbmdsZSAgYnkgdGhlIGdvbGRlbiByYXRpbw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCnNzdW5mbG93ZXIgPC0gZGF0YS5mcmFtZShuID0gbiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHIgPSByLA0KICAgICAgICAgICAgICAgICAgICAgICAgdGhldGEgPSB0aGV0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBoaV9jb3VudCA9ICh0aGV0YSAvICgyICogcGkpKSAvIHBoaSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSByICogY29zKHRoZXRhKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByICogc2luKHRoZXRhKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlID0gInNlZ2VybWFuIg0KICAgICAgICAgICAgICAgICAgICkNCmdncGxvdChzc3VuZmxvd2VyLCBhZXMoeCwgeSwgY29sb3IgPSBzdHlsZSkpICsgDQogIGdndGl0bGUoIkhlbnJ5IFNlZ2VybWFuIHN1bmZsb3dlciBtb2RlbCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgY29vcmRfZml4ZWQoKQ0KYGBgDQoNCiMjIyBIZWxtdXQgVm9nZWwncyBtb2RlbA0KUHJvcG9zZWQgYnkgVm9nZWwgaW4gMTk3OS4gIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZpYm9uYWNjaV9udW1iZXIjTmF0dXJlIGFuZCByZWZlcmVuY2UgdG8gcGFwZXIuICANCiQkXHRoZXRhID0gXGZyYWN7MlxwaX17XHBoaV4yfW4sXCByID0gY1xzcXJ0IG4kJA0KYGBge3J9DQpuIDwtIHNlcSgxLDk1OSkNCg0KciA8LSBzcXJ0KG4pDQoNCnBoaSA8LSAoMSArIHNxcnQoNSkpIC8gMi4wICAgICMgcGhpIGlzIHRoZSBnb2xkZW4gcmF0aW8NCnBoaQ0KDQp0aGV0YSA8LSAoKDIqcGkpIC8gcGhpXjIpICogbg0KDQoNCnZzdW5mbG93ZXIgPC0gZGF0YS5mcmFtZShuID0gbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICByID0gciwNCiAgICAgICAgICAgICAgICAgICAgICAgICB0aGV0YSA9IHRoZXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaV9jb3VudCA9ICh0aGV0YSAvICgyICogcGkpKSAvIHBoaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gciAqIGNvcyh0aGV0YSksDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHIgKiBzaW4odGhldGEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlID0gInZvZ2VsIg0KICAgICAgICAgICAgICAgICAgICkNCmdncGxvdCh2c3VuZmxvd2VyLCBhZXMoeCwgeSwgY29sb3IgPSBzdHlsZSkpICsgDQogIHRoZW1lX21pbmltYWwoKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdndGl0bGUoIkhlbG11dCBWb2dlbCBzdW5mbG93ZXIgbW9kZWwiKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KDQojIyMgQ29tcGFyaW5nIHRoZSB0d28gcmVzdWx0cyB2aXN1YWxseQ0KVGhlIHR3byBtb2RlbHMgYXJlIGNvbWJpbmVkIHRvIHBsb3QgYm90aCBhdCB0aGUgc2FtZSB0aW1lLiAgVGhpcyBpcyBkb25lIGJ5IHRoZSBgYGByYmluZGBgYCBmdW5jdGlvbiwgd2hpY2ggY29tYmluZXMgdGhlIGRhdGEgZnJhbWUgcm93cyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUuICBUaGUgY29tYmluZWQgZGF0YSBmcmFtZSBpcyB0aGUgb2JqZWN0IHByb2Nlc3NlZCBieSB0aGUgZ2dwbG90IGZ1bmN0aW9uLg0KYGBge3J9DQpnZyA8LSBnZ3Bsb3QocmJpbmQoc3N1bmZsb3dlciwgdnN1bmZsb3dlciksIA0KICAgICAgIGFlcyh4LCB5LCBjb2xvciA9IHN0eWxlKSkgKyANCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2d0aXRsZSgiT3ZlcmxheSBtb2RlbHMgZm9yIGNvbXBhcmlzb24iKSArDQogIGNvb3JkX2ZpeGVkKCkNCg0KZ2cNCmBgYA0KIyMjIGBgYGdncGxvdGx5YGBgIGZvciByaWNoZXIgdXNlciBleHBlcmllbmNlIG9uIHRoZSB3ZWINCldyYXBwaW5nIGEgYGBgZ2dwbG90YGBgIHdpdGggYGBgZ2dwbG90bHlgYGAgbWFrZXMgaXQgcG9zc2libGUgdG8gaW50ZXJhY3Qgd2l0aCBncmFwaHMgb24gdGhlIHdlYiBldmVuIHdoZXJlIHRoZXJlIGlzIG5vIGFuaW1hdGlvbi4NCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQ0KZ2dwbG90bHkoZ2cpDQpgYGA=