($INBOX_DIR/description missing)  
help / color / mirror / Atom feed
[PATCH 8/8] batch build
29+ messages / 5 participants
[nested] [flat]

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)

---
 src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
 src/backend/access/brin/brin_tuple.c        |   1 +
 src/include/access/brin_tuple.h             |   2 +-
 3 files changed, 186 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
 #include "access/brin.h"
 #include "access/brin_internal.h"
 #include "access/brin_tuple.h"
+#include "access/hash.h"	/* XXX strange that it fails because of BRIN_AM_OID without this */
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
 typedef struct Ranges
 {
 	Oid		typid;
+	Oid		colloid;
+	AttrNumber	attno;
 
 	/* (2*nranges + nvalues) <= maxvalues */
 	int		nranges;	/* number of ranges in the array (stored) */
 	int		nvalues;	/* number of values in the data array (all) */
 	int		maxvalues;	/* maximum number of values (reloption) */
 
+	/*
+	 * Batch mode means we're simply appending values into new range,
+	 * without any expensive steps (sorting, deduplication, ...). The
+	 * buffer is sized to be much larger, but we keep the actual target
+	 * size so that when serializing values we can do the right thing.
+	 */
+	bool	batch_mode;
+	int		target_maxvalues;
+
 	/* values stored for this range - either raw values, or ranges */
 	Datum	values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
 	range->nvalues = serialized->nvalues;
 	range->maxvalues = serialized->maxvalues;
 	range->typid = serialized->typid;
+	range->batch_mode = false;
 
 	typbyval = get_typbyval(serialized->typid);
 	typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
 /*
  * Sort combine ranges using qsort (with BTLessStrategyNumber function).
  */
-static void
+static int
 sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
-					CombineRange *cranges, int ncranges)
+					CombineRange *cranges, int ncranges,
+					bool deduplicate)
 {
-	compare_context cxt;
+	int				n;
+	int				i;
+	compare_context	cxt;
 
 	/* sort the values */
 	cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
 
 	qsort_arg(cranges, ncranges, sizeof(CombineRange),
 			  compare_combine_ranges, (void *) &cxt);
+
+	if (!deduplicate)
+		return ncranges;
+
+	/* optionally deduplicate the ranges */
+	n = 1;
+	for (i = 1; i < ncranges; i++)
+	{
+		if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+		{
+			if (i != n)
+				memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+			n++;
+		}
+	}
+
+	return n;
 }
 
 /*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
  */
 static CombineRange *
 build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
-					 Datum newvalue, int *nranges)
+					 bool addvalue, Datum newvalue, int *nranges,
+					 bool deduplicate)
 {
 	int				ncranges;
 	CombineRange   *cranges;
 
 	/* now do the actual merge sort */
-	ncranges = ranges->nranges + ranges->nvalues + 1;
+	ncranges = ranges->nranges + ranges->nvalues;
+
+	/* should we add an extra value? */
+	if (addvalue)
+		ncranges += 1;
+
 	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
-	*nranges = ncranges;
 
 	/* put the new value at the beginning */
-	cranges[0].minval = newvalue;
-	cranges[0].maxval = newvalue;
-	cranges[0].collapsed = true;
+	if (addvalue)
+	{
+		cranges[0].minval = newvalue;
+		cranges[0].maxval = newvalue;
+		cranges[0].collapsed = true;
 
-	/* then the regular and collapsed ranges */
-	fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+		/* then the regular and collapsed ranges */
+		fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+	}
+	else
+		fill_combine_ranges(cranges, ncranges, ranges);
 
 	/* and sort the ranges */
-	sort_combine_ranges(cmp, colloid, cranges, ncranges);
+	ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+								   deduplicate);
+
+	/* remember how many cranges we built */
+	*nranges = ncranges;
 
 	return cranges;
 }
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
 }
 #endif
 
+
 /*
  * reduce_combine_ranges
  *		reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 
 	Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
 
+	Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+	/*
+	 * When batch-building, there should be no ranges. So either the
+	 * number of ranges is 0 or we're not in batching mode.
+	 */
+	Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+	/* When batch-building, just add it and we're done. */
+	if (ranges->batch_mode)
+	{
+		/* there has to be free space, if we've sized the struct */
+		Assert(ranges->nvalues < ranges->maxvalues);
+
+		ranges->values[ranges->nvalues++] = newval;
+
+		return true;
+	}
+
 	/*
 	 * Bail out if the value already is covered by the range.
 	 *
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 					 ranges->nvalues);
 
 	/* OK build the combine ranges */
