Prelimaries

We will the two basic examples for a dense and sparse array simply to create array data on disk to refer to later in examples that follow.

create_array(uridense)
[1] "/tmp/RtmpaoIiNJ/dense"

API Usage

Creating Arrays

Create Dimension

library(tiledb)

# Create dimension
# In C++: Dimension::create<T>(...)
dim <- tiledb_dim("dim", c(1L, 4L), 2L, "INT32")

Create Array Domain

library(tiledb)

#  .. create dimensions `dim1`, `dim2`
dim1 <- tiledb_dim("dim1", c(1L, 4L), 2L, "INT32")
dim2 <- tiledb_dim("dim2", c(1L, 2L), 2L, "INT32")

# Create domain with two dimensions
# In C++: domain.add_dimensions(dim1).add_dimension(dim2)
dom <- tiledb_domain(dims = c(dim1, dim2))

Creating Attributes

# Create attribute
# In C++: Attribute::create<T>(ctx, "attr")
attr <- tiledb_attr("attr", "INT32")
# We can also set the number of attributes to a value other than one
tiledb:::libtiledb_attribute_set_cell_val_num(attr@ptr, 3)

# And we can set the a variable number of attributes (using NA to select variable size)
tiledb:::libtiledb_attribute_set_cell_val_num(attr@ptr, NA)
## TODO: R function for level setter

Setting a Compressor

comp <- tiledb_filter("GZIP")
tiledb_filter_set_option(comp,"COMPRESSION_LEVEL", 10)

# Create a filter list with the compressor
filter_list <- tiledb_filter_list(c(comp))

# Create attribute with the filter list
attr <- tiledb_attr("attr", "INT32", filter_list = filter_list)

Setting Other Filters

# Create filters
f1 <- tiledb_filter("BIT_WIDTH_REDUCTION")
f2 <- tiledb_filter("ZSTD")

# Create a filter list with the two filters
filter_list <- tiledb_filter_list(c(f1,f2))

# Create attribute with the filter list
attr <- tiledb_attr("attr", "INT32", filter_list = filter_list)

Setting the Tile Chunk Size

# ... create filter list
set_max_chunk_size(filter_list, 10000)

Creating the Array Schema

# ... create domain dom
attr1 <- tiledb_attr("attr1", "INT32", filter_list = filter_list)
attr2 <- tiledb_attr("attr2", "FLOAT64", filter_list = filter_list)

# Create a dense array
schema <- tiledb_array_schema(dom, c(attr1, attr2), sparse = FALSE)
# Or, create a spaese array
# schema <- tiledb_array_schema(dom, c(attr1, attr2), sparse = TRUE)

Setting the Tile and Cell Order

# ... create domain dom
# ... create attributes attr1, attr2

# The tile and order can be "ROW_MAJOR" or "COL_MAJOR"
schema <- tiledb_array_schema(dom, c(attr1, attr2),
                              cell_order = "COL_MAJOR",
                              tile_order = "ROW_MAJOR")

Setting the Data Tile Capacity

tiledb:::libtiledb_array_schema_set_capacity(schema@ptr, 100000)
tiledb:::libtiledb_array_schema_get_capacity(schema@ptr)
[1] 100000
## TODO R function for accessor

Setting Coordinate and Offset Filters

# ... create domain dom
# ... create attributes attr1, attr2
# ... create filter lists fl1, fl2, similar to attributes
f1 <- tiledb_filter("BIT_WIDTH_REDUCTION")
f2 <- tiledb_filter("ZSTD")
fl1 <- tiledb_filter_list(c(f1))
fl2 <- tiledb_filter_list(c(f2))

# Create the schema setting the coordinates and offsets filter lists
schema <- tiledb_array_schema(dom, c(attr1, attr2),
                              coords_filter_list = fl1,
                              offsets_filter_list = fl2)

Checking Correctness

tiledb:::libtiledb_array_schema_check(schema@ptr)
## TODO: R function for access

Creating the Array

# ... create array schema

# Create the array
tiledb_array_create(uridense, schema)

Creating Encrypted Arrays

encryption_key <- "0123456789abcdeF0123456789abcdeF"
uridensewkey <- file.path(tdir, "densewkey")
if (dir.exists(uridensewkey)) unlink(uridensewkey, recursive=TRUE)

# shortcut: borrow schema from dense array; could create schema too
schema <- schema(tiledb_dense(uridense))
tiledb:::libtiledb_array_create_with_key(uridensewkey, schema@ptr, encryption_key)
[1] "/tmp/RtmppsnBrT/densewkey"
##TODO: R support

Writing Arrays

Writing in Dense Subarrays

## prepare a larger 5 x 5 to embed into
tmp <- tempfile()
d1  <- tiledb_dim(domain = c(1L, 5L))
d2  <- tiledb_dim(domain = c(1L, 5L))
dom <- tiledb_domain(c(d1, d2))
val <- tiledb_attr(name="val", type = "INT32")
sch <- tiledb_array_schema(dom, c(val))
tiledb_array_create(tmp, sch)

dat <- matrix(as.integer(rnorm(25)*100), 5, 5)
arr <- tiledb_dense(tmp, as.data.frame=FALSE)
arr[] <- dat


# Prepare a 2x3 dense array
# Contrary to Python, R by default stores arrays in col-major order
data <-  array(c(1L, 4L, 2L, 5L, 3L, 6L), dim=c(2,3))

# Prepare the [1,2] x [2,4] subarray to write to
I <- c(1:2)
J <- c(2:4)

# Open the array and write the data to it
A <- tiledb_dense(uri = tmp)
A[I, J] <- data

unlink(tmp, recursive=TRUE)

Basic Writing using Low-Level Code

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridense, "WRITE")

## data: simple (integer sequence) of 1:16 times 10
vec <- 1:16 * 10L
subarr <- c(1L,4L, 1L,4L)

qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "WRITE")
qryptr <- tiledb:::libtiledb_query_set_subarray(qryptr, subarr)
qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", vec)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
res <- tiledb:::libtiledb_array_close(arrptr)

Writing Sparse Cells

tmp <- urisparse
unlink(tmp, recursive=TRUE)

d1  <- tiledb_dim(domain = c(1L, 5L))
d2  <- tiledb_dim(domain = c(1L, 5L))
dom <- tiledb_domain(c(d1, d2))
val <- tiledb_attr("val", type = "INT32")
sch <- tiledb_array_schema(dom, c(val), sparse=TRUE)
tiledb_array_create(tmp, sch)
[1] "/tmp/RtmppsnBrT/sparse"
# Prepare some data
data <- c(3L, 4L, 1L, 2L)

I <- c(3, 4, 1, 2)
J <- c(3, 4, 2, 1)

# Open the array and write the data to it
A <- tiledb_sparse(uri = tmp)
A[I, J] <- data

Writing Encrypted Arrays

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open_with_key(ctx@ptr, uridensewkey, "WRITE", encryption_key)

## data: simple (integer sequence) of 1:16 times 10
vec <- 1:16 * 10L
subarr <- c(1L,4L, 1L,4L)

qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "WRITE")
qryptr <- tiledb:::libtiledb_query_set_subarray(qryptr, subarr)
qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", vec)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
res <- tiledb:::libtiledb_array_close(arrptr)
# TODO Higher-level R support

Fixed-length Attributes

if (dir.exists(uridensefix)) unlink(uridensefix, recursive=TRUE)
d1  <- tiledb_dim(domain = c(1L, 4L))
d2  <- tiledb_dim(domain = c(1L, 4L))
dom <- tiledb_domain(c(d1, d2))

vec <- 1:32 * 10L
attr <- tiledb_attr("a", type = r_to_tiledb_type(vec))

## set to two values per cell
tiledb:::libtiledb_attribute_set_cell_val_num(attr@ptr, 2)
sch <- tiledb_array_schema(dom, c(attr))
tiledb_array_create(uridensefix, sch)
[1] "/tmp/RtmppsnBrT/densefix"
ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridensefix, "WRITE")
subarr <- c(1L,4L, 1L,4L)

qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "WRITE")
qryptr <- tiledb:::libtiledb_query_set_subarray(qryptr, subarr)
qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", vec)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
res <- tiledb:::libtiledb_array_close(arrptr)

#TODO Higher-level R support

Var-length Attributes

if (dir.exists(uridensevar)) unlink(uridensevar, recursive=TRUE)
## Define array
## The array will be 4x4 with dimensions "rows" and "cols", with domain [1,4].
dom <- tiledb_domain(dims = c(tiledb_dim("rows", c(1L, 4L), 4L, "INT32"),
                              tiledb_dim("cols", c(1L, 4L), 4L, "INT32")))


attr <- tiledb_attr("a1", type = "CHAR")
## set to variable length
tiledb:::libtiledb_attribute_set_cell_val_num(attr@ptr, NA)