-	cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+	cranges = build_combine_ranges(cmpFn, colloid, ranges,
+								   true, newval, &ncranges,
+								   false);
 
 	/* Reduce the ranges if needed */
 	if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
 	return true;
 }
 
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* combine ranges */
+	CombineRange   *cranges;
+	int				ncranges;
+	DistanceValue  *distances;
+
+	MemoryContext	ctx;
+	MemoryContext	oldctx;
+
+	/*
+	 * This should only be used in batch mode, and there should be no
+	 * ranges, just individual values.
+	 */
+	Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+	/*
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it. Otherwise
+	 * we'd have problems e.g. when building indexes. So we create a local
+	 * memory context and make sure we free the memory before leaving this
+	 * function (not after every call).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* OK build the combine ranges */
+	cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+								   false, (Datum) 0, &ncranges,
+								   true);	/* deduplicate */
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used at the very end of "batch mode" and we
+	 * don't expect more tuples to be inserted soon.
+	 */
+	ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(cranges, ncranges) <= max_values);
+
+	/* decompose the combine ranges into regular ranges and single values */
+	store_combine_ranges(ranges, cranges, ncranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Check the ordering invariants are not violated (for both parts). */
+	AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+	AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+					 ranges->nvalues);
+}
+
 Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
 }
 
 static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
 {
 	Ranges *ranges = (Ranges *) DatumGetPointer(src);
-	SerializedRanges *s = range_serialize(ranges);
+	SerializedRanges *s;
+
+	/* In batch mode, we need to compress the accumulated values. */
+	if (ranges->batch_mode)
+		compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	s = range_serialize(ranges);
 	dst[0] = PointerGetDatum(s);
 }
 
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
 	/*
 	 * If this is the first non-null value, we need to initialize the range
 	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode,
+	 * i.e. we size the buffer for the maximum possible number of items in
+	 * the range (based on range size and max number of items on a page).
+	 *
+	 * XXX This may require quite a bit of memory, so maybe we should use
+	 * some value in between. OTOH most tables will have much wider rows,
+	 * so the number of rows per page is much lower.
 	 */
 	if (column->bv_allnulls)
 	{
 		MemoryContext oldctx;
 
-		oldctx = MemoryContextSwitchTo(column->bv_context);
+		BlockNumber		pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
 
-		ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
 		ranges->typid = attr->atttypid;
+		ranges->batch_mode = true;
+		ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
 
 		MemoryContextSwitchTo(oldctx);
 
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	oldctx = MemoryContextSwitchTo(ctx);
 
 	/* allocate and fill */
-	cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+	cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
 
 	/* fill the combine ranges with entries for the first range */
 	fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
 	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
 											 BTLessStrategyNumber);
 