## now set the schema
ctx <- tiledb_ctx()
schptr <- tiledb:::libtiledb_array_schema_create(ctx@ptr, "DENSE")
tiledb:::libtiledb_array_schema_set_domain(schptr, dom@ptr)
tiledb:::libtiledb_array_schema_set_cell_order(schptr, "ROW_MAJOR")
tiledb:::libtiledb_array_schema_set_tile_order(schptr, "ROW_MAJOR")
tiledb:::libtiledb_array_schema_add_attribute(schptr, attr@ptr)



## Create the (empty) array on disk.
tiledb:::libtiledb_array_create(uridensevar, schptr)
[1] "/tmp/RtmppsnBrT/densevar"
data <- "abbcccddeeefghhhijjjkklmnoop";
offsets <- c(0L, 1L, 3L, 6L, 8L, 11L, 12L, 13L, 16L, 17L, 20L, 22L, 23L, 24L, 25L, 27L)

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridensevar, "WRITE")
qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "WRITE")
qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")

bufptr <- tiledb:::libtiledb_query_buffer_var_char_create(offsets, data)
qryptr <- tiledb:::libtiledb_query_set_buffer_var_char(qryptr, "a1", bufptr)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
tiledb:::libtiledb_array_close(arrptr)
<pointer: 0x7fb5fc018dc0>
#TODO Higher-level R support

Getting the Fragment Info

## continuing from previous example on dense variable length array
## (but this works of course with any array after a write is needed
numfrag <- tiledb:::libtiledb_query_get_fragment_num(qryptr)
uritxt <- tiledb:::libtiledb_query_get_fragment_uri(qryptr, 0)

##TODO Higher-level R support

Reading Arrays

Reading the Array Schema

array_name <- urisparse
A <- tiledb_sparse(uri = array_name)
# can also load dense
sch <- schema(A)
# can also open encrypted arrays with key

Inspecting the array schema

array_name <- urisparse

# Get array schema
A <- tiledb_sparse(uri = array_name)
# or use tiledb_dense(uri = array_name) for dense
schema <- schema(A)

# Get array type
sparse <- is.sparse(schema)

# Get tile capacity
tiledb:::libtiledb_array_schema_get_capacity(sch@ptr)
[1] 10000
# TODO: R accessor for tile

# Get tile order
t_order <- tile_order(schema)

# Get cell order
c_order <- cell_order(schema)

# Get coordinates and offset filter list
reslist <- filter_list(schema)

# Get the array domain
dom <- domain(schema)

# Get all attributes as list
attrs <- attrs(schema)

# Get attribute from name
attr <- attrs(schema, "attr")

# Dump the array schema in ASCII format to stdout
show(schema)
- Array type: sparse
- Cell order: col-major
- Tile order: col-major
- Capacity: 10000
- Coordinates compressor: ZSTD
- Coordinates compression level: -1

=== Domain ===
- Dimensions type: INT32

### Dimension ###
- Name: <anonymous>
- Domain: [1,5]
- Tile extent: 5

### Dimension ###
- Name: <anonymous>
- Domain: [1,5]
- Tile extent: 5

### Attribute ###
- Name: val
- Type: INT32
- Compressor: NO_COMPRESSION
- Compression level: -1
- Cell val num: 1

Inspecting Domain

# ... get array schema
# ... get domain from schema
dom <- domain(schema)

# Get the domain datatype (i.e., the datatype of all dimensions)
type <- datatype(dom)

# Get number of dimensions
dim_num <- dim(dom)

# Get all dimension
dims <- dimensions(dom)

# Dump the domain in ASCII format in the selected output
show(dom)
=== Domain ===
- Dimensions type: INT32

### Dimension ###
- Name: <anonymous>
- Domain: [1,5]
- Tile extent: 5

### Dimension ###
- Name: <anonymous>
- Domain: [1,5]
- Tile extent: 5

Inspecting Dimensions

# ... get array schema
# ... get domain
# ... get dimension by index or name

#FIXME

# Get dimension name
dim_name <- name(dim)

# Get dimension datatype
dim_type <- datatype(dim)

# Get dimension domain
domain <- domain(dim)

# Get tile extent
tile_extent <- tile(dim)

# Dump the dimension in ASCII format in the selected output
show(dim)

Inspecting Attributes

# ... get array schema
# ... get attribute by index or name

stopifnot(is.null(attr))
# Get attribute name
attr_name <- name(attr)

# Get attribute datatype
attr_type <- datatype(attr)

# Get filter list
filter_list <- filter_list(attr)

# Check if attribute is variable-length
# variable length attribtues are not supported in R currently

# Get number of values per cell
num <- ncells(attr)

# Get cell size for this attribute
sz <-- tiledb:::libtiledb_attribute_get_cell_val(attr@ptr)

# Dump the attribute in ASCII format in the selected output
show(attr)

Inspecting Filters

fl <- tiledb_filter_list()

# get number of filter
tiledb:::libtiledb_filter_list_get_nfilters(fl@ptr)

# get max chunk size
tiledb:::libtiledb_filter_list_get_max_chunk_size(fl@ptr)

# get filter from index
ind <- i
tiledb:::libtiledb_filter_list_get_filter_from_index(fl@ptr, ind)

Basic Reading

# Create a TileDB context
ctx <- tiledb_ctx()

# Open a dense array
A <- tiledb_dense(uri = uridense, ctx=ctx)

# Or, open a sparse array
# A <- tiledb_sparse(uri = "<array-uri>", ctx=ctx)

# Slice only rows 1, 2 and cols 2, 3, 4
data <- A[1:2, 2:4]
show(data)
     [,1] [,2] [,3]
[1,]   20   30   40
[2,]   60   70   80

Basic Reading using Low-Level Code

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridense, "READ")
## subarray of rows 1,2 and cols 2,3,4
subarr <- c(1L,2L, 2L,4L)

qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "READ")
qryptr <- tiledb:::libtiledb_query_set_subarray(qryptr, subarr)
qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")
v <- integer(6)  # reserve space
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", v)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
print(v)         # unformed array, no coordinates
[1] 20 30 40 60 70 80
res <- tiledb:::libtiledb_array_close(arrptr)

Reading Encrypted Arrays

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open_with_key(ctx@ptr, uridensewkey, "READ", encryption_key)

## subarray of rows 1,2 and cols 2,3,4
subarr <- c(1L,2L, 2L,4L)

qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "READ")
qryptr <- tiledb:::libtiledb_query_set_subarray(qryptr, subarr)
qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")
v <- integer(6)  # reserve space
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", v)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
print(v)         # unformed array, no coordinates
[1] 20 30 40 60 70 80
res <- tiledb:::libtiledb_array_close(arrptr)

## TODO open at timestamp example

Multi-range Subarrays

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridense, "READ")

qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "READ")

## range of rows 1 and 2, and 4 for dim 1, all rows for dim 2
qryptr <- tiledb:::libtiledb_query_add_range(qryptr, 0, 1L, 2L)
qryptr <- tiledb:::libtiledb_query_add_range(qryptr, 0, 4L, 4L)
qryptr <- tiledb:::libtiledb_query_add_range(qryptr, 1, 1L, 4L)

qryptr <- tiledb:::libtiledb_query_set_layout(qryptr, "ROW_MAJOR")
v <- integer(12)  # reserve space
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", v)
qryptr <- tiledb:::libtiledb_query_submit(qryptr)
print(v)         # unformed array, no coordinates
 [1]  10  20  30  40  50  60  70  80 130 140 150 160
res <- tiledb:::libtiledb_array_close(arrptr)

Incomplete Queries

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridense, "READ")
qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "READ")
subarr <- c(1L,4L, 1L,4L)
qryptr <- tiledb:::libtiledb_query_set_subarray(qryptr, subarr)
vec <- integer(4)  # reserve (insufficient) space
qryptr <- tiledb:::libtiledb_query_set_buffer(qryptr, "a", vec)
finished <- FALSE
while (!finished) {
  qryptr <- tiledb:::libtiledb_query_submit(qryptr)
  print(vec)
  finished <- tiledb:::libtiledb_query_status(qryptr) == "COMPLETE"
}
[1] 10 20 30 40
[1] 50 60 70 80
[1]  90 100 110 120
[1] 130 140 150 160
res <- tiledb:::libtiledb_array_close(arrptr)

Result Estimation

ctx <- tiledb_ctx()
arrptr <- tiledb:::libtiledb_array_open(ctx@ptr, uridensevar, "READ")
qryptr <- tiledb:::libtiledb_query(ctx@ptr, arrptr, "READ")
estsz <- tiledb:::libtiledb_query_get_est_result_size_var(qryptr, "a1")
res <- tiledb:::libtiledb_array_close(arrptr)
estsz
[1] 16 28

Time Traveling

v
[1] 2 3 4 6 7 8

Embedded SQL

Asynchronous Queries

Configuration

Array Metadata

Writing Array Metadata

res <- tiledb_put_metadata(uridense, "aaa", 100L)
res <- tiledb_put_metadata(uridense, "bb", c(1.1, 2.2))

Reading Array Metadata

One can read by key:

tiledb_get_metadata(uridense, "aaa")

Or one can retrieve all metadata at once:

md <- tiledb_get_all_metadata(uridense)
print(md)

Deleting Array Metadata

res <- tiledb_delete_metadata(uridense, "aaa")
LS0tCnRpdGxlOiAiVGlsZURCIFIgTm90ZWJvb2siCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBsdW1lbgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKPCEtLVRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuICBUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouICAgQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4gIFRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4tLT4KCiMgUHJlbGltYXJpZXMKCldlIHdpbGwgdGhlIHR3byBiYXNpYyBleGFtcGxlcyBmb3IgYSBkZW5zZSBhbmQgc3BhcnNlIGFycmF5IHNpbXBseSB0byBjcmVhdGUKYXJyYXkgZGF0YSBvbiBkaXNrIHRvIHJlZmVyIHRvIGxhdGVyIGluIGV4YW1wbGVzIHRoYXQgZm9sbG93LgoKYGBge3IgcnVuZXhhbXBsZXN9CmxpYnJhcnkodGlsZWRiKQoKdGRpciA8LSB0ZW1wZGlyKCkKdXJpZGVuc2UgPC0gZmlsZS5wYXRoKHRkaXIsICJkZW5zZSIpCnVyaWRlbnNlZml4IDwtIGZpbGUucGF0aCh0ZGlyLCAiZGVuc2VmaXgiKQp1cmlkZW5zZXZhciA8LSBmaWxlLnBhdGgodGRpciwgImRlbnNldmFyIikKCmNyZWF0ZV9hcnJheSA8LSBmdW5jdGlvbihhcnJheV9uYW1lKSB7CiAgICAjIENoZWNrIGlmIHRoZSBhcnJheSBhbHJlYWR5IGV4aXN0cy4KICAgIGlmICh0aWxlZGJfb2JqZWN0X3R5cGUoYXJyYXlfbmFtZSkgPT0gIkFSUkFZIikgewogICAgICAgIG1lc3NhZ2UoIkFycmF5IGFscmVhZHkgZXhpc3RzLiIpCiAgICAgICAgcmV0dXJuKGludmlzaWJsZShOVUxMKSkKICAgIH0KCiAgICAjIFRoZSBhcnJheSB3aWxsIGJlIDR4NCB3aXRoIGRpbWVuc2lvbnMgInJvd3MiIGFuZCAiY29scyIsIHdpdGggZG9tYWluIFsxLDRdLgogICAgZG9tIDwtIHRpbGVkYl9kb21haW4oZGltcyA9IGModGlsZWRiX2RpbSgicm93cyIsIGMoMUwsIDRMKSwgNEwsICJJTlQzMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGlsZWRiX2RpbSgiY29scyIsIGMoMUwsIDRMKSwgNEwsICJJTlQzMiIpKSkKCiAgICAjIFRoZSBhcnJheSB3aWxsIGJlIGRlbnNlIHdpdGggYSBzaW5nbGUgYXR0cmlidXRlICJhIiBzbyBlYWNoIChpLGopIGNlbGwgY2FuIHN0b3JlIGFuIGludGVnZXIuCiAgICBzY2hlbWEgPC0gdGlsZWRiX2FycmF5X3NjaGVtYShkb20sIGF0dHJzID0gYyh0aWxlZGJfYXR0cigiYSIsIHR5cGUgPSAiSU5UMzIiKSkpCgogICAgIyBDcmVhdGUgdGhlIChlbXB0eSkgYXJyYXkgb24gZGlzay4KICAgIHRpbGVkYl9hcnJheV9jcmVhdGUoYXJyYXlfbmFtZSwgc2NoZW1hKQp9Cgp3cml0ZV9hcnJheSA8LSBmdW5jdGlvbihhcnJheV9uYW1lKSB7CiAgICBkYXRhIDwtIGFycmF5KGMoYygxTCwgNUwsIDlMLCAxM0wpLAogICAgICAgICAgICAgICAgICAgIGMoMkwsIDZMLCAxMEwsIDE0TCksCiAgICAgICAgICAgICAgICAgICAgYygzTCwgN0wsIDExTCwgMTVMKSwKICAgICAgICAgICAgICAgICAgICBjKDRMLCA4TCwgMTJMLCAxNkwpKSwgZGltID0gYyg0LDQpKQogICAgIyBPcGVuIHRoZSBhcnJheSBhbmQgd3JpdGUgdG8gaXQuCiAgICBBIDwtIHRpbGVkYl9kZW5zZSh1cmkgPSBhcnJheV9uYW1lKQogICAgQVtdIDwtIGRhdGEKfQoKY3JlYXRlX2FycmF5KHVyaWRlbnNlKQp3cml0ZV9hcnJheSh1cmlkZW5zZSkKCgp1cmlzcGFyc2UgPC0gZmlsZS5wYXRoKHRkaXIsICJzcGFyc2UiKQoKY3JlYXRlX2FycmF5IDwtIGZ1bmN0aW9uKGFycmF5X25hbWUpIHsKICAgICMgQ2hlY2sgaWYgdGhlIGFycmF5IGFscmVhZHkgZXhpc3RzLgogICAgaWYgKHRpbGVkYl9vYmplY3RfdHlwZShhcnJheV9uYW1lKSA9PSAiQVJSQVkiKSB7CiAgICAgICAgbWVzc2FnZSgiQXJyYXkgYWxyZWFkeSBleGlzdHMuIikKICAgICAgICByZXR1cm4oaW52aXNpYmxlKE5VTEwpKQogICAgfQoKICAgICMgVGhlIGFycmF5IHdpbGwgYmUgNHg0IHdpdGggZGltZW5zaW9ucyAicm93cyIgYW5kICJjb2xzIiwgd2l0aCBkb21haW4gWzEsNF0uCiAgICBkb20gPC0gdGlsZWRiX2RvbWFpbihkaW1zID0gYyh0aWxlZGJfZGltKCJyb3dzIiwgYygxTCwgNEwpLCA0TCwgIklOVDMyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWxlZGJfZGltKCJjb2xzIiwgYygxTCwgNEwpLCA0TCwgIklOVDMyIikpKQoKICAgIyBUaGUgYXJyYXkgd2lsbCBiZSBkZW5zZSB3aXRoIGEgc2luZ2xlIGF0dHJpYnV0ZSAiYSIgc28gZWFjaCAoaSxqKSBjZWxsIGNhbiBzdG9yZSBhbiBpbnRlZ2VyLgogICAgc2NoZW1hID0gdGlsZWRiX2FycmF5X3NjaGVtYShkb20sIGF0dHJzPWModGlsZWRiX2F0dHIoImEiLCB0eXBlID0gIklOVDMyIikpLCBzcGFyc2UgPSBUUlVFKQoKICAgICMgQ3JlYXRlIHRoZSAoZW1wdHkpIGFycmF5IG9uIGRpc2suCiAgICB0aWxlZGJfYXJyYXlfY3JlYXRlKGFycmF5X25hbWUsIHNjaGVtYSkKfQoKd3JpdGVfYXJyYXkgPC0gZnVuY3Rpb24oYXJyYXlfbmFtZSkgewogICAgSSA8LSBjKDEsIDIsIDIpCiAgICBKIDwtIGMoMSwgNCwgMykKICAgIGRhdGEgPC0gYygxTCwgMkwsIDNMKQogICAgIyBPcGVuIHRoZSBhcnJheSBhbmQgd3JpdGUgdG8gaXQuCiAgICBBIDwtIHRpbGVkYl9zcGFyc2UodXJpID0gYXJyYXlfbmFtZSkKICAgIEFbSSwgSl0gPC0gZGF0YQp9CgpjcmVhdGVfYXJyYXkodXJpc3BhcnNlKQp3cml0ZV9hcnJheSh1cmlzcGFyc2UpCgpgYGAKCgoKIyBBUEkgVXNhZ2UKCiMjIENyZWF0aW5nIEFycmF5cwoKIyMjIENyZWF0ZSBEaW1lbnNpb24KCmBgYHtyIGNyZWF0ZWRpbX0KbGlicmFyeSh0aWxlZGIpCgojIENyZWF0ZSBkaW1lbnNpb24KIyBJbiBDKys6IERpbWVuc2lvbjo6Y3JlYXRlPFQ+KC4uLikKZGltIDwtIHRpbGVkYl9kaW0oImRpbSIsIGMoMUwsIDRMKSwgMkwsICJJTlQzMiIpCmBgYAoKCiMjIyBDcmVhdGUgQXJyYXkgRG9tYWluCgpgYGB7ciBjcmVhdGFycmRvbX0KbGlicmFyeSh0aWxlZGIpCgojICAuLiBjcmVhdGUgZGltZW5zaW9ucyBgZGltMWAsIGBkaW0yYApkaW0xIDwtIHRpbGVkYl9kaW0oImRpbTEiLCBjKDFMLCA0TCksIDJMLCAiSU5UMzIiKQpkaW0yIDwtIHRpbGVkYl9kaW0oImRpbTIiLCBjKDFMLCAyTCksIDJMLCAiSU5UMzIiKQoKIyBDcmVhdGUgZG9tYWluIHdpdGggdHdvIGRpbWVuc2lvbnMKIyBJbiBDKys6IGRvbWFpbi5hZGRfZGltZW5zaW9ucyhkaW0xKS5hZGRfZGltZW5zaW9uKGRpbTIpCmRvbSA8LSB0aWxlZGJfZG9tYWluKGRpbXMgPSBjKGRpbTEsIGRpbTIpKQpgYGAKCiMjIyBDcmVhdGluZyBBdHRyaWJ1dGVzCgpgYGB7ciBjcmVhdGVhdHRyfQojIENyZWF0ZSBhdHRyaWJ1dGUKIyBJbiBDKys6IEF0dHJpYnV0ZTo6Y3JlYXRlPFQ+KGN0eCwgImF0dHIiKQphdHRyIDwtIHRpbGVkYl9hdHRyKCJhdHRyIiwgIklOVDMyIikKYGBgCgpgYGB7ciBjcmVhdGVhdHRydmFybnVtfQojIFdlIGNhbiBhbHNvIHNldCB0aGUgbnVtYmVyIG9mIGF0dHJpYnV0ZXMgdG8gYSB2YWx1ZSBvdGhlciB0aGFuIG9uZQp0aWxlZGI6OjpsaWJ0aWxlZGJfYXR0cmlidXRlX3NldF9jZWxsX3ZhbF9udW0oYXR0ckBwdHIsIDMpCgojIEFuZCB3ZSBjYW4gc2V0IHRoZSBhIHZhcmlhYmxlIG51bWJlciBvZiBhdHRyaWJ1dGVzICh1c2luZyBOQSB0byBzZWxlY3QgdmFyaWFibGUgc2l6ZSkKdGlsZWRiOjo6bGlidGlsZWRiX2F0dHJpYnV0ZV9zZXRfY2VsbF92YWxfbnVtKGF0dHJAcHRyLCBOQSkKIyMgVE9ETzogUiBmdW5jdGlvbiBmb3IgbGV2ZWwgc2V0dGVyCmBgYAoKIyMjIyBTZXR0aW5nIGEgQ29tcHJlc3NvcgoKYGBge3Igc2V0Y29tcHJ9CmNvbXAgPC0gdGlsZWRiX2ZpbHRlcigiR1pJUCIpCnRpbGVkYl9maWx0ZXJfc2V0X29wdGlvbihjb21wLCJDT01QUkVTU0lPTl9MRVZFTCIsIDEwKQoKIyBDcmVhdGUgYSBmaWx0ZXIgbGlzdCB3aXRoIHRoZSBjb21wcmVzc29yCmZpbHRlcl9saXN0IDwtIHRpbGVkYl9maWx0ZXJfbGlzdChjKGNvbXApKQoKIyBDcmVhdGUgYXR0cmlidXRlIHdpdGggdGhlIGZpbHRlciBsaXN0CmF0dHIgPC0gdGlsZWRiX2F0dHIoImF0dHIiLCAiSU5UMzIiLCBmaWx0ZXJfbGlzdCA9IGZpbHRlcl9saXN0KQpgYGAKCgojIyMjIFNldHRpbmcgT3RoZXIgRmlsdGVycwoKYGBge3Igc2V0Zmlscn0KIyBDcmVhdGUgZmlsdGVycwpmMSA8LSB0aWxlZGJfZmlsdGVyKCJCSVRfV0lEVEhfUkVEVUNUSU9OIikKZjIgPC0gdGlsZWRiX2ZpbHRlcigiWlNURCIpCgojIENyZWF0ZSBhIGZpbHRlciBsaXN0IHdpdGggdGhlIHR3byBmaWx0ZXJzCmZpbHRlcl9saXN0IDwtIHRpbGVkYl9maWx0ZXJfbGlzdChjKGYxLGYyKSkKCiMgQ3JlYXRlIGF0dHJpYnV0ZSB3aXRoIHRoZSBmaWx0ZXIgbGlzdAphdHRyIDwtIHRpbGVkYl9hdHRyKCJhdHRyIiwgIklOVDMyIiwgZmlsdGVyX2xpc3QgPSBmaWx0ZXJfbGlzdCkKYGBgCgoKIyMjIyBTZXR0aW5nIHRoZSBUaWxlIENodW5rIFNpemUKCmBgYHtyIHNldHRpbGVjaHVua3NpemV9CiMgLi4uIGNyZWF0ZSBmaWx0ZXIgbGlzdApzZXRfbWF4X2NodW5rX3NpemUoZmlsdGVyX2xpc3QsIDEwMDAwKQpgYGAKCgojIyMgQ3JlYXRpbmcgdGhlIEFycmF5IFNjaGVtYQoKYGBge3IgY3JlYXRlYXJyc2NoZW1hfQojIC4uLiBjcmVhdGUgZG9tYWluIGRvbQphdHRyMSA8LSB0aWxlZGJfYXR0cigiYXR0cjEiLCAiSU5UMzIiLCBmaWx0ZXJfbGlzdCA9IGZpbHRlcl9saXN0KQphdHRyMiA8LSB0aWxlZGJfYXR0cigiYXR0cjIiLCAiRkxPQVQ2NCIsIGZpbHRlcl9saXN0ID0gZmlsdGVyX2xpc3QpCgojIENyZWF0ZSBhIGRlbnNlIGFycmF5CnNjaGVtYSA8LSB0aWxlZGJfYXJyYXlfc2NoZW1hKGRvbSwgYyhhdHRyMSwgYXR0cjIpLCBzcGFyc2UgPSBGQUxTRSkKIyBPciwgY3JlYXRlIGEgc3BhZXNlIGFycmF5CiMgc2NoZW1hIDwtIHRpbGVkYl9hcnJheV9zY2hlbWEoZG9tLCBjKGF0dHIxLCBhdHRyMiksIHNwYXJzZSA9IFRSVUUpCmBgYAoKIyMjIyBTZXR0aW5nIHRoZSBUaWxlIGFuZCBDZWxsIE9yZGVyCgpgYGB7ciBzZXR0aWxlY2VsbG9yZGVyfQojIC4uLiBjcmVhdGUgZG9tYWluIGRvbQojIC4uLiBjcmVhdGUgYXR0cmlidXRlcyBhdHRyMSwgYXR0cjIKCiMgVGhlIHRpbGUgYW5kIG9yZGVyIGNhbiBiZSAiUk9XX01BSk9SIiBvciAiQ09MX01BSk9SIgpzY2hlbWEgPC0gdGlsZWRiX2FycmF5X3NjaGVtYShkb20sIGMoYXR0cjEsIGF0dHIyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbF9vcmRlciA9ICJDT0xfTUFKT1IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWxlX29yZGVyID0gIlJPV19NQUpPUiIpCmBgYAoKIyMjIyBTZXR0aW5nIHRoZSBEYXRhIFRpbGUgQ2FwYWNpdHkKCmBgYHtyIHNldHRpbGVjYXBhY2l0eX0KdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X3NjaGVtYV9zZXRfY2FwYWNpdHkoc2NoZW1hQHB0ciwgMTAwMDAwKQp0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfc2NoZW1hX2dldF9jYXBhY2l0eShzY2hlbWFAcHRyKQojIyBUT0RPIFIgZnVuY3Rpb24gZm9yIGFjY2Vzc29yCmBgYAoKIyMjIyBTZXR0aW5nIENvb3JkaW5hdGUgYW5kIE9mZnNldCBGaWx0ZXJzCgpgYGB7ciBzZXRjb29yZG9mZnNldGZpbHR9CiMgLi4uIGNyZWF0ZSBkb21haW4gZG9tCiMgLi4uIGNyZWF0ZSBhdHRyaWJ1dGVzIGF0dHIxLCBhdHRyMgojIC4uLiBjcmVhdGUgZmlsdGVyIGxpc3RzIGZsMSwgZmwyLCBzaW1pbGFyIHRvIGF0dHJpYnV0ZXMKZjEgPC0gdGlsZWRiX2ZpbHRlcigiQklUX1dJRFRIX1JFRFVDVElPTiIpCmYyIDwtIHRpbGVkYl9maWx0ZXIoIlpTVEQiKQpmbDEgPC0gdGlsZWRiX2ZpbHRlcl9saXN0KGMoZjEpKQpmbDIgPC0gdGlsZWRiX2ZpbHRlcl9saXN0KGMoZjIpKQoKIyBDcmVhdGUgdGhlIHNjaGVtYSBzZXR0aW5nIHRoZSBjb29yZGluYXRlcyBhbmQgb2Zmc2V0cyBmaWx0ZXIgbGlzdHMKc2NoZW1hIDwtIHRpbGVkYl9hcnJheV9zY2hlbWEoZG9tLCBjKGF0dHIxLCBhdHRyMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3Jkc19maWx0ZXJfbGlzdCA9IGZsMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0c19maWx0ZXJfbGlzdCA9IGZsMikKYGBgCgoKIyMjIyBDaGVja2luZyBDb3JyZWN0bmVzcwoKYGBge3IgYXJyYXlzY2hlbWFjaGVja30KdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X3NjaGVtYV9jaGVjayhzY2hlbWFAcHRyKQojIyBUT0RPOiBSIGZ1bmN0aW9uIGZvciBhY2Nlc3MKYGBgCgoKCiMjIyBDcmVhdGluZyB0aGUgQXJyYXkKCmBgYHtyIGNyZWF0ZWFycmF5LCBldmFsPUZBTFNFfQojIC4uLiBjcmVhdGUgYXJyYXkgc2NoZW1hCgojIENyZWF0ZSB0aGUgYXJyYXkKdGlsZWRiX2FycmF5X2NyZWF0ZSh1cmlkZW5zZSwgc2NoZW1hKQpgYGAKCiMjIyBDcmVhdGluZyBFbmNyeXB0ZWQgQXJyYXlzCgpgYGB7ciBjcmVhdGV3aXRoa2V5fQplbmNyeXB0aW9uX2tleSA8LSAiMDEyMzQ1Njc4OWFiY2RlRjAxMjM0NTY3ODlhYmNkZUYiCnVyaWRlbnNld2tleSA8LSBmaWxlLnBhdGgodGRpciwgImRlbnNld2tleSIpCmlmIChkaXIuZXhpc3RzKHVyaWRlbnNld2tleSkpIHVubGluayh1cmlkZW5zZXdrZXksIHJlY3Vyc2l2ZT1UUlVFKQoKIyBzaG9ydGN1dDogYm9ycm93IHNjaGVtYSBmcm9tIGRlbnNlIGFycmF5OyBjb3VsZCBjcmVhdGUgc2NoZW1hIHRvbwpzY2hlbWEgPC0gc2NoZW1hKHRpbGVkYl9kZW5zZSh1cmlkZW5zZSkpCnRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jcmVhdGVfd2l0aF9rZXkodXJpZGVuc2V3a2V5LCBzY2hlbWFAcHRyLCBlbmNyeXB0aW9uX2tleSkKIyNUT0RPOiBSIHN1cHBvcnQKYGBgCgoKIyMgV3JpdGluZyBBcnJheXMKCiMjIyBXcml0aW5nIGluIERlbnNlIFN1YmFycmF5cwoKYGBge3Igd3JpdGVkZW5zZXN1YmFyciwgZXZhbD1GQUxTRX0KIyMgcHJlcGFyZSBhIGxhcmdlciA1IHggNSB0byBlbWJlZCBpbnRvCnRtcCA8LSB0ZW1wZmlsZSgpCmQxICA8LSB0aWxlZGJfZGltKGRvbWFpbiA9IGMoMUwsIDVMKSkKZDIgIDwtIHRpbGVkYl9kaW0oZG9tYWluID0gYygxTCwgNUwpKQpkb20gPC0gdGlsZWRiX2RvbWFpbihjKGQxLCBkMikpCnZhbCA8LSB0aWxlZGJfYXR0cihuYW1lPSJ2YWwiLCB0eXBlID0gIklOVDMyIikKc2NoIDwtIHRpbGVkYl9hcnJheV9zY2hlbWEoZG9tLCBjKHZhbCkpCnRpbGVkYl9hcnJheV9jcmVhdGUodG1wLCBzY2gpCgpkYXQgPC0gbWF0cml4KGFzLmludGVnZXIocm5vcm0oMjUpKjEwMCksIDUsIDUpCmFyciA8LSB0aWxlZGJfZGVuc2UodG1wLCBhcy5kYXRhLmZyYW1lPUZBTFNFKQphcnJbXSA8LSBkYXQKCgojIFByZXBhcmUgYSAyeDMgZGVuc2UgYXJyYXkKIyBDb250cmFyeSB0byBQeXRob24sIFIgYnkgZGVmYXVsdCBzdG9yZXMgYXJyYXlzIGluIGNvbC1tYWpvciBvcmRlcgpkYXRhIDwtICBhcnJheShjKDFMLCA0TCwgMkwsIDVMLCAzTCwgNkwpLCBkaW09YygyLDMpKQoKIyBQcmVwYXJlIHRoZSBbMSwyXSB4IFsyLDRdIHN1YmFycmF5IHRvIHdyaXRlIHRvCkkgPC0gYygxOjIpCkogPC0gYygyOjQpCgojIE9wZW4gdGhlIGFycmF5IGFuZCB3cml0ZSB0aGUgZGF0YSB0byBpdApBIDwtIHRpbGVkYl9kZW5zZSh1cmkgPSB0bXApCkFbSSwgSl0gPC0gZGF0YQoKdW5saW5rKHRtcCwgcmVjdXJzaXZlPVRSVUUpCmBgYAoKCiMjIyMgQmFzaWMgV3JpdGluZyB1c2luZyBMb3ctTGV2ZWwgQ29kZQoKYGBge3IgYmFzaXNjd3JpdGluZ2xvd2xldmVsfQpjdHggPC0gdGlsZWRiX2N0eCgpCmFycnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfb3BlbihjdHhAcHRyLCB1cmlkZW5zZSwgIldSSVRFIikKCiMjIGRhdGE6IHNpbXBsZSAoaW50ZWdlciBzZXF1ZW5jZSkgb2YgMToxNiB0aW1lcyAxMAp2ZWMgPC0gMToxNiAqIDEwTApzdWJhcnIgPC0gYygxTCw0TCwgMUwsNEwpCgpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5KGN0eEBwdHIsIGFycnB0ciwgIldSSVRFIikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfc3ViYXJyYXkocXJ5cHRyLCBzdWJhcnIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X2xheW91dChxcnlwdHIsICJST1dfTUFKT1IiKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9idWZmZXIocXJ5cHRyLCAiYSIsIHZlYykKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zdWJtaXQocXJ5cHRyKQpyZXMgPC0gdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X2Nsb3NlKGFycnB0cikKYGBgCgoKIyMjIFdyaXRpbmcgU3BhcnNlIENlbGxzCgpgYGB7ciB3cml0ZXNwYXJzZWNlbGxzfQp0bXAgPC0gdXJpc3BhcnNlCnVubGluayh0bXAsIHJlY3Vyc2l2ZT1UUlVFKQoKZDEgIDwtIHRpbGVkYl9kaW0oZG9tYWluID0gYygxTCwgNUwpKQpkMiAgPC0gdGlsZWRiX2RpbShkb21haW4gPSBjKDFMLCA1TCkpCmRvbSA8LSB0aWxlZGJfZG9tYWluKGMoZDEsIGQyKSkKdmFsIDwtIHRpbGVkYl9hdHRyKCJ2YWwiLCB0eXBlID0gIklOVDMyIikKc2NoIDwtIHRpbGVkYl9hcnJheV9zY2hlbWEoZG9tLCBjKHZhbCksIHNwYXJzZT1UUlVFKQp0aWxlZGJfYXJyYXlfY3JlYXRlKHRtcCwgc2NoKQoKCiMgUHJlcGFyZSBzb21lIGRhdGEKZGF0YSA8LSBjKDNMLCA0TCwgMUwsIDJMKQoKSSA8LSBjKDMsIDQsIDEsIDIpCkogPC0gYygzLCA0LCAyLCAxKQoKIyBPcGVuIHRoZSBhcnJheSBhbmQgd3JpdGUgdGhlIGRhdGEgdG8gaXQKQSA8LSB0aWxlZGJfc3BhcnNlKHVyaSA9IHRtcCkKQVtJLCBKXSA8LSBkYXRhCgpgYGAKCgojIyMgV3JpdGluZyBFbmNyeXB0ZWQgQXJyYXlzCgpgYGB7ciB3cml0aW5nZW5jcnlwdGVkbG93bGV2ZWx9CmN0eCA8LSB0aWxlZGJfY3R4KCkKYXJycHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9vcGVuX3dpdGhfa2V5KGN0eEBwdHIsIHVyaWRlbnNld2tleSwgIldSSVRFIiwgZW5jcnlwdGlvbl9rZXkpCgojIyBkYXRhOiBzaW1wbGUgKGludGVnZXIgc2VxdWVuY2UpIG9mIDE6MTYgdGltZXMgMTAKdmVjIDwtIDE6MTYgKiAxMEwKc3ViYXJyIDwtIGMoMUwsNEwsIDFMLDRMKQoKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeShjdHhAcHRyLCBhcnJwdHIsICJXUklURSIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X3N1YmFycmF5KHFyeXB0ciwgc3ViYXJyKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9sYXlvdXQocXJ5cHRyLCAiUk9XX01BSk9SIikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfYnVmZmVyKHFyeXB0ciwgImEiLCB2ZWMpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc3VibWl0KHFyeXB0cikKcmVzIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jbG9zZShhcnJwdHIpCiMgVE9ETyBIaWdoZXItbGV2ZWwgUiBzdXBwb3J0CgpgYGAKCgoKCiMjIyBGaXhlZC1sZW5ndGggQXR0cmlidXRlcwoKYGBge3IgZml4ZWRsZW5ndGhhdHRyaWJ1dGVzfQppZiAoZGlyLmV4aXN0cyh1cmlkZW5zZWZpeCkpIHVubGluayh1cmlkZW5zZWZpeCwgcmVjdXJzaXZlPVRSVUUpCmQxICA8LSB0aWxlZGJfZGltKGRvbWFpbiA9IGMoMUwsIDRMKSkKZDIgIDwtIHRpbGVkYl9kaW0oZG9tYWluID0gYygxTCwgNEwpKQpkb20gPC0gdGlsZWRiX2RvbWFpbihjKGQxLCBkMikpCgp2ZWMgPC0gMTozMiAqIDEwTAphdHRyIDwtIHRpbGVkYl9hdHRyKCJhIiwgdHlwZSA9IHJfdG9fdGlsZWRiX3R5cGUodmVjKSkKCiMjIHNldCB0byB0d28gdmFsdWVzIHBlciBjZWxsCnRpbGVkYjo6OmxpYnRpbGVkYl9hdHRyaWJ1dGVfc2V0X2NlbGxfdmFsX251bShhdHRyQHB0ciwgMikKc2NoIDwtIHRpbGVkYl9hcnJheV9zY2hlbWEoZG9tLCBjKGF0dHIpKQp0aWxlZGJfYXJyYXlfY3JlYXRlKHVyaWRlbnNlZml4LCBzY2gpCgpjdHggPC0gdGlsZWRiX2N0eCgpCmFycnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfb3BlbihjdHhAcHRyLCB1cmlkZW5zZWZpeCwgIldSSVRFIikKc3ViYXJyIDwtIGMoMUwsNEwsIDFMLDRMKQoKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeShjdHhAcHRyLCBhcnJwdHIsICJXUklURSIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X3N1YmFycmF5KHFyeXB0ciwgc3ViYXJyKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9sYXlvdXQocXJ5cHRyLCAiUk9XX01BSk9SIikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfYnVmZmVyKHFyeXB0ciwgImEiLCB2ZWMpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc3VibWl0KHFyeXB0cikKcmVzIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jbG9zZShhcnJwdHIpCgojVE9ETyBIaWdoZXItbGV2ZWwgUiBzdXBwb3J0CmBgYAoKIyMjIFZhci1sZW5ndGggQXR0cmlidXRlcwoKYGBge3J9CmlmIChkaXIuZXhpc3RzKHVyaWRlbnNldmFyKSkgdW5saW5rKHVyaWRlbnNldmFyLCByZWN1cnNpdmU9VFJVRSkKIyMgRGVmaW5lIGFycmF5CiMjIFRoZSBhcnJheSB3aWxsIGJlIDR4NCB3aXRoIGRpbWVuc2lvbnMgInJvd3MiIGFuZCAiY29scyIsIHdpdGggZG9tYWluIFsxLDRdLgpkb20gPC0gdGlsZWRiX2RvbWFpbihkaW1zID0gYyh0aWxlZGJfZGltKCJyb3dzIiwgYygxTCwgNEwpLCA0TCwgIklOVDMyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbGVkYl9kaW0oImNvbHMiLCBjKDFMLCA0TCksIDRMLCAiSU5UMzIiKSkpCgoKYXR0ciA8LSB0aWxlZGJfYXR0cigiYTEiLCB0eXBlID0gIkNIQVIiKQojIyBzZXQgdG8gdmFyaWFibGUgbGVuZ3RoCnRpbGVkYjo6OmxpYnRpbGVkYl9hdHRyaWJ1dGVfc2V0X2NlbGxfdmFsX251bShhdHRyQHB0ciwgTkEpCgojIyBub3cgc2V0IHRoZSBzY2hlbWEKY3R4IDwtIHRpbGVkYl9jdHgoKQpzY2hwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X3NjaGVtYV9jcmVhdGUoY3R4QHB0ciwgIkRFTlNFIikKdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X3NjaGVtYV9zZXRfZG9tYWluKHNjaHB0ciwgZG9tQHB0cikKdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X3NjaGVtYV9zZXRfY2VsbF9vcmRlcihzY2hwdHIsICJST1dfTUFKT1IiKQp0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfc2NoZW1hX3NldF90aWxlX29yZGVyKHNjaHB0ciwgIlJPV19NQUpPUiIpCnRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9zY2hlbWFfYWRkX2F0dHJpYnV0ZShzY2hwdHIsIGF0dHJAcHRyKQoKCgojIyBDcmVhdGUgdGhlIChlbXB0eSkgYXJyYXkgb24gZGlzay4KdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X2NyZWF0ZSh1cmlkZW5zZXZhciwgc2NocHRyKQoKZGF0YSA8LSAiYWJiY2NjZGRlZWVmZ2hoaGlqampra2xtbm9vcCI7Cm9mZnNldHMgPC0gYygwTCwgMUwsIDNMLCA2TCwgOEwsIDExTCwgMTJMLCAxM0wsIDE2TCwgMTdMLCAyMEwsIDIyTCwgMjNMLCAyNEwsIDI1TCwgMjdMKQoKY3R4IDwtIHRpbGVkYl9jdHgoKQphcnJwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X29wZW4oY3R4QHB0ciwgdXJpZGVuc2V2YXIsICJXUklURSIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnkoY3R4QHB0ciwgYXJycHRyLCAiV1JJVEUiKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9sYXlvdXQocXJ5cHRyLCAiUk9XX01BSk9SIikKCmJ1ZnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfYnVmZmVyX3Zhcl9jaGFyX2NyZWF0ZShvZmZzZXRzLCBkYXRhKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9idWZmZXJfdmFyX2NoYXIocXJ5cHRyLCAiYTEiLCBidWZwdHIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc3VibWl0KHFyeXB0cikKdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X2Nsb3NlKGFycnB0cikKCiNUT0RPIEhpZ2hlci1sZXZlbCBSIHN1cHBvcnQKYGBgCgoKIyMjIEdldHRpbmcgdGhlIEZyYWdtZW50IEluZm8KCmBgYHtyIGZyYWdtZW50aW5mb30KIyMgY29udGludWluZyBmcm9tIHByZXZpb3VzIGV4YW1wbGUgb24gZGVuc2UgdmFyaWFibGUgbGVuZ3RoIGFycmF5CiMjIChidXQgdGhpcyB3b3JrcyBvZiBjb3Vyc2Ugd2l0aCBhbnkgYXJyYXkgYWZ0ZXIgYSB3cml0ZSBpcyBuZWVkZWQKbnVtZnJhZyA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfZ2V0X2ZyYWdtZW50X251bShxcnlwdHIpCnVyaXR4dCA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfZ2V0X2ZyYWdtZW50X3VyaShxcnlwdHIsIDApCgojI1RPRE8gSGlnaGVyLWxldmVsIFIgc3VwcG9ydApgYGAKCiMjIFJlYWRpbmcgQXJyYXlzCgojIyMgUmVhZGluZyB0aGUgQXJyYXkgU2NoZW1hCgpgYGB7ciBzY2hlbWFsb2FkfQphcnJheV9uYW1lIDwtIHVyaXNwYXJzZQpBIDwtIHRpbGVkYl9zcGFyc2UodXJpID0gYXJyYXlfbmFtZSkKIyBjYW4gYWxzbyBsb2FkIGRlbnNlCnNjaCA8LSBzY2hlbWEoQSkKIyBjYW4gYWxzbyBvcGVuIGVuY3J5cHRlZCBhcnJheXMgd2l0aCBrZXkKYGBgCgojIyMjIEluc3BlY3RpbmcgdGhlIGFycmF5IHNjaGVtYQoKYGBge3Igc2NoZW1haW5zcGVjdH0KYXJyYXlfbmFtZSA8LSB1cmlzcGFyc2UKCiMgR2V0IGFycmF5IHNjaGVtYQpBIDwtIHRpbGVkYl9zcGFyc2UodXJpID0gYXJyYXlfbmFtZSkKIyBvciB1c2UgdGlsZWRiX2RlbnNlKHVyaSA9IGFycmF5X25hbWUpIGZvciBkZW5zZQpzY2hlbWEgPC0gc2NoZW1hKEEpCgojIEdldCBhcnJheSB0eXBlCnNwYXJzZSA8LSBpcy5zcGFyc2Uoc2NoZW1hKQoKIyBHZXQgdGlsZSBjYXBhY2l0eQp0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfc2NoZW1hX2dldF9jYXBhY2l0eShzY2hAcHRyKQojIFRPRE86IFIgYWNjZXNzb3IgZm9yIHRpbGUKCiMgR2V0IHRpbGUgb3JkZXIKdF9vcmRlciA8LSB0aWxlX29yZGVyKHNjaGVtYSkKCiMgR2V0IGNlbGwgb3JkZXIKY19vcmRlciA8LSBjZWxsX29yZGVyKHNjaGVtYSkKCiMgR2V0IGNvb3JkaW5hdGVzIGFuZCBvZmZzZXQgZmlsdGVyIGxpc3QKcmVzbGlzdCA8LSBmaWx0ZXJfbGlzdChzY2hlbWEpCgojIEdldCB0aGUgYXJyYXkgZG9tYWluCmRvbSA8LSBkb21haW4oc2NoZW1hKQoKIyBHZXQgYWxsIGF0dHJpYnV0ZXMgYXMgbGlzdAphdHRycyA8LSBhdHRycyhzY2hlbWEpCgojIEdldCBhdHRyaWJ1dGUgZnJvbSBuYW1lCmF0dHIgPC0gYXR0cnMoc2NoZW1hLCAiYXR0ciIpCgojIER1bXAgdGhlIGFycmF5IHNjaGVtYSBpbiBBU0NJSSBmb3JtYXQgdG8gc3Rkb3V0CnNob3coc2NoZW1hKQpgYGAKCgojIyMjIEluc3BlY3RpbmcgRG9tYWluCgpgYGB7ciBpbnNwZWN0ZG9tYWlufQojIC4uLiBnZXQgYXJyYXkgc2NoZW1hCiMgLi4uIGdldCBkb21haW4gZnJvbSBzY2hlbWEKZG9tIDwtIGRvbWFpbihzY2hlbWEpCgojIEdldCB0aGUgZG9tYWluIGRhdGF0eXBlIChpLmUuLCB0aGUgZGF0YXR5cGUgb2YgYWxsIGRpbWVuc2lvbnMpCnR5cGUgPC0gZGF0YXR5cGUoZG9tKQoKIyBHZXQgbnVtYmVyIG9mIGRpbWVuc2lvbnMKZGltX251bSA8LSBkaW0oZG9tKQoKIyBHZXQgYWxsIGRpbWVuc2lvbgpkaW1zIDwtIGRpbWVuc2lvbnMoZG9tKQoKIyBEdW1wIHRoZSBkb21haW4gaW4gQVNDSUkgZm9ybWF0IGluIHRoZSBzZWxlY3RlZCBvdXRwdXQKc2hvdyhkb20pCmBgYAoKIyMjIyBJbnNwZWN0aW5nIERpbWVuc2lvbnMKCmBgYHtyIGluc3BlY3RkaW1lbnNpb25zLCBldmFsPUZBTFNFfQojIC4uLiBnZXQgYXJyYXkgc2NoZW1hCiMgLi4uIGdldCBkb21haW4KIyAuLi4gZ2V0IGRpbWVuc2lvbiBieSBpbmRleCBvciBuYW1lCgojRklYTUUKCiMgR2V0IGRpbWVuc2lvbiBuYW1lCmRpbV9uYW1lIDwtIG5hbWUoZGltKQoKIyBHZXQgZGltZW5zaW9uIGRhdGF0eXBlCmRpbV90eXBlIDwtIGRhdGF0eXBlKGRpbSkKCiMgR2V0IGRpbWVuc2lvbiBkb21haW4KZG9tYWluIDwtIGRvbWFpbihkaW0pCgojIEdldCB0aWxlIGV4dGVudAp0aWxlX2V4dGVudCA8LSB0aWxlKGRpbSkKCiMgRHVtcCB0aGUgZGltZW5zaW9uIGluIEFTQ0lJIGZvcm1hdCBpbiB0aGUgc2VsZWN0ZWQgb3V0cHV0CnNob3coZGltKQoKYGBgCgoKIyMjIyBJbnNwZWN0aW5nIEF0dHJpYnV0ZXMKCmBgYHtyIGluc3BlY3RhdHRyLCBldmFsPUZBTFNFfQojIC4uLiBnZXQgYXJyYXkgc2NoZW1hCiMgLi4uIGdldCBhdHRyaWJ1dGUgYnkgaW5kZXggb3IgbmFtZQoKc3RvcGlmbm90KGlzLm51bGwoYXR0cikpCiMgR2V0IGF0dHJpYnV0ZSBuYW1lCmF0dHJfbmFtZSA8LSBuYW1lKGF0dHIpCgojIEdldCBhdHRyaWJ1dGUgZGF0YXR5cGUKYXR0cl90eXBlIDwtIGRhdGF0eXBlKGF0dHIpCgojIEdldCBmaWx0ZXIgbGlzdApmaWx0ZXJfbGlzdCA8LSBmaWx0ZXJfbGlzdChhdHRyKQoKIyBDaGVjayBpZiBhdHRyaWJ1dGUgaXMgdmFyaWFibGUtbGVuZ3RoCiMgdmFyaWFibGUgbGVuZ3RoIGF0dHJpYnR1ZXMgYXJlIG5vdCBzdXBwb3J0ZWQgaW4gUiBjdXJyZW50bHkKCiMgR2V0IG51bWJlciBvZiB2YWx1ZXMgcGVyIGNlbGwKbnVtIDwtIG5jZWxscyhhdHRyKQoKIyBHZXQgY2VsbCBzaXplIGZvciB0aGlzIGF0dHJpYnV0ZQpzeiA8LS0gdGlsZWRiOjo6bGlidGlsZWRiX2F0dHJpYnV0ZV9nZXRfY2VsbF92YWwoYXR0ckBwdHIpCgojIER1bXAgdGhlIGF0dHJpYnV0ZSBpbiBBU0NJSSBmb3JtYXQgaW4gdGhlIHNlbGVjdGVkIG91dHB1dApzaG93KGF0dHIpCmBgYAoKCgojIyMjIEluc3BlY3RpbmcgRmlsdGVycwoKYGBge3IgaW5zcGVjdGZpbHRlcnMsIGV2YWw9RkFMU0V9CmZsIDwtIHRpbGVkYl9maWx0ZXJfbGlzdCgpCgojIGdldCBudW1iZXIgb2YgZmlsdGVyCnRpbGVkYjo6OmxpYnRpbGVkYl9maWx0ZXJfbGlzdF9nZXRfbmZpbHRlcnMoZmxAcHRyKQoKIyBnZXQgbWF4IGNodW5rIHNpemUKdGlsZWRiOjo6bGlidGlsZWRiX2ZpbHRlcl9saXN0X2dldF9tYXhfY2h1bmtfc2l6ZShmbEBwdHIpCgojIGdldCBmaWx0ZXIgZnJvbSBpbmRleAppbmQgPC0gaQp0aWxlZGI6OjpsaWJ0aWxlZGJfZmlsdGVyX2xpc3RfZ2V0X2ZpbHRlcl9mcm9tX2luZGV4KGZsQHB0ciwgaW5kKQpgYGAKCiMjIyBCYXNpYyBSZWFkaW5nCgpgYGB7ciBiYXNpc2NyZWFkaW5nfQojIENyZWF0ZSBhIFRpbGVEQiBjb250ZXh0CmN0eCA8LSB0aWxlZGJfY3R4KCkKCiMgT3BlbiBhIGRlbnNlIGFycmF5CkEgPC0gdGlsZWRiX2RlbnNlKHVyaSA9IHVyaWRlbnNlLCBjdHg9Y3R4KQoKIyBPciwgb3BlbiBhIHNwYXJzZSBhcnJheQojIEEgPC0gdGlsZWRiX3NwYXJzZSh1cmkgPSAiPGFycmF5LXVyaT4iLCBjdHg9Y3R4KQoKIyBTbGljZSBvbmx5IHJvd3MgMSwgMiBhbmQgY29scyAyLCAzLCA0CmRhdGEgPC0gQVsxOjIsIDI6NF0Kc2hvdyhkYXRhKQpgYGAKCiMjIyMgQmFzaWMgUmVhZGluZyB1c2luZyBMb3ctTGV2ZWwgQ29kZQoKYGBge3IgYmFzaXNjcmVhZGluZ2xvd2xldmVsfQpjdHggPC0gdGlsZWRiX2N0eCgpCmFycnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfb3BlbihjdHhAcHRyLCB1cmlkZW5zZSwgIlJFQUQiKQojIyBzdWJhcnJheSBvZiByb3dzIDEsMiBhbmQgY29scyAyLDMsNApzdWJhcnIgPC0gYygxTCwyTCwgMkwsNEwpCgpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5KGN0eEBwdHIsIGFycnB0ciwgIlJFQUQiKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9zdWJhcnJheShxcnlwdHIsIHN1YmFycikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfbGF5b3V0KHFyeXB0ciwgIlJPV19NQUpPUiIpCnYgPC0gaW50ZWdlcig2KSAgIyByZXNlcnZlIHNwYWNlCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X2J1ZmZlcihxcnlwdHIsICJhIiwgdikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zdWJtaXQocXJ5cHRyKQpwcmludCh2KSAgICAgICAgICMgdW5mb3JtZWQgYXJyYXksIG5vIGNvb3JkaW5hdGVzCnJlcyA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfY2xvc2UoYXJycHRyKQpgYGAKCiMjIyBSZWFkaW5nIEVuY3J5cHRlZCBBcnJheXMKCmBgYHtyIHJlYWRlbmNyeXB0ZWR9CmN0eCA8LSB0aWxlZGJfY3R4KCkKYXJycHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9vcGVuX3dpdGhfa2V5KGN0eEBwdHIsIHVyaWRlbnNld2tleSwgIlJFQUQiLCBlbmNyeXB0aW9uX2tleSkKCiMjIHN1YmFycmF5IG9mIHJvd3MgMSwyIGFuZCBjb2xzIDIsMyw0CnN1YmFyciA8LSBjKDFMLDJMLCAyTCw0TCkKCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnkoY3R4QHB0ciwgYXJycHRyLCAiUkVBRCIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X3N1YmFycmF5KHFyeXB0ciwgc3ViYXJyKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9sYXlvdXQocXJ5cHRyLCAiUk9XX01BSk9SIikKdiA8LSBpbnRlZ2VyKDYpICAjIHJlc2VydmUgc3BhY2UKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfYnVmZmVyKHFyeXB0ciwgImEiLCB2KQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3N1Ym1pdChxcnlwdHIpCnByaW50KHYpICAgICAgICAgIyB1bmZvcm1lZCBhcnJheSwgbm8gY29vcmRpbmF0ZXMKcmVzIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jbG9zZShhcnJwdHIpCgojIyBUT0RPIG9wZW4gYXQgdGltZXN0YW1wIGV4YW1wbGUKYGBgCgojIyMgTXVsdGktcmFuZ2UgU3ViYXJyYXlzCgpgYGB7ciBtdWx0aXJhbmdlfQpjdHggPC0gdGlsZWRiX2N0eCgpCmFycnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfb3BlbihjdHhAcHRyLCB1cmlkZW5zZSwgIlJFQUQiKQoKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeShjdHhAcHRyLCBhcnJwdHIsICJSRUFEIikKCiMjIHJhbmdlIG9mIHJvd3MgMSBhbmQgMiwgYW5kIDQgZm9yIGRpbSAxLCBhbGwgcm93cyBmb3IgZGltIDIKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9hZGRfcmFuZ2UocXJ5cHRyLCAwLCAxTCwgMkwpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfYWRkX3JhbmdlKHFyeXB0ciwgMCwgNEwsIDRMKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X2FkZF9yYW5nZShxcnlwdHIsIDEsIDFMLCA0TCkKCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X2xheW91dChxcnlwdHIsICJST1dfTUFKT1IiKQp2IDwtIGludGVnZXIoMTIpICAjIHJlc2VydmUgc3BhY2UKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfYnVmZmVyKHFyeXB0ciwgImEiLCB2KQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3N1Ym1pdChxcnlwdHIpCnByaW50KHYpICAgICAgICAgIyB1bmZvcm1lZCBhcnJheSwgbm8gY29vcmRpbmF0ZXMKcmVzIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jbG9zZShhcnJwdHIpCmBgYAoKIyMjIEluY29tcGxldGUgUXVlcmllcwoKYGBge3IgaW5jb21wbGV0ZXJlYWRzfQpjdHggPC0gdGlsZWRiX2N0eCgpCmFycnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfb3BlbihjdHhAcHRyLCB1cmlkZW5zZSwgIlJFQUQiKQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5KGN0eEBwdHIsIGFycnB0ciwgIlJFQUQiKQpzdWJhcnIgPC0gYygxTCw0TCwgMUwsNEwpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X3N1YmFycmF5KHFyeXB0ciwgc3ViYXJyKQp2ZWMgPC0gaW50ZWdlcig0KSAgIyByZXNlcnZlIChpbnN1ZmZpY2llbnQpIHNwYWNlCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X2J1ZmZlcihxcnlwdHIsICJhIiwgdmVjKQpmaW5pc2hlZCA8LSBGQUxTRQp3aGlsZSAoIWZpbmlzaGVkKSB7CiAgcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zdWJtaXQocXJ5cHRyKQogIHByaW50KHZlYykKICBmaW5pc2hlZCA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc3RhdHVzKHFyeXB0cikgPT0gIkNPTVBMRVRFIgp9CnJlcyA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfY2xvc2UoYXJycHRyKQpgYGAKCiMjIyBSZXN1bHQgRXN0aW1hdGlvbgoKYGBge3IgcmVzdWx0ZXN0aW1hdGlvbn0KY3R4IDwtIHRpbGVkYl9jdHgoKQphcnJwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX2FycmF5X29wZW4oY3R4QHB0ciwgdXJpZGVuc2V2YXIsICJSRUFEIikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeShjdHhAcHRyLCBhcnJwdHIsICJSRUFEIikKZXN0c3ogPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X2dldF9lc3RfcmVzdWx0X3NpemVfdmFyKHFyeXB0ciwgImExIikKcmVzIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jbG9zZShhcnJwdHIpCmVzdHN6CmBgYAoKIyMjIFRpbWUgVHJhdmVsaW5nCgpgYGB7ciB0aW1ldHJhdmVsfQpjdHggPC0gdGlsZWRiX2N0eCgpCnRzdGFtcCA8LSBTeXMudGltZSgpIC0gMS4wOyAjIG9uZSBzZWNvbmRzIGFnbyAoYXMgdGhlIGFycmF5cyBjcmVhdGVkIGhlcmUgYXJlIGluIHRlbXAuZmlsZXMpCmFycnB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfYXJyYXlfb3Blbl9hdChjdHhAcHRyLCB1cmlkZW5zZSwgIlJFQUQiLCB0c3RhbXApCnN1YmFyciA8LSBjKDFMLDJMLCAyTCw0TCkKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeShjdHhAcHRyLCBhcnJwdHIsICJSRUFEIikKcXJ5cHRyIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9xdWVyeV9zZXRfc3ViYXJyYXkocXJ5cHRyLCBzdWJhcnIpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc2V0X2xheW91dChxcnlwdHIsICJST1dfTUFKT1IiKQp2IDwtIGludGVnZXIoNikgICMgcmVzZXJ2ZSBzcGFjZQpxcnlwdHIgPC0gdGlsZWRiOjo6bGlidGlsZWRiX3F1ZXJ5X3NldF9idWZmZXIocXJ5cHRyLCAiYSIsIHYpCnFyeXB0ciA8LSB0aWxlZGI6OjpsaWJ0aWxlZGJfcXVlcnlfc3VibWl0KHFyeXB0cikKcmVzIDwtIHRpbGVkYjo6OmxpYnRpbGVkYl9hcnJheV9jbG9zZShhcnJwdHIpCnYKYGBgCgojIyBFbWJlZGRlZCBTUUwKCiMjIEFzeW5jaHJvbm91cyBRdWVyaWVzCgojIyBDb25maWd1cmF0aW9uCgojIyBBcnJheSBNZXRhZGF0YQoKIyMjIFdyaXRpbmcgQXJyYXkgTWV0YWRhdGEKCmBgYHtyIHdyaXRlbWV0YWRhdGF9CnJlcyA8LSB0aWxlZGJfcHV0X21ldGFkYXRhKHVyaWRlbnNlLCAiYWFhIiwgMTAwTCkKcmVzIDwtIHRpbGVkYl9wdXRfbWV0YWRhdGEodXJpZGVuc2UsICJiYiIsIGMoMS4xLCAyLjIpKQpgYGAKCiMjIyBSZWFkaW5nIEFycmF5IE1ldGFkYXRhCgpPbmUgY2FuIHJlYWQgYnkga2V5OgoKYGBge3IgcmVhZG1ldGFkYXRhfQp0aWxlZGJfZ2V0X21ldGFkYXRhKHVyaWRlbnNlLCAiYWFhIikKYGBgCgpPciBvbmUgY2FuIHJldHJpZXZlIGFsbCBtZXRhZGF0YSBhdCBvbmNlOgoKYGBge3IgcmVhZG1ldGFkYXRhYWxsfQptZCA8LSB0aWxlZGJfZ2V0X2FsbF9tZXRhZGF0YSh1cmlkZW5zZSkKcHJpbnQobWQpCmBgYAoKIyMjIERlbGV0aW5nIEFycmF5IE1ldGFkYXRhCgpgYGB7ciBkZWxldGVtZXRhZGF0YX0KcmVzIDwtIHRpbGVkYl9kZWxldGVfbWV0YWRhdGEodXJpZGVuc2UsICJhYWEiKQpgYGAK