-	/* sort the combine ranges */
-	sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+	/* sort the combine ranges (don't deduplicate) */
+	sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
 
 	/*
 	 * We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_serialize)
 		{
 			tuple->bt_columns[keyno].bv_serialize(
+				brdesc,
 				tuple->bt_columns[keyno].bv_mem_value,
 				tuple->bt_columns[keyno].bv_values);
 		}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
-- 
2.26.2


--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
 name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="brin.sh"

IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--





^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
@ 2022-04-15 06:33 Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  0 siblings, 1 reply; 29+ messages in thread

From: Kyotaro Horiguchi @ 2022-04-15 06:33 UTC (permalink / raw)
  To: [email protected]; +Cc: [email protected]; [email protected]; [email protected]; pgsql-hackers

At Fri, 15 Apr 2022 14:55:48 +0900, Shinya Kato <[email protected]> wrote in 
> I understand. For backward compatibility, I left the ROLE clause
> option as it is and changed the IN ROLE clause option to --membership
> option.

Thanks!

-	printf(_("  -g, --role=ROLE           new role will be a member of this role\n"));
+	printf(_("  -g, --role=ROLE        new role will be a member of this role\n"));

This looks lik an unexpected change.  We shoudl preserve it, but *I*
think that we can add a synonym of the old --role for
understandability/memorability.  (By the way "-g" looks like coming
from "group", which looks somewhat strange..)

> printf(_("  -b, --belongs-to=ROLE     new role will be a member of this role\n"));

+	printf(_("  -m, --membership=ROLE     this role will be a member of new role\n"));

membership sounds somewhat obscure, it seems *to me* members is clearer

> printf(_("  -m, --member=ROLE        new role will be a member of this role\n"));

I'd like to hear others' opinions.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center






^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
@ 2022-04-18 13:59 ` Robert Haas <[email protected]>
  2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  0 siblings, 1 reply; 29+ messages in thread

From: Robert Haas @ 2022-04-18 13:59 UTC (permalink / raw)
  To: Kyotaro Horiguchi <[email protected]>; +Cc: Shinya Kato <[email protected]>; Daniel Gustafsson <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers

On Fri, Apr 15, 2022 at 2:33 AM Kyotaro Horiguchi
<[email protected]> wrote:
> > printf(_("  -b, --belongs-to=ROLE     new role will be a member of this role\n"));
>
> +       printf(_("  -m, --membership=ROLE     this role will be a member of new role\n"));
>
> membership sounds somewhat obscure, it seems *to me* members is clearer
>
> > printf(_("  -m, --member=ROLE        new role will be a member of this role\n"));
>
> I'd like to hear others' opinions.

I think that we need to preserve consistency with the SQL syntax as
much as possible -- and neither MEMBER nor MEMBERSHIP nor BELONGS_TO
appear in that syntax. A lot of the terminology in this area seems
poorly chosen and confusing to me, but having two ways to refer to
something probably won't be an improvement even if the second name is
better-chosen than the first one.

-- 
Robert Haas
EDB: http://www.enterprisedb.com






^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
@ 2022-04-19 01:50   ` Kyotaro Horiguchi <[email protected]>
  2022-04-19 16:13     ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  0 siblings, 1 reply; 29+ messages in thread

From: Kyotaro Horiguchi @ 2022-04-19 01:50 UTC (permalink / raw)
  To: [email protected]; +Cc: [email protected]; [email protected]; [email protected]; pgsql-hackers

Thanks!

At Mon, 18 Apr 2022 09:59:48 -0400, Robert Haas <[email protected]> wrote in 
> On Fri, Apr 15, 2022 at 2:33 AM Kyotaro Horiguchi
> <[email protected]> wrote:
> > > printf(_("  -b, --belongs-to=ROLE     new role will be a member of this role\n"));
> >
> > +       printf(_("  -m, --membership=ROLE     this role will be a member of new role\n"));
> >
> > membership sounds somewhat obscure, it seems *to me* members is clearer
> >
> > > printf(_("  -m, --member=ROLE        new role will be a member of this role\n"));
> >
> > I'd like to hear others' opinions.
> 
> I think that we need to preserve consistency with the SQL syntax as
> much as possible -- and neither MEMBER nor MEMBERSHIP nor BELONGS_TO
> appear in that syntax. A lot of the terminology in this area seems
> poorly chosen and confusing to me, but having two ways to refer to
> something probably won't be an improvement even if the second name is
> better-chosen than the first one.

Hmm.. So, "-r/--role" and "-m/--member(ship)" is the (least worse) way
to go?  Or we can give up adding -m for the reason of being hard to
name it..

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center






^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
@ 2022-04-19 16:13     ` Robert Haas <[email protected]>
  2022-04-20 08:04       ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-21 04:29       ` Re: Add --{no-,}bypassrls flags to createuser Michael Paquier <[email protected]>
  0 siblings, 2 replies; 29+ messages in thread

From: Robert Haas @ 2022-04-19 16:13 UTC (permalink / raw)
  To: Kyotaro Horiguchi <[email protected]>; +Cc: Shinya Kato <[email protected]>; Daniel Gustafsson <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers

On Mon, Apr 18, 2022 at 9:50 PM Kyotaro Horiguchi
<[email protected]> wrote:
> Hmm.. So, "-r/--role" and "-m/--member(ship)" is the (least worse) way
> to go?  Or we can give up adding -m for the reason of being hard to
> name it..

Hmm, yeah, I hadn't quite realized what the problem was when I wrote
that. I honestly don't know what to do about that. Renaming the
existing option is not great, but having the syntax diverge between
SQL and CLI is not great either. Giving up is also not great. Not sure
what is best.

-- 
Robert Haas
EDB: http://www.enterprisedb.com






^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-19 16:13     ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
@ 2022-04-20 08:04       ` Kyotaro Horiguchi <[email protected]>
  1 sibling, 0 replies; 29+ messages in thread

From: Kyotaro Horiguchi @ 2022-04-20 08:04 UTC (permalink / raw)
  To: [email protected]; +Cc: [email protected]; [email protected]; [email protected]; pgsql-hackers

At Tue, 19 Apr 2022 12:13:51 -0400, Robert Haas <[email protected]> wrote in 
> On Mon, Apr 18, 2022 at 9:50 PM Kyotaro Horiguchi
> <[email protected]> wrote:
> > Hmm.. So, "-r/--role" and "-m/--member(ship)" is the (least worse) way
> > to go?  Or we can give up adding -m for the reason of being hard to
> > name it..
> 
> Hmm, yeah, I hadn't quite realized what the problem was when I wrote
> that. I honestly don't know what to do about that. Renaming the
> existing option is not great, but having the syntax diverge between
> SQL and CLI is not great either. Giving up is also not great. Not sure
> what is best.

Exactly..  So I'm stuckX(

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center






^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-19 16:13     ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
@ 2022-04-21 04:29       ` Michael Paquier <[email protected]>
  2022-04-21 19:51         ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  1 sibling, 1 reply; 29+ messages in thread

From: Michael Paquier @ 2022-04-21 04:29 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; +Cc: Kyotaro Horiguchi <[email protected]>; Shinya Kato <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Tue, Apr 19, 2022 at 12:13:51PM -0400, Robert Haas wrote:
> On Mon, Apr 18, 2022 at 9:50 PM Kyotaro Horiguchi
> <[email protected]> wrote:
>> Hmm.. So, "-r/--role" and "-m/--member(ship)" is the (least worse) way
>> to go?  Or we can give up adding -m for the reason of being hard to
>> name it..
> 
> Hmm, yeah, I hadn't quite realized what the problem was when I wrote
> that. I honestly don't know what to do about that. Renaming the
> existing option is not great, but having the syntax diverge between
> SQL and CLI is not great either. Giving up is also not great. Not sure
> what is best.

Changing one existing option to mean something entirely different
should be avoided, as this could lead to silent breakages.  As the
origin of the problem is that the option --role means "IN ROLE" in the
SQL grammar, we could keep around --role for compatibility while
marking it deprecated, and add two new options whose names would be
more consistent with each other.  One choice could be --role-name and
--in-role-name, where --in-role-name maps to the older --role, just to
give an idea.
--
Michael


Attachments:

  [application/pgp-signature] signature.asc (833B, ../../YmDdxXPVNR8Km0v%[email protected]/2-signature.asc)
  download

^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-19 16:13     ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-21 04:29       ` Re: Add --{no-,}bypassrls flags to createuser Michael Paquier <[email protected]>
@ 2022-04-21 19:51         ` Robert Haas <[email protected]>
  2022-04-21 20:21           ` Re: Add --{no-,}bypassrls flags to createuser David G. Johnston <[email protected]>
  0 siblings, 1 reply; 29+ messages in thread

From: Robert Haas @ 2022-04-21 19:51 UTC (permalink / raw)
  To: Michael Paquier <[email protected]>; +Cc: Kyotaro Horiguchi <[email protected]>; Shinya Kato <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Thu, Apr 21, 2022 at 12:30 AM Michael Paquier <[email protected]> wrote:
> On Tue, Apr 19, 2022 at 12:13:51PM -0400, Robert Haas wrote:
> > On Mon, Apr 18, 2022 at 9:50 PM Kyotaro Horiguchi
> > <[email protected]> wrote:
> >> Hmm.. So, "-r/--role" and "-m/--member(ship)" is the (least worse) way
> >> to go?  Or we can give up adding -m for the reason of being hard to
> >> name it..
> >
> > Hmm, yeah, I hadn't quite realized what the problem was when I wrote
> > that. I honestly don't know what to do about that. Renaming the
> > existing option is not great, but having the syntax diverge between
> > SQL and CLI is not great either. Giving up is also not great. Not sure
> > what is best.
>
> Changing one existing option to mean something entirely different
> should be avoided, as this could lead to silent breakages.  As the
> origin of the problem is that the option --role means "IN ROLE" in the
> SQL grammar, we could keep around --role for compatibility while
> marking it deprecated, and add two new options whose names would be
> more consistent with each other.  One choice could be --role-name and
> --in-role-name, where --in-role-name maps to the older --role, just to
> give an idea.

I don't think that having both --role and --role-name, doing different
things, is going to be clear at all.

-- 
Robert Haas
EDB: http://www.enterprisedb.com






^ permalink  raw  reply  [nested|flat] 29+ messages in thread

* Re: Add --{no-,}bypassrls flags to createuser
  2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
  2022-04-19 16:13     ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
  2022-04-21 04:29       ` Re: Add --{no-,}bypassrls flags to createuser Michael Paquier <[email protected]>
  2022-04-21 19:51         ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
@ 2022-04-21 20:21           ` David G. Johnston <[email protected]>
  0 siblings, 0 replies; 29+ messages in thread

From: David G. Johnston @ 2022-04-21 20:21 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; +Cc: Michael Paquier <[email protected]>; Kyotaro Horiguchi <[email protected]>; Shinya Kato <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

On Thu, Apr 21, 2022 at 12:51 PM Robert Haas <[email protected]> wrote:

> On Thu, Apr 21, 2022 at 12:30 AM Michael Paquier <[email protected]>
> wrote:
> > On Tue, Apr 19, 2022 at 12:13:51PM -0400, Robert Haas wrote:
> > > On Mon, Apr 18, 2022 at 9:50 PM Kyotaro Horiguchi
> > > <[email protected]> wrote:
> > >> Hmm.. So, "-r/--role" and "-m/--member(ship)" is the (least worse) way
> > >> to go?  Or we can give up adding -m for the reason of being hard to
> > >> name it..
> > >
> > > Hmm, yeah, I hadn't quite realized what the problem was when I wrote
> > > that. I honestly don't know what to do about that. Renaming the
> > > existing option is not great, but having the syntax diverge between
> > > SQL and CLI is not great either. Giving up is also not great. Not sure
> > > what is best.
> >
> > Changing one existing option to mean something entirely different
> > should be avoided, as this could lead to silent breakages.  As the
> > origin of the problem is that the option --role means "IN ROLE" in the
> > SQL grammar, we could keep around --role for compatibility while
> > marking it deprecated, and add two new options whose names would be
> > more consistent with each other.  One choice could be --role-name and
> > --in-role-name, where --in-role-name maps to the older --role, just to
> > give an idea.
>
> I don't think that having both --role and --role-name, doing different
> things, is going to be clear at all.
>
>
-g/--role   or maybe/additionally (--in-role)?
-m/--role-to or maybe/additionally (--to-role)?

I'm ok with -m/--member as well (like with --role only one role can be
specified per switch instance so member, not membership, the later meaning,
at least for me, the collective).

That -m doesn't match --role-to is no worse than -g not matching --role, a
short option seems worthwhile, and the -m (membership) mnemonic should be
simple to pick-up.

I don't see the addition of "-name" to the option name being beneficial.

Yes, the standard doesn't use the "TO" prefix for "ROLE" - but taking that
liberty for consistency here is very appealing and there isn't another SQL
clause that it would be confused with.

David J.


^ permalink  raw  reply  [nested|flat] 29+ messages in thread


end of thread, other threads:[~2022-04-21 20:21 UTC | newest]

Thread overview: 29+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2022-04-15 06:33 Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
2022-04-18 13:59 ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
2022-04-19 01:50   ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
2022-04-19 16:13     ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
2022-04-20 08:04       ` Re: Add --{no-,}bypassrls flags to createuser Kyotaro Horiguchi <[email protected]>
2022-04-21 04:29       ` Re: Add --{no-,}bypassrls flags to createuser Michael Paquier <[email protected]>
2022-04-21 19:51         ` Re: Add --{no-,}bypassrls flags to createuser Robert Haas <[email protected]>
2022-04-21 20:21           ` Re: Add --{no-,}bypassrls flags to createuser David G. Johnston <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox